summaryrefslogtreecommitdiff
path: root/apps/web/src/lib/animation/curve-bridge.ts
diff options
context:
space:
mode:
authorMaze Winther <mazewinther@gmail.com>2026-04-06 01:23:47 +0200
committerMaze Winther <mazewinther@gmail.com>2026-04-06 01:23:47 +0200
commitfcc09e80e253831a163e2e4941542e0b34f80740 (patch)
tree4cc7aafd0756bb3fa23dfd41080237e462b5ecf2 /apps/web/src/lib/animation/curve-bridge.ts
parentc56d6f8188cc1c0e72d75611269a8bda7b79e727 (diff)
parentdedb13546a16235e7d6649961d0c9b235f62aa34 (diff)
downloadopencut-fcc09e80e253831a163e2e4941542e0b34f80740.tar.gz
Merge branch 'temp'
Diffstat (limited to 'apps/web/src/lib/animation/curve-bridge.ts')
-rw-r--r--apps/web/src/lib/animation/curve-bridge.ts82
1 files changed, 82 insertions, 0 deletions
diff --git a/apps/web/src/lib/animation/curve-bridge.ts b/apps/web/src/lib/animation/curve-bridge.ts
new file mode 100644
index 00000000..55f96933
--- /dev/null
+++ b/apps/web/src/lib/animation/curve-bridge.ts
@@ -0,0 +1,82 @@
+import { TIME_EPSILON_SECONDS } from "@/constants/animation-constants";
+import {
+ getDefaultLeftHandle,
+ getDefaultRightHandle,
+} from "@/lib/animation/bezier";
+import type {
+ CurveHandle,
+ NormalizedCubicBezier,
+ ScalarAnimationKey,
+} from "@/lib/animation/types";
+
+const VALUE_EPSILON = 1e-6;
+
+function clamp01({ value }: { value: number }): number {
+ return Math.max(0, Math.min(1, value));
+}
+
+export function getNormalizedCubicBezierForScalarSegment({
+ leftKey,
+ rightKey,
+}: {
+ leftKey: ScalarAnimationKey;
+ rightKey: ScalarAnimationKey;
+}): NormalizedCubicBezier | null {
+ const spanTime = rightKey.time - leftKey.time;
+ const spanValue = rightKey.value - leftKey.value;
+ if (
+ Math.abs(spanTime) <= TIME_EPSILON_SECONDS ||
+ Math.abs(spanValue) <= VALUE_EPSILON
+ ) {
+ return null;
+ }
+
+ const rightHandle =
+ leftKey.rightHandle ?? getDefaultRightHandle({ leftKey, rightKey });
+ const leftHandle =
+ rightKey.leftHandle ?? getDefaultLeftHandle({ leftKey, rightKey });
+
+ return [
+ clamp01({ value: rightHandle.dt / spanTime }),
+ rightHandle.dv / spanValue,
+ clamp01({ value: 1 + leftHandle.dt / spanTime }),
+ 1 + leftHandle.dv / spanValue,
+ ];
+}
+
+export function getCurveHandlesForNormalizedCubicBezier({
+ leftKey,
+ rightKey,
+ cubicBezier,
+}: {
+ leftKey: ScalarAnimationKey;
+ rightKey: ScalarAnimationKey;
+ cubicBezier: NormalizedCubicBezier;
+}): {
+ rightHandle: CurveHandle;
+ leftHandle: CurveHandle;
+} | null {
+ const spanTime = rightKey.time - leftKey.time;
+ const spanValue = rightKey.value - leftKey.value;
+ if (
+ Math.abs(spanTime) <= TIME_EPSILON_SECONDS ||
+ Math.abs(spanValue) <= VALUE_EPSILON
+ ) {
+ return null;
+ }
+
+ const [rawX1, y1, rawX2, y2] = cubicBezier;
+ const x1 = clamp01({ value: rawX1 });
+ const x2 = clamp01({ value: rawX2 });
+
+ return {
+ rightHandle: {
+ dt: spanTime * x1,
+ dv: spanValue * y1,
+ },
+ leftHandle: {
+ dt: spanTime * (x2 - 1),
+ dv: spanValue * (y2 - 1),
+ },
+ };
+}