lbrnts 0.0.7 → 0.0.8

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,7 @@ interface CutSettingInit {
91
91
  angle?: number;
92
92
  overScanning?: number;
93
93
  lineAngle?: number;
94
+ crossHatch?: boolean;
94
95
  }
95
96
  declare class CutSetting extends LightBurnBaseElement {
96
97
  private _type;
@@ -115,6 +116,7 @@ declare class CutSetting extends LightBurnBaseElement {
115
116
  private _angle?;
116
117
  private _overScanning?;
117
118
  private _lineAngle?;
119
+ private _crossHatch?;
118
120
  constructor(init?: CutSettingInit);
119
121
  get type(): string;
120
122
  set type(value: string);
@@ -160,6 +162,8 @@ declare class CutSetting extends LightBurnBaseElement {
160
162
  set overScanning(value: number | undefined);
161
163
  get lineAngle(): number | undefined;
162
164
  set lineAngle(value: number | undefined);
165
+ get crossHatch(): boolean | undefined;
166
+ set crossHatch(value: boolean | undefined);
163
167
  getXmlAttributes(): Record<string, string | number | boolean | undefined>;
164
168
  getChildren(): LightBurnBaseElement[];
165
169
  static fromXmlJson(node: XmlJsonElement): CutSetting;
package/dist/index.js CHANGED
@@ -293,6 +293,7 @@ var CutSetting = class _CutSetting extends LightBurnBaseElement {
293
293
  _angle;
294
294
  _overScanning;
295
295
  _lineAngle;
296
+ _crossHatch;
296
297
  constructor(init) {
297
298
  super();
298
299
  this.token = "CutSetting";
@@ -321,6 +322,7 @@ var CutSetting = class _CutSetting extends LightBurnBaseElement {
321
322
  if (init.overScanning !== void 0)
322
323
  this._overScanning = init.overScanning;
323
324
  if (init.lineAngle !== void 0) this._lineAngle = init.lineAngle;
325
+ if (init.crossHatch !== void 0) this._crossHatch = init.crossHatch;
324
326
  }
325
327
  }
326
328
  get type() {
@@ -455,6 +457,12 @@ var CutSetting = class _CutSetting extends LightBurnBaseElement {
455
457
  set lineAngle(value) {
456
458
  this._lineAngle = value;
457
459
  }
460
+ get crossHatch() {
461
+ return this._crossHatch;
462
+ }
463
+ set crossHatch(value) {
464
+ this._crossHatch = value;
465
+ }
458
466
  getXmlAttributes() {
459
467
  return {
460
468
  type: this._type
@@ -483,7 +491,8 @@ var CutSetting = class _CutSetting extends LightBurnBaseElement {
483
491
  "interval",
484
492
  "angle",
485
493
  "overScanning",
486
- "lineAngle"
494
+ "lineAngle",
495
+ "crossHatch"
487
496
  ];
488
497
  for (const prop of props) {
489
498
  const value = this[`_${prop}`];
@@ -523,6 +532,7 @@ var CutSetting = class _CutSetting extends LightBurnBaseElement {
523
532
  cs.angle = num(getChildValue("angle"), void 0);
524
533
  cs.overScanning = num(getChildValue("overScanning"), void 0);
525
534
  cs.lineAngle = num(getChildValue("lineAngle"), void 0);
535
+ cs.crossHatch = boolish(getChildValue("crossHatch"), void 0);
526
536
  return cs;
527
537
  }
528
538
  };
@@ -1238,6 +1248,28 @@ function collectShapes(root) {
1238
1248
  return out;
1239
1249
  }
1240
1250
 
1251
+ // lib/svg-gen/collect-cut-settings.ts
1252
+ function collectCutSettings(root) {
1253
+ const settings = /* @__PURE__ */ new Map();
1254
+ const processElement = (el) => {
1255
+ if (el instanceof CutSetting && el.index !== void 0) {
1256
+ settings.set(el.index, el);
1257
+ } else if (el instanceof LightBurnProject) {
1258
+ for (const child of el.children) {
1259
+ if (child instanceof CutSetting && child.index !== void 0) {
1260
+ settings.set(child.index, child);
1261
+ }
1262
+ }
1263
+ }
1264
+ };
1265
+ if (Array.isArray(root)) {
1266
+ root.forEach(processElement);
1267
+ } else {
1268
+ processElement(root);
1269
+ }
1270
+ return settings;
1271
+ }
1272
+
1241
1273
  // lib/svg-gen/options.ts
1242
1274
  var DEFAULT_OPTIONS = {
1243
1275
  margin: 10
@@ -1354,7 +1386,7 @@ var bitmapRenderer = {
1354
1386
  corners.map((p) => apply(xform, p))
1355
1387
  );
1356
1388
  },
1357
- toSvg: (bmp) => {
1389
+ toSvg: (bmp, _cutSettings) => {
1358
1390
  const xform = bmp.xform ? arrayToMatrix(bmp.xform) : identity();
1359
1391
  const transform = matToSvg(xform);
1360
1392
  const w = bmp.w || 0;
@@ -1372,6 +1404,122 @@ var bitmapRenderer = {
1372
1404
  }
1373
1405
  };
1374
1406
 
1407
+ // lib/svg-gen/fill-patterns.ts
1408
+ function generateScanLines(bbox, settings, color) {
1409
+ const lines = [];
1410
+ const angleRad = settings.angle * Math.PI / 180;
1411
+ const primaryLines = generateLinesAtAngle(
1412
+ bbox,
1413
+ settings.interval,
1414
+ angleRad,
1415
+ color
1416
+ );
1417
+ lines.push(...primaryLines);
1418
+ if (settings.crossHatch) {
1419
+ const perpAngle = angleRad + Math.PI / 2;
1420
+ const crossLines = generateLinesAtAngle(
1421
+ bbox,
1422
+ settings.interval,
1423
+ perpAngle,
1424
+ color
1425
+ );
1426
+ lines.push(...crossLines);
1427
+ }
1428
+ return lines;
1429
+ }
1430
+ function generateLinesAtAngle(bbox, interval, angle, color) {
1431
+ const lines = [];
1432
+ const diagonal = Math.sqrt(
1433
+ Math.pow(bbox.maxX - bbox.minX, 2) + Math.pow(bbox.maxY - bbox.minY, 2)
1434
+ );
1435
+ const dx = Math.cos(angle);
1436
+ const dy = Math.sin(angle);
1437
+ const px = -Math.sin(angle);
1438
+ const py = Math.cos(angle);
1439
+ const cx = (bbox.minX + bbox.maxX) / 2;
1440
+ const cy = (bbox.minY + bbox.maxY) / 2;
1441
+ const numLines = Math.ceil(diagonal / interval) + 2;
1442
+ for (let i = -numLines / 2; i <= numLines / 2; i++) {
1443
+ const offset = i * interval;
1444
+ const startX = cx + px * offset - dx * diagonal;
1445
+ const startY = cy + py * offset - dy * diagonal;
1446
+ const endX = cx + px * offset + dx * diagonal;
1447
+ const endY = cy + py * offset + dy * diagonal;
1448
+ const clipped = clipLineToBBox(
1449
+ startX,
1450
+ startY,
1451
+ endX,
1452
+ endY,
1453
+ bbox.minX,
1454
+ bbox.minY,
1455
+ bbox.maxX,
1456
+ bbox.maxY
1457
+ );
1458
+ if (clipped) {
1459
+ lines.push(
1460
+ leaf("line", {
1461
+ x1: String(clipped.x1),
1462
+ y1: String(clipped.y1),
1463
+ x2: String(clipped.x2),
1464
+ y2: String(clipped.y2),
1465
+ stroke: color,
1466
+ "stroke-width": "0.1",
1467
+ "stroke-opacity": "0.8"
1468
+ })
1469
+ );
1470
+ }
1471
+ }
1472
+ return lines;
1473
+ }
1474
+ function clipLineToBBox(x1, y1, x2, y2, minX, minY, maxX, maxY) {
1475
+ const INSIDE = 0;
1476
+ const LEFT = 1;
1477
+ const RIGHT = 2;
1478
+ const BOTTOM = 4;
1479
+ const TOP = 8;
1480
+ function computeOutCode(x, y) {
1481
+ let code = INSIDE;
1482
+ if (x < minX) code |= LEFT;
1483
+ else if (x > maxX) code |= RIGHT;
1484
+ if (y < minY) code |= BOTTOM;
1485
+ else if (y > maxY) code |= TOP;
1486
+ return code;
1487
+ }
1488
+ let outcode1 = computeOutCode(x1, y1);
1489
+ let outcode2 = computeOutCode(x2, y2);
1490
+ while (true) {
1491
+ if (!(outcode1 | outcode2)) {
1492
+ return { x1, y1, x2, y2 };
1493
+ } else if (outcode1 & outcode2) {
1494
+ return null;
1495
+ }
1496
+ const outcodeOut = outcode1 ? outcode1 : outcode2;
1497
+ let x, y;
1498
+ if (outcodeOut & TOP) {
1499
+ x = x1 + (x2 - x1) * (maxY - y1) / (y2 - y1);
1500
+ y = maxY;
1501
+ } else if (outcodeOut & BOTTOM) {
1502
+ x = x1 + (x2 - x1) * (minY - y1) / (y2 - y1);
1503
+ y = minY;
1504
+ } else if (outcodeOut & RIGHT) {
1505
+ y = y1 + (y2 - y1) * (maxX - x1) / (x2 - x1);
1506
+ x = maxX;
1507
+ } else {
1508
+ y = y1 + (y2 - y1) * (minX - x1) / (x2 - x1);
1509
+ x = minX;
1510
+ }
1511
+ if (outcodeOut === outcode1) {
1512
+ x1 = x;
1513
+ y1 = y;
1514
+ outcode1 = computeOutCode(x1, y1);
1515
+ } else {
1516
+ x2 = x;
1517
+ y2 = y;
1518
+ outcode2 = computeOutCode(x2, y2);
1519
+ }
1520
+ }
1521
+ }
1522
+
1375
1523
  // lib/svg-gen/palette.ts
1376
1524
  var LIGHTBURN_COLORS = {
1377
1525
  0: "#000000",
@@ -1448,12 +1596,30 @@ var ellipseRenderer = {
1448
1596
  extremes.map((p) => apply(xform, p))
1449
1597
  );
1450
1598
  },
1451
- toSvg: (el) => {
1599
+ toSvg: (el, cutSettings) => {
1452
1600
  const xform = el.xform ? arrayToMatrix(el.xform) : identity();
1453
1601
  const transform = matToSvg(xform);
1454
1602
  const rx = el.rx || 0;
1455
1603
  const ry = el.ry || 0;
1456
1604
  const stroke = colorForCutIndex(el.cutIndex);
1605
+ const children = [];
1606
+ const cutSetting = el.cutIndex !== void 0 ? cutSettings.get(el.cutIndex) : void 0;
1607
+ const shouldShowFill = cutSetting && (cutSetting.type === "Scan" || cutSetting.type === "Scan+Cut");
1608
+ if (shouldShowFill && cutSetting) {
1609
+ const localBBox = {
1610
+ minX: -rx,
1611
+ minY: -ry,
1612
+ maxX: rx,
1613
+ maxY: ry
1614
+ };
1615
+ const fillSettings = {
1616
+ interval: cutSetting.interval || 0.1,
1617
+ angle: cutSetting.angle || 0,
1618
+ crossHatch: cutSetting.crossHatch || false
1619
+ };
1620
+ const fillLines = generateScanLines(localBBox, fillSettings, stroke);
1621
+ children.push(...fillLines);
1622
+ }
1457
1623
  const child = rx === ry ? leaf("circle", {
1458
1624
  cx: "0",
1459
1625
  cy: "0",
@@ -1468,7 +1634,8 @@ var ellipseRenderer = {
1468
1634
  fill: "none",
1469
1635
  stroke
1470
1636
  });
1471
- return g({ transform }, [child]);
1637
+ children.push(child);
1638
+ return g({ transform }, children);
1472
1639
  }
1473
1640
  };
1474
1641
 
@@ -1478,10 +1645,10 @@ var groupRenderer = {
1478
1645
  bbox: (grp) => {
1479
1646
  return grp.children.filter((c) => c instanceof ShapeBase).reduce((bb, c) => boxUnion(bb, bboxOfShape(c)), emptyBox());
1480
1647
  },
1481
- toSvg: (grp) => {
1648
+ toSvg: (grp, cutSettings) => {
1482
1649
  const xform = grp.xform ? arrayToMatrix(grp.xform) : identity();
1483
1650
  const transform = matToSvg(xform);
1484
- const children = grp.children.filter((c) => c instanceof ShapeBase).map(svgForShape);
1651
+ const children = grp.children.filter((c) => c instanceof ShapeBase).map((c) => svgForShape(c, cutSettings));
1485
1652
  return g({ transform }, children);
1486
1653
  }
1487
1654
  };
@@ -1494,10 +1661,11 @@ var pathRenderer = {
1494
1661
  const pts = p.verts.map((v) => apply(xform, { x: v.x, y: v.y }));
1495
1662
  return addPts(emptyBox(), pts);
1496
1663
  },
1497
- toSvg: (p) => {
1664
+ toSvg: (p, cutSettings) => {
1498
1665
  const xform = p.xform ? arrayToMatrix(p.xform) : identity();
1499
1666
  const transform = matToSvg(xform);
1500
1667
  const stroke = colorForCutIndex(p.cutIndex);
1668
+ const children = [];
1501
1669
  let d = "";
1502
1670
  for (let i = 0; i < p.prims.length; i++) {
1503
1671
  const prim = p.prims[i];
@@ -1519,14 +1687,46 @@ var pathRenderer = {
1519
1687
  if (d.length > 0 && p.isClosed) {
1520
1688
  d += " Z";
1521
1689
  }
1522
- return g({ transform }, [
1690
+ const cutSetting = p.cutIndex !== void 0 ? cutSettings.get(p.cutIndex) : void 0;
1691
+ const shouldShowFill = p.isClosed && cutSetting && (cutSetting.type === "Scan" || cutSetting.type === "Scan+Cut");
1692
+ if (shouldShowFill && cutSetting) {
1693
+ const bbox = addPts(
1694
+ emptyBox(),
1695
+ p.verts.map((v) => ({ x: v.x, y: v.y }))
1696
+ );
1697
+ const fillSettings = {
1698
+ interval: cutSetting.interval || 0.1,
1699
+ angle: cutSetting.angle || 0,
1700
+ crossHatch: cutSetting.crossHatch || false
1701
+ };
1702
+ const fillLines = generateScanLines(bbox, fillSettings, stroke);
1703
+ const clipId = `clip-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
1704
+ const clipPath = {
1705
+ name: "clipPath",
1706
+ type: "element",
1707
+ value: "",
1708
+ attributes: { id: clipId },
1709
+ children: [leaf("path", { d })]
1710
+ };
1711
+ const clippedGroup = {
1712
+ name: "g",
1713
+ type: "element",
1714
+ value: "",
1715
+ attributes: { "clip-path": `url(#${clipId})` },
1716
+ children: fillLines
1717
+ };
1718
+ children.push(clipPath);
1719
+ children.push(clippedGroup);
1720
+ }
1721
+ children.push(
1523
1722
  leaf("path", {
1524
1723
  d,
1525
1724
  fill: "none",
1526
1725
  stroke,
1527
1726
  "stroke-width": "0.1"
1528
1727
  })
1529
- ]);
1728
+ );
1729
+ return g({ transform }, children);
1530
1730
  }
1531
1731
  };
1532
1732
 
@@ -1548,14 +1748,32 @@ var rectRenderer = {
1548
1748
  corners.map((p) => apply(xform, p))
1549
1749
  );
1550
1750
  },
1551
- toSvg: (rect) => {
1751
+ toSvg: (rect, cutSettings) => {
1552
1752
  const xform = rect.xform ? arrayToMatrix(rect.xform) : identity();
1553
1753
  const transform = matToSvg(xform);
1554
1754
  const w = rect.w || 0;
1555
1755
  const h = rect.h || 0;
1556
1756
  const cr = rect.cr || 0;
1557
1757
  const stroke = colorForCutIndex(rect.cutIndex);
1558
- return g({ transform }, [
1758
+ const children = [];
1759
+ const cutSetting = rect.cutIndex !== void 0 ? cutSettings.get(rect.cutIndex) : void 0;
1760
+ const shouldShowFill = cutSetting && (cutSetting.type === "Scan" || cutSetting.type === "Scan+Cut");
1761
+ if (shouldShowFill && cutSetting) {
1762
+ const localBBox = {
1763
+ minX: 0,
1764
+ minY: 0,
1765
+ maxX: w,
1766
+ maxY: h
1767
+ };
1768
+ const fillSettings = {
1769
+ interval: cutSetting.interval || 0.1,
1770
+ angle: cutSetting.angle || 0,
1771
+ crossHatch: cutSetting.crossHatch || false
1772
+ };
1773
+ const fillLines = generateScanLines(localBBox, fillSettings, stroke);
1774
+ children.push(...fillLines);
1775
+ }
1776
+ children.push(
1559
1777
  leaf("rect", {
1560
1778
  x: "0",
1561
1779
  y: "0",
@@ -1566,7 +1784,8 @@ var rectRenderer = {
1566
1784
  fill: "none",
1567
1785
  stroke
1568
1786
  })
1569
- ]);
1787
+ );
1788
+ return g({ transform }, children);
1570
1789
  }
1571
1790
  };
1572
1791
 
@@ -1586,7 +1805,7 @@ var textRenderer = {
1586
1805
  apply(xform, { x: 100, y: 50 })
1587
1806
  ]);
1588
1807
  },
1589
- toSvg: (t) => {
1808
+ toSvg: (t, _cutSettings) => {
1590
1809
  const xform = t.xform ? arrayToMatrix(t.xform) : identity();
1591
1810
  const transform = matToSvg(xform);
1592
1811
  const stroke = colorForCutIndex(t.cutIndex);
@@ -1620,22 +1839,23 @@ function findRenderer(shape) {
1620
1839
  function bboxOfShape(shape) {
1621
1840
  return findRenderer(shape).bbox(shape);
1622
1841
  }
1623
- function svgForShape(shape) {
1624
- return findRenderer(shape).toSvg(shape);
1842
+ function svgForShape(shape, cutSettings) {
1843
+ return findRenderer(shape).toSvg(shape, cutSettings);
1625
1844
  }
1626
1845
  function measure(shapes) {
1627
1846
  return shapes.reduce((acc, s) => boxUnion(acc, bboxOfShape(s)), emptyBox());
1628
1847
  }
1629
- function renderAll(shapes) {
1630
- return shapes.map(svgForShape);
1848
+ function renderAll(shapes, cutSettings) {
1849
+ return shapes.map((s) => svgForShape(s, cutSettings));
1631
1850
  }
1632
1851
 
1633
1852
  // lib/svg-gen/index.ts
1634
1853
  function generateLightBurnSvg(root, options) {
1635
1854
  const shapes = collectShapes(root);
1855
+ const cutSettings = collectCutSettings(root);
1636
1856
  const bbox = measure(shapes);
1637
1857
  const layout = computeLayout(bbox, options);
1638
- const nodes = renderAll(shapes);
1858
+ const nodes = renderAll(shapes, cutSettings);
1639
1859
  const svgTree = assembleSvg(nodes, layout);
1640
1860
  return stringify(svgTree);
1641
1861
  }
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.8",
6
6
  "files": [
7
7
  "dist"
8
8
  ],