@faicad/sheetmetal 0.16.0 → 0.17.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 +32 -0
  50. package/dist/geometryOps.d.ts.map +1 -0
  51. package/dist/geometryOps.js +83 -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
package/dist/formFns.js CHANGED
@@ -1,4 +1,5 @@
1
- import { ok, err, validationError, box, cylinder, line, wireLoop, face, extrude, cut, fuse, rotate, translate, getSolids, isValid, isPlanarWire, vecAdd, vecScale, vecCross, vecNormalize, } from '@faicad/faijs/brepjs-compat';
1
+ import { cylinder, translate } from './geometryOps.js';
2
+ import { ok, err, kernelError, validationError, box, line, wireLoop, face, extrude, cut, fuse, rotate, getSolids, isValid, isPlanarWire, vecAdd, vecScale, vecCross, vecNormalize } from '@faicad/faijs/api';
2
3
  import { normalizeSolid } from './internal.js';
3
4
  import { regionFrames, regionDevExtent, mapToFrame2 } from './cutoutFns.js';
4
5
  const EPS = 1e-6;
@@ -29,7 +30,7 @@ const CIRCLE_SEGMENTS = 48;
29
30
  * @param spec - the form specification.
30
31
  * @returns the updated part with the form feature recorded, or an error.
31
32
  */
