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.
- package/LICENSE +21 -0
- package/README.md +111 -0
- package/THIRD_PARTY_NOTICES.md +20 -0
- package/dist/cad/compare.d.ts +31 -0
- package/dist/cad/compare.d.ts.map +1 -0
- package/dist/cad/compare.js +51 -0
- package/dist/cad/compare.js.map +1 -0
- package/dist/cad/model-store.d.ts +52 -0
- package/dist/cad/model-store.d.ts.map +1 -0
- package/dist/cad/model-store.js +227 -0
- package/dist/cad/model-store.js.map +1 -0
- package/dist/cad/query/edges.d.ts +6 -0
- package/dist/cad/query/edges.d.ts.map +1 -0
- package/dist/cad/query/edges.js +257 -0
- package/dist/cad/query/edges.js.map +1 -0
- package/dist/cad/query/entities.d.ts +4 -0
- package/dist/cad/query/entities.d.ts.map +1 -0
- package/dist/cad/query/entities.js +185 -0
- package/dist/cad/query/entities.js.map +1 -0
- package/dist/cad/query/faces.d.ts +6 -0
- package/dist/cad/query/faces.d.ts.map +1 -0
- package/dist/cad/query/faces.js +305 -0
- package/dist/cad/query/faces.js.map +1 -0
- package/dist/cad/query/pmi.d.ts +3 -0
- package/dist/cad/query/pmi.d.ts.map +1 -0
- package/dist/cad/query/pmi.js +141 -0
- package/dist/cad/query/pmi.js.map +1 -0
- package/dist/cad/query/shared.d.ts +74 -0
- package/dist/cad/query/shared.d.ts.map +1 -0
- package/dist/cad/query/shared.js +181 -0
- package/dist/cad/query/shared.js.map +1 -0
- package/dist/cad/schema-version.d.ts +2 -0
- package/dist/cad/schema-version.d.ts.map +1 -0
- package/dist/cad/schema-version.js +2 -0
- package/dist/cad/schema-version.js.map +1 -0
- package/dist/compare.d.ts +31 -0
- package/dist/compare.d.ts.map +1 -0
- package/dist/compare.js +51 -0
- package/dist/compare.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +113 -0
- package/dist/index.js.map +1 -0
- package/dist/kernel/aag-utils.d.ts +7 -0
- package/dist/kernel/aag-utils.d.ts.map +1 -0
- package/dist/kernel/aag-utils.js +54 -0
- package/dist/kernel/aag-utils.js.map +1 -0
- package/dist/kernel/import.d.ts +4 -0
- package/dist/kernel/import.d.ts.map +1 -0
- package/dist/kernel/import.js +25 -0
- package/dist/kernel/import.js.map +1 -0
- package/dist/kernel/kernel.d.ts +3 -0
- package/dist/kernel/kernel.d.ts.map +1 -0
- package/dist/kernel/kernel.js +7 -0
- package/dist/kernel/kernel.js.map +1 -0
- package/dist/kernel/measure.d.ts +6 -0
- package/dist/kernel/measure.d.ts.map +1 -0
- package/dist/kernel/measure.js +23 -0
- package/dist/kernel/measure.js.map +1 -0
- package/dist/kernel/query-entities.d.ts +75 -0
- package/dist/kernel/query-entities.d.ts.map +1 -0
- package/dist/kernel/query-entities.js +190 -0
- package/dist/kernel/query-entities.js.map +1 -0
- package/dist/kernel/topology.d.ts +4 -0
- package/dist/kernel/topology.d.ts.map +1 -0
- package/dist/kernel/topology.js +48 -0
- package/dist/kernel/topology.js.map +1 -0
- package/dist/model-store.d.ts +52 -0
- package/dist/model-store.d.ts.map +1 -0
- package/dist/model-store.js +227 -0
- package/dist/model-store.js.map +1 -0
- package/dist/pmi/metadata.d.ts +15 -0
- package/dist/pmi/metadata.d.ts.map +1 -0
- package/dist/pmi/metadata.js +93 -0
- package/dist/pmi/metadata.js.map +1 -0
- package/dist/pmi/parser.d.ts +46 -0
- package/dist/pmi/parser.d.ts.map +1 -0
- package/dist/pmi/parser.js +400 -0
- package/dist/pmi/parser.js.map +1 -0
- package/dist/pmi/semantic-provider.d.ts +7 -0
- package/dist/pmi/semantic-provider.d.ts.map +1 -0
- package/dist/pmi/semantic-provider.js +96 -0
- package/dist/pmi/semantic-provider.js.map +1 -0
- package/dist/providers/brep.d.ts +39 -0
- package/dist/providers/brep.d.ts.map +1 -0
- package/dist/providers/brep.js +2 -0
- package/dist/providers/brep.js.map +1 -0
- package/dist/providers/lightweight-step/metadata.d.ts +15 -0
- package/dist/providers/lightweight-step/metadata.d.ts.map +1 -0
- package/dist/providers/lightweight-step/metadata.js +93 -0
- package/dist/providers/lightweight-step/metadata.js.map +1 -0
- package/dist/providers/lightweight-step/pmi-parser.d.ts +46 -0
- package/dist/providers/lightweight-step/pmi-parser.d.ts.map +1 -0
- package/dist/providers/lightweight-step/pmi-parser.js +400 -0
- package/dist/providers/lightweight-step/pmi-parser.js.map +1 -0
- package/dist/providers/lightweight-step/semantic-provider.d.ts +7 -0
- package/dist/providers/lightweight-step/semantic-provider.d.ts.map +1 -0
- package/dist/providers/lightweight-step/semantic-provider.js +96 -0
- package/dist/providers/lightweight-step/semantic-provider.js.map +1 -0
- package/dist/providers/occt-wasm/aag-utils.d.ts +7 -0
- package/dist/providers/occt-wasm/aag-utils.d.ts.map +1 -0
- package/dist/providers/occt-wasm/aag-utils.js +54 -0
- package/dist/providers/occt-wasm/aag-utils.js.map +1 -0
- package/dist/providers/occt-wasm/import.d.ts +4 -0
- package/dist/providers/occt-wasm/import.d.ts.map +1 -0
- package/dist/providers/occt-wasm/import.js +25 -0
- package/dist/providers/occt-wasm/import.js.map +1 -0
- package/dist/providers/occt-wasm/kernel.d.ts +3 -0
- package/dist/providers/occt-wasm/kernel.d.ts.map +1 -0
- package/dist/providers/occt-wasm/kernel.js +7 -0
- package/dist/providers/occt-wasm/kernel.js.map +1 -0
- package/dist/providers/occt-wasm/measure.d.ts +6 -0
- package/dist/providers/occt-wasm/measure.d.ts.map +1 -0
- package/dist/providers/occt-wasm/measure.js +23 -0
- package/dist/providers/occt-wasm/measure.js.map +1 -0
- package/dist/providers/occt-wasm/query-entities.d.ts +71 -0
- package/dist/providers/occt-wasm/query-entities.d.ts.map +1 -0
- package/dist/providers/occt-wasm/query-entities.js +177 -0
- package/dist/providers/occt-wasm/query-entities.js.map +1 -0
- package/dist/providers/occt-wasm/topology.d.ts +4 -0
- package/dist/providers/occt-wasm/topology.d.ts.map +1 -0
- package/dist/providers/occt-wasm/topology.js +48 -0
- package/dist/providers/occt-wasm/topology.js.map +1 -0
- package/dist/providers/schema.d.ts +50 -0
- package/dist/providers/schema.d.ts.map +1 -0
- package/dist/providers/schema.js +2 -0
- package/dist/providers/schema.js.map +1 -0
- package/dist/providers/semantic.d.ts +34 -0
- package/dist/providers/semantic.d.ts.map +1 -0
- package/dist/providers/semantic.js +2 -0
- package/dist/providers/semantic.js.map +1 -0
- package/dist/query/edges.d.ts +6 -0
- package/dist/query/edges.d.ts.map +1 -0
- package/dist/query/edges.js +257 -0
- package/dist/query/edges.js.map +1 -0
- package/dist/query/entities.d.ts +4 -0
- package/dist/query/entities.d.ts.map +1 -0
- package/dist/query/entities.js +203 -0
- package/dist/query/entities.js.map +1 -0
- package/dist/query/faces.d.ts +6 -0
- package/dist/query/faces.d.ts.map +1 -0
- package/dist/query/faces.js +309 -0
- package/dist/query/faces.js.map +1 -0
- package/dist/query/pmi.d.ts +3 -0
- package/dist/query/pmi.d.ts.map +1 -0
- package/dist/query/pmi.js +141 -0
- package/dist/query/pmi.js.map +1 -0
- package/dist/query/shared.d.ts +74 -0
- package/dist/query/shared.d.ts.map +1 -0
- package/dist/query/shared.js +181 -0
- package/dist/query/shared.js.map +1 -0
- package/dist/schema-version.d.ts +2 -0
- package/dist/schema-version.d.ts.map +1 -0
- package/dist/schema-version.js +2 -0
- package/dist/schema-version.js.map +1 -0
- package/dist/tools/shared.d.ts +11 -0
- package/dist/tools/shared.d.ts.map +1 -0
- package/dist/tools/shared.js +19 -0
- package/dist/tools/shared.js.map +1 -0
- package/dist/tools/step-tools.d.ts +758 -0
- package/dist/tools/step-tools.d.ts.map +1 -0
- package/dist/tools/step-tools.js +836 -0
- package/dist/tools/step-tools.js.map +1 -0
- package/dist/types/brep.d.ts +39 -0
- package/dist/types/brep.d.ts.map +1 -0
- package/dist/types/brep.js +2 -0
- package/dist/types/brep.js.map +1 -0
- package/dist/types/schema.d.ts +50 -0
- package/dist/types/schema.d.ts.map +1 -0
- package/dist/types/schema.js +2 -0
- package/dist/types/schema.js.map +1 -0
- package/dist/types/semantic.d.ts +34 -0
- package/dist/types/semantic.d.ts.map +1 -0
- package/dist/types/semantic.js +2 -0
- package/dist/types/semantic.js.map +1 -0
- package/dist/utils/errors.d.ts +7 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +7 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/ids.d.ts +2 -0
- package/dist/utils/ids.d.ts.map +1 -0
- package/dist/utils/ids.js +4 -0
- package/dist/utils/ids.js.map +1 -0
- package/dist/utils/numbers.d.ts +11 -0
- package/dist/utils/numbers.d.ts.map +1 -0
- package/dist/utils/numbers.js +15 -0
- package/dist/utils/numbers.js.map +1 -0
- package/dist/utils/vectors.d.ts +4 -0
- package/dist/utils/vectors.d.ts.map +1 -0
- package/dist/utils/vectors.js +14 -0
- package/dist/utils/vectors.js.map +1 -0
- package/docs/EXAMPLE_PROMPTS.md +61 -0
- package/node_modules/occt-wasm/dist/index.d.ts +303 -0
- package/node_modules/occt-wasm/dist/index.d.ts.map +1 -0
- package/node_modules/occt-wasm/dist/index.js +1125 -0
- package/node_modules/occt-wasm/dist/index.js.map +1 -0
- package/node_modules/occt-wasm/dist/occt-wasm.js +2 -0
- package/node_modules/occt-wasm/dist/occt-wasm.wasm +0 -0
- package/node_modules/occt-wasm/dist/raw-types.d.ts +229 -0
- package/node_modules/occt-wasm/dist/raw-types.d.ts.map +1 -0
- package/node_modules/occt-wasm/dist/raw-types.js +8 -0
- package/node_modules/occt-wasm/dist/raw-types.js.map +1 -0
- package/node_modules/occt-wasm/dist/types.d.ts +223 -0
- package/node_modules/occt-wasm/dist/types.d.ts.map +1 -0
- package/node_modules/occt-wasm/dist/types.js +129 -0
- package/node_modules/occt-wasm/dist/types.js.map +1 -0
- package/node_modules/occt-wasm/package.json +44 -0
- 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
|