Make kaleido pivot keyframable

This commit is contained in:
th committed 2026-06-05 14:31:31 +02:00
1 parent c646f66e8f
commit 70cf131c47
9 files changed
+285 -87

No files matched your search

+21 -18
View File
@@ -4,7 +4,7 @@
// (lib/exportPlayer.ts engine source — by mirroring the formulas in
// JS) all derive identical geometry from the same definitions.
import type { Layer, ClipInstance } from '@/types/scene'
import type { AnimatableProps, Layer, ClipInstance } from '@/types/scene'
/** Number of radial copies to render. Returns 1 when kaleido is off
* or the count is below the floor — the caller should treat 1 as
@@ -107,42 +107,45 @@ export function layerKaleidoCount(layer: Layer): number {
return kaleidoCountFor(layer)
}
/** Effective pivot for a layer. Defaults to the layer's sampled
* position; an explicit `kaleidoPivotX/Y` overrides per-axis. */
/** Effective pivot for a layer. Lookup chain:
* 1. `props.kaleidoPivotX/Y` — keyframed value (lets the pivot
* animate)
* 2. `Layer.kaleidoPivotX/Y` — legacy static override (kept for
* back-compat with projects saved before the feature went
* keyframable)
* 3. layer's sampled (x, y) — default
*/
export function layerKaleidoPivot(
layer: Layer,
sampledX: number,
sampledY: number,
props: AnimatableProps,
): { x: number; y: number } {
return {
x: layer.kaleidoPivotX ?? sampledX,
y: layer.kaleidoPivotY ?? sampledY,
x: props.kaleidoPivotX ?? layer.kaleidoPivotX ?? props.x,
y: props.kaleidoPivotY ?? layer.kaleidoPivotY ?? props.y,
}
}
/** World-space (x, y, rotation) for arm `index` of a kaleido'd
* layer. The source (index 0) is unchanged. Higher indices
* rotate around the pivot — both their position AND their
* orientation, since layer kaleido always rotates copies. */
* layer. The source (index 0) is unchanged. Higher indices rotate
* around the pivot — both their position AND their orientation,
* since layer kaleido always rotates copies. */
export function layerArmTransform(
layer: Layer,
sampledX: number,
sampledY: number,
sampledRotation: number,
props: AnimatableProps,
index: number,
count: number,
): { x: number; y: number; rotation: number } {
if (index === 0 || count <= 1) {
return { x: sampledX, y: sampledY, rotation: sampledRotation }
return { x: props.x, y: props.y, rotation: props.rotation }
}
const { x: px, y: py } = layerKaleidoPivot(layer, sampledX, sampledY)
const { x: px, y: py } = layerKaleidoPivot(layer, props)
const angle = kaleidoAngleDegrees(index, count)
const a = (angle * Math.PI) / 180
const dx = sampledX - px
const dy = sampledY - py
const dx = props.x - px
const dy = props.y - py
return {
x: px + dx * Math.cos(a) - dy * Math.sin(a),
y: py + dx * Math.sin(a) + dy * Math.cos(a),
rotation: sampledRotation + angle,
rotation: props.rotation + angle,
}
}