@faicad/sheetmetal 0.16.1 → 0.18.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 (103) hide show
  1. package/dist/allowanceFns.d.ts +1 -1
  2. package/dist/allowanceFns.d.ts.map +1 -1
  3. package/dist/allowanceFns.js +1 -1
  4. package/dist/allowanceFns.js.map +1 -1
  5. package/dist/api.d.ts +21 -20
  6. package/dist/api.d.ts.map +1 -1
  7. package/dist/api.js +20 -20
  8. package/dist/api.js.map +1 -1
  9. package/dist/authorFns.d.ts +2 -2
  10. package/dist/authorFns.d.ts.map +1 -1
  11. package/dist/authorFns.js +61 -51
  12. package/dist/authorFns.js.map +1 -1
  13. package/dist/bendTableFns.d.ts +1 -1
  14. package/dist/bendTableFns.d.ts.map +1 -1
  15. package/dist/bendTableFns.js +1 -1
  16. package/dist/bendTableFns.js.map +1 -1
  17. package/dist/contourFlangeFns.d.ts +5 -4
  18. package/dist/contourFlangeFns.d.ts.map +1 -1
  19. package/dist/contourFlangeFns.js +59 -44
  20. package/dist/contourFlangeFns.js.map +1 -1
  21. package/dist/cutoutFns.d.ts +5 -5
  22. package/dist/cutoutFns.d.ts.map +1 -1
  23. package/dist/cutoutFns.js +22 -14
  24. package/dist/cutoutFns.js.map +1 -1
  25. package/dist/dxfFns.d.ts +1 -1
  26. package/dist/dxfFns.d.ts.map +1 -1
  27. package/dist/dxfFns.js +1 -1
  28. package/dist/dxfFns.js.map +1 -1
  29. package/dist/facade.d.ts +43 -42
  30. package/dist/facade.d.ts.map +1 -1
  31. package/dist/facade.js +83 -83
  32. package/dist/facade.js.map +1 -1
  33. package/dist/featureTreeFns.d.ts +1 -1
  34. package/dist/featureTreeFns.d.ts.map +1 -1
  35. package/dist/featureTreeFns.js +1 -1
  36. package/dist/featureTreeFns.js.map +1 -1
  37. package/dist/foldFns.d.ts +3 -3
  38. package/dist/foldFns.d.ts.map +1 -1
  39. package/dist/foldFns.js +12 -12
  40. package/dist/foldFns.js.map +1 -1
  41. package/dist/foreignUnfoldFns.d.ts +2 -1
  42. package/dist/foreignUnfoldFns.d.ts.map +1 -1
  43. package/dist/foreignUnfoldFns.js +21 -10
  44. package/dist/foreignUnfoldFns.js.map +1 -1
  45. package/dist/formFns.d.ts +4 -4
  46. package/dist/formFns.d.ts.map +1 -1
  47. package/dist/formFns.js +91 -76
  48. package/dist/formFns.js.map +1 -1
  49. package/dist/geometryOps.d.ts +53 -0
  50. package/dist/geometryOps.d.ts.map +1 -0
  51. package/dist/geometryOps.js +104 -0
  52. package/dist/geometryOps.js.map +1 -0
  53. package/dist/hemFns.d.ts +2 -2
  54. package/dist/hemFns.d.ts.map +1 -1
  55. package/dist/hemFns.js +4 -4
  56. package/dist/hemFns.js.map +1 -1
  57. package/dist/internal.d.ts +2 -1
  58. package/dist/internal.d.ts.map +1 -1
  59. package/dist/internal.js +1 -1
  60. package/dist/internal.js.map +1 -1
  61. package/dist/jogFns.d.ts +2 -2
  62. package/dist/jogFns.d.ts.map +1 -1
  63. package/dist/jogFns.js +6 -6
  64. package/dist/jogFns.js.map +1 -1
  65. package/dist/loftedFlangeFns.d.ts +2 -2
  66. package/dist/loftedFlangeFns.d.ts.map +1 -1
  67. package/dist/loftedFlangeFns.js +35 -25
  68. package/dist/loftedFlangeFns.js.map +1 -1
  69. package/dist/miterFns.d.ts +3 -3
  70. package/dist/miterFns.d.ts.map +1 -1
  71. package/dist/miterFns.js +30 -19
  72. package/dist/miterFns.js.map +1 -1
  73. package/dist/nestFns.d.ts +1 -1
  74. package/dist/nestFns.d.ts.map +1 -1
  75. package/dist/nestFns.js +1 -1
  76. package/dist/nestFns.js.map +1 -1
  77. package/dist/polygonFns.d.ts +2 -2
  78. package/dist/polygonFns.d.ts.map +1 -1
  79. package/dist/polygonFns.js +1 -1
  80. package/dist/polygonFns.js.map +1 -1
  81. package/dist/reliefFns.d.ts +4 -4
  82. package/dist/reliefFns.d.ts.map +1 -1
  83. package/dist/reliefFns.js +39 -31
  84. package/dist/reliefFns.js.map +1 -1
  85. package/dist/reportFns.d.ts +1 -1
  86. package/dist/reportFns.d.ts.map +1 -1
  87. package/dist/reportFns.js +1 -1
  88. package/dist/reportFns.js.map +1 -1
  89. package/dist/tabFns.d.ts +3 -3
  90. package/dist/tabFns.d.ts.map +1 -1
  91. package/dist/tabFns.js +21 -13
  92. package/dist/tabFns.js.map +1 -1
  93. package/dist/types.d.ts +21 -1
  94. package/dist/types.d.ts.map +1 -1
  95. package/dist/types.js.map +1 -1
  96. package/dist/unfoldFns.d.ts +1 -1
  97. package/dist/unfoldFns.d.ts.map +1 -1
  98. package/dist/unfoldFns.js +2 -2
  99. package/dist/unfoldFns.js.map +1 -1
  100. package/dist/validateFns.d.ts.map +1 -1
  101. package/dist/validateFns.js +4 -6
  102. package/dist/validateFns.js.map +1 -1
  103. package/package.json +2 -2
@@ -1,4 +1,4 @@
1
- import { type Result, type Vec3 } from '@faicad/faijs/brepjs-compat';
1
+ import { type Result, type Vec3 } from '@faicad/faijs/api';
2
2
  import type { SheetMetalPart } from './types.js';
3
3
  /** An oriented cutting plane: material on the `+normal` side is removed. */
