@flighthq/scene2d-formats 0.2.1-next.840.857425c

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,894 @@
1
+ import { createAnimationChannel, createAnimationClip, createAnimationClipEvent, createAnimationTrack, sampleAnimationTrack, } from '@flighthq/animation';
2
+ import { createClipRegionFromPath } from '@flighthq/clip';
3
+ import { packColor } from '@flighthq/color';
4
+ import { easeCubicBezier } from '@flighthq/easing';
5
+ import { createGradientTransformMatrix } from '@flighthq/geometry';
6
+ import { reportImportDiagnostic } from '@flighthq/importdiagnostics';
7
+ import { addNodeChild } from '@flighthq/node';
8
+ import { appendPathCubicCurveTo, appendPathEllipse, appendPathLineTo, appendPathMoveTo, appendPathPolygon, appendPathRectangle, appendPathRoundRectangle, createPath, dashPath, getPathLength, } from '@flighthq/path';
9
+ import { applyAnimationClipToNode2D, createBitmap, createDisplayObject } from '@flighthq/scene2d';
10
+ import { appendShapeBeginFill, appendShapeBeginGradientFill, appendShapeEndFill, appendShapeLineGradientStyle, appendShapeLineStyle, appendShapePath, clearShapeCommands, createShape, } from '@flighthq/shape';
11
+ import { createTextLabel } from '@flighthq/text';
12
+ import { BlendMode, ImportDiagnosticSeverity } from '@flighthq/types';
13
+ // Applies both the shared Node2DAnimationTarget channels and the format-owned mutable-content
14
+ // targets used by animated shape/paint/mask records.
15
+ export function applyAnimationClipToLottieDocument(clip, time) {
16
+ applyAnimationClipToNode2D(clip, time);
17
+ for (const channel of clip.channels) {
18
+ const target = channel.targetRef;
19
+ if (target === null || typeof target !== 'object' || target.lottieApply === undefined)
20
+ continue;
21
+ sampleAnimationTrack(_sampleScratch, channel.track, time);
22
+ target.lottieApply(_sampleScratch);
23
+ }
24
+ }
25
+ /**
26
+ * Imports a Bodymovin/Lottie document into a display subtree and target-bound AnimationClip.
27
+ * Playback remains explicit: call applyAnimationClipToLottieDocument with the returned clip.
28
+ */
29
+ export function createScene2DFromLottieDocument(source, diagnostics, options) {
30
+ const document = parseLottieDocument(source);
31
+ const root = createDisplayObject();
32
+ if (document === null || !isValidLottieDocument(document)) {
33
+ reportImportDiagnostic(diagnostics, ImportDiagnosticSeverity.Reject, 'lottie.invalid-document', 'createScene2DFromLottieDocument');
34
+ return { clip: createAnimationClip([]), duration: 0, frameRate: 0, root };
35
+ }
36
+ const context = {
37
+ assets: new Map((document.assets ?? []).map((asset) => [asset.id, asset])),
38
+ channels: [],
39
+ diagnostics,
40
+ document,
41
+ frameOffset: 0,
42
+ frameScale: 1,
43
+ options,
44
+ resolvingPrecompositions: new Set(),
45
+ };
46
+ appendLottieLayers(root, document.layers, context);
47
+ const duration = Math.max(0, (document.op - document.ip) / document.fr);
48
+ const events = (document.markers ?? []).map((marker) => createAnimationClipEvent(clamp((marker.tm - document.ip) / document.fr, 0, duration), marker.cm, {
49
+ duration: marker.dr / document.fr,
50
+ }));
51
+ return {
52
+ clip: createAnimationClip(context.channels, duration, events),
53
+ duration,
54
+ frameRate: document.fr,
55
+ root,
56
+ };
57
+ }
58
+ function appendLottieLayers(root, layers, context) {
59
+ const nodes = new Map();
60
+ const ordered = [];
61
+ for (const layer of layers) {
62
+ const node = createLottieLayerNode(layer, context);
63
+ ordered.push({ layer, node });
64
+ if (layer.ind !== undefined)
65
+ nodes.set(layer.ind, node);
66
+ }
67
+ // Bodymovin stores the topmost layer first. Reverse insertion preserves that visual stacking in
68
+ // Flight's back-to-front child order.
69
+ for (let index = ordered.length - 1; index >= 0; index--) {
70
+ const { layer, node } = ordered[index];
71
+ const parent = layer.parent === undefined ? undefined : nodes.get(layer.parent);
72
+ addNodeChild(parent ?? root, node);
73
+ }
74
+ }
75
+ function createLottieLayerNode(layer, context) {
76
+ const container = createDisplayObject({ name: layer.nm ?? null });
77
+ applyLottieTransform(container, layer.ks, context);
78
+ applyLottieLayerVisibility(container, layer, context);
79
+ applyLottieBlendMode(container, layer, context);
80
+ reportLottieLayerExclusions(layer, context);
81
+ if (layer.ty === 0)
82
+ appendLottiePrecomposition(container, layer, context);
83
+ else if (layer.ty === 1)
84
+ appendLottieSolid(container, layer);
85
+ else if (layer.ty === 2)
86
+ appendLottieImage(container, layer, context);
87
+ else if (layer.ty === 3) {
88
+ // Null layers intentionally contain only their transform and children.
89
+ }
90
+ else if (layer.ty === 4)
91
+ appendLottieShapeItems(container, layer.shapes ?? [], context);
92
+ else if (layer.ty === 5)
93
+ appendLottieText(container, layer, context);
94
+ else if (layer.ty === 6 || layer.ty === 13) {
95
+ reportLottieSkip(context, layer.ty === 6 ? 'lottie.unsupported-audio-layer' : 'lottie.unsupported-camera-layer', {
96
+ layer: layer.nm ?? '',
97
+ });
98
+ }
99
+ else {
100
+ reportLottieSkip(context, 'lottie.unsupported-layer', { layerType: layer.ty });
101
+ }
102
+ applyLottieMasks(container, layer.masksProperties ?? [], context);
103
+ return container;
104
+ }
105
+ function applyLottieTransform(target, transform, context) {
106
+ if (transform === undefined)
107
+ return;
108
+ if (isSeparatedPosition(transform.p)) {
109
+ applyScalarProperty(target, transform.p.x, 'X', (value) => value, context);
110
+ applyScalarProperty(target, transform.p.y, 'Y', (value) => value, context);
111
+ if (transform.p.z !== undefined) {
112
+ reportLottieSkip(context, 'lottie.unsupported-3d-transform', { property: 'position.z' });
113
+ }
114
+ }
115
+ else {
116
+ applyVectorProperty(target, transform.p, 'Position', ['X', 'Y'], 2, (value) => value, context);
117
+ }
118
+ applyVectorProperty(target, transform.a, 'Pivot', ['PivotX', 'PivotY'], 2, (value) => value, context);
119
+ applyVectorProperty(target, transform.s, 'Scale', ['ScaleX', 'ScaleY'], 2, (value) => value / 100, context);
120
+ applyScalarProperty(target, transform.r ?? transform.rz, 'Rotation', degreesToRadians, context);
121
+ applyScalarProperty(target, transform.o, 'Alpha', (value) => value / 100, context);
122
+ if (transform.sk !== undefined) {
123
+ applyScalarProperty(target, transform.sk, 'SkewX', degreesToRadians, context);
124
+ if (transform.sa !== undefined) {
125
+ reportLottieSkip(context, 'lottie.unsupported-skew-axis', { property: 'transform.sa' });
126
+ }
127
+ }
128
+ }
129
+ function applyVectorProperty(target, property, vectorPath, scalarPaths, components, convert, context) {
130
+ if (property === undefined)
131
+ return;
132
+ const initial = numericValue(initialLottieValue(property), components).map(convert);
133
+ applyDisplaySample(target, vectorPath, initial);
134
+ if (!isAnimatedProperty(property))
135
+ return;
136
+ appendNumericPropertyChannels(property, components, (component) => ({
137
+ node: target,
138
+ path: component === null ? vectorPath : scalarPaths[component],
139
+ }), convert, context);
140
+ }
141
+ function applyScalarProperty(target, property, path, convert, context) {
142
+ if (property === undefined)
143
+ return;
144
+ applyDisplaySample(target, path, [convert(numericValue(initialLottieValue(property), 1)[0])]);
145
+ if (!isAnimatedProperty(property))
146
+ return;
147
+ appendNumericPropertyChannels(property, 1, () => ({ node: target, path }), convert, context);
148
+ }
149
+ function appendNumericPropertyChannels(property, components, target, convert, context) {
150
+ if (!isAnimatedProperty(property))
151
+ return;
152
+ const keyframes = property.k;
153
+ if (keyframes.length === 0)
154
+ return;
155
+ const componentSpecific = hasComponentSpecificEasing(keyframes, components);
156
+ if (componentSpecific && components > 1) {
157
+ for (let component = 0; component < components; component++) {
158
+ context.channels.push(createAnimationChannel(createLottieTrack(keyframes, 1, context, (value) => [convert(numericValue(value, components)[component], component)], component), target(component)));
159
+ }
160
+ return;
161
+ }
162
+ context.channels.push(createAnimationChannel(createLottieTrack(keyframes, components, context, (value) => numericValue(value, components).map(convert), 0), target(null)));
163
+ }
164
+ function bindMutableNumericProperty(property, current, convert, onChange, context) {
165
+ if (!isAnimatedProperty(property))
166
+ return;
167
+ appendNumericPropertyChannels(property, current.length, (component) => ({
168
+ lottieApply(sample) {
169
+ if (component === null) {
170
+ for (let index = 0; index < current.length; index++)
171
+ current[index] = sample[index];
172
+ }
173
+ else {
174
+ current[component] = sample[0];
175
+ }
176
+ onChange();
177
+ },
178
+ }), convert, context);
179
+ }
180
+ function createLottieTrack(keyframes, components, context, valueOf, easingComponent) {
181
+ const times = [];
182
+ const values = [];
183
+ const retained = [];
184
+ for (let index = 0; index < keyframes.length; index++) {
185
+ const keyframe = keyframes[index];
186
+ const time = frameToSeconds(keyframe.t, context);
187
+ if (times.length > 0 && time <= times[times.length - 1])
188
+ continue;
189
+ times.push(time);
190
+ retained.push(keyframe);
191
+ const source = keyframe.s ?? keyframes[index - 1]?.e;
192
+ values.push(...valueOf(source, index));
193
+ }
194
+ const segmentEasings = [];
195
+ for (let index = 0; index < retained.length - 1; index++) {
196
+ const keyframe = retained[index];
197
+ if (keyframe.h === 1) {
198
+ segmentEasings.push(_holdEasing);
199
+ }
200
+ else {
201
+ segmentEasings.push(createLottieSegmentEasing(keyframe.o, retained[index + 1].i, easingComponent));
202
+ }
203
+ }
204
+ return createAnimationTrack({
205
+ components,
206
+ interpolation: 'Linear',
207
+ segmentEasings,
208
+ times,
209
+ values,
210
+ });
211
+ }
212
+ function createLottieSegmentEasing(outgoing, incoming, component) {
213
+ if (outgoing === undefined || incoming === undefined)
214
+ return null;
215
+ return easeCubicBezier(handleComponent(outgoing.x, component), handleComponent(outgoing.y, component), handleComponent(incoming.x, component), handleComponent(incoming.y, component));
216
+ }
217
+ function appendLottieSolid(parent, layer) {
218
+ const shape = createShape();
219
+ const color = parseHexColor(layer.sc ?? '#000000');
220
+ appendShapeBeginFill(shape, color, 1);
221
+ const path = createPath();
222
+ appendPathRectangle(path, 0, 0, layer.sw ?? 0, layer.sh ?? 0);
223
+ appendShapePath(shape, path.commands.slice(), path.data.slice(), path.winding);
224
+ appendShapeEndFill(shape);
225
+ addNodeChild(parent, shape);
226
+ }
227
+ function appendLottieImage(parent, layer, context) {
228
+ const asset = layer.refId === undefined ? undefined : context.assets.get(layer.refId);
229
+ if (asset === undefined || !isImageAsset(asset)) {
230
+ reportLottieDrop(context, 'lottie.unresolved-asset', { id: layer.refId ?? '' });
231
+ return;
232
+ }
233
+ const image = context.options?.resolveImageResource?.(asset) ?? null;
234
+ if (image === null) {
235
+ reportLottieSkip(context, 'lottie.unresolved-image', { id: asset.id });
236
+ return;
237
+ }
238
+ addNodeChild(parent, createBitmap({ data: { image, smoothing: true } }));
239
+ }
240
+ function appendLottieText(parent, layer, context) {
241
+ const textData = layer.t;
242
+ const first = textData?.d.k[0]?.s;
243
+ if (first === undefined) {
244
+ reportLottieDrop(context, 'lottie.text-missing-document', { layer: layer.nm ?? '' });
245
+ return;
246
+ }
247
+ const label = createTextLabel({
248
+ data: {
249
+ autoSize: 'left',
250
+ height: (first.s ?? 16) * 1.25,
251
+ text: first.t,
252
+ textFormat: createLottieTextFormat(first),
253
+ width: context.document.w,
254
+ },
255
+ });
256
+ addNodeChild(parent, label);
257
+ if ((textData?.a?.length ?? 0) > 0) {
258
+ reportLottieSkip(context, 'lottie.unsupported-text-animator', { layer: layer.nm ?? '' });
259
+ }
260
+ if ((textData?.d.k.length ?? 0) > 1) {
261
+ reportLottieSkip(context, 'lottie.unsupported-animated-text-document', { layer: layer.nm ?? '' });
262
+ }
263
+ }
264
+ function appendLottiePrecomposition(parent, layer, context) {
265
+ const id = layer.refId;
266
+ const asset = id === undefined ? undefined : context.assets.get(id);
267
+ if (id === undefined || asset === undefined || !isPrecompositionAsset(asset)) {
268
+ reportLottieDrop(context, 'lottie.unresolved-asset', { id: id ?? '' });
269
+ return;
270
+ }
271
+ if (context.resolvingPrecompositions.has(id)) {
272
+ reportLottieDrop(context, 'lottie.recursive-precomposition', { id });
273
+ return;
274
+ }
275
+ context.resolvingPrecompositions.add(id);
276
+ appendLottieLayers(parent, asset.layers, {
277
+ ...context,
278
+ frameOffset: context.frameOffset + (layer.st ?? 0) * context.frameScale,
279
+ frameScale: context.frameScale * (layer.sr ?? 1),
280
+ });
281
+ context.resolvingPrecompositions.delete(id);
282
+ }
283
+ function appendLottieShapeItems(parent, items, context) {
284
+ const group = createDisplayObject();
285
+ const transform = items.find((item) => item.ty === 'tr');
286
+ if (transform?.ty === 'tr')
287
+ applyLottieTransform(group, transform, context);
288
+ const state = {
289
+ fill: null,
290
+ gradient: null,
291
+ paths: [],
292
+ shape: createShape(),
293
+ stroke: null,
294
+ };
295
+ const rerender = () => renderLottieShapeState(state);
296
+ for (const item of items) {
297
+ if (item.hd === true)
298
+ continue;
299
+ if (item.ty === 'gr') {
300
+ appendLottieShapeItems(group, item.it, context);
301
+ continue;
302
+ }
303
+ const path = createLottieShapeItemPath(item);
304
+ if (path !== null) {
305
+ const pathIndex = state.paths.length;
306
+ state.paths.push(path);
307
+ bindLottieGeometryItem(item, state, pathIndex, rerender, context);
308
+ }
309
+ if (item.ty === 'fl') {
310
+ const fill = item;
311
+ const color = numericValue(initialLottieValue(fill.c), 3);
312
+ const opacity = [numericValue(initialLottieValue(fill.o), 1)[0] / 100];
313
+ state.fill = {
314
+ color,
315
+ opacity: opacity[0],
316
+ winding: fill.r === 2 ? 'evenOdd' : 'nonZero',
317
+ };
318
+ bindMutableNumericProperty(fill.c, color, (value) => value, rerender, context);
319
+ bindMutableNumericProperty(fill.o, opacity, (value) => value / 100, () => {
320
+ state.fill.opacity = opacity[0];
321
+ rerender();
322
+ }, context);
323
+ }
324
+ else if (item.ty === 'st') {
325
+ const stroke = item;
326
+ const color = numericValue(initialLottieValue(stroke.c), 3);
327
+ const opacity = [numericValue(initialLottieValue(stroke.o), 1)[0] / 100];
328
+ const width = [numericValue(initialLottieValue(stroke.w), 1)[0]];
329
+ state.stroke = {
330
+ color,
331
+ opacity: opacity[0],
332
+ width: width[0],
333
+ };
334
+ bindMutableNumericProperty(stroke.c, color, (value) => value, rerender, context);
335
+ bindMutableNumericProperty(stroke.o, opacity, (value) => value / 100, () => {
336
+ state.stroke.opacity = opacity[0];
337
+ rerender();
338
+ }, context);
339
+ bindMutableNumericProperty(stroke.w, width, (value) => value, () => {
340
+ state.stroke.width = width[0];
341
+ rerender();
342
+ }, context);
343
+ if (stroke.d !== undefined) {
344
+ reportLottieSkip(context, 'lottie.unsupported-animated-dash', { shape: item.nm ?? '' });
345
+ }
346
+ }
347
+ else if (item.ty === 'gf' || item.ty === 'gs') {
348
+ const gradient = item;
349
+ const values = numericValue(initialLottieValue(gradient.g.k), gradient.g.p * 4);
350
+ const start = numericValue(initialLottieValue(gradient.s), 2);
351
+ const end = numericValue(initialLottieValue(gradient.e), 2);
352
+ const opacity = [gradient.o === undefined ? 100 : numericValue(initialLottieValue(gradient.o), 1)[0]];
353
+ const width = [gradient.w === undefined ? 1 : numericValue(initialLottieValue(gradient.w), 1)[0]];
354
+ state.gradient = {
355
+ count: gradient.g.p,
356
+ end,
357
+ kind: gradient.t,
358
+ opacity: opacity[0] / 100,
359
+ start,
360
+ type: gradient.ty,
361
+ values,
362
+ width: width[0],
363
+ };
364
+ bindMutableNumericProperty(gradient.g.k, values, (value) => value, rerender, context);
365
+ bindMutableNumericProperty(gradient.s, start, (value) => value, rerender, context);
366
+ bindMutableNumericProperty(gradient.e, end, (value) => value, rerender, context);
367
+ if (gradient.o !== undefined) {
368
+ bindMutableNumericProperty(gradient.o, opacity, (value) => value, () => {
369
+ state.gradient.opacity = opacity[0] / 100;
370
+ rerender();
371
+ }, context);
372
+ }
373
+ if (gradient.w !== undefined) {
374
+ bindMutableNumericProperty(gradient.w, width, (value) => value, () => {
375
+ state.gradient.width = width[0];
376
+ rerender();
377
+ }, context);
378
+ }
379
+ }
380
+ else if (item.ty === 'tm') {
381
+ const trim = item;
382
+ if (isAnimatedProperty(trim.s) || isAnimatedProperty(trim.e) || isAnimatedProperty(trim.o)) {
383
+ reportLottieSkip(context, 'lottie.unsupported-shape-modifier', { modifier: item.ty });
384
+ }
385
+ }
386
+ else if (item.ty === 'rp' || item.ty === 'mm' || item.ty === 'rd') {
387
+ reportLottieSkip(context, 'lottie.unsupported-shape-modifier', { modifier: item.ty });
388
+ }
389
+ else if (item.ty !== 'sh' && item.ty !== 'rc' && item.ty !== 'el' && item.ty !== 'sr' && item.ty !== 'tr') {
390
+ reportLottieSkip(context, 'lottie.unsupported-shape-item', { shapeType: item.ty });
391
+ }
392
+ reportLottieExpression(item, context);
393
+ }
394
+ applyStaticLottieTrim(items, state, context);
395
+ renderLottieShapeState(state);
396
+ if (state.paths.length > 0)
397
+ addNodeChild(group, state.shape);
398
+ addNodeChild(parent, group);
399
+ }
400
+ function createLottieShapeItemPath(item) {
401
+ if (item.ty === 'sh') {
402
+ const shapePath = item;
403
+ const value = initialLottieValue(shapePath.ks);
404
+ if (value === undefined)
405
+ return null;
406
+ return createLottieBezierPath(value);
407
+ }
408
+ const path = createPath();
409
+ if (item.ty === 'rc') {
410
+ const rectangle = item;
411
+ const position = numericValue(initialLottieValue(rectangle.p), 2);
412
+ const size = numericValue(initialLottieValue(rectangle.s), 2);
413
+ const radius = numericValue(initialLottieValue(rectangle.r), 1)[0];
414
+ if (radius > 0) {
415
+ appendPathRoundRectangle(path, position[0] - size[0] / 2, position[1] - size[1] / 2, size[0], size[1], radius);
416
+ }
417
+ else {
418
+ appendPathRectangle(path, position[0] - size[0] / 2, position[1] - size[1] / 2, size[0], size[1]);
419
+ }
420
+ return path;
421
+ }
422
+ if (item.ty === 'el') {
423
+ const ellipse = item;
424
+ const position = numericValue(initialLottieValue(ellipse.p), 2);
425
+ const size = numericValue(initialLottieValue(ellipse.s), 2);
426
+ appendPathEllipse(path, position[0], position[1], size[0] / 2, size[1] / 2);
427
+ return path;
428
+ }
429
+ if (item.ty === 'sr') {
430
+ const polystar = item;
431
+ const center = numericValue(initialLottieValue(polystar.p), 2);
432
+ const points = Math.max(2, Math.round(numericValue(initialLottieValue(polystar.pt), 1)[0]));
433
+ const outer = numericValue(initialLottieValue(polystar.or), 1)[0];
434
+ const inner = polystar.sy === 1 ? numericValue(initialLottieValue(polystar.ir), 1)[0] : outer;
435
+ const rotation = numericValue(initialLottieValue(polystar.r), 1)[0];
436
+ return createLottiePolystarPath(polystar.sy, center, points, outer, inner, rotation);
437
+ }
438
+ return null;
439
+ }
440
+ function createLottiePolystarPath(kind, center, pointCount, outer, inner, rotationDegrees) {
441
+ const path = createPath();
442
+ const points = Math.max(2, Math.round(pointCount));
443
+ const rotation = degreesToRadians(rotationDegrees - 90);
444
+ const vertices = [];
445
+ const count = kind === 1 ? points * 2 : points;
446
+ for (let index = 0; index < count; index++) {
447
+ const radius = kind === 1 && index % 2 === 1 ? inner : outer;
448
+ const angle = rotation + (index * Math.PI * 2) / count;
449
+ vertices.push(center[0] + Math.cos(angle) * radius, center[1] + Math.sin(angle) * radius);
450
+ }
451
+ appendPathPolygon(path, vertices);
452
+ return path;
453
+ }
454
+ function bindLottieGeometryItem(item, state, pathIndex, rerender, context) {
455
+ const rebuild = (path) => {
456
+ state.paths[pathIndex] = path;
457
+ rerender();
458
+ };
459
+ if (item.ty === 'sh') {
460
+ const shape = item;
461
+ if (!isAnimatedProperty(shape.ks))
462
+ return;
463
+ const template = initialLottieValue(shape.ks);
464
+ if (template === undefined)
465
+ return;
466
+ const current = flattenLottieShapePath(template);
467
+ const apply = () => rebuild(createLottieBezierPath(unflattenLottieShapePath(template, current)));
468
+ appendLottieShapePathChannels(shape.ks.k, current, apply, context);
469
+ return;
470
+ }
471
+ if (item.ty === 'rc') {
472
+ const rectangle = item;
473
+ const position = numericValue(initialLottieValue(rectangle.p), 2);
474
+ const size = numericValue(initialLottieValue(rectangle.s), 2);
475
+ const radius = [numericValue(initialLottieValue(rectangle.r), 1)[0]];
476
+ const apply = () => {
477
+ const path = createPath();
478
+ if (radius[0] > 0) {
479
+ appendPathRoundRectangle(path, position[0] - size[0] / 2, position[1] - size[1] / 2, size[0], size[1], radius[0]);
480
+ }
481
+ else {
482
+ appendPathRectangle(path, position[0] - size[0] / 2, position[1] - size[1] / 2, size[0], size[1]);
483
+ }
484
+ rebuild(path);
485
+ };
486
+ bindMutableNumericProperty(rectangle.p, position, (value) => value, apply, context);
487
+ bindMutableNumericProperty(rectangle.s, size, (value) => value, apply, context);
488
+ bindMutableNumericProperty(rectangle.r, radius, (value) => value, apply, context);
489
+ return;
490
+ }
491
+ if (item.ty === 'el') {
492
+ const ellipse = item;
493
+ const position = numericValue(initialLottieValue(ellipse.p), 2);
494
+ const size = numericValue(initialLottieValue(ellipse.s), 2);
495
+ const apply = () => {
496
+ const path = createPath();
497
+ appendPathEllipse(path, position[0], position[1], size[0] / 2, size[1] / 2);
498
+ rebuild(path);
499
+ };
500
+ bindMutableNumericProperty(ellipse.p, position, (value) => value, apply, context);
501
+ bindMutableNumericProperty(ellipse.s, size, (value) => value, apply, context);
502
+ return;
503
+ }
504
+ if (item.ty === 'sr') {
505
+ const polystar = item;
506
+ const center = numericValue(initialLottieValue(polystar.p), 2);
507
+ const points = [numericValue(initialLottieValue(polystar.pt), 1)[0]];
508
+ const outer = [numericValue(initialLottieValue(polystar.or), 1)[0]];
509
+ const inner = [polystar.sy === 1 ? numericValue(initialLottieValue(polystar.ir), 1)[0] : outer[0]];
510
+ const rotation = [numericValue(initialLottieValue(polystar.r), 1)[0]];
511
+ const apply = () => {
512
+ rebuild(createLottiePolystarPath(polystar.sy, center, points[0], outer[0], inner[0], rotation[0]));
513
+ };
514
+ bindMutableNumericProperty(polystar.p, center, (value) => value, apply, context);
515
+ bindMutableNumericProperty(polystar.pt, points, (value) => value, apply, context);
516
+ bindMutableNumericProperty(polystar.or, outer, (value) => value, apply, context);
517
+ if (polystar.ir !== undefined)
518
+ bindMutableNumericProperty(polystar.ir, inner, (value) => value, apply, context);
519
+ bindMutableNumericProperty(polystar.r, rotation, (value) => value, apply, context);
520
+ }
521
+ }
522
+ function appendLottieShapePathChannels(keyframes, current, apply, context) {
523
+ if (keyframes.length === 0)
524
+ return;
525
+ if (keyframes.some((keyframe) => {
526
+ const value = keyframe.s ?? keyframe.e;
527
+ return value !== undefined && flattenLottieShapePath(value).length !== current.length;
528
+ })) {
529
+ reportLottieDrop(context, 'lottie.incompatible-animated-shape-path');
530
+ return;
531
+ }
532
+ const componentSpecific = hasComponentSpecificEasing(keyframes, current.length);
533
+ if (componentSpecific) {
534
+ for (let component = 0; component < current.length; component++) {
535
+ context.channels.push(createAnimationChannel(createLottieTrack(keyframes, 1, context, (value) => [flattenLottieShapePath(value ?? keyframes[0].s)[component] ?? current[component]], component), {
536
+ lottieApply(sample) {
537
+ current[component] = sample[0];
538
+ apply();
539
+ },
540
+ }));
541
+ }
542
+ return;
543
+ }
544
+ context.channels.push(createAnimationChannel(createLottieTrack(keyframes, current.length, context, (value) => flattenLottieShapePath(value ?? keyframes[0].s), 0), {
545
+ lottieApply(sample) {
546
+ for (let index = 0; index < current.length; index++)
547
+ current[index] = sample[index];
548
+ apply();
549
+ },
550
+ }));
551
+ }
552
+ function flattenLottieShapePath(path) {
553
+ const out = [];
554
+ for (const points of [path.v, path.i, path.o]) {
555
+ for (const point of points)
556
+ out.push(point[0] ?? 0, point[1] ?? 0);
557
+ }
558
+ return out;
559
+ }
560
+ function unflattenLottieShapePath(template, values) {
561
+ const count = template.v.length;
562
+ const readPoints = (offset) => {
563
+ const out = [];
564
+ for (let index = 0; index < count; index++) {
565
+ out.push([values[offset + index * 2] ?? 0, values[offset + index * 2 + 1] ?? 0]);
566
+ }
567
+ return out;
568
+ };
569
+ return {
570
+ c: template.c,
571
+ i: readPoints(count * 2),
572
+ o: readPoints(count * 4),
573
+ v: readPoints(0),
574
+ };
575
+ }
576
+ function applyStaticLottieTrim(items, state, context) {
577
+ const raw = items.find((item) => item.ty === 'tm');
578
+ if (raw === undefined)
579
+ return;
580
+ const trim = raw;
581
+ if (isAnimatedProperty(trim.s) || isAnimatedProperty(trim.e) || isAnimatedProperty(trim.o))
582
+ return;
583
+ const start = numericValue(initialLottieValue(trim.s), 1)[0] / 100;
584
+ const end = numericValue(initialLottieValue(trim.e), 1)[0] / 100;
585
+ const offset = numericValue(initialLottieValue(trim.o), 1)[0] / 360;
586
+ let visible = (((end - start) % 1) + 1) % 1;
587
+ if (Math.abs(end - start) >= 1)
588
+ visible = 1;
589
+ state.paths = state.paths.map((path) => {
590
+ if (visible >= 1)
591
+ return path;
592
+ const length = getPathLength(path);
593
+ const trimmed = createPath(path.winding);
594
+ if (length <= 0 || visible <= 0)
595
+ return trimmed;
596
+ dashPath(path, [visible * length, (1 - visible) * length], (start + offset) * length, trimmed);
597
+ return trimmed;
598
+ });
599
+ if (trim.m === 2 && state.paths.length > 1) {
600
+ reportLottieSkip(context, 'lottie.trim-individual-approximated', { count: state.paths.length });
601
+ }
602
+ }
603
+ function createLottieBezierPath(value) {
604
+ const path = createPath();
605
+ const count = value.v.length;
606
+ if (count === 0)
607
+ return path;
608
+ appendPathMoveTo(path, value.v[0][0], value.v[0][1]);
609
+ const limit = value.c ? count + 1 : count;
610
+ for (let index = 1; index < limit; index++) {
611
+ const previous = (index - 1) % count;
612
+ const current = index % count;
613
+ const start = value.v[previous];
614
+ const end = value.v[current];
615
+ const outgoing = value.o[previous] ?? [0, 0];
616
+ const incoming = value.i[current] ?? [0, 0];
617
+ if (outgoing[0] === 0 && outgoing[1] === 0 && incoming[0] === 0 && incoming[1] === 0) {
618
+ appendPathLineTo(path, end[0], end[1]);
619
+ }
620
+ else {
621
+ appendPathCubicCurveTo(path, start[0] + outgoing[0], start[1] + outgoing[1], end[0] + incoming[0], end[1] + incoming[1], end[0], end[1]);
622
+ }
623
+ }
624
+ return path;
625
+ }
626
+ function renderLottieShapeState(state) {
627
+ clearShapeCommands(state.shape);
628
+ if (state.fill !== null) {
629
+ appendShapeBeginFill(state.shape, lottieRgb(state.fill.color), state.fill.opacity);
630
+ }
631
+ else if (state.gradient !== null && state.gradient.type === 'gf') {
632
+ appendLottieGradientFill(state.shape, state.gradient);
633
+ }
634
+ if (state.stroke !== null) {
635
+ appendShapeLineStyle(state.shape, state.stroke.width, lottieRgb(state.stroke.color), state.stroke.opacity);
636
+ }
637
+ else if (state.gradient !== null && state.gradient.type === 'gs') {
638
+ appendLottieGradientStroke(state.shape, state.gradient);
639
+ }
640
+ for (const path of state.paths) {
641
+ appendShapePath(state.shape, path.commands.slice(), path.data.slice(), state.fill?.winding ?? path.winding);
642
+ }
643
+ if (state.fill !== null || state.gradient?.type === 'gf')
644
+ appendShapeEndFill(state.shape);
645
+ }
646
+ function appendLottieGradientFill(shape, state) {
647
+ const gradient = parseLottieGradient(state.values, state.count, state.opacity);
648
+ appendShapeBeginGradientFill(shape, state.kind === 2 ? 'radial' : 'linear', gradient.colors, gradient.alphas, gradient.ratios, createLottieGradientMatrix(state.start, state.end));
649
+ }
650
+ function appendLottieGradientStroke(shape, state) {
651
+ const gradient = parseLottieGradient(state.values, state.count, state.opacity);
652
+ appendShapeLineStyle(shape, state.width);
653
+ appendShapeLineGradientStyle(shape, state.kind === 2 ? 'radial' : 'linear', gradient.colors, gradient.alphas, gradient.ratios, createLottieGradientMatrix(state.start, state.end));
654
+ }
655
+ function applyLottieMasks(target, masks, context) {
656
+ const active = masks.filter((mask) => mask.mode !== 'n');
657
+ if (active.length === 0)
658
+ return;
659
+ const first = active[0];
660
+ if (first.mode !== 'a' || first.inv === true || active.length > 1) {
661
+ reportLottieSkip(context, 'lottie.unsupported-mask-composition', {
662
+ count: active.length,
663
+ mode: first.mode,
664
+ });
665
+ return;
666
+ }
667
+ const initial = initialLottieValue(first.pt);
668
+ if (initial === undefined)
669
+ return;
670
+ target.clip = createClipRegionFromPath(createLottieBezierPath(initial));
671
+ if (isAnimatedProperty(first.pt)) {
672
+ const current = flattenLottieShapePath(initial);
673
+ appendLottieShapePathChannels(first.pt.k, current, () => {
674
+ target.clip = createClipRegionFromPath(createLottieBezierPath(unflattenLottieShapePath(initial, current)));
675
+ }, context);
676
+ }
677
+ if (first.f !== undefined || first.x !== undefined) {
678
+ reportLottieSkip(context, 'lottie.unsupported-soft-mask', { mask: first.nm ?? '' });
679
+ }
680
+ }
681
+ function applyLottieLayerVisibility(target, layer, context) {
682
+ const start = frameToSeconds(layer.ip ?? context.document.ip, context);
683
+ const end = frameToSeconds(layer.op ?? context.document.op, context);
684
+ target.visible = start <= 0 && end > 0;
685
+ const duration = Math.max(0, (context.document.op - context.document.ip) / context.document.fr);
686
+ const times = [0, clamp(start, 0, duration), clamp(end, 0, duration), duration].filter((time, index, all) => index === 0 || time > all[index - 1]);
687
+ if (times.length < 2)
688
+ return;
689
+ const values = times.map((time) => (time >= start && time < end ? 1 : 0));
690
+ context.channels.push(createAnimationChannel(createAnimationTrack({ interpolation: 'Step', times, values }), {
691
+ node: target,
692
+ path: 'Visible',
693
+ }));
694
+ }
695
+ function applyLottieBlendMode(target, layer, context) {
696
+ const mode = layer.bm ?? 0;
697
+ if (mode === 0)
698
+ target.blendMode = BlendMode.Normal;
699
+ else if (mode === 1)
700
+ target.blendMode = BlendMode.Multiply;
701
+ else if (mode === 2)
702
+ target.blendMode = BlendMode.Screen;
703
+ else if (mode === 3)
704
+ target.blendMode = BlendMode.Add;
705
+ else if (mode === 8)
706
+ target.blendMode = BlendMode.Darken;
707
+ else if (mode === 9)
708
+ target.blendMode = BlendMode.Lighten;
709
+ else
710
+ reportLottieSkip(context, 'lottie.unsupported-blend-mode', { blendMode: mode });
711
+ }
712
+ function reportLottieLayerExclusions(layer, context) {
713
+ if (layer.ddd === 1)
714
+ reportLottieSkip(context, 'lottie.unsupported-3d-layer', { layer: layer.nm ?? '' });
715
+ if ((layer.ef?.length ?? 0) > 0)
716
+ reportLottieSkip(context, 'lottie.unsupported-effect', { layer: layer.nm ?? '' });
717
+ if (layer.tm !== undefined)
718
+ reportLottieSkip(context, 'lottie.unsupported-time-remap', { layer: layer.nm ?? '' });
719
+ if (layer.tt !== undefined || layer.td !== undefined) {
720
+ reportLottieSkip(context, 'lottie.unsupported-matte', { layer: layer.nm ?? '' });
721
+ }
722
+ reportLottieExpression(layer.ks, context);
723
+ }
724
+ function reportLottieExpression(value, context) {
725
+ if (value === null || typeof value !== 'object')
726
+ return;
727
+ if ('x' in value && typeof value.x === 'string') {
728
+ reportLottieSkip(context, 'lottie.unsupported-expression');
729
+ }
730
+ for (const child of Object.values(value)) {
731
+ if (child !== value)
732
+ reportLottieExpression(child, context);
733
+ }
734
+ }
735
+ function applyDisplaySample(target, path, sample) {
736
+ if (path === 'Position') {
737
+ target.x = sample[0];
738
+ target.y = sample[1];
739
+ }
740
+ else if (path === 'X')
741
+ target.x = sample[0];
742
+ else if (path === 'Y')
743
+ target.y = sample[0];
744
+ else if (path === 'Pivot') {
745
+ target.pivotX = sample[0];
746
+ target.pivotY = sample[1];
747
+ }
748
+ else if (path === 'PivotX')
749
+ target.pivotX = sample[0];
750
+ else if (path === 'PivotY')
751
+ target.pivotY = sample[0];
752
+ else if (path === 'Scale') {
753
+ target.scaleX = sample[0];
754
+ target.scaleY = sample[1];
755
+ }
756
+ else if (path === 'ScaleX')
757
+ target.scaleX = sample[0];
758
+ else if (path === 'ScaleY')
759
+ target.scaleY = sample[0];
760
+ else if (path === 'Rotation')
761
+ target.rotation = sample[0];
762
+ else if (path === 'SkewX')
763
+ target.skewX = sample[0];
764
+ else if (path === 'Alpha')
765
+ target.alpha = sample[0];
766
+ }
767
+ function parseLottieDocument(source) {
768
+ if (typeof source !== 'string')
769
+ return source;
770
+ try {
771
+ return JSON.parse(source);
772
+ }
773
+ catch {
774
+ return null;
775
+ }
776
+ }
777
+ function isValidLottieDocument(document) {
778
+ return (Number.isFinite(document.fr) &&
779
+ document.fr > 0 &&
780
+ Number.isFinite(document.ip) &&
781
+ Number.isFinite(document.op) &&
782
+ document.op >= document.ip &&
783
+ Number.isFinite(document.w) &&
784
+ Number.isFinite(document.h) &&
785
+ Array.isArray(document.layers));
786
+ }
787
+ function isAnimatedProperty(property) {
788
+ return property.a === 1 && Array.isArray(property.k);
789
+ }
790
+ function initialLottieValue(property) {
791
+ if (property === undefined)
792
+ return undefined;
793
+ if (!isAnimatedProperty(property))
794
+ return property.k;
795
+ return property.k[0]?.s ?? property.k[0]?.e;
796
+ }
797
+ function isSeparatedPosition(property) {
798
+ return property !== undefined && 's' in property && property.s === true && 'x' in property && 'y' in property;
799
+ }
800
+ function numericValue(value, components) {
801
+ const source = Array.isArray(value) ? value : [value];
802
+ const out = new Array(components);
803
+ for (let index = 0; index < components; index++) {
804
+ const candidate = Number(source[index] ?? source[0] ?? 0);
805
+ out[index] = Number.isFinite(candidate) ? candidate : 0;
806
+ }
807
+ return out;
808
+ }
809
+ function hasComponentSpecificEasing(keyframes, components) {
810
+ if (components < 2)
811
+ return false;
812
+ for (let index = 0; index < keyframes.length - 1; index++) {
813
+ const current = keyframes[index];
814
+ const next = keyframes[index + 1];
815
+ for (const handle of [current.o, next.i]) {
816
+ if (handle === undefined)
817
+ continue;
818
+ if (handleVaries(handle.x, components) || handleVaries(handle.y, components))
819
+ return true;
820
+ }
821
+ }
822
+ return false;
823
+ }
824
+ function handleVaries(value, components) {
825
+ if (!Array.isArray(value) || value.length < 2)
826
+ return false;
827
+ for (let index = 1; index < components; index++) {
828
+ if ((value[index] ?? value[0]) !== value[0])
829
+ return true;
830
+ }
831
+ return false;
832
+ }
833
+ function handleComponent(value, component) {
834
+ return Array.isArray(value) ? (value[component] ?? value[0] ?? 0) : value;
835
+ }
836
+ function frameToSeconds(frame, context) {
837
+ return (context.frameOffset + frame * context.frameScale - context.document.ip) / context.document.fr;
838
+ }
839
+ function isImageAsset(asset) {
840
+ return 'p' in asset;
841
+ }
842
+ function isPrecompositionAsset(asset) {
843
+ return 'layers' in asset;
844
+ }
845
+ function createLottieTextFormat(document) {
846
+ const color = document.fc ?? [0, 0, 0];
847
+ return {
848
+ align: document.j === 1 ? 'right' : document.j === 2 ? 'center' : 'left',
849
+ color: packColor(color[0] ?? 0, color[1] ?? 0, color[2] ?? 0, 1),
850
+ font: document.f,
851
+ leading: document.lh,
852
+ letterSpacing: document.tr,
853
+ size: document.s,
854
+ };
855
+ }
856
+ function parseLottieGradient(values, count, opacity) {
857
+ const colors = [];
858
+ const ratios = [];
859
+ for (let index = 0; index < count; index++) {
860
+ const offset = index * 4;
861
+ ratios.push(Math.round(clamp(values[offset] ?? 0, 0, 1) * 255));
862
+ colors.push(lottieRgb(values.slice(offset + 1, offset + 4)));
863
+ }
864
+ return { alphas: new Array(count).fill(opacity), colors, ratios };
865
+ }
866
+ function createLottieGradientMatrix(start, end) {
867
+ const dx = end[0] - start[0];
868
+ const dy = end[1] - start[1];
869
+ return createGradientTransformMatrix(Math.hypot(dx, dy) * 2, Math.hypot(dx, dy) * 2, Math.atan2(dy, dx), start[0], start[1]);
870
+ }
871
+ function lottieRgb(color) {
872
+ return ((Math.round(clamp(color[0] ?? 0, 0, 1) * 255) << 16) |
873
+ (Math.round(clamp(color[1] ?? 0, 0, 1) * 255) << 8) |
874
+ Math.round(clamp(color[2] ?? 0, 0, 1) * 255));
875
+ }
876
+ function parseHexColor(value) {
877
+ const parsed = Number.parseInt(value.replace(/^#/, ''), 16);
878
+ return Number.isFinite(parsed) ? parsed & 0xffffff : 0;
879
+ }
880
+ function degreesToRadians(value) {
881
+ return (value * Math.PI) / 180;
882
+ }
883
+ function clamp(value, minimum, maximum) {
884
+ return Math.min(maximum, Math.max(minimum, value));
885
+ }
886
+ function reportLottieSkip(context, kind, detail) {
887
+ reportImportDiagnostic(context.diagnostics, ImportDiagnosticSeverity.Skip, kind, 'lottieDocument', detail);
888
+ }
889
+ function reportLottieDrop(context, kind, detail) {
890
+ reportImportDiagnostic(context.diagnostics, ImportDiagnosticSeverity.Drop, kind, 'lottieDocument', detail);
891
+ }
892
+ const _sampleScratch = new Array(256).fill(0);
893
+ const _holdEasing = () => 0;
894
+ //# sourceMappingURL=lottieDocument.js.map