brepjs 18.101.0 → 18.103.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 CHANGED
@@ -2132,6 +2132,139 @@ function roof(w, options) {
2132
2132
  }
2133
2133
  }
2134
2134
  //#endregion
2135
+ //#region src/operations/instanceFns.ts
2136
+ /**
2137
+ * Transform-only instancing — hold ONE source shape + N placements without
2138
+ * copying the kernel shape. Real geometry (a Compound, or a fused solid) or a
2139
+ * render payload (one mesh + N matrices) is produced only on demand.
2140
+ *
2141
+ * A 10x10 grid is 1 solid + 100 transforms, not 100 booleans. Materialize-on-
2142
+ * export keeps the kernel work deferred; `instancedMesh` meshes the source once
2143
+ * for an instanced (e.g. three.js InstancedMesh) preview.
2144
+ */
2145
+ function translation(v) {
2146
+ return [
2147
+ [
2148
+ 1,
2149
+ 0,
2150
+ 0,
2151
+ v[0]
2152
+ ],
2153
+ [
2154
+ 0,
2155
+ 1,
2156
+ 0,
2157
+ v[1]
2158
+ ],
2159
+ [
2160
+ 0,
2161
+ 0,
2162
+ 1,
2163
+ v[2]
2164
+ ],
2165
+ [
2166
+ 0,
2167
+ 0,
2168
+ 0,
2169
+ 1
2170
+ ]
2171
+ ];
2172
+ }
2173
+ function isVec3Array(p) {
2174
+ const first = p[0];
2175
+ return first !== void 0 && typeof first[0] === "number";
2176
+ }
2177
+ function make(source, placements, grid) {
2178
+ return {
2179
+ __instanced: true,
2180
+ source,
2181
+ placements,
2182
+ grid,
2183
+ [Symbol.dispose]() {
2184
+ source[Symbol.dispose]();
2185
+ }
2186
+ };
2187
+ }
2188
+ /** Type guard for an InstancedShape. */
2189
+ function isInstanced(x) {
2190
+ return typeof x === "object" && x !== null && x.__instanced === true;
2191
+ }
2192
+ /**
2193
+ * Instance a shape across explicit placements. `Vec3[]` is translate-only sugar
2194
+ * for `Matrix4x4[]`. The source is owned by the returned container.
2195
+ */
2196
+ function instance(source, placements) {
2197
+ return make(source, isVec3Array(placements) ? placements.map(translation) : placements.map((m) => m.map((row) => [...row])));
2198
+ }
2199
+ /** Instance a shape across a cols x rows grid in the XY plane. */
2200
+ function instanceGrid(source, opts) {
2201
+ const { cols, rows, pitchX, pitchY } = opts;
2202
+ if (!Number.isInteger(cols) || !Number.isInteger(rows) || cols < 1 || rows < 1) throw new RangeError(`instanceGrid: cols and rows must be positive integers, got ${cols}x${rows}`);
2203
+ const placements = [];
2204
+ for (let i = 0; i < cols; i++) for (let j = 0; j < rows; j++) placements.push(translation([
2205
+ i * pitchX,
2206
+ j * pitchY,
2207
+ 0
2208
+ ]));
2209
+ return make(source, placements, {
2210
+ cols,
2211
+ rows,
2212
+ pitchX,
2213
+ pitchY
2214
+ });
2215
+ }
2216
+ /** Number of placements. */
2217
+ function instanceCount(inst) {
2218
+ return inst.placements.length;
2219
+ }
2220
+ /**
2221
+ * Produce real geometry: a Compound of N placed copies (default), or a single
2222
+ * fused solid (`fuse: true`, 3D only). This is where the N kernel transforms
2223
+ * happen. A grid-built instance fuses via the faster kernel `gridPattern`.
2224
+ */
2225
+ function materialize(inst, opts = {}) {
2226
+ const { source, placements, grid } = inst;
2227
+ if (placements.length === 0) return require_errors.err(require_errors.validationError("INSTANCE_EMPTY", "materialize: instance has no placements"));
2228
+ if (opts.fuse) {
2229
+ if (!require_shapeTypes.isShape3D(source)) return require_errors.err(require_errors.validationError("MATERIALIZE_NOT_3D", "materialize({fuse:true}) requires a 3D source shape"));
2230
+ if (grid) return require_threadFns.gridPattern(source, [
2231
+ 1,
2232
+ 0,
2233
+ 0
2234
+ ], [
2235
+ 0,
2236
+ 1,
2237
+ 0
2238
+ ], grid.cols, grid.rows, grid.pitchX, grid.pitchY);
2239
+ return combine(source, placements, true);
2240
+ }
2241
+ return combine(source, placements, false);
2242
+ }
2243
+ function combine(source, placements, fuse) {
2244
+ const copies = [];
2245
+ for (const m of placements) {
2246
+ const placed = require_shapeFns.applyMatrix(source, m);
2247
+ if (!placed.ok) {
2248
+ for (const c of copies) c[Symbol.dispose]();
2249
+ return placed;
2250
+ }
2251
+ copies.push(placed.value);
2252
+ }
2253
+ const result = fuse ? require_solidBuilders.fuseAll(copies, { unsafe: true }) : require_errors.ok(require_solidBuilders.makeCompound(copies));
2254
+ for (const c of copies) if (!result.ok || c !== result.value) c[Symbol.dispose]();
2255
+ return result;
2256
+ }
2257
+ /**
2258
+ * Mesh the source once and return it alongside the placements — the
2259
+ * "one tessellation, N placements" render payload for an instanced draw.
2260
+ */
2261
+ function instancedMesh(inst, opts) {
2262
+ return {
2263
+ geometry: require_meshFns.mesh(inst.source, opts),
2264
+ instances: inst.placements
2265
+ };
2266
+ }
2267
+ //#endregion
2135
2268
  //#region src/kernel/solverAdapter.ts
