@code3d/core 0.0.1-alpha.7 → 0.0.1-alpha.9

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.
@@ -1109,9 +1109,9 @@ function sketchCurveClosestParameter(curve, position2) {
1109
1109
  );
1110
1110
  if (curve.kind === "circle") return sketchPositiveAngle(angle) / tau;
1111
1111
  const sign3 = Math.sign(curve.sweep), sweep = Math.abs(curve.sweep);
1112
- const distance3 = sketchPositiveAngle(sign3 * (angle - curve.start));
1113
- if (distance3 <= sweep) return sweep ? distance3 / sweep : 0;
1114
- return distance3 - sweep < tau - distance3 ? 1 : 0;
1112
+ const distance4 = sketchPositiveAngle(sign3 * (angle - curve.start));
1113
+ if (distance4 <= sweep) return sweep ? distance4 / sweep : 0;
1114
+ return distance4 - sweep < tau - distance4 ? 1 : 0;
1115
1115
  }
1116
1116
  __name(sketchCurveClosestParameter, "sketchCurveClosestParameter");
1117
1117
  function sketchCurveBounds(curve) {
@@ -2433,15 +2433,15 @@ function endpointPolicy(problem, point3, scope) {
2433
2433
  controls.delete(point3);
2434
2434
  if (controls.size) return { kind: "endpoint", anchors: [...controls] };
2435
2435
  const graph = neighbors(problem);
2436
- const distance3 = /* @__PURE__ */ new Map([[point3, 0]]);
2437
- for (const [p, d] of distance3)
2436
+ const distance4 = /* @__PURE__ */ new Map([[point3, 0]]);
2437
+ for (const [p, d] of distance4)
2438
2438
  for (const next of graph[p])
2439
- if (scope.has(next) && !distance3.has(next)) distance3.set(next, d + 1);
2439
+ if (scope.has(next) && !distance4.has(next)) distance4.set(next, d + 1);
2440
2440
  let farthest = -1, max12 = 0;
2441
2441
  for (const p of scope)
2442
- if ((distance3.get(p) ?? 0) > max12) {
2442
+ if ((distance4.get(p) ?? 0) > max12) {
2443
2443
  farthest = p;
2444
- max12 = distance3.get(p);
2444
+ max12 = distance4.get(p);
2445
2445
  }
2446
2446
  return { kind: "endpoint", anchors: farthest < 0 ? [] : [farthest] };
2447
2447
  }
@@ -3112,22 +3112,22 @@ function nearestPoint(point3, geometry) {
3112
3112
  case "ellipse": {
3113
3113
  const y = cross2(geometry.normal, geometry.major);
3114
3114
  const x0 = dot(delta, geometry.major), y0 = dot(delta, y);
3115
- const distance3 = /* @__PURE__ */ __name((angle2) => (geometry.radius * Math.cos(angle2) - x0) ** 2 + (geometry.minorRadius * Math.sin(angle2) - y0) ** 2, "distance");
3115
+ const distance4 = /* @__PURE__ */ __name((angle2) => (geometry.radius * Math.cos(angle2) - x0) ** 2 + (geometry.minorRadius * Math.sin(angle2) - y0) ** 2, "distance");
3116
3116
  const step = Math.PI / 16, candidates2 = [];
3117
3117
  for (let i = 0; i < 32; i++) {
3118
3118
  const angle2 = i * step;
3119
- if (distance3(angle2) > distance3(angle2 - step) || distance3(angle2) > distance3(angle2 + step))
3119
+ if (distance4(angle2) > distance4(angle2 - step) || distance4(angle2) > distance4(angle2 + step))
3120
3120
  continue;
3121
3121
  let lower = angle2 - step, upper = angle2 + step;
3122
3122
  for (let iteration = 0; iteration < 64; iteration++) {
3123
3123
  const a = lower + (upper - lower) * 0.3819660112501051, b = upper - (upper - lower) * 0.3819660112501051;
3124
- if (distance3(a) < distance3(b)) upper = b;
3124
+ if (distance4(a) < distance4(b)) upper = b;
3125
3125
  else lower = a;
3126
3126
  }
3127
3127
  candidates2.push((lower + upper) / 2);
3128
3128
  }
3129
3129
  const angle = candidates2.reduce(
3130
- (best, angle2) => distance3(angle2) < distance3(best) ? angle2 : best,
3130
+ (best, angle2) => distance4(angle2) < distance4(best) ? angle2 : best,
3131
3131
  candidates2[0]
3132
3132
  );
3133
3133
  return addVectors(
@@ -3300,8 +3300,8 @@ function filletEdges(shape, source, radius, edgeIds) {
3300
3300
  return modifyEdges("fillet", shape, source, radius, edgeIds);
3301
3301
  }
3302
3302
  __name(filletEdges, "filletEdges");
3303
- function chamferEdges(shape, source, distance3, edgeIds) {
3304
- return modifyEdges("chamfer", shape, source, distance3, edgeIds);
3303
+ function chamferEdges(shape, source, distance4, edgeIds) {
3304
+ return modifyEdges("chamfer", shape, source, distance4, edgeIds);
3305
3305
  }
3306
3306
  __name(chamferEdges, "chamferEdges");
3307
3307
  function booleanWithTopology(left, right, operation) {
@@ -3528,7 +3528,7 @@ function modifyEdges(kind, shape, source, amount, requestedIds) {
3528
3528
  return {
3529
3529
  shape: result,
3530
3530
  topology: transferShapeTopology(
3531
- [{ shape, topology: source, index: 1 }],
3531
+ [{ shape, topology: source, namespace: "preserve" }],
3532
3532
  result,
3533
3533
  builder
3534
3534
  ),
@@ -3603,15 +3603,18 @@ function transferShapeTopology(inputs, output, history) {
3603
3603
  const transfer = /* @__PURE__ */ __name((kind) => {
3604
3604
  const originals = [];
3605
3605
  const results = topologyShapes(output, kind);
3606
+ let nextId = 1;
3606
3607
  try {
3607
3608
  const candidates2 = inputs.flatMap((input, inputIndex) => {
3608
3609
  const shapes = topologyShapes(input.shape, kind);
3609
3610
  originals.push(shapes);
3610
3611
  const source = input.topology[topologyMetadata[kind].plural];
3611
3612
  assertTopologyLength(kind, shapes, source);
3613
+ if (input.namespace === "preserve")
3614
+ nextId = Math.max(nextId, source.nextId);
3612
3615
  return shapes.map((shape, index) => {
3613
3616
  const id = source.ids[index];
3614
- const inherited2 = input.index !== void 0 ? inheritedTopologyId(input.index, id) : typeof id === "number" ? void 0 : id;
3617
+ const inherited2 = typeof input.namespace === "number" ? inheritedTopologyId(input.namespace, id) : input.namespace === "preserve" || typeof id !== "number" ? id : void 0;
3615
3618
  const unchanged = matchingOutputShapes(shape.wrapped, results);
3616
3619
  if (unchanged.length) return { id: inherited2, matches: unchanged };
3617
3620
  if (typeof history !== "function") {
@@ -3648,10 +3651,8 @@ function transferShapeTopology(inputs, output, history) {
3648
3651
  inherited.set(index, candidate.id);
3649
3652
  }
3650
3653
  }
3651
- let nextId = 1;
3652
- return stableTopology(
3653
- results.map((_, index) => inherited.get(index) ?? nextId++)
3654
- );
3654
+ const ids = results.map((_, index) => inherited.get(index) ?? nextId++);
3655
+ return stableTopology(ids, nextId);
3655
3656
  } finally {
3656
3657
  originals.forEach(deleteShapes);
3657
3658
  deleteShapes(results);
@@ -3710,11 +3711,14 @@ function stableGroupIds(kind, shapes, topology, kernelIds) {
3710
3711
  }
3711
3712
  __name(stableGroupIds, "stableGroupIds");
3712
3713
  function initialTopology(shapes) {
3713
- return stableTopology(shapes.map((_, index) => index + 1));
3714
+ return stableTopology(
3715
+ shapes.map((_, index) => index + 1),
3716
+ shapes.length + 1
3717
+ );
3714
3718
  }
3715
3719
  __name(initialTopology, "initialTopology");
3716
- function stableTopology(ids) {
3717
- return { ids };
3720
+ function stableTopology(ids, nextId) {
3721
+ return { ids, nextId };
3718
3722
  }
3719
3723
  __name(stableTopology, "stableTopology");
3720
3724
  function shapeTopology(vertices, edges, surfaces) {
@@ -3815,7 +3819,11 @@ function loftWithTopology(sections, spine, ruled) {
3815
3819
  ruled,
3816
3820
  holes.map((wires) => wires[0])
3817
3821
  );
3818
- return booleanWithTopology(outer, inner, "cut");
3822
+ return booleanWithTopology(
3823
+ { ...outer, namespace: "intermediate" },
3824
+ { ...inner, namespace: "intermediate" },
3825
+ "cut"
3826
+ );
3819
3827
  } finally {
3820
3828
  inner?.shape.delete();
3821
3829
  outer?.shape.delete();
@@ -6053,7 +6061,7 @@ function openShell(shape, topology, thickness, removedIds, sourceVolume) {
6053
6061
  return {
6054
6062
  shape: result,
6055
6063
  topology: transferShapeTopology(
6056
- [{ shape, topology, index: 1 }],
6064
+ [{ shape, topology, namespace: "preserve" }],
6057
6065
  result,
6058
6066
  builder
6059
6067
  )
@@ -6129,7 +6137,7 @@ function closedShell(shape, topology, thickness, sourceVolume) {
6129
6137
  return {
6130
6138
  shape: result,
6131
6139
  topology: transferShapeTopology(
6132
- [{ shape, topology, index: 1 }],
6140
+ [{ shape, topology, namespace: "preserve" }],
6133
6141
  result,
6134
6142
  cut2
6135
6143
  )
@@ -20281,6 +20289,7 @@ function inspectSurface(face) {
20281
20289
  __name(inspectSurface, "inspectSurface");
20282
20290
 
20283
20291
  // packages/core/src/library/runtime.ts
20292
+ var observeDistance;
20284
20293
  function isAxisSelection(pivot2) {
20285
20294
  return pivot2.kind === "axisLine" || pivot2.kind === "axisEdge";
20286
20295
  }
@@ -20296,10 +20305,16 @@ __name(mapSelectionReference, "mapSelectionReference");
20296
20305
  var activeRelate;
20297
20306
  var valueTraces = /* @__PURE__ */ new WeakMap();
20298
20307
  var operationTraces = /* @__PURE__ */ new WeakMap();
20299
- function beginModelEvaluation(checkCancelled) {
20308
+ function beginModelEvaluation(checkCancelled, distanceObserver) {
20300
20309
  valueTraces = /* @__PURE__ */ new WeakMap();
20301
20310
  operationTraces = /* @__PURE__ */ new WeakMap();
20302
- return beginKernelOperationEvaluation(checkCancelled);
20311
+ const previous = observeDistance;
20312
+ observeDistance = distanceObserver;
20313
+ const finish = beginKernelOperationEvaluation(checkCancelled);
20314
+ return () => {
20315
+ observeDistance = previous;
20316
+ finish();
20317
+ };
20303
20318
  }
20304
20319
  __name(beginModelEvaluation, "beginModelEvaluation");
20305
20320
  function valueTrace(value) {
@@ -20727,10 +20742,10 @@ function rotationPointReference(point3) {
20727
20742
  return reference;
20728
20743
  }
20729
20744
  __name(rotationPointReference, "rotationPointReference");
20730
- function rotationAxisReference(axis) {
20745
+ function straightAxisReference(axis, operation = "axisLine()") {
20731
20746
  const reference = anchorReference(axis);
20732
20747
  if (reference.kind !== "line")
20733
- throw new Error("axisLine() requires an axis or straight edge.");
20748
+ throw new Error(`${operation} requires an axis or straight edge.`);
20734
20749
  const geometry = reference.topology?.source[modelGeometry]()?.value ?? (reference.whole && isModelObject(reference.model) ? reference.model[modelGeometry]()?.value : void 0);
20735
20750
  if (geometry) {
20736
20751
  const straight = reference.topology ? withTopologyShape(
@@ -20741,12 +20756,12 @@ function rotationAxisReference(axis) {
20741
20756
  ) : geometry.shape.geomType === "LINE";
20742
20757
  if (!straight)
20743
20758
  throw new Error(
20744
- "axisLine() requires a straight axis; curved edges do not define one rotation axis."
20759
+ `${operation} requires a straight axis; curved edges do not define one axis.`
20745
20760
  );
20746
20761
  }
20747
20762
  return reference;
20748
20763
  }
20749
- __name(rotationAxisReference, "rotationAxisReference");
20764
+ __name(straightAxisReference, "straightAxisReference");
20750
20765
  var PivotRotation = class extends RelationExpression {
20751
20766
  /** @internal */
20752
20767
  constructor(chain, selection, previous) {
@@ -20862,7 +20877,7 @@ __name(axisEdge, "axisEdge");
20862
20877
  function axisLine(axis) {
20863
20878
  return new AxisChain(new TransformationRotation(), {
20864
20879
  kind: "axisLine",
20865
- axis: rotationAxisReference(axis)
20880
+ axis: straightAxisReference(axis)
20866
20881
  });
20867
20882
  }
20868
20883
  __name(axisLine, "axisLine");
@@ -21753,33 +21768,64 @@ var ModelObject = class _ModelObject extends RelationObject {
21753
21768
  operation
21754
21769
  );
21755
21770
  }
21756
- /** Resolve both own and occurrence references into this model's local frame. */
21757
- localElements(sources) {
21771
+ /**
21772
+ * Measure finite geometry in this model's local frame, or in relativeTo's frame.
21773
+ * An explicit reference includes solved placement and nested group occurrences.
21774
+ * Empty groups have no finite bounds.
21775
+ */
21776
+ bounds(relativeTo) {
21777
+ const target = relativeTo === void 0 ? this : requireModelObject(relativeTo, "bounds requires a reference model.");
21778
+ const transform = target.localFrames([this])[0];
21779
+ const [minimum, maximum] = this[referenceBounds](
21780
+ this.relationAnchorReference(),
21781
+ transform
21782
+ );
21783
+ return {
21784
+ minimum,
21785
+ maximum,
21786
+ size: [
21787
+ maximum[0] - minimum[0],
21788
+ maximum[1] - minimum[1],
21789
+ maximum[2] - minimum[2]
21790
+ ]
21791
+ };
21792
+ }
21793
+ /**
21794
+ * Model-origin coordinates in relativeTo's local frame, including placement.
21795
+ * This model's origin in its own frame is always [0, 0, 0], even for point models.
21796
+ */
21797
+ position(relativeTo) {
21798
+ const target = requireModelObject(
21799
+ relativeTo,
21800
+ "position requires a reference model."
21801
+ );
21802
+ return target.localFrames([this])[0].position;
21803
+ }
21804
+ /** Resolve source frames without losing nested occurrence positions. */
21805
+ localFrames(sources) {
21758
21806
  const members = this.memberPoses();
21759
- const external = sources.map((source) => anchorReference(source).model).filter((model) => !members.has(model));
21807
+ const external = sources.filter((model) => !members.has(model));
21760
21808
  const context = external.length ? _ModelObject.createSolveContext([this, ...external]) : void 0;
21761
21809
  return sources.map((source) => {
21762
- const reference = anchorReference(source);
21763
- const {
21764
- kind,
21765
- transform: frame,
21766
- topology,
21767
- members: nested,
21768
- bound,
21769
- facing
21770
- } = source instanceof _ModelObject ? source.exposedElement() : reference;
21771
- const memberPose = members.get(reference.model);
21810
+ const memberPose = members.get(source);
21772
21811
  if (memberPose === null)
21773
21812
  throw new Error(
21774
21813
  "The point or element belongs to multiple occurrences. Expose it through the intended child model's named reference."
21775
21814
  );
21776
- const transform = memberPose ?? relativeTransform(
21777
- reference.model.solvePose(context),
21778
- this.solvePose(context)
21779
- );
21815
+ return memberPose ?? relativeTransform(source.solvePose(context), this.solvePose(context));
21816
+ });
21817
+ }
21818
+ /** Resolve both own and occurrence references into this model's local frame. */
21819
+ localElements(sources) {
21820
+ const references2 = sources.map(anchorReference);
21821
+ const transforms = this.localFrames(
21822
+ references2.map((reference) => reference.model)
21823
+ );
21824
+ return sources.map((source, index) => {
21825
+ const { kind, transform, topology, parts, members, bound, facing } = source instanceof _ModelObject ? source.exposedElement() : references2[index];
21780
21826
  return transformElement(
21781
- { kind, transform: frame, topology, members: nested, bound, facing },
21782
- transform
21827
+ { kind, transform, topology, parts, members, bound, facing },
21828
+ transforms[index]
21783
21829
  );
21784
21830
  });
21785
21831
  }
@@ -21821,7 +21867,11 @@ var ModelObject = class _ModelObject extends RelationObject {
21821
21867
  /** @internal */
21822
21868
  exposedElement() {
21823
21869
  if (this.kind === "group")
21824
- return { ...this.geometryAnchor, members: this.elements };
21870
+ return {
21871
+ ...this.geometryAnchor,
21872
+ parts: this.geometryParts(),
21873
+ members: this.elements
21874
+ };
21825
21875
  const geometry = this.requireGeometry().value;
21826
21876
  const kind = this.kind === "face" ? "surface" : this.kind;
21827
21877
  const selection = kind === "solid" ? { kind } : {
@@ -22026,14 +22076,14 @@ var ModelObject = class _ModelObject extends RelationObject {
22026
22076
  storedOperation("scaled", [{ model: this, role: "source", index: 0 }])
22027
22077
  );
22028
22078
  }
22029
- extrude(distance3 = 10) {
22079
+ extrude(distance4 = 10) {
22030
22080
  if (this.kind !== "face")
22031
22081
  throw new Error("extrude requires a single face model.");
22032
- if (!Number.isFinite(distance3) || distance3 === 0)
22082
+ if (!Number.isFinite(distance4) || distance4 === 0)
22033
22083
  throw new Error("Extrusion distance must be finite and non-zero.");
22034
22084
  const source = this.requireGeometry();
22035
22085
  const direction = rotateVector(
22036
- [0, distance3, 0],
22086
+ [0, distance4, 0],
22037
22087
  this.geometryAnchor.transform.quaternion
22038
22088
  );
22039
22089
  const geometry = evaluateSolidGeometry(
@@ -22044,7 +22094,7 @@ var ModelObject = class _ModelObject extends RelationObject {
22044
22094
  {
22045
22095
  shape: source.value.shape,
22046
22096
  topology: source.value.topology,
22047
- index: 1
22097
+ namespace: 1
22048
22098
  },
22049
22099
  direction
22050
22100
  )
@@ -22111,8 +22161,8 @@ var ModelObject = class _ModelObject extends RelationObject {
22111
22161
  })
22112
22162
  );
22113
22163
  }
22114
- chamfer(distance3 = 1, edgeIds) {
22115
- assertPositive("distance", distance3);
22164
+ chamfer(distance4 = 1, edgeIds) {
22165
+ assertPositive("distance", distance4);
22116
22166
  const source = this.requireSolidGeometry();
22117
22167
  const selectedEdgeIds = resolveEdgeSelection(
22118
22168
  source.value.topology.edges,
@@ -22120,13 +22170,13 @@ var ModelObject = class _ModelObject extends RelationObject {
22120
22170
  );
22121
22171
  const geometry = evaluateSolidGeometry(
22122
22172
  "chamfer",
22123
- [distance3, selectedEdgeIds],
22173
+ [distance4, selectedEdgeIds],
22124
22174
  [source],
22125
22175
  () => {
22126
22176
  const result = chamferEdges(
22127
22177
  source.value.shape,
22128
22178
  source.value.topology,
22129
- distance3,
22179
+ distance4,
22130
22180
  selectedEdgeIds
22131
22181
  );
22132
22182
  return { shape: result.shape, topology: result.topology };
@@ -22440,12 +22490,12 @@ var ModelObject = class _ModelObject extends RelationObject {
22440
22490
  {
22441
22491
  shape: geometry.value.shape,
22442
22492
  topology: geometry.value.topology,
22443
- index: otherIndex === 0 ? 1 : void 0
22493
+ namespace: otherIndex === 0 ? 1 : "intermediate"
22444
22494
  },
22445
22495
  {
22446
22496
  shape: operand.value,
22447
22497
  topology: otherGeometry.value.topology,
22448
- index: otherIndex + 2
22498
+ namespace: otherIndex + 2
22449
22499
  },
22450
22500
  operation
22451
22501
  );
@@ -22494,6 +22544,180 @@ var ModelObject = class _ModelObject extends RelationObject {
22494
22544
  }
22495
22545
  return members;
22496
22546
  }
22547
+ /** Finite members in this value's saved local assembly, without re-solving children. */
22548
+ geometryParts(transform = identityRigidTransform) {
22549
+ if (this.geometry)
22550
+ return [{ source: this, selection: { kind: "solid" }, transform, scale: 1 }];
22551
+ return this.children.flatMap(
22552
+ (child) => child.geometryParts(
22553
+ composeTransforms(transform, child.solvePose(this.assembly))
22554
+ )
22555
+ );
22556
+ }
22557
+ /** @internal */
22558
+ static distance(a, b, axis) {
22559
+ const left = anchorReference(a), right = anchorReference(b);
22560
+ const axisReference = axis !== void 0 && typeof axis !== "string" && !Array.isArray(axis) ? straightAxisReference(axis, "distance() axis") : void 0;
22561
+ const context = _ModelObject.createSolveContext([
22562
+ left.model,
22563
+ right.model,
22564
+ ...axisReference ? [axisReference.model] : []
22565
+ ]);
22566
+ const topologyParts = /* @__PURE__ */ __name((reference) => reference.topology ? [reference.topology] : reference.parts ?? (reference.whole && reference.model instanceof _ModelObject ? reference.model.geometryParts() : void 0), "topologyParts");
22567
+ const parts = /* @__PURE__ */ __name((reference) => {
22568
+ const pose = reference.model.solvePose(context);
22569
+ const frame = composeTransforms(pose, reference.transform);
22570
+ if (reference.kind === "point") return [{ points: [frame.position] }];
22571
+ const topology = topologyParts(reference);
22572
+ if (topology)
22573
+ return topology.map((part) => ({
22574
+ geometry: part.source.requireGeometry(),
22575
+ selection: part.selection,
22576
+ scale: part.scale,
22577
+ transform: composeTransforms(pose, part.transform)
22578
+ }));
22579
+ if (reference.bound) {
22580
+ const [x, z] = reference.bound.size;
22581
+ const corners = x === 0 && z === 0 ? [origin] : x === 0 ? [
22582
+ [0, 0, -z / 2],
22583
+ [0, 0, z / 2]
22584
+ ] : z === 0 ? [
22585
+ [-x / 2, 0, 0],
22586
+ [x / 2, 0, 0]
22587
+ ] : [
22588
+ [-x / 2, 0, -z / 2],
22589
+ [x / 2, 0, -z / 2],
22590
+ [x / 2, 0, z / 2],
22591
+ [-x / 2, 0, z / 2]
22592
+ ];
22593
+ return [
22594
+ {
22595
+ points: corners.map(
22596
+ (point3) => composeTransforms(frame, translation(point3)).position
22597
+ )
22598
+ }
22599
+ ];
22600
+ }
22601
+ throw new Error(
22602
+ "distance() requires finite geometry or a point; select an edge or face instead of an infinite axis or plane."
22603
+ );
22604
+ }, "parts");
22605
+ const first = parts(left), second = parts(right);
22606
+ if (!first.length || !second.length)
22607
+ throw new Error("distance() cannot measure an empty group.");
22608
+ const record = /* @__PURE__ */ __name((result, direction2) => {
22609
+ if (observeDistance) {
22610
+ const operands = [left, right].map((reference) => {
22611
+ const topology = topologyParts(reference);
22612
+ const elements = topology && reference.kind !== "point" ? topology.map(
22613
+ (part) => snapshotElements({
22614
+ [reference.name]: {
22615
+ ...reference,
22616
+ topology: part,
22617
+ members: void 0
22618
+ }
22619
+ })[0]
22620
+ ) : [
22621
+ snapshotElements({
22622
+ [reference.name]: { ...reference, members: void 0 }
22623
+ })[0]
22624
+ ];
22625
+ return {
22626
+ nodeId: reference.model.nodeId,
22627
+ whole: reference.whole === true,
22628
+ elements
22629
+ };
22630
+ });
22631
+ observeDistance(
22632
+ {
22633
+ ...result,
22634
+ axis: direction2,
22635
+ axisName: typeof axis === "string" ? axis : void 0,
22636
+ operands,
22637
+ placements: [...context.poses].map(([model, pose]) => ({
22638
+ nodeId: model.nodeId,
22639
+ transform: toTransform(pose)
22640
+ }))
22641
+ },
22642
+ [...context.poses.keys()]
22643
+ );
22644
+ }
22645
+ return result.value;
22646
+ }, "record");
22647
+ if (axis === void 0) {
22648
+ let result;
22649
+ for (const a2 of first)
22650
+ for (const b2 of second) {
22651
+ const candidate = "points" in a2 && a2.points.length === 1 && "points" in b2 && b2.points.length === 1 ? {
22652
+ value: Math.hypot(
22653
+ ...a2.points[0].map((value, i) => value - b2.points[0][i])
22654
+ ),
22655
+ start: a2.points[0],
22656
+ end: b2.points[0]
22657
+ } : distanceBetweenParts(a2, b2).value;
22658
+ if (!result || candidate.value < result.value) result = candidate;
22659
+ if (result.value === 0) return record(result);
22660
+ }
22661
+ return record(result);
22662
+ }
22663
+ const direction = axisReference ? rotateVector(
22664
+ [0, 1, 0],
22665
+ composeTransforms(
22666
+ axisReference.model.solvePose(context),
22667
+ axisReference.transform
22668
+ ).quaternion
22669
+ ) : typeof axis === "string" ? distanceAxes[axis] : axis;
22670
+ assertFiniteVector("distance() axis", direction);
22671
+ const magnitude = Math.hypot(...direction);
22672
+ if (magnitude === 0) throw new Error("distance() axis must be non-zero.");
22673
+ const normalized = direction.map(
22674
+ (value) => value / magnitude
22675
+ );
22676
+ const project = invertTransform(frameFromYAxis(origin, normalized));
22677
+ const projectedBounds = /* @__PURE__ */ __name((parts2) => {
22678
+ const bounds = parts2.map((part) => {
22679
+ if ("points" in part)
22680
+ return pointBounds(
22681
+ part.points.map(
22682
+ (point3) => composeTransforms(project, translation(point3)).position
22683
+ )
22684
+ );
22685
+ return evaluateSnapshotQuery(
22686
+ boundsQuery(
22687
+ part.geometry,
22688
+ composeTransforms(project, part.transform),
22689
+ part.selection,
22690
+ part.scale
22691
+ )
22692
+ );
22693
+ });
22694
+ return [
22695
+ [0, 1, 2].map(
22696
+ (i) => Math.min(...bounds.map((value) => value[0][i]))
22697
+ ),
22698
+ [0, 1, 2].map(
22699
+ (i) => Math.max(...bounds.map((value) => value[1][i]))
22700
+ )
22701
+ ];
22702
+ }, "projectedBounds");
22703
+ const aBounds = projectedBounds(first), bBounds = projectedBounds(second);
22704
+ const [aMin, aMax] = [aBounds[0][1], aBounds[1][1]], [bMin, bMax] = [bBounds[0][1], bBounds[1][1]];
22705
+ const overlap2 = (Math.max(aMin, bMin) + Math.min(aMax, bMax)) / 2;
22706
+ const [start, end] = bMin > aMax ? [aMax, bMin] : aMin > bMax ? [aMin, bMax] : [overlap2, overlap2];
22707
+ const center = [0, 1, 2].map(
22708
+ (i) => (Math.max(aBounds[0][i], bBounds[0][i]) + Math.min(aBounds[1][i], bBounds[1][i])) / 2
22709
+ );
22710
+ const unproject = invertTransform(project);
22711
+ const at = /* @__PURE__ */ __name((y) => composeTransforms(unproject, translation([center[0], y, center[2]])).position, "at");
22712
+ return record(
22713
+ {
22714
+ value: Math.max(0, bMin - aMax, aMin - bMax),
22715
+ start: at(start),
22716
+ end: at(end)
22717
+ },
22718
+ normalized
22719
+ );
22720
+ }
22497
22721
  /** Bounds of the selected finite geometry after a rigid transform. */
22498
22722
  [referenceBoundsParts](reference, transform) {
22499
22723
  if (reference.bound)
@@ -22525,6 +22749,15 @@ var ModelObject = class _ModelObject extends RelationObject {
22525
22749
  )
22526
22750
  ];
22527
22751
  }
22752
+ if (reference.parts)
22753
+ return reference.parts.map(
22754
+ (part) => boundsQuery(
22755
+ part.source.requireGeometry(),
22756
+ composeTransforms(transform, part.transform),
22757
+ part.selection,
22758
+ part.scale
22759
+ )
22760
+ );
22528
22761
  return [{ bounds: super[referenceBounds](reference, transform) }];
22529
22762
  }
22530
22763
  /** Bounds of the selected finite geometry after a rigid transform. */
@@ -22604,7 +22837,7 @@ var ModelObject = class _ModelObject extends RelationObject {
22604
22837
  );
22605
22838
  }
22606
22839
  edgeReference(id) {
22607
- return { ...rotationAxisReference(this.edge(id)), model: void 0 };
22840
+ return { ...straightAxisReference(this.edge(id)), model: void 0 };
22608
22841
  }
22609
22842
  vertexPosition(id) {
22610
22843
  return anchorReference(this.vertex(id)).transform.position;
@@ -22968,11 +23201,11 @@ function assertCoilClearance(radius, wireRadius, pitch, turns) {
22968
23201
  else upper = middle;
22969
23202
  }
22970
23203
  const separation = Math.min(fullTurn * turns, (lower + upper) / 2);
22971
- const distance3 = Math.hypot(
23204
+ const distance4 = Math.hypot(
22972
23205
  2 * radius * Math.sin(separation / 2),
22973
23206
  pitch * separation / fullTurn
22974
23207
  );
22975
- if (distance3 <= 2 * wireRadius) {
23208
+ if (distance4 <= 2 * wireRadius) {
22976
23209
  throw new Error(
22977
23210
  "Coil turns must not touch or overlap; increase pitch or decrease wireRadius."
22978
23211
  );
@@ -23165,12 +23398,16 @@ function group(children, name = "Group") {
23165
23398
  });
23166
23399
  }
23167
23400
  __name(group, "group");
23401
+ function distance2(a, b, axis) {
23402
+ return ModelObject.distance(a, b, axis);
23403
+ }
23404
+ __name(distance2, "distance");
23168
23405
  function union(operands) {
23169
23406
  const { first, others } = booleanOperands("union", operands);
23170
23407
  return first[combineModels]("fuse", others);
23171
23408
  }
23172
23409
  __name(union, "union");
23173
- function extrude(face, distance3 = 10) {
23410
+ function extrude(face, distance4 = 10) {
23174
23411
  const faces = (Array.isArray(face) ? face : [face]).map(
23175
23412
  (value) => requireModelKind(
23176
23413
  value,
@@ -23178,7 +23415,7 @@ function extrude(face, distance3 = 10) {
23178
23415
  "extrude requires a face model or an array of face models."
23179
23416
  )
23180
23417
  );
23181
- const solids = faces.map((value) => value.extrude(distance3));
23418
+ const solids = faces.map((value) => value.extrude(distance4));
23182
23419
  return Array.isArray(face) ? solids : solids[0];
23183
23420
  }
23184
23421
  __name(extrude, "extrude");
@@ -23278,6 +23515,83 @@ function relatedModelObjects(object) {
23278
23515
  return object.relatedObjects();
23279
23516
  }
23280
23517
  __name(relatedModelObjects, "relatedModelObjects");
23518
+ var distanceAxes = { x: [1, 0, 0], y: [0, 1, 0], z: [0, 0, 1] };
23519
+ function withDistanceShape(part, visit) {
23520
+ if (!("points" in part))
23521
+ return withTransformedGeometry(part.geometry.value, part, visit);
23522
+ const points = part.points;
23523
+ if (points.length <= 2) {
23524
+ const shape = points.length === 1 ? makeVertex(toPoint(points[0])) : makeLine2(toPoint(points[0]), toPoint(points[1]));
23525
+ try {
23526
+ return visit(shape);
23527
+ } finally {
23528
+ shape.delete();
23529
+ }
23530
+ }
23531
+ const edges = [];
23532
+ let wire;
23533
+ let face;
23534
+ try {
23535
+ points.forEach(
23536
+ (point3, index) => edges.push(
23537
+ makeLine2(toPoint(point3), toPoint(points[(index + 1) % points.length]))
23538
+ )
23539
+ );
23540
+ wire = assembleWire(edges);
23541
+ face = makeFace(wire);
23542
+ return visit(face);
23543
+ } finally {
23544
+ face?.delete();
23545
+ wire?.delete();
23546
+ edges.forEach((edge) => edge.delete());
23547
+ }
23548
+ }
23549
+ __name(withDistanceShape, "withDistanceShape");
23550
+ var distanceBetweenParts = cachedArtifact(
23551
+ (a, b) => withDistanceShape(
23552
+ a,
23553
+ (left) => withDistanceShape(b, (right) => {
23554
+ const query = new (getOC12()).BRepExtrema_DistShapeShape();
23555
+ try {
23556
+ query.LoadS1(left.wrapped);
23557
+ query.LoadS2(right.wrapped);
23558
+ query.Perform();
23559
+ if (!query.IsDone())
23560
+ throw new Error("distance() could not measure these geometries.");
23561
+ const first = query.PointOnShape1(1), second = query.PointOnShape2(1);
23562
+ try {
23563
+ return {
23564
+ value: query.Value(),
23565
+ start: [first.X(), first.Y(), first.Z()],
23566
+ end: [second.X(), second.Y(), second.Z()]
23567
+ };
23568
+ } finally {
23569
+ first.delete();
23570
+ second.delete();
23571
+ }
23572
+ } finally {
23573
+ query.delete();
23574
+ }
23575
+ })
23576
+ ),
23577
+ {
23578
+ key: /* @__PURE__ */ __name((a, b) => kernelOperationKey(
23579
+ "distance-witnesses",
23580
+ [a, b].map(
23581
+ (part) => "points" in part ? ["points", part.points] : [
23582
+ "shape",
23583
+ part.geometry.id,
23584
+ part.selection.kind,
23585
+ part.selection.kind === "solid" ? null : part.selection.id,
23586
+ part.transform.position,
23587
+ part.transform.quaternion,
23588
+ part.scale
23589
+ ]
23590
+ ),
23591
+ [a, b].flatMap((part) => "points" in part ? [] : [part.geometry])
23592
+ ), "key")
23593
+ }
23594
+ );
23281
23595
  function boundsQuery(geometry, transform, selection = { kind: "solid" }, scale3 = 1) {
23282
23596
  return {
23283
23597
  inputId: geometry.id,
@@ -23500,6 +23814,7 @@ var authoringApi = Object.freeze({
23500
23814
  frustum,
23501
23815
  regularPrism,
23502
23816
  group,
23817
+ distance: distance2,
23503
23818
  union,
23504
23819
  cut,
23505
23820
  intersect
@@ -23797,7 +24112,7 @@ function buildLoftGeometry(sections, spine, ruled) {
23797
24112
  section.transform
23798
24113
  ),
23799
24114
  topology: section.geometry.value.topology,
23800
- index: index + 1
24115
+ namespace: index + 1
23801
24116
  });
23802
24117
  }
23803
24118
  if (spine) {
@@ -23877,6 +24192,10 @@ function axisAlignedBounds(bounds, transform) {
23877
24192
  }
23878
24193
  __name(axisAlignedBounds, "axisAlignedBounds");
23879
24194
  function computeTransformedBounds(geometry, query) {
24195
+ return withTransformedGeometry(geometry, query, shapeBounds);
24196
+ }
24197
+ __name(computeTransformedBounds, "computeTransformedBounds");
24198
+ function withTransformedGeometry(geometry, query, visit) {
23880
24199
  const { transform, selection, scale: scale3 } = query;
23881
24200
  return withTopologyShape(
23882
24201
  geometry.shape,
@@ -23887,7 +24206,7 @@ function computeTransformedBounds(geometry, query) {
23887
24206
  try {
23888
24207
  const moved = shapeWithTransform(scaled, transform);
23889
24208
  try {
23890
- return shapeBounds(moved);
24209
+ return visit(moved);
23891
24210
  } finally {
23892
24211
  moved.delete();
23893
24212
  }
@@ -23897,7 +24216,7 @@ function computeTransformedBounds(geometry, query) {
23897
24216
  }
23898
24217
  );
23899
24218
  }
23900
- __name(computeTransformedBounds, "computeTransformedBounds");
24219
+ __name(withTransformedGeometry, "withTransformedGeometry");
23901
24220
  function pointBounds(points) {
23902
24221
  if (!points.length)
23903
24222
  throw new Error("Empty geometry has no directional bounds.");
@@ -23999,6 +24318,10 @@ function transformElement(element, transform) {
23999
24318
  ...element.topology,
24000
24319
  transform: composeTransforms(transform, element.topology.transform)
24001
24320
  } : void 0,
24321
+ parts: element.parts?.map((part) => ({
24322
+ ...part,
24323
+ transform: composeTransforms(transform, part.transform)
24324
+ })),
24002
24325
  members: element.members ? Object.fromEntries(
24003
24326
  Object.entries(element.members).map(([name, member]) => [
24004
24327
  name,
@@ -24023,6 +24346,11 @@ function scaleElement(element, factor) {
24023
24346
  scale: element.topology.scale * factor,
24024
24347
  transform: scaleFrame(element.topology.transform, factor)
24025
24348
  } : void 0,
24349
+ parts: element.parts?.map((part) => ({
24350
+ ...part,
24351
+ scale: part.scale * factor,
24352
+ transform: scaleFrame(part.transform, factor)
24353
+ })),
24026
24354
  members: element.members ? scaleElements(element.members, factor) : void 0,
24027
24355
  transform: {
24028
24356
  ...element.transform,
@@ -24990,7 +25318,7 @@ __name(solvedProblem, "solvedProblem");
24990
25318
 
24991
25319
  // packages/core/src/library/sketch-regions.ts
24992
25320
  var tau2 = 2 * Math.PI;
24993
- var distance2 = /* @__PURE__ */ __name((a, b) => Math.hypot(a[0] - b[0], a[1] - b[1]), "distance");
25321
+ var distance3 = /* @__PURE__ */ __name((a, b) => Math.hypot(a[0] - b[0], a[1] - b[1]), "distance");
24994
25322
  function sketchRegions(layers) {
24995
25323
  const resolve = sketchPointResolver(layers);
24996
25324
  const points = new Map(
@@ -25012,7 +25340,7 @@ function sketchRegions(layers) {
25012
25340
  const vertices = [];
25013
25341
  const vertex = /* @__PURE__ */ __name((position2, tolerance2) => {
25014
25342
  const index = vertices.findIndex(
25015
- (v) => distance2(v.position, position2) <= Math.min(v.tolerance, tolerance2)
25343
+ (v) => distance3(v.position, position2) <= Math.min(v.tolerance, tolerance2)
25016
25344
  );
25017
25345
  if (index >= 0) return index;
25018
25346
  vertices.push({ position: position2, tolerance: tolerance2, edges: [] });
@@ -25042,7 +25370,7 @@ function sketchRegions(layers) {
25042
25370
  ) || contacts.some(
25043
25371
  (c) => c.parameters.some((_, j) => {
25044
25372
  const curve = j === 0 ? a.curve : b.curve;
25045
- return curve.kind === "circle" || distance2(c.position, sketchCurvePosition(curve, 0)) > sketchCurveTolerance(curve) && distance2(c.position, sketchCurvePosition(curve, 1)) > sketchCurveTolerance(curve);
25373
+ return curve.kind === "circle" || distance3(c.position, sketchCurvePosition(curve, 0)) > sketchCurveTolerance(curve) && distance3(c.position, sketchCurvePosition(curve, 1)) > sketchCurveTolerance(curve);
25046
25374
  })
25047
25375
  ))
25048
25376
  throw new Error(
@@ -25117,7 +25445,7 @@ function overlap(a, b, intersections) {
25117
25445
  if (a.kind === "line" || b.kind === "line") {
25118
25446
  if (a.kind !== "line" || b.kind !== "line" || intersections.length < 2)
25119
25447
  return false;
25120
- } else if (distance2(a.center, b.center) > Math.min(sketchCurveTolerance(a), sketchCurveTolerance(b)) || Math.abs(a.radius - b.radius) > Math.min(sketchCurveTolerance(a), sketchCurveTolerance(b)))
25448
+ } else if (distance3(a.center, b.center) > Math.min(sketchCurveTolerance(a), sketchCurveTolerance(b)) || Math.abs(a.radius - b.radius) > Math.min(sketchCurveTolerance(a), sketchCurveTolerance(b)))
25121
25449
  return false;
25122
25450
  const cuts = [0, ...intersections, 1].sort((a2, b2) => a2 - b2);
25123
25451
  return cuts.slice(1).some(
@@ -25251,6 +25579,7 @@ export {
25251
25579
  regularPrism,
25252
25580
  primitiveConstructor,
25253
25581
  group,
25582
+ distance2 as distance,
25254
25583
  union,
25255
25584
  extrude,
25256
25585
  text,
@@ -25286,4 +25615,4 @@ export {
25286
25615
  assertSketchDragConnections,
25287
25616
  solveSketchSnapshot
25288
25617
  };
25289
- //# sourceMappingURL=chunk-4WLVDYUJ.js.map
25618
+ //# sourceMappingURL=chunk-OALDEU3F.js.map