@vvfx/ge-ir 0.0.1-beta.4 → 0.0.1-beta.6

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.
@@ -1,20 +1,18 @@
1
1
  /**
2
- * Unified IR → GE scene generation pipeline. All IR sources (lottie, dsl2ge,
3
- * future) are processed here. Source-specific features are gated by data
4
- * existence (e.g. mask, precomp, shape layers are absent in dsl IR and
5
- * naturally skipped); `ir.source` branches are used only for B-class real
6
- * semantic differences that cannot be expressed via data existence.
2
+ * Unified IR → GE scene generation pipeline. All IR sources (lottie, dsl,
3
+ * future) are processed here. Normalized animation semantics select the GE
4
+ * lowering plan; source-specific payloads are consulted only for fidelity
5
+ * restoration such as GE hierarchy, mask and precomp graphs.
7
6
  */
8
- import type { IRComposition } from '@vvfx/animation-ir';
7
+ import { type IRComposition } from '@vvfx/animation-ir';
9
8
  import type { JSONScene } from '@galacean/effects-specification';
10
9
  /**
11
10
  * Convert an IR composition to a GE JSONScene.
12
11
  *
13
- * All IR sources (lottie, dsl2ge, future) go through this single unified
14
- * pipeline. Source-specific features are gated by data existence (e.g. mask,
15
- * precomp, shape layers are absent in dsl IR and naturally skipped);
16
- * `ir.source` branches are used only for B-class real semantic differences
17
- * that cannot be expressed via data existence.
12
+ * All IR sources (lottie, dsl, future) go through this single unified
13
+ * pipeline. Normalized animation semantics select the GE lowering plan;
14
+ * source-specific payloads may preserve authored GE hierarchy or restore
15
+ * mask and precomp graphs.
18
16
  *
19
17
  * The optional `duration` arg is a caller override; lottie2ge currently passes
20
18
  * the duration computed from the raw json upstream. When omitted, the
@@ -2,12 +2,12 @@ import type { IRComposition, IRLayerNode, IRShapeData } from '@vvfx/animation-ir
2
2
  import type { LayerProcessingContext } from '../types';
3
3
  export declare function buildComponent(layer: IRLayerNode, itemId: string, anchorScaleX: number, anchorScaleY: number, ctx: LayerProcessingContext): string | undefined;
4
4
  /**
5
- * Copy the raw GE mask object (from ge2ir passthrough metadata) onto a freshly
5
+ * Copy the raw GE mask object (from specific.effects passthrough) onto a freshly
6
6
  * built component, as a top-level `mask` field matching GE's structure. For a
7
7
  * mask SOURCE this carries `{isMask:true, alphaMaskEnabled}`; for a TARGET it
8
8
  * carries `{isMask:false, reference, inverted}` where `reference.id` is still
9
- * the stale GE1 component id — `applyGeMaskRemap` rewrites it in post-process.
10
- * No-op for non-ge sources (Lottie masks go through the IRLayerNode.mask path).
9
+ * the stale GE1 component id — `applyGEMaskRemap` rewrites it in post-process.
10
+ * No-op for non-GE sources (Lottie masks go through the IRLayerNode.mask path).
11
11
  */
12
- export declare function applyGeMask(ir: IRComposition, layer: IRLayerNode, component: Record<string, unknown>): void;
12
+ export declare function applyGEMask(ir: IRComposition, layer: IRLayerNode, component: Record<string, unknown>): void;
13
13
  export declare function buildShapeOptions(shapeData: IRShapeData, ratio: number, ctx?: LayerProcessingContext): Record<string, unknown>;
@@ -4,14 +4,14 @@ import type { IRLayerNode } from '@vvfx/animation-ir';
4
4
  * the time-stretch and offset semantics from `IRTimeContext`.
5
5
  *
6
6
  * dsl-source IRs leave `stretch` and `offsetFrame` undefined, in which case
