File size: 1,875 Bytes
1cb6ef5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
export interface Keyframe {
  id: string;
  time: number;
  position: [number, number, number];
  rotation: [number, number, number];
  scale: [number, number, number];
  easing: 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out';
}

export interface Track {
  objectId: string;
  keyframes: Keyframe[];
}

export function interpolateKF(
  a: Keyframe,
  b: Keyframe,
  t: number
): { position: [number,number,number]; rotation: [number,number,number]; scale: [number,number,number] } {
  const ease = (x: number, type: Keyframe['easing']): number => {
    switch (type) {
      case 'ease-in':     return x * x;
      case 'ease-out':    return 1 - (1 - x) * (1 - x);
      case 'ease-in-out': return x < 0.5 ? 2*x*x : 1 - Math.pow(-2*x+2, 2)/2;
      default:            return x;
    }
  };
  const et = ease(t, b.easing);
  const lerp = (a: number, b: number) => a + (b - a) * et;
  const lerpV3 = (
    a: [number,number,number],
    b: [number,number,number]
  ): [number,number,number] => [
    lerp(a[0], b[0]),
    lerp(a[1], b[1]),
    lerp(a[2], b[2]),
  ];
  return {
    position: lerpV3(a.position, b.position),
    rotation: lerpV3(a.rotation, b.rotation),
    scale:    lerpV3(a.scale,    b.scale),
  };
}

export function getFramesAtTime(keyframes: Keyframe[], time: number) {
  const sorted = [...keyframes].sort((a, b) => a.time - b.time);
  if (!sorted.length) return null;
  if (time <= sorted[0].time) return { a: sorted[0], b: sorted[0], t: 0 };
  const last = sorted[sorted.length - 1];
  if (time >= last.time) return { a: last, b: last, t: 0 };
  for (let i = 0; i < sorted.length - 1; i++) {
    if (time >= sorted[i].time && time <= sorted[i+1].time) {
      const span = sorted[i+1].time - sorted[i].time;
      const t = span === 0 ? 0 : (time - sorted[i].time) / span;
      return { a: sorted[i], b: sorted[i+1], t };
    }
  }
  return null;
}