Skip to content

Commit

Permalink
Merge pull request #24 from nasshu2916/feature/amin-reduce-key
Browse files Browse the repository at this point in the history
フレーム削減周りのリファクタ
  • Loading branch information
nasshu2916 authored Nov 16, 2024
2 parents 174c6b0 + 360bfec commit 71e735e
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 51 deletions.
69 changes: 18 additions & 51 deletions Assets/ArtNet/Editor/DmxRecorder/TimelineConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,10 @@ public void SaveDmxTimelineClips(string directory)
var curves = timelineUniverse.AnimationCurves();
for (var i = 0; i < curves.Length; i++)
{
if (curves[i].keys.Length == 0) continue;
clip.SetCurve($"Universe{universe}", typeof(DmxData), $"Ch{i + 1:D3}", curves[i]);
var curve = curves[i];
if (curve.keys.Length == 0) continue;

clip.SetCurve($"Universe{universe}", typeof(DmxData), $"Ch{i + 1:D3}", curve);
}
}
SaveAsset(clip, directory, "ArtNetDmx.anim");
Expand All @@ -75,17 +77,17 @@ private static void SaveAsset<T>(T asset, string directory, string fileName) whe
public class TimelineUniverse
{
public int Universe { get; }
private List<DmxFrameData>[] ChannelDmxFrameData { get; }
private List<KeyFrameData>[] ChannelDmxFrameData { get; }

public TimelineUniverse(int groupKey, IReadOnlyCollection<UniverseData> universeData)
{
Universe = groupKey;
ChannelDmxFrameData = new List<DmxFrameData>[512];
ChannelDmxFrameData = new List<KeyFrameData>[512];

for (var i = 0; i < ChannelDmxFrameData.Length; i++)
{
ChannelDmxFrameData[i] = universeData.Where(x => x.Values.Length > i)
.Select(x => new DmxFrameData((float) x.Time, x.Values[i]))
.Select(x => new KeyFrameData((float) x.Time, x.Values[i]))
.OrderBy(x => x.Time).ToList();
}
}
Expand All @@ -94,7 +96,7 @@ public TimelineUniverse(int universe, AnimationClip clip)
{
Universe = universe;
var curveBindings = AnimationUtility.GetCurveBindings(clip);
ChannelDmxFrameData = new List<DmxFrameData>[512];
ChannelDmxFrameData = new List<KeyFrameData>[512];
for (var i = 0; i < ChannelDmxFrameData.Length; i++)
{
var propertyName = $"Ch{i + 1:D3}";
Expand All @@ -106,7 +108,7 @@ public TimelineUniverse(int universe, AnimationClip clip)

if (curve is null) continue;

ChannelDmxFrameData[i] = curve.keys.Select(x => new DmxFrameData((int) (x.time * 1000), (byte) x.value)).ToList();
ChannelDmxFrameData[i] = curve.keys.Select(x => new KeyFrameData((int) (x.time * 1000), (byte) x.value)).ToList();
}
}

Expand Down Expand Up @@ -162,7 +164,14 @@ public AnimationCurve[] AnimationCurves()
{
var keyframes = ChannelDmxFrameData[i]
.Select(data => new Keyframe(data.Time, data.Value)).ToArray();
curves[i] = new AnimationCurve(keyframes);
var curve = new AnimationCurve(keyframes);
for (var j = 0; j < curve.keys.Length; j++)
{
AnimationUtility.SetKeyLeftTangentMode(curve, j, AnimationUtility.TangentMode.Constant);
AnimationUtility.SetKeyRightTangentMode(curve, j, AnimationUtility.TangentMode.Constant);
}

curves[i] = curve;
}

return curves;
Expand All @@ -173,40 +182,10 @@ public void ThinOutUnchangedFrames()
for (var i = 0; i < ChannelDmxFrameData.Length; i++)
{
var dmxFrameData = ChannelDmxFrameData[i];
if (dmxFrameData.Count == 0) continue;

var latest = dmxFrameData[0];
var newDmxFrameData = new List<DmxFrameData> { dmxFrameData[0] };

for (var j = 1; j < dmxFrameData.Count - 1; j++)
{
var current = dmxFrameData[j];
var next = dmxFrameData[j + 1];
if (IsOmittedFrame(latest, current, next)) continue;

latest = current;
newDmxFrameData.Add(dmxFrameData[j]);
}

newDmxFrameData.Add(dmxFrameData[^1]);
ChannelDmxFrameData[i] = newDmxFrameData;
ChannelDmxFrameData[i] = KeyFrameReducer.Reduce(dmxFrameData);
}
}

