versioncam 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +124 -0
- package/README.md +116 -13
- package/dist/cli/commands/doctor.js +39 -2
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/init.js +25 -0
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/preview.js +10 -1
- package/dist/cli/commands/preview.js.map +1 -1
- package/dist/cli/commands/render.d.ts +17 -0
- package/dist/cli/commands/render.js +78 -21
- package/dist/cli/commands/render.js.map +1 -1
- package/dist/cli/cwd.d.ts +46 -0
- package/dist/cli/cwd.js +102 -0
- package/dist/cli/cwd.js.map +1 -0
- package/dist/cli/main.js +11 -2
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/usage.d.ts +3 -2
- package/dist/cli/usage.js +9 -4
- package/dist/cli/usage.js.map +1 -1
- package/dist/config.d.ts +24 -0
- package/dist/config.js +25 -0
- package/dist/config.js.map +1 -1
- package/dist/core/camera-track.d.ts +63 -0
- package/dist/core/camera-track.js +230 -0
- package/dist/core/camera-track.js.map +1 -0
- package/dist/core/camera.d.ts +14 -1
- package/dist/core/camera.js +18 -2
- package/dist/core/camera.js.map +1 -1
- package/dist/core/follow.d.ts +129 -0
- package/dist/core/follow.js +151 -0
- package/dist/core/follow.js.map +1 -0
- package/dist/core/motion-defaults.d.ts +29 -0
- package/dist/core/motion-defaults.js +29 -0
- package/dist/core/motion-defaults.js.map +1 -1
- package/dist/core/timeline.d.ts +10 -2
- package/dist/core/timeline.js.map +1 -1
- package/dist/driver/session.d.ts +22 -1
- package/dist/driver/session.js +48 -12
- package/dist/driver/session.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/loader.d.ts +7 -1
- package/dist/loader.js +23 -8
- package/dist/loader.js.map +1 -1
- package/dist/page/assets/index-fZtacWyp.js +2 -0
- package/dist/page/index.html +1 -1
- package/dist/render/encode.d.ts +12 -1
- package/dist/render/encode.js +94 -35
- package/dist/render/encode.js.map +1 -1
- package/dist/render/presentation.d.ts +12 -0
- package/dist/render/presentation.js +15 -0
- package/dist/render/presentation.js.map +1 -1
- package/dist/render/render.js +18 -1
- package/dist/render/render.js.map +1 -1
- package/dist/render/sampling.d.ts +13 -0
- package/dist/render/sampling.js +47 -19
- package/dist/render/sampling.js.map +1 -1
- package/dist/render/scenes.d.ts +16 -0
- package/dist/render/scenes.js +108 -0
- package/dist/render/scenes.js.map +1 -0
- package/dist/render/sequence.d.ts +18 -0
- package/dist/render/sequence.js +67 -13
- package/dist/render/sequence.js.map +1 -1
- package/dist/review/review.js +46 -21
- package/dist/review/review.js.map +1 -1
- package/dsl.md +37 -0
- package/package.json +1 -1
- package/plugin/README.md +7 -2
- package/plugin/agents/versioncam-reviewer.md +3 -2
- package/plugin/skills/versioncam/SKILL.md +60 -14
- package/plugin/skills/versioncam/authoring.md +6 -1
- package/plugin/skills/versioncam/onboarding.md +17 -17
- package/dist/page/assets/index-DUom5amc.js +0 -1
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { interpolateRect } from "./camera.js";
|
|
2
|
+
import { minjerk } from "./easing.js";
|
|
3
|
+
import { followCentres } from "./follow.js";
|
|
4
|
+
import { frameAt, timeAt } from "./timeline.js";
|
|
5
|
+
/**
|
|
6
|
+
* Turn a clip's camera writes into the keys the renderer interpolates.
|
|
7
|
+
*
|
|
8
|
+
* Every write starts from where the camera is, moving or not, and blends
|
|
9
|
+
* from there to where it is going:
|
|
10
|
+
*
|
|
11
|
+
* camera(t) = interpolateRect(before(t), destination(t), minjerk(u))
|
|
12
|
+
*
|
|
13
|
+
* where `before` is the camera as it would have gone on without this write,
|
|
14
|
+
* and `u` runs from 0 to 1 over the write's duration. Min-jerk has no slope
|
|
15
|
+
* at either end, so the camera's velocity is continuous where a blend starts
|
|
16
|
+
* and where it ends: entering a follow from a keyed zoom, leaving it for a
|
|
17
|
+
* reset, or a zoom written while another is still under way. That last one
|
|
18
|
+
* used to wait for the move before it to finish and then run short, because
|
|
19
|
+
* each glide set off from the previous glide's target.
|
|
20
|
+
*
|
|
21
|
+
* A clip that never follows and never writes a move while another is under
|
|
22
|
+
* way gets exactly the keys it always got: a hold where each write starts and
|
|
23
|
+
* a min-jerk key where it ends. Every other clip gets the camera sampled at
|
|
24
|
+
* each frame (the `"linear"` keys), since a blend between two moving framings
|
|
25
|
+
* is no longer a glide between two keys.
|
|
26
|
+
*/
|
|
27
|
+
export function cameraTrack(writes, context) {
|
|
28
|
+
const { viewport } = context;
|
|
29
|
+
const full = { x: 0, y: 0, w: viewport.width, h: viewport.height };
|
|
30
|
+
const opening = { t: 0, rect: full, ease: "hold" };
|
|
31
|
+
const effective = effectiveWrites(writes);
|
|
32
|
+
if (glidesOnly(effective))
|
|
33
|
+
return glideKeys(effective, opening);
|
|
34
|
+
return sampledKeys(effective, context, full);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Whether every write is a framing that starts once the one before it has
|
|
38
|
+
* arrived: the only case the old keys describe exactly.
|
|
39
|
+
*/
|
|
40
|
+
function glidesOnly(writes) {
|
|
41
|
+
let arrived = 0;
|
|
42
|
+
for (const write of writes) {
|
|
43
|
+
if (write.kind !== "frame" || write.t < arrived)
|
|
44
|
+
return false;
|
|
45
|
+
// The same expression the key's time is computed with below, so the
|
|
46
|
+
// comparison is between the same two doubles the keys will hold.
|
|
47
|
+
arrived = write.t + write.durationMs / 1000;
|
|
48
|
+
}
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The keys the session wrote before it could follow: hold the current
|
|
53
|
+
* framing until the write, then glide. Kept to the byte, so no recording of
|
|
54
|
+
* a clip that only zooms changes.
|
|
55
|
+
*/
|
|
56
|
+
function glideKeys(writes, opening) {
|
|
57
|
+
const keys = [opening];
|
|
58
|
+
for (const write of writes) {
|
|
59
|
+
const last = keys[keys.length - 1];
|
|
60
|
+
if (last.t < write.t) {
|
|
61
|
+
keys.push({ t: write.t, rect: last.rect, ease: "hold" });
|
|
62
|
+
}
|
|
63
|
+
keys.push({
|
|
64
|
+
t: write.t + write.durationMs / 1000,
|
|
65
|
+
rect: write.rect,
|
|
66
|
+
ease: "minjerk",
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return keys;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The writes that take effect.
|
|
73
|
+
*
|
|
74
|
+
* A release with no follow to release is nothing to do. A write replaces an
|
|
75
|
+
* earlier one at the same instant that was still to take time: two moves
|
|
76
|
+
* written together are one intention stated twice, and blending the first
|
|
77
|
+
* under the second would bend the camera towards a framing no frame was
|
|
78
|
+
* meant to show. A cut (duration 0) at the same instant is kept, since it has
|
|
79
|
+
* already happened by the time the next write starts from it.
|
|
80
|
+
*/
|
|
81
|
+
function effectiveWrites(writes) {
|
|
82
|
+
const effective = [];
|
|
83
|
+
for (const write of writes) {
|
|
84
|
+
const last = effective[effective.length - 1];
|
|
85
|
+
if (write.kind === "release") {
|
|
86
|
+
if (last?.kind === "follow")
|
|
87
|
+
effective.push(write);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
while (effective.length > 0) {
|
|
91
|
+
const previous = effective[effective.length - 1];
|
|
92
|
+
if (previous.kind === "release" ||
|
|
93
|
+
previous.t !== write.t ||
|
|
94
|
+
previous.durationMs <= 0) {
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
effective.pop();
|
|
98
|
+
}
|
|
99
|
+
effective.push(write);
|
|
100
|
+
}
|
|
101
|
+
return effective;
|
|
102
|
+
}
|
|
103
|
+
/** The first frame whose time is at or after `t`. */
|
|
104
|
+
function firstFrameFrom(t, fps) {
|
|
105
|
+
let frame = Math.max(0, Math.ceil(t * fps - 1e-9));
|
|
106
|
+
while (timeAt(frame, fps) < t)
|
|
107
|
+
frame += 1;
|
|
108
|
+
while (frame > 0 && timeAt(frame - 1, fps) >= t)
|
|
109
|
+
frame -= 1;
|
|
110
|
+
return frame;
|
|
111
|
+
}
|
|
112
|
+
/** To a thousandth of a pixel: far below anything a frame can show. */
|
|
113
|
+
const round = (value) => Math.round(value * 1000) / 1000;
|
|
114
|
+
/**
|
|
115
|
+
* A sample, rounded, so a camera at rest compares equal to itself from one
|
|
116
|
+
* frame to the next and its run of samples collapses to two.
|
|
117
|
+
*
|
|
118
|
+
* Rounding may not carry an edge that was inside the page outside it: a
|
|
119
|
+
* frame at the page's right edge is `x + w = width` exactly, and rounding
|
|
120
|
+
* each half up would put a thousandth of a pixel of nothing in the picture.
|
|
121
|
+
*/
|
|
122
|
+
function roundRect(rect, viewport) {
|
|
123
|
+
const w = round(rect.w);
|
|
124
|
+
const h = round(rect.h);
|
|
125
|
+
return {
|
|
126
|
+
x: roundInside(rect.x, rect.w, w, viewport.width),
|
|
127
|
+
y: roundInside(rect.y, rect.h, h, viewport.height),
|
|
128
|
+
w,
|
|
129
|
+
h,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* One edge's position, rounded, and kept inside `[0, limit]` if it was
|
|
134
|
+
* inside to begin with, give or take the last bit of a double: a frame
|
|
135
|
+
* centred as far right as it goes is `x + w = width` only up to the float
|
|
136
|
+
* arithmetic that centred it. A rectangle a clip asked for outside the page
|
|
137
|
+
* (`camera.to` accepts one) is left where it was asked for.
|
|
138
|
+
*/
|
|
139
|
+
function roundInside(start, size, rounded, limit) {
|
|
140
|
+
const slack = 1e-6;
|
|
141
|
+
const at = round(start);
|
|
142
|
+
if (start < -slack || start + size > limit + slack)
|
|
143
|
+
return at;
|
|
144
|
+
if (at < 0)
|
|
145
|
+
return 0;
|
|
146
|
+
if (at + rounded > limit)
|
|
147
|
+
return Math.floor((limit - rounded) * 1000) / 1000;
|
|
148
|
+
return at;
|
|
149
|
+
}
|
|
150
|
+
function sameRect(a, b) {
|
|
151
|
+
return a.x === b.x && a.y === b.y && a.w === b.w && a.h === b.h;
|
|
152
|
+
}
|
|
153
|
+
function sampledKeys(effective, context, full) {
|
|
154
|
+
const { fps, viewport } = context;
|
|
155
|
+
const lastFrame = context.durationFrames - 1;
|
|
156
|
+
// Sampled through the last frame and then on, past it, until every write
|
|
157
|
+
// has arrived: a move still running when the clip ends leaves keys after
|
|
158
|
+
// the end, as a glide's key after the end always has, and that is what
|
|
159
|
+
// `review` warns about.
|
|
160
|
+
let endFrame = lastFrame;
|
|
161
|
+
for (const write of effective) {
|
|
162
|
+
if (write.kind === "release")
|
|
163
|
+
continue;
|
|
164
|
+
endFrame = Math.max(endFrame, frameAt(write.t + write.durationMs / 1000, fps));
|
|
165
|
+
}
|
|
166
|
+
const movers = [];
|
|
167
|
+
effective.forEach((write, index) => {
|
|
168
|
+
if (write.kind === "release")
|
|
169
|
+
return;
|
|
170
|
+
const duration = write.durationMs / 1000;
|
|
171
|
+
if (write.kind === "frame") {
|
|
172
|
+
const rect = write.rect;
|
|
173
|
+
movers.push({ t: write.t, duration, destination: () => rect });
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
// A follow reads the cursor until the next write of any kind, release
|
|
177
|
+
// included.
|
|
178
|
+
const next = effective[index + 1];
|
|
179
|
+
const firstFrame = firstFrameFrom(write.t, fps);
|
|
180
|
+
const centres = followCentres({
|
|
181
|
+
cursor: context.cursor,
|
|
182
|
+
cursorStart: context.cursorStart,
|
|
183
|
+
viewport,
|
|
184
|
+
fps,
|
|
185
|
+
spec: write.spec,
|
|
186
|
+
firstFrame,
|
|
187
|
+
lastFrame: endFrame,
|
|
188
|
+
until: next ? next.t : Number.POSITIVE_INFINITY,
|
|
189
|
+
});
|
|
190
|
+
const w = viewport.width / write.spec.scale;
|
|
191
|
+
const h = viewport.height / write.spec.scale;
|
|
192
|
+
movers.push({
|
|
193
|
+
t: write.t,
|
|
194
|
+
duration,
|
|
195
|
+
destination: (frame) => {
|
|
196
|
+
const [cx, cy] = centres[frame - firstFrame];
|
|
197
|
+
return { x: cx - w / 2, y: cy - h / 2, w, h };
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
// The camera at a frame: the latest write that has started, blended with
|
|
202
|
+
// the camera as it was going before it, for as long as the blend lasts.
|
|
203
|
+
const cameraAt = (index, frame, t) => {
|
|
204
|
+
if (index < 0)
|
|
205
|
+
return full;
|
|
206
|
+
const mover = movers[index];
|
|
207
|
+
const destination = mover.destination(frame);
|
|
208
|
+
const u = mover.duration > 0 ? (t - mover.t) / mover.duration : 1;
|
|
209
|
+
if (u >= 1)
|
|
210
|
+
return destination;
|
|
211
|
+
return interpolateRect(cameraAt(index - 1, frame, t), destination, minjerk(u));
|
|
212
|
+
};
|
|
213
|
+
const keys = [];
|
|
214
|
+
let latest = -1;
|
|
215
|
+
for (let frame = 0; frame <= endFrame; frame += 1) {
|
|
216
|
+
const t = timeAt(frame, fps);
|
|
217
|
+
while (latest + 1 < movers.length && movers[latest + 1].t <= t) {
|
|
218
|
+
latest += 1;
|
|
219
|
+
}
|
|
220
|
+
const rect = roundRect(cameraAt(latest, frame, t), viewport);
|
|
221
|
+
keys.push({ t, rect, ease: frame === 0 ? "hold" : "linear" });
|
|
222
|
+
}
|
|
223
|
+
// Where the camera is still, only the two ends of the stillness matter:
|
|
224
|
+
// between two equal samples, every sample is the same.
|
|
225
|
+
return keys.filter((key, i) => i === 0 ||
|
|
226
|
+
i === keys.length - 1 ||
|
|
227
|
+
!sameRect(keys[i - 1].rect, key.rect) ||
|
|
228
|
+
!sameRect(key.rect, keys[i + 1].rect));
|
|
229
|
+
}
|
|
230
|
+
//# sourceMappingURL=camera-track.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"camera-track.js","sourceRoot":"","sources":["../../src/core/camera-track.ts"],"names":[],"mappings":"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"}
|
package/dist/core/camera.d.ts
CHANGED
|
@@ -22,9 +22,22 @@ export declare function clampRect(rect: Rect, viewport: Viewport): Rect;
|
|
|
22
22
|
export declare function zoomRect(target: Rect, viewport: Viewport, options?: ZoomOptions): Rect;
|
|
23
23
|
/** The magnification a camera rectangle represents. */
|
|
24
24
|
export declare function scaleOf(rect: Rect, viewport: Viewport): number;
|
|
25
|
+
/**
|
|
26
|
+
* Interpolate one camera framing towards another.
|
|
27
|
+
*
|
|
28
|
+
* The size moves geometrically and the centre linearly. Interpolating width
|
|
29
|
+
* linearly is the usual reason a zoom feels wrong: going from 1440 wide to 780
|
|
30
|
+
* wide, the first half of the move changes the magnification by 1.22x and the
|
|
31
|
+
* second half by 1.51x, so a zoom that is mathematically even reads as rushing
|
|
32
|
+
* into the target and then crawling. In log space each equal step multiplies
|
|
33
|
+
* the scale by the same factor, which is what the eye actually reads as
|
|
34
|
+
* constant speed.
|
|
35
|
+
*/
|
|
36
|
+
export declare function interpolateRect(a: Rect, b: Rect, u: number): Rect;
|
|
25
37
|
/**
|
|
26
38
|
* Where the camera is at time `t`. A key's `ease` describes how the camera
|
|
27
39
|
* reaches it from the previous key: `minjerk` glides, `hold` stays put and
|
|
28
|
-
* then cuts
|
|
40
|
+
* then cuts, `linear` is the next sample of a path the recording computed
|
|
41
|
+
* frame by frame, a follow or a blend.
|
|
29
42
|
*/
|
|
30
43
|
export declare function evalCamera(keys: CameraKey[], t: number): Rect;
|
package/dist/core/camera.js
CHANGED
|
@@ -56,7 +56,7 @@ export function scaleOf(rect, viewport) {
|
|
|
56
56
|
* the scale by the same factor, which is what the eye actually reads as
|
|
57
57
|
* constant speed.
|
|
58
58
|
*/
|
|
59
|
-
function interpolateRect(a, b, u) {
|
|
59
|
+
export function interpolateRect(a, b, u) {
|
|
60
60
|
const cxA = a.x + a.w / 2;
|
|
61
61
|
const cyA = a.y + a.h / 2;
|
|
62
62
|
const cxB = b.x + b.w / 2;
|
|
@@ -67,10 +67,24 @@ function interpolateRect(a, b, u) {
|
|
|
67
67
|
const cy = lerp(cyA, cyB, u);
|
|
68
68
|
return { x: cx - w / 2, y: cy - h / 2, w, h };
|
|
69
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Straight between two samples of a path, for keys a frame apart. The
|
|
72
|
+
* renderer asks only for frame times, where this lands on a sample exactly;
|
|
73
|
+
* between them, what `review` asks for is off by less than a frame's motion.
|
|
74
|
+
*/
|
|
75
|
+
function lerpRect(a, b, u) {
|
|
76
|
+
return {
|
|
77
|
+
x: lerp(a.x, b.x, u),
|
|
78
|
+
y: lerp(a.y, b.y, u),
|
|
79
|
+
w: lerp(a.w, b.w, u),
|
|
80
|
+
h: lerp(a.h, b.h, u),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
70
83
|
/**
|
|
71
84
|
* Where the camera is at time `t`. A key's `ease` describes how the camera
|
|
72
85
|
* reaches it from the previous key: `minjerk` glides, `hold` stays put and
|
|
73
|
-
* then cuts
|
|
86
|
+
* then cuts, `linear` is the next sample of a path the recording computed
|
|
87
|
+
* frame by frame, a follow or a blend.
|
|
74
88
|
*/
|
|
75
89
|
export function evalCamera(keys, t) {
|
|
76
90
|
if (keys.length === 0) {
|
|
@@ -87,6 +101,8 @@ export function evalCamera(keys, t) {
|
|
|
87
101
|
return prev.rect;
|
|
88
102
|
const span = next.t - prev.t;
|
|
89
103
|
const u = span <= 0 ? 1 : (t - prev.t) / span;
|
|
104
|
+
if (next.ease === "linear")
|
|
105
|
+
return lerpRect(prev.rect, next.rect, u);
|
|
90
106
|
return interpolateRect(prev.rect, next.rect, minjerk(u));
|
|
91
107
|
}
|
|
92
108
|
return keys[keys.length - 1].rect;
|
package/dist/core/camera.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"camera.js","sourceRoot":"","sources":["../../src/core/camera.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAgBnD,MAAM,WAAW,GAAG,IAAI,CAAC;AACzB,MAAM,iBAAiB,GAAG,IAAI,CAAC;AAE/B,MAAM,UAAU,SAAS,CAAC,IAAU,EAAE,QAAkB;IACtD,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC3C,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC5C,OAAO;QACL,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC,KAAK,GAAG,CAAC,CAAC;QACvC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;QACxC,CAAC;QACD,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CACtB,MAAY,EACZ,QAAkB,EAClB,UAAuB,EAAE;IAEzB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,WAAW,CAAC;IACvC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,iBAAiB,CAAC;IACvD,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC;IAEhD,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC;IAExC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;IACjC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;IAEjC,yEAAyE;IACzE,wCAAwC;IACxC,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM;QAAE,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;;QAC9B,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;IAEpB,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,GAAG,QAAQ,CAAC;IAC3C,IAAI,CAAC,GAAG,QAAQ,EAAE,CAAC;QACjB,CAAC,GAAG,QAAQ,CAAC;QACb,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;IACjB,CAAC;IACD,IAAI,CAAC,GAAG,QAAQ,CAAC,KAAK,EAAE,CAAC;QACvB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC;QACnB,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC;IACtB,CAAC;IAED,OAAO,SAAS,CAAC,EAAE,CAAC,EAAE,OAAO,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,OAAO,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;AAC/E,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,OAAO,CAAC,IAAU,EAAE,QAAkB;IACpD,OAAO,QAAQ,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC;AACjC,CAAC;AAED;;;;;;;;;;GAUG;AACH,
|
|
1
|
+
{"version":3,"file":"camera.js","sourceRoot":"","sources":["../../src/core/camera.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import type { Viewport } from "./camera.js";
|
|
2
|
+
import type { CursorTrack, Vec2 } from "./timeline.js";
|
|
3
|
+
/**
|
|
4
|
+
* A camera that follows the cursor.
|
|
5
|
+
*
|
|
6
|
+
* The framing at each frame is a fixed size, `viewport / scale`, centred as
|
|
7
|
+
* near the cursor as three things allow:
|
|
8
|
+
*
|
|
9
|
+
* 1. **A dead zone.** The camera keeps a window centre that moves only when
|
|
10
|
+
* the cursor pushes past the window's edge, the way a platform game's
|
|
11
|
+
* camera follows its character. A cursor moving to the next field of a
|
|
12
|
+
* form stays inside the window, so the camera does not answer it: a
|
|
13
|
+
* camera that answered every movement would be the zooming in and out
|
|
14
|
+
* this mode exists to stop, only smaller.
|
|
15
|
+
* 2. **The page.** The window centre is clamped so the frame never leaves
|
|
16
|
+
* the captured viewport, which has nothing beyond its edge to show.
|
|
17
|
+
* 3. **A critically damped spring, run forward and then backward** over the
|
|
18
|
+
* window's path.
|
|
19
|
+
*
|
|
20
|
+
* Why a spring run both ways, rather than once: a spring that only runs
|
|
21
|
+
* forward trails its target by twice its time constant times the target's
|
|
22
|
+
* speed. At 1.35× on a 1440-wide page, a spring soft enough to look smooth
|
|
23
|
+
* trailed a cursor crossing the page by enough to put the cursor at the
|
|
24
|
+
* frame's edge, and past it. The recording knows the whole cursor path
|
|
25
|
+
* before anything is drawn, so the camera may use the path's future as well
|
|
26
|
+
* as its past, and run backward the spring's lag cancels its own lag. What it
|
|
27
|
+
* costs is a little anticipation: the camera sets off a moment before the
|
|
28
|
+
* cursor does, which is what a camera operator who knew the script would do.
|
|
29
|
+
*
|
|
30
|
+
* The spring's impulse response is never negative, and neither is the
|
|
31
|
+
* reversed one, so every camera position is a weighted average of window
|
|
32
|
+
* positions. Three properties follow from that alone, with no clamping
|
|
33
|
+
* afterwards to break them: the camera never overshoots, because it never
|
|
34
|
+
* goes beyond where the window has been; it stays inside the page, because
|
|
35
|
+
* an average of positions inside an interval is inside it; and it moves
|
|
36
|
+
* smoothly, because a critically damped spring's velocity and acceleration
|
|
37
|
+
* are continuous however sharply its target turns.
|
|
38
|
+
*/
|
|
39
|
+
export type FollowSpec = {
|
|
40
|
+
/** Magnification, exactly: the frame is `viewport / scale` the whole time. */
|
|
41
|
+
scale: number;
|
|
42
|
+
/**
|
|
43
|
+
* The share of the frame, around its centre, the cursor can move within
|
|
44
|
+
* without moving the camera: `0.3` is a box 30% of the frame's width by
|
|
45
|
+
* 30% of its height.
|
|
46
|
+
*/
|
|
47
|
+
deadZone: number;
|
|
48
|
+
/**
|
|
49
|
+
* How long the camera takes over a move the cursor made at once, from 3%
|
|
50
|
+
* to 97% of the way, in milliseconds. That span is eight of the spring's
|
|
51
|
+
* time constants, so this is eight times the time constant.
|
|
52
|
+
*/
|
|
53
|
+
smoothingMs: number;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* One step of a critically damped spring, `y'' + 2ωy' + ω²y = ω²u`, from
|
|
57
|
+
* position `y` and velocity `v`, with the target held at `u` for `dt`.
|
|
58
|
+
*
|
|
59
|
+
* Exact for a target held still over the step, rather than an Euler step's
|
|
60
|
+
* approximation, so the path depends on the frame rate only through where the
|
|
61
|
+
* cursor is sampled: a draft at 15 frames a second follows the same curve as
|
|
62
|
+
* a recording at 60.
|
|
63
|
+
*/
|
|
64
|
+
export declare function springStep(y: number, v: number, u: number, omega: number, dt: number): [number, number];
|
|
65
|
+
/**
|
|
66
|
+
* A spring run forward and then backward: the same curve with no lag.
|
|
67
|
+
*
|
|
68
|
+
* Holding each sample over the step that ends on it leads the input by half
|
|
69
|
+
* a step on the way forward, and the backward pass trails it by the same
|
|
70
|
+
* half step, so the two cancel as well.
|
|
71
|
+
*/
|
|
72
|
+
export declare function zeroPhaseSpring(input: readonly number[], omega: number, dt: number): number[];
|
|
73
|
+
export type FollowPathInput = {
|
|
74
|
+
cursor: CursorTrack;
|
|
75
|
+
/** Where the cursor is when the track is empty: its resting place in frame 0. */
|
|
76
|
+
cursorStart: Vec2;
|
|
77
|
+
viewport: Viewport;
|
|
78
|
+
fps: number;
|
|
79
|
+
spec: FollowSpec;
|
|
80
|
+
/** The first frame the follow is asked for: the first at or after its write. */
|
|
81
|
+
firstFrame: number;
|
|
82
|
+
/** The last frame anything will ask for. */
|
|
83
|
+
lastFrame: number;
|
|
84
|
+
/**
|
|
85
|
+
* When the follow stops reading the cursor, in seconds: the next camera
|
|
86
|
+
* write. From then on it treats the cursor as still where it was, so the
|
|
87
|
+
* camera comes to rest on the framing it was heading for instead of
|
|
88
|
+
* chasing a cursor the clip has stopped following.
|
|
89
|
+
*/
|
|
90
|
+
until: number;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* The centre of the follow's frame at every frame from `firstFrame` to
|
|
94
|
+
* `lastFrame`, in viewport pixels. The frame itself is `viewport / scale`,
|
|
95
|
+
* centred there.
|
|
96
|
+
*
|
|
97
|
+
* A pure function of its input: the same cursor track and the same numbers
|
|
98
|
+
* make the same path, to the bit, which is what lets a recording that follows
|
|
99
|
+
* the cursor be compared byte for byte with the next one.
|
|
100
|
+
*/
|
|
101
|
+
export declare function followCentres(input: FollowPathInput): Vec2[];
|
|
102
|
+
export type FollowOptions = {
|
|
103
|
+
/** Magnification, exactly. */
|
|
104
|
+
scale?: number;
|
|
105
|
+
/** The share of the frame the cursor can move within without moving the camera. */
|
|
106
|
+
deadZone?: number;
|
|
107
|
+
/** How softly the camera follows, in milliseconds. */
|
|
108
|
+
smoothing?: number;
|
|
109
|
+
/** How long the camera takes to get from where it was into the follow. */
|
|
110
|
+
duration?: number;
|
|
111
|
+
};
|
|
112
|
+
export type FollowDefaults = {
|
|
113
|
+
followZoom: number;
|
|
114
|
+
followDeadZone: number;
|
|
115
|
+
followSmoothingMs: number;
|
|
116
|
+
zoomDurationMs: number;
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* A follow's options, with the config's defaults filled in and each one
|
|
120
|
+
* checked where the clip wrote it.
|
|
121
|
+
*
|
|
122
|
+
* Checked here rather than when the path is computed, because by then the
|
|
123
|
+
* clip has run to the end: a scale of 0.8 found in `finish()` would fail the
|
|
124
|
+
* recording without naming the line that asked for it.
|
|
125
|
+
*/
|
|
126
|
+
export declare function resolveFollow(options: FollowOptions, defaults: FollowDefaults): {
|
|
127
|
+
spec: FollowSpec;
|
|
128
|
+
durationMs: number;
|
|
129
|
+
};
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { clamp } from "./easing.js";
|
|
2
|
+
import { evalCursor } from "./cursor.js";
|
|
3
|
+
/** Time constants in `smoothingMs`: the span of a jump's response, 3% to 97%. */
|
|
4
|
+
const TIME_CONSTANTS_PER_SMOOTHING = 8;
|
|
5
|
+
/**
|
|
6
|
+
* Time constants of input held still before the backward pass starts.
|
|
7
|
+
*
|
|
8
|
+
* The backward pass has to start from rest, and a spring coming to rest never
|
|
9
|
+
* quite arrives: after thirty time constants the remainder is under a
|
|
10
|
+
* billionth of the distance it had to go, which on any page is far below a
|
|
11
|
+
* thousandth of a pixel.
|
|
12
|
+
*/
|
|
13
|
+
const SETTLED_AFTER = 30;
|
|
14
|
+
/**
|
|
15
|
+
* One step of a critically damped spring, `y'' + 2ωy' + ω²y = ω²u`, from
|
|
16
|
+
* position `y` and velocity `v`, with the target held at `u` for `dt`.
|
|
17
|
+
*
|
|
18
|
+
* Exact for a target held still over the step, rather than an Euler step's
|
|
19
|
+
* approximation, so the path depends on the frame rate only through where the
|
|
20
|
+
* cursor is sampled: a draft at 15 frames a second follows the same curve as
|
|
21
|
+
* a recording at 60.
|
|
22
|
+
*/
|
|
23
|
+
export function springStep(y, v, u, omega, dt) {
|
|
24
|
+
const e = y - u;
|
|
25
|
+
const k = v + omega * e;
|
|
26
|
+
const decay = Math.exp(-omega * dt);
|
|
27
|
+
return [u + (e + k * dt) * decay, (v - omega * k * dt) * decay];
|
|
28
|
+
}
|
|
29
|
+
/** A spring over a whole sequence, starting at rest on its first value. */
|
|
30
|
+
function spring(input, omega, dt) {
|
|
31
|
+
const out = new Array(input.length);
|
|
32
|
+
let y = input[0];
|
|
33
|
+
let v = 0;
|
|
34
|
+
for (let i = 0; i < input.length; i += 1) {
|
|
35
|
+
if (i > 0)
|
|
36
|
+
[y, v] = springStep(y, v, input[i], omega, dt);
|
|
37
|
+
out[i] = y;
|
|
38
|
+
}
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* A spring run forward and then backward: the same curve with no lag.
|
|
43
|
+
*
|
|
44
|
+
* Holding each sample over the step that ends on it leads the input by half
|
|
45
|
+
* a step on the way forward, and the backward pass trails it by the same
|
|
46
|
+
* half step, so the two cancel as well.
|
|
47
|
+
*/
|
|
48
|
+
export function zeroPhaseSpring(input, omega, dt) {
|
|
49
|
+
if (input.length === 0)
|
|
50
|
+
return [];
|
|
51
|
+
const forward = spring(input, omega, dt);
|
|
52
|
+
return spring(forward.reverse(), omega, dt).reverse();
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The centre of the follow's frame at every frame from `firstFrame` to
|
|
56
|
+
* `lastFrame`, in viewport pixels. The frame itself is `viewport / scale`,
|
|
57
|
+
* centred there.
|
|
58
|
+
*
|
|
59
|
+
* A pure function of its input: the same cursor track and the same numbers
|
|
60
|
+
* make the same path, to the bit, which is what lets a recording that follows
|
|
61
|
+
* the cursor be compared byte for byte with the next one.
|
|
62
|
+
*/
|
|
63
|
+
export function followCentres(input) {
|
|
64
|
+
const { viewport, fps, spec, firstFrame, lastFrame } = input;
|
|
65
|
+
if (lastFrame < firstFrame)
|
|
66
|
+
return [];
|
|
67
|
+
if (!(spec.smoothingMs > 0)) {
|
|
68
|
+
throw new RangeError(`A follow's smoothing has to be more than 0ms, not ${spec.smoothingMs}.`);
|
|
69
|
+
}
|
|
70
|
+
const frameW = viewport.width / spec.scale;
|
|
71
|
+
const frameH = viewport.height / spec.scale;
|
|
72
|
+
// Half the dead zone each side of the window's centre.
|
|
73
|
+
const slackX = (spec.deadZone * frameW) / 2;
|
|
74
|
+
const slackY = (spec.deadZone * frameH) / 2;
|
|
75
|
+
const dt = 1 / fps;
|
|
76
|
+
const timeConstant = spec.smoothingMs / 1000 / TIME_CONSTANTS_PER_SMOOTHING;
|
|
77
|
+
const omega = 1 / timeConstant;
|
|
78
|
+
const cursorAt = (t) => input.cursor.length === 0
|
|
79
|
+
? input.cursorStart
|
|
80
|
+
: evalCursor(input.cursor, Math.min(t, input.until));
|
|
81
|
+
// Past the last frame anything asks for, the path goes on until the window
|
|
82
|
+
// has been still for long enough that the backward pass can start from
|
|
83
|
+
// rest. The window is still once the cursor is: after the follow stops
|
|
84
|
+
// reading it, or after the track's last step, whichever is first.
|
|
85
|
+
const trackEnd = input.cursor.length === 0 ? 0 : input.cursor[input.cursor.length - 1].t1;
|
|
86
|
+
const stillFrom = Math.ceil(Math.min(input.until, trackEnd) * fps);
|
|
87
|
+
const farFrame = Math.max(lastFrame, stillFrom) +
|
|
88
|
+
Math.ceil(SETTLED_AFTER * timeConstant * fps);
|
|
89
|
+
const windowX = [];
|
|
90
|
+
const windowY = [];
|
|
91
|
+
let x = 0;
|
|
92
|
+
let y = 0;
|
|
93
|
+
for (let frame = firstFrame; frame <= farFrame; frame += 1) {
|
|
94
|
+
const [px, py] = cursorAt(frame / fps);
|
|
95
|
+
if (frame === firstFrame) {
|
|
96
|
+
// A follow starts centred on the cursor, as near as the page allows.
|
|
97
|
+
// The dead zone is for what the cursor does next; starting at its edge
|
|
98
|
+
// would frame the cursor off-centre for no reason anyone could see.
|
|
99
|
+
x = px;
|
|
100
|
+
y = py;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
x = clamp(x, px - slackX, px + slackX);
|
|
104
|
+
y = clamp(y, py - slackY, py + slackY);
|
|
105
|
+
}
|
|
106
|
+
x = clamp(x, frameW / 2, viewport.width - frameW / 2);
|
|
107
|
+
y = clamp(y, frameH / 2, viewport.height - frameH / 2);
|
|
108
|
+
windowX.push(x);
|
|
109
|
+
windowY.push(y);
|
|
110
|
+
}
|
|
111
|
+
const smoothX = zeroPhaseSpring(windowX, omega, dt);
|
|
112
|
+
const smoothY = zeroPhaseSpring(windowY, omega, dt);
|
|
113
|
+
const centres = [];
|
|
114
|
+
for (let i = 0; i <= lastFrame - firstFrame; i += 1) {
|
|
115
|
+
centres.push([smoothX[i], smoothY[i]]);
|
|
116
|
+
}
|
|
117
|
+
return centres;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* A follow's options, with the config's defaults filled in and each one
|
|
121
|
+
* checked where the clip wrote it.
|
|
122
|
+
*
|
|
123
|
+
* Checked here rather than when the path is computed, because by then the
|
|
124
|
+
* clip has run to the end: a scale of 0.8 found in `finish()` would fail the
|
|
125
|
+
* recording without naming the line that asked for it.
|
|
126
|
+
*/
|
|
127
|
+
export function resolveFollow(options, defaults) {
|
|
128
|
+
const scale = options.scale ?? defaults.followZoom;
|
|
129
|
+
const deadZone = options.deadZone ?? defaults.followDeadZone;
|
|
130
|
+
const smoothingMs = options.smoothing ?? defaults.followSmoothingMs;
|
|
131
|
+
const durationMs = options.duration ?? defaults.zoomDurationMs;
|
|
132
|
+
if (!Number.isFinite(scale) || scale < 1) {
|
|
133
|
+
throw new RangeError(`camera.follow: scale is ${scale}. It is a magnification, at least 1: ` +
|
|
134
|
+
"1.35 shows the page at 1.35 times its size.");
|
|
135
|
+
}
|
|
136
|
+
if (!Number.isFinite(deadZone) || deadZone < 0 || deadZone >= 1) {
|
|
137
|
+
throw new RangeError(`camera.follow: deadZone is ${deadZone}. It is a share of the frame, ` +
|
|
138
|
+
"from 0 (answer every movement) to below 1: 0.3 lets the cursor " +
|
|
139
|
+
"move across the middle 30% without moving the camera.");
|
|
140
|
+
}
|
|
141
|
+
if (!Number.isFinite(smoothingMs) || smoothingMs <= 0) {
|
|
142
|
+
throw new RangeError(`camera.follow: smoothing is ${smoothingMs}. It is in milliseconds, ` +
|
|
143
|
+
"and more than 0: larger follows more softly.");
|
|
144
|
+
}
|
|
145
|
+
if (!Number.isFinite(durationMs) || durationMs < 0) {
|
|
146
|
+
throw new RangeError(`camera.follow: duration is ${durationMs}. It is in milliseconds, ` +
|
|
147
|
+
"0 or more: 0 cuts straight into the follow.");
|
|
148
|
+
}
|
|
149
|
+
return { spec: { scale, deadZone, smoothingMs }, durationMs };
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=follow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"follow.js","sourceRoot":"","sources":["../../src/core/follow.ts"],"names":[],"mappings":"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"}
|
|
@@ -45,6 +45,35 @@ export declare const MOTION: {
|
|
|
45
45
|
readonly maxZoom: 1.65;
|
|
46
46
|
/** Padding around a zoom target, as a fraction of its size. */
|
|
47
47
|
readonly zoomPad: 0.12;
|
|
48
|
+
/**
|
|
49
|
+
* How far in a camera that follows the cursor goes. Gentler than a zoom's
|
|
50
|
+
* 1.5, because a zoom frames one thing and stops, while a follow keeps the
|
|
51
|
+
* cursor in frame as it travels and needs more of the page around it to
|
|
52
|
+
* say where it is going. A website review that found clips zooming in and
|
|
53
|
+
* out too much settled on this: in a little, once, and pan.
|
|
54
|
+
*/
|
|
55
|
+
readonly followZoom: 1.35;
|
|
56
|
+
/**
|
|
57
|
+
* The share of a following camera's frame, around its centre, that the
|
|
58
|
+
* cursor can move within without the camera answering. At 1.35× on a page
|
|
59
|
+
* 1440 wide that is 160 pixels either side and 100 up or down: a move to
|
|
60
|
+
* the next field of a form stays inside it, a move across the page does
|
|
61
|
+
* not. Much smaller and the camera fidgets with every small movement.
|
|
62
|
+
*/
|
|
63
|
+
readonly followDeadZone: 0.3;
|
|
64
|
+
/**
|
|
65
|
+
* How softly a following camera moves: the time it would take over a move
|
|
66
|
+
* the cursor made at once, 3% to 97% of the way. The camera runs over the
|
|
67
|
+
* cursor's whole path, so it has no lag to trade against; larger is
|
|
68
|
+
* smoother, and sets off earlier before each move the cursor makes. At
|
|
69
|
+
* 600, with the dead zone above, it sets off within about a tenth of a
|
|
70
|
+
* second of the cursor and comes to rest about a fifth of a second after
|
|
71
|
+
* the cursor stops: close enough to read as following, soft enough that a
|
|
72
|
+
* pan across the page never lurches. With no dead zone it would lead the
|
|
73
|
+
* cursor by a quarter of a second, which starts to read as a camera that
|
|
74
|
+
* moved first.
|
|
75
|
+
*/
|
|
76
|
+
readonly followSmoothingMs: 600;
|
|
48
77
|
/** How long a highlight stays up if the script does not say. */
|
|
49
78
|
readonly highlightMs: 1800;
|
|
50
79
|
/** Click ripple, in the renderer. */
|