Tanoda
AnimationCurveUtil.cs
Go to the documentation of this file.
1/******************************************************************************
2 * Copyright (C) Ultraleap, Inc. 2011-2020. *
3 * *
4 * Use subject to the terms of the Apache License 2.0 available at *
5 * http://www.apache.org/licenses/LICENSE-2.0, or another agreement *
6 * between Ultraleap and you, your company or other organization. *
7 ******************************************************************************/
8
9using System;
10using UnityEngine;
11#if UNITY_EDITOR
12using UnityEditor;
13#endif
14using Leap.Unity.Query;
15
16namespace Leap.Unity {
17
18 public static class DefaultCurve {
19
20 public static AnimationCurve Zero {
21 get {
22 AnimationCurve curve = new AnimationCurve();
23 curve.AddKey(0, 0);
24 curve.AddKey(1, 0);
25 return curve;
26 }
27 }
28
29 public static AnimationCurve One {
30 get {
31 AnimationCurve curve = new AnimationCurve();
32 curve.AddKey(0, 1);
33 curve.AddKey(1, 1);
34 return curve;
35 }
36 }
37
38 public static AnimationCurve LinearUp {
39 get {
40 AnimationCurve curve = new AnimationCurve();
41 curve.AddKey(new Keyframe(0, 0, 1, 1));
42 curve.AddKey(new Keyframe(1, 1, 1, 1));
43 return curve;
44 }
45 }
46
47 public static AnimationCurve LinearDown {
48 get {
49 AnimationCurve curve = new AnimationCurve();
50 curve.AddKey(new Keyframe(0, 1, -1, -1));
51 curve.AddKey(new Keyframe(1, 0, -1, -1));
52 return curve;
53 }
54 }
55
56 public static AnimationCurve SigmoidUp {
57 get {
58 AnimationCurve curve = new AnimationCurve();
59 curve.AddKey(new Keyframe(0, 0, 0, 0));
60 curve.AddKey(new Keyframe(1, 1, 0, 0));
61 return curve;
62 }
63 }
64
65 public static AnimationCurve SigmoidDown {
66 get {
67 AnimationCurve curve = new AnimationCurve();
68 curve.AddKey(new Keyframe(0, 1, 0, 0));
69 curve.AddKey(new Keyframe(1, 0, 0, 0));
70 return curve;
71 }
72 }
73
74 public static AnimationCurve SigmoidUpDown {
75 get {
76 AnimationCurve curve = new AnimationCurve();
77 curve.AddKey(new Keyframe(0, 0, 0, 0));
78 curve.AddKey(new Keyframe(0.5f, 1, 0, 0));
79 curve.AddKey(new Keyframe(1, 0, 0, 0));
80 return curve;
81 }
82 }
83 }
84
85 public static class AnimationCurveUtil {
86
87 public static bool IsConstant(this AnimationCurve curve) {
88 var keys = curve.keys;
89 var first = keys[0];
90 for (int i = 0; i < keys.Length; i++) {
91 var key = keys[i];
92
93 if (!Mathf.Approximately(first.value, key.value)) {
94 return false;
95 }
96
97 if (!Mathf.Approximately(key.inTangent, 0) && !float.IsInfinity(key.inTangent)) {
98 return false;
99 }
100
101 if (!Mathf.Approximately(key.outTangent, 0) && !float.IsInfinity(key.outTangent)) {
102 return false;
103 }
104 }
105 return true;
106 }
107
108 public static bool ContainsKeyAtTime(this AnimationCurve curve, float time, float tolerance = 0.0000001f) {
109 return curve.keys.Query().Any(k => Mathf.Abs(k.time - time) < tolerance);
110 }
111
112 public static AnimationCurve GetCropped(this AnimationCurve curve, float start, float end, bool slideToStart = true) {
113 AnimationCurve newCurve = new AnimationCurve();
114
115 //Get a copy of the latest key before start
116 Keyframe? latestBeforeStart = null;
117 var keys = curve.keys;
118 for (int i = 0; i < keys.Length; i++) {
119 var key = keys[i];
120 if (key.time >= start) break;
121
122 latestBeforeStart = key;
123 }
124
125 //Remove all keys before start or after end
126 for (int i = keys.Length; i-- != 0;) {
127 if (keys[i].time < start || keys[i].time > end) {
128 curve.RemoveKey(i);
129 }
130 }
131
132 bool alreadyHasZero = false;
133 for (int i = 0; i < keys.Length; i++) {
134 var key = keys[i];
135 if (key.time >= start && key.time <= end) {
136 if (slideToStart) {
137 key.time -= start;
138 }
139
140 if (Mathf.Approximately(key.time, 0)) {
141 alreadyHasZero = true;
142 }
143
144 newCurve.AddKey(key);
145 }
146 }
147
148 if (latestBeforeStart.HasValue && !alreadyHasZero) {
149 var toInsert = latestBeforeStart.Value;
150 toInsert.time = 0;
151 newCurve.AddKey(toInsert);
152 }
153
154 return newCurve;
155 }
156
157#if UNITY_EDITOR
158 public static AnimationCurve Compress(AnimationCurve curve, float maxDelta = 0.005f, int checkSteps = 8) {
159 var curveArray = new AnimationCurve[] { curve };
160
161 var result = CompressCurves(curveArray,
162 (src, dst, t) => {
163 float originalValue = src[0].Evaluate(t);
164 float compressedValue = dst[0].Evaluate(t);
165 return Mathf.Abs(originalValue - compressedValue) < maxDelta;
166 },
167 checkSteps);
168
169 return result[0];
170 }
171
172 public static void CompressRotations(AnimationCurve xCurve,
173 AnimationCurve yCurve,
174 AnimationCurve zCurve,
175 AnimationCurve wCurve,
176 out AnimationCurve compressedXCurve,
177 out AnimationCurve compressedYCurve,
178 out AnimationCurve compressedZCurve,
179 out AnimationCurve compressedWCurve,
180 float maxAngleError = 1,
181 int checkSteps = 8) {
182 var curveArray = new AnimationCurve[] {
183 xCurve,
184 yCurve,
185 zCurve,
186 wCurve
187 };
188
189 var result = CompressCurves(curveArray,
190 (src, dst, t) => {
191 Quaternion srcRot;
192 srcRot.x = src[0].Evaluate(t);
193 srcRot.y = src[1].Evaluate(t);
194 srcRot.z = src[2].Evaluate(t);
195 srcRot.w = src[3].Evaluate(t);
196
197 Quaternion dstRot;
198 dstRot.x = dst[0].Evaluate(t);
199 dstRot.y = dst[1].Evaluate(t);
200 dstRot.z = dst[2].Evaluate(t);
201 dstRot.w = dst[3].Evaluate(t);
202
203 float angle;
204 Vector3 axis;
205 (Quaternion.Inverse(dstRot) * srcRot).ToAngleAxis(out angle, out axis);
206
207 return angle < maxAngleError;
208 },
209 checkSteps);
210
211 compressedXCurve = result[0];
212 compressedYCurve = result[1];
213 compressedZCurve = result[2];
214 compressedWCurve = result[3];
215 }
216
217 public static void CompressPositions(AnimationCurve xCurve,
218 AnimationCurve yCurve,
219 AnimationCurve zCurve,
220 out AnimationCurve compressedXCurve,
221 out AnimationCurve compressedYCurve,
222 out AnimationCurve compressedZCurve,
223 float maxDistanceError = 0.005f,
224 int checkSteps = 8) {
225 var curveArray = new AnimationCurve[] {
226 xCurve,
227 yCurve,
228 zCurve
229 };
230
231 var results = CompressCurves(curveArray,
232 (src, dst, t) => {
233 Vector3 srcPos;
234 srcPos.x = src[0].Evaluate(t);
235 srcPos.y = src[1].Evaluate(t);
236 srcPos.z = src[2].Evaluate(t);
237
238 Vector3 dstPos;
239 dstPos.x = dst[0].Evaluate(t);
240 dstPos.y = dst[1].Evaluate(t);
241 dstPos.z = dst[2].Evaluate(t);
242
243 return Vector3.Distance(srcPos, dstPos) < maxDistanceError;
244 },
245 checkSteps);
246
247 compressedXCurve = results[0];
248 compressedYCurve = results[1];
249 compressedZCurve = results[2];
250 }
251
252 public static AnimationCurve CompressScale(AnimationCurve curve,
253 float maxScaleFactor,
254 int checkSteps = 8) {
255 var curveArray = new AnimationCurve[] {
256 curve,
257 };
258
259 var results = CompressCurves(curveArray,
260 (src, dst, t) => {
261 float srcValue = src[0].Evaluate(t);
262 float dstValue = dst[0].Evaluate(t);
263
264 if (Mathf.Sign(srcValue) == Mathf.Sign(dstValue)) {
265 return srcValue / dstValue < maxScaleFactor
266 && dstValue / srcValue < maxScaleFactor;
267 } else {
268 return false;
269 }
270 },
271 checkSteps);
272
273 return results[0];
274 }
275
276 public static void CompressColorsHSV(AnimationCurve rCurve,
277 AnimationCurve gCurve,
278 AnimationCurve bCurve,
279 out AnimationCurve compressedRCurve,
280 out AnimationCurve compressedGCurve,
281 out AnimationCurve compressedBCurve,
282 float maxHueError,
283 float maxSaturationError,
284 float maxValueError,
285 int checkSteps = 8) {
286 var curveArray = new AnimationCurve[] {
287 rCurve,
288 gCurve,
289 bCurve
290 };
291
292 var results = CompressCurves(curveArray,
293 (src, dst, t) => {
294 Color srcColor;
295 srcColor.r = src[0].Evaluate(t);
296 srcColor.g = src[1].Evaluate(t);
297 srcColor.b = src[2].Evaluate(t);
298 srcColor.a = 1;
299
300 Color dstColor;
301 dstColor.r = dst[0].Evaluate(t);
302 dstColor.g = dst[1].Evaluate(t);
303 dstColor.b = dst[2].Evaluate(t);
304 dstColor.a = 1;
305
306 float sH, sS, sV;
307 float dH, dS, dV;
308 Color.RGBToHSV(srcColor, out sH, out sS, out sV);
309 Color.RGBToHSV(dstColor, out dH, out dS, out dV);
310
311 return Mathf.Abs(sH - dH) < maxHueError &&
312 Mathf.Abs(sS - dS) < maxSaturationError &&
313 Mathf.Abs(sV - dV) < maxValueError;
314 },
315 checkSteps);
316
317 compressedRCurve = results[0];
318 compressedGCurve = results[1];
319 compressedBCurve = results[2];
320 }
321
322 public static AnimationCurve[] CompressCurves(AnimationCurve[] curves,
323 Func<AnimationCurve[], AnimationCurve[], float, bool> isGood,
324 int checkSteps = 8) {
325 var keyframes = new Keyframe[curves.Length][];
326 var position = new int[curves.Length];
327 var nextFrame = new int[curves.Length];
328 var compressedCurves = new AnimationCurve[curves.Length];
329
330 for (int i = 0; i < curves.Length; i++) {
331 var keys = curves[i].keys;
332
333 compressedCurves[i] = new AnimationCurve(keys);
334
335 for (int j = 0; j < keys.Length; j++) {
336 var leftT = AnimationUtility.GetKeyLeftTangentMode(curves[i], j);
337 var rightT = AnimationUtility.GetKeyRightTangentMode(curves[i], j);
338
339 AnimationUtility.SetKeyLeftTangentMode(compressedCurves[i], j, leftT);
340 AnimationUtility.SetKeyRightTangentMode(compressedCurves[i], j, rightT);
341 }
342
343 keyframes[i] = keys;
344 position[i] = keys.Length - 2;
345 nextFrame[i] = keys.Length - 1;
346 }
347
348 do {
349 for (int i = 0; i < curves.Length; i++) {
350
351 if (position[i] > 0) {
352 Keyframe nextKeyframe = keyframes[i][nextFrame[i]];
353 Keyframe currKeyframe = keyframes[i][position[i]];
354 Keyframe prevKeyframe = keyframes[i][position[i] - 1];
355
356 var leftT = AnimationUtility.GetKeyLeftTangentMode(compressedCurves[i], position[i]);
357 var rightT = AnimationUtility.GetKeyRightTangentMode(compressedCurves[i], position[i]);
358 compressedCurves[i].RemoveKey(position[i]);
359
360 for (int k = 0; k < checkSteps; k++) {
361 float percent = k / (checkSteps - 1.0f);
362
363 float prevTime = Mathf.Lerp(currKeyframe.time, prevKeyframe.time, percent);
364 float nextTime = Mathf.Lerp(currKeyframe.time, nextKeyframe.time, percent);
365
366 bool isPrevGood = isGood(curves, compressedCurves, prevTime);
367 bool isNextgood = isGood(curves, compressedCurves, nextTime);
368
369 if (!isPrevGood || !isNextgood) {
370 int index = compressedCurves[i].AddKey(currKeyframe);
371 AnimationUtility.SetKeyLeftTangentMode(compressedCurves[i], index, leftT);
372 AnimationUtility.SetKeyRightTangentMode(compressedCurves[i], index, rightT);
373
374 nextFrame[i] = position[i];
375 break;
376 }
377 }
378
379 position[i]--;
380 }
381 }
382 } while (position.Query().Any(p => p > 0));
383
384 return compressedCurves;
385 }
386#endif
387
388 public static void AddBooleanKey(this AnimationCurve curve, float time, bool value) {
389 var keyframe = new Keyframe() { time = time, value = value ? 1 : 0 };
390
391#if UNITY_EDITOR
392 int keyframeIdx = curve.AddKey(keyframe);
393 AnimationUtility.SetKeyBroken(curve, keyframeIdx, true);
394 AnimationUtility.SetKeyLeftTangentMode(curve, keyframeIdx,
395 AnimationUtility.TangentMode.Constant);
396 AnimationUtility.SetKeyRightTangentMode(curve, keyframeIdx,
397 AnimationUtility.TangentMode.Constant);
398#else
399 curve.AddKey(keyframe);
400#endif
401 }
402 }
403
404
405}
UnityEngine.Color Color
Definition: TestScript.cs:32