brepjs 8.3.0 → 8.7.4

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 (119) hide show
  1. package/dist/2d/lib/Curve2D.d.ts.map +1 -1
  2. package/dist/2d.cjs +2 -2
  3. package/dist/2d.js +13 -13
  4. package/dist/{Blueprint-a3ukJMG4.cjs → Blueprint-BcbOBF-9.cjs} +17 -102
  5. package/dist/{Blueprint-CdVaHDSx.js → Blueprint-Cmh8lKc4.js} +35 -120
  6. package/dist/{boolean2D-pvPIs21j.cjs → boolean2D-CqacqjME.cjs} +24 -25
  7. package/dist/{boolean2D-DzA0STqC.js → boolean2D-D94Axs3i.js} +23 -24
  8. package/dist/{booleanFns-BcQUqjUu.js → booleanFns-DdjtpcM6.js} +306 -12
  9. package/dist/{booleanFns-Cd414V3l.cjs → booleanFns-NtKxkiXn.cjs} +299 -5
  10. package/dist/brepjs.cjs +1782 -68
  11. package/dist/brepjs.js +2030 -317
  12. package/dist/core/errors.d.ts +25 -0
  13. package/dist/core/errors.d.ts.map +1 -1
  14. package/dist/core.cjs +4 -4
  15. package/dist/core.js +4 -4
  16. package/dist/{cornerFinder-DvPiz-VR.js → cornerFinder-BBOYfsXl.js} +1 -1
  17. package/dist/{cornerFinder-BdKtobgb.cjs → cornerFinder-Bqy8Lw2p.cjs} +1 -1
  18. package/dist/{curveFns-CyHyk29c.js → curveFns-B85Glnfo.js} +19 -17
  19. package/dist/{curveFns-B5EQsSwv.cjs → curveFns-BXCbASW-.cjs} +6 -4
  20. package/dist/{drawFns-CAAE4Z88.js → drawFns-B-gJ2WUc.js} +52 -23
  21. package/dist/{drawFns-Mr2pghU8.cjs → drawFns-CAmFEqd1.cjs} +68 -39
  22. package/dist/{errors-wGhcJMpB.js → errors-Coh_5_19.js} +35 -1
  23. package/dist/{errors-DK1VAdP4.cjs → errors-eRQu29oc.cjs} +35 -1
  24. package/dist/{faceFns-ub3CugDN.js → faceFns-CltrEfOo.js} +109 -12
  25. package/dist/{faceFns-D1Sqnlu6.cjs → faceFns-DcndPHWm.cjs} +103 -6
  26. package/dist/{helpers-CP2KrBZl.cjs → helpers-CC21GeAr.cjs} +8 -9
  27. package/dist/{helpers-r_e-u1JM.js → helpers-SksQIreB.js} +16 -17
  28. package/dist/index.d.ts +20 -3
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/io/dxfImportFns.d.ts +17 -0
  31. package/dist/io/dxfImportFns.d.ts.map +1 -0
  32. package/dist/io/objImportFns.d.ts +19 -0
  33. package/dist/io/objImportFns.d.ts.map +1 -0
  34. package/dist/io/threemfImportFns.d.ts +19 -0
  35. package/dist/io/threemfImportFns.d.ts.map +1 -0
  36. package/dist/io.cjs +5 -5
  37. package/dist/io.js +5 -5
  38. package/dist/kernel/hullOps.d.ts +23 -0
  39. package/dist/kernel/hullOps.d.ts.map +1 -0
  40. package/dist/kernel/occtAdapter.d.ts +11 -0
  41. package/dist/kernel/occtAdapter.d.ts.map +1 -1
  42. package/dist/kernel/solverAdapter.d.ts +39 -0
  43. package/dist/kernel/solverAdapter.d.ts.map +1 -0
  44. package/dist/kernel/types.d.ts +11 -0
  45. package/dist/kernel/types.d.ts.map +1 -1
  46. package/dist/{loft-PMRx9iMG.cjs → loft-BcyyvWCj.cjs} +28 -28
  47. package/dist/{loft-BHn7GKm8.js → loft-CJMPx1NQ.js} +16 -16
  48. package/dist/{measurement-BfhEneUl.js → measurement-ByOztLxb.js} +3 -3
  49. package/dist/{measurement-B06hNs89.cjs → measurement-DU3ry-0Q.cjs} +3 -3
  50. package/dist/measurement.cjs +1 -1
  51. package/dist/measurement.js +1 -1
  52. package/dist/{meshFns-BEvGVcym.js → meshFns-D2gLyLFt.js} +3 -3
  53. package/dist/{meshFns-CJV_k_EQ.cjs → meshFns-DawUwI3W.cjs} +3 -3
  54. package/dist/{occtBoundary-CoXB2xvx.js → occtBoundary-CWzWqBCm.js} +436 -7
  55. package/dist/{occtBoundary-BFAaUtA7.cjs → occtBoundary-DH2VO-rq.cjs} +431 -2
  56. package/dist/operations/assemblyFns.d.ts +1 -0
  57. package/dist/operations/assemblyFns.d.ts.map +1 -1
  58. package/dist/operations/guidedSweepFns.d.ts +25 -0
  59. package/dist/operations/guidedSweepFns.d.ts.map +1 -0
  60. package/dist/operations/mateFns.d.ts +50 -0
  61. package/dist/operations/mateFns.d.ts.map +1 -0
  62. package/dist/operations/multiSweepFns.d.ts +32 -0
  63. package/dist/operations/multiSweepFns.d.ts.map +1 -0
  64. package/dist/operations/roofFns.d.ts +16 -0
  65. package/dist/operations/roofFns.d.ts.map +1 -0
  66. package/dist/operations/straightSkeleton.d.ts +28 -0
  67. package/dist/operations/straightSkeleton.d.ts.map +1 -0
  68. package/dist/{operations-CYGNxn5D.cjs → operations-CdELWxgv.cjs} +7 -7
  69. package/dist/{operations-B314mytX.js → operations-DiXo_4t9.js} +15 -15
  70. package/dist/operations.cjs +2 -2
  71. package/dist/operations.js +13 -13
  72. package/dist/query.cjs +5 -5
  73. package/dist/query.js +7 -7
  74. package/dist/result.cjs +1 -1
  75. package/dist/result.js +1 -1
  76. package/dist/{shapeFns-Z_ScEjmn.cjs → shapeFns-3RYtsUVY.cjs} +54 -21
  77. package/dist/{shapeFns-CWd_ASDV.js → shapeFns-4ioRrhih.js} +52 -19
  78. package/dist/{shapeTypes-UqVCIO_T.cjs → shapeTypes-CMjrTv36.cjs} +1 -1
  79. package/dist/{shapeTypes-BU2LKv2S.js → shapeTypes-D0vfRxWb.js} +13 -13
  80. package/dist/sketching.cjs +2 -2
  81. package/dist/sketching.js +2 -2
  82. package/dist/{curveBuilders-CN72XaIQ.js → surfaceBuilders-B7Jxob8g.js} +106 -13
  83. package/dist/{curveBuilders-Du03_Yyf.cjs → surfaceBuilders-Xx9DRRxs.cjs} +96 -3
  84. package/dist/text/textBlueprints.d.ts +38 -0
  85. package/dist/text/textBlueprints.d.ts.map +1 -1
  86. package/dist/topology/api.d.ts +5 -0
  87. package/dist/topology/api.d.ts.map +1 -1
  88. package/dist/topology/booleanFns.d.ts +10 -1
  89. package/dist/topology/booleanFns.d.ts.map +1 -1
  90. package/dist/topology/colorFns.d.ts +38 -0
  91. package/dist/topology/colorFns.d.ts.map +1 -0
  92. package/dist/topology/curveFns.d.ts +1 -1
  93. package/dist/topology/curveFns.d.ts.map +1 -1
  94. package/dist/topology/faceTagFns.d.ts +44 -0
  95. package/dist/topology/faceTagFns.d.ts.map +1 -0
  96. package/dist/topology/hullFns.d.ts +16 -0
  97. package/dist/topology/hullFns.d.ts.map +1 -0
  98. package/dist/topology/minkowskiFns.d.ts +20 -0
  99. package/dist/topology/minkowskiFns.d.ts.map +1 -0
  100. package/dist/topology/modifierFns.d.ts.map +1 -1
  101. package/dist/topology/polyhedronFns.d.ts +8 -0
  102. package/dist/topology/polyhedronFns.d.ts.map +1 -0
  103. package/dist/topology/shapeFns.d.ts +4 -0
  104. package/dist/topology/shapeFns.d.ts.map +1 -1
  105. package/dist/topology/surfaceBuilders.d.ts +7 -0
  106. package/dist/topology/surfaceBuilders.d.ts.map +1 -1
  107. package/dist/topology/surfaceFns.d.ts +38 -0
  108. package/dist/topology/surfaceFns.d.ts.map +1 -0
  109. package/dist/{topology-A7-jUtHB.cjs → topology-D-nGjCzV.cjs} +19 -20
  110. package/dist/{topology-BupialMm.js → topology-DRP9zreU.js} +8 -9
  111. package/dist/topology.cjs +13 -14
  112. package/dist/topology.js +51 -52
  113. package/dist/{vectors-BhfKwL9J.js → vectors-CZV4ZrTz.js} +2 -2
  114. package/dist/{vectors-t1XG4LpL.cjs → vectors-DwFeX0Ja.cjs} +2 -2
  115. package/dist/vectors.cjs +2 -2
  116. package/dist/vectors.js +2 -2
  117. package/package.json +8 -7
  118. package/dist/cast-C107o5ow.cjs +0 -102
  119. package/dist/cast-D0OhP1nV.js +0 -103
package/dist/brepjs.cjs CHANGED
@@ -1,30 +1,29 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const occtBoundary = require("./occtBoundary-BFAaUtA7.cjs");
4
- const errors = require("./errors-DK1VAdP4.cjs");
5
- const shapeTypes = require("./shapeTypes-UqVCIO_T.cjs");
3
+ const occtBoundary = require("./occtBoundary-DH2VO-rq.cjs");
4
+ const errors = require("./errors-eRQu29oc.cjs");
5
+ const shapeTypes = require("./shapeTypes-CMjrTv36.cjs");
6
6
  const vecOps = require("./vecOps-CjRL1jau.cjs");
7
- const Blueprint = require("./Blueprint-a3ukJMG4.cjs");
8
- const curveFns = require("./curveFns-B5EQsSwv.cjs");
9
- const loft$2 = require("./loft-PMRx9iMG.cjs");
10
- const operations = require("./operations-CYGNxn5D.cjs");
11
- const boolean2D = require("./boolean2D-pvPIs21j.cjs");
7
+ const Blueprint = require("./Blueprint-BcbOBF-9.cjs");
8
+ const curveFns = require("./curveFns-BXCbASW-.cjs");
9
+ const loft$2 = require("./loft-BcyyvWCj.cjs");
10
+ const operations = require("./operations-CdELWxgv.cjs");
11
+ const boolean2D = require("./boolean2D-CqacqjME.cjs");
12
12
  const _2d = require("./2d.cjs");
13
- const helpers = require("./helpers-CP2KrBZl.cjs");
13
+ const helpers = require("./helpers-CC21GeAr.cjs");
14
14
  const io = require("./io.cjs");
15
- const drawFns = require("./drawFns-Mr2pghU8.cjs");
16
- const vectors = require("./vectors-t1XG4LpL.cjs");
17
- const shapeFns = require("./shapeFns-Z_ScEjmn.cjs");
18
- const topology = require("./topology-A7-jUtHB.cjs");
19
- const faceFns = require("./faceFns-D1Sqnlu6.cjs");
20
- const meshFns = require("./meshFns-CJV_k_EQ.cjs");
21
- const booleanFns = require("./booleanFns-Cd414V3l.cjs");
22
- const measurement = require("./measurement-B06hNs89.cjs");
23
- const curveBuilders = require("./curveBuilders-Du03_Yyf.cjs");
24
- const cast = require("./cast-C107o5ow.cjs");
15
+ const drawFns = require("./drawFns-CAmFEqd1.cjs");
16
+ const vectors = require("./vectors-DwFeX0Ja.cjs");
17
+ const shapeFns = require("./shapeFns-3RYtsUVY.cjs");
18
+ const booleanFns = require("./booleanFns-NtKxkiXn.cjs");
19
+ const topology = require("./topology-D-nGjCzV.cjs");
20
+ const faceFns = require("./faceFns-DcndPHWm.cjs");
21
+ const meshFns = require("./meshFns-DawUwI3W.cjs");
22
+ const measurement = require("./measurement-DU3ry-0Q.cjs");
23
+ const surfaceBuilders = require("./surfaceBuilders-Xx9DRRxs.cjs");
25
24
  const query = require("./query.cjs");
26
25
  const result = require("./result.cjs");
27
- const cornerFinder = require("./cornerFinder-BdKtobgb.cjs");
26
+ const cornerFinder = require("./cornerFinder-Bqy8Lw2p.cjs");
28
27
  const worker = require("./worker.cjs");
