@flighthq/particles-formats 0.1.0

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.
Files changed (99) hide show
  1. package/dist/detect.d.ts +18 -0
  2. package/dist/detect.d.ts.map +1 -0
  3. package/dist/detect.js +75 -0
  4. package/dist/detect.js.map +1 -0
  5. package/dist/formatRegistry.d.ts +60 -0
  6. package/dist/formatRegistry.d.ts.map +1 -0
  7. package/dist/formatRegistry.js +75 -0
  8. package/dist/formatRegistry.js.map +1 -0
  9. package/dist/index.d.ts +21 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +21 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/libgdxParse.d.ts +27 -0
  14. package/dist/libgdxParse.d.ts.map +1 -0
  15. package/dist/libgdxParse.js +316 -0
  16. package/dist/libgdxParse.js.map +1 -0
  17. package/dist/libgdxSchema.d.ts +68 -0
  18. package/dist/libgdxSchema.d.ts.map +1 -0
  19. package/dist/libgdxSchema.js +12 -0
  20. package/dist/libgdxSchema.js.map +1 -0
  21. package/dist/libgdxSerialize.d.ts +17 -0
  22. package/dist/libgdxSerialize.d.ts.map +1 -0
  23. package/dist/libgdxSerialize.js +295 -0
  24. package/dist/libgdxSerialize.js.map +1 -0
  25. package/dist/parseParticleConfig.d.ts +34 -0
  26. package/dist/parseParticleConfig.d.ts.map +1 -0
  27. package/dist/parseParticleConfig.js +96 -0
  28. package/dist/parseParticleConfig.js.map +1 -0
  29. package/dist/particleDesignerParse.d.ts +23 -0
  30. package/dist/particleDesignerParse.d.ts.map +1 -0
  31. package/dist/particleDesignerParse.js +214 -0
  32. package/dist/particleDesignerParse.js.map +1 -0
  33. package/dist/particleDesignerSchema.d.ts +51 -0
  34. package/dist/particleDesignerSchema.d.ts.map +1 -0
  35. package/dist/particleDesignerSchema.js +5 -0
  36. package/dist/particleDesignerSchema.js.map +1 -0
  37. package/dist/particleDesignerSerialize.d.ts +22 -0
  38. package/dist/particleDesignerSerialize.d.ts.map +1 -0
  39. package/dist/particleDesignerSerialize.js +209 -0
  40. package/dist/particleDesignerSerialize.js.map +1 -0
  41. package/dist/pixiParse.d.ts +18 -0
  42. package/dist/pixiParse.d.ts.map +1 -0
  43. package/dist/pixiParse.js +173 -0
  44. package/dist/pixiParse.js.map +1 -0
  45. package/dist/serializeResult.d.ts +15 -0
  46. package/dist/serializeResult.d.ts.map +1 -0
  47. package/dist/serializeResult.js +2 -0
  48. package/dist/serializeResult.js.map +1 -0
  49. package/dist/spineParse.d.ts +18 -0
  50. package/dist/spineParse.d.ts.map +1 -0
  51. package/dist/spineParse.js +263 -0
  52. package/dist/spineParse.js.map +1 -0
  53. package/dist/spineSchema.d.ts +41 -0
  54. package/dist/spineSchema.d.ts.map +1 -0
  55. package/dist/spineSchema.js +8 -0
  56. package/dist/spineSchema.js.map +1 -0
  57. package/dist/spineSerialize.d.ts +16 -0
  58. package/dist/spineSerialize.d.ts.map +1 -0
  59. package/dist/spineSerialize.js +108 -0
  60. package/dist/spineSerialize.js.map +1 -0
  61. package/dist/starlingPexParse.d.ts +29 -0
  62. package/dist/starlingPexParse.d.ts.map +1 -0
  63. package/dist/starlingPexParse.js +254 -0
  64. package/dist/starlingPexParse.js.map +1 -0
  65. package/dist/starlingPexSchema.d.ts +49 -0
  66. package/dist/starlingPexSchema.d.ts.map +1 -0
  67. package/dist/starlingPexSchema.js +15 -0
  68. package/dist/starlingPexSchema.js.map +1 -0
  69. package/dist/starlingPexSerialize.d.ts +20 -0
  70. package/dist/starlingPexSerialize.d.ts.map +1 -0
  71. package/dist/starlingPexSerialize.js +184 -0
  72. package/dist/starlingPexSerialize.js.map +1 -0
  73. package/dist/unityParse.d.ts +23 -0
  74. package/dist/unityParse.d.ts.map +1 -0
  75. package/dist/unityParse.js +347 -0
  76. package/dist/unityParse.js.map +1 -0
  77. package/dist/unitySchema.d.ts +109 -0
  78. package/dist/unitySchema.d.ts.map +1 -0
  79. package/dist/unitySchema.js +9 -0
  80. package/dist/unitySchema.js.map +1 -0
  81. package/dist/unitySerialize.d.ts +20 -0
  82. package/dist/unitySerialize.d.ts.map +1 -0
  83. package/dist/unitySerialize.js +148 -0
  84. package/dist/unitySerialize.js.map +1 -0
  85. package/package.json +38 -0
  86. package/src/detect.test.ts +99 -0
  87. package/src/formatRegistry.test.ts +118 -0
  88. package/src/libgdxParse.test.ts +309 -0
  89. package/src/libgdxSerialize.test.ts +279 -0
  90. package/src/parseParticleConfig.test.ts +447 -0
  91. package/src/particleDesignerParse.test.ts +279 -0
  92. package/src/particleDesignerSerialize.test.ts +113 -0
  93. package/src/pixiParse.test.ts +131 -0
  94. package/src/spineParse.test.ts +289 -0
  95. package/src/spineSerialize.test.ts +81 -0
  96. package/src/starlingPexParse.test.ts +154 -0
  97. package/src/starlingPexSerialize.test.ts +115 -0
  98. package/src/unityParse.test.ts +328 -0
  99. package/src/unitySerialize.test.ts +77 -0
