brepjs 18.115.0 → 18.115.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
@@ -5837,6 +5837,76 @@ 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
+ /**
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`).
5846
+ */
5847
+ function peelTranslate(node, env) {
5848
+ 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");
5854
+ if (!v.ok) break;
5855
+ ox += v.value[0];
5856
+ oy += v.value[1];
5857
+ oz += v.value[2];
5858
+ inner = inner.target;
5859
+ }
5860
+ return {
5861
+ inner,
5862
+ offset: [
5863
+ ox,
5864
+ oy,
5865
+ oz
5866
+ ]
5867
+ };
5868
+ }
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;
5878
+ }
5879
+ return {
5880
+ ...m,
5881
+ vertices
5882
+ };
5883
+ }
5884
+ /**
5885
+ * Re-key a reused inner mesh's face groups onto the PLACED shape. `locate` gives
5886
+ * moved faces location-dependent hashes, so the inner mesh's `faceId`s describe
5887
+ * the *unplaced* shape; remap them to the placed faces (1:1 by iteration order,
5888
+ * 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.
5891
+ *
5892
+ * Takes pre-captured hash arrays (not shapes): a bounded shape cache can evict
5893
+ * and dispose one shape while the other is being evaluated, so the caller reads
5894
+ * each shape's face hashes immediately after evaluating it, never holding a
5895
+ * shape handle across the next `evaluate`.
5896
+ */
5897
+ function relocateFaceGroups(faceGroups, innerHashes, placedHashes) {
5898
+ const remap = /* @__PURE__ */ new Map();
5899
+ const n = Math.min(innerHashes.length, placedHashes.length);
5900
+ for (let i = 0; i < n; i++) {
5901
+ const a = innerHashes[i];
5902
+ const b = placedHashes[i];
5903
+ if (a !== void 0 && b !== void 0) remap.set(a, b);
5904
+ }
5905
+ return faceGroups.map((g) => ({
5906
+ ...g,
5907
+ faceId: remap.get(g.faceId) ?? g.faceId
5908
+ }));
5909
+ }
5840
5910
  var Evaluator = class {
5841
5911
  cache = /* @__PURE__ */ new Map();
5842
5912
  refCounts = /* @__PURE__ */ new Map();
@@ -5913,6 +5983,29 @@ var Evaluator = class {
5913
5983
  }
5914
5984
  return require_errors.ok(cached);
5915
5985
  }
5986
+ const { inner, offset } = peelTranslate(node, env);
5987
+ if (inner !== node) {
5988
+ const innerMesh = this.evaluateMesh(inner, env, meshOpts);
5989
+ if (!innerMesh.ok) return innerMesh;
5990
+ const innerShape = this.evaluate(inner, env);
5991
+ if (!innerShape.ok) return innerShape;
5992
+ const innerHashes = require_topologyQueryFns.getFaces(innerShape.value).map(require_shapeFns.getHashCode);
5993
+ const placedShape = this.evaluate(node, env);
5994
+ if (!placedShape.ok) return placedShape;
5995
+ const placedHashes = require_topologyQueryFns.getFaces(placedShape.value).map(require_shapeFns.getHashCode);
5996
+ const moved = translateMesh(innerMesh.value, offset);
5997
+ const faceGroups = relocateFaceGroups(moved.faceGroups, innerHashes, placedHashes);
5998
+ const placedSet = new Set(placedHashes);
5999
+ if (faceGroups.every((g) => placedSet.has(g.faceId))) {
6000
+ const placed = {
6001
+ ...moved,
6002
+ faceGroups
6003
+ };
6004
+ this.meshCache.set(meshKey, placed);
6005
+ if (this.maxMeshCacheEntries !== void 0) this.trimMeshCache(this.maxMeshCacheEntries);
6006
+ return require_errors.ok(placed);
6007
+ }
6008
+ }
5916
6009
  }
5917
6010
  const shape = this.evaluate(node, env);
5918
6011
  if (!shape.ok) return shape;