7
- * this collapses to `delay = startFrame / fr` and
7
+ * this collapses to `delay = (startFrame - compositionInPoint) / fr` and
8
8
  * `duration = (endFrame - startFrame) / fr` — exactly what dsl2ge used to
9
9
  * compute inline.
10
10
  *
11
11
  * Zero/negative durations are clamped to a 0.0001 epsilon so GE keeps the
12
12
  * activation track alive for the (degenerate) layer.
13
13
  */
14
- export declare function computeLayerTiming(layer: IRLayerNode, frameRate: number): {
14
+ export declare function computeLayerTiming(layer: IRLayerNode, frameRate: number, compositionInPoint?: number): {
15
15
  delay: number;
16
16
  layerDuration: number;
17
17
  };
@@ -2,7 +2,7 @@ export { convertIRToGE } from './assembly/generator';
2
2
  export type { DataPath, JSONScene } from '@galacean/effects-specification';
3
3
  export { readLottieSpecific } from './ir/specific';
4
4
  export type { LottieLayerSpecific } from './ir/specific';
5
- export { isTransformOnlyParentLink } from './ir/parent-link';
5
+ export { hasIndependentParentLifetime } from './ir/parent-link';
6
6
  export type { ECSCollector, GEDataPath, GEItem, GEPoolIndex, GERecord, GETransform } from '../shared/types';
7
7
  export type { ProcessedItem, TransformAnimation } from './types';
8
8
  export { assembleScene } from './assembly/scene-assembly';
@@ -10,15 +10,15 @@ import type { IRComposition, IRLayerNode } from '@vvfx/animation-ir';
10
10
  * sits at the anchor; the sprite carries the visual content and is parented
11
11
  * to the null with an anchor-relative position.
12
12
  *
13
- * The split decision is read from `IRLayerNode.transformSplit` when set by the
14
- * producer; for lottie IR without that flag the accessor falls back to
15
- * runtime geometry checks (rotation/scale animation + off-center anchor).
13
+ * The decision is a GE lowering detail derived from source-neutral IR geometry
14
+ * and animation semantics. GE-originated IR may additionally preserve its
15
+ * original hierarchy through `specific.effects.sourceHierarchy`.
16
16
  */
17
- export type AnchorStrategy = {
17
+ export type GELayerLoweringPlan = {
18
18
  kind: 'inline-anchor';
19
19
  anchorScale: [number, number];
20
20
  anchorOffset: [number, number];
21
21
  } | {
22
22
  kind: 'split-null-sprite';
23
23
  };
24
- export declare function getStaticAnchorBehavior(ir: IRComposition, layer: IRLayerNode): AnchorStrategy;
24
+ export declare function planGELayerLowering(ir: IRComposition, layer: IRLayerNode): GELayerLoweringPlan;
@@ -1,13 +1,7 @@
1
- import type { IRLayerNode } from '@vvfx/animation-ir';
1
+ import type { IRLayerNode, IRLocalBounds } from '@vvfx/animation-ir';
2
2
  /**
3
- * Read the producer-baked display size from an IR layer. When the producer
4
- * did not resolve one, returns `fallback` (defaults to `{ 1, 1 }` — the
5
- * value every existing call site was using inline).
3
+ * Read source-neutral local bounds from an IR layer. When the producer
4
+ * did not resolve one, returns `fallback` (defaults to bounds at the origin
5
+ * with a one-pixel width and height).
6
6
  */
7
- export declare function getLayerDisplaySize(layer: IRLayerNode, fallback?: {
8
- width: number;
9
- height: number;
10
- }): {
11
- width: number;
12
- height: number;
13
- };
7
+ export declare function getLayerLocalBounds(layer: IRLayerNode, fallback?: IRLocalBounds): IRLocalBounds;
@@ -1,11 +1,11 @@
1
- import type { IRLayerNode } from '@vvfx/animation-ir';
1
+ import type { IRLayerNode, IRLocalBounds } from '@vvfx/animation-ir';
2
2
  import type { IRToGETimeContext } from '../builders/bezier';
3
3
  import type { LayerProcessingContext } from '../types';
4
4
  export { computeLayerTiming } from '../builders/timing';
5
5
  export declare function computeStaticPosition(layer: IRLayerNode, ctx: LayerProcessingContext, anchorOffsetX?: number, anchorOffsetY?: number): [number, number, number];
6
6
  export declare function getStaticAnchor(layer: IRLayerNode): [number, number];
7
- export declare function applyScalePositionAdjust(position: [number, number, number], layer: IRLayerNode, layerWidth: number, layerHeight: number, ratio: number): [number, number, number];
8
- export declare function computeAnchorInfo(layer: IRLayerNode, layerWidth: number, layerHeight: number): {
7
+ export declare function applyScalePositionAdjust(position: [number, number, number], layer: IRLayerNode, bounds: IRLocalBounds, ratio: number): [number, number, number];
8
+ export declare function computeAnchorInfo(layer: IRLayerNode, bounds: IRLocalBounds): {
9
9
  anchorScaleX: number;
10
10
  anchorScaleY: number;
11
11
  anchorOffsetX: number;
@@ -1,15 +1,6 @@
1
1
  import type { IRLayerNode } from '@vvfx/animation-ir';
2
2
  /**
3
- * True when the layer's parent only inherits transform — visibility and
4
- * lifetime are independent of the parent's window.
5
- *
6
- * Reads `IRLayerReference.linkKind` directly. Producers that don't set the
7
- * field (e.g. dsl2ge) effectively get `false` here, which is the correct
8
- * default for "full" parent containment.
9
- *
10
- * Lottie's parser sets `linkKind: 'transform-only'` when a child's parent
11
- * relationship comes from `layer.parent` rather than precomp expansion;
12
- * `applyPostProcess` consumes this to decide whether to advance the parent's
13
- * activation track when the child starts earlier.
3
+ * True when the child lifetime is independent of its transform parent's
4
+ * clipping window. An omitted lifetimeMode keeps the legacy clipped default.
14
5
  */
15
- export declare function isTransformOnlyParentLink(layer: IRLayerNode): boolean;
6
+ export declare function hasIndependentParentLifetime(layer: IRLayerNode): boolean;
@@ -2,12 +2,10 @@
2
2
  * Read-side accessors for the `IRLayerNode.specific.lottie` slot.
3
3
  *
4
4
  * The shared generator must not branch on lottie semantics directly — every
5
- * lottie-specific read goes through these helpers so the rest of `ir2ge` stays
5
+ * Lottie-specific reads go through these helpers so the rest of ir2ge stays
6
6
  * agnostic to the producing tool.
7
7
  *
8
- * `isTransformOnlyParentLink` used to live here, reading
9
- * `specific.lottie.explicitParent`. It was promoted to a generic IR field
10
- * (`IRLayerReference.linkKind`); the accessor now lives at `./parent-link.ts`.
8
+ * Parent lifetime semantics are represented directly by the generic IR relation.
11
9
  */
12
10
  import type { IRLayerNode, LottieLayerSpecific } from '@vvfx/animation-ir';
13
11
  export type { LottieLayerSpecific };
@@ -3,10 +3,10 @@ import type { ECSCollector } from '../types';
3
3
  * Remap GE mask references after ge2ir round-trip.
4
4
  *
5
5
  * ge2ir carries each masked component's raw `mask` object verbatim (via
6
- * `IRLayerNode.metadata.ge2ir.geMask`), but `mask.reference.id` still points at the
6
+ * `IRLayerNode.specific.effects.mask`), but `mask.reference.id` still points at the
7
7
  * ORIGINAL (GE1) mask-source component id, which ir2ge has regenerated. This
8
8
  * pass rewrites every target's `reference.id` from the stale GE1 id to the
9
- * freshly-minted GE2 component id, using the `geComponentId → newId` map built
9
+ * freshly-minted GE2 component id, using the `GEComponentId → newId` map built
10
10
  * during the layer walk.
11
11
  *
12
12
  * Mirrors `applyParenting`: an IR-side reference (here the stale id stored in
@@ -15,4 +15,4 @@ import type { ECSCollector } from '../types';
15
15
  * e.g. dropped by the null+sprite merger) have their `mask` removed so the GE
16
16
  * runtime doesn't choke on an id that resolves to nothing.
17
17
  */
18
- export declare function applyGeMaskRemap(collector: ECSCollector, geComponentIdToNewId: Record<string, string>): void;
18
+ export declare function applyGEMaskRemap(collector: ECSCollector, GEComponentIdToNewId: Record<string, string>): void;
@@ -21,7 +21,7 @@ export interface PostProcessState {
21
21
  height?: number;
22
22
  frameRate?: number;
23
23
  /** GE1→GE2 component-id map for remapping ge2ir mask passthrough references. */
24
- geComponentIdToNewId?: Record<string, string>;
24
+ GEComponentIdToNewId?: Record<string, string>;
25
25
  }
26
26
  /**
27
27
  * Run post-emit mutations on the collector before `assembleScene`.
@@ -6,21 +6,21 @@ interface NormalizeVisibilityParams {
6
6
  items: Record<string, unknown>[];
7
7
  miscs: Record<string, unknown>[];
8
8
  processedItems: ProcessedItemRef[];
9
- /** Item IDs whose parent-child relationship only inherits transform, not visibility. */
10
- transformOnlyParentLinkItemIds?: Set<string>;
9
+ /** Child item IDs whose lifetime is independent of the transform parent's window. */
10
+ independentLifetimeChildItemIds?: Set<string>;
11
11
  roundNumber: (value: number, precision: number) => number;
12
12
  }
13
13
  /**
14
14
  * Normalize child visibility delays so children never appear before their parent.
15
15
  *
16
16
  * Topologically sorts items (parent-first) so by the time we process any child
17
- * its parent's delay/duration are already finalized. For transform-only
18
- * parent-link children, we instead advance the parent's delay/duration so the
17
+ * its parent's delay/duration are already finalized. For children with an
18
+ * independent lifetime, we instead advance the parent's delay/duration so the
19
19
  * child can render from its own start time (GE requires the parent to be active
20
20
  * for transform inheritance, but the parent-link relationship does not propagate
21
21
  * visibility). Only the ActivationTrack clip is updated; TransformTrack
22
22
  * keyframes are normalized to the original time range and would hold their
23
23
  * first frame value if the track started earlier.
24
24
  */
25
- export declare function normalizeChildVisibilityByParentDelays({ items, miscs, processedItems, transformOnlyParentLinkItemIds, roundNumber, }: NormalizeVisibilityParams): void;
25
+ export declare function normalizeChildVisibilityByParentDelays({ items, miscs, processedItems, independentLifetimeChildItemIds, roundNumber, }: NormalizeVisibilityParams): void;
26
26
  export {};
@@ -22,7 +22,7 @@ export interface TransformAnimation {
22
22
  }
23
23
  /** Per-layer context threaded through the unified layer processors. */
24
24
  export interface LayerProcessingContext {
25
- /** Full IR composition; needed by shared accessors that dispatch on `ir.source`. */
25
+ /** Full IR composition; needed for normalized semantics and guarded fidelity payloads. */
26
26
  ir: IRComposition;
27
27
  collector: ECSCollector;
28
28
  processedItems: ProcessedItem[];
@@ -0,0 +1,15 @@
1
+ import type { EffectsCompositionSpecific, EffectsLayerSpecific, IRComposition, IRLayerNode } from '@vvfx/animation-ir';
2
+ export type EffectsLayerSpecificInvalidationTarget = 'position' | 'rotation' | 'scale' | 'color' | 'opacity' | 'anchor' | 'mask' | 'precomp';
3
+ /** Read GE layer passthrough data. */
4
+ export declare function getEffectsLayerSpecific(ir: IRComposition, layer: IRLayerNode): EffectsLayerSpecific | undefined;
5
+ /** Read GE composition passthrough data. */
6
+ export declare function getEffectsCompositionSpecific(ir: IRComposition): EffectsCompositionSpecific | undefined;
7
+ /**
8
+ * Remove raw GE values that would otherwise shadow an edited normalized field.
9
+ * Empty `effects` and `specific` containers are pruned so serialization stays clean.
10
+ */
11
+ export declare function invalidateEffectsLayerSpecific(layer: IRLayerNode, target: EffectsLayerSpecificInvalidationTarget): (keyof EffectsLayerSpecific)[];
12
+ /** Whether invalidating opacity would also discard a GE color animation. */
13
+ export declare function hasEffectsColorAnimationPassthrough(layer: IRLayerNode): boolean;
14
+ /** Whether the layer retains any raw GE animation payload. */
15
+ export declare function hasEffectsAnimationPassthrough(layer: IRLayerNode): boolean;
@@ -0,0 +1,2 @@
1
+ /** Return a detached value whose runtime shape is stable across JSON persistence. */
2
+ export declare function normalizeJsonValue<T>(value: T): T;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vvfx/ge-ir",
3
- "version": "0.0.1-beta.4",
3
+ "version": "0.0.1-beta.6",
4
4
  "description": "Galacean Effects and animation IR bidirectional conversion package",
5
5
  "module": "./dist/index.mjs",
6
6
  "main": "./dist/index.js",
@@ -18,7 +18,7 @@
18
18
  "dependencies": {
19
19
  "@galacean/effects-specification": "^2.8.0",
20
20
  "uuid": "^13.0.0",
21
- "@vvfx/animation-ir": "0.0.1-beta.4"
21
+ "@vvfx/animation-ir": "0.0.1-beta.6"
22
22
  },
23
23
  "contributors": [
24
24
  {
@@ -37,6 +37,7 @@
37
37
  "build:module": "rollup -c --bundleConfigAsCjs",
38
38
  "build:declaration": "tsc -b ../animation-ir tsconfig.json --emitDeclarationOnly",
39
39
  "clean": "rimraf dist *.tsbuildinfo",
40
- "test": "vitest run"
40
+ "test": "vitest run",
41
+ "verify:dist": "node scripts/verify-dist.mjs"
41
42
  }
42
43
  }
@@ -1,30 +0,0 @@
1
- import type { IRComposition, IRLayerNode } from '@vvfx/animation-ir';
2
- /**
3
- * Round-trip passthrough contract written by ge2ir and consumed by ir2ge.
4
- *
5
- * Supported keys:
6
- * positionOverLifetime, rotationOverLifetime, sizeOverLifetime,
7
- * colorOverLifetime, geComponentId, geMask, precompRefId,
8
- * precompContent.
9
- */
10
- export interface GE2IRPassthroughMetadata {
11
- positionOverLifetime?: Record<string, unknown>;
12
- rotationOverLifetime?: Record<string, unknown>;
13
- sizeOverLifetime?: Record<string, unknown>;
14
- colorOverLifetime?: Record<string, unknown>;
15
- geComponentId?: string;
16
- geMask?: Record<string, unknown>;
17
- precompRefId?: string;
18
- precompContent?: Record<string, unknown>;
19
- }
20
- /**
21
- * Composition-level GE metadata. `sourceScene` is intentionally opaque: it is
22
- * used only by ir2ge to restore untouched precomp resource pools that are
23
- * outside the single-composition editable IR.
24
- */
25
- export interface GE2IRCompositionMetadata {
26
- sourceCompositionId?: string;
27
- sourceScene?: Record<string, unknown>;
28
- }
29
- export declare function readGE2IRMetadata(ir: IRComposition, layer: IRLayerNode): GE2IRPassthroughMetadata | undefined;
30
- export declare function readGE2IRCompositionMetadata(ir: IRComposition): GE2IRCompositionMetadata | undefined;