lbrnts 0.0.1

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.js ADDED
@@ -0,0 +1,1424 @@
1
+ // lib/xml-parsing/parseXml.ts
2
+ import { XMLParser } from "fast-xml-parser";
3
+ var parser = new XMLParser({
4
+ ignoreAttributes: false,
5
+ // keep attributes
6
+ attributeNamePrefix: "@_",
7
+ // Use @_ prefix, we'll normalize to $ below
8
+ allowBooleanAttributes: true,
9
+ parseAttributeValue: false,
10
+ // keep as strings; we'll coerce per-class
11
+ ignoreDeclaration: true
12
+ // ignore <?xml ...?> declaration
13
+ });
14
+ var parseXml = (xml) => {
15
+ const raw = parser.parse(xml);
16
+ function normalize(value) {
17
+ if (value === null || typeof value !== "object") return value;
18
+ if (Array.isArray(value)) return value.map(normalize);
19
+ const out = {};
20
+ const attrs = {};
21
+ for (const [k, v] of Object.entries(value)) {
22
+ if (k.startsWith("@_")) {
23
+ attrs[k.slice(2)] = v;
24
+ } else if (k === "#text") {
25
+ out._ = v;
26
+ } else {
27
+ out[k] = normalize(v);
28
+ }
29
+ }
30
+ if (Object.keys(attrs).length) out.$ = attrs;
31
+ return out;
32
+ }
33
+ const rootKey = Object.keys(raw)[0];
34
+ if (!rootKey) {
35
+ throw new Error("Failed to parse XML: no root element found");
36
+ }
37
+ const root = normalize(raw[rootKey]);
38
+ return { [rootKey]: root };
39
+ };
40
+
41
+ // lib/classes/LightBurnBaseElement.ts
42
+ var LightBurnBaseElement = class _LightBurnBaseElement {
43
+ token;
44
+ static token;
45
+ getChildren() {
46
+ return [];
47
+ }
48
+ getStringIndented() {
49
+ return this.getString().split("\n").map((line) => ` ${line}`).join("\n");
50
+ }
51
+ getString() {
52
+ const children = this.getChildren();
53
+ if (children.length === 0) {
54
+ return `(${this.token})`;
55
+ }
56
+ const lines = [`(${this.token}`];
57
+ for (const p of children) {
58
+ lines.push(p.getStringIndented());
59
+ }
60
+ lines.push(")");
61
+ return lines.join("\n");
62
+ }
63
+ get [Symbol.toStringTag]() {
64
+ return this.getString();
65
+ }
66
+ [Symbol.for("nodejs.util.inspect.custom")]() {
67
+ return this.getString();
68
+ }
69
+ // =========================== STATIC METHODS ===========================
70
+ static elementRegistry = {};
71
+ static shapeRegistry = {};
72
+ /**
73
+ * Should be called after class definition to register the class for parsing
74
+ */
75
+ static register(token, klass) {
76
+ if (token.startsWith("Shape.")) {
77
+ _LightBurnBaseElement.shapeRegistry[token] = klass;
78
+ } else {
79
+ _LightBurnBaseElement.elementRegistry[token] = klass;
80
+ }
81
+ }
82
+ /**
83
+ * Parse an XML string into registered LightBurnBaseElement instances
84
+ */
85
+ static parse(xml) {
86
+ const ShapePath2 = _LightBurnBaseElement.shapeRegistry["Shape.Path"];
87
+ if (ShapePath2 && typeof ShapePath2.clearTemplateRegistry === "function") {
88
+ ShapePath2.clearTemplateRegistry();
89
+ }
90
+ const xmlJson = parseXml(xml);
91
+ return _LightBurnBaseElement.parseXmlJson(xmlJson);
92
+ }
93
+ static fromXmlJson(node) {
94
+ throw new Error(
95
+ `"${_LightBurnBaseElement.name}" class has not implemented fromXmlJson`
96
+ );
97
+ }
98
+ static parseXmlJson(xmlJson) {
99
+ if (xmlJson && typeof xmlJson === "object" && !Array.isArray(xmlJson) && !("$" in xmlJson || "_" in xmlJson)) {
100
+ const keys = Object.keys(xmlJson);
101
+ if (keys.length === 1) {
102
+ const rootTag = keys[0];
103
+ const rootNode = xmlJson[rootTag];
104
+ return _LightBurnBaseElement.instantiateElement(rootTag, rootNode);
105
+ }
106
+ }
107
+ if (Array.isArray(xmlJson)) {
108
+ return xmlJson.map((item) => _LightBurnBaseElement.parseXmlJson(item));
109
+ }
110
+ if (typeof xmlJson === "number" || typeof xmlJson === "string" || typeof xmlJson === "boolean" || xmlJson === null) {
111
+ return xmlJson;
112
+ }
113
+ throw new Error(`Couldn't parse XML JSON: ${JSON.stringify(xmlJson)}`);
114
+ }
115
+ static instantiateElement(tag, node) {
116
+ if (tag === "Shape") {
117
+ const type = node.$?.Type;
118
+ if (!type) {
119
+ throw new Error("Shape element missing Type attribute");
120
+ }
121
+ const shapeToken = `Shape.${type}`;
122
+ const ShapeClass = _LightBurnBaseElement.shapeRegistry[shapeToken];
123
+ if (ShapeClass) {
124
+ return ShapeClass.fromXmlJson(node);
125
+ }
126
+ throw new Error(`Unknown shape type "${type}"`);
127
+ }
128
+ const ElementClass = _LightBurnBaseElement.elementRegistry[tag];
129
+ if (ElementClass) {
130
+ return ElementClass.fromXmlJson(node);
131
+ }
132
+ throw new Error(`Unknown element "${tag}"`);
133
+ }
134
+ };
135
+
136
+ // lib/classes/elements/_coerce.ts
137
+ function num(v, defaultValue = 0) {
138
+ if (v === null || v === void 0) return defaultValue;
139
+ if (typeof v === "number") return v;
140
+ const parsed = Number(v);
141
+ return Number.isNaN(parsed) ? defaultValue : parsed;
142
+ }
143
+ function boolish(v, defaultValue = false) {
144
+ if (v === null || v === void 0) return defaultValue;
145
+ if (typeof v === "boolean") return v;
146
+ if (typeof v === "string") {
147
+ const lower = v.toLowerCase();
148
+ if (lower === "true" || lower === "1") return true;
149
+ if (lower === "false" || lower === "0") return false;
150
+ }
151
+ if (typeof v === "number") return v !== 0;
152
+ return defaultValue;
153
+ }
154
+ function str(v, defaultValue = "") {
155
+ if (v === null || v === void 0) return defaultValue;
156
+ return String(v);
157
+ }
158
+
159
+ // lib/classes/elements/LightBurnProject.ts
160
+ var LightBurnProject = class _LightBurnProject extends LightBurnBaseElement {
161
+ appVersion;
162
+ formatVersion;
163
+ materialHeight;
164
+ mirrorX;
165
+ mirrorY;
166
+ children = [];
167
+ constructor(init) {
168
+ super();
169
+ this.token = "LightBurnProject";
170
+ if (init) {
171
+ if (init.appVersion !== void 0) this.appVersion = init.appVersion;
172
+ if (init.formatVersion !== void 0)
173
+ this.formatVersion = init.formatVersion;
174
+ if (init.materialHeight !== void 0)
175
+ this.materialHeight = init.materialHeight;
176
+ if (init.mirrorX !== void 0) this.mirrorX = init.mirrorX;
177
+ if (init.mirrorY !== void 0) this.mirrorY = init.mirrorY;
178
+ if (init.children !== void 0) this.children = init.children;
179
+ }
180
+ }
181
+ static fromXmlJson(node) {
182
+ const project = new _LightBurnProject();
183
+ if (node.$) {
184
+ project.appVersion = str(node.$.AppVersion, void 0);
185
+ project.formatVersion = str(node.$.FormatVersion, void 0);
186
+ project.materialHeight = num(node.$.MaterialHeight, void 0);
187
+ project.mirrorX = boolish(node.$.MirrorX, void 0);
188
+ project.mirrorY = boolish(node.$.MirrorY, void 0);
189
+ }
190
+ const childTags = [
191
+ "Thumbnail",
192
+ "VariableText",
193
+ "UIPrefs",
194
+ "CutSetting",
195
+ "Shape",
196
+ "Notes"
197
+ ];
198
+ for (const tag of childTags) {
199
+ const childNode = node[tag];
200
+ if (childNode) {
201
+ if (Array.isArray(childNode)) {
202
+ for (const item of childNode) {
203
+ project.children.push(
204
+ LightBurnBaseElement.instantiateElement(tag, item)
205
+ );
206
+ }
207
+ } else {
208
+ project.children.push(
209
+ LightBurnBaseElement.instantiateElement(tag, childNode)
210
+ );
211
+ }
212
+ }
213
+ }
214
+ return project;
215
+ }
216
+ getChildren() {
217
+ return this.children;
218
+ }
219
+ };
220
+ LightBurnBaseElement.register("LightBurnProject", LightBurnProject);
221
+
222
+ // lib/classes/elements/CutSetting.ts
223
+ var CutSetting = class _CutSetting extends LightBurnBaseElement {
224
+ _type = "Cut";
225
+ _index;
226
+ _name;
227
+ _priority;
228
+ _minPower;
229
+ _maxPower;
230
+ _minPower2;
231
+ _maxPower2;
232
+ _speed;
233
+ _kerf;
234
+ _zOffset;
235
+ _enablePowerRamp;
236
+ _rampLength;
237
+ _numPasses;
238
+ _zPerPass;
239
+ _perforate;
240
+ _dotMode;
241
+ _scanOpt;
242
+ _interval;
243
+ _angle;
244
+ _overScanning;
245
+ _lineAngle;
246
+ constructor(init) {
247
+ super();
248
+ this.token = "CutSetting";
249
+ if (init) {
250
+ if (init.type !== void 0) this._type = init.type;
251
+ if (init.index !== void 0) this._index = init.index;
252
+ if (init.name !== void 0) this._name = init.name;
253
+ if (init.priority !== void 0) this._priority = init.priority;
254
+ if (init.minPower !== void 0) this._minPower = init.minPower;
255
+ if (init.maxPower !== void 0) this._maxPower = init.maxPower;
256
+ if (init.minPower2 !== void 0) this._minPower2 = init.minPower2;
257
+ if (init.maxPower2 !== void 0) this._maxPower2 = init.maxPower2;
258
+ if (init.speed !== void 0) this._speed = init.speed;
259
+ if (init.kerf !== void 0) this._kerf = init.kerf;
260
+ if (init.zOffset !== void 0) this._zOffset = init.zOffset;
261
+ if (init.enablePowerRamp !== void 0)
262
+ this._enablePowerRamp = init.enablePowerRamp;
263
+ if (init.rampLength !== void 0) this._rampLength = init.rampLength;
264
+ if (init.numPasses !== void 0) this._numPasses = init.numPasses;
265
+ if (init.zPerPass !== void 0) this._zPerPass = init.zPerPass;
266
+ if (init.perforate !== void 0) this._perforate = init.perforate;
267
+ if (init.dotMode !== void 0) this._dotMode = init.dotMode;
268
+ if (init.scanOpt !== void 0) this._scanOpt = init.scanOpt;
269
+ if (init.interval !== void 0) this._interval = init.interval;
270
+ if (init.angle !== void 0) this._angle = init.angle;
271
+ if (init.overScanning !== void 0)
272
+ this._overScanning = init.overScanning;
273
+ if (init.lineAngle !== void 0) this._lineAngle = init.lineAngle;
274
+ }
275
+ }
276
+ get type() {
277
+ return this._type;
278
+ }
279
+ set type(value) {
280
+ this._type = value;
281
+ }
282
+ get index() {
283
+ return this._index;
284
+ }
285
+ set index(value) {
286
+ this._index = value;
287
+ }
288
+ get name() {
289
+ return this._name;
290
+ }
291
+ set name(value) {
292
+ this._name = value;
293
+ }
294
+ get priority() {
295
+ return this._priority;
296
+ }
297
+ set priority(value) {
298
+ this._priority = value;
299
+ }
300
+ get minPower() {
301
+ return this._minPower;
302
+ }
303
+ set minPower(value) {
304
+ this._minPower = value;
305
+ }
306
+ get maxPower() {
307
+ return this._maxPower;
308
+ }
309
+ set maxPower(value) {
310
+ this._maxPower = value;
311
+ }
312
+ get minPower2() {
313
+ return this._minPower2;
314
+ }
315
+ set minPower2(value) {
316
+ this._minPower2 = value;
317
+ }
318
+ get maxPower2() {
319
+ return this._maxPower2;
320
+ }
321
+ set maxPower2(value) {
322
+ this._maxPower2 = value;
323
+ }
324
+ get speed() {
325
+ return this._speed;
326
+ }
327
+ set speed(value) {
328
+ this._speed = value;
329
+ }
330
+ get kerf() {
331
+ return this._kerf;
332
+ }
333
+ set kerf(value) {
334
+ this._kerf = value;
335
+ }
336
+ get zOffset() {
337
+ return this._zOffset;
338
+ }
339
+ set zOffset(value) {
340
+ this._zOffset = value;
341
+ }
342
+ get enablePowerRamp() {
343
+ return this._enablePowerRamp;
344
+ }
345
+ set enablePowerRamp(value) {
346
+ this._enablePowerRamp = value;
347
+ }
348
+ get rampLength() {
349
+ return this._rampLength;
350
+ }
351
+ set rampLength(value) {
352
+ this._rampLength = value;
353
+ }
354
+ get numPasses() {
355
+ return this._numPasses;
356
+ }
357
+ set numPasses(value) {
358
+ this._numPasses = value;
359
+ }
360
+ get zPerPass() {
361
+ return this._zPerPass;
362
+ }
363
+ set zPerPass(value) {
364
+ this._zPerPass = value;
365
+ }
366
+ get perforate() {
367
+ return this._perforate;
368
+ }
369
+ set perforate(value) {
370
+ this._perforate = value;
371
+ }
372
+ get dotMode() {
373
+ return this._dotMode;
374
+ }
375
+ set dotMode(value) {
376
+ this._dotMode = value;
377
+ }
378
+ get scanOpt() {
379
+ return this._scanOpt;
380
+ }
381
+ set scanOpt(value) {
382
+ this._scanOpt = value;
383
+ }
384
+ get interval() {
385
+ return this._interval;
386
+ }
387
+ set interval(value) {
388
+ this._interval = value;
389
+ }
390
+ get angle() {
391
+ return this._angle;
392
+ }
393
+ set angle(value) {
394
+ this._angle = value;
395
+ }
396
+ get overScanning() {
397
+ return this._overScanning;
398
+ }
399
+ set overScanning(value) {
400
+ this._overScanning = value;
401
+ }
402
+ get lineAngle() {
403
+ return this._lineAngle;
404
+ }
405
+ set lineAngle(value) {
406
+ this._lineAngle = value;
407
+ }
408
+ static fromXmlJson(node) {
409
+ const cs = new _CutSetting();
410
+ if (node.$) {
411
+ cs.type = str(node.$.type, "Cut");
412
+ }
413
+ const getChildValue = (key) => {
414
+ const child = node[key];
415
+ return child?.$?.Value;
416
+ };
417
+ cs.index = num(getChildValue("index"), void 0);
418
+ cs.name = str(getChildValue("name"), void 0);
419
+ cs.priority = num(getChildValue("priority"), void 0);
420
+ cs.minPower = num(getChildValue("minPower"), void 0);
421
+ cs.maxPower = num(getChildValue("maxPower"), void 0);
422
+ cs.minPower2 = num(getChildValue("minPower2"), void 0);
423
+ cs.maxPower2 = num(getChildValue("maxPower2"), void 0);
424
+ cs.speed = num(getChildValue("speed"), void 0);
425
+ cs.kerf = num(getChildValue("kerf"), void 0);
426
+ cs.zOffset = num(getChildValue("zOffset"), void 0);
427
+ cs.enablePowerRamp = boolish(getChildValue("enablePowerRamp"), void 0);
428
+ cs.rampLength = num(getChildValue("rampLength"), void 0);
429
+ cs.numPasses = num(getChildValue("numPasses"), void 0);
430
+ cs.zPerPass = num(getChildValue("zPerPass"), void 0);
431
+ cs.perforate = boolish(getChildValue("perforate"), void 0);
432
+ cs.dotMode = boolish(getChildValue("dotMode"), void 0);
433
+ cs.scanOpt = str(getChildValue("scanOpt"), void 0);
434
+ cs.interval = num(getChildValue("interval"), void 0);
435
+ cs.angle = num(getChildValue("angle"), void 0);
436
+ cs.overScanning = num(getChildValue("overScanning"), void 0);
437
+ cs.lineAngle = num(getChildValue("lineAngle"), void 0);
438
+ return cs;
439
+ }
440
+ };
441
+ LightBurnBaseElement.register("CutSetting", CutSetting);
442
+
443
+ // lib/classes/elements/Notes.ts
444
+ var Notes = class _Notes extends LightBurnBaseElement {
445
+ showOnLoad;
446
+ text;
447
+ constructor() {
448
+ super();
449
+ this.token = "Notes";
450
+ }
451
+ static fromXmlJson(node) {
452
+ const notes = new _Notes();
453
+ if (node.$) {
454
+ notes.showOnLoad = boolish(node.$.ShowOnLoad, void 0);
455
+ notes.text = str(node.$.Notes, void 0);
456
+ }
457
+ return notes;
458
+ }
459
+ };
460
+ LightBurnBaseElement.register("Notes", Notes);
461
+
462
+ // lib/classes/elements/VariableText.ts
463
+ var VariableText = class _VariableText extends LightBurnBaseElement {
464
+ start;
465
+ end;
466
+ current;
467
+ increment;
468
+ autoAdvance;
469
+ constructor() {
470
+ super();
471
+ this.token = "VariableText";
472
+ }
473
+ static fromXmlJson(node) {
474
+ const vt = new _VariableText();
475
+ if (node.Start?.$) {
476
+ vt.start = num(node.Start.$.Value, void 0);
477
+ }
478
+ if (node.End?.$) {
479
+ vt.end = num(node.End.$.Value, void 0);
480
+ }
481
+ if (node.Current?.$) {
482
+ vt.current = num(node.Current.$.Value, void 0);
483
+ }
484
+ if (node.Increment?.$) {
485
+ vt.increment = num(node.Increment.$.Value, void 0);
486
+ }
487
+ if (node.AutoAdvance?.$) {
488
+ vt.autoAdvance = boolish(node.AutoAdvance.$.Value, void 0);
489
+ }
490
+ return vt;
491
+ }
492
+ };
493
+ LightBurnBaseElement.register("VariableText", VariableText);
494
+
495
+ // lib/classes/elements/UIPrefs.ts
496
+ var UIPrefs = class _UIPrefs extends LightBurnBaseElement {
497
+ prefs = {};
498
+ constructor() {
499
+ super();
500
+ this.token = "UIPrefs";
501
+ }
502
+ static fromXmlJson(node) {
503
+ const ui = new _UIPrefs();
504
+ for (const [key, value] of Object.entries(node)) {
505
+ if (key !== "$" && key !== "_" && value && typeof value === "object") {
506
+ const val = value.$?.Value;
507
+ if (val !== void 0) {
508
+ ui.prefs[key] = str(val);
509
+ }
510
+ }
511
+ }
512
+ return ui;
513
+ }
514
+ };
515
+ LightBurnBaseElement.register("UIPrefs", UIPrefs);
516
+
517
+ // lib/classes/elements/Thumbnail.ts
518
+ var Thumbnail = class _Thumbnail extends LightBurnBaseElement {
519
+ pngBase64;
520
+ constructor() {
521
+ super();
522
+ this.token = "Thumbnail";
523
+ }
524
+ static fromXmlJson(node) {
525
+ const thumbnail = new _Thumbnail();
526
+ if (node.$) {
527
+ thumbnail.pngBase64 = str(node.$.Source, void 0);
528
+ }
529
+ return thumbnail;
530
+ }
531
+ };
532
+ LightBurnBaseElement.register("Thumbnail", Thumbnail);
533
+
534
+ // lib/classes/elements/shapes/ShapeBase.ts
535
+ var ShapeBase = class extends LightBurnBaseElement {
536
+ cutIndex;
537
+ locked;
538
+ xform;
539
+ static readCommon(node) {
540
+ const common = {};
541
+ if (node.$) {
542
+ if (node.$.CutIndex !== void 0) {
543
+ common.cutIndex = num(node.$.CutIndex, void 0);
544
+ }
545
+ if (node.$.Locked !== void 0) {
546
+ common.locked = boolish(node.$.Locked, void 0);
547
+ }
548
+ }
549
+ const xformNode = node.XForm;
550
+ if (xformNode) {
551
+ const xformText = typeof xformNode === "string" ? xformNode : str(xformNode._, "");
552
+ if (xformText) {
553
+ const parts = xformText.trim().split(/\s+/).map(Number);
554
+ if (parts.length === 6) {
555
+ common.xform = parts;
556
+ }
557
+ }
558
+ }
559
+ return common;
560
+ }
561
+ };
562
+
563
+ // lib/classes/elements/shapes/ShapeEllipse.ts
564
+ var ShapeEllipse = class _ShapeEllipse extends ShapeBase {
565
+ rx;
566
+ ry;
567
+ constructor() {
568
+ super();
569
+ this.token = "Shape.Ellipse";
570
+ }
571
+ static fromXmlJson(node) {
572
+ const ellipse = new _ShapeEllipse();
573
+ const common = ShapeBase.readCommon(node);
574
+ Object.assign(ellipse, common);
575
+ if (node.$) {
576
+ ellipse.rx = num(node.$.Rx, void 0);
577
+ ellipse.ry = num(node.$.Ry, void 0);
578
+ }
579
+ return ellipse;
580
+ }
581
+ };
582
+ LightBurnBaseElement.register("Shape.Ellipse", ShapeEllipse);
583
+
584
+ // lib/classes/elements/shapes/ShapeRect.ts
585
+ var ShapeRect = class _ShapeRect extends ShapeBase {
586
+ w;
587
+ h;
588
+ cr;
589
+ constructor() {
590
+ super();
591
+ this.token = "Shape.Rect";
592
+ }
593
+ static fromXmlJson(node) {
594
+ const rect = new _ShapeRect();
595
+ const common = ShapeBase.readCommon(node);
596
+ Object.assign(rect, common);
597
+ if (node.$) {
598
+ rect.w = num(node.$.W, void 0);
599
+ rect.h = num(node.$.H, void 0);
600
+ rect.cr = num(node.$.Cr, void 0);
601
+ }
602
+ return rect;
603
+ }
604
+ };
605
+ LightBurnBaseElement.register("Shape.Rect", ShapeRect);
606
+
607
+ // lib/classes/elements/shapes/ShapePath.ts
608
+ var ShapePath = class _ShapePath extends ShapeBase {
609
+ verts = [];
610
+ prims = [];
611
+ isClosed = true;
612
+ // Whether the path is closed (default) or open
613
+ // Static registry to track shape templates by VertID/PrimID
614
+ static templateRegistry = /* @__PURE__ */ new Map();
615
+ constructor(init) {
616
+ super();
617
+ this.token = "Shape.Path";
618
+ if (init) {
619
+ if (init.verts !== void 0) this.verts = init.verts;
620
+ if (init.prims !== void 0) this.prims = init.prims;
621
+ if (init.isClosed !== void 0) this.isClosed = init.isClosed;
622
+ if (init.cutIndex !== void 0) this.cutIndex = init.cutIndex;
623
+ if (init.locked !== void 0) this.locked = init.locked;
624
+ if (init.xform !== void 0) this.xform = init.xform;
625
+ }
626
+ }
627
+ static clearTemplateRegistry() {
628
+ _ShapePath.templateRegistry.clear();
629
+ }
630
+ static fromXmlJson(node) {
631
+ const path = new _ShapePath();
632
+ const common = ShapeBase.readCommon(node);
633
+ Object.assign(path, common);
634
+ const vertID = num(node.$?.VertID, void 0);
635
+ const primID = num(node.$?.PrimID, void 0);
636
+ const templateKey = `${vertID}_${primID}`;
637
+ const vertList = node.VertList;
638
+ if (vertList) {
639
+ if (typeof vertList === "string") {
640
+ path.verts = _ShapePath.parseEncodedVertList(vertList);
641
+ } else if (vertList.Vert) {
642
+ const verts = Array.isArray(vertList.Vert) ? vertList.Vert : [vertList.Vert];
643
+ for (const v of verts) {
644
+ if (v.$) {
645
+ path.verts.push({
646
+ x: num(v.$.x, 0),
647
+ y: num(v.$.y, 0),
648
+ c: num(v.$.c, void 0)
649
+ });
650
+ }
651
+ }
652
+ }
653
+ }
654
+ const primList = node.PrimList;
655
+ if (primList) {
656
+ if (typeof primList === "string") {
657
+ const isOpen = primList.includes("Open");
658
+ path.isClosed = !isOpen;
659
+ const primCount = isOpen ? path.verts.length - 1 : path.verts.length;
660
+ for (let i = 0; i < primCount; i++) {
661
+ path.prims.push({ type: 0 });
662
+ }
663
+ } else if (primList.Prim) {
664
+ const prims = Array.isArray(primList.Prim) ? primList.Prim : [primList.Prim];
665
+ for (const p of prims) {
666
+ if (p.$) {
667
+ path.prims.push({
668
+ type: num(p.$.type, 0)
669
+ });
670
+ }
671
+ }
672
+ }
673
+ }
674
+ const primPolyline = node.PrimPolyline;
675
+ if (primPolyline && typeof primPolyline === "string") {
676
+ path.prims = _ShapePath.parseEncodedPrimPolyline(primPolyline);
677
+ }
678
+ if (path.verts.length > 0 && vertID !== void 0 && primID !== void 0) {
679
+ _ShapePath.templateRegistry.set(templateKey, {
680
+ verts: path.verts,
681
+ prims: path.prims,
682
+ isClosed: path.isClosed
683
+ });
684
+ }
685
+ if (path.verts.length === 0 && vertID !== void 0 && primID !== void 0) {
686
+ const template = _ShapePath.templateRegistry.get(templateKey);
687
+ if (template) {
688
+ path.verts = template.verts.map((v) => ({ ...v }));
689
+ path.prims = template.prims.map((p) => ({ ...p }));
690
+ path.isClosed = template.isClosed;
691
+ }
692
+ }
693
+ if (path.verts.length > 0 && path.prims.length === 0) {
694
+ path.prims = _ShapePath.generatePrimsFromVerts(path.verts, primID);
695
+ }
696
+ return path;
697
+ }
698
+ /**
699
+ * Parse encoded VertList string format
700
+ * Format: V{x} {y}c{flag}x{flag}c{flag}x{c1x}c{flag}y{c1y}...
701
+ * Example: V2.1156745 -12.3306c0x1c1x1.5871694c1y-12.3306
702
+ *
703
+ * Control points are encoded as:
704
+ * - c0x{value}c0y{value} = control point 0 (when both present)
705
+ * - c1x{value}c1y{value} = control point 1 (when both present)
706
+ * - c{num}x{num} alone (like c0x1) = flag, not a coordinate
707
+ */
708
+ static parseEncodedVertList(encoded) {
709
+ const verts = [];
710
+ const parts = encoded.split("V").filter((p) => p.trim());
711
+ for (const part of parts) {
712
+ const match = part.match(/^([-\d.]+)\s+([-\d.]+)/);
713
+ if (match) {
714
+ const x = parseFloat(match[1]);
715
+ const y = parseFloat(match[2]);
716
+ const vert = { x, y };
717
+ const cMatch = part.match(/c(\d+)/);
718
+ if (cMatch) {
719
+ vert.c = parseInt(cMatch[1], 10);
720
+ }
721
+ const c0xMatch = part.match(/c0x([-\d.]+)/);
722
+ const c0yMatch = part.match(/c0y([-\d.]+)/);
723
+ const c1xMatch = part.match(/c1x([-\d.]+)/);
724
+ const c1yMatch = part.match(/c1y([-\d.]+)/);
725
+ if (c0xMatch && c0yMatch && (c0xMatch[1].includes(".") || c0xMatch[1].length > 1)) {
726
+ vert.c0x = parseFloat(c0xMatch[1]);
727
+ vert.c0y = parseFloat(c0yMatch[1]);
728
+ }
729
+ if (c1xMatch && c1yMatch && (c1xMatch[1].includes(".") || c1xMatch[1].length > 1)) {
730
+ vert.c1x = parseFloat(c1xMatch[1]);
731
+ vert.c1y = parseFloat(c1yMatch[1]);
732
+ }
733
+ verts.push(vert);
734
+ }
735
+ }
736
+ return verts;
737
+ }
738
+ /**
739
+ * Parse encoded PrimPolyline string format
740
+ * This typically contains line-to commands, each prim is type 0 (LineTo)
741
+ */
742
+ static parseEncodedPrimPolyline(encoded) {
743
+ const prims = [];
744
+ const vertCount = (encoded.match(/[-\d.]+\s+[-\d.]+/g) || []).length;
745
+ for (let i = 0; i < vertCount; i++) {
746
+ prims.push({ type: 0 });
747
+ }
748
+ return prims;
749
+ }
750
+ /**
751
+ * Generate primitives from vertices based on PrimID
752
+ * PrimID meanings:
753
+ * - 0: Simple polyline (all LineTo)
754
+ * - 2: Rounded rectangle with bezier curves at corners
755
+ * - 3: Other curve type
756
+ */
757
+ static generatePrimsFromVerts(verts, primID) {
758
+ const prims = [];
759
+ if (verts.length === 0) {
760
+ return prims;
761
+ }
762
+ if (primID === 2 && verts.length === 8) {
763
+ for (let i = 0; i < 8; i++) {
764
+ prims.push({ type: i % 2 === 0 ? 0 : 1 });
765
+ }
766
+ } else {
767
+ for (let i = 0; i < verts.length; i++) {
768
+ prims.push({ type: 0 });
769
+ }
770
+ }
771
+ return prims;
772
+ }
773
+ };
774
+ LightBurnBaseElement.register("Shape.Path", ShapePath);
775
+
776
+ // lib/classes/elements/shapes/ShapeText.ts
777
+ var ShapeText = class _ShapeText extends ShapeBase {
778
+ text;
779
+ font;
780
+ backupPath;
781
+ constructor() {
782
+ super();
783
+ this.token = "Shape.Text";
784
+ }
785
+ static fromXmlJson(node) {
786
+ const text = new _ShapeText();
787
+ const common = ShapeBase.readCommon(node);
788
+ Object.assign(text, common);
789
+ if (node.$) {
790
+ text.text = str(node.$.Text, void 0);
791
+ text.font = str(node.$.Font, void 0);
792
+ }
793
+ const backupPath = node.BackupPath;
794
+ if (backupPath) {
795
+ text.backupPath = ShapePath.fromXmlJson(backupPath);
796
+ }
797
+ return text;
798
+ }
799
+ };
800
+ LightBurnBaseElement.register("Shape.Text", ShapeText);
801
+
802
+ // lib/classes/elements/shapes/ShapeGroup.ts
803
+ var ShapeGroup = class _ShapeGroup extends ShapeBase {
804
+ children = [];
805
+ constructor() {
806
+ super();
807
+ this.token = "Shape.Group";
808
+ }
809
+ static fromXmlJson(node) {
810
+ const group = new _ShapeGroup();
811
+ const common = ShapeBase.readCommon(node);
812
+ Object.assign(group, common);
813
+ const shapes = node.Shape;
814
+ if (shapes) {
815
+ if (Array.isArray(shapes)) {
816
+ for (const shape of shapes) {
817
+ group.children.push(
818
+ LightBurnBaseElement.instantiateElement("Shape", shape)
819
+ );
820
+ }
821
+ } else {
822
+ group.children.push(
823
+ LightBurnBaseElement.instantiateElement("Shape", shapes)
824
+ );
825
+ }
826
+ }
827
+ return group;
828
+ }
829
+ getChildren() {
830
+ return this.children;
831
+ }
832
+ };
833
+ LightBurnBaseElement.register("Shape.Group", ShapeGroup);
834
+
835
+ // lib/classes/elements/shapes/ShapeBitmap.ts
836
+ var ShapeBitmap = class _ShapeBitmap extends ShapeBase {
837
+ _w;
838
+ _h;
839
+ _dataBase64;
840
+ _grayscale;
841
+ _dpi;
842
+ _ditherMode;
843
+ _halftone;
844
+ _negative;
845
+ _brightnessAdjust;
846
+ _contrastAdjust;
847
+ _gammaAdjust;
848
+ constructor() {
849
+ super();
850
+ this.token = "Shape.Bitmap";
851
+ }
852
+ get w() {
853
+ return this._w;
854
+ }
855
+ set w(value) {
856
+ this._w = value;
857
+ }
858
+ get h() {
859
+ return this._h;
860
+ }
861
+ set h(value) {
862
+ this._h = value;
863
+ }
864
+ get dataBase64() {
865
+ return this._dataBase64;
866
+ }
867
+ set dataBase64(value) {
868
+ this._dataBase64 = value;
869
+ }
870
+ get grayscale() {
871
+ return this._grayscale;
872
+ }
873
+ set grayscale(value) {
874
+ this._grayscale = value;
875
+ }
876
+ get dpi() {
877
+ return this._dpi;
878
+ }
879
+ set dpi(value) {
880
+ this._dpi = value;
881
+ }
882
+ get ditherMode() {
883
+ return this._ditherMode;
884
+ }
885
+ set ditherMode(value) {
886
+ this._ditherMode = value;
887
+ }
888
+ get halftone() {
889
+ return this._halftone;
890
+ }
891
+ set halftone(value) {
892
+ this._halftone = value;
893
+ }
894
+ get negative() {
895
+ return this._negative;
896
+ }
897
+ set negative(value) {
898
+ this._negative = value;
899
+ }
900
+ get brightnessAdjust() {
901
+ return this._brightnessAdjust;
902
+ }
903
+ set brightnessAdjust(value) {
904
+ this._brightnessAdjust = value;
905
+ }
906
+ get contrastAdjust() {
907
+ return this._contrastAdjust;
908
+ }
909
+ set contrastAdjust(value) {
910
+ this._contrastAdjust = value;
911
+ }
912
+ get gammaAdjust() {
913
+ return this._gammaAdjust;
914
+ }
915
+ set gammaAdjust(value) {
916
+ this._gammaAdjust = value;
917
+ }
918
+ static fromXmlJson(node) {
919
+ const bitmap = new _ShapeBitmap();
920
+ const common = ShapeBase.readCommon(node);
921
+ Object.assign(bitmap, common);
922
+ if (node.$) {
923
+ bitmap.w = num(node.$.W, void 0);
924
+ bitmap.h = num(node.$.H, void 0);
925
+ bitmap.grayscale = boolish(node.$.Grayscale, void 0);
926
+ bitmap.negative = boolish(node.$.Negative, void 0);
927
+ bitmap.dataBase64 = str(node.$.Data, void 0);
928
+ }
929
+ const getChildValue = (key) => {
930
+ const child = node[key];
931
+ return child?.$?.Value;
932
+ };
933
+ bitmap.dpi = num(getChildValue("DPI"), void 0);
934
+ bitmap.ditherMode = str(getChildValue("DitherMode"), void 0);
935
+ bitmap.halftone = boolish(getChildValue("Halftone"), void 0);
936
+ bitmap.brightnessAdjust = num(getChildValue("BrightnessAdjust"), void 0);
937
+ bitmap.contrastAdjust = num(getChildValue("ContrastAdjust"), void 0);
938
+ bitmap.gammaAdjust = num(getChildValue("GammaAdjust"), void 0);
939
+ return bitmap;
940
+ }
941
+ };
942
+ LightBurnBaseElement.register("Shape.Bitmap", ShapeBitmap);
943
+
944
+ // lib/svg-gen/index.ts
945
+ import { stringify } from "svgson";
946
+
947
+ // lib/svg-gen/assemble.ts
948
+ function assembleSvg(children, layout) {
949
+ return {
950
+ name: "svg",
951
+ type: "element",
952
+ value: "",
953
+ attributes: {
954
+ xmlns: "http://www.w3.org/2000/svg",
955
+ width: String(layout.width),
956
+ height: String(layout.height),
957
+ viewBox: layout.viewBox,
958
+ style: "background-color: white;"
959
+ },
960
+ children: [
961
+ {
962
+ name: "rect",
963
+ type: "element",
964
+ value: "",
965
+ attributes: {
966
+ x: String(layout.bg.x),
967
+ y: String(layout.bg.y),
968
+ width: String(layout.bg.width),
969
+ height: String(layout.bg.height),
970
+ fill: "white"
971
+ },
972
+ children: []
973
+ },
974
+ {
975
+ name: "g",
976
+ type: "element",
977
+ value: "",
978
+ attributes: {
979
+ transform: `matrix(1 0 0 -1 0 ${layout.flipY})`
980
+ },
981
+ children
982
+ }
983
+ ]
984
+ };
985
+ }
986
+
987
+ // lib/svg-gen/collect.ts
988
+ function collectShapes(root) {
989
+ const out = [];
990
+ const pushFrom = (el) => {
991
+ if (el instanceof ShapeBase) {
992
+ out.push(el);
993
+ } else if (el instanceof LightBurnProject) {
994
+ for (const c of el.children) {
995
+ if (c instanceof ShapeBase) {
996
+ out.push(c);
997
+ }
998
+ }
999
+ }
1000
+ };
1001
+ Array.isArray(root) ? root.forEach(pushFrom) : pushFrom(root);
1002
+ return out;
1003
+ }
1004
+
1005
+ // lib/svg-gen/options.ts
1006
+ var DEFAULT_OPTIONS = {
1007
+ margin: 10
1008
+ };
1009
+
1010
+ // lib/svg-gen/layout.ts
1011
+ function computeLayout(bbox, options) {
1012
+ const margin = options?.margin ?? DEFAULT_OPTIONS.margin;
1013
+ const isEmpty = bbox.minX === Infinity || bbox.maxX === -Infinity;
1014
+ if (isEmpty) {
1015
+ bbox = { minX: 0, minY: 0, maxX: 100, maxY: 100 };
1016
+ }
1017
+ const minX = Math.min(0, bbox.minX) - margin;
1018
+ const minY = Math.min(0, bbox.minY) - margin;
1019
+ const maxX = Math.max(0, bbox.maxX) + margin;
1020
+ const maxY = Math.max(0, bbox.maxY) + margin;
1021
+ const width = maxX - minX;
1022
+ const height = maxY - minY;
1023
+ const viewBox = `${minX} ${minY} ${width} ${height}`;
1024
+ const flipY = maxY + minY;
1025
+ return {
1026
+ viewBox,
1027
+ width,
1028
+ height,
1029
+ flipY,
1030
+ bg: { x: minX, y: minY, width, height }
1031
+ };
1032
+ }
1033
+
1034
+ // lib/svg-gen/_math.ts
1035
+ import {
1036
+ applyToPoint,
1037
+ compose,
1038
+ identity as matrixIdentity,
1039
+ toSVG
1040
+ } from "transformation-matrix";
1041
+ function arrayToMatrix(arr) {
1042
+ const [a, b, c, d, e, f] = arr;
1043
+ return { a, b, c, d, e, f };
1044
+ }
1045
+ function apply(m, p) {
1046
+ return applyToPoint(m, p);
1047
+ }
1048
+ function matToSvg(m) {
1049
+ return toSVG(m);
1050
+ }
1051
+ function identity() {
1052
+ return matrixIdentity();
1053
+ }
1054
+ function emptyBox() {
1055
+ return {
1056
+ minX: Number.POSITIVE_INFINITY,
1057
+ minY: Number.POSITIVE_INFINITY,
1058
+ maxX: Number.NEGATIVE_INFINITY,
1059
+ maxY: Number.NEGATIVE_INFINITY
1060
+ };
1061
+ }
1062
+ function boxUnion(a, b) {
1063
+ return {
1064
+ minX: Math.min(a.minX, b.minX),
1065
+ minY: Math.min(a.minY, b.minY),
1066
+ maxX: Math.max(a.maxX, b.maxX),
1067
+ maxY: Math.max(a.maxY, b.maxY)
1068
+ };
1069
+ }
1070
+ function addPts(bb, pts) {
1071
+ for (const p of pts) {
1072
+ bb.minX = Math.min(bb.minX, p.x);
1073
+ bb.minY = Math.min(bb.minY, p.y);
1074
+ bb.maxX = Math.max(bb.maxX, p.x);
1075
+ bb.maxY = Math.max(bb.maxY, p.y);
1076
+ }
1077
+ return bb;
1078
+ }
1079
+
1080
+ // lib/svg-gen/node-helpers.ts
1081
+ var g = (attrs, children = []) => ({
1082
+ name: "g",
1083
+ type: "element",
1084
+ value: "",
1085
+ attributes: attrs,
1086
+ children
1087
+ });
1088
+ var leaf = (name, attrs) => ({
1089
+ name,
1090
+ type: "element",
1091
+ value: "",
1092
+ attributes: attrs,
1093
+ children: []
1094
+ });
1095
+ var textNode = (value) => ({
1096
+ name: "",
1097
+ type: "text",
1098
+ value,
1099
+ attributes: {},
1100
+ children: []
1101
+ });
1102
+
1103
+ // lib/svg-gen/registry/shape-bitmap.ts
1104
+ var bitmapRenderer = {
1105
+ match: (s) => s instanceof ShapeBitmap,
1106
+ bbox: (bmp) => {
1107
+ const w = bmp.w || 0;
1108
+ const h = bmp.h || 0;
1109
+ const xform = bmp.xform ? arrayToMatrix(bmp.xform) : identity();
1110
+ const corners = [
1111
+ { x: 0, y: 0 },
1112
+ { x: w, y: 0 },
1113
+ { x: w, y: h },
1114
+ { x: 0, y: h }
1115
+ ];
1116
+ return addPts(
1117
+ emptyBox(),
1118
+ corners.map((p) => apply(xform, p))
1119
+ );
1120
+ },
1121
+ toSvg: (bmp) => {
1122
+ const xform = bmp.xform ? arrayToMatrix(bmp.xform) : identity();
1123
+ const transform = matToSvg(xform);
1124
+ const w = bmp.w || 0;
1125
+ const h = bmp.h || 0;
1126
+ const data = bmp.dataBase64 || "";
1127
+ return g({ transform }, [
1128
+ leaf("image", {
1129
+ href: `data:image/png;base64,${data}`,
1130
+ x: "0",
1131
+ y: "0",
1132
+ width: String(w),
1133
+ height: String(h)
1134
+ })
1135
+ ]);
1136
+ }
1137
+ };
1138
+
1139
+ // lib/svg-gen/palette.ts
1140
+ var LIGHTBURN_COLORS = {
1141
+ 0: "#000000",
1142
+ // C00 - Black
1143
+ 1: "#0000FF",
1144
+ // C01 - Blue
1145
+ 2: "#FF0000",
1146
+ // C02 - Red
1147
+ 3: "#00FF00",
1148
+ // C03 - Green
1149
+ 4: "#FFFF00",
1150
+ // C04 - Yellow
1151
+ 5: "#FF8000",
1152
+ // C05 - Orange
1153
+ 6: "#00FFFF",
1154
+ // C06 - Cyan
1155
+ 7: "#FF00FF",
1156
+ // C07 - Magenta
1157
+ 8: "#C0C0C0",
1158
+ // C08 - Light Gray
1159
+ 9: "#808080",
1160
+ // C09 - Gray
1161
+ 10: "#800000",
1162
+ // C10 - Maroon
1163
+ 11: "#008000",
1164
+ // C11 - Dark Green
1165
+ 12: "#000080",
1166
+ // C12 - Navy
1167
+ 13: "#808000",
1168
+ // C13 - Olive
1169
+ 14: "#800080",
1170
+ // C14 - Purple
1171
+ 15: "#008080",
1172
+ // C15 - Teal
1173
+ 16: "#A0A0A0",
1174
+ // C16 - Gray
1175
+ 17: "#8080C0",
1176
+ // C17 - Light Blue/Purple
1177
+ 18: "#FFC0C0",
1178
+ // C18 - Light Pink
1179
+ 19: "#0080FF",
1180
+ // C19 - Bright Blue
1181
+ 20: "#FF0080",
1182
+ // C20 - Hot Pink/Magenta
1183
+ 21: "#00FF80",
1184
+ // C21 - Spring Green
1185
+ 22: "#FF8040",
1186
+ // C22 - Light Orange/Peach
1187
+ 23: "#FFC0FF",
1188
+ // C23 - Light Magenta/Pink
1189
+ 24: "#FF80C0"
1190
+ // C24 - Pink
1191
+ };
1192
+ function colorForCutIndex(cutIndex) {
1193
+ if (cutIndex === void 0) return "black";
1194
+ return LIGHTBURN_COLORS[cutIndex] || "black";
1195
+ }
1196
+
1197
+ // lib/svg-gen/registry/shape-ellipse.ts
1198
+ var ellipseRenderer = {
1199
+ match: (s) => s instanceof ShapeEllipse,
1200
+ bbox: (el) => {
1201
+ const rx = el.rx || 0;
1202
+ const ry = el.ry || 0;
1203
+ const xform = el.xform ? arrayToMatrix(el.xform) : identity();
1204
+ const extremes = [
1205
+ { x: rx, y: 0 },
1206
+ { x: -rx, y: 0 },
1207
+ { x: 0, y: ry },
1208
+ { x: 0, y: -ry }
1209
+ ];
1210
+ return addPts(
1211
+ emptyBox(),
1212
+ extremes.map((p) => apply(xform, p))
1213
+ );
1214
+ },
1215
+ toSvg: (el) => {
1216
+ const xform = el.xform ? arrayToMatrix(el.xform) : identity();
1217
+ const transform = matToSvg(xform);
1218
+ const rx = el.rx || 0;
1219
+ const ry = el.ry || 0;
1220
+ const stroke = colorForCutIndex(el.cutIndex);
1221
+ const child = rx === ry ? leaf("circle", {
1222
+ cx: "0",
1223
+ cy: "0",
1224
+ r: String(rx),
1225
+ fill: "none",
1226
+ stroke
1227
+ }) : leaf("ellipse", {
1228
+ cx: "0",
1229
+ cy: "0",
1230
+ rx: String(rx),
1231
+ ry: String(ry),
1232
+ fill: "none",
1233
+ stroke
1234
+ });
1235
+ return g({ transform }, [child]);
1236
+ }
1237
+ };
1238
+
1239
+ // lib/svg-gen/registry/shape-group.ts
1240
+ var groupRenderer = {
1241
+ match: (s) => s instanceof ShapeGroup,
1242
+ bbox: (grp) => {
1243
+ return grp.children.filter((c) => c instanceof ShapeBase).reduce((bb, c) => boxUnion(bb, bboxOfShape(c)), emptyBox());
1244
+ },
1245
+ toSvg: (grp) => {
1246
+ const xform = grp.xform ? arrayToMatrix(grp.xform) : identity();
1247
+ const transform = matToSvg(xform);
1248
+ const children = grp.children.filter((c) => c instanceof ShapeBase).map(svgForShape);
1249
+ return g({ transform }, children);
1250
+ }
1251
+ };
1252
+
1253
+ // lib/svg-gen/registry/shape-path.ts
1254
+ var pathRenderer = {
1255
+ match: (s) => s instanceof ShapePath,
1256
+ bbox: (p) => {
1257
+ const xform = p.xform ? arrayToMatrix(p.xform) : identity();
1258
+ const pts = p.verts.map((v) => apply(xform, { x: v.x, y: v.y }));
1259
+ return addPts(emptyBox(), pts);
1260
+ },
1261
+ toSvg: (p) => {
1262
+ const xform = p.xform ? arrayToMatrix(p.xform) : identity();
1263
+ const transform = matToSvg(xform);
1264
+ const stroke = colorForCutIndex(p.cutIndex);
1265
+ let d = "";
1266
+ for (let i = 0; i < p.prims.length; i++) {
1267
+ const prim = p.prims[i];
1268
+ const startV = p.verts[i];
1269
+ const endV = p.verts[(i + 1) % p.verts.length];
1270
+ if (i === 0) {
1271
+ d += `M ${startV.x} ${startV.y}`;
1272
+ }
1273
+ if (prim.type === 0) {
1274
+ d += ` L ${endV.x} ${endV.y}`;
1275
+ } else if (prim.type === 1) {
1276
+ const c0x = startV.c0x ?? startV.x;
1277
+ const c0y = startV.c0y ?? startV.y;
1278
+ const c1x = endV.c1x ?? endV.x;
1279
+ const c1y = endV.c1y ?? endV.y;
1280
+ d += ` C ${c0x} ${c0y} ${c1x} ${c1y} ${endV.x} ${endV.y}`;
1281
+ }
1282
+ }
1283
+ if (d.length > 0 && p.isClosed) {
1284
+ d += " Z";
1285
+ }
1286
+ return g({ transform }, [
1287
+ leaf("path", {
1288
+ d,
1289
+ fill: "none",
1290
+ stroke,
1291
+ "stroke-width": "0.1"
1292
+ })
1293
+ ]);
1294
+ }
1295
+ };
1296
+
1297
+ // lib/svg-gen/registry/shape-rect.ts
1298
+ var rectRenderer = {
1299
+ match: (s) => s instanceof ShapeRect,
1300
+ bbox: (rect) => {
1301
+ const w = rect.w || 0;
1302
+ const h = rect.h || 0;
1303
+ const xform = rect.xform ? arrayToMatrix(rect.xform) : identity();
1304
+ const corners = [
1305
+ { x: 0, y: 0 },
1306
+ { x: w, y: 0 },
1307
+ { x: w, y: h },
1308
+ { x: 0, y: h }
1309
+ ];
1310
+ return addPts(
1311
+ emptyBox(),
1312
+ corners.map((p) => apply(xform, p))
1313
+ );
1314
+ },
1315
+ toSvg: (rect) => {
1316
+ const xform = rect.xform ? arrayToMatrix(rect.xform) : identity();
1317
+ const transform = matToSvg(xform);
1318
+ const w = rect.w || 0;
1319
+ const h = rect.h || 0;
1320
+ const cr = rect.cr || 0;
1321
+ const stroke = colorForCutIndex(rect.cutIndex);
1322
+ return g({ transform }, [
1323
+ leaf("rect", {
1324
+ x: "0",
1325
+ y: "0",
1326
+ width: String(w),
1327
+ height: String(h),
1328
+ rx: String(cr),
1329
+ ry: String(cr),
1330
+ fill: "none",
1331
+ stroke
1332
+ })
1333
+ ]);
1334
+ }
1335
+ };
1336
+
1337
+ // lib/svg-gen/registry/shape-text.ts
1338
+ var textRenderer = {
1339
+ match: (s) => s instanceof ShapeText,
1340
+ bbox: (t) => {
1341
+ const xform = t.xform ? arrayToMatrix(t.xform) : identity();
1342
+ if (t.backupPath && t.backupPath.verts.length) {
1343
+ const pts = t.backupPath.verts.map(
1344
+ (v) => apply(xform, { x: v.x, y: v.y })
1345
+ );
1346
+ return addPts(emptyBox(), pts);
1347
+ }
1348
+ return addPts(emptyBox(), [
1349
+ apply(xform, { x: 0, y: 0 }),
1350
+ apply(xform, { x: 100, y: 50 })
1351
+ ]);
1352
+ },
1353
+ toSvg: (t) => {
1354
+ const xform = t.xform ? arrayToMatrix(t.xform) : identity();
1355
+ const transform = matToSvg(xform);
1356
+ const stroke = colorForCutIndex(t.cutIndex);
1357
+ const text = t.text || "";
1358
+ return g({ transform }, [
1359
+ {
1360
+ name: "text",
1361
+ type: "element",
1362
+ value: "",
1363
+ attributes: { x: "0", y: "0", fill: stroke },
1364
+ children: [textNode(text)]
1365
+ }
1366
+ ]);
1367
+ }
1368
+ };
1369
+
1370
+ // lib/svg-gen/registry/index.ts
1371
+ var REGISTRY = [
1372
+ rectRenderer,
1373
+ ellipseRenderer,
1374
+ pathRenderer,
1375
+ bitmapRenderer,
1376
+ textRenderer,
1377
+ groupRenderer
1378
+ ];
1379
+ function findRenderer(shape) {
1380
+ const r = REGISTRY.find((r2) => r2.match(shape));
1381
+ if (!r) throw new Error(`No renderer for ${shape.token}`);
1382
+ return r;
1383
+ }
1384
+ function bboxOfShape(shape) {
1385
+ return findRenderer(shape).bbox(shape);
1386
+ }
1387
+ function svgForShape(shape) {
1388
+ return findRenderer(shape).toSvg(shape);
1389
+ }
1390
+ function measure(shapes) {
1391
+ return shapes.reduce((acc, s) => boxUnion(acc, bboxOfShape(s)), emptyBox());
1392
+ }
1393
+ function renderAll(shapes) {
1394
+ return shapes.map(svgForShape);
1395
+ }
1396
+
1397
+ // lib/svg-gen/index.ts
1398
+ function generateLightBurnSvg(root, options) {
1399
+ const shapes = collectShapes(root);
1400
+ const bbox = measure(shapes);
1401
+ const layout = computeLayout(bbox, options);
1402
+ const nodes = renderAll(shapes);
1403
+ const svgTree = assembleSvg(nodes, layout);
1404
+ return stringify(svgTree);
1405
+ }
1406
+ export {
1407
+ LightBurnBaseElement as BaseLightBurnElement,
1408
+ CutSetting,
1409
+ LightBurnBaseElement,
1410
+ LightBurnProject,
1411
+ Notes,
1412
+ ShapeBase,
1413
+ ShapeBitmap,
1414
+ ShapeEllipse,
1415
+ ShapeGroup,
1416
+ ShapePath,
1417
+ ShapeRect,
1418
+ ShapeText,
1419
+ Thumbnail,
1420
+ UIPrefs,
1421
+ VariableText,
1422
+ generateLightBurnSvg,
1423
+ parseXml
1424
+ };