package/dist/brepjs.js CHANGED
@@ -5840,6 +5840,76 @@ 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
+ /**
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`).
5849
+ */
5850
+ function peelTranslate(node, env) {
5851
+ 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");
5857
+ if (!v.ok) break;
5858
+ ox += v.value[0];
5859
+ oy += v.value[1];
5860
+ oz += v.value[2];
5861
+ inner = inner.target;
5862
+ }
5863
+ return {
5864
+ inner,
5865
+ offset: [
5866
+ ox,
5867
+ oy,
5868
+ oz
5869
+ ]
5870
+ };
5871
+ }
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;
5881
+ }
5882
+ return {
5883
+ ...m,
5884
+ vertices
5885
+ };
5886
+ }
5887
+ /**
5888
+ * Re-key a reused inner mesh's face groups onto the PLACED shape. `locate` gives
5889
+ * moved faces location-dependent hashes, so the inner mesh's `faceId`s describe
5890
+ * the *unplaced* shape; remap them to the placed faces (1:1 by iteration order,
5891
+ * 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.
5894
+ *
5895
+ * Takes pre-captured hash arrays (not shapes): a bounded shape cache can evict
5896
+ * and dispose one shape while the other is being evaluated, so the caller reads
5897
+ * each shape's face hashes immediately after evaluating it, never holding a
5898
+ * shape handle across the next `evaluate`.
5899
+ */
5900
+ function relocateFaceGroups(faceGroups, innerHashes, placedHashes) {
5901
+ const remap = /* @__PURE__ */ new Map();
5902
+ const n = Math.min(innerHashes.length, placedHashes.length);
5903
+ for (let i = 0; i < n; i++) {
5904
+ const a = innerHashes[i];
5905
+ const b = placedHashes[i];
5906
+ if (a !== void 0 && b !== void 0) remap.set(a, b);
5907
+ }
5908
+ return faceGroups.map((g) => ({
5909
+ ...g,
5910
+ faceId: remap.get(g.faceId) ?? g.faceId
5911
+ }));
5912
+ }
5843
5913
  var Evaluator = class {
5844
5914
  cache = /* @__PURE__ */ new Map();
5845
5915
  refCounts = /* @__PURE__ */ new Map();
@@ -5916,6 +5986,29 @@ var Evaluator = class {
5916
5986
  }
5917
5987
  return ok(cached);
5918
5988
  }
5989
+ const { inner, offset } = peelTranslate(node, env);
5990
+ if (inner !== node) {
5991
+ const innerMesh = this.evaluateMesh(inner, env, meshOpts);
5992
+ if (!innerMesh.ok) return innerMesh;
5993
+ const innerShape = this.evaluate(inner, env);
5994
+ if (!innerShape.ok) return innerShape;
5995
+ const innerHashes = getFaces(innerShape.value).map(getHashCode);
5996
+ const placedShape = this.evaluate(node, env);
5997
+ if (!placedShape.ok) return placedShape;
5998
+ const placedHashes = getFaces(placedShape.value).map(getHashCode);
5999
+ const moved = translateMesh(innerMesh.value, offset);
6000
+ const faceGroups = relocateFaceGroups(moved.faceGroups, innerHashes, placedHashes);
6001
+ const placedSet = new Set(placedHashes);
6002
+ if (faceGroups.every((g) => placedSet.has(g.faceId))) {
6003
+ const placed = {
6004
+ ...moved,
6005
+ faceGroups
6006
+ };
6007
+ this.meshCache.set(meshKey, placed);
6008
+ if (this.maxMeshCacheEntries !== void 0) this.trimMeshCache(this.maxMeshCacheEntries);
6009
+ return ok(placed);
6010
+ }
6011
+ }
5919
6012
  }
5920
6013
  const shape = this.evaluate(node, env);
5921
6014
  if (!shape.ok) return shape;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brepjs",
3
- "version": "18.115.0",
3
+ "version": "18.115.1",
4
4
  "description": "Web CAD library with pluggable geometry kernel",
5
5
  "keywords": [
6
6
  "cad",