29
28
  const errorFactories = {
30
29
  OCCT_OPERATION: (code, message, cause) => ({ kind: "OCCT_OPERATION", code, message, cause }),
@@ -82,7 +81,7 @@ function buildWireFinder(filters) {
82
81
  isOpen: () => withFilter((wire2) => !curveFns.curveIsClosed(wire2)),
83
82
  ofEdgeCount: (count) => withFilter((wire2) => {
84
83
  let edgeCount = 0;
85
- for (const _raw of cast.iterTopo(wire2.wrapped, "edge")) {
84
+ for (const _raw of faceFns.iterTopo(wire2.wrapped, "edge")) {
86
85
  edgeCount++;
87
86
  }
88
87
  return edgeCount === count;
@@ -145,6 +144,1228 @@ function buildVertexFinder(filters) {
145
144
  function vertexFinder() {
146
145
  return buildVertexFinder([]);
147
146
  }
147
+ function surfaceFromGrid(heights, options = {}) {
148
+ if (heights.length < 2) {
149
+ return errors.err(
150
+ errors.validationError(
151
+ errors.BrepErrorCode.SURFACE_GRID_TOO_SMALL,
152
+ `surfaceFromGrid: need at least 2 rows, got ${heights.length}`
153
+ )
154
+ );
155
+ }
156
+ const rows = heights.length;
157
+ const cols = heights[0]?.length ?? 0;
158
+ if (cols < 2) {
159
+ return errors.err(
160
+ errors.validationError(
161
+ errors.BrepErrorCode.SURFACE_GRID_TOO_SMALL,
162
+ `surfaceFromGrid: need at least 2 columns, got ${cols}`
163
+ )
164
+ );
165
+ }
166
+ for (let r = 0; r < rows; r++) {
167
+ const row = heights[r];
168
+ if (!row || row.length !== cols) {
169
+ return errors.err(
170
+ errors.validationError(
171
+ errors.BrepErrorCode.SURFACE_GRID_JAGGED,
172
+ `surfaceFromGrid: row ${r} has ${row?.length ?? 0} columns, expected ${cols}`
173
+ )
174
+ );
175
+ }
176
+ }
177
+ const { width = cols - 1, depth = rows - 1, scaleZ = 1 } = options;
178
+ const dx = width / (cols - 1);
179
+ const dy = depth / (rows - 1);
180
+ try {
181
+ return buildBSplineSurface(heights, rows, cols, dx, dy, scaleZ);
182
+ } catch {
183
+ }
184
+ try {
185
+ return buildTriangulatedSurface(heights, rows, cols, dx, dy, scaleZ);
186
+ } catch (e) {
187
+ const raw = e instanceof Error ? e.message : String(e);
188
+ return errors.err(errors.occtError(errors.BrepErrorCode.SURFACE_FAILED, `surfaceFromGrid failed: ${raw}`, e));
189
+ }
190
+ }
191
+ function buildBSplineSurface(heights, rows, cols, dx, dy, scaleZ) {
192
+ const oc = occtBoundary.getKernel().oc;
193
+ const OC = oc;
194
+ const pntArray = new OC.TColgp_Array2OfPnt_2(1, rows, 1, cols);
195
+ try {
196
+ for (let r = 0; r < rows; r++) {
197
+ for (let c = 0; c < cols; c++) {
198
+ const row = heights[r];
199
+ const z = (row ? row[c] ?? 0 : 0) * scaleZ;
200
+ const pnt = new oc.gp_Pnt_3(c * dx, r * dy, z);
201
+ pntArray.SetValue(r + 1, c + 1, pnt);
202
+ pnt.delete();
203
+ }
204
+ }
205
+ const fitter = new OC.GeomAPI_PointsToBSplineSurface_2(pntArray, 3, 8, 0, 1e-3);
206
+ const surface = fitter.Surface();
207
+ const faceMaker = new OC.BRepBuilderAPI_MakeFace_8(surface, 1e-6);
208
+ let result2;
209
+ if (faceMaker.IsDone()) {
210
+ const shape2 = shapeTypes.castShape(faceMaker.Face());
211
+ if (shapeTypes.isFace(shape2)) {
212
+ result2 = errors.ok(shape2);
213
+ } else {
214
+ shape2[Symbol.dispose]();
215
+ result2 = errors.err(
216
+ errors.occtError(errors.BrepErrorCode.SURFACE_FAILED, "B-spline surface did not produce a face")
217
+ );
218
+ }
219
+ } else {
220
+ result2 = errors.err(
221
+ errors.occtError(
222
+ errors.BrepErrorCode.SURFACE_FAILED,
223
+ "BRepBuilderAPI_MakeFace failed for B-spline surface"
224
+ )
225
+ );
226
+ }
227
+ faceMaker.delete();
228
+ fitter.delete();
229
+ return result2;
230
+ } finally {
231
+ pntArray.delete();
232
+ }
233
+ }
234
+ function buildTriangulatedSurface(heights, rows, cols, dx, dy, scaleZ) {
235
+ const oc = occtBoundary.getKernel().oc;
236
+ function pt(r, c) {
237
+ const row = heights[r];
238
+ const z = (row ? row[c] ?? 0 : 0) * scaleZ;
239
+ return { x: c * dx, y: r * dy, z };
240
+ }
241
+ function buildTriFace2(a, b, c) {
242
+ const gpA = new oc.gp_Pnt_3(a.x, a.y, a.z);
243
+ const gpB = new oc.gp_Pnt_3(b.x, b.y, b.z);
244
+ const gpC = new oc.gp_Pnt_3(c.x, c.y, c.z);
245
+ const e1 = new oc.BRepBuilderAPI_MakeEdge_3(gpA, gpB);
246
+ const e2 = new oc.BRepBuilderAPI_MakeEdge_3(gpB, gpC);
247
+ const e3 = new oc.BRepBuilderAPI_MakeEdge_3(gpC, gpA);
248
+ const wireBuilder = new oc.BRepBuilderAPI_MakeWire_1();
249
+ wireBuilder.Add_1(e1.Edge());
250
+ wireBuilder.Add_1(e2.Edge());
251
+ wireBuilder.Add_1(e3.Edge());
252
+ let face2 = null;
253
+ if (wireBuilder.IsDone()) {
254
+ const makeFace = new oc.BRepBuilderAPI_MakeFace_15(wireBuilder.Wire(), false);
255
+ if (makeFace.IsDone()) {
256
+ face2 = makeFace.Face();
257
+ }
258
+ makeFace.delete();
259
+ }
260
+ wireBuilder.delete();
261
+ e1.delete();
262
+ e2.delete();
263
+ e3.delete();
264
+ gpA.delete();
265
+ gpB.delete();
266
+ gpC.delete();
267
+ return face2;
268
+ }
269
+ const sewing = new oc.BRepBuilderAPI_Sewing(1e-6, true, true, true, false);
270
+ let faceCount = 0;
271
+ try {
272
+ for (let r = 0; r < rows - 1; r++) {
273
+ for (let c = 0; c < cols - 1; c++) {
274
+ const p00 = pt(r, c);
275
+ const p10 = pt(r + 1, c);
276
+ const p11 = pt(r + 1, c + 1);
277
+ const p01 = pt(r, c + 1);
278
+ const f1 = buildTriFace2(p00, p10, p11);
279
+ if (f1 !== null) {
280
+ sewing.Add(f1);
281
+ faceCount++;
282
+ }
283
+ const f2 = buildTriFace2(p00, p11, p01);
284
+ if (f2 !== null) {
285
+ sewing.Add(f2);
286
+ faceCount++;
287
+ }
288
+ }
289
+ }
290
+ if (faceCount === 0) {
291
+ sewing.delete();
292
+ return errors.err(
293
+ errors.occtError(errors.BrepErrorCode.SURFACE_FAILED, "surfaceFromGrid: no valid triangular faces built")
294
+ );
295
+ }
296
+ const sewProgress = new oc.Message_ProgressRange_1();
297
+ sewing.Perform(sewProgress);
298
+ sewProgress.delete();
299
+ const sewn = sewing.SewedShape();
300
+ const shape2 = shapeTypes.castShape(sewn);
301
+ if (shapeTypes.isFace(shape2)) {
302
+ return errors.ok(shape2);
303
+ }
304
+ if (shapeTypes.isShell(shape2)) {
305
+ return errors.ok(shape2);
306
+ }
307
+ shape2[Symbol.dispose]();
308
+ return errors.err(
309
+ errors.occtError(errors.BrepErrorCode.SURFACE_FAILED, "surfaceFromGrid: unexpected shape type from sewing")
310
+ );
311
+ } finally {
312
+ sewing.delete();
313
+ }
314
+ }
315
+ async function surfaceFromImage(blob, options = {}) {
316
+ const channel = options.channel ?? "luminance";
317
+ const downsample = Math.max(1, Math.round(options.downsample ?? 1));
318
+ if (typeof createImageBitmap !== "function") {
319
+ return errors.err(
320
+ errors.ioError(
321
+ errors.BrepErrorCode.SURFACE_FAILED,
322
+ "surfaceFromImage requires createImageBitmap (not available in this environment)"
323
+ )
324
+ );
325
+ }
326
+ let bitmap;
327
+ try {
328
+ bitmap = await createImageBitmap(blob);
329
+ } catch (e) {
330
+ return errors.err(
331
+ errors.ioError(
332
+ errors.BrepErrorCode.SURFACE_FAILED,
333
+ `surfaceFromImage: failed to decode image — ${e instanceof Error ? e.message : String(e)}`
334
+ )
335
+ );
336
+ }
337
+ const w = bitmap.width;
338
+ const h = bitmap.height;
339
+ if (w < 2 || h < 2) {
340
+ bitmap.close();
341
+ return errors.err(
342
+ errors.validationError(
343
+ errors.BrepErrorCode.SURFACE_GRID_TOO_SMALL,
344
+ `surfaceFromImage: image too small (${w}x${h}), need at least 2x2`
345
+ )
346
+ );
347
+ }
348
+ if (typeof OffscreenCanvas !== "function") {
349
+ bitmap.close();
350
+ return errors.err(
351
+ errors.ioError(
352
+ errors.BrepErrorCode.SURFACE_FAILED,
353
+ "surfaceFromImage requires OffscreenCanvas (not available in this environment)"
354
+ )
355
+ );
356
+ }
357
+ const canvas = new OffscreenCanvas(w, h);
358
+ const ctx = canvas.getContext("2d");
359
+ if (!ctx) {
360
+ bitmap.close();
361
+ return errors.err(
362
+ errors.ioError(errors.BrepErrorCode.SURFACE_FAILED, "surfaceFromImage: could not get 2D canvas context")
363
+ );
364
+ }
365
+ ctx.drawImage(bitmap, 0, 0);
366
+ bitmap.close();
367
+ const imageData = ctx.getImageData(0, 0, w, h);
368
+ const data = imageData.data;
369
+ const rows = [];
370
+ for (let y = 0; y < h; y += downsample) {
371
+ const row = [];
372
+ for (let x = 0; x < w; x += downsample) {
373
+ const idx = (y * w + x) * 4;
374
+ const r = data[idx] ?? 0;
375
+ const g = data[idx + 1] ?? 0;
376
+ const b = data[idx + 2] ?? 0;
377
+ let value;
378
+ switch (channel) {
379
+ case "r":
380
+ value = r / 255;
381
+ break;
382
+ case "g":
383
+ value = g / 255;
384
+ break;
385
+ case "b":
386
+ value = b / 255;
387
+ break;
388
+ default:
389
+ value = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
390
+ break;
391
+ }
392
+ row.push(value);
393
+ }
394
+ rows.push(row);
395
+ }
396
+ const gridOpts = {};
397
+ if (options.width !== void 0) gridOpts.width = options.width;
398
+ if (options.depth !== void 0) gridOpts.depth = options.depth;
399
+ if (options.scaleZ !== void 0) gridOpts.scaleZ = options.scaleZ;
400
+ return surfaceFromGrid(rows, gridOpts);
401
+ }
402
+ function validateNotNull$1(shape2, label) {
403
+ if (shape2.wrapped.IsNull()) {
404
+ return errors.err(errors.validationError(errors.BrepErrorCode.NULL_SHAPE_INPUT, `${label} is a null shape`));
405
+ }
406
+ return errors.ok(void 0);
407
+ }
408
+ function hull(shapes, options = {}) {
409
+ if (shapes.length === 0) {
410
+ return errors.err(
411
+ errors.validationError(
412
+ errors.BrepErrorCode.HULL_EMPTY_INPUT,
413
+ "hull: at least one shape is required",
414
+ void 0,
415
+ void 0,
416
+ "Provide one or more shapes to compute a convex hull"
417
+ )
418
+ );
419
+ }
420
+ for (const [i, shape2] of shapes.entries()) {
421
+ const check = validateNotNull$1(shape2, `hull: shapes[${i}]`);
422
+ if (errors.isErr(check)) return check;
423
+ }
424
+ const tolerance = options.tolerance ?? 0.1;
425
+ try {
426
+ const kernel = occtBoundary.getKernel();
427
+ const ocShapes = shapes.map((s) => s.wrapped);
428
+ const resultOc = kernel.hull(ocShapes, tolerance);
429
+ const cast = shapeTypes.castShape(resultOc);
430
+ if (!shapeTypes.isSolid(cast)) {
431
+ return errors.err(
432
+ errors.occtError(errors.BrepErrorCode.HULL_NOT_3D, "Hull result is not a solid; input may be degenerate")
433
+ );
434
+ }
435
+ return errors.ok(cast);
436
+ } catch (e) {
437
+ const raw = e instanceof Error ? e.message : String(e);
438
+ if (raw.includes("coplanar") || raw.includes("fewer than") || raw.includes("degenerate")) {
439
+ return errors.err(errors.occtError(errors.BrepErrorCode.HULL_DEGENERATE, `Hull degenerate: ${raw}`, e));
440
+ }
441
+ return errors.err(errors.occtError(errors.BrepErrorCode.HULL_FAILED, `Hull operation failed: ${raw}`, e));
442
+ }
443
+ }
444
+ function detectSphere(shape2) {
445
+ const oc = occtBoundary.getKernel().oc;
446
+ const faces = shapeFns.getFaces(shape2);
447
+ if (faces.length !== 1) return null;
448
+ const face2 = faces[0];
449
+ const r = shapeTypes.gcWithScope();
450
+ const adaptor = r(new oc.BRepAdaptor_Surface_2(face2.wrapped, true));
451
+ const surfType = adaptor.GetType();
452
+ if (surfType !== oc.GeomAbs_SurfaceType.GeomAbs_Sphere) return null;
453
+ const ocSphere = adaptor.Sphere();
454
+ const radius = ocSphere.Radius();
455
+ ocSphere.delete();
456
+ return radius;
457
+ }
458
+ function minkowskiSphere(shape2, radius, tolerance) {
459
+ const oc = occtBoundary.getKernel().oc;
460
+ const r = shapeTypes.gcWithScope();
461
+ try {
462
+ const offsetMaker = r(new oc.BRepOffsetAPI_MakeOffsetShape());
463
+ const progress = r(new oc.Message_ProgressRange_1());
464
+ offsetMaker.PerformByJoin(
465
+ shape2.wrapped,
466
+ radius,
467
+ tolerance,
468
+ oc.BRepOffset_Mode.BRepOffset_Skin,
469
+ false,
470
+ false,
471
+ oc.GeomAbs_JoinType.GeomAbs_Arc,
472
+ false,
473
+ progress
474
+ );
475
+ const resultShape = offsetMaker.Shape();
476
+ const wrapped = shapeTypes.castShape(resultShape);
477
+ if (!shapeTypes.isShape3D(wrapped)) {
478
+ wrapped[Symbol.dispose]();
479
+ return errors.err(
480
+ errors.typeCastError(
481
+ errors.BrepErrorCode.MINKOWSKI_NOT_3D,
482
+ "Minkowski sphere offset did not produce a 3D shape"
483
+ )
484
+ );
485
+ }
486
+ return errors.ok(wrapped);
487
+ } catch (e) {
488
+ const raw = e instanceof Error ? e.message : String(e);
489
+ return errors.err(
490
+ errors.occtError(errors.BrepErrorCode.MINKOWSKI_FAILED, `Minkowski sphere offset failed: ${raw}`, e, {
491
+ operation: "minkowski",
492
+ fastPath: "sphere"
493
+ })
494
+ );
495
+ }
496
+ }
497
+ function minkowskiGeneral(shape2, tool, _tolerance) {
498
+ const oc = occtBoundary.getKernel().oc;
499
+ try {
500
+ const shapeVerts = shapeFns.getVertices(shape2);
501
+ const toolVerts = shapeFns.getVertices(tool);
502
+ if (shapeVerts.length === 0 || toolVerts.length === 0) {
503
+ return errors.err(
504
+ errors.occtError(
505
+ errors.BrepErrorCode.MINKOWSKI_FAILED,
506
+ "Minkowski sum: one or both shapes have no vertices",
507
+ void 0,
508
+ {
509
+ operation: "minkowski"
510
+ }
511
+ )
512
+ );
513
+ }
514
+ const sumPoints = [];
515
+ for (const sv of shapeVerts) {
516
+ const r1 = shapeTypes.gcWithScope();
517
+ const pa = r1(oc.BRep_Tool.Pnt(sv.wrapped));
518
+ const ax = pa.X(), ay = pa.Y(), az = pa.Z();
519
+ for (const tv of toolVerts) {
520
+ const r2 = shapeTypes.gcWithScope();
521
+ const pb = r2(oc.BRep_Tool.Pnt(tv.wrapped));
522
+ const bx = pb.X(), by = pb.Y(), bz = pb.Z();
523
+ sumPoints.push({ x: ax + bx, y: ay + by, z: az + bz });
524
+ }
525
+ }
526
+ const kernel = occtBoundary.getKernel();
527
+ const hullShape = kernel.hullFromPoints(sumPoints, _tolerance);
528
+ const wrapped = shapeTypes.castShape(hullShape);
529
+ if (!shapeTypes.isShape3D(wrapped)) {
530
+ wrapped[Symbol.dispose]();
531
+ return errors.err(
532
+ errors.typeCastError(errors.BrepErrorCode.MINKOWSKI_NOT_3D, "Minkowski hull did not produce a 3D shape")
533
+ );
534
+ }
535
+ return errors.ok(wrapped);
536
+ } catch (e) {
537
+ const raw = e instanceof Error ? e.message : String(e);
538
+ return errors.err(
539
+ errors.occtError(errors.BrepErrorCode.MINKOWSKI_FAILED, `Minkowski general path failed: ${raw}`, e, {
540
+ operation: "minkowski"
541
+ })
542
+ );
543
+ }
544
+ }
545
+ function minkowski(shape2, tool, options = {}) {
546
+ const { tolerance = 1e-6 } = options;
547
+ if (shape2.wrapped.IsNull()) {
548
+ return errors.err(errors.validationError(errors.BrepErrorCode.NULL_SHAPE_INPUT, "minkowski: shape is a null shape"));
549
+ }
550
+ if (tool.wrapped.IsNull()) {
551
+ return errors.err(
552
+ errors.validationError(errors.BrepErrorCode.MINKOWSKI_NULL_TOOL, "minkowski: tool is a null shape")
553
+ );
554
+ }
555
+ if (!shapeTypes.isShape3D(shape2) || !shapeTypes.isShape3D(tool)) {
556
+ return errors.err(
557
+ errors.validationError(errors.BrepErrorCode.MINKOWSKI_NOT_3D, "minkowski: both shape and tool must be 3D")
558
+ );
559
+ }
560
+ const sphereRadius = detectSphere(tool);
561
+ if (sphereRadius !== null) {
562
+ return minkowskiSphere(shape2, sphereRadius, tolerance);
563
+ }
564
+ return minkowskiGeneral(shape2, tool, tolerance);
565
+ }
566
+ function polyhedron(points, faces, options = {}) {
567
+ const { tolerance = 1e-6 } = options;
568
+ if (points.length < 4) {
569
+ return errors.err(
570
+ errors.validationError(
571
+ errors.BrepErrorCode.POLYHEDRON_INSUFFICIENT_POINTS,
572
+ `polyhedron: need at least 4 points, got ${points.length}`
573
+ )
574
+ );
575
+ }
576
+ if (faces.length < 4) {
577
+ return errors.err(
578
+ errors.validationError(
579
+ errors.BrepErrorCode.POLYHEDRON_INSUFFICIENT_FACES,
580
+ `polyhedron: need at least 4 faces, got ${faces.length}`
581
+ )
582
+ );
583
+ }
584
+ const triangles = [];
585
+ for (const [fi, face2] of faces.entries()) {
586
+ for (const idx of face2) {
587
+ if (idx < 0 || idx >= points.length) {
588
+ return errors.err(
589
+ errors.validationError(
590
+ errors.BrepErrorCode.POLYHEDRON_INVALID_INDEX,
591
+ `polyhedron: face ${fi} has out-of-range index ${idx} (${points.length} points)`
592
+ )
593
+ );
594
+ }
595
+ }
596
+ if (face2.length < 3) continue;
597
+ const v0 = face2[0];
598
+ for (let i = 1; i < face2.length - 1; i++) {
599
+ triangles.push([v0, face2[i], face2[i + 1]]);
600
+ }
601
+ }
602
+ try {
603
+ const kernel = occtBoundary.getKernel();
604
+ const ptObjs = points.map(([x, y, z]) => ({ x, y, z }));
605
+ const resultOc = kernel.buildSolidFromFaces(ptObjs, triangles, tolerance);
606
+ const cast = shapeTypes.castShape(resultOc);
607
+ if (!shapeTypes.isSolid(cast)) {
608
+ cast[Symbol.dispose]();
609
+ return errors.err(errors.occtError(errors.BrepErrorCode.POLYHEDRON_FAILED, "Polyhedron did not produce a solid"));
610
+ }
611
+ return errors.ok(cast);
612
+ } catch (e) {
613
+ const raw = e instanceof Error ? e.message : String(e);
614
+ return errors.err(errors.occtError(errors.BrepErrorCode.POLYHEDRON_FAILED, `Polyhedron failed: ${raw}`, e));
615
+ }
616
+ }
617
+ function multiSectionSweep(sections, spine, options) {
618
+ if (sections.length < 2) {
619
+ return errors.err(
620
+ errors.validationError(
621
+ errors.BrepErrorCode.MULTI_SWEEP_INSUFFICIENT_SECTIONS,
622
+ `Multi-section sweep requires at least 2 sections, got ${sections.length}`
623
+ )
624
+ );
625
+ }
626
+ const { solid: solid2 = true, ruled = false, tolerance = 1e-6 } = options ?? {};
627
+ try {
628
+ const oc = occtBoundary.getKernel().oc;
629
+ const r = shapeTypes.gcWithScope();
630
+ const adaptor = r(new oc.BRepAdaptor_CompCurve_2(spine.wrapped, false));
631
+ const uFirst = Number(adaptor.FirstParameter());
632
+ const uLast = Number(adaptor.LastParameter());
633
+ const uRange = uLast - uFirst;
634
+ const params = sections.map((s, i) => {
635
+ if (s.location !== void 0) {
636
+ return uFirst + s.location * uRange;
637
+ }
638
+ return uFirst + i / (sections.length - 1) * uRange;
639
+ });
640
+ const builder = r(new oc.BRepOffsetAPI_ThruSections(solid2, ruled, tolerance));
641
+ for (let i = 0; i < sections.length; i++) {
642
+ const param = params[i];
643
+ const section2 = sections[i];
644
+ if (param === void 0 || section2 === void 0) continue;
645
+ const pnt = r(new oc.gp_Pnt_1());
646
+ const tangent = r(new oc.gp_Vec_1());
647
+ adaptor.D1(param, pnt, tangent);
648
+ const tangentDir = r(new oc.gp_Dir_2(tangent));
649
+ const toAx3 = r(new oc.gp_Ax3_4(pnt, tangentDir));
650
+ const trsf = r(new oc.gp_Trsf_1());
651
+ trsf.SetTransformation_2(toAx3);
652
+ trsf.Invert();
653
+ const transformer = r(new oc.BRepBuilderAPI_Transform_2(section2.wire.wrapped, trsf, true));
654
+ const transformedShape = transformer.Shape();
655
+ const transformedWire = oc.TopoDS.Wire_1(transformedShape);
656
+ builder.AddWire(transformedWire);
657
+ }
658
+ const progress = r(new oc.Message_ProgressRange_1());
659
+ builder.Build(progress);
660
+ if (!builder.IsDone()) {
661
+ return errors.err(errors.occtError(errors.BrepErrorCode.MULTI_SWEEP_FAILED, "Multi-section sweep build failed"));
662
+ }
663
+ const result2 = shapeTypes.castShape(builder.Shape());
664
+ if (!shapeTypes.isShape3D(result2)) {
665
+ return errors.err(
666
+ errors.typeCastError("MULTI_SWEEP_NOT_3D", "Multi-section sweep did not produce a 3D shape")
667
+ );
668
+ }
669
+ return errors.ok(result2);
670
+ } catch (e) {
671
+ const raw = e instanceof Error ? e.message : String(e);
672
+ return errors.err(
673
+ errors.occtError(errors.BrepErrorCode.MULTI_SWEEP_FAILED, `Multi-section sweep failed: ${raw}`, e)
674
+ );
675
+ }
676
+ }
677
+ function guidedSweep(profile, spine, guides, options = {}) {
678
+ const { transition = "transformed", solid: solid2 = true, tolerance } = options;
679
+ try {
680
+ const oc = occtBoundary.getKernel().oc;
681
+ const r = shapeTypes.gcWithScope();
682
+ const builder = r(new oc.BRepOffsetAPI_MakePipeShell(spine.wrapped));
683
+ const modeMap = {
684
+ transformed: oc.BRepBuilderAPI_TransitionMode.BRepBuilderAPI_Transformed,
685
+ round: oc.BRepBuilderAPI_TransitionMode.BRepBuilderAPI_RoundCorner,
686
+ right: oc.BRepBuilderAPI_TransitionMode.BRepBuilderAPI_RightCorner
687
+ };
688
+ builder.SetTransitionMode(modeMap[transition]);
689
+ if (tolerance !== void 0) {
690
+ builder.SetTolerance(tolerance, tolerance, 1e-7);
691
+ }
692
+ if (guides.length > 0) {
693
+ const firstGuide = guides[0];
694
+ builder.SetMode_5(firstGuide.wrapped, false, oc.BRepFill_TypeOfContact.BRepFill_NoContact);
695
+ }
696
+ builder.Add_1(profile.wrapped, false, false);
697
+ const progress = r(new oc.Message_ProgressRange_1());
698
+ builder.Build(progress);
699
+ if (!builder.IsDone()) {
700
+ return errors.err(errors.occtError(errors.BrepErrorCode.GUIDED_SWEEP_FAILED, "Guided sweep build failed"));
701
+ }
702
+ if (solid2) {
703
+ builder.MakeSolid();
704
+ }
705
+ const result2 = shapeTypes.castShape(builder.Shape());
706
+ if (!shapeTypes.isShape3D(result2)) {
707
+ return errors.err(errors.typeCastError("GUIDED_SWEEP_NOT_3D", "Guided sweep did not produce a 3D shape"));
708
+ }
709
+ return errors.ok(result2);
710
+ } catch (e) {
711
+ const raw = e instanceof Error ? e.message : String(e);
712
+ return errors.err(errors.occtError(errors.BrepErrorCode.GUIDED_SWEEP_FAILED, `Guided sweep failed: ${raw}`, e));
713
+ }
714
+ }
715
+ const EPS = 1e-10;
716
+ function cross2(ax, ay, bx, by) {
717
+ return ax * by - ay * bx;
718
+ }
719
+ function dot2(ax, ay, bx, by) {
720
+ return ax * bx + ay * by;
721
+ }
722
+ function len2(x, y) {
723
+ return Math.sqrt(x * x + y * y);
724
+ }
725
+ function polyAt(poly, i) {
726
+ const p = poly[(i % poly.length + poly.length) % poly.length];
727
+ if (!p) throw new Error(`Invalid polygon index ${i} for length ${poly.length}`);
728
+ return p;
729
+ }
730
+ function ensureCCW(poly) {
731
+ let area = 0;
732
+ for (let i = 0; i < poly.length; i++) {
733
+ const cur = polyAt(poly, i);
734
+ const nxt = polyAt(poly, i + 1);
735
+ area += cur.x * nxt.y - nxt.x * cur.y;
736
+ }
737
+ if (area < 0) return [...poly].reverse();
738
+ return poly;
739
+ }
740
+ function bisector(poly, i) {
741
+ const prev = polyAt(poly, i - 1);
742
+ const cur = polyAt(poly, i);
743
+ const next = polyAt(poly, i + 1);
744
+ const e1x = cur.x - prev.x;
745
+ const e1y = cur.y - prev.y;
746
+ const e1l = len2(e1x, e1y);
747
+ const e2x = next.x - cur.x;
748
+ const e2y = next.y - cur.y;
749
+ const e2l = len2(e2x, e2y);
750
+ if (e1l < EPS || e2l < EPS) return { dx: 0, dy: 0 };
751
+ const n1x = -e1y / e1l;
752
+ const n1y = e1x / e1l;
753
+ const n2x = -e2y / e2l;
754
+ const n2y = e2x / e2l;
755
+ let bx = n1x + n2x;
756
+ let by = n1y + n2y;
757
+ const bl = len2(bx, by);
758
+ if (bl < EPS) {
759
+ return { dx: n1x, dy: n1y };
760
+ }
761
+ bx /= bl;
762
+ by /= bl;
763
+ const cosHalf = dot2(bx, by, n1x, n1y);
764
+ const speed = Math.abs(cosHalf) > EPS ? 1 / cosHalf : 1;
765
+ return { dx: bx * speed, dy: by * speed };
766
+ }
767
+ function isLavNodeReflex(node) {
768
+ const prev = node.prev;
769
+ const next = node.next;
770
+ return cross2(node.x - prev.x, node.y - prev.y, next.x - node.x, next.y - node.y) < -EPS;
771
+ }
772
+ function createLav(poly) {
773
+ const nodes = poly.map((p, i) => {
774
+ const b = bisector(poly, i);
775
+ return {
776
+ x: p.x,
777
+ y: p.y,
778
+ bx: b.dx,
779
+ by: b.dy,
780
+ origIdx: i,
781
+ prev: null,
782
+ next: null,
783
+ active: true
784
+ };
785
+ });
786
+ for (let i = 0; i < nodes.length; i++) {
787
+ const node = nodes[i];
788
+ const prevNode = nodes[(i - 1 + nodes.length) % nodes.length];
789
+ const nextNode = nodes[(i + 1) % nodes.length];
790
+ if (node && prevNode && nextNode) {
791
+ node.prev = prevNode;
792
+ node.next = nextNode;
793
+ }
794
+ }
795
+ return nodes;
796
+ }
797
+ function lavSize(start) {
798
+ let count = 1;
799
+ let cur = start.next;
800
+ while (cur !== start) {
801
+ count++;
802
+ cur = cur.next;
803
+ if (count > 1e4) break;
804
+ }
805
+ return count;
806
+ }
807
+ function bisectorIntersectTime(a, b) {
808
+ const ddx = a.bx - b.bx;
809
+ const ddy = a.by - b.by;
810
+ const dxp = b.x - a.x;
811
+ const dyp = b.y - a.y;
812
+ if (Math.abs(ddx) < EPS && Math.abs(ddy) < EPS) return null;
813
+ let t;
814
+ if (Math.abs(ddx) > Math.abs(ddy)) {
815
+ t = dxp / ddx;
816
+ } else {
817
+ t = dyp / ddy;
818
+ }
819
+ if (t < EPS) return null;
820
+ const otherDd = Math.abs(ddx) > Math.abs(ddy) ? ddy : ddx;
821
+ const otherDp = Math.abs(ddx) > Math.abs(ddy) ? dyp : dxp;
822
+ if (Math.abs(otherDd) > EPS) {
823
+ const t2 = otherDp / otherDd;
824
+ if (Math.abs(t - t2) > 1e-4 * Math.max(1, Math.abs(t))) return null;
825
+ }
826
+ return t;
827
+ }
828
+ function raySplitTime(node, eA, eB) {
829
+ const edx = eB.x - eA.x;
830
+ const edy = eB.y - eA.y;
831
+ const el = len2(edx, edy);
832
+ if (el < EPS) return null;
833
+ const enx = -edy / el;
834
+ const eny = edx / el;
835
+ const d0 = (node.x - eA.x) * enx + (node.y - eA.y) * eny;
836
+ const relBx = node.bx - (eA.bx + eB.bx) / 2;
837
+ const relBy = node.by - (eA.by + eB.by) / 2;
838
+ const dRate = relBx * enx + relBy * eny;
839
+ if (Math.abs(dRate) < EPS) return null;
840
+ const t = -d0 / dRate;
841
+ if (t < EPS) return null;
842
+ const px = node.x + t * node.bx;
843
+ const py = node.y + t * node.by;
844
+ const ax = eA.x + t * eA.bx;
845
+ const ay = eA.y + t * eA.by;
846
+ const bxx = eB.x + t * eB.bx;
847
+ const byy = eB.y + t * eB.by;
848
+ const segDx = bxx - ax;
849
+ const segDy = byy - ay;
850
+ const segL = len2(segDx, segDy);
851
+ if (segL < EPS) return t;
852
+ const s = dot2(px - ax, py - ay, segDx, segDy) / (segL * segL);
853
+ if (s < -0.01 || s > 1.01) return null;
854
+ return t;
855
+ }
856
+ function computeEvents(lavNodes) {
857
+ const events = [];
858
+ for (const node of lavNodes) {
859
+ if (!node.active) continue;
860
+ const t = bisectorIntersectTime(node, node.next);
861
+ if (t !== null && t > EPS) {
862
+ const x = node.x + t * node.bx;
863
+ const y = node.y + t * node.by;
864
+ events.push({ time: t, x, y, nodeA: node, nodeB: node.next, type: "edge" });
865
+ }
866
+ if (isLavNodeReflex(node)) {
867
+ let cur = node.next.next;
868
+ let count = 0;
869
+ while (cur !== node.prev && cur !== node && count < 1e3) {
870
+ const st = raySplitTime(node, cur, cur.next);
871
+ if (st !== null && st > EPS) {
872
+ const x = node.x + st * node.bx;
873
+ const y = node.y + st * node.by;
874
+ events.push({ time: st, x, y, nodeA: node, nodeB: cur, type: "split" });
875
+ }
876
+ cur = cur.next;
877
+ count++;
878
+ }
879
+ }
880
+ }
881
+ events.sort((a, b) => a.time - b.time);
882
+ return events;
883
+ }
884
+ function computeStraightSkeleton(polygon2) {
885
+ if (polygon2.length < 3) {
886
+ return { nodes: [], faces: [] };
887
+ }
888
+ const poly = ensureCCW(polygon2);
889
+ const n = poly.length;
890
+ const skeletonNodes = [];
891
+ const vertexToSkelNodes = Array.from({ length: n }, () => []);
892
+ const lavNodes = createLav(poly);
893
+ let iterations = 0;
894
+ const maxIter = n * n * 2;
895
+ while (iterations < maxIter) {
896
+ iterations++;
897
+ const activeStart = lavNodes.find((nd) => nd.active);
898
+ if (!activeStart) break;
899
+ const sz = lavSize(activeStart);
900
+ if (sz <= 3) {
901
+ if (sz === 3) {
902
+ const a = activeStart;
903
+ const b = a.next;
904
+ const c = b.next;
905
+ const t = bisectorIntersectTime(a, b);
906
+ const time = t !== null && t > EPS ? t : 0;
907
+ const mx = (a.x + b.x + c.x) / 3 + time * (a.bx + b.bx + c.bx) / 3;
908
+ const my = (a.y + b.y + c.y) / 3 + time * (a.by + b.by + c.by) / 3;
909
+ const nodeIdx = skeletonNodes.length;
910
+ skeletonNodes.push({ x: mx, y: my, height: time });
911
+ const aNodes = vertexToSkelNodes[a.origIdx];
912
+ const bNodes = vertexToSkelNodes[b.origIdx];
913
+ const cNodes = vertexToSkelNodes[c.origIdx];
914
+ if (aNodes) aNodes.push(nodeIdx);
915
+ if (bNodes) bNodes.push(nodeIdx);
916
+ if (cNodes) cNodes.push(nodeIdx);
917
+ a.active = false;
918
+ b.active = false;
919
+ c.active = false;
920
+ } else {
921
+ let cur = activeStart;
922
+ for (let i = 0; i < sz; i++) {
923
+ cur.active = false;
924
+ cur = cur.next;
925
+ }
926
+ }
927
+ continue;
928
+ }
929
+ const activeNodes = lavNodes.filter((nd) => nd.active);
930
+ const events = computeEvents(activeNodes);
931
+ if (events.length === 0) {
932
+ for (const nd of activeNodes) {
933
+ nd.active = false;
934
+ }
935
+ break;
936
+ }
937
+ const ev = events[0];
938
+ if (!ev) break;
939
+ if (ev.type === "edge") {
940
+ const a = ev.nodeA;
941
+ const b = ev.nodeB;
942
+ if (!a.active || !b.active) continue;
943
+ const nodeIdx = skeletonNodes.length;
944
+ skeletonNodes.push({ x: ev.x, y: ev.y, height: ev.time });
945
+ const aNodes = vertexToSkelNodes[a.origIdx];
946
+ const bNodes = vertexToSkelNodes[b.origIdx];
947
+ if (aNodes) aNodes.push(nodeIdx);
948
+ if (bNodes) bNodes.push(nodeIdx);
949
+ a.x = ev.x;
950
+ a.y = ev.y;
951
+ a.next = b.next;
952
+ b.next.prev = a;
953
+ b.active = false;
954
+ const lavPoly = [];
955
+ let cur = a;
956
+ do {
957
+ lavPoly.push({ x: cur.x, y: cur.y });
958
+ cur = cur.next;
959
+ } while (cur !== a);
960
+ const bDir = bisector(lavPoly, 0);
961
+ a.bx = bDir.dx;
962
+ a.by = bDir.dy;
963
+ } else {
964
+ const a = ev.nodeA;
965
+ const b = ev.nodeB;
966
+ if (!a.active || !b.active) continue;
967
+ const nodeIdx = skeletonNodes.length;
968
+ skeletonNodes.push({ x: ev.x, y: ev.y, height: ev.time });
969
+ const aNodes = vertexToSkelNodes[a.origIdx];
970
+ if (aNodes) aNodes.push(nodeIdx);
971
+ const bNodes = vertexToSkelNodes[b.origIdx];
972
+ if (bNodes) bNodes.push(nodeIdx);
973
+ const aCopy = {
974
+ x: ev.x,
975
+ y: ev.y,
976
+ bx: 0,
977
+ by: 0,
978
+ origIdx: a.origIdx,
979
+ prev: null,
980
+ next: null,
981
+ active: true
982
+ };
983
+ lavNodes.push(aCopy);
984
+ a.x = ev.x;
985
+ a.y = ev.y;
986
+ const aNext = a.next;
987
+ const bNext = b.next;
988
+ a.next = bNext;
989
+ bNext.prev = a;
990
+ aCopy.next = aNext;
991
+ aNext.prev = aCopy;
992
+ aCopy.prev = b;
993
+ b.next = aCopy;
994
+ const buildLavPoly = (start) => {
995
+ const poly2 = [];
996
+ let c = start;
997
+ do {
998
+ poly2.push({ x: c.x, y: c.y });
999
+ c = c.next;
1000
+ } while (c !== start);
1001
+ return poly2;
1002
+ };
1003
+ const lav1Poly = buildLavPoly(a);
1004
+ const bDir1 = bisector(lav1Poly, 0);
1005
+ a.bx = bDir1.dx;
1006
+ a.by = bDir1.dy;
1007
+ const lav2Poly = buildLavPoly(aCopy);
1008
+ const bDir2 = bisector(lav2Poly, 0);
1009
+ aCopy.bx = bDir2.dx;
1010
+ aCopy.by = bDir2.dy;
1011
+ }
1012
+ }
1013
+ const faces = [];
1014
+ for (let i = 0; i < n; i++) {
1015
+ const j = (i + 1) % n;
1016
+ const pi = polyAt(poly, i);
1017
+ const pj = polyAt(poly, j);
1018
+ const faceVerts = [pi, pj];
1019
+ const faceHeights = [0, 0];
1020
+ const jNodes = vertexToSkelNodes[j];
1021
+ const iNodes = vertexToSkelNodes[i];
1022
+ if (jNodes) {
1023
+ for (const ni of jNodes) {
1024
+ const sn = skeletonNodes[ni];
1025
+ if (sn) {
1026
+ faceVerts.push({ x: sn.x, y: sn.y });
1027
+ faceHeights.push(sn.height);
1028
+ }
1029
+ }
1030
+ }
1031
+ if (iNodes) {
1032
+ for (let k = iNodes.length - 1; k >= 0; k--) {
1033
+ const idx = iNodes[k];
1034
+ if (idx === void 0) continue;
1035
+ const sn = skeletonNodes[idx];
1036
+ if (!sn) continue;
1037
+ const lastVert = faceVerts[faceVerts.length - 1];
1038
+ if (!lastVert) continue;
1039
+ const dist = len2(sn.x - lastVert.x, sn.y - lastVert.y);
1040
+ if (dist > EPS) {
1041
+ faceVerts.push({ x: sn.x, y: sn.y });
1042
+ faceHeights.push(sn.height);
1043
+ }
1044
+ }
1045
+ }
1046
+ if (faceVerts.length >= 3) {
1047
+ faces.push({ vertices: faceVerts, heights: faceHeights });
1048
+ }
1049
+ }
1050
+ const uniqueNodes = [];
1051
+ for (const sn of skeletonNodes) {
1052
+ const exists = uniqueNodes.some(
1053
+ (un) => Math.abs(un.x - sn.x) < 0.01 && Math.abs(un.y - sn.y) < 0.01
1054
+ );
1055
+ if (!exists) {
1056
+ uniqueNodes.push(sn);
1057
+ }
1058
+ }
1059
+ return { nodes: uniqueNodes, faces };
1060
+ }
1061
+ function extractPolygon(w) {
1062
+ const edges = shapeFns.getEdges(w);
1063
+ const pts = edges.map((e) => {
1064
+ const pt = curveFns.curveStartPoint(e);
1065
+ return { x: pt[0], y: pt[1] };
1066
+ });
1067
+ const first = pts[0];
1068
+ const last = pts[pts.length - 1];
1069
+ if (pts.length > 1 && first && last && Math.abs(first.x - last.x) < 1e-10 && Math.abs(first.y - last.y) < 1e-10) {
1070
+ pts.pop();
1071
+ }
1072
+ return pts;
1073
+ }
1074
+ function fanTriangulate(count) {
1075
+ const tris = [];
1076
+ for (let i = 1; i < count - 1; i++) {
1077
+ tris.push([0, i, i + 1]);
1078
+ }
1079
+ return tris;
1080
+ }
1081
+ function buildTriFace$2(oc, a, b, c) {
1082
+ const gpA = new oc.gp_Pnt_3(a[0], a[1], a[2]);
1083
+ const gpB = new oc.gp_Pnt_3(b[0], b[1], b[2]);
1084
+ const gpC = new oc.gp_Pnt_3(c[0], c[1], c[2]);
1085
+ const e1 = new oc.BRepBuilderAPI_MakeEdge_3(gpA, gpB);
1086
+ const e2 = new oc.BRepBuilderAPI_MakeEdge_3(gpB, gpC);
1087
+ const e3 = new oc.BRepBuilderAPI_MakeEdge_3(gpC, gpA);
1088
+ const wireBuilder = new oc.BRepBuilderAPI_MakeWire_1();
1089
+ wireBuilder.Add_1(e1.Edge());
1090
+ wireBuilder.Add_1(e2.Edge());
1091
+ wireBuilder.Add_1(e3.Edge());
1092
+ let face2 = null;
1093
+ if (wireBuilder.IsDone()) {
1094
+ const makeFace = new oc.BRepBuilderAPI_MakeFace_15(wireBuilder.Wire(), false);
1095
+ if (makeFace.IsDone()) {
1096
+ face2 = makeFace.Face();
1097
+ }
1098
+ makeFace.delete();
1099
+ }
1100
+ wireBuilder.delete();
1101
+ e1.delete();
1102
+ e2.delete();
1103
+ e3.delete();
1104
+ gpA.delete();
1105
+ gpB.delete();
1106
+ gpC.delete();
1107
+ return face2;
1108
+ }
1109
+ function roof(w, options) {
1110
+ const angle = (options?.angle ?? 45) * (Math.PI / 180);
1111
+ const tanAngle = Math.tan(angle);
1112
+ try {
1113
+ const polygon2 = extractPolygon(w);
1114
+ if (polygon2.length < 3) {
1115
+ return errors.err(
1116
+ errors.occtError(errors.BrepErrorCode.ROOF_FAILED, "Wire must have at least 3 edges for roof generation")
1117
+ );
1118
+ }
1119
+ const skeleton = computeStraightSkeleton(polygon2);
1120
+ if (skeleton.faces.length === 0) {
1121
+ return errors.err(
1122
+ errors.occtError(errors.BrepErrorCode.ROOF_FAILED, "Straight skeleton computation produced no faces")
1123
+ );
1124
+ }
1125
+ const oc = occtBoundary.getKernel().oc;
1126
+ const sewing = new oc.BRepBuilderAPI_Sewing(1e-6, true, true, true, false);
1127
+ let faceCount = 0;
1128
+ try {
1129
+ for (const skFace of skeleton.faces) {
1130
+ const verts3d = skFace.vertices.map(
1131
+ (v, i) => [
1132
+ v.x,
1133
+ v.y,
1134
+ (skFace.heights[i] ?? 0) * tanAngle
1135
+ ]
1136
+ );
1137
+ const tris = fanTriangulate(verts3d.length);
1138
+ for (const [ai, bi, ci] of tris) {
1139
+ const va = verts3d[ai];
1140
+ const vb = verts3d[bi];
1141
+ const vc = verts3d[ci];
1142
+ if (!va || !vb || !vc) continue;
1143
+ const abx = vb[0] - va[0];
1144
+ const aby = vb[1] - va[1];
1145
+ const abz = vb[2] - va[2];
1146
+ const acx = vc[0] - va[0];
1147
+ const acy = vc[1] - va[1];
1148
+ const acz = vc[2] - va[2];
1149
+ const nx = aby * acz - abz * acy;
1150
+ const ny = abz * acx - abx * acz;
1151
+ const nz = abx * acy - aby * acx;
1152
+ const areaSq = nx * nx + ny * ny + nz * nz;
1153
+ if (areaSq < 1e-20) continue;
1154
+ const triFace = buildTriFace$2(oc, va, vb, vc);
1155
+ if (triFace !== null) {
1156
+ sewing.Add(triFace);
1157
+ faceCount++;
1158
+ }
1159
+ }
1160
+ }
1161
+ const p0 = polygon2[0];
1162
+ if (p0) {
1163
+ for (let i = 1; i < polygon2.length - 1; i++) {
1164
+ const pi = polygon2[i];
1165
+ const pi1 = polygon2[i + 1];
1166
+ if (!pi || !pi1) continue;
1167
+ const va = [p0.x, p0.y, 0];
1168
+ const vb = [pi.x, pi.y, 0];
1169
+ const vc = [pi1.x, pi1.y, 0];
1170
+ const triFace = buildTriFace$2(oc, va, vc, vb);
1171
+ if (triFace !== null) {
1172
+ sewing.Add(triFace);
1173
+ faceCount++;
1174
+ }
1175
+ }
1176
+ }
1177
+ if (faceCount === 0) {
1178
+ return errors.err(
1179
+ errors.occtError(errors.BrepErrorCode.ROOF_FAILED, "No valid triangular faces could be built")
1180
+ );
1181
+ }
1182
+ const progress = new oc.Message_ProgressRange_1();
1183
+ sewing.Perform(progress);
1184
+ progress.delete();
1185
+ const sewn = sewing.SewedShape();
1186
+ const fixer = new oc.ShapeFix_Solid_1();
1187
+ try {
1188
+ const shell2 = oc.TopoDS.Shell_1(sewn);
1189
+ const solid2 = fixer.SolidFromShell(shell2);
1190
+ const shapeFixer = new oc.ShapeFix_Shape_1(solid2);
1191
+ const shapeFixProgress = new oc.Message_ProgressRange_1();
1192
+ try {
1193
+ shapeFixer.Perform(shapeFixProgress);
1194
+ const fixed = shapeFixer.Shape();
1195
+ return errors.ok(shapeTypes.createSolid(fixed));
1196
+ } finally {
1197
+ shapeFixProgress.delete();
1198
+ shapeFixer.delete();
1199
+ }
1200
+ } catch {
1201
+ return errors.ok(shapeTypes.castShape(sewn));
1202
+ } finally {
1203
+ fixer.delete();
1204
+ }
1205
+ } finally {
1206
+ sewing.delete();
1207
+ }
1208
+ } catch (e) {
1209
+ const msg = e instanceof Error ? e.message : String(e);
1210
+ return errors.err(errors.occtError(errors.BrepErrorCode.ROOF_FAILED, `Roof generation failed: ${msg}`, e));
1211
+ }
1212
+ }
1213
+ function solveConstraints(nodes, constraints) {
1214
+ const transforms = /* @__PURE__ */ new Map();
1215
+ for (const node of nodes) {
1216
+ transforms.set(node, {
1217
+ position: [0, 0, 0],
1218
+ rotation: [1, 0, 0, 0]
1219
+ });
1220
+ }
1221
+ for (const c of constraints) {
1222
+ if (c.type === "coincident" && c.entityA && c.entityB) {
1223
+ const a = c.entityA;
1224
+ const b = c.entityB;
1225
+ if (a.entity.type === "plane" && b.entity.type === "plane") {
1226
+ const aNormal = a.entity.normal ?? [0, 0, 1];
1227
+ const aOrigin = a.entity.origin;
1228
+ const bOrigin = b.entity.origin;
1229
+ const dot = aNormal[0] * (aOrigin[0] - bOrigin[0]) + aNormal[1] * (aOrigin[1] - bOrigin[1]) + aNormal[2] * (aOrigin[2] - bOrigin[2]);
1230
+ const pos = [dot * aNormal[0], dot * aNormal[1], dot * aNormal[2]];
1231
+ transforms.set(b.node, { position: pos, rotation: [1, 0, 0, 0] });
1232
+ }
1233
+ }
1234
+ if (c.type === "distance" && c.entityA && c.entityB && c.value !== void 0) {
1235
+ const a = c.entityA;
1236
+ const b = c.entityB;
1237
+ if (a.entity.type === "plane" && b.entity.type === "plane") {
1238
+ const aNormal = a.entity.normal ?? [0, 0, 1];
1239
+ const aOrigin = a.entity.origin;
1240
+ const bOrigin = b.entity.origin;
1241
+ const currentDist = aNormal[0] * (aOrigin[0] - bOrigin[0]) + aNormal[1] * (aOrigin[1] - bOrigin[1]) + aNormal[2] * (aOrigin[2] - bOrigin[2]);
1242
+ const offset2 = currentDist + c.value;
1243
+ const pos = [offset2 * aNormal[0], offset2 * aNormal[1], offset2 * aNormal[2]];
1244
+ transforms.set(b.node, { position: pos, rotation: [1, 0, 0, 0] });
1245
+ }
1246
+ }
1247
+ }
1248
+ return { transforms, dof: 0, converged: true };
1249
+ }
1250
+ function extractEntity(mate) {
1251
+ if (mate.face) {
1252
+ const origin = faceFns.faceCenter(mate.face);
1253
+ const normal = faceFns.normalAt(mate.face);
1254
+ return { type: "plane", origin, normal };
1255
+ }
1256
+ if (mate.point) {
1257
+ return { type: "point", origin: mate.point };
1258
+ }
1259
+ return null;
1260
+ }
1261
+ function addMate(assembly, constraint) {
1262
+ const existing = assembly.mates ?? [];
1263
+ return { ...assembly, mates: [...existing, constraint] };
1264
+ }
1265
+ function solveAssembly(assembly) {
1266
+ const mates = assembly.mates;
1267
+ if (!mates || mates.length === 0) {
1268
+ return errors.err(
1269
+ errors.validationError(errors.BrepErrorCode.ASSEMBLY_MATE_INVALID, "solveAssembly: no mates defined")
1270
+ );
1271
+ }
1272
+ try {
1273
+ const nodes = [];
1274
+ operations.walkAssembly(assembly, (node) => {
1275
+ nodes.push(node.name);
1276
+ });
1277
+ const solverConstraints = [];
1278
+ for (const mate of mates) {
1279
+ if (mate.type === "fixed") {
1280
+ solverConstraints.push({
1281
+ type: "fixed",
1282
+ entityA: { node: mate.entity.node, entity: { type: "point", origin: [0, 0, 0] } }
1283
+ });
1284
+ continue;
1285
+ }
1286
+ if (mate.type === "coincident") {
1287
+ const entA = extractEntity(mate.entityA);
1288
+ const entB = extractEntity(mate.entityB);
1289
+ if (!entA || !entB) {
1290
+ return errors.err(
1291
+ errors.validationError(
1292
+ errors.BrepErrorCode.ASSEMBLY_MATE_INVALID,
1293
+ "solveAssembly: could not extract geometry from mate entities"
1294
+ )
1295
+ );
1296
+ }
1297
+ solverConstraints.push({
1298
+ type: "coincident",
1299
+ entityA: { node: mate.entityA.node, entity: entA },
1300
+ entityB: { node: mate.entityB.node, entity: entB }
1301
+ });
1302
+ }
1303
+ if (mate.type === "distance") {
1304
+ const entA = extractEntity(mate.entityA);
1305
+ const entB = extractEntity(mate.entityB);
1306
+ if (!entA || !entB) {
1307
+ return errors.err(
1308
+ errors.validationError(
1309
+ errors.BrepErrorCode.ASSEMBLY_MATE_INVALID,
1310
+ "solveAssembly: could not extract geometry from mate entities"
1311
+ )
1312
+ );
1313
+ }
1314
+ solverConstraints.push({
1315
+ type: "distance",
1316
+ entityA: { node: mate.entityA.node, entity: entA },
1317
+ entityB: { node: mate.entityB.node, entity: entB },
1318
+ value: mate.distance
1319
+ });
1320
+ }
1321
+ if (mate.type === "angle") {
1322
+ const entA = extractEntity(mate.entityA);
1323
+ const entB = extractEntity(mate.entityB);
1324
+ if (!entA || !entB) {
1325
+ return errors.err(
1326
+ errors.validationError(
1327
+ errors.BrepErrorCode.ASSEMBLY_MATE_INVALID,
1328
+ "solveAssembly: could not extract geometry from mate entities"
1329
+ )
1330
+ );
1331
+ }
1332
+ solverConstraints.push({
1333
+ type: "angle",
1334
+ entityA: { node: mate.entityA.node, entity: entA },
1335
+ entityB: { node: mate.entityB.node, entity: entB },
1336
+ value: mate.angle
1337
+ });
1338
+ }
1339
+ if (mate.type === "concentric") {
1340
+ const entA = extractEntity(mate.axisA);
1341
+ const entB = extractEntity(mate.axisB);
1342
+ if (!entA || !entB) {
1343
+ return errors.err(
1344
+ errors.validationError(
1345
+ errors.BrepErrorCode.ASSEMBLY_MATE_INVALID,
1346
+ "solveAssembly: could not extract geometry from mate entities"
1347
+ )
1348
+ );
1349
+ }
1350
+ solverConstraints.push({
1351
+ type: "concentric",
1352
+ entityA: { node: mate.axisA.node, entity: entA },
1353
+ entityB: { node: mate.axisB.node, entity: entB }
1354
+ });
1355
+ }
1356
+ }
1357
+ const result2 = solveConstraints(nodes, solverConstraints);
1358
+ if (!result2.converged) ;
1359
+ return errors.ok({
1360
+ transforms: result2.transforms,
1361
+ dof: result2.dof,
1362
+ converged: result2.converged
1363
+ });
1364
+ } catch (e) {
1365
+ const raw = e instanceof Error ? e.message : String(e);
1366
+ return errors.err(errors.occtError(errors.BrepErrorCode.ASSEMBLY_SOLVE_FAILED, `Assembly solve failed: ${raw}`, e));
1367
+ }
1368
+ }
148
1369
  function checkInterference(shape1, shape2, tolerance = 1e-6) {
149
1370
  if (shape1.wrapped.IsNull()) {
150
1371
  return errors.err(
@@ -182,6 +1403,458 @@ function checkAllInterferences(shapes, tolerance = 1e-6) {
182
1403
  });
183
1404
  return pairs;
184
1405
  }
1406
+ function parseEntities(text) {
1407
+ const lines = text.split(/\r?\n/);
1408
+ const entities = [];
1409
+ let inEntities = false;
1410
+ let current;
1411
+ for (let i = 0; i < lines.length - 1; i += 2) {
1412
+ const codeLine = lines[i];
1413
+ const valueLine = lines[i + 1];
1414
+ if (codeLine === void 0 || valueLine === void 0) continue;
1415
+ const code = parseInt(codeLine.trim(), 10);
1416
+ const value = valueLine.trim();
1417
+ if (isNaN(code)) continue;
1418
+ if (code === 2 && value === "ENTITIES") {
1419
+ inEntities = true;
1420
+ continue;
1421
+ }
1422
+ if (!inEntities) continue;
1423
+ if (code === 0) {
1424
+ if (value === "ENDSEC" || value === "EOF") {
1425
+ if (current) entities.push(current);
1426
+ break;
1427
+ }
1428
+ if (current) entities.push(current);
1429
+ current = { type: value, layer: "0", data: /* @__PURE__ */ new Map() };
1430
+ continue;
1431
+ }
1432
+ if (current) {
1433
+ if (code === 8) {
1434
+ current.layer = value;
1435
+ } else {
1436
+ current.data.set(code, value);
1437
+ }
1438
+ }
1439
+ }
1440
+ return entities;
1441
+ }
1442
+ function getNum(data, code, fallback = 0) {
1443
+ const v = data.get(code);
1444
+ if (v === void 0) return fallback;
1445
+ const n = parseFloat(v);
1446
+ return isNaN(n) ? fallback : n;
1447
+ }
1448
+ function entityToEdge(entity, oc) {
1449
+ const { type, data } = entity;
1450
+ if (type === "LINE") {
1451
+ const p1 = new oc.gp_Pnt_3(getNum(data, 10), getNum(data, 20), getNum(data, 30));
1452
+ const p2 = new oc.gp_Pnt_3(getNum(data, 11), getNum(data, 21), getNum(data, 31));
1453
+ try {
1454
+ const builder = new oc.BRepBuilderAPI_MakeEdge_3(p1, p2);
1455
+ const edge = builder.Edge();
1456
+ builder.delete();
1457
+ return edge;
1458
+ } finally {
1459
+ p1.delete();
1460
+ p2.delete();
1461
+ }
1462
+ }
1463
+ if (type === "CIRCLE") {
1464
+ const cx = getNum(data, 10);
1465
+ const cy = getNum(data, 20);
1466
+ const cz = getNum(data, 30);
1467
+ const radius = getNum(data, 40);
1468
+ const center = new oc.gp_Pnt_3(cx, cy, cz);
1469
+ const dir = new oc.gp_Dir_4(0, 0, 1);
1470
+ const ax2 = new oc.gp_Ax2_3(center, dir);
1471
+ const circ = new oc.gp_Circ_2(ax2, radius);
1472
+ try {
1473
+ const builder = new oc.BRepBuilderAPI_MakeEdge_8(circ);
1474
+ const edge = builder.Edge();
1475
+ builder.delete();
1476
+ return edge;
1477
+ } finally {
1478
+ center.delete();
1479
+ dir.delete();
1480
+ ax2.delete();
1481
+ circ.delete();
1482
+ }
1483
+ }
1484
+ if (type === "ARC") {
1485
+ const cx = getNum(data, 10);
1486
+ const cy = getNum(data, 20);
1487
+ const cz = getNum(data, 30);
1488
+ const radius = getNum(data, 40);
1489
+ const startAngleDeg = getNum(data, 50);
1490
+ const endAngleDeg = getNum(data, 51);
1491
+ const startAngle = startAngleDeg * Math.PI / 180;
1492
+ const endAngle = endAngleDeg * Math.PI / 180;
1493
+ const center = new oc.gp_Pnt_3(cx, cy, cz);
1494
+ const dir = new oc.gp_Dir_4(0, 0, 1);
1495
+ const ax2 = new oc.gp_Ax2_3(center, dir);
1496
+ const circ = new oc.gp_Circ_2(ax2, radius);
1497
+ try {
1498
+ const builder = new oc.BRepBuilderAPI_MakeEdge_9(circ, startAngle, endAngle);
1499
+ const edge = builder.Edge();
1500
+ builder.delete();
1501
+ return edge;
1502
+ } finally {
1503
+ center.delete();
1504
+ dir.delete();
1505
+ ax2.delete();
1506
+ circ.delete();
1507
+ }
1508
+ }
1509
+ return void 0;
1510
+ }
1511
+ async function importDXF(blob, options) {
1512
+ const oc = occtBoundary.getKernel().oc;
1513
+ let text;
1514
+ try {
1515
+ text = await blob.text();
1516
+ } catch (cause) {
1517
+ return errors.err(errors.ioError(errors.BrepErrorCode.DXF_IMPORT_FAILED, "Failed to read DXF blob", cause));
1518
+ }
1519
+ const allEntities = parseEntities(text);
1520
+ const entities = options?.layer !== void 0 ? allEntities.filter((e) => e.layer === options.layer) : allEntities;
1521
+ if (entities.length === 0) {
1522
+ return errors.ok([]);
1523
+ }
1524
+ const edges = [];
1525
+ try {
1526
+ for (const entity of entities) {
1527
+ const edge = entityToEdge(entity, oc);
1528
+ if (edge !== void 0) {
1529
+ edges.push(edge);
1530
+ }
1531
+ }
1532
+ if (edges.length === 0) {
1533
+ return errors.ok([]);
1534
+ }
1535
+ const wireBuilder = new oc.BRepBuilderAPI_MakeWire_1();
1536
+ try {
1537
+ for (const edge of edges) {
1538
+ wireBuilder.Add_1(edge);
1539
+ }
1540
+ if (wireBuilder.IsDone()) {
1541
+ const wire2 = wireBuilder.Wire();
1542
+ return errors.ok([shapeTypes.createWire(wire2)]);
1543
+ }
1544
+ return errors.err(
1545
+ errors.ioError(errors.BrepErrorCode.DXF_IMPORT_FAILED, "Failed to assemble DXF edges into a wire")
1546
+ );
1547
+ } finally {
1548
+ wireBuilder.delete();
1549
+ }
1550
+ } catch (cause) {
1551
+ return errors.err(
1552
+ errors.ioError(errors.BrepErrorCode.DXF_IMPORT_FAILED, "Failed to convert DXF entities to geometry", cause)
1553
+ );
1554
+ } finally {
1555
+ for (const edge of edges) {
1556
+ edge.delete();
1557
+ }
1558
+ }
1559
+ }
1560
+ async function importOBJ(blob) {
1561
+ const text = await blob.text();
1562
+ const lines = text.split("\n");
1563
+ const vertices = [];
1564
+ const faces = [];
1565
+ for (const raw of lines) {
1566
+ const line2 = raw.trim();
1567
+ if (line2.startsWith("v ")) {
1568
+ const parts = line2.split(/\s+/);
1569
+ const x = parseFloat(parts[1] ?? "");
1570
+ const y = parseFloat(parts[2] ?? "");
1571
+ const z = parseFloat(parts[3] ?? "");
1572
+ if (isNaN(x) || isNaN(y) || isNaN(z)) continue;
1573
+ vertices.push([x, y, z]);
1574
+ } else if (line2.startsWith("f ")) {
1575
+ const parts = line2.split(/\s+/).slice(1);
1576
+ const indices = [];
1577
+ for (const p of parts) {
1578
+ const idx = parseInt(p.split("/")[0] ?? "", 10);
1579
+ if (!isNaN(idx)) indices.push(idx);
1580
+ }
1581
+ if (indices.length >= 3) faces.push(indices);
1582
+ }
1583
+ }
1584
+ if (vertices.length === 0 || faces.length === 0) {
1585
+ return errors.err(errors.ioError(errors.BrepErrorCode.OBJ_IMPORT_FAILED, "OBJ file contains no valid geometry"));
1586
+ }
1587
+ try {
1588
+ return buildSolidFromMesh$1(vertices, faces);
1589
+ } catch (e) {
1590
+ const msg = e instanceof Error ? e.message : String(e);
1591
+ return errors.err(errors.ioError(errors.BrepErrorCode.OBJ_IMPORT_FAILED, `OBJ import failed: ${msg}`, e));
1592
+ }
1593
+ }
1594
+ function buildSolidFromMesh$1(vertices, faces) {
1595
+ const oc = occtBoundary.getKernel().oc;
1596
+ const sewing = new oc.BRepBuilderAPI_Sewing(1e-6, true, true, true, false);
1597
+ let faceCount = 0;
1598
+ try {
1599
+ for (const face2 of faces) {
1600
+ for (let i = 1; i < face2.length - 1; i++) {
1601
+ const rawA = face2[0] ?? 0;
1602
+ const rawB = face2[i] ?? 0;
1603
+ const rawC = face2[i + 1] ?? 0;
1604
+ const ai = rawA > 0 ? rawA - 1 : vertices.length + rawA;
1605
+ const bi = rawB > 0 ? rawB - 1 : vertices.length + rawB;
1606
+ const ci = rawC > 0 ? rawC - 1 : vertices.length + rawC;
1607
+ const va = vertices[ai];
1608
+ const vb = vertices[bi];
1609
+ const vc = vertices[ci];
1610
+ if (!va || !vb || !vc) continue;
1611
+ const triFace = buildTriFace$1(oc, va, vb, vc);
1612
+ if (triFace !== null) {
1613
+ sewing.Add(triFace);
1614
+ faceCount++;
1615
+ }
1616
+ }
1617
+ }
1618
+ if (faceCount === 0) {
1619
+ return errors.err(
1620
+ errors.ioError(errors.BrepErrorCode.OBJ_IMPORT_FAILED, "No valid triangular faces could be built")
1621
+ );
1622
+ }
1623
+ const progress = new oc.Message_ProgressRange_1();
1624
+ sewing.Perform(progress);
1625
+ progress.delete();
1626
+ const sewn = sewing.SewedShape();
1627
+ const fixer = new oc.ShapeFix_Solid_1();
1628
+ try {
1629
+ const shell2 = oc.TopoDS.Shell_1(sewn);
1630
+ const solid2 = fixer.SolidFromShell(shell2);
1631
+ return errors.ok(shapeTypes.castShape(solid2));
1632
+ } catch {
1633
+ return errors.ok(shapeTypes.castShape(sewn));
1634
+ } finally {
1635
+ fixer.delete();
1636
+ }
1637
+ } finally {
1638
+ sewing.delete();
1639
+ }
1640
+ }
1641
+ function buildTriFace$1(oc, a, b, c) {
1642
+ const gpA = new oc.gp_Pnt_3(a[0], a[1], a[2]);
1643
+ const gpB = new oc.gp_Pnt_3(b[0], b[1], b[2]);
1644
+ const gpC = new oc.gp_Pnt_3(c[0], c[1], c[2]);
1645
+ const e1 = new oc.BRepBuilderAPI_MakeEdge_3(gpA, gpB);
1646
+ const e2 = new oc.BRepBuilderAPI_MakeEdge_3(gpB, gpC);
1647
+ const e3 = new oc.BRepBuilderAPI_MakeEdge_3(gpC, gpA);
1648
+ const wireBuilder = new oc.BRepBuilderAPI_MakeWire_1();
1649
+ wireBuilder.Add_1(e1.Edge());
1650
+ wireBuilder.Add_1(e2.Edge());
1651
+ wireBuilder.Add_1(e3.Edge());
1652
+ let face2 = null;
1653
+ if (wireBuilder.IsDone()) {
1654
+ const makeFace = new oc.BRepBuilderAPI_MakeFace_15(wireBuilder.Wire(), false);
1655
+ if (makeFace.IsDone()) {
1656
+ face2 = makeFace.Face();
1657
+ }
1658
+ makeFace.delete();
1659
+ }
1660
+ wireBuilder.delete();
1661
+ e1.delete();
1662
+ e2.delete();
1663
+ e3.delete();
1664
+ gpA.delete();
1665
+ gpB.delete();
1666
+ gpC.delete();
1667
+ return face2;
1668
+ }
1669
+ function extractFromZip(data, target) {
1670
+ let eocdOffset = -1;
1671
+ for (let i = data.length - 22; i >= 0; i--) {
1672
+ if (data[i] === 80 && data[i + 1] === 75 && data[i + 2] === 5 && data[i + 3] === 6) {
1673
+ eocdOffset = i;
1674
+ break;
1675
+ }
1676
+ }
1677
+ if (eocdOffset < 0) return null;
1678
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
1679
+ const cdOffset = view.getUint32(eocdOffset + 16, true);
1680
+ const cdSize = view.getUint32(eocdOffset + 12, true);
1681
+ const cdEnd = cdOffset + cdSize;
1682
+ let pos = cdOffset;
1683
+ const decoder = new TextDecoder();
1684
+ while (pos < cdEnd) {
1685
+ const sig = view.getUint32(pos, true);
1686
+ if (sig !== 33639248) break;
1687
+ const nameLen = view.getUint16(pos + 28, true);
1688
+ const extraLen = view.getUint16(pos + 30, true);
1689
+ const commentLen = view.getUint16(pos + 32, true);
1690
+ const localOffset = view.getUint32(pos + 42, true);
1691
+ const name = decoder.decode(data.subarray(pos + 46, pos + 46 + nameLen));
1692
+ if (name === target) {
1693
+ const compressionMethod = view.getUint16(localOffset + 8, true);
1694
+ if (compressionMethod !== 0) {
1695
+ return null;
1696
+ }
1697
+ const localNameLen = view.getUint16(localOffset + 26, true);
1698
+ const localExtraLen = view.getUint16(localOffset + 28, true);
1699
+ const compressedSize = view.getUint32(localOffset + 18, true);
1700
+ const dataStart = localOffset + 30 + localNameLen + localExtraLen;
1701
+ return data.subarray(dataStart, dataStart + compressedSize);
1702
+ }
1703
+ pos += 46 + nameLen + extraLen + commentLen;
1704
+ }
1705
+ return null;
1706
+ }
1707
+ function isAttrChar(code) {
1708
+ return code >= 97 && code <= 122 || // a-z
1709
+ code >= 65 && code <= 90 || // A-Z
1710
+ code >= 48 && code <= 57 || // 0-9
1711
+ code === 95;
1712
+ }
1713
+ function parseTagAttrs(tag) {
1714
+ const attrs = {};
1715
+ let pos = 0;
1716
+ while (pos < tag.length) {
1717
+ const eq = tag.indexOf('="', pos);
1718
+ if (eq < 0) break;
1719
+ let nameStart = eq;
1720
+ while (nameStart > 0 && isAttrChar(tag.charCodeAt(nameStart - 1))) nameStart--;
1721
+ if (nameStart === eq) {
1722
+ pos = eq + 2;
1723
+ continue;
1724
+ }
1725
+ const name = tag.slice(nameStart, eq);
1726
+ const valStart = eq + 2;
1727
+ const closeQuote = tag.indexOf('"', valStart);
1728
+ if (closeQuote < 0) break;
1729
+ attrs[name] = tag.slice(valStart, closeQuote);
1730
+ pos = closeQuote + 1;
1731
+ }
1732
+ return attrs;
1733
+ }
1734
+ function findTags(xml, tagName) {
1735
+ const tags = [];
1736
+ const needle = `<${tagName} `;
1737
+ let pos = 0;
1738
+ while (pos < xml.length) {
1739
+ const start = xml.indexOf(needle, pos);
1740
+ if (start < 0) break;
1741
+ const end = xml.indexOf(">", start);
1742
+ if (end < 0) break;
1743
+ tags.push(xml.slice(start, end + 1));
1744
+ pos = end + 1;
1745
+ }
1746
+ return tags;
1747
+ }
1748
+ function parseModelXml(xml) {
1749
+ const vertices = [];
1750
+ const triangles = [];
1751
+ for (const tag of findTags(xml, "vertex")) {
1752
+ const a = parseTagAttrs(tag);
1753
+ if (a["x"] !== void 0 && a["y"] !== void 0 && a["z"] !== void 0) {
1754
+ vertices.push([parseFloat(a["x"]), parseFloat(a["y"]), parseFloat(a["z"])]);
1755
+ }
1756
+ }
1757
+ for (const tag of findTags(xml, "triangle")) {
1758
+ const a = parseTagAttrs(tag);
1759
+ if (a["v1"] !== void 0 && a["v2"] !== void 0 && a["v3"] !== void 0) {
1760
+ triangles.push([parseInt(a["v1"], 10), parseInt(a["v2"], 10), parseInt(a["v3"], 10)]);
1761
+ }
1762
+ }
1763
+ return { vertices, triangles };
1764
+ }
1765
+ function buildTriFace(oc, a, b, c) {
1766
+ const gpA = new oc.gp_Pnt_3(a[0], a[1], a[2]);
1767
+ const gpB = new oc.gp_Pnt_3(b[0], b[1], b[2]);
1768
+ const gpC = new oc.gp_Pnt_3(c[0], c[1], c[2]);
1769
+ const e1 = new oc.BRepBuilderAPI_MakeEdge_3(gpA, gpB);
1770
+ const e2 = new oc.BRepBuilderAPI_MakeEdge_3(gpB, gpC);
1771
+ const e3 = new oc.BRepBuilderAPI_MakeEdge_3(gpC, gpA);
1772
+ const wireBuilder = new oc.BRepBuilderAPI_MakeWire_1();
1773
+ wireBuilder.Add_1(e1.Edge());
1774
+ wireBuilder.Add_1(e2.Edge());
1775
+ wireBuilder.Add_1(e3.Edge());
1776
+ let face2 = null;
1777
+ if (wireBuilder.IsDone()) {
1778
+ const makeFace = new oc.BRepBuilderAPI_MakeFace_15(wireBuilder.Wire(), false);
1779
+ if (makeFace.IsDone()) {
1780
+ face2 = makeFace.Face();
1781
+ }
1782
+ makeFace.delete();
1783
+ }
1784
+ wireBuilder.delete();
1785
+ e1.delete();
1786
+ e2.delete();
1787
+ e3.delete();
1788
+ gpA.delete();
1789
+ gpB.delete();
1790
+ gpC.delete();
1791
+ return face2;
1792
+ }
1793
+ function buildSolidFromMesh(mesh2) {
1794
+ const oc = occtBoundary.getKernel().oc;
1795
+ const sewing = new oc.BRepBuilderAPI_Sewing(1e-6, true, true, true, false);
1796
+ let faceCount = 0;
1797
+ try {
1798
+ for (const [v1, v2, v3] of mesh2.triangles) {
1799
+ const va = mesh2.vertices[v1];
1800
+ const vb = mesh2.vertices[v2];
1801
+ const vc = mesh2.vertices[v3];
1802
+ if (!va || !vb || !vc) continue;
1803
+ const triFace = buildTriFace(oc, va, vb, vc);
1804
+ if (triFace !== null) {
1805
+ sewing.Add(triFace);
1806
+ faceCount++;
1807
+ }
1808
+ }
1809
+ if (faceCount === 0) {
1810
+ return errors.err(
1811
+ errors.ioError(errors.BrepErrorCode.THREEMF_IMPORT_FAILED, "No valid triangular faces could be built")
1812
+ );
1813
+ }
1814
+ const progress = new oc.Message_ProgressRange_1();
1815
+ sewing.Perform(progress);
1816
+ progress.delete();
1817
+ const sewn = sewing.SewedShape();
1818
+ const fixer = new oc.ShapeFix_Solid_1();
1819
+ try {
1820
+ const shell2 = oc.TopoDS.Shell_1(sewn);
1821
+ const solid2 = fixer.SolidFromShell(shell2);
1822
+ return errors.ok(shapeTypes.castShape(solid2));
1823
+ } catch {
1824
+ return errors.ok(shapeTypes.castShape(sewn));
1825
+ } finally {
1826
+ fixer.delete();
1827
+ }
1828
+ } finally {
1829
+ sewing.delete();
1830
+ }
1831
+ }
1832
+ async function importThreeMF(blob) {
1833
+ try {
1834
+ const arrayBuf = await blob.arrayBuffer();
1835
+ const data = new Uint8Array(arrayBuf);
1836
+ const modelData = extractFromZip(data, "3D/3dmodel.model");
1837
+ if (!modelData) {
1838
+ return errors.err(
1839
+ errors.ioError(
1840
+ errors.BrepErrorCode.THREEMF_IMPORT_FAILED,
1841
+ "3MF archive does not contain 3D/3dmodel.model (or uses unsupported compression)"
1842
+ )
1843
+ );
1844
+ }
1845
+ const xml = new TextDecoder().decode(modelData);
1846
+ const parsed = parseModelXml(xml);
1847
+ if (parsed.vertices.length === 0 || parsed.triangles.length === 0) {
1848
+ return errors.err(
1849
+ errors.ioError(errors.BrepErrorCode.THREEMF_IMPORT_FAILED, "3MF model contains no valid geometry")
1850
+ );
1851
+ }
1852
+ return buildSolidFromMesh(parsed);
1853
+ } catch (e) {
1854
+ const msg = e instanceof Error ? e.message : String(e);
1855
+ return errors.err(errors.ioError(errors.BrepErrorCode.THREEMF_IMPORT_FAILED, `3MF import failed: ${msg}`, e));
1856
+ }
1857
+ }
185
1858
  function resolve(s) {
186
1859
  if ("__wrapped" in s) {
187
1860
  return s.val;
@@ -248,18 +1921,18 @@ function ellipsoid(rx, ry, rz, options) {
248
1921
  return solid2;
249
1922
  }
250
1923
  function line(from, to) {
251
- return curveBuilders.makeLine(from, to);
1924
+ return surfaceBuilders.makeLine(from, to);
252
1925
  }
253
1926
  function circle(radius, options) {
254
1927
  const axisDir = options?.axis ?? [0, 0, 1];
255
- return curveBuilders.makeCircle(radius, options?.at ?? [0, 0, 0], axisDir);
1928
+ return surfaceBuilders.makeCircle(radius, options?.at ?? [0, 0, 0], axisDir);
256
1929
  }
257
1930
  function ellipse(majorRadius, minorRadius, options) {
258
1931
  const axisDir = options?.axis ?? [0, 0, 1];
259
- return curveBuilders.makeEllipse(majorRadius, minorRadius, options?.at ?? [0, 0, 0], axisDir, options?.xDir);
1932
+ return surfaceBuilders.makeEllipse(majorRadius, minorRadius, options?.at ?? [0, 0, 0], axisDir, options?.xDir);
260
1933
  }
261
1934
  function helix(pitch, height, radius, options) {
262
- return curveBuilders.makeHelix(
1935
+ return surfaceBuilders.makeHelix(
263
1936
  pitch,
264
1937
  height,
265
1938
  radius,
@@ -269,11 +1942,11 @@ function helix(pitch, height, radius, options) {
269
1942
  );
270
1943
  }
271
1944
  function threePointArc(p1, p2, p3) {
272
- return curveBuilders.makeThreePointArc(p1, p2, p3);
1945
+ return surfaceBuilders.makeThreePointArc(p1, p2, p3);
273
1946
  }
274
1947
  function ellipseArc(majorRadius, minorRadius, startAngle, endAngle, options) {
275
1948
  const axisDir = options?.axis ?? [0, 0, 1];
276
- return curveBuilders.makeEllipseArc(
1949
+ return surfaceBuilders.makeEllipseArc(
277
1950
  majorRadius,
278
1951
  minorRadius,
279
1952
  startAngle * vecOps.DEG2RAD,
@@ -284,28 +1957,28 @@ function ellipseArc(majorRadius, minorRadius, startAngle, endAngle, options) {
284
1957
  );
285
1958
  }
286
1959
  function bsplineApprox(points, config) {
287
- return curveBuilders.makeBSplineApproximation(points, config);
1960
+ return surfaceBuilders.makeBSplineApproximation(points, config);
288
1961
  }
289
1962
  function bezier(points) {
290
- return curveBuilders.makeBezierCurve(points);
1963
+ return surfaceBuilders.makeBezierCurve(points);
291
1964
  }
292
1965
  function tangentArc(startPoint, startTgt, endPoint) {
293
- return curveBuilders.makeTangentArc(startPoint, startTgt, endPoint);
1966
+ return surfaceBuilders.makeTangentArc(startPoint, startTgt, endPoint);
294
1967
  }
295
1968
  function wire(listOfEdges) {
296
- return curveBuilders.assembleWire(listOfEdges);
1969
+ return surfaceBuilders.assembleWire(listOfEdges);
297
1970
  }
298
1971
  function face(w, holes) {
299
- return Blueprint.makeFace(w, holes);
1972
+ return surfaceBuilders.makeFace(w, holes);
300
1973
  }
301
1974
  function filledFace(w) {
302
- return Blueprint.makeNonPlanarFace(w);
1975
+ return surfaceBuilders.makeNonPlanarFace(w);
303
1976
  }
304
1977
  function subFace(originFace, w) {
305
- return Blueprint.makeNewFaceWithinFace(originFace, w);
1978
+ return surfaceBuilders.makeNewFaceWithinFace(originFace, w);
306
1979
  }
307
1980
  function polygon(points) {
308
- return Blueprint.makePolygon(points);
1981
+ return surfaceBuilders.makePolygon(points);
309
1982
  }
310
1983
  function vertex(point) {
311
1984
  return loft$2.makeVertex(point);
@@ -323,7 +1996,7 @@ function sewShells(facesOrShells, ignoreType) {
323
1996
  return loft$2.weldShellsAndFaces(facesOrShells, ignoreType);
324
1997
  }
325
1998
  function addHoles(f, holes) {
326
- return Blueprint.addHolesInFace(f, holes);
1999
+ return surfaceBuilders.addHolesInFace(f, holes);
327
2000
  }
328
2001
  function validateNotNull(shape2, label) {
329
2002
  if (shape2.wrapped.IsNull()) {
@@ -342,9 +2015,11 @@ function thicken$1(shape2, thickness) {
342
2015
  const progress = r(new oc.Message_ProgressRange_1());
343
2016
  builder.Build(progress);
344
2017
  const resultOc = builder.Shape();
345
- const cast2 = shapeTypes.castShape(resultOc);
346
- shapeFns.propagateOrigins(builder, [shape2], cast2);
347
- return errors.ok(cast2);
2018
+ const cast = shapeTypes.castShape(resultOc);
2019
+ shapeFns.propagateOrigins(builder, [shape2], cast);
2020
+ booleanFns.propagateFaceTags(builder, [shape2], cast);
2021
+ booleanFns.propagateColors(builder, [shape2], cast);
2022
+ return errors.ok(cast);
348
2023
  } catch (e) {
349
2024
  const raw = e instanceof Error ? e.message : String(e);
350
2025
  return errors.err(errors.occtError("THICKEN_FAILED", `Thicken operation failed: ${raw}`, e));
@@ -403,12 +2078,14 @@ function fillet$1(shape2, edges, radius) {
403
2078
  }
404
2079
  }
405
2080
  const resultOc = builder.Shape();
406
- const cast2 = shapeTypes.castShape(resultOc);
407
- if (!shapeTypes.isShape3D(cast2)) {
2081
+ const cast = shapeTypes.castShape(resultOc);
2082
+ if (!shapeTypes.isShape3D(cast)) {
408
2083
  return errors.err(errors.occtError("FILLET_RESULT_NOT_3D", "Fillet result is not a 3D shape"));
409
2084
  }
410
- shapeFns.propagateOrigins(builder, [shape2], cast2);
411
- return errors.ok(cast2);
2085
+ shapeFns.propagateOrigins(builder, [shape2], cast);
2086
+ booleanFns.propagateFaceTags(builder, [shape2], cast);
2087
+ booleanFns.propagateColors(builder, [shape2], cast);
2088
+ return errors.ok(cast);
412
2089
  } catch (e) {
413
2090
  const raw = e instanceof Error ? e.message : String(e);
414
2091
  return errors.err(
@@ -497,12 +2174,14 @@ function chamfer$1(shape2, edges, distance) {
497
2174
  }
498
2175
  }
499
2176
  const resultOc = builder.Shape();
500
- const cast2 = shapeTypes.castShape(resultOc);
501
- if (!shapeTypes.isShape3D(cast2)) {
2177
+ const cast = shapeTypes.castShape(resultOc);
2178
+ if (!shapeTypes.isShape3D(cast)) {
502
2179
  return errors.err(errors.occtError("CHAMFER_RESULT_NOT_3D", "Chamfer result is not a 3D shape"));
503
2180
  }
504
- shapeFns.propagateOrigins(builder, [shape2], cast2);
505
- return errors.ok(cast2);
2181
+ shapeFns.propagateOrigins(builder, [shape2], cast);
2182
+ booleanFns.propagateFaceTags(builder, [shape2], cast);
2183
+ booleanFns.propagateColors(builder, [shape2], cast);
2184
+ return errors.ok(cast);
506
2185
  } catch (e) {
507
2186
  const raw = e instanceof Error ? e.message : String(e);
508
2187
  return errors.err(
@@ -545,12 +2224,14 @@ function shell$1(shape2, faces, thickness, tolerance = 1e-3) {
545
2224
  progress
546
2225
  );
547
2226
  const resultOc = builder.Shape();
548
- const cast2 = shapeTypes.castShape(resultOc);
549
- if (!shapeTypes.isShape3D(cast2)) {
2227
+ const cast = shapeTypes.castShape(resultOc);
2228
+ if (!shapeTypes.isShape3D(cast)) {
550
2229
  return errors.err(errors.occtError("SHELL_RESULT_NOT_3D", "Shell result is not a 3D shape"));
551
2230
  }
552
- shapeFns.propagateOrigins(builder, [shape2], cast2);
553
- return errors.ok(cast2);
2231
+ shapeFns.propagateOrigins(builder, [shape2], cast);
2232
+ booleanFns.propagateFaceTags(builder, [shape2], cast);
2233
+ booleanFns.propagateColors(builder, [shape2], cast);
2234
+ return errors.ok(cast);
554
2235
  } catch (e) {
555
2236
  const raw = e instanceof Error ? e.message : String(e);
556
2237
  return errors.err(
@@ -585,12 +2266,14 @@ function offset$1(shape2, distance, tolerance = 1e-6) {
585
2266
  progress
586
2267
  );
587
2268
  const resultOc = builder.Shape();
588
- const cast2 = shapeTypes.castShape(resultOc);
589
- if (!shapeTypes.isShape3D(cast2)) {
2269
+ const cast = shapeTypes.castShape(resultOc);
2270
+ if (!shapeTypes.isShape3D(cast)) {
590
2271
  return errors.err(errors.occtError("OFFSET_RESULT_NOT_3D", "Offset result is not a 3D shape"));
591
2272
  }
592
- shapeFns.propagateOrigins(builder, [shape2], cast2);
593
- return errors.ok(cast2);
2273
+ shapeFns.propagateOrigins(builder, [shape2], cast);
2274
+ booleanFns.propagateFaceTags(builder, [shape2], cast);
2275
+ booleanFns.propagateColors(builder, [shape2], cast);
2276
+ return errors.ok(cast);
594
2277
  } catch (e) {
595
2278
  const raw = e instanceof Error ? e.message : String(e);
596
2279
  return errors.err(errors.occtError("OFFSET_FAILED", `Offset operation failed: ${raw}`, e));
@@ -631,6 +2314,9 @@ function intersect(a, b, options) {
631
2314
  function section(shape2, plane, options) {
632
2315
  return booleanFns.section(resolve(shape2), plane, options);
633
2316
  }
2317
+ function sectionToFace(shape2, plane, options) {
2318
+ return booleanFns.sectionToFace(resolve(shape2), plane, options);
2319
+ }
634
2320
  function split(shape2, tools) {
635
2321
  return booleanFns.split(resolve(shape2), tools);
636
2322
  }
@@ -740,7 +2426,7 @@ function toBREP(shape2) {
740
2426
  return shapeFns.toBREP(resolve(shape2));
741
2427
  }
742
2428
  function fromBREP(data) {
743
- return cast.fromBREP(data);
2429
+ return faceFns.fromBREP(data);
744
2430
  }
745
2431
  function isValid(shape2) {
746
2432
  return topology.isValid(resolve(shape2));
@@ -865,7 +2551,7 @@ function pocket(shape2, options) {
865
2551
  const targetFace = resolveTargetFace(s, options.face);
866
2552
  const normal = faceFns.normalAt(targetFace);
867
2553
  const w = toWire(profile);
868
- const faceResult = Blueprint.makeFace(w);
2554
+ const faceResult = surfaceBuilders.makeFace(w);
869
2555
  if (errors.isErr(faceResult)) return faceResult;
870
2556
  const extDir = vecOps.vecScale(vecOps.vecNormalize(normal), -depth);
871
2557
  const toolResult = operations.extrude(faceResult.value, extDir);
@@ -881,7 +2567,7 @@ function boss(shape2, options) {
881
2567
  const targetFace = resolveTargetFace(s, options.face);
882
2568
  const normal = faceFns.normalAt(targetFace);
883
2569
  const w = toWire(profile);
884
- const faceResult = Blueprint.makeFace(w);
2570
+ const faceResult = surfaceBuilders.makeFace(w);
885
2571
  if (errors.isErr(faceResult)) return faceResult;
886
2572
  const extDir = vecOps.vecScale(vecOps.vecNormalize(normal), height);
887
2573
  const toolResult = operations.extrude(faceResult.value, extDir);
@@ -1307,6 +2993,7 @@ exports.drawingFillet = drawFns.drawingFillet;
1307
2993
  exports.drawingFuse = drawFns.drawingFuse;
1308
2994
  exports.drawingIntersect = drawFns.drawingIntersect;
1309
2995
  exports.drawingToSketchOnPlane = drawFns.drawingToSketchOnPlane;
2996
+ exports.fontMetrics = drawFns.fontMetrics;
1310
2997
  exports.getFont = drawFns.getFont;
1311
2998
  exports.isProjectionPlane = drawFns.isProjectionPlane;
1312
2999
  exports.loadFont = drawFns.loadFont;
@@ -1333,6 +3020,7 @@ exports.sketchSweep = drawFns.sketchSweep;
1333
3020
  exports.sketchText = drawFns.sketchText;
1334
3021
  exports.sketchWires = drawFns.sketchWires;
1335
3022
  exports.textBlueprints = drawFns.textBlueprints;
3023
+ exports.textMetrics = drawFns.textMetrics;
1336
3024
  exports.translateDrawing = drawFns.translateDrawing;
1337
3025
  exports.createNamedPlane = vectors.createNamedPlane;
1338
3026
  exports.createPlane = vectors.createPlane;
@@ -1353,8 +3041,21 @@ exports.iterEdges = shapeFns.iterEdges;
1353
3041
  exports.iterFaces = shapeFns.iterFaces;
1354
3042
  exports.iterVertices = shapeFns.iterVertices;
1355
3043
  exports.iterWires = shapeFns.iterWires;
3044
+ exports.resize = shapeFns.resize;
1356
3045
  exports.setShapeOrigin = shapeFns.setShapeOrigin;
1357
3046
  exports.vertexPosition = shapeFns.vertexPosition;
3047
+ exports.applyGlue = booleanFns.applyGlue;
3048
+ exports.colorFaces = booleanFns.colorFaces;
3049
+ exports.colorShape = booleanFns.colorShape;
3050
+ exports.cutAll = booleanFns.cutAll;
3051
+ exports.findFacesByTag = booleanFns.findFacesByTag;
3052
+ exports.fuseAll = booleanFns.fuseAll;
3053
+ exports.getFaceColor = booleanFns.getFaceColor;
3054
+ exports.getFaceTags = booleanFns.getFaceTags;
3055
+ exports.getShapeColor = booleanFns.getShapeColor;
3056
+ exports.getTagMetadata = booleanFns.getTagMetadata;
3057
+ exports.setTagMetadata = booleanFns.setTagMetadata;
3058
+ exports.tagFaces = booleanFns.tagFaces;
1358
3059
  exports.adjacentFaces = topology.adjacentFaces;
1359
3060
  exports.autoHeal = topology.autoHeal;
1360
3061
  exports.chamferDistAngleShape = topology.chamferDistAngle;
@@ -1369,17 +3070,24 @@ exports.toGroupedBufferGeometryData = topology.toGroupedBufferGeometryData;
1369
3070
  exports.toLineGeometryData = topology.toLineGeometryData;
1370
3071
  exports.verticesOfEdge = topology.verticesOfEdge;
1371
3072
  exports.wiresOfFace = topology.wiresOfFace;
3073
+ exports.asTopo = faceFns.asTopo;
3074
+ exports.cast = faceFns.cast;
1372
3075
  exports.classifyPointOnFace = faceFns.classifyPointOnFace;
3076
+ exports.deserializeShape = faceFns.fromBREP;
3077
+ exports.downcast = faceFns.downcast;
1373
3078
  exports.faceCenter = faceFns.faceCenter;
1374
3079
  exports.faceGeomType = faceFns.faceGeomType;
1375
3080
  exports.faceOrientation = faceFns.faceOrientation;
1376
3081
  exports.flipFaceOrientation = faceFns.flipFaceOrientation;
1377
3082
  exports.getSurfaceType = faceFns.getSurfaceType;
1378
3083
  exports.innerWires = faceFns.innerWires;
3084
+ exports.isCompSolid = faceFns.isCompSolid;
3085
+ exports.iterTopo = faceFns.iterTopo;
1379
3086
  exports.normalAt = faceFns.normalAt;
1380
3087
  exports.outerWire = faceFns.outerWire;
1381
3088
  exports.pointOnSurface = faceFns.pointOnSurface;
1382
3089
  exports.projectPointOnFace = faceFns.projectPointOnFace;
3090
+ exports.shapeType = faceFns.shapeType;
1383
3091
  exports.uvBounds = faceFns.uvBounds;
1384
3092
  exports.uvCoordinates = faceFns.uvCoordinates;
1385
3093
  exports.clearMeshCache = meshFns.clearMeshCache;
@@ -1387,9 +3095,6 @@ exports.createMeshCache = meshFns.createMeshCache;
1387
3095
  exports.exportIGES = meshFns.exportIGES;
1388
3096
  exports.exportSTEP = meshFns.exportSTEP;
1389
3097
  exports.exportSTL = meshFns.exportSTL;
1390
- exports.applyGlue = booleanFns.applyGlue;
1391
- exports.cutAll = booleanFns.cutAll;
1392
- exports.fuseAll = booleanFns.fuseAll;
1393
3098
  exports.createDistanceQuery = measurement.createDistanceQuery;
1394
3099
  exports.measureArea = measurement.measureArea;
1395
3100
  exports.measureCurvatureAt = measurement.measureCurvatureAt;
@@ -1400,13 +3105,7 @@ exports.measureLinearProps = measurement.measureLinearProps;
1400
3105
  exports.measureSurfaceProps = measurement.measureSurfaceProps;
1401
3106
  exports.measureVolume = measurement.measureVolume;
1402
3107
  exports.measureVolumeProps = measurement.measureVolumeProps;
1403
- exports.asTopo = cast.asTopo;
1404
- exports.cast = cast.cast;
1405
- exports.deserializeShape = cast.fromBREP;
1406
- exports.downcast = cast.downcast;
1407
- exports.isCompSolid = cast.isCompSolid;
1408
- exports.iterTopo = cast.iterTopo;
1409
- exports.shapeType = cast.shapeType;
3108
+ exports.fill = surfaceBuilders.fill;
1410
3109
  exports.edgeFinder = query.edgeFinder;
1411
3110
  exports.BrepBugError = result.BrepBugError;
1412
3111
  exports.bug = result.bug;
@@ -1428,6 +3127,7 @@ exports.registerHandler = worker.registerHandler;
1428
3127
  exports.rejectAll = worker.rejectAll;
1429
3128
  exports.BrepWrapperError = BrepWrapperError;
1430
3129
  exports.addHoles = addHoles;
3130
+ exports.addMate = addMate;
1431
3131
  exports.applyMatrix = applyMatrix;
1432
3132
  exports.bezier = bezier;
1433
3133
  exports.boss = boss;
@@ -1439,6 +3139,7 @@ exports.checkInterference = checkInterference;
1439
3139
  exports.circle = circle;
1440
3140
  exports.clone = clone;
1441
3141
  exports.compound = compound;
3142
+ exports.computeStraightSkeleton = computeStraightSkeleton;
1442
3143
  exports.cone = cone;
1443
3144
  exports.cut = cut;
1444
3145
  exports.cylinder = cylinder;
@@ -1453,8 +3154,13 @@ exports.filledFace = filledFace;
1453
3154
  exports.fillet = fillet;
1454
3155
  exports.fromBREP = fromBREP;
1455
3156
  exports.fuse = fuse;
3157
+ exports.guidedSweep = guidedSweep;
1456
3158
  exports.heal = heal;
1457
3159
  exports.helix = helix;
3160
+ exports.hull = hull;
3161
+ exports.importDXF = importDXF;
3162
+ exports.importOBJ = importOBJ;
3163
+ exports.importThreeMF = importThreeMF;
1458
3164
  exports.intersect = intersect;
1459
3165
  exports.isChamferRadius = isChamferRadius;
1460
3166
  exports.isEmpty = isEmpty;
@@ -1467,28 +3173,36 @@ exports.line = line;
1467
3173
  exports.loft = loft;
1468
3174
  exports.mesh = mesh;
1469
3175
  exports.meshEdges = meshEdges;
3176
+ exports.minkowski = minkowski;
1470
3177
  exports.mirror = mirror;
1471
3178
  exports.mirrorJoin = mirrorJoin;
3179
+ exports.multiSectionSweep = multiSectionSweep;
1472
3180
  exports.offset = offset;
1473
3181
  exports.offsetFace = offsetFace;
1474
3182
  exports.pocket = pocket;
1475
3183
  exports.polygon = polygon;
3184
+ exports.polyhedron = polyhedron;
1476
3185
  exports.rectangularPattern = rectangularPattern;
1477
3186
  exports.resolve = resolve;
1478
3187
  exports.resolve3D = resolve3D;
1479
3188
  exports.revolve = revolve;
3189
+ exports.roof = roof;
1480
3190
  exports.rotate = rotate;
1481
3191
  exports.scale = scale;
1482
3192
  exports.section = section;
3193
+ exports.sectionToFace = sectionToFace;
1483
3194
  exports.sewShells = sewShells;
1484
3195
  exports.shape = shape;
1485
3196
  exports.shell = shell;
1486
3197
  exports.simplify = simplify;
1487
3198
  exports.slice = slice;
1488
3199
  exports.solid = solid;
3200
+ exports.solveAssembly = solveAssembly;
1489
3201
  exports.sphere = sphere;
1490
3202
  exports.split = split;
1491
3203
  exports.subFace = subFace;
3204
+ exports.surfaceFromGrid = surfaceFromGrid;
3205
+ exports.surfaceFromImage = surfaceFromImage;
1492
3206
  exports.tangentArc = tangentArc;
1493
3207
  exports.thicken = thicken;
1494
3208
  exports.threePointArc = threePointArc;