@code3d/core 0.0.1-alpha.6 → 0.0.1-alpha.8

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.
Files changed (37) hide show
  1. package/README.md +53 -3
  2. package/bld/chunks/{chunk-3RIMOCPP.js → chunk-EWPRWRLV.js} +2 -2
  3. package/bld/chunks/{chunk-T6COR2SG.js → chunk-HSU2FH3L.js} +3 -3
  4. package/bld/chunks/{chunk-T3RAUNSF.js → chunk-MICNLFBF.js} +452 -107
  5. package/bld/chunks/chunk-MICNLFBF.js.map +7 -0
  6. package/bld/chunks/{chunk-KYW4AWKO.js → chunk-OAVABPL5.js} +2 -2
  7. package/bld/library/cached.d.ts +5 -3
  8. package/bld/library/cached.d.ts.map +1 -1
  9. package/bld/library/index.d.ts +4 -4
  10. package/bld/library/index.d.ts.map +1 -1
  11. package/bld/library/index.js +5 -3
  12. package/bld/library/kernel-cache.d.ts +5 -3
  13. package/bld/library/kernel-cache.d.ts.map +1 -1
  14. package/bld/library/loft.d.ts.map +1 -1
  15. package/bld/library/replicad.js +2 -2
  16. package/bld/library/runtime.d.ts +61 -6
  17. package/bld/library/runtime.d.ts.map +1 -1
  18. package/bld/library/topology.d.ts +4 -2
  19. package/bld/library/topology.d.ts.map +1 -1
  20. package/bld/node/index.js +7 -5
  21. package/bld/node/replicad.js +4 -4
  22. package/bld/tooling/index.d.ts +2 -2
  23. package/bld/tooling/index.d.ts.map +1 -1
  24. package/bld/tooling/index.js +4 -2
  25. package/package.json +1 -1
  26. package/src/library/cached.ts +29 -8
  27. package/src/library/index.ts +5 -2
  28. package/src/library/kernel-cache.ts +29 -5
  29. package/src/library/loft.ts +5 -1
  30. package/src/library/runtime.ts +508 -43
  31. package/src/library/shell.ts +3 -3
  32. package/src/library/topology.ts +24 -15
  33. package/src/tooling/index.ts +2 -0
  34. package/bld/chunks/chunk-T3RAUNSF.js.map +0 -7
  35. /package/bld/chunks/{chunk-3RIMOCPP.js.map → chunk-EWPRWRLV.js.map} +0 -0
  36. /package/bld/chunks/{chunk-T6COR2SG.js.map → chunk-HSU2FH3L.js.map} +0 -0
  37. /package/bld/chunks/{chunk-KYW4AWKO.js.map → chunk-OAVABPL5.js.map} +0 -0
@@ -369,6 +369,7 @@ __name(estimateRetainedBytes, "estimateRetainedBytes");
369
369
  // packages/core/src/library/kernel-cache.ts
