brepjs 18.115.1 → 18.116.1

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
@@ -17,7 +17,7 @@ const require_arrayAccess = require("./arrayAccess-e4H9cBfh.cjs");
17
17
  const require_surfaceBuilders = require("./surfaceBuilders-UyyfZy8w.cjs");
18
18
  const require_solidBuilders = require("./solidBuilders-DiUv_mD0.cjs");
19
19
  const require_healingFns = require("./healingFns-xLJ6Cp_O.cjs");
20
- const require_threadFns = require("./threadFns-C8bErjVC.cjs");
20
+ const require_threadFns = require("./threadFns-BqWHWpea.cjs");
21
21
  const require_blueprintSketcher = require("./blueprintSketcher-RK-cH6ps.cjs");
22
22
  const require_helpers = require("./helpers-BSLPCflF.cjs");
23
23
  const require_drawFns = require("./drawFns-DNxs7PAT.cjs");
@@ -30,7 +30,7 @@ const require_loftFns = require("./loftFns-fpTmrIdd.cjs");
30
30
  const require_cameraFns = require("./cameraFns-uo1sZbBT.cjs");
31
31
  const require_textMetrics = require("./textMetrics-IC_IoI7l.cjs");
32
32
  const require_adjacencyFns = require("./adjacencyFns-d8ZabkvX.cjs");
33
- const require_refResolveFns = require("./refResolveFns-pRVyjy35.cjs");
33
+ const require_refResolveFns = require("./refResolveFns-BiEBAREW.cjs");
34
34
  const require_workerPool = require("./workerPool-D30NsiIW.cjs");
35
35
  const require_primitiveFns = require("./primitiveFns-DLIWtQWA.cjs");
36
36
  //#region src/topology/shapeBooleans.ts
@@ -5837,48 +5837,144 @@ function cacheKey(node, env, kernelId, tolerance) {
5837
5837
  const tolHash = tolerance === void 0 ? "d" : fnvMixNumber(fnvInit(), tolerance).toString(16);
5838
5838
  return `${toHex(node.structuralHash)}:${kernelId}:${toHex(projHash)}:${tolHash}`;
5839
5839
  }
5840
+ var IDENTITY_QUAT = [
5841
+ 1,
5842
+ 0,
5843
+ 0,
5844
+ 0
5845
+ ];
5846
+ /** Resolve a Rotate node's angle/axis/center in `env`, applying the same
5847
+ * defaults as the evaluator (Z axis, origin pivot). Null if any free param
5848
+ * can't be evaluated, so the caller stops peeling there. */
5849
+ function evalRotateParams(node, env) {
5850
+ const a = evalScalar(node.angle, env, "evaluateMesh.peelRigid");
5851
+ if (!a.ok) return null;
5852
+ const axis = node.axis ? evalVec3(node.axis, env, "evaluateMesh.peelRigid") : require_errors.ok([
5853
+ 0,
5854
+ 0,
5855
+ 1
5856
+ ]);
5857
+ if (!axis.ok) return null;
5858
+ const center = node.at ? evalVec3(node.at, env, "evaluateMesh.peelRigid") : require_errors.ok([
5859
+ 0,
5860
+ 0,
5861
+ 0
5862
+ ]);
5863
+ if (!center.ok) return null;
5864
+ return {
5865
+ angle: a.value,
5866
+ axis: axis.value,
5867
+ center: center.value
5868
+ };
5869
+ }
5840
5870
  /**
5841
- * Peel outer Translate nodes off `node`, accumulating their env-evaluated
5842
- * offsets. Pure translations compose by addition, so the inner geometry is
5843
- * placement-independent: it meshes once and the cached mesh is shifted per
5844
- * placement instead of re-tessellating. Stops at the first non-Translate node
5845
- * (or one whose offset can't be evaluated in `env`).
5871
+ * Peel outer rigid-motion nodes (Translate / Rotate) off `node`, composing them
5872
+ * into a single rotation + translation. A rigid motion shares its inner
5873
+ * geometry's tessellation the shape path re-tags it via `locate` (#1633) — so
5874
+ * the inner meshes once and the cached mesh is moved per placement instead of
5875
+ * re-tessellating. Stops at the first non-rigid node (Scale/Mirror/boolean/…) or
5876
+ * one whose parameters can't be evaluated in `env`.
5877
+ *
5878
+ * Composition is outer∘inner: peeling outward, each node's local transform acts
5879
+ * on points the inner nodes already placed, so it post-multiplies the
5880
+ * accumulator. A pure-translation chain keeps the identity rotation, so the mesh
5881
+ * move below degenerates to the exact vertex-shift fast path (normals untouched).
5846
5882
  */
