cad-mcp-server 0.1.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 (208) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +111 -0
  3. package/THIRD_PARTY_NOTICES.md +20 -0
  4. package/dist/cad/compare.d.ts +31 -0
  5. package/dist/cad/compare.d.ts.map +1 -0
  6. package/dist/cad/compare.js +51 -0
  7. package/dist/cad/compare.js.map +1 -0
  8. package/dist/cad/model-store.d.ts +52 -0
  9. package/dist/cad/model-store.d.ts.map +1 -0
  10. package/dist/cad/model-store.js +227 -0
  11. package/dist/cad/model-store.js.map +1 -0
  12. package/dist/cad/query/edges.d.ts +6 -0
  13. package/dist/cad/query/edges.d.ts.map +1 -0
  14. package/dist/cad/query/edges.js +257 -0
  15. package/dist/cad/query/edges.js.map +1 -0
  16. package/dist/cad/query/entities.d.ts +4 -0
  17. package/dist/cad/query/entities.d.ts.map +1 -0
  18. package/dist/cad/query/entities.js +185 -0
  19. package/dist/cad/query/entities.js.map +1 -0
  20. package/dist/cad/query/faces.d.ts +6 -0
  21. package/dist/cad/query/faces.d.ts.map +1 -0
  22. package/dist/cad/query/faces.js +305 -0
  23. package/dist/cad/query/faces.js.map +1 -0
  24. package/dist/cad/query/pmi.d.ts +3 -0
  25. package/dist/cad/query/pmi.d.ts.map +1 -0
  26. package/dist/cad/query/pmi.js +141 -0
  27. package/dist/cad/query/pmi.js.map +1 -0
  28. package/dist/cad/query/shared.d.ts +74 -0
  29. package/dist/cad/query/shared.d.ts.map +1 -0
  30. package/dist/cad/query/shared.js +181 -0
  31. package/dist/cad/query/shared.js.map +1 -0
  32. package/dist/cad/schema-version.d.ts +2 -0
  33. package/dist/cad/schema-version.d.ts.map +1 -0
  34. package/dist/cad/schema-version.js +2 -0
  35. package/dist/cad/schema-version.js.map +1 -0
  36. package/dist/compare.d.ts +31 -0
  37. package/dist/compare.d.ts.map +1 -0
  38. package/dist/compare.js +51 -0
  39. package/dist/compare.js.map +1 -0
  40. package/dist/index.d.ts +17 -0
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +113 -0
  43. package/dist/index.js.map +1 -0
  44. package/dist/kernel/aag-utils.d.ts +7 -0
  45. package/dist/kernel/aag-utils.d.ts.map +1 -0
  46. package/dist/kernel/aag-utils.js +54 -0
  47. package/dist/kernel/aag-utils.js.map +1 -0
  48. package/dist/kernel/import.d.ts +4 -0
  49. package/dist/kernel/import.d.ts.map +1 -0
  50. package/dist/kernel/import.js +25 -0
  51. package/dist/kernel/import.js.map +1 -0
  52. package/dist/kernel/kernel.d.ts +3 -0
  53. package/dist/kernel/kernel.d.ts.map +1 -0
  54. package/dist/kernel/kernel.js +7 -0
  55. package/dist/kernel/kernel.js.map +1 -0
  56. package/dist/kernel/measure.d.ts +6 -0
  57. package/dist/kernel/measure.d.ts.map +1 -0
  58. package/dist/kernel/measure.js +23 -0
  59. package/dist/kernel/measure.js.map +1 -0
  60. package/dist/kernel/query-entities.d.ts +75 -0
  61. package/dist/kernel/query-entities.d.ts.map +1 -0
  62. package/dist/kernel/query-entities.js +190 -0
  63. package/dist/kernel/query-entities.js.map +1 -0
  64. package/dist/kernel/topology.d.ts +4 -0
  65. package/dist/kernel/topology.d.ts.map +1 -0
  66. package/dist/kernel/topology.js +48 -0
  67. package/dist/kernel/topology.js.map +1 -0
  68. package/dist/model-store.d.ts +52 -0
  69. package/dist/model-store.d.ts.map +1 -0
  70. package/dist/model-store.js +227 -0
  71. package/dist/model-store.js.map +1 -0
  72. package/dist/pmi/metadata.d.ts +15 -0
  73. package/dist/pmi/metadata.d.ts.map +1 -0
  74. package/dist/pmi/metadata.js +93 -0
  75. package/dist/pmi/metadata.js.map +1 -0
  76. package/dist/pmi/parser.d.ts +46 -0
  77. package/dist/pmi/parser.d.ts.map +1 -0
  78. package/dist/pmi/parser.js +400 -0
  79. package/dist/pmi/parser.js.map +1 -0
  80. package/dist/pmi/semantic-provider.d.ts +7 -0
  81. package/dist/pmi/semantic-provider.d.ts.map +1 -0
  82. package/dist/pmi/semantic-provider.js +96 -0
  83. package/dist/pmi/semantic-provider.js.map +1 -0
  84. package/dist/providers/brep.d.ts +39 -0
  85. package/dist/providers/brep.d.ts.map +1 -0
  86. package/dist/providers/brep.js +2 -0
  87. package/dist/providers/brep.js.map +1 -0
  88. package/dist/providers/lightweight-step/metadata.d.ts +15 -0
  89. package/dist/providers/lightweight-step/metadata.d.ts.map +1 -0
  90. package/dist/providers/lightweight-step/metadata.js +93 -0
  91. package/dist/providers/lightweight-step/metadata.js.map +1 -0
  92. package/dist/providers/lightweight-step/pmi-parser.d.ts +46 -0
  93. package/dist/providers/lightweight-step/pmi-parser.d.ts.map +1 -0
  94. package/dist/providers/lightweight-step/pmi-parser.js +400 -0
  95. package/dist/providers/lightweight-step/pmi-parser.js.map +1 -0
  96. package/dist/providers/lightweight-step/semantic-provider.d.ts +7 -0
  97. package/dist/providers/lightweight-step/semantic-provider.d.ts.map +1 -0
  98. package/dist/providers/lightweight-step/semantic-provider.js +96 -0
  99. package/dist/providers/lightweight-step/semantic-provider.js.map +1 -0
  100. package/dist/providers/occt-wasm/aag-utils.d.ts +7 -0
  101. package/dist/providers/occt-wasm/aag-utils.d.ts.map +1 -0
  102. package/dist/providers/occt-wasm/aag-utils.js +54 -0
  103. package/dist/providers/occt-wasm/aag-utils.js.map +1 -0
  104. package/dist/providers/occt-wasm/import.d.ts +4 -0
  105. package/dist/providers/occt-wasm/import.d.ts.map +1 -0
  106. package/dist/providers/occt-wasm/import.js +25 -0
  107. package/dist/providers/occt-wasm/import.js.map +1 -0
  108. package/dist/providers/occt-wasm/kernel.d.ts +3 -0
  109. package/dist/providers/occt-wasm/kernel.d.ts.map +1 -0
  110. package/dist/providers/occt-wasm/kernel.js +7 -0
  111. package/dist/providers/occt-wasm/kernel.js.map +1 -0
  112. package/dist/providers/occt-wasm/measure.d.ts +6 -0
  113. package/dist/providers/occt-wasm/measure.d.ts.map +1 -0
  114. package/dist/providers/occt-wasm/measure.js +23 -0
  115. package/dist/providers/occt-wasm/measure.js.map +1 -0
  116. package/dist/providers/occt-wasm/query-entities.d.ts +71 -0
  117. package/dist/providers/occt-wasm/query-entities.d.ts.map +1 -0
  118. package/dist/providers/occt-wasm/query-entities.js +177 -0
  119. package/dist/providers/occt-wasm/query-entities.js.map +1 -0
  120. package/dist/providers/occt-wasm/topology.d.ts +4 -0
  121. package/dist/providers/occt-wasm/topology.d.ts.map +1 -0
  122. package/dist/providers/occt-wasm/topology.js +48 -0
  123. package/dist/providers/occt-wasm/topology.js.map +1 -0
  124. package/dist/providers/schema.d.ts +50 -0
  125. package/dist/providers/schema.d.ts.map +1 -0
  126. package/dist/providers/schema.js +2 -0
  127. package/dist/providers/schema.js.map +1 -0
  128. package/dist/providers/semantic.d.ts +34 -0
  129. package/dist/providers/semantic.d.ts.map +1 -0
  130. package/dist/providers/semantic.js +2 -0
  131. package/dist/providers/semantic.js.map +1 -0
  132. package/dist/query/edges.d.ts +6 -0
  133. package/dist/query/edges.d.ts.map +1 -0
  134. package/dist/query/edges.js +257 -0
  135. package/dist/query/edges.js.map +1 -0
  136. package/dist/query/entities.d.ts +4 -0
  137. package/dist/query/entities.d.ts.map +1 -0
  138. package/dist/query/entities.js +203 -0
  139. package/dist/query/entities.js.map +1 -0
  140. package/dist/query/faces.d.ts +6 -0
  141. package/dist/query/faces.d.ts.map +1 -0
  142. package/dist/query/faces.js +309 -0
  143. package/dist/query/faces.js.map +1 -0
  144. package/dist/query/pmi.d.ts +3 -0
  145. package/dist/query/pmi.d.ts.map +1 -0
  146. package/dist/query/pmi.js +141 -0
  147. package/dist/query/pmi.js.map +1 -0
  148. package/dist/query/shared.d.ts +74 -0
  149. package/dist/query/shared.d.ts.map +1 -0
  150. package/dist/query/shared.js +181 -0
  151. package/dist/query/shared.js.map +1 -0
  152. package/dist/schema-version.d.ts +2 -0
  153. package/dist/schema-version.d.ts.map +1 -0
  154. package/dist/schema-version.js +2 -0
  155. package/dist/schema-version.js.map +1 -0
  156. package/dist/tools/shared.d.ts +11 -0
  157. package/dist/tools/shared.d.ts.map +1 -0
  158. package/dist/tools/shared.js +19 -0
  159. package/dist/tools/shared.js.map +1 -0
  160. package/dist/tools/step-tools.d.ts +758 -0
  161. package/dist/tools/step-tools.d.ts.map +1 -0
  162. package/dist/tools/step-tools.js +836 -0
  163. package/dist/tools/step-tools.js.map +1 -0
  164. package/dist/types/brep.d.ts +39 -0
  165. package/dist/types/brep.d.ts.map +1 -0
  166. package/dist/types/brep.js +2 -0
  167. package/dist/types/brep.js.map +1 -0
  168. package/dist/types/schema.d.ts +50 -0
  169. package/dist/types/schema.d.ts.map +1 -0
  170. package/dist/types/schema.js +2 -0
  171. package/dist/types/schema.js.map +1 -0
  172. package/dist/types/semantic.d.ts +34 -0
  173. package/dist/types/semantic.d.ts.map +1 -0
  174. package/dist/types/semantic.js +2 -0
  175. package/dist/types/semantic.js.map +1 -0
  176. package/dist/utils/errors.d.ts +7 -0
  177. package/dist/utils/errors.d.ts.map +1 -0
  178. package/dist/utils/errors.js +7 -0
  179. package/dist/utils/errors.js.map +1 -0
  180. package/dist/utils/ids.d.ts +2 -0
  181. package/dist/utils/ids.d.ts.map +1 -0
  182. package/dist/utils/ids.js +4 -0
  183. package/dist/utils/ids.js.map +1 -0
  184. package/dist/utils/numbers.d.ts +11 -0
  185. package/dist/utils/numbers.d.ts.map +1 -0
  186. package/dist/utils/numbers.js +15 -0
  187. package/dist/utils/numbers.js.map +1 -0
  188. package/dist/utils/vectors.d.ts +4 -0
  189. package/dist/utils/vectors.d.ts.map +1 -0
  190. package/dist/utils/vectors.js +14 -0
  191. package/dist/utils/vectors.js.map +1 -0
  192. package/docs/EXAMPLE_PROMPTS.md +61 -0
  193. package/node_modules/occt-wasm/dist/index.d.ts +303 -0
  194. package/node_modules/occt-wasm/dist/index.d.ts.map +1 -0
  195. package/node_modules/occt-wasm/dist/index.js +1125 -0
  196. package/node_modules/occt-wasm/dist/index.js.map +1 -0
  197. package/node_modules/occt-wasm/dist/occt-wasm.js +2 -0
  198. package/node_modules/occt-wasm/dist/occt-wasm.wasm +0 -0
  199. package/node_modules/occt-wasm/dist/raw-types.d.ts +229 -0
  200. package/node_modules/occt-wasm/dist/raw-types.d.ts.map +1 -0
  201. package/node_modules/occt-wasm/dist/raw-types.js +8 -0
  202. package/node_modules/occt-wasm/dist/raw-types.js.map +1 -0
  203. package/node_modules/occt-wasm/dist/types.d.ts +223 -0
  204. package/node_modules/occt-wasm/dist/types.d.ts.map +1 -0
  205. package/node_modules/occt-wasm/dist/types.js +129 -0
  206. package/node_modules/occt-wasm/dist/types.js.map +1 -0
  207. package/node_modules/occt-wasm/package.json +44 -0
  208. package/package.json +102 -0
