@vvfx/dsl-ir 0.0.1-beta.10
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/LICENSE +22 -0
- package/README.md +240 -0
- package/dist/common/particle-expression.d.ts +6 -0
- package/dist/common/transform.d.ts +11 -0
- package/dist/common/utils.d.ts +19 -0
- package/dist/dsl/number-expression.d.ts +7 -0
- package/dist/dsl/parse-particle-shape.d.ts +2 -0
- package/dist/dsl/particle-shape.d.ts +86 -0
- package/dist/dsl/schema.d.ts +100 -0
- package/dist/dsl/validator/index.d.ts +2 -0
- package/dist/dsl/validator/labels.d.ts +1 -0
- package/dist/dsl/validator/particle.d.ts +8 -0
- package/dist/dsl/validator/types.d.ts +33 -0
- package/dist/dsl/validator/validation.d.ts +15 -0
- package/dist/dsl2ir/assemble.d.ts +3 -0
- package/dist/dsl2ir/content-conversion.d.ts +11 -0
- package/dist/dsl2ir/convert-layers.d.ts +4 -0
- package/dist/dsl2ir/convert-particle.d.ts +4 -0
- package/dist/dsl2ir/convert-property.d.ts +6 -0
- package/dist/dsl2ir/convert.d.ts +4 -0
- package/dist/dsl2ir/recombine/compare-properties.d.ts +18 -0
- package/dist/dsl2ir/recombine/convert.d.ts +5 -0
- package/dist/dsl2ir/recombine/update-ir.d.ts +6 -0
- package/dist/dsl2ir/recombine/update-layer-order.d.ts +5 -0
- package/dist/dsl2ir/recombine/update-layer.d.ts +4 -0
- package/dist/dsl2ir/recombine/update-particle.d.ts +4 -0
- package/dist/dsl2ir/types.d.ts +35 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +4424 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +4418 -0
- package/dist/index.mjs.map +1 -0
- package/dist/ir2dsl/assemble.d.ts +5 -0
- package/dist/ir2dsl/convert-layers.d.ts +10 -0
- package/dist/ir2dsl/convert-particle.d.ts +4 -0
- package/dist/ir2dsl/convert.d.ts +4 -0
- package/dist/ir2dsl/expand-precompositions.d.ts +6 -0
- package/dist/ir2dsl/types.d.ts +45 -0
- package/package.json +45 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT LICENSE
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019-present Ant Group Co., Ltd. https://www.antgroup.com/
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# @vvfx/dsl-ir
|
|
2
|
+
|
|
3
|
+
Bidirectional conversion and validation for editable DSL and source-neutral Animation IR.
|
|
4
|
+
|
|
5
|
+
The package owns the DSL boundary only. Importing and exporting Galacean Effects JSONScene, including preservation of
|
|
6
|
+
GE-only data, is handled by `@vvfx/ge-ir`.
|
|
7
|
+
|
|
8
|
+
## Contents
|
|
9
|
+
|
|
10
|
+
- [Install](#install)
|
|
11
|
+
- [Usage](#usage)
|
|
12
|
+
- [Data and editing boundaries](#data-and-editing-boundaries)
|
|
13
|
+
- [API](#api)
|
|
14
|
+
- [Project structure](#project-structure)
|
|
15
|
+
- [Contributing](#contributing)
|
|
16
|
+
- [License](#license)
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
Node.js 22 or later is required.
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pnpm add @vvfx/dsl-ir
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Install `@vvfx/ge-ir` as well when working with Galacean Effects:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pnpm add @vvfx/dsl-ir @vvfx/ge-ir
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
### Convert DSL to Animation IR
|
|
35
|
+
|
|
36
|
+
A DSL document created from scratch does not need preserved data:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import { convertDSLToIR, type Composition } from '@vvfx/dsl-ir';
|
|
40
|
+
|
|
41
|
+
const dsl: Composition = {
|
|
42
|
+
meta: {
|
|
43
|
+
name: 'example',
|
|
44
|
+
duration: 2,
|
|
45
|
+
fps: 30,
|
|
46
|
+
width: 512,
|
|
47
|
+
height: 512,
|
|
48
|
+
loop: true,
|
|
49
|
+
background_color: null,
|
|
50
|
+
},
|
|
51
|
+
layers: [],
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const { scene, diagnostics } = convertDSLToIR(dsl);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
DSL timing is expressed in seconds. Animation IR timing is expressed in frames, using `dsl.meta.fps` as the frame
|
|
58
|
+
rate. Adjacent animation segments may have different end and start values to represent an instantaneous reset.
|
|
59
|
+
|
|
60
|
+
DSL uses the initial `position.z` as a sibling stacking value: higher values render above lower values.
|
|
61
|
+
Array order does not control stacking. Equal values retain the imported order with preserved data; standalone DSL
|
|
62
|
+
uses layer IDs as a deterministic tie-breaker. Imported DSL exposes painter ranks in Z, while the paired sidecar
|
|
63
|
+
retains source spatial depth. Changing only Z reorders whole sibling subtrees without moving them toward the camera.
|
|
64
|
+
Standalone DSL positions are placed on the IR 2D plane (`z = 0`). Animated position uses its first segment's start Z
|
|
65
|
+
as a fixed rank. Editing XY on a layer with varying source depth retains depth at matching keyframe times;
|
|
66
|
+
changing that position's timing or replacing it with a static position is unsupported.
|
|
67
|
+
|
|
68
|
+
### Convert Animation IR to DSL
|
|
69
|
+
|
|
70
|
+
`convertIRToDSL` always returns the editable DSL, diagnostics, and an `IRToDSLPreservedData` sidecar:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { convertIRToDSL } from '@vvfx/dsl-ir';
|
|
74
|
+
|
|
75
|
+
const { dsl, preservedData, diagnostics } = convertIRToDSL(scene);
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The sidecar retains IR data and write-back relationships that DSL cannot represent completely. It is adapter-owned
|
|
79
|
+
data rather than business data. It can be persisted with `JSON.stringify` and `JSON.parse`, but applications should not
|
|
80
|
+
modify or depend on its internal structure.
|
|
81
|
+
|
|
82
|
+
### Write edited DSL back to its source IR
|
|
83
|
+
|
|
84
|
+
Pass the DSL and sidecar produced by the same conversion back to `convertDSLToIR`:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
const converted = convertIRToDSL(scene);
|
|
88
|
+
const editedDSL = structuredClone(converted.dsl);
|
|
89
|
+
|
|
90
|
+
editedDSL.layers[0].opacity = { a: false, value: 0.5 };
|
|
91
|
+
|
|
92
|
+
const { scene: updatedScene, diagnostics } = convertDSLToIR(editedDSL, {
|
|
93
|
+
preservedData: converted.preservedData,
|
|
94
|
+
onChange: change => console.info(change),
|
|
95
|
+
onWarning: warning => console.warn(warning),
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
When `preservedData` is supplied, the converter applies DSL-representable changes to the retained IR snapshot while
|
|
100
|
+
keeping information outside the DSL contract. `strict: true` promotes conversion warnings and unsupported edits to
|
|
101
|
+
errors.
|
|
102
|
+
|
|
103
|
+
### Combine with `@vvfx/ge-ir`
|
|
104
|
+
|
|
105
|
+
The application coordinates the adapters and keeps their sidecars paired.
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import { convertGEToIR, convertIRToGE, updateGEToIRPreservedData } from '@vvfx/ge-ir';
|
|
109
|
+
import { convertIRToDSL, convertDSLToIR } from '@vvfx/dsl-ir';
|
|
110
|
+
|
|
111
|
+
const ge = convertGEToIR(geScene, { frameRate: 30 });
|
|
112
|
+
const dsl = convertIRToDSL(ge.scene);
|
|
113
|
+
const edited = structuredClone(dsl.dsl);
|
|
114
|
+
|
|
115
|
+
edited.meta.duration = 4;
|
|
116
|
+
|
|
117
|
+
const updated = convertDSLToIR(edited, { preservedData: dsl.preservedData });
|
|
118
|
+
const preservedData = updateGEToIRPreservedData(ge.scene, updated.scene, ge.preservedData);
|
|
119
|
+
const restored = convertIRToGE(updated.scene, { preservedData });
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Particle layers
|
|
123
|
+
|
|
124
|
+
`Composition` and `Layer` define the DSL for all layer types. Particle layers use `type: 'particle'` and the same
|
|
125
|
+
transform semantics as other layers. All layers may omit `time_range`, `rotation`, `scale`, `anchor`, and
|
|
126
|
+
`opacity`. Preserved write-back leaves omitted channels unchanged; fresh conversion uses composition timing and
|
|
127
|
+
identity transforms. Input DSL objects are not completed or mutated. `convertIRToDSL` produces complete layers;
|
|
128
|
+
`validateDSL` and `convertDSLToIR` validate and convert them through the common pipeline. `parseParticleShape` parses optional,
|
|
129
|
+
read-only emitter geometry supplied by a source adapter.
|
|
130
|
+
|
|
131
|
+
Applications compose source effects, target layout, model requests and GE resource restoration through public adapter APIs.
|
|
132
|
+
|
|
133
|
+
## Data and editing boundaries
|
|
134
|
+
|
|
135
|
+
- `@vvfx/animation-ir` is the single source of truth for Animation IR types and validation.
|
|
136
|
+
- DSL is an editing-oriented subset, not a mirror of any source format schema.
|
|
137
|
+
- `convertIRToDSL` expands precompositions and stores the write-back relationships in `IRToDSLPreservedData`.
|
|
138
|
+
- Fidelity-preserving write-back supports field edits, deletion, and reordering of existing layers under the same
|
|
139
|
+
parent. Unsafe edits are reported through diagnostics or warnings.
|
|
140
|
+
- Both conversion directions validate their IR boundary. Every successfully returned `scene` satisfies the Animation
|
|
141
|
+
IR contract.
|
|
142
|
+
- External images entering Animation IR must use absolute URLs or Data URLs. DSL font families become system font
|
|
143
|
+
assets.
|
|
144
|
+
Inline SVG assets use their SVG content when `url` is omitted or empty.
|
|
145
|
+
- Particle Layers use the same `convertIRToDSL` and `convertDSLToIR` workflow as other supported Layers. Editable
|
|
146
|
+
`options` and `emission` values are represented in DSL. Source adapters may also expose an optional read-only
|
|
147
|
+
`shape` block for model context; renderer, resources, and other GE-only fields remain in the GE adapter sidecar.
|
|
148
|
+
- Burst `cycles` defaults to one; zero means unbounded repetition until the Layer ends. Repeated bursts require a
|
|
149
|
+
positive `interval` in seconds.
|
|
150
|
+
- Particle lifetime and emission values use the DSL subset of GE `NumberExpression`: constant tag `0` and
|
|
151
|
+
random-range tag `4`. IR curves are projected with diagnostics and retained in the DSL sidecar until the
|
|
152
|
+
corresponding field is edited. A projected `shape` block is not written into Animation IR; the source adapter
|
|
153
|
+
remains responsible for restoring the original emitter geometry.
|
|
154
|
+
|
|
155
|
+
Further documentation:
|
|
156
|
+
|
|
157
|
+
- [DSL format](../../docs/dsl_definition.md)
|
|
158
|
+
- [DSL and IR guide](../../docs/dsl-ir-guide.md)
|
|
159
|
+
- [DSL and IR editing capabilities](../../docs/dsl-ir-editing-capabilities.md)
|
|
160
|
+
- [DSL, IR, and GE package boundaries](../../docs/dsl-ir-ge-ir-boundary.md)
|
|
161
|
+
- [Animation IR particle contract](../../docs/particle-ir-contract.md)
|
|
162
|
+
|
|
163
|
+
## API
|
|
164
|
+
|
|
165
|
+
### Runtime exports
|
|
166
|
+
|
|
167
|
+
| Export | Purpose |
|
|
168
|
+
| --- | --- |
|
|
169
|
+
| `convertDSLToIR` | Convert a new DSL document to IR, or write DSL values back to retained IR |
|
|
170
|
+
| `convertIRToDSL` | Convert IR to editable DSL and return the sidecar needed for write-back |
|
|
171
|
+
| `validateDSL` | Validate an unknown value against the DSL contract |
|
|
172
|
+
| `DSLValidationError` | Structured error thrown when conversion cannot continue |
|
|
173
|
+
|
|
174
|
+
Core signatures:
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
function convertDSLToIR(composition: Composition, options?: ConvertDSLToIROptions): DSLToIRResult;
|
|
178
|
+
|
|
179
|
+
function convertIRToDSL(scene: IRScene): IRToDSLResult;
|
|
180
|
+
|
|
181
|
+
function validateDSL(dsl: unknown, options?: ValidateDSLOptions): DSLValidationResult;
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Type exports
|
|
185
|
+
|
|
186
|
+
All public types are imported from the package root:
|
|
187
|
+
|
|
188
|
+
- DSL schema: `AnimationCurve`, `AnimatedProperty`, `Asset`, `Composition`, `CompositionMeta`, `Description`, `Layer`,
|
|
189
|
+
`LLMSVGAsset`, `NumberExpression`, the `Particle*` types, `TextProperties`, `UserAsset`, and
|
|
190
|
+
`ValueType`.
|
|
191
|
+
- Validation: `DSLValidationIssue`, `DSLValidationResult`, and `ValidateDSLOptions`.
|
|
192
|
+
- DSL to IR: `ConvertDSLToIROptions`, `DSLToIRChange`, `DSLToIRResult`, and `DSLToIRWarning`.
|
|
193
|
+
- IR to DSL: `IRToDSLPreservedData` and `IRToDSLResult`.
|
|
194
|
+
|
|
195
|
+
Important result and option types:
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
interface IRToDSLResult {
|
|
199
|
+
dsl: Composition;
|
|
200
|
+
preservedData: IRToDSLPreservedData;
|
|
201
|
+
diagnostics: ConversionDiagnostic[];
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
interface DSLToIRResult {
|
|
205
|
+
scene: IRScene;
|
|
206
|
+
diagnostics: ConversionDiagnostic[];
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
interface ConvertDSLToIROptions {
|
|
210
|
+
preservedData?: IRToDSLPreservedData;
|
|
211
|
+
strict?: boolean;
|
|
212
|
+
onWarning?: (warning: DSLToIRWarning) => void;
|
|
213
|
+
onChange?: (change: DSLToIRChange) => void;
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Project structure
|
|
218
|
+
|
|
219
|
+
```text
|
|
220
|
+
src/
|
|
221
|
+
dsl/ DSL schema and validation
|
|
222
|
+
dsl2ir/ DSL to Animation IR
|
|
223
|
+
recombine/ Preserved-data write-back into retained IR
|
|
224
|
+
ir2dsl/ Animation IR to DSL and precomposition expansion
|
|
225
|
+
common/ Value mappings shared by both directions
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Contributing
|
|
229
|
+
|
|
230
|
+
Use repository issues for questions and bug reports. Pull requests are welcome. Run the package checks after changing
|
|
231
|
+
public behavior:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
pnpm --filter @vvfx/dsl-ir test
|
|
235
|
+
pnpm --filter @vvfx/dsl-ir build
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## License
|
|
239
|
+
|
|
240
|
+
[MIT](./LICENSE) © 2019-present Ant Group Co., Ltd.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { IRParticleScalarExpression } from '@vvfx/animation-ir';
|
|
2
|
+
import { type NumberExpression } from '../dsl/number-expression';
|
|
3
|
+
export declare function mapDSLParticleExpressionToIR(expression: NumberExpression): IRParticleScalarExpression;
|
|
4
|
+
export declare function mapIRParticleExpressionToDSL(expression: IRParticleScalarExpression): NumberExpression;
|
|
5
|
+
export declare function isDSLCompatibleParticleExpression(expression: IRParticleScalarExpression): boolean;
|
|
6
|
+
export declare function isDSLNumberExpressionEqual(left: NumberExpression, right: NumberExpression): boolean;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Layer } from '../dsl/schema';
|
|
2
|
+
/** Static defaults for absent transform channels. */
|
|
3
|
+
export declare const TRANSFORM_FALLBACK: {
|
|
4
|
+
position: [number, number, number];
|
|
5
|
+
rotation: [number, number, number];
|
|
6
|
+
scale: [number, number, number];
|
|
7
|
+
anchor: [number, number, number];
|
|
8
|
+
opacity: number;
|
|
9
|
+
};
|
|
10
|
+
export declare function getLayerStackingOrder(layer: Layer): number;
|
|
11
|
+
export declare function withPositionZ(position: Layer['position'], depth: number): Layer['position'];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { IRColor } from '@vvfx/animation-ir';
|
|
2
|
+
export declare const EPSILON = 0.000001;
|
|
3
|
+
export type HexColorInput = readonly [number, number, number] | readonly [number, number, number, number] | IRColor;
|
|
4
|
+
/** Generate a 32-character hexadecimal UUID v4. */
|
|
5
|
+
export declare function createId(): string;
|
|
6
|
+
/** Convert normalized RGB(A) channels to a canonical hex color. */
|
|
7
|
+
export declare function colorToHex(color: HexColorInput): string;
|
|
8
|
+
/** Normalize supported hex forms for semantic comparison. */
|
|
9
|
+
export declare function normalizeHex(hexColor: string): string;
|
|
10
|
+
/** Compare nullable hex colors after normalization. */
|
|
11
|
+
export declare function isHexEqual(leftHex: string | null | undefined, rightHex: string | null | undefined): boolean;
|
|
12
|
+
export declare function isValidHexColor(value: unknown): value is string;
|
|
13
|
+
export declare function hexToIRColor(hexColor: string): IRColor;
|
|
14
|
+
export declare function resolveFiniteNumber(value: unknown, fallback: number): number;
|
|
15
|
+
export declare function secondsToFrames(seconds: number, frameRate: number): number;
|
|
16
|
+
export declare function isScalarEqual(leftValue: number, rightValue: number): boolean;
|
|
17
|
+
export declare function isNumberArrayEqual(leftValues: readonly number[], rightValues: readonly number[]): boolean;
|
|
18
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
19
|
+
export declare function toJSONPath(path: string): string;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare enum ValueType {
|
|
2
|
+
CONSTANT = 0,
|
|
3
|
+
RANDOM = 4
|
|
4
|
+
}
|
|
5
|
+
export type NumberExpression = ConstantNumber | RandomValue;
|
|
6
|
+
export type ConstantNumber = [type: ValueType.CONSTANT, value: number];
|
|
7
|
+
export type RandomValue = [type: ValueType.RANDOM, value: [min: number, max: number]];
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export type ParticleShapeArcMode = 0 | 1 | 2 | 3;
|
|
2
|
+
export type ParticleShapeName = ParticleShape['shape'];
|
|
3
|
+
/** Source expression retained only to describe emitter direction. */
|
|
4
|
+
export type ParticleShapeNumberExpression = [type: number, value: unknown];
|
|
5
|
+
interface ParticleShapeBase {
|
|
6
|
+
arcMode: ParticleShapeArcMode;
|
|
7
|
+
alignSpeedDirection?: boolean;
|
|
8
|
+
upDirection?: [x: number, y: number, z: number];
|
|
9
|
+
turbulenceX?: ParticleShapeNumberExpression;
|
|
10
|
+
turbulenceY?: ParticleShapeNumberExpression;
|
|
11
|
+
turbulenceZ?: ParticleShapeNumberExpression;
|
|
12
|
+
}
|
|
13
|
+
interface ParticleShapeNone {
|
|
14
|
+
type: 0;
|
|
15
|
+
shape: 'None';
|
|
16
|
+
turbulenceX?: ParticleShapeNumberExpression;
|
|
17
|
+
turbulenceY?: ParticleShapeNumberExpression;
|
|
18
|
+
turbulenceZ?: ParticleShapeNumberExpression;
|
|
19
|
+
upDirection?: number[];
|
|
20
|
+
}
|
|
21
|
+
interface ParticleShapeSphere extends ParticleShapeBase {
|
|
22
|
+
type: 1;
|
|
23
|
+
shape: 'Sphere';
|
|
24
|
+
radius: number;
|
|
25
|
+
arc: number;
|
|
26
|
+
}
|
|
27
|
+
interface ParticleShapeCone extends ParticleShapeBase {
|
|
28
|
+
type: 2;
|
|
29
|
+
shape: 'Cone';
|
|
30
|
+
angle: number;
|
|
31
|
+
radius: number;
|
|
32
|
+
arc: number;
|
|
33
|
+
}
|
|
34
|
+
interface ParticleShapeHemisphere extends ParticleShapeBase {
|
|
35
|
+
type: 3;
|
|
36
|
+
shape: 'Hemisphere';
|
|
37
|
+
radius: number;
|
|
38
|
+
arc: number;
|
|
39
|
+
}
|
|
40
|
+
interface ParticleShapeCircle extends ParticleShapeBase {
|
|
41
|
+
type: 4;
|
|
42
|
+
shape: 'Circle';
|
|
43
|
+
radius: number;
|
|
44
|
+
arc: number;
|
|
45
|
+
}
|
|
46
|
+
interface ParticleShapeDonut extends ParticleShapeBase {
|
|
47
|
+
type: 5;
|
|
48
|
+
shape: 'Donut';
|
|
49
|
+
radius: number;
|
|
50
|
+
donutRadius: number;
|
|
51
|
+
arc: number;
|
|
52
|
+
}
|
|
53
|
+
interface ParticleShapeRectangle extends ParticleShapeBase {
|
|
54
|
+
type: 6;
|
|
55
|
+
shape: 'Rectangle';
|
|
56
|
+
width: number;
|
|
57
|
+
height: number;
|
|
58
|
+
}
|
|
59
|
+
interface ParticleShapeRectangleEdge extends ParticleShapeBase {
|
|
60
|
+
type: 7;
|
|
61
|
+
shape: 'RectangleEdge';
|
|
62
|
+
width: number;
|
|
63
|
+
height: number;
|
|
64
|
+
}
|
|
65
|
+
interface ParticleShapeEdge extends ParticleShapeBase {
|
|
66
|
+
type: 8;
|
|
67
|
+
shape: 'Edge';
|
|
68
|
+
width: number;
|
|
69
|
+
}
|
|
70
|
+
export interface ParticleTextureShapeDetail {
|
|
71
|
+
width: number;
|
|
72
|
+
height: number;
|
|
73
|
+
block: [width: number, height: number];
|
|
74
|
+
anchors: number[];
|
|
75
|
+
}
|
|
76
|
+
interface ParticleShapeTexture extends ParticleShapeBase {
|
|
77
|
+
type: 9;
|
|
78
|
+
shape: 'Texture';
|
|
79
|
+
detail: ParticleTextureShapeDetail;
|
|
80
|
+
random: number;
|
|
81
|
+
width: number;
|
|
82
|
+
height: number;
|
|
83
|
+
}
|
|
84
|
+
/** Particle emitter geometry projected from a source adapter for model context. */
|
|
85
|
+
export type ParticleShape = ParticleShapeNone | ParticleShapeSphere | ParticleShapeCone | ParticleShapeHemisphere | ParticleShapeCircle | ParticleShapeDonut | ParticleShapeRectangle | ParticleShapeRectangleEdge | ParticleShapeEdge | ParticleShapeTexture;
|
|
86
|
+
export {};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { NumberExpression } from './number-expression';
|
|
2
|
+
import type { ParticleShape } from './particle-shape';
|
|
3
|
+
export interface AnimationCurve {
|
|
4
|
+
t: [number, number];
|
|
5
|
+
start: number[];
|
|
6
|
+
end: number[];
|
|
7
|
+
easing: [number, number, number, number];
|
|
8
|
+
in?: [number, number, number];
|
|
9
|
+
out?: [number, number, number];
|
|
10
|
+
}
|
|
11
|
+
export type AnimatedProperty = {
|
|
12
|
+
a: false;
|
|
13
|
+
value: number | number[];
|
|
14
|
+
} | {
|
|
15
|
+
a: true;
|
|
16
|
+
animation: AnimationCurve[];
|
|
17
|
+
};
|
|
18
|
+
export interface Description {
|
|
19
|
+
self: string;
|
|
20
|
+
composite: string;
|
|
21
|
+
}
|
|
22
|
+
export interface UserAsset {
|
|
23
|
+
source: 'user';
|
|
24
|
+
url: string;
|
|
25
|
+
prompt: '';
|
|
26
|
+
}
|
|
27
|
+
export interface LLMSVGAsset {
|
|
28
|
+
source: 'llm_svg';
|
|
29
|
+
format: 'svg';
|
|
30
|
+
svg: string;
|
|
31
|
+
prompt: string;
|
|
32
|
+
url?: string;
|
|
33
|
+
}
|
|
34
|
+
export type Asset = UserAsset | LLMSVGAsset;
|
|
35
|
+
export interface TextProperties {
|
|
36
|
+
content: string;
|
|
37
|
+
font?: string;
|
|
38
|
+
size: number;
|
|
39
|
+
color: string;
|
|
40
|
+
align?: 'left' | 'center' | 'right';
|
|
41
|
+
line_height?: number;
|
|
42
|
+
letter_spacing?: number;
|
|
43
|
+
}
|
|
44
|
+
export interface ParticleOptions {
|
|
45
|
+
maxCount: number;
|
|
46
|
+
startLifetime: NumberExpression;
|
|
47
|
+
}
|
|
48
|
+
export interface ParticleBurst {
|
|
49
|
+
time: number;
|
|
50
|
+
count: number;
|
|
51
|
+
/** Total cycles. Omitted means one cycle; zero means unbounded repetition. */
|
|
52
|
+
cycles?: number;
|
|
53
|
+
/** Seconds between repeated cycles. Required to be positive when repetition is enabled. */
|
|
54
|
+
interval?: number;
|
|
55
|
+
}
|
|
56
|
+
export interface ParticleEmissionProperties {
|
|
57
|
+
rateOverTime: NumberExpression;
|
|
58
|
+
bursts?: ParticleBurst[];
|
|
59
|
+
}
|
|
60
|
+
export interface ParticleProperties {
|
|
61
|
+
/** Read-only emitter geometry supplied by source adapters when available. */
|
|
62
|
+
shape?: ParticleShape;
|
|
63
|
+
options: ParticleOptions;
|
|
64
|
+
emission: ParticleEmissionProperties;
|
|
65
|
+
}
|
|
66
|
+
export interface Layer {
|
|
67
|
+
id: string;
|
|
68
|
+
name: string;
|
|
69
|
+
type: 'null' | 'layer' | 'text' | 'particle';
|
|
70
|
+
width: number;
|
|
71
|
+
height: number;
|
|
72
|
+
description: Description;
|
|
73
|
+
parent?: string | null;
|
|
74
|
+
/** Omission retains source timing during preserved write-back, otherwise uses the composition duration. */
|
|
75
|
+
time_range?: [number, number];
|
|
76
|
+
position: AnimatedProperty;
|
|
77
|
+
/** Omitted channels retain source values during preserved write-back, otherwise use identity defaults. */
|
|
78
|
+
rotation?: AnimatedProperty;
|
|
79
|
+
scale?: AnimatedProperty;
|
|
80
|
+
anchor?: AnimatedProperty;
|
|
81
|
+
opacity?: AnimatedProperty;
|
|
82
|
+
asset?: Asset;
|
|
83
|
+
text?: TextProperties;
|
|
84
|
+
particle?: ParticleProperties;
|
|
85
|
+
children?: Layer[];
|
|
86
|
+
}
|
|
87
|
+
export interface CompositionMeta {
|
|
88
|
+
name: string;
|
|
89
|
+
duration: number;
|
|
90
|
+
fps: number;
|
|
91
|
+
width: number;
|
|
92
|
+
height: number;
|
|
93
|
+
loop: boolean;
|
|
94
|
+
background_color?: string | null;
|
|
95
|
+
}
|
|
96
|
+
/** Public DSL document. Its runtime shape matches the persisted DSL JSON. */
|
|
97
|
+
export interface Composition {
|
|
98
|
+
meta: CompositionMeta;
|
|
99
|
+
layers: Layer[];
|
|
100
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const DSL_CODE_LABELS: Readonly<Record<string, string>>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
type DSLRecord = Record<string, unknown>;
|
|
2
|
+
export interface ParticleValidationIssue {
|
|
3
|
+
code: string;
|
|
4
|
+
message: string;
|
|
5
|
+
path: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function lintParticle(particle: DSLRecord, layer: DSLRecord, particlePath: string): ParticleValidationIssue[];
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type DSLSeverity = 'ERROR' | 'WARNING';
|
|
2
|
+
export interface DSLLintIssue {
|
|
3
|
+
severity: DSLSeverity;
|
|
4
|
+
code: string;
|
|
5
|
+
message: string;
|
|
6
|
+
path: string;
|
|
7
|
+
layerId: string | null;
|
|
8
|
+
}
|
|
9
|
+
export interface DSLLintReport {
|
|
10
|
+
ok: boolean;
|
|
11
|
+
hasErrors: boolean;
|
|
12
|
+
counts: {
|
|
13
|
+
ERROR: number;
|
|
14
|
+
WARNING: number;
|
|
15
|
+
};
|
|
16
|
+
issues: DSLLintIssue[];
|
|
17
|
+
}
|
|
18
|
+
export interface DSLValidationIssue {
|
|
19
|
+
code: string;
|
|
20
|
+
label: string;
|
|
21
|
+
message: string;
|
|
22
|
+
path: string;
|
|
23
|
+
layerId: string | null;
|
|
24
|
+
}
|
|
25
|
+
export interface DSLValidationResult {
|
|
26
|
+
result: 'pass' | 'fail';
|
|
27
|
+
summary: string;
|
|
28
|
+
errors: DSLValidationIssue[];
|
|
29
|
+
warnings: DSLValidationIssue[];
|
|
30
|
+
}
|
|
31
|
+
export interface ValidateDSLOptions {
|
|
32
|
+
strict?: boolean;
|
|
33
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { DSLLintReport, DSLValidationIssue, DSLValidationResult, ValidateDSLOptions } from './types';
|
|
2
|
+
/** Lint an unnormalized DSL value while preserving issue severity. */
|
|
3
|
+
export declare function lintDSL(dsl: unknown): DSLLintReport;
|
|
4
|
+
/**
|
|
5
|
+
* Validate DSL and return the public validation result.
|
|
6
|
+
* Strings are interpreted as raw JSON. File-system paths are intentionally not
|
|
7
|
+
* accepted so the same validator works unchanged in Node.js and browsers.
|
|
8
|
+
*/
|
|
9
|
+
export declare function validateDSL(dsl: unknown, options?: ValidateDSLOptions): DSLValidationResult;
|
|
10
|
+
/** Error thrown when a value cannot be used as a valid DSL composition. */
|
|
11
|
+
export declare class DSLValidationError extends TypeError {
|
|
12
|
+
readonly issues: DSLValidationIssue[];
|
|
13
|
+
constructor(issues: DSLValidationIssue[]);
|
|
14
|
+
static fromValidationResult(validationResult: DSLValidationResult): DSLValidationError;
|
|
15
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { IRAsset, IRFontAsset, IRTextContent } from '@vvfx/animation-ir';
|
|
2
|
+
import type { Asset } from '../dsl/schema';
|
|
3
|
+
interface EstimatedTextBounds {
|
|
4
|
+
width: number;
|
|
5
|
+
height: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function normalizeDSLFontFamily(family: string | undefined): string;
|
|
8
|
+
export declare function ensureSystemFontAsset(assets: IRAsset[], family: string, style: IRFontAsset['style'], weight: number): string;
|
|
9
|
+
export declare function estimateTextBounds(text: IRTextContent): EstimatedTextBounds;
|
|
10
|
+
export declare function resolveAssetUrl(asset: Asset): string;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type IRImageAsset, type IRFontAsset, type IRLayer } from '@vvfx/animation-ir';
|
|
2
|
+
import type { Composition } from '../dsl/schema';
|
|
3
|
+
/** Convert authored 2D DSL sibling groups to flat IR layers in bottom-to-top painter order. */
|
|
4
|
+
export declare function convertDSLLayersToIR(composition: Composition, imageAssets?: IRImageAsset[], fontAssets?: IRFontAsset[]): IRLayer[];
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { IRParticleBurst, IRParticleContent } from '@vvfx/animation-ir';
|
|
2
|
+
import type { ParticleBurst, ParticleProperties } from '../dsl/schema';
|
|
3
|
+
export declare function convertDSLParticleToIRContent(particle: ParticleProperties, frameRate: number): IRParticleContent;
|
|
4
|
+
export declare function mapDSLParticleBurstsToIR(bursts: ParticleBurst[], frameRate: number): IRParticleBurst[];
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { IRAnimatedProperty, IRRotation, IRStaticProperty, IRVec3 } from '@vvfx/animation-ir';
|
|
2
|
+
import type { AnimatedProperty } from '../dsl/schema';
|
|
3
|
+
export declare function convertDSLPropertyToIRVec3(property: AnimatedProperty | undefined, frameRate: number, fallback: IRVec3, includeSpatialTangents?: boolean): IRAnimatedProperty<IRVec3>;
|
|
4
|
+
export declare function convertDSLPropertyToStaticIRVec3(property: AnimatedProperty | undefined, fallback: IRVec3): IRStaticProperty<IRVec3>;
|
|
5
|
+
export declare function convertDSLPropertyToIRNumber(property: AnimatedProperty | undefined, frameRate: number, fallback: number): IRAnimatedProperty<number>;
|
|
6
|
+
export declare function convertDSLPropertyToIRRotation(property: AnimatedProperty | undefined, frameRate: number, fallback: IRVec3): IRRotation;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Composition } from '../dsl/schema';
|
|
2
|
+
import type { ConvertDSLToIROptions, DSLToIRResult } from './types';
|
|
3
|
+
/** Convert an editable DSL composition into a shared Animation IR scene. */
|
|
4
|
+
export declare function convertDSLToIR(composition: Composition, options?: ConvertDSLToIROptions): DSLToIRResult;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { AnimatedProperty, Asset, Layer, TextProperties } from '../../dsl/schema';
|
|
2
|
+
declare const TEXT_PROPERTY_FIELDS: readonly ["content", "font", "size", "color", "align", "line_height", "letter_spacing"];
|
|
3
|
+
interface TextPropertyChange {
|
|
4
|
+
field: (typeof TEXT_PROPERTY_FIELDS)[number];
|
|
5
|
+
oldValue: TextProperties[(typeof TEXT_PROPERTY_FIELDS)[number]];
|
|
6
|
+
newValue: TextProperties[(typeof TEXT_PROPERTY_FIELDS)[number]];
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Structural, epsilon-tolerant equality between two editable-DSL properties.
|
|
10
|
+
*
|
|
11
|
+
* Used for preserved-data change detection so writeback replaces only edited
|
|
12
|
+
* fields in the IR snapshot.
|
|
13
|
+
*/
|
|
14
|
+
export declare function isAnimatedPropertyEqual(leftProperty: AnimatedProperty | undefined, rightProperty: AnimatedProperty | undefined): boolean;
|
|
15
|
+
export declare function collectTextPropertyChanges(baselineText: TextProperties, editedText: TextProperties): TextPropertyChange[];
|
|
16
|
+
export declare function readTextProperties(layer: Layer): TextProperties | undefined;
|
|
17
|
+
export declare function isAssetEqual(leftAsset: Asset, rightAsset: Asset): boolean;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Composition } from '../../dsl/schema';
|
|
2
|
+
import type { IRToDSLPreservedData } from '../../ir2dsl/types';
|
|
3
|
+
import type { ConvertDSLToIROptions, DSLToIRResult } from '../types';
|
|
4
|
+
/** Convert DSL to a clone of the baseline IR carried by the adapter-owned preserved data. */
|
|
5
|
+
export declare function convertDSLToIRWithPreservedData(composition: Composition, preservedData: IRToDSLPreservedData, options?: ConvertDSLToIROptions): DSLToIRResult;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type IRScene } from '@vvfx/animation-ir';
|
|
2
|
+
import type { Composition } from '../../dsl/schema';
|
|
3
|
+
import type { IRToDSLLayerMapping } from '../../ir2dsl/types';
|
|
4
|
+
import type { ConvertDSLToIROptions } from '../types';
|
|
5
|
+
/** Apply previously validated DSL to a clone of the baseline IR. */
|
|
6
|
+
export declare function updateValidatedIRFromDSL(baselineScene: IRScene, editedDocument: Composition, layerMapping: IRToDSLLayerMapping, options?: ConvertDSLToIROptions): IRScene;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { IRComposition } from '@vvfx/animation-ir';
|
|
2
|
+
import type { Layer } from '../../dsl/schema';
|
|
3
|
+
import type { DSLToIRChangeReporter } from '../types';
|
|
4
|
+
/** Apply edited sibling order without changing parent relationships. */
|
|
5
|
+
export declare function updateLayerOrder(updatedIR: IRComposition, editedLayers: Layer[], irLayerIdByEditedLayer: Map<Layer, string>, reportChange: DSLToIRChangeReporter, rootDSLParentId?: string): void;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { IRLayer, IRScene } from '@vvfx/animation-ir';
|
|
2
|
+
import type { Layer } from '../../dsl/schema';
|
|
3
|
+
import type { DSLToIRChangeReporter, DSLToIRWarningReporter } from '../types';
|
|
4
|
+
export declare function updateIRLayer(irLayer: IRLayer, irScene: IRScene, editedLayer: Layer, baselineLayer: Layer, dslPath: string, frameRate: number, strict: boolean, reportWarning: DSLToIRWarningReporter, reportChange: DSLToIRChangeReporter): void;
|