5847
- function peelTranslate(node, env) {
5883
+ function peelRigid(node, env) {
5848
5884
  let inner = node;
5849
- let ox = 0;
5850
- let oy = 0;
5851
- let oz = 0;
5852
- while (inner.kind === "Translate") {
5853
- const v = evalVec3(inner.vector, env, "evaluateMesh.peelTranslate");
5885
+ let rot = IDENTITY_QUAT;
5886
+ let trans = [
5887
+ 0,
5888
+ 0,
5889
+ 0
5890
+ ];
5891
+ for (;;) if (inner.kind === "Translate") {
5892
+ const v = evalVec3(inner.vector, env, "evaluateMesh.peelRigid");
5854
5893
  if (!v.ok) break;
5855
- ox += v.value[0];
5856
- oy += v.value[1];
5857
- oz += v.value[2];
5894
+ const rv = require_threadFns.quatRotate(rot, v.value);
5895
+ trans = [
5896
+ trans[0] + rv[0],
5897
+ trans[1] + rv[1],
5898
+ trans[2] + rv[2]
5899
+ ];
5858
5900
  inner = inner.target;
5859
- }
5901
+ } else if (inner.kind === "Rotate") {
5902
+ const p = evalRotateParams(inner, env);
5903
+ if (!p) break;
5904
+ const r = require_threadFns.quatFromAxisAngle(p.axis, p.angle * Math.PI / 180);
5905
+ const newRot = require_threadFns.quatMultiply(rot, r);
5906
+ const rotC = require_threadFns.quatRotate(rot, p.center);
5907
+ const newRotC = require_threadFns.quatRotate(newRot, p.center);
5908
+ trans = [
5909
+ trans[0] + rotC[0] - newRotC[0],
5910
+ trans[1] + rotC[1] - newRotC[1],
5911
+ trans[2] + rotC[2] - newRotC[2]
5912
+ ];
5913
+ rot = newRot;
5914
+ inner = inner.target;
5915
+ } else break;
5860
5916
  return {
5861
5917
  inner,
5862
- offset: [
5863
- ox,
5864
- oy,
5865
- oz
5866
- ]
5918
+ rot,
5919
+ trans
5867
5920
  };
5868
5921
  }
5869
- /** Shift every vertex of a mesh by `offset`; normals, UVs and triangles are unchanged. */
5870
- function translateMesh(m, offset) {
5871
- const [dx, dy, dz] = offset;
5872
- if (dx === 0 && dy === 0 && dz === 0) return m;
5873
- const vertices = new Float32Array(m.vertices.length);
5874
- for (let i = 0; i < m.vertices.length; i += 3) {
5875
- vertices[i] = (m.vertices[i] ?? 0) + dx;
5876
- vertices[i + 1] = (m.vertices[i + 1] ?? 0) + dy;
5877
- vertices[i + 2] = (m.vertices[i + 2] ?? 0) + dz;
5922
+ function isIdentityQuat(q) {
5923
+ return q[0] === 1 && q[1] === 0 && q[2] === 0 && q[3] === 0;
5924
+ }
5925
+ /**
5926
+ * Write `src` (flat xyz) rigidly transformed by quaternion (qw,qx,qy,qz) then
5927
+ * translation (tx,ty,tz) into `dst`. The quaternion-rotate math is inlined with
5928
+ * scalar locals no per-vertex array allocation — because this runs once per
5929
+ * mesh vertex/normal and helper-allocated vector math regressed the gridfinity
5930
+ * benchmark 17% (see `utils/vec3.ts`). Pass zero translation for normals.
5931
+ */
5932
+ function rotateXyzBuffer(dst, src, qw, qx, qy, qz, tx, ty, tz) {
5933
+ for (let i = 0; i < src.length; i += 3) {
5934
+ const vx = src[i] ?? 0;
5935
+ const vy = src[i + 1] ?? 0;
5936
+ const vz = src[i + 2] ?? 0;
5937
+ const cx = 2 * (qy * vz - qz * vy);
5938
+ const cy = 2 * (qz * vx - qx * vz);
5939
+ const cz = 2 * (qx * vy - qy * vx);
5940
+ dst[i] = vx + qw * cx + (qy * cz - qz * cy) + tx;
5941
+ dst[i + 1] = vy + qw * cy + (qz * cx - qx * cz) + ty;
5942
+ dst[i + 2] = vz + qw * cz + (qx * cy - qy * cx) + tz;
5878
5943
  }
5944
+ }
5945
+ /**
5946
+ * Apply a rigid motion to a mesh: vertices get the rotation then translation,
5947
+ * normals get the rotation only (a unit rotation preserves length, so no
5948
+ * renormalization). UVs and triangles are motion-invariant. A pure translation
5949
+ * (identity rotation) keeps the source normals array by reference and only
5950
+ * shifts vertices — the exact previous translation-only fast path.
5951
+ */
5952
+ function transformMeshRigid(m, rot, trans) {
5953
+ const [tx, ty, tz] = trans;
5954
+ const pureTranslation = isIdentityQuat(rot);
5955
+ if (pureTranslation && tx === 0 && ty === 0 && tz === 0) return m;
5956
+ const src = m.vertices;
5957
+ const vertices = new Float32Array(src.length);
5958
+ if (pureTranslation) {
5959
+ for (let i = 0; i < src.length; i += 3) {
5960
+ vertices[i] = (src[i] ?? 0) + tx;
5961
+ vertices[i + 1] = (src[i + 1] ?? 0) + ty;
5962
+ vertices[i + 2] = (src[i + 2] ?? 0) + tz;
5963
+ }
5964
+ return {
5965
+ ...m,
5966
+ vertices
5967
+ };
5968
+ }
5969
+ const [qw, qx, qy, qz] = rot;
5970
+ rotateXyzBuffer(vertices, src, qw, qx, qy, qz, tx, ty, tz);
5971
+ const sn = m.normals;
5972
+ const normals = new Float32Array(sn.length);
5973
+ rotateXyzBuffer(normals, sn, qw, qx, qy, qz, 0, 0, 0);
5879
5974
  return {
5880
5975
  ...m,
5881
- vertices
5976
+ vertices,
5977
+ normals
5882
5978
  };
5883
5979
  }
5884
5980
  /**
@@ -5886,8 +5982,8 @@ function translateMesh(m, offset) {
5886
5982
  * moved faces location-dependent hashes, so the inner mesh's `faceId`s describe
5887
5983
  * the *unplaced* shape; remap them to the placed faces (1:1 by iteration order,
5888
5984
  * since `locate` shares the source TShape) so face picking and metadata lookup
5889
- * resolve against the placed mesh. Origins are translation-invariant, so only
5890
- * `faceId` changes.
5985
+ * resolve against the placed mesh. Origins are boolean-lineage tags, invariant
5986
+ * under any rigid motion, so only `faceId` changes.
5891
5987
  *
5892
5988
  * Takes pre-captured hash arrays (not shapes): a bounded shape cache can evict
5893
5989
  * and dispose one shape while the other is being evaluated, so the caller reads
@@ -5983,7 +6079,7 @@ var Evaluator = class {
5983
6079
  }
5984
6080
  return require_errors.ok(cached);
5985
6081
  }
5986
- const { inner, offset } = peelTranslate(node, env);
6082
+ const { inner, rot, trans } = peelRigid(node, env);
5987
6083
  if (inner !== node) {
5988
6084
  const innerMesh = this.evaluateMesh(inner, env, meshOpts);
5989
6085
  if (!innerMesh.ok) return innerMesh;
@@ -5993,7 +6089,7 @@ var Evaluator = class {
5993
6089
  const placedShape = this.evaluate(node, env);
5994
6090
  if (!placedShape.ok) return placedShape;
5995
6091
  const placedHashes = require_topologyQueryFns.getFaces(placedShape.value).map(require_shapeFns.getHashCode);
5996
- const moved = translateMesh(innerMesh.value, offset);
6092
+ const moved = transformMeshRigid(innerMesh.value, rot, trans);
5997
6093
  const faceGroups = relocateFaceGroups(moved.faceGroups, innerHashes, placedHashes);
5998
6094
  const placedSet = new Set(placedHashes);
5999
6095
  if (faceGroups.every((g) => placedSet.has(g.faceId))) {
package/dist/brepjs.js CHANGED
@@ -15,7 +15,7 @@ import { n as getAtOrThrow, r as lastOrThrow, t as firstOrThrow } from "./arrayA
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-rkE-jE90.js";
16
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-C32wXQWG.js";
17
17
  import { A as toBufferGeometryData, C as shellWithEvolution, D as getNurbsCurveData, E as fuseAllBisect, M as toLODGeometryData, N as toLODGeometryLevels, O as getNurbsSurfaceData, P as toLineGeometryData, 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 toGroupedBufferGeometryData, k as chamferDistAngle, 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 } from "./healingFns-2TJHHDg5.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-bCOhVQLb.js";
18
+ import { A as setJointValue, B as createAssemblyNode, C as cylindricalJoint, D as planarJoint, E as mechanismDOF, F as quatMultiply, G as circularPattern, H as removeChild, I as quatRotate, J as exportAssemblySTEP, K as gridPattern, L as addChild, M as sphericalJoint, N as quatFromAxisAngle, O as prismaticJoint, P as quatFromTo, R as collectShapes, S as addJoint, T as jointTransform, U as updateNode, V as findNode, W as walkAssembly, Y as createAssembly, _ 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 linearPattern, 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 countNodes } from "./threadFns-KbYZu7Q9.js";
19
19
  import { n as BaseSketcher2d, r as organiseBlueprints, t as BlueprintSketcher } from "./blueprintSketcher-7rtwqUHZ.js";
20
20
  import { a as createTypedFinder, i as wireFinder, n as edgeFinder, r as faceFinder, t as getSingleFace } from "./helpers-DhJwFrkC.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-SQIBls6p.js";
@@ -29,7 +29,7 @@ import { a as Sketch, c as compoundSketchLoft, d as sketchFace, f as sketchLoft,
29
29
  import { a as makeProjectedEdges, i as projectEdges, n as cameraLookAt, r as createCamera, s as isProjectionPlane, t as cameraFromPlane } from "./cameraFns-bk9e0F-T.js";
30
30
  import { n as textMetrics, r as sketchText, t as fontMetrics } from "./textMetrics-COGYk0LE.js";
31
31
  import { a as sharedEdges, c as wiresOfFace, i as facesOfVertex, n as edgesOfFace, o as verticesOfEdge, r as facesOfEdge, s as verticesOfFace, t as adjacentFaces } from "./adjacencyFns-BYnyXXqt.js";
32
- import { _ as createRef, a as isVertexRef, b as defaultScorer, c as resolveRefParams, d as createVertexRef, f as resolveVertexRef, g as captureHint, h as assignRoles, i as isLineageRef, l as createDerivedFaceRef, m as resolveEdgeRef, n as isEdgeRef, o as resolveLineageRef, p as createEdgeRef, r as isFaceRef, s as resolveRefIn, t as isDerivedFaceRef, u as resolveDerivedFaceRef, v as resolveRef, y as updateRoles } from "./refResolveFns-hEOEcFZ8.js";
32
+ import { _ as createRef, a as isVertexRef, b as defaultScorer, c as resolveRefParams, d as createVertexRef, f as resolveVertexRef, g as captureHint, h as assignRoles, i as isLineageRef, l as createDerivedFaceRef, m as resolveEdgeRef, n as isEdgeRef, o as resolveLineageRef, p as createEdgeRef, r as isFaceRef, s as resolveRefIn, t as isDerivedFaceRef, u as resolveDerivedFaceRef, v as resolveRef, y as updateRoles } from "./refResolveFns-Ck6kJzLE.js";
33
33
  import { _ as isSuccessResponse, a as createWorkerClient, c as enqueueTask, d as rejectAll, f as isBatchRequest, g as isOperationRequest, h as isInitRequest, i as registerHandler, l as isEmpty$1, m as isErrorResponse, n as createOperationRegistry, o as createTaskQueue, p as isDisposeRequest, r as createWorkerHandler, s as dequeueTask, t as createWorkerPool, u as pendingCount } from "./workerPool-XcYX2UZ0.js";
34
34
  import { C as threePointArc, D as wireLoop, E as wire, S as tangentArc, T as vertex, _ as polygon, a as circle, b as sphere$1, c as cylinder, d as ellipsoid, f as face, g as offsetFace, h as line, i as bsplineApprox, l as ellipse, m as helix, n as bezier, o as compound, p as filledFace, r as box, s as cone, t as addHoles, u as ellipseArc, v as sewShells, w as torus$1, x as subFace, y as solid } from "./primitiveFns-CBrOyk5S.js";
35
35
  //#region \0rolldown/runtime.js
@@ -5840,48 +5840,144 @@ function cacheKey(node, env, kernelId, tolerance) {
5840
5840
  const tolHash = tolerance === void 0 ? "d" : fnvMixNumber(fnvInit(), tolerance).toString(16);
5841
5841
  return `${toHex(node.structuralHash)}:${kernelId}:${toHex(projHash)}:${tolHash}`;
5842
5842
  }
5843
+ var IDENTITY_QUAT = [
5844
+ 1,
5845
+ 0,
5846
+ 0,
5847
+ 0
5848
+ ];
5849
+ /** Resolve a Rotate node's angle/axis/center in `env`, applying the same
5850
+ * defaults as the evaluator (Z axis, origin pivot). Null if any free param
5851
+ * can't be evaluated, so the caller stops peeling there. */
5852
+ function evalRotateParams(node, env) {
5853
+ const a = evalScalar(node.angle, env, "evaluateMesh.peelRigid");
5854
+ if (!a.ok) return null;
5855
+ const axis = node.axis ? evalVec3(node.axis, env, "evaluateMesh.peelRigid") : ok([
5856
+ 0,
5857
+ 0,
5858
+ 1
5859
+ ]);
5860
+ if (!axis.ok) return null;
5861
+ const center = node.at ? evalVec3(node.at, env, "evaluateMesh.peelRigid") : ok([
5862
+ 0,
5863
+ 0,
5864
+ 0
5865
+ ]);
5866
+ if (!center.ok) return null;
5867
+ return {
5868
+ angle: a.value,
5869
+ axis: axis.value,
5870
+ center: center.value
5871
+ };
5872
+ }
5843
5873
  /**
5844
- * Peel outer Translate nodes off `node`, accumulating their env-evaluated
5845
- * offsets. Pure translations compose by addition, so the inner geometry is
5846
- * placement-independent: it meshes once and the cached mesh is shifted per
5847
- * placement instead of re-tessellating. Stops at the first non-Translate node
5848
- * (or one whose offset can't be evaluated in `env`).
5874
+ * Peel outer rigid-motion nodes (Translate / Rotate) off `node`, composing them
5875
+ * into a single rotation + translation. A rigid motion shares its inner
5876
+ * geometry's tessellation the shape path re-tags it via `locate` (#1633) — so
5877
+ * the inner meshes once and the cached mesh is moved per placement instead of
5878
+ * re-tessellating. Stops at the first non-rigid node (Scale/Mirror/boolean/…) or
5879
+ * one whose parameters can't be evaluated in `env`.
5880
+ *
5881
+ * Composition is outer∘inner: peeling outward, each node's local transform acts
5882
+ * on points the inner nodes already placed, so it post-multiplies the
5883
+ * accumulator. A pure-translation chain keeps the identity rotation, so the mesh
5884
+ * move below degenerates to the exact vertex-shift fast path (normals untouched).
5849
5885
  */
5850
- function peelTranslate(node, env) {
5886
+ function peelRigid(node, env) {
5851
5887
  let inner = node;
5852
- let ox = 0;
5853
- let oy = 0;
5854
- let oz = 0;
5855
- while (inner.kind === "Translate") {
5856
- const v = evalVec3(inner.vector, env, "evaluateMesh.peelTranslate");
5888
+ let rot = IDENTITY_QUAT;
5889
+ let trans = [
5890
+ 0,
5891
+ 0,
5892
+ 0
5893
+ ];
5894
+ for (;;) if (inner.kind === "Translate") {
5895
+ const v = evalVec3(inner.vector, env, "evaluateMesh.peelRigid");
5857
5896
  if (!v.ok) break;
5858
- ox += v.value[0];
5859
- oy += v.value[1];
5860
- oz += v.value[2];
5897
+ const rv = quatRotate(rot, v.value);
5898
+ trans = [
5899
+ trans[0] + rv[0],
5900
+ trans[1] + rv[1],
5901
+ trans[2] + rv[2]
5902
+ ];
5861
5903
  inner = inner.target;
5862
- }
5904
+ } else if (inner.kind === "Rotate") {
5905
+ const p = evalRotateParams(inner, env);
5906
+ if (!p) break;
5907
+ const r = quatFromAxisAngle(p.axis, p.angle * Math.PI / 180);
5908
+ const newRot = quatMultiply(rot, r);
5909
+ const rotC = quatRotate(rot, p.center);
5910
+ const newRotC = quatRotate(newRot, p.center);
5911
+ trans = [
5912
+ trans[0] + rotC[0] - newRotC[0],
5913
+ trans[1] + rotC[1] - newRotC[1],
5914
+ trans[2] + rotC[2] - newRotC[2]
5915
+ ];
5916
+ rot = newRot;
5917
+ inner = inner.target;
5918
+ } else break;
5863
5919
  return {
5864
5920
  inner,
5865
- offset: [
5866
- ox,
5867
- oy,
5868
- oz
5869
- ]
5921
+ rot,
5922
+ trans
5870
5923
  };
5871
5924
  }
5872
- /** Shift every vertex of a mesh by `offset`; normals, UVs and triangles are unchanged. */
5873
- function translateMesh(m, offset) {
5874
- const [dx, dy, dz] = offset;
5875
- if (dx === 0 && dy === 0 && dz === 0) return m;
5876
- const vertices = new Float32Array(m.vertices.length);
5877
- for (let i = 0; i < m.vertices.length; i += 3) {
5878
- vertices[i] = (m.vertices[i] ?? 0) + dx;
5879
- vertices[i + 1] = (m.vertices[i + 1] ?? 0) + dy;
5880
- vertices[i + 2] = (m.vertices[i + 2] ?? 0) + dz;
5925
+ function isIdentityQuat(q) {
5926
+ return q[0] === 1 && q[1] === 0 && q[2] === 0 && q[3] === 0;
5927
+ }
5928
+ /**
5929
+ * Write `src` (flat xyz) rigidly transformed by quaternion (qw,qx,qy,qz) then
5930
+ * translation (tx,ty,tz) into `dst`. The quaternion-rotate math is inlined with
5931
+ * scalar locals no per-vertex array allocation — because this runs once per
5932
+ * mesh vertex/normal and helper-allocated vector math regressed the gridfinity
5933
+ * benchmark 17% (see `utils/vec3.ts`). Pass zero translation for normals.
5934
+ */
5935
+ function rotateXyzBuffer(dst, src, qw, qx, qy, qz, tx, ty, tz) {
5936
+ for (let i = 0; i < src.length; i += 3) {
5937
+ const vx = src[i] ?? 0;
5938
+ const vy = src[i + 1] ?? 0;
5939
+ const vz = src[i + 2] ?? 0;
5940
+ const cx = 2 * (qy * vz - qz * vy);
5941
+ const cy = 2 * (qz * vx - qx * vz);
5942
+ const cz = 2 * (qx * vy - qy * vx);
5943
+ dst[i] = vx + qw * cx + (qy * cz - qz * cy) + tx;
5944
+ dst[i + 1] = vy + qw * cy + (qz * cx - qx * cz) + ty;
5945
+ dst[i + 2] = vz + qw * cz + (qx * cy - qy * cx) + tz;
5881
5946
  }
5947
+ }
5948
+ /**
5949
+ * Apply a rigid motion to a mesh: vertices get the rotation then translation,
5950
+ * normals get the rotation only (a unit rotation preserves length, so no
5951
+ * renormalization). UVs and triangles are motion-invariant. A pure translation
5952
+ * (identity rotation) keeps the source normals array by reference and only
5953
+ * shifts vertices — the exact previous translation-only fast path.
5954
+ */
5955
+ function transformMeshRigid(m, rot, trans) {
5956
+ const [tx, ty, tz] = trans;
5957
+ const pureTranslation = isIdentityQuat(rot);
5958
+ if (pureTranslation && tx === 0 && ty === 0 && tz === 0) return m;
5959
+ const src = m.vertices;
5960
+ const vertices = new Float32Array(src.length);
5961
+ if (pureTranslation) {
5962
+ for (let i = 0; i < src.length; i += 3) {
5963
+ vertices[i] = (src[i] ?? 0) + tx;
5964
+ vertices[i + 1] = (src[i + 1] ?? 0) + ty;
5965
+ vertices[i + 2] = (src[i + 2] ?? 0) + tz;
5966
+ }
5967
+ return {
5968
+ ...m,
5969
+ vertices
5970
+ };
5971
+ }
5972
+ const [qw, qx, qy, qz] = rot;
5973
+ rotateXyzBuffer(vertices, src, qw, qx, qy, qz, tx, ty, tz);
5974
+ const sn = m.normals;
5975
+ const normals = new Float32Array(sn.length);
5976
+ rotateXyzBuffer(normals, sn, qw, qx, qy, qz, 0, 0, 0);
5882
5977
  return {
5883
5978
  ...m,
5884
- vertices
5979
+ vertices,
5980
+ normals
5885
5981
  };
5886
5982
  }
5887
5983
  /**
@@ -5889,8 +5985,8 @@ function translateMesh(m, offset) {
5889
5985
  * moved faces location-dependent hashes, so the inner mesh's `faceId`s describe
5890
5986
  * the *unplaced* shape; remap them to the placed faces (1:1 by iteration order,
5891
5987
  * since `locate` shares the source TShape) so face picking and metadata lookup
5892
- * resolve against the placed mesh. Origins are translation-invariant, so only
5893
- * `faceId` changes.
5988
+ * resolve against the placed mesh. Origins are boolean-lineage tags, invariant
5989
+ * under any rigid motion, so only `faceId` changes.
5894
5990
  *
5895
5991
  * Takes pre-captured hash arrays (not shapes): a bounded shape cache can evict
5896
5992
  * and dispose one shape while the other is being evaluated, so the caller reads
@@ -5986,7 +6082,7 @@ var Evaluator = class {
5986
6082
  }
5987
6083
  return ok(cached);
5988
6084
  }
5989
- const { inner, offset } = peelTranslate(node, env);
6085
+ const { inner, rot, trans } = peelRigid(node, env);
5990
6086
  if (inner !== node) {
5991
6087
  const innerMesh = this.evaluateMesh(inner, env, meshOpts);
5992
6088
  if (!innerMesh.ok) return innerMesh;
@@ -5996,7 +6092,7 @@ var Evaluator = class {
5996
6092
  const placedShape = this.evaluate(node, env);
5997
6093
  if (!placedShape.ok) return placedShape;
5998
6094
  const placedHashes = getFaces(placedShape.value).map(getHashCode);
5999
- const moved = translateMesh(innerMesh.value, offset);
6095
+ const moved = transformMeshRigid(innerMesh.value, rot, trans);
6000
6096
  const faceGroups = relocateFaceGroups(moved.faceGroups, innerHashes, placedHashes);
6001
6097
  const placedSet = new Set(placedHashes);
6002
6098
  if (faceGroups.every((g) => placedSet.has(g.faceId))) {
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_threadFns = require("./threadFns-C8bErjVC.cjs");
2
+ const require_threadFns = require("./threadFns-BqWHWpea.cjs");
3
3
  const require_loftFns = require("./loftFns-fpTmrIdd.cjs");
4
4
  exports.addChild = require_threadFns.addChild;
5
5
  exports.addJoint = require_threadFns.addJoint;
@@ -1,3 +1,3 @@
1
- import { A as setJointValue, B as findNode, C as cylindricalJoint, D as planarJoint, E as mechanismDOF, G as gridPattern, H as updateNode, I as addChild, J as createAssembly, K as linearPattern, L as collectShapes, M as sphericalJoint, O as prismaticJoint, R as countNodes, S as addJoint, T as jointTransform, U as walkAssembly, V as removeChild, W as circularPattern, _ as exportURDF, 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, 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-bCOhVQLb.js";
1
+ import { A as setJointValue, B as createAssemblyNode, C as cylindricalJoint, D as planarJoint, E as mechanismDOF, G as circularPattern, H as removeChild, J as exportAssemblySTEP, K as gridPattern, L as addChild, M as sphericalJoint, O as prismaticJoint, R as collectShapes, S as addJoint, T as jointTransform, U as updateNode, V as findNode, W as walkAssembly, Y as createAssembly, _ as exportURDF, 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, q as linearPattern, 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 countNodes } from "./threadFns-KbYZu7Q9.js";
2
2
  import { d as twistExtrude, l as supportExtrude, o as complexExtrude, u as sweep } from "./loftFns-CmShFQlv.js";
3
3
  export { addChild, addJoint, addStep, circularPattern, collectShapes, complexExtrude, countNodes, createAssembly, createAssemblyNode, createHistory, createRegistry, cylindricalJoint, exportAssemblySTEP, exportURDF, findNode, findStep, forwardKinematics, getShape as getHistoryShape, gridPattern, importURDF, inverseKinematics, jointTrajectory, jointTransform, jointsFromDH, linearPattern, mechanismDOF, modifyStep, planarJoint, prismaticJoint, registerOperation, registerShape, removeChild, replayFrom, replayHistory, revoluteJoint, setJointValue, setJointValues, sphericalJoint, stepCount, stepsFrom, supportExtrude, sweep, thread, twistExtrude, undoLast, updateNode, walkAssembly };
@@ -739,19 +739,38 @@ function resolveRefIn(ref, shape) {
739
739
  return resolveLineageRef(ref, rolesFor(ref, shape, /* @__PURE__ */ new Map()), shape);
740
740
  }
741
741
  /**
742
+ * A plain options object worth recursing into for nested refs — a literal `{}`
743
+ * (or null-prototype) that isn't a lineage ref, a shape handle, or a Date/Map/
744
+ * class instance (which `Object.entries` would silently flatten away).
745
+ */
746
+ function isPlainOptions(value) {
747
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
748
+ const proto = Object.getPrototypeOf(value);
749
+ if (proto !== Object.prototype && proto !== null) return false;
750
+ return !("wrapped" in value);
751
+ }
752
+ /**
742
753
  * Replace every lineage ref in an operation's params with the live entity it
743
- * resolves to in `shape`, recursing into arrays (multi-entity selections like a
744
- * fillet's edge list). Refs that can't resolve are left as-is. Role tables are
745
- * re-derived once per `origin` and reused across the whole params map. Lets a
746
- * replay engine pass stable entity selections that survive upstream edits.
754
+ * resolves to in `shape`, recursing into arrays (a fillet's edge list) and
755
+ * nested plain objects (a `{ selection: { edge } }` options bag). Refs that
756
+ * can't resolve are left as-is. Role tables are re-derived once per `origin` and
757
+ * reused across the whole params map. Lets a replay engine pass stable entity
758
+ * selections that survive upstream edits.
747
759
  */
748
760
  function resolveRefParams(params, shape) {
749
761
  const roleCache = /* @__PURE__ */ new Map();
750
762
  const resolveValue = (value) => {
763
+ if (isLineageRef(value)) {
764
+ const res = resolveLineageRef(value, rolesFor(value, shape, roleCache), shape);
765
+ return res.ok ? res.entity : value;
766
+ }
751
767
  if (Array.isArray(value)) return value.map(resolveValue);
752
- if (!isLineageRef(value)) return value;
753
- const res = resolveLineageRef(value, rolesFor(value, shape, roleCache), shape);
754
- return res.ok ? res.entity : value;
768
+ if (isPlainOptions(value)) {
769
+ const nested = {};
770
+ for (const [k, v] of Object.entries(value)) nested[k] = resolveValue(v);
771
+ return nested;
772
+ }
773
+ return value;
755
774
  };
756
775
  const out = { ...params };
757
776
  for (const [key, value] of Object.entries(params)) out[key] = resolveValue(value);
@@ -739,19 +739,38 @@ function resolveRefIn(ref, shape) {
739
739
  return resolveLineageRef(ref, rolesFor(ref, shape, /* @__PURE__ */ new Map()), shape);
740
740
  }
741
741
  /**
742
+ * A plain options object worth recursing into for nested refs — a literal `{}`
743
+ * (or null-prototype) that isn't a lineage ref, a shape handle, or a Date/Map/
744
+ * class instance (which `Object.entries` would silently flatten away).
745
+ */
746
+ function isPlainOptions(value) {
747
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
748
+ const proto = Object.getPrototypeOf(value);
749
+ if (proto !== Object.prototype && proto !== null) return false;
750
+ return !("wrapped" in value);
751
+ }
752
+ /**
742
753
  * Replace every lineage ref in an operation's params with the live entity it
743
- * resolves to in `shape`, recursing into arrays (multi-entity selections like a
744
- * fillet's edge list). Refs that can't resolve are left as-is. Role tables are
745
- * re-derived once per `origin` and reused across the whole params map. Lets a
746
- * replay engine pass stable entity selections that survive upstream edits.
754
+ * resolves to in `shape`, recursing into arrays (a fillet's edge list) and
755
+ * nested plain objects (a `{ selection: { edge } }` options bag). Refs that
756
+ * can't resolve are left as-is. Role tables are re-derived once per `origin` and
757
+ * reused across the whole params map. Lets a replay engine pass stable entity
758
+ * selections that survive upstream edits.
747
759
  */
748
760
  function resolveRefParams(params, shape) {
749
761
  const roleCache = /* @__PURE__ */ new Map();
750
762
  const resolveValue = (value) => {
763
+ if (isLineageRef(value)) {
764
+ const res = resolveLineageRef(value, rolesFor(value, shape, roleCache), shape);
765
+ return res.ok ? res.entity : value;
766
+ }
751
767
  if (Array.isArray(value)) return value.map(resolveValue);
752
- if (!isLineageRef(value)) return value;
753
- const res = resolveLineageRef(value, rolesFor(value, shape, roleCache), shape);
754
- return res.ok ? res.entity : value;
768
+ if (isPlainOptions(value)) {
769
+ const nested = {};
770
+ for (const [k, v] of Object.entries(value)) nested[k] = resolveValue(v);
771
+ return nested;
772
+ }
773
+ return value;
755
774
  };
756
775
  const out = { ...params };
757
776
  for (const [key, value] of Object.entries(params)) out[key] = resolveValue(value);
package/dist/shapeRef.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_refResolveFns = require("./refResolveFns-pRVyjy35.cjs");
2
+ const require_refResolveFns = require("./refResolveFns-BiEBAREW.cjs");
3
3
  exports.assignRoles = require_refResolveFns.assignRoles;
4
4
  exports.captureHint = require_refResolveFns.captureHint;
5
5
  exports.createDerivedFaceRef = require_refResolveFns.createDerivedFaceRef;
package/dist/shapeRef.js CHANGED
@@ -1,2 +1,2 @@
1
- import { _ as createRef, a as isVertexRef, b as defaultScorer, c as resolveRefParams, d as createVertexRef, f as resolveVertexRef, g as captureHint, h as assignRoles, i as isLineageRef, l as createDerivedFaceRef, m as resolveEdgeRef, n as isEdgeRef, o as resolveLineageRef, p as createEdgeRef, r as isFaceRef, s as resolveRefIn, t as isDerivedFaceRef, u as resolveDerivedFaceRef, v as resolveRef, y as updateRoles } from "./refResolveFns-hEOEcFZ8.js";
1
+ import { _ as createRef, a as isVertexRef, b as defaultScorer, c as resolveRefParams, d as createVertexRef, f as resolveVertexRef, g as captureHint, h as assignRoles, i as isLineageRef, l as createDerivedFaceRef, m as resolveEdgeRef, n as isEdgeRef, o as resolveLineageRef, p as createEdgeRef, r as isFaceRef, s as resolveRefIn, t as isDerivedFaceRef, u as resolveDerivedFaceRef, v as resolveRef, y as updateRoles } from "./refResolveFns-Ck6kJzLE.js";
2
2
  export { assignRoles, captureHint, createDerivedFaceRef, createEdgeRef, createRef, createVertexRef, defaultScorer, isDerivedFaceRef, isEdgeRef, isFaceRef, isLineageRef, isVertexRef, resolveDerivedFaceRef, resolveEdgeRef, resolveLineageRef, resolveRef, resolveRefIn, resolveRefParams, resolveVertexRef, updateRoles };
@@ -5,6 +5,7 @@ const require_faceFns = require("./faceFns-BV0GnTlt.cjs");
5
5
  const require_shapeFns = require("./shapeFns-ybo0INGL.cjs");
6
6
  const require_solidBuilders = require("./solidBuilders-DiUv_mD0.cjs");
7
7
  const require_loftFns = require("./loftFns-fpTmrIdd.cjs");
8
+ const require_refResolveFns = require("./refResolveFns-BiEBAREW.cjs");
8
9
  const require_primitiveFns = require("./primitiveFns-DLIWtQWA.cjs");
9
10
  //#region src/utils/uuid.ts
10
11
  /** Generate a v4-style UUID string using `crypto.getRandomValues`. */
@@ -1300,6 +1301,22 @@ function registerOperation(registry, type, fn) {
1300
1301
  return { operations: ops };
1301
1302
  }
1302
1303
  /**
1304
+ * Resolve any lineage refs in a step's params against its input shape, so an
1305
+ * operation re-targets the SAME entity (edge/face/vertex) after an upstream
1306
+ * parameter edit rebuilds the model. The stored step keeps its refs; resolution
1307
+ * happens fresh at replay.
1308
+ *
1309
+ * Only **single-input** steps auto-resolve: with multiple inputs we can't tell
1310
+ * which input a ref targets, and resolving against the wrong one could silently
1311
+ * return an entity from the wrong shape. Multi-input refs (and ref-free or non-3D
1312
+ * cases) are left raw for the operation to resolve against the input it chooses.
1313
+ */
1314
+ function resolveStepParams(params, inputs) {
1315
+ const [primary, second] = inputs;
1316
+ if (primary !== void 0 && second === void 0 && require_shapeTypes.isShape3D(primary)) return require_refResolveFns.resolveRefParams(params, primary);
1317
+ return { ...params };
1318
+ }
1319
+ /**
1303
1320
  * Replay an entire history from scratch using the given registry.
1304
1321
  *
1305
1322
  * All initial shapes (those not produced by any step) must already be in the
@@ -1323,7 +1340,7 @@ function replayHistory(history, registry) {
1323
1340
  inputs.push(shape);
1324
1341
  }
1325
1342
  try {
1326
- const output = fn(inputs, step.parameters);
1343
+ const output = fn(inputs, resolveStepParams(step.parameters, inputs));
1327
1344
  current = addStep(current, {
1328
1345
  id: step.id,
1329
1346
  type: step.type,
@@ -1367,7 +1384,7 @@ function replayFrom(history, stepId, registry) {
1367
1384
  inputs.push(shape);
1368
1385
  }
1369
1386
  try {
1370
- const output = fn(inputs, step.parameters);
1387
+ const output = fn(inputs, resolveStepParams(step.parameters, inputs));
1371
1388
  current = addStep(current, {
1372
1389
  id: step.id,
1373
1390
  type: step.type,
@@ -1682,6 +1699,12 @@ Object.defineProperty(exports, "quatFromTo", {
1682
1699
  return quatFromTo;
1683
1700
  }
1684
1701
  });
1702
+ Object.defineProperty(exports, "quatMultiply", {
1703
+ enumerable: true,
1704
+ get: function() {
1705
+ return quatMultiply;
1706
+ }
1707
+ });
1685
1708
  Object.defineProperty(exports, "quatRotate", {
1686
1709
  enumerable: true,
1687
1710
  get: function() {
@@ -1,10 +1,11 @@
1
- import { B as createKernelHandle, R as DisposalScope, Y as _usingCtx, Z as getKernel, t as castShape } from "./shapeTypes-Dpu6mD3m.js";
1
+ import { B as createKernelHandle, R as DisposalScope, Y as _usingCtx, Z as getKernel, h as isShape3D, t as castShape } from "./shapeTypes-Dpu6mD3m.js";
2
2
  import { A as ok, T as isOk, b as err, d as validationError, n as computationError, r as ioError, t as BrepErrorCode } from "./errors-DNWJsfVU.js";
3
3
  import { d as vecNormalize, s as vecIsZero } from "./vecOps-SKPRvPH-.js";
4
4
  import { y as fromBREP } from "./faceFns-B86WGqDg.js";
5
5
  import { s as toBREP } from "./shapeFns-DrZOVwHX.js";
6
6
  import { h as fuseAll } from "./solidBuilders-C32wXQWG.js";
7
7
  import { t as loft } from "./loftFns-CmShFQlv.js";
8
+ import { c as resolveRefParams } from "./refResolveFns-Ck6kJzLE.js";
8
9
  import { E as wire, h as line } from "./primitiveFns-CBrOyk5S.js";
9
10
  //#region src/utils/uuid.ts
10
11
  /** Generate a v4-style UUID string using `crypto.getRandomValues`. */
@@ -1300,6 +1301,22 @@ function registerOperation(registry, type, fn) {
1300
1301
  return { operations: ops };
1301
1302
  }
1302
1303
  /**
1304
+ * Resolve any lineage refs in a step's params against its input shape, so an
1305
+ * operation re-targets the SAME entity (edge/face/vertex) after an upstream
1306
+ * parameter edit rebuilds the model. The stored step keeps its refs; resolution
1307
+ * happens fresh at replay.
1308
+ *
1309
+ * Only **single-input** steps auto-resolve: with multiple inputs we can't tell
1310
+ * which input a ref targets, and resolving against the wrong one could silently
1311
+ * return an entity from the wrong shape. Multi-input refs (and ref-free or non-3D
1312
+ * cases) are left raw for the operation to resolve against the input it chooses.
1313
+ */
1314
+ function resolveStepParams(params, inputs) {
1315
+ const [primary, second] = inputs;
1316
+ if (primary !== void 0 && second === void 0 && isShape3D(primary)) return resolveRefParams(params, primary);
1317
+ return { ...params };
1318
+ }
1319
+ /**
1303
1320
  * Replay an entire history from scratch using the given registry.
1304
1321
  *
1305
1322
  * All initial shapes (those not produced by any step) must already be in the
@@ -1323,7 +1340,7 @@ function replayHistory(history, registry) {
1323
1340
  inputs.push(shape);
1324
1341
  }
1325
1342
  try {
1326
- const output = fn(inputs, step.parameters);
1343
+ const output = fn(inputs, resolveStepParams(step.parameters, inputs));
1327
1344
  current = addStep(current, {
1328
1345
  id: step.id,
1329
1346
  type: step.type,
@@ -1367,7 +1384,7 @@ function replayFrom(history, stepId, registry) {
1367
1384
  inputs.push(shape);
1368
1385
  }
1369
1386
  try {
1370
- const output = fn(inputs, step.parameters);
1387
+ const output = fn(inputs, resolveStepParams(step.parameters, inputs));
1371
1388
  current = addStep(current, {
1372
1389
  id: step.id,
1373
1390
  type: step.type,
@@ -1496,4 +1513,4 @@ function thread(options) {
1496
1513
  }
1497
1514
  }
1498
1515
  //#endregion
1499
- export { setJointValue as A, findNode as B, cylindricalJoint as C, planarJoint as D, mechanismDOF as E, quatRotate as F, gridPattern as G, updateNode as H, addChild as I, createAssembly as J, linearPattern as K, collectShapes as L, sphericalJoint as M, quatFromAxisAngle as N, prismaticJoint as O, quatFromTo as P, countNodes as R, addJoint as S, jointTransform as T, walkAssembly as U, removeChild as V, circularPattern as W, exportURDF as _, deserializeHistory as a, inverseKinematics as b, modifyStep as c, replayFrom as d, replayHistory as f, undoLast as g, stepsFrom as h, createRegistry as i, setJointValues as j, revoluteJoint as k, registerOperation as l, stepCount as m, addStep as n, findStep as o, serializeHistory as p, exportAssemblySTEP as q, createHistory as r, getShape as s, thread as t, registerShape as u, importURDF as v, forwardKinematics as w, jointTrajectory as x, jointsFromDH as y, createAssemblyNode as z };
1516
+ export { setJointValue as A, createAssemblyNode as B, cylindricalJoint as C, planarJoint as D, mechanismDOF as E, quatMultiply as F, circularPattern as G, removeChild as H, quatRotate as I, exportAssemblySTEP as J, gridPattern as K, addChild as L, sphericalJoint as M, quatFromAxisAngle as N, prismaticJoint as O, quatFromTo as P, collectShapes as R, addJoint as S, jointTransform as T, updateNode as U, findNode as V, walkAssembly as W, createAssembly as Y, exportURDF as _, deserializeHistory as a, inverseKinematics as b, modifyStep as c, replayFrom as d, replayHistory as f, undoLast as g, stepsFrom as h, createRegistry as i, setJointValues as j, revoluteJoint as k, registerOperation as l, stepCount as m, addStep as n, findStep as o, serializeHistory as p, linearPattern as q, createHistory as r, getShape as s, thread as t, registerShape as u, importURDF as v, forwardKinematics as w, jointTrajectory as x, jointsFromDH as y, countNodes as z };
@@ -45,9 +45,10 @@ export declare function resolveLineageRef(ref: LineageRef, roles: RoleTable, sha
45
45
  export declare function resolveRefIn(ref: LineageRef, shape: Shape3D): LineageResolution;
46
46
  /**
47
47
  * Replace every lineage ref in an operation's params with the live entity it
48
- * resolves to in `shape`, recursing into arrays (multi-entity selections like a
49
- * fillet's edge list). Refs that can't resolve are left as-is. Role tables are
50
- * re-derived once per `origin` and reused across the whole params map. Lets a
51
- * replay engine pass stable entity selections that survive upstream edits.
48
+ * resolves to in `shape`, recursing into arrays (a fillet's edge list) and
49
+ * nested plain objects (a `{ selection: { edge } }` options bag). Refs that
50
+ * can't resolve are left as-is. Role tables are re-derived once per `origin` and
51
+ * reused across the whole params map. Lets a replay engine pass stable entity
52
+ * selections that survive upstream edits.
52
53
  */
53
54
  export declare function resolveRefParams(params: Readonly<Record<string, unknown>>, shape: Shape3D): Record<string, unknown>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brepjs",
3
- "version": "18.115.1",
3
+ "version": "18.116.1",
4
4
  "description": "Web CAD library with pluggable geometry kernel",
5
5
  "keywords": [
6
6
  "cad",