@vvfx/ge-ir 0.0.1-beta.5 → 0.0.1-beta.7

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/dist/index.mjs CHANGED
@@ -3,10 +3,10 @@
3
3
  * Description: Galacean Effects and animation IR bidirectional conversion package
4
4
  * Author: Ant Group CO., Ltd.
5
5
  * Contributors: 澄弈
6
- * Version: v0.0.1-beta.5
6
+ * Version: v0.0.1-beta.7
7
7
  */
8
8
 
9
- import { IRBlendMode, IRShapePrimitiveType, IRFillType, IRLineCap, IRLineJoin, IRShapeFillRule, IREndBehavior, validateIRCompositionWithWarnings } from '@vvfx/animation-ir';
9
+ import { IRBlendMode, IRShapePrimitiveType, IRFillType, IRLineCap, IRLineJoin, IREndBehavior, validateIRCompositionOrThrow } from '@vvfx/animation-ir';
10
10
  import { v4 } from 'uuid';
11
11
 
12
12
  function _extends() {
@@ -60,6 +60,92 @@ function _create_for_of_iterator_helper_loose(o, allowArrayLike) {
60
60
  throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
61
61
  }
62
62
 
63
+ /** Read GE layer passthrough data. */ function getEffectsLayerSpecific(ir, layer) {
64
+ var _layer_specific;
65
+ return ir.source === 'effects' ? (_layer_specific = layer.specific) == null ? void 0 : _layer_specific.effects : undefined;
66
+ }
67
+ /** Read GE composition passthrough data. */ function getEffectsCompositionSpecific(ir) {
68
+ var _ir_specific;
69
+ return ir.source === 'effects' ? (_ir_specific = ir.specific) == null ? void 0 : _ir_specific.effects : undefined;
70
+ }
71
+ var EFFECTS_ANIMATION_SPECIFIC_KEYS = [
72
+ 'positionOverLifetime',
73
+ 'rotationOverLifetime',
74
+ 'sizeOverLifetime',
75
+ 'colorOverLifetime'
76
+ ];
77
+ var KEYS_BY_INVALIDATION_TARGET = {
78
+ position: [
79
+ 'positionOverLifetime'
80
+ ],
81
+ rotation: [
82
+ 'rotationOverLifetime',
83
+ 'sourceHierarchy'
84
+ ],
85
+ scale: [
86
+ 'sizeOverLifetime',
87
+ 'sourceHierarchy'
88
+ ],
89
+ color: [
90
+ 'colorOverLifetime'
91
+ ],
92
+ opacity: [
93
+ 'colorOverLifetime'
94
+ ],
95
+ anchor: [
96
+ 'sourceHierarchy'
97
+ ],
98
+ mask: [
99
+ 'mask'
100
+ ],
101
+ precomp: [
102
+ 'precompRefId',
103
+ 'precompContent'
104
+ ]
105
+ };
106
+ /**
107
+ * Remove raw GE values that would otherwise shadow an edited normalized field.
108
+ * Empty `effects` and `specific` containers are pruned so serialization stays clean.
109
+ */ function invalidateEffectsLayerSpecific(layer, target) {
110
+ var _layer_specific;
111
+ var effects = (_layer_specific = layer.specific) == null ? void 0 : _layer_specific.effects;
112
+ if (effects == null) {
113
+ return [];
114
+ }
115
+ var removed = [];
116
+ for(var _iterator = _create_for_of_iterator_helper_loose(KEYS_BY_INVALIDATION_TARGET[target]), _step; !(_step = _iterator()).done;){
117
+ var key = _step.value;
118
+ if (key in effects) {
119
+ delete effects[key];
120
+ removed.push(key);
121
+ }
122
+ }
123
+ if (Object.keys(effects).length === 0 && layer.specific != null) {
124
+ delete layer.specific.effects;
125
+ }
126
+ if (layer.specific != null && Object.keys(layer.specific).length === 0) {
127
+ delete layer.specific;
128
+ }
129
+ return removed;
130
+ }
131
+ /** Whether invalidating opacity would also discard a GE color animation. */ function hasEffectsColorAnimationPassthrough(layer) {
132
+ var _layer_specific_effects, _layer_specific;
133
+ var color = (_layer_specific = layer.specific) == null ? void 0 : (_layer_specific_effects = _layer_specific.effects) == null ? void 0 : _layer_specific_effects.colorOverLifetime;
134
+ if (color == null) {
135
+ return false;
136
+ }
137
+ return Object.keys(color).some(function(channelKey) {
138
+ return channelKey !== 'opacity';
139
+ });
140
+ }
141
+ /** Whether the layer retains any raw GE animation payload. */ function hasEffectsAnimationPassthrough(layer) {
142
+ var _layer_specific;
143
+ var effects = (_layer_specific = layer.specific) == null ? void 0 : _layer_specific.effects;
144
+ return effects != null && EFFECTS_ANIMATION_SPECIFIC_KEYS.some(function(key) {
145
+ return effects[key] != null;
146
+ });
147
+ }
148
+
63
149
  var DEFAULT_CAMERA_Z = 8;
64
150
  var DEFAULT_CAMERA_FOV = 60;
65
151
  var DEFAULT_CLIP_MODE = 1;
@@ -1193,36 +1279,29 @@ function getScalarValue(keyframe, dimensionIndex) {
1193
1279
  for(var i = 0; i < keyframes.length; i++){
1194
1280
  var _kf_outTangent, _kf_inTangent;
1195
1281
  var kf = keyframes[i];
1196
- var normalizedT = normalizeTime(kf.t, timeContext.startFrame, timeContext.endFrame);
1282
+ var normalizedT = normalizeTime(kf.frame, timeContext.startFrame, timeContext.endFrame);
1197
1283
  timingCurvePoints.push({
1198
1284
  t: normalizedT,
1199
1285
  outTangent: (_kf_outTangent = kf.outTangent) != null ? _kf_outTangent : DEFAULT_OUT_TANGENT,
1200
1286
  inTangent: (_kf_inTangent = kf.inTangent) != null ? _kf_inTangent : DEFAULT_IN_TANGENT
1201
1287
  });
1202
1288
  if (i < keyframes.length - 1) {
1203
- var _kf_value, _kf_endValue, _nextKf_value, _startPos_, _startPos_1, _stitchEnd_, _stitchEnd_1;
1289
+ var _kf_value, _ref, _kf_endValue, _startPos_, _startPos_1, _segmentEnd_, _segmentEnd_1;
1204
1290
  var nextKf = keyframes[i + 1];
1205
1291
  var startPos = (_kf_value = kf.value) != null ? _kf_value : [
1206
1292
  0,
1207
1293
  0,
1208
1294
  0
1209
1295
  ];
1210
- // Lottie segment end value: kf.endValue is authoritative; falls back to kf.value (mirrors master's `e ?? s`).
1211
- // nextKf.value is only used later in the path-fixup pass to stitch adjacent segments.
1212
- var segmentEnd = kf.hold === true ? startPos : (_kf_endValue = kf.endValue) != null ? _kf_endValue : startPos;
1213
- var stitchEnd = kf.hold === true ? startPos : (_nextKf_value = nextKf.value) != null ? _nextKf_value : [
1214
- 0,
1215
- 0,
1216
- 0
1217
- ];
1296
+ var segmentEnd = kf.hold === true ? startPos : (_ref = (_kf_endValue = kf.endValue) != null ? _kf_endValue : nextKf.value) != null ? _ref : startPos;
1218
1297
  pathPoints.push([
1219
1298
  [
1220
1299
  (_startPos_ = startPos[0]) != null ? _startPos_ : 0,
1221
1300
  (_startPos_1 = startPos[1]) != null ? _startPos_1 : 0
1222
1301
  ],
1223
1302
  [
1224
- (_stitchEnd_ = stitchEnd[0]) != null ? _stitchEnd_ : 0,
1225
- (_stitchEnd_1 = stitchEnd[1]) != null ? _stitchEnd_1 : 0
1303
+ (_segmentEnd_ = segmentEnd[0]) != null ? _segmentEnd_ : 0,
1304
+ (_segmentEnd_1 = segmentEnd[1]) != null ? _segmentEnd_1 : 0
1226
1305
  ]
1227
1306
  ]);
1228
1307
  // Spatial control points (per IRKeyframe convention): outgoing CP from
@@ -1241,11 +1320,11 @@ function getScalarValue(keyframe, dimensionIndex) {
1241
1320
  (_nextKf_spatialIn_1 = nextKf.spatialIn[1]) != null ? _nextKf_spatialIn_1 : 0
1242
1321
  ];
1243
1322
  } else {
1244
- var _startPos_2, _startPos_3, _segmentEnd_, _segmentEnd_1;
1323
+ var _startPos_2, _startPos_3, _segmentEnd_2, _segmentEnd_3;
1245
1324
  var sx = (_startPos_2 = startPos[0]) != null ? _startPos_2 : 0;
1246
1325
  var sy = (_startPos_3 = startPos[1]) != null ? _startPos_3 : 0;
1247
- var ex = (_segmentEnd_ = segmentEnd[0]) != null ? _segmentEnd_ : 0;
1248
- var ey = (_segmentEnd_1 = segmentEnd[1]) != null ? _segmentEnd_1 : 0;
1326
+ var ex = (_segmentEnd_2 = segmentEnd[0]) != null ? _segmentEnd_2 : 0;
1327
+ var ey = (_segmentEnd_3 = segmentEnd[1]) != null ? _segmentEnd_3 : 0;
1249
1328
  toPoint = [
1250
1329
  -sx / 3 + ex / 3,
1251
1330
  -sy / 3 + ey / 3
@@ -1261,10 +1340,6 @@ function getScalarValue(keyframe, dimensionIndex) {
1261
1340
  ]);
1262
1341
  }
1263
1342
  }
1264
- // Fix up path points: adjacent segments should share endpoints
1265
- for(var i1 = 0; i1 < pathPoints.length - 1; i1++){
1266
- pathPoints[i1][1] = pathPoints[i1 + 1][0];
1267
- }
1268
1343
  // Prepend a static segment if the first keyframe doesn't start at t=0.
1269
1344
  // The bridge segment runs synthetic-at-t=0 → original-kf[0]; for it to be a
1270
1345
  // flat hold the END-side tangent (which `buildPositionTimingCurves` reads as
@@ -1273,7 +1348,7 @@ function getScalarValue(keyframe, dimensionIndex) {
1273
1348
  // the IR convention leaves a first-kf inTangent undefined, or whatever the
1274
1349
  // producer set as a no-op).
1275
1350
  if (timingCurvePoints.length > 0 && timingCurvePoints[0].t > 0) {
1276
- var _ref, _ref1;
1351
+ var _ref1, _ref2;
1277
1352
  var _pathPoints_, _pathPoints_1;
1278
1353
  timingCurvePoints[0].inTangent = {
1279
1354
  x: 1,
@@ -1291,11 +1366,11 @@ function getScalarValue(keyframe, dimensionIndex) {
1291
1366
  }
1292
1367
  });
1293
1368
  pathPoints.unshift([
1294
- (_ref = (_pathPoints_ = pathPoints[0]) == null ? void 0 : _pathPoints_[0]) != null ? _ref : [
1369
+ (_ref1 = (_pathPoints_ = pathPoints[0]) == null ? void 0 : _pathPoints_[0]) != null ? _ref1 : [
1295
1370
  0,
1296
1371
  0
1297
1372
  ],
1298
- (_ref1 = (_pathPoints_1 = pathPoints[0]) == null ? void 0 : _pathPoints_1[0]) != null ? _ref1 : [
1373
+ (_ref2 = (_pathPoints_1 = pathPoints[0]) == null ? void 0 : _pathPoints_1[0]) != null ? _ref2 : [
1299
1374
  0,
1300
1375
  0
1301
1376
  ]
@@ -1372,10 +1447,13 @@ function buildCurveForDimension(keyframes, timeContext, dimensionIndex) {
1372
1447
  var _kf_outTangent, _nextKf_inTangent;
1373
1448
  var kf = keyframes[i];
1374
1449
  var nextKf = keyframes[i + 1];
1375
- var t0 = normalizeTime(kf.t, timeContext.startFrame, timeContext.endFrame);
1376
- var t1 = normalizeTime(nextKf.t, timeContext.startFrame, timeContext.endFrame);
1450
+ var t0 = normalizeTime(kf.frame, timeContext.startFrame, timeContext.endFrame);
1451
+ var t1 = normalizeTime(nextKf.frame, timeContext.startFrame, timeContext.endFrame);
1377
1452
  var v0 = getScalarValue(kf, dimensionIndex);
1378
- var v1 = kf.hold === true ? v0 : getScalarValue(nextKf, dimensionIndex);
1453
+ var endKeyframe = kf.endValue != null ? _extends({}, kf, {
1454
+ value: kf.endValue
1455
+ }) : nextKf;
1456
+ var v1 = kf.hold === true ? v0 : getScalarValue(endKeyframe, dimensionIndex);
1379
1457
  var outHandle = kf.hold === true ? {
1380
1458
  x: 0,
1381
1459
  y: 0
@@ -1482,20 +1560,11 @@ function buildPositionTimingCurves(points) {
1482
1560
  }
1483
1561
 
1484
1562
  /**
1485
- * True when the layer's parent only inherits transform — visibility and
1486
- * lifetime are independent of the parent's window.
1487
- *
1488
- * Reads `IRLayerReference.linkKind` directly. Producers that don't set the
1489
- * field (e.g. dsl2ge) effectively get `false` here, which is the correct
1490
- * default for "full" parent containment.
1491
- *
1492
- * Lottie's parser sets `linkKind: 'transform-only'` when a child's parent
1493
- * relationship comes from `layer.parent` rather than precomp expansion;
1494
- * `applyPostProcess` consumes this to decide whether to advance the parent's
1495
- * activation track when the child starts earlier.
1496
- */ function isTransformOnlyParentLink(layer) {
1563
+ * True when the child lifetime is independent of its transform parent's
1564
+ * clipping window. An omitted lifetimeMode keeps the legacy clipped default.
1565
+ */ function hasIndependentParentLifetime(layer) {
1497
1566
  var _layer_parent;
1498
- return ((_layer_parent = layer.parent) == null ? void 0 : _layer_parent.linkKind) === 'transform-only';
1567
+ return ((_layer_parent = layer.parent) == null ? void 0 : _layer_parent.lifetimeMode) === 'independent';
1499
1568
  }
1500
1569
 
1501
1570
  /**
@@ -1503,18 +1572,19 @@ function buildPositionTimingCurves(points) {
1503
1572
  * the time-stretch and offset semantics from `IRTimeContext`.
1504
1573
  *
1505
1574
  * dsl-source IRs leave `stretch` and `offsetFrame` undefined, in which case
1506
- * this collapses to `delay = startFrame / fr` and
1575
+ * this collapses to `delay = (startFrame - compositionInPoint) / fr` and
1507
1576
  * `duration = (endFrame - startFrame) / fr` — exactly what dsl2ge used to
1508
1577
  * compute inline.
1509
1578
  *
1510
1579
  * Zero/negative durations are clamped to a 0.0001 epsilon so GE keeps the
1511
1580
  * activation track alive for the (degenerate) layer.
1512
- */ function computeLayerTiming(layer, frameRate) {
1581
+ */ function computeLayerTiming(layer, frameRate, compositionInPoint) {
1582
+ if (compositionInPoint === void 0) compositionInPoint = 0;
1513
1583
  var _time_stretch, _time_offsetFrame;
1514
1584
  var time = layer.time;
1515
1585
  var stretch = (_time_stretch = time.stretch) != null ? _time_stretch : 1;
1516
1586
  var offsetFrame = (_time_offsetFrame = time.offsetFrame) != null ? _time_offsetFrame : 0;
1517
- var delay = roundNumber((offsetFrame + time.startFrame * stretch) / frameRate, 4);
1587
+ var delay = roundNumber((offsetFrame + time.startFrame * stretch - compositionInPoint) / frameRate, 4);
1518
1588
  var rawDuration = (time.endFrame - time.startFrame) * stretch / frameRate;
1519
1589
  var layerDuration = rawDuration > 0 ? roundNumber(rawDuration, 4) : 0.0001;
1520
1590
  return {
@@ -1526,37 +1596,29 @@ function buildPositionTimingCurves(points) {
1526
1596
  function computeStaticPosition(layer, ctx, anchorOffsetX, anchorOffsetY) {
1527
1597
  if (anchorOffsetX === void 0) anchorOffsetX = 0;
1528
1598
  if (anchorOffsetY === void 0) anchorOffsetY = 0;
1529
- var _transform_position;
1599
+ var _transform_position, _transform_position1, _transform_position_keyframes_, _transform_position2;
1530
1600
  var transform = layer.transform;
1531
- if (transform == null ? void 0 : (_transform_position = transform.position) == null ? void 0 : _transform_position.keyframed) {
1601
+ if ((transform == null ? void 0 : (_transform_position = transform.position) == null ? void 0 : _transform_position.keyframed) === true && transform.position.keyframes.length !== 1) {
1532
1602
  return convert2dTo3dPoint(ctx.width / 2 + anchorOffsetX, ctx.height / 2 + anchorOffsetY, 0, ctx.width, ctx.height);
1533
1603
  }
1534
1604
  var isNullType = layer.type === 'null' || layer.type === 'precomp';
1535
- if ((transform == null ? void 0 : transform.position) != null && Array.isArray(transform.position.value)) {
1605
+ var positionValue = (transform == null ? void 0 : (_transform_position1 = transform.position) == null ? void 0 : _transform_position1.keyframed) === true ? (_transform_position_keyframes_ = transform.position.keyframes[0]) == null ? void 0 : _transform_position_keyframes_.value : transform == null ? void 0 : (_transform_position2 = transform.position) == null ? void 0 : _transform_position2.value;
1606
+ if (Array.isArray(positionValue)) {
1536
1607
  var _pos_, _pos_1, _pos_2;
1537
- var pos = transform.position.value;
1608
+ var pos = positionValue;
1538
1609
  return convert2dTo3dPoint(((_pos_ = pos[0]) != null ? _pos_ : 0) + anchorOffsetX, ((_pos_1 = pos[1]) != null ? _pos_1 : 0) + anchorOffsetY, isNullType ? 0 : (_pos_2 = pos[2]) != null ? _pos_2 : 0, ctx.width, ctx.height);
1539
1610
  }
1540
1611
  return convert2dTo3dPoint(ctx.width / 2 + anchorOffsetX, ctx.height / 2 + anchorOffsetY, 0, ctx.width, ctx.height);
1541
1612
  }
1542
1613
  function getStaticAnchor(layer) {
1543
- var _layer_transform;
1544
- var anchor = (_layer_transform = layer.transform) == null ? void 0 : _layer_transform.anchor;
1545
- if (anchor != null && !anchor.keyframed && Array.isArray(anchor.value) && anchor.value.length >= 2) {
1614
+ var _layer_transform_anchor, _layer_transform;
1615
+ var value = (_layer_transform = layer.transform) == null ? void 0 : (_layer_transform_anchor = _layer_transform.anchor) == null ? void 0 : _layer_transform_anchor.value;
1616
+ if (Array.isArray(value) && value.length >= 2) {
1546
1617
  return [
1547
- anchor.value[0],
1548
- anchor.value[1]
1618
+ value[0],
1619
+ value[1]
1549
1620
  ];
1550
1621
  }
1551
- if ((anchor == null ? void 0 : anchor.keyframed) && anchor.keyframes != null && anchor.keyframes.length > 0) {
1552
- var firstVal = anchor.keyframes[0].value;
1553
- if (Array.isArray(firstVal) && firstVal.length >= 2) {
1554
- return [
1555
- firstVal[0],
1556
- firstVal[1]
1557
- ];
1558
- }
1559
- }
1560
1622
  return [
1561
1623
  0,
1562
1624
  0
@@ -1564,15 +1626,21 @@ function getStaticAnchor(layer) {
1564
1626
  }
1565
1627
  function getStaticScaleFromIR(layer) {
1566
1628
  var _value_, _value_1;
1567
- var _layer_transform;
1629
+ var _layer_transform, _scale_keyframes_;
1568
1630
  var scale = (_layer_transform = layer.transform) == null ? void 0 : _layer_transform.scale;
1569
- if (scale == null || scale.keyframed) {
1631
+ if (scale == null) {
1632
+ return [
1633
+ 1,
1634
+ 1
1635
+ ];
1636
+ }
1637
+ if (scale.keyframed && scale.keyframes.length !== 1) {
1570
1638
  return [
1571
1639
  1,
1572
1640
  1
1573
1641
  ];
1574
1642
  }
1575
- var value = scale.value;
1643
+ var value = scale.keyframed ? (_scale_keyframes_ = scale.keyframes[0]) == null ? void 0 : _scale_keyframes_.value : scale.value;
1576
1644
  if (!Array.isArray(value)) {
1577
1645
  return [
1578
1646
  1,
@@ -1584,14 +1652,15 @@ function getStaticScaleFromIR(layer) {
1584
1652
  (_value_1 = value[1]) != null ? _value_1 : 1
1585
1653
  ];
1586
1654
  }
1587
- function applyScalePositionAdjust(position, layer, layerWidth, layerHeight, ratio) {
1655
+ function applyScalePositionAdjust(position, layer, bounds, ratio) {
1588
1656
  var staticScale = getStaticScaleFromIR(layer);
1589
- if (staticScale[0] === 1 && staticScale[1] === 1 || layerWidth <= 0 || layerHeight <= 0) {
1657
+ var x = bounds.x, y = bounds.y, width = bounds.width, height = bounds.height;
1658
+ if (staticScale[0] === 1 && staticScale[1] === 1 || width <= 0 || height <= 0) {
1590
1659
  return position;
1591
1660
  }
1592
1661
  var anchor = getStaticAnchor(layer);
1593
- var anchorDiffX = anchor[0] - layerWidth / 2;
1594
- var anchorDiffY = anchor[1] - layerHeight / 2;
1662
+ var anchorDiffX = anchor[0] - (x + width / 2);
1663
+ var anchorDiffY = anchor[1] - (y + height / 2);
1595
1664
  if (Math.abs(anchorDiffX) <= 0.5 && Math.abs(anchorDiffY) <= 0.5) {
1596
1665
  return position;
1597
1666
  }
@@ -1601,43 +1670,46 @@ function applyScalePositionAdjust(position, layer, layerWidth, layerHeight, rati
1601
1670
  position[2]
1602
1671
  ];
1603
1672
  }
1604
- function computeAnchorInfo(layer, layerWidth, layerHeight) {
1673
+ function computeAnchorInfo(layer, bounds) {
1605
1674
  var anchor = getStaticAnchor(layer);
1675
+ var x = bounds.x, y = bounds.y, width = bounds.width, height = bounds.height;
1676
+ var localAnchorX = anchor[0] - x;
1677
+ var localAnchorY = anchor[1] - y;
1606
1678
  var anchorScaleX = 0.5;
1607
1679
  var anchorScaleY = 0.5;
1608
1680
  var anchorOffsetX = 0;
1609
1681
  var anchorOffsetY = 0;
1610
- if (layerWidth > 0 && layerHeight > 0) {
1611
- anchorScaleX = anchor[0] / layerWidth;
1612
- anchorScaleY = anchor[1] / layerHeight;
1613
- anchorOffsetX = -(anchor[0] - layerWidth / 2);
1614
- anchorOffsetY = -(anchor[1] - layerHeight / 2);
1682
+ if (width > 0 && height > 0) {
1683
+ anchorScaleX = localAnchorX / width;
1684
+ anchorScaleY = localAnchorY / height;
1685
+ anchorOffsetX = -(localAnchorX - width / 2);
1686
+ anchorOffsetY = -(localAnchorY - height / 2);
1615
1687
  }
1616
- if (layer.type === 'text' && layer.text != null && layerWidth > 0 && layerHeight > 0) {
1688
+ if (layer.type === 'text' && layer.text != null && width > 0 && height > 0) {
1617
1689
  if (layer.text.anchorAdjust === true) {
1618
1690
  if (layer.text.textAlign === 'left') {
1619
- anchorOffsetX = -(anchor[0] - layerWidth / 2);
1620
- anchorScaleX = anchor[0] / layerWidth - 0.5;
1691
+ anchorOffsetX = -(localAnchorX - width / 2);
1692
+ anchorScaleX = localAnchorX / width - 0.5;
1621
1693
  }
1622
1694
  if (layer.text.textAlign === 'center') {
1623
- anchorOffsetX = -anchor[0];
1624
- anchorScaleX = anchor[0] / layerWidth + 0.5;
1695
+ anchorOffsetX = -localAnchorX;
1696
+ anchorScaleX = localAnchorX / width + 0.5;
1625
1697
  }
1626
1698
  if (layer.text.textAlign === 'right') {
1627
- anchorOffsetX = -(anchor[0] + layerWidth / 2);
1628
- anchorScaleX = anchor[0] / layerWidth + 1.5;
1699
+ anchorOffsetX = -(localAnchorX + width / 2);
1700
+ anchorScaleX = localAnchorX / width + 1.5;
1629
1701
  }
1630
1702
  // GE renders glyphs in the lower half of the quad, so offset by half the
1631
1703
  // layer height to align the visible text center with Lottie's anchor.
1632
- var textHeight = layerHeight / 2;
1633
- anchorOffsetY = -(anchor[1] + textHeight);
1634
- anchorScaleY = -anchor[1] / textHeight;
1704
+ var textHeight = height / 2;
1705
+ anchorOffsetY = -(localAnchorY + textHeight);
1706
+ anchorScaleY = -localAnchorY / textHeight;
1635
1707
  }
1636
1708
  }
1637
1709
  var isSpriteForAnchor = layer.type === 'image' || layer.type === 'solid';
1638
1710
  var staticRotationZ = getStaticRotationZ(layer);
1639
1711
  var hasStaticRotation = staticRotationZ != null;
1640
- if (isSpriteForAnchor && hasStaticRotation && isTransformOnlyParentLink(layer) && (anchorOffsetX !== 0 || anchorOffsetY !== 0)) {
1712
+ if (isSpriteForAnchor && hasStaticRotation && hasIndependentParentLifetime(layer) && (anchorOffsetX !== 0 || anchorOffsetY !== 0)) {
1641
1713
  var rotValue = staticRotationZ != null ? staticRotationZ : 0;
1642
1714
  if (rotValue !== 0) {
1643
1715
  var rad = rotValue * Math.PI / 180;
@@ -1657,15 +1729,10 @@ function computeAnchorInfo(layer, layerWidth, layerHeight) {
1657
1729
  };
1658
1730
  }
1659
1731
  function getStaticRotationZ(layer) {
1660
- var _layer_transform, _layer_transform_separatedRotation, _layer_transform1;
1661
- var rotation = (_layer_transform = layer.transform) == null ? void 0 : _layer_transform.rotation;
1662
- if (rotation != null && !rotation.keyframed && Array.isArray(rotation.value)) {
1663
- var _rotation_value_;
1664
- return (_rotation_value_ = rotation.value[2]) != null ? _rotation_value_ : 0;
1665
- }
1666
- var separatedRotationZ = (_layer_transform1 = layer.transform) == null ? void 0 : (_layer_transform_separatedRotation = _layer_transform1.separatedRotation) == null ? void 0 : _layer_transform_separatedRotation.z;
1667
- if (separatedRotationZ != null && !separatedRotationZ.keyframed && typeof separatedRotationZ.value === 'number') {
1668
- return separatedRotationZ.value;
1732
+ var _layer_transform_rotation, _layer_transform;
1733
+ var rotationZ = (_layer_transform = layer.transform) == null ? void 0 : (_layer_transform_rotation = _layer_transform.rotation) == null ? void 0 : _layer_transform_rotation.z;
1734
+ if (rotationZ != null && !rotationZ.keyframed && typeof rotationZ.value === 'number') {
1735
+ return rotationZ.value;
1669
1736
  }
1670
1737
  return undefined;
1671
1738
  }
@@ -1676,99 +1743,50 @@ function getTimeContext(layer) {
1676
1743
  };
1677
1744
  }
1678
1745
 
1679
- function readGE2IRMetadata(ir, layer) {
1680
- if (ir.source !== 'ge') {
1681
- return undefined;
1682
- }
1683
- var metadata = layer.metadata;
1684
- if (metadata == null) {
1685
- return undefined;
1686
- }
1687
- var namespaced = metadata['ge2ir'];
1688
- if (isRecord$4(namespaced)) {
1689
- return namespaced;
1690
- }
1691
- var legacy = readLegacyGE2IRMetadata(metadata);
1692
- return Object.keys(legacy).length > 0 ? legacy : undefined;
1693
- }
1694
- function readLegacyGE2IRMetadata(metadata) {
1695
- var legacy = {};
1696
- var positionOverLifetime = metadata['positionOverLifetime'];
1697
- var rotationOverLifetime = metadata['rotationOverLifetime'];
1698
- var sizeOverLifetime = metadata['sizeOverLifetime'];
1699
- var colorOverLifetime = metadata['colorOverLifetime'];
1700
- var geComponentId = metadata['geComponentId'];
1701
- var geMask = metadata['geMask'];
1702
- var precompRefId = metadata['precompRefId'];
1703
- var precompContent = metadata['precompContent'];
1704
- if (isRecord$4(positionOverLifetime)) {
1705
- legacy.positionOverLifetime = positionOverLifetime;
1706
- }
1707
- if (isRecord$4(rotationOverLifetime)) {
1708
- legacy.rotationOverLifetime = rotationOverLifetime;
1709
- }
1710
- if (isRecord$4(sizeOverLifetime)) {
1711
- legacy.sizeOverLifetime = sizeOverLifetime;
1712
- }
1713
- if (isRecord$4(colorOverLifetime)) {
1714
- legacy.colorOverLifetime = colorOverLifetime;
1715
- }
1716
- if (typeof geComponentId === 'string') {
1717
- legacy.geComponentId = geComponentId;
1718
- }
1719
- if (isRecord$4(geMask)) {
1720
- legacy.geMask = geMask;
1721
- }
1722
- if (typeof precompRefId === 'string') {
1723
- legacy.precompRefId = precompRefId;
1724
- }
1725
- if (isRecord$4(precompContent)) {
1726
- legacy.precompContent = precompContent;
1727
- }
1728
- return legacy;
1729
- }
1730
- function readGE2IRCompositionMetadata(ir) {
1731
- if (ir.source !== 'ge') {
1732
- return undefined;
1733
- }
1734
- var metadata = ir.metadata;
1735
- if (metadata == null) {
1736
- return undefined;
1737
- }
1738
- var namespaced = metadata['ge2ir'];
1739
- return isRecord$4(namespaced) ? namespaced : undefined;
1740
- }
1741
- function isRecord$4(value) {
1742
- return value != null && (typeof value === "undefined" ? "undefined" : _type_of(value)) === 'object' && !Array.isArray(value);
1743
- }
1744
-
1745
1746
  function buildTransformAnimation(layer, ctx) {
1746
- var transform = layer.transform;
1747
- if (transform == null) {
1747
+ if (layer.transform == null) {
1748
1748
  return {};
1749
1749
  }
1750
- var timeContext = getTimeContext(layer);
1751
- // Passthrough: ge2ir stores raw animation data in metadata to bypass the
1750
+ // Passthrough: ge2ir stores raw animation data in specific.effects to bypass the
1752
1751
  // IR keyframe conversion pipeline. Raw GE data is used verbatim when
1753
1752
  // available; otherwise the standard IR → GE conversion runs.
1754
- var ge2irMetadata = readGE2IRMetadata(ctx.ir, layer);
1755
- var passthroughPosition = ge2irMetadata == null ? void 0 : ge2irMetadata.positionOverLifetime;
1756
- var passthroughRotation = ge2irMetadata == null ? void 0 : ge2irMetadata.rotationOverLifetime;
1757
- var passthroughSize = ge2irMetadata == null ? void 0 : ge2irMetadata.sizeOverLifetime;
1758
- var positionOverLifetime = passthroughPosition != null ? passthroughPosition : buildPositionAnim(transform, timeContext, ctx);
1759
- var rotationOverLifetime = passthroughRotation != null ? passthroughRotation : buildRotationAnim(transform, timeContext);
1760
- var sizeOverLifetime = passthroughSize != null ? passthroughSize : buildSizeAnim(transform, timeContext);
1753
+ var effectsSpecific = getEffectsLayerSpecific(ctx.ir, layer);
1754
+ var passthroughPosition = effectsSpecific == null ? void 0 : effectsSpecific.positionOverLifetime;
1755
+ var passthroughRotation = effectsSpecific == null ? void 0 : effectsSpecific.rotationOverLifetime;
1756
+ var passthroughSize = effectsSpecific == null ? void 0 : effectsSpecific.sizeOverLifetime;
1757
+ var normalized = buildNormalizedTransformAnimation(layer, ctx);
1758
+ var positionOverLifetime = passthroughPosition != null ? passthroughPosition : normalized.positionOverLifetime;
1759
+ var rotationOverLifetime = passthroughRotation != null ? passthroughRotation : normalized.rotationOverLifetime;
1760
+ var sizeOverLifetime = passthroughSize != null ? passthroughSize : normalized.sizeOverLifetime;
1761
1761
  return {
1762
1762
  positionOverLifetime: positionOverLifetime,
1763
1763
  rotationOverLifetime: rotationOverLifetime,
1764
1764
  sizeOverLifetime: sizeOverLifetime
1765
1765
  };
1766
1766
  }
1767
- function buildPositionAnim(transform, timeContext, ctx) {
1767
+ /**
1768
+ * Encode normalized IR transform channels as GE timeline payloads without
1769
+ * consulting composition-specific passthrough data. Sibling converters use
1770
+ * this when they need the canonical curve encoding for a preserved sub-scene.
1771
+ */ function buildNormalizedTransformAnimation(layer, ctx) {
1772
+ var transform = layer.transform;
1773
+ if (transform == null) {
1774
+ return {};
1775
+ }
1776
+ var timeContext = getTimeContext(layer);
1777
+ return {
1778
+ positionOverLifetime: buildPositionAnim(layer, transform, timeContext, ctx),
1779
+ rotationOverLifetime: buildRotationAnim(transform, timeContext),
1780
+ sizeOverLifetime: buildSizeAnim(transform, timeContext)
1781
+ };
1782
+ }
1783
+ function buildPositionAnim(layer, transform, timeContext, ctx) {
1768
1784
  var _posResult_curves_;
1785
+ var _posResult_pathPoints_;
1769
1786
  if (transform.position == null || !transform.position.keyframed) {
1770
1787
  return undefined;
1771
1788
  }
1789
+ assertContiguousPositionSegments(transform.position, layer);
1772
1790
  var posResult = irPositionKeyframesToGE(transform.position, timeContext);
1773
1791
  if (posResult == null) {
1774
1792
  return undefined;
@@ -1776,28 +1794,28 @@ function buildPositionAnim(transform, timeContext, ctx) {
1776
1794
  var pathPoints = [];
1777
1795
  var controlPoints = [];
1778
1796
  var useKeyframeMode = shouldUseKeyframeMode(transform.position);
1779
- for(var _iterator = _create_for_of_iterator_helper_loose(posResult.pathPoints), _step; !(_step = _iterator()).done;){
1780
- var pp = _step.value;
1781
- var point3d = convert2dTo3dPoint(pp[0][0], pp[0][1], 0, ctx.width, ctx.height);
1797
+ var firstPathPoint = (_posResult_pathPoints_ = posResult.pathPoints[0]) == null ? void 0 : _posResult_pathPoints_[0];
1798
+ if (firstPathPoint != null) {
1799
+ var point3d = convert2dTo3dPoint(firstPathPoint[0], firstPathPoint[1], 0, ctx.width, ctx.height);
1782
1800
  pathPoints.push(point3d);
1783
1801
  }
1784
- if (posResult.pathPoints.length > 0) {
1785
- var lastPp = posResult.pathPoints[posResult.pathPoints.length - 1];
1786
- var lastPoint3d = convert2dTo3dPoint(lastPp[1][0], lastPp[1][1], 0, ctx.width, ctx.height);
1787
- pathPoints.push(lastPoint3d);
1802
+ for(var _iterator = _create_for_of_iterator_helper_loose(posResult.pathPoints), _step; !(_step = _iterator()).done;){
1803
+ var pathPoint = _step.value;
1804
+ var endPoint3d = convert2dTo3dPoint(pathPoint[1][0], pathPoint[1][1], 0, ctx.width, ctx.height);
1805
+ pathPoints.push(endPoint3d);
1788
1806
  }
1789
1807
  for(var i = 0; i < posResult.controlPoints.length; i++){
1790
1808
  var cp = posResult.controlPoints[i];
1791
- var pp1 = posResult.pathPoints[i];
1809
+ var pp = posResult.pathPoints[i];
1792
1810
  if (useKeyframeMode) {
1793
- var dx = pp1[1][0] - pp1[0][0];
1794
- var dy = pp1[1][1] - pp1[0][1];
1795
- var cp1 = convert2dTo3dPoint(dx / 3 + pp1[0][0], dy / 3 + pp1[0][1], 0, ctx.width, ctx.height);
1796
- var cp2 = convert2dTo3dPoint(-dx / 3 + pp1[1][0], -dy / 3 + pp1[1][1], 0, ctx.width, ctx.height);
1811
+ var dx = pp[1][0] - pp[0][0];
1812
+ var dy = pp[1][1] - pp[0][1];
1813
+ var cp1 = convert2dTo3dPoint(dx / 3 + pp[0][0], dy / 3 + pp[0][1], 0, ctx.width, ctx.height);
1814
+ var cp2 = convert2dTo3dPoint(-dx / 3 + pp[1][0], -dy / 3 + pp[1][1], 0, ctx.width, ctx.height);
1797
1815
  controlPoints.push(cp1, cp2);
1798
1816
  } else {
1799
- var cp11 = convert2dTo3dPoint(cp[0][0] + pp1[0][0], cp[0][1] + pp1[0][1], 0, ctx.width, ctx.height);
1800
- var cp21 = convert2dTo3dPoint(cp[1][0] + pp1[1][0], cp[1][1] + pp1[1][1], 0, ctx.width, ctx.height);
1817
+ var cp11 = convert2dTo3dPoint(cp[0][0] + pp[0][0], cp[0][1] + pp[0][1], 0, ctx.width, ctx.height);
1818
+ var cp21 = convert2dTo3dPoint(cp[1][0] + pp[1][0], cp[1][1] + pp[1][1], 0, ctx.width, ctx.height);
1801
1819
  controlPoints.push(cp11, cp21);
1802
1820
  }
1803
1821
  }
@@ -1836,112 +1854,39 @@ function buildPositionAnim(transform, timeContext, ctx) {
1836
1854
  ]
1837
1855
  };
1838
1856
  }
1857
+ function assertContiguousPositionSegments(position, layer) {
1858
+ var _loop = function(index) {
1859
+ var segmentEnd = position.keyframes[index].endValue;
1860
+ var nextStart = position.keyframes[index + 1].value;
1861
+ if (segmentEnd == null) {
1862
+ return "continue";
1863
+ }
1864
+ var isContiguous = segmentEnd.every(function(value, dimension) {
1865
+ return Math.abs(value - nextStart[dimension]) <= 1e-6;
1866
+ });
1867
+ if (!isContiguous) {
1868
+ throw new Error("[ge-ir] Layer '" + layer.name + "' has a discontiguous position segment at keyframe " + index + "; " + 'GE path animation cannot preserve a segment endValue that differs from the next keyframe value.');
1869
+ }
1870
+ };
1871
+ for(var index = 0; index < position.keyframes.length - 1; index++)_loop(index);
1872
+ }
1839
1873
  function buildRotationAnim(transform, timeContext) {
1840
1874
  var result = {};
1841
- var hasZ = false;
1842
- var hasX = false;
1843
- var hasY = false;
1844
1875
  var rotation = transform.rotation;
1845
- if (rotation != null) {
1846
- if (rotation.keyframed) {
1847
- var _curves_, _curves_1, _curves_2;
1848
- var curves = irVectorKeyframesToGE(rotation, timeContext);
1849
- if (curves != null && axisHasRotationValue(rotation, 0) && ((_curves_ = curves[0]) == null ? void 0 : _curves_.length)) {
1850
- result['x'] = [
1851
- ValueType.BEZIER_CURVE,
1852
- curves[0]
1853
- ];
1854
- hasX = true;
1855
- }
1856
- if (curves != null && axisHasRotationValue(rotation, 1) && ((_curves_1 = curves[1]) == null ? void 0 : _curves_1.length)) {
1857
- result['y'] = [
1858
- ValueType.BEZIER_CURVE,
1859
- curves[1]
1860
- ];
1861
- hasY = true;
1862
- }
1863
- if (curves != null && axisHasRotationValue(rotation, 2) && ((_curves_2 = curves[2]) == null ? void 0 : _curves_2.length)) {
1864
- result['z'] = [
1865
- ValueType.BEZIER_CURVE,
1866
- curves[2]
1867
- ];
1868
- hasZ = true;
1869
- }
1870
- } else if (rotation.value != null) {
1871
- var _rotation_value = rotation.value, tmp = _rotation_value[0], x = tmp === void 0 ? 0 : tmp, tmp1 = _rotation_value[1], y = tmp1 === void 0 ? 0 : tmp1, tmp2 = _rotation_value[2], z = tmp2 === void 0 ? 0 : tmp2;
1872
- if (Math.abs(x) > 0.0001) {
1873
- result['x'] = [
1874
- ValueType.CONSTANT,
1875
- x
1876
- ];
1877
- hasX = true;
1878
- }
1879
- if (Math.abs(y) > 0.0001) {
1880
- result['y'] = [
1881
- ValueType.CONSTANT,
1882
- y
1883
- ];
1884
- hasY = true;
1885
- }
1886
- if (Math.abs(z) > 0.0001) {
1887
- result['z'] = [
1888
- ValueType.CONSTANT,
1889
- z
1890
- ];
1891
- hasZ = true;
1892
- }
1893
- }
1894
- } else {
1895
- var _separated_x, _separated_x1, _separated_y, _separated_y1, _separated_z, _separated_z1;
1896
- var separated = transform.separatedRotation;
1897
- if (separated == null ? void 0 : (_separated_x = separated.x) == null ? void 0 : _separated_x.keyframed) {
1898
- var curves1 = irScalarKeyframesToGE(separated.x, timeContext);
1899
- if (curves1 != null) {
1900
- result['x'] = [
1901
- ValueType.BEZIER_CURVE,
1902
- curves1[0]
1903
- ];
1904
- hasX = true;
1905
- }
1906
- } else if ((separated == null ? void 0 : (_separated_x1 = separated.x) == null ? void 0 : _separated_x1.value) != null && separated.x.value !== 0) {
1907
- result['x'] = [
1908
- ValueType.CONSTANT,
1909
- separated.x.value
1910
- ];
1911
- hasX = true;
1912
- }
1913
- if (separated == null ? void 0 : (_separated_y = separated.y) == null ? void 0 : _separated_y.keyframed) {
1914
- var curves2 = irScalarKeyframesToGE(separated.y, timeContext);
1915
- if (curves2 != null) {
1916
- result['y'] = [
1917
- ValueType.BEZIER_CURVE,
1918
- curves2[0]
1919
- ];
1920
- hasY = true;
1921
- }
1922
- } else if ((separated == null ? void 0 : (_separated_y1 = separated.y) == null ? void 0 : _separated_y1.value) != null && separated.y.value !== 0) {
1923
- result['y'] = [
1924
- ValueType.CONSTANT,
1925
- separated.y.value
1926
- ];
1927
- hasY = true;
1928
- }
1929
- if (separated == null ? void 0 : (_separated_z = separated.z) == null ? void 0 : _separated_z.keyframed) {
1930
- var curves3 = irScalarKeyframesToGE(separated.z, timeContext);
1931
- if (curves3 != null) {
1932
- result['z'] = [
1933
- ValueType.BEZIER_CURVE,
1934
- curves3[0]
1935
- ];
1936
- hasZ = true;
1937
- }
1938
- } else if ((separated == null ? void 0 : (_separated_z1 = separated.z) == null ? void 0 : _separated_z1.value) != null && separated.z.value !== 0) {
1939
- result['z'] = [
1940
- ValueType.CONSTANT,
1941
- separated.z.value
1942
- ];
1943
- hasZ = true;
1944
- }
1876
+ var x = buildRotationAxis(rotation == null ? void 0 : rotation.x, timeContext);
1877
+ var y = buildRotationAxis(rotation == null ? void 0 : rotation.y, timeContext);
1878
+ var z = buildRotationAxis(rotation == null ? void 0 : rotation.z, timeContext);
1879
+ var hasX = x != null;
1880
+ var hasY = y != null;
1881
+ var hasZ = z != null;
1882
+ if (x != null) {
1883
+ result['x'] = x;
1884
+ }
1885
+ if (y != null) {
1886
+ result['y'] = y;
1887
+ }
1888
+ if (z != null) {
1889
+ result['z'] = z;
1945
1890
  }
1946
1891
  if (!hasZ && !hasX && !hasY) {
1947
1892
  return undefined;
@@ -1953,25 +1898,46 @@ function buildRotationAnim(transform, timeContext) {
1953
1898
  }
1954
1899
  return result;
1955
1900
  }
1956
- function axisHasRotationValue(rotation, axis) {
1957
- var _ref;
1958
- var _rotation_keyframes;
1959
- if (!rotation.keyframed) {
1960
- var _ref1;
1961
- var _rotation_value;
1962
- return Math.abs((_ref1 = (_rotation_value = rotation.value) == null ? void 0 : _rotation_value[axis]) != null ? _ref1 : 0) > 0.0001;
1901
+ function buildRotationAxis(property, timeContext) {
1902
+ if (property == null) {
1903
+ return undefined;
1963
1904
  }
1964
- return (_ref = (_rotation_keyframes = rotation.keyframes) == null ? void 0 : _rotation_keyframes.some(function(keyframe) {
1965
- var _keyframe_value_axis, _ref;
1905
+ if (!property.keyframed) {
1906
+ return property.value !== 0 ? [
1907
+ ValueType.CONSTANT,
1908
+ property.value
1909
+ ] : undefined;
1910
+ }
1911
+ if (property.keyframes.length === 1) {
1912
+ var value = property.keyframes[0].value;
1913
+ return value !== 0 ? [
1914
+ ValueType.CONSTANT,
1915
+ value
1916
+ ] : undefined;
1917
+ }
1918
+ if (!axisHasRotationValue(property)) {
1919
+ return undefined;
1920
+ }
1921
+ var curves = irScalarKeyframesToGE(property, timeContext);
1922
+ return curves != null ? [
1923
+ ValueType.BEZIER_CURVE,
1924
+ curves[0]
1925
+ ] : undefined;
1926
+ }
1927
+ function axisHasRotationValue(rotation) {
1928
+ return rotation.keyframes.some(function(keyframe) {
1966
1929
  var _keyframe_endValue;
1967
- return Math.abs((_keyframe_value_axis = keyframe.value[axis]) != null ? _keyframe_value_axis : 0) > 0.0001 || Math.abs((_ref = (_keyframe_endValue = keyframe.endValue) == null ? void 0 : _keyframe_endValue[axis]) != null ? _ref : 0) > 0.0001;
1968
- })) != null ? _ref : false;
1930
+ return Math.abs(keyframe.value) > 0.0001 || Math.abs((_keyframe_endValue = keyframe.endValue) != null ? _keyframe_endValue : 0) > 0.0001;
1931
+ });
1969
1932
  }
1970
1933
  function buildSizeAnim(transform, timeContext) {
1971
1934
  if (transform.scale == null) {
1972
1935
  return undefined;
1973
1936
  }
1974
1937
  if (transform.scale.keyframed) {
1938
+ if (transform.scale.keyframes.length === 1) {
1939
+ return buildConstantSizeAnimation(transform.scale.keyframes[0].value);
1940
+ }
1975
1941
  var curves = irVectorKeyframesToGE(transform.scale, timeContext);
1976
1942
  if (curves != null && curves.length >= 2) {
1977
1943
  return _extends({
@@ -1993,33 +1959,37 @@ function buildSizeAnim(transform, timeContext) {
1993
1959
  }
1994
1960
  return undefined;
1995
1961
  }
1996
- var scale = transform.scale.value;
1997
- if (Array.isArray(scale) && (Math.abs(scale[0] - 1) > 0.0001 || Math.abs(scale[1] - 1) > 0.0001)) {
1998
- var _scale_;
1999
- return {
2000
- separateAxes: true,
2001
- x: [
2002
- ValueType.CONSTANT,
2003
- scale[0]
2004
- ],
2005
- y: [
2006
- ValueType.CONSTANT,
2007
- scale[1]
2008
- ],
2009
- z: [
2010
- ValueType.CONSTANT,
2011
- (_scale_ = scale[2]) != null ? _scale_ : 1
2012
- ]
2013
- };
1962
+ return buildConstantSizeAnimation(transform.scale.value);
1963
+ }
1964
+ function buildConstantSizeAnimation(scale) {
1965
+ if (Math.abs(scale[0] - 1) <= 0.0001 && Math.abs(scale[1] - 1) <= 0.0001) {
1966
+ return undefined;
2014
1967
  }
2015
- return undefined;
1968
+ return {
1969
+ separateAxes: true,
1970
+ x: [
1971
+ ValueType.CONSTANT,
1972
+ scale[0]
1973
+ ],
1974
+ y: [
1975
+ ValueType.CONSTANT,
1976
+ scale[1]
1977
+ ],
1978
+ z: [
1979
+ ValueType.CONSTANT,
1980
+ scale[2]
1981
+ ]
1982
+ };
2016
1983
  }
2017
1984
  function buildColorAnimation(layer, ir) {
2018
- var _readGE2IRMetadata;
2019
- var passthroughColor = (_readGE2IRMetadata = readGE2IRMetadata(ir, layer)) == null ? void 0 : _readGE2IRMetadata.colorOverLifetime;
1985
+ var _getEffectsLayerSpecific;
1986
+ var passthroughColor = (_getEffectsLayerSpecific = getEffectsLayerSpecific(ir, layer)) == null ? void 0 : _getEffectsLayerSpecific.colorOverLifetime;
2020
1987
  if (passthroughColor != null) {
2021
1988
  return passthroughColor;
2022
1989
  }
1990
+ return buildNormalizedColorAnimation(layer);
1991
+ }
1992
+ /** Encode normalized IR opacity as a GE color-over-lifetime payload. */ function buildNormalizedColorAnimation(layer) {
2023
1993
  var transform = layer.transform;
2024
1994
  if ((transform == null ? void 0 : transform.opacity) == null) {
2025
1995
  return undefined;
@@ -2034,6 +2004,15 @@ function buildColorAnimation(layer, ir) {
2034
2004
  };
2035
2005
  }
2036
2006
  if (transform.opacity.keyframed) {
2007
+ if (transform.opacity.keyframes.length === 1) {
2008
+ var opacityValue = transform.opacity.keyframes[0].value;
2009
+ return opacityValue !== 1 ? {
2010
+ opacity: [
2011
+ ValueType.CONSTANT,
2012
+ opacityValue
2013
+ ]
2014
+ } : undefined;
2015
+ }
2037
2016
  var curves = irScalarKeyframesToGE(transform.opacity, timeContext);
2038
2017
  if (curves != null) {
2039
2018
  return {
@@ -2045,12 +2024,12 @@ function buildColorAnimation(layer, ir) {
2045
2024
  }
2046
2025
  return undefined;
2047
2026
  }
2048
- var opacityValue = transform.opacity.value;
2049
- if (typeof opacityValue === 'number' && opacityValue !== 1) {
2027
+ var opacityValue1 = transform.opacity.value;
2028
+ if (typeof opacityValue1 === 'number' && opacityValue1 !== 1) {
2050
2029
  return {
2051
2030
  opacity: [
2052
2031
  ValueType.CONSTANT,
2053
- opacityValue
2032
+ opacityValue1
2054
2033
  ]
2055
2034
  };
2056
2035
  }
@@ -2132,18 +2111,20 @@ function mapBlendingMode(blendMode) {
2132
2111
  return GE_BLENDING_BY_IR_MODE[blendMode];
2133
2112
  }
2134
2113
 
2135
- var DEFAULT_DISPLAY_SIZE = {
2114
+ var DEFAULT_LOCAL_BOUNDS = {
2115
+ x: 0,
2116
+ y: 0,
2136
2117
  width: 1,
2137
2118
  height: 1
2138
2119
  };
2139
2120
  /**
2140
- * Read the producer-baked display size from an IR layer. When the producer
2141
- * did not resolve one, returns `fallback` (defaults to `{ 1, 1 }` — the
2142
- * value every existing call site was using inline).
2143
- */ function getLayerDisplaySize(layer, fallback) {
2144
- if (fallback === void 0) fallback = DEFAULT_DISPLAY_SIZE;
2145
- var _layer_displaySize;
2146
- return (_layer_displaySize = layer.displaySize) != null ? _layer_displaySize : fallback;
2121
+ * Read source-neutral local bounds from an IR layer. When the producer
2122
+ * did not resolve one, returns `fallback` (defaults to bounds at the origin
2123
+ * with a one-pixel width and height).
2124
+ */ function getLayerLocalBounds(layer, fallback) {
2125
+ if (fallback === void 0) fallback = DEFAULT_LOCAL_BOUNDS;
2126
+ var _layer_localBounds;
2127
+ return (_layer_localBounds = layer.localBounds) != null ? _layer_localBounds : fallback;
2147
2128
  }
2148
2129
 
2149
2130
  function buildComponent(layer, itemId, anchorScaleX, anchorScaleY, ctx) {
@@ -2160,21 +2141,20 @@ function buildComponent(layer, itemId, anchorScaleX, anchorScaleY, ctx) {
2160
2141
  }
2161
2142
  }
2162
2143
  /**
2163
- * Copy the raw GE mask object (from ge2ir passthrough metadata) onto a freshly
2144
+ * Copy the raw GE mask object (from specific.effects passthrough) onto a freshly
2164
2145
  * built component, as a top-level `mask` field matching GE's structure. For a
2165
2146
  * mask SOURCE this carries `{isMask:true, alphaMaskEnabled}`; for a TARGET it
2166
2147
  * carries `{isMask:false, reference, inverted}` where `reference.id` is still
2167
- * the stale GE1 component id — `applyGeMaskRemap` rewrites it in post-process.
2168
- * No-op for non-ge sources (Lottie masks go through the IRLayerNode.mask path).
2169
- */ function applyGeMask(ir, layer, component) {
2170
- var _readGE2IRMetadata;
2171
- var geMask = (_readGE2IRMetadata = readGE2IRMetadata(ir, layer)) == null ? void 0 : _readGE2IRMetadata.geMask;
2172
- if (geMask != null && component['mask'] == null) {
2173
- component['mask'] = _extends({}, geMask);
2148
+ * the stale GE1 component id — `applyGEMaskRemap` rewrites it in post-process.
2149
+ * No-op for non-GE sources (Lottie masks go through the IRLayerNode.mask path).
2150
+ */ function applyGEMask(ir, layer, component) {
2151
+ var _getEffectsLayerSpecific;
2152
+ var effectsMask = (_getEffectsLayerSpecific = getEffectsLayerSpecific(ir, layer)) == null ? void 0 : _getEffectsLayerSpecific.mask;
2153
+ if (effectsMask != null && component['mask'] == null) {
2154
+ component['mask'] = _extends({}, effectsMask);
2174
2155
  }
2175
2156
  }
2176
2157
  function buildSpriteComponent(layer, itemId, anchorScaleX, anchorScaleY, ctx) {
2177
- var _ref;
2178
2158
  var _layer_asset, _layer_asset1;
2179
2159
  var componentId = createId();
2180
2160
  var blending = mapBlendingMode(layer.blendMode);
@@ -2190,7 +2170,8 @@ function buildSpriteComponent(layer, itemId, anchorScaleX, anchorScaleY, ctx) {
2190
2170
  1
2191
2171
  ];
2192
2172
  var textureDataPath;
2193
- var textureUrl = (_ref = (_layer_asset = layer.asset) == null ? void 0 : _layer_asset.url) != null ? _ref : (_layer_asset1 = layer.asset) == null ? void 0 : _layer_asset1.refId;
2173
+ var assetUrl = (_layer_asset = layer.asset) == null ? void 0 : _layer_asset.url;
2174
+ var textureUrl = assetUrl != null && assetUrl !== '' ? assetUrl : (_layer_asset1 = layer.asset) == null ? void 0 : _layer_asset1.refId;
2194
2175
  if (textureUrl != null && textureUrl !== '' && textureUrl !== 'mask://placeholder') {
2195
2176
  textureDataPath = registerTexture(ctx.collector, textureUrl, ctx.textureCache);
2196
2177
  }
@@ -2216,7 +2197,7 @@ function buildSpriteComponent(layer, itemId, anchorScaleX, anchorScaleY, ctx) {
2216
2197
  roundNumber(anchorScaleY, 4)
2217
2198
  ];
2218
2199
  }
2219
- applyGeMask(ctx.ir, layer, component);
2200
+ applyGEMask(ctx.ir, layer, component);
2220
2201
  ctx.collector.components.push(component);
2221
2202
  return componentId;
2222
2203
  }
@@ -2237,7 +2218,7 @@ function buildTextComponent(layer, itemId, anchorScaleX, anchorScaleY, ctx) {
2237
2218
  var textWidth = (_textInfo_width = textInfo.width) != null ? _textInfo_width : estimateTextWidth(textInfo.content, textInfo.fontSize, textInfo.letterSpacing);
2238
2219
  var lineHeight = typeof textInfo.lineHeight === 'number' && textInfo.lineHeight > 0 ? textInfo.lineHeight : textInfo.fontSize;
2239
2220
  var textHeight = Math.max((_textInfo_height = textInfo.height) != null ? _textInfo_height : textInfo.fontSize * 1.2, lineHeight);
2240
- var _getLayerDisplaySize = getLayerDisplaySize(layer), layerWidth = _getLayerDisplaySize.width, layerHeight = _getLayerDisplaySize.height;
2221
+ var _getLayerLocalBounds = getLayerLocalBounds(layer), layerWidth = _getLayerLocalBounds.width, layerHeight = _getLayerLocalBounds.height;
2241
2222
  var textSizeW = roundNumber(ctx.ratio * layerWidth, 4);
2242
2223
  var textSizeH = roundNumber(ctx.ratio * layerHeight, 4);
2243
2224
  var component = {
@@ -2272,7 +2253,7 @@ function buildTextComponent(layer, itemId, anchorScaleX, anchorScaleY, ctx) {
2272
2253
  blending: blending
2273
2254
  })
2274
2255
  };
2275
- applyGeMask(ctx.ir, layer, component);
2256
+ applyGEMask(ctx.ir, layer, component);
2276
2257
  ctx.collector.components.push(component);
2277
2258
  return componentId;
2278
2259
  }
@@ -2294,7 +2275,7 @@ function buildShapeComponent(layer, itemId, ctx) {
2294
2275
  blending: blending
2295
2276
  })
2296
2277
  });
2297
- applyGeMask(ctx.ir, layer, component);
2278
+ applyGEMask(ctx.ir, layer, component);
2298
2279
  ctx.collector.components.push(component);
2299
2280
  return componentId;
2300
2281
  }
@@ -2302,21 +2283,35 @@ function buildShapeOptions(shapeData, ratio, ctx) {
2302
2283
  var options = {
2303
2284
  type: mapShapePrimitiveType(shapeData.primitiveType)
2304
2285
  };
2305
- if (shapeData.size != null) {
2306
- options['xSize'] = roundNumber(shapeData.size[0] * ratio, 4);
2307
- options['ySize'] = roundNumber(shapeData.size[1] * ratio, 4);
2308
- }
2309
- setScaledNumber(options, shapeData, 'xRadius', ratio);
2310
- setScaledNumber(options, shapeData, 'yRadius', ratio);
2311
- setScaledNumber(options, shapeData, 'width', ratio);
2312
- setScaledNumber(options, shapeData, 'height', ratio);
2313
- setScaledNumber(options, shapeData, 'innerRadius', ratio);
2314
- setScaledNumber(options, shapeData, 'outerRadius', ratio);
2315
- setScaledNumber(options, shapeData, 'radius', ratio);
2316
- setRawNumber(options, shapeData, 'roundness');
2317
- setRawNumber(options, shapeData, 'pointCount');
2318
- setRawNumber(options, shapeData, 'innerRoundness');
2319
- setRawNumber(options, shapeData, 'outerRoundness');
2286
+ switch(shapeData.primitiveType){
2287
+ case IRShapePrimitiveType.Custom:
2288
+ Object.assign(options, mapCustomPaths(shapeData.customPaths, ratio));
2289
+ break;
2290
+ case IRShapePrimitiveType.Rectangle:
2291
+ var _shapeData_roundness;
2292
+ options['width'] = roundNumber(shapeData.width * ratio, 4);
2293
+ options['height'] = roundNumber(shapeData.height * ratio, 4);
2294
+ options['roundness'] = (_shapeData_roundness = shapeData.roundness) != null ? _shapeData_roundness : 0;
2295
+ break;
2296
+ case IRShapePrimitiveType.Ellipse:
2297
+ options['xRadius'] = roundNumber(shapeData.xRadius * ratio, 4);
2298
+ options['yRadius'] = roundNumber(shapeData.yRadius * ratio, 4);
2299
+ break;
2300
+ case IRShapePrimitiveType.Polygon:
2301
+ var _shapeData_roundness1;
2302
+ options['pointCount'] = shapeData.pointCount;
2303
+ options['radius'] = roundNumber(shapeData.radius * ratio, 4);
2304
+ options['roundness'] = (_shapeData_roundness1 = shapeData.roundness) != null ? _shapeData_roundness1 : 0;
2305
+ break;
2306
+ case IRShapePrimitiveType.Star:
2307
+ var _shapeData_innerRoundness, _shapeData_outerRoundness;
2308
+ options['pointCount'] = shapeData.pointCount;
2309
+ options['innerRadius'] = roundNumber(shapeData.innerRadius * ratio, 4);
2310
+ options['outerRadius'] = roundNumber(shapeData.outerRadius * ratio, 4);
2311
+ options['innerRoundness'] = (_shapeData_innerRoundness = shapeData.innerRoundness) != null ? _shapeData_innerRoundness : 0;
2312
+ options['outerRoundness'] = (_shapeData_outerRoundness = shapeData.outerRoundness) != null ? _shapeData_outerRoundness : 0;
2313
+ break;
2314
+ }
2320
2315
  if (shapeData.fills != null && shapeData.fills.length > 0) {
2321
2316
  options['fills'] = shapeData.fills.map(function(fill) {
2322
2317
  return mapShapePaint(fill, ratio, ctx);
@@ -2344,18 +2339,6 @@ function buildShapeOptions(shapeData, ratio, ctx) {
2344
2339
  if (shapeData.strokeJoin != null) {
2345
2340
  options['strokeJoin'] = mapLineJoin(shapeData.strokeJoin);
2346
2341
  }
2347
- if (shapeData.fillRule != null) {
2348
- options['fillRule'] = mapShapeFillRule(shapeData.fillRule);
2349
- }
2350
- if (shapeData.customPaths != null && shapeData.customPaths.length > 0) {
2351
- Object.assign(options, mapCustomPaths(shapeData.customPaths, ratio));
2352
- }
2353
- if (shapeData.trimPath != null) {
2354
- options['trimPath'] = shapeData.trimPath;
2355
- }
2356
- if (shapeData.repeater != null) {
2357
- options['repeater'] = shapeData.repeater;
2358
- }
2359
2342
  return options;
2360
2343
  }
2361
2344
  var _obj;
@@ -2366,8 +2349,6 @@ var _obj2;
2366
2349
  var LINE_CAP_TO_GE = (_obj2 = {}, _obj2[IRLineCap.Butt] = 0, _obj2[IRLineCap.Round] = 1, _obj2[IRLineCap.Square] = 2, _obj2);
2367
2350
  var _obj3;
2368
2351
  var LINE_JOIN_TO_GE = (_obj3 = {}, _obj3[IRLineJoin.Round] = 0, _obj3[IRLineJoin.Bevel] = 1, _obj3[IRLineJoin.Miter] = 2, _obj3);
2369
- var _obj4;
2370
- var SHAPE_FILL_RULE_TO_GE = (_obj4 = {}, _obj4[IRShapeFillRule.NonZero] = 0, _obj4[IRShapeFillRule.EvenOdd] = 1, _obj4);
2371
2352
  function mapShapePrimitiveType(primitiveType) {
2372
2353
  return SHAPE_PRIMITIVE_TYPE_TO_GE[primitiveType];
2373
2354
  }
@@ -2379,10 +2360,6 @@ function mapLineJoin(lineJoin) {
2379
2360
  var _LINE_JOIN_TO_GE_lineJoin;
2380
2361
  return (_LINE_JOIN_TO_GE_lineJoin = LINE_JOIN_TO_GE[lineJoin]) != null ? _LINE_JOIN_TO_GE_lineJoin : 2;
2381
2362
  }
2382
- function mapShapeFillRule(fillRule) {
2383
- var _SHAPE_FILL_RULE_TO_GE_fillRule;
2384
- return (_SHAPE_FILL_RULE_TO_GE_fillRule = SHAPE_FILL_RULE_TO_GE[fillRule]) != null ? _SHAPE_FILL_RULE_TO_GE_fillRule : 0;
2385
- }
2386
2363
  function mapShapePaint(paint, ratio, ctx) {
2387
2364
  if (paint.type === IRFillType.Solid) {
2388
2365
  return {
@@ -2391,7 +2368,7 @@ function mapShapePaint(paint, ratio, ctx) {
2391
2368
  };
2392
2369
  }
2393
2370
  if (paint.type === IRFillType.LinearGradient || paint.type === IRFillType.RadialGradient || paint.type === IRFillType.AngularGradient) {
2394
- return _extends({
2371
+ return {
2395
2372
  type: FILL_TYPE_TO_GE[paint.type],
2396
2373
  gradientStops: [
2397
2374
  ValueType.GRADIENT_COLOR,
@@ -2399,13 +2376,12 @@ function mapShapePaint(paint, ratio, ctx) {
2399
2376
  ],
2400
2377
  startPoint: vec2ToGE(paint.gradientStart, ratio),
2401
2378
  endPoint: vec2ToGE(paint.gradientEnd, ratio)
2402
- }, paint.color != null && {
2403
- color: colorToGE(paint.color)
2404
- });
2379
+ };
2405
2380
  }
2406
2381
  if (paint.type === IRFillType.Texture) {
2407
- var _ref, _paint_texture_url, _paint_scaleMode;
2408
- var textureUrl = (_ref = (_paint_texture_url = paint.texture.url) != null ? _paint_texture_url : paint.texture.refId) != null ? _ref : paint.texture.id;
2382
+ var _paint_scaleMode;
2383
+ var textureAssetUrl = paint.texture.url;
2384
+ var textureUrl = textureAssetUrl != null && textureAssetUrl !== '' ? textureAssetUrl : paint.texture.refId;
2409
2385
  var texture = textureUrl != null && ctx != null ? registerTexture(ctx.collector, textureUrl, ctx.textureCache) : undefined;
2410
2386
  return _extends({
2411
2387
  type: FILL_TYPE_TO_GE[paint.type]
@@ -2443,8 +2419,8 @@ function mapCustomPaths(paths, ratio) {
2443
2419
  for(var index = 0; index < path.points.length; index++){
2444
2420
  var _ref, _ref1, _ref2, _ref3;
2445
2421
  var point = path.points[index];
2446
- var easingIn = path.easingIns[index];
2447
- var easingOut = path.easingOuts[index];
2422
+ var easingIn = path.inTangents[index];
2423
+ var easingOut = path.outTangents[index];
2448
2424
  var pointIndex = points.length;
2449
2425
  points.push({
2450
2426
  x: roundNumber(point.x * ratio, 4),
@@ -2465,7 +2441,7 @@ function mapCustomPaths(paths, ratio) {
2465
2441
  });
2466
2442
  }
2467
2443
  shapes.push({
2468
- close: path.close,
2444
+ close: path.closed,
2469
2445
  indexes: indexes
2470
2446
  });
2471
2447
  }
@@ -2476,100 +2452,54 @@ function mapCustomPaths(paths, ratio) {
2476
2452
  shapes: shapes
2477
2453
  };
2478
2454
  }
2479
- function setScaledNumber(options, shapeData, key, ratio) {
2480
- var value = shapeData[key];
2481
- if (typeof value === 'number') {
2482
- options[key] = roundNumber(value * ratio, 4);
2483
- }
2484
- }
2485
- function setRawNumber(options, shapeData, key) {
2486
- var value = shapeData[key];
2487
- if (typeof value === 'number') {
2488
- options[key] = value;
2489
- }
2490
- }
2491
2455
 
2492
- function getStaticAnchorBehavior(ir, layer) {
2493
- if (layer.transformSplit === true) {
2456
+ function planGELayerLowering(ir, layer) {
2457
+ var _getEffectsLayerSpecific;
2458
+ var sourceHierarchy = (_getEffectsLayerSpecific = getEffectsLayerSpecific(ir, layer)) == null ? void 0 : _getEffectsLayerSpecific.sourceHierarchy;
2459
+ if (sourceHierarchy === 'null-sprite-pair') {
2494
2460
  return {
2495
2461
  kind: 'split-null-sprite'
2496
2462
  };
2497
2463
  }
2498
- // B-class real semantic difference — kept as source branch because pre-baking
2499
- // anchorScale/anchorOffset into the IR would require pulling computeAnchorInfo's
2500
- // full input set (text anchorAdjust, static-rotation anchor rotation, display
2501
- // size fallback) into the producer, at high cost with no readability benefit.
2502
- // The split gate itself is already pre-baked via `transformSplit` above.
2503
- //
2504
- // ge source shares the lottie inline-anchor math: ge2ir reverses GE's
2505
- // `renderer.anchor` into IRLayerNode.transform.anchor (px) + displaySize, so
2506
- // computeAnchorInfo reproduces the exact anchorScale/anchorOffset that GE had.
2507
- // Without this, ge layers fall through to the centered [0.5,0.5] default and
2508
- // their authored anchor (e.g. a top-anchored sweep) is lost on round-trip.
2509
- // ge never reaches the lottieNeedsTransformSplit geometry check — its split
2510
- // decision is pre-baked via `transformSplit` by the ge2ir null+sprite merger.
2511
- if (ir.source === 'lottie' || ir.source === 'ge') {
2512
- if (ir.source === 'lottie' && lottieNeedsTransformSplit(layer)) {
2513
- return {
2514
- kind: 'split-null-sprite'
2515
- };
2516
- }
2517
- // The inline-anchor math needs non-zero dimensions to avoid divide-by-zero
2518
- // in `computeAnchorInfo`; substitute the legacy {1, 1} fallback at the call site
2519
- // so the field-level default stays neutral.
2520
- var _getLayerDisplaySize = getLayerDisplaySize(layer), width = _getLayerDisplaySize.width, height = _getLayerDisplaySize.height;
2521
- var info = computeAnchorInfo(layer, width, height);
2464
+ var bounds = getLayerLocalBounds(layer);
2465
+ if (sourceHierarchy !== 'single-item' && needsPivotContainer(layer, bounds)) {
2522
2466
  return {
2523
- kind: 'inline-anchor',
2524
- anchorScale: [
2525
- info.anchorScaleX,
2526
- info.anchorScaleY
2527
- ],
2528
- anchorOffset: [
2529
- info.anchorOffsetX,
2530
- info.anchorOffsetY
2531
- ]
2467
+ kind: 'split-null-sprite'
2532
2468
  };
2533
2469
  }
2534
- // dsl layers with off-center anchor have `transformSplit = true` set by the
2535
- // producer, caught by the guard at the top. Any layer reaching here is
2536
- // centered — return the default inline-anchor.
2470
+ var info = computeAnchorInfo(layer, bounds);
2537
2471
  return {
2538
2472
  kind: 'inline-anchor',
2539
2473
  anchorScale: [
2540
- 0.5,
2541
- 0.5
2474
+ info.anchorScaleX,
2475
+ info.anchorScaleY
2542
2476
  ],
2543
2477
  anchorOffset: [
2544
- 0,
2545
- 0
2478
+ info.anchorOffsetX,
2479
+ info.anchorOffsetY
2546
2480
  ]
2547
2481
  };
2548
2482
  }
2549
- function lottieNeedsTransformSplit(layer) {
2550
- var _transform_rotation, _sep_x, _sep_y, _sep_z, _transform_scale;
2483
+ function needsPivotContainer(layer, bounds) {
2484
+ var _rotation_x, _rotation_y, _rotation_z, _transform_scale;
2551
2485
  if (layer.type !== 'image' && layer.type !== 'solid') {
2552
2486
  return false;
2553
2487
  }
2554
- // Without a producer-baked displaySize we cannot decide off-center;
2555
- // fall through to inline-anchor (matches the pre-displaySize behavior
2556
- // where missing dimensions returned the {1, 1} default and never split).
2557
- var size = layer.displaySize;
2558
- if (size == null) {
2488
+ if (layer.localBounds == null) {
2559
2489
  return false;
2560
2490
  }
2561
- var width = size.width, height = size.height;
2491
+ var x = bounds.x, y = bounds.y, width = bounds.width, height = bounds.height;
2562
2492
  if (width <= 0 || height <= 0) {
2563
2493
  return false;
2564
2494
  }
2565
2495
  var anchor = getStaticAnchor(layer);
2566
- var anchorOffCenter = Math.abs(anchor[0] - width / 2) > 0.5 || Math.abs(anchor[1] - height / 2) > 0.5;
2496
+ var anchorOffCenter = Math.abs(anchor[0] - (x + width / 2)) > 0.5 || Math.abs(anchor[1] - (y + height / 2)) > 0.5;
2567
2497
  if (!anchorOffCenter) {
2568
2498
  return false;
2569
2499
  }
2570
2500
  var transform = layer.transform;
2571
- var sep = transform == null ? void 0 : transform.separatedRotation;
2572
- var hasRotationAnim = (transform == null ? void 0 : (_transform_rotation = transform.rotation) == null ? void 0 : _transform_rotation.keyframed) === true || (sep == null ? void 0 : (_sep_x = sep.x) == null ? void 0 : _sep_x.keyframed) === true || (sep == null ? void 0 : (_sep_y = sep.y) == null ? void 0 : _sep_y.keyframed) === true || (sep == null ? void 0 : (_sep_z = sep.z) == null ? void 0 : _sep_z.keyframed) === true;
2501
+ var rotation = transform == null ? void 0 : transform.rotation;
2502
+ var hasRotationAnim = (rotation == null ? void 0 : (_rotation_x = rotation.x) == null ? void 0 : _rotation_x.keyframed) === true || (rotation == null ? void 0 : (_rotation_y = rotation.y) == null ? void 0 : _rotation_y.keyframed) === true || (rotation == null ? void 0 : (_rotation_z = rotation.z) == null ? void 0 : _rotation_z.keyframed) === true;
2573
2503
  var hasScaleAnim = (transform == null ? void 0 : (_transform_scale = transform.scale) == null ? void 0 : _transform_scale.keyframed) === true;
2574
2504
  return hasRotationAnim || hasScaleAnim;
2575
2505
  }
@@ -2578,7 +2508,7 @@ function processNullLayer(layer, ctx) {
2578
2508
  var itemId = createId();
2579
2509
  var layerIndex = layer.index;
2580
2510
  ctx.layerToItemId[layerIndex] = itemId;
2581
- var _computeLayerTiming = computeLayerTiming(layer, ctx.frameRate), delay = _computeLayerTiming.delay, layerDuration = _computeLayerTiming.layerDuration;
2511
+ var _computeLayerTiming = computeLayerTiming(layer, ctx.frameRate, ctx.ir.inPoint), delay = _computeLayerTiming.delay, layerDuration = _computeLayerTiming.layerDuration;
2582
2512
  var animation = buildTransformAnimation(layer, ctx);
2583
2513
  var position = computeStaticPosition(layer, ctx);
2584
2514
  var objectBindingTrackId = buildTimelineTracks(ctx.collector, delay, layerDuration, 4, animation, 'null');
@@ -2588,7 +2518,7 @@ function processNullLayer(layer, ctx) {
2588
2518
  name: layer.name,
2589
2519
  duration: layerDuration,
2590
2520
  type: '3',
2591
- visible: layer.visibility.enabled && !layer.visibility.hidden,
2521
+ visible: layer.visible,
2592
2522
  endBehavior: 4,
2593
2523
  delay: delay,
2594
2524
  renderLevel: 'B+',
@@ -2624,19 +2554,19 @@ function processPrecompLayer(layer, ctx) {
2624
2554
  var itemId = createId();
2625
2555
  var layerIndex = layer.index;
2626
2556
  ctx.layerToItemId[layerIndex] = itemId;
2627
- var _computeLayerTiming = computeLayerTiming(layer, ctx.frameRate), delay = _computeLayerTiming.delay, layerDuration = _computeLayerTiming.layerDuration;
2557
+ var _computeLayerTiming = computeLayerTiming(layer, ctx.frameRate, ctx.ir.inPoint), delay = _computeLayerTiming.delay, layerDuration = _computeLayerTiming.layerDuration;
2628
2558
  var animation = buildTransformAnimation(layer, ctx);
2629
2559
  var position = computeStaticPosition(layer, ctx);
2630
2560
  var objectBindingTrackId = buildTimelineTracks(ctx.collector, delay, layerDuration, 4, animation, 'precomp');
2631
- var geMetadata = readGE2IRMetadata(ctx.ir, layer);
2632
- var content = buildPrecompContent(geMetadata == null ? void 0 : geMetadata.precompContent, geMetadata == null ? void 0 : geMetadata.precompRefId);
2561
+ var effectsSpecific = getEffectsLayerSpecific(ctx.ir, layer);
2562
+ var content = buildPrecompContent(effectsSpecific == null ? void 0 : effectsSpecific.precompContent, effectsSpecific == null ? void 0 : effectsSpecific.precompRefId);
2633
2563
  var item = {
2634
2564
  id: itemId,
2635
2565
  dataType: 'VFXItemData',
2636
2566
  name: layer.name,
2637
2567
  duration: layerDuration,
2638
2568
  type: '7',
2639
- visible: layer.visibility.enabled && !layer.visibility.hidden,
2569
+ visible: layer.visible,
2640
2570
  endBehavior: 4,
2641
2571
  delay: delay,
2642
2572
  renderLevel: 'B+',
@@ -2680,14 +2610,13 @@ function isRecord$3(value) {
2680
2610
  * the layer's logical position; the sprite/text child carries the visual
2681
2611
  * content offset by the anchor so the pivot is at the right point.
2682
2612
  *
2683
- * Used by both `processParentLayer` (layers referenced as `parent` by others)
2684
- * and `processTransformSplitLayer` (layers with `transformSplit=true`). The
2685
- * only knob is `childScale` — parent-layer callers pass a text-aware scale
2686
- * while transformSplit always uses `[1, 1, 1]`.
2687
- */ function emitNullSpritePair(layer, ctx, childScale) {
2613
+ * Used by both `processParentLayer` and GE lowering for layers that require a
2614
+ * pivot container.
2615
+ */ function emitNullSpritePair(layer, ctx) {
2688
2616
  var layerIndex = layer.index;
2689
- var _computeLayerTiming = computeLayerTiming(layer, ctx.frameRate), delay = _computeLayerTiming.delay, layerDuration = _computeLayerTiming.layerDuration;
2690
- var _getLayerDisplaySize = getLayerDisplaySize(layer), layerWidth = _getLayerDisplaySize.width, layerHeight = _getLayerDisplaySize.height;
2617
+ var _computeLayerTiming = computeLayerTiming(layer, ctx.frameRate, ctx.ir.inPoint), delay = _computeLayerTiming.delay, layerDuration = _computeLayerTiming.layerDuration;
2618
+ var bounds = getLayerLocalBounds(layer);
2619
+ var x = bounds.x, y = bounds.y, layerWidth = bounds.width, layerHeight = bounds.height;
2691
2620
  var nullItemId = createId();
2692
2621
  ctx.layerToItemId[layerIndex] = nullItemId;
2693
2622
  var nullAnimation = buildTransformAnimation(layer, ctx);
@@ -2732,9 +2661,11 @@ function isRecord$3(value) {
2732
2661
  });
2733
2662
  var childItemId = createId();
2734
2663
  var anchor = getStaticAnchor(layer);
2664
+ var centerX = x + layerWidth / 2;
2665
+ var centerY = y + layerHeight / 2;
2735
2666
  var childPosition = [
2736
- -ctx.ratio * (anchor[0] - layerWidth / 2),
2737
- ctx.ratio * (anchor[1] - layerHeight / 2),
2667
+ -ctx.ratio * (anchor[0] - centerX),
2668
+ ctx.ratio * (anchor[1] - centerY),
2738
2669
  0
2739
2670
  ];
2740
2671
  var childColorAnim = buildColorAnimation(layer, ctx.ir);
@@ -2747,13 +2678,22 @@ function isRecord$3(value) {
2747
2678
  }
2748
2679
  var scaledW = roundNumber(ctx.ratio * layerWidth, 4);
2749
2680
  var scaledH = roundNumber(ctx.ratio * layerHeight, 4);
2681
+ var childScale = layer.type === 'text' ? [
2682
+ scaledW,
2683
+ scaledH,
2684
+ 1
2685
+ ] : [
2686
+ 1,
2687
+ 1,
2688
+ 1
2689
+ ];
2750
2690
  var childItem = {
2751
2691
  id: childItemId,
2752
2692
  dataType: 'VFXItemData',
2753
2693
  name: layer.name,
2754
2694
  duration: layerDuration,
2755
2695
  type: getGEItemTypeCode(layer.type),
2756
- visible: layer.visibility.enabled && !layer.visibility.hidden,
2696
+ visible: layer.visible,
2757
2697
  endBehavior: 0,
2758
2698
  delay: delay,
2759
2699
  renderLevel: 'B+',
@@ -2784,35 +2724,24 @@ function isRecord$3(value) {
2784
2724
  });
2785
2725
  }
2786
2726
  function processParentLayer(layer, ctx) {
2787
- var _getLayerDisplaySize = getLayerDisplaySize(layer), layerWidth = _getLayerDisplaySize.width, layerHeight = _getLayerDisplaySize.height;
2788
- var scaledW = roundNumber(ctx.ratio * layerWidth, 4);
2789
- var scaledH = roundNumber(ctx.ratio * layerHeight, 4);
2790
- var childScale = layer.type === 'text' ? [
2791
- scaledW,
2792
- scaledH,
2793
- 1
2794
- ] : [
2795
- 1,
2796
- 1,
2797
- 1
2798
- ];
2799
- emitNullSpritePair(layer, ctx, childScale);
2727
+ emitNullSpritePair(layer, ctx);
2800
2728
  }
2801
2729
  function processVisualLayer(layer, ctx) {
2802
- if (layer.type === 'shape' && layer.shapes != null && layer.shapes.length > 0) {
2803
- processShapeLayerMultiItem(layer, ctx);
2730
+ var strategy = planGELayerLowering(ctx.ir, layer);
2731
+ if (strategy.kind === 'split-null-sprite') {
2732
+ processPivotContainerLayer(layer, ctx);
2804
2733
  return;
2805
2734
  }
2806
- var strategy = getStaticAnchorBehavior(ctx.ir, layer);
2807
- if (strategy.kind === 'split-null-sprite') {
2808
- processTransformSplitLayer(layer, ctx);
2735
+ if (layer.type === 'shape' && layer.shapes != null && layer.shapes.length > 0) {
2736
+ processShapeLayerMultiItem(layer, ctx);
2809
2737
  return;
2810
2738
  }
2811
2739
  var itemId = createId();
2812
2740
  var layerIndex = layer.index;
2813
2741
  ctx.layerToItemId[layerIndex] = itemId;
2814
- var _computeLayerTiming = computeLayerTiming(layer, ctx.frameRate), delay = _computeLayerTiming.delay, layerDuration = _computeLayerTiming.layerDuration;
2815
- var _getLayerDisplaySize = getLayerDisplaySize(layer), layerWidth = _getLayerDisplaySize.width, layerHeight = _getLayerDisplaySize.height;
2742
+ var _computeLayerTiming = computeLayerTiming(layer, ctx.frameRate, ctx.ir.inPoint), delay = _computeLayerTiming.delay, layerDuration = _computeLayerTiming.layerDuration;
2743
+ var bounds = getLayerLocalBounds(layer);
2744
+ var layerWidth = bounds.width, layerHeight = bounds.height;
2816
2745
  var scaledW = ctx.ratio * layerWidth;
2817
2746
  var scaledH = ctx.ratio * layerHeight;
2818
2747
  var _strategy_anchorScale = strategy.anchorScale, anchorScaleX = _strategy_anchorScale[0], anchorScaleY = _strategy_anchorScale[1];
@@ -2823,7 +2752,7 @@ function processVisualLayer(layer, ctx) {
2823
2752
  colorOverLifetime: colorAnimation
2824
2753
  });
2825
2754
  var rawPosition = computeStaticPosition(layer, ctx, anchorOffsetX, anchorOffsetY);
2826
- var position = applyScalePositionAdjust(rawPosition, layer, layerWidth, layerHeight, ctx.ratio);
2755
+ var position = applyScalePositionAdjust(rawPosition, layer, bounds, ctx.ratio);
2827
2756
  var itemType = getGEItemType(layer.type);
2828
2757
  var objectBindingTrackId = buildTimelineTracks(ctx.collector, delay, layerDuration, 0, fullAnimation, itemType);
2829
2758
  var componentId = buildComponent(layer, itemId, anchorScaleX, anchorScaleY, ctx);
@@ -2850,7 +2779,7 @@ function processVisualLayer(layer, ctx) {
2850
2779
  name: layer.name,
2851
2780
  duration: layerDuration,
2852
2781
  type: getGEItemTypeCode(layer.type),
2853
- visible: layer.visibility.enabled && !layer.visibility.hidden,
2782
+ visible: layer.visible,
2854
2783
  endBehavior: 0,
2855
2784
  delay: delay,
2856
2785
  renderLevel: 'B+',
@@ -2879,19 +2808,24 @@ function processVisualLayer(layer, ctx) {
2879
2808
  layerIndex: layerIndex
2880
2809
  });
2881
2810
  }
2882
- function processTransformSplitLayer(layer, ctx) {
2883
- emitNullSpritePair(layer, ctx, [
2884
- 1,
2885
- 1,
2886
- 1
2887
- ]);
2811
+ function processPivotContainerLayer(layer, ctx) {
2812
+ emitNullSpritePair(layer, ctx);
2888
2813
  }
2889
2814
  function processShapeLayerMultiItem(layer, ctx) {
2890
2815
  var layerIndex = layer.index;
2891
- var _computeLayerTiming = computeLayerTiming(layer, ctx.frameRate), delay = _computeLayerTiming.delay, layerDuration = _computeLayerTiming.layerDuration;
2892
- var _getLayerDisplaySize = getLayerDisplaySize(layer), layerWidth = _getLayerDisplaySize.width, layerHeight = _getLayerDisplaySize.height;
2816
+ var _computeLayerTiming = computeLayerTiming(layer, ctx.frameRate, ctx.ir.inPoint), delay = _computeLayerTiming.delay, layerDuration = _computeLayerTiming.layerDuration;
2817
+ // A GE Shape item commonly has no transform.size, so ge2ir cannot recover
2818
+ // layer-local bounds. Treat missing bounds as empty here: the generic 1×1
2819
+ // fallback would invent a half-pixel anchor offset and shift every rebuilt
2820
+ // shape (notably mask sources) away from its authored position.
2821
+ var bounds = getLayerLocalBounds(layer, {
2822
+ x: 0,
2823
+ y: 0,
2824
+ width: 0,
2825
+ height: 0
2826
+ });
2893
2827
  var anchor = getStaticAnchor(layer);
2894
- var _computeAnchorInfo = computeAnchorInfo(layer, layerWidth, layerHeight), anchorOffsetX = _computeAnchorInfo.anchorOffsetX, anchorOffsetY = _computeAnchorInfo.anchorOffsetY;
2828
+ var _computeAnchorInfo = computeAnchorInfo(layer, bounds), anchorOffsetX = _computeAnchorInfo.anchorOffsetX, anchorOffsetY = _computeAnchorInfo.anchorOffsetY;
2895
2829
  var animation = buildTransformAnimation(layer, ctx);
2896
2830
  var colorAnimation = buildColorAnimation(layer, ctx.ir);
2897
2831
  var fullAnimation = _extends({}, animation, {
@@ -2926,7 +2860,7 @@ function processShapeLayerMultiItem(layer, ctx) {
2926
2860
  });
2927
2861
  // Mask source shape (isMask:true) — copy onto the first shape item only.
2928
2862
  if (shapeIdx === 0) {
2929
- applyGeMask(ctx.ir, layer, component);
2863
+ applyGEMask(ctx.ir, layer, component);
2930
2864
  }
2931
2865
  ctx.collector.components.push(component);
2932
2866
  if (shapeIdx === 0) {
@@ -2939,7 +2873,7 @@ function processShapeLayerMultiItem(layer, ctx) {
2939
2873
  name: shapeIdx === 0 ? layer.name : layer.name + "_shape_" + shapeIdx,
2940
2874
  duration: layerDuration,
2941
2875
  type: 'shape',
2942
- visible: layer.visibility.enabled && !layer.visibility.hidden,
2876
+ visible: layer.visible,
2943
2877
  endBehavior: 0,
2944
2878
  delay: delay,
2945
2879
  renderLevel: 'B+',
@@ -3072,16 +3006,20 @@ function deriveDuration(ir) {
3072
3006
  }
3073
3007
  /**
3074
3008
  * Map IR composition end behavior to GE's numeric end-behavior.
3075
- * Lottie IR currently leaves this unset; treat undefined as Loop (5) so the
3076
- * legacy lottie behavior is preserved. dsl2ge writes either Loop or Stop.
3009
+ * Undefined defaults to Restart (5) for backward compatibility. DSL's boolean
3010
+ * loop flag maps to either Restart or Destroy at the adapter boundary.
3077
3011
  */ function mapCompositionEndBehavior(eb) {
3078
- if (eb == null || eb === IREndBehavior.Loop) {
3079
- return 5;
3080
- }
3081
- if (eb === IREndBehavior.Hold) {
3082
- return 4;
3012
+ switch(eb){
3013
+ case undefined:
3014
+ case IREndBehavior.Restart:
3015
+ return 5;
3016
+ case IREndBehavior.Forward:
3017
+ return 2;
3018
+ case IREndBehavior.Freeze:
3019
+ return 4;
3020
+ case IREndBehavior.Destroy:
3021
+ return 0;
3083
3022
  }
3084
- return 0;
3085
3023
  }
3086
3024
  function colorToRoundedRgba(color) {
3087
3025
  return [
@@ -3152,13 +3090,10 @@ function applyMasks(ir, collector, processedItems, ratio, width, height, frameRa
3152
3090
  continue;
3153
3091
  }
3154
3092
  var layerComponentId = lookup.getComponentId(layer.index);
3155
- buildMaskForLayer(layer, layerItemId, layerComponentId, collector, processedItems, ratio, width, height, frameRate, lookup, maskSlotMeta, componentById, maskData);
3093
+ buildMaskForLayer(layer, layerItemId, layerComponentId, collector, processedItems, ratio, width, height, frameRate, ir.inPoint, lookup, maskSlotMeta, componentById, maskData);
3156
3094
  }
3157
3095
  }
3158
- function getPrimaryPath(mask) {
3159
- return mask.paths[0];
3160
- }
3161
- function buildMaskForLayer(layer, layerItemId, layerComponentId, collector, processedItems, ratio, width, height, frameRate, lookup, maskSlotMeta, componentById, maskData) {
3096
+ function buildMaskForLayer(layer, layerItemId, layerComponentId, collector, processedItems, ratio, width, height, frameRate, compositionInPoint, lookup, maskSlotMeta, componentById, maskData) {
3162
3097
  var mask = maskData != null ? maskData : layer.mask;
3163
3098
  var maskItemId = createId();
3164
3099
  var maskComponentId = createId();
@@ -3166,7 +3101,7 @@ function buildMaskForLayer(layer, layerItemId, layerComponentId, collector, proc
3166
3101
  var _getStaticAnchor = getStaticAnchor(layer), anchorX = _getStaticAnchor[0], anchorY = _getStaticAnchor[1];
3167
3102
  var maskPosX = roundNumber((maskBounds.cx - anchorX) * ratio, 4);
3168
3103
  var maskPosY = roundNumber(-(maskBounds.cy - anchorY) * ratio, 4);
3169
- var timing = computeLayerTiming(layer, frameRate);
3104
+ var timing = computeLayerTiming(layer, frameRate, compositionInPoint);
3170
3105
  var delay = timing.delay;
3171
3106
  var maskDuration = timing.layerDuration;
3172
3107
  var layerIsParent = lookup.isParentLayer(layer.index) || layer.type === 'null' || layer.type === 'precomp';
@@ -3358,8 +3293,8 @@ function computePointBounds(points) {
3358
3293
  };
3359
3294
  }
3360
3295
  function isMaskExactCrop(mask, layer, width, height, lookup) {
3361
- var path = getPrimaryPath(mask);
3362
- if (path == null || path.points.length === 0) {
3296
+ var path = mask.path;
3297
+ if (path.points.length === 0) {
3363
3298
  return true;
3364
3299
  }
3365
3300
  var _computePointBounds = computePointBounds(path.points), minX = _computePointBounds.minX, minY = _computePointBounds.minY, maxX = _computePointBounds.maxX, maxY = _computePointBounds.maxY;
@@ -3384,8 +3319,8 @@ function isMaskExactCrop(mask, layer, width, height, lookup) {
3384
3319
  return false;
3385
3320
  }
3386
3321
  function getMaskBounds(mask) {
3387
- var path = getPrimaryPath(mask);
3388
- if (path == null || path.points.length === 0) {
3322
+ var path = mask.path;
3323
+ if (path.points.length === 0) {
3389
3324
  return {
3390
3325
  cx: 0,
3391
3326
  cy: 0,
@@ -3402,47 +3337,41 @@ function getMaskBounds(mask) {
3402
3337
  };
3403
3338
  }
3404
3339
  function isSimpleRectMask(mask) {
3405
- var path = getPrimaryPath(mask);
3406
- if (path == null) {
3407
- return false;
3408
- }
3409
- return path.points.length === 4 && path.easingIns.every(function(e) {
3410
- return Math.abs(e.x) < 0.01 && Math.abs(e.y) < 0.01;
3411
- }) && path.easingOuts.every(function(e) {
3412
- return Math.abs(e.x) < 0.01 && Math.abs(e.y) < 0.01;
3340
+ var path = mask.path;
3341
+ return path.points.length === 4 && path.inTangents.every(function(tangent) {
3342
+ return Math.abs(tangent.x) < 0.01 && Math.abs(tangent.y) < 0.01;
3343
+ }) && path.outTangents.every(function(tangent) {
3344
+ return Math.abs(tangent.x) < 0.01 && Math.abs(tangent.y) < 0.01;
3413
3345
  });
3414
3346
  }
3415
3347
  function buildPolygonMaskComponent(maskComponentId, maskItemId, mask, bounds, ratio) {
3416
- var _ref;
3417
- var path = getPrimaryPath(mask);
3348
+ var path = mask.path;
3418
3349
  var customPoints = [];
3419
3350
  var customEasingIns = [];
3420
3351
  var customEasingOuts = [];
3421
3352
  var customIndexes = [];
3422
- if (path != null) {
3423
- for(var idx = 0; idx < path.points.length; idx++){
3424
- var _ref1, _ref2, _ref3, _ref4;
3425
- var pt = path.points[idx];
3426
- var ei = path.easingIns[idx];
3427
- var eo = path.easingOuts[idx];
3428
- customPoints.push({
3429
- x: roundNumber((pt.x - bounds.cx) * ratio, 4),
3430
- y: roundNumber(-(pt.y - bounds.cy) * ratio, 4)
3431
- });
3432
- customEasingIns.push({
3433
- x: roundNumber(((_ref1 = ei == null ? void 0 : ei.x) != null ? _ref1 : 0) * ratio, 4),
3434
- y: roundNumber(-((_ref2 = ei == null ? void 0 : ei.y) != null ? _ref2 : 0) * ratio, 4)
3435
- });
3436
- customEasingOuts.push({
3437
- x: roundNumber(((_ref3 = eo == null ? void 0 : eo.x) != null ? _ref3 : 0) * ratio, 4),
3438
- y: roundNumber(-((_ref4 = eo == null ? void 0 : eo.y) != null ? _ref4 : 0) * ratio, 4)
3439
- });
3440
- customIndexes.push({
3441
- point: idx,
3442
- easingIn: idx,
3443
- easingOut: idx
3444
- });
3445
- }
3353
+ for(var idx = 0; idx < path.points.length; idx++){
3354
+ var _ref, _ref1, _ref2, _ref3;
3355
+ var pt = path.points[idx];
3356
+ var ei = path.inTangents[idx];
3357
+ var eo = path.outTangents[idx];
3358
+ customPoints.push({
3359
+ x: roundNumber((pt.x - bounds.cx) * ratio, 4),
3360
+ y: roundNumber(-(pt.y - bounds.cy) * ratio, 4)
3361
+ });
3362
+ customEasingIns.push({
3363
+ x: roundNumber(((_ref = ei == null ? void 0 : ei.x) != null ? _ref : 0) * ratio, 4),
3364
+ y: roundNumber(-((_ref1 = ei == null ? void 0 : ei.y) != null ? _ref1 : 0) * ratio, 4)
3365
+ });
3366
+ customEasingOuts.push({
3367
+ x: roundNumber(((_ref2 = eo == null ? void 0 : eo.x) != null ? _ref2 : 0) * ratio, 4),
3368
+ y: roundNumber(-((_ref3 = eo == null ? void 0 : eo.y) != null ? _ref3 : 0) * ratio, 4)
3369
+ });
3370
+ customIndexes.push({
3371
+ point: idx,
3372
+ easingIn: idx,
3373
+ easingOut: idx
3374
+ });
3446
3375
  }
3447
3376
  return {
3448
3377
  id: maskComponentId,
@@ -3454,7 +3383,7 @@ function buildPolygonMaskComponent(maskComponentId, maskItemId, mask, bounds, ra
3454
3383
  easingOuts: customEasingOuts,
3455
3384
  shapes: [
3456
3385
  {
3457
- close: (_ref = path == null ? void 0 : path.close) != null ? _ref : true,
3386
+ close: path.closed,
3458
3387
  indexes: customIndexes
3459
3388
  }
3460
3389
  ],
@@ -3487,10 +3416,10 @@ function buildPolygonMaskComponent(maskComponentId, maskItemId, mask, bounds, ra
3487
3416
  * Remap GE mask references after ge2ir round-trip.
3488
3417
  *
3489
3418
  * ge2ir carries each masked component's raw `mask` object verbatim (via
3490
- * `IRLayerNode.metadata.ge2ir.geMask`), but `mask.reference.id` still points at the
3419
+ * `IRLayerNode.specific.effects.mask`), but `mask.reference.id` still points at the
3491
3420
  * ORIGINAL (GE1) mask-source component id, which ir2ge has regenerated. This
3492
3421
  * pass rewrites every target's `reference.id` from the stale GE1 id to the
3493
- * freshly-minted GE2 component id, using the `geComponentId → newId` map built
3422
+ * freshly-minted GE2 component id, using the `GEComponentId → newId` map built
3494
3423
  * during the layer walk.
3495
3424
  *
3496
3425
  * Mirrors `applyParenting`: an IR-side reference (here the stale id stored in
@@ -3498,7 +3427,7 @@ function buildPolygonMaskComponent(maskComponentId, maskItemId, mask, bounds, ra
3498
3427
  * no `reference` and are skipped. Dangling references (source not regenerated —
3499
3428
  * e.g. dropped by the null+sprite merger) have their `mask` removed so the GE
3500
3429
  * runtime doesn't choke on an id that resolves to nothing.
3501
- */ function applyGeMaskRemap(collector, geComponentIdToNewId) {
3430
+ */ function applyGEMaskRemap(collector, GEComponentIdToNewId) {
3502
3431
  for(var _iterator = _create_for_of_iterator_helper_loose(collector.components), _step; !(_step = _iterator()).done;){
3503
3432
  var component = _step.value;
3504
3433
  var mask = component['mask'];
@@ -3510,7 +3439,7 @@ function buildPolygonMaskComponent(maskComponentId, maskItemId, mask, bounds, ra
3510
3439
  if (oldId == null) {
3511
3440
  continue;
3512
3441
  }
3513
- var newId = geComponentIdToNewId[oldId];
3442
+ var newId = GEComponentIdToNewId[oldId];
3514
3443
  if (newId == null) {
3515
3444
  // Dangling: the referenced source was not regenerated. Drop the mask so
3516
3445
  // the target renders unmasked rather than referencing a nonexistent id.
@@ -3525,15 +3454,15 @@ function buildPolygonMaskComponent(maskComponentId, maskItemId, mask, bounds, ra
3525
3454
  * Normalize child visibility delays so children never appear before their parent.
3526
3455
  *
3527
3456
  * Topologically sorts items (parent-first) so by the time we process any child
3528
- * its parent's delay/duration are already finalized. For transform-only
3529
- * parent-link children, we instead advance the parent's delay/duration so the
3457
+ * its parent's delay/duration are already finalized. For children with an
3458
+ * independent lifetime, we instead advance the parent's delay/duration so the
3530
3459
  * child can render from its own start time (GE requires the parent to be active
3531
3460
  * for transform inheritance, but the parent-link relationship does not propagate
3532
3461
  * visibility). Only the ActivationTrack clip is updated; TransformTrack
3533
3462
  * keyframes are normalized to the original time range and would hold their
3534
3463
  * first frame value if the track started earlier.
3535
3464
  */ function normalizeChildVisibilityByParentDelays(param) {
3536
- var items = param.items, miscs = param.miscs, processedItems = param.processedItems, _param_transformOnlyParentLinkItemIds = param.transformOnlyParentLinkItemIds, transformOnlyParentLinkItemIds = _param_transformOnlyParentLinkItemIds === void 0 ? new Set() : _param_transformOnlyParentLinkItemIds, roundNumber = param.roundNumber;
3465
+ var items = param.items, miscs = param.miscs, processedItems = param.processedItems, _param_independentLifetimeChildItemIds = param.independentLifetimeChildItemIds, independentLifetimeChildItemIds = _param_independentLifetimeChildItemIds === void 0 ? new Set() : _param_independentLifetimeChildItemIds, roundNumber = param.roundNumber;
3537
3466
  var itemById = new Map();
3538
3467
  for(var _iterator = _create_for_of_iterator_helper_loose(items), _step; !(_step = _iterator()).done;){
3539
3468
  var item = _step.value;
@@ -3549,9 +3478,9 @@ function buildPolygonMaskComponent(maskComponentId, maskItemId, mask, bounds, ra
3549
3478
  var misc = _step2.value;
3550
3479
  miscById.set(misc['id'], misc);
3551
3480
  }
3552
- // Phase 1: Advance transform-only parent-link parents so their children can
3481
+ // Phase 1: Advance transform parents so independent-lifetime children can
3553
3482
  // render from the child's own start time.
3554
- advanceTransformOnlyParentLinkParents(items, itemById, processedByItemId, miscById, transformOnlyParentLinkItemIds, roundNumber);
3483
+ advanceIndependentLifetimeParents(items, itemById, processedByItemId, miscById, independentLifetimeChildItemIds, roundNumber);
3555
3484
  // Phase 2: Normalize precomp child visibility (unchanged).
3556
3485
  var effectiveDelayCache = new Map();
3557
3486
  function getEffectiveDelay(itemId, visited) {
@@ -3613,8 +3542,8 @@ function buildPolygonMaskComponent(maskComponentId, maskItemId, mask, bounds, ra
3613
3542
  syncTimelineClips(itemId, effectiveDelay, newDuration, processedByItemId, miscById, roundNumber);
3614
3543
  }
3615
3544
  }
3616
- function advanceTransformOnlyParentLinkParents(items, itemById, processedByItemId, miscById, transformOnlyParentLinkItemIds, roundNumber) {
3617
- if (transformOnlyParentLinkItemIds.size === 0) {
3545
+ function advanceIndependentLifetimeParents(items, itemById, processedByItemId, miscById, independentLifetimeChildItemIds, roundNumber) {
3546
+ if (independentLifetimeChildItemIds.size === 0) {
3618
3547
  return;
3619
3548
  }
3620
3549
  // Collect the maximum advance (head) and extension (tail) needed per parent.
@@ -3623,7 +3552,7 @@ function advanceTransformOnlyParentLinkParents(items, itemById, processedByItemI
3623
3552
  for(var _iterator = _create_for_of_iterator_helper_loose(items), _step; !(_step = _iterator()).done;){
3624
3553
  var item = _step.value;
3625
3554
  var _item_delay, _item_duration, _parentItem_delay, _parentItem_duration;
3626
- if (!transformOnlyParentLinkItemIds.has(item['id'])) {
3555
+ if (!independentLifetimeChildItemIds.has(item['id'])) {
3627
3556
  continue;
3628
3557
  }
3629
3558
  var parentId = item['parentId'];
@@ -3759,18 +3688,18 @@ function syncTimelineClips(itemId, delay, duration, processedByItemId, miscById,
3759
3688
  */ function applyPostProcess(ir, state) {
3760
3689
  applyParentingStep(ir, state);
3761
3690
  applySharedMaskPostProcess(ir, state);
3762
- applyGeMaskRemapStep(ir, state);
3691
+ applyGEMaskRemapStep(ir, state);
3763
3692
  applyVisibilityStep(ir, state);
3764
3693
  }
3765
- function applyGeMaskRemapStep(ir, state) {
3766
- // Only ge-sourced compositions carry passthrough mask references that need
3694
+ function applyGEMaskRemapStep(ir, state) {
3695
+ // Only Effects-sourced compositions carry passthrough mask references that need
3767
3696
  // remapping. Lottie/dsl masks are built with correct ids by applyMasks and
3768
- // must not be touched (their reference ids are absent from the ge map, so a
3697
+ // must not be touched (their reference ids are absent from the GE map, so a
3769
3698
  // blind remap would wrongly delete them).
3770
- if (ir.source !== 'ge' || state.geComponentIdToNewId == null) {
3699
+ if (ir.source !== 'effects' || state.GEComponentIdToNewId == null) {
3771
3700
  return;
3772
3701
  }
3773
- applyGeMaskRemap(state.collector, state.geComponentIdToNewId);
3702
+ applyGEMaskRemap(state.collector, state.GEComponentIdToNewId);
3774
3703
  }
3775
3704
  function applySharedMaskPostProcess(ir, state) {
3776
3705
  if (state.maskLookup == null || state.ratio == null || state.width == null || state.height == null || state.frameRate == null) {
@@ -3790,13 +3719,13 @@ function applyVisibilityStep(ir, state) {
3790
3719
  if (layerToItemId == null) {
3791
3720
  return;
3792
3721
  }
3793
- var transformOnlyParentLinkItemIds = new Set();
3722
+ var independentLifetimeChildItemIds = new Set();
3794
3723
  for(var _iterator = _create_for_of_iterator_helper_loose(ir.layers), _step; !(_step = _iterator()).done;){
3795
3724
  var layer = _step.value;
3796
- if (isTransformOnlyParentLink(layer)) {
3725
+ if (hasIndependentParentLifetime(layer)) {
3797
3726
  var itemId = layerToItemId[layer.index];
3798
3727
  if (itemId != null) {
3799
- transformOnlyParentLinkItemIds.add(itemId);
3728
+ independentLifetimeChildItemIds.add(itemId);
3800
3729
  }
3801
3730
  }
3802
3731
  }
@@ -3804,7 +3733,7 @@ function applyVisibilityStep(ir, state) {
3804
3733
  items: collector.items,
3805
3734
  miscs: collector.miscs,
3806
3735
  processedItems: processedItems,
3807
- transformOnlyParentLinkItemIds: transformOnlyParentLinkItemIds,
3736
+ independentLifetimeChildItemIds: independentLifetimeChildItemIds,
3808
3737
  roundNumber: roundNumber
3809
3738
  });
3810
3739
  }
@@ -3861,16 +3790,15 @@ function normalize(value, path, inArray) {
3861
3790
  * Convert an IR composition to a GE JSONScene.
3862
3791
  *
3863
3792
  * All IR sources (lottie, dsl, future) go through this single unified
3864
- * pipeline. Source-specific features are gated by data existence (e.g. mask,
3865
- * precomp, shape layers are absent in dsl IR and naturally skipped);
3866
- * `ir.source` branches are used only for B-class real semantic differences
3867
- * that cannot be expressed via data existence.
3793
+ * pipeline. Normalized animation semantics select the GE lowering plan;
3794
+ * source-specific payloads may preserve authored GE hierarchy or restore
3795
+ * mask and precomp graphs.
3868
3796
  *
3869
3797
  * The optional `duration` arg is a caller override; lottie2ge currently passes
3870
3798
  * the duration computed from the raw json upstream. When omitted, the
3871
3799
  * canonical assembler derives it from `(outPoint - inPoint) / frameRate`.
3872
3800
  */ function convertIRToGE(ir, duration) {
3873
- validateIRCompositionWithWarnings(ir);
3801
+ validateIRCompositionOrThrow(ir);
3874
3802
  var width = ir.width, height = ir.height, frameRate = ir.frameRate;
3875
3803
  var ratio = get2dTo3dRatio(width, height);
3876
3804
  var collector = {
@@ -3913,15 +3841,15 @@ function normalize(value, path, inArray) {
3913
3841
  }
3914
3842
  // Build GE1-component-id → regenerated-component-id map for mask reference
3915
3843
  // remapping (ge2ir round-trip). Keyed by the original GE component id that
3916
- // ge2ir stored in metadata; value is the component id this layer minted.
3917
- var geComponentIdToNewId = {};
3844
+ // ge2ir stored in specific.effects; value is the component id this layer minted.
3845
+ var GEComponentIdToNewId = {};
3918
3846
  for(var _iterator2 = _create_for_of_iterator_helper_loose(workingIR.layers), _step2; !(_step2 = _iterator2()).done;){
3919
3847
  var layer2 = _step2.value;
3920
- var _readGE2IRMetadata;
3921
- var geComponentId = (_readGE2IRMetadata = readGE2IRMetadata(workingIR, layer2)) == null ? void 0 : _readGE2IRMetadata.geComponentId;
3848
+ var _getEffectsLayerSpecific;
3849
+ var GEComponentId = (_getEffectsLayerSpecific = getEffectsLayerSpecific(workingIR, layer2)) == null ? void 0 : _getEffectsLayerSpecific.sourceComponentId;
3922
3850
  var newId = layerToComponentId[layer2.index];
3923
- if (geComponentId != null && newId != null) {
3924
- geComponentIdToNewId[geComponentId] = newId;
3851
+ if (GEComponentId != null && newId != null) {
3852
+ GEComponentIdToNewId[GEComponentId] = newId;
3925
3853
  }
3926
3854
  }
3927
3855
  applyPostProcess(workingIR, {
@@ -3932,7 +3860,7 @@ function normalize(value, path, inArray) {
3932
3860
  width: width,
3933
3861
  height: height,
3934
3862
  frameRate: frameRate,
3935
- geComponentIdToNewId: geComponentIdToNewId,
3863
+ GEComponentIdToNewId: GEComponentIdToNewId,
3936
3864
  maskLookup: {
3937
3865
  getItemId: function getItemId(idx) {
3938
3866
  return layerToItemId[idx];
@@ -3965,16 +3893,16 @@ function processLayer(layer, ctx) {
3965
3893
  }
3966
3894
  }
3967
3895
  function hasGEPrecompMetadata(ir, layer) {
3968
- var metadata = readGE2IRMetadata(ir, layer);
3969
- return (metadata == null ? void 0 : metadata.precompRefId) != null || (metadata == null ? void 0 : metadata.precompContent) != null;
3896
+ var specific = getEffectsLayerSpecific(ir, layer);
3897
+ return (specific == null ? void 0 : specific.precompRefId) != null || (specific == null ? void 0 : specific.precompContent) != null;
3970
3898
  }
3971
3899
  /**
3972
- * Extend parent layers' endFrame to cover transform-only children's time spans.
3900
+ * Extend parent layers' endFrame to cover independent child lifetimes.
3973
3901
  *
3974
3902
  * In Lottie, child layers (linked via layer.parent) inherit the parent's transform
3975
3903
  * even after the parent becomes invisible. GE requires the parent item to be active
3976
3904
  * for transform inheritance to work, so we extend the parent's endFrame to cover
3977
- * the full lifetime of its transform-only children.
3905
+ * the full lifetime of children whose lifetime is independent.
3978
3906
  *
3979
3907
  * Keyframes beyond the extended endFrame are NOT included — they represent animation
3980
3908
  * that never plays in Lottie (layer is invisible and has no child needing it).
@@ -3993,7 +3921,7 @@ function hasGEPrecompMetadata(ir, layer) {
3993
3921
  for(var _iterator1 = _create_for_of_iterator_helper_loose(clonedLayers), _step1; !(_step1 = _iterator1()).done;){
3994
3922
  var child = _step1.value;
3995
3923
  var _child_parent;
3996
- if (((_child_parent = child.parent) == null ? void 0 : _child_parent.index) === layer.index && child.parent.linkKind === 'transform-only') {
3924
+ if (((_child_parent = child.parent) == null ? void 0 : _child_parent.index) === layer.index && child.parent.lifetimeMode === 'independent') {
3997
3925
  if (child.time.endFrame > maxEnd) {
3998
3926
  maxEnd = child.time.endFrame;
3999
3927
  }
@@ -4012,13 +3940,13 @@ function mergePreservedPrecompScene(generated, ir) {
4012
3940
  return !isExcludedPoolEntry(pool, entry, excluded);
4013
3941
  }));
4014
3942
  };
4015
- var metadata = readGE2IRCompositionMetadata(ir);
4016
- var sourceScene = metadata == null ? void 0 : metadata.sourceScene;
3943
+ var specific = getEffectsCompositionSpecific(ir);
3944
+ var sourceScene = specific == null ? void 0 : specific.sourceScene;
4017
3945
  if (sourceScene == null) {
4018
3946
  return generated;
4019
3947
  }
4020
3948
  var result = _extends({}, generated);
4021
- var sourceCompositionId = metadata == null ? void 0 : metadata.sourceCompositionId;
3949
+ var sourceCompositionId = specific == null ? void 0 : specific.sourceCompositionId;
4022
3950
  var excluded = collectRootCompositionIds(sourceScene, sourceCompositionId);
4023
3951
  var pools = [
4024
3952
  'images',
@@ -4238,23 +4166,25 @@ function resolveDataPathId(ref) {
4238
4166
  return typeof id === 'string' ? id : undefined;
4239
4167
  }
4240
4168
 
4241
- function reverseEndBehavior(geBehavior) {
4242
- if (geBehavior == null) {
4169
+ function reverseEndBehavior(GEBehavior) {
4170
+ if (GEBehavior == null) {
4243
4171
  return undefined;
4244
4172
  }
4245
- switch(geBehavior){
4173
+ switch(GEBehavior){
4246
4174
  case 0:
4247
- return IREndBehavior.Stop;
4175
+ return IREndBehavior.Destroy;
4176
+ case 2:
4177
+ return IREndBehavior.Forward;
4248
4178
  case 4:
4249
- return IREndBehavior.Hold;
4179
+ return IREndBehavior.Freeze;
4250
4180
  case 5:
4251
- return IREndBehavior.Loop;
4181
+ return IREndBehavior.Restart;
4252
4182
  default:
4253
4183
  return undefined;
4254
4184
  }
4255
4185
  }
4256
- function reverseCompositionEndBehavior(geBehavior) {
4257
- return reverseEndBehavior(geBehavior);
4186
+ function reverseCompositionEndBehavior(GEBehavior) {
4187
+ return reverseEndBehavior(GEBehavior);
4258
4188
  }
4259
4189
 
4260
4190
  function buildSceneContext(scene, options) {
@@ -4274,7 +4204,10 @@ function buildSceneContext(scene, options) {
4274
4204
  var frameRate = options.frameRate;
4275
4205
  var ratio = get2dTo3dRatio(width, height);
4276
4206
  var inPoint = 0;
4277
- var outPoint = Math.round(duration * frameRate);
4207
+ // GE durations are expressed in seconds and are not guaranteed to land on
4208
+ // an integer frame. Preserve the fractional frame so GE → IR → GE does not
4209
+ // shorten or extend the composition at the tail.
4210
+ var outPoint = duration * frameRate;
4278
4211
  var endBehavior = reverseCompositionEndBehavior(composition['endBehavior']);
4279
4212
  var backgroundColor;
4280
4213
  var bg = composition['background'];
@@ -4482,16 +4415,26 @@ function parseStaticTransform(itemTransform, ctx) {
4482
4415
  }
4483
4416
  var eulerHint = itemTransform['eulerHint'];
4484
4417
  if (eulerHint != null) {
4485
- var hasRotation = Math.abs(eulerHint.x) > 1e-6 || Math.abs(eulerHint.y) > 1e-6 || Math.abs(eulerHint.z) > 1e-6;
4486
- if (hasRotation) {
4487
- channels.rotation = {
4488
- keyframed: false,
4489
- value: [
4490
- eulerHint.x,
4491
- eulerHint.y,
4492
- eulerHint.z
4493
- ]
4494
- };
4418
+ var x = Math.abs(eulerHint.x) > 1e-6 ? {
4419
+ keyframed: false,
4420
+ value: eulerHint.x
4421
+ } : undefined;
4422
+ var y = Math.abs(eulerHint.y) > 1e-6 ? {
4423
+ keyframed: false,
4424
+ value: eulerHint.y
4425
+ } : undefined;
4426
+ var z = Math.abs(eulerHint.z) > 1e-6 ? {
4427
+ keyframed: false,
4428
+ value: eulerHint.z
4429
+ } : undefined;
4430
+ if (x != null || y != null || z != null) {
4431
+ channels.rotation = _extends({}, x != null && {
4432
+ x: x
4433
+ }, y != null && {
4434
+ y: y
4435
+ }, z != null && {
4436
+ z: z
4437
+ });
4495
4438
  }
4496
4439
  }
4497
4440
  return channels;
@@ -4540,7 +4483,7 @@ function parseBezierKeyframes(curves, startFrame, endFrame) {
4540
4483
  };
4541
4484
  if (i === 0) {
4542
4485
  keyframes.push(_extends({
4543
- t: frameTime,
4486
+ frame: frameTime,
4544
4487
  value: v0
4545
4488
  }, isHold ? {
4546
4489
  hold: true
@@ -4560,7 +4503,7 @@ function parseBezierKeyframes(curves, startFrame, endFrame) {
4560
4503
  y: inverseLerp(prevV0, prevV1, prevInCpY)
4561
4504
  };
4562
4505
  keyframes.push(_extends({
4563
- t: frameTime,
4506
+ frame: frameTime,
4564
4507
  value: v0,
4565
4508
  inTangent: inTangent
4566
4509
  }, isHold ? {
@@ -4579,7 +4522,7 @@ function parseBezierKeyframes(curves, startFrame, endFrame) {
4579
4522
  y: inverseLerp(lastSeg[1], lastSeg[7], lastSeg[5])
4580
4523
  };
4581
4524
  keyframes.push({
4582
- t: lastFrameTime,
4525
+ frame: lastFrameTime,
4583
4526
  value: lastV1,
4584
4527
  inTangent: lastInTangent
4585
4528
  });
@@ -4675,7 +4618,7 @@ function parsePositionTimingKeyframes(curves, startFrame, endFrame) {
4675
4618
  };
4676
4619
  if (i === 0) {
4677
4620
  result.push({
4678
- t: frameTime,
4621
+ frame: frameTime,
4679
4622
  outTangent: outTangent
4680
4623
  });
4681
4624
  } else {
@@ -4685,7 +4628,7 @@ function parsePositionTimingKeyframes(curves, startFrame, endFrame) {
4685
4628
  y: inverseLerp(prevSeg[0], prevSeg[6], prevSeg[5])
4686
4629
  };
4687
4630
  result.push({
4688
- t: frameTime,
4631
+ frame: frameTime,
4689
4632
  outTangent: outTangent,
4690
4633
  inTangent: inTangent
4691
4634
  });
@@ -4698,7 +4641,7 @@ function parsePositionTimingKeyframes(curves, startFrame, endFrame) {
4698
4641
  y: inverseLerp(lastSeg[0], lastSeg[6], lastSeg[5])
4699
4642
  };
4700
4643
  result.push({
4701
- t: lastFrameTime,
4644
+ frame: lastFrameTime,
4702
4645
  inTangent: lastInTangent
4703
4646
  });
4704
4647
  return result;
@@ -4715,12 +4658,9 @@ function parseTransformAnimation(asset, startFrame, endFrame, ctx) {
4715
4658
  }
4716
4659
  var rotOverLifetime = asset['rotationOverLifetime'];
4717
4660
  if (rotOverLifetime != null) {
4718
- var rotResult = parseRotationAnimation(rotOverLifetime, startFrame, endFrame);
4719
- if (rotResult.rotation != null) {
4720
- channels.rotation = rotResult.rotation;
4721
- }
4722
- if (rotResult.separatedRotation != null) {
4723
- channels.separatedRotation = rotResult.separatedRotation;
4661
+ var rotation = parseRotationAnimation(rotOverLifetime, startFrame, endFrame);
4662
+ if (rotation != null) {
4663
+ channels.rotation = rotation;
4724
4664
  }
4725
4665
  }
4726
4666
  var sizeOverLifetime = asset['sizeOverLifetime'];
@@ -4800,7 +4740,7 @@ function parsePositionAnimation(posOverLifetime, startFrame, endFrame, ctx) {
4800
4740
  var pp = pathPoints[Math.min(i, pathPoints.length - 1)];
4801
4741
  var pos2d = convert3dTo2dPoint(pp[0], pp[1], (_pp_ = pp[2]) != null ? _pp_ : 0, ctx.width, ctx.height);
4802
4742
  var kf = {
4803
- t: timing.t,
4743
+ frame: timing.frame,
4804
4744
  value: pos2d
4805
4745
  };
4806
4746
  if (i > startIdx && timing.inTangent != null) {
@@ -4849,83 +4789,26 @@ function parsePositionAnimation(posOverLifetime, startFrame, endFrame, ctx) {
4849
4789
  };
4850
4790
  }
4851
4791
  function parseRotationAnimation(rotOverLifetime, startFrame, endFrame) {
4852
- var separateAxes = rotOverLifetime['separateAxes'];
4792
+ var separateAxes = rotOverLifetime['separateAxes'] === true;
4853
4793
  var xData = rotOverLifetime['x'];
4854
4794
  var yData = rotOverLifetime['y'];
4855
4795
  var zData = rotOverLifetime['z'];
4856
- if (separateAxes === true) {
4857
- var x = parseScalarChannel(xData, startFrame, endFrame);
4858
- var y = parseScalarChannel(yData, startFrame, endFrame);
4859
- var z = parseScalarChannel(zData, startFrame, endFrame);
4860
- var hasMultiAxis = (x != null ? 1 : 0) + (y != null ? 1 : 0) + (z != null ? 1 : 0) > 1;
4861
- var hasXOrY = x != null || y != null;
4862
- if (hasMultiAxis || hasXOrY) {
4863
- return {
4864
- separatedRotation: _extends({}, x != null && {
4865
- x: x
4866
- }, y != null && {
4867
- y: y
4868
- }, z != null && {
4869
- z: z
4870
- })
4871
- };
4872
- }
4873
- if (z != null) {
4874
- return {
4875
- separatedRotation: {
4876
- z: z
4877
- }
4878
- };
4879
- }
4880
- }
4881
- // Single-axis z rotation (non-separated)
4882
- if (zData != null) {
4883
- var z1 = parseScalarChannel(zData, startFrame, endFrame);
4884
- if (z1 != null) {
4885
- if (z1.keyframed) {
4886
- var vecKeyframes = z1.keyframes.map(function(kf) {
4887
- var result = {
4888
- t: kf.t,
4889
- value: [
4890
- 0,
4891
- 0,
4892
- kf.value
4893
- ]
4894
- };
4895
- if (kf.hold != null) {
4896
- result.hold = kf.hold;
4897
- }
4898
- if (kf.inTangent != null) {
4899
- result.inTangent = kf.inTangent;
4900
- }
4901
- if (kf.outTangent != null) {
4902
- result.outTangent = kf.outTangent;
4903
- }
4904
- return result;
4905
- });
4906
- return {
4907
- rotation: {
4908
- keyframed: true,
4909
- keyframes: vecKeyframes
4910
- }
4911
- };
4912
- }
4913
- return {
4914
- rotation: {
4915
- keyframed: false,
4916
- value: [
4917
- 0,
4918
- 0,
4919
- z1.value
4920
- ]
4921
- }
4922
- };
4923
- }
4796
+ var x = separateAxes ? parseScalarChannel(xData, startFrame, endFrame) : undefined;
4797
+ var y = separateAxes ? parseScalarChannel(yData, startFrame, endFrame) : undefined;
4798
+ var z = parseScalarChannel(zData, startFrame, endFrame);
4799
+ if (x == null && y == null && z == null) {
4800
+ return undefined;
4924
4801
  }
4925
- return {};
4802
+ return _extends({}, x != null && {
4803
+ x: x
4804
+ }, y != null && {
4805
+ y: y
4806
+ }, z != null && {
4807
+ z: z
4808
+ });
4926
4809
  }
4927
4810
  function parseScaleAnimation(sizeOverLifetime, startFrame, endFrame) {
4928
- var _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8;
4811
+ var _ref, _ref1, _ref2;
4929
4812
  var xData = sizeOverLifetime['x'];
4930
4813
  var yData = sizeOverLifetime['y'];
4931
4814
  var zData = sizeOverLifetime['z'];
@@ -4938,71 +4821,72 @@ function parseScaleAnimation(sizeOverLifetime, startFrame, endFrame) {
4938
4821
  var xKeyframed = (x == null ? void 0 : x.keyframed) === true;
4939
4822
  var yKeyframed = (y == null ? void 0 : y.keyframed) === true;
4940
4823
  var zKeyframed = (z == null ? void 0 : z.keyframed) === true;
4824
+ var xStaticValue = (x == null ? void 0 : x.keyframed) === false ? x.value : 1;
4825
+ var yStaticValue = (y == null ? void 0 : y.keyframed) === false ? y.value : 1;
4826
+ var zStaticValue = (z == null ? void 0 : z.keyframed) === false ? z.value : 1;
4941
4827
  if (!xKeyframed && !yKeyframed && !zKeyframed) {
4942
- var _ref9, _ref10, _ref11;
4943
4828
  return {
4944
4829
  keyframed: false,
4945
4830
  value: [
4946
- (_ref9 = x == null ? void 0 : x.value) != null ? _ref9 : 1,
4947
- (_ref10 = y == null ? void 0 : y.value) != null ? _ref10 : 1,
4948
- (_ref11 = z == null ? void 0 : z.value) != null ? _ref11 : 1
4831
+ xStaticValue,
4832
+ yStaticValue,
4833
+ zStaticValue
4949
4834
  ]
4950
4835
  };
4951
4836
  }
4952
4837
  var xKfs = (_ref = x == null ? void 0 : x.keyframes) != null ? _ref : [
4953
4838
  {
4954
- t: startFrame,
4955
- value: (_ref1 = x == null ? void 0 : x.value) != null ? _ref1 : 1
4839
+ frame: startFrame,
4840
+ value: xStaticValue
4956
4841
  },
4957
4842
  {
4958
- t: endFrame,
4959
- value: (_ref2 = x == null ? void 0 : x.value) != null ? _ref2 : 1
4843
+ frame: endFrame,
4844
+ value: xStaticValue
4960
4845
  }
4961
4846
  ];
4962
- var yKfs = (_ref3 = y == null ? void 0 : y.keyframes) != null ? _ref3 : [
4847
+ var yKfs = (_ref1 = y == null ? void 0 : y.keyframes) != null ? _ref1 : [
4963
4848
  {
4964
- t: startFrame,
4965
- value: (_ref4 = y == null ? void 0 : y.value) != null ? _ref4 : 1
4849
+ frame: startFrame,
4850
+ value: yStaticValue
4966
4851
  },
4967
4852
  {
4968
- t: endFrame,
4969
- value: (_ref5 = y == null ? void 0 : y.value) != null ? _ref5 : 1
4853
+ frame: endFrame,
4854
+ value: yStaticValue
4970
4855
  }
4971
4856
  ];
4972
- var zKfs = (_ref6 = z == null ? void 0 : z.keyframes) != null ? _ref6 : [
4857
+ var zKfs = (_ref2 = z == null ? void 0 : z.keyframes) != null ? _ref2 : [
4973
4858
  {
4974
- t: startFrame,
4975
- value: (_ref7 = z == null ? void 0 : z.value) != null ? _ref7 : 1
4859
+ frame: startFrame,
4860
+ value: zStaticValue
4976
4861
  },
4977
4862
  {
4978
- t: endFrame,
4979
- value: (_ref8 = z == null ? void 0 : z.value) != null ? _ref8 : 1
4863
+ frame: endFrame,
4864
+ value: zStaticValue
4980
4865
  }
4981
4866
  ];
4982
- var allTimes = new Set();
4867
+ var allFrames = new Set();
4983
4868
  for(var _iterator = _create_for_of_iterator_helper_loose(xKfs), _step; !(_step = _iterator()).done;){
4984
4869
  var kf = _step.value;
4985
- allTimes.add(kf.t);
4870
+ allFrames.add(kf.frame);
4986
4871
  }
4987
4872
  for(var _iterator1 = _create_for_of_iterator_helper_loose(yKfs), _step1; !(_step1 = _iterator1()).done;){
4988
4873
  var kf1 = _step1.value;
4989
- allTimes.add(kf1.t);
4874
+ allFrames.add(kf1.frame);
4990
4875
  }
4991
4876
  for(var _iterator2 = _create_for_of_iterator_helper_loose(zKfs), _step2; !(_step2 = _iterator2()).done;){
4992
4877
  var kf2 = _step2.value;
4993
- allTimes.add(kf2.t);
4878
+ allFrames.add(kf2.frame);
4994
4879
  }
4995
4880
  // Keep Array.from: loose SWC array-spread lowering does not expand Set in dist.
4996
- var sortedTimes = Array.from(allTimes).sort(function(a, b) {
4881
+ var sortedFrames = Array.from(allFrames).sort(function(a, b) {
4997
4882
  return a - b;
4998
4883
  });
4999
- var keyframes = sortedTimes.map(function(t) {
5000
- var _ref, _ref1, _ref2;
5001
- var xv = findValueAtTime(xKfs, t, (_ref = x == null ? void 0 : x.value) != null ? _ref : 1);
5002
- var yv = findValueAtTime(yKfs, t, (_ref1 = y == null ? void 0 : y.value) != null ? _ref1 : 1);
5003
- var zv = findValueAtTime(zKfs, t, (_ref2 = z == null ? void 0 : z.value) != null ? _ref2 : 1);
4884
+ var keyframes = sortedFrames.map(function(frame) {
4885
+ var xv = findValueAtFrame(xKfs, frame, xStaticValue);
4886
+ var yv = findValueAtFrame(yKfs, frame, yStaticValue);
4887
+ var zv = findValueAtFrame(zKfs, frame, zStaticValue);
5004
4888
  var kf = {
5005
- t: t,
4889
+ frame: frame,
5006
4890
  value: [
5007
4891
  xv.value,
5008
4892
  yv.value,
@@ -5048,10 +4932,10 @@ function parseScalarChannel(data, startFrame, endFrame) {
5048
4932
  }
5049
4933
  return undefined;
5050
4934
  }
5051
- function findValueAtTime(keyframes, t, defaultValue) {
4935
+ function findValueAtFrame(keyframes, frame, defaultValue) {
5052
4936
  for(var _iterator = _create_for_of_iterator_helper_loose(keyframes), _step; !(_step = _iterator()).done;){
5053
4937
  var kf = _step.value;
5054
- if (Math.abs(kf.t - t) < 1e-6) {
4938
+ if (Math.abs(kf.frame - frame) < 1e-6) {
5055
4939
  return {
5056
4940
  value: kf.value,
5057
4941
  outTangent: kf.outTangent,
@@ -5083,11 +4967,11 @@ var IR_BLEND_MODE_BY_GE = {
5083
4967
  15: IRBlendMode.SoftLight,
5084
4968
  16: IRBlendMode.Luminosity
5085
4969
  };
5086
- function reverseBlendMode(geCode) {
5087
- if (geCode == null) {
4970
+ function reverseBlendMode(GECode) {
4971
+ if (GECode == null) {
5088
4972
  return undefined;
5089
4973
  }
5090
- return IR_BLEND_MODE_BY_GE[geCode];
4974
+ return IR_BLEND_MODE_BY_GE[GECode];
5091
4975
  }
5092
4976
 
5093
4977
  function parseComponent(item, ctx) {
@@ -5117,7 +5001,7 @@ function parseComponent(item, ctx) {
5117
5001
  if (result == null) {
5118
5002
  continue;
5119
5003
  }
5120
- result.geComponentId = component['id'];
5004
+ result.GEComponentId = component['id'];
5121
5005
  var mask = component['mask'];
5122
5006
  if (mask != null) {
5123
5007
  result.rawMask = mask;
@@ -5241,7 +5125,7 @@ function parseShapeComponent(component, ctx) {
5241
5125
  if (renderer != null) {
5242
5126
  blendMode = reverseBlendMode(renderer['blending']);
5243
5127
  }
5244
- var shapeData = parseShapeOptions(component, ctx.ratio);
5128
+ var shapeData = parseShapeOptions(component, ctx);
5245
5129
  return {
5246
5130
  type: 'shape',
5247
5131
  shapes: shapeData != null ? [
@@ -5250,85 +5134,100 @@ function parseShapeComponent(component, ctx) {
5250
5134
  blendMode: blendMode
5251
5135
  };
5252
5136
  }
5253
- function parseShapeOptions(component, ratio) {
5254
- var geType = component['type'];
5255
- var primitiveType = reverseShapePrimitiveType(geType != null ? geType : 0);
5256
- var xSize = component['xSize'];
5257
- var ySize = component['ySize'];
5258
- var size = [
5259
- xSize != null ? xSize / ratio : 0,
5260
- ySize != null ? ySize / ratio : 0
5261
- ];
5262
- var shape = {
5263
- primitiveType: primitiveType,
5137
+ function parseShapeOptions(component, ctx) {
5138
+ var GEType = component['type'];
5139
+ var primitiveType = reverseShapePrimitiveType(GEType != null ? GEType : 0);
5140
+ var style = {
5264
5141
  position: [
5265
5142
  0,
5266
5143
  0
5267
5144
  ],
5268
- size: size,
5269
5145
  fills: [],
5270
5146
  strokes: []
5271
5147
  };
5272
- var shapeRec = shape;
5273
- reverseScaledNumber(shapeRec, component, 'xRadius', ratio);
5274
- reverseScaledNumber(shapeRec, component, 'yRadius', ratio);
5275
- reverseScaledNumber(shapeRec, component, 'width', ratio);
5276
- reverseScaledNumber(shapeRec, component, 'height', ratio);
5277
- reverseScaledNumber(shapeRec, component, 'innerRadius', ratio);
5278
- reverseScaledNumber(shapeRec, component, 'outerRadius', ratio);
5279
- reverseScaledNumber(shapeRec, component, 'radius', ratio);
5280
- reverseRawNumber(shapeRec, component, 'roundness');
5281
- reverseRawNumber(shapeRec, component, 'pointCount');
5282
- reverseRawNumber(shapeRec, component, 'innerRoundness');
5283
- reverseRawNumber(shapeRec, component, 'outerRoundness');
5284
- var geFills = component['fills'];
5285
- if (geFills != null) {
5286
- shape.fills = geFills.map(function(f) {
5287
- return reverseShapePaint(f, ratio);
5148
+ var GEFills = component['fills'];
5149
+ if (GEFills != null) {
5150
+ style.fills = GEFills.map(function(f) {
5151
+ return reverseShapePaint(f, ctx);
5288
5152
  }).filter(function(f) {
5289
5153
  return f != null;
5290
5154
  });
5291
5155
  }
5292
- var geStrokes = component['strokes'];
5293
- if (geStrokes != null) {
5294
- shape.strokes = geStrokes.map(function(s) {
5295
- return reverseShapePaint(s, ratio);
5156
+ var GEStrokes = component['strokes'];
5157
+ if (GEStrokes != null) {
5158
+ style.strokes = GEStrokes.map(function(s) {
5159
+ return reverseShapePaint(s, ctx);
5296
5160
  }).filter(function(s) {
5297
5161
  return s != null;
5298
5162
  });
5299
5163
  }
5300
- var geStrokeWidth = component['strokeWidth'];
5301
- if (geStrokeWidth != null) {
5302
- shape.strokeWidth = geStrokeWidth / ratio;
5303
- }
5304
- var geStrokeCap = component['strokeCap'];
5305
- if (geStrokeCap != null) {
5306
- shape.strokeCap = reverseLineCap(geStrokeCap);
5164
+ var GEStrokeWidth = component['strokeWidth'];
5165
+ if (typeof GEStrokeWidth === 'number' && Number.isFinite(GEStrokeWidth) && GEStrokeWidth >= 0) {
5166
+ style.strokeWidth = GEStrokeWidth / ctx.ratio;
5307
5167
  }
5308
- var geStrokeJoin = component['strokeJoin'];
5309
- if (geStrokeJoin != null) {
5310
- shape.strokeJoin = reverseLineJoin(geStrokeJoin);
5168
+ var GEStrokeCap = component['strokeCap'];
5169
+ if (GEStrokeCap != null) {
5170
+ style.strokeCap = reverseLineCap(GEStrokeCap);
5311
5171
  }
5312
- var geFillRule = component['fillRule'];
5313
- if (geFillRule != null) {
5314
- shape.fillRule = reverseShapeFillRule(geFillRule);
5172
+ var GEStrokeJoin = component['strokeJoin'];
5173
+ if (GEStrokeJoin != null) {
5174
+ style.strokeJoin = reverseLineJoin(GEStrokeJoin);
5315
5175
  }
5316
- var geShapes = component['shapes'];
5317
- if (geShapes != null) {
5318
- var paths = reverseCustomPaths(component, ratio);
5319
- if (paths.length > 0) {
5320
- shape.customPaths = paths;
5321
- }
5322
- }
5323
- var trimPath = component['trimPath'];
5324
- if (trimPath != null) {
5325
- shape.trimPath = trimPath;
5326
- }
5327
- var repeater = component['repeater'];
5328
- if (repeater != null) {
5329
- shape.repeater = repeater;
5176
+ switch(primitiveType){
5177
+ case IRShapePrimitiveType.Custom:
5178
+ {
5179
+ var customPaths = reverseCustomPaths(component, ctx.ratio);
5180
+ var firstPath = customPaths[0];
5181
+ return firstPath != null ? _extends({}, style, {
5182
+ primitiveType: primitiveType,
5183
+ customPaths: [].concat([
5184
+ firstPath
5185
+ ], customPaths.slice(1))
5186
+ }) : undefined;
5187
+ }
5188
+ case IRShapePrimitiveType.Rectangle:
5189
+ {
5190
+ var width = readScaledNumber(component, 'width', ctx.ratio);
5191
+ var height = readScaledNumber(component, 'height', ctx.ratio);
5192
+ return width != null && height != null ? _extends({}, style, {
5193
+ primitiveType: primitiveType,
5194
+ width: width,
5195
+ height: height
5196
+ }, readOptionalNumberField(component, 'roundness')) : undefined;
5197
+ }
5198
+ case IRShapePrimitiveType.Ellipse:
5199
+ {
5200
+ var xRadius = readScaledNumber(component, 'xRadius', ctx.ratio);
5201
+ var yRadius = readScaledNumber(component, 'yRadius', ctx.ratio);
5202
+ return xRadius != null && yRadius != null ? _extends({}, style, {
5203
+ primitiveType: primitiveType,
5204
+ xRadius: xRadius,
5205
+ yRadius: yRadius
5206
+ }) : undefined;
5207
+ }
5208
+ case IRShapePrimitiveType.Polygon:
5209
+ {
5210
+ var pointCount = readPointCount(component);
5211
+ var radius = readScaledNumber(component, 'radius', ctx.ratio);
5212
+ return pointCount != null && radius != null ? _extends({}, style, {
5213
+ primitiveType: primitiveType,
5214
+ pointCount: pointCount,
5215
+ radius: radius
5216
+ }, readOptionalNumberField(component, 'roundness')) : undefined;
5217
+ }
5218
+ case IRShapePrimitiveType.Star:
5219
+ {
5220
+ var pointCount1 = readPointCount(component);
5221
+ var innerRadius = readScaledNumber(component, 'innerRadius', ctx.ratio);
5222
+ var outerRadius = readScaledNumber(component, 'outerRadius', ctx.ratio);
5223
+ return pointCount1 != null && innerRadius != null && outerRadius != null ? _extends({}, style, {
5224
+ primitiveType: primitiveType,
5225
+ pointCount: pointCount1,
5226
+ innerRadius: innerRadius,
5227
+ outerRadius: outerRadius
5228
+ }, readOptionalNumberField(component, 'innerRoundness'), readOptionalNumberField(component, 'outerRoundness')) : undefined;
5229
+ }
5330
5230
  }
5331
- return shape;
5332
5231
  }
5333
5232
  var GE_TO_SHAPE_PRIMITIVE_TYPE = {
5334
5233
  0: IRShapePrimitiveType.Custom,
@@ -5337,13 +5236,13 @@ var GE_TO_SHAPE_PRIMITIVE_TYPE = {
5337
5236
  3: IRShapePrimitiveType.Polygon,
5338
5237
  4: IRShapePrimitiveType.Star
5339
5238
  };
5340
- function reverseShapePrimitiveType(geType) {
5341
- var _GE_TO_SHAPE_PRIMITIVE_TYPE_geType;
5342
- return (_GE_TO_SHAPE_PRIMITIVE_TYPE_geType = GE_TO_SHAPE_PRIMITIVE_TYPE[geType]) != null ? _GE_TO_SHAPE_PRIMITIVE_TYPE_geType : IRShapePrimitiveType.Custom;
5239
+ function reverseShapePrimitiveType(GEType) {
5240
+ var _GE_TO_SHAPE_PRIMITIVE_TYPE_GEType;
5241
+ return (_GE_TO_SHAPE_PRIMITIVE_TYPE_GEType = GE_TO_SHAPE_PRIMITIVE_TYPE[GEType]) != null ? _GE_TO_SHAPE_PRIMITIVE_TYPE_GEType : IRShapePrimitiveType.Custom;
5343
5242
  }
5344
- function reverseShapePaint(paint, ratio) {
5345
- var geType = paint['type'];
5346
- if (geType === 0) {
5243
+ function reverseShapePaint(paint, ctx) {
5244
+ var GEType = paint['type'];
5245
+ if (GEType === 0) {
5347
5246
  var color = paint['color'];
5348
5247
  if (color == null) {
5349
5248
  return undefined;
@@ -5358,7 +5257,7 @@ function reverseShapePaint(paint, ratio) {
5358
5257
  ]
5359
5258
  };
5360
5259
  }
5361
- if (geType === 1 || geType === 2 || geType === 3) {
5260
+ if (GEType === 1 || GEType === 2 || GEType === 3) {
5362
5261
  var typeMap = {
5363
5262
  1: IRFillType.LinearGradient,
5364
5263
  2: IRFillType.RadialGradient,
@@ -5369,45 +5268,50 @@ function reverseShapePaint(paint, ratio) {
5369
5268
  var startPoint = paint['startPoint'];
5370
5269
  var endPoint = paint['endPoint'];
5371
5270
  return {
5372
- type: typeMap[geType],
5271
+ type: typeMap[GEType],
5373
5272
  gradientStops: stops,
5374
5273
  gradientStart: startPoint != null ? [
5375
- startPoint.x / ratio,
5376
- -startPoint.y / ratio
5274
+ startPoint.x / ctx.ratio,
5275
+ -startPoint.y / ctx.ratio
5377
5276
  ] : undefined,
5378
5277
  gradientEnd: endPoint != null ? [
5379
- endPoint.x / ratio,
5380
- -endPoint.y / ratio
5278
+ endPoint.x / ctx.ratio,
5279
+ -endPoint.y / ctx.ratio
5381
5280
  ] : undefined
5382
5281
  };
5383
5282
  }
5283
+ if (GEType === 4) {
5284
+ var texture = resolveTextureAsset(paint['texture'], ctx);
5285
+ if (texture == null) {
5286
+ return undefined;
5287
+ }
5288
+ var scaleMode = paint['scaleMode'];
5289
+ return _extends({
5290
+ type: IRFillType.Texture,
5291
+ texture: texture
5292
+ }, isTextureScaleMode(scaleMode) && {
5293
+ scaleMode: scaleMode
5294
+ });
5295
+ }
5384
5296
  return undefined;
5385
5297
  }
5386
- function reverseLineCap(geCode) {
5387
- var _map_geCode;
5298
+ function reverseLineCap(GECode) {
5299
+ var _map_GECode;
5388
5300
  var map = {
5389
5301
  0: IRLineCap.Butt,
5390
5302
  1: IRLineCap.Round,
5391
5303
  2: IRLineCap.Square
5392
5304
  };
5393
- return (_map_geCode = map[geCode]) != null ? _map_geCode : IRLineCap.Butt;
5305
+ return (_map_GECode = map[GECode]) != null ? _map_GECode : IRLineCap.Butt;
5394
5306
  }
5395
- function reverseLineJoin(geCode) {
5396
- var _map_geCode;
5307
+ function reverseLineJoin(GECode) {
5308
+ var _map_GECode;
5397
5309
  var map = {
5398
5310
  0: IRLineJoin.Round,
5399
5311
  1: IRLineJoin.Bevel,
5400
5312
  2: IRLineJoin.Miter
5401
5313
  };
5402
- return (_map_geCode = map[geCode]) != null ? _map_geCode : IRLineJoin.Miter;
5403
- }
5404
- function reverseShapeFillRule(geCode) {
5405
- var _map_geCode;
5406
- var map = {
5407
- 0: IRShapeFillRule.NonZero,
5408
- 1: IRShapeFillRule.EvenOdd
5409
- };
5410
- return (_map_geCode = map[geCode]) != null ? _map_geCode : IRShapeFillRule.NonZero;
5314
+ return (_map_GECode = map[GECode]) != null ? _map_GECode : IRLineJoin.Miter;
5411
5315
  }
5412
5316
  function reverseCustomPaths(component, ratio) {
5413
5317
  var points = component['points'];
@@ -5419,8 +5323,8 @@ function reverseCustomPaths(component, ratio) {
5419
5323
  }
5420
5324
  return shapes.map(function(shape) {
5421
5325
  var pathPoints = [];
5422
- var pathEasingIns = [];
5423
- var pathEasingOuts = [];
5326
+ var pathInTangents = [];
5327
+ var pathOutTangents = [];
5424
5328
  for(var _iterator = _create_for_of_iterator_helper_loose(shape.indexes), _step; !(_step = _iterator()).done;){
5425
5329
  var idx = _step.value;
5426
5330
  var _ref, _ref1, _ref2, _ref3;
@@ -5431,34 +5335,50 @@ function reverseCustomPaths(component, ratio) {
5431
5335
  x: pt.x / ratio,
5432
5336
  y: -pt.y / ratio
5433
5337
  });
5434
- pathEasingIns.push({
5338
+ pathInTangents.push({
5435
5339
  x: ((_ref = ei == null ? void 0 : ei.x) != null ? _ref : 0) / ratio,
5436
5340
  y: -((_ref1 = ei == null ? void 0 : ei.y) != null ? _ref1 : 0) / ratio
5437
5341
  });
5438
- pathEasingOuts.push({
5342
+ pathOutTangents.push({
5439
5343
  x: ((_ref2 = eo == null ? void 0 : eo.x) != null ? _ref2 : 0) / ratio,
5440
5344
  y: -((_ref3 = eo == null ? void 0 : eo.y) != null ? _ref3 : 0) / ratio
5441
5345
  });
5442
5346
  }
5443
5347
  return {
5444
5348
  points: pathPoints,
5445
- easingIns: pathEasingIns,
5446
- easingOuts: pathEasingOuts,
5447
- close: shape.close
5349
+ inTangents: pathInTangents,
5350
+ outTangents: pathOutTangents,
5351
+ closed: shape.close
5448
5352
  };
5449
5353
  });
5450
5354
  }
5451
- function reverseScaledNumber(shape, component, key, ratio) {
5355
+ function readScaledNumber(component, key, ratio) {
5452
5356
  var value = component[key];
5453
- if (typeof value === 'number') {
5454
- shape[key] = value / ratio;
5357
+ if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) {
5358
+ return undefined;
5455
5359
  }
5360
+ return value / ratio;
5456
5361
  }
5457
- function reverseRawNumber(shape, component, key) {
5362
+ function readOptionalNumberField(component, key) {
5458
5363
  var value = component[key];
5459
- if (typeof value === 'number') {
5460
- shape[key] = value;
5461
- }
5364
+ var _obj;
5365
+ return typeof value === 'number' && Number.isFinite(value) ? (_obj = {}, _obj[key] = value, _obj) : {};
5366
+ }
5367
+ function readPointCount(component) {
5368
+ var value = component['pointCount'];
5369
+ return typeof value === 'number' && Number.isInteger(value) && value >= 2 ? value : undefined;
5370
+ }
5371
+ function resolveTextureAsset(textureRef, ctx) {
5372
+ var texture = resolveDataPath(ctx.textureById, textureRef);
5373
+ var sourceRef = texture == null ? void 0 : texture['source'];
5374
+ var image = sourceRef != null ? resolveDataPath(ctx.imageById, sourceRef) : undefined;
5375
+ var url = image == null ? void 0 : image['url'];
5376
+ return typeof url === 'string' && url.trim() !== '' ? {
5377
+ url: url
5378
+ } : undefined;
5379
+ }
5380
+ function isTextureScaleMode(value) {
5381
+ return typeof value === 'number' && Number.isInteger(value) && value >= 0 && value <= 3;
5462
5382
  }
5463
5383
 
5464
5384
  function reverseLayerTiming(delay, duration, frameRate) {
@@ -5499,7 +5419,7 @@ function mergeNullSpritePairs(descriptors, ctx) {
5499
5419
  // child is exactly one same-name visual sprite. ir2ge's emitNullSpritePair
5500
5420
  // produces precisely this shape (null + one same-name child). If the null
5501
5421
  // has any OTHER children, it is a genuine Lottie parent null — merging it
5502
- // into a transformSplit would change the anchor those other children are
5422
+ // into a single semantic layer would change the anchor those other children are
5503
5423
  // parented against, shifting them. Leave such nulls intact.
5504
5424
  if (children.length !== 1) {
5505
5425
  continue;
@@ -5613,19 +5533,17 @@ function isRecord$1(value) {
5613
5533
  return value != null && (typeof value === "undefined" ? "undefined" : _type_of(value)) === 'object' && !Array.isArray(value);
5614
5534
  }
5615
5535
  function mergeNullAndSprite(nullDesc, childDesc, ctx) {
5616
- var _childDesc_layer_transform;
5536
+ var _childDesc_layer_transform, _merged_specific;
5617
5537
  var merged = _extends({}, nullDesc.layer);
5618
5538
  // Take type from child
5619
5539
  merged.type = childDesc.layer.type;
5620
5540
  merged.asset = childDesc.layer.asset;
5621
5541
  merged.text = childDesc.layer.text;
5622
5542
  merged.shapes = childDesc.layer.shapes;
5623
- merged.shapeLayer = childDesc.layer.shapeLayer;
5624
5543
  merged.solidColor = childDesc.layer.solidColor;
5625
5544
  merged.blendMode = childDesc.layer.blendMode;
5626
- merged.transformSplit = true;
5627
5545
  // Visibility from child
5628
- merged.visibility = childDesc.layer.visibility;
5546
+ merged.visible = childDesc.layer.visible;
5629
5547
  // Compute anchor from child position offset
5630
5548
  var childTransform = childDesc.item['transform'];
5631
5549
  var childPosition = childTransform == null ? void 0 : childTransform['position'];
@@ -5646,7 +5564,9 @@ function mergeNullAndSprite(nullDesc, childDesc, ctx) {
5646
5564
  0
5647
5565
  ]
5648
5566
  };
5649
- merged.displaySize = {
5567
+ merged.localBounds = {
5568
+ x: 0,
5569
+ y: 0,
5650
5570
  width: layerWidth,
5651
5571
  height: layerHeight
5652
5572
  };
@@ -5659,17 +5579,21 @@ function mergeNullAndSprite(nullDesc, childDesc, ctx) {
5659
5579
  merged.transform.opacity = childDesc.layer.transform.opacity;
5660
5580
  }
5661
5581
  // The merged layer renders the child's visual component, but the parent owns
5662
- // transform animation passthrough. Deep-merge the ge2ir namespace so neither
5663
- // side overwrites the other's GE round-trip data.
5664
- if (childDesc.layer.metadata != null) {
5665
- var _merged_metadata;
5666
- var parentGE2IR = isRecord$1((_merged_metadata = merged.metadata) == null ? void 0 : _merged_metadata['ge2ir']) ? merged.metadata['ge2ir'] : undefined;
5667
- var childGE2IR = isRecord$1(childDesc.layer.metadata['ge2ir']) ? childDesc.layer.metadata['ge2ir'] : undefined;
5668
- merged.metadata = _extends({}, merged.metadata, childDesc.layer.metadata);
5669
- if (parentGE2IR != null || childGE2IR != null) {
5670
- merged.metadata['ge2ir'] = _extends({}, parentGE2IR != null ? parentGE2IR : {}, childGE2IR != null ? childGE2IR : {});
5671
- }
5582
+ // transform animation passthrough. Deep-merge specific.effects so neither side
5583
+ // loses GE reconstruction data.
5584
+ if (childDesc.layer.specific != null) {
5585
+ var _merged_specific1;
5586
+ var parentEffects = (_merged_specific1 = merged.specific) == null ? void 0 : _merged_specific1.effects;
5587
+ var childEffects = childDesc.layer.specific.effects;
5588
+ merged.specific = _extends({}, merged.specific, childDesc.layer.specific, (parentEffects != null || childEffects != null) && {
5589
+ effects: _extends({}, parentEffects != null ? parentEffects : {}, childEffects != null ? childEffects : {})
5590
+ });
5672
5591
  }
5592
+ merged.specific = _extends({}, merged.specific, {
5593
+ effects: _extends({}, (_merged_specific = merged.specific) == null ? void 0 : _merged_specific.effects, {
5594
+ sourceHierarchy: 'null-sprite-pair'
5595
+ })
5596
+ });
5673
5597
  return {
5674
5598
  itemId: nullDesc.itemId,
5675
5599
  item: nullDesc.item,
@@ -5698,7 +5622,7 @@ function resolveHierarchy(descriptors, ctx) {
5698
5622
  }
5699
5623
  desc1.layer.parent = {
5700
5624
  index: parentIndex,
5701
- name: parentDesc.layer.name
5625
+ lifetimeMode: 'clipped'
5702
5626
  };
5703
5627
  var parentAnchor = getAnchorFromDesc(parentDesc);
5704
5628
  var pos = (_desc_layer_transform = desc1.layer.transform) == null ? void 0 : _desc_layer_transform.position;
@@ -5763,7 +5687,7 @@ function convertGEToIR(scene, options) {
5763
5687
  return d.layer;
5764
5688
  });
5765
5689
  return normalizeJsonValue({
5766
- source: 'ge',
5690
+ source: 'effects',
5767
5691
  name: ctx.compositionName,
5768
5692
  width: ctx.width,
5769
5693
  height: ctx.height,
@@ -5773,14 +5697,13 @@ function convertGEToIR(scene, options) {
5773
5697
  backgroundColor: ctx.backgroundColor,
5774
5698
  endBehavior: ctx.endBehavior,
5775
5699
  layers: layers,
5776
- metadata: buildCompositionMetadata(scene, ctx.compositionId, itemIds, ctx)
5700
+ specific: buildCompositionSpecific(scene, ctx.compositionId, itemIds, ctx)
5777
5701
  });
5778
5702
  }
5779
5703
  function parseItemToLayer(item, itemId, index, ctx) {
5780
- var _item_name, _item_visible, _ref, _item_delay, _ref1, _item_duration;
5781
- var _item_transform;
5704
+ var _item_name, _item_visible, _ref, _item_delay, _ref1, _item_duration, _ref2;
5782
5705
  var name = (_item_name = item['name']) != null ? _item_name : "layer_" + index;
5783
- var geType = item['type'];
5706
+ var GEType = item['type'];
5784
5707
  var visible = (_item_visible = item['visible']) != null ? _item_visible : true;
5785
5708
  var rawTransformPassthrough;
5786
5709
  var timeline = parseTimeline(itemId, ctx);
@@ -5789,42 +5712,50 @@ function parseItemToLayer(item, itemId, index, ctx) {
5789
5712
  var time = reverseLayerTiming(delay, duration, ctx.frameRate);
5790
5713
  var componentData = parseComponent(item, ctx);
5791
5714
  var type;
5792
- if (geType === '3') {
5715
+ if (GEType === '3') {
5793
5716
  type = 'null';
5794
- } else if ((componentData == null ? void 0 : componentData.type) === 'text' || geType === 'text') {
5717
+ } else if ((componentData == null ? void 0 : componentData.type) === 'text' || GEType === 'text') {
5795
5718
  type = 'text';
5796
- } else if ((componentData == null ? void 0 : componentData.type) === 'shape' || geType === 'shape') {
5719
+ } else if ((componentData == null ? void 0 : componentData.type) === 'shape' || GEType === 'shape') {
5797
5720
  type = 'shape';
5798
5721
  } else if ((componentData == null ? void 0 : componentData.type) === 'image') {
5799
5722
  type = 'image';
5800
- } else if (geType === '7') {
5723
+ } else if (GEType === '7') {
5801
5724
  type = 'precomp';
5802
5725
  } else if ((componentData == null ? void 0 : componentData.solidColor) != null) {
5803
5726
  type = 'solid';
5804
- } else if (geType === '1') {
5727
+ } else if (GEType === '1') {
5805
5728
  type = 'solid';
5806
5729
  } else {
5807
5730
  type = 'null';
5808
5731
  }
5809
5732
  // Parse transforms
5810
5733
  var itemTransform = item['transform'];
5734
+ var itemSize = readItemSize(itemTransform);
5735
+ var textBounds = type === 'text' && itemSize == null ? readTextBounds(componentData == null ? void 0 : componentData.text) : undefined;
5736
+ var localBounds = itemSize != null ? {
5737
+ x: 0,
5738
+ y: 0,
5739
+ width: itemSize.x / ctx.ratio,
5740
+ height: itemSize.y / ctx.ratio
5741
+ } : textBounds;
5811
5742
  var transform;
5812
5743
  if (itemTransform != null) {
5813
5744
  var staticChannels = parseStaticTransform(itemTransform, ctx);
5814
5745
  transform = _extends({}, staticChannels);
5815
5746
  // Text layers carry their pixel-sizing scale on transform.scale
5816
- // (scale = ratio × displaySize). ir2ge RECOMPUTES this item scale from
5817
- // displaySize in processVisualLayer, so keeping it in the IR scale channel
5747
+ // (scale = ratio × local bounds). ir2ge RECOMPUTES this item scale from
5748
+ // localBounds in processVisualLayer, so keeping it in the IR scale channel
5818
5749
  // would make buildSizeAnim emit a duplicate sizeOverLifetime constant that
5819
5750
  // double-scales the glyphs (garbled/stretched text). Drop the static scale
5820
5751
  // for text; genuine text scale animation still rides through
5821
- // metadata.ge2ir.sizeOverLifetime passthrough.
5752
+ // specific.effects.sizeOverLifetime passthrough.
5822
5753
  if (type === 'text') {
5823
5754
  delete transform.scale;
5824
5755
  }
5825
5756
  }
5826
5757
  // Parse animation from timeline tracks — passthrough raw GE animation data
5827
- // via metadata. Converting to IR keyframes and back would lose data (ir2ge
5758
+ // via specific.effects. Converting to IR keyframes and back would lose data (ir2ge
5828
5759
  // only handles specific ValueType variants and makes assumptions about
5829
5760
  // static positions). The raw data is injected verbatim by ir2ge.
5830
5761
  if ((timeline == null ? void 0 : timeline.transformAsset) != null) {
@@ -5838,10 +5769,6 @@ function parseItemToLayer(item, itemId, index, ctx) {
5838
5769
  if (animChannels.rotation != null) {
5839
5770
  transform.rotation = animChannels.rotation;
5840
5771
  }
5841
- if (animChannels.separatedRotation != null) {
5842
- transform.separatedRotation = animChannels.separatedRotation;
5843
- delete transform.rotation;
5844
- }
5845
5772
  if (animChannels.scale != null) {
5846
5773
  transform.scale = animChannels.scale;
5847
5774
  }
@@ -5878,108 +5805,104 @@ function parseItemToLayer(item, itemId, index, ctx) {
5878
5805
  rawColorOverLifetime = col;
5879
5806
  }
5880
5807
  }
5881
- // Anchor from component — default to center (0.5, 0.5) when not explicitly set
5882
- if (type !== 'null') {
5883
- var _item_transform1;
5884
- var itemSize = (_item_transform1 = item['transform']) == null ? void 0 : _item_transform1['size'];
5885
- if (itemSize != null) {
5886
- var _ref2;
5887
- var layerWidth = itemSize.x / ctx.ratio;
5888
- var layerHeight = itemSize.y / ctx.ratio;
5889
- var anchorScale = (_ref2 = componentData == null ? void 0 : componentData.anchorScale) != null ? _ref2 : [
5890
- 0.5,
5891
- 0.5
5808
+ // Anchor from component — default to center (0.5, 0.5) when not explicitly set.
5809
+ // Some GE text has no transform.size, so its bounds come from TextComponent.options.
5810
+ // The anchor itself still follows the same normalized renderer coordinates as every
5811
+ // other visual layer; text alignment and glyph baseline are renderer-internal details.
5812
+ if (type !== 'null' && localBounds != null) {
5813
+ var _ref3;
5814
+ var layerWidth = localBounds.width;
5815
+ var layerHeight = localBounds.height;
5816
+ var anchorScale = (_ref3 = componentData == null ? void 0 : componentData.anchorScale) != null ? _ref3 : [
5817
+ 0.5,
5818
+ 0.5
5819
+ ];
5820
+ var anchorInfo = reverseGenericAnchor(anchorScale, layerWidth, layerHeight);
5821
+ var anchorX = anchorInfo.anchorX, anchorY = anchorInfo.anchorY, anchorOffsetX = anchorInfo.anchorOffsetX, anchorOffsetY = anchorInfo.anchorOffsetY;
5822
+ if (transform == null) {
5823
+ transform = {};
5824
+ }
5825
+ transform.anchor = {
5826
+ keyframed: false,
5827
+ value: [
5828
+ anchorX,
5829
+ anchorY,
5830
+ 0
5831
+ ]
5832
+ };
5833
+ // Reverse the position adjustments ir2ge's processVisualLayer re-applies
5834
+ // for non-centered anchors, so they don't double-apply on round-trip.
5835
+ // Forward order (in 2D-px equivalent): pos += anchorOffset (computeStaticPosition),
5836
+ // then pos += scaleAdjust (applyScalePositionAdjust). We subtract both.
5837
+ var pos = transform.position;
5838
+ if (pos != null && !pos.keyframed && pos.value != null) {
5839
+ var _scale_, _scale_1;
5840
+ var _transform_scale;
5841
+ // scaleAdjust (only when static scale != 1 and anchor off-center by >0.5px):
5842
+ // 3D: x += anchorDiffX*(1-sx)*ratio ; y -= anchorDiffY*(1-sy)*ratio
5843
+ // In 2D-px (÷ratio, y-axis flips sign): x2d += anchorDiffX*(1-sx) ;
5844
+ // y2d += anchorDiffY*(1-sy) (the two sign flips cancel).
5845
+ var scale = ((_transform_scale = transform.scale) == null ? void 0 : _transform_scale.keyframed) === false && Array.isArray(transform.scale.value) ? transform.scale.value : [
5846
+ 1,
5847
+ 1,
5848
+ 1
5892
5849
  ];
5893
- var anchorX = anchorScale[0] * layerWidth;
5894
- var anchorY = anchorScale[1] * layerHeight;
5895
- if (transform == null) {
5896
- transform = {};
5850
+ var sx = (_scale_ = scale[0]) != null ? _scale_ : 1;
5851
+ var sy = (_scale_1 = scale[1]) != null ? _scale_1 : 1;
5852
+ var anchorDiffX = anchorX - layerWidth / 2;
5853
+ var anchorDiffY = anchorY - layerHeight / 2;
5854
+ var anchorOffCenter = Math.abs(anchorDiffX) > 0.5 || Math.abs(anchorDiffY) > 0.5;
5855
+ var hasStaticScale = !(sx === 1 && sy === 1);
5856
+ var scaleAdjustX = 0;
5857
+ var scaleAdjustY = 0;
5858
+ if (hasStaticScale && anchorOffCenter && layerWidth > 0 && layerHeight > 0) {
5859
+ scaleAdjustX = anchorDiffX * (1 - sx);
5860
+ scaleAdjustY = anchorDiffY * (1 - sy);
5897
5861
  }
5898
- transform.anchor = {
5899
- keyframed: false,
5900
- value: [
5901
- anchorX,
5902
- anchorY,
5903
- 0
5904
- ]
5905
- };
5906
- // Reverse the position adjustments ir2ge's processVisualLayer re-applies
5907
- // for non-centered anchors, so they don't double-apply on round-trip.
5908
- // Forward order (in 2D-px equivalent): pos += anchorOffset (computeStaticPosition),
5909
- // then pos += scaleAdjust (applyScalePositionAdjust). We subtract both.
5910
- var pos = transform.position;
5911
- if (pos != null && !pos.keyframed && pos.value != null) {
5912
- var _scale_, _scale_1;
5913
- var _transform_scale;
5914
- // anchorOffset = -(anchor - size/2), added to the raw 2D position.
5915
- var anchorOffsetX = -(anchorX - layerWidth / 2);
5916
- var anchorOffsetY = -(anchorY - layerHeight / 2);
5917
- // scaleAdjust (only when static scale != 1 and anchor off-center by >0.5px):
5918
- // 3D: x += anchorDiffX*(1-sx)*ratio ; y -= anchorDiffY*(1-sy)*ratio
5919
- // In 2D-px (÷ratio, y-axis flips sign): x2d += anchorDiffX*(1-sx) ;
5920
- // y2d += anchorDiffY*(1-sy) (the two sign flips cancel).
5921
- var scale = ((_transform_scale = transform.scale) == null ? void 0 : _transform_scale.keyframed) === false && Array.isArray(transform.scale.value) ? transform.scale.value : [
5922
- 1,
5923
- 1,
5924
- 1
5925
- ];
5926
- var sx = (_scale_ = scale[0]) != null ? _scale_ : 1;
5927
- var sy = (_scale_1 = scale[1]) != null ? _scale_1 : 1;
5928
- var anchorDiffX = anchorX - layerWidth / 2;
5929
- var anchorDiffY = anchorY - layerHeight / 2;
5930
- var anchorOffCenter = Math.abs(anchorDiffX) > 0.5 || Math.abs(anchorDiffY) > 0.5;
5931
- var hasStaticScale = !(sx === 1 && sy === 1);
5932
- var scaleAdjustX = 0;
5933
- var scaleAdjustY = 0;
5934
- if (hasStaticScale && anchorOffCenter && layerWidth > 0 && layerHeight > 0) {
5935
- scaleAdjustX = anchorDiffX * (1 - sx);
5936
- scaleAdjustY = anchorDiffY * (1 - sy);
5937
- }
5938
- var totalX = anchorOffsetX + scaleAdjustX;
5939
- var totalY = anchorOffsetY + scaleAdjustY;
5940
- if (Math.abs(totalX) > 1e-6 || Math.abs(totalY) > 1e-6) {
5941
- var _pos_value_;
5942
- transform.position = {
5943
- keyframed: false,
5944
- value: [
5945
- pos.value[0] - totalX,
5946
- pos.value[1] - totalY,
5947
- (_pos_value_ = pos.value[2]) != null ? _pos_value_ : 0
5948
- ]
5949
- };
5950
- }
5862
+ var totalX = anchorOffsetX + scaleAdjustX;
5863
+ var totalY = anchorOffsetY + scaleAdjustY;
5864
+ if (Math.abs(totalX) > 1e-6 || Math.abs(totalY) > 1e-6) {
5865
+ var _pos_value_;
5866
+ transform.position = {
5867
+ keyframed: false,
5868
+ value: [
5869
+ pos.value[0] - totalX,
5870
+ pos.value[1] - totalY,
5871
+ (_pos_value_ = pos.value[2]) != null ? _pos_value_ : 0
5872
+ ]
5873
+ };
5951
5874
  }
5952
5875
  }
5953
5876
  }
5954
- // Display size from item transform.size
5955
- var displaySize;
5956
- var itemSize1 = (_item_transform = item['transform']) == null ? void 0 : _item_transform['size'];
5957
- if (itemSize1 != null) {
5958
- displaySize = {
5959
- width: itemSize1.x / ctx.ratio,
5960
- height: itemSize1.y / ctx.ratio
5961
- };
5877
+ var text = (componentData == null ? void 0 : componentData.text) == null ? undefined : _extends({}, componentData.text);
5878
+ var solidColor = type === 'solid' ? (_ref2 = componentData == null ? void 0 : componentData.solidColor) != null ? _ref2 : [
5879
+ 1,
5880
+ 1,
5881
+ 1,
5882
+ 1
5883
+ ] : componentData == null ? void 0 : componentData.solidColor;
5884
+ if ((text == null ? void 0 : text.autoWidth) === false) {
5885
+ var _text, _width, _text1, _height;
5886
+ (_width = (_text = text).width) != null ? _width : _text.width = localBounds == null ? void 0 : localBounds.width;
5887
+ (_height = (_text1 = text).height) != null ? _height : _text1.height = localBounds == null ? void 0 : localBounds.height;
5962
5888
  }
5963
5889
  var layer = {
5964
5890
  index: index,
5965
5891
  name: name,
5966
5892
  type: type,
5967
5893
  time: time,
5968
- visibility: {
5969
- hidden: !visible,
5970
- enabled: visible
5971
- },
5894
+ visible: visible,
5972
5895
  transform: transform,
5973
- displaySize: displaySize
5896
+ localBounds: localBounds
5974
5897
  };
5975
5898
  if ((componentData == null ? void 0 : componentData.asset) != null) {
5976
5899
  layer.asset = componentData.asset;
5977
5900
  }
5978
- if ((componentData == null ? void 0 : componentData.solidColor) != null) {
5979
- layer.solidColor = componentData.solidColor;
5901
+ if (solidColor != null) {
5902
+ layer.solidColor = solidColor;
5980
5903
  }
5981
- if ((componentData == null ? void 0 : componentData.text) != null) {
5982
- layer.text = componentData.text;
5904
+ if (text != null) {
5905
+ layer.text = text;
5983
5906
  }
5984
5907
  if ((componentData == null ? void 0 : componentData.shapes) != null) {
5985
5908
  layer.shapes = componentData.shapes;
@@ -5987,38 +5910,39 @@ function parseItemToLayer(item, itemId, index, ctx) {
5987
5910
  if ((componentData == null ? void 0 : componentData.blendMode) != null) {
5988
5911
  layer.blendMode = componentData.blendMode;
5989
5912
  }
5990
- var geComponentId = componentData == null ? void 0 : componentData.geComponentId;
5913
+ var GEComponentId = componentData == null ? void 0 : componentData.GEComponentId;
5991
5914
  var rawMask = componentData == null ? void 0 : componentData.rawMask;
5992
- var precompContent = geType === '7' ? item['content'] : undefined;
5915
+ var precompContent = GEType === '7' ? item['content'] : undefined;
5993
5916
  var precompRefId = isRecord(precompContent == null ? void 0 : precompContent['options']) ? precompContent['options']['refId'] : undefined;
5994
- if (rawTransformPassthrough != null || rawColorOverLifetime != null || geComponentId != null || rawMask != null || typeof precompRefId === 'string' || precompContent != null) {
5995
- // Mask round-trip: geComponentId is the rendezvous key linking a target's
5996
- // mask.reference.id (a GE1 component id) to the regenerated GE2 component id.
5997
- // ir2ge copies ge2ir.geMask onto the rebuilt component and a post-process pass
5998
- // remaps the stale reference id.
5999
- var ge2irMetadata = _extends({}, rawTransformPassthrough != null ? rawTransformPassthrough : {}, rawColorOverLifetime != null && {
6000
- colorOverLifetime: rawColorOverLifetime
6001
- }, geComponentId != null && {
6002
- geComponentId: geComponentId
6003
- }, rawMask != null && {
6004
- geMask: rawMask
6005
- }, typeof precompRefId === 'string' && {
6006
- precompRefId: precompRefId
6007
- }, precompContent != null && {
6008
- precompContent: cloneJson(precompContent)
6009
- });
6010
- layer.metadata = _extends({}, layer.metadata, {
6011
- ge2ir: ge2irMetadata
6012
- });
6013
- }
5917
+ // Mask round-trip: GEComponentId is the rendezvous key linking a target's
5918
+ // mask.reference.id (a GE1 component id) to the regenerated GE2 component id.
5919
+ // ir2ge copies specific.effects.mask onto the rebuilt component and a post-process pass
5920
+ // remaps the stale reference id. sourceHierarchy records the pre-merge shape;
5921
+ // mergeNullSpritePairs replaces it when a pair collapses into one semantic layer.
5922
+ var effectsSpecific = _extends({
5923
+ sourceHierarchy: 'single-item'
5924
+ }, rawTransformPassthrough != null ? rawTransformPassthrough : {}, rawColorOverLifetime != null && {
5925
+ colorOverLifetime: rawColorOverLifetime
5926
+ }, GEComponentId != null && {
5927
+ sourceComponentId: GEComponentId
5928
+ }, rawMask != null && {
5929
+ mask: rawMask
5930
+ }, typeof precompRefId === 'string' && {
5931
+ precompRefId: precompRefId
5932
+ }, precompContent != null && {
5933
+ precompContent: cloneJson(precompContent)
5934
+ });
5935
+ layer.specific = _extends({}, layer.specific, {
5936
+ effects: effectsSpecific
5937
+ });
6014
5938
  return layer;
6015
5939
  }
6016
- function buildCompositionMetadata(scene, compositionId, itemIds, ctx) {
5940
+ function buildCompositionSpecific(scene, compositionId, itemIds, ctx) {
6017
5941
  if (!hasPrecompItem(itemIds, ctx)) {
6018
5942
  return undefined;
6019
5943
  }
6020
5944
  return {
6021
- ge2ir: {
5945
+ effects: {
6022
5946
  sourceCompositionId: compositionId,
6023
5947
  sourceScene: cloneJson(scene)
6024
5948
  }
@@ -6036,21 +5960,51 @@ function cloneJson(value) {
6036
5960
  function isRecord(value) {
6037
5961
  return value != null && (typeof value === "undefined" ? "undefined" : _type_of(value)) === 'object' && !Array.isArray(value);
6038
5962
  }
5963
+ function readItemSize(itemTransform) {
5964
+ var size = itemTransform == null ? void 0 : itemTransform['size'];
5965
+ if (!isRecord(size)) {
5966
+ return undefined;
5967
+ }
5968
+ var x = size['x'];
5969
+ var y = size['y'];
5970
+ return typeof x === 'number' && Number.isFinite(x) && x > 0 && typeof y === 'number' && Number.isFinite(y) && y > 0 ? {
5971
+ x: x,
5972
+ y: y
5973
+ } : undefined;
5974
+ }
5975
+ function readTextBounds(text) {
5976
+ var width = text == null ? void 0 : text.width;
5977
+ var height = text == null ? void 0 : text.height;
5978
+ return typeof width === 'number' && Number.isFinite(width) && width > 0 && typeof height === 'number' && Number.isFinite(height) && height > 0 ? {
5979
+ x: 0,
5980
+ y: 0,
5981
+ width: width,
5982
+ height: height
5983
+ } : undefined;
5984
+ }
5985
+ function reverseGenericAnchor(anchorScale, width, height) {
5986
+ var anchorX = anchorScale[0] * width;
5987
+ var anchorY = anchorScale[1] * height;
5988
+ return {
5989
+ anchorX: anchorX,
5990
+ anchorY: anchorY,
5991
+ anchorOffsetX: -(anchorX - width / 2),
5992
+ anchorOffsetY: -(anchorY - height / 2)
5993
+ };
5994
+ }
6039
5995
 
6040
5996
  /**
6041
5997
  * Read-side accessors for the `IRLayerNode.specific.lottie` slot.
6042
5998
  *
6043
5999
  * The shared generator must not branch on lottie semantics directly — every
6044
- * lottie-specific read goes through these helpers so the rest of `ir2ge` stays
6000
+ * Lottie-specific reads go through these helpers so the rest of ir2ge stays
6045
6001
  * agnostic to the producing tool.
6046
6002
  *
6047
- * `isTransformOnlyParentLink` used to live here, reading
6048
- * `specific.lottie.explicitParent`. It was promoted to a generic IR field
6049
- * (`IRLayerReference.linkKind`); the accessor now lives at `./parent-link.ts`.
6003
+ * Parent lifetime semantics are represented directly by the generic IR relation.
6050
6004
  */ function readLottieSpecific(layer) {
6051
6005
  var _layer_specific;
6052
6006
  return (_layer_specific = layer.specific) == null ? void 0 : _layer_specific.lottie;
6053
6007
  }
6054
6008
 
6055
- export { DEFAULT_CAMERA_FOV, DEFAULT_CAMERA_Z, DEFAULT_CLIP_MODE, assembleScene, buildTimelineTracks, convert2dTo3dPoint, convertGEToIR, convertIRToGE, createId, estimateTextWidth, get2dTo3dRatio, irPositionKeyframesToGE, irScalarKeyframesToGE, irVectorKeyframesToGE, isTransformOnlyParentLink, lerp, normalizeChildVisibilityByParentDelays, readLottieSpecific, roundNumber, shouldUseKeyframeMode, toDataPath };
6009
+ export { DEFAULT_CAMERA_FOV, DEFAULT_CAMERA_Z, DEFAULT_CLIP_MODE, assembleScene, buildNormalizedColorAnimation, buildNormalizedTransformAnimation, buildTimelineTracks, convert2dTo3dPoint, convertGEToIR, convertIRToGE, createId, estimateTextWidth, get2dTo3dRatio, getEffectsCompositionSpecific, getEffectsLayerSpecific, hasEffectsAnimationPassthrough, hasEffectsColorAnimationPassthrough, hasIndependentParentLifetime, invalidateEffectsLayerSpecific, irPositionKeyframesToGE, irScalarKeyframesToGE, irVectorKeyframesToGE, lerp, normalizeChildVisibilityByParentDelays, readLottieSpecific, roundNumber, shouldUseKeyframeMode, toDataPath };
6056
6010
  //# sourceMappingURL=index.mjs.map