brepjs 18.102.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
@@ -5471,6 +5471,19 @@ function compound$1(children) {
5471
5471
  freeParams: depsOf(...children)
5472
5472
  };
5473
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
+ }
5474
5487
  //#endregion
5475
5488
  //#region src/csg/types.ts
5476
5489
  function outputKindOf(node) {
@@ -5495,6 +5508,7 @@ function outputKindOf(node) {
5495
5508
  case "Scale":
5496
5509
  case "Mirror": return outputKindOf(node.target);
5497
5510
  case "Compound": return "Compound";
5511
+ case "Instance": return node.fuse ? "Solid" : "Compound";
5498
5512
  }
5499
5513
  }
5500
5514
  //#endregion
@@ -5728,6 +5742,13 @@ function evalEmpty() {
5728
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"));
5729
5743
  }
5730
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
5731
5752
  //#region src/csg/evaluate.ts
5732
5753
  function dispatch(node, ctx) {
5733
5754
  switch (node.kind) {
@@ -5751,6 +5772,7 @@ function dispatch(node, ctx) {
5751
5772
  case "Scale": return evalScale(node, ctx);
5752
5773
  case "Mirror": return evalMirror(node, ctx);
5753
5774
  case "Compound": return evalCompound(node, ctx);
5775
+ case "Instance": return evalInstance(node, ctx);
5754
5776
  }
5755
5777
  }
5756
5778
  function hashExprValue(h, v) {
@@ -6085,6 +6107,12 @@ function nodeToJson(n) {
6085
6107
  kind: "Compound",
6086
6108
  children: n.children.map(nodeToJson)
6087
6109
  };
6110
+ if (n.kind === "Instance") return {
6111
+ kind: "Instance",
6112
+ source: nodeToJson(n.source),
6113
+ placements: n.placements,
6114
+ fuse: n.fuse
6115
+ };
6088
6116
  return primitiveToJson(n) ?? booleanToJson(n) ?? transformToJson(n);
6089
6117
  }
6090
6118
  function bad(msg) {
@@ -6225,6 +6253,7 @@ function readNode(j) {
6225
6253
  case "Scale":
6226
6254
  case "Mirror": return readTransform(kind, j);
6227
6255
  case "Compound": return readCompound(j);
6256
+ case "Instance": return readInstance(j);
6228
6257
  default: return bad(`unknown node kind: ${String(kind)}`);
6229
6258
  }
6230
6259
  }
@@ -6383,6 +6412,16 @@ function readCompound(j) {
6383
6412
  const children = readNodeArray(j["children"], "Compound.children");
6384
6413
  return children.ok ? require_errors.ok(compound$1(children.value)) : children;
6385
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
+ }
6386
6425
  //#endregion
6387
6426
  //#region src/csg/optimize.ts
6388
6427
  function optimize(node) {
@@ -6479,6 +6518,7 @@ function optimizeNode(n) {
6479
6518
  at: n.at ? foldExpr(n.at) : void 0
6480
6519
  });
6481
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);
6482
6522
  }
6483
6523
  }
6484
6524
  function optimizeFuse(a, b, tol) {
@@ -6568,6 +6608,7 @@ function rebuildChildren(n, pred, repl) {
6568
6608
  at: n.at
6569
6609
  });
6570
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);
6571
6612
  }
6572
6613
  }
6573
6614
  function forEachNode(root, fn) {
@@ -6596,6 +6637,7 @@ function childrenOf(n) {
6596
6637
  case "Scale":
6597
6638
  case "Mirror": return [n.target];
6598
6639
  case "Compound": return n.children;
6640
+ case "Instance": return [n.source];
6599
6641
  }
6600
6642
  }
