brepjs 8.2.0 → 8.4.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 (85) 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 +3 -3
  4. package/dist/{Blueprint-D_luVeES.js → Blueprint-Bp45tnh0.js} +16 -12
  5. package/dist/{Blueprint-CTAwjJMN.cjs → Blueprint-zgFe_5Qj.cjs} +17 -13
  6. package/dist/{boolean2D-B5axNhjN.cjs → boolean2D-CfEbRMPF.cjs} +11 -11
  7. package/dist/{boolean2D-vw76Gayn.js → boolean2D-DN6ETTCq.js} +11 -11
  8. package/dist/{booleanFns-Yc3EBxdm.cjs → booleanFns-5dDG0jpA.cjs} +46 -9
  9. package/dist/{booleanFns-BhqXpQoZ.js → booleanFns-C-M6qqvB.js} +46 -9
  10. package/dist/brepjs.cjs +364 -109
  11. package/dist/brepjs.js +433 -177
  12. package/dist/{cast-C107o5ow.cjs → cast-CPNOTNFm.cjs} +3 -3
  13. package/dist/{cast-D0OhP1nV.js → cast-Cerqtxtb.js} +3 -3
  14. package/dist/core/errors.d.ts +7 -0
  15. package/dist/core/errors.d.ts.map +1 -1
  16. package/dist/core.cjs +4 -4
  17. package/dist/core.js +4 -4
  18. package/dist/{cornerFinder-DuStF5jK.cjs → cornerFinder-BQ-_VJx0.cjs} +1 -1
  19. package/dist/{cornerFinder-CPm2baSJ.js → cornerFinder-CC_MunIh.js} +1 -1
  20. package/dist/{curveBuilders-CN72XaIQ.js → curveBuilders-BREwqvuc.js} +3 -3
  21. package/dist/{curveBuilders-Du03_Yyf.cjs → curveBuilders-BkEJ-RVn.cjs} +3 -3
  22. package/dist/curveFns-VMxgfkqw.cjs +177 -0
  23. package/dist/curveFns-ZuQUBZvd.js +178 -0
  24. package/dist/{drawFns-CzBbcoXA.js → drawFns-BbhX1IUq.js} +19 -15
  25. package/dist/{drawFns-CiNxPu6J.cjs → drawFns-CKaHgGSK.cjs} +22 -18
  26. package/dist/{errors-wGhcJMpB.js → errors-CSYOlCCR.js} +10 -1
  27. package/dist/{errors-DK1VAdP4.cjs → errors-D13q2HCk.cjs} +10 -1
  28. package/dist/{faceFns-ub3CugDN.js → faceFns-CfJIbHY3.js} +4 -4
  29. package/dist/{faceFns-D1Sqnlu6.cjs → faceFns-es3GENII.cjs} +4 -4
  30. package/dist/{helpers-Dje6wrKi.cjs → helpers-C0q_FVxq.cjs} +10 -10
  31. package/dist/{helpers-BSQfs538.js → helpers-CmVkMubc.js} +7 -7
  32. package/dist/index.d.ts +3 -1
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/io.cjs +5 -5
  35. package/dist/io.js +5 -5
  36. package/dist/kernel/hullOps.d.ts +22 -0
  37. package/dist/kernel/hullOps.d.ts.map +1 -0
  38. package/dist/kernel/occtAdapter.d.ts +6 -0
  39. package/dist/kernel/occtAdapter.d.ts.map +1 -1
  40. package/dist/kernel/types.d.ts +6 -0
  41. package/dist/kernel/types.d.ts.map +1 -1
  42. package/dist/{loft-PMRx9iMG.cjs → loft-B-UCPW9P.cjs} +5 -5
  43. package/dist/{loft-BHn7GKm8.js → loft-oJq2OD3A.js} +5 -5
  44. package/dist/{measurement-B06hNs89.cjs → measurement-CYmT-C77.cjs} +3 -3
  45. package/dist/{measurement-BfhEneUl.js → measurement-Cf_SoIiR.js} +3 -3
  46. package/dist/measurement.cjs +1 -1
  47. package/dist/measurement.js +1 -1
  48. package/dist/{meshFns-BKSPaPXS.js → meshFns-CqNwW0PO.js} +6 -3
  49. package/dist/{meshFns-CFVxKBlE.cjs → meshFns-DDC_2U81.cjs} +6 -3
  50. package/dist/{occtBoundary-BFAaUtA7.cjs → occtBoundary-CocN2VKx.cjs} +419 -2
  51. package/dist/{occtBoundary-CoXB2xvx.js → occtBoundary-D_gjqgzo.js} +419 -2
  52. package/dist/{operations-CjQHEu1h.js → operations-6hdpuYmY.js} +6 -6
  53. package/dist/{operations-CdGb6IBU.cjs → operations-BQeW_DSM.cjs} +6 -6
  54. package/dist/operations.cjs +2 -2
  55. package/dist/operations.js +2 -2
  56. package/dist/query.cjs +5 -5
  57. package/dist/query.js +6 -6
  58. package/dist/result.cjs +1 -1
  59. package/dist/result.js +1 -1
  60. package/dist/{curveFns-ByeCqutv.cjs → shapeFns-B0zSdO9c.cjs} +98 -177
  61. package/dist/{curveFns-C5gSZ5EY.js → shapeFns-k1YHFwmB.js} +117 -196
  62. package/dist/{shapeTypes-UqVCIO_T.cjs → shapeTypes-BxVxLdiD.cjs} +1 -1
  63. package/dist/{shapeTypes-BU2LKv2S.js → shapeTypes-c-_pgYCx.js} +1 -1
  64. package/dist/sketching.cjs +2 -2
  65. package/dist/sketching.js +2 -2
  66. package/dist/text/textBlueprints.d.ts.map +1 -1
  67. package/dist/topology/booleanFns.d.ts.map +1 -1
  68. package/dist/topology/hullFns.d.ts +16 -0
  69. package/dist/topology/hullFns.d.ts.map +1 -0
  70. package/dist/topology/meshFns.d.ts +1 -0
  71. package/dist/topology/meshFns.d.ts.map +1 -1
  72. package/dist/topology/minkowskiFns.d.ts +20 -0
  73. package/dist/topology/minkowskiFns.d.ts.map +1 -0
  74. package/dist/topology/modifierFns.d.ts.map +1 -1
  75. package/dist/topology/shapeFns.d.ts +30 -0
  76. package/dist/topology/shapeFns.d.ts.map +1 -1
  77. package/dist/{topology-D8Au8q4i.cjs → topology-CycEc6Oe.cjs} +14 -13
  78. package/dist/{topology-BFB3LI_y.js → topology-tMKHJgw2.js} +9 -8
  79. package/dist/topology.cjs +34 -33
  80. package/dist/topology.js +59 -58
  81. package/dist/{vectors-BhfKwL9J.js → vectors-DE0XriuQ.js} +2 -2
  82. package/dist/{vectors-t1XG4LpL.cjs → vectors-DVmHF4zt.cjs} +2 -2
  83. package/dist/vectors.cjs +2 -2
  84. package/dist/vectors.js +2 -2
  85. package/package.json +5 -5