4
4
  export interface MiterPlane {
@@ -14,7 +14,7 @@ export interface MiterPlane {
14
14
  * @param plane - the miter plane definition.
15
15
  * @returns the updated part with the miter cut applied, or an error.
16
16
  */
17
- export declare function miterCut(part: SheetMetalPart, plane: MiterPlane): Result<SheetMetalPart>;
17
+ export declare function miterCut(part: SheetMetalPart, plane: MiterPlane): Promise<Result<SheetMetalPart>>;
18
18
  /**
19
19
  * Auto corner-miter between two adjacent flanges. The miter plane bisects the two
20
20
  * flanges' fold-up directions at their shared corner and is offset by half the gap,
@@ -27,5 +27,5 @@ export declare function miterCut(part: SheetMetalPart, plane: MiterPlane): Resul
27
27
  * @param gap - the gap between flanges (default 0).
28
28
  * @returns the updated part with the miter corner applied, or an error.
29
29
  */
30
- export declare function autoMiterCorner(part: SheetMetalPart, flangeIdA: string, flangeIdB: string, gap?: number): Result<SheetMetalPart>;
30
+ export declare function autoMiterCorner(part: SheetMetalPart, flangeIdA: string, flangeIdB: string, gap?: number): Promise<Result<SheetMetalPart>>;
31
31
  //# sourceMappingURL=miterFns.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"miterFns.d.ts","sourceRoot":"","sources":["../src/miterFns.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,MAAM,EACX,KAAK,IAAI,EAgBV,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjD,4EAA4E;AAC5E,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,IAAI,CAAC;IACb,MAAM,EAAE,IAAI,CAAC;CACd;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,UAAU,GAAG,MAAM,CAAC,cAAc,CAAC,CAYxF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,cAAc,EACpB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,GAAG,SAAI,GACN,MAAM,CAAC,cAAc,CAAC,CAqCxB"}
1
+ {"version":3,"file":"miterFns.d.ts","sourceRoot":"","sources":["../src/miterFns.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,KAAK,MAAM,EAAE,KAAK,IAAI,EAAoH,MAAM,mBAAmB,CAAC;AAC5K,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjD,4EAA4E;AAC5E,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,IAAI,CAAC;IACb,MAAM,EAAE,IAAI,CAAC;CACd;AAED;;;;;;;;GAQG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAevG;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,cAAc,EACpB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,GAAG,SAAI,GACN,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAqCjC"}
package/dist/miterFns.js CHANGED
@@ -1,4 +1,6 @@
1
- import { ok, err, validationError, box, cut, rotate, translate, getBounds, vecSub, vecAdd, vecScale, vecCross, vecDot, vecNormalize, } from '@faicad/faijs/brepjs-compat';
1
+ import { getBounds } from './geometryOps.js';
2
+ import { translate } from './geometryOps.js';
3
+ import { ok, err, kernelError, validationError, box, cut, rotate, vecSub, vecAdd, vecScale, vecCross, vecDot, vecNormalize } from '@faicad/faijs/api';
2
4
  import { normalizeSolid } from './internal.js';
3
5
  /**
4
6
  * General miter primitive: cut a sheet-metal part by a plane, removing the
@@ -9,18 +11,22 @@ import { normalizeSolid } from './internal.js';
9
11
  * @param plane - the miter plane definition.
10
12
  * @returns the updated part with the miter cut applied, or an error.
11
13
  */
12
- export function miterCut(part, plane) {
14
+ export async function miterCut(part, plane) {
13
15
  const solid = part.solid;
14
16
  if (solid === undefined) {
15
17
  return err(validationError('NO_SOLID', 'miterCut: part has no folded solid to cut'));
16
18
  }
17
- const tool = halfSpaceTool(solid, plane);
18
- if (!tool.ok)
19
- return tool;
20
- const result = cut(solid, tool.value);
21
- if (!result.ok)
22
- return result;
23
- return ok({ ...part, solid: normalizeSolid(result.value) });
19
+ let solidOut;
20
+ try {
21
+ const tool = await halfSpaceTool(solid, plane);
22
+ if (!tool.ok)
23
+ return tool;
24
+ solidOut = normalizeSolid((await cut(solid, tool.value)));
25
+ }
26
+ catch (e) {
27
+ return err(kernelError('CUT_FAILED', `miter cut: ${e instanceof Error ? e.message : String(e)}`));
28
+ }
29
+ return ok({ ...part, solid: solidOut });
24
30
  }
25
31
  /**
26
32
  * Auto corner-miter between two adjacent flanges. The miter plane bisects the two
@@ -34,7 +40,7 @@ export function miterCut(part, plane) {
34
40
  * @param gap - the gap between flanges (default 0).
35
41
  * @returns the updated part with the miter corner applied, or an error.
36
42
  */
37
- export function autoMiterCorner(part, flangeIdA, flangeIdB, gap = 0) {
43
+ export async function autoMiterCorner(part, flangeIdA, flangeIdB, gap = 0) {
38
44
  if (part.solid === undefined) {
39
45
  return err(validationError('NO_SOLID', 'autoMiterCorner: part has no folded solid'));
40
46
  }
@@ -57,7 +63,7 @@ export function autoMiterCorner(part, flangeIdA, flangeIdB, gap = 0) {
57
63
  return err(validationError('PARALLEL_FLANGES', 'autoMiterCorner: flange bend axes are parallel; no corner to miter'));
58
64
  }
59
65
  const origin = vecAdd(corner, vecScale(planeNormal, gap * 0.5));
60
- const cutResult = miterCut(part, { origin, normal: planeNormal });
66
+ const cutResult = await miterCut(part, { origin, normal: planeNormal });
61
67
  if (!cutResult.ok)
62
68
  return cutResult;
63
69
  return ok({
@@ -96,7 +102,7 @@ function miterNormal(dirA, dirB) {
96
102
  return vecNormalize(sum);
97
103
  }
98
104
  const Z_AXIS = [0, 0, 1];
99
- function halfSpaceTool(solid, plane) {
105
+ async function halfSpaceTool(solid, plane) {
100
106
  const normal = vecNormalize(plane.normal);
101
107
  if (Math.hypot(normal[0], normal[1], normal[2]) < 1e-9) {
102
108
  return err(validationError('INVALID_PLANE', 'miter plane normal must be non-zero'));
@@ -108,21 +114,26 @@ function halfSpaceTool(solid, plane) {
108
114
  // up in +Z, then rotated so +Z maps onto the plane normal and translated onto the
109
115
  // plane origin. After alignment its open boundary is the miter plane and its body
110
116
  // fills the +normal half-space.
111
- let tool = box(size, size, size);
112
- tool = translate(tool, [-size / 2, -size / 2, 0]);
113
- const aligned = alignZTo(tool, normal);
114
- return ok(translate(aligned, plane.origin));
117
+ try {
118
+ let tool = (await box({ width: size, depth: size, height: size }));
119
+ tool = (await translate(tool, [-size / 2, -size / 2, 0]));
120
+ const aligned = await alignZTo(tool, normal);
121
+ return ok((await translate(aligned, plane.origin)));
122
+ }
123
+ catch (e) {
124
+ return err(kernelError('MITER_TOOL_FAILED', `half-space tool: ${e instanceof Error ? e.message : String(e)}`));
125
+ }
115
126
  }
116
127
  /** Rotate a shape so its local +Z axis points along `target` (about the origin). */
117
- function alignZTo(shape, target) {
128
+ async function alignZTo(shape, target) {
118
129
  const dot = vecDot(Z_AXIS, target);
119
130
  if (dot > 1 - 1e-9)
120
131
  return shape;
121
132
  if (dot < -1 + 1e-9) {
122
- return rotate(shape, 180, { at: [0, 0, 0], axis: [1, 0, 0] });
133
+ return (await rotate(shape, 180, { at: [0, 0, 0], axis: [1, 0, 0] }));
123
134
  }
124
135
  const axis = vecNormalize(vecCross(Z_AXIS, target));
125
136
  const angleDeg = (Math.acos(Math.max(-1, Math.min(1, dot))) * 180) / Math.PI;
126
- return rotate(shape, angleDeg, { at: [0, 0, 0], axis });
137
+ return (await rotate(shape, angleDeg, { at: [0, 0, 0], axis }));
127
138
  }
128
139
  //# sourceMappingURL=miterFns.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"miterFns.js","sourceRoot":"","sources":["../src/miterFns.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type Result,\n type Vec3,\n type Solid,\n ok,\n err,\n validationError,\n box,\n cut,\n rotate,\n translate,\n getBounds,\n vecSub,\n vecAdd,\n vecScale,\n vecCross,\n vecDot,\n vecNormalize,\n} from '@faicad/faijs/brepjs-compat';\nimport type { SheetMetalPart } from './types.js';\nimport { normalizeSolid } from './internal.js';\n\n/** An oriented cutting plane: material on the `+normal` side is removed. */\nexport interface MiterPlane {\n origin: Vec3;\n normal: Vec3;\n}\n\n/**\n * General miter primitive: cut a sheet-metal part by a plane, removing the\n * material on the `+normal` side. The tool is a half-space block sized to the\n * part's bounding box, so the cut is exact regardless of part extent.\n *\n * @param part - the sheet metal part to cut.\n * @param plane - the miter plane definition.\n * @returns the updated part with the miter cut applied, or an error.\n */\nexport function miterCut(part: SheetMetalPart, plane: MiterPlane): Result<SheetMetalPart> {\n const solid = part.solid;\n if (solid === undefined) {\n return err(validationError('NO_SOLID', 'miterCut: part has no folded solid to cut'));\n }\n const tool = halfSpaceTool(solid, plane);\n if (!tool.ok) return tool;\n\n const result = cut(solid, tool.value);\n if (!result.ok) return result;\n\n return ok({ ...part, solid: normalizeSolid(result.value) });\n}\n\n/**\n * Auto corner-miter between two adjacent flanges. The miter plane bisects the two\n * flanges' fold-up directions at their shared corner and is offset by half the gap,\n * so the cut falls on the flat/flange regions and never crosses a bend patch. The\n * single bisector cut trims both flanges to a clean mitered corner.\n *\n * @param part - the sheet metal part to miter.\n * @param flangeIdA - the first flange ID.\n * @param flangeIdB - the second flange ID.\n * @param gap - the gap between flanges (default 0).\n * @returns the updated part with the miter corner applied, or an error.\n */\nexport function autoMiterCorner(\n part: SheetMetalPart,\n flangeIdA: string,\n flangeIdB: string,\n gap = 0\n): Result<SheetMetalPart> {\n if (part.solid === undefined) {\n return err(validationError('NO_SOLID', 'autoMiterCorner: part has no folded solid'));\n }\n if (!Number.isFinite(gap) || gap < 0) {\n return err(validationError('INVALID_GAP', `miter gap must be a finite, non-negative number, got ${gap}`));\n }\n const bendA = part.bends.find((b) => b.id === flangeIdA);\n const bendB = part.bends.find((b) => b.id === flangeIdB);\n if (bendA === undefined || bendB === undefined) {\n return err(\n validationError('UNKNOWN_FLANGE', `autoMiterCorner: flange '${flangeIdA}' or '${flangeIdB}' not found`)\n );\n }\n\n const axisA = vecNormalize(bendA.axisDir);\n const axisB = vecNormalize(bendB.axisDir);\n const corner = cornerPoint(bendA.axisOrigin, axisA, bendB.axisOrigin, axisB);\n\n // Each flange runs perpendicular to its own bend axis. The miter plane contains\n // the corner and is spanned by the two bend axes; its normal is their bisector in\n // the run plane, which is exactly the bisector of the two flange run directions.\n const planeNormal = miterNormal(axisA, axisB);\n if (planeNormal === undefined) {\n return err(\n validationError('PARALLEL_FLANGES', 'autoMiterCorner: flange bend axes are parallel; no corner to miter')\n );\n }\n const origin = vecAdd(corner, vecScale(planeNormal, gap * 0.5));\n\n const cutResult = miterCut(part, { origin, normal: planeNormal });\n if (!cutResult.ok) return cutResult;\n\n return ok({\n ...cutResult.value,\n miters: [...(part.miters ?? []), { flangeA: flangeIdA, flangeB: flangeIdB, gap }],\n });\n}\n\n/** Point on the line of bend A closest to the line of bend B (their shared corner). */\nfunction cornerPoint(originA: Vec3, dirA: Vec3, originB: Vec3, dirB: Vec3): Vec3 {\n const w0 = vecSub(originA, originB);\n const a = vecDot(dirA, dirA);\n const b = vecDot(dirA, dirB);\n const c = vecDot(dirB, dirB);\n const d = vecDot(dirA, w0);\n const e = vecDot(dirB, w0);\n const denom = a * c - b * b;\n if (Math.abs(denom) < 1e-9) return originA;\n const sc = (b * e - c * d) / denom;\n return vecAdd(originA, vecScale(dirA, sc));\n}\n\n/**\n * Normal of the miter plane between two flanges whose bend axes are `dirA`/`dirB`.\n * The plane is spanned by the two axes (it contains the corner edge of each\n * flange); its normal is `dirA × dirB` rotated to bisect — equivalently the\n * normalized sum of the two run directions, which both lie perpendicular to the\n * respective axis in the part's run plane.\n */\nfunction miterNormal(dirA: Vec3, dirB: Vec3): Vec3 | undefined {\n const cross = vecCross(dirA, dirB);\n if (Math.hypot(cross[0], cross[1], cross[2]) < 1e-9) return undefined;\n const sum = vecAdd(dirA, dirB);\n if (Math.hypot(sum[0], sum[1], sum[2]) < 1e-9) return vecNormalize(cross);\n return vecNormalize(sum);\n}\n\nconst Z_AXIS: Vec3 = [0, 0, 1];\n\nfunction halfSpaceTool(solid: Solid, plane: MiterPlane): Result<Solid> {\n const normal = vecNormalize(plane.normal);\n if (Math.hypot(normal[0], normal[1], normal[2]) < 1e-9) {\n return err(validationError('INVALID_PLANE', 'miter plane normal must be non-zero'));\n }\n\n const b = getBounds(solid);\n const diag = Math.hypot(b.xMax - b.xMin, b.yMax - b.yMin, b.zMax - b.zMin);\n const size = diag * 2 + 2;\n\n // A cube whose bottom (−Z) face lies on the XY plane through the origin, extending\n // up in +Z, then rotated so +Z maps onto the plane normal and translated onto the\n // plane origin. After alignment its open boundary is the miter plane and its body\n // fills the +normal half-space.\n let tool: Solid = box(size, size, size);\n tool = translate(tool, [-size / 2, -size / 2, 0]);\n\n const aligned = alignZTo(tool, normal);\n return ok(translate(aligned, plane.origin));\n}\n\n/** Rotate a shape so its local +Z axis points along `target` (about the origin). */\nfunction alignZTo(shape: Solid, target: Vec3): Solid {\n const dot = vecDot(Z_AXIS, target);\n if (dot > 1 - 1e-9) return shape;\n if (dot < -1 + 1e-9) {\n return rotate(shape, 180, { at: [0, 0, 0], axis: [1, 0, 0] });\n }\n const axis = vecNormalize(vecCross(Z_AXIS, target));\n const angleDeg = (Math.acos(Math.max(-1, Math.min(1, dot))) * 180) / Math.PI;\n return rotate(shape, angleDeg, { at: [0, 0, 0], axis });\n}\n"]}
1
+ {"version":3,"file":"miterFns.js","sourceRoot":"","sources":["../src/miterFns.ts"],"names":[],"mappings":"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","sourcesContent":["import { getBounds } from './geometryOps.js';\nimport type { Solid } from './types.js';\nimport { translate } from './geometryOps.js';\nimport {type Result, type Vec3, ok, err, kernelError, validationError, box, cut, rotate, vecSub, vecAdd, vecScale, vecCross, vecDot, vecNormalize} from '@faicad/faijs/api';\nimport type { SheetMetalPart } from './types.js';\nimport { normalizeSolid } from './internal.js';\n\n/** An oriented cutting plane: material on the `+normal` side is removed. */\nexport interface MiterPlane {\n origin: Vec3;\n normal: Vec3;\n}\n\n/**\n * General miter primitive: cut a sheet-metal part by a plane, removing the\n * material on the `+normal` side. The tool is a half-space block sized to the\n * part's bounding box, so the cut is exact regardless of part extent.\n *\n * @param part - the sheet metal part to cut.\n * @param plane - the miter plane definition.\n * @returns the updated part with the miter cut applied, or an error.\n */\nexport async function miterCut(part: SheetMetalPart, plane: MiterPlane): Promise<Result<SheetMetalPart>> {\n const solid = part.solid;\n if (solid === undefined) {\n return err(validationError('NO_SOLID', 'miterCut: part has no folded solid to cut'));\n }\n let solidOut: Solid;\n try {\n const tool = await halfSpaceTool(solid, plane);\n if (!tool.ok) return tool;\n solidOut = normalizeSolid((await cut(solid, tool.value)) as Solid);\n } catch (e) {\n return err(kernelError('CUT_FAILED', `miter cut: ${e instanceof Error ? e.message : String(e)}`));\n }\n\n return ok({ ...part, solid: solidOut });\n}\n\n/**\n * Auto corner-miter between two adjacent flanges. The miter plane bisects the two\n * flanges' fold-up directions at their shared corner and is offset by half the gap,\n * so the cut falls on the flat/flange regions and never crosses a bend patch. The\n * single bisector cut trims both flanges to a clean mitered corner.\n *\n * @param part - the sheet metal part to miter.\n * @param flangeIdA - the first flange ID.\n * @param flangeIdB - the second flange ID.\n * @param gap - the gap between flanges (default 0).\n * @returns the updated part with the miter corner applied, or an error.\n */\nexport async function autoMiterCorner(\n part: SheetMetalPart,\n flangeIdA: string,\n flangeIdB: string,\n gap = 0\n): Promise<Result<SheetMetalPart>> {\n if (part.solid === undefined) {\n return err(validationError('NO_SOLID', 'autoMiterCorner: part has no folded solid'));\n }\n if (!Number.isFinite(gap) || gap < 0) {\n return err(validationError('INVALID_GAP', `miter gap must be a finite, non-negative number, got ${gap}`));\n }\n const bendA = part.bends.find((b) => b.id === flangeIdA);\n const bendB = part.bends.find((b) => b.id === flangeIdB);\n if (bendA === undefined || bendB === undefined) {\n return err(\n validationError('UNKNOWN_FLANGE', `autoMiterCorner: flange '${flangeIdA}' or '${flangeIdB}' not found`)\n );\n }\n\n const axisA = vecNormalize(bendA.axisDir);\n const axisB = vecNormalize(bendB.axisDir);\n const corner = cornerPoint(bendA.axisOrigin, axisA, bendB.axisOrigin, axisB);\n\n // Each flange runs perpendicular to its own bend axis. The miter plane contains\n // the corner and is spanned by the two bend axes; its normal is their bisector in\n // the run plane, which is exactly the bisector of the two flange run directions.\n const planeNormal = miterNormal(axisA, axisB);\n if (planeNormal === undefined) {\n return err(\n validationError('PARALLEL_FLANGES', 'autoMiterCorner: flange bend axes are parallel; no corner to miter')\n );\n }\n const origin = vecAdd(corner, vecScale(planeNormal, gap * 0.5));\n\n const cutResult = await miterCut(part, { origin, normal: planeNormal });\n if (!cutResult.ok) return cutResult;\n\n return ok({\n ...cutResult.value,\n miters: [...(part.miters ?? []), { flangeA: flangeIdA, flangeB: flangeIdB, gap }],\n });\n}\n\n/** Point on the line of bend A closest to the line of bend B (their shared corner). */\nfunction cornerPoint(originA: Vec3, dirA: Vec3, originB: Vec3, dirB: Vec3): Vec3 {\n const w0 = vecSub(originA, originB);\n const a = vecDot(dirA, dirA);\n const b = vecDot(dirA, dirB);\n const c = vecDot(dirB, dirB);\n const d = vecDot(dirA, w0);\n const e = vecDot(dirB, w0);\n const denom = a * c - b * b;\n if (Math.abs(denom) < 1e-9) return originA;\n const sc = (b * e - c * d) / denom;\n return vecAdd(originA, vecScale(dirA, sc));\n}\n\n/**\n * Normal of the miter plane between two flanges whose bend axes are `dirA`/`dirB`.\n * The plane is spanned by the two axes (it contains the corner edge of each\n * flange); its normal is `dirA × dirB` rotated to bisect — equivalently the\n * normalized sum of the two run directions, which both lie perpendicular to the\n * respective axis in the part's run plane.\n */\nfunction miterNormal(dirA: Vec3, dirB: Vec3): Vec3 | undefined {\n const cross = vecCross(dirA, dirB);\n if (Math.hypot(cross[0], cross[1], cross[2]) < 1e-9) return undefined;\n const sum = vecAdd(dirA, dirB);\n if (Math.hypot(sum[0], sum[1], sum[2]) < 1e-9) return vecNormalize(cross);\n return vecNormalize(sum);\n}\n\nconst Z_AXIS: Vec3 = [0, 0, 1];\n\nasync function halfSpaceTool(solid: Solid, plane: MiterPlane): Promise<Result<Solid>> {\n const normal = vecNormalize(plane.normal);\n if (Math.hypot(normal[0], normal[1], normal[2]) < 1e-9) {\n return err(validationError('INVALID_PLANE', 'miter plane normal must be non-zero'));\n }\n\n const b = getBounds(solid);\n const diag = Math.hypot(b.xMax - b.xMin, b.yMax - b.yMin, b.zMax - b.zMin);\n const size = diag * 2 + 2;\n\n // A cube whose bottom (−Z) face lies on the XY plane through the origin, extending\n // up in +Z, then rotated so +Z maps onto the plane normal and translated onto the\n // plane origin. After alignment its open boundary is the miter plane and its body\n // fills the +normal half-space.\n try {\n let tool: Solid = (await box({ width: size, depth: size, height: size })) as Solid;\n tool = (await translate(tool, [-size / 2, -size / 2, 0])) as Solid;\n\n const aligned = await alignZTo(tool, normal);\n return ok((await translate(aligned, plane.origin)) as Solid);\n } catch (e) {\n return err(kernelError('MITER_TOOL_FAILED', `half-space tool: ${e instanceof Error ? e.message : String(e)}`));\n }\n}\n\n/** Rotate a shape so its local +Z axis points along `target` (about the origin). */\nasync function alignZTo(shape: Solid, target: Vec3): Promise<Solid> {\n const dot = vecDot(Z_AXIS, target);\n if (dot > 1 - 1e-9) return shape;\n if (dot < -1 + 1e-9) {\n return (await rotate(shape, 180, { at: [0, 0, 0], axis: [1, 0, 0] })) as Solid;\n }\n const axis = vecNormalize(vecCross(Z_AXIS, target));\n const angleDeg = (Math.acos(Math.max(-1, Math.min(1, dot))) * 180) / Math.PI;\n return (await rotate(shape, angleDeg, { at: [0, 0, 0], axis })) as Solid;\n}\n"]}
package/dist/nestFns.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type Result } from '@faicad/faijs/brepjs-compat';
1
+ import { type Result } from '@faicad/faijs/api';
2
2
  import type { FlatPattern, SheetMetalWarning } from './types.js';
3
3
  import { type DxfOptions } from './dxfFns.js';
4
4
  /** Stock sheet the parts are packed onto (the gross blank, before margin). */
@@ -1 +1 @@
1
- {"version":3,"file":"nestFns.d.ts","sourceRoot":"","sources":["../src/nestFns.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAuD,MAAM,6BAA6B,CAAC;AAC/G,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAqB,KAAK,UAAU,EAAuC,MAAM,aAAa,CAAC;AAUtG,8EAA8E;AAC9E,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,6FAA6F;AAC7F,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,SAAS,CAAC;IACjB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B;iDAC6C;IAC7C,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,oFAAoF;IACpF,aAAa,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACpC;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;CACvC;AAED;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,8DAA8D;IAC9D,YAAY,EAAE,MAAM,CAAC;IACrB,+EAA+E;IAC/E,CAAC,EAAE,MAAM,CAAC;IACV,+EAA+E;IAC/E,CAAC,EAAE,MAAM,CAAC;IACV,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,kGAAkG;AAClG,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB;;;;;;;OAOG;IACH,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,uFAAuF;AACvF,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,uEAAuE;IACvE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B;AAED,mFAAmF;AACnF,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAID;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,EAAE,OAAO,EAAE,WAAW,GAAG,MAAM,CAAC,UAAU,CAAC,CA4EtF;AAsYD;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,CAsB9D;AAoDD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CACvB,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,WAAW,EAAE,EACvB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,UAAU,GACnB,MAAM,CAAC,MAAM,CAAC,CAgBhB"}
1
+ {"version":3,"file":"nestFns.d.ts","sourceRoot":"","sources":["../src/nestFns.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,MAAM,EAAsD,MAAM,mBAAmB,CAAC;AACnG,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAqB,KAAK,UAAU,EAAuC,MAAM,aAAa,CAAC;AAUtG,8EAA8E;AAC9E,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,6FAA6F;AAC7F,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,SAAS,CAAC;IACjB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B;iDAC6C;IAC7C,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,oFAAoF;IACpF,aAAa,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACpC;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;CACvC;AAED;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,8DAA8D;IAC9D,YAAY,EAAE,MAAM,CAAC;IACrB,+EAA+E;IAC/E,CAAC,EAAE,MAAM,CAAC;IACV,+EAA+E;IAC/E,CAAC,EAAE,MAAM,CAAC;IACV,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,kGAAkG;AAClG,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB;;;;;;;OAOG;IACH,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,uFAAuF;AACvF,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,uEAAuE;IACvE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B;AAED,mFAAmF;AACnF,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAID;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,EAAE,OAAO,EAAE,WAAW,GAAG,MAAM,CAAC,UAAU,CAAC,CA4EtF;AAsYD;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,CAsB9D;AAoDD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CACvB,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,WAAW,EAAE,EACvB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,UAAU,GACnB,MAAM,CAAC,MAAM,CAAC,CAgBhB"}
package/dist/nestFns.js CHANGED
@@ -1,4 +1,4 @@
1
- import { ok, err, validationError, getEdges, curveStartPoint } from '@faicad/faijs/brepjs-compat';
1
+ import { ok, err, validationError, getEdges, curveStartPoint } from '@faicad/faijs/api';
2
2
  import { multiPatternToDXF } from './dxfFns.js';
3
3
  import { wireToPolygon, transformPolygon, polygonBounds, polygonsOverlapWithClearance, signedArea, } from './polygonFns.js';
4
4
  const EPS = 1e-9;
@@ -1 +1 @@
1
- {"version":3,"file":"nestFns.js","sourceRoot":"","sources":["../src/nestFns.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Result, ok, err, validationError, getEdges, curveStartPoint } from '@faicad/faijs/brepjs-compat';\nimport type { FlatPattern, SheetMetalWarning } from './types.js';\nimport { multiPatternToDXF, type DxfOptions, type Transform2, type PlacedPattern } from './dxfFns.js';\nimport {\n wireToPolygon,\n transformPolygon,\n polygonBounds,\n polygonsOverlapWithClearance,\n signedArea,\n type Polygon,\n} from './polygonFns.js';\n\n/** Stock sheet the parts are packed onto (the gross blank, before margin). */\nexport interface SheetSpec {\n width: number;\n height: number;\n}\n\n/** Nesting options: the stock sheet plus optional margin, spacing, rotation and strategy. */\nexport interface NestOptions {\n sheet: SheetSpec;\n /** Clear border kept empty around the sheet edge (per side). Default `0`. */\n margin?: number | undefined;\n /** Minimum clear gap between any two placed parts (and the bbox padding used\n * for the non-overlap test). Default `0`. */\n spacing?: number | undefined;\n /** Try a 90° rotation per part and keep whichever orientation fits/packs better. */\n allowRotation?: boolean | undefined;\n /**\n * Packing strategy. Default `\"bbox\"`: each part is packed as its axis-aligned\n * bounding box (the original {@link nest} behavior — fast, parts never interlock).\n * `\"nfp\"` is true-shape / no-fit-polygon nesting: the actual outline polygons are\n * packed so concave (L-shaped) parts interlock for higher material utilization.\n * The NFP packer is a HEURISTIC (bottom-left-fill, largest-first) — not provably\n * optimal — but it never overlaps parts and never drops a part silently.\n */\n strategy?: 'bbox' | 'nfp' | undefined;\n}\n\n/**\n * Where a single pattern lands on a sheet. For the bbox strategy `rotationDeg` is\n * `0` or `90`; the true-shape (nfp) strategy may also emit `180` or `270`. `(x, y)`\n * is the lower-left of the part's transformed bounding box in sheet coordinates.\n */\nexport interface Placement {\n /** Index into the `patterns` array passed to {@link nest}. */\n patternIndex: number;\n /** Lower-left x of the part's (rotated) bounding box, in sheet coordinates. */\n x: number;\n /** Lower-left y of the part's (rotated) bounding box, in sheet coordinates. */\n y: number;\n rotationDeg: number;\n}\n\n/** One populated sheet produced by {@link nest}: where each pattern landed and how full it is. */\nexport interface NestSheet {\n placements: Placement[];\n /**\n * Σ placed part areas ÷ usable-sheet area, in `(0, 1]`. \"Usable\" =\n * `(width − 2·margin) × (height − 2·margin)`. For the `\"bbox\"` strategy the part\n * area is its BOUNDING-BOX area (intra-bbox waste of a non-rectangular part is not\n * credited — a conservative measure). For the `\"nfp\"` true-shape strategy it is the\n * actual OUTLINE-POLYGON area, so an L-shaped part credits only its true material,\n * making the two strategies' utilizations directly comparable on the same parts.\n */\n utilization: number;\n}\n\n/** Outcome of {@link nest}: the placed sheets, any unplaced patterns, and warnings. */\nexport interface NestResult {\n sheets: NestSheet[];\n /** Indices of patterns too large for the usable sheet even rotated. */\n unplaced: number[];\n warnings: SheetMetalWarning[];\n}\n\n/** Axis-aligned bounding box of one pattern outline, in developed-plane coords. */\nexport interface Bbox {\n minX: number;\n minY: number;\n width: number;\n height: number;\n}\n\nconst EPS = 1e-9;\n\n/**\n * Bounding-box nest: arrange developed flat patterns onto stock sheets to reduce\n * waste. This is BOUNDING-BOX nesting — each part is packed as its axis-aligned\n * outline bounding box, so parts never interlock and concave parts leave their\n * bounding-box waste unused. True-shape (no-fit-polygon) nesting is a follow-up.\n *\n * Algorithm: a shelf (level) packing heuristic. Parts are sorted largest-first by\n * bbox height then area, then placed left-to-right into horizontal shelves; a new\n * shelf opens below when the current row is full, and a NEW sheet opens when the\n * next shelf would overflow the usable height. With `allowRotation`, each part is\n * tried both at 0° and 90° and the orientation that fits the current shelf (or,\n * failing that, fits at all) is kept. A part larger than the usable sheet even\n * rotated is reported in `unplaced` with a warning — never dropped silently, never\n * retried in an infinite loop.\n *\n * @param patterns - developed flat patterns to place onto the sheets.\n * @param options - sheet dimensions plus margin, spacing, rotation and strategy.\n * @returns `Result<NestResult>` - the populated sheets plus any unplaced patterns and\n * warnings, or an error for invalid sheet/options or an unmeasurable outline.\n */\nexport function nest(patterns: FlatPattern[], options: NestOptions): Result<NestResult> {\n const { sheet } = options;\n const margin = options.margin ?? 0;\n const spacing = options.spacing ?? 0;\n const allowRotation = options.allowRotation ?? false;\n\n if (!Number.isFinite(sheet.width) || sheet.width <= 0 || !Number.isFinite(sheet.height) || sheet.height <= 0) {\n return err(validationError('INVALID_SHEET', `sheet width/height must be positive, got ${sheet.width}×${sheet.height}`));\n }\n if (!Number.isFinite(margin) || margin < 0 || !Number.isFinite(spacing) || spacing < 0) {\n return err(validationError('INVALID_NEST_OPTS', `margin and spacing must be non-negative finite numbers, got margin=${margin} spacing=${spacing}`));\n }\n\n const usableW = sheet.width - 2 * margin;\n const usableH = sheet.height - 2 * margin;\n if (usableW <= EPS || usableH <= EPS) {\n return err(validationError('INVALID_SHEET', `margin ${margin} leaves no usable area on a ${sheet.width}×${sheet.height} sheet`));\n }\n\n if (options.strategy === 'nfp') {\n return nestNfp(patterns, margin, spacing, allowRotation, usableW, usableH);\n }\n\n const boxes: Bbox[] = [];\n for (const pattern of patterns) {\n const b = patternBbox(pattern);\n if (!b.ok) return b;\n boxes.push(b.value);\n }\n\n const usableArea = usableW * usableH;\n const warnings: SheetMetalWarning[] = [];\n const unplaced: number[] = [];\n\n // Largest-first by height then area — the standard shelf-packing sort that keeps\n // tall parts from fragmenting later shelves.\n const order = boxes.map((_, i) => i).sort((a, b) => {\n const ba = boxes[a];\n const bb = boxes[b];\n if (ba === undefined || bb === undefined) return 0;\n if (Math.abs(bb.height - ba.height) > EPS) return bb.height - ba.height;\n return bb.width * bb.height - ba.width * ba.height;\n });\n\n const placeable: number[] = [];\n for (const idx of order) {\n const box = boxes[idx];\n if (box === undefined) continue;\n const fits = fitsUsable(box, usableW, usableH, false);\n const fitsRot = allowRotation && fitsUsable(box, usableW, usableH, true);\n if (!fits && !fitsRot) {\n unplaced.push(idx);\n warnings.push({\n code: 'PART_TOO_LARGE',\n message: `pattern ${idx} (${box.width.toFixed(2)}×${box.height.toFixed(2)}) does not fit the usable sheet ${usableW.toFixed(2)}×${usableH.toFixed(2)} even rotated; left unplaced`,\n featureId: `pattern-${idx}`,\n });\n continue;\n }\n placeable.push(idx);\n }\n\n const { sheets, dropped } = packShelves(placeable, boxes, usableW, usableH, margin, spacing, allowRotation, usableArea);\n for (const idx of dropped) {\n // Should be unreachable (pre-filtered parts fit an empty sheet); guards against\n // a future regression silently dropping a part.\n unplaced.push(idx);\n warnings.push({\n code: 'PART_TOO_LARGE',\n message: `pattern ${idx} could not be placed on a fresh sheet; left unplaced`,\n featureId: `pattern-${idx}`,\n });\n }\n\n unplaced.sort((a, b) => a - b);\n return ok({ sheets, unplaced, warnings });\n}\n\n/** A part outline normalised to the developed-plane origin, with its candidate\n * orientations precomputed. Each orientation's polygon is anchored so its own\n * bounding box lower-left sits at `(0, 0)`. */\ninterface NfpPart {\n index: number;\n /** Outline area (orientation-invariant) — used for the largest-first sort. */\n area: number;\n /** Candidate orientations to try, largest-first packing keeps the lowest-left fit. */\n orientations: NfpOrientation[];\n}\n\ninterface NfpOrientation {\n rotationDeg: number;\n /** Polygon anchored with its bounding-box lower-left at the origin. */\n poly: Polygon;\n /** Width/height of the (rotated) bounding box. */\n w: number;\n h: number;\n}\n\n/** One concrete placement on the current sheet: the orientation polygon translated\n * to its sheet position, kept (with its cached bounds) for the overlap test against\n * later parts. */\ninterface PlacedPoly {\n poly: Polygon;\n bounds: [number, number, number, number];\n}\n\nconst NFP_ROTATIONS = [0, 90, 180, 270];\n\n/**\n * True-shape (no-fit-polygon) nest. Packs the actual outline polygons so concave\n * parts interlock. This is a HEURISTIC bottom-left-fill packer (not provably\n * optimal): parts are placed largest-first; each part slides to the lowest-then-\n * leftmost feasible position on the current sheet where its outline polygon does not\n * overlap (within `spacing`) any already-placed polygon and stays inside the usable\n * area. Feasible positions are found by a discretised raster scan (a finite grid\n * stepped by a fraction of the smallest part extent), so the search always\n * terminates and a part can slide into a neighbour's concave notch.\n *\n * Each part is tried at 0/90/180/270° (90/270 only when `allowRotation`; 0/180\n * otherwise — 180° keeps the footprint but enables interlocking); the orientation\n * giving the lowest-leftmost feasible placement wins. A part that fits no position on\n * the current sheet opens a new sheet; one that fits no empty sheet in any orientation\n * goes to `unplaced` with a PART_TOO_LARGE warning — never dropped, never an infinite\n * loop.\n */\nfunction nestNfp(\n patterns: FlatPattern[],\n margin: number,\n spacing: number,\n allowRotation: boolean,\n usableW: number,\n usableH: number\n): Result<NestResult> {\n const parts: NfpPart[] = [];\n for (let i = 0; i < patterns.length; i += 1) {\n const pattern = patterns[i];\n if (pattern === undefined) continue;\n const built = buildNfpPart(i, pattern, allowRotation);\n if (!built.ok) return built;\n parts.push(built.value);\n }\n\n const usableArea = usableW * usableH;\n const warnings: SheetMetalWarning[] = [];\n const unplaced: number[] = [];\n\n // Largest-first by outline area — large concave parts placed first leave pockets\n // the smaller parts can interlock into.\n parts.sort((a, b) => b.area - a.area);\n\n // Bottom-left-fill raster step: a fraction of the smallest part extent. Finer steps\n // find tighter interlocks at more cost; this fixed ratio keeps the packer\n // deterministic and bounded (the scan grid is finite, so it always terminates).\n const step = rasterStep(parts, usableW, usableH);\n\n // Pre-filter parts that fit no empty usable sheet in any tried orientation.\n const placeable: NfpPart[] = [];\n for (const part of parts) {\n const fits = part.orientations.some((o) => o.w <= usableW + EPS && o.h <= usableH + EPS);\n if (!fits) {\n unplaced.push(part.index);\n const o0 = part.orientations[0];\n const dims = o0 === undefined ? '?' : `${o0.w.toFixed(2)}×${o0.h.toFixed(2)}`;\n warnings.push({\n code: 'PART_TOO_LARGE',\n message: `pattern ${part.index} (${dims}) does not fit the usable sheet ${usableW.toFixed(2)}×${usableH.toFixed(2)} even rotated; left unplaced`,\n featureId: `pattern-${part.index}`,\n });\n continue;\n }\n placeable.push(part);\n }\n\n const sheets: NestSheet[] = [];\n let current: PlacedPoly[] = [];\n let placements: Placement[] = [];\n let currentArea = 0;\n\n const flush = (): void => {\n if (placements.length === 0) return;\n sheets.push({ placements, utilization: currentArea / usableArea });\n };\n\n for (const part of placeable) {\n const placed = placeOnSheet(part, current, spacing, margin, usableW, usableH, step);\n if (placed === undefined) {\n // Current sheet full for this part — close it and start fresh. Every placeable\n // part fits an empty usable sheet, so this retry must succeed; if it somehow\n // does not, route the part to unplaced (self-enforcing safety net, no loop).\n flush();\n current = [];\n placements = [];\n currentArea = 0;\n const retry = placeOnSheet(part, current, spacing, margin, usableW, usableH, step);\n if (retry === undefined) {\n unplaced.push(part.index);\n warnings.push({\n code: 'PART_TOO_LARGE',\n message: `pattern ${part.index} could not be placed on a fresh sheet; left unplaced`,\n featureId: `pattern-${part.index}`,\n });\n continue;\n }\n current.push({ poly: retry.poly, bounds: polygonBounds(retry.poly) });\n placements.push(retry.placement);\n currentArea += part.area;\n continue;\n }\n current.push({ poly: placed.poly, bounds: polygonBounds(placed.poly) });\n placements.push(placed.placement);\n currentArea += part.area;\n }\n\n flush();\n unplaced.sort((a, b) => a - b);\n return ok({ sheets, unplaced, warnings });\n}\n\n/** Build the orientation set for one pattern outline (origin-anchored polygons). */\nfunction buildNfpPart(index: number, pattern: FlatPattern, allowRotation: boolean): Result<NfpPart> {\n const polyResult = wireToPolygon(pattern.outline);\n if (!polyResult.ok) return polyResult;\n let base = polyResult.value;\n // Normalise to CCW so the area sign and point-in-polygon orientation are consistent.\n if (signedArea(base) < 0) base = base.slice().reverse();\n const area = Math.abs(signedArea(base));\n if (area <= EPS) {\n return err(validationError('EMPTY_OUTLINE', `pattern ${index} outline has degenerate area`));\n }\n\n const rotations = allowRotation ? NFP_ROTATIONS : [0, 180];\n const orientations: NfpOrientation[] = [];\n const seen = new Set<string>();\n for (const rot of rotations) {\n const rotated = transformPolygon(base, 0, 0, rot);\n const [minX, minY, maxX, maxY] = polygonBounds(rotated);\n const anchored = transformPolygon(rotated, -minX, -minY, 0);\n const w = maxX - minX;\n const h = maxY - minY;\n // Skip an orientation whose anchored polygon is identical to an earlier one (e.g.\n // 180° of a centrally-symmetric part). Keyed on the rounded vertex loop, NOT the\n // bounding box — a concave part can share its bbox across rotations while its\n // SHAPE (and thus its interlocking behaviour) differs.\n const key = anchored.map(([x, y]) => `${x.toFixed(4)},${y.toFixed(4)}`).join(';');\n if (seen.has(key)) continue;\n seen.add(key);\n orientations.push({ rotationDeg: rot, poly: anchored, w, h });\n }\n return ok({ index, area, orientations });\n}\n\n/**\n * Bottom-left-fill raster step. The scan grid steps by a fraction of the smallest\n * part extent, so a part can slide INTO a neighbour's concave notch (a fit a sparse\n * bbox-corner candidate set would miss). Clamped to keep the per-sheet scan bounded.\n */\nfunction rasterStep(parts: NfpPart[], usableW: number, usableH: number): number {\n let minExtent = Infinity;\n for (const part of parts) {\n for (const o of part.orientations) {\n minExtent = Math.min(minExtent, o.w, o.h);\n }\n }\n if (!Number.isFinite(minExtent) || minExtent <= EPS) return Math.max(usableW, usableH) / 100;\n // ~1/8 of the smallest extent gives tight interlocks at a bounded scan count; never\n // finer than 1/400 of the larger sheet dimension (caps the worst-case grid size).\n const floor = Math.max(usableW, usableH) / 400;\n return Math.max(minExtent / 8, floor);\n}\n\n/**\n * Find the lowest-then-leftmost feasible placement of `part` on the current sheet\n * across all its orientations, by a discretised bottom-left raster scan. Returns the\n * translated polygon (for later overlap tests) and the {@link Placement}, or\n * undefined if nothing fits. The scan is over a finite grid so it always terminates.\n */\nfunction placeOnSheet(\n part: NfpPart,\n placed: PlacedPoly[],\n spacing: number,\n margin: number,\n usableW: number,\n usableH: number,\n step: number\n): { poly: Polygon; placement: Placement } | undefined {\n let best: { poly: Polygon; placement: Placement; key: [number, number] } | undefined;\n for (const o of part.orientations) {\n if (o.w > usableW + EPS || o.h > usableH + EPS) continue;\n const fit = scanOrientation(part.index, o, placed, spacing, margin, usableW, usableH, step);\n if (fit === undefined) continue;\n // Lower y wins; ties broken by lower x — the bottom-left-fill objective.\n if (\n best === undefined ||\n fit.key[1] < best.key[1] - EPS ||\n (Math.abs(fit.key[1] - best.key[1]) <= EPS && fit.key[0] < best.key[0] - EPS)\n ) {\n best = fit;\n }\n }\n if (best === undefined) return undefined;\n return { poly: best.poly, placement: best.placement };\n}\n\n/** Lowest-then-leftmost feasible raster position of one orientation, or undefined. */\nfunction scanOrientation(\n index: number,\n o: NfpOrientation,\n placed: PlacedPoly[],\n spacing: number,\n margin: number,\n usableW: number,\n usableH: number,\n step: number\n): { poly: Polygon; placement: Placement; key: [number, number] } | undefined {\n const maxX = margin + usableW - o.w;\n const maxY = margin + usableH - o.h;\n // Scan bottom-to-top, left-to-right; the first feasible cell is the bottom-left fit.\n for (let y = margin; y <= maxY + EPS; y += step) {\n for (let x = margin; x <= maxX + EPS; x += step) {\n const candidate = transformPolygon(o.poly, x, y, 0);\n if (overlapsAny(candidate, placed, spacing)) continue;\n return { poly: candidate, placement: { patternIndex: index, x, y, rotationDeg: o.rotationDeg }, key: [x, y] };\n }\n }\n return undefined;\n}\n\nfunction overlapsAny(candidate: Polygon, placed: PlacedPoly[], spacing: number): boolean {\n // Broad-phase bbox reject first (cheap), then the exact polygon overlap test.\n const [cMinX, cMinY, cMaxX, cMaxY] = polygonBounds(candidate);\n for (const pp of placed) {\n const [pMinX, pMinY, pMaxX, pMaxY] = pp.bounds;\n if (cMinX > pMaxX + spacing + EPS || pMinX > cMaxX + spacing + EPS) continue;\n if (cMinY > pMaxY + spacing + EPS || pMinY > cMaxY + spacing + EPS) continue;\n if (polygonsOverlapWithClearance(candidate, pp.poly, spacing)) return true;\n }\n return false;\n}\n\n/** Does a part fit the usable sheet in the given orientation? */\nfunction fitsUsable(box: Bbox, usableW: number, usableH: number, rotated: boolean): boolean {\n const w = rotated ? box.height : box.width;\n const h = rotated ? box.width : box.height;\n return w <= usableW + EPS && h <= usableH + EPS;\n}\n\ninterface ShelfState {\n placements: Placement[];\n /** Bottom y (sheet coords) of the current open shelf. */\n shelfY: number;\n /** Height of the current open shelf (the tallest part placed in it). */\n shelfH: number;\n /** Next free x (sheet coords) on the current shelf. */\n cursorX: number;\n placedArea: number;\n}\n\nfunction packShelves(\n placeable: number[],\n boxes: Bbox[],\n usableW: number,\n usableH: number,\n margin: number,\n spacing: number,\n allowRotation: boolean,\n usableArea: number\n): { sheets: NestSheet[]; dropped: number[] } {\n const sheets: NestSheet[] = [];\n const dropped: number[] = [];\n let state = newSheet(margin);\n\n const commit = (): void => {\n sheets.push({ placements: state.placements, utilization: state.placedArea / usableArea });\n };\n\n for (const idx of placeable) {\n const box = boxes[idx];\n if (box === undefined) continue;\n\n if (!tryPlace(state, box, idx, usableW, usableH, margin, spacing, allowRotation)) {\n // Current sheet is full for this part — close it and open a fresh one. Every\n // part in `placeable` fits an empty usable sheet, so this second attempt should\n // always succeed; record it as dropped (caller routes to `unplaced`) as a\n // self-enforcing safety net rather than relying on that reasoning alone.\n commit();\n state = newSheet(margin);\n if (!tryPlace(state, box, idx, usableW, usableH, margin, spacing, allowRotation)) {\n dropped.push(idx);\n }\n }\n }\n\n if (state.placements.length > 0) commit();\n return { sheets, dropped };\n}\n\nfunction newSheet(margin: number): ShelfState {\n return { placements: [], shelfY: margin, shelfH: 0, cursorX: margin, placedArea: 0 };\n}\n\n/**\n * Try to place `box` on the current sheet, mutating `state` on success. Tries the\n * current shelf first (with rotation if allowed), then opens a new shelf below.\n * Returns false when the part fits neither the current nor a new shelf on this\n * sheet (the caller then opens a new sheet).\n */\nfunction tryPlace(\n state: ShelfState,\n box: Bbox,\n idx: number,\n usableW: number,\n usableH: number,\n margin: number,\n spacing: number,\n allowRotation: boolean\n): boolean {\n const right = margin + usableW;\n const top = margin + usableH;\n\n // Candidate orientations: 0° always, 90° when allowed (and the box is not square).\n const orientations: { rot: 0 | 90; w: number; h: number }[] = [{ rot: 0, w: box.width, h: box.height }];\n if (allowRotation && Math.abs(box.width - box.height) > EPS) {\n orientations.push({ rot: 90, w: box.height, h: box.width });\n }\n\n const firstOnShelf = state.cursorX <= margin + EPS;\n\n // 1) Fit on the current open shelf. Prefer the orientation that keeps the shelf\n // shortest (least new vertical waste) among those that fit horizontally and\n // within the shelf's established height once started.\n let best: { rot: 0 | 90; w: number; h: number } | undefined;\n for (const o of orientations) {\n const xStart = firstOnShelf ? state.cursorX : state.cursorX + spacing;\n if (xStart + o.w > right + EPS) continue;\n // A started shelf has a fixed bottom; the part must fit under the usable top\n // from the shelf bottom (it may grow shelfH up to the top).\n if (state.shelfY + o.h > top + EPS) continue;\n if (best === undefined || o.h < best.h - EPS) best = o;\n }\n if (best !== undefined) {\n const xStart = firstOnShelf ? state.cursorX : state.cursorX + spacing;\n state.placements.push({ patternIndex: idx, x: xStart, y: state.shelfY, rotationDeg: best.rot });\n state.cursorX = xStart + best.w;\n state.shelfH = Math.max(state.shelfH, best.h);\n state.placedArea += best.w * best.h;\n return true;\n }\n\n // 2) Open a new shelf below the current one (if anything is on the current shelf).\n if (state.placements.length === 0) return false;\n const nextShelfY = state.shelfY + state.shelfH + spacing;\n let bestNew: { rot: 0 | 90; w: number; h: number } | undefined;\n for (const o of orientations) {\n if (margin + o.w > right + EPS) continue;\n if (nextShelfY + o.h > top + EPS) continue;\n if (bestNew === undefined || o.h < bestNew.h - EPS) bestNew = o;\n }\n if (bestNew === undefined) return false;\n state.shelfY = nextShelfY;\n state.shelfH = bestNew.h;\n state.cursorX = margin + bestNew.w;\n state.placements.push({ patternIndex: idx, x: margin, y: nextShelfY, rotationDeg: bestNew.rot });\n state.placedArea += bestNew.w * bestNew.h;\n return true;\n}\n\n/**\n * Axis-aligned bounding box of a pattern's OUTER outline wire, read from the wire\n * vertices (each edge start point) — the same 2D-coordinate read the DXF/SVG writers\n * use. Only the outer outline is packed; holes ride inside it.\n *\n * @param pattern - the flat pattern whose outer outline is measured.\n * @returns `Result<Bbox>` - the axis-aligned bounding box of the outer outline, or an\n * error if the outline is empty or degenerate.\n */\nexport function patternBbox(pattern: FlatPattern): Result<Bbox> {\n const edges = getEdges(pattern.outline);\n if (edges.length === 0) {\n return err(validationError('EMPTY_OUTLINE', 'pattern outline has no edges to bound'));\n }\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n for (const edge of edges) {\n const p = curveStartPoint(edge);\n if (p[0] < minX) minX = p[0];\n if (p[0] > maxX) maxX = p[0];\n if (p[1] < minY) minY = p[1];\n if (p[1] > maxY) maxY = p[1];\n }\n const width = maxX - minX;\n const height = maxY - minY;\n if (!Number.isFinite(minX) || width <= EPS || height <= EPS) {\n return err(validationError('EMPTY_OUTLINE', 'pattern outline bounding box is degenerate'));\n }\n return ok({ minX, minY, width, height });\n}\n\n/**\n * Sheet-placement transform for one placement: shift the pattern's developed-plane\n * bbox lower-left to the origin, rotate `rotationDeg` (CCW) about it, then translate\n * the rotated shape so its rotated bounding box's lower-left lands at `(x, y)` on the\n * sheet. A point at developed `(px, py)` therefore lands inside the box the packer\n * reserved. The 0° and 90° branches are kept as closed-form integer-stable maps so\n * the bbox-strategy DXF output is byte-identical to the pre-NFP writer; finer angles\n * (180/270 and any nfp-emitted value) take the general rotation path.\n */\nfunction placementTransform(box: Bbox, x: number, y: number, rotationDeg: number): Transform2 {\n if (rotationDeg === 0) {\n return ([px, py]) => [x + (px - box.minX), y + (py - box.minY)];\n }\n if (rotationDeg === 90) {\n // After shifting to origin: (lx, ly) in [0,w]×[0,h]. Rotate 90° CCW: (-ly, lx)\n // → [-h,0]×[0,w]; add h to bring x back to [0,h], then translate to (x, y).\n return ([px, py]) => {\n const lx = px - box.minX;\n const ly = py - box.minY;\n return [x + (box.height - ly), y + lx];\n };\n }\n // General CCW rotation about the bbox lower-left, then re-anchor the rotated bbox's\n // lower-left to (x, y) so the placement matches the box the packer reserved.\n const rad = (rotationDeg * Math.PI) / 180;\n const c = Math.cos(rad);\n const s = Math.sin(rad);\n const corners: [number, number][] = [\n [0, 0],\n [box.width, 0],\n [box.width, box.height],\n [0, box.height],\n ];\n let minRx = Infinity;\n let minRy = Infinity;\n for (const [cx, cy] of corners) {\n const rx = cx * c - cy * s;\n const ry = cx * s + cy * c;\n if (rx < minRx) minRx = rx;\n if (ry < minRy) minRy = ry;\n }\n return ([px, py]) => {\n const lx = px - box.minX;\n const ly = py - box.minY;\n const rx = lx * c - ly * s;\n const ry = lx * s + ly * c;\n return [x + (rx - minRx), y + (ry - minRy)];\n };\n}\n\n/**\n * Emit one fabrication-ready DXF for a single nested sheet: every pattern placed on\n * `result.sheets[sheetIndex]` is translated and rotated onto the sheet (via the same\n * bounding boxes the packer used) and written through {@link multiPatternToDXF}.\n * `patterns` must be the SAME array passed to {@link nest} (placements index into it).\n *\n * @param result - the {@link NestResult} returned by {@link nest}.\n * @param patterns - the same `FlatPattern[]` array that was passed to {@link nest}.\n * @param sheetIndex - index into `result.sheets` of the sheet to emit.\n * @param options - optional DXF options forwarded to {@link multiPatternToDXF}.\n * @returns `Result<string>` - the DXF text for the selected sheet, or an error if the\n * sheet index is invalid, a placement references a missing pattern, or a pattern\n * outline cannot be measured.\n */\nexport function nestToDXF(\n result: NestResult,\n patterns: FlatPattern[],\n sheetIndex: number,\n options?: DxfOptions\n): Result<string> {\n const sheet = result.sheets[sheetIndex];\n if (sheet === undefined) {\n return err(validationError('INVALID_SHEET_INDEX', `no nested sheet at index ${sheetIndex} (have ${result.sheets.length})`));\n }\n const placed: PlacedPattern[] = [];\n for (const p of sheet.placements) {\n const pattern = patterns[p.patternIndex];\n if (pattern === undefined) {\n return err(validationError('INVALID_PATTERN_INDEX', `placement references missing pattern ${p.patternIndex}`));\n }\n const box = patternBbox(pattern);\n if (!box.ok) return box;\n placed.push({ pattern, transform: placementTransform(box.value, p.x, p.y, p.rotationDeg) });\n }\n return multiPatternToDXF(placed, options ?? {});\n}\n"]}
1
+ {"version":3,"file":"nestFns.js","sourceRoot":"","sources":["../src/nestFns.ts"],"names":[],"mappings":"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","sourcesContent":["import {type Result, ok, err, validationError, getEdges, curveStartPoint} from '@faicad/faijs/api';\nimport type { FlatPattern, SheetMetalWarning } from './types.js';\nimport { multiPatternToDXF, type DxfOptions, type Transform2, type PlacedPattern } from './dxfFns.js';\nimport {\n wireToPolygon,\n transformPolygon,\n polygonBounds,\n polygonsOverlapWithClearance,\n signedArea,\n type Polygon,\n} from './polygonFns.js';\n\n/** Stock sheet the parts are packed onto (the gross blank, before margin). */\nexport interface SheetSpec {\n width: number;\n height: number;\n}\n\n/** Nesting options: the stock sheet plus optional margin, spacing, rotation and strategy. */\nexport interface NestOptions {\n sheet: SheetSpec;\n /** Clear border kept empty around the sheet edge (per side). Default `0`. */\n margin?: number | undefined;\n /** Minimum clear gap between any two placed parts (and the bbox padding used\n * for the non-overlap test). Default `0`. */\n spacing?: number | undefined;\n /** Try a 90° rotation per part and keep whichever orientation fits/packs better. */\n allowRotation?: boolean | undefined;\n /**\n * Packing strategy. Default `\"bbox\"`: each part is packed as its axis-aligned\n * bounding box (the original {@link nest} behavior — fast, parts never interlock).\n * `\"nfp\"` is true-shape / no-fit-polygon nesting: the actual outline polygons are\n * packed so concave (L-shaped) parts interlock for higher material utilization.\n * The NFP packer is a HEURISTIC (bottom-left-fill, largest-first) — not provably\n * optimal — but it never overlaps parts and never drops a part silently.\n */\n strategy?: 'bbox' | 'nfp' | undefined;\n}\n\n/**\n * Where a single pattern lands on a sheet. For the bbox strategy `rotationDeg` is\n * `0` or `90`; the true-shape (nfp) strategy may also emit `180` or `270`. `(x, y)`\n * is the lower-left of the part's transformed bounding box in sheet coordinates.\n */\nexport interface Placement {\n /** Index into the `patterns` array passed to {@link nest}. */\n patternIndex: number;\n /** Lower-left x of the part's (rotated) bounding box, in sheet coordinates. */\n x: number;\n /** Lower-left y of the part's (rotated) bounding box, in sheet coordinates. */\n y: number;\n rotationDeg: number;\n}\n\n/** One populated sheet produced by {@link nest}: where each pattern landed and how full it is. */\nexport interface NestSheet {\n placements: Placement[];\n /**\n * Σ placed part areas ÷ usable-sheet area, in `(0, 1]`. \"Usable\" =\n * `(width − 2·margin) × (height − 2·margin)`. For the `\"bbox\"` strategy the part\n * area is its BOUNDING-BOX area (intra-bbox waste of a non-rectangular part is not\n * credited — a conservative measure). For the `\"nfp\"` true-shape strategy it is the\n * actual OUTLINE-POLYGON area, so an L-shaped part credits only its true material,\n * making the two strategies' utilizations directly comparable on the same parts.\n */\n utilization: number;\n}\n\n/** Outcome of {@link nest}: the placed sheets, any unplaced patterns, and warnings. */\nexport interface NestResult {\n sheets: NestSheet[];\n /** Indices of patterns too large for the usable sheet even rotated. */\n unplaced: number[];\n warnings: SheetMetalWarning[];\n}\n\n/** Axis-aligned bounding box of one pattern outline, in developed-plane coords. */\nexport interface Bbox {\n minX: number;\n minY: number;\n width: number;\n height: number;\n}\n\nconst EPS = 1e-9;\n\n/**\n * Bounding-box nest: arrange developed flat patterns onto stock sheets to reduce\n * waste. This is BOUNDING-BOX nesting — each part is packed as its axis-aligned\n * outline bounding box, so parts never interlock and concave parts leave their\n * bounding-box waste unused. True-shape (no-fit-polygon) nesting is a follow-up.\n *\n * Algorithm: a shelf (level) packing heuristic. Parts are sorted largest-first by\n * bbox height then area, then placed left-to-right into horizontal shelves; a new\n * shelf opens below when the current row is full, and a NEW sheet opens when the\n * next shelf would overflow the usable height. With `allowRotation`, each part is\n * tried both at 0° and 90° and the orientation that fits the current shelf (or,\n * failing that, fits at all) is kept. A part larger than the usable sheet even\n * rotated is reported in `unplaced` with a warning — never dropped silently, never\n * retried in an infinite loop.\n *\n * @param patterns - developed flat patterns to place onto the sheets.\n * @param options - sheet dimensions plus margin, spacing, rotation and strategy.\n * @returns `Result<NestResult>` - the populated sheets plus any unplaced patterns and\n * warnings, or an error for invalid sheet/options or an unmeasurable outline.\n */\nexport function nest(patterns: FlatPattern[], options: NestOptions): Result<NestResult> {\n const { sheet } = options;\n const margin = options.margin ?? 0;\n const spacing = options.spacing ?? 0;\n const allowRotation = options.allowRotation ?? false;\n\n if (!Number.isFinite(sheet.width) || sheet.width <= 0 || !Number.isFinite(sheet.height) || sheet.height <= 0) {\n return err(validationError('INVALID_SHEET', `sheet width/height must be positive, got ${sheet.width}×${sheet.height}`));\n }\n if (!Number.isFinite(margin) || margin < 0 || !Number.isFinite(spacing) || spacing < 0) {\n return err(validationError('INVALID_NEST_OPTS', `margin and spacing must be non-negative finite numbers, got margin=${margin} spacing=${spacing}`));\n }\n\n const usableW = sheet.width - 2 * margin;\n const usableH = sheet.height - 2 * margin;\n if (usableW <= EPS || usableH <= EPS) {\n return err(validationError('INVALID_SHEET', `margin ${margin} leaves no usable area on a ${sheet.width}×${sheet.height} sheet`));\n }\n\n if (options.strategy === 'nfp') {\n return nestNfp(patterns, margin, spacing, allowRotation, usableW, usableH);\n }\n\n const boxes: Bbox[] = [];\n for (const pattern of patterns) {\n const b = patternBbox(pattern);\n if (!b.ok) return b;\n boxes.push(b.value);\n }\n\n const usableArea = usableW * usableH;\n const warnings: SheetMetalWarning[] = [];\n const unplaced: number[] = [];\n\n // Largest-first by height then area — the standard shelf-packing sort that keeps\n // tall parts from fragmenting later shelves.\n const order = boxes.map((_, i) => i).sort((a, b) => {\n const ba = boxes[a];\n const bb = boxes[b];\n if (ba === undefined || bb === undefined) return 0;\n if (Math.abs(bb.height - ba.height) > EPS) return bb.height - ba.height;\n return bb.width * bb.height - ba.width * ba.height;\n });\n\n const placeable: number[] = [];\n for (const idx of order) {\n const box = boxes[idx];\n if (box === undefined) continue;\n const fits = fitsUsable(box, usableW, usableH, false);\n const fitsRot = allowRotation && fitsUsable(box, usableW, usableH, true);\n if (!fits && !fitsRot) {\n unplaced.push(idx);\n warnings.push({\n code: 'PART_TOO_LARGE',\n message: `pattern ${idx} (${box.width.toFixed(2)}×${box.height.toFixed(2)}) does not fit the usable sheet ${usableW.toFixed(2)}×${usableH.toFixed(2)} even rotated; left unplaced`,\n featureId: `pattern-${idx}`,\n });\n continue;\n }\n placeable.push(idx);\n }\n\n const { sheets, dropped } = packShelves(placeable, boxes, usableW, usableH, margin, spacing, allowRotation, usableArea);\n for (const idx of dropped) {\n // Should be unreachable (pre-filtered parts fit an empty sheet); guards against\n // a future regression silently dropping a part.\n unplaced.push(idx);\n warnings.push({\n code: 'PART_TOO_LARGE',\n message: `pattern ${idx} could not be placed on a fresh sheet; left unplaced`,\n featureId: `pattern-${idx}`,\n });\n }\n\n unplaced.sort((a, b) => a - b);\n return ok({ sheets, unplaced, warnings });\n}\n\n/** A part outline normalised to the developed-plane origin, with its candidate\n * orientations precomputed. Each orientation's polygon is anchored so its own\n * bounding box lower-left sits at `(0, 0)`. */\ninterface NfpPart {\n index: number;\n /** Outline area (orientation-invariant) — used for the largest-first sort. */\n area: number;\n /** Candidate orientations to try, largest-first packing keeps the lowest-left fit. */\n orientations: NfpOrientation[];\n}\n\ninterface NfpOrientation {\n rotationDeg: number;\n /** Polygon anchored with its bounding-box lower-left at the origin. */\n poly: Polygon;\n /** Width/height of the (rotated) bounding box. */\n w: number;\n h: number;\n}\n\n/** One concrete placement on the current sheet: the orientation polygon translated\n * to its sheet position, kept (with its cached bounds) for the overlap test against\n * later parts. */\ninterface PlacedPoly {\n poly: Polygon;\n bounds: [number, number, number, number];\n}\n\nconst NFP_ROTATIONS = [0, 90, 180, 270];\n\n/**\n * True-shape (no-fit-polygon) nest. Packs the actual outline polygons so concave\n * parts interlock. This is a HEURISTIC bottom-left-fill packer (not provably\n * optimal): parts are placed largest-first; each part slides to the lowest-then-\n * leftmost feasible position on the current sheet where its outline polygon does not\n * overlap (within `spacing`) any already-placed polygon and stays inside the usable\n * area. Feasible positions are found by a discretised raster scan (a finite grid\n * stepped by a fraction of the smallest part extent), so the search always\n * terminates and a part can slide into a neighbour's concave notch.\n *\n * Each part is tried at 0/90/180/270° (90/270 only when `allowRotation`; 0/180\n * otherwise — 180° keeps the footprint but enables interlocking); the orientation\n * giving the lowest-leftmost feasible placement wins. A part that fits no position on\n * the current sheet opens a new sheet; one that fits no empty sheet in any orientation\n * goes to `unplaced` with a PART_TOO_LARGE warning — never dropped, never an infinite\n * loop.\n */\nfunction nestNfp(\n patterns: FlatPattern[],\n margin: number,\n spacing: number,\n allowRotation: boolean,\n usableW: number,\n usableH: number\n): Result<NestResult> {\n const parts: NfpPart[] = [];\n for (let i = 0; i < patterns.length; i += 1) {\n const pattern = patterns[i];\n if (pattern === undefined) continue;\n const built = buildNfpPart(i, pattern, allowRotation);\n if (!built.ok) return built;\n parts.push(built.value);\n }\n\n const usableArea = usableW * usableH;\n const warnings: SheetMetalWarning[] = [];\n const unplaced: number[] = [];\n\n // Largest-first by outline area — large concave parts placed first leave pockets\n // the smaller parts can interlock into.\n parts.sort((a, b) => b.area - a.area);\n\n // Bottom-left-fill raster step: a fraction of the smallest part extent. Finer steps\n // find tighter interlocks at more cost; this fixed ratio keeps the packer\n // deterministic and bounded (the scan grid is finite, so it always terminates).\n const step = rasterStep(parts, usableW, usableH);\n\n // Pre-filter parts that fit no empty usable sheet in any tried orientation.\n const placeable: NfpPart[] = [];\n for (const part of parts) {\n const fits = part.orientations.some((o) => o.w <= usableW + EPS && o.h <= usableH + EPS);\n if (!fits) {\n unplaced.push(part.index);\n const o0 = part.orientations[0];\n const dims = o0 === undefined ? '?' : `${o0.w.toFixed(2)}×${o0.h.toFixed(2)}`;\n warnings.push({\n code: 'PART_TOO_LARGE',\n message: `pattern ${part.index} (${dims}) does not fit the usable sheet ${usableW.toFixed(2)}×${usableH.toFixed(2)} even rotated; left unplaced`,\n featureId: `pattern-${part.index}`,\n });\n continue;\n }\n placeable.push(part);\n }\n\n const sheets: NestSheet[] = [];\n let current: PlacedPoly[] = [];\n let placements: Placement[] = [];\n let currentArea = 0;\n\n const flush = (): void => {\n if (placements.length === 0) return;\n sheets.push({ placements, utilization: currentArea / usableArea });\n };\n\n for (const part of placeable) {\n const placed = placeOnSheet(part, current, spacing, margin, usableW, usableH, step);\n if (placed === undefined) {\n // Current sheet full for this part — close it and start fresh. Every placeable\n // part fits an empty usable sheet, so this retry must succeed; if it somehow\n // does not, route the part to unplaced (self-enforcing safety net, no loop).\n flush();\n current = [];\n placements = [];\n currentArea = 0;\n const retry = placeOnSheet(part, current, spacing, margin, usableW, usableH, step);\n if (retry === undefined) {\n unplaced.push(part.index);\n warnings.push({\n code: 'PART_TOO_LARGE',\n message: `pattern ${part.index} could not be placed on a fresh sheet; left unplaced`,\n featureId: `pattern-${part.index}`,\n });\n continue;\n }\n current.push({ poly: retry.poly, bounds: polygonBounds(retry.poly) });\n placements.push(retry.placement);\n currentArea += part.area;\n continue;\n }\n current.push({ poly: placed.poly, bounds: polygonBounds(placed.poly) });\n placements.push(placed.placement);\n currentArea += part.area;\n }\n\n flush();\n unplaced.sort((a, b) => a - b);\n return ok({ sheets, unplaced, warnings });\n}\n\n/** Build the orientation set for one pattern outline (origin-anchored polygons). */\nfunction buildNfpPart(index: number, pattern: FlatPattern, allowRotation: boolean): Result<NfpPart> {\n const polyResult = wireToPolygon(pattern.outline);\n if (!polyResult.ok) return polyResult;\n let base = polyResult.value;\n // Normalise to CCW so the area sign and point-in-polygon orientation are consistent.\n if (signedArea(base) < 0) base = base.slice().reverse();\n const area = Math.abs(signedArea(base));\n if (area <= EPS) {\n return err(validationError('EMPTY_OUTLINE', `pattern ${index} outline has degenerate area`));\n }\n\n const rotations = allowRotation ? NFP_ROTATIONS : [0, 180];\n const orientations: NfpOrientation[] = [];\n const seen = new Set<string>();\n for (const rot of rotations) {\n const rotated = transformPolygon(base, 0, 0, rot);\n const [minX, minY, maxX, maxY] = polygonBounds(rotated);\n const anchored = transformPolygon(rotated, -minX, -minY, 0);\n const w = maxX - minX;\n const h = maxY - minY;\n // Skip an orientation whose anchored polygon is identical to an earlier one (e.g.\n // 180° of a centrally-symmetric part). Keyed on the rounded vertex loop, NOT the\n // bounding box — a concave part can share its bbox across rotations while its\n // SHAPE (and thus its interlocking behaviour) differs.\n const key = anchored.map(([x, y]) => `${x.toFixed(4)},${y.toFixed(4)}`).join(';');\n if (seen.has(key)) continue;\n seen.add(key);\n orientations.push({ rotationDeg: rot, poly: anchored, w, h });\n }\n return ok({ index, area, orientations });\n}\n\n/**\n * Bottom-left-fill raster step. The scan grid steps by a fraction of the smallest\n * part extent, so a part can slide INTO a neighbour's concave notch (a fit a sparse\n * bbox-corner candidate set would miss). Clamped to keep the per-sheet scan bounded.\n */\nfunction rasterStep(parts: NfpPart[], usableW: number, usableH: number): number {\n let minExtent = Infinity;\n for (const part of parts) {\n for (const o of part.orientations) {\n minExtent = Math.min(minExtent, o.w, o.h);\n }\n }\n if (!Number.isFinite(minExtent) || minExtent <= EPS) return Math.max(usableW, usableH) / 100;\n // ~1/8 of the smallest extent gives tight interlocks at a bounded scan count; never\n // finer than 1/400 of the larger sheet dimension (caps the worst-case grid size).\n const floor = Math.max(usableW, usableH) / 400;\n return Math.max(minExtent / 8, floor);\n}\n\n/**\n * Find the lowest-then-leftmost feasible placement of `part` on the current sheet\n * across all its orientations, by a discretised bottom-left raster scan. Returns the\n * translated polygon (for later overlap tests) and the {@link Placement}, or\n * undefined if nothing fits. The scan is over a finite grid so it always terminates.\n */\nfunction placeOnSheet(\n part: NfpPart,\n placed: PlacedPoly[],\n spacing: number,\n margin: number,\n usableW: number,\n usableH: number,\n step: number\n): { poly: Polygon; placement: Placement } | undefined {\n let best: { poly: Polygon; placement: Placement; key: [number, number] } | undefined;\n for (const o of part.orientations) {\n if (o.w > usableW + EPS || o.h > usableH + EPS) continue;\n const fit = scanOrientation(part.index, o, placed, spacing, margin, usableW, usableH, step);\n if (fit === undefined) continue;\n // Lower y wins; ties broken by lower x — the bottom-left-fill objective.\n if (\n best === undefined ||\n fit.key[1] < best.key[1] - EPS ||\n (Math.abs(fit.key[1] - best.key[1]) <= EPS && fit.key[0] < best.key[0] - EPS)\n ) {\n best = fit;\n }\n }\n if (best === undefined) return undefined;\n return { poly: best.poly, placement: best.placement };\n}\n\n/** Lowest-then-leftmost feasible raster position of one orientation, or undefined. */\nfunction scanOrientation(\n index: number,\n o: NfpOrientation,\n placed: PlacedPoly[],\n spacing: number,\n margin: number,\n usableW: number,\n usableH: number,\n step: number\n): { poly: Polygon; placement: Placement; key: [number, number] } | undefined {\n const maxX = margin + usableW - o.w;\n const maxY = margin + usableH - o.h;\n // Scan bottom-to-top, left-to-right; the first feasible cell is the bottom-left fit.\n for (let y = margin; y <= maxY + EPS; y += step) {\n for (let x = margin; x <= maxX + EPS; x += step) {\n const candidate = transformPolygon(o.poly, x, y, 0);\n if (overlapsAny(candidate, placed, spacing)) continue;\n return { poly: candidate, placement: { patternIndex: index, x, y, rotationDeg: o.rotationDeg }, key: [x, y] };\n }\n }\n return undefined;\n}\n\nfunction overlapsAny(candidate: Polygon, placed: PlacedPoly[], spacing: number): boolean {\n // Broad-phase bbox reject first (cheap), then the exact polygon overlap test.\n const [cMinX, cMinY, cMaxX, cMaxY] = polygonBounds(candidate);\n for (const pp of placed) {\n const [pMinX, pMinY, pMaxX, pMaxY] = pp.bounds;\n if (cMinX > pMaxX + spacing + EPS || pMinX > cMaxX + spacing + EPS) continue;\n if (cMinY > pMaxY + spacing + EPS || pMinY > cMaxY + spacing + EPS) continue;\n if (polygonsOverlapWithClearance(candidate, pp.poly, spacing)) return true;\n }\n return false;\n}\n\n/** Does a part fit the usable sheet in the given orientation? */\nfunction fitsUsable(box: Bbox, usableW: number, usableH: number, rotated: boolean): boolean {\n const w = rotated ? box.height : box.width;\n const h = rotated ? box.width : box.height;\n return w <= usableW + EPS && h <= usableH + EPS;\n}\n\ninterface ShelfState {\n placements: Placement[];\n /** Bottom y (sheet coords) of the current open shelf. */\n shelfY: number;\n /** Height of the current open shelf (the tallest part placed in it). */\n shelfH: number;\n /** Next free x (sheet coords) on the current shelf. */\n cursorX: number;\n placedArea: number;\n}\n\nfunction packShelves(\n placeable: number[],\n boxes: Bbox[],\n usableW: number,\n usableH: number,\n margin: number,\n spacing: number,\n allowRotation: boolean,\n usableArea: number\n): { sheets: NestSheet[]; dropped: number[] } {\n const sheets: NestSheet[] = [];\n const dropped: number[] = [];\n let state = newSheet(margin);\n\n const commit = (): void => {\n sheets.push({ placements: state.placements, utilization: state.placedArea / usableArea });\n };\n\n for (const idx of placeable) {\n const box = boxes[idx];\n if (box === undefined) continue;\n\n if (!tryPlace(state, box, idx, usableW, usableH, margin, spacing, allowRotation)) {\n // Current sheet is full for this part — close it and open a fresh one. Every\n // part in `placeable` fits an empty usable sheet, so this second attempt should\n // always succeed; record it as dropped (caller routes to `unplaced`) as a\n // self-enforcing safety net rather than relying on that reasoning alone.\n commit();\n state = newSheet(margin);\n if (!tryPlace(state, box, idx, usableW, usableH, margin, spacing, allowRotation)) {\n dropped.push(idx);\n }\n }\n }\n\n if (state.placements.length > 0) commit();\n return { sheets, dropped };\n}\n\nfunction newSheet(margin: number): ShelfState {\n return { placements: [], shelfY: margin, shelfH: 0, cursorX: margin, placedArea: 0 };\n}\n\n/**\n * Try to place `box` on the current sheet, mutating `state` on success. Tries the\n * current shelf first (with rotation if allowed), then opens a new shelf below.\n * Returns false when the part fits neither the current nor a new shelf on this\n * sheet (the caller then opens a new sheet).\n */\nfunction tryPlace(\n state: ShelfState,\n box: Bbox,\n idx: number,\n usableW: number,\n usableH: number,\n margin: number,\n spacing: number,\n allowRotation: boolean\n): boolean {\n const right = margin + usableW;\n const top = margin + usableH;\n\n // Candidate orientations: 0° always, 90° when allowed (and the box is not square).\n const orientations: { rot: 0 | 90; w: number; h: number }[] = [{ rot: 0, w: box.width, h: box.height }];\n if (allowRotation && Math.abs(box.width - box.height) > EPS) {\n orientations.push({ rot: 90, w: box.height, h: box.width });\n }\n\n const firstOnShelf = state.cursorX <= margin + EPS;\n\n // 1) Fit on the current open shelf. Prefer the orientation that keeps the shelf\n // shortest (least new vertical waste) among those that fit horizontally and\n // within the shelf's established height once started.\n let best: { rot: 0 | 90; w: number; h: number } | undefined;\n for (const o of orientations) {\n const xStart = firstOnShelf ? state.cursorX : state.cursorX + spacing;\n if (xStart + o.w > right + EPS) continue;\n // A started shelf has a fixed bottom; the part must fit under the usable top\n // from the shelf bottom (it may grow shelfH up to the top).\n if (state.shelfY + o.h > top + EPS) continue;\n if (best === undefined || o.h < best.h - EPS) best = o;\n }\n if (best !== undefined) {\n const xStart = firstOnShelf ? state.cursorX : state.cursorX + spacing;\n state.placements.push({ patternIndex: idx, x: xStart, y: state.shelfY, rotationDeg: best.rot });\n state.cursorX = xStart + best.w;\n state.shelfH = Math.max(state.shelfH, best.h);\n state.placedArea += best.w * best.h;\n return true;\n }\n\n // 2) Open a new shelf below the current one (if anything is on the current shelf).\n if (state.placements.length === 0) return false;\n const nextShelfY = state.shelfY + state.shelfH + spacing;\n let bestNew: { rot: 0 | 90; w: number; h: number } | undefined;\n for (const o of orientations) {\n if (margin + o.w > right + EPS) continue;\n if (nextShelfY + o.h > top + EPS) continue;\n if (bestNew === undefined || o.h < bestNew.h - EPS) bestNew = o;\n }\n if (bestNew === undefined) return false;\n state.shelfY = nextShelfY;\n state.shelfH = bestNew.h;\n state.cursorX = margin + bestNew.w;\n state.placements.push({ patternIndex: idx, x: margin, y: nextShelfY, rotationDeg: bestNew.rot });\n state.placedArea += bestNew.w * bestNew.h;\n return true;\n}\n\n/**\n * Axis-aligned bounding box of a pattern's OUTER outline wire, read from the wire\n * vertices (each edge start point) — the same 2D-coordinate read the DXF/SVG writers\n * use. Only the outer outline is packed; holes ride inside it.\n *\n * @param pattern - the flat pattern whose outer outline is measured.\n * @returns `Result<Bbox>` - the axis-aligned bounding box of the outer outline, or an\n * error if the outline is empty or degenerate.\n */\nexport function patternBbox(pattern: FlatPattern): Result<Bbox> {\n const edges = getEdges(pattern.outline);\n if (edges.length === 0) {\n return err(validationError('EMPTY_OUTLINE', 'pattern outline has no edges to bound'));\n }\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n for (const edge of edges) {\n const p = curveStartPoint(edge);\n if (p[0] < minX) minX = p[0];\n if (p[0] > maxX) maxX = p[0];\n if (p[1] < minY) minY = p[1];\n if (p[1] > maxY) maxY = p[1];\n }\n const width = maxX - minX;\n const height = maxY - minY;\n if (!Number.isFinite(minX) || width <= EPS || height <= EPS) {\n return err(validationError('EMPTY_OUTLINE', 'pattern outline bounding box is degenerate'));\n }\n return ok({ minX, minY, width, height });\n}\n\n/**\n * Sheet-placement transform for one placement: shift the pattern's developed-plane\n * bbox lower-left to the origin, rotate `rotationDeg` (CCW) about it, then translate\n * the rotated shape so its rotated bounding box's lower-left lands at `(x, y)` on the\n * sheet. A point at developed `(px, py)` therefore lands inside the box the packer\n * reserved. The 0° and 90° branches are kept as closed-form integer-stable maps so\n * the bbox-strategy DXF output is byte-identical to the pre-NFP writer; finer angles\n * (180/270 and any nfp-emitted value) take the general rotation path.\n */\nfunction placementTransform(box: Bbox, x: number, y: number, rotationDeg: number): Transform2 {\n if (rotationDeg === 0) {\n return ([px, py]) => [x + (px - box.minX), y + (py - box.minY)];\n }\n if (rotationDeg === 90) {\n // After shifting to origin: (lx, ly) in [0,w]×[0,h]. Rotate 90° CCW: (-ly, lx)\n // → [-h,0]×[0,w]; add h to bring x back to [0,h], then translate to (x, y).\n return ([px, py]) => {\n const lx = px - box.minX;\n const ly = py - box.minY;\n return [x + (box.height - ly), y + lx];\n };\n }\n // General CCW rotation about the bbox lower-left, then re-anchor the rotated bbox's\n // lower-left to (x, y) so the placement matches the box the packer reserved.\n const rad = (rotationDeg * Math.PI) / 180;\n const c = Math.cos(rad);\n const s = Math.sin(rad);\n const corners: [number, number][] = [\n [0, 0],\n [box.width, 0],\n [box.width, box.height],\n [0, box.height],\n ];\n let minRx = Infinity;\n let minRy = Infinity;\n for (const [cx, cy] of corners) {\n const rx = cx * c - cy * s;\n const ry = cx * s + cy * c;\n if (rx < minRx) minRx = rx;\n if (ry < minRy) minRy = ry;\n }\n return ([px, py]) => {\n const lx = px - box.minX;\n const ly = py - box.minY;\n const rx = lx * c - ly * s;\n const ry = lx * s + ly * c;\n return [x + (rx - minRx), y + (ry - minRy)];\n };\n}\n\n/**\n * Emit one fabrication-ready DXF for a single nested sheet: every pattern placed on\n * `result.sheets[sheetIndex]` is translated and rotated onto the sheet (via the same\n * bounding boxes the packer used) and written through {@link multiPatternToDXF}.\n * `patterns` must be the SAME array passed to {@link nest} (placements index into it).\n *\n * @param result - the {@link NestResult} returned by {@link nest}.\n * @param patterns - the same `FlatPattern[]` array that was passed to {@link nest}.\n * @param sheetIndex - index into `result.sheets` of the sheet to emit.\n * @param options - optional DXF options forwarded to {@link multiPatternToDXF}.\n * @returns `Result<string>` - the DXF text for the selected sheet, or an error if the\n * sheet index is invalid, a placement references a missing pattern, or a pattern\n * outline cannot be measured.\n */\nexport function nestToDXF(\n result: NestResult,\n patterns: FlatPattern[],\n sheetIndex: number,\n options?: DxfOptions\n): Result<string> {\n const sheet = result.sheets[sheetIndex];\n if (sheet === undefined) {\n return err(validationError('INVALID_SHEET_INDEX', `no nested sheet at index ${sheetIndex} (have ${result.sheets.length})`));\n }\n const placed: PlacedPattern[] = [];\n for (const p of sheet.placements) {\n const pattern = patterns[p.patternIndex];\n if (pattern === undefined) {\n return err(validationError('INVALID_PATTERN_INDEX', `placement references missing pattern ${p.patternIndex}`));\n }\n const box = patternBbox(pattern);\n if (!box.ok) return box;\n placed.push({ pattern, transform: placementTransform(box.value, p.x, p.y, p.rotationDeg) });\n }\n return multiPatternToDXF(placed, options ?? {});\n}\n"]}
@@ -10,8 +10,8 @@
10
10
  * developed pattern) is approximated by its two endpoints — the same pre-existing
11
11
  * assumption the DXF/SVG writers and the bbox nester make.
12
12
  */
13
- import { type Result } from '@faicad/faijs/brepjs-compat';
14
- import type { Wire } from '@faicad/faijs/brepjs-compat';
13
+ import type { Wire } from './types.js';
14
+ import { type Result } from '@faicad/faijs/api';
15
15
  import type { FlatPattern } from './types.js';
16
16
  /** A 2D point as a plain `[x, y]` coordinate tuple. */
17
17
  export type Pt2 = [number, number];
@@ -1 +1 @@
1
- {"version":3,"file":"polygonFns.d.ts","sourceRoot":"","sources":["../src/polygonFns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EACL,KAAK,MAAM,EAQZ,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,6BAA6B,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,uDAAuD;AACvD,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEnC,uFAAuF;AACvF,MAAM,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC;AAI5B;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,CAc5D;AAED,6EAA6E;AAC7E,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,GAAG,CAAC;IACV,EAAE,EAAE,GAAG,CAAC;IACR,SAAS,EAAE,IAAI,GAAG,MAAM,CAAC;CAC1B;AAED,wFAAwF;AACxF,MAAM,WAAW,oBAAoB;IACnC,oEAAoE;IACpE,OAAO,EAAE,OAAO,CAAC;IACjB,+DAA+D;IAC/D,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,gDAAgD;IAChD,SAAS,EAAE,mBAAmB,EAAE,CAAC;IACjC,qFAAqF;IACrF,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,oFAAoF;IACpF,UAAU,EAAE;QAAE,IAAI,EAAE,GAAG,CAAC;QAAC,EAAE,EAAE,GAAG,CAAA;KAAE,EAAE,CAAC;CACtC;AA2BD;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,WAAW,GAAG,oBAAoB,CAwBjF;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAShD;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAY7E;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CASpG;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAW3E;AAeD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,GAAG,OAAO,CAe7E;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAsB/D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,4BAA4B,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAI/F"}
1
+ {"version":3,"file":"polygonFns.d.ts","sourceRoot":"","sources":["../src/polygonFns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAC,KAAK,MAAM,EAA2E,MAAM,mBAAmB,CAAC;AAExH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,uDAAuD;AACvD,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEnC,uFAAuF;AACvF,MAAM,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC;AAI5B;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,CAc5D;AAED,6EAA6E;AAC7E,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,GAAG,CAAC;IACV,EAAE,EAAE,GAAG,CAAC;IACR,SAAS,EAAE,IAAI,GAAG,MAAM,CAAC;CAC1B;AAED,wFAAwF;AACxF,MAAM,WAAW,oBAAoB;IACnC,oEAAoE;IACpE,OAAO,EAAE,OAAO,CAAC;IACjB,+DAA+D;IAC/D,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,gDAAgD;IAChD,SAAS,EAAE,mBAAmB,EAAE,CAAC;IACjC,qFAAqF;IACrF,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,oFAAoF;IACpF,UAAU,EAAE;QAAE,IAAI,EAAE,GAAG,CAAC;QAAC,EAAE,EAAE,GAAG,CAAA;KAAE,EAAE,CAAC;CACtC;AA2BD;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,WAAW,GAAG,oBAAoB,CAwBjF;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAShD;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAY7E;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CASpG;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAW3E;AAeD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,GAAG,OAAO,CAe7E;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAsB/D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,4BAA4B,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAI/F"}
@@ -10,7 +10,7 @@
10
10
  * developed pattern) is approximated by its two endpoints — the same pre-existing
11
11
  * assumption the DXF/SVG writers and the bbox nester make.
12
12
  */
13
- import { ok, err, isOk, validationError, getEdges, curveStartPoint, curveEndPoint, } from '@faicad/faijs/brepjs-compat';
13
+ import { ok, err, isOk, validationError, getEdges, curveStartPoint, curveEndPoint } from '@faicad/faijs/api';
14
14
  const EPS = 1e-9;
15
15
  /**
16
16
  * Ordered vertex loop of a closed outline wire — one vertex per edge start point, the