lbrnts 0.0.19 → 0.0.22

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/README.md CHANGED
@@ -20,6 +20,32 @@ const project = LightBurnProject.parse(fs.readFileSync("project.lbrn2", "utf8"))
20
20
  console.log(project.children)
21
21
  ```
22
22
 
23
+ ### Offsetting Project Content
24
+
25
+ ```tsx
26
+ import {
27
+ applyOffsetToLbrn,
28
+ LightBurnBaseElement,
29
+ LightBurnProject,
30
+ } from "lbrnts"
31
+
32
+ const source = fs.readFileSync("project.lbrn2", "utf8")
33
+ const project = LightBurnBaseElement.parse(source)
34
+
35
+ if (project instanceof LightBurnProject) {
36
+ applyOffsetToLbrn({
37
+ lbrnProject: project,
38
+ xOffset: 10,
39
+ yOffset: -5,
40
+ })
41
+
42
+ const shifted = project.getString()
43
+ }
44
+ ```
45
+
46
+ This mutates the parsed project by offsetting top-level shape transforms. Call
47
+ `getString()` when you need LightBurn XML output.
48
+
23
49
  ### Creating Projects from Scratch
24
50
 
25
51
  You can programmatically create LightBurn projects by constructing the project and its elements. Below is a table of all constructible classes with links to their documentation:
@@ -132,6 +158,8 @@ new CutSetting(init?: {
132
158
  angle?: number // Scan angle
133
159
  overScanning?: number // Over-scanning distance
134
160
  lineAngle?: number // Line angle for fill
161
+ wobbleEnable?: boolean // Enable wobble for scan fills
162
+ anglePerPass?: number // Hatch angle increment per pass
135
163
  })
136
164
  ```
137
165
 
package/dist/index.d.ts CHANGED
@@ -92,6 +92,8 @@ interface CutSettingInit {
92
92
  overScanning?: number;
93
93
  lineAngle?: number;
94
94
  crossHatch?: boolean;
95
+ wobbleEnable?: boolean;
96
+ anglePerPass?: number;
95
97
  frequency?: number;
96
98
  qPulseWidth?: number;
97
99
  }
