okgeometry-api 1.33.0 → 1.34.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/dist/Brep.d.ts +18 -0
- package/dist/Brep.d.ts.map +1 -1
- package/dist/Brep.js +53 -1
- package/dist/Brep.js.map +1 -1
- package/dist/Mesh.d.ts +7 -0
- package/dist/Mesh.d.ts.map +1 -1
- package/dist/Mesh.js +7 -1
- package/dist/Mesh.js.map +1 -1
- package/dist/mesh-boolean.protocol.d.ts +9 -1
- package/dist/mesh-boolean.protocol.d.ts.map +1 -1
- package/dist/mesh-boolean.worker.d.ts.map +1 -1
- package/dist/mesh-boolean.worker.js +18 -0
- package/dist/mesh-boolean.worker.js.map +1 -1
- package/dist/wasm-base64.d.ts +1 -1
- package/dist/wasm-base64.js +1 -1
- package/package.json +69 -69
- package/src/Brep.ts +66 -1
- package/src/Mesh.ts +7 -1
- package/src/mesh-boolean.protocol.ts +13 -2
- package/src/mesh-boolean.worker.ts +30 -1
- package/src/wasm-base64.ts +1 -1
package/package.json
CHANGED
|
@@ -1,69 +1,69 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "okgeometry-api",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Geometry engine API for AEC applications - NURBS, meshes, booleans, intersections. Powered by Rust/WASM.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "dist/index.js",
|
|
7
|
-
"types": "dist/index.d.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"types": "./dist/index.d.ts",
|
|
11
|
-
"import": "./dist/index.js"
|
|
12
|
-
}
|
|
13
|
-
},
|
|
14
|
-
"files": [
|
|
15
|
-
"dist/",
|
|
16
|
-
"src/"
|
|
17
|
-
],
|
|
18
|
-
"scripts": {
|
|
19
|
-
"build:wasm": "node -e \"require('fs').rmSync('wasm',{recursive:true,force:true})\" && cd .. && wasm-pack build --target web --out-dir okgeometry-api/wasm",
|
|
20
|
-
"sync:wasm-bindings": "node scripts/sync-wasm-bindings.mjs",
|
|
21
|
-
"inline-wasm": "tsx scripts/inline-wasm.ts",
|
|
22
|
-
"build": "npm run build:wasm && npm run sync:wasm-bindings && npm run inline-wasm && tsc",
|
|
23
|
-
"bench:boolean-heavy": "npm run build && tsx scripts/bench-boolean-heavy.ts",
|
|
24
|
-
"bench:boolean-ab": "npm run build && tsx scripts/bench-boolean-ab.ts",
|
|
25
|
-
"bench:boolean-batch": "npm run build && tsx scripts/bench-boolean-batch.ts",
|
|
26
|
-
"bench:boolean-matrix": "tsx scripts/bench-boolean-matrix.ts",
|
|
27
|
-
"smoke:brep-boolean": "tsx scripts/smoke-brep-boolean.ts",
|
|
28
|
-
"prepublishOnly": "npm run build",
|
|
29
|
-
"smoke:brep-parametric": "tsx scripts/smoke-brep-parametric.ts",
|
|
30
|
-
"smoke:brep-from-mesh": "tsx scripts/smoke-brep-from-mesh.ts",
|
|
31
|
-
"smoke:brep-faces": "tsx scripts/smoke-brep-faces.ts",
|
|
32
|
-
"smoke:curve-intersections": "tsx scripts/smoke-curve-intersections.ts",
|
|
33
|
-
"smoke:brep-intersections": "tsx scripts/smoke-brep-intersections.ts",
|
|
34
|
-
"smoke:brep-sheet-boolean": "tsx scripts/smoke-brep-sheet-boolean.ts",
|
|
35
|
-
"smoke:geometry-boolean": "tsx scripts/smoke-geometry-boolean.ts",
|
|
36
|
-
"smoke:curve-region": "tsx scripts/smoke-curve-region.ts",
|
|
37
|
-
"smoke:sculpt": "tsx scripts/smoke-sculpt.ts",
|
|
38
|
-
"smoke:offset-chamfer": "tsx scripts/smoke-offset-chamfer.ts",
|
|
39
|
-
"smoke:rebuild": "tsx scripts/smoke-rebuild.ts",
|
|
40
|
-
"smoke:mesh-residency": "tsx scripts/smoke-mesh-residency.ts",
|
|
41
|
-
"build:wasm-threaded": "node scripts/build-threaded.mjs",
|
|
42
|
-
"smoke:browser-threaded": "node scripts/browser-smoke-threaded.mjs",
|
|
43
|
-
"smoke:browser-heavy": "node scripts/browser-smoke-heavy-worker.mjs",
|
|
44
|
-
"smoke:thicken": "tsx scripts/smoke-thicken.ts",
|
|
45
|
-
"bench:thicken": "tsx scripts/bench-thicken.ts",
|
|
46
|
-
"smoke:sweep": "tsx scripts/smoke-sweep.ts"
|
|
47
|
-
},
|
|
48
|
-
"keywords": [
|
|
49
|
-
"geometry",
|
|
50
|
-
"wasm",
|
|
51
|
-
"nurbs",
|
|
52
|
-
"mesh",
|
|
53
|
-
"boolean",
|
|
54
|
-
"cad",
|
|
55
|
-
"aec",
|
|
56
|
-
"3d"
|
|
57
|
-
],
|
|
58
|
-
"author": "Mostafa El Ayoubi",
|
|
59
|
-
"license": "Proprietary License",
|
|
60
|
-
"repository": {
|
|
61
|
-
"type": "git",
|
|
62
|
-
"url": "https://www.orkestra.online"
|
|
63
|
-
},
|
|
64
|
-
"devDependencies": {
|
|
65
|
-
"tsx": "^4.7.0",
|
|
66
|
-
"typescript": "^5.4.0",
|
|
67
|
-
"playwright": "^1.49.0"
|
|
68
|
-
}
|
|
69
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "okgeometry-api",
|
|
3
|
+
"version": "1.34.0",
|
|
4
|
+
"description": "Geometry engine API for AEC applications - NURBS, meshes, booleans, intersections. Powered by Rust/WASM.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist/",
|
|
16
|
+
"src/"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build:wasm": "node -e \"require('fs').rmSync('wasm',{recursive:true,force:true})\" && cd .. && wasm-pack build --target web --out-dir okgeometry-api/wasm",
|
|
20
|
+
"sync:wasm-bindings": "node scripts/sync-wasm-bindings.mjs",
|
|
21
|
+
"inline-wasm": "tsx scripts/inline-wasm.ts",
|
|
22
|
+
"build": "npm run build:wasm && npm run sync:wasm-bindings && npm run inline-wasm && tsc",
|
|
23
|
+
"bench:boolean-heavy": "npm run build && tsx scripts/bench-boolean-heavy.ts",
|
|
24
|
+
"bench:boolean-ab": "npm run build && tsx scripts/bench-boolean-ab.ts",
|
|
25
|
+
"bench:boolean-batch": "npm run build && tsx scripts/bench-boolean-batch.ts",
|
|
26
|
+
"bench:boolean-matrix": "tsx scripts/bench-boolean-matrix.ts",
|
|
27
|
+
"smoke:brep-boolean": "tsx scripts/smoke-brep-boolean.ts",
|
|
28
|
+
"prepublishOnly": "npm run build",
|
|
29
|
+
"smoke:brep-parametric": "tsx scripts/smoke-brep-parametric.ts",
|
|
30
|
+
"smoke:brep-from-mesh": "tsx scripts/smoke-brep-from-mesh.ts",
|
|
31
|
+
"smoke:brep-faces": "tsx scripts/smoke-brep-faces.ts",
|
|
32
|
+
"smoke:curve-intersections": "tsx scripts/smoke-curve-intersections.ts",
|
|
33
|
+
"smoke:brep-intersections": "tsx scripts/smoke-brep-intersections.ts",
|
|
34
|
+
"smoke:brep-sheet-boolean": "tsx scripts/smoke-brep-sheet-boolean.ts",
|
|
35
|
+
"smoke:geometry-boolean": "tsx scripts/smoke-geometry-boolean.ts",
|
|
36
|
+
"smoke:curve-region": "tsx scripts/smoke-curve-region.ts",
|
|
37
|
+
"smoke:sculpt": "tsx scripts/smoke-sculpt.ts",
|
|
38
|
+
"smoke:offset-chamfer": "tsx scripts/smoke-offset-chamfer.ts",
|
|
39
|
+
"smoke:rebuild": "tsx scripts/smoke-rebuild.ts",
|
|
40
|
+
"smoke:mesh-residency": "tsx scripts/smoke-mesh-residency.ts",
|
|
41
|
+
"build:wasm-threaded": "node scripts/build-threaded.mjs",
|
|
42
|
+
"smoke:browser-threaded": "node scripts/browser-smoke-threaded.mjs",
|
|
43
|
+
"smoke:browser-heavy": "node scripts/browser-smoke-heavy-worker.mjs",
|
|
44
|
+
"smoke:thicken": "tsx scripts/smoke-thicken.ts",
|
|
45
|
+
"bench:thicken": "tsx scripts/bench-thicken.ts",
|
|
46
|
+
"smoke:sweep": "tsx scripts/smoke-sweep.ts"
|
|
47
|
+
},
|
|
48
|
+
"keywords": [
|
|
49
|
+
"geometry",
|
|
50
|
+
"wasm",
|
|
51
|
+
"nurbs",
|
|
52
|
+
"mesh",
|
|
53
|
+
"boolean",
|
|
54
|
+
"cad",
|
|
55
|
+
"aec",
|
|
56
|
+
"3d"
|
|
57
|
+
],
|
|
58
|
+
"author": "Mostafa El Ayoubi",
|
|
59
|
+
"license": "Proprietary License",
|
|
60
|
+
"repository": {
|
|
61
|
+
"type": "git",
|
|
62
|
+
"url": "https://www.orkestra.online"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"tsx": "^4.7.0",
|
|
66
|
+
"typescript": "^5.4.0",
|
|
67
|
+
"playwright": "^1.49.0"
|
|
68
|
+
}
|
|
69
|
+
}
|
package/src/Brep.ts
CHANGED
|
@@ -2,10 +2,12 @@ import { ensureInit } from "./engine.js";
|
|
|
2
2
|
import { Point } from "./Point.js";
|
|
3
3
|
import { Vec3 } from "./Vec3.js";
|
|
4
4
|
import { Plane } from "./Plane.js";
|
|
5
|
-
import { Mesh, type MeshBooleanOptions } from "./Mesh.js";
|
|
5
|
+
import { Mesh, type MeshBooleanOptions, shouldFallbackFromWorkerFailure } from "./Mesh.js";
|
|
6
6
|
import { Polyline } from "./Polyline.js";
|
|
7
7
|
import { NurbsCurve } from "./NurbsCurve.js";
|
|
8
8
|
import { NurbsSurface } from "./NurbsSurface.js";
|
|
9
|
+
import { MeshBooleanExecutionError, type MeshBooleanAsyncOptions } from "./mesh-boolean.protocol.js";
|
|
10
|
+
import { runMeshHeavyInWorkerPool } from "./mesh-boolean.pool.js";
|
|
9
11
|
import { wasm } from "./wasm-active.js";
|
|
10
12
|
|
|
11
13
|
/** Options for Brep boolean operations. */
|
|
@@ -26,6 +28,13 @@ export interface BrepParametricBooleanOptions {
|
|
|
26
28
|
tolerance?: number;
|
|
27
29
|
}
|
|
28
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Options for the ASYNC parametric Brep booleans: the parametric options
|
|
33
|
+
* plus the worker-pool controls (signal, timeout, progress, pool sizing).
|
|
34
|
+
*/
|
|
35
|
+
export type BrepParametricBooleanAsyncOptions = BrepParametricBooleanOptions &
|
|
36
|
+
Omit<MeshBooleanAsyncOptions, "limits" | "allowUnsafe" | "debugForceFaceID">;
|
|
37
|
+
|
|
29
38
|
/** Options for mesh → BREP conversion ({@link Brep.fromMesh}). */
|
|
30
39
|
export interface MeshToBrepConversionOptions {
|
|
31
40
|
/**
|
|
@@ -1125,6 +1134,62 @@ export class Brep {
|
|
|
1125
1134
|
return this.runParametricBoolean(other, "intersect", options);
|
|
1126
1135
|
}
|
|
1127
1136
|
|
|
1137
|
+
private async runParametricBooleanAsync(
|
|
1138
|
+
other: Brep | Mesh,
|
|
1139
|
+
op: "union" | "subtract" | "intersect",
|
|
1140
|
+
options?: BrepParametricBooleanAsyncOptions,
|
|
1141
|
+
): Promise<Brep> {
|
|
1142
|
+
ensureInit();
|
|
1143
|
+
const rhs = Brep.resolveParametricOperand(other);
|
|
1144
|
+
try {
|
|
1145
|
+
const { meta } = await runMeshHeavyInWorkerPool("brepBoolean", [], {
|
|
1146
|
+
signal: options?.signal,
|
|
1147
|
+
timeoutMs: options?.timeoutMs,
|
|
1148
|
+
workerFactory: options?.workerFactory,
|
|
1149
|
+
poolSize: options?.poolSize,
|
|
1150
|
+
onProgress: options?.onProgress,
|
|
1151
|
+
params: {
|
|
1152
|
+
brepA: this.json,
|
|
1153
|
+
brepB: rhs.json,
|
|
1154
|
+
brepOp: op,
|
|
1155
|
+
tolerance: options?.tolerance,
|
|
1156
|
+
},
|
|
1157
|
+
});
|
|
1158
|
+
const json = meta?.brepJson;
|
|
1159
|
+
if (typeof json !== "string" || json.length === 0) {
|
|
1160
|
+
throw new MeshBooleanExecutionError({
|
|
1161
|
+
code: "worker_protocol",
|
|
1162
|
+
message: `Brep.${op}BrepAsync worker returned no result body.`,
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
return new Brep(json);
|
|
1166
|
+
} catch (error) {
|
|
1167
|
+
if (!shouldFallbackFromWorkerFailure(error)) throw error;
|
|
1168
|
+
console.warn(`Brep.${op}BrepAsync worker failed; falling back to main-thread boolean.`, error);
|
|
1169
|
+
return this.runParametricBoolean(other, op, options);
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
/**
|
|
1174
|
+
* {@link unionBrep} on the worker pool (non-blocking). Worker
|
|
1175
|
+
* INFRASTRUCTURE failures fall back to the synchronous main-thread path;
|
|
1176
|
+
* kernel-level failures propagate (they would fail again on the main
|
|
1177
|
+
* thread).
|
|
1178
|
+
*/
|
|
1179
|
+
unionBrepAsync(other: Brep | Mesh, options?: BrepParametricBooleanAsyncOptions): Promise<Brep> {
|
|
1180
|
+
return this.runParametricBooleanAsync(other, "union", options);
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
/** {@link subtractBrep} on the worker pool (non-blocking). See {@link unionBrepAsync}. */
|
|
1184
|
+
subtractBrepAsync(other: Brep | Mesh, options?: BrepParametricBooleanAsyncOptions): Promise<Brep> {
|
|
1185
|
+
return this.runParametricBooleanAsync(other, "subtract", options);
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
/** {@link intersectBrep} on the worker pool (non-blocking). See {@link unionBrepAsync}. */
|
|
1189
|
+
intersectBrepAsync(other: Brep | Mesh, options?: BrepParametricBooleanAsyncOptions): Promise<Brep> {
|
|
1190
|
+
return this.runParametricBooleanAsync(other, "intersect", options);
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1128
1193
|
// ── Mesh → BREP and solid ÷ sheet split ──
|
|
1129
1194
|
|
|
1130
1195
|
/**
|
package/src/Mesh.ts
CHANGED
|
@@ -275,7 +275,13 @@ export interface MeshCurveSplitOptions extends MeshBooleanOptions {
|
|
|
275
275
|
curveSegments?: number;
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
-
|
|
278
|
+
/**
|
|
279
|
+
* Whether an async worker failure should fall back to the synchronous
|
|
280
|
+
* main-thread path: only worker INFRASTRUCTURE failures qualify — a
|
|
281
|
+
* kernel-level error would fail again on the main thread and must
|
|
282
|
+
* propagate instead. Shared by the Mesh and Brep async wrappers.
|
|
283
|
+
*/
|
|
284
|
+
export function shouldFallbackFromWorkerFailure(error: unknown): boolean {
|
|
279
285
|
if (!error || typeof error !== "object") return false;
|
|
280
286
|
const code = "code" in error ? error.code : undefined;
|
|
281
287
|
return code === "worker_unavailable" || code === "worker_crashed" || code === "worker_protocol";
|
|
@@ -129,13 +129,15 @@ export class MeshBooleanExecutionError extends Error {
|
|
|
129
129
|
//
|
|
130
130
|
// Multi-second kernel operations beyond pairwise booleans run on the same
|
|
131
131
|
// worker pool: batch booleans, watertight repair, voxel remesh, solid edge
|
|
132
|
-
// chamfer. Buffers are op-specific:
|
|
132
|
+
// chamfer, parametric BREP booleans. Buffers are op-specific:
|
|
133
133
|
// unionAll: [mesh0, mesh1, ...]
|
|
134
134
|
// differenceAll: [host, cutter0, cutter1, ...]
|
|
135
135
|
// makeWatertight: [mesh]
|
|
136
136
|
// voxelRemesh: [mesh]
|
|
137
137
|
// chamferEdges: [mesh, edgeSegments] (flat [ax,ay,az,bx,by,bz] per edge)
|
|
138
138
|
// filletEdges: [mesh, edgeSegments] (same layout, radius + segments in params)
|
|
139
|
+
// brepBoolean: [] (both operands ride `params` as BREP JSON strings;
|
|
140
|
+
// the result BREP JSON returns in the response `meta`)
|
|
139
141
|
|
|
140
142
|
export type MeshHeavyOperation =
|
|
141
143
|
| "unionAll"
|
|
@@ -143,7 +145,8 @@ export type MeshHeavyOperation =
|
|
|
143
145
|
| "makeWatertight"
|
|
144
146
|
| "voxelRemesh"
|
|
145
147
|
| "chamferEdges"
|
|
146
|
-
| "filletEdges"
|
|
148
|
+
| "filletEdges"
|
|
149
|
+
| "brepBoolean";
|
|
147
150
|
|
|
148
151
|
export interface MeshHeavyParams {
|
|
149
152
|
resolution?: number;
|
|
@@ -151,6 +154,14 @@ export interface MeshHeavyParams {
|
|
|
151
154
|
distance?: number;
|
|
152
155
|
radius?: number;
|
|
153
156
|
segments?: number;
|
|
157
|
+
/** brepBoolean: operand A as BREP JSON. */
|
|
158
|
+
brepA?: string;
|
|
159
|
+
/** brepBoolean: operand B as BREP JSON. */
|
|
160
|
+
brepB?: string;
|
|
161
|
+
/** brepBoolean: which parametric boolean to run. */
|
|
162
|
+
brepOp?: "union" | "subtract" | "intersect";
|
|
163
|
+
/** brepBoolean: absolute geometric tolerance (default 1e-6). */
|
|
164
|
+
tolerance?: number;
|
|
154
165
|
}
|
|
155
166
|
|
|
156
167
|
export interface MeshHeavyWorkerRequest {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { init, isInitialized } from "./engine.js";
|
|
2
|
+
import { Brep } from "./Brep.js";
|
|
2
3
|
import { Mesh } from "./Mesh.js";
|
|
3
4
|
import { Point } from "./Point.js";
|
|
4
5
|
import type {
|
|
@@ -77,7 +78,17 @@ function runHeavyOperation(message: {
|
|
|
77
78
|
operation: string;
|
|
78
79
|
buffers: ArrayBuffer[];
|
|
79
80
|
trusted?: boolean[];
|
|
80
|
-
params?: {
|
|
81
|
+
params?: {
|
|
82
|
+
resolution?: number;
|
|
83
|
+
iso?: number;
|
|
84
|
+
distance?: number;
|
|
85
|
+
radius?: number;
|
|
86
|
+
segments?: number;
|
|
87
|
+
brepA?: string;
|
|
88
|
+
brepB?: string;
|
|
89
|
+
brepOp?: "union" | "subtract" | "intersect";
|
|
90
|
+
tolerance?: number;
|
|
91
|
+
};
|
|
81
92
|
options: { allowUnsafe?: boolean; limits?: unknown; debugForceFaceID?: boolean };
|
|
82
93
|
}): { buffer: ArrayBuffer; meta?: Record<string, string | number | boolean> } {
|
|
83
94
|
const meshes = () =>
|
|
@@ -134,6 +145,24 @@ function runHeavyOperation(message: {
|
|
|
134
145
|
const result = mesh.chamferEdges(edges, params.distance ?? 0);
|
|
135
146
|
return { buffer: new Float64Array(result.rawBuffer).buffer };
|
|
136
147
|
}
|
|
148
|
+
case "brepBoolean": {
|
|
149
|
+
// Parametric BREP boolean: operands ride params as JSON strings; the
|
|
150
|
+
// result BREP JSON returns via meta (the buffer channel stays empty).
|
|
151
|
+
const { brepA, brepB, brepOp } = params;
|
|
152
|
+
if (!brepA || !brepB || !brepOp) {
|
|
153
|
+
throw new Error("brepBoolean requires brepA, brepB and brepOp params");
|
|
154
|
+
}
|
|
155
|
+
const a = Brep.fromJson(brepA);
|
|
156
|
+
const b = Brep.fromJson(brepB);
|
|
157
|
+
const opts = params.tolerance !== undefined ? { tolerance: params.tolerance } : undefined;
|
|
158
|
+
const result =
|
|
159
|
+
brepOp === "union"
|
|
160
|
+
? a.unionBrep(b, opts)
|
|
161
|
+
: brepOp === "subtract"
|
|
162
|
+
? a.subtractBrep(b, opts)
|
|
163
|
+
: a.intersectBrep(b, opts);
|
|
164
|
+
return { buffer: new ArrayBuffer(0), meta: { brepJson: result.toJson() } };
|
|
165
|
+
}
|
|
137
166
|
case "filletEdges": {
|
|
138
167
|
const mesh = Mesh.fromBuffer(new Float64Array(message.buffers[0]), {
|
|
139
168
|
trustedBooleanInput: message.trusted?.[0],
|