summaryrefslogtreecommitdiff
path: root/apps/web/src/lib/animation
diff options
context:
space:
mode:
authorMaze Winther <mazewinther@gmail.com>2026-04-07 01:09:13 +0200
committerMaze Winther <mazewinther@gmail.com>2026-04-07 01:09:13 +0200
commite4b67094e757917588fb624a30aa046667403add (patch)
tree55452692eac69a5a63b58f2a5cdb701dd7023d68 /apps/web/src/lib/animation
parent79df73643185c02493364a077402726d1fa7b9ec (diff)
downloadopencut-e4b67094e757917588fb624a30aa046667403add.tar.gz
refactor: split effects and masks into dedicated rust crates, introduce MediaTime and FrameRate
Diffstat (limited to 'apps/web/src/lib/animation')
-rw-r--r--apps/web/src/lib/animation/curve-bridge.ts5
-rw-r--r--apps/web/src/lib/animation/interpolation.ts26
-rw-r--r--apps/web/src/lib/animation/keyframe-query.ts8
-rw-r--r--apps/web/src/lib/animation/keyframes.ts7
4 files changed, 18 insertions, 28 deletions
diff --git a/apps/web/src/lib/animation/curve-bridge.ts b/apps/web/src/lib/animation/curve-bridge.ts
index 55f96933..12103c3f 100644
--- a/apps/web/src/lib/animation/curve-bridge.ts
+++ b/apps/web/src/lib/animation/curve-bridge.ts
@@ -1,4 +1,3 @@
-import { TIME_EPSILON_SECONDS } from "@/constants/animation-constants";
import {
getDefaultLeftHandle,
getDefaultRightHandle,
@@ -25,7 +24,7 @@ export function getNormalizedCubicBezierForScalarSegment({
const spanTime = rightKey.time - leftKey.time;
const spanValue = rightKey.value - leftKey.value;
if (
- Math.abs(spanTime) <= TIME_EPSILON_SECONDS ||
+ spanTime === 0 ||
Math.abs(spanValue) <= VALUE_EPSILON
) {
return null;
@@ -59,7 +58,7 @@ export function getCurveHandlesForNormalizedCubicBezier({
const spanTime = rightKey.time - leftKey.time;
const spanValue = rightKey.value - leftKey.value;
if (
- Math.abs(spanTime) <= TIME_EPSILON_SECONDS ||
+ spanTime === 0 ||
Math.abs(spanValue) <= VALUE_EPSILON
) {
return null;
diff --git a/apps/web/src/lib/animation/interpolation.ts b/apps/web/src/lib/animation/interpolation.ts
index 67456aa0..1a3c0fed 100644
--- a/apps/web/src/lib/animation/interpolation.ts
+++ b/apps/web/src/lib/animation/interpolation.ts
@@ -8,7 +8,6 @@ import type {
ScalarAnimationKey,
ScalarSegmentType,
} from "@/lib/animation/types";
-import { TIME_EPSILON_SECONDS } from "@/constants/animation-constants";
import { clamp } from "@/utils/math";
import {
getBezierPoint,
@@ -36,10 +35,7 @@ function isWithinTimePair({
leftTime: number;
rightTime: number;
}): boolean {
- return (
- time >= leftTime - TIME_EPSILON_SECONDS &&
- time <= rightTime + TIME_EPSILON_SECONDS
- );
+ return time >= leftTime && time <= rightTime;
}
function lerpNumber({
@@ -67,7 +63,7 @@ function normalizeRightHandle({
return undefined;
}
- const span = Math.max(TIME_EPSILON_SECONDS, rightKey.time - leftKey.time);
+ const span = Math.max(1, rightKey.time - leftKey.time);
return {
dt: Math.min(span, Math.max(0, handle.dt)),
dv: handle.dv,
@@ -87,7 +83,7 @@ function normalizeLeftHandle({
return undefined;
}
- const span = Math.max(TIME_EPSILON_SECONDS, rightKey.time - leftKey.time);
+ const span = Math.max(1, rightKey.time - leftKey.time);
return {
dt: Math.max(-span, Math.min(0, handle.dt)),
dv: handle.dv,
@@ -187,7 +183,7 @@ function extrapolateScalarEdge({
}
const span = neighborKey.time - edgeKey.time;
- if (Math.abs(span) <= TIME_EPSILON_SECONDS) {
+ if (span === 0) {
return edgeKey.value;
}
@@ -228,8 +224,8 @@ export function getScalarChannelValueAtTime({
return fallbackValue;
}
- if (time <= firstKey.time + TIME_EPSILON_SECONDS) {
- if (time < firstKey.time - TIME_EPSILON_SECONDS) {
+ if (time <= firstKey.time) {
+ if (time < firstKey.time) {
return extrapolateScalarEdge({
mode: normalizedChannel.extrapolation?.before ?? "hold",
edgeKey: firstKey,
@@ -241,8 +237,8 @@ export function getScalarChannelValueAtTime({
return firstKey.value;
}
- if (time >= lastKey.time - TIME_EPSILON_SECONDS) {
- if (time > lastKey.time + TIME_EPSILON_SECONDS) {
+ if (time >= lastKey.time) {
+ if (time > lastKey.time) {
return extrapolateScalarEdge({
mode: normalizedChannel.extrapolation?.after ?? "hold",
edgeKey: lastKey,
@@ -261,7 +257,7 @@ export function getScalarChannelValueAtTime({
) {
const leftKey = normalizedChannel.keys[keyIndex];
const rightKey = normalizedChannel.keys[keyIndex + 1];
- if (Math.abs(time - rightKey.time) <= TIME_EPSILON_SECONDS) {
+ if (time === rightKey.time) {
return rightKey.value;
}
@@ -280,7 +276,7 @@ export function getScalarChannelValueAtTime({
}
const span = rightKey.time - leftKey.time;
- if (Math.abs(span) <= TIME_EPSILON_SECONDS) {
+ if (span === 0) {
return rightKey.value;
}
@@ -336,7 +332,7 @@ export function getDiscreteChannelValueAtTime({
});
let currentValue = fallbackValue;
for (const key of normalizedChannel.keys) {
- if (time + TIME_EPSILON_SECONDS < key.time) {
+ if (time < key.time) {
break;
}
currentValue = key.value;
diff --git a/apps/web/src/lib/animation/keyframe-query.ts b/apps/web/src/lib/animation/keyframe-query.ts
index 304a6698..dbcd041c 100644
--- a/apps/web/src/lib/animation/keyframe-query.ts
+++ b/apps/web/src/lib/animation/keyframe-query.ts
@@ -1,4 +1,3 @@
-import { TIME_EPSILON_SECONDS } from "@/constants/animation-constants";
import type {
AnimationBindingInstance,
AnimationChannel,
@@ -76,8 +75,7 @@ function getUniqueBindingKeyframeMatches({
const previousMatch = uniqueMatches[uniqueMatches.length - 1];
if (
!previousMatch ||
- Math.abs(previousMatch.keyframe.time - match.keyframe.time) >
- TIME_EPSILON_SECONDS
+ previousMatch.keyframe.time !== match.keyframe.time
) {
uniqueMatches.push(match);
continue;
@@ -249,9 +247,7 @@ export function getKeyframeAtTime({
matches: getBindingKeyframeMatches({
animations,
binding,
- }).filter(({ keyframe }) =>
- Math.abs(keyframe.time - time) <= TIME_EPSILON_SECONDS,
- ),
+ }).filter(({ keyframe }) => keyframe.time === time),
});
if (!keyframeMatch) {
return null;
diff --git a/apps/web/src/lib/animation/keyframes.ts b/apps/web/src/lib/animation/keyframes.ts
index d72f9e16..05290c1d 100644
--- a/apps/web/src/lib/animation/keyframes.ts
+++ b/apps/web/src/lib/animation/keyframes.ts
@@ -14,7 +14,6 @@ import type {
ScalarCurveKeyframePatch,
ScalarSegmentType,
} from "@/lib/animation/types";
-import { TIME_EPSILON_SECONDS } from "@/constants/animation-constants";
import { generateUUID } from "@/utils/id";
import {
cloneAnimationBinding,
@@ -44,7 +43,7 @@ function isNearlySameTime({
leftTime: number;
rightTime: number;
}): boolean {
- return Math.abs(leftTime - rightTime) <= TIME_EPSILON_SECONDS;
+ return leftTime === rightTime;
}
function hasChannelKeys({
@@ -1032,8 +1031,8 @@ function splitScalarChannelAtTime({
const rightKey = normalizedChannel.keys[keyIndex + 1];
if (
!(
- splitTime > leftKey.time + TIME_EPSILON_SECONDS &&
- splitTime < rightKey.time - TIME_EPSILON_SECONDS
+ splitTime > leftKey.time &&
+ splitTime < rightKey.time
)
) {
continue;