lbrnts 0.0.7 → 0.0.9

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.d.ts CHANGED
@@ -91,6 +91,9 @@ interface CutSettingInit {
91
91
  angle?: number;
92
92
  overScanning?: number;
93
93
  lineAngle?: number;
94
+ crossHatch?: boolean;
95
+ frequency?: number;
96
+ pulseWidth?: number;
94
97
  }
95
98
  declare class CutSetting extends LightBurnBaseElement {
96
99
  private _type;
@@ -115,6 +118,9 @@ declare class CutSetting extends LightBurnBaseElement {
115
118
  private _angle?;
116
119
  private _overScanning?;
117
120
  private _lineAngle?;
121
+ private _crossHatch?;
122
+ private _frequency?;
123
+ private _pulseWidth?;
118
124
  constructor(init?: CutSettingInit);
119
125
  get type(): string;
120
126
  set type(value: string);
@@ -160,6 +166,12 @@ declare class CutSetting extends LightBurnBaseElement {
160
166
  set overScanning(value: number | undefined);
161
167
  get lineAngle(): number | undefined;
162
168
  set lineAngle(value: number | undefined);
169
+ get crossHatch(): boolean | undefined;
170
+ set crossHatch(value: boolean | undefined);
171
+ get frequency(): number | undefined;
172
+ set frequency(value: number | undefined);
173
+ get pulseWidth(): number | undefined;
174
+ set pulseWidth(value: number | undefined);
163
175
  getXmlAttributes(): Record<string, string | number | boolean | undefined>;
164
176
  getChildren(): LightBurnBaseElement[];
165
177
  static fromXmlJson(node: XmlJsonElement): CutSetting;
package/dist/index.js CHANGED
@@ -104,7 +104,7 @@ ${this.toXml()}`;
104
104
  get [Symbol.toStringTag]() {
105
105
  return this.getString();
106
106
  }
107
- [Symbol.for("nodejs.util.inspect.custom")]() {
107
+ [/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")]() {
108
108
  return this.getString();
109
109
  }
110
110
  // =========================== STATIC METHODS ===========================
@@ -293,6 +293,9 @@ var CutSetting = class _CutSetting extends LightBurnBaseElement {
293
293
  _angle;
294
294
  _overScanning;
295
295
  _lineAngle;
296
+ _crossHatch;
297
+ _frequency;
298
+ _pulseWidth;
296
299
  constructor(init) {
297
300
  super();
298
301
  this.token = "CutSetting";
@@ -321,6 +324,9 @@ var CutSetting = class _CutSetting extends LightBurnBaseElement {
321
324
  if (init.overScanning !== void 0)
322
325
  this._overScanning = init.overScanning;
323
326
  if (init.lineAngle !== void 0) this._lineAngle = init.lineAngle;
327
+ if (init.crossHatch !== void 0) this._crossHatch = init.crossHatch;
328
+ if (init.frequency !== void 0) this._frequency = init.frequency;
329
+ if (init.pulseWidth !== void 0) this._pulseWidth = init.pulseWidth;
324
330
  }
325
331
  }
326
332
  get type() {
@@ -455,6 +461,24 @@ var CutSetting = class _CutSetting extends LightBurnBaseElement {
455
461
  set lineAngle(value) {
456
462
  this._lineAngle = value;
457
463
  }
464
+ get crossHatch() {
465
+ return this._crossHatch;
466
+ }
467
+ set crossHatch(value) {
468
+ this._crossHatch = value;
469
+ }
470
+ get frequency() {
471
+ return this._frequency;
472
+ }
473
+ set frequency(value) {
474
+ this._frequency = value;
475
+ }
476
+ get pulseWidth() {
477
+ return this._pulseWidth;
478
+ }
479
+ set pulseWidth(value) {
480
+ this._pulseWidth = value;
481
+ }
458
482
  getXmlAttributes() {
459
483
  return {
460
484
  type: this._type
@@ -483,7 +507,10 @@ var CutSetting = class _CutSetting extends LightBurnBaseElement {
483
507
  "interval",
484
508
  "angle",
485
509
  "overScanning",
486
- "lineAngle"
510
+ "lineAngle",
511
+ "crossHatch",
512
+ "frequency",
513
+ "pulseWidth"
487
514
  ];
488
515
  for (const prop of props) {
489
516
  const value = this[`_${prop}`];
@@ -523,6 +550,9 @@ var CutSetting = class _CutSetting extends LightBurnBaseElement {
523
550
  cs.angle = num(getChildValue("angle"), void 0);
524
551
  cs.overScanning = num(getChildValue("overScanning"), void 0);
525
552
  cs.lineAngle = num(getChildValue("lineAngle"), void 0);
553
+ cs.crossHatch = boolish(getChildValue("crossHatch"), void 0);
554
+ cs.frequency = num(getChildValue("frequency"), void 0);
555
+ cs.pulseWidth = num(getChildValue("pulseWidth"), void 0);
526
556
  return cs;
527
557
  }
528
558
  };
@@ -535,9 +565,18 @@ var CutSettingPropertyElement = class extends LightBurnBaseElement {
535
565
  this.propName = propName;
536
566
  this.propValue = propValue;
537
567
  }
568
+ formatValue(value) {
569
+ if (typeof value === "number") {
570
+ if (value !== 0 && Math.abs(value) < 1e-3) {
571
+ return value.toFixed(9).replace(/\.?0+$/, "");
572
+ }
573
+ return value.toString();
574
+ }
575
+ return String(value);
576
+ }
538
577
  toXml(indent = 0) {
539
578
  const indentStr = " ".repeat(indent);
540
- return `${indentStr}<${this.propName} Value="${this.propValue}"/>`;
579
+ return `${indentStr}<${this.propName} Value="${this.formatValue(this.propValue)}"/>`;
541
580
  }
542
581
  };
543
582
  LightBurnBaseElement.register("CutSetting", CutSetting);
@@ -1238,6 +1277,28 @@ function collectShapes(root) {
1238
1277
  return out;
1239
1278
  }
1240
1279
 
1280
+ // lib/svg-gen/collect-cut-settings.ts
1281
+ function collectCutSettings(root) {
1282
+ const settings = /* @__PURE__ */ new Map();
1283
+ const processElement = (el) => {
1284
+ if (el instanceof CutSetting && el.index !== void 0) {
1285
+ settings.set(el.index, el);
1286
+ } else if (el instanceof LightBurnProject) {
1287
+ for (const child of el.children) {
1288
+ if (child instanceof CutSetting && child.index !== void 0) {
1289
+ settings.set(child.index, child);
1290
+ }
1291
+ }
1292
+ }
1293
+ };
1294
+ if (Array.isArray(root)) {
1295
+ root.forEach(processElement);
1296
+ } else {
1297
+ processElement(root);
1298
+ }
1299
+ return settings;
1300
+ }
1301
+
1241
1302
  // lib/svg-gen/options.ts
1242
1303
  var DEFAULT_OPTIONS = {
1243
1304
  margin: 10
@@ -1354,7 +1415,7 @@ var bitmapRenderer = {
1354
1415
  corners.map((p) => apply(xform, p))
1355
1416
  );
1356
1417
  },
1357
- toSvg: (bmp) => {
1418
+ toSvg: (bmp, _cutSettings) => {
1358
1419
  const xform = bmp.xform ? arrayToMatrix(bmp.xform) : identity();
1359
1420
  const transform = matToSvg(xform);
1360
1421
  const w = bmp.w || 0;
@@ -1372,6 +1433,122 @@ var bitmapRenderer = {
1372
1433
  }
1373
1434
  };
1374
1435
 
1436
+ // lib/svg-gen/fill-patterns.ts
1437
+ function generateScanLines(bbox, settings, color) {
1438
+ const lines = [];
1439
+ const angleRad = settings.angle * Math.PI / 180;
1440
+ const primaryLines = generateLinesAtAngle(
1441
+ bbox,
1442
+ settings.interval,
1443
+ angleRad,
1444
+ color
1445
+ );
1446
+ lines.push(...primaryLines);
1447
+ if (settings.crossHatch) {
1448
+ const perpAngle = angleRad + Math.PI / 2;
1449
+ const crossLines = generateLinesAtAngle(
1450
+ bbox,
1451
+ settings.interval,
1452
+ perpAngle,
1453
+ color
1454
+ );
1455
+ lines.push(...crossLines);
1456
+ }
1457
+ return lines;
1458
+ }
1459
+ function generateLinesAtAngle(bbox, interval, angle, color) {
1460
+ const lines = [];
1461
+ const diagonal = Math.sqrt(
1462
+ Math.pow(bbox.maxX - bbox.minX, 2) + Math.pow(bbox.maxY - bbox.minY, 2)
1463
+ );
1464
+ const dx = Math.cos(angle);
1465
+ const dy = Math.sin(angle);
1466
+ const px = -Math.sin(angle);
1467
+ const py = Math.cos(angle);
1468
+ const cx = (bbox.minX + bbox.maxX) / 2;
1469
+ const cy = (bbox.minY + bbox.maxY) / 2;
1470
+ const numLines = Math.ceil(diagonal / interval) + 2;
1471
+ for (let i = -numLines / 2; i <= numLines / 2; i++) {
1472
+ const offset = i * interval;
1473
+ const startX = cx + px * offset - dx * diagonal;
1474
+ const startY = cy + py * offset - dy * diagonal;
1475
+ const endX = cx + px * offset + dx * diagonal;
1476
+ const endY = cy + py * offset + dy * diagonal;
1477
+ const clipped = clipLineToBBox(
1478
+ startX,
1479
+ startY,
1480
+ endX,
1481
+ endY,
1482
+ bbox.minX,
1483
+ bbox.minY,
1484
+ bbox.maxX,
1485
+ bbox.maxY
1486
+ );
1487
+ if (clipped) {
1488
+ lines.push(
1489
+ leaf("line", {
1490
+ x1: String(clipped.x1),
1491
+ y1: String(clipped.y1),
1492
+ x2: String(clipped.x2),
1493
+ y2: String(clipped.y2),
1494
+ stroke: color,
1495
+ "stroke-width": "0.1",
1496
+ "stroke-opacity": "0.8"
1497
+ })
1498
+ );
1499
+ }
1500
+ }
1501
+ return lines;
1502
+ }
1503
+ function clipLineToBBox(x1, y1, x2, y2, minX, minY, maxX, maxY) {
1504
+ const INSIDE = 0;
1505
+ const LEFT = 1;
1506
+ const RIGHT = 2;
1507
+ const BOTTOM = 4;
1508
+ const TOP = 8;
1509
+ function computeOutCode(x, y) {
1510
+ let code = INSIDE;
1511
+ if (x < minX) code |= LEFT;
1512
+ else if (x > maxX) code |= RIGHT;
1513
+ if (y < minY) code |= BOTTOM;
1514
+ else if (y > maxY) code |= TOP;
1515
+ return code;
1516
+ }
1517
+ let outcode1 = computeOutCode(x1, y1);
1518
+ let outcode2 = computeOutCode(x2, y2);
1519
+ while (true) {
1520
+ if (!(outcode1 | outcode2)) {
1521
+ return { x1, y1, x2, y2 };
1522
+ } else if (outcode1 & outcode2) {
1523
+ return null;
1524
+ }
1525
+ const outcodeOut = outcode1 ? outcode1 : outcode2;
1526
+ let x, y;
1527
+ if (outcodeOut & TOP) {
1528
+ x = x1 + (x2 - x1) * (maxY - y1) / (y2 - y1);
1529
+ y = maxY;
1530
+ } else if (outcodeOut & BOTTOM) {
1531
+ x = x1 + (x2 - x1) * (minY - y1) / (y2 - y1);
1532
+ y = minY;
1533
+ } else if (outcodeOut & RIGHT) {
1534
+ y = y1 + (y2 - y1) * (maxX - x1) / (x2 - x1);
1535
+ x = maxX;
1536
+ } else {
1537
+ y = y1 + (y2 - y1) * (minX - x1) / (x2 - x1);
1538
+ x = minX;
1539
+ }
1540
+ if (outcodeOut === outcode1) {
1541
+ x1 = x;
1542
+ y1 = y;
1543
+ outcode1 = computeOutCode(x1, y1);
1544
+ } else {
1545
+ x2 = x;
1546
+ y2 = y;
1547
+ outcode2 = computeOutCode(x2, y2);
1548
+ }
1549
+ }
1550
+ }
1551
+
1375
1552
  // lib/svg-gen/palette.ts
1376
1553
  var LIGHTBURN_COLORS = {
1377
1554
  0: "#000000",
@@ -1448,12 +1625,30 @@ var ellipseRenderer = {
1448
1625
  extremes.map((p) => apply(xform, p))
1449
1626
  );
1450
1627
  },
1451
- toSvg: (el) => {
1628
+ toSvg: (el, cutSettings) => {
1452
1629
  const xform = el.xform ? arrayToMatrix(el.xform) : identity();
1453
1630
  const transform = matToSvg(xform);
1454
1631
  const rx = el.rx || 0;
1455
1632
  const ry = el.ry || 0;
1456
1633
  const stroke = colorForCutIndex(el.cutIndex);
1634
+ const children = [];
1635
+ const cutSetting = el.cutIndex !== void 0 ? cutSettings.get(el.cutIndex) : void 0;
1636
+ const shouldShowFill = cutSetting && (cutSetting.type === "Scan" || cutSetting.type === "Scan+Cut");
1637
+ if (shouldShowFill && cutSetting) {
1638
+ const localBBox = {
1639
+ minX: -rx,
1640
+ minY: -ry,
1641
+ maxX: rx,
1642
+ maxY: ry
1643
+ };
1644
+ const fillSettings = {
1645
+ interval: cutSetting.interval || 0.1,
1646
+ angle: cutSetting.angle || 0,
1647
+ crossHatch: cutSetting.crossHatch || false
1648
+ };
1649
+ const fillLines = generateScanLines(localBBox, fillSettings, stroke);
1650
+ children.push(...fillLines);
1651
+ }
1457
1652
  const child = rx === ry ? leaf("circle", {
1458
1653
  cx: "0",
1459
1654
  cy: "0",
@@ -1468,7 +1663,8 @@ var ellipseRenderer = {
1468
1663
  fill: "none",
1469
1664
  stroke
1470
1665
  });
1471
- return g({ transform }, [child]);
1666
+ children.push(child);
1667
+ return g({ transform }, children);
1472
1668
  }
1473
1669
  };
1474
1670
 
@@ -1478,10 +1674,10 @@ var groupRenderer = {
1478
1674
  bbox: (grp) => {
1479
1675
  return grp.children.filter((c) => c instanceof ShapeBase).reduce((bb, c) => boxUnion(bb, bboxOfShape(c)), emptyBox());
1480
1676
  },
1481
- toSvg: (grp) => {
1677
+ toSvg: (grp, cutSettings) => {
1482
1678
  const xform = grp.xform ? arrayToMatrix(grp.xform) : identity();
1483
1679
  const transform = matToSvg(xform);
1484
- const children = grp.children.filter((c) => c instanceof ShapeBase).map(svgForShape);
1680
+ const children = grp.children.filter((c) => c instanceof ShapeBase).map((c) => svgForShape(c, cutSettings));
1485
1681
  return g({ transform }, children);
1486
1682
  }
1487
1683
  };
@@ -1494,10 +1690,11 @@ var pathRenderer = {
1494
1690
  const pts = p.verts.map((v) => apply(xform, { x: v.x, y: v.y }));
1495
1691
  return addPts(emptyBox(), pts);
1496
1692
  },
1497
- toSvg: (p) => {
1693
+ toSvg: (p, cutSettings) => {
1498
1694
  const xform = p.xform ? arrayToMatrix(p.xform) : identity();
1499
1695
  const transform = matToSvg(xform);
1500
1696
  const stroke = colorForCutIndex(p.cutIndex);
1697
+ const children = [];
1501
1698
  let d = "";
1502
1699
  for (let i = 0; i < p.prims.length; i++) {
1503
1700
  const prim = p.prims[i];
@@ -1519,14 +1716,46 @@ var pathRenderer = {
1519
1716
  if (d.length > 0 && p.isClosed) {
1520
1717
  d += " Z";
1521
1718
  }
1522
- return g({ transform }, [
1719
+ const cutSetting = p.cutIndex !== void 0 ? cutSettings.get(p.cutIndex) : void 0;
1720
+ const shouldShowFill = p.isClosed && cutSetting && (cutSetting.type === "Scan" || cutSetting.type === "Scan+Cut");
1721
+ if (shouldShowFill && cutSetting) {
1722
+ const bbox = addPts(
1723
+ emptyBox(),
1724
+ p.verts.map((v) => ({ x: v.x, y: v.y }))
1725
+ );
1726
+ const fillSettings = {
1727
+ interval: cutSetting.interval || 0.1,
1728
+ angle: cutSetting.angle || 0,
1729
+ crossHatch: cutSetting.crossHatch || false
1730
+ };
1731
+ const fillLines = generateScanLines(bbox, fillSettings, stroke);
1732
+ const clipId = `clip-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
1733
+ const clipPath = {
1734
+ name: "clipPath",
1735
+ type: "element",
1736
+ value: "",
1737
+ attributes: { id: clipId },
1738
+ children: [leaf("path", { d })]
1739
+ };
1740
+ const clippedGroup = {
1741
+ name: "g",
1742
+ type: "element",
1743
+ value: "",
1744
+ attributes: { "clip-path": `url(#${clipId})` },
1745
+ children: fillLines
1746
+ };
1747
+ children.push(clipPath);
1748
+ children.push(clippedGroup);
1749
+ }
1750
+ children.push(
1523
1751
  leaf("path", {
1524
1752
  d,
1525
1753
  fill: "none",
1526
1754
  stroke,
1527
1755
  "stroke-width": "0.1"
1528
1756
  })
1529
- ]);
1757
+ );
1758
+ return g({ transform }, children);
1530
1759
  }