package/dist/brepjs.cjs CHANGED
@@ -1,29 +1,30 @@
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-CocN2VKx.cjs");
4
+ const errors = require("./errors-D13q2HCk.cjs");
5
+ const shapeTypes = require("./shapeTypes-BxVxLdiD.cjs");
6
6
  const vecOps = require("./vecOps-CjRL1jau.cjs");
7
- const Blueprint = require("./Blueprint-CTAwjJMN.cjs");
8
- const curveFns = require("./curveFns-ByeCqutv.cjs");
9
- const loft$2 = require("./loft-PMRx9iMG.cjs");
10
- const operations = require("./operations-CdGb6IBU.cjs");
11
- const boolean2D = require("./boolean2D-B5axNhjN.cjs");
7
+ const Blueprint = require("./Blueprint-zgFe_5Qj.cjs");
8
+ const curveFns = require("./curveFns-VMxgfkqw.cjs");
9
+ const loft$2 = require("./loft-B-UCPW9P.cjs");
10
+ const operations = require("./operations-BQeW_DSM.cjs");
11
+ const boolean2D = require("./boolean2D-CfEbRMPF.cjs");
12
12
  const _2d = require("./2d.cjs");
13
- const helpers = require("./helpers-Dje6wrKi.cjs");
13
+ const helpers = require("./helpers-C0q_FVxq.cjs");
14
14
  const io = require("./io.cjs");
15
- const drawFns = require("./drawFns-CiNxPu6J.cjs");
16
- const vectors = require("./vectors-t1XG4LpL.cjs");
17
- const topology = require("./topology-D8Au8q4i.cjs");
18
- const faceFns = require("./faceFns-D1Sqnlu6.cjs");
19
- const meshFns = require("./meshFns-CFVxKBlE.cjs");
20
- const booleanFns = require("./booleanFns-Yc3EBxdm.cjs");
21
- const measurement = require("./measurement-B06hNs89.cjs");
22
- const curveBuilders = require("./curveBuilders-Du03_Yyf.cjs");
23
- const cast = require("./cast-C107o5ow.cjs");
15
+ const drawFns = require("./drawFns-CKaHgGSK.cjs");
16
+ const vectors = require("./vectors-DVmHF4zt.cjs");
17
+ const shapeFns = require("./shapeFns-B0zSdO9c.cjs");
18
+ const topology = require("./topology-CycEc6Oe.cjs");
19
+ const faceFns = require("./faceFns-es3GENII.cjs");
20
+ const meshFns = require("./meshFns-DDC_2U81.cjs");
21
+ const booleanFns = require("./booleanFns-5dDG0jpA.cjs");
22
+ const measurement = require("./measurement-CYmT-C77.cjs");
23
+ const curveBuilders = require("./curveBuilders-BkEJ-RVn.cjs");
24
+ const cast = require("./cast-CPNOTNFm.cjs");
24
25
  const query = require("./query.cjs");
25
26
  const result = require("./result.cjs");
26
- const cornerFinder = require("./cornerFinder-DuStF5jK.cjs");
27
+ const cornerFinder = require("./cornerFinder-BQ-_VJx0.cjs");
27
28
  const worker = require("./worker.cjs");
