occt-wasm 0.1.7 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  /**
2
- * @occt-wasm/core — OCCT compiled to WASM with clean TypeScript bindings.
2
+ * occt-wasm — OCCT compiled to WASM with clean TypeScript bindings.
3
3
  *
4
4
  * @example
5
5
  * ```ts
6
- * import { OcctKernel } from '@occt-wasm/core';
6
+ * import { OcctKernel } from 'occt-wasm';
7
7
  *
8
8
  * const kernel = await OcctKernel.init();
9
9
  * const box = kernel.makeBox(10, 20, 30);
@@ -15,11 +15,12 @@
15
15
  export { OcctError, } from "./types.js";
16
16
  export { XCAFDocument } from "./xcaf-document.js";
17
17
  import { OcctError } from "./types.js";
18
- /** Cast a raw number to a branded ShapeHandle. */
18
+ // ---------------------------------------------------------------------------
19
+ // Helpers
20
+ // ---------------------------------------------------------------------------
19
21
  function handle(id) {
20
22
  return id;
21
23
  }
22
- /** Wrap an Embind call, catching errors and re-throwing as OcctError. */
23
24
  function wrap(operation, fn) {
24
25
  try {
25
26
  return fn();
@@ -31,12 +32,57 @@ function wrap(operation, fn) {
31
32
  throw new OcctError(operation, String(e));
32
33
  }
33
34
  }
35
+ function vecToNumbers(vec) {
36
+ const result = [];
37
+ for (let i = 0; i < vec.size(); i++) {
38
+ result.push(vec.get(i));
39
+ }
40
+ return result;
41
+ }
42
+ /**
43
+ * Extract the first n elements from an Embind vector into an array,
44
+ * then delete the vector and return the result.
45
+ */
46
+ function extractFromVector(vec, count) {
47
+ const result = [];
48
+ for (let i = 0; i < count; i++) {
49
+ result.push(vec.get(i));
50
+ }
51
+ vec.delete();
52
+ return result;
53
+ }
54
+ function vecToHandles(vec) {
55
+ const result = [];
56
+ for (let i = 0; i < vec.size(); i++) {
57
+ result.push(handle(vec.get(i)));
58
+ }
59
+ vec.delete();
60
+ return result;
61
+ }
62
+ /**
63
+ * Safety net: releases the raw Embind kernel if an OcctKernel instance is
64
+ * garbage-collected without being disposed. Prefer `using` or explicit
65
+ * `kernel[Symbol.dispose]()` — the FinalizationRegistry is a last resort.
66
+ */
67
+ const kernelRegistry = new FinalizationRegistry((raw) => {
68
+ try {
69
+ raw.releaseAll();
70
+ raw.delete();
71
+ }
72
+ catch {
73
+ // Already disposed — ignore.
74
+ }
75
+ });
76
+ // ---------------------------------------------------------------------------
77
+ // OcctKernel
78
+ // ---------------------------------------------------------------------------
34
79
  /**
35
80
  * OCCT kernel compiled to WASM. Arena-based shape management
36
81
  * with branded handle types for type safety.
37
82
  *
38
83
  * Create via `OcctKernel.init()`. Dispose via `kernel[Symbol.dispose]()` or
39
- * the `using` keyword.
84
+ * the `using` keyword. A FinalizationRegistry safety net catches leaked
85
+ * instances, but deterministic disposal is strongly preferred.
40
86
  */
41
87
  export class OcctKernel {
42
88
  #raw;
@@ -44,6 +90,7 @@ export class OcctKernel {
44
90
  constructor(module) {
45
91
  this.#module = module;
46
92
  this.#raw = new module.OcctKernel();
93
+ kernelRegistry.register(this, this.#raw, this);
47
94
  }
48
95
  /**
49
96
  * Initialize the WASM module and create a kernel instance.
@@ -55,12 +102,11 @@ export class OcctKernel {
55
102
  *
56
103
  * // Node.js with explicit path:
57
104
  * const kernel = await OcctKernel.init({
58
- * wasmPath: './node_modules/@occt-wasm/core/dist/occt-wasm.wasm'
105
+ * wasmPath: './node_modules/occt-wasm/dist/occt-wasm.wasm'
59
106
  * });
60
107
  * ```
61
108
  */
62
109
  static async init(options) {
63
- // Dynamic import of the Emscripten-generated module.
64
110
  // @ts-expect-error -- occt-wasm.js is generated at build time, no .d.ts
65
111
  const imported = await import(/* webpackIgnore: true */ "./occt-wasm.js");
66
112
  const createModule = imported.default;
@@ -76,45 +122,146 @@ export class OcctKernel {
76
122
  const module = await createModule(moduleOpts);
77
123
  return new OcctKernel(module);
78
124
  }
79
- // --- Primitives ---
125
+ // =======================================================================
126
+ // Primitives
127
+ // =======================================================================
128
+ /** Create an axis-aligned box solid with the given dimensions (BRepPrimAPI_MakeBox).
129
+ * @throws OcctError if dimensions are non-positive */
80
130
  makeBox(dx, dy, dz) {
81
131
  return wrap("makeBox", () => handle(this.#raw.makeBox(dx, dy, dz)));
82
132
  }
133
+ /** Create a box solid from two opposite corner points.
134
+ * @throws OcctError if corners are coincident */
135
+ makeBoxFromCorners(corner1, corner2) {
136
+ return wrap("makeBoxFromCorners", () => handle(this.#raw.makeBoxFromCorners(corner1.x, corner1.y, corner1.z, corner2.x, corner2.y, corner2.z)));
137
+ }
138
+ /** Create a cylinder solid centered on the Z axis (BRepPrimAPI_MakeCylinder).
139
+ * @throws OcctError */
83
140
  makeCylinder(radius, height) {
84
141
  return wrap("makeCylinder", () => handle(this.#raw.makeCylinder(radius, height)));
85
142
  }
143
+ /** Create a sphere solid at the origin (BRepPrimAPI_MakeSphere).
144
+ * @throws OcctError */
86
145
  makeSphere(radius) {
87
146
  return wrap("makeSphere", () => handle(this.#raw.makeSphere(radius)));
88
147
  }
148
+ /** Create a cone (or truncated cone) solid along the Z axis (BRepPrimAPI_MakeCone).
149
+ * @param r1 - bottom radius
150
+ * @param r2 - top radius (0 for a full cone)
151
+ * @throws OcctError */
89
152
  makeCone(r1, r2, height) {
90
153
  return wrap("makeCone", () => handle(this.#raw.makeCone(r1, r2, height)));
91
154
  }
155
+ /** Create a torus solid at the origin (BRepPrimAPI_MakeTorus).
156
+ * @throws OcctError */
92
157
  makeTorus(majorRadius, minorRadius) {
93
158
  return wrap("makeTorus", () => handle(this.#raw.makeTorus(majorRadius, minorRadius)));
94
159
  }
95
- // --- Booleans ---
160
+ /** Create an ellipsoid solid at the origin by scaling a sphere.
161
+ * @param rx - radius along X
162
+ * @param ry - radius along Y
163
+ * @param rz - radius along Z
164
+ * @throws OcctError */
165
+ makeEllipsoid(rx, ry, rz) {
166
+ return wrap("makeEllipsoid", () => handle(this.#raw.makeEllipsoid(rx, ry, rz)));
167
+ }
168
+ /** Create a rectangular planar face on the XY plane at the origin.
169
+ * @throws OcctError */
170
+ makeRectangle(width, height) {
171
+ return wrap("makeRectangle", () => handle(this.#raw.makeRectangle(width, height)));
172
+ }
173
+ // =======================================================================
174
+ // Booleans
175
+ // =======================================================================
176
+ /** Boolean union (BRepAlgoAPI_Fuse). Combines two shapes into one.
177
+ * @throws OcctError */
96
178
  fuse(a, b) {
97
179
  return wrap("fuse", () => handle(this.#raw.fuse(a, b)));
98
180
  }
181
+ /** Boolean subtraction (BRepAlgoAPI_Cut). Removes b from a.
182
+ * @throws OcctError */
99
183
  cut(a, b) {
100
184
  return wrap("cut", () => handle(this.#raw.cut(a, b)));
101
185
  }
186
+ /** Boolean intersection (BRepAlgoAPI_Common). Keeps only the overlapping volume.
187
+ * @throws OcctError */
102
188
  common(a, b) {
103
189
  return wrap("common", () => handle(this.#raw.common(a, b)));
104
190
  }
191
+ /** Boolean intersection — alias for common (BRepAlgoAPI_Common).
192
+ * @throws OcctError */
193
+ intersect(a, b) {
194
+ return wrap("intersect", () => handle(this.#raw.intersect(a, b)));
195
+ }
196
+ /** Compute the intersection edges/vertices of two shapes (BRepAlgoAPI_Section).
197
+ * @returns A compound of edges/vertices at the intersection
198
+ * @throws OcctError */
105
199
  section(a, b) {
106
200
  return wrap("section", () => handle(this.#raw.section(a, b)));
107
201
  }
108
- // --- Modeling ---
202
+ /** Fuse all shapes in the array into a single shape.
203
+ * @throws OcctError */
204
+ fuseAll(shapes) {
205
+ return wrap("fuseAll", () => {
206
+ const vec = this.#makeVectorU32(shapes);
207
+ try {
208
+ return handle(this.#raw.fuseAll(vec));
209
+ }
210
+ finally {
211
+ vec.delete();
212
+ }
213
+ });
214
+ }
215
+ /** Subtract all tool shapes from the base shape.
216
+ * @throws OcctError */
217
+ cutAll(shape, tools) {
218
+ return wrap("cutAll", () => {
219
+ const vec = this.#makeVectorU32(tools);
220
+ try {
221
+ return handle(this.#raw.cutAll(shape, vec));
222
+ }
223
+ finally {
224
+ vec.delete();
225
+ }
226
+ });
227
+ }
228
+ /** Split a shape using tool shapes as splitting surfaces (BOPAlgo_Splitter).
229
+ * @returns A compound of the split fragments
230
+ * @throws OcctError */
231
+ split(shape, tools) {
232
+ return wrap("split", () => {
233
+ const vec = this.#makeVectorU32(tools);
234
+ try {
235
+ return handle(this.#raw.split(shape, vec));
236
+ }
237
+ finally {
238
+ vec.delete();
239
+ }
240
+ });
241
+ }
242
+ // =======================================================================
243
+ // Modeling
244
+ // =======================================================================
245
+ /** Extrude a shape along a direction vector (BRepPrimAPI_MakePrism).
246
+ * @param dx - extrusion vector X component
247
+ * @param dy - extrusion vector Y component
248
+ * @param dz - extrusion vector Z component
249
+ * @throws OcctError */
109
250
  extrude(shape, dx, dy, dz) {
110
251
  return wrap("extrude", () => handle(this.#raw.extrude(shape, dx, dy, dz)));
111
252
  }
253
+ /** Revolve a shape around an axis (BRepPrimAPI_MakeRevol).
254
+ * @param axis - rotation axis defined by a point and direction
255
+ * @param angleRad - sweep angle in radians (2*PI for full revolution)
256
+ * @throws OcctError */
112
257
  revolve(shape, axis, angleRad) {
113
258
  return wrap("revolve", () => handle(this.#raw.revolve(shape, axis.point.x, axis.point.y, axis.point.z, axis.direction.x, axis.direction.y, axis.direction.z, angleRad)));
114
259
  }
260
+ /** Apply a constant-radius fillet to edges of a solid (BRepFilletAPI_MakeFillet).
261
+ * @throws OcctError */
115
262
  fillet(solid, edges, radius) {
116
263
  return wrap("fillet", () => {
117
- const vec = this.#makeVector(edges);
264
+ const vec = this.#makeVectorU32(edges);
118
265
  try {
119
266
  return handle(this.#raw.fillet(solid, vec, radius));
120
267
  }
@@ -125,7 +272,7 @@ export class OcctKernel {
125
272
  }
126
273
  chamfer(solid, edges, distance) {
127
274
  return wrap("chamfer", () => {
128
- const vec = this.#makeVector(edges);
275
+ const vec = this.#makeVectorU32(edges);
129
276
  try {
130
277
  return handle(this.#raw.chamfer(solid, vec, distance));
131
278
  }
@@ -134,9 +281,20 @@ export class OcctKernel {
134
281
  }
135
282
  });
136
283
  }
284
+ chamferDistAngle(solid, edges, distance, angleDeg) {
285
+ return wrap("chamferDistAngle", () => {
286
+ const vec = this.#makeVectorU32(edges);
287
+ try {
288
+ return handle(this.#raw.chamferDistAngle(solid, vec, distance, angleDeg));
289
+ }
290
+ finally {
291
+ vec.delete();
292
+ }
293
+ });
294
+ }
137
295
  shell(solid, facesToRemove, thickness) {
138
296
  return wrap("shell", () => {
139
- const vec = this.#makeVector(facesToRemove);
297
+ const vec = this.#makeVectorU32(facesToRemove);
140
298
  try {
141
299
  return handle(this.#raw.shell(solid, vec, thickness));
142
300
  }
@@ -148,13 +306,21 @@ export class OcctKernel {
148
306
  offset(solid, distance) {
149
307
  return wrap("offset", () => handle(this.#raw.offset(solid, distance)));
150
308
  }
151
- // --- Sweeps ---
309
+ draft(shape, face, angleRad, direction) {
310
+ return wrap("draft", () => handle(this.#raw.draft(shape, face, angleRad, direction.x, direction.y, direction.z)));
311
+ }
312
+ // =======================================================================
313
+ // Sweeps
314
+ // =======================================================================
152
315
  pipe(profile, spine) {
153
316
  return wrap("pipe", () => handle(this.#raw.pipe(profile, spine)));
154
317
  }
318
+ simplePipe(profile, spine) {
319
+ return wrap("simplePipe", () => handle(this.#raw.simplePipe(profile, spine)));
320
+ }
155
321
  loft(wires, isSolid) {
156
322
  return wrap("loft", () => {
157
- const vec = this.#makeVector(wires);
323
+ const vec = this.#makeVectorU32(wires);
158
324
  try {
159
325
  return handle(this.#raw.loft(vec, isSolid));
160
326
  }
@@ -163,16 +329,73 @@ export class OcctKernel {
163
329
  }
164
330
  });
165
331
  }
166
- // --- Construction ---
332
+ loftWithVertices(wires, isSolid, startVertex, endVertex) {
333
+ return wrap("loftWithVertices", () => {
334
+ const vec = this.#makeVectorU32(wires);
335
+ try {
336
+ return handle(this.#raw.loftWithVertices(vec, isSolid, startVertex, endVertex));
337
+ }
338
+ finally {
339
+ vec.delete();
340
+ }
341
+ });
342
+ }
343
+ sweep(wire, spine, transitionMode = 0) {
344
+ return wrap("sweep", () => handle(this.#raw.sweep(wire, spine, transitionMode)));
345
+ }
346
+ sweepPipeShell(profile, spine, freenet = false, smooth = true) {
347
+ return wrap("sweepPipeShell", () => handle(this.#raw.sweepPipeShell(profile, spine, freenet, smooth)));
348
+ }
349
+ draftPrism(shape, dx, dy, dz, angleDeg) {
350
+ return wrap("draftPrism", () => handle(this.#raw.draftPrism(shape, dx, dy, dz, angleDeg)));
351
+ }
352
+ // =======================================================================
353
+ // Construction
354
+ // =======================================================================
167
355
  makeVertex(x, y, z) {
168
356
  return wrap("makeVertex", () => handle(this.#raw.makeVertex(x, y, z)));
169
357
  }
170
358
  makeEdge(v1, v2) {
171
359
  return wrap("makeEdge", () => handle(this.#raw.makeEdge(v1, v2)));
172
360
  }
361
+ makeLineEdge(start, end) {
362
+ return wrap("makeLineEdge", () => handle(this.#raw.makeLineEdge(start.x, start.y, start.z, end.x, end.y, end.z)));
363
+ }
364
+ makeCircleEdge(center, normal, radius) {
365
+ return wrap("makeCircleEdge", () => handle(this.#raw.makeCircleEdge(center.x, center.y, center.z, normal.x, normal.y, normal.z, radius)));
366
+ }
367
+ makeCircleArc(center, normal, radius, startAngle, endAngle) {
368
+ return wrap("makeCircleArc", () => handle(this.#raw.makeCircleArc(center.x, center.y, center.z, normal.x, normal.y, normal.z, radius, startAngle, endAngle)));
369
+ }
370
+ makeArcEdge(start, mid, end) {
371
+ return wrap("makeArcEdge", () => handle(this.#raw.makeArcEdge(start.x, start.y, start.z, mid.x, mid.y, mid.z, end.x, end.y, end.z)));
372
+ }
373
+ makeEllipseEdge(center, normal, majorRadius, minorRadius) {
374
+ return wrap("makeEllipseEdge", () => handle(this.#raw.makeEllipseEdge(center.x, center.y, center.z, normal.x, normal.y, normal.z, majorRadius, minorRadius)));
375
+ }
376
+ makeEllipseArc(center, normal, majorRadius, minorRadius, startAngle, endAngle) {
377
+ return wrap("makeEllipseArc", () => handle(this.#raw.makeEllipseArc(center.x, center.y, center.z, normal.x, normal.y, normal.z, majorRadius, minorRadius, startAngle, endAngle)));
378
+ }
379
+ makeBezierEdge(controlPoints) {
380
+ return wrap("makeBezierEdge", () => {
381
+ const flat = this.#flattenPoints(controlPoints);
382
+ try {
383
+ return handle(this.#raw.makeBezierEdge(flat));
384
+ }
385
+ finally {
386
+ flat.delete();
387
+ }
388
+ });
389
+ }
390
+ makeTangentArc(start, tangent, end) {
391
+ return wrap("makeTangentArc", () => handle(this.#raw.makeTangentArc(start.x, start.y, start.z, tangent.x, tangent.y, tangent.z, end.x, end.y, end.z)));
392
+ }
393
+ makeHelixWire(origin, axis, pitch, height, radius) {
394
+ return wrap("makeHelixWire", () => handle(this.#raw.makeHelixWire(origin.x, origin.y, origin.z, axis.x, axis.y, axis.z, pitch, height, radius)));
395
+ }
173
396
  makeWire(edges) {
174
397
  return wrap("makeWire", () => {
175
- const vec = this.#makeVector(edges);
398
+ const vec = this.#makeVectorU32(edges);
176
399
  try {
177
400
  return handle(this.#raw.makeWire(vec));
178
401
  }
@@ -184,9 +407,70 @@ export class OcctKernel {
184
407
  makeFace(wire) {
185
408
  return wrap("makeFace", () => handle(this.#raw.makeFace(wire)));
186
409
  }
410
+ makeNonPlanarFace(wire) {
411
+ return wrap("makeNonPlanarFace", () => handle(this.#raw.makeNonPlanarFace(wire)));
412
+ }
413
+ addHolesInFace(face, holeWires) {
414
+ return wrap("addHolesInFace", () => {
415
+ const vec = this.#makeVectorU32(holeWires);
416
+ try {
417
+ return handle(this.#raw.addHolesInFace(face, vec));
418
+ }
419
+ finally {
420
+ vec.delete();
421
+ }
422
+ });
423
+ }
424
+ removeHolesFromFace(face, holeIndices) {
425
+ return wrap("removeHolesFromFace", () => {
426
+ const vec = this.#makeVectorI32(holeIndices);
427
+ try {
428
+ return handle(this.#raw.removeHolesFromFace(face, vec));
429
+ }
430
+ finally {
431
+ vec.delete();
432
+ }
433
+ });
434
+ }
435
+ makeSolid(shell) {
436
+ return wrap("makeSolid", () => handle(this.#raw.makeSolid(shell)));
437
+ }
438
+ sew(shapes, tolerance = 1e-6) {
439
+ return wrap("sew", () => {
440
+ const vec = this.#makeVectorU32(shapes);
441
+ try {
442
+ return handle(this.#raw.sew(vec, tolerance));
443
+ }
444
+ finally {
445
+ vec.delete();
446
+ }
447
+ });
448
+ }
449
+ sewAndSolidify(faces, tolerance = 1e-6) {
450
+ return wrap("sewAndSolidify", () => {
451
+ const vec = this.#makeVectorU32(faces);
452
+ try {
453
+ return handle(this.#raw.sewAndSolidify(vec, tolerance));
454
+ }
455
+ finally {
456
+ vec.delete();
457
+ }
458
+ });
459
+ }
460
+ buildSolidFromFaces(faces, tolerance = 1e-6) {
461
+ return wrap("buildSolidFromFaces", () => {
462
+ const vec = this.#makeVectorU32(faces);
463
+ try {
464
+ return handle(this.#raw.buildSolidFromFaces(vec, tolerance));
465
+ }
466
+ finally {
467
+ vec.delete();
468
+ }
469
+ });
470
+ }
187
471
  makeCompound(shapes) {
188
472
  return wrap("makeCompound", () => {
189
- const vec = this.#makeVector(shapes);
473
+ const vec = this.#makeVectorU32(shapes);
190
474
  try {
191
475
  return handle(this.#raw.makeCompound(vec));
192
476
  }
@@ -195,7 +479,18 @@ export class OcctKernel {
195
479
  }
196
480
  });
197
481
  }
198
- // --- Transforms ---
482
+ buildTriFace(a, b, c) {
483
+ return wrap("buildTriFace", () => handle(this.#raw.buildTriFace(a.x, a.y, a.z, b.x, b.y, b.z, c.x, c.y, c.z)));
484
+ }
485
+ makeFaceOnSurface(face, wire) {
486
+ return wrap("makeFaceOnSurface", () => handle(this.#raw.makeFaceOnSurface(face, wire)));
487
+ }
488
+ makeNullShape() {
489
+ return wrap("makeNullShape", () => handle(this.#raw.makeNullShape()));
490
+ }
491
+ // =======================================================================
492
+ // Transforms
493
+ // =======================================================================
199
494
  translate(shape, dx, dy, dz) {
200
495
  return wrap("translate", () => handle(this.#raw.translate(shape, dx, dy, dz)));
201
496
  }
@@ -211,77 +506,232 @@ export class OcctKernel {
211
506
  copy(shape) {
212
507
  return wrap("copy", () => handle(this.#raw.copy(shape)));
213
508
  }
214
- // --- Topology ---
215
- getShapeType(shape) {
216
- return this.#raw.getShapeType(shape);
509
+ /** Apply a 3x4 row-major affine transformation matrix (12 doubles: [r00,r01,r02,tx, r10,r11,r12,ty, r20,r21,r22,tz]). */
510
+ transform(shape, matrix) {
511
+ return wrap("transform", () => {
512
+ const vec = this.#makeVectorF64(matrix);
513
+ try {
514
+ return handle(this.#raw.transform(shape, vec));
515
+ }
516
+ finally {
517
+ vec.delete();
518
+ }
519
+ });
217
520
  }
218
- getSubShapes(shape, type) {
219
- return wrap("getSubShapes", () => {
220
- const vec = this.#raw.getSubShapes(shape, type);
221
- const result = [];
222
- for (let i = 0; i < vec.size(); i++) {
223
- result.push(handle(vec.get(i)));
521
+ /** Apply a general (possibly non-affine) 3x4 row-major transformation matrix (12 doubles). */
522
+ generalTransform(shape, matrix) {
523
+ return wrap("generalTransform", () => {
524
+ const vec = this.#makeVectorF64(matrix);
525
+ try {
526
+ return handle(this.#raw.generalTransform(shape, vec));
527
+ }
528
+ finally {
529
+ vec.delete();
224
530
  }
225
- vec.delete();
226
- return result;
227
531
  });
228
532
  }
533
+ linearPattern(shape, direction, spacing, count) {
534
+ return wrap("linearPattern", () => handle(this.#raw.linearPattern(shape, direction.x, direction.y, direction.z, spacing, count)));
535
+ }
536
+ circularPattern(shape, center, axis, angle, count) {
537
+ return wrap("circularPattern", () => handle(this.#raw.circularPattern(shape, center.x, center.y, center.z, axis.x, axis.y, axis.z, angle, count)));
538
+ }
539
+ /** Compose two 3x4 row-major transformation matrices. Returns a 12-element array. */
540
+ composeTransform(m1, m2) {
541
+ return wrap("composeTransform", () => {
542
+ const v1 = this.#makeVectorF64(m1);
543
+ const v2 = this.#makeVectorF64(m2);
544
+ try {
545
+ const result = this.#raw.composeTransform(v1, v2);
546
+ const out = vecToNumbers(result);
547
+ result.delete();
548
+ return out;
549
+ }
550
+ finally {
551
+ v1.delete();
552
+ v2.delete();
553
+ }
554
+ });
555
+ }
556
+ // =======================================================================
557
+ // Batch Operations
558
+ // =======================================================================
559
+ /** Translate multiple shapes by their respective offsets in a single WASM call. */
560
+ translateBatch(shapes, offsets) {
561
+ return wrap("translateBatch", () => {
562
+ const ids = this.#makeVectorU32(shapes);
563
+ const off = this.#makeVectorF64(offsets);
564
+ try {
565
+ const result = this.#raw.translateBatch(ids, off);
566
+ return vecToHandles(result);
567
+ }
568
+ finally {
569
+ ids.delete();
570
+ off.delete();
571
+ }
572
+ });
573
+ }
574
+ /** Chain boolean operations in a single WASM call. */
575
+ booleanPipeline(base, opCodes, tools) {
576
+ return wrap("booleanPipeline", () => {
577
+ const ops = this.#makeVectorI32(opCodes);
578
+ const ids = this.#makeVectorU32(tools);
579
+ try {
580
+ return handle(this.#raw.booleanPipeline(base, ops, ids));
581
+ }
582
+ finally {
583
+ ops.delete();
584
+ ids.delete();
585
+ }
586
+ });
587
+ }
588
+ // =======================================================================
589
+ // Topology
590
+ // =======================================================================
591
+ getShapeType(shape) {
592
+ return wrap("getShapeType", () => this.#raw.getShapeType(shape));
593
+ }
594
+ getSubShapes(shape, type) {
595
+ return wrap("getSubShapes", () => vecToHandles(this.#raw.getSubShapes(shape, type)));
596
+ }
597
+ downcast(shape, targetType) {
598
+ return wrap("downcast", () => handle(this.#raw.downcast(shape, targetType)));
599
+ }
229
600
  distanceBetween(a, b) {
230
601
  return wrap("distanceBetween", () => this.#raw.distanceBetween(a, b));
231
602
  }
232
- // --- Tessellation ---
233
- /**
234
- * Tessellate a shape into a triangle mesh.
235
- * Returns copied Float32Array/Uint32Array data (safe to keep).
236
- */
603
+ isSame(a, b) {
604
+ return wrap("isSame", () => this.#raw.isSame(a, b));
605
+ }
606
+ isEqual(a, b) {
607
+ return wrap("isEqual", () => this.#raw.isEqual(a, b));
608
+ }
609
+ isNull(shape) {
610
+ return wrap("isNull", () => this.#raw.isNull(shape));
611
+ }
612
+ hashCode(shape, upperBound) {
613
+ return wrap("hashCode", () => this.#raw.hashCode(shape, upperBound));
614
+ }
615
+ shapeOrientation(shape) {
616
+ return wrap("shapeOrientation", () => this.#raw.shapeOrientation(shape));
617
+ }
618
+ sharedEdges(faceA, faceB) {
619
+ return wrap("sharedEdges", () => vecToHandles(this.#raw.sharedEdges(faceA, faceB)));
620
+ }
621
+ adjacentFaces(shape, face) {
622
+ return wrap("adjacentFaces", () => vecToHandles(this.#raw.adjacentFaces(shape, face)));
623
+ }
624
+ iterShapes(shape) {
625
+ return wrap("iterShapes", () => vecToHandles(this.#raw.iterShapes(shape)));
626
+ }
627
+ /** Returns a flat array mapping edge hashes to face hashes. */
628
+ edgeToFaceMap(shape, hashUpperBound) {
629
+ return wrap("edgeToFaceMap", () => {
630
+ const vec = this.#raw.edgeToFaceMap(shape, hashUpperBound);
631
+ const result = vecToNumbers(vec);
632
+ vec.delete();
633
+ return result;
634
+ });
635
+ }
636
+ // =======================================================================
637
+ // Tessellation
638
+ // =======================================================================
639
+ /** Tessellate a shape into a triangle mesh. Returns copied data (safe to keep). */
237
640
  tessellate(shape, options) {
238
641
  return wrap("tessellate", () => {
239
- const linearDeflection = options?.linearDeflection ?? 0.1;
240
- const angularDeflection = options?.angularDeflection ?? 0.5;
241
- const raw = this.#raw.tessellate(shape, linearDeflection, angularDeflection);
242
- try {
243
- const vertexCount = raw.positionCount / 3;
244
- const triangleCount = raw.indexCount / 3;
245
- // Copy data out of WASM heap (safe survives memory growth)
246
- const positions = new Float32Array(this.#module.HEAPF32.buffer.slice(raw.getPositionsPtr(), raw.getPositionsPtr() + raw.positionCount * 4));
247
- const normals = new Float32Array(this.#module.HEAPF32.buffer.slice(raw.getNormalsPtr(), raw.getNormalsPtr() + raw.normalCount * 4));
248
- const indices = new Uint32Array(this.#module.HEAPU32.buffer.slice(raw.getIndicesPtr(), raw.getIndicesPtr() + raw.indexCount * 4));
249
- return {
250
- positions,
251
- normals,
252
- indices,
253
- vertexCount,
254
- triangleCount,
255
- };
642
+ const linDefl = options?.linearDeflection ?? 0.1;
643
+ const angDefl = options?.angularDeflection ?? 0.5;
644
+ return this.#extractMesh(this.#raw.tessellate(shape, linDefl, angDefl));
645
+ });
646
+ }
647
+ /** Sample edges as polylines for wireframe rendering. */
648
+ wireframe(shape, deflection = 0.1) {
649
+ return wrap("wireframe", () => {
650
+ const raw = this.#raw.wireframe(shape, deflection);
651
+ try {
652
+ const points = new Float32Array(this.#module.HEAPF32.buffer.slice(raw.getPointsPtr(), raw.getPointsPtr() + raw.pointCount * 4));
653
+ const edgeCount = raw.edgeGroupCount / 3;
654
+ const edgeGroups = new Int32Array(this.#module.HEAP32.buffer.slice(raw.getEdgeGroupsPtr(), raw.getEdgeGroupsPtr() + raw.edgeGroupCount * 4));
655
+ return { points, edgeGroups, pointCount: raw.pointCount, edgeCount };
256
656
  }
257
657
  finally {
258
658
  raw.delete();
259
659
  }
260
660
  });
261
661
  }
262
- // --- I/O ---
662
+ hasTriangulation(shape) {
663
+ return wrap("hasTriangulation", () => this.#raw.hasTriangulation(shape));
664
+ }
665
+ /** Tessellate with face group data (per-face triangle ranges + hashes). */
666
+ meshShape(shape, options) {
667
+ return wrap("meshShape", () => {
668
+ const linDefl = options?.linearDeflection ?? 0.1;
669
+ const angDefl = options?.angularDeflection ?? 0.5;
670
+ return this.#extractMeshWithFaceGroups(this.#raw.meshShape(shape, linDefl, angDefl));
671
+ });
672
+ }
673
+ /** Tessellate multiple shapes in a single WASM call. */
674
+ meshBatch(shapes, options) {
675
+ return wrap("meshBatch", () => {
676
+ const ids = this.#makeVectorU32(shapes);
677
+ const linDefl = options?.linearDeflection ?? 0.1;
678
+ const angDefl = options?.angularDeflection ?? 0.5;
679
+ try {
680
+ const raw = this.#raw.meshBatch(ids, linDefl, angDefl);
681
+ try {
682
+ const positions = new Float32Array(this.#module.HEAPF32.buffer.slice(raw.getPositionsPtr(), raw.getPositionsPtr() + raw.positionCount * 4));
683
+ const normals = new Float32Array(this.#module.HEAPF32.buffer.slice(raw.getNormalsPtr(), raw.getNormalsPtr() + raw.normalCount * 4));
684
+ const indices = new Uint32Array(this.#module.HEAPU32.buffer.slice(raw.getIndicesPtr(), raw.getIndicesPtr() + raw.indexCount * 4));
685
+ const shapeOffsets = new Int32Array(this.#module.HEAP32.buffer.slice(raw.getShapeOffsetsPtr(), raw.getShapeOffsetsPtr() + raw.shapeCount * 4 * 4));
686
+ return {
687
+ positions,
688
+ normals,
689
+ indices,
690
+ shapeOffsets,
691
+ shapeCount: raw.shapeCount,
692
+ vertexCount: raw.positionCount / 3,
693
+ triangleCount: raw.indexCount / 3,
694
+ };
695
+ }
696
+ finally {
697
+ raw.delete();
698
+ }
699
+ }
700
+ finally {
701
+ ids.delete();
702
+ }
703
+ });
704
+ }
705
+ // =======================================================================
706
+ // I/O
707
+ // =======================================================================
263
708
  importStep(data) {
264
709
  return wrap("importStep", () => {
265
- const str = typeof data === "string"
266
- ? data
267
- : new TextDecoder().decode(data);
710
+ const str = typeof data === "string" ? data : new TextDecoder().decode(data);
268
711
  return handle(this.#raw.importStep(str));
269
712
  });
270
713
  }
271
714
  exportStep(shape) {
272
715
  return wrap("exportStep", () => this.#raw.exportStep(shape));
273
716
  }
274
- exportStl(shape, linearDeflection = 0.1) {
275
- return wrap("exportStl", () => this.#raw.exportStl(shape, linearDeflection));
717
+ importStl(data) {
718
+ return wrap("importStl", () => {
719
+ const str = typeof data === "string" ? data : new TextDecoder().decode(data);
720
+ return handle(this.#raw.importStl(str));
721
+ });
276
722
  }
277
- // --- Healing ---
278
- fixShape(shape) {
279
- return wrap("fixShape", () => handle(this.#raw.fixShape(shape)));
723
+ exportStl(shape, linearDeflection = 0.1, ascii = false) {
724
+ return wrap("exportStl", () => this.#raw.exportStl(shape, linearDeflection, ascii));
280
725
  }
281
- unifySameDomain(shape) {
282
- return wrap("unifySameDomain", () => handle(this.#raw.unifySameDomain(shape)));
726
+ toBREP(shape) {
727
+ return wrap("toBREP", () => this.#raw.toBREP(shape));
283
728
  }
284
- // --- Query ---
729
+ fromBREP(data) {
730
+ return wrap("fromBREP", () => handle(this.#raw.fromBREP(data)));
731
+ }
732
+ // =======================================================================
733
+ // Query / Measure
734
+ // =======================================================================
285
735
  getBoundingBox(shape) {
286
736
  return wrap("getBoundingBox", () => this.#raw.getBoundingBox(shape));
287
737
  }
@@ -291,7 +741,406 @@ export class OcctKernel {
291
741
  getSurfaceArea(shape) {
292
742
  return wrap("getSurfaceArea", () => this.#raw.getSurfaceArea(shape));
293
743
  }
294
- // --- Memory ---
744
+ getLength(shape) {
745
+ return wrap("getLength", () => this.#raw.getLength(shape));
746
+ }
747
+ getCenterOfMass(shape) {
748
+ return wrap("getCenterOfMass", () => {
749
+ const v = this.#raw.getCenterOfMass(shape);
750
+ return this.#vec3FromEmbind(v);
751
+ });
752
+ }
753
+ getLinearCenterOfMass(shape) {
754
+ return wrap("getLinearCenterOfMass", () => {
755
+ const v = this.#raw.getLinearCenterOfMass(shape);
756
+ return this.#vec3FromEmbind(v);
757
+ });
758
+ }
759
+ surfaceCurvature(face, u, v) {
760
+ return wrap("surfaceCurvature", () => this.#curvatureDataFromEmbind(this.#raw.surfaceCurvature(face, u, v)));
761
+ }
762
+ // =======================================================================
763
+ // Surfaces
764
+ // =======================================================================
765
+ vertexPosition(vertex) {
766
+ return wrap("vertexPosition", () => {
767
+ const v = this.#raw.vertexPosition(vertex);
768
+ return this.#vec3FromEmbind(v);
769
+ });
770
+ }
771
+ surfaceType(face) {
772
+ return wrap("surfaceType", () => this.#raw.surfaceType(face));
773
+ }
774
+ surfaceNormal(face, u, v) {
775
+ return wrap("surfaceNormal", () => {
776
+ const vec = this.#raw.surfaceNormal(face, u, v);
777
+ return this.#vec3FromEmbind(vec);
778
+ });
779
+ }
780
+ pointOnSurface(face, u, v) {
781
+ return wrap("pointOnSurface", () => {
782
+ const vec = this.#raw.pointOnSurface(face, u, v);
783
+ return this.#vec3FromEmbind(vec);
784
+ });
785
+ }
786
+ outerWire(face) {
787
+ return wrap("outerWire", () => handle(this.#raw.outerWire(face)));
788
+ }
789
+ uvBounds(face) {
790
+ return wrap("uvBounds", () => this.#uvBoundsFromEmbind(this.#raw.uvBounds(face)));
791
+ }
792
+ /** Project a 3D point onto a face, returning [u, v]. */
793
+ uvFromPoint(face, point) {
794
+ return wrap("uvFromPoint", () => this.#vec2FromEmbind(this.#raw.uvFromPoint(face, point.x, point.y, point.z)));
795
+ }
796
+ /** Project a 3D point onto a face, returning the closest point as Vec3. */
797
+ projectPointOnFace(face, point) {
798
+ return wrap("projectPointOnFace", () => {
799
+ const vec = this.#raw.projectPointOnFace(face, point.x, point.y, point.z);
800
+ return this.#vec3FromEmbind(vec);
801
+ });
802
+ }
803
+ /** Classify a UV point relative to a face boundary. */
804
+ classifyPointOnFace(face, u, v) {
805
+ return wrap("classifyPointOnFace", () => this.#raw.classifyPointOnFace(face, u, v));
806
+ }
807
+ /** Create a BSpline surface from a grid of control points. */
808
+ bsplineSurface(controlPoints, rows, cols) {
809
+ return wrap("bsplineSurface", () => {
810
+ const flat = this.#flattenPoints(controlPoints);
811
+ try {
812
+ return handle(this.#raw.bsplineSurface(flat, rows, cols));
813
+ }
814
+ finally {
815
+ flat.delete();
816
+ }
817
+ });
818
+ }
819
+ // =======================================================================
820
+ // Curves
821
+ // =======================================================================
822
+ curveType(edge) {
823
+ return wrap("curveType", () => this.#raw.curveType(edge));
824
+ }
825
+ curvePointAtParam(edge, param) {
826
+ return wrap("curvePointAtParam", () => {
827
+ const vec = this.#raw.curvePointAtParam(edge, param);
828
+ return this.#vec3FromEmbind(vec);
829
+ });
830
+ }
831
+ curveTangent(edge, param) {
832
+ return wrap("curveTangent", () => {
833
+ const vec = this.#raw.curveTangent(edge, param);
834
+ return this.#vec3FromEmbind(vec);
835
+ });
836
+ }
837
+ /** Returns [firstParam, lastParam]. */
838
+ curveParameters(edge) {
839
+ return wrap("curveParameters", () => {
840
+ const { u: first, v: last } = this.#vec2FromEmbind(this.#raw.curveParameters(edge));
841
+ return { first, last };
842
+ });
843
+ }
844
+ curveIsClosed(edge) {
845
+ return wrap("curveIsClosed", () => this.#raw.curveIsClosed(edge));
846
+ }
847
+ curveIsPeriodic(edge) {
848
+ return wrap("curveIsPeriodic", () => this.#raw.curveIsPeriodic(edge));
849
+ }
850
+ curveLength(edge) {
851
+ return wrap("curveLength", () => this.#raw.curveLength(edge));
852
+ }
853
+ interpolatePoints(points, periodic = false) {
854
+ return wrap("interpolatePoints", () => {
855
+ const flat = this.#flattenPoints(points);
856
+ try {
857
+ return handle(this.#raw.interpolatePoints(flat, periodic));
858
+ }
859
+ finally {
860
+ flat.delete();
861
+ }
862
+ });
863
+ }
864
+ approximatePoints(points, tolerance = 1e-3) {
865
+ return wrap("approximatePoints", () => {
866
+ const flat = this.#flattenPoints(points);
867
+ try {
868
+ return handle(this.#raw.approximatePoints(flat, tolerance));
869
+ }
870
+ finally {
871
+ flat.delete();
872
+ }
873
+ });
874
+ }
875
+ getNurbsCurveData(edge) {
876
+ return wrap("getNurbsCurveData", () => {
877
+ const raw = this.#raw.getNurbsCurveData(edge);
878
+ const result = {
879
+ degree: raw.degree,
880
+ rational: raw.rational,
881
+ periodic: raw.periodic,
882
+ knots: extractFromVector(raw.knots, raw.knots.size()),
883
+ multiplicities: extractFromVector(raw.multiplicities, raw.multiplicities.size()),
884
+ poles: extractFromVector(raw.poles, raw.poles.size()),
885
+ weights: extractFromVector(raw.weights, raw.weights.size()),
886
+ };
887
+ return result;
888
+ });
889
+ }
890
+ liftCurve2dToPlane(points2d, planeOrigin, planeZ, planeX) {
891
+ return wrap("liftCurve2dToPlane", () => {
892
+ const flat = new this.#module.VectorDouble();
893
+ for (const p of points2d) {
894
+ flat.push_back(p.x);
895
+ flat.push_back(p.y);
896
+ }
897
+ try {
898
+ return handle(this.#raw.liftCurve2dToPlane(flat, planeOrigin.x, planeOrigin.y, planeOrigin.z, planeZ.x, planeZ.y, planeZ.z, planeX.x, planeX.y, planeX.z));
899
+ }
900
+ finally {
901
+ flat.delete();
902
+ }
903
+ });
904
+ }
905
+ // =======================================================================
906
+ // Projection (HLR)
907
+ // =======================================================================
908
+ projectEdges(shape, viewOrigin, viewDirection, xAxis) {
909
+ return wrap("projectEdges", () => {
910
+ const hasXAxis = xAxis !== undefined;
911
+ const xx = xAxis?.x ?? 0;
912
+ const xy = xAxis?.y ?? 0;
913
+ const xz = xAxis?.z ?? 0;
914
+ const raw = this.#raw.projectEdges(shape, viewOrigin.x, viewOrigin.y, viewOrigin.z, viewDirection.x, viewDirection.y, viewDirection.z, xx, xy, xz, hasXAxis);
915
+ return {
916
+ visibleOutline: handle(raw.visibleOutline),
917
+ visibleSmooth: handle(raw.visibleSmooth),
918
+ visibleSharp: handle(raw.visibleSharp),
919
+ hiddenOutline: handle(raw.hiddenOutline),
920
+ hiddenSmooth: handle(raw.hiddenSmooth),
921
+ hiddenSharp: handle(raw.hiddenSharp),
922
+ };
923
+ });
924
+ }
925
+ // =======================================================================
926
+ // Modifiers
927
+ // =======================================================================
928
+ thicken(shape, thickness) {
929
+ return wrap("thicken", () => handle(this.#raw.thicken(shape, thickness)));
930
+ }
931
+ defeature(shape, faces) {
932
+ return wrap("defeature", () => {
933
+ const vec = this.#makeVectorU32(faces);
934
+ try {
935
+ return handle(this.#raw.defeature(shape, vec));
936
+ }
937
+ finally {
938
+ vec.delete();
939
+ }
940
+ });
941
+ }
942
+ reverseShape(shape) {
943
+ return wrap("reverseShape", () => handle(this.#raw.reverseShape(shape)));
944
+ }
945
+ simplify(shape) {
946
+ return wrap("simplify", () => handle(this.#raw.simplify(shape)));
947
+ }
948
+ filletVariable(solid, edge, startRadius, endRadius) {
949
+ return wrap("filletVariable", () => handle(this.#raw.filletVariable(solid, edge, startRadius, endRadius)));
950
+ }
951
+ /** Offset a 2D wire. */
952
+ offsetWire2D(wire, offset, joinType = 0) {
953
+ return wrap("offsetWire2D", () => handle(this.#raw.offsetWire2D(wire, offset, joinType)));
954
+ }
955
+ // =======================================================================
956
+ // Evolution (operations with shape history)
957
+ // =======================================================================
958
+ translateWithHistory(shape, dx, dy, dz, inputFaceHashes, hashUpperBound) {
959
+ return wrap("translateWithHistory", () => {
960
+ const hashes = this.#makeVectorI32(inputFaceHashes);
961
+ try {
962
+ return this.#extractEvolution(this.#raw.translateWithHistory(shape, dx, dy, dz, hashes, hashUpperBound));
963
+ }
964
+ finally {
965
+ hashes.delete();
966
+ }
967
+ });
968
+ }
969
+ fuseWithHistory(a, b, inputFaceHashes, hashUpperBound) {
970
+ return wrap("fuseWithHistory", () => {
971
+ const hashes = this.#makeVectorI32(inputFaceHashes);
972
+ try {
973
+ return this.#extractEvolution(this.#raw.fuseWithHistory(a, b, hashes, hashUpperBound));
974
+ }
975
+ finally {
976
+ hashes.delete();
977
+ }
978
+ });
979
+ }
980
+ cutWithHistory(a, b, inputFaceHashes, hashUpperBound) {
981
+ return wrap("cutWithHistory", () => {
982
+ const hashes = this.#makeVectorI32(inputFaceHashes);
983
+ try {
984
+ return this.#extractEvolution(this.#raw.cutWithHistory(a, b, hashes, hashUpperBound));
985
+ }
986
+ finally {
987
+ hashes.delete();
988
+ }
989
+ });
990
+ }
991
+ filletWithHistory(solid, edges, radius, inputFaceHashes, hashUpperBound) {
992
+ return wrap("filletWithHistory", () => {
993
+ const edgeVec = this.#makeVectorU32(edges);
994
+ const hashes = this.#makeVectorI32(inputFaceHashes);
995
+ try {
996
+ return this.#extractEvolution(this.#raw.filletWithHistory(solid, edgeVec, radius, hashes, hashUpperBound));
997
+ }
998
+ finally {
999
+ edgeVec.delete();
1000
+ hashes.delete();
1001
+ }
1002
+ });
1003
+ }
1004
+ rotateWithHistory(shape, axis, angleRad, inputFaceHashes, hashUpperBound) {
1005
+ return wrap("rotateWithHistory", () => {
1006
+ const hashes = this.#makeVectorI32(inputFaceHashes);
1007
+ try {
1008
+ return this.#extractEvolution(this.#raw.rotateWithHistory(shape, axis.point.x, axis.point.y, axis.point.z, axis.direction.x, axis.direction.y, axis.direction.z, angleRad, hashes, hashUpperBound));
1009
+ }
1010
+ finally {
1011
+ hashes.delete();
1012
+ }
1013
+ });
1014
+ }
1015
+ mirrorWithHistory(shape, point, normal, inputFaceHashes, hashUpperBound) {
1016
+ return wrap("mirrorWithHistory", () => {
1017
+ const hashes = this.#makeVectorI32(inputFaceHashes);
1018
+ try {
1019
+ return this.#extractEvolution(this.#raw.mirrorWithHistory(shape, point.x, point.y, point.z, normal.x, normal.y, normal.z, hashes, hashUpperBound));
1020
+ }
1021
+ finally {
1022
+ hashes.delete();
1023
+ }
1024
+ });
1025
+ }
1026
+ scaleWithHistory(shape, center, factor, inputFaceHashes, hashUpperBound) {
1027
+ return wrap("scaleWithHistory", () => {
1028
+ const hashes = this.#makeVectorI32(inputFaceHashes);
1029
+ try {
1030
+ return this.#extractEvolution(this.#raw.scaleWithHistory(shape, center.x, center.y, center.z, factor, hashes, hashUpperBound));
1031
+ }
1032
+ finally {
1033
+ hashes.delete();
1034
+ }
1035
+ });
1036
+ }
1037
+ intersectWithHistory(a, b, inputFaceHashes, hashUpperBound) {
1038
+ return wrap("intersectWithHistory", () => {
1039
+ const hashes = this.#makeVectorI32(inputFaceHashes);
1040
+ try {
1041
+ return this.#extractEvolution(this.#raw.intersectWithHistory(a, b, hashes, hashUpperBound));
1042
+ }
1043
+ finally {
1044
+ hashes.delete();
1045
+ }
1046
+ });
1047
+ }
1048
+ chamferWithHistory(solid, edges, distance, inputFaceHashes, hashUpperBound) {
1049
+ return wrap("chamferWithHistory", () => {
1050
+ const edgeVec = this.#makeVectorU32(edges);
1051
+ const hashes = this.#makeVectorI32(inputFaceHashes);
1052
+ try {
1053
+ return this.#extractEvolution(this.#raw.chamferWithHistory(solid, edgeVec, distance, hashes, hashUpperBound));
1054
+ }
1055
+ finally {
1056
+ edgeVec.delete();
1057
+ hashes.delete();
1058
+ }
1059
+ });
1060
+ }
1061
+ shellWithHistory(solid, faces, thickness, inputFaceHashes, hashUpperBound) {
1062
+ return wrap("shellWithHistory", () => {
1063
+ const faceVec = this.#makeVectorU32(faces);
1064
+ const hashes = this.#makeVectorI32(inputFaceHashes);
1065
+ try {
1066
+ return this.#extractEvolution(this.#raw.shellWithHistory(solid, faceVec, thickness, hashes, hashUpperBound));
1067
+ }
1068
+ finally {
1069
+ faceVec.delete();
1070
+ hashes.delete();
1071
+ }
1072
+ });
1073
+ }
1074
+ offsetWithHistory(solid, distance, inputFaceHashes, hashUpperBound) {
1075
+ return wrap("offsetWithHistory", () => {
1076
+ const hashes = this.#makeVectorI32(inputFaceHashes);
1077
+ try {
1078
+ return this.#extractEvolution(this.#raw.offsetWithHistory(solid, distance, hashes, hashUpperBound));
1079
+ }
1080
+ finally {
1081
+ hashes.delete();
1082
+ }
1083
+ });
1084
+ }
1085
+ thickenWithHistory(shape, thickness, inputFaceHashes, hashUpperBound) {
1086
+ return wrap("thickenWithHistory", () => {
1087
+ const hashes = this.#makeVectorI32(inputFaceHashes);
1088
+ try {
1089
+ return this.#extractEvolution(this.#raw.thickenWithHistory(shape, thickness, hashes, hashUpperBound));
1090
+ }
1091
+ finally {
1092
+ hashes.delete();
1093
+ }
1094
+ });
1095
+ }
1096
+ // =======================================================================
1097
+ // Extrusion Law
1098
+ // =======================================================================
1099
+ buildExtrusionLaw(profile, length, endFactor) {
1100
+ return wrap("buildExtrusionLaw", () => handle(this.#raw.buildExtrusionLaw(profile, length, endFactor)));
1101
+ }
1102
+ trimLaw(law, first, last) {
1103
+ return wrap("trimLaw", () => handle(this.#raw.trimLaw(law, first, last)));
1104
+ }
1105
+ sweepWithLaw(profile, spine, law) {
1106
+ return wrap("sweepWithLaw", () => handle(this.#raw.sweepWithLaw(profile, spine, law)));
1107
+ }
1108
+ // =======================================================================
1109
+ // Healing / Repair
1110
+ // =======================================================================
1111
+ fixShape(shape) {
1112
+ return wrap("fixShape", () => handle(this.#raw.fixShape(shape)));
1113
+ }
1114
+ unifySameDomain(shape) {
1115
+ return wrap("unifySameDomain", () => handle(this.#raw.unifySameDomain(shape)));
1116
+ }
1117
+ isValid(shape) {
1118
+ return wrap("isValid", () => this.#raw.isValid(shape));
1119
+ }
1120
+ healSolid(shape, tolerance = 1e-6) {
1121
+ return wrap("healSolid", () => handle(this.#raw.healSolid(shape, tolerance)));
1122
+ }
1123
+ healFace(shape, tolerance = 1e-6) {
1124
+ return wrap("healFace", () => handle(this.#raw.healFace(shape, tolerance)));
1125
+ }
1126
+ healWire(shape, tolerance = 1e-6) {
1127
+ return wrap("healWire", () => handle(this.#raw.healWire(shape, tolerance)));
1128
+ }
1129
+ fixFaceOrientations(shape) {
1130
+ return wrap("fixFaceOrientations", () => handle(this.#raw.fixFaceOrientations(shape)));
1131
+ }
1132
+ removeDegenerateEdges(shape) {
1133
+ return wrap("removeDegenerateEdges", () => handle(this.#raw.removeDegenerateEdges(shape)));
1134
+ }
1135
+ buildCurves3d(wire) {
1136
+ wrap("buildCurves3d", () => this.#raw.buildCurves3d(wire));
1137
+ }
1138
+ fixWireOnFace(wire, face, tolerance = 1e-6) {
1139
+ return wrap("fixWireOnFace", () => handle(this.#raw.fixWireOnFace(wire, face, tolerance)));
1140
+ }
1141
+ // =======================================================================
1142
+ // Memory
1143
+ // =======================================================================
295
1144
  release(shape) {
296
1145
  this.#raw.release(shape);
297
1146
  }
@@ -301,17 +1150,133 @@ export class OcctKernel {
301
1150
  get shapeCount() {
302
1151
  return this.#raw.getShapeCount();
303
1152
  }
1153
+ // =======================================================================
1154
+ // Debugging
1155
+ // =======================================================================
1156
+ /** Return a human-readable summary of a shape for debugging. */
1157
+ describe(shape) {
1158
+ const type = this.getShapeType(shape);
1159
+ const bbox = this.getBoundingBox(shape);
1160
+ const dims = `[${(bbox.xmax - bbox.xmin).toFixed(2)} x ${(bbox.ymax - bbox.ymin).toFixed(2)} x ${(bbox.zmax - bbox.zmin).toFixed(2)}]`;
1161
+ const parts = [`${type} ${dims}`];
1162
+ if (type === "solid" || type === "compound" || type === "compsolid") {
1163
+ parts.push(`vol=${this.getVolume(shape).toFixed(3)}`);
1164
+ parts.push(`area=${this.getSurfaceArea(shape).toFixed(3)}`);
1165
+ }
1166
+ const faces = this.getSubShapes(shape, "face");
1167
+ const edges = this.getSubShapes(shape, "edge");
1168
+ const verts = this.getSubShapes(shape, "vertex");
1169
+ parts.push(`F:${faces.length} E:${edges.length} V:${verts.length}`);
1170
+ return parts.join(" | ");
1171
+ }
304
1172
  [Symbol.dispose]() {
1173
+ kernelRegistry.unregister(this);
305
1174
  this.#raw.releaseAll();
306
1175
  this.#raw.delete();
307
1176
  }
308
- /** Create an Embind VectorUint32 from a JS array of ShapeHandles. */
309
- #makeVector(ids) {
310
- const vec = new this.#module.VectorUint32();
311
- for (const id of ids) {
312
- vec.push_back(id);
1177
+ // =======================================================================
1178
+ // Private helpers
1179
+ // =======================================================================
1180
+ #makeVector(ctor, values) {
1181
+ const vec = new ctor();
1182
+ for (const v of values) {
1183
+ vec.push_back(v);
313
1184
  }
314
1185
  return vec;
315
1186
  }
1187
+ #makeVectorU32(ids) {
1188
+ return this.#makeVector(this.#module.VectorUint32, ids);
1189
+ }
1190
+ #makeVectorF64(values) {
1191
+ return this.#makeVector(this.#module.VectorDouble, values);
1192
+ }
1193
+ #makeVectorI32(values) {
1194
+ return this.#makeVector(this.#module.VectorInt, values);
1195
+ }
1196
+ #flattenPoints(points) {
1197
+ const vec = new this.#module.VectorDouble();
1198
+ for (const p of points) {
1199
+ vec.push_back(p.x);
1200
+ vec.push_back(p.y);
1201
+ vec.push_back(p.z);
1202
+ }
1203
+ return vec;
1204
+ }
1205
+ #vec2FromEmbind(vec) {
1206
+ const u = vec.get(0);
1207
+ const v = vec.get(1);
1208
+ vec.delete();
1209
+ return { u, v };
1210
+ }
1211
+ #uvBoundsFromEmbind(vec) {
1212
+ const result = {
1213
+ uMin: vec.get(0),
1214
+ uMax: vec.get(1),
1215
+ vMin: vec.get(2),
1216
+ vMax: vec.get(3),
1217
+ };
1218
+ vec.delete();
1219
+ return result;
1220
+ }
1221
+ #curvatureDataFromEmbind(vec) {
1222
+ const result = {
1223
+ min: vec.get(0),
1224
+ max: vec.get(1),
1225
+ gaussian: vec.get(2),
1226
+ mean: vec.get(3),
1227
+ };
1228
+ vec.delete();
1229
+ return result;
1230
+ }
1231
+ #vec3FromEmbind(vec) {
1232
+ const x = vec.get(0);
1233
+ const y = vec.get(1);
1234
+ const z = vec.get(2);
1235
+ vec.delete();
1236
+ return { x, y, z };
1237
+ }
1238
+ #extractMesh(raw) {
1239
+ try {
1240
+ return this.#extractMeshFromRaw(raw);
1241
+ }
1242
+ finally {
1243
+ raw.delete();
1244
+ }
1245
+ }
1246
+ #extractMeshFromRaw(raw) {
1247
+ const vertexCount = raw.positionCount / 3;
1248
+ const triangleCount = raw.indexCount / 3;
1249
+ const positions = new Float32Array(this.#module.HEAPF32.buffer.slice(raw.getPositionsPtr(), raw.getPositionsPtr() + raw.positionCount * 4));
1250
+ const normals = new Float32Array(this.#module.HEAPF32.buffer.slice(raw.getNormalsPtr(), raw.getNormalsPtr() + raw.normalCount * 4));
1251
+ const indices = new Uint32Array(this.#module.HEAPU32.buffer.slice(raw.getIndicesPtr(), raw.getIndicesPtr() + raw.indexCount * 4));
1252
+ return { positions, normals, indices, vertexCount, triangleCount };
1253
+ }
1254
+ #extractMeshWithFaceGroups(raw) {
1255
+ try {
1256
+ const mesh = this.#extractMeshFromRaw(raw);
1257
+ if (raw.faceGroupCount > 0) {
1258
+ mesh.faceGroups = new Int32Array(this.#module.HEAP32.buffer.slice(raw.getFaceGroupsPtr(), raw.getFaceGroupsPtr() + raw.faceGroupCount * 4));
1259
+ mesh.faceCount = raw.faceGroupCount / 3;
1260
+ }
1261
+ return mesh;
1262
+ }
1263
+ finally {
1264
+ raw.delete();
1265
+ }
1266
+ }
1267
+ #extractEvolution(raw) {
1268
+ const modified = vecToNumbers(raw.modified);
1269
+ raw.modified.delete();
1270
+ const generated = vecToNumbers(raw.generated);
1271
+ raw.generated.delete();
1272
+ const deleted = vecToNumbers(raw.deleted);
1273
+ raw.deleted.delete();
1274
+ return {
1275
+ result: handle(raw.resultId),
1276
+ modified,
1277
+ generated,
1278
+ deleted,
1279
+ };
1280
+ }
316
1281
  }
317
1282
  //# sourceMappingURL=index.js.map