6601
6643
  function nodeCount(root) {
@@ -6632,6 +6674,7 @@ var csg_exports = /* @__PURE__ */ require_textBlueprints.__exportAll({
6632
6674
  fromJSON: () => fromJSON,
6633
6675
  fuse: () => fuse$1,
6634
6676
  fuseAll: () => fuseAll$1,
6677
+ instance: () => instance$1,
6635
6678
  intersect: () => intersect$1,
6636
6679
  line: () => line$1,
6637
6680
  mirror: () => mirror$1,
package/dist/brepjs.js CHANGED
@@ -5474,6 +5474,19 @@ function compound$1(children) {
5474
5474
  freeParams: depsOf(...children)
5475
5475
  };
5476
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
+ }
5477
5490
  //#endregion
5478
5491
  //#region src/csg/types.ts
5479
5492
  function outputKindOf(node) {
@@ -5498,6 +5511,7 @@ function outputKindOf(node) {
5498
5511
  case "Scale":
5499
5512
  case "Mirror": return outputKindOf(node.target);
5500
5513
  case "Compound": return "Compound";
5514
+ case "Instance": return node.fuse ? "Solid" : "Compound";
5501
5515
  }
5502
5516
  }
5503
5517
  //#endregion
@@ -5731,6 +5745,13 @@ function evalEmpty() {
5731
5745
  return err(computationError(BrepErrorCode.NULL_SHAPE_INPUT, "Empty: cannot materialize an Empty node directly — only valid as boolean/transform operand"));
5732
5746
  }
5733
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
5734
5755
  //#region src/csg/evaluate.ts
5735
5756
  function dispatch(node, ctx) {
5736
5757
  switch (node.kind) {
@@ -5754,6 +5775,7 @@ function dispatch(node, ctx) {
5754
5775
  case "Scale": return evalScale(node, ctx);
5755
5776
  case "Mirror": return evalMirror(node, ctx);
5756
5777
  case "Compound": return evalCompound(node, ctx);
5778
+ case "Instance": return evalInstance(node, ctx);
5757
5779
  }
5758
5780
  }
5759
5781
  function hashExprValue(h, v) {
@@ -6088,6 +6110,12 @@ function nodeToJson(n) {
6088
6110
  kind: "Compound",
6089
6111
  children: n.children.map(nodeToJson)
6090
6112
  };
6113
+ if (n.kind === "Instance") return {
6114
+ kind: "Instance",
6115
+ source: nodeToJson(n.source),
6116
+ placements: n.placements,
6117
+ fuse: n.fuse
6118
+ };
6091
6119
  return primitiveToJson(n) ?? booleanToJson(n) ?? transformToJson(n);
6092
6120
  }
6093
6121
  function bad(msg) {
@@ -6228,6 +6256,7 @@ function readNode(j) {
6228
6256
  case "Scale":
6229
6257
  case "Mirror": return readTransform(kind, j);
6230
6258
  case "Compound": return readCompound(j);
6259
+ case "Instance": return readInstance(j);
6231
6260
  default: return bad(`unknown node kind: ${String(kind)}`);
6232
6261
  }
6233
6262
  }
@@ -6386,6 +6415,16 @@ function readCompound(j) {
6386
6415
  const children = readNodeArray(j["children"], "Compound.children");
6387
6416
  return children.ok ? ok(compound$1(children.value)) : children;
6388
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
+ }
6389
6428
  //#endregion
6390
6429
  //#region src/csg/optimize.ts
6391
6430
  function optimize(node) {
@@ -6482,6 +6521,7 @@ function optimizeNode(n) {
6482
6521
  at: n.at ? foldExpr(n.at) : void 0
6483
6522
  });
6484
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);
6485
6525
  }
6486
6526
  }
6487
6527
  function optimizeFuse(a, b, tol) {
@@ -6571,6 +6611,7 @@ function rebuildChildren(n, pred, repl) {
6571
6611
  at: n.at
6572
6612
  });
6573
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);
6574
6615
  }
6575
6616
  }
6576
6617
  function forEachNode(root, fn) {
@@ -6599,6 +6640,7 @@ function childrenOf(n) {
6599
6640
  case "Scale":
6600
6641
  case "Mirror": return [n.target];
6601
6642
  case "Compound": return n.children;
6643
+ case "Instance": return [n.source];
6602
6644
  }
6603
6645
  }
6604
6646
  function nodeCount(root) {
@@ -6635,6 +6677,7 @@ var csg_exports = /* @__PURE__ */ __exportAll({
6635
6677
  fromJSON: () => fromJSON,
6636
6678
  fuse: () => fuse$1,
6637
6679
  fuseAll: () => fuseAll$1,
6680
+ instance: () => instance$1,
6638
6681
  intersect: () => intersect$1,
6639
6682
  line: () => line$1,
6640
6683
  mirror: () => mirror$1,
@@ -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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brepjs",
3
- "version": "18.102.0",
3
+ "version": "18.103.0",
4
4
  "description": "Web CAD library with pluggable geometry kernel",
5
5
  "keywords": [
6
6
  "cad",