32
- export function addForm(part, spec) {
33
+ export async function addForm(part, spec) {
33
34
  const solid = part.solid;
34
35
  if (solid === undefined) {
35
36
  return err(validationError('NO_SOLID', 'addForm: part has no folded solid to form'));
@@ -40,11 +41,11 @@ export function addForm(part, spec) {
40
41
  const { regionId, world, dev } = framesResult.value;
41
42
  const ext = regionDevExtent(part, regionId, world);
42
43
  if (spec.kind === 'louver')
43
- return louverForm(part, solid, spec, regionId, world, dev, ext);
44
- return embossForm(part, solid, spec, regionId, world, dev, ext);
44
+ return await louverForm(part, solid, spec, regionId, world, dev, ext);
45
+ return await embossForm(part, solid, spec, regionId, world, dev, ext);
45
46
  }
46
47
  /** Louver vent: cut the opening, fuse the tilted flap, emit the U-cut + hinge in 2D. */
47
- function louverForm(part, partSolid, spec, regionId, world, dev, ext) {
48
+ async function louverForm(part, partSolid, spec, regionId, world, dev, ext) {
48
49
  if (!Number.isFinite(spec.length) || spec.length <= 0 || !Number.isFinite(spec.width) || spec.width <= 0) {
49
50
  return err(validationError('INVALID_FORM', 'louver length/width must be positive'));
50
51
  }
@@ -71,51 +72,51 @@ function louverForm(part, partSolid, spec, regionId, world, dev, ext) {
71
72
  [x1, y1],
72
73
  [x0, y1],
73
74
  ];
74
- const opening = buildThroughTool(openingLocal, world, part.thickness);
75
- if (!opening.ok)
76
- return opening;
77
- const cutResult = cut(partSolid, opening.value);
78
- if (!cutResult.ok)
79
- return cutResult;
80
- let solid = normalizeSolid(cutResult.value);
81
- // Fuse the tilted flap, hinged on the −v edge (y0), rising over the opening to
82
- // `height` on the formed face. Built in region-local space then placed via the
83
- // world frame; overlaps the hinge edge so it fuses into one body.
84
- const flap = buildLouverFlap(world, x0, x1, y0, spec.width, spec.height, part.thickness, sign);
85
- if (!flap.ok)
86
- return flap;
87
- const fused = fuse(solid, flap.value);
88
- if (!fused.ok)
89
- return fused;
90
- solid = normalizeSolid(fused.value);
91
- if (!isValid(solid) || getSolids(solid).length > 1) {
92
- return err(validationError('FORM_INVALID_SOLID', `addForm: louver on region '${spec.region}' did not form a single valid body`));
75
+ let solid;
76
+ try {
77
+ const opening = await buildThroughTool(openingLocal, world, part.thickness);
78
+ if (!opening.ok)
79
+ return opening;
80
+ solid = normalizeSolid((await cut(partSolid, opening.value)));
81
+ // Fuse the tilted flap, hinged on the −v edge (y0), rising over the opening to
82
+ // `height` on the formed face. Built in region-local space then placed via the
83
+ // world frame; overlaps the hinge edge so it fuses into one body.
84
+ const flap = await buildLouverFlap(world, x0, x1, y0, spec.width, spec.height, part.thickness, sign);
85
+ if (!flap.ok)
86
+ return flap;
87
+ solid = normalizeSolid((await fuse(solid, flap.value)));
88
+ if (!isValid(solid) || getSolids(solid).length > 1) {
89
+ return err(validationError('FORM_INVALID_SOLID', `addForm: louver on region '${spec.region}' did not form a single valid body`));
90
+ }
91
+ // 2D: the louver footprint as the OPEN three-side U-cut the fabricator actually
92
+ // cuts (all but the hinge at y0), plus the hinge segment as a separate fold line.
93
+ // The cut path walks the three non-hinge sides — [x1,y0]→[x1,y1]→[x0,y1]→[x0,y0] —
94
+ // leaving the y0 edge uncut so the flap stays hinged; emitting a closed loop would
95
+ // tell the fabricator to cut all four sides and drop the flap out entirely.
96
+ // Developed outline/area unchanged (forming is net-material-neutral).
97
+ const cutPath = [
98
+ mapToFrame2([x1, y0], dev),
99
+ mapToFrame2([x1, y1], dev),
100
+ mapToFrame2([x0, y1], dev),
101
+ mapToFrame2([x0, y0], dev),
102
+ ];
103
+ const hingeA = mapToFrame2([x0, y0], dev);
104
+ const hingeB = mapToFrame2([x1, y0], dev);
105
+ const feature = {
106
+ spec,
107
+ region: regionId,
108
+ cuts: [cutPath],
109
+ markers: [],
110
+ hinge: [hingeA, hingeB],
111
+ };
112
+ return ok({ ...part, solid, forms: [...(part.forms ?? []), feature] });
113
+ }
114
+ catch (e) {
115
+ return err(kernelError('FORM_FAILED', `louver: ${e instanceof Error ? e.message : String(e)}`));
93
116
  }
94
- // 2D: the louver footprint as the OPEN three-side U-cut the fabricator actually
95
- // cuts (all but the hinge at y0), plus the hinge segment as a separate fold line.
96
- // The cut path walks the three non-hinge sides — [x1,y0]→[x1,y1]→[x0,y1]→[x0,y0] —
97
- // leaving the y0 edge uncut so the flap stays hinged; emitting a closed loop would
98
- // tell the fabricator to cut all four sides and drop the flap out entirely.
99
- // Developed outline/area unchanged (forming is net-material-neutral).
100
- const cutPath = [
101
- mapToFrame2([x1, y0], dev),
102
- mapToFrame2([x1, y1], dev),
103
- mapToFrame2([x0, y1], dev),
104
- mapToFrame2([x0, y0], dev),
105
- ];
106
- const hingeA = mapToFrame2([x0, y0], dev);
107
- const hingeB = mapToFrame2([x1, y0], dev);
108
- const feature = {
109
- spec,
110
- region: regionId,
111
- cuts: [cutPath],
112
- markers: [],
113
- hinge: [hingeA, hingeB],
114
- };
115
- return ok({ ...part, solid, forms: [...(part.forms ?? []), feature] });
116
117
  }
117
118
  /** Emboss/dimple: fuse a raised cylinder or cut a shallow recess; emit a 2D footprint. */
118
- function embossForm(part, partSolid, spec, regionId, world, dev, ext) {
119
+ async function embossForm(part, partSolid, spec, regionId, world, dev, ext) {
119
120
  if (!Number.isFinite(spec.diameter) || spec.diameter <= 0) {
120
121
  return err(validationError('INVALID_FORM', `emboss diameter must be positive, got ${spec.diameter}`));
121
122
  }
@@ -136,21 +137,25 @@ function embossForm(part, partSolid, spec, regionId, world, dev, ext) {
136
137
  // Raised cylinder: base sunk a hair into the sheet for a robust fuse, extending
137
138
  // +n by `height`.
138
139
  const base = vecAdd(faceCentre, vecScale(world.n, -HAIR));
139
- const bump = cylinder(r, spec.height + HAIR, { at: base, axis: world.n });
140
- const fused = fuse(partSolid, bump);
141
- if (!fused.ok)
142
- return fused;
143
- solid = normalizeSolid(fused.value);
140
+ try {
141
+ const bump = await cylinder(r, spec.height + HAIR, { at: base, axis: world.n });
142
+ solid = normalizeSolid((await fuse(partSolid, bump)));
143
+ }
144
+ catch (e) {
145
+ return err(kernelError('FORM_FAILED', `emboss: ${e instanceof Error ? e.message : String(e)}`));
146
+ }
144
147
  }
145
148
  else {
146
149
  // Recess: cut a shallow cylinder down from the formed face by `height` (+HAIR so
147
150
  // the tool pokes cleanly through the top surface).
148
151
  const top = vecAdd(faceCentre, vecScale(world.n, HAIR));
149
- const tool = cylinder(r, spec.height + HAIR, { at: top, axis: vecScale(world.n, -1) });
150
- const cutResult = cut(partSolid, tool);
151
- if (!cutResult.ok)
152
- return cutResult;
153
- solid = normalizeSolid(cutResult.value);
152
+ try {
153
+ const tool = await cylinder(r, spec.height + HAIR, { at: top, axis: vecScale(world.n, -1) });
154
+ solid = normalizeSolid((await cut(partSolid, tool)));
155
+ }
156
+ catch (e) {
157
+ return err(kernelError('FORM_FAILED', `dimple: ${e instanceof Error ? e.message : String(e)}`));
158
+ }
154
159
  }
155
160
  if (!isValid(solid) || getSolids(solid).length > 1) {
156
161
  return err(validationError('FORM_INVALID_SOLID', `addForm: ${spec.form} on region '${spec.region}' did not form a single valid body`));
@@ -170,7 +175,7 @@ function embossForm(part, partSolid, spec, regionId, world, dev, ext) {
170
175
  * @param opts - louver geometry (region, x, y, length, width, height, direction?).
171
176
  * @returns the updated part with the louver feature recorded, or an error.
172
177
  */
173
- export function louver(part, opts) {
178
+ export async function louver(part, opts) {
174
179
  return addForm(part, { kind: 'louver', ...opts });
175
180
  }
176
181
  /**
@@ -179,7 +184,7 @@ export function louver(part, opts) {
179
184
  * @param opts - emboss geometry (region, x, y, diameter, height, direction?).
180
185
  * @returns the updated part with the emboss feature recorded, or an error.
181
186
  */
182
- export function emboss(part, opts) {
187
+ export async function emboss(part, opts) {
183
188
  return addForm(part, {
184
189
  kind: 'emboss',
185
190
  region: opts.region,
@@ -191,7 +196,7 @@ export function emboss(part, opts) {
191
196
  });
192
197
  }
193
198
  /** Extrude a region-local loop fully through the sheet (a through-cut tool). */
194
- function buildThroughTool(local, f, thickness) {
199
+ async function buildThroughTool(local, f, thickness) {
195
200
  const base = vecAdd(f.origin, vecScale(f.n, -HAIR));
196
201
  const worldPts = local.map(([x, y]) => vecAdd(vecAdd(base, vecScale(f.u, x)), vecScale(f.v, y)));
197
202
  const edges = [];
@@ -213,7 +218,12 @@ function buildThroughTool(local, f, thickness) {
213
218
  if (!profile.ok)
214
219
  return profile;
215
220
  const depth = thickness + 2 * HAIR;
216
- return extrude(profile.value, [f.n[0] * depth, f.n[1] * depth, f.n[2] * depth]);
221
+ try {
222
+ return ok((await extrude(profile.value, [f.n[0] * depth, f.n[1] * depth, f.n[2] * depth])));
223
+ }
224
+ catch (e) {
225
+ return err(kernelError('FORM_TOOL_FAILED', `extrude tool: ${e instanceof Error ? e.message : String(e)}`));
226
+ }
217
227
  }
218
228
  /**
219
229
  * Tilted louver flap, hinged on the `y0` (−v) edge of the opening: a thin plate
@@ -222,31 +232,36 @@ function buildThroughTool(local, f, thickness) {
222
232
  * a canonical local frame (hinge along +X at the origin, depth +Y, thickness +Z),
223
233
  * tilted about +X, then mapped onto the world frame's u/v/n.
224
234
  */
225
- function buildLouverFlap(f, x0, x1, y0, width, height, thickness, sign) {
235
+ async function buildLouverFlap(f, x0, x1, y0, width, height, thickness, sign) {
226
236
  const plateThk = Math.max(thickness, EPS);
227
237
  const len = x1 - x0;
228
238
  // Tilt angle so the far edge (depth `width`) rises by `height`.
229
239
  const tiltDeg = (Math.atan2(height, width) * 180) / Math.PI;
230
240
  // Canonical flap: hinge along +X over [0,len], depth +Y over [0,width], thickness
231
241
  // sunk a hair below 0 so it overlaps the sheet at the hinge for a robust fuse.
232
- let flap = box(len, width, plateThk);
233
- flap = translate(flap, [0, 0, -plateThk + HAIR]);
234
- flap = rotate(flap, -sign * tiltDeg, { at: [0, 0, 0], axis: [1, 0, 0] });
235
- // Place: canonical +X→world u, +Y→world v, +Z→world n, origin at the hinge corner
236
- // on the formed face.
237
- const u = vecNormalize(f.u);
238
- const n = vecNormalize(f.n);
239
- const v = vecNormalize(vecCross(n, u));
240
- const hingeCorner = vecAdd(vecAdd(vecAdd(f.origin, vecScale(f.u, x0)), vecScale(f.v, y0)), vecScale(f.n, thickness));
241
- const placed = mapLocalToWorld(flap, u, v, n, hingeCorner);
242
- return ok(placed);
242
+ try {
243
+ let flap = (await box({ width: len, depth: width, height: plateThk }));
244
+ flap = (await translate(flap, [0, 0, -plateThk + HAIR]));
245
+ flap = (await rotate(flap, -sign * tiltDeg, { at: [0, 0, 0], axis: [1, 0, 0] }));
246
+ // Place: canonical +X→world u, +Y→world v, +Z→world n, origin at the hinge corner
247
+ // on the formed face.
248
+ const u = vecNormalize(f.u);
249
+ const n = vecNormalize(f.n);
250
+ const v = vecNormalize(vecCross(n, u));
251
+ const hingeCorner = vecAdd(vecAdd(vecAdd(f.origin, vecScale(f.u, x0)), vecScale(f.v, y0)), vecScale(f.n, thickness));
252
+ const placed = await mapLocalToWorld(flap, u, v, n, hingeCorner);
253
+ return ok(placed);
254
+ }
255
+ catch (e) {
256
+ return err(kernelError('FORM_FAILED', `louver flap: ${e instanceof Error ? e.message : String(e)}`));
257
+ }
243
258
  }
244
259
  /** Map a solid built in a canonical XYZ frame onto world axes (uT, vT, nT) at origin. */
245
- function mapLocalToWorld(s, uT, vT, nT, origin) {
260
+ async function mapLocalToWorld(s, uT, vT, nT, origin) {
246
261
  const m = [uT[0], vT[0], nT[0], uT[1], vT[1], nT[1], uT[2], vT[2], nT[2]];
247
262
  const aa = matrixToAxisAngle(m);
248
- const rotated = aa.angleDeg === 0 ? s : rotate(s, aa.angleDeg, { at: [0, 0, 0], axis: aa.axis });
249
- return translate(rotated, origin);
263
+ const rotated = aa.angleDeg === 0 ? s : await rotate(s, aa.angleDeg, { at: [0, 0, 0], axis: aa.axis });
264
+ return (await translate(rotated, origin));
250
265
  }
251
266
  /** Axis-angle of a 3×3 row-major rotation matrix (columns = target basis). */
252
267
  function matrixToAxisAngle(m) {
@@ -1 +1 @@
1
- {"version":3,"file":"formFns.js","sourceRoot":"","sources":["../src/formFns.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 cylinder,\n line,\n wireLoop,\n face,\n extrude,\n cut,\n fuse,\n rotate,\n translate,\n getSolids,\n isValid,\n isPlanarWire,\n vecAdd,\n vecScale,\n vecCross,\n vecNormalize,\n} from '@faicad/faijs/brepjs-compat';\nimport type { FormSpec, FormFeature, SheetMetalPart } from './types.js';\nimport { normalizeSolid } from './internal.js';\nimport type { FlatFrame } from './authorFns.js';\nimport type { Frame2 } from './unfoldFns.js';\nimport { regionFrames, regionDevExtent, mapToFrame2 } from './cutoutFns.js';\n\nconst EPS = 1e-6;\nconst HAIR = 0.05;\nconst CIRCLE_SEGMENTS = 48;\n\ntype Pt2 = [number, number];\n\n/**\n * Add a form feature (louver or emboss/dimple) to a named flat region.\n *\n * 3D FIDELITY (simplified, by design — the public CSG-only API makes true forming\n * impractical):\n * - LOUVER: the vent opening (`length × width`) is cut fully through the sheet, and\n * the formed flap is represented as a thin box hinged on one side and tilted up to\n * `height`, fused so it stays connected to the body at the hinge. True forming\n * keeps the flap continuous with the parent and bends it; the cut+tilted-flap\n * representation captures the vent geometry while keeping a single valid solid.\n * - EMBOSS: a short cylinder fused onto the formed face (raised by `height`).\n * DIMPLE: a shallow cylindrical recess cut into the formed face (recessed by\n * `height`, never through). A true spherical/conical form is approximated by the\n * flat-topped round so the result stays a valid single body.\n *\n * FLAT PATTERN (the fabrication-critical representation): the louver emits its\n * footprint as an OPEN three-side cut path (the fabricator cuts three of its four\n * sides — all but the hinge) plus the hinge fold line; the emboss emits its footprint\n * circle as a marker. Forming removes no net material, so the developed outline and\n * area are unchanged. Guards a valid, single-bodied solid.\n *\n * @param part - the sheet metal part to form.\n * @param spec - the form specification.\n * @returns the updated part with the form feature recorded, or an error.\n */\nexport function addForm(part: SheetMetalPart, spec: FormSpec): Result<SheetMetalPart> {\n const solid = part.solid;\n if (solid === undefined) {\n return err(validationError('NO_SOLID', 'addForm: part has no folded solid to form'));\n }\n\n const framesResult = regionFrames(part, spec.region);\n if (!framesResult.ok) return framesResult;\n const { regionId, world, dev } = framesResult.value;\n const ext = regionDevExtent(part, regionId, world);\n\n if (spec.kind === 'louver') return louverForm(part, solid, spec, regionId, world, dev, ext);\n return embossForm(part, solid, spec, regionId, world, dev, ext);\n}\n\n/** Louver vent: cut the opening, fuse the tilted flap, emit the U-cut + hinge in 2D. */\nfunction louverForm(\n part: SheetMetalPart,\n partSolid: Solid,\n spec: Extract<FormSpec, { kind: 'louver' }>,\n regionId: string,\n world: FlatFrame,\n dev: Frame2,\n ext: { uMax: number; vMax: number }\n): Result<SheetMetalPart> {\n if (!Number.isFinite(spec.length) || spec.length <= 0 || !Number.isFinite(spec.width) || spec.width <= 0) {\n return err(validationError('INVALID_FORM', 'louver length/width must be positive'));\n }\n if (!Number.isFinite(spec.height) || spec.height <= 0) {\n return err(validationError('INVALID_FORM', `louver height must be positive, got ${spec.height}`));\n }\n // Louver footprint in region-local coords, centred at (x, y): `length` along u,\n // `width` along v. The hinge is the −v edge of the footprint.\n const hl = spec.length / 2;\n const hw = spec.width / 2;\n const x0 = spec.x - hl;\n const x1 = spec.x + hl;\n const y0 = spec.y - hw;\n const y1 = spec.y + hw;\n if (x0 < -EPS || x1 > ext.uMax + EPS || y0 < -EPS || y1 > ext.vMax + EPS) {\n return err(\n validationError('FORM_OUT_OF_BOUNDS', `louver at (${spec.x}, ${spec.y}) lies outside region [0,${ext.uMax}]×[0,${ext.vMax}]`)\n );\n }\n\n const dir = spec.direction ?? 'up';\n const sign = dir === 'up' ? 1 : -1;\n\n // Cut the vent opening fully through the sheet.\n const openingLocal: Pt2[] = [\n [x0, y0],\n [x1, y0],\n [x1, y1],\n [x0, y1],\n ];\n const opening = buildThroughTool(openingLocal, world, part.thickness);\n if (!opening.ok) return opening;\n const cutResult = cut(partSolid, opening.value);\n if (!cutResult.ok) return cutResult;\n let solid = normalizeSolid(cutResult.value);\n\n // Fuse the tilted flap, hinged on the −v edge (y0), rising over the opening to\n // `height` on the formed face. Built in region-local space then placed via the\n // world frame; overlaps the hinge edge so it fuses into one body.\n const flap = buildLouverFlap(world, x0, x1, y0, spec.width, spec.height, part.thickness, sign);\n if (!flap.ok) return flap;\n const fused = fuse(solid, flap.value);\n if (!fused.ok) return fused;\n solid = normalizeSolid(fused.value);\n\n if (!isValid(solid) || getSolids(solid).length > 1) {\n return err(\n validationError('FORM_INVALID_SOLID', `addForm: louver on region '${spec.region}' did not form a single valid body`)\n );\n }\n\n // 2D: the louver footprint as the OPEN three-side U-cut the fabricator actually\n // cuts (all but the hinge at y0), plus the hinge segment as a separate fold line.\n // The cut path walks the three non-hinge sides — [x1,y0]→[x1,y1]→[x0,y1]→[x0,y0] —\n // leaving the y0 edge uncut so the flap stays hinged; emitting a closed loop would\n // tell the fabricator to cut all four sides and drop the flap out entirely.\n // Developed outline/area unchanged (forming is net-material-neutral).\n const cutPath: Pt2[] = [\n mapToFrame2([x1, y0], dev),\n mapToFrame2([x1, y1], dev),\n mapToFrame2([x0, y1], dev),\n mapToFrame2([x0, y0], dev),\n ];\n const hingeA = mapToFrame2([x0, y0], dev);\n const hingeB = mapToFrame2([x1, y0], dev);\n const feature: FormFeature = {\n spec,\n region: regionId,\n cuts: [cutPath],\n markers: [],\n hinge: [hingeA, hingeB],\n };\n return ok({ ...part, solid, forms: [...(part.forms ?? []), feature] });\n}\n\n/** Emboss/dimple: fuse a raised cylinder or cut a shallow recess; emit a 2D footprint. */\nfunction embossForm(\n part: SheetMetalPart,\n partSolid: Solid,\n spec: Extract<FormSpec, { kind: 'emboss' }>,\n regionId: string,\n world: FlatFrame,\n dev: Frame2,\n ext: { uMax: number; vMax: number }\n): Result<SheetMetalPart> {\n if (!Number.isFinite(spec.diameter) || spec.diameter <= 0) {\n return err(validationError('INVALID_FORM', `emboss diameter must be positive, got ${spec.diameter}`));\n }\n if (!Number.isFinite(spec.height) || spec.height <= 0) {\n return err(validationError('INVALID_FORM', `emboss height must be positive, got ${spec.height}`));\n }\n const r = spec.diameter / 2;\n if (spec.x - r < -EPS || spec.x + r > ext.uMax + EPS || spec.y - r < -EPS || spec.y + r > ext.vMax + EPS) {\n return err(\n validationError('FORM_OUT_OF_BOUNDS', `emboss at (${spec.x}, ${spec.y}) lies outside region [0,${ext.uMax}]×[0,${ext.vMax}]`)\n );\n }\n if (spec.form === 'dimple' && spec.height >= part.thickness - EPS) {\n return err(\n validationError('INVALID_FORM', `dimple depth ${spec.height} must be less than thickness ${part.thickness}`)\n );\n }\n\n // Centre of the form on the formed (+n) face, in world space.\n const faceCentre = vecAdd(\n vecAdd(vecAdd(world.origin, vecScale(world.u, spec.x)), vecScale(world.v, spec.y)),\n vecScale(world.n, part.thickness)\n );\n\n let solid: Solid;\n if (spec.form === 'emboss') {\n // Raised cylinder: base sunk a hair into the sheet for a robust fuse, extending\n // +n by `height`.\n const base = vecAdd(faceCentre, vecScale(world.n, -HAIR));\n const bump = cylinder(r, spec.height + HAIR, { at: base, axis: world.n });\n const fused = fuse(partSolid, bump);\n if (!fused.ok) return fused;\n solid = normalizeSolid(fused.value);\n } else {\n // Recess: cut a shallow cylinder down from the formed face by `height` (+HAIR so\n // the tool pokes cleanly through the top surface).\n const top = vecAdd(faceCentre, vecScale(world.n, HAIR));\n const tool = cylinder(r, spec.height + HAIR, { at: top, axis: vecScale(world.n, -1) });\n const cutResult = cut(partSolid, tool);\n if (!cutResult.ok) return cutResult;\n solid = normalizeSolid(cutResult.value);\n }\n\n if (!isValid(solid) || getSolids(solid).length > 1) {\n return err(\n validationError('FORM_INVALID_SOLID', `addForm: ${spec.form} on region '${spec.region}' did not form a single valid body`)\n );\n }\n\n const marker = circleLocal([spec.x, spec.y], r).map((p) => mapToFrame2(p, dev));\n const feature: FormFeature = {\n spec,\n region: regionId,\n cuts: [],\n markers: [marker],\n };\n return ok({ ...part, solid, forms: [...(part.forms ?? []), feature] });\n}\n\n/**\n * A louver vent on a region; see {@link addForm}.\n * @param part - the sheet metal part to form.\n * @param opts - louver geometry (region, x, y, length, width, height, direction?).\n * @returns the updated part with the louver feature recorded, or an error.\n */\nexport function louver(\n part: SheetMetalPart,\n opts: {\n region: string;\n x: number;\n y: number;\n length: number;\n width: number;\n height: number;\n direction?: 'up' | 'down';\n }\n): Result<SheetMetalPart> {\n return addForm(part, { kind: 'louver', ...opts });\n}\n\n/**\n * An emboss (raised) or dimple (recessed) round form on a region; see {@link addForm}.\n * @param part - the sheet metal part to form.\n * @param opts - emboss geometry (region, x, y, diameter, height, direction?).\n * @returns the updated part with the emboss feature recorded, or an error.\n */\nexport function emboss(\n part: SheetMetalPart,\n opts: { region: string; x: number; y: number; diameter: number; height: number; kind: 'dimple' | 'emboss' }\n): Result<SheetMetalPart> {\n return addForm(part, {\n kind: 'emboss',\n region: opts.region,\n x: opts.x,\n y: opts.y,\n diameter: opts.diameter,\n height: opts.height,\n form: opts.kind,\n });\n}\n\n/** Extrude a region-local loop fully through the sheet (a through-cut tool). */\nfunction buildThroughTool(local: Pt2[], f: FlatFrame, thickness: number): Result<Solid> {\n const base = vecAdd(f.origin, vecScale(f.n, -HAIR));\n const worldPts: Vec3[] = local.map(([x, y]) =>\n vecAdd(vecAdd(base, vecScale(f.u, x)), vecScale(f.v, y))\n );\n const edges = [];\n for (let i = 0; i < worldPts.length; i += 1) {\n const a = worldPts[i];\n const b = worldPts[(i + 1) % worldPts.length];\n if (a === undefined || b === undefined) {\n return err(validationError('FORM_TOOL_FAILED', 'failed to index form loop points'));\n }\n edges.push(line(a, b));\n }\n const wire = wireLoop(edges);\n if (!wire.ok) return wire;\n if (!isPlanarWire(wire.value)) {\n return err(validationError('FORM_TOOL_FAILED', 'form profile wire is not planar'));\n }\n const profile = face(wire.value);\n if (!profile.ok) return profile;\n const depth = thickness + 2 * HAIR;\n return extrude(profile.value, [f.n[0] * depth, f.n[1] * depth, f.n[2] * depth]);\n}\n\n/**\n * Tilted louver flap, hinged on the `y0` (−v) edge of the opening: a thin plate\n * spanning `[x0,x1]` along u and `width` along v, tilted up about the hinge so its\n * far edge rises by `height` on the formed face (`sign` = +1 up / −1 down). Built in\n * a canonical local frame (hinge along +X at the origin, depth +Y, thickness +Z),\n * tilted about +X, then mapped onto the world frame's u/v/n.\n */\nfunction buildLouverFlap(\n f: FlatFrame,\n x0: number,\n x1: number,\n y0: number,\n width: number,\n height: number,\n thickness: number,\n sign: number\n): Result<Solid> {\n const plateThk = Math.max(thickness, EPS);\n const len = x1 - x0;\n // Tilt angle so the far edge (depth `width`) rises by `height`.\n const tiltDeg = (Math.atan2(height, width) * 180) / Math.PI;\n\n // Canonical flap: hinge along +X over [0,len], depth +Y over [0,width], thickness\n // sunk a hair below 0 so it overlaps the sheet at the hinge for a robust fuse.\n let flap: Solid = box(len, width, plateThk);\n flap = translate(flap, [0, 0, -plateThk + HAIR]);\n flap = rotate(flap, -sign * tiltDeg, { at: [0, 0, 0], axis: [1, 0, 0] });\n\n // Place: canonical +X→world u, +Y→world v, +Z→world n, origin at the hinge corner\n // on the formed face.\n const u = vecNormalize(f.u);\n const n = vecNormalize(f.n);\n const v = vecNormalize(vecCross(n, u));\n const hingeCorner = vecAdd(\n vecAdd(vecAdd(f.origin, vecScale(f.u, x0)), vecScale(f.v, y0)),\n vecScale(f.n, thickness)\n );\n const placed = mapLocalToWorld(flap, u, v, n, hingeCorner);\n return ok(placed);\n}\n\n/** Map a solid built in a canonical XYZ frame onto world axes (uT, vT, nT) at origin. */\nfunction mapLocalToWorld(s: Solid, uT: Vec3, vT: Vec3, nT: Vec3, origin: Vec3): Solid {\n const m: number[] = [uT[0], vT[0], nT[0], uT[1], vT[1], nT[1], uT[2], vT[2], nT[2]];\n const aa = matrixToAxisAngle(m);\n const rotated = aa.angleDeg === 0 ? s : rotate(s, aa.angleDeg, { at: [0, 0, 0], axis: aa.axis });\n return translate(rotated, origin);\n}\n\n/** Axis-angle of a 3×3 row-major rotation matrix (columns = target basis). */\nfunction matrixToAxisAngle(m: number[]): { axis: Vec3; angleDeg: number } {\n const m00 = m[0] ?? 0;\n const m01 = m[1] ?? 0;\n const m02 = m[2] ?? 0;\n const m10 = m[3] ?? 0;\n const m11 = m[4] ?? 0;\n const m12 = m[5] ?? 0;\n const m20 = m[6] ?? 0;\n const m21 = m[7] ?? 0;\n const m22 = m[8] ?? 0;\n const trace = m00 + m11 + m22;\n const cos = Math.max(-1, Math.min(1, (trace - 1) / 2));\n const angle = Math.acos(cos);\n const angleDeg = (angle * 180) / Math.PI;\n if (angle < 1e-9) return { axis: [0, 0, 1], angleDeg: 0 };\n if (Math.PI - angle < 1e-6) {\n const xx = (m00 + 1) / 2;\n const yy = (m11 + 1) / 2;\n const zz = (m22 + 1) / 2;\n let axis: Vec3;\n if (xx >= yy && xx >= zz) {\n const x = Math.sqrt(Math.max(xx, 0));\n axis = [x, (m01 + m10) / (4 * x), (m02 + m20) / (4 * x)];\n } else if (yy >= zz) {\n const y = Math.sqrt(Math.max(yy, 0));\n axis = [(m01 + m10) / (4 * y), y, (m12 + m21) / (4 * y)];\n } else {\n const z = Math.sqrt(Math.max(zz, 0));\n axis = [(m02 + m20) / (4 * z), (m12 + m21) / (4 * z), z];\n }\n return { axis: vecNormalize(axis), angleDeg: 180 };\n }\n const denom = 2 * Math.sin(angle);\n const axis: Vec3 = [(m21 - m12) / denom, (m02 - m20) / denom, (m10 - m01) / denom];\n return { axis: vecNormalize(axis), angleDeg };\n}\n\n/** Region-local circle loop (CCW) centred at `c` of radius `r`. */\nfunction circleLocal(c: Pt2, r: number): Pt2[] {\n const pts: Pt2[] = [];\n for (let i = 0; i < CIRCLE_SEGMENTS; i += 1) {\n const a = (2 * Math.PI * i) / CIRCLE_SEGMENTS;\n pts.push([c[0] + r * Math.cos(a), c[1] + r * Math.sin(a)]);\n }\n return pts;\n}\n"]}
1
+ {"version":3,"file":"formFns.js","sourceRoot":"","sources":["../src/formFns.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Solid } from './types.js';\nimport { cylinder, translate } from './geometryOps.js';\nimport {type Result, type Vec3, ok, err, kernelError, validationError, box, line, wireLoop, face, extrude, cut, fuse, rotate, getSolids, isValid, isPlanarWire, vecAdd, vecScale, vecCross, vecNormalize} from '@faicad/faijs/api';\nimport type { FormSpec, FormFeature, SheetMetalPart } from './types.js';\nimport { normalizeSolid } from './internal.js';\nimport type { FlatFrame } from './authorFns.js';\nimport type { Frame2 } from './unfoldFns.js';\nimport { regionFrames, regionDevExtent, mapToFrame2 } from './cutoutFns.js';\n\nconst EPS = 1e-6;\nconst HAIR = 0.05;\nconst CIRCLE_SEGMENTS = 48;\n\ntype Pt2 = [number, number];\n\n/**\n * Add a form feature (louver or emboss/dimple) to a named flat region.\n *\n * 3D FIDELITY (simplified, by design — the public CSG-only API makes true forming\n * impractical):\n * - LOUVER: the vent opening (`length × width`) is cut fully through the sheet, and\n * the formed flap is represented as a thin box hinged on one side and tilted up to\n * `height`, fused so it stays connected to the body at the hinge. True forming\n * keeps the flap continuous with the parent and bends it; the cut+tilted-flap\n * representation captures the vent geometry while keeping a single valid solid.\n * - EMBOSS: a short cylinder fused onto the formed face (raised by `height`).\n * DIMPLE: a shallow cylindrical recess cut into the formed face (recessed by\n * `height`, never through). A true spherical/conical form is approximated by the\n * flat-topped round so the result stays a valid single body.\n *\n * FLAT PATTERN (the fabrication-critical representation): the louver emits its\n * footprint as an OPEN three-side cut path (the fabricator cuts three of its four\n * sides — all but the hinge) plus the hinge fold line; the emboss emits its footprint\n * circle as a marker. Forming removes no net material, so the developed outline and\n * area are unchanged. Guards a valid, single-bodied solid.\n *\n * @param part - the sheet metal part to form.\n * @param spec - the form specification.\n * @returns the updated part with the form feature recorded, or an error.\n */\nexport async function addForm(part: SheetMetalPart, spec: FormSpec): Promise<Result<SheetMetalPart>> {\n const solid = part.solid;\n if (solid === undefined) {\n return err(validationError('NO_SOLID', 'addForm: part has no folded solid to form'));\n }\n\n const framesResult = regionFrames(part, spec.region);\n if (!framesResult.ok) return framesResult;\n const { regionId, world, dev } = framesResult.value;\n const ext = regionDevExtent(part, regionId, world);\n\n if (spec.kind === 'louver') return await louverForm(part, solid, spec, regionId, world, dev, ext);\n return await embossForm(part, solid, spec, regionId, world, dev, ext);\n}\n\n/** Louver vent: cut the opening, fuse the tilted flap, emit the U-cut + hinge in 2D. */\nasync function louverForm(\n part: SheetMetalPart,\n partSolid: Solid,\n spec: Extract<FormSpec, { kind: 'louver' }>,\n regionId: string,\n world: FlatFrame,\n dev: Frame2,\n ext: { uMax: number; vMax: number }\n): Promise<Result<SheetMetalPart>> {\n if (!Number.isFinite(spec.length) || spec.length <= 0 || !Number.isFinite(spec.width) || spec.width <= 0) {\n return err(validationError('INVALID_FORM', 'louver length/width must be positive'));\n }\n if (!Number.isFinite(spec.height) || spec.height <= 0) {\n return err(validationError('INVALID_FORM', `louver height must be positive, got ${spec.height}`));\n }\n // Louver footprint in region-local coords, centred at (x, y): `length` along u,\n // `width` along v. The hinge is the −v edge of the footprint.\n const hl = spec.length / 2;\n const hw = spec.width / 2;\n const x0 = spec.x - hl;\n const x1 = spec.x + hl;\n const y0 = spec.y - hw;\n const y1 = spec.y + hw;\n if (x0 < -EPS || x1 > ext.uMax + EPS || y0 < -EPS || y1 > ext.vMax + EPS) {\n return err(\n validationError('FORM_OUT_OF_BOUNDS', `louver at (${spec.x}, ${spec.y}) lies outside region [0,${ext.uMax}]×[0,${ext.vMax}]`)\n );\n }\n\n const dir = spec.direction ?? 'up';\n const sign = dir === 'up' ? 1 : -1;\n\n // Cut the vent opening fully through the sheet.\n const openingLocal: Pt2[] = [\n [x0, y0],\n [x1, y0],\n [x1, y1],\n [x0, y1],\n ];\n let solid: Solid;\n try {\n const opening = await buildThroughTool(openingLocal, world, part.thickness);\n if (!opening.ok) return opening;\n solid = normalizeSolid((await cut(partSolid, opening.value)) as Solid);\n\n // Fuse the tilted flap, hinged on the −v edge (y0), rising over the opening to\n // `height` on the formed face. Built in region-local space then placed via the\n // world frame; overlaps the hinge edge so it fuses into one body.\n const flap = await buildLouverFlap(world, x0, x1, y0, spec.width, spec.height, part.thickness, sign);\n if (!flap.ok) return flap;\n solid = normalizeSolid((await fuse(solid, flap.value)) as Solid);\n\n if (!isValid(solid) || getSolids(solid).length > 1) {\n return err(\n validationError('FORM_INVALID_SOLID', `addForm: louver on region '${spec.region}' did not form a single valid body`)\n );\n }\n\n // 2D: the louver footprint as the OPEN three-side U-cut the fabricator actually\n // cuts (all but the hinge at y0), plus the hinge segment as a separate fold line.\n // The cut path walks the three non-hinge sides — [x1,y0]→[x1,y1]→[x0,y1]→[x0,y0] —\n // leaving the y0 edge uncut so the flap stays hinged; emitting a closed loop would\n // tell the fabricator to cut all four sides and drop the flap out entirely.\n // Developed outline/area unchanged (forming is net-material-neutral).\n const cutPath: Pt2[] = [\n mapToFrame2([x1, y0], dev),\n mapToFrame2([x1, y1], dev),\n mapToFrame2([x0, y1], dev),\n mapToFrame2([x0, y0], dev),\n ];\n const hingeA = mapToFrame2([x0, y0], dev);\n const hingeB = mapToFrame2([x1, y0], dev);\n const feature: FormFeature = {\n spec,\n region: regionId,\n cuts: [cutPath],\n markers: [],\n hinge: [hingeA, hingeB],\n };\n return ok({ ...part, solid, forms: [...(part.forms ?? []), feature] });\n } catch (e) {\n return err(kernelError('FORM_FAILED', `louver: ${e instanceof Error ? e.message : String(e)}`));\n }\n}\n\n/** Emboss/dimple: fuse a raised cylinder or cut a shallow recess; emit a 2D footprint. */\nasync function embossForm(\n part: SheetMetalPart,\n partSolid: Solid,\n spec: Extract<FormSpec, { kind: 'emboss' }>,\n regionId: string,\n world: FlatFrame,\n dev: Frame2,\n ext: { uMax: number; vMax: number }\n): Promise<Result<SheetMetalPart>> {\n if (!Number.isFinite(spec.diameter) || spec.diameter <= 0) {\n return err(validationError('INVALID_FORM', `emboss diameter must be positive, got ${spec.diameter}`));\n }\n if (!Number.isFinite(spec.height) || spec.height <= 0) {\n return err(validationError('INVALID_FORM', `emboss height must be positive, got ${spec.height}`));\n }\n const r = spec.diameter / 2;\n if (spec.x - r < -EPS || spec.x + r > ext.uMax + EPS || spec.y - r < -EPS || spec.y + r > ext.vMax + EPS) {\n return err(\n validationError('FORM_OUT_OF_BOUNDS', `emboss at (${spec.x}, ${spec.y}) lies outside region [0,${ext.uMax}]×[0,${ext.vMax}]`)\n );\n }\n if (spec.form === 'dimple' && spec.height >= part.thickness - EPS) {\n return err(\n validationError('INVALID_FORM', `dimple depth ${spec.height} must be less than thickness ${part.thickness}`)\n );\n }\n\n // Centre of the form on the formed (+n) face, in world space.\n const faceCentre = vecAdd(\n vecAdd(vecAdd(world.origin, vecScale(world.u, spec.x)), vecScale(world.v, spec.y)),\n vecScale(world.n, part.thickness)\n );\n\n let solid: Solid;\n if (spec.form === 'emboss') {\n // Raised cylinder: base sunk a hair into the sheet for a robust fuse, extending\n // +n by `height`.\n const base = vecAdd(faceCentre, vecScale(world.n, -HAIR));\n try {\n const bump = await cylinder(r, spec.height + HAIR, { at: base, axis: world.n });\n solid = normalizeSolid((await fuse(partSolid, bump)) as Solid);\n } catch (e) {\n return err(kernelError('FORM_FAILED', `emboss: ${e instanceof Error ? e.message : String(e)}`));\n }\n } else {\n // Recess: cut a shallow cylinder down from the formed face by `height` (+HAIR so\n // the tool pokes cleanly through the top surface).\n const top = vecAdd(faceCentre, vecScale(world.n, HAIR));\n try {\n const tool = await cylinder(r, spec.height + HAIR, { at: top, axis: vecScale(world.n, -1) });\n solid = normalizeSolid((await cut(partSolid, tool)) as Solid);\n } catch (e) {\n return err(kernelError('FORM_FAILED', `dimple: ${e instanceof Error ? e.message : String(e)}`));\n }\n }\n\n if (!isValid(solid) || getSolids(solid).length > 1) {\n return err(\n validationError('FORM_INVALID_SOLID', `addForm: ${spec.form} on region '${spec.region}' did not form a single valid body`)\n );\n }\n\n const marker = circleLocal([spec.x, spec.y], r).map((p) => mapToFrame2(p, dev));\n const feature: FormFeature = {\n spec,\n region: regionId,\n cuts: [],\n markers: [marker],\n };\n return ok({ ...part, solid, forms: [...(part.forms ?? []), feature] });\n}\n\n/**\n * A louver vent on a region; see {@link addForm}.\n * @param part - the sheet metal part to form.\n * @param opts - louver geometry (region, x, y, length, width, height, direction?).\n * @returns the updated part with the louver feature recorded, or an error.\n */\nexport async function louver(\n part: SheetMetalPart,\n opts: {\n region: string;\n x: number;\n y: number;\n length: number;\n width: number;\n height: number;\n direction?: 'up' | 'down';\n }\n): Promise<Result<SheetMetalPart>> {\n return addForm(part, { kind: 'louver', ...opts });\n}\n\n/**\n * An emboss (raised) or dimple (recessed) round form on a region; see {@link addForm}.\n * @param part - the sheet metal part to form.\n * @param opts - emboss geometry (region, x, y, diameter, height, direction?).\n * @returns the updated part with the emboss feature recorded, or an error.\n */\nexport async function emboss(\n part: SheetMetalPart,\n opts: { region: string; x: number; y: number; diameter: number; height: number; kind: 'dimple' | 'emboss' }\n): Promise<Result<SheetMetalPart>> {\n return addForm(part, {\n kind: 'emboss',\n region: opts.region,\n x: opts.x,\n y: opts.y,\n diameter: opts.diameter,\n height: opts.height,\n form: opts.kind,\n });\n}\n\n/** Extrude a region-local loop fully through the sheet (a through-cut tool). */\nasync function buildThroughTool(local: Pt2[], f: FlatFrame, thickness: number): Promise<Result<Solid>> {\n const base = vecAdd(f.origin, vecScale(f.n, -HAIR));\n const worldPts: Vec3[] = local.map(([x, y]) =>\n vecAdd(vecAdd(base, vecScale(f.u, x)), vecScale(f.v, y))\n );\n const edges = [];\n for (let i = 0; i < worldPts.length; i += 1) {\n const a = worldPts[i];\n const b = worldPts[(i + 1) % worldPts.length];\n if (a === undefined || b === undefined) {\n return err(validationError('FORM_TOOL_FAILED', 'failed to index form loop points'));\n }\n edges.push(line(a, b));\n }\n const wire = wireLoop(edges);\n if (!wire.ok) return wire;\n if (!isPlanarWire(wire.value)) {\n return err(validationError('FORM_TOOL_FAILED', 'form profile wire is not planar'));\n }\n const profile = face(wire.value);\n if (!profile.ok) return profile;\n const depth = thickness + 2 * HAIR;\n try {\n return ok((await extrude(profile.value, [f.n[0] * depth, f.n[1] * depth, f.n[2] * depth])) as Solid);\n } catch (e) {\n return err(kernelError('FORM_TOOL_FAILED', `extrude tool: ${e instanceof Error ? e.message : String(e)}`));\n }\n}\n\n/**\n * Tilted louver flap, hinged on the `y0` (−v) edge of the opening: a thin plate\n * spanning `[x0,x1]` along u and `width` along v, tilted up about the hinge so its\n * far edge rises by `height` on the formed face (`sign` = +1 up / −1 down). Built in\n * a canonical local frame (hinge along +X at the origin, depth +Y, thickness +Z),\n * tilted about +X, then mapped onto the world frame's u/v/n.\n */\nasync function buildLouverFlap(\n f: FlatFrame,\n x0: number,\n x1: number,\n y0: number,\n width: number,\n height: number,\n thickness: number,\n sign: number\n): Promise<Result<Solid>> {\n const plateThk = Math.max(thickness, EPS);\n const len = x1 - x0;\n // Tilt angle so the far edge (depth `width`) rises by `height`.\n const tiltDeg = (Math.atan2(height, width) * 180) / Math.PI;\n\n // Canonical flap: hinge along +X over [0,len], depth +Y over [0,width], thickness\n // sunk a hair below 0 so it overlaps the sheet at the hinge for a robust fuse.\n try {\n let flap: Solid = (await box({ width: len, depth: width, height: plateThk })) as Solid;\n flap = (await translate(flap, [0, 0, -plateThk + HAIR])) as Solid;\n flap = (await rotate(flap, -sign * tiltDeg, { at: [0, 0, 0], axis: [1, 0, 0] })) as Solid;\n\n // Place: canonical +X→world u, +Y→world v, +Z→world n, origin at the hinge corner\n // on the formed face.\n const u = vecNormalize(f.u);\n const n = vecNormalize(f.n);\n const v = vecNormalize(vecCross(n, u));\n const hingeCorner = vecAdd(\n vecAdd(vecAdd(f.origin, vecScale(f.u, x0)), vecScale(f.v, y0)),\n vecScale(f.n, thickness)\n );\n const placed = await mapLocalToWorld(flap, u, v, n, hingeCorner);\n return ok(placed);\n } catch (e) {\n return err(kernelError('FORM_FAILED', `louver flap: ${e instanceof Error ? e.message : String(e)}`));\n }\n}\n\n/** Map a solid built in a canonical XYZ frame onto world axes (uT, vT, nT) at origin. */\nasync function mapLocalToWorld(s: Solid, uT: Vec3, vT: Vec3, nT: Vec3, origin: Vec3): Promise<Solid> {\n const m: number[] = [uT[0], vT[0], nT[0], uT[1], vT[1], nT[1], uT[2], vT[2], nT[2]];\n const aa = matrixToAxisAngle(m);\n const rotated = aa.angleDeg === 0 ? s : await rotate(s, aa.angleDeg, { at: [0, 0, 0], axis: aa.axis });\n return (await translate(rotated, origin)) as Solid;\n}\n\n/** Axis-angle of a 3×3 row-major rotation matrix (columns = target basis). */\nfunction matrixToAxisAngle(m: number[]): { axis: Vec3; angleDeg: number } {\n const m00 = m[0] ?? 0;\n const m01 = m[1] ?? 0;\n const m02 = m[2] ?? 0;\n const m10 = m[3] ?? 0;\n const m11 = m[4] ?? 0;\n const m12 = m[5] ?? 0;\n const m20 = m[6] ?? 0;\n const m21 = m[7] ?? 0;\n const m22 = m[8] ?? 0;\n const trace = m00 + m11 + m22;\n const cos = Math.max(-1, Math.min(1, (trace - 1) / 2));\n const angle = Math.acos(cos);\n const angleDeg = (angle * 180) / Math.PI;\n if (angle < 1e-9) return { axis: [0, 0, 1], angleDeg: 0 };\n if (Math.PI - angle < 1e-6) {\n const xx = (m00 + 1) / 2;\n const yy = (m11 + 1) / 2;\n const zz = (m22 + 1) / 2;\n let axis: Vec3;\n if (xx >= yy && xx >= zz) {\n const x = Math.sqrt(Math.max(xx, 0));\n axis = [x, (m01 + m10) / (4 * x), (m02 + m20) / (4 * x)];\n } else if (yy >= zz) {\n const y = Math.sqrt(Math.max(yy, 0));\n axis = [(m01 + m10) / (4 * y), y, (m12 + m21) / (4 * y)];\n } else {\n const z = Math.sqrt(Math.max(zz, 0));\n axis = [(m02 + m20) / (4 * z), (m12 + m21) / (4 * z), z];\n }\n return { axis: vecNormalize(axis), angleDeg: 180 };\n }\n const denom = 2 * Math.sin(angle);\n const axis: Vec3 = [(m21 - m12) / denom, (m02 - m20) / denom, (m10 - m01) / denom];\n return { axis: vecNormalize(axis), angleDeg };\n}\n\n/** Region-local circle loop (CCW) centred at `c` of radius `r`. */\nfunction circleLocal(c: Pt2, r: number): Pt2[] {\n const pts: Pt2[] = [];\n for (let i = 0; i < CIRCLE_SEGMENTS; i += 1) {\n const a = (2 * Math.PI * i) / CIRCLE_SEGMENTS;\n pts.push([c[0] + r * Math.cos(a), c[1] + r * Math.sin(a)]);\n }\n return pts;\n}\n"]}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * faijs 建模 op 适配层(core-decouple wrapup §2.2)
3
+ *
4
+ * sheetmetal 原 brepjs 调用形态 → faijs op 组合。faijs 建模 op 均为
5
+ * 异步 op(`Promise<Shape>`);本层只做形态适配,不复制任何 brepjs
6
+ * 兼容层(裁决 9)。说明:
7
+ *
8
+ * - `rotate` — faijs 原生 op(绕任意轴 + `at`,签名兼容原调用),直透。
9
+ * - `translate` — faijs TS 面收 `{ offset }`(transform.ts defineOp),
10
+ * 原调用传 Vec3 数组,在此平铺包装。
11
+ * - `cylinder` — faijs `cylinder(radius, height, { at })` 默认 +Z 轴,
12
+ * 原调用带任意 `axis`:组合 = 生成 → `applyMatrix`(Z→axis) → 平移 `at`。
13
+ * - `getBounds` — 由 faijs `bounds3D`(brep 引擎包围盒,`cad.boundingBox`
14
+ * 只读 mesh 载荷、brep 产物空载荷无效)组合成大写字段 `Bounds3D`。
15
+ */
16
+ import type { Shape, Vec3 } from '@faicad/faijs/api';
17
+ import type { Bounds3D } from './types.js';
18
+ /** faijs 风格 rotate:绕过 `at` 的 `axis` 旋转 `angleDeg`°(faijs rotate op 原生签名,直透)。 */
19
+ export declare function rotate(shape: Shape, angleDeg: number, opts: {
20
+ at: Vec3;
21
+ axis: Vec3;
22
+ }): Promise<Shape>;
23
+ /** faijs 风格 translate:平移 `offset`(faijs TS 面收 `{ offset }`,此处平铺 Vec3)。 */
24
+ export declare function translate(shape: Shape, offset: Vec3): Promise<Shape>;
25
+ /** faijs 风格 cylinder:底面圆心 `at`、轴向 `axis` 的圆柱(组合:Z 轴圆柱 → 取向 → 定位)。 */
26
+ export declare function cylinder(radius: number, height: number, opts: {
27
+ at: Vec3;
28
+ axis: Vec3;
29
+ }): Promise<Shape>;
30
+ /** faijs 风格 getBounds:brep 引擎包围盒组合成 `Bounds3D`(大写字段形态)。 */
31
+ export declare function getBounds(shape: Shape): Bounds3D;
32
+ //# sourceMappingURL=geometryOps.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geometryOps.d.ts","sourceRoot":"","sources":["../src/geometryOps.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAC,KAAK,EAAE,IAAI,EAAC,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAsC3C,gFAAgF;AAChF,wBAAgB,MAAM,CACpB,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAE,GAC7B,OAAO,CAAC,KAAK,CAAC,CAEhB;AAED,0EAA0E;AAC1E,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAEpE;AAED,qEAAqE;AACrE,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,IAAI,EAAE;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAE,GAC7B,OAAO,CAAC,KAAK,CAAC,CAOhB;AAED,2DAA2D;AAC3D,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,QAAQ,CAUhD"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * faijs 建模 op 适配层(core-decouple wrapup §2.2)
3
+ *
4
+ * sheetmetal 原 brepjs 调用形态 → faijs op 组合。faijs 建模 op 均为
5
+ * 异步 op(`Promise<Shape>`);本层只做形态适配,不复制任何 brepjs
6
+ * 兼容层(裁决 9)。说明:
7
+ *
8
+ * - `rotate` — faijs 原生 op(绕任意轴 + `at`,签名兼容原调用),直透。
9
+ * - `translate` — faijs TS 面收 `{ offset }`(transform.ts defineOp),
10
+ * 原调用传 Vec3 数组,在此平铺包装。
11
+ * - `cylinder` — faijs `cylinder(radius, height, { at })` 默认 +Z 轴,
12
+ * 原调用带任意 `axis`:组合 = 生成 → `applyMatrix`(Z→axis) → 平移 `at`。
13
+ * - `getBounds` — 由 faijs `bounds3D`(brep 引擎包围盒,`cad.boundingBox`
14
+ * 只读 mesh 载荷、brep 产物空载荷无效)组合成大写字段 `Bounds3D`。
15
+ */
16
+ import { rotate as rotateOp, translate as translateOp, cylinder as cylinderOp, applyMatrix, bounds3D } from '@faicad/faijs/api';
17
+ /** 把 +Z 轴旋到 `axis`(单位化)的 3×3 线性矩阵 + 零平移(Rodrigues;数学同
18
+ * core brepHelpers.rotationZTo)。返回 `{ linear, translation }` 形态——faijs
19
+ * `applyMatrix` 的 brep 实现(api/brep-mirror/topologyFns.parseMatrixInput)
20
+ * 接受该对象形态;扁平的 3×4 数组会落入数组解构分支导致线性矩阵全为
21
+ * undefined(2026-09-25 实测:applyMatrix 后体积为 0 的根因)。 */
22
+ function rotationZTo(axis) {
23
+ const [x, y, z] = axis;
24
+ const len = Math.hypot(x, y, z);
25
+ if (len < 1e-12)
26
+ throw new Error('[sheetmetal/geometryOps] rotationZTo: zero-length axis');
27
+ const nx = x / len;
28
+ const ny = y / len;
29
+ const nz = z / len;
30
+ const ZERO = [0, 0, 0];
31
+ if (Math.abs(nz - 1) < 1e-9)
32
+ return { linear: [1, 0, 0, 0, 1, 0, 0, 0, 1], translation: ZERO };
33
+ if (Math.abs(nz + 1) < 1e-9) {
34
+ // Z → −Z:绕 X 转 180°。
35
+ return { linear: [1, 0, 0, 0, -1, 0, 0, 0, -1], translation: ZERO };
36
+ }
37
+ const cross = [-ny, nx, 0]; // [0,0,1] × axis
38
+ const crossLen = Math.hypot(...cross);
39
+ const kx = cross[0] / crossLen;
40
+ const ky = cross[1] / crossLen;
41
+ const kz = cross[2] / crossLen;
42
+ const cosA = nz;
43
+ const sinA = Math.hypot(nx, ny);
44
+ const t = 1 - cosA;
45
+ return {
46
+ linear: [
47
+ t * kx * kx + cosA, t * kx * ky - kz * sinA, t * kx * kz + ky * sinA,
48
+ t * kx * ky + kz * sinA, t * ky * ky + cosA, t * ky * kz - kx * sinA,
49
+ t * kx * kz - ky * sinA, t * ky * kz + kx * sinA, t * kz * kz + cosA,
50
+ ],
51
+ translation: ZERO,
52
+ };
53
+ }
54
+ /** faijs 风格 rotate:绕过 `at` 的 `axis` 旋转 `angleDeg`°(faijs rotate op 原生签名,直透)。 */
55
+ export function rotate(shape, angleDeg, opts) {
56
+ return rotateOp(shape, angleDeg, opts);
57
+ }
58
+ /** faijs 风格 translate:平移 `offset`(faijs TS 面收 `{ offset }`,此处平铺 Vec3)。 */
59
+ export function translate(shape, offset) {
60
+ return translateOp(shape, { offset: [offset[0], offset[1], offset[2]] });
61
+ }
62
+ /** faijs 风格 cylinder:底面圆心 `at`、轴向 `axis` 的圆柱(组合:Z 轴圆柱 → 取向 → 定位)。 */
63
+ export async function cylinder(radius, height, opts) {
64
+ // faijs cylinder TS 面为单参对象形态(defineOp 单参装箱:radius/height/at)。
65
+ const c = await cylinderOp({ radius, height, at: [0, 0, 0] });
66
+ const [ax, ay, az] = opts.axis;
67
+ const axisIsZ = ax === 0 && ay === 0 && az > 0;
68
+ const oriented = axisIsZ ? c : await applyMatrix(c, rotationZTo(opts.axis));
69
+ return translateOp(oriented, { offset: [opts.at[0], opts.at[1], opts.at[2]] });
70
+ }
71
+ /** faijs 风格 getBounds:brep 引擎包围盒组合成 `Bounds3D`(大写字段形态)。 */
72
+ export function getBounds(shape) {
73
+ const b = bounds3D(shape);
74
+ return {
75
+ xMin: b.min[0],
76
+ xMax: b.max[0],
77
+ yMin: b.min[1],
78
+ yMax: b.max[1],
79
+ zMin: b.min[2],
80
+ zMax: b.max[2],
81
+ };
82
+ }
83
+ //# sourceMappingURL=geometryOps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geometryOps.js","sourceRoot":"","sources":["../src/geometryOps.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * faijs 建模 op 适配层(core-decouple wrapup §2.2)\n *\n * sheetmetal 原 brepjs 调用形态 → faijs op 组合。faijs 建模 op 均为\n * 异步 op(`Promise<Shape>`);本层只做形态适配,不复制任何 brepjs\n * 兼容层(裁决 9)。说明:\n *\n * - `rotate` — faijs 原生 op(绕任意轴 + `at`,签名兼容原调用),直透。\n * - `translate` — faijs TS 面收 `{ offset }`(transform.ts defineOp),\n * 原调用传 Vec3 数组,在此平铺包装。\n * - `cylinder` — faijs `cylinder(radius, height, { at })` 默认 +Z 轴,\n * 原调用带任意 `axis`:组合 = 生成 → `applyMatrix`(Z→axis) → 平移 `at`。\n * - `getBounds` — 由 faijs `bounds3D`(brep 引擎包围盒,`cad.boundingBox`\n * 只读 mesh 载荷、brep 产物空载荷无效)组合成大写字段 `Bounds3D`。\n */\n\nimport {rotate as rotateOp, translate as translateOp, cylinder as cylinderOp, applyMatrix, bounds3D} from '@faicad/faijs/api';\nimport type {Shape, Vec3} from '@faicad/faijs/api';\nimport type { Bounds3D } from './types.js';\n\n/** 把 +Z 轴旋到 `axis`(单位化)的 3×3 线性矩阵 + 零平移(Rodrigues;数学同\n * core brepHelpers.rotationZTo)。返回 `{ linear, translation }` 形态——faijs\n * `applyMatrix` 的 brep 实现(api/brep-mirror/topologyFns.parseMatrixInput)\n * 接受该对象形态;扁平的 3×4 数组会落入数组解构分支导致线性矩阵全为\n * undefined(2026-09-25 实测:applyMatrix 后体积为 0 的根因)。 */\nfunction rotationZTo(axis: readonly [number, number, number]): { linear: number[]; translation: readonly [number, number, number] } {\n const [x, y, z] = axis;\n const len = Math.hypot(x, y, z);\n if (len < 1e-12) throw new Error('[sheetmetal/geometryOps] rotationZTo: zero-length axis');\n const nx = x / len;\n const ny = y / len;\n const nz = z / len;\n const ZERO: readonly [number, number, number] = [0, 0, 0];\n if (Math.abs(nz - 1) < 1e-9) return { linear: [1, 0, 0, 0, 1, 0, 0, 0, 1], translation: ZERO };\n if (Math.abs(nz + 1) < 1e-9) {\n // Z → −Z:绕 X 转 180°。\n return { linear: [1, 0, 0, 0, -1, 0, 0, 0, -1], translation: ZERO };\n }\n const cross = [-ny, nx, 0]; // [0,0,1] × axis\n const crossLen = Math.hypot(...cross);\n const kx = cross[0] / crossLen;\n const ky = cross[1] / crossLen;\n const kz = cross[2] / crossLen;\n const cosA = nz;\n const sinA = Math.hypot(nx, ny);\n const t = 1 - cosA;\n return {\n linear: [\n t * kx * kx + cosA, t * kx * ky - kz * sinA, t * kx * kz + ky * sinA,\n t * kx * ky + kz * sinA, t * ky * ky + cosA, t * ky * kz - kx * sinA,\n t * kx * kz - ky * sinA, t * ky * kz + kx * sinA, t * kz * kz + cosA,\n ],\n translation: ZERO,\n };\n}\n\n/** faijs 风格 rotate:绕过 `at` 的 `axis` 旋转 `angleDeg`°(faijs rotate op 原生签名,直透)。 */\nexport function rotate(\n shape: Shape,\n angleDeg: number,\n opts: { at: Vec3; axis: Vec3 },\n): Promise<Shape> {\n return rotateOp(shape as never, angleDeg, opts as never);\n}\n\n/** faijs 风格 translate:平移 `offset`(faijs TS 面收 `{ offset }`,此处平铺 Vec3)。 */\nexport function translate(shape: Shape, offset: Vec3): Promise<Shape> {\n return translateOp(shape, { offset: [offset[0], offset[1], offset[2]] });\n}\n\n/** faijs 风格 cylinder:底面圆心 `at`、轴向 `axis` 的圆柱(组合:Z 轴圆柱 → 取向 → 定位)。 */\nexport async function cylinder(\n radius: number,\n height: number,\n opts: { at: Vec3; axis: Vec3 },\n): Promise<Shape> {\n // faijs cylinder TS 面为单参对象形态(defineOp 单参装箱:radius/height/at)。\n const c = await cylinderOp({ radius, height, at: [0, 0, 0] });\n const [ax, ay, az] = opts.axis;\n const axisIsZ = ax === 0 && ay === 0 && az > 0;\n const oriented = axisIsZ ? c : await applyMatrix(c as never, rotationZTo(opts.axis) as never);\n return translateOp(oriented, { offset: [opts.at[0], opts.at[1], opts.at[2]] });\n}\n\n/** faijs 风格 getBounds:brep 引擎包围盒组合成 `Bounds3D`(大写字段形态)。 */\nexport function getBounds(shape: Shape): Bounds3D {\n const b = bounds3D(shape);\n return {\n xMin: b.min[0],\n xMax: b.max[0],\n yMin: b.min[1],\n yMax: b.max[1],\n zMin: b.min[2],\n zMax: b.max[2],\n };\n}\n"]}
package/dist/hemFns.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 { HemSpec, SheetMetalPart } from './types.js';
3
3
  /**
4
4
  * Author a hem: fold a region edge back ~180°+ onto its parent flat, then run a
@@ -13,5 +13,5 @@ import type { HemSpec, SheetMetalPart } from './types.js';
13
13
  * @param spec - the hem specification.
14
14
  * @returns the updated part with the hem feature recorded, or an error.
15
15
  */
16
- export declare function hem(part: SheetMetalPart, spec: HemSpec): Result<SheetMetalPart>;
16
+ export declare function hem(part: SheetMetalPart, spec: HemSpec): Promise<Result<SheetMetalPart>>;
17
17
  //# sourceMappingURL=hemFns.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"hemFns.d.ts","sourceRoot":"","sources":["../src/hemFns.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAwC,MAAM,6BAA6B,CAAC;AAChG,OAAO,KAAK,EAIV,OAAO,EACP,cAAc,EACf,MAAM,YAAY,CAAC;AAmCpB;;;;;;;;;;;;GAYG;AACH,wBAAgB,GAAG,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC,cAAc,CAAC,CAqH/E"}
1
+ {"version":3,"file":"hemFns.d.ts","sourceRoot":"","sources":["../src/hemFns.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,KAAK,MAAM,EAA2B,MAAM,mBAAmB,CAAC;AACxE,OAAO,KAAK,EAIV,OAAO,EACP,cAAc,EACf,MAAM,YAAY,CAAC;AAmCpB;;;;;;;;;;;;GAYG;AACH,wBAAsB,GAAG,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAqH9F"}
package/dist/hemFns.js CHANGED
@@ -1,4 +1,4 @@
1
- import { ok, err, validationError } from '@faicad/faijs/brepjs-compat';
1
+ import { ok, err, validationError } from '@faicad/faijs/api';
2
2
  import { normalizeSolid } from './internal.js';
3
3
  import { developedLength } from './allowanceFns.js';
4
4
  import { worldFrames } from './authorFns.js';
@@ -28,7 +28,7 @@ const MAX_SUBARC_DEG = 120;
28
28
  * @param spec - the hem specification.
29
29
  * @returns the updated part with the hem feature recorded, or an error.
30
30
  */
31
- export function hem(part, spec) {
31
+ export async function hem(part, spec) {
32
32
  if (part.solid === undefined) {
33
33
  return err(validationError('NO_SOLID', `part has no solid to attach hem '${hemId(spec)}'`));
34
34
  }
@@ -87,7 +87,7 @@ export function hem(part, spec) {
87
87
  const subDeg = subAngles[i];
88
88
  if (subDeg === undefined)
89
89
  continue;
90
- const built = buildArcBend(solid, frame, span, thickness, {
90
+ const built = await buildArcBend(solid, frame, span, thickness, {
91
91
  kind: 'arc',
92
92
  radius: plan.radius,
93
93
  angleDeg: subDeg,
@@ -112,7 +112,7 @@ export function hem(part, spec) {
112
112
  devTotal += dev;
113
113
  }
114
114
  if (plan.returnLength > 0) {
115
- const built = buildLineLeg(solid, frame, span, thickness, plan.returnLength);
115
+ const built = await buildLineLeg(solid, frame, span, thickness, plan.returnLength);
116
116
  if (!built.ok)
117
117
  return built;
118
118
  solid = built.value.solid;