@3driseai/3drise-viewer 0.1.24 → 0.1.26

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.
Files changed (133) hide show
  1. package/dist/animationsV2/ClipPlayer.d.ts +13 -0
  2. package/dist/animationsV2/ClipPlayer.d.ts.map +1 -0
  3. package/dist/animationsV2/ClipPlayer.js +140 -0
  4. package/dist/animationsV2/ClipsRuntime.d.ts +111 -0
  5. package/dist/animationsV2/ClipsRuntime.d.ts.map +1 -0
  6. package/dist/animationsV2/ClipsRuntime.js +476 -0
  7. package/dist/animationsV2/appliers.d.ts +16 -0
  8. package/dist/animationsV2/appliers.d.ts.map +1 -0
  9. package/dist/animationsV2/appliers.js +204 -0
  10. package/dist/animationsV2/clipUtils.d.ts +33 -0
  11. package/dist/animationsV2/clipUtils.d.ts.map +1 -0
  12. package/dist/animationsV2/clipUtils.js +164 -0
  13. package/dist/animationsV2/elementSettings.d.ts +15 -0
  14. package/dist/animationsV2/elementSettings.d.ts.map +1 -0
  15. package/dist/animationsV2/elementSettings.js +16 -0
  16. package/dist/animationsV2/index.d.ts +16 -0
  17. package/dist/animationsV2/index.d.ts.map +1 -0
  18. package/dist/animationsV2/index.js +12 -0
  19. package/dist/animationsV2/nodeTracks.d.ts +13 -0
  20. package/dist/animationsV2/nodeTracks.d.ts.map +1 -0
  21. package/dist/animationsV2/nodeTracks.js +165 -0
  22. package/dist/animationsV2/normalize.d.ts +4 -0
  23. package/dist/animationsV2/normalize.d.ts.map +1 -0
  24. package/dist/animationsV2/normalize.js +49 -0
  25. package/dist/animationsV2/sequenceUtils.d.ts +57 -0
  26. package/dist/animationsV2/sequenceUtils.d.ts.map +1 -0
  27. package/dist/animationsV2/sequenceUtils.js +120 -0
  28. package/dist/animationsV2/types.d.ts +95 -0
  29. package/dist/animationsV2/types.d.ts.map +1 -0
  30. package/dist/animationsV2/types.js +8 -0
  31. package/dist/api/sceneHandle.d.ts +102 -0
  32. package/dist/api/sceneHandle.d.ts.map +1 -0
  33. package/dist/api/sceneHandle.js +237 -0
  34. package/dist/components/CameraController.d.ts.map +1 -1
  35. package/dist/components/CameraController.js +3 -44
  36. package/dist/components/Clouds.d.ts.map +1 -1
  37. package/dist/components/Clouds.js +0 -34
  38. package/dist/components/CreatedObject.d.ts.map +1 -1
  39. package/dist/components/CreatedObject.js +19 -1
  40. package/dist/components/EnvironmentManager.d.ts.map +1 -1
  41. package/dist/components/EnvironmentManager.js +6 -4
  42. package/dist/components/ObjectNode.d.ts.map +1 -1
  43. package/dist/components/ObjectNode.js +5 -1
  44. package/dist/components/ObjectsRenderer.d.ts +5 -1
  45. package/dist/components/ObjectsRenderer.d.ts.map +1 -1
  46. package/dist/components/ObjectsRenderer.js +24 -3
  47. package/dist/components/Ocean.d.ts.map +1 -1
  48. package/dist/components/Ocean.js +0 -63
  49. package/dist/components/ProjectLoader.d.ts +9 -3
  50. package/dist/components/ProjectLoader.d.ts.map +1 -1
  51. package/dist/components/ProjectLoader.js +5 -4
  52. package/dist/components/SceneBuilder.d.ts +9 -1
  53. package/dist/components/SceneBuilder.d.ts.map +1 -1
  54. package/dist/components/SceneBuilder.js +30 -5
  55. package/dist/components/SkyController.d.ts.map +1 -1
  56. package/dist/components/SkyController.js +0 -39
  57. package/dist/components/Stars.d.ts.map +1 -1
  58. package/dist/components/Stars.js +3 -46
  59. package/dist/components/terrain/TerrainSurface.d.ts.map +1 -1
  60. package/dist/components/terrain/TerrainSurface.js +0 -5
  61. package/dist/components/volumetricClouds/VolumetricCloudsPass.d.ts.map +1 -1
  62. package/dist/components/volumetricClouds/VolumetricCloudsPass.js +0 -6
  63. package/dist/hooks/index.d.ts +2 -2
  64. package/dist/hooks/index.d.ts.map +1 -1
  65. package/dist/hooks/index.js +2 -2
  66. package/dist/hooks/useInteraction.d.ts +7 -7
  67. package/dist/hooks/useInteraction.d.ts.map +1 -1
  68. package/dist/hooks/useInteraction.js +87 -233
  69. package/dist/hooks/useLightConfig.d.ts.map +1 -1
  70. package/dist/hooks/useLightConfig.js +7 -0
  71. package/dist/hooks/useLightLive.d.ts.map +1 -1
  72. package/dist/hooks/useLightLive.js +2 -0
  73. package/dist/hooks/useLightTargets.d.ts +7 -0
  74. package/dist/hooks/useLightTargets.d.ts.map +1 -0
  75. package/dist/hooks/useLightTargets.js +132 -0
  76. package/dist/hooks/useObjectAnimation.d.ts.map +1 -1
  77. package/dist/hooks/useObjectAnimation.js +0 -2
  78. package/dist/hooks/useOceanMaterial.d.ts.map +1 -1
  79. package/dist/hooks/useOceanMaterial.js +0 -5
  80. package/dist/hooks/useRuntimeSettings.d.ts +15 -0
  81. package/dist/hooks/useRuntimeSettings.d.ts.map +1 -0
  82. package/dist/hooks/useRuntimeSettings.js +22 -0
  83. package/dist/hooks/useSequenceAnimation.d.ts.map +1 -1
  84. package/dist/hooks/useSequenceAnimation.js +27 -9
  85. package/dist/index.d.ts +6 -1
  86. package/dist/index.d.ts.map +1 -1
  87. package/dist/index.js +5 -0
  88. package/dist/interactions/index.d.ts +3 -0
  89. package/dist/interactions/index.d.ts.map +1 -0
  90. package/dist/interactions/index.js +1 -0
  91. package/dist/interactions/runner.d.ts +9 -0
  92. package/dist/interactions/runner.d.ts.map +1 -0
  93. package/dist/interactions/runner.js +23 -0
  94. package/dist/operations/catalogue/displayImage.d.ts +3 -0
  95. package/dist/operations/catalogue/displayImage.d.ts.map +1 -0
  96. package/dist/operations/catalogue/displayImage.js +39 -0
  97. package/dist/operations/catalogue/fade.d.ts +3 -0
  98. package/dist/operations/catalogue/fade.d.ts.map +1 -0
  99. package/dist/operations/catalogue/fade.js +44 -0
  100. package/dist/operations/catalogue/focus.d.ts +3 -0
  101. package/dist/operations/catalogue/focus.d.ts.map +1 -0
  102. package/dist/operations/catalogue/focus.js +22 -0
  103. package/dist/operations/catalogue/glow.d.ts +3 -0
  104. package/dist/operations/catalogue/glow.d.ts.map +1 -0
  105. package/dist/operations/catalogue/glow.js +49 -0
  106. package/dist/operations/catalogue/materialFx.d.ts +6 -0
  107. package/dist/operations/catalogue/materialFx.d.ts.map +1 -0
  108. package/dist/operations/catalogue/materialFx.js +13 -0
  109. package/dist/operations/catalogue/moveToFront.d.ts +3 -0
  110. package/dist/operations/catalogue/moveToFront.d.ts.map +1 -0
  111. package/dist/operations/catalogue/moveToFront.js +25 -0
  112. package/dist/operations/catalogue/visibility.d.ts +3 -0
  113. package/dist/operations/catalogue/visibility.d.ts.map +1 -0
  114. package/dist/operations/catalogue/visibility.js +24 -0
  115. package/dist/operations/catalogue/zoom.d.ts +3 -0
  116. package/dist/operations/catalogue/zoom.d.ts.map +1 -0
  117. package/dist/operations/catalogue/zoom.js +32 -0
  118. package/dist/operations/index.d.ts +5 -0
  119. package/dist/operations/index.d.ts.map +1 -0
  120. package/dist/operations/index.js +4 -0
  121. package/dist/operations/registry.d.ts +11 -0
  122. package/dist/operations/registry.d.ts.map +1 -0
  123. package/dist/operations/registry.js +21 -0
  124. package/dist/operations/runtime.d.ts +22 -0
  125. package/dist/operations/runtime.d.ts.map +1 -0
  126. package/dist/operations/runtime.js +161 -0
  127. package/dist/operations/types.d.ts +96 -0
  128. package/dist/operations/types.d.ts.map +1 -0
  129. package/dist/operations/types.js +3 -0
  130. package/dist/operations/useOperationsRuntime.d.ts +3 -0
  131. package/dist/operations/useOperationsRuntime.d.ts.map +1 -0
  132. package/dist/operations/useOperationsRuntime.js +31 -0
  133. package/package.json +2 -2