2136
2269
  /**
2137
2270
  * Constraint solver adapter — analytical solver for simple assembly mates.
@@ -5338,6 +5471,19 @@ function compound$1(children) {
5338
5471
  freeParams: depsOf(...children)
5339
5472
  };
5340
5473
  }
5474
+ function instance$1(source, placements, fuse = false) {
5475
+ const copied = placements.map((m) => m.map((row) => [...row]));
5476
+ let h = fnvMixInt32(fnvMixBool(mix(startHash("Instance"), source), fuse), copied.length);
5477
+ for (const m of copied) for (const v of m.flat()) h = fnvMixNumber(h, v);
5478
+ return {
5479
+ kind: "Instance",
5480
+ source,
5481
+ placements: copied,
5482
+ fuse,
5483
+ structuralHash: h,
5484
+ freeParams: depsOf(source)
5485
+ };
5486
+ }
5341
5487
  //#endregion
5342
5488
  //#region src/csg/types.ts
5343
5489
  function outputKindOf(node) {
@@ -5362,6 +5508,7 @@ function outputKindOf(node) {
5362
5508
  case "Scale":
5363
5509
  case "Mirror": return outputKindOf(node.target);
5364
5510
  case "Compound": return "Compound";
5511
+ case "Instance": return node.fuse ? "Solid" : "Compound";
5365
5512
  }
5366
5513
  }
5367
5514
  //#endregion
@@ -5595,6 +5742,13 @@ function evalEmpty() {
5595
5742
  return require_errors.err(require_errors.computationError(require_errors.BrepErrorCode.NULL_SHAPE_INPUT, "Empty: cannot materialize an Empty node directly — only valid as boolean/transform operand"));
5596
5743
  }
5597
5744
  //#endregion
5745
+ //#region src/csg/evaluators/instance.ts
5746
+ function evalInstance(node, ctx) {
5747
+ const r = ctx.evalNode(node.source);
5748
+ if (!r.ok) return r;
5749
+ return materialize(instance(r.value, node.placements), { fuse: node.fuse });
5750
+ }
5751
+ //#endregion
5598
5752
  //#region src/csg/evaluate.ts
5599
5753
  function dispatch(node, ctx) {
5600
5754
  switch (node.kind) {
@@ -5618,6 +5772,7 @@ function dispatch(node, ctx) {
5618
5772
  case "Scale": return evalScale(node, ctx);
5619
5773
  case "Mirror": return evalMirror(node, ctx);
5620
5774
  case "Compound": return evalCompound(node, ctx);
5775
+ case "Instance": return evalInstance(node, ctx);
5621
5776
  }
5622
5777
  }
5623
5778
  function hashExprValue(h, v) {
@@ -5952,6 +6107,12 @@ function nodeToJson(n) {
5952
6107
  kind: "Compound",
5953
6108
  children: n.children.map(nodeToJson)
5954
6109
  };
6110
+ if (n.kind === "Instance") return {
6111
+ kind: "Instance",
6112
+ source: nodeToJson(n.source),
6113
+ placements: n.placements,
6114
+ fuse: n.fuse
6115
+ };
5955
6116
  return primitiveToJson(n) ?? booleanToJson(n) ?? transformToJson(n);
5956
6117
  }
5957
6118
  function bad(msg) {
@@ -6092,6 +6253,7 @@ function readNode(j) {
6092
6253
  case "Scale":
6093
6254
  case "Mirror": return readTransform(kind, j);
6094
6255
  case "Compound": return readCompound(j);
6256
+ case "Instance": return readInstance(j);
6095
6257
  default: return bad(`unknown node kind: ${String(kind)}`);
6096
6258
  }
6097
6259
  }
@@ -6250,6 +6412,16 @@ function readCompound(j) {
6250
6412
  const children = readNodeArray(j["children"], "Compound.children");
6251
6413
  return children.ok ? require_errors.ok(compound$1(children.value)) : children;
6252
6414
  }
6415
+ function isMatrix4x4(v) {
6416
+ return Array.isArray(v) && v.length === 4 && v.every((row) => Array.isArray(row) && row.length === 4 && row.every(isNumber$1));
6417
+ }
6418
+ function readInstance(j) {
6419
+ const source = readNode(j["source"]);
6420
+ if (!source.ok) return source;
6421
+ const placements = j["placements"];
6422
+ if (!Array.isArray(placements) || !placements.every(isMatrix4x4)) return bad("Instance.placements must be an array of 4x4 number matrices");
6423
+ return require_errors.ok(instance$1(source.value, placements, j["fuse"] === true));
6424
+ }
6253
6425
  //#endregion
6254
6426
  //#region src/csg/optimize.ts
6255
6427
  function optimize(node) {
@@ -6346,6 +6518,7 @@ function optimizeNode(n) {
6346
6518
  at: n.at ? foldExpr(n.at) : void 0
6347
6519
  });
6348
6520
  case "Compound": return compound$1(n.children.map(optimizeNode).filter((c) => c.kind !== "Empty"));
6521
+ case "Instance": return instance$1(optimizeNode(n.source), n.placements, n.fuse);
6349
6522
  }
6350
6523
  }
6351
6524
  function optimizeFuse(a, b, tol) {
@@ -6435,6 +6608,7 @@ function rebuildChildren(n, pred, repl) {
6435
6608
  at: n.at
6436
6609
  });
6437
6610
  case "Compound": return compound$1(n.children.map((c) => walk(c, pred, repl)));
6611
+ case "Instance": return instance$1(walk(n.source, pred, repl), n.placements, n.fuse);
6438
6612
  }
6439
6613
  }
6440
6614
  function forEachNode(root, fn) {
@@ -6463,6 +6637,7 @@ function childrenOf(n) {
6463
6637
  case "Scale":
6464
6638
  case "Mirror": return [n.target];
6465
6639
  case "Compound": return n.children;
6640
+ case "Instance": return [n.source];
6466
6641
  }
6467
6642
  }
6468
6643
  function nodeCount(root) {
@@ -6499,6 +6674,7 @@ var csg_exports = /* @__PURE__ */ require_textBlueprints.__exportAll({
6499
6674
  fromJSON: () => fromJSON,
6500
6675
  fuse: () => fuse$1,
6501
6676
  fuseAll: () => fuseAll$1,
6677
+ instance: () => instance$1,
6502
6678
  intersect: () => intersect$1,
6503
6679
  line: () => line$1,
6504
6680
  mirror: () => mirror$1,
@@ -6788,6 +6964,7 @@ exports.getTagMetadata = require_shapeFns.getTagMetadata;
6788
6964
  exports.getVertices = require_topologyQueryFns.getVertices;
6789
6965
  exports.getVoxel = getVoxel;
6790
6966
  exports.getWires = require_topologyQueryFns.getWires;
6967
+ exports.gridPattern = require_threadFns.gridPattern;
6791
6968
  exports.guidedSweep = require_loftFns.guidedSweep;
6792
6969
  exports.heal = heal;
6793
6970
  exports.healFace = require_healingFns.healFace;
@@ -6810,6 +6987,10 @@ exports.initFromManifold = require_shapeTypes.initFromManifold;
6810
6987
  exports.initFromOC = require_shapeTypes.initFromOC;
6811
6988
  exports.initVoxel = initVoxel;
6812
6989
  exports.innerWires = require_faceFns.innerWires;
6990
+ exports.instance = instance;
6991
+ exports.instanceCount = instanceCount;
6992
+ exports.instanceGrid = instanceGrid;
6993
+ exports.instancedMesh = instancedMesh;
6813
6994
  exports.interpolateCurve = require_curveFns.interpolateCurve;
6814
6995
  exports.intersect = intersect;
6815
6996
  exports.intersect2D = require_boolean2D.intersect2D;
@@ -6840,6 +7021,7 @@ exports.isFace = require_shapeTypes.isFace;
6840
7021
  exports.isFilletRadius = isFilletRadius;
6841
7022
  exports.isInitRequest = require_workerHandler.isInitRequest;
6842
7023
  exports.isInside2D = require_blueprintFns.isInside2D;
7024
+ exports.isInstanced = isInstanced;
6843
7025
  exports.isLive = require_shapeTypes.isLive;
6844
7026
  exports.isManifoldShell = require_shapeTypes.isManifoldShell;
6845
7027
  exports.isNumber = isNumber;
@@ -6893,6 +7075,7 @@ exports.map = require_errors.map;
6893
7075
  exports.mapBoth = require_errors.mapBoth;
6894
7076
  exports.mapErr = require_errors.mapErr;
6895
7077
  exports.match = require_errors.match;
7078
+ exports.materialize = materialize;
6896
7079
  exports.measureArea = require_measureFns.measureArea;
6897
7080
  exports.measureCurvatureAt = require_measureFns.measureCurvatureAt;
6898
7081
  exports.measureCurvatureAtMid = require_measureFns.measureCurvatureAtMid;
package/dist/brepjs.js CHANGED
@@ -13,9 +13,9 @@ import { a as curveIsPeriodic, c as curvePointAt, d as flipOrientation, f as get
13
13
  import { a as meshEdges$1, c as createMeshCache, i as mesh$1, n as exportSTEP, o as meshMultiLOD, r as exportSTL, s as clearMeshCache, t as exportIGES } from "./meshFns-CAAarhVm.js";
14
14
  import { n as getAtOrThrow, r as lastOrThrow, t as firstOrThrow } from "./arrayAccess-DrUGPADn.js";
15
15
  import { _ as makeThreePointArc, d as makeCircle, h as makeLine, l as makeBSplineInterpolation, n as fill, r as makeFace, s as assembleWire } from "./surfaceBuilders-Bww4jhBo.js";
16
- import { _ as section$1, b as split$1, d as booleanPipeline, f as cut$2, g as intersect$2, h as fuseAll$2, m as fuse$2, p as cutAll$2, r as makeCylinder, v as sectionToFace$1, y as slice$1 } from "./solidBuilders-Biwv2cW3.js";
16
+ import { _ as section$1, b as split$1, d as booleanPipeline, f as cut$2, g as intersect$2, h as fuseAll$2, m as fuse$2, p as cutAll$2, r as makeCylinder, t as makeCompound, v as sectionToFace$1, y as slice$1 } from "./solidBuilders-Biwv2cW3.js";
17
17
  import { A as edgesOfFace, C as shellWithEvolution, D as getNurbsCurveData, E as fuseAllBisect, F as chamferDistAngle, I as toBufferGeometryData, L as toGroupedBufferGeometryData, M as sharedEdges, N as verticesOfEdge, O as getNurbsSurfaceData, P as wiresOfFace, R as toLODGeometryData, S as intersectWithEvolution, T as cutAllBisect, _ as positionOnCurve, a as healFace, b as filletWithEvolution, c as isValid$1, d as draft$1, f as fillet$1, g as variableFillet, h as thicken$1, i as heal$1, j as facesOfEdge, k as adjacentFaces, l as solidFromShell, m as shell$1, n as fixSelfIntersection, o as healSolid, p as offset$1, r as fixShape, s as healWire, t as autoHeal, u as chamfer$1, v as chamferWithEvolution, w as checkBoolean, x as fuseWithEvolution, y as cutWithEvolution, z as toLineGeometryData } from "./healingFns-Bb3eEBNm.js";
18
- import { A as setJointValue, B as findNode, C as cylindricalJoint, D as planarJoint, E as mechanismDOF, F as quatRotate, H as updateNode, I as addChild, J as createAssembly, K as linearPattern, L as collectShapes, M as sphericalJoint, N as quatFromAxisAngle, O as prismaticJoint, P as quatFromTo, R as countNodes, S as addJoint, T as jointTransform, U as walkAssembly, V as removeChild, W as circularPattern, _ as exportURDF, a as deserializeHistory, b as inverseKinematics, c as modifyStep, d as replayFrom, f as replayHistory, g as undoLast, h as stepsFrom, i as createRegistry, j as setJointValues, k as revoluteJoint, l as registerOperation, m as stepCount, n as addStep, o as findStep, p as serializeHistory, q as exportAssemblySTEP, r as createHistory, s as getShape, t as thread, u as registerShape, v as importURDF, w as forwardKinematics, x as jointTrajectory, y as jointsFromDH, z as createAssemblyNode } from "./threadFns-8rKpQXiR.js";
18
+ import { A as setJointValue, B as findNode, C as cylindricalJoint, D as planarJoint, E as mechanismDOF, F as quatRotate, G as gridPattern, H as updateNode, I as addChild, J as createAssembly, K as linearPattern, L as collectShapes, M as sphericalJoint, N as quatFromAxisAngle, O as prismaticJoint, P as quatFromTo, R as countNodes, S as addJoint, T as jointTransform, U as walkAssembly, V as removeChild, W as circularPattern, _ as exportURDF, a as deserializeHistory, b as inverseKinematics, c as modifyStep, d as replayFrom, f as replayHistory, g as undoLast, h as stepsFrom, i as createRegistry, j as setJointValues, k as revoluteJoint, l as registerOperation, m as stepCount, n as addStep, o as findStep, p as serializeHistory, q as exportAssemblySTEP, r as createHistory, s as getShape, t as thread, u as registerShape, v as importURDF, w as forwardKinematics, x as jointTrajectory, y as jointsFromDH, z as createAssemblyNode } from "./threadFns-8rKpQXiR.js";
19
19
  import { n as BaseSketcher2d, r as organiseBlueprints, t as BlueprintSketcher } from "./blueprintSketcher-CEQ8s7TD.js";
20
20
  import { a as createTypedFinder, i as wireFinder, n as edgeFinder, r as faceFinder, t as getSingleFace } from "./helpers-DT44wBAD.js";
21
21
  import { A as sketchEllipse, D as makeBaseBox, E as deserializeDrawing, F as sketchRectangle, I as sketchRoundedRectangle, L as FaceSketcher, M as sketchHelix, N as sketchParametricFunction, O as polysideInnerRadius, P as sketchPolysides, R as Sketcher, S as drawText, _ as drawPolysides, a as drawingIntersect, b as drawSingleCircle, c as rotateDrawing, d as drawFaceOutline, f as drawProjection, g as drawPointsInterpolation, h as drawParametricFunction, i as drawingFuse, j as sketchFaceOffset, k as sketchCircle, l as scaleDrawing, m as drawEllipse, n as drawingCut, o as drawingToSketchOnPlane, p as drawCircle, r as drawingFillet, s as mirrorDrawing, t as drawingChamfer, u as translateDrawing, v as drawRectangle, w as draw, x as drawSingleEllipse, y as drawRoundedRectangle } from "./drawFns-CsXPFyDe.js";
@@ -2143,6 +2143,139 @@ function roof(w, options) {
2143
2143
  }
2144
2144
  }
2145
2145
  //#endregion
2146
+ //#region src/operations/instanceFns.ts
2147
+ /**
2148
+ * Transform-only instancing — hold ONE source shape + N placements without
2149
+ * copying the kernel shape. Real geometry (a Compound, or a fused solid) or a
2150
+ * render payload (one mesh + N matrices) is produced only on demand.
2151
+ *
2152
+ * A 10x10 grid is 1 solid + 100 transforms, not 100 booleans. Materialize-on-
2153
+ * export keeps the kernel work deferred; `instancedMesh` meshes the source once
2154
+ * for an instanced (e.g. three.js InstancedMesh) preview.
2155
+ */
2156
+ function translation(v) {
2157
+ return [
2158
+ [
2159
+ 1,
2160
+ 0,
2161
+ 0,
2162
+ v[0]
2163
+ ],
2164
+ [
2165
+ 0,
2166
+ 1,
2167
+ 0,
2168
+ v[1]
2169
+ ],
2170
+ [
2171
+ 0,
2172
+ 0,
2173
+ 1,
2174
+ v[2]
2175
+ ],
2176
+ [
2177
+ 0,
2178
+ 0,
2179
+ 0,
2180
+ 1
2181
+ ]
2182
+ ];
2183
+ }
2184
+ function isVec3Array(p) {
2185
+ const first = p[0];
2186
+ return first !== void 0 && typeof first[0] === "number";
2187
+ }
2188
+ function make(source, placements, grid) {
2189
+ return {
2190
+ __instanced: true,
2191
+ source,
2192
+ placements,
2193
+ grid,
2194
+ [Symbol.dispose]() {
2195
+ source[Symbol.dispose]();
2196
+ }
2197
+ };
2198
+ }
2199
+ /** Type guard for an InstancedShape. */
2200
+ function isInstanced(x) {
2201
+ return typeof x === "object" && x !== null && x.__instanced === true;
2202
+ }
2203
+ /**
2204
+ * Instance a shape across explicit placements. `Vec3[]` is translate-only sugar
2205
+ * for `Matrix4x4[]`. The source is owned by the returned container.
2206
+ */
2207
+ function instance(source, placements) {
2208
+ return make(source, isVec3Array(placements) ? placements.map(translation) : placements.map((m) => m.map((row) => [...row])));
2209
+ }
2210
+ /** Instance a shape across a cols x rows grid in the XY plane. */
2211
+ function instanceGrid(source, opts) {
2212
+ const { cols, rows, pitchX, pitchY } = opts;
2213
+ if (!Number.isInteger(cols) || !Number.isInteger(rows) || cols < 1 || rows < 1) throw new RangeError(`instanceGrid: cols and rows must be positive integers, got ${cols}x${rows}`);
2214
+ const placements = [];
2215
+ for (let i = 0; i < cols; i++) for (let j = 0; j < rows; j++) placements.push(translation([
2216
+ i * pitchX,
2217
+ j * pitchY,
2218
+ 0
2219
+ ]));
2220
+ return make(source, placements, {
2221
+ cols,
2222
+ rows,
2223
+ pitchX,
2224
+ pitchY
2225
+ });
2226
+ }
2227
+ /** Number of placements. */
2228
+ function instanceCount(inst) {
2229
+ return inst.placements.length;
2230
+ }
2231
+ /**
2232
+ * Produce real geometry: a Compound of N placed copies (default), or a single
2233
+ * fused solid (`fuse: true`, 3D only). This is where the N kernel transforms
2234
+ * happen. A grid-built instance fuses via the faster kernel `gridPattern`.
2235
+ */
2236
+ function materialize(inst, opts = {}) {
2237
+ const { source, placements, grid } = inst;
2238
+ if (placements.length === 0) return err(validationError("INSTANCE_EMPTY", "materialize: instance has no placements"));
2239
+ if (opts.fuse) {
2240
+ if (!isShape3D(source)) return err(validationError("MATERIALIZE_NOT_3D", "materialize({fuse:true}) requires a 3D source shape"));
2241
+ if (grid) return gridPattern(source, [
2242
+ 1,
2243
+ 0,
2244
+ 0
2245
+ ], [
2246
+ 0,
2247
+ 1,
2248
+ 0
2249
+ ], grid.cols, grid.rows, grid.pitchX, grid.pitchY);
2250
+ return combine(source, placements, true);
2251
+ }
2252
+ return combine(source, placements, false);
2253
+ }
2254
+ function combine(source, placements, fuse) {
2255
+ const copies = [];
2256
+ for (const m of placements) {
2257
+ const placed = applyMatrix$1(source, m);
2258
+ if (!placed.ok) {
2259
+ for (const c of copies) c[Symbol.dispose]();
2260
+ return placed;
2261
+ }
2262
+ copies.push(placed.value);
2263
+ }
2264
+ const result = fuse ? fuseAll$2(copies, { unsafe: true }) : ok(makeCompound(copies));
2265
+ for (const c of copies) if (!result.ok || c !== result.value) c[Symbol.dispose]();
2266
+ return result;
2267
+ }
2268
+ /**
2269
+ * Mesh the source once and return it alongside the placements — the
2270
+ * "one tessellation, N placements" render payload for an instanced draw.
2271
+ */
2272
+ function instancedMesh(inst, opts) {
2273
+ return {
2274
+ geometry: mesh$1(inst.source, opts),
2275
+ instances: inst.placements
2276
+ };
2277
+ }
2278
+ //#endregion
2146
2279
  //#region src/kernel/solverAdapter.ts
2147
2280
  /**
2148
2281
  * Constraint solver adapter — analytical solver for simple assembly mates.
@@ -5341,6 +5474,19 @@ function compound$1(children) {
5341
5474
  freeParams: depsOf(...children)
5342
5475
  };
5343
5476
  }
5477
+ function instance$1(source, placements, fuse = false) {
5478
+ const copied = placements.map((m) => m.map((row) => [...row]));
5479
+ let h = fnvMixInt32(fnvMixBool(mix(startHash("Instance"), source), fuse), copied.length);
5480
+ for (const m of copied) for (const v of m.flat()) h = fnvMixNumber(h, v);
5481
+ return {
5482
+ kind: "Instance",
5483
+ source,
5484
+ placements: copied,
5485
+ fuse,
5486
+ structuralHash: h,
5487
+ freeParams: depsOf(source)
5488
+ };
5489
+ }
5344
5490
  //#endregion
5345
5491
  //#region src/csg/types.ts
5346
5492
  function outputKindOf(node) {
@@ -5365,6 +5511,7 @@ function outputKindOf(node) {
5365
5511
  case "Scale":
5366
5512
  case "Mirror": return outputKindOf(node.target);
5367
5513
  case "Compound": return "Compound";
5514
+ case "Instance": return node.fuse ? "Solid" : "Compound";
5368
5515
  }
5369
5516
  }
5370
5517
  //#endregion
@@ -5598,6 +5745,13 @@ function evalEmpty() {
5598
5745
  return err(computationError(BrepErrorCode.NULL_SHAPE_INPUT, "Empty: cannot materialize an Empty node directly — only valid as boolean/transform operand"));
5599
5746
  }
5600
5747
  //#endregion
5748
+ //#region src/csg/evaluators/instance.ts
5749
+ function evalInstance(node, ctx) {
5750
+ const r = ctx.evalNode(node.source);
5751
+ if (!r.ok) return r;
5752
+ return materialize(instance(r.value, node.placements), { fuse: node.fuse });
5753
+ }
5754
+ //#endregion
5601
5755
  //#region src/csg/evaluate.ts
5602
5756
  function dispatch(node, ctx) {
5603
5757
  switch (node.kind) {
@@ -5621,6 +5775,7 @@ function dispatch(node, ctx) {
5621
5775
  case "Scale": return evalScale(node, ctx);
5622
5776
  case "Mirror": return evalMirror(node, ctx);
5623
5777
  case "Compound": return evalCompound(node, ctx);
5778
+ case "Instance": return evalInstance(node, ctx);
5624
5779
  }
5625
5780
  }
5626
5781
  function hashExprValue(h, v) {
@@ -5955,6 +6110,12 @@ function nodeToJson(n) {
5955
6110
  kind: "Compound",
5956
6111
  children: n.children.map(nodeToJson)
5957
6112
  };
6113
+ if (n.kind === "Instance") return {
6114
+ kind: "Instance",
6115
+ source: nodeToJson(n.source),
6116
+ placements: n.placements,
6117
+ fuse: n.fuse
6118
+ };
5958
6119
  return primitiveToJson(n) ?? booleanToJson(n) ?? transformToJson(n);
5959
6120
  }
5960
6121
  function bad(msg) {
@@ -6095,6 +6256,7 @@ function readNode(j) {
6095
6256
  case "Scale":
6096
6257
  case "Mirror": return readTransform(kind, j);
6097
6258
  case "Compound": return readCompound(j);
6259
+ case "Instance": return readInstance(j);
6098
6260
  default: return bad(`unknown node kind: ${String(kind)}`);
6099
6261
  }
6100
6262
  }
@@ -6253,6 +6415,16 @@ function readCompound(j) {
6253
6415
  const children = readNodeArray(j["children"], "Compound.children");
6254
6416
  return children.ok ? ok(compound$1(children.value)) : children;
6255
6417
  }
6418
+ function isMatrix4x4(v) {
6419
+ return Array.isArray(v) && v.length === 4 && v.every((row) => Array.isArray(row) && row.length === 4 && row.every(isNumber$1));
6420
+ }
6421
+ function readInstance(j) {
6422
+ const source = readNode(j["source"]);
6423
+ if (!source.ok) return source;
6424
+ const placements = j["placements"];
6425
+ if (!Array.isArray(placements) || !placements.every(isMatrix4x4)) return bad("Instance.placements must be an array of 4x4 number matrices");
6426
+ return ok(instance$1(source.value, placements, j["fuse"] === true));
6427
+ }
6256
6428
  //#endregion
6257
6429
  //#region src/csg/optimize.ts
6258
6430
  function optimize(node) {
@@ -6349,6 +6521,7 @@ function optimizeNode(n) {
6349
6521
  at: n.at ? foldExpr(n.at) : void 0
6350
6522
  });
6351
6523
  case "Compound": return compound$1(n.children.map(optimizeNode).filter((c) => c.kind !== "Empty"));
6524
+ case "Instance": return instance$1(optimizeNode(n.source), n.placements, n.fuse);
6352
6525
  }
6353
6526
  }
6354
6527
  function optimizeFuse(a, b, tol) {
@@ -6438,6 +6611,7 @@ function rebuildChildren(n, pred, repl) {
6438
6611
  at: n.at
6439
6612
  });
6440
6613
  case "Compound": return compound$1(n.children.map((c) => walk(c, pred, repl)));
6614
+ case "Instance": return instance$1(walk(n.source, pred, repl), n.placements, n.fuse);
6441
6615
  }
6442
6616
  }
6443
6617
  function forEachNode(root, fn) {
@@ -6466,6 +6640,7 @@ function childrenOf(n) {
6466
6640
  case "Scale":
6467
6641
  case "Mirror": return [n.target];
6468
6642
  case "Compound": return n.children;
6643
+ case "Instance": return [n.source];
6469
6644
  }
6470
6645
  }
6471
6646
  function nodeCount(root) {
@@ -6502,6 +6677,7 @@ var csg_exports = /* @__PURE__ */ __exportAll({
6502
6677
  fromJSON: () => fromJSON,
6503
6678
  fuse: () => fuse$1,
6504
6679
  fuseAll: () => fuseAll$1,
6680
+ instance: () => instance$1,
6505
6681
  intersect: () => intersect$1,
6506
6682
  line: () => line$1,
6507
6683
  mirror: () => mirror$1,
@@ -6526,4 +6702,4 @@ var csg_exports = /* @__PURE__ */ __exportAll({
6526
6702
  withEvaluator: () => withEvaluator
6527
6703
  });
6528
6704
  //#endregion
6529
- export { BaseSketcher2d, BlueprintSketcher, BrepBugError, BrepErrorCode, BrepWrapperError, BrepkitAdapter, CompoundSketch, DEFAULT_CAPABILITIES, DEG2RAD, DisposalScope, EXACT_BREP_CAPABILITIES, FaceSketcher, HASH_CODE_MAX, OK, OcctWasmAdapter, RAD2DEG, Sketch, Sketcher, Sketches, addChild, addHoles, addJoint, 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, currentQuality, curve2dBoundingBox, curve2dDistanceFrom, curve2dFirstPoint, curve2dIsOnCurve, curve2dLastPoint, curve2dParameter, curve2dSplitAt, curve2dTangentAt, curveAxis, curveEndPoint, curveIsClosed, curveIsPeriodic, curveLength, curvePeriod, curvePointAt, curveStartPoint, curveTangentAt, cut, cut2D, cutAll, cutAllBisect, cutBlueprints, cutWithEvolution, cylinder, cylindricalJoint, 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, exportURDF, extrude, extrudeAll, face, faceAxis, faceCenter, faceFinder, faceGeomType, faceOrientation, facesOfEdge, fieldBoolean, fieldContour, fieldOffset, fieldReinit, fieldShell, fill, filledFace, fillet, filletWithEvolution, findFacesByTag, findNode, findStep, fixSelfIntersection, fixShape, flatMap, flatten, flipFaceOrientation, flipOrientation, fontMetrics, forwardKinematics, 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, getKernelCapabilities, getKernelTier, 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, importURDF, init, initFromManifold, initFromOC, initVoxel, innerWires, interpolateCurve, intersect, intersect2D, intersectBlueprints, intersectWithEvolution, invalidateShapeCache, inverseKinematics, 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, jointTrajectory, jointTransform, jointsFromDH, 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, mechanismDOF, 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, planarJoint, planarWire, planetPlacements, pocket, pointOnSurface, pointsInside, polygon, polyhedron, polysideInnerRadius, polysidesBlueprint, positionOnCurve, prewarm, primitives_exports as primitives, prismaticJoint, projectEdges, projectPointOnFace, query_exports as query, queryError, rectangularPattern, registerHandler, registerKernel, registerKernelTier, registerOperation, registerShape, registerVoxel, rejectAll, removeChild, removeHolesFromFace, repairMesh, replayFrom, replayHistory, resetDisposalStats, resetPerformanceStats, resize, resolve, resolve3D, resolveDirection, resolvePlane, resolveRef, reverseCurve, revoluteJoint, revolve, roof, rotate, rotate2D, rotateDrawing, roundedRectangleBlueprint, scale, scale2D, scaleDrawing, box$1 as sdfBox, capsule as sdfCapsule, cone$1 as sdfCone, cylinder$1 as sdfCylinder, fieldAxialRamp as sdfFieldAxialRamp, fieldClamp as sdfFieldClamp, fieldConst as sdfFieldConst, fieldFromSdf as sdfFieldFromSdf, fieldRadialRamp as sdfFieldRadialRamp, lattice as sdfLattice, plane as sdfPlane, roundedBox as sdfRoundedBox, sphere as sdfSphere, strutLattice as sdfStrutLattice, sweep as sdfSweep, torus as sdfTorus, section, sectionToFace, serializeHistory, setJointValue, setJointValues, 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$1 as sphere, sphericalJoint, split, stepCount, stepsFrom, stretch2D, subFace, supportExtrude, supportsConstraintSketch, supportsProjection, surfaceFromGrid, surfaceFromImage, sweep$1 as sweep, tagFaces, tangentArc, tap, tapErr, textBlueprints, textMetrics, thicken, thread, threePointArc, toBREP, toBufferGeometryData, toGroupedBufferGeometryData, toKernelVec, toLODGeometryData, toLineGeometryData, toSVGPathD, toVec2, toVec3, torus$1 as 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, voxelBooleanField, voxelBooleanFieldShapes, voxelBooleanShapes, voxelField, voxelFieldFromShape, walkAssembly, windingNumbers, wire, wireFinder, wireLoop, wiresOfFace, withKernel, withKernelDir, withKernelPnt, withKernelVec, withQuality, withScope, withScopeResult, withScopeResultAsync, withTier, zip as zipResults };
6705
+ export { BaseSketcher2d, BlueprintSketcher, BrepBugError, BrepErrorCode, BrepWrapperError, BrepkitAdapter, CompoundSketch, DEFAULT_CAPABILITIES, DEG2RAD, DisposalScope, EXACT_BREP_CAPABILITIES, FaceSketcher, HASH_CODE_MAX, OK, OcctWasmAdapter, RAD2DEG, Sketch, Sketcher, Sketches, addChild, addHoles, addJoint, 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, currentQuality, curve2dBoundingBox, curve2dDistanceFrom, curve2dFirstPoint, curve2dIsOnCurve, curve2dLastPoint, curve2dParameter, curve2dSplitAt, curve2dTangentAt, curveAxis, curveEndPoint, curveIsClosed, curveIsPeriodic, curveLength, curvePeriod, curvePointAt, curveStartPoint, curveTangentAt, cut, cut2D, cutAll, cutAllBisect, cutBlueprints, cutWithEvolution, cylinder, cylindricalJoint, 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, exportURDF, extrude, extrudeAll, face, faceAxis, faceCenter, faceFinder, faceGeomType, faceOrientation, facesOfEdge, fieldBoolean, fieldContour, fieldOffset, fieldReinit, fieldShell, fill, filledFace, fillet, filletWithEvolution, findFacesByTag, findNode, findStep, fixSelfIntersection, fixShape, flatMap, flatten, flipFaceOrientation, flipOrientation, fontMetrics, forwardKinematics, 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, getKernelCapabilities, getKernelTier, getNurbsCurveData, getNurbsSurfaceData, getOrientation, getOrientation2D, getPerformanceStats, getShapeColor, getShapeKind, getShells, getSingleFace, getSolids, getSurfaceType, getTagMetadata, getVertices, getVoxel, getWires, gridPattern, guidedSweep, heal, healFace, healSolid, healWire, helix, hull, importDXF, importGLB, importIGES, importOBJ, importSTEP, importSTL, importSVG, importSVGPathD, importThreeMF, importURDF, init, initFromManifold, initFromOC, initVoxel, innerWires, instance, instanceCount, instanceGrid, instancedMesh, interpolateCurve, intersect, intersect2D, intersectBlueprints, intersectWithEvolution, invalidateShapeCache, inverseKinematics, ioNs_exports as io, ioError, is2D, is3D, isChamferRadius, isClosedWire, isCompSolid, isCompound, isDisposeRequest, isEdge, isEmpty, isEqualShape, isErr, isErrorResponse, isFace, isFilletRadius, isInitRequest, isInside2D, isInstanced, 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, jointTrajectory, jointTransform, jointsFromDH, kernelCall, kernelCallRaw, kernelCallScoped, kernelError, latticeInfill, latticeInfillShape, line, linearPattern, loadFont, loft, loftAll, makeBaseBox, makeExternalGear, makeInternalGear, makePlane, makePlanetaryGear, makeProjectedEdges, manifoldShell, map, mapBoth, mapErr, match, materialize, measureArea, measureCurvatureAt, measureCurvatureAtMid, measureDistance, measureDistanceProps, measureLength, measureLinearProps, measureSurfaceProps, measureVolume, measureVolumeProps, measurement_exports as measurement, mechanismDOF, 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, planarJoint, planarWire, planetPlacements, pocket, pointOnSurface, pointsInside, polygon, polyhedron, polysideInnerRadius, polysidesBlueprint, positionOnCurve, prewarm, primitives_exports as primitives, prismaticJoint, projectEdges, projectPointOnFace, query_exports as query, queryError, rectangularPattern, registerHandler, registerKernel, registerKernelTier, registerOperation, registerShape, registerVoxel, rejectAll, removeChild, removeHolesFromFace, repairMesh, replayFrom, replayHistory, resetDisposalStats, resetPerformanceStats, resize, resolve, resolve3D, resolveDirection, resolvePlane, resolveRef, reverseCurve, revoluteJoint, revolve, roof, rotate, rotate2D, rotateDrawing, roundedRectangleBlueprint, scale, scale2D, scaleDrawing, box$1 as sdfBox, capsule as sdfCapsule, cone$1 as sdfCone, cylinder$1 as sdfCylinder, fieldAxialRamp as sdfFieldAxialRamp, fieldClamp as sdfFieldClamp, fieldConst as sdfFieldConst, fieldFromSdf as sdfFieldFromSdf, fieldRadialRamp as sdfFieldRadialRamp, lattice as sdfLattice, plane as sdfPlane, roundedBox as sdfRoundedBox, sphere as sdfSphere, strutLattice as sdfStrutLattice, sweep as sdfSweep, torus as sdfTorus, section, sectionToFace, serializeHistory, setJointValue, setJointValues, 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$1 as sphere, sphericalJoint, split, stepCount, stepsFrom, stretch2D, subFace, supportExtrude, supportsConstraintSketch, supportsProjection, surfaceFromGrid, surfaceFromImage, sweep$1 as sweep, tagFaces, tangentArc, tap, tapErr, textBlueprints, textMetrics, thicken, thread, threePointArc, toBREP, toBufferGeometryData, toGroupedBufferGeometryData, toKernelVec, toLODGeometryData, toLineGeometryData, toSVGPathD, toVec2, toVec3, torus$1 as 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, voxelBooleanField, voxelBooleanFieldShapes, voxelBooleanShapes, voxelField, voxelFieldFromShape, walkAssembly, windingNumbers, wire, wireFinder, wireLoop, wiresOfFace, withKernel, withKernelDir, withKernelPnt, withKernelVec, withQuality, withScope, withScopeResult, withScopeResultAsync, withTier, zip as zipResults };
@@ -1,5 +1,6 @@
1
1
  import { ScalarInput, Vec3Input } from './expressions.js';
2
- import { BoxNode, SphereNode, CylinderNode, ConeNode, TorusNode, PolygonNode, CircleNode, LineNode, VertexLitNode, EmptyNode, FuseNode, CutNode, IntersectNode, FuseAllNode, CutAllNode, TranslateNode, RotateNode, ScaleNode, MirrorNode, CompoundNode, IRNode, SolidNode, FaceNode, EdgeNode, VertexNode } from './types.js';
2
+ import { Matrix4x4 } from '../core/types.js';
3
+ import { BoxNode, SphereNode, CylinderNode, ConeNode, TorusNode, PolygonNode, CircleNode, LineNode, VertexLitNode, EmptyNode, FuseNode, CutNode, IntersectNode, FuseAllNode, CutAllNode, TranslateNode, RotateNode, ScaleNode, MirrorNode, CompoundNode, InstanceNode, IRNode, SolidNode, FaceNode, EdgeNode, VertexNode } from './types.js';
3
4
  export declare function box(x: ScalarInput, y: ScalarInput, z: ScalarInput): BoxNode;
4
5
  export declare function sphere(radius: ScalarInput): SphereNode;
5
6
  export declare function cylinder(radius: ScalarInput, height: ScalarInput): CylinderNode;
@@ -33,4 +34,5 @@ export interface MirrorOptions {
33
34
  }
34
35
  export declare function mirror(target: IRNode, options?: MirrorOptions): MirrorNode;
35
36
  export declare function compound(children: ReadonlyArray<IRNode>): CompoundNode;
37
+ export declare function instance(source: IRNode, placements: ReadonlyArray<Matrix4x4>, fuse?: boolean): InstanceNode;
36
38
  export type { FaceNode, EdgeNode, VertexNode, SolidNode };
@@ -0,0 +1,5 @@
1
+ import { Result } from '../../core/result.js';
2
+ import { AnyShape, Dimension } from '../../core/shapeTypes.js';
3
+ import { InstanceNode } from '../types.js';
4
+ import { EvalContext } from './context.js';
5
+ export declare function evalInstance(node: InstanceNode, ctx: EvalContext): Result<AnyShape<Dimension>>;
@@ -5,9 +5,9 @@
5
5
  * parameterize via named expression bindings; evaluate against the active
6
6
  * kernel with subtree-level cache reuse for incremental parametric edits.
7
7
  */
8
- export { box, sphere, cylinder, cone, torus, polygon, circle, line, vertex, emptySolid, emptyFace, emptyWire, fuse, cut, intersect, fuseAll, cutAll, translate, rotate, scale, mirror, compound, type RotateOptions, type ScaleOptions, type MirrorOptions, } from './builders.js';
8
+ export { box, sphere, cylinder, cone, torus, polygon, circle, line, vertex, emptySolid, emptyFace, emptyWire, fuse, cut, intersect, fuseAll, cutAll, translate, rotate, scale, mirror, compound, instance, type RotateOptions, type ScaleOptions, type MirrorOptions, } from './builders.js';
9
9
  export { numLit, vec3Lit, vec2Lit, param, binOp, unaryOp, component, buildVec, add, mul, asScalarExpr, asVec3Expr, asVec2Expr, type Expr, type ExprValue, type Env, type ScalarInput, type Vec3Input, type Vec2Input, type BinaryOp, type UnaryOp, } from './expressions.js';
10
- export type { IRNode, NodeKind, OutputKind, PrimitiveNode, BooleanNode, TransformIRNode, SolidNode, FaceNode, EdgeNode, VertexNode, AnyNode, } from './types.js';
10
+ export type { IRNode, NodeKind, OutputKind, PrimitiveNode, BooleanNode, TransformIRNode, InstanceNode, SolidNode, FaceNode, EdgeNode, VertexNode, AnyNode, } from './types.js';
11
11
  export { outputKindOf } from './types.js';
12
12
  export { Evaluator, withEvaluator, type EvaluatorOptions, type StepInfo, type CacheStats, } from './evaluate.js';
13
13
  export { toJSON, fromJSON, CSG_VERSION, type CsgEnvelope } from './serialize.js';
@@ -1,4 +1,5 @@
1
1
  import { Expr } from './expressions.js';
2
+ import { Matrix4x4 } from '../core/types.js';
2
3
  export type OutputKind = 'Solid' | 'Face' | 'Wire' | 'Edge' | 'Vertex' | 'Compound';
3
4
  /** Output kinds that have a corresponding empty-shape builder + serializer. */
4
5
  export type EmptyOutputKind = 'Solid' | 'Face' | 'Wire';
@@ -111,10 +112,18 @@ export interface CompoundNode extends IRNodeBase {
111
112
  readonly kind: 'Compound';
112
113
  readonly children: readonly IRNode[];
113
114
  }
115
+ export interface InstanceNode extends IRNodeBase {
116
+ readonly kind: 'Instance';
117
+ readonly source: IRNode;
118
+ /** Per-instance world transforms (row-major 4x4 literals). */
119
+ readonly placements: readonly Matrix4x4[];
120
+ /** Fuse the placed copies into one solid; otherwise a Compound. */
121
+ readonly fuse: boolean;
122
+ }
114
123
  export type PrimitiveNode = BoxNode | SphereNode | CylinderNode | ConeNode | TorusNode | PolygonNode | CircleNode | LineNode | VertexLitNode | EmptyNode;
115
124
  export type BooleanNode = FuseNode | CutNode | IntersectNode | FuseAllNode | CutAllNode;
116
125
  export type TransformIRNode = TranslateNode | RotateNode | ScaleNode | MirrorNode;
117
- export type IRNode = PrimitiveNode | BooleanNode | TransformIRNode | CompoundNode;
126
+ export type IRNode = PrimitiveNode | BooleanNode | TransformIRNode | CompoundNode | InstanceNode;
118
127
  export type NodeKind = IRNode['kind'];
119
128
  export type AnyNode = IRNode;
120
129
  /** Nodes that produce a 3D solid. */
package/dist/index.d.ts CHANGED
@@ -118,7 +118,8 @@ export { guidedSweep, type GuidedSweepOptions } from './operations/guidedSweepFn
118
118
  export { roof, type RoofOptions } from './operations/roofFns.js';
119
119
  export { computeStraightSkeleton, type SkPoint2D, type SkeletonNode, type SkeletonFace, type StraightSkeleton, } from './operations/straightSkeleton.js';
120
120
  export { exportAssemblySTEP, type ShapeOptions, type SupportedUnit, } from './operations/exporterFns.js';
121
- export { linearPattern, circularPattern } from './operations/patternFns.js';
121
+ export { linearPattern, circularPattern, gridPattern } from './operations/patternFns.js';
122
+ export { instance, instanceGrid, materialize, instancedMesh, instanceCount, isInstanced, type InstancedShape, type InstancedMesh, type InstanceGridOptions, type MaterializeOptions, } from './operations/instanceFns.js';
122
123
  export { createAssemblyNode, addChild, removeChild, updateNode, findNode, walkAssembly, countNodes, collectShapes, type AssemblyNode, type AssemblyNodeOptions, } from './operations/assemblyFns.js';
123
124
  export { addMate, solveAssembly, type MateConstraint, type MateEntity, type AssemblySolveResult, } from './operations/mateFns.js';
124
125
  export { revoluteJoint, prismaticJoint, cylindricalJoint, planarJoint, sphericalJoint, setJointValue, setJointValues, jointTransform, addJoint, forwardKinematics, mechanismDOF, type Joint, type JointDOF, type JointAxis, type JointType, type JointPose, type JointOptions, type CylindricalOptions, type PlanarOptions, type SphericalOptions, } from './operations/jointFns.js';
@@ -0,0 +1,66 @@
1
+ import { Result } from '../core/result.js';
2
+ import { AnyShape, Dimension } from '../core/shapeTypes.js';
3
+ import { Matrix4x4, Vec3 } from '../core/types.js';
4
+ import { ShapeMesh, MeshOptions } from '../topology/meshFns.js';
5
+ /** Grid provenance, set by `instanceGrid`, lets `materialize({fuse})` route
6
+ * through the faster kernel `gridPattern` path instead of fusing N copies. */
7
+ interface GridSpec {
8
+ readonly cols: number;
9
+ readonly rows: number;
10
+ readonly pitchX: number;
11
+ readonly pitchY: number;
12
+ }
13
+ /**
14
+ * One source shape replicated across N placements. Not a kernel-backed shape
15
+ * (no `.wrapped`) — an app-level container that owns the single source handle.
16
+ */
17
+ export interface InstancedShape<D extends Dimension = '3D'> extends Disposable {
18
+ readonly __instanced: true;
19
+ /** The single shared source shape (owned — disposed with this container). */
20
+ readonly source: AnyShape<D>;
21
+ /** Per-instance world transforms (row-major 4x4). */
22
+ readonly placements: ReadonlyArray<Matrix4x4>;
23
+ /** Present when built by `instanceGrid` — enables the gridPattern fuse path. */
24
+ readonly grid?: GridSpec | undefined;
25
+ [Symbol.dispose](): void;
26
+ }
27
+ /** Type guard for an InstancedShape. */
28
+ export declare function isInstanced(x: unknown): x is InstancedShape<Dimension>;
29
+ /**
30
+ * Instance a shape across explicit placements. `Vec3[]` is translate-only sugar
31
+ * for `Matrix4x4[]`. The source is owned by the returned container.
32
+ */
33
+ export declare function instance<D extends Dimension>(source: AnyShape<D>, placements: readonly Matrix4x4[] | readonly Vec3[]): InstancedShape<D>;
34
+ export interface InstanceGridOptions {
35
+ readonly cols: number;
36
+ readonly rows: number;
37
+ readonly pitchX: number;
38
+ readonly pitchY: number;
39
+ }
40
+ /** Instance a shape across a cols x rows grid in the XY plane. */
41
+ export declare function instanceGrid<D extends Dimension>(source: AnyShape<D>, opts: InstanceGridOptions): InstancedShape<D>;
42
+ /** Number of placements. */
43
+ export declare function instanceCount(inst: InstancedShape<Dimension>): number;
44
+ export interface MaterializeOptions {
45
+ /** Fuse the placed copies into a single solid (3D only). Default: false —
46
+ * returns a Compound of separate placed parts. */
47
+ readonly fuse?: boolean;
48
+ }
49
+ /**
50
+ * Produce real geometry: a Compound of N placed copies (default), or a single
51
+ * fused solid (`fuse: true`, 3D only). This is where the N kernel transforms
52
+ * happen. A grid-built instance fuses via the faster kernel `gridPattern`.
53
+ */
54
+ export declare function materialize<D extends Dimension>(inst: InstancedShape<D>, opts?: MaterializeOptions): Result<AnyShape<Dimension>>;
55
+ export interface InstancedMesh {
56
+ /** The source shape meshed ONCE. */
57
+ readonly geometry: ShapeMesh;
58
+ /** Per-instance transforms — feed to e.g. three.js InstancedMesh. */
59
+ readonly instances: ReadonlyArray<Matrix4x4>;
60
+ }
61
+ /**
62
+ * Mesh the source once and return it alongside the placements — the
63
+ * "one tessellation, N placements" render payload for an instanced draw.
64
+ */
65
+ export declare function instancedMesh(inst: InstancedShape<Dimension>, opts?: MeshOptions): InstancedMesh;
66
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brepjs",
3
- "version": "18.101.0",
3
+ "version": "18.103.0",
4
4
  "description": "Web CAD library with pluggable geometry kernel",
5
5
  "keywords": [
6
6
  "cad",