@@ -0,0 +1,447 @@
1
+ import {
2
+ LibgdxParticleFormatKind,
3
+ ParticleDesignerFormatKind,
4
+ PixiParticleFormatKind,
5
+ SpineParticleFormatKind,
6
+ StarlingPexFormatKind,
7
+ UnityParticleFormatKind,
8
+ } from '@flighthq/types';
9
+
10
+ import { parseParticleConfig, parseParticleConfigDocument } from './parseParticleConfig';
11
+
12
+ const PLIST_SNIPPET = `<?xml version="1.0" encoding="utf-8"?>
13
+ <plist version="1.0">
14
+ <dict>
15
+ <key>maxParticles</key><integer>150</integer>
16
+ <key>emitterType</key><integer>0</integer>
17
+ <key>duration</key><real>-1</real>
18
+ <key>particleLifespan</key><real>1.0</real>
19
+ <key>particleLifespanVariance</key><real>0.0</real>
20
+ <key>speed</key><real>100</real>
21
+ <key>speedVariance</key><real>0</real>
22
+ <key>angle</key><real>90</real>
23
+ <key>angleVariance</key><real>0</real>
24
+ <key>gravityx</key><real>0</real>
25
+ <key>gravityy</key><real>0</real>
26
+ <key>sourcePositionVariancex</key><real>0</real>
27
+ <key>sourcePositionVariancey</key><real>0</real>
28
+ <key>startParticleSize</key><real>16</real>
29
+ <key>startParticleSizeVariance</key><real>0</real>
30
+ <key>finishParticleSize</key><real>8</real>
31
+ <key>finishParticleSizeVariance</key><real>0</real>
32
+ <key>startColorRed</key><real>1</real>
33
+ <key>startColorGreen</key><real>1</real>
34
+ <key>startColorBlue</key><real>1</real>
35
+ <key>startColorAlpha</key><real>1</real>
36
+ <key>startColorVarianceRed</key><real>0</real>
37
+ <key>startColorVarianceGreen</key><real>0</real>
38
+ <key>startColorVarianceBlue</key><real>0</real>
39
+ <key>startColorVarianceAlpha</key><real>0</real>
40
+ <key>finishColorRed</key><real>1</real>
41
+ <key>finishColorGreen</key><real>1</real>
42
+ <key>finishColorBlue</key><real>1</real>
43
+ <key>finishColorAlpha</key><real>0</real>
44
+ <key>finishColorVarianceRed</key><real>0</real>
45
+ <key>finishColorVarianceGreen</key><real>0</real>
46
+ <key>finishColorVarianceBlue</key><real>0</real>
47
+ <key>finishColorVarianceAlpha</key><real>0</real>
48
+ <key>rotationStart</key><real>0</real>
49
+ <key>rotationStartVariance</key><real>0</real>
50
+ <key>rotationEnd</key><real>0</real>
51
+ <key>rotationEndVariance</key><real>0</real>
52
+ <key>maxRadius</key><real>0</real>
53
+ <key>maxRadiusVariance</key><real>0</real>
54
+ <key>minRadius</key><real>0</real>
55
+ <key>minRadiusVariance</key><real>0</real>
56
+ <key>rotatePerSecond</key><real>0</real>
57
+ <key>rotatePerSecondVariance</key><real>0</real>
58
+ <key>blendFuncSource</key><integer>770</integer>
59
+ <key>blendFuncDestination</key><integer>771</integer>
60
+ <key>textureFileName</key><string>test.png</string>
61
+ </dict>
62
+ </plist>`;
63
+ const SPINE_JSON = JSON.stringify({
64
+ name: 'spark',
65
+ maxParticles: 300,
66
+ continuous: true,
67
+ duration: -1,
68
+ emission: { low: 80, high: 120 },
69
+ life: { low: 400, high: 800 },
70
+ lifeOffset: { low: 0, high: 0 },
71
+ x: { low: 0, high: 0 },
72
+ y: { low: 0, high: 0 },
73
+ spawnShape: 'point',
74
+ spawnWidth: { low: 0, high: 0 },
75
+ spawnHeight: { low: 0, high: 0 },
76
+ velocity: { low: 50, high: 200 },
77
+ angle: { low: 60, high: 120 },
78
+ rotation: { low: 0, high: 360 },
79
+ wind: { low: 0, high: 0 },
80
+ gravity: { low: 200, high: 200 },
81
+ scale: { low: 0.5, high: 1.5 },
82
+ scaleEnd: { low: 0, high: 0 },
83
+ tint: [
84
+ { time: 0, color: 'ffffff' },
85
+ { time: 1, color: 'ff0000' },
86
+ ],
87
+ alpha: [
88
+ { time: 0, alpha: 1 },
89
+ { time: 1, alpha: 0 },
90
+ ],
91
+ blendMode: 'normal',
92
+ premultiplied: false,
93
+ images: ['spark.png'],
94
+ });
95
+ const LIBGDX_SNIPPET = `Particle Effect
96
+ - Spark -
97
+ maxParticleCount: 75
98
+ additive: false
99
+ imagePath: fire.png
100
+
101
+ Delay
102
+ active: false
103
+ lowMin: 0
104
+ lowMax: 0
105
+ highMin: 0
106
+ highMax: 0
107
+ relative: false
108
+ scalingCount: 1
109
+ scaling0: 1
110
+ timelineCount: 1
111
+ timeline0: 0
112
+
113
+ Duration
114
+ lowMin: 2000
115
+ lowMax: 2000
116
+ highMin: 2000
117
+ highMax: 2000
118
+ relative: false
119
+ scalingCount: 1
120
+ scaling0: 1
121
+ timelineCount: 1
122
+ timeline0: 0
123
+
124
+ Emission
125
+ lowMin: 0
126
+ lowMax: 0
127
+ highMin: 50
128
+ highMax: 50
129
+ relative: false
130
+ scalingCount: 1
131
+ scaling0: 1
132
+ timelineCount: 1
133
+ timeline0: 0
134
+
135
+ Life
136
+ lowMin: 500
137
+ lowMax: 500
138
+ highMin: 1000
139
+ highMax: 1000
140
+ relative: false
141
+ scalingCount: 1
142
+ scaling0: 1
143
+ timelineCount: 1
144
+ timeline0: 0
145
+
146
+ Life Offset
147
+ active: false
148
+ lowMin: 0
149
+ lowMax: 0
150
+ highMin: 0
151
+ highMax: 0
152
+ relative: false
153
+ scalingCount: 1
154
+ scaling0: 1
155
+ timelineCount: 1
156
+ timeline0: 0
157
+
158
+ X Offset
159
+ active: false
160
+ lowMin: 0
161
+ lowMax: 0
162
+ highMin: 0
163
+ highMax: 0
164
+ relative: false
165
+ scalingCount: 1
166
+ scaling0: 1
167
+ timelineCount: 1
168
+ timeline0: 0
169
+
170
+ Y Offset
171
+ active: false
172
+ lowMin: 0
173
+ lowMax: 0
174
+ highMin: 0
175
+ highMax: 0
176
+ relative: false
177
+ scalingCount: 1
178
+ scaling0: 1
179
+ timelineCount: 1
180
+ timeline0: 0
181
+
182
+ Spawn Shape
183
+ shape: point
184
+
185
+ Spawn Width
186
+ lowMin: 0
187
+ lowMax: 0
188
+ highMin: 0
189
+ highMax: 0
190
+ relative: false
191
+ scalingCount: 1
192
+ scaling0: 1
193
+ timelineCount: 1
194
+ timeline0: 0
195
+
196
+ Spawn Height
197
+ lowMin: 0
198
+ lowMax: 0
199
+ highMin: 0
200
+ highMax: 0
201
+ relative: false
202
+ scalingCount: 1
203
+ scaling0: 1
204
+ timelineCount: 1
205
+ timeline0: 0
206
+
207
+ Scale
208
+ lowMin: 16
209
+ lowMax: 16
210
+ highMin: 16
211
+ highMax: 16
212
+ relative: false
213
+ scalingCount: 1
214
+ scaling0: 1
215
+ timelineCount: 1
216
+ timeline0: 0
217
+
218
+ Velocity
219
+ active: true
220
+ lowMin: 100
221
+ lowMax: 100
222
+ highMin: 100
223
+ highMax: 100
224
+ relative: false
225
+ scalingCount: 1
226
+ scaling0: 1
227
+ timelineCount: 1
228
+ timeline0: 0
229
+
230
+ Angle
231
+ active: false
232
+ lowMin: 0
233
+ lowMax: 0
234
+ highMin: 360
235
+ highMax: 360
236
+ relative: false
237
+ scalingCount: 1
238
+ scaling0: 1
239
+ timelineCount: 1
240
+ timeline0: 0
241
+
242
+ Rotation
243
+ active: false
244
+ lowMin: 0
245
+ lowMax: 0
246
+ highMin: 0
247
+ highMax: 0
248
+ relative: false
249
+ scalingCount: 1
250
+ scaling0: 1
251
+ timelineCount: 1
252
+ timeline0: 0
253
+
254
+ Wind
255
+ active: false
256
+ lowMin: 0
257
+ lowMax: 0
258
+ highMin: 0
259
+ highMax: 0
260
+ relative: false
261
+ scalingCount: 1
262
+ scaling0: 1
263
+ timelineCount: 1
264
+ timeline0: 0
265
+
266
+ Gravity
267
+ active: false
268
+ lowMin: 0
269
+ lowMax: 0
270
+ highMin: 0
271
+ highMax: 0
272
+ relative: false
273
+ scalingCount: 1
274
+ scaling0: 1
275
+ timelineCount: 1
276
+ timeline0: 0
277
+
278
+ Tint
279
+ colors: ffffff
280
+ timelineCount: 1
281
+ timeline0: 0
282
+
283
+ Transparency
284
+ lowMin: 0
285
+ lowMax: 0
286
+ highMin: 1
287
+ highMax: 1
288
+ relative: false
289
+ scalingCount: 1
290
+ scaling0: 1
291
+ timelineCount: 1
292
+ timeline0: 0
293
+
294
+ `;
295
+ const PIXI_JSON = JSON.stringify({
296
+ pos: { x: 0, y: 0 },
297
+ alpha: { start: 1, end: 0 },
298
+ speed: { start: 100, end: 50 },
299
+ lifetime: { min: 0.5, max: 1.5 },
300
+ maxParticles: 600,
301
+ frequency: 0.01,
302
+ blendMode: 'add',
303
+ });
304
+ const STARLING_PEX_SNIPPET = `<?xml version="1.0" encoding="utf-8"?>
305
+ <particleEmitterConfig>
306
+ <attribute name="maxParticles" value="250"/>
307
+ <attribute name="emitterType" value="0"/>
308
+ <attribute name="duration" value="-1"/>
309
+ <attribute name="particleLifespan" value="1.0"/>
310
+ <attribute name="particleLifespanVariance" value="0.0"/>
311
+ <attribute name="speed" value="80"/>
312
+ <attribute name="speedVariance" value="0"/>
313
+ <attribute name="angle" value="90"/>
314
+ <attribute name="angleVariance" value="0"/>
315
+ <attribute name="gravityx" value="0"/>
316
+ <attribute name="gravityy" value="0"/>
317
+ <attribute name="sourcePositionVariancex" value="0"/>
318
+ <attribute name="sourcePositionVariancey" value="0"/>
319
+ <attribute name="startParticleSize" value="16"/>
320
+ <attribute name="startParticleSizeVariance" value="0"/>
321
+ <attribute name="finishParticleSize" value="8"/>
322
+ <attribute name="finishParticleSizeVariance" value="0"/>
323
+ <attribute name="startColor" red="1" green="1" blue="1" alpha="1"/>
324
+ <attribute name="startColorVariance" red="0" green="0" blue="0" alpha="0"/>
325
+ <attribute name="finishColor" red="1" green="1" blue="1" alpha="0"/>
326
+ <attribute name="finishColorVariance" red="0" green="0" blue="0" alpha="0"/>
327
+ <attribute name="rotationStart" value="0"/>
328
+ <attribute name="rotationStartVariance" value="0"/>
329
+ <attribute name="rotationEnd" value="0"/>
330
+ <attribute name="rotationEndVariance" value="0"/>
331
+ <attribute name="maxRadius" value="0"/>
332
+ <attribute name="maxRadiusVariance" value="0"/>
333
+ <attribute name="minRadius" value="0"/>
334
+ <attribute name="minRadiusVariance" value="0"/>
335
+ <attribute name="rotatePerSecond" value="0"/>
336
+ <attribute name="rotatePerSecondVariance" value="0"/>
337
+ <attribute name="radialAcceleration" value="0"/>
338
+ <attribute name="radialAccelVariance" value="0"/>
339
+ <attribute name="tangentialAcceleration" value="0"/>
340
+ <attribute name="tangentialAccelVariance" value="0"/>
341
+ <attribute name="blendFuncSource" value="770"/>
342
+ <attribute name="blendFuncDestination" value="1"/>
343
+ </particleEmitterConfig>`;
344
+ const UNITY_JSON = JSON.stringify({
345
+ name: 'smoke',
346
+ duration: 5.0,
347
+ looping: true,
348
+ prewarm: false,
349
+ maxParticles: 500,
350
+ startLifetime: { mode: 'twoConstants', constantMin: 1.0, constantMax: 2.5 },
351
+ startSpeed: { mode: 'twoConstants', constantMin: 0.5, constantMax: 1.5 },
352
+ startSize: { mode: 'constant', constant: 1.0 },
353
+ startRotation: { mode: 'constant', constant: 0 },
354
+ startColor: { r: 0.8, g: 0.8, b: 0.8, a: 1.0 },
355
+ gravityModifier: 0.0,
356
+ physicsGravity: 9.81,
357
+ emission: { rateOverTime: { mode: 'constant', constant: 20 }, bursts: [] },
358
+ shape: { enabled: false, shapeType: 'Cone', radius: 0, angle: 25, scale: { x: 1, y: 1, z: 1 } },
359
+ colorOverLifetime: { enabled: false, colorStart: { r: 1, g: 1, b: 1, a: 1 }, colorEnd: { r: 1, g: 1, b: 1, a: 0 } },
360
+ sizeOverLifetime: { enabled: false, sizeStart: 1, sizeEnd: 1 },
361
+ rotationOverLifetime: { enabled: false, angularVelocity: { mode: 'constant', constant: 0 } },
362
+ });
363
+
364
+ describe('parseParticleConfig', () => {
365
+ it('parses libGDX .p format', () => {
366
+ const config = parseParticleConfig(LIBGDX_SNIPPET);
367
+ expect(config.maxParticles).toBe(75);
368
+ });
369
+ it('parses Particle Designer plist', () => {
370
+ const config = parseParticleConfig(PLIST_SNIPPET);
371
+ expect(config.maxParticles).toBe(150);
372
+ });
373
+ it('parses Pixi particle JSON', () => {
374
+ const config = parseParticleConfig(PIXI_JSON);
375
+ expect(config.maxParticles).toBe(600);
376
+ });
377
+ it('parses Spine JSON', () => {
378
+ const config = parseParticleConfig(SPINE_JSON);
379
+ expect(config.maxParticles).toBe(300);
380
+ });
381
+ it('parses Starling PEX XML', () => {
382
+ const config = parseParticleConfig(STARLING_PEX_SNIPPET);
383
+ expect(config.maxParticles).toBe(250);
384
+ });
385
+ it('parses Unity JSON', () => {
386
+ const config = parseParticleConfig(UNITY_JSON);
387
+ expect(config.maxParticles).toBe(500);
388
+ });
389
+ it('returns a default config for unknown input without throwing', () => {
390
+ const config = parseParticleConfig('not a particle file');
391
+ expect(typeof config.maxParticles).toBe('number');
392
+ expect(Number.isFinite(config.maxParticles)).toBe(true);
393
+ });
394
+ it('returns a default config for empty string without throwing', () => {
395
+ expect(() => parseParticleConfig('')).not.toThrow();
396
+ });
397
+ });
398
+
399
+ describe('parseParticleConfigDocument', () => {
400
+ it('returns libGDX format kind for .p format', () => {
401
+ const result = parseParticleConfigDocument(LIBGDX_SNIPPET);
402
+ expect(result.format).toBe(LibgdxParticleFormatKind);
403
+ expect(result.config.maxParticles).toBe(75);
404
+ });
405
+ it('returns ParticleDesigner format kind for plist', () => {
406
+ const result = parseParticleConfigDocument(PLIST_SNIPPET);
407
+ expect(result.format).toBe(ParticleDesignerFormatKind);
408
+ expect(result.config.maxParticles).toBe(150);
409
+ expect(result.warnings).toEqual([]);
410
+ });
411
+ it('returns Pixi format kind for Pixi JSON', () => {
412
+ const result = parseParticleConfigDocument(PIXI_JSON);
413
+ expect(result.format).toBe(PixiParticleFormatKind);
414
+ expect(result.config.maxParticles).toBe(600);
415
+ });
416
+ it('returns Spine format kind for Spine JSON', () => {
417
+ const result = parseParticleConfigDocument(SPINE_JSON);
418
+ expect(result.format).toBe(SpineParticleFormatKind);
419
+ expect(result.config.maxParticles).toBe(300);
420
+ });
421
+ it('returns Starling PEX format kind for PEX XML', () => {
422
+ const result = parseParticleConfigDocument(STARLING_PEX_SNIPPET);
423
+ expect(result.format).toBe(StarlingPexFormatKind);
424
+ expect(result.config.maxParticles).toBe(250);
425
+ expect(result.config.blendMode).toBe('add');
426
+ });
427
+ it('returns Unity format kind for Unity JSON', () => {
428
+ const result = parseParticleConfigDocument(UNITY_JSON);
429
+ expect(result.format).toBe(UnityParticleFormatKind);
430
+ expect(result.config.maxParticles).toBe(500);
431
+ });
432
+ it('returns null format and unknown-format warning for unrecognised input', () => {
433
+ const result = parseParticleConfigDocument('totally unknown content');
434
+ expect(result.format).toBeNull();
435
+ expect(result.warnings.some((w) => w.includes('unknown-format'))).toBe(true);
436
+ });
437
+ it('returns null format for empty string', () => {
438
+ const result = parseParticleConfigDocument('');
439
+ expect(result.format).toBeNull();
440
+ expect(result.warnings.length).toBeGreaterThan(0);
441
+ });
442
+ it('does not throw for malformed JSON that passes detection', () => {
443
+ // A JSON object with Unity-ish keys but truncated
444
+ const truncated = '{ "gravityModifier": 0.5, "looping"';
445
+ expect(() => parseParticleConfigDocument(truncated)).not.toThrow();
446
+ });
447
+ });