private static bool IsOmittedFrame(
DmxFrameData prev,
DmxFrameData current,
DmxFrameData next,
float tolerance = 0.01f)
{
var prevDiff = current.Value - prev.Value;
var nextDiff = next.Value - current.Value;
var prevDiffTime = current.Time - prev.Time;
var nextDiffTime = next.Time - current.Time;

return Math.Abs(prevDiff / prevDiffTime - nextDiff / nextDiffTime) <= tolerance;
}

public IEnumerable<UniverseData> ToUniverseData()
{
var universeData = new List<UniverseData>();
Expand All @@ -226,16 +205,4 @@ public IEnumerable<UniverseData> ToUniverseData()
return universeData;
}
}

public struct DmxFrameData
{
public float Time { get; }
public byte Value { get; }

public DmxFrameData(float time, byte value)
{
Time = time;
Value = value;
}
}
}
14 changes: 14 additions & 0 deletions Assets/ArtNet/Editor/KeyFrameData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace ArtNet.Editor
{
public struct KeyFrameData
{
public float Time { get; }
public byte Value { get; }

public KeyFrameData(float time, byte value)
{
Time = time;
Value = value;
}
}
}
3 changes: 3 additions & 0 deletions Assets/ArtNet/Editor/KeyFrameData.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions Assets/ArtNet/Editor/KeyFrameReducer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;

namespace ArtNet.Editor
{
public static class KeyFrameReducer
{
public static List<KeyFrameData> Reduce(List<KeyFrameData> keyFrameData)
{
if (keyFrameData.Count <= 2) return keyFrameData;

var newDmxFrameData = new List<KeyFrameData> { keyFrameData[0] };
var latest = keyFrameData[0];

for (var i = 1; i < keyFrameData.Count - 1; i++)
{
var current = keyFrameData[i];
if (latest.Value == current.Value) continue;

newDmxFrameData.Add(current);
latest = current;
}

newDmxFrameData.Add(keyFrameData[^1]);
return newDmxFrameData;
}
}
}
3 changes: 3 additions & 0 deletions Assets/ArtNet/Editor/KeyFrameReducer.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

78 changes: 78 additions & 0 deletions Assets/ArtNet/Editor/RamerDouglasPeucker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using UnityEngine;

namespace ArtNet.Editor
{
public class RamerDouglasPeucker
{
private readonly float _threshold;

public RamerDouglasPeucker(float errorThreshold)
{
_threshold = errorThreshold * errorThreshold;
}

public List<Keyframe> Reduce(ReadOnlySpan<Keyframe> keys)
{
return Execute(keys, 0, keys.Length - 1);
}

private List<Keyframe> Execute(ReadOnlySpan<Keyframe> keys, int startIndex, int endIndex)
{
if (endIndex - startIndex < 2)
{
return new List<Keyframe> { keys[startIndex], keys[endIndex] };
}

// 最大距離のKeyframeを探索
var maxDistance = 0f;
var maxIndex = startIndex;
for (var i = startIndex + 1; i < endIndex; i++)
{
var distance = PerpendicularDistanceSquared(keys[i], keys[startIndex], keys[endIndex]);
if (distance <= maxDistance) continue;

maxIndex = i;
maxDistance = distance;
}

// 最大距離が閾値未満なら直線を返す
if (maxDistance < _threshold)
{
return new List<Keyframe> { keys[startIndex], keys[endIndex] };
}

// 最大距離の点で再帰的に処理
var result1 = Execute(keys, startIndex, maxIndex);
var result2 = Execute(keys, maxIndex, endIndex);

// 重複を取り除いて結合
result1.RemoveAt(result1.Count - 1);
result1.AddRange(result2);

return result1;
}

/// <summary>
/// 垂線距離の2乗を計算
/// </summary>
private static float PerpendicularDistanceSquared(Keyframe point, Keyframe startPoint, Keyframe endPoint)
{
var dx = endPoint.time - startPoint.time;
var dy = endPoint.value - startPoint.value;

var denominator = dx * dx + dy * dy;

if (denominator < 1e-6f)
{
var psx = point.time - startPoint.time;
var psy = point.value - startPoint.value;
return psx * psx + psy * psy;
}

var numerator = dy * point.time - dx * point.value + endPoint.time * startPoint.value - endPoint.value * startPoint.time;
return (numerator * numerator) / denominator;
}
}
}
3 changes: 3 additions & 0 deletions Assets/ArtNet/Editor/RamerDouglasPeucker.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 71e735e

Please sign in to comment.