@@ -0,0 +1,476 @@
1
+ import { getAtPath, sampleTrack } from './clipUtils.js';
2
+ import { applyValue, clearApplierCaches } from './appliers.js';
3
+ import { restoreNodeTrack } from './nodeTracks.js';
4
+ import { entryLocalTime, isEntryActive, resolveSequence, } from './sequenceUtils.js';
5
+ const targetKey = (state, node, path) => `${state}|${node ?? ''}|${path}`;
6
+ class ClipsRuntime {
7
+ clips = new Map();
8
+ sequences = new Map();
9
+ ctx = null;
10
+ order = 0;
11
+ version = 0;
12
+ listeners = new Set();
13
+ eventListeners = new Set();
14
+ /** tracks whose authored value must be written back on the next tick */
15
+ pendingRestore = [];
16
+ /** last written value per property (targetKey -> JSON) */
17
+ written = new Map();
18
+ // --- wiring -------------------------------------------------------------------
19
+ setContext(ctx) {
20
+ this.ctx = ctx;
21
+ this.written.clear();
22
+ clearApplierCaches();
23
+ }
24
+ subscribe(listener) {
25
+ this.listeners.add(listener);
26
+ return () => this.listeners.delete(listener);
27
+ }
28
+ getVersion() { return this.version; }
29
+ on(listener) {
30
+ this.eventListeners.add(listener);
31
+ return () => this.eventListeners.delete(listener);
32
+ }
33
+ // --- clips ----------------------------------------------------------------------
34
+ play(clip, opts = {}) {
35
+ const existing = this.clips.get(clip.id);
36
+ const direction = opts.reverse ? -1 : 1;
37
+ let time = opts.from ?? existing?.time ?? 0;
38
+ // Restart from the edge when playing past the end.
39
+ if (direction === 1 && time >= clip.duration)
40
+ time = 0;
41
+ if (direction === -1 && time <= 0)
42
+ time = clip.duration;
43
+ this.clips.set(clip.id, { clip, time, speed: opts.speed ?? 1, direction, playing: true, order: ++this.order });
44
+ this.emit({ type: 'start', kind: 'clip', id: clip.id });
45
+ this.bump();
46
+ }
47
+ pause(clipId) {
48
+ const inst = this.clips.get(clipId);
49
+ if (!inst)
50
+ return;
51
+ inst.playing = false;
52
+ this.bump();
53
+ }
54
+ resume(clipId) {
55
+ const inst = this.clips.get(clipId);
56
+ if (!inst)
57
+ return;
58
+ inst.playing = true;
59
+ this.bump();
60
+ }
61
+ /** Stop and put the clip's properties back to their authored values. */
62
+ stop(clipId) {
63
+ const inst = this.clips.get(clipId);
64
+ if (!inst)
65
+ return;
66
+ this.clips.delete(clipId);
67
+ this.pendingRestore.push(...inst.clip.tracks);
68
+ this.bump();
69
+ }
70
+ /** Hold the clip at t (seconds) — the editor playhead, or seek() from outside. */
71
+ pose(clip, t) {
72
+ const inst = this.clips.get(clip.id);
73
+ const time = Math.min(clip.duration, Math.max(0, t));
74
+ if (inst) {
75
+ inst.clip = clip;
76
+ inst.time = time;
77
+ }
78
+ else {
79
+ this.clips.set(clip.id, { clip, time, speed: 1, direction: 1, playing: false, order: ++this.order });
80
+ }
81
+ this.bump();
82
+ }
83
+ /** The clip data changed (editor edit): keep time and play state, use the new tracks. */
84
+ updateClip(clip) {
85
+ const inst = this.clips.get(clip.id);
86
+ if (!inst)
87
+ return;
88
+ const removed = inst.clip.tracks.filter(t => !clip.tracks.some(n => n.id === t.id));
89
+ if (removed.length)
90
+ this.pendingRestore.push(...removed);
91
+ inst.clip = clip;
92
+ inst.time = Math.min(inst.time, clip.duration);
93
+ this.written.clear(); // values may have changed under the same time
94
+ this.bump();
95
+ }
96
+ getTime(clipId) { return this.clips.get(clipId)?.time ?? 0; }
97
+ isPlaying(clipId) { return !!this.clips.get(clipId)?.playing; }
98
+ isActive(clipId) { return this.clips.has(clipId); }
99
+ /** Value of a track at the clip's current time (the panel's value column). */
100
+ sampleAt(clip, track) {
101
+ return sampleTrack(track, this.getTime(clip.id));
102
+ }
103
+ // --- sequences ------------------------------------------------------------------
104
+ /**
105
+ * Play a sequence. `reverse` defaults to the sequence's own; `from` is sequence
106
+ * seconds (defaults to the current time, or the start edge of the run).
107
+ */
108
+ playSequence(seq, clips, opts = {}) {
109
+ const existing = this.sequences.get(seq.id);
110
+ const schedule = resolveSequence(seq, clips);
111
+ const direction = (opts.reverse ?? seq.reverse) ? -1 : 1;
112
+ const driven = isDriven(seq);
113
+ let time = opts.from ?? existing?.time ?? (direction === 1 ? 0 : schedule.duration);
114
+ // A played sequence restarts from its start edge; a driven one stays where it is.
115
+ if (!driven && direction === 1 && time >= schedule.duration)
116
+ time = 0;
117
+ if (!driven && direction === -1 && time <= 0)
118
+ time = schedule.duration;
119
+ this.sequences.set(seq.id, {
120
+ driven,
121
+ target: time,
122
+ seq,
123
+ schedule,
124
+ time,
125
+ speed: opts.speed ?? 1,
126
+ direction,
127
+ baseDirection: direction,
128
+ playing: true,
129
+ order: ++this.order,
130
+ active: existing?.active ?? new Set(),
131
+ });
132
+ this.emit({ type: 'start', kind: 'sequence', id: seq.id });
133
+ this.bump();
134
+ }
135
+ // --- input drivers --------------------------------------------------------------
136
+ /** Driven sequences that are running — what the input layer (ClipPlayer) feeds. */
137
+ getDrivenSequences() {
138
+ const out = [];
139
+ for (const inst of this.sequences.values()) {
140
+ if (!inst.driven || !inst.playing)
141
+ continue;
142
+ out.push({ id: inst.seq.id, driver: inst.seq.driver, settings: inst.seq.driverSettings, progress: this.progressOf(inst, inst.target) });
143
+ }
144
+ return out;
145
+ }
146
+ /**
147
+ * Set a sequence's position, 0–1 along its own direction (reverse flips it). A
148
+ * driven sequence eases there (its smoothing); any other is posed there at once.
149
+ */
150
+ setSequenceProgress(seqId, progress) {
151
+ const inst = this.sequences.get(seqId);
152
+ if (!inst)
153
+ return false;
154
+ const T = this.timeOf(inst, progress);
155
+ inst.target = T;
156
+ if (!inst.driven)
157
+ inst.time = T;
158
+ this.bump();
159
+ return true;
160
+ }
161
+ /**
162
+ * Move a driven sequence's target by `delta` of its length (the wheel). Returns
163
+ * false when it is already at that end — the caller then lets the event through
164
+ * (page scroll), so a visitor is never trapped inside the canvas.
165
+ */
166
+ nudgeSequenceProgress(seqId, delta) {
167
+ const inst = this.sequences.get(seqId);
168
+ if (!inst)
169
+ return false;
170
+ const p = this.progressOf(inst, inst.target);
171
+ const next = Math.min(1, Math.max(0, p + delta));
172
+ if (Math.abs(next - p) < 1e-6)
173
+ return false;
174
+ inst.target = this.timeOf(inst, next);
175
+ this.bump();
176
+ return true;
177
+ }
178
+ getSequenceProgress(seqId) {
179
+ const inst = this.sequences.get(seqId);
180
+ return inst ? this.progressOf(inst, inst.time) : 0;
181
+ }
182
+ progressOf(inst, T) {
183
+ const total = inst.schedule.duration;
184
+ if (total <= 0)
185
+ return 0;
186
+ const p = Math.min(1, Math.max(0, T / total));
187
+ return inst.seq.reverse ? 1 - p : p;
188
+ }
189
+ timeOf(inst, progress) {
190
+ const p = Math.min(1, Math.max(0, Number.isFinite(progress) ? progress : 0));
191
+ return (inst.seq.reverse ? 1 - p : p) * inst.schedule.duration;
192
+ }
193
+ pauseSequence(seqId) {
194
+ const inst = this.sequences.get(seqId);
195
+ if (!inst)
196
+ return;
197
+ inst.playing = false;
198
+ this.bump();
199
+ }
200
+ resumeSequence(seqId) {
201
+ const inst = this.sequences.get(seqId);
202
+ if (!inst)
203
+ return;
204
+ inst.playing = true;
205
+ this.bump();
206
+ }
207
+ /** Stop and put every property the sequence drove back to its authored value. */
208
+ stopSequence(seqId) {
209
+ const inst = this.sequences.get(seqId);
210
+ if (!inst)
211
+ return;
212
+ this.sequences.delete(seqId);
213
+ inst.schedule.entries.forEach(e => this.pendingRestore.push(...e.clip.tracks));
214
+ this.bump();
215
+ }
216
+ /** Hold the sequence at T (sequence seconds) — the panel's scrub, or seek(). */
217
+ poseSequence(seq, clips, T) {
218
+ const inst = this.sequences.get(seq.id);
219
+ if (inst) {
220
+ // Edited data goes through the re-resolve path (restores what the old schedule drove).
221
+ if (inst.seq !== seq)
222
+ this.updateSequence(seq, clips);
223
+ inst.time = Math.min(inst.schedule.duration, Math.max(0, T));
224
+ inst.target = inst.time;
225
+ }
226
+ else {
227
+ const schedule = resolveSequence(seq, clips);
228
+ const time = Math.min(schedule.duration, Math.max(0, T));
229
+ this.sequences.set(seq.id, {
230
+ seq, schedule, time, target: time, driven: false,
231
+ speed: 1, direction: 1, baseDirection: 1, playing: false, order: ++this.order, active: new Set(),
232
+ });
233
+ }
234
+ this.bump();
235
+ }
236
+ /** The sequence or one of its clips changed: re-resolve, keep time and play state. */
237
+ updateSequence(seq, clips) {
238
+ const inst = this.sequences.get(seq.id);
239
+ if (!inst)
240
+ return;
241
+ // Restore everything the old schedule drove; whatever is still driven re-writes this frame.
242
+ inst.schedule.entries.forEach(e => this.pendingRestore.push(...e.clip.tracks));
243
+ inst.seq = seq;
244
+ inst.schedule = resolveSequence(seq, clips);
245
+ inst.time = Math.min(inst.time, inst.schedule.duration);
246
+ inst.target = Math.min(inst.target, inst.schedule.duration);
247
+ // The driver can change while it runs (editing): a running sequence switches mode.
248
+ if (inst.playing)
249
+ inst.driven = isDriven(seq);
250
+ inst.active = new Set();
251
+ this.bump();
252
+ }
253
+ getSequenceTime(seqId) { return this.sequences.get(seqId)?.time ?? 0; }
254
+ getSequenceDuration(seqId) { return this.sequences.get(seqId)?.schedule.duration ?? 0; }
255
+ isSequencePlaying(seqId) { return !!this.sequences.get(seqId)?.playing; }
256
+ isSequenceActive(seqId) { return this.sequences.has(seqId); }
257
+ // --- frame ------------------------------------------------------------------
258
+ tick(delta) {
259
+ const ctx = this.ctx;
260
+ if (!ctx)
261
+ return;
262
+ const dt = Math.min(delta, 0.1);
263
+ let anyPlaying = false;
264
+ for (const inst of this.clips.values()) {
265
+ if (!inst.playing)
266
+ continue;
267
+ anyPlaying = true;
268
+ this.advanceClip(inst, dt);
269
+ }
270
+ for (const inst of [...this.sequences.values()]) {
271
+ if (!inst.playing)
272
+ continue;
273
+ anyPlaying = true;
274
+ this.advanceSequence(inst, dt);
275
+ }
276
+ // Sequence entries that stopped writing (scrubbed back before their start, or
277
+ // a 'restore' clip past its end) give their properties back.
278
+ for (const inst of this.sequences.values()) {
279
+ const now = new Set();
280
+ for (const e of inst.schedule.entries)
281
+ if (isEntryActive(e, inst.time))
282
+ now.add(e.id);
283
+ for (const id of inst.active) {
284
+ if (now.has(id))
285
+ continue;
286
+ const e = inst.schedule.entries.find(x => x.id === id);
287
+ if (e)
288
+ this.pendingRestore.push(...e.clip.tracks);
289
+ }
290
+ inst.active = now;
291
+ }
292
+ if (this.pendingRestore.length)
293
+ this.runRestores(ctx);
294
+ // Proposals in priority order; the last one per property wins.
295
+ const winners = new Map();
296
+ for (const p of this.proposals())
297
+ winners.set(targetKey(p.state, p.node, p.path), p);
298
+ for (const [key, p] of winners) {
299
+ const json = JSON.stringify(p.value);
300
+ if (this.written.get(key) === json)
301
+ continue;
302
+ // Recorded only when it landed: a target that is not mounted yet (a model still
303
+ // loading) is retried next frame instead of being skipped for good.
304
+ if (applyValue(ctx, p.state, p.path, p.value, p.node))
305
+ this.written.set(key, json);
306
+ }
307
+ if (anyPlaying)
308
+ this.bump();
309
+ }
310
+ /** Every driver's values for this frame, lowest priority first. */
311
+ *proposals() {
312
+ const drivers = [];
313
+ for (const inst of this.clips.values()) {
314
+ drivers.push({ order: inst.order, run: () => sampleTracks(inst.clip.tracks, inst.time) });
315
+ }
316
+ for (const inst of this.sequences.values()) {
317
+ drivers.push({
318
+ order: inst.order,
319
+ run: function* () {
320
+ for (const e of inst.schedule.entries) {
321
+ if (!inst.active.has(e.id))
322
+ continue;
323
+ yield* sampleTracks(e.clip.tracks, entryLocalTime(e, inst.time));
324
+ }
325
+ },
326
+ });
327
+ }
328
+ drivers.sort((a, b) => a.order - b.order);
329
+ for (const d of drivers)
330
+ yield* d.run();
331
+ }
332
+ runRestores(ctx) {
333
+ const tracks = this.pendingRestore;
334
+ this.pendingRestore = [];
335
+ const done = new Set();
336
+ for (const track of tracks) {
337
+ const key = targetKey(track.state, track.node, track.path);
338
+ if (done.has(key))
339
+ continue;
340
+ done.add(key);
341
+ this.written.delete(key);
342
+ if (track.node) {
343
+ // Node tracks restore their exact pre-animation snapshot (see nodeTracks).
344
+ const node = track.node;
345
+ restoreNodeTrack(track.state, node, track.path, (path) => this.isDriven(track.state, node, path));
346
+ continue;
347
+ }
348
+ // Another driver may still own this property; it re-writes this frame.
349
+ const authored = getAtPath(ctx.getSettings(track.state), track.path);
350
+ if (authored !== undefined)
351
+ applyValue(ctx, track.state, track.path, authored);
352
+ }
353
+ }
354
+ /** Whether any active driver still has a track on (state, node, path). */
355
+ isDriven(state, node, path) {
356
+ const hit = (t) => t.state === state && t.node === node && t.path === path && !t.muted;
357
+ for (const inst of this.clips.values())
358
+ if (inst.clip.tracks.some(hit))
359
+ return true;
360
+ for (const inst of this.sequences.values()) {
361
+ for (const e of inst.schedule.entries)
362
+ if (inst.active.has(e.id) && e.clip.tracks.some(hit))
363
+ return true;
364
+ }
365
+ return false;
366
+ }
367
+ advanceClip(inst, delta) {
368
+ const { clip } = inst;
369
+ inst.time += delta * inst.speed * inst.direction;
370
+ const atEnd = inst.direction === 1 ? inst.time >= clip.duration : inst.time <= 0;
371
+ if (!atEnd)
372
+ return;
373
+ if (clip.yoyo) {
374
+ // Bounce at either edge. Without loop, stop after the return leg.
375
+ const returning = inst.direction === -1;
376
+ inst.time = inst.direction === 1 ? clip.duration : 0;
377
+ if (returning && !clip.loop)
378
+ return this.finishClip(inst);
379
+ inst.direction = inst.direction === 1 ? -1 : 1;
380
+ if (returning)
381
+ this.emit({ type: 'loop', kind: 'clip', id: clip.id });
382
+ return;
383
+ }
384
+ if (clip.loop) {
385
+ inst.time = inst.direction === 1 ? inst.time - clip.duration : inst.time + clip.duration;
386
+ this.emit({ type: 'loop', kind: 'clip', id: clip.id });
387
+ return;
388
+ }
389
+ inst.time = inst.direction === 1 ? clip.duration : 0;
390
+ this.finishClip(inst);
391
+ }
392
+ finishClip(inst) {
393
+ inst.playing = false;
394
+ this.emit({ type: 'end', kind: 'clip', id: inst.clip.id });
395
+ if (inst.clip.endMode === 'restore')
396
+ this.stop(inst.clip.id);
397
+ }
398
+ /**
399
+ * Same edge rules as a clip, on the sequence clock. "Forward" is the run's base
400
+ * direction: a reversed run starts at the end and finishes at 0.
401
+ */
402
+ advanceSequence(inst, delta) {
403
+ const { seq } = inst;
404
+ const total = inst.schedule.duration;
405
+ if (inst.driven) {
406
+ // Ease toward the input's target. `smoothing` ≈ seconds to settle; 0 = exact.
407
+ const diff = inst.target - inst.time;
408
+ if (Math.abs(diff) < 1e-4) {
409
+ inst.time = inst.target;
410
+ return;
411
+ }
412
+ const smoothing = Math.max(0, seq.driverSettings?.smoothing ?? 0);
413
+ const k = smoothing <= 0 ? 1 : 1 - Math.exp(-delta * 4 / smoothing);
414
+ inst.time += diff * k;
415
+ return;
416
+ }
417
+ if (total <= 0) {
418
+ inst.time = 0;
419
+ return this.finishSequence(inst);
420
+ }
421
+ inst.time += delta * inst.speed * inst.direction;
422
+ const atEnd = inst.direction === 1 ? inst.time >= total : inst.time <= 0;
423
+ if (!atEnd)
424
+ return;
425
+ const edge = inst.direction === 1 ? total : 0;
426
+ const returning = inst.direction !== inst.baseDirection;
427
+ if (seq.yoyo) {
428
+ inst.time = edge;
429
+ if (returning && !seq.loop)
430
+ return this.finishSequence(inst);
431
+ inst.direction = inst.direction === 1 ? -1 : 1;
432
+ if (returning)
433
+ this.emit({ type: 'loop', kind: 'sequence', id: seq.id });
434
+ return;
435
+ }
436
+ if (seq.loop) {
437
+ // Jump back to the run's start edge. Entries past their window restore next tick.
438
+ inst.time = inst.direction === 1 ? inst.time - total : inst.time + total;
439
+ this.emit({ type: 'loop', kind: 'sequence', id: seq.id });
440
+ return;
441
+ }
442
+ inst.time = edge;
443
+ this.finishSequence(inst);
444
+ }
445
+ finishSequence(inst) {
446
+ inst.playing = false;
447
+ this.emit({ type: 'end', kind: 'sequence', id: inst.seq.id });
448
+ if (inst.seq.endMode === 'restore')
449
+ this.stopSequence(inst.seq.id);
450
+ }
451
+ emit(e) {
452
+ this.eventListeners.forEach(l => { try {
453
+ l(e);
454
+ }
455
+ catch (err) {
456
+ console.error('[ClipsRuntime] listener failed', err);
457
+ } });
458
+ }
459
+ bump() {
460
+ this.version += 1;
461
+ this.listeners.forEach(l => l());
462
+ }
463
+ }
464
+ const isDriven = (seq) => !!seq.driver && seq.driver !== 'time';
465
+ function* sampleTracks(tracks, t) {
466
+ for (const track of tracks) {
467
+ if (track.muted || track.keys.length === 0)
468
+ continue;
469
+ const value = sampleTrack(track, t);
470
+ if (value === undefined)
471
+ continue;
472
+ yield { state: track.state, node: track.node, path: track.path, value };
473
+ }
474
+ }
475
+ const clipsRuntime = new ClipsRuntime();
476
+ export default clipsRuntime;
@@ -0,0 +1,16 @@
1
+ import type { PerspectiveCamera } from 'three';
2
+ import type { KeyValue } from './types';
3
+ export interface ApplyContext {
4
+ camera: PerspectiveCamera | null;
5
+ /** drei OrbitControls (makeDefault), when present */
6
+ controls: any;
7
+ /** Authored settings for an element (object id or 'camera'), action patches included. */
8
+ getSettings: (state: string) => any;
9
+ }
10
+ export declare function clearApplierCaches(): void;
11
+ /** Write one value. Unknown paths are ignored (the track picker never offers them). */
12
+ /** Write one value to the live scene. False when its target is not mounted yet. */
13
+ export declare function applyValue(ctx: ApplyContext, state: string, path: string, value: KeyValue, node?: string): boolean;
14
+ /** Whether a (state, path) has an applier — used by the track picker. */
15
+ export declare function isApplicable(state: string, path: string): boolean;
16
+ //# sourceMappingURL=appliers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"appliers.d.ts","sourceRoot":"","sources":["../../src/animationsV2/appliers.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAc/C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGxC,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACjC,qDAAqD;IACrD,QAAQ,EAAE,GAAG,CAAC;IACd,yFAAyF;IACzF,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,GAAG,CAAC;CACrC;AAWD,wBAAgB,kBAAkB,IAAI,IAAI,CAIzC;AAqKD,uFAAuF;AACvF,mFAAmF;AACnF,wBAAgB,UAAU,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAIlH;AAED,yEAAyE;AACzE,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAEjE"}