370
370
  function createComputationCache({
371
371
  maximumBytes = 2 * 1024 ** 3,
372
+ minimumPersistenceMilliseconds = 1,
372
373
  nativeAllocatedBytes
373
374
  }) {
374
375
  const entries = /* @__PURE__ */ new Map();
@@ -391,6 +392,10 @@ function createComputationCache({
391
392
  evictHistoricalEntries();
392
393
  }
393
394
  __name(setKernelCacheBudget2, "setKernelCacheBudget");
395
+ function setKernelCachePersistenceThreshold2(milliseconds) {
396
+ minimumPersistenceMilliseconds = milliseconds;
397
+ }
398
+ __name(setKernelCachePersistenceThreshold2, "setKernelCachePersistenceThreshold");
394
399
  function setKernelExternalBytes2(bytes) {
395
400
  externalBytes = bytes;
396
401
  evictHistoricalEntries();
@@ -431,8 +436,10 @@ function createComputationCache({
431
436
  function evaluateCachedArtifact2(key, lifecycle, compute, codec) {
432
437
  const hit = findKernelOperation2(key, lifecycle, codec);
433
438
  if (hit) return hit;
439
+ const started = performance.now();
434
440
  const value = compute();
435
- acceptKernelOperation2(key, lifecycle, value, codec);
441
+ const milliseconds = performance.now() - started;
442
+ acceptKernelOperation2(key, lifecycle, value, milliseconds, codec);
436
443
  return { id: key.id, value };
437
444
  }
438
445
  __name(evaluateCachedArtifact2, "evaluateCachedArtifact");
@@ -460,8 +467,8 @@ function createComputationCache({
460
467
  function lookup(key, lifecycle, codec, read) {
461
468
  currentEvaluation?.checkCancelled?.();
462
469
  const { id, signature } = key;
463
- let cached2 = entries.get(id);
464
- if (!cached2 && codec !== false) {
470
+ let cached = entries.get(id);
471
+ if (!cached && codec !== false) {
465
472
  const bytes = read();
466
473
  if (bytes) {
467
474
  let restored;
@@ -473,25 +480,26 @@ function createComputationCache({
473
480
  misses += 1;
474
481
  return void 0;
475
482
  }
476
- cached2 = retainEntry(key, lifecycle, restored);
483
+ cached = retainEntry(key, lifecycle, restored, true);
477
484
  persistentHits += 1;
478
485
  persisted.add(id);
479
486
  }
480
487
  }
481
- if (!cached2) {
488
+ if (!cached) {
482
489
  misses += 1;
483
490
  return void 0;
484
491
  }
485
- if (cached2.signature !== signature)
492
+ if (cached.signature !== signature)
486
493
  throw new Error(`Kernel operation cache identity collision: ${id}`);
487
494
  hits += 1;
488
- const value = cached2.instantiate(cached2.value);
489
- if (codec !== false) persist(key, cached2.value, codec, true);
490
- touchEntry(id, cached2);
495
+ const value = cached.instantiate(cached.value);
496
+ if (cached.persistenceEligible && codec !== false)
497
+ persist(key, cached.value, codec, true);
498
+ touchEntry(id, cached);
491
499
  return { id, value };
492
500
  }
493
501
  __name(lookup, "lookup");
494
- function acceptKernelOperation2(key, lifecycle, value, codec) {
502
+ function acceptKernelOperation2(key, lifecycle, value, milliseconds, codec) {
495
503
  const existing = entries.get(key.id);
496
504
  if (existing) {
497
505
  if (existing.signature !== key.signature)
@@ -506,8 +514,14 @@ function createComputationCache({
506
514
  lifecycle.release(value);
507
515
  throw error;
508
516
  }
509
- const entry = retainEntry(key, lifecycle, retained);
510
- if (codec !== false) persist(key, retained, codec);
517
+ const entry = retainEntry(
518
+ key,
519
+ lifecycle,
520
+ retained,
521
+ milliseconds >= minimumPersistenceMilliseconds
522
+ );
523
+ if (entry.persistenceEligible && codec !== false)
524
+ persist(key, retained, codec);
511
525
  touchEntry(key.id, entry);
512
526
  }
513
527
  __name(acceptKernelOperation2, "acceptKernelOperation");
@@ -553,8 +567,9 @@ function createComputationCache({
553
567
  pendingPersistence.clear();
554
568
  }
555
569
  __name(flushPendingPersistence, "flushPendingPersistence");
556
- function retainEntry(key, lifecycle, retained) {
570
+ function retainEntry(key, lifecycle, retained, persistenceEligible) {
557
571
  const entry = {
572
+ persistenceEligible,
558
573
  estimatedBytes: 256 + estimateRetainedBytes(key.signature) + lifecycle.estimateBytes(retained),
559
574
  signature: key.signature,
560
575
  value: retained,
@@ -630,6 +645,7 @@ function createComputationCache({
630
645
  clearKernelOperationCache: clearKernelOperationCache2,
631
646
  kernelOperationCacheStats: kernelOperationCacheStats2,
632
647
  setKernelCacheBudget: setKernelCacheBudget2,
648
+ setKernelCachePersistenceThreshold: setKernelCachePersistenceThreshold2,
633
649
  setKernelArtifactStore: setKernelArtifactStore2,
634
650
  findKernelOperation: findKernelOperation2,
635
651
  findKernelOperations: findKernelOperations2,
@@ -649,6 +665,7 @@ var {
649
665
  clearKernelOperationCache,
650
666
  kernelOperationCacheStats,
651
667
  setKernelCacheBudget,
668
+ setKernelCachePersistenceThreshold,
652
669
  setKernelArtifactStore,
653
670
  findKernelOperation,
654
671
  findKernelOperations,
@@ -713,19 +730,19 @@ var dataLifecycle = {
713
730
  release() {
714
731
  }
715
732
  };
716
- function cached(compute, options) {
733
+ function cache(compute, args, options) {
717
734
  const operation = cachedArtifact(
718
- (...args) => {
719
- const value = compute(...args);
735
+ (...args2) => {
736
+ const value = compute(...args2);
720
737
  if (value && typeof value === "object" && "then" in value && typeof value.then === "function")
721
- throw new Error("cached() requires a synchronous computation.");
738
+ throw new Error("cache() requires a synchronous computation.");
722
739
  return value;
723
740
  },
724
741
  { identity: compute, codec: options }
725
742
  );
726
- return (...args) => operation(...args).value;
743
+ return args === void 0 ? (...args2) => operation(...args2).value : operation(...args).value;
727
744
  }
728
- __name(cached, "cached");
745
+ __name(cache, "cache");
729
746
  function cachedArtifact(compute, {
730
747
  key,
731
748
  lifecycle = dataLifecycle,
@@ -742,7 +759,7 @@ function cachedArtifact(compute, {
742
759
  ));
743
760
  const find = /* @__PURE__ */ __name((key2) => findKernelOperation(key2, lifecycle, persistence), "find");
744
761
  const findMany = /* @__PURE__ */ __name((keys) => findKernelOperations(keys, lifecycle, persistence), "findMany");
745
- const accept = /* @__PURE__ */ __name((key2, value) => acceptKernelOperation(key2, lifecycle, value, persistence), "accept");
762
+ const accept = /* @__PURE__ */ __name((key2, value, milliseconds) => acceptKernelOperation(key2, lifecycle, value, milliseconds, persistence), "accept");
746
763
  return Object.assign(
747
764
  (...args) => {
748
765
  return evaluateCachedArtifact(
@@ -1092,9 +1109,9 @@ function sketchCurveClosestParameter(curve, position2) {
1092
1109
  );
1093
1110
  if (curve.kind === "circle") return sketchPositiveAngle(angle) / tau;
1094
1111
  const sign3 = Math.sign(curve.sweep), sweep = Math.abs(curve.sweep);
1095
- const distance3 = sketchPositiveAngle(sign3 * (angle - curve.start));
1096
- if (distance3 <= sweep) return sweep ? distance3 / sweep : 0;
1097
- 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;
1098
1115
  }
1099
1116
  __name(sketchCurveClosestParameter, "sketchCurveClosestParameter");
1100
1117
  function sketchCurveBounds(curve) {
@@ -2416,15 +2433,15 @@ function endpointPolicy(problem, point3, scope) {
2416
2433
  controls.delete(point3);
2417
2434
  if (controls.size) return { kind: "endpoint", anchors: [...controls] };
2418
2435
  const graph = neighbors(problem);
2419
- const distance3 = /* @__PURE__ */ new Map([[point3, 0]]);
2420
- for (const [p, d] of distance3)
2436
+ const distance4 = /* @__PURE__ */ new Map([[point3, 0]]);
2437
+ for (const [p, d] of distance4)
2421
2438
  for (const next of graph[p])
2422
- 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);
2423
2440
  let farthest = -1, max12 = 0;
2424
2441
  for (const p of scope)
2425
- if ((distance3.get(p) ?? 0) > max12) {
2442
+ if ((distance4.get(p) ?? 0) > max12) {
2426
2443
  farthest = p;
2427
- max12 = distance3.get(p);
2444
+ max12 = distance4.get(p);
2428
2445
  }
2429
2446
  return { kind: "endpoint", anchors: farthest < 0 ? [] : [farthest] };
2430
2447
  }
@@ -3095,22 +3112,22 @@ function nearestPoint(point3, geometry) {
3095
3112
  case "ellipse": {
3096
3113
  const y = cross2(geometry.normal, geometry.major);
3097
3114
  const x0 = dot(delta, geometry.major), y0 = dot(delta, y);
3098
- 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");
3099
3116
  const step = Math.PI / 16, candidates2 = [];
3100
3117
  for (let i = 0; i < 32; i++) {
3101
3118
  const angle2 = i * step;
3102
- if (distance3(angle2) > distance3(angle2 - step) || distance3(angle2) > distance3(angle2 + step))
3119
+ if (distance4(angle2) > distance4(angle2 - step) || distance4(angle2) > distance4(angle2 + step))
3103
3120
  continue;
3104
3121
  let lower = angle2 - step, upper = angle2 + step;
3105
3122
  for (let iteration = 0; iteration < 64; iteration++) {
3106
3123
  const a = lower + (upper - lower) * 0.3819660112501051, b = upper - (upper - lower) * 0.3819660112501051;
3107
- if (distance3(a) < distance3(b)) upper = b;
3124
+ if (distance4(a) < distance4(b)) upper = b;
3108
3125
  else lower = a;
3109
3126
  }
3110
3127
  candidates2.push((lower + upper) / 2);
3111
3128
  }
3112
3129
  const angle = candidates2.reduce(
3113
- (best, angle2) => distance3(angle2) < distance3(best) ? angle2 : best,
3130
+ (best, angle2) => distance4(angle2) < distance4(best) ? angle2 : best,
3114
3131
  candidates2[0]
3115
3132
  );
3116
3133
  return addVectors(
@@ -3283,8 +3300,8 @@ function filletEdges(shape, source, radius, edgeIds) {
3283
3300
  return modifyEdges("fillet", shape, source, radius, edgeIds);
3284
3301
  }
3285
3302
  __name(filletEdges, "filletEdges");
3286
- function chamferEdges(shape, source, distance3, edgeIds) {
3287
- return modifyEdges("chamfer", shape, source, distance3, edgeIds);
3303
+ function chamferEdges(shape, source, distance4, edgeIds) {
3304
+ return modifyEdges("chamfer", shape, source, distance4, edgeIds);
3288
3305
  }
3289
3306
  __name(chamferEdges, "chamferEdges");
3290
3307
  function booleanWithTopology(left, right, operation) {
@@ -3511,7 +3528,7 @@ function modifyEdges(kind, shape, source, amount, requestedIds) {
3511
3528
  return {
3512
3529
  shape: result,
3513
3530
  topology: transferShapeTopology(
3514
- [{ shape, topology: source, index: 1 }],
3531
+ [{ shape, topology: source, namespace: "preserve" }],
3515
3532
  result,
3516
3533
  builder
3517
3534
  ),
@@ -3586,15 +3603,18 @@ function transferShapeTopology(inputs, output, history) {
3586
3603
  const transfer = /* @__PURE__ */ __name((kind) => {
3587
3604
  const originals = [];
3588
3605
  const results = topologyShapes(output, kind);
3606
+ let nextId = 1;
3589
3607
  try {
3590
3608
  const candidates2 = inputs.flatMap((input, inputIndex) => {
3591
3609
  const shapes = topologyShapes(input.shape, kind);
3592
3610
  originals.push(shapes);
3593
3611
  const source = input.topology[topologyMetadata[kind].plural];
3594
3612
  assertTopologyLength(kind, shapes, source);
3613
+ if (input.namespace === "preserve")
3614
+ nextId = Math.max(nextId, source.nextId);
3595
3615
  return shapes.map((shape, index) => {
3596
3616
  const id = source.ids[index];
3597
- 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;
3598
3618
  const unchanged = matchingOutputShapes(shape.wrapped, results);
3599
3619
  if (unchanged.length) return { id: inherited2, matches: unchanged };
3600
3620
  if (typeof history !== "function") {
@@ -3631,10 +3651,8 @@ function transferShapeTopology(inputs, output, history) {
3631
3651
  inherited.set(index, candidate.id);
3632
3652
  }
3633
3653
  }
3634
- let nextId = 1;
3635
- return stableTopology(
3636
- results.map((_, index) => inherited.get(index) ?? nextId++)
3637
- );
3654
+ const ids = results.map((_, index) => inherited.get(index) ?? nextId++);
3655
+ return stableTopology(ids, nextId);
3638
3656
  } finally {
3639
3657
  originals.forEach(deleteShapes);
3640
3658
  deleteShapes(results);
@@ -3693,11 +3711,14 @@ function stableGroupIds(kind, shapes, topology, kernelIds) {
3693
3711
  }
3694
3712
  __name(stableGroupIds, "stableGroupIds");
3695
3713
  function initialTopology(shapes) {
3696
- return stableTopology(shapes.map((_, index) => index + 1));
3714
+ return stableTopology(
3715
+ shapes.map((_, index) => index + 1),
3716
+ shapes.length + 1
3717
+ );
3697
3718
  }
3698
3719
  __name(initialTopology, "initialTopology");
3699
- function stableTopology(ids) {
3700
- return { ids };
3720
+ function stableTopology(ids, nextId) {
3721
+ return { ids, nextId };
3701
3722
  }
3702
3723
  __name(stableTopology, "stableTopology");
3703
3724
  function shapeTopology(vertices, edges, surfaces) {
@@ -3798,7 +3819,11 @@ function loftWithTopology(sections, spine, ruled) {
3798
3819
  ruled,
3799
3820
  holes.map((wires) => wires[0])
3800
3821
  );
3801
- return booleanWithTopology(outer, inner, "cut");
3822
+ return booleanWithTopology(
3823
+ { ...outer, namespace: "intermediate" },
3824
+ { ...inner, namespace: "intermediate" },
3825
+ "cut"
3826
+ );
3802
3827
  } finally {
3803
3828
  inner?.shape.delete();
3804
3829
  outer?.shape.delete();
@@ -6036,7 +6061,7 @@ function openShell(shape, topology, thickness, removedIds, sourceVolume) {
6036
6061
  return {
6037
6062
  shape: result,
6038
6063
  topology: transferShapeTopology(
6039
- [{ shape, topology, index: 1 }],
6064
+ [{ shape, topology, namespace: "preserve" }],
6040
6065
  result,
6041
6066
  builder
6042
6067
  )
@@ -6112,7 +6137,7 @@ function closedShell(shape, topology, thickness, sourceVolume) {
6112
6137
  return {
6113
6138
  shape: result,
6114
6139
  topology: transferShapeTopology(
6115
- [{ shape, topology, index: 1 }],
6140
+ [{ shape, topology, namespace: "preserve" }],
6116
6141
  result,
6117
6142
  cut2
6118
6143
  )
@@ -20264,6 +20289,7 @@ function inspectSurface(face) {
20264
20289
  __name(inspectSurface, "inspectSurface");
20265
20290
 
20266
20291
  // packages/core/src/library/runtime.ts
20292
+ var observeDistance;
20267
20293
  function isAxisSelection(pivot2) {
20268
20294
  return pivot2.kind === "axisLine" || pivot2.kind === "axisEdge";
20269
20295
  }
@@ -20279,10 +20305,16 @@ __name(mapSelectionReference, "mapSelectionReference");
20279
20305
  var activeRelate;
20280
20306
  var valueTraces = /* @__PURE__ */ new WeakMap();
20281
20307
  var operationTraces = /* @__PURE__ */ new WeakMap();
20282
- function beginModelEvaluation(checkCancelled) {
20308
+ function beginModelEvaluation(checkCancelled, distanceObserver) {
20283
20309
  valueTraces = /* @__PURE__ */ new WeakMap();
20284
20310
  operationTraces = /* @__PURE__ */ new WeakMap();
20285
- return beginKernelOperationEvaluation(checkCancelled);
20311
+ const previous = observeDistance;
20312
+ observeDistance = distanceObserver;
20313
+ const finish = beginKernelOperationEvaluation(checkCancelled);
20314
+ return () => {
20315
+ observeDistance = previous;
20316
+ finish();
20317
+ };
20286
20318
  }
20287
20319
  __name(beginModelEvaluation, "beginModelEvaluation");
20288
20320
  function valueTrace(value) {
@@ -20710,10 +20742,10 @@ function rotationPointReference(point3) {
20710
20742
  return reference;
20711
20743
  }
20712
20744
  __name(rotationPointReference, "rotationPointReference");
20713
- function rotationAxisReference(axis) {
20745
+ function straightAxisReference(axis, operation = "axisLine()") {
20714
20746
  const reference = anchorReference(axis);
20715
20747
  if (reference.kind !== "line")
20716
- throw new Error("axisLine() requires an axis or straight edge.");
20748
+ throw new Error(`${operation} requires an axis or straight edge.`);
20717
20749
  const geometry = reference.topology?.source[modelGeometry]()?.value ?? (reference.whole && isModelObject(reference.model) ? reference.model[modelGeometry]()?.value : void 0);
20718
20750
  if (geometry) {
20719
20751
  const straight = reference.topology ? withTopologyShape(
@@ -20724,12 +20756,12 @@ function rotationAxisReference(axis) {
20724
20756
  ) : geometry.shape.geomType === "LINE";
20725
20757
  if (!straight)
20726
20758
  throw new Error(
20727
- "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.`
20728
20760
  );
20729
20761
  }
20730
20762
  return reference;
20731
20763
  }
20732
- __name(rotationAxisReference, "rotationAxisReference");
20764
+ __name(straightAxisReference, "straightAxisReference");
20733
20765
  var PivotRotation = class extends RelationExpression {
20734
20766
  /** @internal */
20735
20767
  constructor(chain, selection, previous) {
@@ -20845,7 +20877,7 @@ __name(axisEdge, "axisEdge");
20845
20877
  function axisLine(axis) {
20846
20878
  return new AxisChain(new TransformationRotation(), {
20847
20879
  kind: "axisLine",
20848
- axis: rotationAxisReference(axis)
20880
+ axis: straightAxisReference(axis)
20849
20881
  });
20850
20882
  }
20851
20883
  __name(axisLine, "axisLine");
@@ -21736,33 +21768,64 @@ var ModelObject = class _ModelObject extends RelationObject {
21736
21768
  operation
21737
21769
  );
21738
21770
  }
21739
- /** Resolve both own and occurrence references into this model's local frame. */
21740
- 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) {
21741
21806
  const members = this.memberPoses();
21742
- const external = sources.map((source) => anchorReference(source).model).filter((model) => !members.has(model));
21807
+ const external = sources.filter((model) => !members.has(model));
21743
21808
  const context = external.length ? _ModelObject.createSolveContext([this, ...external]) : void 0;
21744
21809
  return sources.map((source) => {
21745
- const reference = anchorReference(source);
21746
- const {
21747
- kind,
21748
- transform: frame,
21749
- topology,
21750
- members: nested,
21751
- bound,
21752
- facing
21753
- } = source instanceof _ModelObject ? source.exposedElement() : reference;
21754
- const memberPose = members.get(reference.model);
21810
+ const memberPose = members.get(source);
21755
21811
  if (memberPose === null)
21756
21812
  throw new Error(
21757
21813
  "The point or element belongs to multiple occurrences. Expose it through the intended child model's named reference."
21758
21814
  );
21759
- const transform = memberPose ?? relativeTransform(
21760
- reference.model.solvePose(context),
21761
- this.solvePose(context)
21762
- );
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];
21763
21826
  return transformElement(
21764
- { kind, transform: frame, topology, members: nested, bound, facing },
21765
- transform
21827
+ { kind, transform, topology, parts, members, bound, facing },
21828
+ transforms[index]
21766
21829
  );
21767
21830
  });
21768
21831
  }
@@ -21804,7 +21867,11 @@ var ModelObject = class _ModelObject extends RelationObject {
21804
21867
  /** @internal */
21805
21868
  exposedElement() {
21806
21869
  if (this.kind === "group")
21807
- return { ...this.geometryAnchor, members: this.elements };
21870
+ return {
21871
+ ...this.geometryAnchor,
21872
+ parts: this.geometryParts(),
21873
+ members: this.elements
21874
+ };
21808
21875
  const geometry = this.requireGeometry().value;
21809
21876
  const kind = this.kind === "face" ? "surface" : this.kind;
21810
21877
  const selection = kind === "solid" ? { kind } : {
@@ -22009,14 +22076,14 @@ var ModelObject = class _ModelObject extends RelationObject {
22009
22076
  storedOperation("scaled", [{ model: this, role: "source", index: 0 }])
22010
22077
  );
22011
22078
  }
22012
- extrude(distance3 = 10) {
22079
+ extrude(distance4 = 10) {
22013
22080
  if (this.kind !== "face")
22014
22081
  throw new Error("extrude requires a single face model.");
22015
- if (!Number.isFinite(distance3) || distance3 === 0)
22082
+ if (!Number.isFinite(distance4) || distance4 === 0)
22016
22083
  throw new Error("Extrusion distance must be finite and non-zero.");
22017
22084
  const source = this.requireGeometry();
22018
22085
  const direction = rotateVector(
22019
- [0, distance3, 0],
22086
+ [0, distance4, 0],
22020
22087
  this.geometryAnchor.transform.quaternion
22021
22088
  );
22022
22089
  const geometry = evaluateSolidGeometry(
@@ -22027,7 +22094,7 @@ var ModelObject = class _ModelObject extends RelationObject {
22027
22094
  {
22028
22095
  shape: source.value.shape,
22029
22096
  topology: source.value.topology,
22030
- index: 1
22097
+ namespace: 1
22031
22098
  },
22032
22099
  direction
22033
22100
  )
@@ -22094,8 +22161,8 @@ var ModelObject = class _ModelObject extends RelationObject {
22094
22161
  })
22095
22162
  );
22096
22163
  }
22097
- chamfer(distance3 = 1, edgeIds) {
22098
- assertPositive("distance", distance3);
22164
+ chamfer(distance4 = 1, edgeIds) {
22165
+ assertPositive("distance", distance4);
22099
22166
  const source = this.requireSolidGeometry();
22100
22167
  const selectedEdgeIds = resolveEdgeSelection(
22101
22168
  source.value.topology.edges,
@@ -22103,13 +22170,13 @@ var ModelObject = class _ModelObject extends RelationObject {
22103
22170
  );
22104
22171
  const geometry = evaluateSolidGeometry(
22105
22172
  "chamfer",
22106
- [distance3, selectedEdgeIds],
22173
+ [distance4, selectedEdgeIds],
22107
22174
  [source],
22108
22175
  () => {
22109
22176
  const result = chamferEdges(
22110
22177
  source.value.shape,
22111
22178
  source.value.topology,
22112
- distance3,
22179
+ distance4,
22113
22180
  selectedEdgeIds
22114
22181
  );
22115
22182
  return { shape: result.shape, topology: result.topology };
@@ -22423,12 +22490,12 @@ var ModelObject = class _ModelObject extends RelationObject {
22423
22490
  {
22424
22491
  shape: geometry.value.shape,
22425
22492
  topology: geometry.value.topology,
22426
- index: otherIndex === 0 ? 1 : void 0
22493
+ namespace: otherIndex === 0 ? 1 : "intermediate"
22427
22494
  },
22428
22495
  {
22429
22496
  shape: operand.value,
22430
22497
  topology: otherGeometry.value.topology,
22431
- index: otherIndex + 2
22498
+ namespace: otherIndex + 2
22432
22499
  },
22433
22500
  operation
22434
22501
  );
@@ -22477,6 +22544,176 @@ var ModelObject = class _ModelObject extends RelationObject {
22477
22544
  }
22478
22545
  return members;
22479
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 { nodeId: reference.model.nodeId, elements };
22626
+ });
22627
+ observeDistance(
22628
+ {
22629
+ ...result,
22630
+ axis: direction2,
22631
+ axisName: typeof axis === "string" ? axis : void 0,
22632
+ operands,
22633
+ placements: [...context.poses].map(([model, pose]) => ({
22634
+ nodeId: model.nodeId,
22635
+ transform: toTransform(pose)
22636
+ }))
22637
+ },
22638
+ [...context.poses.keys()]
22639
+ );
22640
+ }
22641
+ return result.value;
22642
+ }, "record");
22643
+ if (axis === void 0) {
22644
+ let result;
22645
+ for (const a2 of first)
22646
+ for (const b2 of second) {
22647
+ const candidate = "points" in a2 && a2.points.length === 1 && "points" in b2 && b2.points.length === 1 ? {
22648
+ value: Math.hypot(
22649
+ ...a2.points[0].map((value, i) => value - b2.points[0][i])
22650
+ ),
22651
+ start: a2.points[0],
22652
+ end: b2.points[0]
22653
+ } : distanceBetweenParts(a2, b2).value;
22654
+ if (!result || candidate.value < result.value) result = candidate;
22655
+ if (result.value === 0) return record(result);
22656
+ }
22657
+ return record(result);
22658
+ }
22659
+ const direction = axisReference ? rotateVector(
22660
+ [0, 1, 0],
22661
+ composeTransforms(
22662
+ axisReference.model.solvePose(context),
22663
+ axisReference.transform
22664
+ ).quaternion
22665
+ ) : typeof axis === "string" ? distanceAxes[axis] : axis;
22666
+ assertFiniteVector("distance() axis", direction);
22667
+ const magnitude = Math.hypot(...direction);
22668
+ if (magnitude === 0) throw new Error("distance() axis must be non-zero.");
22669
+ const normalized = direction.map(
22670
+ (value) => value / magnitude
22671
+ );
22672
+ const project = invertTransform(frameFromYAxis(origin, normalized));
22673
+ const projectedBounds = /* @__PURE__ */ __name((parts2) => {
22674
+ const bounds = parts2.map((part) => {
22675
+ if ("points" in part)
22676
+ return pointBounds(
22677
+ part.points.map(
22678
+ (point3) => composeTransforms(project, translation(point3)).position
22679
+ )
22680
+ );
22681
+ return evaluateSnapshotQuery(
22682
+ boundsQuery(
22683
+ part.geometry,
22684
+ composeTransforms(project, part.transform),
22685
+ part.selection,
22686
+ part.scale
22687
+ )
22688
+ );
22689
+ });
22690
+ return [
22691
+ [0, 1, 2].map(
22692
+ (i) => Math.min(...bounds.map((value) => value[0][i]))
22693
+ ),
22694
+ [0, 1, 2].map(
22695
+ (i) => Math.max(...bounds.map((value) => value[1][i]))
22696
+ )
22697
+ ];
22698
+ }, "projectedBounds");
22699
+ const aBounds = projectedBounds(first), bBounds = projectedBounds(second);
22700
+ const [aMin, aMax] = [aBounds[0][1], aBounds[1][1]], [bMin, bMax] = [bBounds[0][1], bBounds[1][1]];
22701
+ const overlap2 = (Math.max(aMin, bMin) + Math.min(aMax, bMax)) / 2;
22702
+ const [start, end] = bMin > aMax ? [aMax, bMin] : aMin > bMax ? [aMin, bMax] : [overlap2, overlap2];
22703
+ const center = [0, 1, 2].map(
22704
+ (i) => (Math.max(aBounds[0][i], bBounds[0][i]) + Math.min(aBounds[1][i], bBounds[1][i])) / 2
22705
+ );
22706
+ const unproject = invertTransform(project);
22707
+ const at = /* @__PURE__ */ __name((y) => composeTransforms(unproject, translation([center[0], y, center[2]])).position, "at");
22708
+ return record(
22709
+ {
22710
+ value: Math.max(0, bMin - aMax, aMin - bMax),
22711
+ start: at(start),
22712
+ end: at(end)
22713
+ },
22714
+ normalized
22715
+ );
22716
+ }
22480
22717
  /** Bounds of the selected finite geometry after a rigid transform. */
22481
22718
  [referenceBoundsParts](reference, transform) {
22482
22719
  if (reference.bound)
@@ -22508,6 +22745,15 @@ var ModelObject = class _ModelObject extends RelationObject {
22508
22745
  )
22509
22746
  ];
22510
22747
  }
22748
+ if (reference.parts)
22749
+ return reference.parts.map(
22750
+ (part) => boundsQuery(
22751
+ part.source.requireGeometry(),
22752
+ composeTransforms(transform, part.transform),
22753
+ part.selection,
22754
+ part.scale
22755
+ )
22756
+ );
22511
22757
  return [{ bounds: super[referenceBounds](reference, transform) }];
22512
22758
  }
22513
22759
  /** Bounds of the selected finite geometry after a rigid transform. */
@@ -22587,7 +22833,7 @@ var ModelObject = class _ModelObject extends RelationObject {
22587
22833
  );
22588
22834
  }
22589
22835
  edgeReference(id) {
22590
- return { ...rotationAxisReference(this.edge(id)), model: void 0 };
22836
+ return { ...straightAxisReference(this.edge(id)), model: void 0 };
22591
22837
  }
22592
22838
  vertexPosition(id) {
22593
22839
  return anchorReference(this.vertex(id)).transform.position;
@@ -22951,11 +23197,11 @@ function assertCoilClearance(radius, wireRadius, pitch, turns) {
22951
23197
  else upper = middle;
22952
23198
  }
22953
23199
  const separation = Math.min(fullTurn * turns, (lower + upper) / 2);
22954
- const distance3 = Math.hypot(
23200
+ const distance4 = Math.hypot(
22955
23201
  2 * radius * Math.sin(separation / 2),
22956
23202
  pitch * separation / fullTurn
22957
23203
  );
22958
- if (distance3 <= 2 * wireRadius) {
23204
+ if (distance4 <= 2 * wireRadius) {
22959
23205
  throw new Error(
22960
23206
  "Coil turns must not touch or overlap; increase pitch or decrease wireRadius."
22961
23207
  );
@@ -23148,12 +23394,16 @@ function group(children, name = "Group") {
23148
23394
  });
23149
23395
  }
23150
23396
  __name(group, "group");
23397
+ function distance2(a, b, axis) {
23398
+ return ModelObject.distance(a, b, axis);
23399
+ }
23400
+ __name(distance2, "distance");
23151
23401
  function union(operands) {
23152
23402
  const { first, others } = booleanOperands("union", operands);
23153
23403
  return first[combineModels]("fuse", others);
23154
23404
  }
23155
23405
  __name(union, "union");
23156
- function extrude(face, distance3 = 10) {
23406
+ function extrude(face, distance4 = 10) {
23157
23407
  const faces = (Array.isArray(face) ? face : [face]).map(
23158
23408
  (value) => requireModelKind(
23159
23409
  value,
@@ -23161,7 +23411,7 @@ function extrude(face, distance3 = 10) {
23161
23411
  "extrude requires a face model or an array of face models."
23162
23412
  )
23163
23413
  );
23164
- const solids = faces.map((value) => value.extrude(distance3));
23414
+ const solids = faces.map((value) => value.extrude(distance4));
23165
23415
  return Array.isArray(face) ? solids : solids[0];
23166
23416
  }
23167
23417
  __name(extrude, "extrude");
@@ -23261,6 +23511,83 @@ function relatedModelObjects(object) {
23261
23511
  return object.relatedObjects();
23262
23512
  }
23263
23513
  __name(relatedModelObjects, "relatedModelObjects");
23514
+ var distanceAxes = { x: [1, 0, 0], y: [0, 1, 0], z: [0, 0, 1] };
23515
+ function withDistanceShape(part, visit) {
23516
+ if (!("points" in part))
23517
+ return withTransformedGeometry(part.geometry.value, part, visit);
23518
+ const points = part.points;
23519
+ if (points.length <= 2) {
23520
+ const shape = points.length === 1 ? makeVertex(toPoint(points[0])) : makeLine2(toPoint(points[0]), toPoint(points[1]));
23521
+ try {
23522
+ return visit(shape);
23523
+ } finally {
23524
+ shape.delete();
23525
+ }
23526
+ }
23527
+ const edges = [];
23528
+ let wire;
23529
+ let face;
23530
+ try {
23531
+ points.forEach(
23532
+ (point3, index) => edges.push(
23533
+ makeLine2(toPoint(point3), toPoint(points[(index + 1) % points.length]))
23534
+ )
23535
+ );
23536
+ wire = assembleWire(edges);
23537
+ face = makeFace(wire);
23538
+ return visit(face);
23539
+ } finally {
23540
+ face?.delete();
23541
+ wire?.delete();
23542
+ edges.forEach((edge) => edge.delete());
23543
+ }
23544
+ }
23545
+ __name(withDistanceShape, "withDistanceShape");
23546
+ var distanceBetweenParts = cachedArtifact(
23547
+ (a, b) => withDistanceShape(
23548
+ a,
23549
+ (left) => withDistanceShape(b, (right) => {
23550
+ const query = new (getOC12()).BRepExtrema_DistShapeShape();
23551
+ try {
23552
+ query.LoadS1(left.wrapped);
23553
+ query.LoadS2(right.wrapped);
23554
+ query.Perform();
23555
+ if (!query.IsDone())
23556
+ throw new Error("distance() could not measure these geometries.");
23557
+ const first = query.PointOnShape1(1), second = query.PointOnShape2(1);
23558
+ try {
23559
+ return {
23560
+ value: query.Value(),
23561
+ start: [first.X(), first.Y(), first.Z()],
23562
+ end: [second.X(), second.Y(), second.Z()]
23563
+ };
23564
+ } finally {
23565
+ first.delete();
23566
+ second.delete();
23567
+ }
23568
+ } finally {
23569
+ query.delete();
23570
+ }
23571
+ })
23572
+ ),
23573
+ {
23574
+ key: /* @__PURE__ */ __name((a, b) => kernelOperationKey(
23575
+ "distance-witnesses",
23576
+ [a, b].map(
23577
+ (part) => "points" in part ? ["points", part.points] : [
23578
+ "shape",
23579
+ part.geometry.id,
23580
+ part.selection.kind,
23581
+ part.selection.kind === "solid" ? null : part.selection.id,
23582
+ part.transform.position,
23583
+ part.transform.quaternion,
23584
+ part.scale
23585
+ ]
23586
+ ),
23587
+ [a, b].flatMap((part) => "points" in part ? [] : [part.geometry])
23588
+ ), "key")
23589
+ }
23590
+ );
23264
23591
  function boundsQuery(geometry, transform, selection = { kind: "solid" }, scale3 = 1) {
23265
23592
  return {
23266
23593
  inputId: geometry.id,
@@ -23375,7 +23702,7 @@ function planModelSnapshotQueries(objects) {
23375
23702
  sourceRef,
23376
23703
  weight: (1 + (input.geometry.topology?.edges.ids.length ?? 0) + (input.geometry.topology?.surfaces.ids.length ?? 0)) * queries.length,
23377
23704
  encode: /* @__PURE__ */ __name(() => encodeKernelArtifact(id, input.geometry), "encode"),
23378
- accept: /* @__PURE__ */ __name((query, value) => snapshotQuery.accept(query.key, value), "accept")
23705
+ accept: /* @__PURE__ */ __name((query, value, milliseconds) => snapshotQuery.accept(query.key, value, milliseconds), "accept")
23379
23706
  }));
23380
23707
  }
23381
23708
  __name(planModelSnapshotQueries, "planModelSnapshotQueries");
@@ -23389,7 +23716,9 @@ function executeSnapshotQueryBatch(id, bytes, queries, checkCancelled, onResult,
23389
23716
  onRestore?.(performance.now() - started);
23390
23717
  for (const query of queries) {
23391
23718
  checkCancelled();
23392
- onResult(query, computeSnapshotQuery(geometry, query));
23719
+ const started2 = performance.now();
23720
+ const value = computeSnapshotQuery(geometry, query);
23721
+ onResult(query, value, performance.now() - started2);
23393
23722
  }
23394
23723
  } finally {
23395
23724
  geometry.shape.delete();
@@ -23457,7 +23786,7 @@ var authoringApi = Object.freeze({
23457
23786
  pivotPoint,
23458
23787
  axisEdge,
23459
23788
  axisLine,
23460
- cached,
23789
+ cache,
23461
23790
  font,
23462
23791
  googleFont,
23463
23792
  text,
@@ -23481,6 +23810,7 @@ var authoringApi = Object.freeze({
23481
23810
  frustum,
23482
23811
  regularPrism,
23483
23812
  group,
23813
+ distance: distance2,
23484
23814
  union,
23485
23815
  cut,
23486
23816
  intersect
@@ -23585,15 +23915,15 @@ function evaluateSolidGeometry(operation, arguments_, inputs, compute) {
23585
23915
  );
23586
23916
  }
23587
23917
  __name(evaluateSolidGeometry, "evaluateSolidGeometry");
23588
- function renderMesh(artifact, shape, cache, tolerance2, topology) {
23589
- const cached2 = cache.get(shape);
23590
- if (cached2) {
23591
- return cached2;
23918
+ function renderMesh(artifact, shape, cache2, tolerance2, topology) {
23919
+ const cached = cache2.get(shape);
23920
+ if (cached) {
23921
+ return cached;
23592
23922
  }
23593
23923
  const mesh = evaluateSnapshotQuery(
23594
23924
  meshQuery(artifact, shape, tolerance2, topology)
23595
23925
  );
23596
- cache.set(shape, mesh);
23926
+ cache2.set(shape, mesh);
23597
23927
  return mesh;
23598
23928
  }
23599
23929
  __name(renderMesh, "renderMesh");
@@ -23778,7 +24108,7 @@ function buildLoftGeometry(sections, spine, ruled) {
23778
24108
  section.transform
23779
24109
  ),
23780
24110
  topology: section.geometry.value.topology,
23781
- index: index + 1
24111
+ namespace: index + 1
23782
24112
  });
23783
24113
  }
23784
24114
  if (spine) {
@@ -23858,6 +24188,10 @@ function axisAlignedBounds(bounds, transform) {
23858
24188
  }
23859
24189
  __name(axisAlignedBounds, "axisAlignedBounds");
23860
24190
  function computeTransformedBounds(geometry, query) {
24191
+ return withTransformedGeometry(geometry, query, shapeBounds);
24192
+ }
24193
+ __name(computeTransformedBounds, "computeTransformedBounds");
24194
+ function withTransformedGeometry(geometry, query, visit) {
23861
24195
  const { transform, selection, scale: scale3 } = query;
23862
24196
  return withTopologyShape(
23863
24197
  geometry.shape,
@@ -23868,7 +24202,7 @@ function computeTransformedBounds(geometry, query) {
23868
24202
  try {
23869
24203
  const moved = shapeWithTransform(scaled, transform);
23870
24204
  try {
23871
- return shapeBounds(moved);
24205
+ return visit(moved);
23872
24206
  } finally {
23873
24207
  moved.delete();
23874
24208
  }
@@ -23878,7 +24212,7 @@ function computeTransformedBounds(geometry, query) {
23878
24212
  }
23879
24213
  );
23880
24214
  }
23881
- __name(computeTransformedBounds, "computeTransformedBounds");
24215
+ __name(withTransformedGeometry, "withTransformedGeometry");
23882
24216
  function pointBounds(points) {
23883
24217
  if (!points.length)
23884
24218
  throw new Error("Empty geometry has no directional bounds.");
@@ -23980,6 +24314,10 @@ function transformElement(element, transform) {
23980
24314
  ...element.topology,
23981
24315
  transform: composeTransforms(transform, element.topology.transform)
23982
24316
  } : void 0,
24317
+ parts: element.parts?.map((part) => ({
24318
+ ...part,
24319
+ transform: composeTransforms(transform, part.transform)
24320
+ })),
23983
24321
  members: element.members ? Object.fromEntries(
23984
24322
  Object.entries(element.members).map(([name, member]) => [
23985
24323
  name,
@@ -24004,6 +24342,11 @@ function scaleElement(element, factor) {
24004
24342
  scale: element.topology.scale * factor,
24005
24343
  transform: scaleFrame(element.topology.transform, factor)
24006
24344
  } : void 0,
24345
+ parts: element.parts?.map((part) => ({
24346
+ ...part,
24347
+ scale: part.scale * factor,
24348
+ transform: scaleFrame(part.transform, factor)
24349
+ })),
24007
24350
  members: element.members ? scaleElements(element.members, factor) : void 0,
24008
24351
  transform: {
24009
24352
  ...element.transform,
@@ -24655,8 +24998,8 @@ function sketchPointResolver(layers) {
24655
24998
  const visiting = /* @__PURE__ */ new Set();
24656
24999
  const resolve = /* @__PURE__ */ __name((ref) => {
24657
25000
  const key = JSON.stringify([ref.layer, ref.id]);
24658
- const cached2 = resolved.get(key);
24659
- if (cached2) return cached2;
25001
+ const cached = resolved.get(key);
25002
+ if (cached) return cached;
24660
25003
  const point3 = points.get(key);
24661
25004
  if (!point3) throw new Error(`Missing sketch point ${ref.id}.`);
24662
25005
  if (visiting.has(key))
@@ -24971,7 +25314,7 @@ __name(solvedProblem, "solvedProblem");
24971
25314
 
24972
25315
  // packages/core/src/library/sketch-regions.ts
24973
25316
  var tau2 = 2 * Math.PI;
24974
- var distance2 = /* @__PURE__ */ __name((a, b) => Math.hypot(a[0] - b[0], a[1] - b[1]), "distance");
25317
+ var distance3 = /* @__PURE__ */ __name((a, b) => Math.hypot(a[0] - b[0], a[1] - b[1]), "distance");
24975
25318
  function sketchRegions(layers) {
24976
25319
  const resolve = sketchPointResolver(layers);
24977
25320
  const points = new Map(
@@ -24993,7 +25336,7 @@ function sketchRegions(layers) {
24993
25336
  const vertices = [];
24994
25337
  const vertex = /* @__PURE__ */ __name((position2, tolerance2) => {
24995
25338
  const index = vertices.findIndex(
24996
- (v) => distance2(v.position, position2) <= Math.min(v.tolerance, tolerance2)
25339
+ (v) => distance3(v.position, position2) <= Math.min(v.tolerance, tolerance2)
24997
25340
  );
24998
25341
  if (index >= 0) return index;
24999
25342
  vertices.push({ position: position2, tolerance: tolerance2, edges: [] });
@@ -25023,7 +25366,7 @@ function sketchRegions(layers) {
25023
25366
  ) || contacts.some(
25024
25367
  (c) => c.parameters.some((_, j) => {
25025
25368
  const curve = j === 0 ? a.curve : b.curve;
25026
- return curve.kind === "circle" || distance2(c.position, sketchCurvePosition(curve, 0)) > sketchCurveTolerance(curve) && distance2(c.position, sketchCurvePosition(curve, 1)) > sketchCurveTolerance(curve);
25369
+ return curve.kind === "circle" || distance3(c.position, sketchCurvePosition(curve, 0)) > sketchCurveTolerance(curve) && distance3(c.position, sketchCurvePosition(curve, 1)) > sketchCurveTolerance(curve);
25027
25370
  })
25028
25371
  ))
25029
25372
  throw new Error(
@@ -25098,7 +25441,7 @@ function overlap(a, b, intersections) {
25098
25441
  if (a.kind === "line" || b.kind === "line") {
25099
25442
  if (a.kind !== "line" || b.kind !== "line" || intersections.length < 2)
25100
25443
  return false;
25101
- } 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)))
25444
+ } 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)))
25102
25445
  return false;
25103
25446
  const cuts = [0, ...intersections, 1].sort((a2, b2) => a2 - b2);
25104
25447
  return cuts.slice(1).some(
@@ -25161,10 +25504,11 @@ export {
25161
25504
  clearKernelOperationCache,
25162
25505
  kernelOperationCacheStats,
25163
25506
  setKernelCacheBudget,
25507
+ setKernelCachePersistenceThreshold,
25164
25508
  setKernelArtifactStore,
25165
25509
  setKernelExternalBytes,
25166
25510
  identifyCachedFunction,
25167
- cached,
25511
+ cache,
25168
25512
  googleFontUrl,
25169
25513
  googleFontSources,
25170
25514
  installFontEngine,
@@ -25231,6 +25575,7 @@ export {
25231
25575
  regularPrism,
25232
25576
  primitiveConstructor,
25233
25577
  group,
25578
+ distance2 as distance,
25234
25579
  union,
25235
25580
  extrude,
25236
25581
  text,
@@ -25266,4 +25611,4 @@ export {
25266
25611
  assertSketchDragConnections,
25267
25612
  solveSketchSnapshot
25268
25613
  };
25269
- //# sourceMappingURL=chunk-T3RAUNSF.js.map
25614
+ //# sourceMappingURL=chunk-MICNLFBF.js.map