@@ -119,6 +121,8 @@ declare class CutSetting extends LightBurnBaseElement {
119
121
  private _overScanning?;
120
122
  private _lineAngle?;
121
123
  private _crossHatch?;
124
+ private _wobbleEnable?;
125
+ private _anglePerPass?;
122
126
  private _frequency?;
123
127
  private _qPulseWidth?;
124
128
  constructor(init?: CutSettingInit);
@@ -168,6 +172,10 @@ declare class CutSetting extends LightBurnBaseElement {
168
172
  set lineAngle(value: number | undefined);
169
173
  get crossHatch(): boolean | undefined;
170
174
  set crossHatch(value: boolean | undefined);
175
+ get wobbleEnable(): boolean | undefined;
176
+ set wobbleEnable(value: boolean | undefined);
177
+ get anglePerPass(): number | undefined;
178
+ set anglePerPass(value: number | undefined);
171
179
  get frequency(): number | undefined;
172
180
  set frequency(value: number | undefined);
173
181
  get qPulseWidth(): number | undefined;
@@ -373,6 +381,22 @@ interface GenerateSvgOptions {
373
381
 
374
382
  declare function generateLightBurnSvg(root: LightBurnBaseElement | LightBurnBaseElement[], options?: GenerateSvgOptions): string;
375
383
 
384
+ interface SplitLightBurnProjectFile {
385
+ fileName: string;
386
+ content: string;
387
+ cutIndex: number;
388
+ cutSettingName: string;
389
+ shapeCount: number;
390
+ }
391
+ declare function splitLightBurnProjectByCutSetting(input: string | LightBurnProject, originalFileName?: string): SplitLightBurnProjectFile[];
392
+
393
+ interface ApplyOffsetToLbrnParams {
394
+ lbrnProject: LightBurnProject;
395
+ xOffset: number;
396
+ yOffset: number;
397
+ }
398
+ declare function applyOffsetToLbrn(params: ApplyOffsetToLbrnParams): LightBurnProject;
399
+
376
400
  declare const parseXml: (xml: string) => XmlJson;
377
401
 
378
- export { LightBurnBaseElement as BaseLightBurnElement, CutSetting, type CutSettingInit, type GenerateSvgOptions, LightBurnBaseElement, LightBurnProject, type LightBurnProjectInit, type Mat, Notes, type Prim, ShapeBase, ShapeBitmap, ShapeEllipse, ShapeGroup, ShapePath, type ShapePathInit, ShapeRect, ShapeText, Thumbnail, UIPrefs, VariableText, type Vert, generateLightBurnSvg, parseXml };
402
+ export { type ApplyOffsetToLbrnParams, LightBurnBaseElement as BaseLightBurnElement, CutSetting, type CutSettingInit, type GenerateSvgOptions, LightBurnBaseElement, LightBurnProject, type LightBurnProjectInit, type Mat, Notes, type Prim, ShapeBase, ShapeBitmap, ShapeEllipse, ShapeGroup, ShapePath, type ShapePathInit, ShapeRect, ShapeText, type SplitLightBurnProjectFile, Thumbnail, UIPrefs, VariableText, type Vert, applyOffsetToLbrn, generateLightBurnSvg, parseXml, splitLightBurnProjectByCutSetting };
package/dist/index.js CHANGED
@@ -294,6 +294,8 @@ var CutSetting = class _CutSetting extends LightBurnBaseElement {
294
294
  _overScanning;
295
295
  _lineAngle;
296
296
  _crossHatch;
297
+ _wobbleEnable;
298
+ _anglePerPass;
297
299
  _frequency;
298
300
  _qPulseWidth;
299
301
  constructor(init) {
@@ -325,6 +327,10 @@ var CutSetting = class _CutSetting extends LightBurnBaseElement {
325
327
  this._overScanning = init.overScanning;
326
328
  if (init.lineAngle !== void 0) this._lineAngle = init.lineAngle;
327
329
  if (init.crossHatch !== void 0) this._crossHatch = init.crossHatch;
330
+ if (init.wobbleEnable !== void 0)
331
+ this._wobbleEnable = init.wobbleEnable;
332
+ if (init.anglePerPass !== void 0)
333
+ this._anglePerPass = init.anglePerPass;
328
334
  if (init.frequency !== void 0) this._frequency = init.frequency;
329
335
  if (init.qPulseWidth !== void 0) this._qPulseWidth = init.qPulseWidth;
330
336
  }
@@ -467,6 +473,18 @@ var CutSetting = class _CutSetting extends LightBurnBaseElement {
467
473
  set crossHatch(value) {
468
474
  this._crossHatch = value;
469
475
  }
476
+ get wobbleEnable() {
477
+ return this._wobbleEnable;
478
+ }
479
+ set wobbleEnable(value) {
480
+ this._wobbleEnable = value;
481
+ }
482
+ get anglePerPass() {
483
+ return this._anglePerPass;
484
+ }
485
+ set anglePerPass(value) {
486
+ this._anglePerPass = value;
487
+ }
470
488
  get frequency() {
471
489
  return this._frequency;
472
490
  }
@@ -509,6 +527,8 @@ var CutSetting = class _CutSetting extends LightBurnBaseElement {
509
527
  "overScanning",
510
528
  "lineAngle",
511
529
  "crossHatch",
530
+ "wobbleEnable",
531
+ "anglePerPass",
512
532
  "frequency",
513
533
  "qPulseWidth"
514
534
  ];
@@ -551,6 +571,8 @@ var CutSetting = class _CutSetting extends LightBurnBaseElement {
551
571
  cs.overScanning = num(getChildValue("overScanning"), void 0);
552
572
  cs.lineAngle = num(getChildValue("lineAngle"), void 0);
553
573
  cs.crossHatch = boolish(getChildValue("crossHatch"), void 0);
574
+ cs.wobbleEnable = boolish(getChildValue("wobbleEnable"), void 0);
575
+ cs.anglePerPass = num(getChildValue("anglePerPass"), void 0);
554
576
  cs.frequency = num(getChildValue("frequency"), void 0);
555
577
  cs.qPulseWidth = num(getChildValue("QPulseWidth"), void 0);
556
578
  return cs;
@@ -566,7 +588,7 @@ var CutSettingPropertyElement = class extends LightBurnBaseElement {
566
588
  this.propValue = propValue;
567
589
  }
568
590
  formatValue(value) {
569
- if (this.propName === "crossHatch" && typeof value === "boolean") {
591
+ if ((this.propName === "crossHatch" || this.propName === "wobbleEnable") && typeof value === "boolean") {
570
592
  return value ? "1" : "0";
571
593
  }
572
594
  if (typeof value === "number") {
@@ -1948,6 +1970,147 @@ function generateLightBurnSvg(root, options) {
1948
1970
  const svgTree = assembleSvg(nodes, layout, []);
1949
1971
  return stringify(svgTree);
1950
1972
  }
1973
+
1974
+ // lib/split-by-cut-setting.ts
1975
+ function parseProject(input) {
1976
+ if (input instanceof LightBurnProject) return input;
1977
+ const parsed = LightBurnBaseElement.parse(input);
1978
+ if (!(parsed instanceof LightBurnProject)) {
1979
+ throw new Error("Expected a LightBurnProject XML document");
1980
+ }
1981
+ return parsed;
1982
+ }
1983
+ function fileBaseName(name, cutIndex) {
1984
+ return name.trim().toLowerCase().replace(/[<>:"/\\|?*\x00-\x1F]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || `cut-index-${cutIndex}`;
1985
+ }
1986
+ function uniqueFileName(baseName, usedFileNames) {
1987
+ let fileName = `${baseName}.lbrn2`;
1988
+ let suffix = 2;
1989
+ while (usedFileNames.has(fileName)) {
1990
+ fileName = `${baseName}-${suffix}.lbrn2`;
1991
+ suffix += 1;
1992
+ }
1993
+ usedFileNames.add(fileName);
1994
+ return fileName;
1995
+ }
1996
+ function folderNameForFile(fileName) {
1997
+ if (!fileName) return void 0;
1998
+ const baseName = fileName.split(/[\\/]/).pop() ?? fileName;
1999
+ return fileBaseName(baseName.replace(/\.lbrn2?$/i, ""), 0);
2000
+ }
2001
+ function cloneGroup(group, children) {
2002
+ const clone = Object.assign(
2003
+ Object.create(Object.getPrototypeOf(group)),
2004
+ group
2005
+ );
2006
+ clone.children = children;
2007
+ return clone;
2008
+ }
2009
+ function countShapes(element) {
2010
+ if (element instanceof ShapeGroup) {
2011
+ return element.children.reduce((sum, child) => sum + countShapes(child), 0);
2012
+ }
2013
+ return element instanceof ShapeBase ? 1 : 0;
2014
+ }
2015
+ function filterShapeForCutIndex(element, cutIndex) {
2016
+ if (element instanceof ShapeGroup) {
2017
+ if (element.cutIndex === cutIndex) {
2018
+ return {
2019
+ shape: cloneGroup(element, [...element.children]),
2020
+ count: countShapes(element)
2021
+ };
2022
+ }
2023
+ const children = [];
2024
+ let count = 0;
2025
+ for (const child of element.children) {
2026
+ const result = filterShapeForCutIndex(child, cutIndex);
2027
+ if (result.shape) children.push(result.shape);
2028
+ count += result.count;
2029
+ }
2030
+ return children.length > 0 ? { shape: cloneGroup(element, children), count } : { count };
2031
+ }
2032
+ if (element instanceof ShapeBase && element.cutIndex === cutIndex) {
2033
+ return { shape: element, count: 1 };
2034
+ }
2035
+ return { count: 0 };
2036
+ }
2037
+ function projectForCutSetting(project, cutSetting) {
2038
+ const children = [];
2039
+ const cutIndex = cutSetting.index;
2040
+ let shapeCount = 0;
2041
+ for (const child of project.children) {
2042
+ if (child instanceof CutSetting) {
2043
+ if (child === cutSetting) children.push(child);
2044
+ continue;
2045
+ }
2046
+ if (child instanceof ShapeBase) {
2047
+ const result = filterShapeForCutIndex(child, cutIndex);
2048
+ if (result.shape) children.push(result.shape);
2049
+ shapeCount += result.count;
2050
+ continue;
2051
+ }
2052
+ children.push(child);
2053
+ }
2054
+ return {
2055
+ project: new LightBurnProject({
2056
+ appVersion: project.appVersion,
2057
+ formatVersion: project.formatVersion,
2058
+ materialHeight: project.materialHeight,
2059
+ mirrorX: project.mirrorX,
2060
+ mirrorY: project.mirrorY,
2061
+ children
2062
+ }),
2063
+ shapeCount
2064
+ };
2065
+ }
2066
+ function splitLightBurnProjectByCutSetting(input, originalFileName) {
2067
+ const project = parseProject(input);
2068
+ const folderName = folderNameForFile(originalFileName);
2069
+ const usedFileNames = /* @__PURE__ */ new Set();
2070
+ const files = [];
2071
+ for (const cutSetting of project.children) {
2072
+ if (!(cutSetting instanceof CutSetting) || cutSetting.index === void 0) {
2073
+ continue;
2074
+ }
2075
+ const cutIndex = cutSetting.index;
2076
+ const cutSettingName = cutSetting.name ?? `CutIndex ${cutIndex}`;
2077
+ const split = projectForCutSetting(project, cutSetting);
2078
+ const fileName = uniqueFileName(
2079
+ fileBaseName(cutSettingName, cutIndex),
2080
+ usedFileNames
2081
+ );
2082
+ files.push({
2083
+ fileName: folderName ? `${folderName}/${fileName}` : fileName,
2084
+ content: split.project.getString(),
2085
+ cutIndex,
2086
+ cutSettingName,
2087
+ shapeCount: split.shapeCount
2088
+ });
2089
+ }
2090
+ return files;
2091
+ }
2092
+
2093
+ // lib/apply-offset.ts
2094
+ function assertFiniteOffset(name, value) {
2095
+ if (!Number.isFinite(value)) {
2096
+ throw new Error(`${name} must be a finite number`);
2097
+ }
2098
+ }
2099
+ function applyOffsetToShape(shape, xOffset, yOffset) {
2100
+ const [a, b, c, d, tx, ty] = shape.xform;
2101
+ shape.xform = [a, b, c, d, tx + xOffset, ty + yOffset];
2102
+ }
2103
+ function applyOffsetToLbrn(params) {
2104
+ const { lbrnProject, xOffset, yOffset } = params;
2105
+ assertFiniteOffset("xOffset", xOffset);
2106
+ assertFiniteOffset("yOffset", yOffset);
2107
+ for (const child of lbrnProject.children) {
2108
+ if (child instanceof ShapeBase) {
2109
+ applyOffsetToShape(child, xOffset, yOffset);
2110
+ }
2111
+ }
2112
+ return lbrnProject;
2113
+ }
1951
2114
  export {
1952
2115
  LightBurnBaseElement as BaseLightBurnElement,
1953
2116
  CutSetting,
@@ -1964,6 +2127,8 @@ export {
1964
2127
  Thumbnail,
1965
2128
  UIPrefs,
1966
2129
  VariableText,
2130
+ applyOffsetToLbrn,
1967
2131
  generateLightBurnSvg,
1968
- parseXml
2132
+ parseXml,
2133
+ splitLightBurnProjectByCutSetting
1969
2134
  };
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.19",
5
+ "version": "0.0.22",
6
6
  "files": [
7
7
  "dist"
8
8
  ],