1531
1760
  };
1532
1761
 
@@ -1548,14 +1777,32 @@ var rectRenderer = {
1548
1777
  corners.map((p) => apply(xform, p))
1549
1778
  );
1550
1779
  },
1551
- toSvg: (rect) => {
1780
+ toSvg: (rect, cutSettings) => {
1552
1781
  const xform = rect.xform ? arrayToMatrix(rect.xform) : identity();
1553
1782
  const transform = matToSvg(xform);
1554
1783
  const w = rect.w || 0;
1555
1784
  const h = rect.h || 0;
1556
1785
  const cr = rect.cr || 0;
1557
1786
  const stroke = colorForCutIndex(rect.cutIndex);
1558
- return g({ transform }, [
1787
+ const children = [];
1788
+ const cutSetting = rect.cutIndex !== void 0 ? cutSettings.get(rect.cutIndex) : void 0;
1789
+ const shouldShowFill = cutSetting && (cutSetting.type === "Scan" || cutSetting.type === "Scan+Cut");
1790
+ if (shouldShowFill && cutSetting) {
1791
+ const localBBox = {
1792
+ minX: 0,
1793
+ minY: 0,
1794
+ maxX: w,
1795
+ maxY: h
1796
+ };
1797
+ const fillSettings = {
1798
+ interval: cutSetting.interval || 0.1,
1799
+ angle: cutSetting.angle || 0,
1800
+ crossHatch: cutSetting.crossHatch || false
1801
+ };
1802
+ const fillLines = generateScanLines(localBBox, fillSettings, stroke);
1803
+ children.push(...fillLines);
1804
+ }
1805
+ children.push(
1559
1806
  leaf("rect", {
1560
1807
  x: "0",
1561
1808
  y: "0",
@@ -1566,7 +1813,8 @@ var rectRenderer = {
1566
1813
  fill: "none",
1567
1814
  stroke
1568
1815
  })
1569
- ]);
1816
+ );
1817
+ return g({ transform }, children);
1570
1818
  }
1571
1819
  };
1572
1820
 
@@ -1586,7 +1834,7 @@ var textRenderer = {
1586
1834
  apply(xform, { x: 100, y: 50 })
1587
1835
  ]);