28
29
  const errorFactories = {
29
30
  OCCT_OPERATION: (code, message, cause) => ({ kind: "OCCT_OPERATION", code, message, cause }),
@@ -97,9 +98,9 @@ function withNearestPostFilter(baseFinder, nearestPoint) {
97
98
  const candidates = baseFinder.findAll(shape2);
98
99
  if (candidates.length === 0) return [];
99
100
  let bestIdx = 0;
100
- let bestDist = vecOps.vecDistance(curveFns.vertexPosition(candidates[0]), nearestPoint);
101
+ let bestDist = vecOps.vecDistance(shapeFns.vertexPosition(candidates[0]), nearestPoint);
101
102
  for (let i = 1; i < candidates.length; i++) {
102
- const d = vecOps.vecDistance(curveFns.vertexPosition(candidates[i]), nearestPoint);
103
+ const d = vecOps.vecDistance(shapeFns.vertexPosition(candidates[i]), nearestPoint);
103
104
  if (d < bestDist) {
104
105
  bestDist = d;
105
106
  bestIdx = i;
@@ -129,13 +130,13 @@ function buildVertexFinder(filters) {
129
130
  buildVertexFinder,
130
131
  (_base, withFilter) => ({
131
132
  nearestTo: (point) => withNearestPostFilter(buildVertexFinder(filters), point),
132
- atPosition: (point, tolerance = 1e-4) => withFilter((vertex2) => vecOps.vecDistance(curveFns.vertexPosition(vertex2), point) < tolerance),
133
+ atPosition: (point, tolerance = 1e-4) => withFilter((vertex2) => vecOps.vecDistance(shapeFns.vertexPosition(vertex2), point) < tolerance),
133
134
  withinBox: (min, max) => withFilter((vertex2) => {
134
- const pos = curveFns.vertexPosition(vertex2);
135
+ const pos = shapeFns.vertexPosition(vertex2);
135
136
  return pos[0] >= min[0] - 1e-6 && pos[0] <= max[0] + 1e-6 && pos[1] >= min[1] - 1e-6 && pos[1] <= max[1] + 1e-6 && pos[2] >= min[2] - 1e-6 && pos[2] <= max[2] + 1e-6;
136
137
  }),
137
138
  atDistance: (distance, point = [0, 0, 0], tolerance = 1e-4) => withFilter((vertex2) => {
138
- const pos = curveFns.vertexPosition(vertex2);
139
+ const pos = shapeFns.vertexPosition(vertex2);
139
140
  return Math.abs(vecOps.vecDistance(pos, point) - distance) < tolerance;
140
141
  })
141
142
  })
@@ -144,6 +145,170 @@ function buildVertexFinder(filters) {
144
145
  function vertexFinder() {
145
146
  return buildVertexFinder([]);
146
147
  }
148
+ function validateNotNull$1(shape2, label) {
149
+ if (shape2.wrapped.IsNull()) {
150
+ return errors.err(errors.validationError(errors.BrepErrorCode.NULL_SHAPE_INPUT, `${label} is a null shape`));
151
+ }
152
+ return errors.ok(void 0);
153
+ }
154
+ function hull(shapes, options = {}) {
155
+ if (shapes.length === 0) {
156
+ return errors.err(
157
+ errors.validationError(
158
+ errors.BrepErrorCode.HULL_EMPTY_INPUT,
159
+ "hull: at least one shape is required",
160
+ void 0,
161
+ void 0,
162
+ "Provide one or more shapes to compute a convex hull"
163
+ )
164
+ );
165
+ }
166
+ for (const [i, shape2] of shapes.entries()) {
167
+ const check = validateNotNull$1(shape2, `hull: shapes[${i}]`);
168
+ if (errors.isErr(check)) return check;
169
+ }
170
+ const tolerance = options.tolerance ?? 0.1;
171
+ try {
172
+ const kernel = occtBoundary.getKernel();
173
+ const ocShapes = shapes.map((s) => s.wrapped);
174
+ const resultOc = kernel.hull(ocShapes, tolerance);
175
+ const cast2 = shapeTypes.castShape(resultOc);
176
+ if (!shapeTypes.isSolid(cast2)) {
177
+ return errors.err(
178
+ errors.occtError(errors.BrepErrorCode.HULL_NOT_3D, "Hull result is not a solid; input may be degenerate")
179
+ );
180
+ }
181
+ return errors.ok(cast2);
182
+ } catch (e) {
183
+ const raw = e instanceof Error ? e.message : String(e);
184
+ if (raw.includes("coplanar") || raw.includes("fewer than") || raw.includes("degenerate")) {
185
+ return errors.err(errors.occtError(errors.BrepErrorCode.HULL_DEGENERATE, `Hull degenerate: ${raw}`, e));
186
+ }
187
+ return errors.err(errors.occtError(errors.BrepErrorCode.HULL_FAILED, `Hull operation failed: ${raw}`, e));
188
+ }
189
+ }
190
+ function detectSphere(shape2) {
191
+ const oc = occtBoundary.getKernel().oc;
192
+ const faces = shapeFns.getFaces(shape2);
193
+ if (faces.length !== 1) return null;
194
+ const face2 = faces[0];
195
+ const r = shapeTypes.gcWithScope();
196
+ const adaptor = r(new oc.BRepAdaptor_Surface_2(face2.wrapped, true));
197
+ const surfType = adaptor.GetType();
198
+ if (surfType !== oc.GeomAbs_SurfaceType.GeomAbs_Sphere) return null;
199
+ const ocSphere = adaptor.Sphere();
200
+ const radius = ocSphere.Radius();
201
+ ocSphere.delete();
202
+ return radius;
203
+ }
204
+ function minkowskiSphere(shape2, radius, tolerance) {
205
+ const oc = occtBoundary.getKernel().oc;
206
+ const r = shapeTypes.gcWithScope();
207
+ try {
208
+ const offsetMaker = r(new oc.BRepOffsetAPI_MakeOffsetShape());
209
+ const progress = r(new oc.Message_ProgressRange_1());
210
+ offsetMaker.PerformByJoin(
211
+ shape2.wrapped,
212
+ radius,
213
+ tolerance,
214
+ oc.BRepOffset_Mode.BRepOffset_Skin,
215
+ false,
216
+ false,
217
+ oc.GeomAbs_JoinType.GeomAbs_Arc,
218
+ false,
219
+ progress
220
+ );
221
+ const resultShape = offsetMaker.Shape();
222
+ const wrapped = shapeTypes.castShape(resultShape);
223
+ if (!shapeTypes.isShape3D(wrapped)) {
224
+ wrapped[Symbol.dispose]();
225
+ return errors.err(
226
+ errors.typeCastError(
227
+ errors.BrepErrorCode.MINKOWSKI_NOT_3D,
228
+ "Minkowski sphere offset did not produce a 3D shape"
229
+ )
230
+ );
231
+ }
232
+ return errors.ok(wrapped);
233
+ } catch (e) {
234
+ const raw = e instanceof Error ? e.message : String(e);
235
+ return errors.err(
236
+ errors.occtError(errors.BrepErrorCode.MINKOWSKI_FAILED, `Minkowski sphere offset failed: ${raw}`, e, {
237
+ operation: "minkowski",
238
+ fastPath: "sphere"
239
+ })
240
+ );
241
+ }
242
+ }
243
+ function minkowskiGeneral(shape2, tool, _tolerance) {
244
+ const oc = occtBoundary.getKernel().oc;
245
+ try {
246
+ const shapeVerts = shapeFns.getVertices(shape2);
247
+ const toolVerts = shapeFns.getVertices(tool);
248
+ if (shapeVerts.length === 0 || toolVerts.length === 0) {
249
+ return errors.err(
250
+ errors.occtError(
251
+ errors.BrepErrorCode.MINKOWSKI_FAILED,
252
+ "Minkowski sum: one or both shapes have no vertices",
253
+ void 0,
254
+ {
255
+ operation: "minkowski"
256
+ }
257
+ )
258
+ );
259
+ }
260
+ const sumPoints = [];
261
+ for (const sv of shapeVerts) {
262
+ const r1 = shapeTypes.gcWithScope();
263
+ const pa = r1(oc.BRep_Tool.Pnt(sv.wrapped));
264
+ const ax = pa.X(), ay = pa.Y(), az = pa.Z();
265
+ for (const tv of toolVerts) {
266
+ const r2 = shapeTypes.gcWithScope();
267
+ const pb = r2(oc.BRep_Tool.Pnt(tv.wrapped));
268
+ const bx = pb.X(), by = pb.Y(), bz = pb.Z();
269
+ sumPoints.push({ x: ax + bx, y: ay + by, z: az + bz });
270
+ }
271
+ }
272
+ const kernel = occtBoundary.getKernel();
273
+ const hullShape = kernel.hullFromPoints(sumPoints, _tolerance);
274
+ const wrapped = shapeTypes.castShape(hullShape);
275
+ if (!shapeTypes.isShape3D(wrapped)) {
276
+ wrapped[Symbol.dispose]();
277
+ return errors.err(
278
+ errors.typeCastError(errors.BrepErrorCode.MINKOWSKI_NOT_3D, "Minkowski hull did not produce a 3D shape")
279
+ );
280
+ }
281
+ return errors.ok(wrapped);
282
+ } catch (e) {
283
+ const raw = e instanceof Error ? e.message : String(e);
284
+ return errors.err(
285
+ errors.occtError(errors.BrepErrorCode.MINKOWSKI_FAILED, `Minkowski general path failed: ${raw}`, e, {
286
+ operation: "minkowski"
287
+ })
288
+ );
289
+ }
290
+ }
291
+ function minkowski(shape2, tool, options = {}) {
292
+ const { tolerance = 1e-6 } = options;
293
+ if (shape2.wrapped.IsNull()) {
294
+ return errors.err(errors.validationError(errors.BrepErrorCode.NULL_SHAPE_INPUT, "minkowski: shape is a null shape"));
295
+ }
296
+ if (tool.wrapped.IsNull()) {
297
+ return errors.err(
298
+ errors.validationError(errors.BrepErrorCode.MINKOWSKI_NULL_TOOL, "minkowski: tool is a null shape")
299
+ );
300
+ }
301
+ if (!shapeTypes.isShape3D(shape2) || !shapeTypes.isShape3D(tool)) {
302
+ return errors.err(
303
+ errors.validationError(errors.BrepErrorCode.MINKOWSKI_NOT_3D, "minkowski: both shape and tool must be 3D")
304
+ );
305
+ }
306
+ const sphereRadius = detectSphere(tool);
307
+ if (sphereRadius !== null) {
308
+ return minkowskiSphere(shape2, sphereRadius, tolerance);
309
+ }
310
+ return minkowskiGeneral(shape2, tool, tolerance);
311
+ }
147
312
  function checkInterference(shape1, shape2, tolerance = 1e-6) {
148
313
  if (shape1.wrapped.IsNull()) {
149
314
  return errors.err(
@@ -197,7 +362,7 @@ function box(width, depth, height, options) {
197
362
  const solid2 = shapeTypes.createSolid(maker.Solid());
198
363
  const center = options?.at ?? (options?.centered ? [0, 0, 0] : void 0);
199
364
  if (center) {
200
- return curveFns.translate(solid2, [center[0] - width / 2, center[1] - depth / 2, center[2] - height / 2]);
365
+ return shapeFns.translate(solid2, [center[0] - width / 2, center[1] - depth / 2, center[2] - height / 2]);
201
366
  }
202
367
  return solid2;
203
368
  }
@@ -211,14 +376,14 @@ function cylinder(radius, height, options) {
211
376
  -axis[1] * height * 0.5,
212
377
  -axis[2] * height * 0.5
213
378
  ];
214
- solid2 = curveFns.translate(solid2, halfShift);
379
+ solid2 = shapeFns.translate(solid2, halfShift);
215
380
  }
216
381
  return solid2;
217
382
  }
218
383
  function sphere(radius, options) {
219
384
  let solid2 = loft$2.makeSphere(radius);
220
385
  if (options?.at) {
221
- solid2 = curveFns.translate(solid2, options.at);
386
+ solid2 = shapeFns.translate(solid2, options.at);
222
387
  }
223
388
  return solid2;
224
389
  }
@@ -232,7 +397,7 @@ function cone(bottomRadius, topRadius, height, options) {
232
397
  -axis[1] * height * 0.5,
233
398
  -axis[2] * height * 0.5
234
399
  ];
235
- solid2 = curveFns.translate(solid2, halfShift);
400
+ solid2 = shapeFns.translate(solid2, halfShift);
236
401
  }
237
402
  return solid2;
238
403
  }
@@ -242,7 +407,7 @@ function torus(majorRadius, minorRadius, options) {
242
407
  function ellipsoid(rx, ry, rz, options) {
243
408
  let solid2 = loft$2.makeEllipsoid(rx, ry, rz);
244
409
  if (options?.at) {
245
- solid2 = curveFns.translate(solid2, options.at);
410
+ solid2 = shapeFns.translate(solid2, options.at);
246
411
  }
247
412
  return solid2;
248
413
  }
@@ -333,11 +498,21 @@ function validateNotNull(shape2, label) {
333
498
  function thicken$1(shape2, thickness) {
334
499
  const check = validateNotNull(shape2, "thicken: shape");
335
500
  if (errors.isErr(check)) return check;
336
- return kernelCall(
337
- () => occtBoundary.getKernel().thicken(shape2.wrapped, thickness),
338
- "THICKEN_FAILED",
339
- "Thicken operation failed"
340
- );
501
+ try {
502
+ const oc = occtBoundary.getKernel().oc;
503
+ const r = shapeTypes.gcWithScope();
504
+ const builder = r(new oc.BRepOffsetAPI_MakeThickSolid());
505
+ builder.MakeThickSolidBySimple(shape2.wrapped, thickness);
506
+ const progress = r(new oc.Message_ProgressRange_1());
507
+ builder.Build(progress);
508
+ const resultOc = builder.Shape();
509
+ const cast2 = shapeTypes.castShape(resultOc);
510
+ shapeFns.propagateOrigins(builder, [shape2], cast2);
511
+ return errors.ok(cast2);
512
+ } catch (e) {
513
+ const raw = e instanceof Error ? e.message : String(e);
514
+ return errors.err(errors.occtError("THICKEN_FAILED", `Thicken operation failed: ${raw}`, e));
515
+ }
341
516
  }
342
517
  function fillet$1(shape2, edges, radius) {
343
518
  const check = validateNotNull(shape2, "fillet: shape");
@@ -364,7 +539,7 @@ function fillet$1(shape2, edges, radius) {
364
539
  )
365
540
  );
366
541
  }
367
- const selectedEdges = edges ?? curveFns.getEdges(shape2);
542
+ const selectedEdges = edges ?? shapeFns.getEdges(shape2);
368
543
  if (selectedEdges.length === 0) {
369
544
  return errors.err(
370
545
  errors.validationError(
@@ -377,23 +552,26 @@ function fillet$1(shape2, edges, radius) {
377
552
  );
378
553
  }
379
554
  try {
380
- const kernel = occtBoundary.getKernel();
381
- const kernelRadius = typeof radius === "function" ? (
382
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- kernel expects OcShape callback
383
- ((ocEdge) => {
384
- const edgeWrapped = shapeTypes.castShape(ocEdge);
385
- return radius(edgeWrapped) ?? 0;
386
- })
387
- ) : radius;
388
- const result2 = kernel.fillet(
389
- shape2.wrapped,
390
- selectedEdges.map((e) => e.wrapped),
391
- kernelRadius
555
+ const oc = occtBoundary.getKernel().oc;
556
+ const r = shapeTypes.gcWithScope();
557
+ const builder = r(
558
+ new oc.BRepFilletAPI_MakeFillet(shape2.wrapped, oc.ChFi3d_FilletShape.ChFi3d_Rational)
392
559
  );
393
- const cast2 = shapeTypes.castShape(result2);
560
+ for (const edge of selectedEdges) {
561
+ const rad = typeof radius === "function" ? radius(edge) ?? 0 : radius;
562
+ if (typeof rad === "number") {
563
+ if (rad > 0) builder.Add_2(rad, edge.wrapped);
564
+ } else {
565
+ const [r1, r2] = rad;
566
+ if (r1 > 0 && r2 > 0) builder.Add_3(r1, r2, edge.wrapped);
567
+ }
568
+ }
569
+ const resultOc = builder.Shape();
570
+ const cast2 = shapeTypes.castShape(resultOc);
394
571
  if (!shapeTypes.isShape3D(cast2)) {
395
572
  return errors.err(errors.occtError("FILLET_RESULT_NOT_3D", "Fillet result is not a 3D shape"));
396
573
  }
574
+ shapeFns.propagateOrigins(builder, [shape2], cast2);
397
575
  return errors.ok(cast2);
398
576
  } catch (e) {
399
577
  const raw = e instanceof Error ? e.message : String(e);
@@ -431,28 +609,63 @@ function chamfer$1(shape2, edges, distance) {
431
609
  )
432
610
  );
433
611
  }
434
- const selectedEdges = edges ?? curveFns.getEdges(shape2);
612
+ const selectedEdges = edges ?? shapeFns.getEdges(shape2);
435
613
  if (selectedEdges.length === 0) {
436
614
  return errors.err(errors.validationError("NO_EDGES", "No edges found for chamfer"));
437
615
  }
438
616
  try {
439
- const kernel = occtBoundary.getKernel();
440
- const kernelDistance = typeof distance === "function" ? (
441
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- kernel expects OcShape callback
442
- ((ocEdge) => {
443
- const edgeWrapped = shapeTypes.castShape(ocEdge);
444
- return distance(edgeWrapped) ?? 0;
445
- })
446
- ) : distance;
447
- const result2 = kernel.chamfer(
448
- shape2.wrapped,
449
- selectedEdges.map((e) => e.wrapped),
450
- kernelDistance
451
- );
452
- const cast2 = shapeTypes.castShape(result2);
617
+ let getEdgeFaceMap = function() {
618
+ if (edgeFaceMap) return edgeFaceMap;
619
+ edgeFaceMap = /* @__PURE__ */ new Map();
620
+ const faceExp = new oc.TopExp_Explorer_2(
621
+ shape2.wrapped,
622
+ oc.TopAbs_ShapeEnum.TopAbs_FACE,
623
+ oc.TopAbs_ShapeEnum.TopAbs_SHAPE
624
+ );
625
+ while (faceExp.More()) {
626
+ const face2 = oc.TopoDS.Face_1(faceExp.Current());
627
+ const edgeExp = new oc.TopExp_Explorer_2(
628
+ face2,
629
+ oc.TopAbs_ShapeEnum.TopAbs_EDGE,
630
+ oc.TopAbs_ShapeEnum.TopAbs_SHAPE
631
+ );
632
+ while (edgeExp.More()) {
633
+ const hash = edgeExp.Current().HashCode(2147483647);
634
+ if (!edgeFaceMap.has(hash)) {
635
+ edgeFaceMap.set(hash, face2);
636
+ }
637
+ edgeExp.Next();
638
+ }
639
+ edgeExp.delete();
640
+ faceExp.Next();
641
+ }
642
+ faceExp.delete();
643
+ return edgeFaceMap;
644
+ };
645
+ const oc = occtBoundary.getKernel().oc;
646
+ const r = shapeTypes.gcWithScope();
647
+ const builder = r(new oc.BRepFilletAPI_MakeChamfer(shape2.wrapped));
648
+ let edgeFaceMap = null;
649
+ for (const edge of selectedEdges) {
650
+ const d = typeof distance === "function" ? distance(edge) ?? 0 : distance;
651
+ if (typeof d === "number") {
652
+ if (d > 0) builder.Add_2(d, edge.wrapped);
653
+ } else {
654
+ const [d1, d2] = d;
655
+ if (d1 > 0 && d2 > 0) {
656
+ const face2 = getEdgeFaceMap().get(edge.wrapped.HashCode(2147483647));
657
+ if (face2) {
658
+ builder.Add_3(d1, d2, oc.TopoDS.Edge_1(edge.wrapped), face2);
659
+ }
660
+ }
661
+ }
662
+ }
663
+ const resultOc = builder.Shape();
664
+ const cast2 = shapeTypes.castShape(resultOc);
453
665
  if (!shapeTypes.isShape3D(cast2)) {
454
666
  return errors.err(errors.occtError("CHAMFER_RESULT_NOT_3D", "Chamfer result is not a 3D shape"));
455
667
  }
668
+ shapeFns.propagateOrigins(builder, [shape2], cast2);
456
669
  return errors.ok(cast2);
457
670
  } catch (e) {
458
671
  const raw = e instanceof Error ? e.message : String(e);
@@ -475,16 +688,32 @@ function shell$1(shape2, faces, thickness, tolerance = 1e-3) {
475
688
  return errors.err(errors.validationError("NO_FACES", "At least one face must be specified for shell"));
476
689
  }
477
690
  try {
478
- const result2 = occtBoundary.getKernel().shell(
691
+ const oc = occtBoundary.getKernel().oc;
692
+ const r = shapeTypes.gcWithScope();
693
+ const facesToRemove = r(new oc.TopTools_ListOfShape_1());
694
+ for (const face2 of faces) {
695
+ facesToRemove.Append_1(face2.wrapped);
696
+ }
697
+ const progress = r(new oc.Message_ProgressRange_1());
698
+ const builder = r(new oc.BRepOffsetAPI_MakeThickSolid());
699
+ builder.MakeThickSolidByJoin(
479
700
  shape2.wrapped,
480
- faces.map((f) => f.wrapped),
481
- thickness,
482
- tolerance
701
+ facesToRemove,
702
+ -thickness,
703
+ tolerance,
704
+ oc.BRepOffset_Mode.BRepOffset_Skin,
705
+ false,
706
+ false,
707
+ oc.GeomAbs_JoinType.GeomAbs_Arc,
708
+ false,
709
+ progress
483
710
  );
484
- const cast2 = shapeTypes.castShape(result2);
711
+ const resultOc = builder.Shape();
712
+ const cast2 = shapeTypes.castShape(resultOc);
485
713
  if (!shapeTypes.isShape3D(cast2)) {
486
714
  return errors.err(errors.occtError("SHELL_RESULT_NOT_3D", "Shell result is not a 3D shape"));
487
715
  }
716
+ shapeFns.propagateOrigins(builder, [shape2], cast2);
488
717
  return errors.ok(cast2);
489
718
  } catch (e) {
490
719
  const raw = e instanceof Error ? e.message : String(e);
@@ -503,34 +732,56 @@ function offset$1(shape2, distance, tolerance = 1e-6) {
503
732
  if (distance === 0) {
504
733
  return errors.err(errors.validationError("ZERO_OFFSET", "Offset distance cannot be zero"));
505
734
  }
506
- return kernelCall(
507
- () => occtBoundary.getKernel().offset(shape2.wrapped, distance, tolerance),
508
- "OFFSET_FAILED",
509
- "Offset operation failed"
510
- );
735
+ try {
736
+ const oc = occtBoundary.getKernel().oc;
737
+ const r = shapeTypes.gcWithScope();
738
+ const progress = r(new oc.Message_ProgressRange_1());
739
+ const builder = r(new oc.BRepOffsetAPI_MakeOffsetShape());
740
+ builder.PerformByJoin(
741
+ shape2.wrapped,
742
+ distance,
743
+ tolerance,
744
+ oc.BRepOffset_Mode.BRepOffset_Skin,
745
+ false,
746
+ false,
747
+ oc.GeomAbs_JoinType.GeomAbs_Arc,
748
+ false,
749
+ progress
750
+ );
751
+ const resultOc = builder.Shape();
752
+ const cast2 = shapeTypes.castShape(resultOc);
753
+ if (!shapeTypes.isShape3D(cast2)) {
754
+ return errors.err(errors.occtError("OFFSET_RESULT_NOT_3D", "Offset result is not a 3D shape"));
755
+ }
756
+ shapeFns.propagateOrigins(builder, [shape2], cast2);
757
+ return errors.ok(cast2);
758
+ } catch (e) {
759
+ const raw = e instanceof Error ? e.message : String(e);
760
+ return errors.err(errors.occtError("OFFSET_FAILED", `Offset operation failed: ${raw}`, e));
761
+ }
511
762
  }
512
763
  function translate(shape2, v) {
513
- return curveFns.translate(resolve(shape2), v);
764
+ return shapeFns.translate(resolve(shape2), v);
514
765
  }
515
766
  function rotate(shape2, angle, options) {
516
767
  const pivotPoint = options?.at;
517
- return curveFns.rotate(resolve(shape2), angle, pivotPoint, options?.axis);
768
+ return shapeFns.rotate(resolve(shape2), angle, pivotPoint, options?.axis);
518
769
  }
519
770
  function mirror(shape2, options) {
520
771
  const planeOrigin = options?.at;
521
- return curveFns.mirror(resolve(shape2), options?.normal ?? [1, 0, 0], planeOrigin);
772
+ return shapeFns.mirror(resolve(shape2), options?.normal ?? [1, 0, 0], planeOrigin);
522
773
  }
523
774
  function scale(shape2, factor, options) {
524
- return curveFns.scale(resolve(shape2), factor, options?.center);
775
+ return shapeFns.scale(resolve(shape2), factor, options?.center);
525
776
  }
526
777
  function clone(shape2) {
527
- return curveFns.clone(resolve(shape2));
778
+ return shapeFns.clone(resolve(shape2));
528
779
  }
529
780
  function applyMatrix(shape2, matrix) {
530
- return curveFns.applyMatrix(resolve(shape2), matrix);
781
+ return shapeFns.applyMatrix(resolve(shape2), matrix);
531
782
  }
532
783
  function transformCopy(shape2, composed) {
533
- return curveFns.transformCopy(resolve(shape2), composed);
784
+ return shapeFns.transformCopy(resolve(shape2), composed);
534
785
  }
535
786
  function fuse(a, b, options) {
536
787
  return booleanFns.fuse(resolve(a), resolve(b), options);
@@ -613,7 +864,7 @@ function chamfer(shape2, edgesOrDistance, maybeDistance) {
613
864
  }
614
865
  const normalized = normalizeChamferDistance(distance);
615
866
  if (normalized.mode === "distAngle") {
616
- const selectedEdges = edges ?? curveFns.getEdges(s);
867
+ const selectedEdges = edges ?? shapeFns.getEdges(s);
617
868
  return topology.chamferDistAngle(
618
869
  s,
619
870
  [...selectedEdges],
@@ -638,7 +889,7 @@ function heal(shape2) {
638
889
  return topology.heal(resolve(shape2));
639
890
  }
640
891
  function simplify(shape2) {
641
- return curveFns.simplify(resolve(shape2));
892
+ return shapeFns.simplify(resolve(shape2));
642
893
  }
643
894
  function mesh(shape2, options) {
644
895
  return meshFns.mesh(resolve(shape2), options);
@@ -647,10 +898,10 @@ function meshEdges(shape2, options) {
647
898
  return meshFns.meshEdges(resolve(shape2), options);
648
899
  }
649
900
  function describe(shape2) {
650
- return curveFns.describe(resolve(shape2));
901
+ return shapeFns.describe(resolve(shape2));
651
902
  }
652
903
  function toBREP(shape2) {
653
- return curveFns.toBREP(resolve(shape2));
904
+ return shapeFns.toBREP(resolve(shape2));
654
905
  }
655
906
  function fromBREP(data) {
656
907
  return cast.fromBREP(data);
@@ -659,7 +910,7 @@ function isValid(shape2) {
659
910
  return topology.isValid(resolve(shape2));
660
911
  }
661
912
  function isEmpty(shape2) {
662
- return curveFns.isEmpty(resolve(shape2));
913
+ return shapeFns.isEmpty(resolve(shape2));
663
914
  }
664
915
  function loft$1(wires, { ruled = true, startPoint, endPoint } = {}, returnShell = false) {
665
916
  if (wires.length === 0 && !startPoint && !endPoint) {
@@ -712,7 +963,7 @@ function loft(wires, options) {
712
963
  }
713
964
  function resolveTargetFace(shape2, faceSpec) {
714
965
  if (faceSpec === void 0) {
715
- const faces = curveFns.getFaces(shape2);
966
+ const faces = shapeFns.getFaces(shape2);
716
967
  if (faces.length === 0) {
717
968
  throw new Error("compoundOps: shape has no faces");
718
969
  }
@@ -758,7 +1009,7 @@ function drill(shape2, options) {
758
1009
  const pos = at.length === 2 ? [at[0], at[1], 0] : [at[0], at[1], at[2]];
759
1010
  let depth = options.depth;
760
1011
  if (depth === void 0) {
761
- const b = curveFns.getBounds(s);
1012
+ const b = shapeFns.getBounds(s);
762
1013
  const dx = b.xMax - b.xMin;
763
1014
  const dy = b.yMax - b.yMin;
764
1015
  const dz = b.zMax - b.zMin;
@@ -766,7 +1017,7 @@ function drill(shape2, options) {
766
1017
  }
767
1018
  const cyl = loft$2.makeCylinder(radius, depth, pos, dir);
768
1019
  const startOffset = options.depth === void 0 ? vecOps.vecScale(dir, -depth / 2) : [0, 0, 0];
769
- const tool = startOffset[0] !== 0 || startOffset[1] !== 0 || startOffset[2] !== 0 ? curveFns.translate(cyl, startOffset) : cyl;
1020
+ const tool = startOffset[0] !== 0 || startOffset[1] !== 0 || startOffset[2] !== 0 ? shapeFns.translate(cyl, startOffset) : cyl;
770
1021
  return booleanFns.cut(s, tool);
771
1022
  }
772
1023
  function pocket(shape2, options) {
@@ -805,7 +1056,7 @@ function mirrorJoin(shape2, options) {
805
1056
  const s = resolve(shape2);
806
1057
  const normal = options?.normal ?? [1, 0, 0];
807
1058
  const planeOrigin = options?.at;
808
- const mirrored = curveFns.mirror(s, normal, planeOrigin);
1059
+ const mirrored = shapeFns.mirror(s, normal, planeOrigin);
809
1060
  return booleanFns.fuse(s, mirrored);
810
1061
  }
811
1062
  function rectangularPattern(shape2, options) {
@@ -832,7 +1083,7 @@ function rectangularPattern(shape2, options) {
832
1083
  xNorm[1] * xSpacing * xi + yNorm[1] * ySpacing * yi,
833
1084
  xNorm[2] * xSpacing * xi + yNorm[2] * ySpacing * yi
834
1085
  ];
835
- copies.push(curveFns.translate(s, offset2));
1086
+ copies.push(shapeFns.translate(s, offset2));
836
1087
  }
837
1088
  }
838
1089
  return booleanFns.fuseAll(copies);
@@ -879,7 +1130,7 @@ function createWrappedBase(val) {
879
1130
  rotateX: (a) => wrapAny(rotate(val, a, { axis: [1, 0, 0] })),
880
1131
  rotateY: (a) => wrapAny(rotate(val, a, { axis: [0, 1, 0] })),
881
1132
  rotateZ: (a) => wrapAny(rotate(val, a, { axis: [0, 0, 1] })),
882
- bounds: () => curveFns.getBounds(val),
1133
+ bounds: () => shapeFns.getBounds(val),
883
1134
  describe: () => describe(val),
884
1135
  clone: () => wrapAny(clone(val)),
885
1136
  // Meshing & Rendering
@@ -940,10 +1191,10 @@ function createWrapped3D(val) {
940
1191
  volumeProps: () => measurement.measureVolumeProps(val),
941
1192
  surfaceProps: () => measurement.measureSurfaceProps(val),
942
1193
  // Queries
943
- edges: () => curveFns.getEdges(val),
944
- faces: () => curveFns.getFaces(val),
945
- wires: () => curveFns.getWires(val),
946
- vertices: () => curveFns.getVertices(val),
1194
+ edges: () => shapeFns.getEdges(val),
1195
+ faces: () => shapeFns.getFaces(val),
1196
+ wires: () => shapeFns.getWires(val),
1197
+ vertices: () => shapeFns.getVertices(val),
947
1198
  // Patterns
948
1199
  linearPattern: (dir, count, spacing) => wrap3D(unwrapOrThrow(operations.linearPattern(val, dir, count, spacing))),
949
1200
  circularPattern: (axis, count, angle) => wrap3D(unwrapOrThrow(operations.circularPattern(val, axis, count, angle)))
@@ -1093,7 +1344,6 @@ exports.Curve2D = Blueprint.Curve2D;
1093
1344
  exports.axis2d = Blueprint.axis2d;
1094
1345
  exports.makePlane = Blueprint.makePlane;
1095
1346
  exports.approximateCurve = curveFns.approximateCurve;
1096
- exports.composeTransforms = curveFns.composeTransforms;
1097
1347
  exports.curveEndPoint = curveFns.curveEndPoint;
1098
1348
  exports.curveIsClosed = curveFns.curveIsClosed;
1099
1349
  exports.curveIsPeriodic = curveFns.curveIsPeriodic;
@@ -1104,23 +1354,10 @@ exports.curveStartPoint = curveFns.curveStartPoint;
1104
1354
  exports.curveTangentAt = curveFns.curveTangentAt;
1105
1355
  exports.findCurveType = curveFns.findCurveType;
1106
1356
  exports.flipOrientation = curveFns.flipOrientation;
1107
- exports.getBounds = curveFns.getBounds;
1108
1357
  exports.getCurveType = curveFns.getCurveType;
1109
- exports.getEdges = curveFns.getEdges;
1110
- exports.getFaces = curveFns.getFaces;
1111
- exports.getHashCode = curveFns.getHashCode;
1112
1358
  exports.getOrientation = curveFns.getOrientation;
1113
- exports.getVertices = curveFns.getVertices;
1114
- exports.getWires = curveFns.getWires;
1115
1359
  exports.interpolateCurve = curveFns.interpolateCurve;
1116
- exports.isEqualShape = curveFns.isEqualShape;
1117
- exports.isSameShape = curveFns.isSameShape;
1118
- exports.iterEdges = curveFns.iterEdges;
1119
- exports.iterFaces = curveFns.iterFaces;
1120
- exports.iterVertices = curveFns.iterVertices;
1121
- exports.iterWires = curveFns.iterWires;
1122
1360
  exports.offsetWire2D = curveFns.offsetWire2D;
1123
- exports.vertexPosition = curveFns.vertexPosition;
1124
1361
  exports.basicFaceExtrusion = loft$2.basicFaceExtrusion;
1125
1362
  exports.genericSweep = loft$2.genericSweep;
1126
1363
  exports.revolution = loft$2.revolution;
@@ -1266,6 +1503,22 @@ exports.createPlane = vectors.createPlane;
1266
1503
  exports.pivotPlane = vectors.pivotPlane;
1267
1504
  exports.resolvePlane = vectors.resolvePlane;
1268
1505
  exports.translatePlane = vectors.translatePlane;
1506
+ exports.composeTransforms = shapeFns.composeTransforms;
1507
+ exports.getBounds = shapeFns.getBounds;
1508
+ exports.getEdges = shapeFns.getEdges;
1509
+ exports.getFaceOrigins = shapeFns.getFaceOrigins;
1510
+ exports.getFaces = shapeFns.getFaces;
1511
+ exports.getHashCode = shapeFns.getHashCode;
1512
+ exports.getVertices = shapeFns.getVertices;
1513
+ exports.getWires = shapeFns.getWires;
1514
+ exports.isEqualShape = shapeFns.isEqualShape;
1515
+ exports.isSameShape = shapeFns.isSameShape;
1516
+ exports.iterEdges = shapeFns.iterEdges;
1517
+ exports.iterFaces = shapeFns.iterFaces;
1518
+ exports.iterVertices = shapeFns.iterVertices;
1519
+ exports.iterWires = shapeFns.iterWires;
1520
+ exports.setShapeOrigin = shapeFns.setShapeOrigin;
1521
+ exports.vertexPosition = shapeFns.vertexPosition;
1269
1522
  exports.adjacentFaces = topology.adjacentFaces;
1270
1523
  exports.autoHeal = topology.autoHeal;
1271
1524
  exports.chamferDistAngleShape = topology.chamferDistAngle;
@@ -1366,6 +1619,7 @@ exports.fromBREP = fromBREP;
1366
1619
  exports.fuse = fuse;
1367
1620
  exports.heal = heal;
1368
1621
  exports.helix = helix;
1622
+ exports.hull = hull;
1369
1623
  exports.intersect = intersect;
1370
1624
  exports.isChamferRadius = isChamferRadius;
1371
1625
  exports.isEmpty = isEmpty;
@@ -1378,6 +1632,7 @@ exports.line = line;
1378
1632
  exports.loft = loft;
1379
1633
  exports.mesh = mesh;
1380
1634
  exports.meshEdges = meshEdges;
1635
+ exports.minkowski = minkowski;
1381
1636
  exports.mirror = mirror;
1382
1637
  exports.mirrorJoin = mirrorJoin;
1383
1638
  exports.offset = offset;