@aelionsdk/render-ir 0.1.0-beta.1

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.
@@ -0,0 +1,562 @@
1
+ import { canonicalStringify } from '@aelionsdk/project-schema';
2
+ function deepFreezePlain(value, seen = new WeakSet()) {
3
+ if (value === null || typeof value !== 'object')
4
+ return value;
5
+ if (seen.has(value))
6
+ return value;
7
+ const prototype = Object.getPrototypeOf(value);
8
+ if (!Array.isArray(value) && prototype !== Object.prototype && prototype !== null)
9
+ return value;
10
+ seen.add(value);
11
+ for (const entry of Object.values(value))
12
+ deepFreezePlain(entry, seen);
13
+ return Object.freeze(value);
14
+ }
15
+ function object(value, context) {
16
+ if (value === null || Array.isArray(value) || typeof value !== 'object') {
17
+ throw new TypeError(`${context} must be an object`);
18
+ }
19
+ return value;
20
+ }
21
+ function jsonObject(value, context) {
22
+ return object(value, context);
23
+ }
24
+ function string(value, context) {
25
+ if (typeof value !== 'string')
26
+ throw new TypeError(`${context} must be a string`);
27
+ return value;
28
+ }
29
+ function number(value, context) {
30
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
31
+ throw new TypeError(`${context} must be a finite number`);
32
+ }
33
+ return value;
34
+ }
35
+ function boolean(value, context) {
36
+ if (typeof value !== 'boolean')
37
+ throw new TypeError(`${context} must be boolean`);
38
+ return value;
39
+ }
40
+ function mediaSource(item) {
41
+ const source = object(item.source, `item ${item.id}.source`);
42
+ const stream = object(source.stream, `item ${item.id}.source.stream`);
43
+ const sourceRange = object(source.sourceRange, `item ${item.id}.source.sourceRange`);
44
+ const timeMapping = object(source.timeMapping, `item ${item.id}.source.timeMapping`);
45
+ const streamType = string(stream.type, 'stream.type');
46
+ if (streamType !== 'video' && streamType !== 'audio') {
47
+ throw new TypeError(`Unsupported stream type ${streamType}`);
48
+ }
49
+ const boundary = string(timeMapping.boundary, 'timeMapping.boundary');
50
+ if (!['error', 'hold', 'loop', 'transparent'].includes(boundary)) {
51
+ throw new TypeError(`Unsupported boundary ${boundary}`);
52
+ }
53
+ let compiledTimeMapping;
54
+ if (timeMapping.type === 'linear') {
55
+ const rate = object(timeMapping.rate, `item ${item.id}.source.timeMapping.rate`);
56
+ compiledTimeMapping = {
57
+ type: 'linear',
58
+ rate: {
59
+ numerator: number(rate.numerator, 'rate.numerator'),
60
+ denominator: number(rate.denominator, 'rate.denominator'),
61
+ },
62
+ reverse: boolean(timeMapping.reverse, 'timeMapping.reverse'),
63
+ };
64
+ }
65
+ else if (timeMapping.type === 'curve') {
66
+ if (!Array.isArray(timeMapping.points))
67
+ throw new TypeError('timeMapping.points must be an array');
68
+ compiledTimeMapping = {
69
+ type: 'curve',
70
+ points: timeMapping.points.map((value, index) => {
71
+ const point = object(value, `timeMapping.points[${index.toString()}]`);
72
+ const interpolation = string(point.interpolation, 'timeMapping point interpolation');
73
+ if (interpolation !== 'linear' && interpolation !== 'hold' && interpolation !== 'cubic') {
74
+ throw new TypeError(`Unsupported TimeMap interpolation ${interpolation}`);
75
+ }
76
+ return {
77
+ itemTimeUs: number(point.itemTimeUs, 'timeMapping point itemTimeUs'),
78
+ sourceTimeUs: number(point.sourceTimeUs, 'timeMapping point sourceTimeUs'),
79
+ interpolation,
80
+ };
81
+ }),
82
+ };
83
+ }
84
+ else {
85
+ throw new TypeError(`Unsupported time mapping for ${item.id}`);
86
+ }
87
+ return {
88
+ assetId: string(source.assetId, 'source.assetId'),
89
+ streamType,
90
+ streamIndex: number(stream.index, 'stream.index'),
91
+ sourceRange: {
92
+ startUs: number(sourceRange.startUs, 'sourceRange.startUs'),
93
+ durationUs: number(sourceRange.durationUs, 'sourceRange.durationUs'),
94
+ },
95
+ timeMapping: compiledTimeMapping,
96
+ ...(compiledTimeMapping.type === 'linear'
97
+ ? { rate: compiledTimeMapping.rate, reverse: compiledTimeMapping.reverse }
98
+ : {}),
99
+ boundary: boundary,
100
+ };
101
+ }
102
+ function nestedSequenceSource(item) {
103
+ const source = object(item.source, `item ${item.id}.source`);
104
+ const sourceRange = object(source.sourceRange, `item ${item.id}.source.sourceRange`);
105
+ const timeMapping = object(source.timeMapping, `item ${item.id}.source.timeMapping`);
106
+ const boundary = string(timeMapping.boundary, 'timeMapping.boundary');
107
+ if (!['error', 'hold', 'loop', 'transparent'].includes(boundary)) {
108
+ throw new TypeError(`Unsupported boundary ${boundary}`);
109
+ }
110
+ let compiledTimeMapping;
111
+ if (timeMapping.type === 'linear') {
112
+ const rate = object(timeMapping.rate, `item ${item.id}.source.timeMapping.rate`);
113
+ compiledTimeMapping = {
114
+ type: 'linear',
115
+ rate: {
116
+ numerator: number(rate.numerator, 'rate.numerator'),
117
+ denominator: number(rate.denominator, 'rate.denominator'),
118
+ },
119
+ reverse: boolean(timeMapping.reverse, 'timeMapping.reverse'),
120
+ };
121
+ }
122
+ else if (timeMapping.type === 'curve') {
123
+ if (!Array.isArray(timeMapping.points))
124
+ throw new TypeError('timeMapping.points must be an array');
125
+ compiledTimeMapping = {
126
+ type: 'curve',
127
+ points: timeMapping.points.map((value, index) => {
128
+ const point = object(value, `timeMapping.points[${index.toString()}]`);
129
+ const interpolation = string(point.interpolation, 'timeMapping point interpolation');
130
+ if (interpolation !== 'linear' && interpolation !== 'hold' && interpolation !== 'cubic') {
131
+ throw new TypeError(`Unsupported TimeMap interpolation ${interpolation}`);
132
+ }
133
+ return {
134
+ itemTimeUs: number(point.itemTimeUs, 'timeMapping point itemTimeUs'),
135
+ sourceTimeUs: number(point.sourceTimeUs, 'timeMapping point sourceTimeUs'),
136
+ interpolation,
137
+ };
138
+ }),
139
+ };
140
+ }
141
+ else {
142
+ throw new TypeError(`Unsupported nested Sequence time mapping for ${item.id}`);
143
+ }
144
+ return {
145
+ sequenceId: string(source.sequenceId, `item ${item.id}.source.sequenceId`),
146
+ sourceRange: {
147
+ startUs: number(sourceRange.startUs, 'sourceRange.startUs'),
148
+ durationUs: number(sourceRange.durationUs, 'sourceRange.durationUs'),
149
+ },
150
+ timeMapping: compiledTimeMapping,
151
+ boundary: boundary,
152
+ };
153
+ }
154
+ function clipFingerprint(item, materials) {
155
+ return [
156
+ canonicalStringify(item),
157
+ ...item.materialInstanceIds.map(id => materialFingerprint(materials[id])),
158
+ ].join('|');
159
+ }
160
+ function materialFingerprint(instance) {
161
+ if (instance === undefined)
162
+ return canonicalStringify(null);
163
+ return canonicalStringify({
164
+ id: instance.id,
165
+ definition: {
166
+ packageId: instance.definition.packageId,
167
+ packageVersion: instance.definition.packageVersion,
168
+ packageIntegrity: instance.definition.packageIntegrity,
169
+ materialId: instance.definition.materialId,
170
+ },
171
+ enabled: instance.enabled,
172
+ previewPolicy: instance.previewPolicy,
173
+ parameters: instance.parameters,
174
+ resourceBindings: instance.resourceBindings,
175
+ inputBindings: instance.inputBindings,
176
+ program: instance.program === undefined
177
+ ? null
178
+ : {
179
+ backend: instance.program.backend,
180
+ nodeSet: instance.program.nodeSet,
181
+ graphHash: instance.program.graphHash,
182
+ },
183
+ });
184
+ }
185
+ function compileClip(item, materials) {
186
+ const base = {
187
+ id: item.id,
188
+ trackId: item.trackId,
189
+ range: { ...item.range },
190
+ enabled: item.enabled,
191
+ materialInstanceIds: [...item.materialInstanceIds],
192
+ dependencyEntityIds: [item.id, ...item.materialInstanceIds],
193
+ fingerprint: clipFingerprint(item, materials),
194
+ };
195
+ if (item.type === 'video' || item.type === 'image') {
196
+ const source = mediaSource(item);
197
+ const visual = object(item.visual, `item ${item.id}.visual`);
198
+ const mask = object(visual.mask ?? {}, `item ${item.id}.visual.mask`);
199
+ const maskSourceId = typeof mask.sourceItemId === 'string' && mask.sourceItemId.length > 0
200
+ ? mask.sourceItemId
201
+ : undefined;
202
+ return {
203
+ ...base,
204
+ dependencyEntityIds: [
205
+ ...base.dependencyEntityIds,
206
+ source.assetId,
207
+ ...(maskSourceId === undefined ? [] : [maskSourceId]),
208
+ ],
209
+ kind: 'visual-clip',
210
+ source,
211
+ visual: visual,
212
+ };
213
+ }
214
+ if (item.type === 'audio') {
215
+ const source = mediaSource(item);
216
+ return {
217
+ ...base,
218
+ dependencyEntityIds: [...base.dependencyEntityIds, source.assetId],
219
+ kind: 'audio-clip',
220
+ source,
221
+ audio: object(item.audio, `item ${item.id}.audio`),
222
+ };
223
+ }
224
+ if (item.type === 'text') {
225
+ const box = object(item.box, `item ${item.id}.box`);
226
+ const paragraphs = item.paragraphs;
227
+ if (!Array.isArray(paragraphs))
228
+ throw new TypeError(`item ${item.id}.paragraphs must be an array`);
229
+ return {
230
+ ...base,
231
+ kind: 'text-clip',
232
+ role: 'text',
233
+ box: {
234
+ x: number(box.x, 'text box.x'),
235
+ y: number(box.y, 'text box.y'),
236
+ width: number(box.width, 'text box.width'),
237
+ height: number(box.height, 'text box.height'),
238
+ },
239
+ overflow: string(item.overflow, 'text overflow'),
240
+ writingMode: string(item.writingMode, 'text writingMode'),
241
+ paragraphs: paragraphs.map((paragraphValue, paragraphIndex) => {
242
+ const paragraph = object(paragraphValue, `paragraphs[${paragraphIndex.toString()}]`);
243
+ if (!Array.isArray(paragraph.runs))
244
+ throw new TypeError('text paragraph.runs must be an array');
245
+ return {
246
+ style: jsonObject(paragraph.style, 'text paragraph.style'),
247
+ runs: paragraph.runs.map((runValue, runIndex) => {
248
+ const run = object(runValue, `text run[${runIndex.toString()}]`);
249
+ return {
250
+ text: string(run.text, 'text run.text'),
251
+ style: jsonObject(run.style, 'text run.style'),
252
+ };
253
+ }),
254
+ };
255
+ }),
256
+ visual: object(item.visual, `item ${item.id}.visual`),
257
+ };
258
+ }
259
+ if (item.type === 'caption') {
260
+ const box = object(item.box, `item ${item.id}.box`);
261
+ return {
262
+ ...base,
263
+ kind: 'text-clip',
264
+ role: 'caption',
265
+ box: {
266
+ x: number(box.x, 'caption box.x'),
267
+ y: number(box.y, 'caption box.y'),
268
+ width: number(box.width, 'caption box.width'),
269
+ height: number(box.height, 'caption box.height'),
270
+ },
271
+ overflow: item.overflow === 'clip' ? 'clip' : 'auto-fit',
272
+ writingMode: 'horizontal-tb',
273
+ paragraphs: [
274
+ {
275
+ style: jsonObject(item.style, `item ${item.id}.style`),
276
+ runs: [
277
+ {
278
+ text: string(item.text, `item ${item.id}.text`),
279
+ style: jsonObject(item.style, `item ${item.id}.style`),
280
+ },
281
+ ],
282
+ },
283
+ ],
284
+ visual: object(item.visual, `item ${item.id}.visual`),
285
+ };
286
+ }
287
+ if (item.type === 'nested-sequence') {
288
+ const source = nestedSequenceSource(item);
289
+ return {
290
+ ...base,
291
+ kind: 'nested-sequence-clip',
292
+ source,
293
+ dependencyEntityIds: [...base.dependencyEntityIds, source.sequenceId],
294
+ visual: object(item.visual, `item ${item.id}.visual`),
295
+ };
296
+ }
297
+ if (item.type === 'generator') {
298
+ return {
299
+ ...base,
300
+ kind: 'generator-clip',
301
+ generator: jsonObject(item.generator, `item ${item.id}.generator`),
302
+ visual: object(item.visual, `item ${item.id}.visual`),
303
+ };
304
+ }
305
+ if (item.type === 'shape') {
306
+ return {
307
+ ...base,
308
+ kind: 'shape-clip',
309
+ shape: jsonObject(item.shape, `item ${item.id}.shape`),
310
+ visual: object(item.visual, `item ${item.id}.visual`),
311
+ };
312
+ }
313
+ if (item.type === 'material-content') {
314
+ const materialInstanceId = string(item.materialInstanceId, `item ${item.id}.materialInstanceId`);
315
+ const contentMaterial = materials[materialInstanceId];
316
+ if (contentMaterial === undefined) {
317
+ throw new ReferenceError(`item ${item.id} references missing MaterialInstance ${materialInstanceId}`);
318
+ }
319
+ const materialInstanceIds = [...new Set([...base.materialInstanceIds, materialInstanceId])];
320
+ return {
321
+ ...base,
322
+ materialInstanceIds,
323
+ dependencyEntityIds: [...new Set([...base.dependencyEntityIds, materialInstanceId])],
324
+ fingerprint: `${base.fingerprint}|${materialFingerprint(contentMaterial)}`,
325
+ kind: 'material-content-clip',
326
+ materialInstanceId,
327
+ visual: object(item.visual, `item ${item.id}.visual`),
328
+ };
329
+ }
330
+ if (item.type === 'adjustment') {
331
+ return {
332
+ ...base,
333
+ kind: 'adjustment-clip',
334
+ visual: object(item.visual, `item ${item.id}.visual`),
335
+ };
336
+ }
337
+ throw new TypeError(`Render IR cannot compile item type ${item.type}`);
338
+ }
339
+ function material(instance, resolveMaterialProgram) {
340
+ const definition = object(instance.definition, 'material definition');
341
+ const compiledDefinition = {
342
+ packageId: string(definition.packageId, 'definition.packageId'),
343
+ packageVersion: string(definition.packageVersion, 'definition.packageVersion'),
344
+ packageIntegrity: string(definition.packageIntegrity, 'definition.packageIntegrity'),
345
+ materialId: string(definition.materialId, 'definition.materialId'),
346
+ };
347
+ const parameters = object(instance.parameters, 'material.parameters');
348
+ const resourceBindings = object(instance.resourceBindings ?? {}, 'material.resourceBindings');
349
+ const inputBindings = object(instance.inputBindings ?? {}, 'material.inputBindings');
350
+ const program = resolveMaterialProgram?.(compiledDefinition, parameters);
351
+ return {
352
+ id: string(instance.id, 'material.id'),
353
+ definition: compiledDefinition,
354
+ enabled: boolean(instance.enabled, 'material.enabled'),
355
+ previewPolicy: instance.previewPolicy === 'skippable-when-degraded' ? 'skippable-when-degraded' : 'required',
356
+ parameters,
357
+ resourceBindings,
358
+ inputBindings,
359
+ ...(program === undefined ? {} : { program }),
360
+ };
361
+ }
362
+ function contentDuration(project, trackIds) {
363
+ return trackIds.reduce((sequenceEnd, trackId) => {
364
+ const track = project.tracks[trackId];
365
+ if (track === undefined)
366
+ return sequenceEnd;
367
+ return track.itemIds.reduce((trackEnd, itemId) => {
368
+ const item = project.items[itemId];
369
+ return item === undefined
370
+ ? trackEnd
371
+ : Math.max(trackEnd, item.range.startUs + item.range.durationUs);
372
+ }, sequenceEnd);
373
+ }, 0);
374
+ }
375
+ export class IncrementalRenderCompiler {
376
+ #previous;
377
+ #compiling = false;
378
+ /**
379
+ * Creates an isolated compiler that reuses this compiler's immutable baseline.
380
+ * Compiling on the fork cannot advance or corrupt the parent baseline; a host
381
+ * can promote the fork only after its surrounding transaction commits.
382
+ */
383
+ fork() {
384
+ const fork = new IncrementalRenderCompiler();
385
+ fork.#previous = this.#previous;
386
+ return fork;
387
+ }
388
+ /** Releases the incremental baseline retained for clip/transition reuse. */
389
+ clear() {
390
+ if (this.#compiling) {
391
+ throw new Error('IncrementalRenderCompiler does not support clearing during compilation');
392
+ }
393
+ this.#previous = undefined;
394
+ }
395
+ compile(project, sequenceId, revision, optionsOrAffectedRanges = {}) {
396
+ if (this.#compiling) {
397
+ throw new Error('IncrementalRenderCompiler does not support reentrant compilation');
398
+ }
399
+ this.#compiling = true;
400
+ try {
401
+ const options = Array.isArray(optionsOrAffectedRanges)
402
+ ? { affectedRanges: optionsOrAffectedRanges }
403
+ : optionsOrAffectedRanges;
404
+ const nestedSequenceStack = options.nestedSequenceStack ?? [];
405
+ if (nestedSequenceStack.includes(sequenceId)) {
406
+ throw new TypeError(`NESTED_SEQUENCE_CYCLE: ${[...nestedSequenceStack, sequenceId].join(' -> ')}`);
407
+ }
408
+ const sequence = project.sequences[sequenceId];
409
+ if (sequence === undefined)
410
+ throw new RangeError(`Sequence ${sequenceId} does not exist`);
411
+ const materials = Object.fromEntries(Object.entries(project.materialInstances).map(([id, value]) => [
412
+ id,
413
+ material(value, options.resolveMaterialProgram),
414
+ ]));
415
+ let compiledClips = 0;
416
+ let reusedClips = 0;
417
+ let compiledTransitions = 0;
418
+ let reusedTransitions = 0;
419
+ const previousClips = new Map((this.#previous?.tracks ?? []).flatMap(track => track.clips.map(clip => [clip.id, clip])));
420
+ const previousTransitions = new Map((this.#previous?.transitions ?? []).map(value => [value.id, value]));
421
+ const affectedEntityIds = new Set(options.affectedEntityIds ?? []);
422
+ const canReuseByEntity = this.#previous !== undefined && options.affectedEntityIds !== undefined;
423
+ const tracks = sequence.trackIds.map(trackId => {
424
+ const track = project.tracks[trackId];
425
+ if (track === undefined)
426
+ throw new RangeError(`Track ${trackId} does not exist`);
427
+ const clips = track.itemIds.flatMap(itemId => {
428
+ const item = project.items[itemId];
429
+ if (item === undefined)
430
+ throw new RangeError(`Item ${itemId} does not exist`);
431
+ if (item.type !== 'video' &&
432
+ item.type !== 'image' &&
433
+ item.type !== 'audio' &&
434
+ item.type !== 'text' &&
435
+ item.type !== 'caption' &&
436
+ item.type !== 'nested-sequence' &&
437
+ item.type !== 'generator' &&
438
+ item.type !== 'shape' &&
439
+ item.type !== 'material-content' &&
440
+ item.type !== 'adjustment')
441
+ throw new TypeError(`Render IR cannot compile item type ${item.type}`);
442
+ const previous = previousClips.get(itemId);
443
+ if (canReuseByEntity &&
444
+ previous !== undefined &&
445
+ !previous.dependencyEntityIds.some(id => affectedEntityIds.has(id))) {
446
+ reusedClips += 1;
447
+ return [previous];
448
+ }
449
+ const candidate = compileClip(item, materials);
450
+ if (previous?.fingerprint === candidate.fingerprint) {
451
+ reusedClips += 1;
452
+ return [previous];
453
+ }
454
+ compiledClips += 1;
455
+ return [candidate];
456
+ });
457
+ return {
458
+ id: track.id,
459
+ kind: track.kind,
460
+ enabled: Boolean(track.enabled),
461
+ ...(track.kind === 'audio'
462
+ ? {
463
+ audio: object(track.audio, `track ${track.id}.audio`),
464
+ }
465
+ : {}),
466
+ clips,
467
+ materialInstanceIds: [...track.materialInstanceIds],
468
+ fingerprint: canonicalStringify(track),
469
+ };
470
+ });
471
+ const transitions = sequence.transitionIds.map(id => {
472
+ const value = project.transitions[id];
473
+ if (value === undefined)
474
+ throw new RangeError(`Transition ${id} does not exist`);
475
+ const previous = previousTransitions.get(id);
476
+ if (canReuseByEntity &&
477
+ previous !== undefined &&
478
+ !previous.dependencyEntityIds.some(entityId => affectedEntityIds.has(entityId))) {
479
+ reusedTransitions += 1;
480
+ return previous;
481
+ }
482
+ const candidate = {
483
+ id,
484
+ trackId: value.trackId,
485
+ fromItemId: value.fromItemId,
486
+ toItemId: value.toItemId,
487
+ range: { ...value.range },
488
+ materialInstanceId: value.materialInstanceId,
489
+ dependencyEntityIds: [id, value.fromItemId, value.toItemId, value.materialInstanceId],
490
+ fingerprint: [
491
+ canonicalStringify(value),
492
+ materialFingerprint(materials[value.materialInstanceId]),
493
+ ].join('|'),
494
+ };
495
+ if (previous?.fingerprint === candidate.fingerprint) {
496
+ reusedTransitions += 1;
497
+ return previous;
498
+ }
499
+ compiledTransitions += 1;
500
+ return candidate;
501
+ });
502
+ const format = object(sequence.format, 'sequence.format');
503
+ const frameRate = object(format.frameRate, 'sequence.format.frameRate');
504
+ const duration = object(sequence.duration, 'sequence.duration');
505
+ const nestedSequenceIds = new Set(tracks.flatMap(track => track.clips.flatMap(clip => clip.kind === 'nested-sequence-clip' ? [clip.source.sequenceId] : [])));
506
+ const subgraphs = Object.fromEntries([...nestedSequenceIds].map(nestedSequenceId => [
507
+ nestedSequenceId,
508
+ new IncrementalRenderCompiler().compile(project, nestedSequenceId, revision, {
509
+ ...(options.resolveMaterialProgram === undefined
510
+ ? {}
511
+ : { resolveMaterialProgram: options.resolveMaterialProgram }),
512
+ nestedSequenceStack: [...nestedSequenceStack, sequenceId],
513
+ }).ir,
514
+ ]));
515
+ const ir = {
516
+ irVersion: '1.0.0',
517
+ projectId: project.projectId,
518
+ sequenceId,
519
+ revision,
520
+ width: number(format.width, 'format.width'),
521
+ height: number(format.height, 'format.height'),
522
+ frameRate: {
523
+ numerator: number(frameRate.numerator, 'frameRate.numerator'),
524
+ denominator: number(frameRate.denominator, 'frameRate.denominator'),
525
+ },
526
+ sampleRate: number(format.sampleRate, 'format.sampleRate'),
527
+ channelLayout: string(format.channelLayout, 'format.channelLayout'),
528
+ workingColorSpace: string(format.workingColorSpace, 'format.workingColorSpace'),
529
+ transferFunction: format.transferFunction === 'gamma22' ||
530
+ format.transferFunction === 'pq' ||
531
+ format.transferFunction === 'hlg'
532
+ ? format.transferFunction
533
+ : 'srgb',
534
+ bitDepth: format.bitDepth === 10 ? 10 : 8,
535
+ backgroundColor: jsonObject(format.backgroundColor, 'format.backgroundColor'),
536
+ durationUs: duration.mode === 'fixed'
537
+ ? number(duration.durationUs, 'duration.durationUs')
538
+ : Math.max(contentDuration(project, sequence.trackIds), ...transitions.map(value => value.range.startUs + value.range.durationUs)),
539
+ tracks,
540
+ transitions,
541
+ materials,
542
+ subgraphs,
543
+ };
544
+ const frozenIr = deepFreezePlain(ir);
545
+ const stats = deepFreezePlain({
546
+ compiledClips,
547
+ reusedClips,
548
+ compiledTransitions,
549
+ reusedTransitions,
550
+ affectedRanges: (options.affectedRanges ?? []).map(range => ({ ...range })),
551
+ });
552
+ this.#previous = frozenIr;
553
+ return {
554
+ ir: frozenIr,
555
+ stats,
556
+ };
557
+ }
558
+ finally {
559
+ this.#compiling = false;
560
+ }
561
+ }
562
+ }
@@ -0,0 +1,8 @@
1
+ import type { ActiveAudioState, ActiveVisualState, IrVisualClip, IrMaterialInstance, EvaluatedMaterialInstance, RenderIr } from './types.js';
2
+ export declare function evaluateAnimatedValue(value: import('@aelionsdk/core').JsonValue, sequenceTimeUs: number, ownerStartUs?: number): import("@aelionsdk/core").JsonValue;
3
+ export declare function evaluateAnimatableNumber(value: import('@aelionsdk/core').JsonValue | undefined, sequenceTimeUs: number, ownerStartUs: number, fallback: number): number;
4
+ export declare function evaluateMaterialInstance(material: IrMaterialInstance, sequenceTimeUs: number, ownerStartUs?: number): EvaluatedMaterialInstance;
5
+ export declare function mapClipSourceTime(clip: IrVisualClip, sequenceTimeUs: number): number | null;
6
+ export declare function evaluateAudioState(ir: RenderIr, startUs: number, durationUs: number): ActiveAudioState;
7
+ export declare function evaluateVisualState(ir: RenderIr, timeUs: number): ActiveVisualState;
8
+ //# sourceMappingURL=evaluate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluate.d.ts","sourceRoot":"","sources":["../src/evaluate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,iBAAiB,EAGjB,YAAY,EAOZ,kBAAkB,EAClB,yBAAyB,EACzB,QAAQ,EACT,MAAM,YAAY,CAAC;AAqGpB,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,OAAO,iBAAiB,EAAE,SAAS,EAC1C,cAAc,EAAE,MAAM,EACtB,YAAY,SAAI,uCA6CjB;AAED,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,OAAO,iBAAiB,EAAE,SAAS,GAAG,SAAS,EACtD,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,GACf,MAAM,CAIR;AAED,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,kBAAkB,EAC5B,cAAc,EAAE,MAAM,EACtB,YAAY,SAAI,GACf,yBAAyB,CAY3B;AAsBD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAE3F;AAqDD,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,QAAQ,EACZ,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,GACjB,gBAAgB,CA8DlB;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,iBAAiB,CA0DnF"}