diff --git a/src/core/animations/keyframe-builder.ts b/src/core/animations/keyframe-builder.ts index dcd96f2a..21c1b32d 100644 --- a/src/core/animations/keyframe-builder.ts +++ b/src/core/animations/keyframe-builder.ts @@ -161,7 +161,7 @@ export class KeyframeBuilder { if (!next) break; - if (current.start + current.length > next.start) { + if (current.start + current.length - next.start > TIME_EPSILON) { throw new Error("Overlapping keyframes detected."); } } diff --git a/tests/keyframe-builder.test.ts b/tests/keyframe-builder.test.ts index 54d81bc4..e92a4eb0 100644 --- a/tests/keyframe-builder.test.ts +++ b/tests/keyframe-builder.test.ts @@ -137,6 +137,22 @@ describe("KeyframeBuilder", () => { }); describe("timeline boundary safety", () => { + it("plays adjacent decimal segments whose normalised end still rounds past the next start", () => { + const builder = new KeyframeBuilder( + [ + { start: 0, length: 0.7, from: 0, to: 1 }, + { start: 0.7, length: 3.2, from: 1, to: 1 }, + { start: 3.9, length: 0.35, from: 1, to: 0 } + ], + 4.25 + ); + + expect(builder.getValue(0.35)).toBeCloseTo(0.5); + expect(builder.getValue(2)).toBe(1); + expect(builder.getValue(4.075)).toBeCloseTo(0.5); + expect(builder.getValue(4.25)).toBe(0); + }); + it("preserves and evaluates a keyframe that extends beyond a shortened clip", () => { const builder = new KeyframeBuilder([{ start: 0, length: 10, from: 0, to: 1, interpolation: "linear" }], 5);