1588
1836
  },
1589
- toSvg: (t) => {
1837
+ toSvg: (t, _cutSettings) => {
1590
1838
  const xform = t.xform ? arrayToMatrix(t.xform) : identity();
1591
1839
  const transform = matToSvg(xform);
1592
1840
  const stroke = colorForCutIndex(t.cutIndex);
@@ -1620,22 +1868,23 @@ function findRenderer(shape) {
1620
1868
  function bboxOfShape(shape) {
1621
1869
  return findRenderer(shape).bbox(shape);
1622
1870
  }
1623
- function svgForShape(shape) {
1624
- return findRenderer(shape).toSvg(shape);
1871
+ function svgForShape(shape, cutSettings) {
1872
+ return findRenderer(shape).toSvg(shape, cutSettings);
1625
1873
  }
1626
1874
  function measure(shapes) {
1627
1875
  return shapes.reduce((acc, s) => boxUnion(acc, bboxOfShape(s)), emptyBox());
1628
1876
  }
1629
- function renderAll(shapes) {
1630
- return shapes.map(svgForShape);
1877
+ function renderAll(shapes, cutSettings) {
1878
+ return shapes.map((s) => svgForShape(s, cutSettings));
1631
1879
  }
1632
1880
 
1633
1881
  // lib/svg-gen/index.ts
1634
1882
  function generateLightBurnSvg(root, options) {
1635
1883
  const shapes = collectShapes(root);
1884
+ const cutSettings = collectCutSettings(root);
1636
1885
  const bbox = measure(shapes);
1637
1886
  const layout = computeLayout(bbox, options);
1638
- const nodes = renderAll(shapes);
1887
+ const nodes = renderAll(shapes, cutSettings);
1639
1888
  const svgTree = assembleSvg(nodes, layout);
1640
1889
  return stringify(svgTree);
1641
1890
  }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "lbrnts",
3
3
  "main": "dist/index.js",
4
4
  "type": "module",
5
- "version": "0.0.7",
5
+ "version": "0.0.9",
6
6
  "files": [
7
7
  "dist"
8
8
  ],