okgeometry-api 1.47.0 → 1.48.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okgeometry-api",
3
- "version": "1.47.0",
3
+ "version": "1.48.0",
4
4
  "description": "Geometry engine API for AEC applications - NURBS, meshes, booleans, intersections. Powered by Rust/WASM.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/Mesh.ts CHANGED
@@ -50,6 +50,23 @@ export interface HlrLine {
50
50
  b3: Point;
51
51
  }
52
52
 
53
+ /** View parameters for `Mesh.hlrProject` / `hlrProjectAsync`. */
54
+ export interface HlrProjectOptions {
55
+ viewDir: { x: number; y: number; z: number };
56
+ up: { x: number; y: number; z: number };
57
+ section?: {
58
+ origin: { x: number; y: number; z: number };
59
+ normal: { x: number; y: number; z: number };
60
+ };
61
+ featureAngleDeg?: number;
62
+ /**
63
+ * View depth in world units beyond the section plane — geometry farther
64
+ * than this is discarded (drafting far clip). Requires `section`; <= 0
65
+ * or absent = unlimited.
66
+ */
67
+ depth?: number;
68
+ }
69
+
53
70
  export interface PlanarRectangle {
54
71
  corners: [Point, Point, Point, Point];
55
72
  width: number;
@@ -4322,24 +4339,21 @@ export class Mesh {
4322
4339
  */
4323
4340
  static hlrProject(
4324
4341
  meshes: Mesh[],
4325
- options: {
4326
- viewDir: { x: number; y: number; z: number };
4327
- up: { x: number; y: number; z: number };
4328
- section?: {
4329
- origin: { x: number; y: number; z: number };
4330
- normal: { x: number; y: number; z: number };
4331
- };
4332
- featureAngleDeg?: number;
4333
- /**
4334
- * View depth in world units beyond the section plane — geometry
4335
- * farther than this is discarded (drafting far clip). Requires
4336
- * `section`; <= 0 or absent = unlimited.
4337
- */
4338
- depth?: number;
4339
- }
4342
+ options: HlrProjectOptions
4340
4343
  ): HlrLine[] {
4344
+ return Mesh.parseHlrResult(Mesh.hlrProjectRaw(meshes, options));
4345
+ }
4346
+
4347
+ /**
4348
+ * The raw f64 result of `hlrProject` (wasm layout: [count, (class,
4349
+ * meshIndex, ax, ay, bx, by, a3xyz, b3xyz) x count]). The worker path
4350
+ * transfers this buffer back to the main thread, which parses it with
4351
+ * `parseHlrResult` — HlrLine carries Point instances, which do not
4352
+ * survive structured clone.
4353
+ */
4354
+ static hlrProjectRaw(meshes: Mesh[], options: HlrProjectOptions): Float64Array {
4341
4355
  ensureInit();
4342
- if (meshes.length === 0) return [];
4356
+ if (meshes.length === 0) return new Float64Array([0]);
4343
4357
  const packed = Mesh.packMeshes(meshes);
4344
4358
  const s = options.section;
4345
4359
  const result = wasm.hlr_project_meshes(
@@ -4355,9 +4369,14 @@ export class Mesh {
4355
4369
  if (result.length === 0) {
4356
4370
  const error = wasm.geometry_last_error?.();
4357
4371
  if (error) throw new Error(`hlrProject failed: ${error}`);
4358
- return [];
4372
+ return new Float64Array([0]);
4359
4373
  }
4374
+ return result;
4375
+ }
4360
4376
 
4377
+ /** Parse a `hlrProjectRaw` result buffer into classified lines. */
4378
+ static parseHlrResult(result: Float64Array): HlrLine[] {
4379
+ if (result.length === 0) return [];
4361
4380
  const lines: HlrLine[] = [];
4362
4381
  const count = result[0];
4363
4382
  let offset = 1;
@@ -4376,6 +4395,44 @@ export class Mesh {
4376
4395
  return lines;
4377
4396
  }
4378
4397
 
4398
+ /**
4399
+ * Async `Mesh.hlrProject` on the worker pool — the whole-scene projection
4400
+ * scales with MODEL size, not edit size, and must not block the UI
4401
+ * thread. `options.transforms` (row-major, aligned with `meshes`) bakes
4402
+ * WORKER-side so the main thread never pays the per-mesh transform pass;
4403
+ * pass LOCAL meshes with their placement transforms. Worker
4404
+ * INFRASTRUCTURE failures fall back to the synchronous main-thread path
4405
+ * (baking transforms first); kernel failures propagate.
4406
+ */
4407
+ static async hlrProjectAsync(
4408
+ meshes: Mesh[],
4409
+ options: HlrProjectOptions & { transforms?: (number[] | undefined)[] },
4410
+ asyncOptions?: MeshBooleanAsyncOptions
4411
+ ): Promise<HlrLine[]> {
4412
+ if (meshes.length === 0) return [];
4413
+ const { transforms, ...view } = options;
4414
+ try {
4415
+ const { buffer } = await runMeshHeavyInWorkerPool(
4416
+ "hlrProject",
4417
+ meshes.map((m) => m._buffer),
4418
+ {
4419
+ ...asyncOptions,
4420
+ trusted: meshes.map((m) => m._trustedBooleanInput),
4421
+ residents: meshes,
4422
+ params: { hlr: { ...view, transforms } },
4423
+ }
4424
+ );
4425
+ return Mesh.parseHlrResult(new Float64Array(buffer));
4426
+ } catch (error) {
4427
+ if (!shouldFallbackFromWorkerFailure(error)) throw error;
4428
+ console.warn("Mesh.hlrProjectAsync worker failed; falling back to main thread.", error);
4429
+ const baked = meshes.map((m, i) =>
4430
+ transforms?.[i] ? m.applyMatrix(transforms[i]) : m
4431
+ );
4432
+ return Mesh.hlrProject(baked, view);
4433
+ }
4434
+ }
4435
+
4379
4436
  /**
4380
4437
  * Compute intersection curves with another mesh.
4381
4438
  * @param other - Mesh to intersect with
@@ -205,7 +205,12 @@ export type MeshHeavyOperation =
205
205
  | "solidSplitBySurface"
206
206
  | "diagnostics"
207
207
  | "repair"
208
- | "importStl";
208
+ | "importStl"
209
+ // hlrProject: [mesh0, mesh1, ...] (LOCAL-space; per-mesh transforms
210
+ // ride `params.hlr.transforms` and bake worker-side).
211
+ // Response buffer = the raw f64 hlr result (parse with
212
+ // Mesh.parseHlrResult).
213
+ | "hlrProject";
209
214
 
210
215
  export interface MeshHeavyParams {
211
216
  resolution?: number;
@@ -249,6 +254,19 @@ export interface MeshHeavyParams {
249
254
  preserveFeatures?: boolean;
250
255
  /** isotropicRemesh: re-project onto the original surface each iteration. */
251
256
  project?: boolean;
257
+ /** hlrProject: view/section parameters + optional per-mesh row-major
258
+ * transforms (aligned with the request buffers, baked worker-side). */
259
+ hlr?: {
260
+ viewDir: { x: number; y: number; z: number };
261
+ up: { x: number; y: number; z: number };
262
+ section?: {
263
+ origin: { x: number; y: number; z: number };
264
+ normal: { x: number; y: number; z: number };
265
+ };
266
+ featureAngleDeg?: number;
267
+ depth?: number;
268
+ transforms?: (number[] | undefined)[];
269
+ };
252
270
  }
253
271
 
254
272
  export interface MeshHeavyWorkerRequest {
@@ -268,6 +268,17 @@ function runHeavyOperation(message: {
268
268
  featureAngleDeg?: number;
269
269
  preserveFeatures?: boolean;
270
270
  project?: boolean;
271
+ hlr?: {
272
+ viewDir: { x: number; y: number; z: number };
273
+ up: { x: number; y: number; z: number };
274
+ section?: {
275
+ origin: { x: number; y: number; z: number };
276
+ normal: { x: number; y: number; z: number };
277
+ };
278
+ featureAngleDeg?: number;
279
+ depth?: number;
280
+ transforms?: (number[] | undefined)[];
281
+ };
271
282
  };
272
283
  options: { allowUnsafe?: boolean; limits?: unknown; debugForceFaceID?: boolean };
273
284
  }): { buffer: ArrayBuffer; meta?: Record<string, string | number | boolean> } {
@@ -308,6 +319,28 @@ function runHeavyOperation(message: {
308
319
  const result = mesh.voxelRemesh(params.resolution ?? 0, params.iso ?? 0.5);
309
320
  return packSingleResult(result, params);
310
321
  }
322
+ case "hlrProject": {
323
+ const view = params.hlr;
324
+ if (!view) throw new Error("hlrProject requires hlr params");
325
+ const input = meshes();
326
+ // Bake per-mesh transforms HERE — the whole point of the worker job
327
+ // is that the main thread pays neither the projection nor the
328
+ // per-mesh transform pass. applyMatrix returns a NEW mesh, so
329
+ // worker-resident operands are never mutated.
330
+ const baked = input.map((m, i) => {
331
+ const t = view.transforms?.[i];
332
+ return t ? m.applyMatrix(t) : m;
333
+ });
334
+ const raw = Mesh.hlrProjectRaw(baked, {
335
+ viewDir: view.viewDir,
336
+ up: view.up,
337
+ section: view.section,
338
+ featureAngleDeg: view.featureAngleDeg,
339
+ depth: view.depth,
340
+ });
341
+ // Copy before transfer: the wasm result may view the wasm heap.
342
+ return { buffer: new Float64Array(raw).buffer };
343
+ }
311
344
  case "isotropicRemesh": {
312
345
  const [mesh] = meshes();
313
346
  const r = mesh.remesh({