@@ -0,0 +1,1125 @@
1
+ /**
2
+ * occt-wasm — OCCT compiled to WASM with clean TypeScript bindings.
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * import { OcctKernel } from 'occt-wasm';
7
+ *
8
+ * const kernel = await OcctKernel.init();
9
+ * const box = kernel.makeBox(10, 20, 30);
10
+ * const mesh = kernel.tessellate(box);
11
+ * console.log(`${mesh.triangleCount} triangles`);
12
+ * kernel.release(box);
13
+ * ```
14
+ */
15
+ var _a;
16
+ export { OcctError, OcctErrorCode, } from "./types.js";
17
+ import { wrap } from "./types.js";
18
+ import { SHAPE_TYPES, SHAPE_ORIENTATIONS, POINT_CLASSIFICATIONS } from "./types.js";
19
+ // ---------------------------------------------------------------------------
20
+ // Helpers
21
+ // ---------------------------------------------------------------------------
22
+ function handle(id) {
23
+ return id;
24
+ }
25
+ // Allowed values for the closed string-union enums returned by the kernel,
26
+ // derived from the single source of truth in types.ts so they can't drift.
27
+ // (SurfaceKind/CurveKind are open unions — `string & {}` — so any string is
28
+ // valid by design and needs no check.)
29
+ const SHAPE_TYPE_VALUES = new Set(SHAPE_TYPES);
30
+ const SHAPE_ORIENTATION_VALUES = new Set(SHAPE_ORIENTATIONS);
31
+ const POINT_CLASSIFICATION_VALUES = new Set(POINT_CLASSIFICATIONS);
32
+ /**
33
+ * Coerce a raw kernel string into a closed union, throwing if the kernel ever
34
+ * returns an unexpected value instead of silently casting it into a lie. Called
35
+ * inside `wrap(...)`, so the throw surfaces as a classified `OcctError`.
36
+ */
37
+ function asEnum(value, allowed, label) {
38
+ if (!allowed.has(value)) {
39
+ throw new Error(`unexpected ${label} from kernel: "${value}"`);
40
+ }
41
+ return value;
42
+ }
43
+ /**
44
+ * Safety net: releases the raw Embind kernel if an OcctKernel instance is
45
+ * garbage-collected without being disposed. Prefer `using` or explicit
46
+ * `kernel[Symbol.dispose]()` — the FinalizationRegistry is a last resort.
47
+ */
48
+ const kernelRegistry = new FinalizationRegistry((raw) => {
49
+ try {
50
+ raw.releaseAll();
51
+ raw.delete();
52
+ }
53
+ catch {
54
+ // Already disposed — ignore.
55
+ }
56
+ });
57
+ // ---------------------------------------------------------------------------
58
+ // OcctKernel
59
+ // ---------------------------------------------------------------------------
60
+ /**
61
+ * OCCT kernel compiled to WASM. Arena-based shape management
62
+ * with branded handle types for type safety.
63
+ *
64
+ * Create via `OcctKernel.init()`. Dispose via `kernel[Symbol.dispose]()` or
65
+ * the `using` keyword. A FinalizationRegistry safety net catches leaked
66
+ * instances, but deterministic disposal is strongly preferred.
67
+ */
68
+ export class OcctKernel {
69
+ #raw;
70
+ #module;
71
+ constructor(module) {
72
+ this.#module = module;
73
+ this.#raw = new module.OcctKernel();
74
+ kernelRegistry.register(this, this.#raw, this);
75
+ }
76
+ /**
77
+ * Initialize the WASM module and create a kernel instance.
78
+ *
79
+ * @example
80
+ * ```ts
81
+ * // Auto-detect (works in browser, Node.js, and Workers):
82
+ * const kernel = await OcctKernel.init();
83
+ *
84
+ * // Explicit WASM location:
85
+ * const kernel = await OcctKernel.init({ wasm: '/path/to/occt-wasm.wasm' });
86
+ *
87
+ * // From pre-fetched binary:
88
+ * const binary = await fetch('/occt-wasm.wasm').then(r => r.arrayBuffer());
89
+ * const kernel = await OcctKernel.init({ wasm: binary });
90
+ * ```
91
+ */
92
+ static async init(options) {
93
+ // @ts-expect-error -- occt-wasm.js is generated at build time, no .d.ts
94
+ const imported = await import(/* webpackIgnore: true */ "./occt-wasm.js");
95
+ const createModule = imported.default;
96
+ const moduleOpts = {};
97
+ // Resolve the WASM source: new `wasm` option > legacy `wasmUrl`/`wasmPath`
98
+ const wasmSource = options?.wasm ?? options?.wasmUrl ?? options?.wasmPath;
99
+ if (wasmSource instanceof ArrayBuffer || wasmSource instanceof Uint8Array) {
100
+ // Pre-loaded binary — pass directly to Emscripten
101
+ // For Uint8Array views with non-zero byteOffset, slice to get the correct region
102
+ const bytes = wasmSource instanceof Uint8Array
103
+ ? wasmSource.buffer.slice(wasmSource.byteOffset, wasmSource.byteOffset + wasmSource.byteLength)
104
+ : wasmSource;
105
+ moduleOpts["wasmBinary"] = bytes;
106
+ }
107
+ else if (wasmSource) {
108
+ // String or URL — use locateFile
109
+ const location = wasmSource instanceof URL ? wasmSource.href : wasmSource;
110
+ moduleOpts["locateFile"] = (path) => {
111
+ if (path.endsWith(".wasm"))
112
+ return location;
113
+ return path;
114
+ };
115
+ }
116
+ // When no source is given, Emscripten's default locateFile resolves
117
+ // relative to the JS module URL, which works when .wasm is co-located.
118
+ const module = await createModule(moduleOpts);
119
+ return new _a(module);
120
+ }
121
+ // =======================================================================
122
+ // Construction
123
+ // =======================================================================
124
+ makeVertex(x, y, z) {
125
+ return wrap("makeVertex", () => handle(this.#raw.makeVertex(x, y, z)));
126
+ }
127
+ makeEdge(v1, v2) {
128
+ return wrap("makeEdge", () => handle(this.#raw.makeEdge(v1, v2)));
129
+ }
130
+ makeLineEdge(start, end) {
131
+ return wrap("makeLineEdge", () => handle(this.#raw.makeLineEdge(start.x, start.y, start.z, end.x, end.y, end.z)));
132
+ }
133
+ makeCircleEdge(center, normal, radius) {
134
+ return wrap("makeCircleEdge", () => handle(this.#raw.makeCircleEdge(center.x, center.y, center.z, normal.x, normal.y, normal.z, radius)));
135
+ }
136
+ makeCircleArc(center, normal, radius, startAngle, endAngle) {
137
+ return wrap("makeCircleArc", () => handle(this.#raw.makeCircleArc(center.x, center.y, center.z, normal.x, normal.y, normal.z, radius, startAngle, endAngle)));
138
+ }
139
+ makeArcEdge(start, mid, end) {
140
+ 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)));
141
+ }
142
+ makeEllipseEdge(center, normal, majorRadius, minorRadius) {
143
+ return wrap("makeEllipseEdge", () => handle(this.#raw.makeEllipseEdge(center.x, center.y, center.z, normal.x, normal.y, normal.z, majorRadius, minorRadius)));
144
+ }
145
+ makeEllipseArc(center, normal, majorRadius, minorRadius, startAngle, endAngle) {
146
+ return wrap("makeEllipseArc", () => handle(this.#raw.makeEllipseArc(center.x, center.y, center.z, normal.x, normal.y, normal.z, majorRadius, minorRadius, startAngle, endAngle)));
147
+ }
148
+ makeBezierEdge(controlPoints) {
149
+ return wrap("makeBezierEdge", () => {
150
+ const flat = this.#flattenPoints(controlPoints);
151
+ try {
152
+ return handle(this.#raw.makeBezierEdge(flat));
153
+ }
154
+ finally {
155
+ flat.delete();
156
+ }
157
+ });
158
+ }
159
+ makeBSplineEdge(poles, weights, knots, multiplicities, degree, periodic = false) {
160
+ return wrap("makeBSplineEdge", () => this.#withF64(poles, (polesVec) => this.#withF64(weights, (weightsVec) => this.#withF64(knots, (knotsVec) => this.#withI32(multiplicities, (multsVec) => handle(this.#raw.makeBSplineEdge(polesVec, weightsVec, knotsVec, multsVec, degree, periodic)))))));
161
+ }
162
+ makeTangentArc(start, tangent, end) {
163
+ 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)));
164
+ }
165
+ makeHelixWire(origin, axis, pitch, height, radius) {
166
+ return wrap("makeHelixWire", () => handle(this.#raw.makeHelixWire(origin.x, origin.y, origin.z, axis.x, axis.y, axis.z, pitch, height, radius)));
167
+ }
168
+ makeWire(edges) {
169
+ return wrap("makeWire", () => {
170
+ return this.#withU32(edges, (vec) => handle(this.#raw.makeWire(vec)));
171
+ });
172
+ }
173
+ makeFace(wire) {
174
+ return wrap("makeFace", () => handle(this.#raw.makeFace(wire)));
175
+ }
176
+ addHolesInFace(face, holeWires) {
177
+ return wrap("addHolesInFace", () => {
178
+ return this.#withU32(holeWires, (vec) => handle(this.#raw.addHolesInFace(face, vec)));
179
+ });
180
+ }
181
+ removeHolesFromFace(face, holeIndices) {
182
+ return wrap("removeHolesFromFace", () => {
183
+ return this.#withI32(holeIndices, (vec) => handle(this.#raw.removeHolesFromFace(face, vec)));
184
+ });
185
+ }
186
+ makeSolid(shell) {
187
+ return wrap("makeSolid", () => handle(this.#raw.makeSolid(shell)));
188
+ }
189
+ sew(shapes, tolerance = 1e-6) {
190
+ return wrap("sew", () => {
191
+ return this.#withU32(shapes, (vec) => handle(this.#raw.sew(vec, tolerance)));
192
+ });
193
+ }
194
+ sewAndSolidify(faces, tolerance = 1e-6) {
195
+ return wrap("sewAndSolidify", () => {
196
+ return this.#withU32(faces, (vec) => handle(this.#raw.sewAndSolidify(vec, tolerance)));
197
+ });
198
+ }
199
+ buildSolidFromFaces(faces, tolerance = 1e-6) {
200
+ return wrap("buildSolidFromFaces", () => {
201
+ return this.#withU32(faces, (vec) => handle(this.#raw.buildSolidFromFaces(vec, tolerance)));
202
+ });
203
+ }
204
+ makeCompound(shapes) {
205
+ return wrap("makeCompound", () => {
206
+ return this.#withU32(shapes, (vec) => handle(this.#raw.makeCompound(vec)));
207
+ });
208
+ }
209
+ buildTriFace(a, b, c) {
210
+ 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)));
211
+ }
212
+ makeFaceOnSurface(face, wire) {
213
+ return wrap("makeFaceOnSurface", () => handle(this.#raw.makeFaceOnSurface(face, wire)));
214
+ }
215
+ makeNullShape() {
216
+ return wrap("makeNullShape", () => handle(this.#raw.makeNullShape()));
217
+ }
218
+ // =======================================================================
219
+ // Transforms
220
+ // =======================================================================
221
+ translate(shape, dx, dy, dz) {
222
+ return wrap("translate", () => handle(this.#raw.translate(shape, dx, dy, dz)));
223
+ }
224
+ /**
225
+ * Translate along X so the chosen bounding-box anchor lands at `target`.
226
+ * Returns a new shape; the input is left untouched.
227
+ */
228
+ alignX(shape, target = 0, anchor = "center") {
229
+ return wrap("alignX", () => {
230
+ const bb = this.getBoundingBox(shape, false);
231
+ const cur = anchor === "min" ? bb.xmin : anchor === "max" ? bb.xmax : (bb.xmin + bb.xmax) / 2;
232
+ return handle(this.#raw.translate(shape, target - cur, 0, 0));
233
+ });
234
+ }
235
+ /** Translate along Y so the chosen bounding-box anchor lands at `target`. */
236
+ alignY(shape, target = 0, anchor = "center") {
237
+ return wrap("alignY", () => {
238
+ const bb = this.getBoundingBox(shape, false);
239
+ const cur = anchor === "min" ? bb.ymin : anchor === "max" ? bb.ymax : (bb.ymin + bb.ymax) / 2;
240
+ return handle(this.#raw.translate(shape, 0, target - cur, 0));
241
+ });
242
+ }
243
+ /** Translate along Z so the chosen bounding-box anchor lands at `target`. */
244
+ alignZ(shape, target = 0, anchor = "center") {
245
+ return wrap("alignZ", () => {
246
+ const bb = this.getBoundingBox(shape, false);
247
+ const cur = anchor === "min" ? bb.zmin : anchor === "max" ? bb.zmax : (bb.zmin + bb.zmax) / 2;
248
+ return handle(this.#raw.translate(shape, 0, 0, target - cur));
249
+ });
250
+ }
251
+ rotate(shape, axis, angleRad) {
252
+ return wrap("rotate", () => handle(this.#raw.rotate(shape, axis.point.x, axis.point.y, axis.point.z, axis.direction.x, axis.direction.y, axis.direction.z, angleRad)));
253
+ }
254
+ scale(shape, center, factor) {
255
+ return wrap("scale", () => handle(this.#raw.scale(shape, center.x, center.y, center.z, factor)));
256
+ }
257
+ mirror(shape, point, normal) {
258
+ return wrap("mirror", () => handle(this.#raw.mirror(shape, point.x, point.y, point.z, normal.x, normal.y, normal.z)));
259
+ }
260
+ copy(shape) {
261
+ return wrap("copy", () => handle(this.#raw.copy(shape)));
262
+ }
263
+ /** Apply a 3x4 row-major affine transformation matrix (12 doubles: [r00,r01,r02,tx, r10,r11,r12,ty, r20,r21,r22,tz]). */
264
+ transform(shape, matrix) {
265
+ return wrap("transform", () => {
266
+ return this.#withF64(matrix, (vec) => handle(this.#raw.transform(shape, vec)));
267
+ });
268
+ }
269
+ /** Apply a general (possibly non-affine) 3x4 row-major transformation matrix (12 doubles). */
270
+ generalTransform(shape, matrix) {
271
+ return wrap("generalTransform", () => {
272
+ return this.#withF64(matrix, (vec) => handle(this.#raw.generalTransform(shape, vec)));
273
+ });
274
+ }
275
+ linearPattern(shape, direction, spacing, count) {
276
+ return wrap("linearPattern", () => handle(this.#raw.linearPattern(shape, direction.x, direction.y, direction.z, spacing, count)));
277
+ }
278
+ circularPattern(shape, center, axis, angle, count) {
279
+ return wrap("circularPattern", () => handle(this.#raw.circularPattern(shape, center.x, center.y, center.z, axis.x, axis.y, axis.z, angle, count)));
280
+ }
281
+ /** Compose two 3x4 row-major transformation matrices. Returns a 12-element array. */
282
+ composeTransform(m1, m2) {
283
+ return wrap("composeTransform", () => this.#withF64(m1, (v1) => this.#withF64(m2, (v2) => this.#drainVector(this.#raw.composeTransform(v1, v2), Float64Array))));
284
+ }
285
+ // =======================================================================
286
+ // Batch Operations
287
+ // =======================================================================
288
+ /** Translate multiple shapes by their respective offsets in a single WASM call. */
289
+ translateBatch(shapes, offsets) {
290
+ return wrap("translateBatch", () => this.#withU32(shapes, (ids) => this.#withF64(offsets, (off) => this.#vecToHandles(this.#raw.translateBatch(ids, off)))));
291
+ }
292
+ /** Query multiple shapes in a single WASM call: bbox, volume, area, center of mass, type, validity. */
293
+ queryBatch(shapes) {
294
+ return wrap("queryBatch", () => this.#withU32(shapes, (ids) => {
295
+ const arr = this.#drainVector(this.#raw.queryBatch(ids), Float64Array);
296
+ const STRIDE = 14;
297
+ const results = [];
298
+ for (let i = 0; i < shapes.length; i++) {
299
+ const o = i * STRIDE;
300
+ results.push({
301
+ volume: arr[o],
302
+ area: arr[o + 1],
303
+ bbox: { xmin: arr[o + 2], ymin: arr[o + 3], zmin: arr[o + 4], xmax: arr[o + 5], ymax: arr[o + 6], zmax: arr[o + 7] },
304
+ centerOfMass: { x: arr[o + 8], y: arr[o + 9], z: arr[o + 10] },
305
+ shapeType: SHAPE_TYPES[arr[o + 11]] ?? "shape",
306
+ isValid: arr[o + 12] === 1.0,
307
+ });
308
+ }
309
+ return results;
310
+ }));
311
+ }
312
+ /** Apply 3x4 affine transforms to multiple shapes in a single WASM call. */
313
+ transformBatch(shapes, matrices) {
314
+ return wrap("transformBatch", () => this.#withU32(shapes, (ids) => this.#withF64(matrices, (mats) => this.#vecToHandles(this.#raw.transformBatch(ids, mats)))));
315
+ }
316
+ /** Rotate multiple shapes in a single WASM call. */
317
+ rotateBatch(shapes, params) {
318
+ return wrap("rotateBatch", () => this.#withU32(shapes, (ids) => this.#withF64(params, (p) => this.#vecToHandles(this.#raw.rotateBatch(ids, p)))));
319
+ }
320
+ /** Scale multiple shapes in a single WASM call. */
321
+ scaleBatch(shapes, params) {
322
+ return wrap("scaleBatch", () => this.#withU32(shapes, (ids) => this.#withF64(params, (p) => this.#vecToHandles(this.#raw.scaleBatch(ids, p)))));
323
+ }
324
+ /** Mirror multiple shapes in a single WASM call. */
325
+ mirrorBatch(shapes, params) {
326
+ return wrap("mirrorBatch", () => this.#withU32(shapes, (ids) => this.#withF64(params, (p) => this.#vecToHandles(this.#raw.mirrorBatch(ids, p)))));
327
+ }
328
+ // =======================================================================
329
+ // Topology
330
+ // =======================================================================
331
+ getShapeType(shape) {
332
+ return wrap("getShapeType", () => asEnum(this.#raw.getShapeType(shape), SHAPE_TYPE_VALUES, "shape type"));
333
+ }
334
+ /** True if the shape is a compound. */
335
+ isCompound(shape) { return this.getShapeType(shape) === "compound"; }
336
+ /** True if the shape is a comp-solid. */
337
+ isCompSolid(shape) { return this.getShapeType(shape) === "compsolid"; }
338
+ /** True if the shape is a solid. */
339
+ isSolid(shape) { return this.getShapeType(shape) === "solid"; }
340
+ /** True if the shape is a shell. */
341
+ isShell(shape) { return this.getShapeType(shape) === "shell"; }
342
+ /** True if the shape is a face. */
343
+ isFace(shape) { return this.getShapeType(shape) === "face"; }
344
+ /** True if the shape is a wire. */
345
+ isWire(shape) { return this.getShapeType(shape) === "wire"; }
346
+ /** True if the shape is an edge. */
347
+ isEdge(shape) { return this.getShapeType(shape) === "edge"; }
348
+ /** True if the shape is a vertex. */
349
+ isVertex(shape) { return this.getShapeType(shape) === "vertex"; }
350
+ getSubShapes(shape, type) {
351
+ return wrap("getSubShapes", () => this.#vecToHandles(this.#raw.getSubShapes(shape, type)));
352
+ }
353
+ downcast(shape, targetType) {
354
+ return wrap("downcast", () => handle(this.#raw.downcast(shape, targetType)));
355
+ }
356
+ distanceBetween(a, b) {
357
+ return wrap("distanceBetween", () => this.#raw.distanceBetween(a, b));
358
+ }
359
+ isSame(a, b) {
360
+ return wrap("isSame", () => this.#raw.isSame(a, b));
361
+ }
362
+ isEqual(a, b) {
363
+ return wrap("isEqual", () => this.#raw.isEqual(a, b));
364
+ }
365
+ isNull(shape) {
366
+ return wrap("isNull", () => this.#raw.isNull(shape));
367
+ }
368
+ hashCode(shape, upperBound) {
369
+ return wrap("hashCode", () => this.#raw.hashCode(shape, upperBound));
370
+ }
371
+ shapeOrientation(shape) {
372
+ return wrap("shapeOrientation", () => asEnum(this.#raw.shapeOrientation(shape), SHAPE_ORIENTATION_VALUES, "shape orientation"));
373
+ }
374
+ sharedEdges(faceA, faceB) {
375
+ return wrap("sharedEdges", () => this.#vecToHandles(this.#raw.sharedEdges(faceA, faceB)));
376
+ }
377
+ adjacentFaces(shape, face) {
378
+ return wrap("adjacentFaces", () => this.#vecToHandles(this.#raw.adjacentFaces(shape, face)));
379
+ }
380
+ iterShapes(shape) {
381
+ return wrap("iterShapes", () => this.#vecToHandles(this.#raw.iterShapes(shape)));
382
+ }
383
+ /** Returns a flat array mapping edge hashes to face hashes. */
384
+ edgeToFaceMap(shape, hashUpperBound) {
385
+ return wrap("edgeToFaceMap", () => {
386
+ const vec = this.#raw.edgeToFaceMap(shape, hashUpperBound);
387
+ return this.#drainVector(vec, Int32Array);
388
+ });
389
+ }
390
+ // =======================================================================
391
+ // Tessellation
392
+ // =======================================================================
393
+ /** Tessellate a shape into a triangle mesh. Returns copied data (safe to keep). */
394
+ tessellate(shape, options) {
395
+ return wrap("tessellate", () => {
396
+ const linDefl = options?.linearDeflection ?? 0.1;
397
+ const angDefl = options?.angularDeflection ?? 0.5;
398
+ const raw = options?.relative
399
+ ? this.#raw.tessellateRelative(shape, linDefl, angDefl)
400
+ : this.#raw.tessellate(shape, linDefl, angDefl);
401
+ return this.#extractMesh(raw);
402
+ });
403
+ }
404
+ /** Sample edges as polylines for wireframe rendering. */
405
+ wireframe(shape, deflection = 0.1) {
406
+ return wrap("wireframe", () => {
407
+ const raw = this.#raw.wireframe(shape, deflection);
408
+ try {
409
+ const points = new Float32Array(this.#module.HEAPF32.buffer.slice(raw.getPointsPtr(), raw.getPointsPtr() + raw.pointCount * 4));
410
+ const edgeCount = raw.edgeGroupCount / 3;
411
+ const edgeGroups = new Int32Array(this.#module.HEAP32.buffer.slice(raw.getEdgeGroupsPtr(), raw.getEdgeGroupsPtr() + raw.edgeGroupCount * 4));
412
+ return { points, edgeGroups, pointCount: raw.pointCount, edgeCount };
413
+ }
414
+ finally {
415
+ raw.delete();
416
+ }
417
+ });
418
+ }
419
+ hasTriangulation(shape) {
420
+ return wrap("hasTriangulation", () => this.#raw.hasTriangulation(shape));
421
+ }
422
+ /** Tessellate with face group data (per-face triangle ranges + hashes). */
423
+ meshShape(shape, options) {
424
+ return wrap("meshShape", () => {
425
+ const linDefl = options?.linearDeflection ?? 0.1;
426
+ const angDefl = options?.angularDeflection ?? 0.5;
427
+ return this.#extractMeshWithFaceGroups(this.#raw.meshShape(shape, linDefl, angDefl));
428
+ });
429
+ }
430
+ /** Tessellate multiple shapes in a single WASM call. */
431
+ meshBatch(shapes, options) {
432
+ return wrap("meshBatch", () => this.#withU32(shapes, (ids) => {
433
+ const linDefl = options?.linearDeflection ?? 0.1;
434
+ const angDefl = options?.angularDeflection ?? 0.5;
435
+ const raw = this.#raw.meshBatch(ids, linDefl, angDefl);
436
+ try {
437
+ const positions = new Float32Array(this.#module.HEAPF32.buffer.slice(raw.getPositionsPtr(), raw.getPositionsPtr() + raw.positionCount * 4));
438
+ const normals = new Float32Array(this.#module.HEAPF32.buffer.slice(raw.getNormalsPtr(), raw.getNormalsPtr() + raw.normalCount * 4));
439
+ const indices = new Uint32Array(this.#module.HEAPU32.buffer.slice(raw.getIndicesPtr(), raw.getIndicesPtr() + raw.indexCount * 4));
440
+ const shapeOffsets = new Int32Array(this.#module.HEAP32.buffer.slice(raw.getShapeOffsetsPtr(), raw.getShapeOffsetsPtr() + raw.shapeCount * 4 * 4));
441
+ return {
442
+ positions,
443
+ normals,
444
+ indices,
445
+ shapeOffsets,
446
+ shapeCount: raw.shapeCount,
447
+ vertexCount: raw.positionCount / 3,
448
+ triangleCount: raw.indexCount / 3,
449
+ };
450
+ }
451
+ finally {
452
+ raw.delete();
453
+ }
454
+ }));
455
+ }
456
+ // =======================================================================
457
+ // I/O
458
+ // =======================================================================
459
+ importStep(data) {
460
+ return wrap("importStep", () => {
461
+ const str = typeof data === "string" ? data : new TextDecoder().decode(data);
462
+ return handle(this.#raw.importStep(str));
463
+ });
464
+ }
465
+ exportStep(shape) {
466
+ return wrap("exportStep", () => this.#raw.exportStep(shape));
467
+ }
468
+ importStl(data) {
469
+ return wrap("importStl", () => {
470
+ const str = typeof data === "string" ? data : new TextDecoder().decode(data);
471
+ return handle(this.#raw.importStl(str));
472
+ });
473
+ }
474
+ exportStl(shape, linearDeflection = 0.1, ascii = false) {
475
+ return wrap("exportStl", () => this.#raw.exportStl(shape, linearDeflection, ascii));
476
+ }
477
+ toBREP(shape) {
478
+ return wrap("toBREP", () => this.#raw.toBREP(shape));
479
+ }
480
+ fromBREP(data) {
481
+ return wrap("fromBREP", () => handle(this.#raw.fromBREP(data)));
482
+ }
483
+ /** Serialize a shape to binary BREP (smaller/faster than the text format). */
484
+ toBREPBinary(shape) {
485
+ return wrap("toBREPBinary", () => {
486
+ const path = this.#raw.exportBrepBinary(shape);
487
+ const bytes = this.#module.FS.readFile(path);
488
+ this.#module.FS.unlink(path);
489
+ return bytes;
490
+ });
491
+ }
492
+ /** Load a shape from binary BREP produced by {@link toBREPBinary}. */
493
+ fromBREPBinary(data) {
494
+ return wrap("fromBREPBinary", () => {
495
+ const path = "/tmp/occt-import.brep.bin";
496
+ this.#module.FS.writeFile(path, data);
497
+ try {
498
+ return handle(this.#raw.importBrepBinary(path));
499
+ }
500
+ finally {
501
+ this.#module.FS.unlink(path);
502
+ }
503
+ });
504
+ }
505
+ cacheStep(stepData) {
506
+ return wrap("cacheStep", () => {
507
+ const shape = this.importStep(stepData);
508
+ try {
509
+ return this.toBREP(shape);
510
+ }
511
+ finally {
512
+ this.release(shape);
513
+ }
514
+ });
515
+ }
516
+ loadCached(brep) {
517
+ return wrap("loadCached", () => this.fromBREP(brep));
518
+ }
519
+ // =======================================================================
520
+ // Query / Measure
521
+ // =======================================================================
522
+ /**
523
+ * Compute the axis-aligned bounding box of a shape.
524
+ *
525
+ * Uses `BRepBndLib::AddOptimal` for surface-precise bounds independent of
526
+ * tessellation state. The simpler `BRepBndLib::Add` falls back to BSpline
527
+ * pole hulls when triangulation is absent, which overshoots curved
528
+ * geometry by ~0.27·r for arcs of radius r — that was the source of the
529
+ * uniform 1.2 mm bounds shift versus brepjs in occt-wasm 2.0.
530
+ *
531
+ * @param useTriangulation - If `true`, use existing triangulation as the
532
+ * starting bound and refine via surface analysis (faster). If `false`,
533
+ * do the surface analysis from scratch (slower, but doesn't depend on
534
+ * prior tessellation). Both modes produce tight bounds; brepjs's
535
+ * `BRepBndLib.Add(shape, box, true)` corresponds to `true` here.
536
+ */
537
+ getBoundingBox(shape, useTriangulation) {
538
+ return wrap("getBoundingBox", () => this.#raw.getBoundingBox(shape, useTriangulation));
539
+ }
540
+ getVolume(shape) {
541
+ return wrap("getVolume", () => this.#raw.getVolume(shape));
542
+ }
543
+ getSurfaceArea(shape) {
544
+ return wrap("getSurfaceArea", () => this.#raw.getSurfaceArea(shape));
545
+ }
546
+ getLength(shape) {
547
+ return wrap("getLength", () => this.#raw.getLength(shape));
548
+ }
549
+ getCenterOfMass(shape) {
550
+ return wrap("getCenterOfMass", () => {
551
+ const v = this.#raw.getCenterOfMass(shape);
552
+ return this.#vec3FromEmbind(v);
553
+ });
554
+ }
555
+ /**
556
+ * Matrix of inertia about the center of mass, as a row-major 3×3 array
557
+ * (length 9). Symmetric: `[1]==[3]`, `[2]==[6]`, `[5]==[7]`.
558
+ */
559
+ getInertia(shape) {
560
+ return wrap("getInertia", () => Array.from(this.#drainVector(this.#raw.getInertia(shape), Float64Array)));
561
+ }
562
+ /** True if `point` lies inside (or on the boundary of) a solid. */
563
+ containsPoint(shape, point, tolerance = 1e-7) {
564
+ return wrap("containsPoint", () => this.#raw.containsPoint(shape, point.x, point.y, point.z, tolerance));
565
+ }
566
+ /**
567
+ * Surface (area-weighted) center of mass for a face. Equivalent to
568
+ * `BRepGProp::SurfaceProperties(face, props).CentreOfMass()`.
569
+ *
570
+ * Use this for face fingerprinting and finder predicates rather than a
571
+ * tessellation-based centroid — for non-planar faces (cylinders, holed
572
+ * planes) the two diverge.
573
+ */
574
+ getSurfaceCenterOfMass(face) {
575
+ return wrap("getSurfaceCenterOfMass", () => {
576
+ const v = this.#raw.getSurfaceCenterOfMass(face);
577
+ return this.#vec3FromEmbind(v);
578
+ });
579
+ }
580
+ getLinearCenterOfMass(shape) {
581
+ return wrap("getLinearCenterOfMass", () => {
582
+ const v = this.#raw.getLinearCenterOfMass(shape);
583
+ return this.#vec3FromEmbind(v);
584
+ });
585
+ }
586
+ surfaceCurvature(face, u, v) {
587
+ return wrap("surfaceCurvature", () => this.#curvatureDataFromEmbind(this.#raw.surfaceCurvature(face, u, v)));
588
+ }
589
+ // =======================================================================
590
+ // Surfaces
591
+ // =======================================================================
592
+ vertexPosition(vertex) {
593
+ return wrap("vertexPosition", () => {
594
+ const v = this.#raw.vertexPosition(vertex);
595
+ return this.#vec3FromEmbind(v);
596
+ });
597
+ }
598
+ surfaceType(face) {
599
+ return wrap("surfaceType", () => this.#raw.surfaceType(face));
600
+ }
601
+ surfaceNormal(face, u, v) {
602
+ return wrap("surfaceNormal", () => {
603
+ const vec = this.#raw.surfaceNormal(face, u, v);
604
+ return this.#vec3FromEmbind(vec);
605
+ });
606
+ }
607
+ pointOnSurface(face, u, v) {
608
+ return wrap("pointOnSurface", () => {
609
+ const vec = this.#raw.pointOnSurface(face, u, v);
610
+ return this.#vec3FromEmbind(vec);
611
+ });
612
+ }
613
+ outerWire(face) {
614
+ return wrap("outerWire", () => handle(this.#raw.outerWire(face)));
615
+ }
616
+ uvBounds(face) {
617
+ return wrap("uvBounds", () => this.#uvBoundsFromEmbind(this.#raw.uvBounds(face)));
618
+ }
619
+ /** Project a 3D point onto a face, returning [u, v]. */
620
+ uvFromPoint(face, point) {
621
+ return wrap("uvFromPoint", () => this.#vec2FromEmbind(this.#raw.uvFromPoint(face, point.x, point.y, point.z)));
622
+ }
623
+ /**
624
+ * Extract cylinder data from a cylindrical face.
625
+ *
626
+ * Returns `null` when the face's underlying surface is not a cylinder,
627
+ * otherwise radius/directness plus cylinder axis data where `isDirect` mirrors
628
+ * `gp_Cylinder::Direct()` (i.e. whether U and V form a right-handed pair).
629
+ */
630
+ getFaceCylinderData(face) {
631
+ return wrap("getFaceCylinderData", () => {
632
+ const vec = this.#raw.getFaceCylinderData(face);
633
+ try {
634
+ if (vec.size() === 0)
635
+ return null;
636
+ return {
637
+ radius: vec.get(0),
638
+ isDirect: vec.get(1) !== 0,
639
+ location: { x: vec.get(2), y: vec.get(3), z: vec.get(4) },
640
+ direction: { x: vec.get(5), y: vec.get(6), z: vec.get(7) },
641
+ };
642
+ }
643
+ finally {
644
+ vec.delete();
645
+ }
646
+ });
647
+ }
648
+ getFaceCylinderAxis(face) {
649
+ return wrap("getFaceCylinderAxis", () => {
650
+ const data = this.getFaceCylinderData(face);
651
+ if (!data)
652
+ return null;
653
+ return { location: data.location, direction: data.direction };
654
+ });
655
+ }
656
+ /** Project a 3D point onto a face, returning the closest point as Vec3. */
657
+ projectPointOnFace(face, point) {
658
+ return wrap("projectPointOnFace", () => {
659
+ const vec = this.#raw.projectPointOnFace(face, point.x, point.y, point.z);
660
+ return this.#vec3FromEmbind(vec);
661
+ });
662
+ }
663
+ /** Classify a UV point relative to a face boundary. */
664
+ classifyPointOnFace(face, u, v) {
665
+ return wrap("classifyPointOnFace", () => asEnum(this.#raw.classifyPointOnFace(face, u, v), POINT_CLASSIFICATION_VALUES, "point classification"));
666
+ }
667
+ /** Create a BSpline surface from a grid of control points. */
668
+ bsplineSurface(controlPoints, rows, cols) {
669
+ return wrap("bsplineSurface", () => {
670
+ const flat = this.#flattenPoints(controlPoints);
671
+ try {
672
+ return handle(this.#raw.bsplineSurface(flat, rows, cols));
673
+ }
674
+ finally {
675
+ flat.delete();
676
+ }
677
+ });
678
+ }
679
+ // =======================================================================
680
+ // Curves
681
+ // =======================================================================
682
+ curveType(edge) {
683
+ return wrap("curveType", () => this.#raw.curveType(edge));
684
+ }
685
+ curvePointAtParam(edge, param) {
686
+ return wrap("curvePointAtParam", () => {
687
+ const vec = this.#raw.curvePointAtParam(edge, param);
688
+ return this.#vec3FromEmbind(vec);
689
+ });
690
+ }
691
+ curveTangent(edge, param) {
692
+ return wrap("curveTangent", () => {
693
+ const vec = this.#raw.curveTangent(edge, param);
694
+ return this.#vec3FromEmbind(vec);
695
+ });
696
+ }
697
+ /** Returns [firstParam, lastParam]. */
698
+ curveParameters(edge) {
699
+ return wrap("curveParameters", () => {
700
+ const { u: first, v: last } = this.#vec2FromEmbind(this.#raw.curveParameters(edge));
701
+ return { first, last };
702
+ });
703
+ }
704
+ curveIsClosed(edge) {
705
+ return wrap("curveIsClosed", () => this.#raw.curveIsClosed(edge));
706
+ }
707
+ curveIsPeriodic(edge) {
708
+ return wrap("curveIsPeriodic", () => this.#raw.curveIsPeriodic(edge));
709
+ }
710
+ curveLength(edge) {
711
+ return wrap("curveLength", () => this.#raw.curveLength(edge));
712
+ }
713
+ interpolatePoints(points, periodic = false) {
714
+ return wrap("interpolatePoints", () => {
715
+ const flat = this.#flattenPoints(points);
716
+ try {
717
+ return handle(this.#raw.interpolatePoints(flat, periodic));
718
+ }
719
+ finally {
720
+ flat.delete();
721
+ }
722
+ });
723
+ }
724
+ /**
725
+ * Interpolate a cubic B-spline through the points with clamped start/end
726
+ * tangent directions.
727
+ */
728
+ interpolatePointsWithTangents(points, startTangent, endTangent) {
729
+ return wrap("interpolatePointsWithTangents", () => {
730
+ const flat = this.#flattenPoints(points);
731
+ try {
732
+ return handle(this.#raw.interpolatePointsWithTangents(flat, startTangent.x, startTangent.y, startTangent.z, endTangent.x, endTangent.y, endTangent.z));
733
+ }
734
+ finally {
735
+ flat.delete();
736
+ }
737
+ });
738
+ }
739
+ /** Closest point on an edge to `point`, with the curve tangent and parameter there. */
740
+ projectPointOnEdge(edge, point) {
741
+ return wrap("projectPointOnEdge", () => {
742
+ const r = this.#drainVector(this.#raw.projectPointOnEdge(edge, point.x, point.y, point.z), Float64Array);
743
+ return {
744
+ point: { x: r[0], y: r[1], z: r[2] },
745
+ tangent: { x: r[3], y: r[4], z: r[5] },
746
+ parameter: r[6],
747
+ };
748
+ });
749
+ }
750
+ approximatePoints(points, tolerance = 1e-3) {
751
+ return wrap("approximatePoints", () => {
752
+ const flat = this.#flattenPoints(points);
753
+ try {
754
+ return handle(this.#raw.approximatePoints(flat, tolerance));
755
+ }
756
+ finally {
757
+ flat.delete();
758
+ }
759
+ });
760
+ }
761
+ getNurbsCurveData(edge) {
762
+ return wrap("getNurbsCurveData", () => {
763
+ const raw = this.#raw.getNurbsCurveData(edge);
764
+ const result = {
765
+ degree: raw.degree,
766
+ rational: raw.rational,
767
+ periodic: raw.periodic,
768
+ knots: this.#drainVector(raw.knots, Float64Array),
769
+ multiplicities: this.#drainVector(raw.multiplicities, Int32Array),
770
+ poles: this.#drainVector(raw.poles, Float64Array),
771
+ weights: this.#drainVector(raw.weights, Float64Array),
772
+ };
773
+ return result;
774
+ });
775
+ }
776
+ curveDegreeElevate(edge, elevateBy) {
777
+ return wrap("curveDegreeElevate", () => handle(this.#raw.curveDegreeElevate(edge, elevateBy)));
778
+ }
779
+ curveKnotInsert(edge, knot, times) {
780
+ return wrap("curveKnotInsert", () => handle(this.#raw.curveKnotInsert(edge, knot, times)));
781
+ }
782
+ curveKnotRemove(edge, knot, tolerance) {
783
+ return wrap("curveKnotRemove", () => handle(this.#raw.curveKnotRemove(edge, knot, tolerance)));
784
+ }
785
+ curveSplit(edge, param) {
786
+ return wrap("curveSplit", () => {
787
+ const parts = this.#vecToHandles(this.#raw.curveSplit(edge, param));
788
+ if (parts.length !== 2) {
789
+ throw new Error(`curveSplit: expected 2 edges, got ${parts.length}`);
790
+ }
791
+ return [parts[0], parts[1]];
792
+ });
793
+ }
794
+ liftCurve2dToPlane(points2d, planeOrigin, planeZ, planeX) {
795
+ return wrap("liftCurve2dToPlane", () => {
796
+ const flatArr = new Array(points2d.length * 2);
797
+ let j = 0;
798
+ for (const p of points2d) {
799
+ flatArr[j++] = p.x;
800
+ flatArr[j++] = p.y;
801
+ }
802
+ return this.#withF64(flatArr, (flat) => handle(this.#raw.liftCurve2dToPlane(flat, planeOrigin.x, planeOrigin.y, planeOrigin.z, planeZ.x, planeZ.y, planeZ.z, planeX.x, planeX.y, planeX.z)));
803
+ });
804
+ }
805
+ // =======================================================================
806
+ // Healing / Repair
807
+ // =======================================================================
808
+ fixShape(shape) {
809
+ return wrap("fixShape", () => handle(this.#raw.fixShape(shape)));
810
+ }
811
+ unifySameDomain(shape) {
812
+ return wrap("unifySameDomain", () => handle(this.#raw.unifySameDomain(shape)));
813
+ }
814
+ isValid(shape) {
815
+ return wrap("isValid", () => this.#raw.isValid(shape));
816
+ }
817
+ healSolid(shape, tolerance = 1e-6) {
818
+ return wrap("healSolid", () => handle(this.#raw.healSolid(shape, tolerance)));
819
+ }
820
+ healFace(shape, tolerance = 1e-6) {
821
+ return wrap("healFace", () => handle(this.#raw.healFace(shape, tolerance)));
822
+ }
823
+ healWire(shape, tolerance = 1e-6) {
824
+ return wrap("healWire", () => handle(this.#raw.healWire(shape, tolerance)));
825
+ }
826
+ fixFaceOrientations(shape) {
827
+ return wrap("fixFaceOrientations", () => handle(this.#raw.fixFaceOrientations(shape)));
828
+ }
829
+ removeDegenerateEdges(shape) {
830
+ return wrap("removeDegenerateEdges", () => handle(this.#raw.removeDegenerateEdges(shape)));
831
+ }
832
+ buildCurves3d(wire) {
833
+ wrap("buildCurves3d", () => this.#raw.buildCurves3d(wire));
834
+ }
835
+ fixWireOnFace(wire, face, tolerance = 1e-6) {
836
+ return wrap("fixWireOnFace", () => handle(this.#raw.fixWireOnFace(wire, face, tolerance)));
837
+ }
838
+ // =======================================================================
839
+ // Memory
840
+ // =======================================================================
841
+ release(shape) {
842
+ this.#raw.release(shape);
843
+ }
844
+ releaseAll() {
845
+ this.#raw.releaseAll();
846
+ }
847
+ get shapeCount() {
848
+ return this.#raw.getShapeCount();
849
+ }
850
+ // =======================================================================
851
+ // Debugging
852
+ // =======================================================================
853
+ /** Return a human-readable summary of a shape for debugging. */
854
+ describe(shape) {
855
+ const type = this.getShapeType(shape);
856
+ const bbox = this.getBoundingBox(shape, true);
857
+ const dims = `[${(bbox.xmax - bbox.xmin).toFixed(2)} x ${(bbox.ymax - bbox.ymin).toFixed(2)} x ${(bbox.zmax - bbox.zmin).toFixed(2)}]`;
858
+ const parts = [`${type} ${dims}`];
859
+ if (type === "solid" || type === "compound" || type === "compsolid") {
860
+ parts.push(`vol=${this.getVolume(shape).toFixed(3)}`);
861
+ parts.push(`area=${this.getSurfaceArea(shape).toFixed(3)}`);
862
+ }
863
+ const faces = this.getSubShapes(shape, "face");
864
+ const edges = this.getSubShapes(shape, "edge");
865
+ const verts = this.getSubShapes(shape, "vertex");
866
+ parts.push(`F:${faces.length} E:${edges.length} V:${verts.length}`);
867
+ return parts.join(" | ");
868
+ }
869
+ [Symbol.dispose]() {
870
+ kernelRegistry.unregister(this);
871
+ try {
872
+ this.#raw.releaseAll();
873
+ this.#raw.delete();
874
+ }
875
+ catch {
876
+ // Raw kernel was already deleted externally (e.g. by an adapter
877
+ // following Embind teardown conventions) — ignore, matching the
878
+ // FinalizationRegistry callback's behavior.
879
+ }
880
+ }
881
+ // =======================================================================
882
+ // Raw module / kernel access (for third-party adapters)
883
+ // =======================================================================
884
+ /**
885
+ * Return the underlying Emscripten module. Intended for integrators who
886
+ * need to hand the raw module to a third-party adapter (e.g.
887
+ * `brepjs.OcctWasmAdapter`) without bypassing {@link OcctKernel.init}.
888
+ *
889
+ * The module is owned by this `OcctKernel` instance — disposing the
890
+ * kernel does not invalidate the module reference, but the raw kernel
891
+ * obtained via {@link getRawKernel} *will* be deleted.
892
+ */
893
+ getRawModule() {
894
+ return this.#module;
895
+ }
896
+ /**
897
+ * Return the underlying raw Embind kernel. Intended for integrators who
898
+ * need to hand the raw kernel to a third-party adapter (e.g.
899
+ * `brepjs.OcctWasmAdapter`).
900
+ *
901
+ * Lifecycle: the raw kernel is owned by this `OcctKernel`. Calling
902
+ * `kernel[Symbol.dispose]()` (or letting the FinalizationRegistry collect
903
+ * the wrapper) will `releaseAll()` and `delete()` the raw kernel — so the
904
+ * adapter must not outlive the `OcctKernel` it was constructed from.
905
+ * Do not call `delete()` or `releaseAll()` on the raw kernel directly.
906
+ */
907
+ getRawKernel() {
908
+ return this.#raw;
909
+ }
910
+ // =======================================================================
911
+ // Private helpers
912
+ // =======================================================================
913
+ // Per-element push_back()/get() each cross the JS->WASM boundary. Below this
914
+ // element count the per-element loop still beats the bulk HEAP-copy path (a
915
+ // malloc round-trip on the way in, a typed-array view + copy on the way out);
916
+ // above it, the single bulk copy wins (measured ~50% of cost on point methods).
917
+ static #BULK_THRESHOLD = 64;
918
+ #makeVector(ctor, values) {
919
+ const vec = new ctor();
920
+ for (const v of values) {
921
+ vec.push_back(v);
922
+ }
923
+ return vec;
924
+ }
925
+ // Copy an array into WASM memory in one shot, then build the vector C++-side.
926
+ // allocBytes() may grow the heap, so the backing buffer is read after it; a
927
+ // fresh typed-array view is layered over it at the malloc'd (aligned) offset.
928
+ #bulkF64(values) {
929
+ const ptr = this.#raw.allocBytes(values.length * 8);
930
+ new Float64Array(this.#module.HEAPU32.buffer, ptr, values.length).set(values);
931
+ try {
932
+ return this.#raw.vectorF64FromHeap(ptr, values.length);
933
+ }
934
+ finally {
935
+ this.#raw.freeBytes(ptr);
936
+ }
937
+ }
938
+ #bulkU32(values) {
939
+ const ptr = this.#raw.allocBytes(values.length * 4);
940
+ new Uint32Array(this.#module.HEAPU32.buffer, ptr, values.length).set(values);
941
+ try {
942
+ return this.#raw.vectorU32FromHeap(ptr, values.length);
943
+ }
944
+ finally {
945
+ this.#raw.freeBytes(ptr);
946
+ }
947
+ }
948
+ #bulkI32(values) {
949
+ const ptr = this.#raw.allocBytes(values.length * 4);
950
+ new Int32Array(this.#module.HEAPU32.buffer, ptr, values.length).set(values);
951
+ try {
952
+ return this.#raw.vectorI32FromHeap(ptr, values.length);
953
+ }
954
+ finally {
955
+ this.#raw.freeBytes(ptr);
956
+ }
957
+ }
958
+ // Reverse of the #bulk* helpers: read a returned vector into a JS array.
959
+ // Each get() is a JS->WASM crossing, so above the threshold we fetch the
960
+ // vector's contiguous storage pointer once and copy the whole block in one
961
+ // shot (2 crossings total, regardless of length). heap.slice() detaches a
962
+ // copy of those WASM bytes before the typed-array view is built, so the
963
+ // caller can free the vector afterward with no aliasing concern.
964
+ #readVector(vec, HeapArray, count) {
965
+ if (count < _a.#BULK_THRESHOLD) {
966
+ const out = new Array(count);
967
+ for (let i = 0; i < count; i++) {
968
+ out[i] = vec.get(i);
969
+ }
970
+ return out;
971
+ }
972
+ const ptr = vec.dataPtr();
973
+ const heap = this.#module.HEAPU32.buffer;
974
+ const buffer = heap.slice(ptr, ptr + count * HeapArray.BYTES_PER_ELEMENT);
975
+ return Array.from(new HeapArray(buffer));
976
+ }
977
+ // Read a vector to numbers, then delete it. Every call site reads-then-frees.
978
+ #drainVector(vec, HeapArray) {
979
+ try {
980
+ return this.#readVector(vec, HeapArray, vec.size());
981
+ }
982
+ finally {
983
+ vec.delete();
984
+ }
985
+ }
986
+ #vecToHandles(vec) {
987
+ try {
988
+ return this.#readVector(vec, Uint32Array, vec.size()).map((id) => handle(id));
989
+ }
990
+ finally {
991
+ vec.delete();
992
+ }
993
+ }
994
+ #makeVectorU32(ids) {
995
+ if (ids.length < _a.#BULK_THRESHOLD) {
996
+ return this.#makeVector(this.#module.VectorUint32, ids);
997
+ }
998
+ return this.#bulkU32(ids);
999
+ }
1000
+ #makeVectorF64(values) {
1001
+ if (values.length < _a.#BULK_THRESHOLD) {
1002
+ return this.#makeVector(this.#module.VectorDouble, values);
1003
+ }
1004
+ return this.#bulkF64(values);
1005
+ }
1006
+ #makeVectorI32(values) {
1007
+ if (values.length < _a.#BULK_THRESHOLD) {
1008
+ return this.#makeVector(this.#module.VectorInt, values);
1009
+ }
1010
+ return this.#bulkI32(values);
1011
+ }
1012
+ // Scope guards: build a vector, run `fn` with it, and always delete it.
1013
+ // Replaces the make/try/finally/delete boilerplate at every vector-arg call
1014
+ // site so the cleanup can't be forgotten or mis-copied.
1015
+ #withU32(ids, fn) {
1016
+ const vec = this.#makeVectorU32(ids);
1017
+ try {
1018
+ return fn(vec);
1019
+ }
1020
+ finally {
1021
+ vec.delete();
1022
+ }
1023
+ }
1024
+ #withF64(values, fn) {
1025
+ const vec = this.#makeVectorF64(values);
1026
+ try {
1027
+ return fn(vec);
1028
+ }
1029
+ finally {
1030
+ vec.delete();
1031
+ }
1032
+ }
1033
+ #withI32(values, fn) {
1034
+ const vec = this.#makeVectorI32(values);
1035
+ try {
1036
+ return fn(vec);
1037
+ }
1038
+ finally {
1039
+ vec.delete();
1040
+ }
1041
+ }
1042
+ #flattenPoints(points) {
1043
+ if (points.length * 3 < _a.#BULK_THRESHOLD) {
1044
+ const vec = new this.#module.VectorDouble();
1045
+ for (const p of points) {
1046
+ vec.push_back(p.x);
1047
+ vec.push_back(p.y);
1048
+ vec.push_back(p.z);
1049
+ }
1050
+ return vec;
1051
+ }
1052
+ const flat = new Float64Array(points.length * 3);
1053
+ let j = 0;
1054
+ for (const p of points) {
1055
+ flat[j++] = p.x;
1056
+ flat[j++] = p.y;
1057
+ flat[j++] = p.z;
1058
+ }
1059
+ return this.#bulkF64(flat);
1060
+ }
1061
+ #vec2FromEmbind(vec) {
1062
+ const u = vec.get(0);
1063
+ const v = vec.get(1);
1064
+ vec.delete();
1065
+ return { u, v };
1066
+ }
1067
+ #uvBoundsFromEmbind(vec) {
1068
+ const result = {
1069
+ uMin: vec.get(0),
1070
+ uMax: vec.get(1),
1071
+ vMin: vec.get(2),
1072
+ vMax: vec.get(3),
1073
+ };
1074
+ vec.delete();
1075
+ return result;
1076
+ }
1077
+ #curvatureDataFromEmbind(vec) {
1078
+ const result = {
1079
+ min: vec.get(0),
1080
+ max: vec.get(1),
1081
+ gaussian: vec.get(2),
1082
+ mean: vec.get(3),
1083
+ };
1084
+ vec.delete();
1085
+ return result;
1086
+ }
1087
+ #vec3FromEmbind(vec) {
1088
+ const x = vec.get(0);
1089
+ const y = vec.get(1);
1090
+ const z = vec.get(2);
1091
+ vec.delete();
1092
+ return { x, y, z };
1093
+ }
1094
+ #extractMesh(raw) {
1095
+ try {
1096
+ return this.#extractMeshFromRaw(raw);
1097
+ }
1098
+ finally {
1099
+ raw.delete();
1100
+ }
1101
+ }
1102
+ #extractMeshFromRaw(raw) {
1103
+ const vertexCount = raw.positionCount / 3;
1104
+ const triangleCount = raw.indexCount / 3;
1105
+ const positions = new Float32Array(this.#module.HEAPF32.buffer.slice(raw.getPositionsPtr(), raw.getPositionsPtr() + raw.positionCount * 4));
1106
+ const normals = new Float32Array(this.#module.HEAPF32.buffer.slice(raw.getNormalsPtr(), raw.getNormalsPtr() + raw.normalCount * 4));
1107
+ const indices = new Uint32Array(this.#module.HEAPU32.buffer.slice(raw.getIndicesPtr(), raw.getIndicesPtr() + raw.indexCount * 4));
1108
+ return { positions, normals, indices, vertexCount, triangleCount };
1109
+ }
1110
+ #extractMeshWithFaceGroups(raw) {
1111
+ try {
1112
+ const mesh = this.#extractMeshFromRaw(raw);
1113
+ if (raw.faceGroupCount > 0) {
1114
+ mesh.faceGroups = new Int32Array(this.#module.HEAP32.buffer.slice(raw.getFaceGroupsPtr(), raw.getFaceGroupsPtr() + raw.faceGroupCount * 4));
1115
+ mesh.faceCount = raw.faceGroupCount / 3;
1116
+ }
1117
+ return mesh;
1118
+ }
1119
+ finally {
1120
+ raw.delete();
1121
+ }
1122
+ }
1123
+ }
1124
+ _a = OcctKernel;
1125
+ //# sourceMappingURL=index.js.map