brepjs 18.38.0 → 18.40.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/brepjs.cjs +307 -7
- package/dist/brepjs.js +298 -8
- package/dist/index.d.ts +4 -2
- package/dist/lattice/index.d.ts +9 -0
- package/dist/lattice/latticeFns.d.ts +47 -0
- package/dist/voxel/engine.d.ts +10 -0
- package/dist/voxel/index.d.ts +3 -0
- package/dist/voxel/meshOpsFns.d.ts +52 -0
- package/dist/voxel/shapeMesh.d.ts +10 -0
- package/package.json +1 -1
package/dist/brepjs.cjs
CHANGED
|
@@ -218,8 +218,8 @@ function pointsInside(mesh, queries, id) {
|
|
|
218
218
|
}
|
|
219
219
|
//#endregion
|
|
220
220
|
//#region src/voxel/repairFns.ts
|
|
221
|
-
var DEFAULT_RESOLUTION = 48;
|
|
222
|
-
var DEFAULT_PADDING = 2;
|
|
221
|
+
var DEFAULT_RESOLUTION$2 = 48;
|
|
222
|
+
var DEFAULT_PADDING$2 = 2;
|
|
223
223
|
/**
|
|
224
224
|
* Repair a (possibly non-watertight) triangle-soup mesh into a closed surface:
|
|
225
225
|
* voxelize an FWN-signed SDF, then Surface-Nets contour it back to triangles.
|
|
@@ -232,16 +232,16 @@ function repairMesh(mesh, opts, id) {
|
|
|
232
232
|
const invalid = validateMesh(mesh);
|
|
233
233
|
if (invalid) return require_errors.err(invalid);
|
|
234
234
|
if (mesh.vertices.length === 0 || mesh.triangles.length === 0) return require_errors.err(require_errors.validationError("VOXEL_EMPTY_MESH", "repairMesh requires a non-empty triangle mesh."));
|
|
235
|
-
const resolution = opts?.resolution ?? DEFAULT_RESOLUTION;
|
|
236
|
-
const padding = opts?.padding ?? DEFAULT_PADDING;
|
|
235
|
+
const resolution = opts?.resolution ?? DEFAULT_RESOLUTION$2;
|
|
236
|
+
const padding = opts?.padding ?? DEFAULT_PADDING$2;
|
|
237
237
|
if (!Number.isInteger(resolution) || resolution < 1) return require_errors.err(require_errors.validationError("VOXEL_INVALID_RESOLUTION", "resolution must be an integer >= 1."));
|
|
238
238
|
if (!Number.isInteger(padding) || padding < 1) return require_errors.err(require_errors.validationError("VOXEL_INVALID_PADDING", "padding must be an integer >= 1."));
|
|
239
239
|
const engine = resolveEngine(id);
|
|
240
240
|
if (require_errors.isErr(engine)) return engine;
|
|
241
241
|
try {
|
|
242
242
|
try {
|
|
243
|
-
var _usingCtx$
|
|
244
|
-
const repaired = _usingCtx$
|
|
243
|
+
var _usingCtx$6 = require_shapeTypes._usingCtx();
|
|
244
|
+
const repaired = _usingCtx$6.u(engine.value.repair_mesh(mesh.vertices, mesh.triangles, resolution, padding));
|
|
245
245
|
const vertexCount = repaired.positions.length / 3;
|
|
246
246
|
return require_errors.ok({
|
|
247
247
|
vertices: repaired.positions,
|
|
@@ -254,13 +254,303 @@ function repairMesh(mesh, opts, id) {
|
|
|
254
254
|
faceHash: 0
|
|
255
255
|
}]
|
|
256
256
|
});
|
|
257
|
+
} catch (_) {
|
|
258
|
+
_usingCtx$6.e = _;
|
|
259
|
+
} finally {
|
|
260
|
+
_usingCtx$6.d();
|
|
261
|
+
}
|
|
262
|
+
} catch (cause) {
|
|
263
|
+
return require_errors.err(require_errors.computationError("VOXEL_REPAIR_FAILED", cause instanceof Error ? cause.message : "voxel repair failed (grid too large?).", cause));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
//#endregion
|
|
267
|
+
//#region src/voxel/shapeMesh.ts
|
|
268
|
+
var DEFAULT_DEFLECTION = .001;
|
|
269
|
+
/**
|
|
270
|
+
* Mesh a B-rep shape into the flat triangle-soup {@link VoxelMeshInput} the voxel
|
|
271
|
+
* ops consume. Tessellates via the topology mesh API at `deflection` linear
|
|
272
|
+
* tolerance and forwards its `{ vertices, triangles }` (already Float32Array /
|
|
273
|
+
* Uint32Array). Meshing failures surface as an `err(...)` rather than a throw.
|
|
274
|
+
*/
|
|
275
|
+
function shapeToMeshInput(shape, deflection = DEFAULT_DEFLECTION) {
|
|
276
|
+
try {
|
|
277
|
+
const tessellated = require_meshFns.mesh(shape, { tolerance: deflection });
|
|
278
|
+
if (tessellated.vertices.length === 0 || tessellated.triangles.length === 0) return require_errors.err(require_errors.computationError("VOXEL_SHAPE_MESH_EMPTY", "shape tessellated to an empty triangle mesh."));
|
|
279
|
+
return require_errors.ok({
|
|
280
|
+
vertices: tessellated.vertices,
|
|
281
|
+
triangles: tessellated.triangles
|
|
282
|
+
});
|
|
283
|
+
} catch (cause) {
|
|
284
|
+
return require_errors.err(require_errors.computationError("VOXEL_SHAPE_MESH_FAILED", cause instanceof Error ? cause.message : "failed to mesh shape for voxel op.", cause));
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
//#endregion
|
|
288
|
+
//#region src/voxel/meshOpsFns.ts
|
|
289
|
+
var DEFAULT_RESOLUTION$1 = 48;
|
|
290
|
+
var DEFAULT_PADDING$1 = 2;
|
|
291
|
+
var BOOLEAN_OP_CODES = {
|
|
292
|
+
union: 0,
|
|
293
|
+
intersection: 1,
|
|
294
|
+
difference: 2
|
|
295
|
+
};
|
|
296
|
+
function resolveGridParams(opts) {
|
|
297
|
+
const resolution = opts?.resolution ?? DEFAULT_RESOLUTION$1;
|
|
298
|
+
const padding = opts?.padding ?? DEFAULT_PADDING$1;
|
|
299
|
+
if (!Number.isInteger(resolution) || resolution < 1) return require_errors.err(require_errors.validationError("VOXEL_INVALID_RESOLUTION", "resolution must be an integer >= 1."));
|
|
300
|
+
if (!Number.isInteger(padding) || padding < 1) return require_errors.err(require_errors.validationError("VOXEL_INVALID_PADDING", "padding must be an integer >= 1."));
|
|
301
|
+
return require_errors.ok({
|
|
302
|
+
resolution,
|
|
303
|
+
padding
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
function meshFromResult(result) {
|
|
307
|
+
if (result.positions.length === 0 || result.indices.length === 0) return require_errors.err(require_errors.computationError("VOXEL_DEGENERATE_RESULT", "the voxel operation produced an empty mesh (over-shrunk offset or disjoint operands?)."));
|
|
308
|
+
const vertexCount = result.positions.length / 3;
|
|
309
|
+
return require_errors.ok({
|
|
310
|
+
vertices: result.positions,
|
|
311
|
+
normals: result.normals,
|
|
312
|
+
triangles: result.indices,
|
|
313
|
+
uvs: new Float32Array(vertexCount * 2),
|
|
314
|
+
faceGroups: [{
|
|
315
|
+
start: 0,
|
|
316
|
+
count: result.indices.length / 3,
|
|
317
|
+
faceHash: 0
|
|
318
|
+
}]
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Offset a mesh by `distance` via a true-SDF iso-level shift: voxelize an
|
|
323
|
+
* FWN-signed SDF, subtract `distance`, then Surface-Nets contour it back.
|
|
324
|
+
*
|
|
325
|
+
* `distance > 0` grows the surface outward, `< 0` shrinks it inward. Returns a
|
|
326
|
+
* {@link KernelMeshResult} in world coordinates (a single face group, no UVs).
|
|
327
|
+
*/
|
|
328
|
+
function offsetMesh(mesh, distance, opts, id) {
|
|
329
|
+
const invalid = validateMesh(mesh);
|
|
330
|
+
if (invalid) return require_errors.err(invalid);
|
|
331
|
+
if (mesh.vertices.length === 0 || mesh.triangles.length === 0) return require_errors.err(require_errors.validationError("VOXEL_EMPTY_MESH", "offsetMesh requires a non-empty triangle mesh."));
|
|
332
|
+
if (!Number.isFinite(distance)) return require_errors.err(require_errors.validationError("VOXEL_INVALID_DISTANCE", "distance must be a finite number."));
|
|
333
|
+
const params = resolveGridParams(opts);
|
|
334
|
+
if (require_errors.isErr(params)) return params;
|
|
335
|
+
const engine = resolveEngine(id);
|
|
336
|
+
if (require_errors.isErr(engine)) return engine;
|
|
337
|
+
try {
|
|
338
|
+
try {
|
|
339
|
+
var _usingCtx$5 = require_shapeTypes._usingCtx();
|
|
340
|
+
return meshFromResult(_usingCtx$5.u(engine.value.offset_mesh(mesh.vertices, mesh.triangles, distance, params.value.resolution, params.value.padding)));
|
|
341
|
+
} catch (_) {
|
|
342
|
+
_usingCtx$5.e = _;
|
|
343
|
+
} finally {
|
|
344
|
+
_usingCtx$5.d();
|
|
345
|
+
}
|
|
346
|
+
} catch (cause) {
|
|
347
|
+
return require_errors.err(require_errors.computationError("VOXEL_OFFSET_FAILED", cause instanceof Error ? cause.message : "voxel offset failed (grid too large?).", cause));
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Hollow a solid mesh into a shell of the given inward `thickness`: voxelize an
|
|
352
|
+
* FWN-signed SDF, take `max(solid, -(solid + thickness))`, then contour it.
|
|
353
|
+
*
|
|
354
|
+
* `thickness` must be finite and > 0 (inward-only). Returns a
|
|
355
|
+
* {@link KernelMeshResult} in world coordinates (a single face group, no UVs).
|
|
356
|
+
*/
|
|
357
|
+
function shellMesh(mesh, thickness, opts, id) {
|
|
358
|
+
const invalid = validateMesh(mesh);
|
|
359
|
+
if (invalid) return require_errors.err(invalid);
|
|
360
|
+
if (mesh.vertices.length === 0 || mesh.triangles.length === 0) return require_errors.err(require_errors.validationError("VOXEL_EMPTY_MESH", "shellMesh requires a non-empty triangle mesh."));
|
|
361
|
+
if (!Number.isFinite(thickness) || thickness <= 0) return require_errors.err(require_errors.validationError("VOXEL_INVALID_THICKNESS", "thickness must be a finite number > 0."));
|
|
362
|
+
const params = resolveGridParams(opts);
|
|
363
|
+
if (require_errors.isErr(params)) return params;
|
|
364
|
+
const engine = resolveEngine(id);
|
|
365
|
+
if (require_errors.isErr(engine)) return engine;
|
|
366
|
+
try {
|
|
367
|
+
try {
|
|
368
|
+
var _usingCtx3 = require_shapeTypes._usingCtx();
|
|
369
|
+
return meshFromResult(_usingCtx3.u(engine.value.shell_mesh(mesh.vertices, mesh.triangles, thickness, params.value.resolution, params.value.padding)));
|
|
370
|
+
} catch (_) {
|
|
371
|
+
_usingCtx3.e = _;
|
|
372
|
+
} finally {
|
|
373
|
+
_usingCtx3.d();
|
|
374
|
+
}
|
|
375
|
+
} catch (cause) {
|
|
376
|
+
return require_errors.err(require_errors.computationError("VOXEL_SHELL_FAILED", cause instanceof Error ? cause.message : "voxel shell failed (grid too large?).", cause));
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Voxel-based CSG of two meshes: voxelize both onto a shared grid, combine their
|
|
381
|
+
* SDFs (union/intersection/difference), then Surface-Nets contour the result.
|
|
382
|
+
*
|
|
383
|
+
* `op` is `'difference'` = A − B. Robust on non-watertight input (FWN sign).
|
|
384
|
+
* Returns a {@link KernelMeshResult} in world coordinates (single group, no UVs).
|
|
385
|
+
*/
|
|
386
|
+
function voxelBoolean(a, b, op, opts, id) {
|
|
387
|
+
const invalidA = validateMesh(a);
|
|
388
|
+
if (invalidA) return require_errors.err(invalidA);
|
|
389
|
+
const invalidB = validateMesh(b);
|
|
390
|
+
if (invalidB) return require_errors.err(invalidB);
|
|
391
|
+
if (a.vertices.length === 0 || a.triangles.length === 0) return require_errors.err(require_errors.validationError("VOXEL_EMPTY_MESH", "voxelBoolean requires a non-empty mesh for operand A."));
|
|
392
|
+
if (b.vertices.length === 0 || b.triangles.length === 0) return require_errors.err(require_errors.validationError("VOXEL_EMPTY_MESH", "voxelBoolean requires a non-empty mesh for operand B."));
|
|
393
|
+
const opCode = BOOLEAN_OP_CODES[op];
|
|
394
|
+
const params = resolveGridParams(opts);
|
|
395
|
+
if (require_errors.isErr(params)) return params;
|
|
396
|
+
const engine = resolveEngine(id);
|
|
397
|
+
if (require_errors.isErr(engine)) return engine;
|
|
398
|
+
try {
|
|
399
|
+
try {
|
|
400
|
+
var _usingCtx4 = require_shapeTypes._usingCtx();
|
|
401
|
+
return meshFromResult(_usingCtx4.u(engine.value.voxel_boolean(a.vertices, a.triangles, b.vertices, b.triangles, opCode, params.value.resolution, params.value.padding)));
|
|
402
|
+
} catch (_) {
|
|
403
|
+
_usingCtx4.e = _;
|
|
404
|
+
} finally {
|
|
405
|
+
_usingCtx4.d();
|
|
406
|
+
}
|
|
407
|
+
} catch (cause) {
|
|
408
|
+
return require_errors.err(require_errors.computationError("VOXEL_BOOLEAN_FAILED", cause instanceof Error ? cause.message : "voxel boolean failed (grid too large?).", cause));
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Offset a B-rep shape by `distance`: tessellate it, then run {@link offsetMesh}
|
|
413
|
+
* on the resulting triangle soup. `distance > 0` grows outward, `< 0` shrinks
|
|
414
|
+
* inward. Threads a meshing failure straight back as an `err(...)`.
|
|
415
|
+
*/
|
|
416
|
+
function offsetShape(shape, distance, opts, id) {
|
|
417
|
+
const meshInput = shapeToMeshInput(shape);
|
|
418
|
+
if (require_errors.isErr(meshInput)) return meshInput;
|
|
419
|
+
return offsetMesh(meshInput.value, distance, opts, id);
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Hollow a B-rep shape into a shell of inward `thickness`: tessellate it, then
|
|
423
|
+
* run {@link shellMesh}. `thickness` must be finite and > 0. Threads a meshing
|
|
424
|
+
* failure straight back as an `err(...)`.
|
|
425
|
+
*/
|
|
426
|
+
function shellShape(shape, thickness, opts, id) {
|
|
427
|
+
const meshInput = shapeToMeshInput(shape);
|
|
428
|
+
if (require_errors.isErr(meshInput)) return meshInput;
|
|
429
|
+
return shellMesh(meshInput.value, thickness, opts, id);
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Voxel CSG of two B-rep shapes: tessellate both, then run {@link voxelBoolean}.
|
|
433
|
+
* `op` is `'difference'` = A − B. Threads either meshing failure back as an
|
|
434
|
+
* `err(...)`.
|
|
435
|
+
*/
|
|
436
|
+
function voxelBooleanShapes(a, b, op, opts, id) {
|
|
437
|
+
const meshA = shapeToMeshInput(a);
|
|
438
|
+
if (require_errors.isErr(meshA)) return meshA;
|
|
439
|
+
const meshB = shapeToMeshInput(b);
|
|
440
|
+
if (require_errors.isErr(meshB)) return meshB;
|
|
441
|
+
return voxelBoolean(meshA.value, meshB.value, op, opts, id);
|
|
442
|
+
}
|
|
443
|
+
//#endregion
|
|
444
|
+
//#region src/lattice/latticeFns.ts
|
|
445
|
+
var LATTICE_TAGS = {
|
|
446
|
+
gyroid: 0,
|
|
447
|
+
schwarzP: 1,
|
|
448
|
+
diamond: 2
|
|
449
|
+
};
|
|
450
|
+
var DEFAULT_RESOLUTION = 48;
|
|
451
|
+
var DEFAULT_PADDING = 2;
|
|
452
|
+
function validateOptions(opts) {
|
|
453
|
+
const tag = LATTICE_TAGS[opts.type];
|
|
454
|
+
if (tag === void 0) return require_errors.err(require_errors.validationError("LATTICE_INVALID_TYPE", `lattice type must be one of gyroid, schwarzP, diamond (got '${opts.type}').`));
|
|
455
|
+
if (!(opts.period > 0)) return require_errors.err(require_errors.validationError("LATTICE_INVALID_PERIOD", "period must be > 0."));
|
|
456
|
+
if (!(opts.thickness > 0)) return require_errors.err(require_errors.validationError("LATTICE_INVALID_THICKNESS", "thickness must be > 0."));
|
|
457
|
+
const resolution = opts.resolution ?? DEFAULT_RESOLUTION;
|
|
458
|
+
const padding = opts.padding ?? DEFAULT_PADDING;
|
|
459
|
+
if (!Number.isInteger(resolution) || resolution < 1) return require_errors.err(require_errors.validationError("LATTICE_INVALID_RESOLUTION", "resolution must be an integer >= 1."));
|
|
460
|
+
if (!Number.isInteger(padding) || padding < 1) return require_errors.err(require_errors.validationError("LATTICE_INVALID_PADDING", "padding must be an integer >= 1."));
|
|
461
|
+
return require_errors.ok({
|
|
462
|
+
tag,
|
|
463
|
+
period: opts.period,
|
|
464
|
+
thickness: opts.thickness,
|
|
465
|
+
resolution,
|
|
466
|
+
padding
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
function toMesh(repaired) {
|
|
470
|
+
const vertexCount = repaired.positions.length / 3;
|
|
471
|
+
return {
|
|
472
|
+
vertices: repaired.positions,
|
|
473
|
+
normals: repaired.normals,
|
|
474
|
+
triangles: repaired.indices,
|
|
475
|
+
uvs: new Float32Array(vertexCount * 2),
|
|
476
|
+
faceGroups: [{
|
|
477
|
+
start: 0,
|
|
478
|
+
count: repaired.indices.length / 3,
|
|
479
|
+
faceHash: 0
|
|
480
|
+
}]
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Fill a solid mesh with a TPMS lattice infill: intersect the FWN-signed solid
|
|
485
|
+
* with the chosen lattice shell field, then Surface-Nets contour the result.
|
|
486
|
+
*
|
|
487
|
+
* Returns a {@link KernelMeshResult} in world coordinates (a single face group,
|
|
488
|
+
* no UVs). The TPMS field is the approximate implicit (raw trig), so the surface
|
|
489
|
+
* is contoured at the zero level without distance normalization (ADR-0013).
|
|
490
|
+
*/
|
|
491
|
+
function latticeInfill(mesh, opts, id) {
|
|
492
|
+
const invalid = validateMesh(mesh);
|
|
493
|
+
if (invalid) return require_errors.err(invalid);
|
|
494
|
+
if (mesh.vertices.length === 0 || mesh.triangles.length === 0) return require_errors.err(require_errors.validationError("LATTICE_EMPTY_MESH", "latticeInfill requires a non-empty triangle mesh."));
|
|
495
|
+
const resolved = validateOptions(opts);
|
|
496
|
+
if (require_errors.isErr(resolved)) return resolved;
|
|
497
|
+
const engine = resolveEngine(id);
|
|
498
|
+
if (require_errors.isErr(engine)) return engine;
|
|
499
|
+
const { tag, period, thickness, resolution, padding } = resolved.value;
|
|
500
|
+
try {
|
|
501
|
+
try {
|
|
502
|
+
var _usingCtx$4 = require_shapeTypes._usingCtx();
|
|
503
|
+
return require_errors.ok(toMesh(_usingCtx$4.u(engine.value.lattice_infill(mesh.vertices, mesh.triangles, resolution, padding, tag, period, thickness))));
|
|
257
504
|
} catch (_) {
|
|
258
505
|
_usingCtx$4.e = _;
|
|
259
506
|
} finally {
|
|
260
507
|
_usingCtx$4.d();
|
|
261
508
|
}
|
|
262
509
|
} catch (cause) {
|
|
263
|
-
return require_errors.err(require_errors.computationError("
|
|
510
|
+
return require_errors.err(require_errors.computationError("LATTICE_INFILL_FAILED", cause instanceof Error ? cause.message : "lattice infill failed (grid too large?).", cause));
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Fill a B-rep shape with a TPMS lattice infill: tessellate the shape, then run
|
|
515
|
+
* {@link latticeInfill} on the resulting triangle soup. Threads a meshing
|
|
516
|
+
* failure straight back as an `err(...)`.
|
|
517
|
+
*/
|
|
518
|
+
function latticeInfillShape(shape, opts, id) {
|
|
519
|
+
const meshInput = shapeToMeshInput(shape);
|
|
520
|
+
if (require_errors.isErr(meshInput)) return meshInput;
|
|
521
|
+
return latticeInfill(meshInput.value, opts, id);
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Contour the infinite TPMS lattice clipped to an axis-aligned box.
|
|
525
|
+
*
|
|
526
|
+
* Returns a {@link KernelMeshResult} in world coordinates (a single face group,
|
|
527
|
+
* no UVs). The TPMS field is the approximate implicit (raw trig), contoured at
|
|
528
|
+
* the zero level without distance normalization (ADR-0013).
|
|
529
|
+
*/
|
|
530
|
+
function tpmsLattice(bounds, opts, id) {
|
|
531
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
532
|
+
const lo = bounds.min[axis];
|
|
533
|
+
const hi = bounds.max[axis];
|
|
534
|
+
if (lo === void 0 || hi === void 0 || !(lo < hi)) return require_errors.err(require_errors.validationError("LATTICE_INVALID_BOUNDS", `bounds.min must be strictly less than bounds.max on every axis (axis ${axis}).`));
|
|
535
|
+
}
|
|
536
|
+
const resolved = validateOptions(opts);
|
|
537
|
+
if (require_errors.isErr(resolved)) return resolved;
|
|
538
|
+
const engine = resolveEngine(id);
|
|
539
|
+
if (require_errors.isErr(engine)) return engine;
|
|
540
|
+
const { tag, period, thickness, resolution, padding } = resolved.value;
|
|
541
|
+
const [minX, minY, minZ] = bounds.min;
|
|
542
|
+
const [maxX, maxY, maxZ] = bounds.max;
|
|
543
|
+
try {
|
|
544
|
+
try {
|
|
545
|
+
var _usingCtx3 = require_shapeTypes._usingCtx();
|
|
546
|
+
return require_errors.ok(toMesh(_usingCtx3.u(engine.value.tpms_box(minX, minY, minZ, maxX, maxY, maxZ, resolution, padding, tag, period, thickness))));
|
|
547
|
+
} catch (_) {
|
|
548
|
+
_usingCtx3.e = _;
|
|
549
|
+
} finally {
|
|
550
|
+
_usingCtx3.d();
|
|
551
|
+
}
|
|
552
|
+
} catch (cause) {
|
|
553
|
+
return require_errors.err(require_errors.computationError("TPMS_LATTICE_FAILED", cause instanceof Error ? cause.message : "tpms lattice failed (grid too large?).", cause));
|
|
264
554
|
}
|
|
265
555
|
}
|
|
266
556
|
//#endregion
|
|
@@ -5621,6 +5911,8 @@ exports.kernelCall = require_topologyQueryFns.kernelCall;
|
|
|
5621
5911
|
exports.kernelCallRaw = require_topologyQueryFns.kernelCallRaw;
|
|
5622
5912
|
exports.kernelCallScoped = require_topologyQueryFns.kernelCallScoped;
|
|
5623
5913
|
exports.kernelError = require_errors.kernelError;
|
|
5914
|
+
exports.latticeInfill = latticeInfill;
|
|
5915
|
+
exports.latticeInfillShape = latticeInfillShape;
|
|
5624
5916
|
exports.line = require_primitiveFns.line;
|
|
5625
5917
|
exports.linearPattern = require_historyFns.linearPattern;
|
|
5626
5918
|
exports.loadFont = require_textBlueprints.loadFont;
|
|
@@ -5673,6 +5965,8 @@ exports.multiSectionSweep = require_extrudeFns.multiSectionSweep;
|
|
|
5673
5965
|
exports.normalAt = require_faceFns.normalAt;
|
|
5674
5966
|
exports.offset = offset;
|
|
5675
5967
|
exports.offsetFace = require_primitiveFns.offsetFace;
|
|
5968
|
+
exports.offsetMesh = offsetMesh;
|
|
5969
|
+
exports.offsetShape = offsetShape;
|
|
5676
5970
|
exports.offsetWire2D = require_curveFns.offsetWire2D;
|
|
5677
5971
|
exports.ok = require_errors.ok;
|
|
5678
5972
|
exports.or = require_errors.or;
|
|
@@ -5753,9 +6047,12 @@ exports.setShapeOrigin = require_shapeFns.setShapeOrigin;
|
|
|
5753
6047
|
exports.setTagMetadata = require_shapeFns.setTagMetadata;
|
|
5754
6048
|
exports.sewShells = require_primitiveFns.sewShells;
|
|
5755
6049
|
exports.shape = shape;
|
|
6050
|
+
exports.shapeToMeshInput = shapeToMeshInput;
|
|
5756
6051
|
exports.shapeType = require_faceFns.shapeType;
|
|
5757
6052
|
exports.sharedEdges = require_primitiveFns.sharedEdges;
|
|
5758
6053
|
exports.shell = shell;
|
|
6054
|
+
exports.shellMesh = shellMesh;
|
|
6055
|
+
exports.shellShape = shellShape;
|
|
5759
6056
|
exports.shellWithEvolution = require_primitiveFns.shellWithEvolution;
|
|
5760
6057
|
exports.simplify = simplify;
|
|
5761
6058
|
exports.sketchCircle = require_drawFns.sketchCircle;
|
|
@@ -5810,6 +6107,7 @@ exports.toSVGPathD = require_blueprintFns.toSVGPathD;
|
|
|
5810
6107
|
exports.toVec2 = require_types.toVec2;
|
|
5811
6108
|
exports.toVec3 = require_types.toVec3;
|
|
5812
6109
|
exports.torus = require_primitiveFns.torus;
|
|
6110
|
+
exports.tpmsLattice = tpmsLattice;
|
|
5813
6111
|
exports.transformCopy = transformCopy;
|
|
5814
6112
|
Object.defineProperty(exports, "transforms", {
|
|
5815
6113
|
enumerable: true,
|
|
@@ -5859,6 +6157,8 @@ exports.vertex = require_primitiveFns.vertex;
|
|
|
5859
6157
|
exports.vertexFinder = vertexFinder;
|
|
5860
6158
|
exports.vertexPosition = require_topologyQueryFns.vertexPosition;
|
|
5861
6159
|
exports.verticesOfEdge = require_primitiveFns.verticesOfEdge;
|
|
6160
|
+
exports.voxelBoolean = voxelBoolean;
|
|
6161
|
+
exports.voxelBooleanShapes = voxelBooleanShapes;
|
|
5862
6162
|
exports.walkAssembly = require_historyFns.walkAssembly;
|
|
5863
6163
|
exports.windingNumbers = windingNumbers;
|
|
5864
6164
|
exports.wire = require_primitiveFns.wire;
|
package/dist/brepjs.js
CHANGED
|
@@ -229,8 +229,8 @@ function pointsInside(mesh, queries, id) {
|
|
|
229
229
|
}
|
|
230
230
|
//#endregion
|
|
231
231
|
//#region src/voxel/repairFns.ts
|
|
232
|
-
var DEFAULT_RESOLUTION = 48;
|
|
233
|
-
var DEFAULT_PADDING = 2;
|
|
232
|
+
var DEFAULT_RESOLUTION$2 = 48;
|
|
233
|
+
var DEFAULT_PADDING$2 = 2;
|
|
234
234
|
/**
|
|
235
235
|
* Repair a (possibly non-watertight) triangle-soup mesh into a closed surface:
|
|
236
236
|
* voxelize an FWN-signed SDF, then Surface-Nets contour it back to triangles.
|
|
@@ -243,16 +243,16 @@ function repairMesh(mesh, opts, id) {
|
|
|
243
243
|
const invalid = validateMesh(mesh);
|
|
244
244
|
if (invalid) return err(invalid);
|
|
245
245
|
if (mesh.vertices.length === 0 || mesh.triangles.length === 0) return err(validationError("VOXEL_EMPTY_MESH", "repairMesh requires a non-empty triangle mesh."));
|
|
246
|
-
const resolution = opts?.resolution ?? DEFAULT_RESOLUTION;
|
|
247
|
-
const padding = opts?.padding ?? DEFAULT_PADDING;
|
|
246
|
+
const resolution = opts?.resolution ?? DEFAULT_RESOLUTION$2;
|
|
247
|
+
const padding = opts?.padding ?? DEFAULT_PADDING$2;
|
|
248
248
|
if (!Number.isInteger(resolution) || resolution < 1) return err(validationError("VOXEL_INVALID_RESOLUTION", "resolution must be an integer >= 1."));
|
|
249
249
|
if (!Number.isInteger(padding) || padding < 1) return err(validationError("VOXEL_INVALID_PADDING", "padding must be an integer >= 1."));
|
|
250
250
|
const engine = resolveEngine(id);
|
|
251
251
|
if (isErr(engine)) return engine;
|
|
252
252
|
try {
|
|
253
253
|
try {
|
|
254
|
-
var _usingCtx$
|
|
255
|
-
const repaired = _usingCtx$
|
|
254
|
+
var _usingCtx$6 = _usingCtx();
|
|
255
|
+
const repaired = _usingCtx$6.u(engine.value.repair_mesh(mesh.vertices, mesh.triangles, resolution, padding));
|
|
256
256
|
const vertexCount = repaired.positions.length / 3;
|
|
257
257
|
return ok({
|
|
258
258
|
vertices: repaired.positions,
|
|
@@ -265,13 +265,303 @@ function repairMesh(mesh, opts, id) {
|
|
|
265
265
|
faceHash: 0
|
|
266
266
|
}]
|
|
267
267
|
});
|
|
268
|
+
} catch (_) {
|
|
269
|
+
_usingCtx$6.e = _;
|
|
270
|
+
} finally {
|
|
271
|
+
_usingCtx$6.d();
|
|
272
|
+
}
|
|
273
|
+
} catch (cause) {
|
|
274
|
+
return err(computationError("VOXEL_REPAIR_FAILED", cause instanceof Error ? cause.message : "voxel repair failed (grid too large?).", cause));
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
//#endregion
|
|
278
|
+
//#region src/voxel/shapeMesh.ts
|
|
279
|
+
var DEFAULT_DEFLECTION = .001;
|
|
280
|
+
/**
|
|
281
|
+
* Mesh a B-rep shape into the flat triangle-soup {@link VoxelMeshInput} the voxel
|
|
282
|
+
* ops consume. Tessellates via the topology mesh API at `deflection` linear
|
|
283
|
+
* tolerance and forwards its `{ vertices, triangles }` (already Float32Array /
|
|
284
|
+
* Uint32Array). Meshing failures surface as an `err(...)` rather than a throw.
|
|
285
|
+
*/
|
|
286
|
+
function shapeToMeshInput(shape, deflection = DEFAULT_DEFLECTION) {
|
|
287
|
+
try {
|
|
288
|
+
const tessellated = mesh$1(shape, { tolerance: deflection });
|
|
289
|
+
if (tessellated.vertices.length === 0 || tessellated.triangles.length === 0) return err(computationError("VOXEL_SHAPE_MESH_EMPTY", "shape tessellated to an empty triangle mesh."));
|
|
290
|
+
return ok({
|
|
291
|
+
vertices: tessellated.vertices,
|
|
292
|
+
triangles: tessellated.triangles
|
|
293
|
+
});
|
|
294
|
+
} catch (cause) {
|
|
295
|
+
return err(computationError("VOXEL_SHAPE_MESH_FAILED", cause instanceof Error ? cause.message : "failed to mesh shape for voxel op.", cause));
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
//#endregion
|
|
299
|
+
//#region src/voxel/meshOpsFns.ts
|
|
300
|
+
var DEFAULT_RESOLUTION$1 = 48;
|
|
301
|
+
var DEFAULT_PADDING$1 = 2;
|
|
302
|
+
var BOOLEAN_OP_CODES = {
|
|
303
|
+
union: 0,
|
|
304
|
+
intersection: 1,
|
|
305
|
+
difference: 2
|
|
306
|
+
};
|
|
307
|
+
function resolveGridParams(opts) {
|
|
308
|
+
const resolution = opts?.resolution ?? DEFAULT_RESOLUTION$1;
|
|
309
|
+
const padding = opts?.padding ?? DEFAULT_PADDING$1;
|
|
310
|
+
if (!Number.isInteger(resolution) || resolution < 1) return err(validationError("VOXEL_INVALID_RESOLUTION", "resolution must be an integer >= 1."));
|
|
311
|
+
if (!Number.isInteger(padding) || padding < 1) return err(validationError("VOXEL_INVALID_PADDING", "padding must be an integer >= 1."));
|
|
312
|
+
return ok({
|
|
313
|
+
resolution,
|
|
314
|
+
padding
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
function meshFromResult(result) {
|
|
318
|
+
if (result.positions.length === 0 || result.indices.length === 0) return err(computationError("VOXEL_DEGENERATE_RESULT", "the voxel operation produced an empty mesh (over-shrunk offset or disjoint operands?)."));
|
|
319
|
+
const vertexCount = result.positions.length / 3;
|
|
320
|
+
return ok({
|
|
321
|
+
vertices: result.positions,
|
|
322
|
+
normals: result.normals,
|
|
323
|
+
triangles: result.indices,
|
|
324
|
+
uvs: new Float32Array(vertexCount * 2),
|
|
325
|
+
faceGroups: [{
|
|
326
|
+
start: 0,
|
|
327
|
+
count: result.indices.length / 3,
|
|
328
|
+
faceHash: 0
|
|
329
|
+
}]
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Offset a mesh by `distance` via a true-SDF iso-level shift: voxelize an
|
|
334
|
+
* FWN-signed SDF, subtract `distance`, then Surface-Nets contour it back.
|
|
335
|
+
*
|
|
336
|
+
* `distance > 0` grows the surface outward, `< 0` shrinks it inward. Returns a
|
|
337
|
+
* {@link KernelMeshResult} in world coordinates (a single face group, no UVs).
|
|
338
|
+
*/
|
|
339
|
+
function offsetMesh(mesh, distance, opts, id) {
|
|
340
|
+
const invalid = validateMesh(mesh);
|
|
341
|
+
if (invalid) return err(invalid);
|
|
342
|
+
if (mesh.vertices.length === 0 || mesh.triangles.length === 0) return err(validationError("VOXEL_EMPTY_MESH", "offsetMesh requires a non-empty triangle mesh."));
|
|
343
|
+
if (!Number.isFinite(distance)) return err(validationError("VOXEL_INVALID_DISTANCE", "distance must be a finite number."));
|
|
344
|
+
const params = resolveGridParams(opts);
|
|
345
|
+
if (isErr(params)) return params;
|
|
346
|
+
const engine = resolveEngine(id);
|
|
347
|
+
if (isErr(engine)) return engine;
|
|
348
|
+
try {
|
|
349
|
+
try {
|
|
350
|
+
var _usingCtx$5 = _usingCtx();
|
|
351
|
+
return meshFromResult(_usingCtx$5.u(engine.value.offset_mesh(mesh.vertices, mesh.triangles, distance, params.value.resolution, params.value.padding)));
|
|
352
|
+
} catch (_) {
|
|
353
|
+
_usingCtx$5.e = _;
|
|
354
|
+
} finally {
|
|
355
|
+
_usingCtx$5.d();
|
|
356
|
+
}
|
|
357
|
+
} catch (cause) {
|
|
358
|
+
return err(computationError("VOXEL_OFFSET_FAILED", cause instanceof Error ? cause.message : "voxel offset failed (grid too large?).", cause));
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Hollow a solid mesh into a shell of the given inward `thickness`: voxelize an
|
|
363
|
+
* FWN-signed SDF, take `max(solid, -(solid + thickness))`, then contour it.
|
|
364
|
+
*
|
|
365
|
+
* `thickness` must be finite and > 0 (inward-only). Returns a
|
|
366
|
+
* {@link KernelMeshResult} in world coordinates (a single face group, no UVs).
|
|
367
|
+
*/
|
|
368
|
+
function shellMesh(mesh, thickness, opts, id) {
|
|
369
|
+
const invalid = validateMesh(mesh);
|
|
370
|
+
if (invalid) return err(invalid);
|
|
371
|
+
if (mesh.vertices.length === 0 || mesh.triangles.length === 0) return err(validationError("VOXEL_EMPTY_MESH", "shellMesh requires a non-empty triangle mesh."));
|
|
372
|
+
if (!Number.isFinite(thickness) || thickness <= 0) return err(validationError("VOXEL_INVALID_THICKNESS", "thickness must be a finite number > 0."));
|
|
373
|
+
const params = resolveGridParams(opts);
|
|
374
|
+
if (isErr(params)) return params;
|
|
375
|
+
const engine = resolveEngine(id);
|
|
376
|
+
if (isErr(engine)) return engine;
|
|
377
|
+
try {
|
|
378
|
+
try {
|
|
379
|
+
var _usingCtx3 = _usingCtx();
|
|
380
|
+
return meshFromResult(_usingCtx3.u(engine.value.shell_mesh(mesh.vertices, mesh.triangles, thickness, params.value.resolution, params.value.padding)));
|
|
381
|
+
} catch (_) {
|
|
382
|
+
_usingCtx3.e = _;
|
|
383
|
+
} finally {
|
|
384
|
+
_usingCtx3.d();
|
|
385
|
+
}
|
|
386
|
+
} catch (cause) {
|
|
387
|
+
return err(computationError("VOXEL_SHELL_FAILED", cause instanceof Error ? cause.message : "voxel shell failed (grid too large?).", cause));
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Voxel-based CSG of two meshes: voxelize both onto a shared grid, combine their
|
|
392
|
+
* SDFs (union/intersection/difference), then Surface-Nets contour the result.
|
|
393
|
+
*
|
|
394
|
+
* `op` is `'difference'` = A − B. Robust on non-watertight input (FWN sign).
|
|
395
|
+
* Returns a {@link KernelMeshResult} in world coordinates (single group, no UVs).
|
|
396
|
+
*/
|
|
397
|
+
function voxelBoolean(a, b, op, opts, id) {
|
|
398
|
+
const invalidA = validateMesh(a);
|
|
399
|
+
if (invalidA) return err(invalidA);
|
|
400
|
+
const invalidB = validateMesh(b);
|
|
401
|
+
if (invalidB) return err(invalidB);
|
|
402
|
+
if (a.vertices.length === 0 || a.triangles.length === 0) return err(validationError("VOXEL_EMPTY_MESH", "voxelBoolean requires a non-empty mesh for operand A."));
|
|
403
|
+
if (b.vertices.length === 0 || b.triangles.length === 0) return err(validationError("VOXEL_EMPTY_MESH", "voxelBoolean requires a non-empty mesh for operand B."));
|
|
404
|
+
const opCode = BOOLEAN_OP_CODES[op];
|
|
405
|
+
const params = resolveGridParams(opts);
|
|
406
|
+
if (isErr(params)) return params;
|
|
407
|
+
const engine = resolveEngine(id);
|
|
408
|
+
if (isErr(engine)) return engine;
|
|
409
|
+
try {
|
|
410
|
+
try {
|
|
411
|
+
var _usingCtx4 = _usingCtx();
|
|
412
|
+
return meshFromResult(_usingCtx4.u(engine.value.voxel_boolean(a.vertices, a.triangles, b.vertices, b.triangles, opCode, params.value.resolution, params.value.padding)));
|
|
413
|
+
} catch (_) {
|
|
414
|
+
_usingCtx4.e = _;
|
|
415
|
+
} finally {
|
|
416
|
+
_usingCtx4.d();
|
|
417
|
+
}
|
|
418
|
+
} catch (cause) {
|
|
419
|
+
return err(computationError("VOXEL_BOOLEAN_FAILED", cause instanceof Error ? cause.message : "voxel boolean failed (grid too large?).", cause));
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Offset a B-rep shape by `distance`: tessellate it, then run {@link offsetMesh}
|
|
424
|
+
* on the resulting triangle soup. `distance > 0` grows outward, `< 0` shrinks
|
|
425
|
+
* inward. Threads a meshing failure straight back as an `err(...)`.
|
|
426
|
+
*/
|
|
427
|
+
function offsetShape(shape, distance, opts, id) {
|
|
428
|
+
const meshInput = shapeToMeshInput(shape);
|
|
429
|
+
if (isErr(meshInput)) return meshInput;
|
|
430
|
+
return offsetMesh(meshInput.value, distance, opts, id);
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Hollow a B-rep shape into a shell of inward `thickness`: tessellate it, then
|
|
434
|
+
* run {@link shellMesh}. `thickness` must be finite and > 0. Threads a meshing
|
|
435
|
+
* failure straight back as an `err(...)`.
|
|
436
|
+
*/
|
|
437
|
+
function shellShape(shape, thickness, opts, id) {
|
|
438
|
+
const meshInput = shapeToMeshInput(shape);
|
|
439
|
+
if (isErr(meshInput)) return meshInput;
|
|
440
|
+
return shellMesh(meshInput.value, thickness, opts, id);
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Voxel CSG of two B-rep shapes: tessellate both, then run {@link voxelBoolean}.
|
|
444
|
+
* `op` is `'difference'` = A − B. Threads either meshing failure back as an
|
|
445
|
+
* `err(...)`.
|
|
446
|
+
*/
|
|
447
|
+
function voxelBooleanShapes(a, b, op, opts, id) {
|
|
448
|
+
const meshA = shapeToMeshInput(a);
|
|
449
|
+
if (isErr(meshA)) return meshA;
|
|
450
|
+
const meshB = shapeToMeshInput(b);
|
|
451
|
+
if (isErr(meshB)) return meshB;
|
|
452
|
+
return voxelBoolean(meshA.value, meshB.value, op, opts, id);
|
|
453
|
+
}
|
|
454
|
+
//#endregion
|
|
455
|
+
//#region src/lattice/latticeFns.ts
|
|
456
|
+
var LATTICE_TAGS = {
|
|
457
|
+
gyroid: 0,
|
|
458
|
+
schwarzP: 1,
|
|
459
|
+
diamond: 2
|
|
460
|
+
};
|
|
461
|
+
var DEFAULT_RESOLUTION = 48;
|
|
462
|
+
var DEFAULT_PADDING = 2;
|
|
463
|
+
function validateOptions(opts) {
|
|
464
|
+
const tag = LATTICE_TAGS[opts.type];
|
|
465
|
+
if (tag === void 0) return err(validationError("LATTICE_INVALID_TYPE", `lattice type must be one of gyroid, schwarzP, diamond (got '${opts.type}').`));
|
|
466
|
+
if (!(opts.period > 0)) return err(validationError("LATTICE_INVALID_PERIOD", "period must be > 0."));
|
|
467
|
+
if (!(opts.thickness > 0)) return err(validationError("LATTICE_INVALID_THICKNESS", "thickness must be > 0."));
|
|
468
|
+
const resolution = opts.resolution ?? DEFAULT_RESOLUTION;
|
|
469
|
+
const padding = opts.padding ?? DEFAULT_PADDING;
|
|
470
|
+
if (!Number.isInteger(resolution) || resolution < 1) return err(validationError("LATTICE_INVALID_RESOLUTION", "resolution must be an integer >= 1."));
|
|
471
|
+
if (!Number.isInteger(padding) || padding < 1) return err(validationError("LATTICE_INVALID_PADDING", "padding must be an integer >= 1."));
|
|
472
|
+
return ok({
|
|
473
|
+
tag,
|
|
474
|
+
period: opts.period,
|
|
475
|
+
thickness: opts.thickness,
|
|
476
|
+
resolution,
|
|
477
|
+
padding
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
function toMesh(repaired) {
|
|
481
|
+
const vertexCount = repaired.positions.length / 3;
|
|
482
|
+
return {
|
|
483
|
+
vertices: repaired.positions,
|
|
484
|
+
normals: repaired.normals,
|
|
485
|
+
triangles: repaired.indices,
|
|
486
|
+
uvs: new Float32Array(vertexCount * 2),
|
|
487
|
+
faceGroups: [{
|
|
488
|
+
start: 0,
|
|
489
|
+
count: repaired.indices.length / 3,
|
|
490
|
+
faceHash: 0
|
|
491
|
+
}]
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Fill a solid mesh with a TPMS lattice infill: intersect the FWN-signed solid
|
|
496
|
+
* with the chosen lattice shell field, then Surface-Nets contour the result.
|
|
497
|
+
*
|
|
498
|
+
* Returns a {@link KernelMeshResult} in world coordinates (a single face group,
|
|
499
|
+
* no UVs). The TPMS field is the approximate implicit (raw trig), so the surface
|
|
500
|
+
* is contoured at the zero level without distance normalization (ADR-0013).
|
|
501
|
+
*/
|
|
502
|
+
function latticeInfill(mesh, opts, id) {
|
|
503
|
+
const invalid = validateMesh(mesh);
|
|
504
|
+
if (invalid) return err(invalid);
|
|
505
|
+
if (mesh.vertices.length === 0 || mesh.triangles.length === 0) return err(validationError("LATTICE_EMPTY_MESH", "latticeInfill requires a non-empty triangle mesh."));
|
|
506
|
+
const resolved = validateOptions(opts);
|
|
507
|
+
if (isErr(resolved)) return resolved;
|
|
508
|
+
const engine = resolveEngine(id);
|
|
509
|
+
if (isErr(engine)) return engine;
|
|
510
|
+
const { tag, period, thickness, resolution, padding } = resolved.value;
|
|
511
|
+
try {
|
|
512
|
+
try {
|
|
513
|
+
var _usingCtx$4 = _usingCtx();
|
|
514
|
+
return ok(toMesh(_usingCtx$4.u(engine.value.lattice_infill(mesh.vertices, mesh.triangles, resolution, padding, tag, period, thickness))));
|
|
268
515
|
} catch (_) {
|
|
269
516
|
_usingCtx$4.e = _;
|
|
270
517
|
} finally {
|
|
271
518
|
_usingCtx$4.d();
|
|
272
519
|
}
|
|
273
520
|
} catch (cause) {
|
|
274
|
-
return err(computationError("
|
|
521
|
+
return err(computationError("LATTICE_INFILL_FAILED", cause instanceof Error ? cause.message : "lattice infill failed (grid too large?).", cause));
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Fill a B-rep shape with a TPMS lattice infill: tessellate the shape, then run
|
|
526
|
+
* {@link latticeInfill} on the resulting triangle soup. Threads a meshing
|
|
527
|
+
* failure straight back as an `err(...)`.
|
|
528
|
+
*/
|
|
529
|
+
function latticeInfillShape(shape, opts, id) {
|
|
530
|
+
const meshInput = shapeToMeshInput(shape);
|
|
531
|
+
if (isErr(meshInput)) return meshInput;
|
|
532
|
+
return latticeInfill(meshInput.value, opts, id);
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Contour the infinite TPMS lattice clipped to an axis-aligned box.
|
|
536
|
+
*
|
|
537
|
+
* Returns a {@link KernelMeshResult} in world coordinates (a single face group,
|
|
538
|
+
* no UVs). The TPMS field is the approximate implicit (raw trig), contoured at
|
|
539
|
+
* the zero level without distance normalization (ADR-0013).
|
|
540
|
+
*/
|
|
541
|
+
function tpmsLattice(bounds, opts, id) {
|
|
542
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
543
|
+
const lo = bounds.min[axis];
|
|
544
|
+
const hi = bounds.max[axis];
|
|
545
|
+
if (lo === void 0 || hi === void 0 || !(lo < hi)) return err(validationError("LATTICE_INVALID_BOUNDS", `bounds.min must be strictly less than bounds.max on every axis (axis ${axis}).`));
|
|
546
|
+
}
|
|
547
|
+
const resolved = validateOptions(opts);
|
|
548
|
+
if (isErr(resolved)) return resolved;
|
|
549
|
+
const engine = resolveEngine(id);
|
|
550
|
+
if (isErr(engine)) return engine;
|
|
551
|
+
const { tag, period, thickness, resolution, padding } = resolved.value;
|
|
552
|
+
const [minX, minY, minZ] = bounds.min;
|
|
553
|
+
const [maxX, maxY, maxZ] = bounds.max;
|
|
554
|
+
try {
|
|
555
|
+
try {
|
|
556
|
+
var _usingCtx3 = _usingCtx();
|
|
557
|
+
return ok(toMesh(_usingCtx3.u(engine.value.tpms_box(minX, minY, minZ, maxX, maxY, maxZ, resolution, padding, tag, period, thickness))));
|
|
558
|
+
} catch (_) {
|
|
559
|
+
_usingCtx3.e = _;
|
|
560
|
+
} finally {
|
|
561
|
+
_usingCtx3.d();
|
|
562
|
+
}
|
|
563
|
+
} catch (cause) {
|
|
564
|
+
return err(computationError("TPMS_LATTICE_FAILED", cause instanceof Error ? cause.message : "tpms lattice failed (grid too large?).", cause));
|
|
275
565
|
}
|
|
276
566
|
}
|
|
277
567
|
//#endregion
|
|
@@ -5293,4 +5583,4 @@ var csg_exports = /* @__PURE__ */ __exportAll({
|
|
|
5293
5583
|
withEvaluator: () => withEvaluator
|
|
5294
5584
|
});
|
|
5295
5585
|
//#endregion
|
|
5296
|
-
export { BaseSketcher2d, BlueprintSketcher, BrepBugError, BrepErrorCode, BrepWrapperError, BrepkitAdapter, CompoundSketch, DEG2RAD, DisposalScope, FaceSketcher, HASH_CODE_MAX, OK, OcctWasmAdapter, RAD2DEG, Sketch, Sketcher, Sketches, addChild, addHoles, addMate, addStep, adjacentFaces, all, andThen, applyGlue, applyMatrix, approximateCurve, as2D, as3D, asTopo, assignRoles, autoHeal, bezier, blueprintToDXF, booleanPipeline, booleans_exports as booleans, boss, box, bsplineApprox, bug, cameraFromPlane, cameraLookAt, captureHint, cast, castShape, castShape3D, chamfer, chamferDistAngle as chamferDistAngleShape, chamferWithEvolution, checkAllInterferences, checkBoolean, checkInterference, circle, circularPattern, classifyPointOnFace, clearMeshCache, clone, closedWire, collect, collectShapes, colorFaces, colorShape, complexExtrude, composeTransforms, compound, compoundSketchExtrude, compoundSketchFace, compoundSketchLoft, compoundSketchRevolve, computationError, computeStraightSkeleton, cone, construction_exports as construction, convexHull, cornerFinder, countNodes, createAssembly, createAssemblyNode, createBlueprint, createCamera, createCompound, createCompoundBlueprint, createDistanceQuery, createEdge, createFace, createHandle, createHistory, createKernelHandle, createMeshCache, createNamedPlane, createOperationRegistry, createPlane, createRef, createRegistry, createShell, createSolid, createTaskQueue, createVertex, createWire, createWorkerClient, createWorkerHandler, csg_exports as csg, curve2dBoundingBox, curve2dDistanceFrom, curve2dFirstPoint, curve2dIsOnCurve, curve2dLastPoint, curve2dParameter, curve2dSplitAt, curve2dTangentAt, curveEndPoint, curveIsClosed, curveIsPeriodic, curveLength, curvePeriod, curvePointAt, curveStartPoint, curveTangentAt, cut, cut2D, cutAll, cutAllBisect, cutBlueprints, cutWithEvolution, cylinder, defaultScorer, dequeueTask, describe, deserializeDrawing, deserializeHistory, fromBREP as deserializeShape, downcast, draft, draw, drawCircle, drawEllipse, drawFaceOutline, drawParametricFunction, drawPointsInterpolation, drawPolysides, drawProjection, drawRectangle, drawRoundedRectangle, drawSingleCircle, drawSingleEllipse, drawText, drawingChamfer, drawingCut, drawingFillet, drawingFuse, drawingIntersect, drawingToSketchOnPlane, drill, edgeFinder, edgesOfFace, ellipse, ellipseArc, ellipsoid, enqueueTask, err, exportAssemblySTEP, exportDXF, exportGlb, exportGltf, exportIGES, exportOBJ, exportSTEP, exportSTEPConfigured, exportSTL, exportThreeMF, extrude, extrudeAll, face, faceCenter, faceFinder, faceGeomType, faceOrientation, facesOfEdge, fill, filledFace, fillet, filletWithEvolution, findFacesByTag, findNode, findStep, fixSelfIntersection, fixShape, flatMap, flatten, flipFaceOrientation, flipOrientation, fontMetrics, fromBREP$1 as fromBREP, fromKernelDir, fromKernelPnt, fromKernelVec, fromNullable, fuse, fuse2D, fuseAll, fuseAllBisect, fuseBlueprints, fuseWithEvolution, gearGeometry, getActiveVoxelId, getBounds, getBounds2D, getCompSolids, getCurveType, getDisposalStats, getEdges, getFaceColor, getFaceOrigins, getFaceTags, getFaces, getFont, getHashCode, getShape as getHistoryShape, getKernel, getNurbsCurveData, getNurbsSurfaceData, getOrientation, getOrientation2D, getPerformanceStats, getShapeColor, getShapeKind, getShells, getSingleFace, getSolids, getSurfaceType, getTagMetadata, getVertices, getVoxel, getWires, guidedSweep, heal, healFace, healSolid, healWire, helix, hull, importDXF, importGLB, importIGES, importOBJ, importSTEP, importSTL, importSVG, importSVGPathD, importThreeMF, init, initFromManifold, initFromOC, initVoxel, innerWires, interpolateCurve, intersect, intersect2D, intersectBlueprints, intersectWithEvolution, invalidateShapeCache, ioNs_exports as io, ioError, is2D, is3D, isChamferRadius, isClosedWire, isCompSolid, isCompound, isDisposeRequest, isEdge, isEmpty, isEqualShape, isErr, isErrorResponse, isFace, isFilletRadius, isInitRequest, isInside2D, isLive, isManifoldShell, isNumber, isOk, isOperationRequest, isOrientedFace, isPlanarFace, isPlanarWire, isProjectionPlane, isEmpty$1 as isQueueEmpty, isSameShape, isShape1D, isShape3D, isShell, isSolid, isSuccessResponse, isValid, isValidSolid, isVertex, isWire, iterCompSolids, iterEdges, iterFaces, iterShells, iterSolids, iterTopo, iterVertices, iterWires, kernelCall, kernelCallRaw, kernelCallScoped, kernelError, line, linearPattern, loadFont, loft, loftAll, makeBaseBox, makeExternalGear, makeInternalGear, makePlane, makePlanetaryGear, makeProjectedEdges, manifoldShell, map, mapBoth, mapErr, match, measureArea, measureCurvatureAt, measureCurvatureAtMid, measureDistance, measureDistanceProps, measureLength, measureLinearProps, measureSurfaceProps, measureVolume, measureVolumeProps, measurement_exports as measurement, mesh, meshEdges, meshMultiLOD, minkowski, mirror, mirror2D, mirrorDrawing, mirrorJoin, modifiers_exports as modifiers, modifyStep, moduleInitError, multiSectionSweep, normalAt, offset, offsetFace, offsetWire2D, ok, or, orElse, organiseBlueprints, orientedFace, outerWire, patterns_exports as patterns, pendingCount, pipeline, pivotPlane, planarFace, planarWire, planetPlacements, pocket, pointOnSurface, pointsInside, polygon, polyhedron, polysideInnerRadius, polysidesBlueprint, positionOnCurve, prewarm, primitives_exports as primitives, projectEdges, projectPointOnFace, query_exports as query, queryError, rectangularPattern, registerHandler, registerKernel, registerOperation, registerShape, registerVoxel, rejectAll, removeChild, removeHolesFromFace, repairMesh, replayFrom, replayHistory, resetDisposalStats, resetPerformanceStats, resize, resolve, resolve3D, resolveDirection, resolvePlane, resolveRef, reverseCurve, revolve, roof, rotate, rotate2D, rotateDrawing, roundedRectangleBlueprint, scale, scale2D, scaleDrawing, section, sectionToFace, serializeHistory, setShapeOrigin, setTagMetadata, sewShells, shape, shapeType, sharedEdges, shell, shellWithEvolution, simplify, sketchCircle, sketchEllipse, sketchExtrude, sketchFace, sketchFaceOffset, sketchHelix, sketchLoft, sketchOnFace2D, sketchOnPlane2D, sketchParametricFunction, sketchPolysides, sketchRectangle, sketchRevolve, sketchRoundedRectangle, sketchSweep, sketchText, sketchWires, sketcherStateError, slice, solid, solidFromShell, solveAssembly, sphere, split, stepCount, stepsFrom, stretch2D, subFace, supportExtrude, supportsConstraintSketch, supportsProjection, surfaceFromGrid, surfaceFromImage, sweep, tagFaces, tangentArc, tap, tapErr, textBlueprints, textMetrics, thicken, threePointArc, toBREP, toBufferGeometryData, toGroupedBufferGeometryData, toKernelVec, toLODGeometryData, toLineGeometryData, toSVGPathD, toVec2, toVec3, torus, transformCopy, transforms_exports as transforms, translate, translate2D, translateDrawing, translatePlane, tryCatch, tryCatchAsync, twistExtrude, typeCastError, undoLast, unsupportedError, unwrap, unwrapErr, unwrapOr, unwrapOrElse, updateNode, updateRoles, uvBounds, uvCoordinates, validSolid, validatePlanetary, validationError, variableFillet, vecAdd, vecAngle, vecCross, vecDistance, vecDot, vecEquals, vecIsZero, vecLength, vecLengthSq, vecNegate, vecNormalize, vecProjectToPlane, vecRepr, vecRotate, vecScale, vecSub, vertex, vertexFinder, vertexPosition, verticesOfEdge, walkAssembly, windingNumbers, wire, wireFinder, wireLoop, wiresOfFace, withKernel, withKernelDir, withKernelPnt, withKernelVec, withScope, withScopeResult, withScopeResultAsync, zip as zipResults };
|
|
5586
|
+
export { BaseSketcher2d, BlueprintSketcher, BrepBugError, BrepErrorCode, BrepWrapperError, BrepkitAdapter, CompoundSketch, DEG2RAD, DisposalScope, FaceSketcher, HASH_CODE_MAX, OK, OcctWasmAdapter, RAD2DEG, Sketch, Sketcher, Sketches, addChild, addHoles, addMate, addStep, adjacentFaces, all, andThen, applyGlue, applyMatrix, approximateCurve, as2D, as3D, asTopo, assignRoles, autoHeal, bezier, blueprintToDXF, booleanPipeline, booleans_exports as booleans, boss, box, bsplineApprox, bug, cameraFromPlane, cameraLookAt, captureHint, cast, castShape, castShape3D, chamfer, chamferDistAngle as chamferDistAngleShape, chamferWithEvolution, checkAllInterferences, checkBoolean, checkInterference, circle, circularPattern, classifyPointOnFace, clearMeshCache, clone, closedWire, collect, collectShapes, colorFaces, colorShape, complexExtrude, composeTransforms, compound, compoundSketchExtrude, compoundSketchFace, compoundSketchLoft, compoundSketchRevolve, computationError, computeStraightSkeleton, cone, construction_exports as construction, convexHull, cornerFinder, countNodes, createAssembly, createAssemblyNode, createBlueprint, createCamera, createCompound, createCompoundBlueprint, createDistanceQuery, createEdge, createFace, createHandle, createHistory, createKernelHandle, createMeshCache, createNamedPlane, createOperationRegistry, createPlane, createRef, createRegistry, createShell, createSolid, createTaskQueue, createVertex, createWire, createWorkerClient, createWorkerHandler, csg_exports as csg, curve2dBoundingBox, curve2dDistanceFrom, curve2dFirstPoint, curve2dIsOnCurve, curve2dLastPoint, curve2dParameter, curve2dSplitAt, curve2dTangentAt, curveEndPoint, curveIsClosed, curveIsPeriodic, curveLength, curvePeriod, curvePointAt, curveStartPoint, curveTangentAt, cut, cut2D, cutAll, cutAllBisect, cutBlueprints, cutWithEvolution, cylinder, defaultScorer, dequeueTask, describe, deserializeDrawing, deserializeHistory, fromBREP as deserializeShape, downcast, draft, draw, drawCircle, drawEllipse, drawFaceOutline, drawParametricFunction, drawPointsInterpolation, drawPolysides, drawProjection, drawRectangle, drawRoundedRectangle, drawSingleCircle, drawSingleEllipse, drawText, drawingChamfer, drawingCut, drawingFillet, drawingFuse, drawingIntersect, drawingToSketchOnPlane, drill, edgeFinder, edgesOfFace, ellipse, ellipseArc, ellipsoid, enqueueTask, err, exportAssemblySTEP, exportDXF, exportGlb, exportGltf, exportIGES, exportOBJ, exportSTEP, exportSTEPConfigured, exportSTL, exportThreeMF, extrude, extrudeAll, face, faceCenter, faceFinder, faceGeomType, faceOrientation, facesOfEdge, fill, filledFace, fillet, filletWithEvolution, findFacesByTag, findNode, findStep, fixSelfIntersection, fixShape, flatMap, flatten, flipFaceOrientation, flipOrientation, fontMetrics, fromBREP$1 as fromBREP, fromKernelDir, fromKernelPnt, fromKernelVec, fromNullable, fuse, fuse2D, fuseAll, fuseAllBisect, fuseBlueprints, fuseWithEvolution, gearGeometry, getActiveVoxelId, getBounds, getBounds2D, getCompSolids, getCurveType, getDisposalStats, getEdges, getFaceColor, getFaceOrigins, getFaceTags, getFaces, getFont, getHashCode, getShape as getHistoryShape, getKernel, getNurbsCurveData, getNurbsSurfaceData, getOrientation, getOrientation2D, getPerformanceStats, getShapeColor, getShapeKind, getShells, getSingleFace, getSolids, getSurfaceType, getTagMetadata, getVertices, getVoxel, getWires, guidedSweep, heal, healFace, healSolid, healWire, helix, hull, importDXF, importGLB, importIGES, importOBJ, importSTEP, importSTL, importSVG, importSVGPathD, importThreeMF, init, initFromManifold, initFromOC, initVoxel, innerWires, interpolateCurve, intersect, intersect2D, intersectBlueprints, intersectWithEvolution, invalidateShapeCache, ioNs_exports as io, ioError, is2D, is3D, isChamferRadius, isClosedWire, isCompSolid, isCompound, isDisposeRequest, isEdge, isEmpty, isEqualShape, isErr, isErrorResponse, isFace, isFilletRadius, isInitRequest, isInside2D, isLive, isManifoldShell, isNumber, isOk, isOperationRequest, isOrientedFace, isPlanarFace, isPlanarWire, isProjectionPlane, isEmpty$1 as isQueueEmpty, isSameShape, isShape1D, isShape3D, isShell, isSolid, isSuccessResponse, isValid, isValidSolid, isVertex, isWire, iterCompSolids, iterEdges, iterFaces, iterShells, iterSolids, iterTopo, iterVertices, iterWires, kernelCall, kernelCallRaw, kernelCallScoped, kernelError, latticeInfill, latticeInfillShape, line, linearPattern, loadFont, loft, loftAll, makeBaseBox, makeExternalGear, makeInternalGear, makePlane, makePlanetaryGear, makeProjectedEdges, manifoldShell, map, mapBoth, mapErr, match, measureArea, measureCurvatureAt, measureCurvatureAtMid, measureDistance, measureDistanceProps, measureLength, measureLinearProps, measureSurfaceProps, measureVolume, measureVolumeProps, measurement_exports as measurement, mesh, meshEdges, meshMultiLOD, minkowski, mirror, mirror2D, mirrorDrawing, mirrorJoin, modifiers_exports as modifiers, modifyStep, moduleInitError, multiSectionSweep, normalAt, offset, offsetFace, offsetMesh, offsetShape, offsetWire2D, ok, or, orElse, organiseBlueprints, orientedFace, outerWire, patterns_exports as patterns, pendingCount, pipeline, pivotPlane, planarFace, planarWire, planetPlacements, pocket, pointOnSurface, pointsInside, polygon, polyhedron, polysideInnerRadius, polysidesBlueprint, positionOnCurve, prewarm, primitives_exports as primitives, projectEdges, projectPointOnFace, query_exports as query, queryError, rectangularPattern, registerHandler, registerKernel, registerOperation, registerShape, registerVoxel, rejectAll, removeChild, removeHolesFromFace, repairMesh, replayFrom, replayHistory, resetDisposalStats, resetPerformanceStats, resize, resolve, resolve3D, resolveDirection, resolvePlane, resolveRef, reverseCurve, revolve, roof, rotate, rotate2D, rotateDrawing, roundedRectangleBlueprint, scale, scale2D, scaleDrawing, section, sectionToFace, serializeHistory, setShapeOrigin, setTagMetadata, sewShells, shape, shapeToMeshInput, shapeType, sharedEdges, shell, shellMesh, shellShape, shellWithEvolution, simplify, sketchCircle, sketchEllipse, sketchExtrude, sketchFace, sketchFaceOffset, sketchHelix, sketchLoft, sketchOnFace2D, sketchOnPlane2D, sketchParametricFunction, sketchPolysides, sketchRectangle, sketchRevolve, sketchRoundedRectangle, sketchSweep, sketchText, sketchWires, sketcherStateError, slice, solid, solidFromShell, solveAssembly, sphere, split, stepCount, stepsFrom, stretch2D, subFace, supportExtrude, supportsConstraintSketch, supportsProjection, surfaceFromGrid, surfaceFromImage, sweep, tagFaces, tangentArc, tap, tapErr, textBlueprints, textMetrics, thicken, threePointArc, toBREP, toBufferGeometryData, toGroupedBufferGeometryData, toKernelVec, toLODGeometryData, toLineGeometryData, toSVGPathD, toVec2, toVec3, torus, tpmsLattice, transformCopy, transforms_exports as transforms, translate, translate2D, translateDrawing, translatePlane, tryCatch, tryCatchAsync, twistExtrude, typeCastError, undoLast, unsupportedError, unwrap, unwrapErr, unwrapOr, unwrapOrElse, updateNode, updateRoles, uvBounds, uvCoordinates, validSolid, validatePlanetary, validationError, variableFillet, vecAdd, vecAngle, vecCross, vecDistance, vecDot, vecEquals, vecIsZero, vecLength, vecLengthSq, vecNegate, vecNormalize, vecProjectToPlane, vecRepr, vecRotate, vecScale, vecSub, vertex, vertexFinder, vertexPosition, verticesOfEdge, voxelBoolean, voxelBooleanShapes, walkAssembly, windingNumbers, wire, wireFinder, wireLoop, wiresOfFace, withKernel, withKernelDir, withKernelPnt, withKernelVec, withScope, withScopeResult, withScopeResultAsync, zip as zipResults };
|
package/dist/index.d.ts
CHANGED
|
@@ -34,8 +34,10 @@ export { exportDXF, blueprintToDXF, type DXFEntity, type DXFExportOptions, } fro
|
|
|
34
34
|
export { exportThreeMF, type ThreeMFExportOptions, type ThreeMFMaterial, } from './io/threemfExportFns.js';
|
|
35
35
|
export { importSVGPathD, importSVG, type SVGImportOptions } from './io/svgImportFns.js';
|
|
36
36
|
export { exportSTEPConfigured, type StepExportOptions, type StepExportPart, } from './io/stepConfigFns.js';
|
|
37
|
-
export { initVoxel, registerVoxel, getVoxel, getActiveVoxelId, windingNumbers, pointsInside, repairMesh, } from './voxel/index.js';
|
|
38
|
-
export type { VoxelEngine, VoxelMeshInput, VoxelRepairResult, RepairOptions, } from './voxel/index.js';
|
|
37
|
+
export { initVoxel, registerVoxel, getVoxel, getActiveVoxelId, windingNumbers, pointsInside, repairMesh, offsetMesh, shellMesh, voxelBoolean, offsetShape, shellShape, voxelBooleanShapes, shapeToMeshInput, } from './voxel/index.js';
|
|
38
|
+
export type { VoxelEngine, VoxelMeshInput, VoxelRepairResult, RepairOptions, VoxelOpOptions, } from './voxel/index.js';
|
|
39
|
+
export { latticeInfill, latticeInfillShape, tpmsLattice } from './lattice/index.js';
|
|
40
|
+
export type { LatticeType, LatticeOptions, LatticeBounds } from './lattice/index.js';
|
|
39
41
|
export { default as Sketcher } from './sketching/sketcher.js';
|
|
40
42
|
export { default as FaceSketcher } from './sketching/faceSketcher.js';
|
|
41
43
|
export { BaseSketcher2d } from './2d/blueprints/baseSketcher2d.js';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lattice / TPMS geometry domain (ADR-0013, Layer 3).
|
|
3
|
+
*
|
|
4
|
+
* Builds triangle meshes from triply-periodic minimal surfaces, either filling a
|
|
5
|
+
* solid (infill) or clipped to a box. Sits on the voxel domain's FWN-signed grid
|
|
6
|
+
* and Surface-Nets contour, surfaced through the shared voxel engine registry.
|
|
7
|
+
*/
|
|
8
|
+
export { latticeInfill, latticeInfillShape, tpmsLattice } from './latticeFns.js';
|
|
9
|
+
export type { LatticeType, LatticeOptions, LatticeBounds } from './latticeFns.js';
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Result } from '../core/result.js';
|
|
2
|
+
import { KernelMeshResult } from '../kernel/types.js';
|
|
3
|
+
import { AnyShape, Dimension } from '../core/shapeTypes.js';
|
|
4
|
+
import { VoxelMeshInput } from '../voxel/signFns.js';
|
|
5
|
+
/** TPMS lattice families. Maps to the wasm tag (0=Gyroid, 1=SchwarzP, 2=Diamond). */
|
|
6
|
+
export type LatticeType = 'gyroid' | 'schwarzP' | 'diamond';
|
|
7
|
+
/**
|
|
8
|
+
* TPMS lattice tuning. `period` is the unit-cell size (world units); `thickness`
|
|
9
|
+
* is the strut wall width in field units. `resolution` sizes the longest bbox
|
|
10
|
+
* axis in voxels; `padding` is the positive air-margin ring (>= 1) Surface Nets
|
|
11
|
+
* needs.
|
|
12
|
+
*/
|
|
13
|
+
export interface LatticeOptions {
|
|
14
|
+
type: LatticeType;
|
|
15
|
+
period: number;
|
|
16
|
+
thickness: number;
|
|
17
|
+
resolution?: number;
|
|
18
|
+
padding?: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Fill a solid mesh with a TPMS lattice infill: intersect the FWN-signed solid
|
|
22
|
+
* with the chosen lattice shell field, then Surface-Nets contour the result.
|
|
23
|
+
*
|
|
24
|
+
* Returns a {@link KernelMeshResult} in world coordinates (a single face group,
|
|
25
|
+
* no UVs). The TPMS field is the approximate implicit (raw trig), so the surface
|
|
26
|
+
* is contoured at the zero level without distance normalization (ADR-0013).
|
|
27
|
+
*/
|
|
28
|
+
export declare function latticeInfill(mesh: VoxelMeshInput, opts: LatticeOptions, id?: string): Result<KernelMeshResult>;
|
|
29
|
+
/**
|
|
30
|
+
* Fill a B-rep shape with a TPMS lattice infill: tessellate the shape, then run
|
|
31
|
+
* {@link latticeInfill} on the resulting triangle soup. Threads a meshing
|
|
32
|
+
* failure straight back as an `err(...)`.
|
|
33
|
+
*/
|
|
34
|
+
export declare function latticeInfillShape(shape: AnyShape<Dimension>, opts: LatticeOptions, id?: string): Result<KernelMeshResult>;
|
|
35
|
+
/** Axis-aligned bounds for a clipped TPMS lattice. */
|
|
36
|
+
export interface LatticeBounds {
|
|
37
|
+
min: [number, number, number];
|
|
38
|
+
max: [number, number, number];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Contour the infinite TPMS lattice clipped to an axis-aligned box.
|
|
42
|
+
*
|
|
43
|
+
* Returns a {@link KernelMeshResult} in world coordinates (a single face group,
|
|
44
|
+
* no UVs). The TPMS field is the approximate implicit (raw trig), contoured at
|
|
45
|
+
* the zero level without distance normalization (ADR-0013).
|
|
46
|
+
*/
|
|
47
|
+
export declare function tpmsLattice(bounds: LatticeBounds, opts: LatticeOptions, id?: string): Result<KernelMeshResult>;
|
package/dist/voxel/engine.d.ts
CHANGED
|
@@ -13,6 +13,16 @@ export interface VoxelEngine {
|
|
|
13
13
|
points_inside(verts: Float32Array, tris: Uint32Array, queries: Float32Array): Uint8Array;
|
|
14
14
|
/** Voxelize-and-contour a mesh into a closed surface (FWN sign keystone). */
|
|
15
15
|
repair_mesh(verts: Float32Array, tris: Uint32Array, resolution: number, padding: number): VoxelRepairResult;
|
|
16
|
+
/** Fill an FWN-signed solid with a TPMS lattice infill, contoured to a mesh. */
|
|
17
|
+
lattice_infill(verts: Float32Array, tris: Uint32Array, resolution: number, padding: number, lattice_type: number, period: number, thickness: number): VoxelRepairResult;
|
|
18
|
+
/** Contour the infinite TPMS lattice clipped to an axis-aligned box. */
|
|
19
|
+
tpms_box(min_x: number, min_y: number, min_z: number, max_x: number, max_y: number, max_z: number, resolution: number, padding: number, lattice_type: number, period: number, thickness: number): VoxelRepairResult;
|
|
20
|
+
/** Offset a mesh by a true-SDF iso-level shift (>0 outward, <0 inward). */
|
|
21
|
+
offset_mesh(verts: Float32Array, tris: Uint32Array, distance: number, resolution: number, padding: number): VoxelRepairResult;
|
|
22
|
+
/** Hollow a solid mesh into an inward shell of the given thickness (>0). */
|
|
23
|
+
shell_mesh(verts: Float32Array, tris: Uint32Array, thickness: number, resolution: number, padding: number): VoxelRepairResult;
|
|
24
|
+
/** Voxel CSG of two meshes (op: 0=union, 1=intersection, 2=difference A−B). */
|
|
25
|
+
voxel_boolean(verts_a: Float32Array, tris_a: Uint32Array, verts_b: Float32Array, tris_b: Uint32Array, op: number, resolution: number, padding: number): VoxelRepairResult;
|
|
16
26
|
/** Engine artifact version, for loader/artifact compatibility checks. */
|
|
17
27
|
version(): string;
|
|
18
28
|
}
|
package/dist/voxel/index.d.ts
CHANGED
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
export type { VoxelEngine, VoxelRepairResult } from './engine.js';
|
|
9
9
|
export type { VoxelMeshInput } from './signFns.js';
|
|
10
10
|
export type { RepairOptions } from './repairFns.js';
|
|
11
|
+
export type { VoxelOpOptions } from './meshOpsFns.js';
|
|
11
12
|
export { registerVoxel, getVoxel, getActiveVoxelId, initVoxel } from './registry.js';
|
|
12
13
|
export { windingNumbers, pointsInside } from './signFns.js';
|
|
13
14
|
export { repairMesh } from './repairFns.js';
|
|
15
|
+
export { offsetMesh, shellMesh, voxelBoolean, offsetShape, shellShape, voxelBooleanShapes, } from './meshOpsFns.js';
|
|
16
|
+
export { shapeToMeshInput } from './shapeMesh.js';
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Result } from '../core/result.js';
|
|
2
|
+
import { KernelMeshResult } from '../kernel/types.js';
|
|
3
|
+
import { AnyShape, Dimension } from '../core/shapeTypes.js';
|
|
4
|
+
import { VoxelMeshInput } from './signFns.js';
|
|
5
|
+
/** Voxel mesh-op tuning. `resolution` sizes the longest bbox axis in voxels;
|
|
6
|
+
* `padding` is the positive air-margin ring (>= 1) Surface Nets needs. */
|
|
7
|
+
export interface VoxelOpOptions {
|
|
8
|
+
resolution?: number;
|
|
9
|
+
padding?: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Offset a mesh by `distance` via a true-SDF iso-level shift: voxelize an
|
|
13
|
+
* FWN-signed SDF, subtract `distance`, then Surface-Nets contour it back.
|
|
14
|
+
*
|
|
15
|
+
* `distance > 0` grows the surface outward, `< 0` shrinks it inward. Returns a
|
|
16
|
+
* {@link KernelMeshResult} in world coordinates (a single face group, no UVs).
|
|
17
|
+
*/
|
|
18
|
+
export declare function offsetMesh(mesh: VoxelMeshInput, distance: number, opts?: VoxelOpOptions, id?: string): Result<KernelMeshResult>;
|
|
19
|
+
/**
|
|
20
|
+
* Hollow a solid mesh into a shell of the given inward `thickness`: voxelize an
|
|
21
|
+
* FWN-signed SDF, take `max(solid, -(solid + thickness))`, then contour it.
|
|
22
|
+
*
|
|
23
|
+
* `thickness` must be finite and > 0 (inward-only). Returns a
|
|
24
|
+
* {@link KernelMeshResult} in world coordinates (a single face group, no UVs).
|
|
25
|
+
*/
|
|
26
|
+
export declare function shellMesh(mesh: VoxelMeshInput, thickness: number, opts?: VoxelOpOptions, id?: string): Result<KernelMeshResult>;
|
|
27
|
+
/**
|
|
28
|
+
* Voxel-based CSG of two meshes: voxelize both onto a shared grid, combine their
|
|
29
|
+
* SDFs (union/intersection/difference), then Surface-Nets contour the result.
|
|
30
|
+
*
|
|
31
|
+
* `op` is `'difference'` = A − B. Robust on non-watertight input (FWN sign).
|
|
32
|
+
* Returns a {@link KernelMeshResult} in world coordinates (single group, no UVs).
|
|
33
|
+
*/
|
|
34
|
+
export declare function voxelBoolean(a: VoxelMeshInput, b: VoxelMeshInput, op: 'union' | 'intersection' | 'difference', opts?: VoxelOpOptions, id?: string): Result<KernelMeshResult>;
|
|
35
|
+
/**
|
|
36
|
+
* Offset a B-rep shape by `distance`: tessellate it, then run {@link offsetMesh}
|
|
37
|
+
* on the resulting triangle soup. `distance > 0` grows outward, `< 0` shrinks
|
|
38
|
+
* inward. Threads a meshing failure straight back as an `err(...)`.
|
|
39
|
+
*/
|
|
40
|
+
export declare function offsetShape(shape: AnyShape<Dimension>, distance: number, opts?: VoxelOpOptions, id?: string): Result<KernelMeshResult>;
|
|
41
|
+
/**
|
|
42
|
+
* Hollow a B-rep shape into a shell of inward `thickness`: tessellate it, then
|
|
43
|
+
* run {@link shellMesh}. `thickness` must be finite and > 0. Threads a meshing
|
|
44
|
+
* failure straight back as an `err(...)`.
|
|
45
|
+
*/
|
|
46
|
+
export declare function shellShape(shape: AnyShape<Dimension>, thickness: number, opts?: VoxelOpOptions, id?: string): Result<KernelMeshResult>;
|
|
47
|
+
/**
|
|
48
|
+
* Voxel CSG of two B-rep shapes: tessellate both, then run {@link voxelBoolean}.
|
|
49
|
+
* `op` is `'difference'` = A − B. Threads either meshing failure back as an
|
|
50
|
+
* `err(...)`.
|
|
51
|
+
*/
|
|
52
|
+
export declare function voxelBooleanShapes(a: AnyShape<Dimension>, b: AnyShape<Dimension>, op: 'union' | 'intersection' | 'difference', opts?: VoxelOpOptions, id?: string): Result<KernelMeshResult>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { AnyShape, Dimension } from '../core/shapeTypes.js';
|
|
2
|
+
import { Result } from '../core/result.js';
|
|
3
|
+
import { VoxelMeshInput } from './signFns.js';
|
|
4
|
+
/**
|
|
5
|
+
* Mesh a B-rep shape into the flat triangle-soup {@link VoxelMeshInput} the voxel
|
|
6
|
+
* ops consume. Tessellates via the topology mesh API at `deflection` linear
|
|
7
|
+
* tolerance and forwards its `{ vertices, triangles }` (already Float32Array /
|
|
8
|
+
* Uint32Array). Meshing failures surface as an `err(...)` rather than a throw.
|
|
9
|
+
*/
|
|
10
|
+
export declare function shapeToMeshInput(shape: AnyShape<Dimension>, deflection?: number): Result<VoxelMeshInput>;
|