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

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 (40) hide show
  1. package/README.md +31 -3
  2. package/bld/chunks/{chunk-U5G2VU2I.js → chunk-3RIMOCPP.js} +2 -2
  3. package/bld/chunks/{chunk-P5VGKAEM.js → chunk-KYW4AWKO.js} +2 -2
  4. package/bld/chunks/{chunk-RPY2WVCB.js → chunk-T3RAUNSF.js} +941 -506
  5. package/bld/chunks/chunk-T3RAUNSF.js.map +7 -0
  6. package/bld/chunks/{chunk-BF55AIEQ.js → chunk-T6COR2SG.js} +3 -3
  7. package/bld/library/bound-solver.d.ts +29 -5
  8. package/bld/library/bound-solver.d.ts.map +1 -1
  9. package/bld/library/index.d.ts +2 -2
  10. package/bld/library/index.d.ts.map +1 -1
  11. package/bld/library/index.js +15 -1
  12. package/bld/library/kernel-cache.d.ts +2 -1
  13. package/bld/library/kernel-cache.d.ts.map +1 -1
  14. package/bld/library/relation-solver.d.ts +12 -7
  15. package/bld/library/relation-solver.d.ts.map +1 -1
  16. package/bld/library/replicad.js +2 -2
  17. package/bld/library/runtime.d.ts +214 -76
  18. package/bld/library/runtime.d.ts.map +1 -1
  19. package/bld/library/sketch.d.ts +2 -2
  20. package/bld/library/sketch.d.ts.map +1 -1
  21. package/bld/library/topology.d.ts +2 -0
  22. package/bld/library/topology.d.ts.map +1 -1
  23. package/bld/node/index.js +17 -3
  24. package/bld/node/replicad.js +4 -4
  25. package/bld/tooling/index.d.ts +3 -3
  26. package/bld/tooling/index.d.ts.map +1 -1
  27. package/bld/tooling/index.js +16 -10
  28. package/package.json +1 -1
  29. package/src/library/bound-solver.ts +103 -73
  30. package/src/library/index.ts +13 -2
  31. package/src/library/kernel-cache.ts +8 -0
  32. package/src/library/relation-solver.ts +194 -110
  33. package/src/library/runtime.ts +1029 -424
  34. package/src/library/sketch.ts +3 -3
  35. package/src/library/topology.ts +13 -2
  36. package/src/tooling/index.ts +14 -7
  37. package/bld/chunks/chunk-RPY2WVCB.js.map +0 -7
  38. /package/bld/chunks/{chunk-U5G2VU2I.js.map → chunk-3RIMOCPP.js.map} +0 -0
  39. /package/bld/chunks/{chunk-P5VGKAEM.js.map → chunk-KYW4AWKO.js.map} +0 -0
  40. /package/bld/chunks/{chunk-BF55AIEQ.js.map → chunk-T6COR2SG.js.map} +0 -0
@@ -132,12 +132,12 @@ var binaryTypes = {
132
132
  };
133
133
  function encodeKernelArtifact(signature, value) {
134
134
  const chunks = [];
135
- let offset = 0;
135
+ let offset2 = 0;
136
136
  const references2 = /* @__PURE__ */ new Map();
137
137
  function binary(kind, bytes2) {
138
- const node = [kind, [offset, bytes2.byteLength]];
138
+ const node = [kind, [offset2, bytes2.byteLength]];
139
139
  chunks.push(bytes2);
140
- offset += bytes2.byteLength;
140
+ offset2 += bytes2.byteLength;
141
141
  return node;
142
142
  }
143
143
  __name(binary, "binary");
@@ -202,13 +202,13 @@ function encodeKernelArtifact(signature, value) {
202
202
  }
203
203
  __name(encodeObject, "encodeObject");
204
204
  const metadata = encoder.encode(JSON.stringify([signature, encode(value)]));
205
- const bytes = new Uint8Array(4 + metadata.byteLength + offset);
205
+ const bytes = new Uint8Array(4 + metadata.byteLength + offset2);
206
206
  new DataView(bytes.buffer).setUint32(0, metadata.byteLength, true);
207
207
  bytes.set(metadata, 4);
208
- offset = 4 + metadata.byteLength;
208
+ offset2 = 4 + metadata.byteLength;
209
209
  for (const chunk of chunks) {
210
- bytes.set(chunk, offset);
211
- offset += chunk.byteLength;
210
+ bytes.set(chunk, offset2);
211
+ offset2 += chunk.byteLength;
212
212
  }
213
213
  return bytes;
214
214
  }
@@ -280,20 +280,20 @@ function decodeKernelArtifact(bytes, signature) {
280
280
  return object;
281
281
  }
282
282
  case "view": {
283
- const [name, buffer, offset, length] = data;
283
+ const [name, buffer, offset2, length] = data;
284
284
  if (!Object.hasOwn(binaryTypes, name))
285
285
  throw new Error("Invalid kernel artifact view");
286
286
  return register(
287
- new binaryTypes[name](decode(buffer), offset, length)
287
+ new binaryTypes[name](decode(buffer), offset2, length)
288
288
  );
289
289
  }
290
290
  case "shape":
291
291
  case "ArrayBuffer": {
292
- const [offset, length] = data;
293
- if (!Number.isSafeInteger(offset) || !Number.isSafeInteger(length) || offset < 0 || length < 0 || start + offset + length > bytes.byteLength) {
292
+ const [offset2, length] = data;
293
+ if (!Number.isSafeInteger(offset2) || !Number.isSafeInteger(length) || offset2 < 0 || length < 0 || start + offset2 + length > bytes.byteLength) {
294
294
  throw new Error("Invalid kernel artifact binary range");
295
295
  }
296
- const chunk = bytes.slice(start + offset, start + offset + length);
296
+ const chunk = bytes.slice(start + offset2, start + offset2 + length);
297
297
  if (kind === "ArrayBuffer") return register(chunk.buffer);
298
298
  const shape = decodeShape(chunk);
299
299
  shapes.push(shape);
@@ -386,6 +386,11 @@ function createComputationCache({
386
386
  const persisted = /* @__PURE__ */ new Set();
387
387
  const pendingPersistence = /* @__PURE__ */ new Map();
388
388
  let externalBytes = 0;
389
+ function setKernelCacheBudget2(bytes) {
390
+ maximumBytes = bytes;
391
+ evictHistoricalEntries();
392
+ }
393
+ __name(setKernelCacheBudget2, "setKernelCacheBudget");
389
394
  function setKernelExternalBytes2(bytes) {
390
395
  externalBytes = bytes;
391
396
  evictHistoricalEntries();
@@ -624,6 +629,7 @@ function createComputationCache({
624
629
  beginKernelOperationEvaluation: beginKernelOperationEvaluation2,
625
630
  clearKernelOperationCache: clearKernelOperationCache2,
626
631
  kernelOperationCacheStats: kernelOperationCacheStats2,
632
+ setKernelCacheBudget: setKernelCacheBudget2,
627
633
  setKernelArtifactStore: setKernelArtifactStore2,
628
634
  findKernelOperation: findKernelOperation2,
629
635
  findKernelOperations: findKernelOperations2,
@@ -642,6 +648,7 @@ var {
642
648
  beginKernelOperationEvaluation,
643
649
  clearKernelOperationCache,
644
650
  kernelOperationCacheStats,
651
+ setKernelCacheBudget,
645
652
  setKernelArtifactStore,
646
653
  findKernelOperation,
647
654
  findKernelOperations,
@@ -1974,12 +1981,12 @@ function candidates(first, second, tolerance2) {
1974
1981
  const gap = Math.abs(perpendicular2);
1975
1982
  if (gap > second.radius + tolerance2) return [];
1976
1983
  const normalized = gap / second.radius;
1977
- const offset = Math.abs(gap - second.radius) <= tolerance2 ? 0 : second.radius * Math.sqrt(Math.max(0, (1 - normalized) * (1 + normalized)));
1984
+ const offset2 = Math.abs(gap - second.radius) <= tolerance2 ? 0 : second.radius * Math.sqrt(Math.max(0, (1 - normalized) * (1 + normalized)));
1978
1985
  const foot2 = [
1979
1986
  second.center[0] + perpendicular2 * unit3[1],
1980
1987
  second.center[1] - perpendicular2 * unit3[0]
1981
1988
  ];
1982
- return [-offset, offset].map((along2) => [
1989
+ return [-offset2, offset2].map((along2) => [
1983
1990
  foot2[0] + along2 * unit3[0],
1984
1991
  foot2[1] + along2 * unit3[1]
1985
1992
  ]);
@@ -2001,9 +2008,9 @@ function candidates(first, second, tolerance2) {
2001
2008
  first.center[0] + along * scale3 * unit2[0],
2002
2009
  first.center[1] + along * scale3 * unit2[1]
2003
2010
  ];
2004
- return [-height, height].map((offset) => [
2005
- foot[0] - offset * unit2[1],
2006
- foot[1] + offset * unit2[0]
2011
+ return [-height, height].map((offset2) => [
2012
+ foot[0] - offset2 * unit2[1],
2013
+ foot[1] + offset2 * unit2[0]
2007
2014
  ]);
2008
2015
  }
2009
2016
  __name(candidates, "candidates");
@@ -3333,7 +3340,17 @@ function stableEdgeGroups(shape, topology, groups) {
3333
3340
  topology,
3334
3341
  groups.map((group2) => group2.edgeId)
3335
3342
  );
3336
- return groups.map((group2, index) => ({ ...group2, edgeId: ids[index] }));
3343
+ const linear = new Map(
3344
+ edges.map((edge) => [
3345
+ edge.hashCode,
3346
+ edge.geomType === "LINE"
3347
+ ])
3348
+ );
3349
+ return groups.map((group2, index) => ({
3350
+ ...group2,
3351
+ edgeId: ids[index],
3352
+ linear: linear.get(group2.edgeId) ?? false
3353
+ }));
3337
3354
  } finally {
3338
3355
  deleteShapes(edges);
3339
3356
  }
@@ -5163,6 +5180,28 @@ function modelMaterialColor(snapshot) {
5163
5180
  __name(modelMaterialColor, "modelMaterialColor");
5164
5181
 
5165
5182
  // packages/core/src/library/bound-solver.ts
5183
+ function externalRotationFrame(action, self, reference, inverse = false) {
5184
+ if ("axis" in action) {
5185
+ const axis = composeTransforms(rotation(reference), action.axis);
5186
+ return {
5187
+ center: axis.position,
5188
+ quaternion: axisRotation(axis, inverse ? -action.angle : action.angle).quaternion
5189
+ };
5190
+ }
5191
+ const local = inverse ? invertTransform(rotation(action.rotation)) : rotation(action.rotation);
5192
+ const before = inverse ? composeTransforms(rotation(self), local) : rotation(self);
5193
+ return {
5194
+ center: addVectors(
5195
+ rotateVector(action.point, reference),
5196
+ rotateVector(action.displacement, before.quaternion)
5197
+ ),
5198
+ quaternion: composeTransforms(
5199
+ composeTransforms(before, local),
5200
+ invertTransform(before)
5201
+ ).quaternion
5202
+ };
5203
+ }
5204
+ __name(externalRotationFrame, "externalRotationFrame");
5166
5205
  var axes = [
5167
5206
  [1, 0, 0],
5168
5207
  [0, 1, 0],
@@ -5194,28 +5233,15 @@ function solveBodies(bodies) {
5194
5233
  "Cyclic external rotation axes cannot determine an authored orientation."
5195
5234
  );
5196
5235
  visiting.add(index);
5197
- const candidates2 = bodies[index].relations.filter((r) => r.rotations.length).map((relation) => {
5198
- let pose2 = identityRigidTransform;
5199
- for (const action of relation.rotations) {
5200
- pose2 = "local" in action ? composeTransforms(pose2, action.local) : composeTransforms(
5201
- axisRotation(
5202
- composeTransforms(seed2(action.body), action.axis),
5203
- action.angle
5204
- ),
5205
- pose2
5206
- );
5207
- }
5208
- return pose2;
5209
- });
5210
- const pose = candidates2[0] ?? identityRigidTransform;
5211
- if (candidates2.some(
5212
- (candidate) => !transformsAreEquivalent(
5213
- rotation(candidate.quaternion),
5214
- rotation(pose.quaternion)
5215
- )
5216
- )) {
5217
- throw new Error(
5218
- `Conflicting explicit rotations for ${bodies[index].name}.`
5236
+ let pose = bodies[index].initial ?? identityRigidTransform;
5237
+ for (const action of bodies[index].transformations ?? []) {
5238
+ if ("offset" in action) continue;
5239
+ pose = "local" in action ? composeTransforms(pose, action.local) : "point" in action ? composeTransforms(pose, rotation(action.rotation)) : composeTransforms(
5240
+ axisRotation(
5241
+ composeTransforms(seed2(action.body), action.axis),
5242
+ action.angle
5243
+ ),
5244
+ pose
5219
5245
  );
5220
5246
  }
5221
5247
  visiting.delete(index);
@@ -5232,17 +5258,18 @@ function solveBodies(bodies) {
5232
5258
  const known = positioned.get(index);
5233
5259
  if (known) return known;
5234
5260
  let pose = {
5235
- quaternion: identityRigidTransform.quaternion,
5261
+ quaternion: (bodies[index].initial ?? identityRigidTransform).quaternion,
5236
5262
  position: {
5237
- constant: origin,
5263
+ constant: (bodies[index].initial ?? identityRigidTransform).position,
5238
5264
  columns: variables.flatMap(
5239
5265
  (body) => axes.map((axis) => body === index ? axis : origin)
5240
5266
  )
5241
5267
  }
5242
5268
  };
5243
- const actions = bodies[index].relations.find((relation) => relation.rotations.length)?.rotations ?? [];
5244
- for (const action of actions) {
5245
- if ("local" in action) {
5269
+ for (const action of bodies[index].transformations ?? []) {
5270
+ if ("offset" in action) {
5271
+ pose = { ...pose, position: shiftPoint(pose.position, action.offset) };
5272
+ } else if ("local" in action) {
5246
5273
  pose = {
5247
5274
  position: shiftPoint(
5248
5275
  pose.position,
@@ -5255,12 +5282,12 @@ function solveBodies(bodies) {
5255
5282
  };
5256
5283
  } else {
5257
5284
  const axisPose = positionBody(action.body);
5258
- const axis = composeTransforms(
5259
- rotation(axisPose.quaternion),
5260
- action.axis
5285
+ const { center, quaternion: q } = externalRotationFrame(
5286
+ action,
5287
+ pose.quaternion,
5288
+ axisPose.quaternion
5261
5289
  );
5262
- const q = axisRotation(axis, action.angle).quaternion;
5263
- const axisPoint = shiftPoint(axisPose.position, axis.position);
5290
+ const axisPoint = shiftPoint(axisPose.position, center);
5264
5291
  const rotatedAxis = rotatePoint(axisPoint, q);
5265
5292
  pose = {
5266
5293
  position: addPoints(
@@ -5282,12 +5309,16 @@ function solveBodies(bodies) {
5282
5309
  const preferences = /* @__PURE__ */ new Map();
5283
5310
  bodies.forEach(
5284
5311
  (body, owner) => body.relations.forEach((relation) => {
5285
- const baseline = undoRotations(poses[owner], relation.rotations, poses);
5312
+ const baseline = undoActions(
5313
+ poses[owner],
5314
+ body.transformations ?? [],
5315
+ poses
5316
+ );
5286
5317
  for (let axis = 0; axis < 3; axis++) {
5287
5318
  const preference = {
5288
5319
  id: relation.id,
5289
5320
  coefficients: baseline.position.columns.map((column) => column[axis]),
5290
- value: -baseline.position.constant[axis]
5321
+ value: (body.initial?.position[axis] ?? 0) - baseline.position.constant[axis]
5291
5322
  };
5292
5323
  preferences.set(
5293
5324
  JSON.stringify([preference.coefficients, preference.value]),
@@ -5315,14 +5346,8 @@ function solveBodies(bodies) {
5315
5346
  source.position,
5316
5347
  rotateVector(center, targetFrame.quaternion)
5317
5348
  );
5318
- const targetPoint = shiftPoint(
5319
- target.position,
5320
- addVectors(
5321
- targetFrame.position,
5322
- rotateVector(relation.offset ?? origin, targetFrame.quaternion)
5323
- )
5324
- );
5325
- for (const localAxis of relation.offset ? axes : [axes[1]]) {
5349
+ const targetPoint = shiftPoint(target.position, targetFrame.position);
5350
+ for (const localAxis of [axes[1]]) {
5326
5351
  const normal = rotateVector(localAxis, targetFrame.quaternion);
5327
5352
  equations.push({
5328
5353
  id: relation.id,
@@ -5357,8 +5382,8 @@ function solveBodies(bodies) {
5357
5382
  }));
5358
5383
  }
5359
5384
  __name(solveBodies, "solveBodies");
5360
- function shiftPoint(point3, offset) {
5361
- return { ...point3, constant: addVectors(point3.constant, offset) };
5385
+ function shiftPoint(point3, offset2) {
5386
+ return { ...point3, constant: addVectors(point3.constant, offset2) };
5362
5387
  }
5363
5388
  __name(shiftPoint, "shiftPoint");
5364
5389
  function rotatePoint(point3, quaternion) {
@@ -5377,9 +5402,14 @@ function addPoints(left, right) {
5377
5402
  };
5378
5403
  }
5379
5404
  __name(addPoints, "addPoints");
5380
- function undoRotations(pose, actions, poses) {
5405
+ function undoActions(pose, actions, poses) {
5381
5406
  for (const action of [...actions].reverse()) {
5382
- if ("local" in action) {
5407
+ if ("offset" in action) {
5408
+ pose = {
5409
+ ...pose,
5410
+ position: shiftPoint(pose.position, negateVector(action.offset))
5411
+ };
5412
+ } else if ("local" in action) {
5383
5413
  const inverse = invertTransform(action.local);
5384
5414
  pose = {
5385
5415
  position: shiftPoint(
@@ -5393,12 +5423,13 @@ function undoRotations(pose, actions, poses) {
5393
5423
  };
5394
5424
  } else {
5395
5425
  const axisPose = poses[action.body];
5396
- const axis = composeTransforms(
5397
- rotation(axisPose.quaternion),
5398
- action.axis
5426
+ const { center, quaternion: q } = externalRotationFrame(
5427
+ action,
5428
+ pose.quaternion,
5429
+ axisPose.quaternion,
5430
+ true
5399
5431
  );
5400
- const q = axisRotation(axis, -action.angle).quaternion;
5401
- const axisPoint = shiftPoint(axisPose.position, axis.position);
5432
+ const axisPoint = shiftPoint(axisPose.position, center);
5402
5433
  const rotatedAxis = rotatePoint(axisPoint, q);
5403
5434
  pose = {
5404
5435
  position: addPoints(
@@ -5414,7 +5445,7 @@ function undoRotations(pose, actions, poses) {
5414
5445
  }
5415
5446
  return pose;
5416
5447
  }
5417
- __name(undoRotations, "undoRotations");
5448
+ __name(undoActions, "undoActions");
5418
5449
  function solveTranslations(equations, preferences, dimension) {
5419
5450
  const basis = [];
5420
5451
  const magnitude = Math.max(
@@ -5503,54 +5534,40 @@ function solveTranslations(equations, preferences, dimension) {
5503
5534
  __name(solveTranslations, "solveTranslations");
5504
5535
 
5505
5536
  // packages/core/src/library/relation-solver.ts
5506
- function beforeRelation(pose, relation, poses, owner) {
5507
- for (const action of [...relation.rotations].reverse())
5508
- pose = "local" in action ? composeTransforms(pose, invertTransform(action.local)) : composeTransforms(
5509
- axisRotation(
5510
- composeTransforms(poses[action.body], action.axis),
5511
- -action.angle
5512
- ),
5513
- pose
5514
- );
5515
- if (relation.kind === "align" && relation.offset) {
5516
- const target = relation.target.body === owner ? pose : poses[relation.target.body];
5517
- const frame = composeTransforms(target, relation.target.transform);
5518
- pose = {
5519
- ...pose,
5520
- position: subtract2(
5521
- pose.position,
5522
- rotateVector(relation.offset, frame.quaternion)
5523
- )
5524
- };
5537
+ function applyExternalRotation(pose, action, reference, inverse = false) {
5538
+ const { center, quaternion } = externalRotationFrame(
5539
+ action,
5540
+ pose.quaternion,
5541
+ reference.quaternion,
5542
+ inverse
5543
+ );
5544
+ const point3 = addVectors(reference.position, center);
5545
+ return composeTransforms(
5546
+ { quaternion, position: subtract2(point3, rotateVector(point3, quaternion)) },
5547
+ pose
5548
+ );
5549
+ }
5550
+ __name(applyExternalRotation, "applyExternalRotation");
5551
+ function beforeTransformations(pose, relation, poses) {
5552
+ for (const action of [...relation.actions].reverse()) {
5553
+ if ("offset" in action) {
5554
+ pose = { ...pose, position: subtract2(pose.position, action.offset) };
5555
+ } else
5556
+ pose = "local" in action ? composeTransforms(pose, invertTransform(action.local)) : applyExternalRotation(pose, action, poses[action.body], true);
5525
5557
  }
5526
5558
  return pose;
5527
5559
  }
5528
- __name(beforeRelation, "beforeRelation");
5529
- function afterRelation(pose, relation, poses, owner) {
5530
- if (relation.kind === "align" && relation.offset) {
5531
- const target = relation.target.body === owner ? pose : poses[relation.target.body];
5532
- pose = {
5533
- ...pose,
5534
- position: addVectors(
5535
- pose.position,
5536
- rotateVector(
5537
- relation.offset,
5538
- composeTransforms(target, relation.target.transform).quaternion
5539
- )
5540
- )
5541
- };
5560
+ __name(beforeTransformations, "beforeTransformations");
5561
+ function afterTransformations(pose, relation, poses) {
5562
+ for (const action of relation.actions) {
5563
+ if ("offset" in action) {
5564
+ pose = { ...pose, position: addVectors(pose.position, action.offset) };
5565
+ } else
5566
+ pose = "local" in action ? composeTransforms(pose, action.local) : applyExternalRotation(pose, action, poses[action.body]);
5542
5567
  }
5543
- for (const action of relation.rotations)
5544
- pose = "local" in action ? composeTransforms(pose, action.local) : composeTransforms(
5545
- axisRotation(
5546
- composeTransforms(poses[action.body], action.axis),
5547
- action.angle
5548
- ),
5549
- pose
5550
- );
5551
5568
  return pose;
5552
5569
  }
5553
- __name(afterRelation, "afterRelation");
5570
+ __name(afterTransformations, "afterTransformations");
5554
5571
  function directionRotation(from, to) {
5555
5572
  const product = dot(from, to);
5556
5573
  if (product < -1 + 1e-10) return [...perpendicular(from), 0];
@@ -5673,8 +5690,19 @@ function solveBodies2(bodies) {
5673
5690
  if (relation.kind === "align")
5674
5691
  validateAlignment(relation.source.geometry, relation.target.geometry);
5675
5692
  const active = bodies.flatMap(
5676
- (body, i) => body.relations.length ? [i] : []
5693
+ (body, i) => body.relations.length || body.transformations?.length ? [i] : []
5677
5694
  );
5695
+ const unplace = /* @__PURE__ */ __name((pose, owner, poses2) => beforeTransformations(
5696
+ pose,
5697
+ { actions: bodies[owner].transformations ?? [] },
5698
+ poses2
5699
+ ), "unplace");
5700
+ const place = /* @__PURE__ */ __name((pose, owner, poses2) => afterTransformations(
5701
+ pose,
5702
+ { actions: bodies[owner].transformations ?? [] },
5703
+ poses2
5704
+ ), "place");
5705
+ const baselinePose = /* @__PURE__ */ __name((pose, poses2, owner) => unplace(pose, owner, poses2), "baselinePose");
5678
5706
  const flexible = bodies.map(
5679
5707
  (body) => body.relations.some((r) => r.kind === "align")
5680
5708
  );
@@ -5685,26 +5713,13 @@ function solveBodies2(bodies) {
5685
5713
  const unique = [
5686
5714
  ...new Map(relations.map((entry) => [key(entry), entry])).entries()
5687
5715
  ].sort(([a], [b]) => a.localeCompare(b)).map(([, entry]) => entry);
5688
- let poses = bodies.map(() => identityRigidTransform);
5689
- for (const owner of active) {
5690
- const authored = unique.find(
5691
- (entry) => entry.owner === owner && entry.relation.rotations.length
5692
- )?.relation ?? unique.find(
5693
- (entry) => entry.owner === owner && entry.relation.kind === "align" && entry.relation.offset
5694
- )?.relation;
5695
- if (authored)
5696
- poses[owner] = afterRelation(
5697
- identityRigidTransform,
5698
- authored,
5699
- poses,
5700
- owner
5701
- );
5702
- }
5716
+ let poses = bodies.map((body) => body.initial ?? identityRigidTransform);
5717
+ for (const owner of active) poses[owner] = place(poses[owner], owner, poses);
5703
5718
  for (const { owner, relation } of unique) {
5704
5719
  if (relation.kind !== "align") continue;
5705
5720
  const sourceSelf = relation.source.body === owner;
5706
5721
  if (relation.target.body === relation.source.body) continue;
5707
- const baseline = beforeRelation(poses[owner], relation, poses, owner);
5722
+ const baseline = baselinePose(poses[owner], poses, owner);
5708
5723
  const source = transformGeometry(
5709
5724
  relation.source.geometry,
5710
5725
  relation.source.body === owner ? baseline : poses[relation.source.body]
@@ -5713,12 +5728,7 @@ function solveBodies2(bodies) {
5713
5728
  relation.target.body === owner ? baseline : poses[relation.target.body]
5714
5729
  );
5715
5730
  const seed2 = sourceSelf ? alignmentSeed(source, target) : alignmentSeed(target, source);
5716
- poses[owner] = afterRelation(
5717
- composeTransforms(seed2, baseline),
5718
- relation,
5719
- poses,
5720
- owner
5721
- );
5731
+ poses[owner] = place(composeTransforms(seed2, baseline), owner, poses);
5722
5732
  }
5723
5733
  const geometryScale = Math.max(
5724
5734
  1,
@@ -5728,12 +5738,23 @@ function solveBodies2(bodies) {
5728
5738
  ) : []
5729
5739
  )
5730
5740
  );
5731
- const variables = active.flatMap(
5732
- (body) => Array.from({ length: 6 }, (_, axis) => ({ body, axis }))
5741
+ const fullVariables = active.flatMap(
5742
+ (body) => (
5743
+ // Bound-only bodies without authored rotations have exactly identity
5744
+ // orientation. Do not numerically perturb those fixed axes: native bound
5745
+ // queries can introduce noise that prevents convergence at the identity.
5746
+ Array.from(
5747
+ {
5748
+ length: flexible[body] || bodies[body].transformations?.some((action) => !("offset" in action)) ? 6 : 3
5749
+ },
5750
+ (_, axis) => ({ body, axis })
5751
+ )
5752
+ )
5733
5753
  );
5754
+ let variables = fullVariables.filter((variable) => variable.axis < 3);
5734
5755
  const residual2 = /* @__PURE__ */ __name((candidate) => [
5735
5756
  ...unique.flatMap(({ owner, relation: r }) => {
5736
- const baseline = beforeRelation(candidate[owner], r, candidate, owner);
5757
+ const baseline = baselinePose(candidate[owner], candidate, owner);
5737
5758
  const source = r.source.body === owner ? baseline : candidate[r.source.body], target = r.target.body === owner ? baseline : candidate[r.target.body];
5738
5759
  if (r.kind === "align")
5739
5760
  return alignmentResidual(
@@ -5755,27 +5776,25 @@ function solveBodies2(bodies) {
5755
5776
  source.position,
5756
5777
  rotateVector(center, frame.quaternion)
5757
5778
  );
5758
- const b = composeTransforms(
5759
- frame,
5760
- translation(r.offset ?? origin)
5761
- ).position;
5779
+ const b = frame.position;
5762
5780
  const delta = rotateVector(
5763
5781
  subtract2(a, b),
5764
5782
  invertTransform(frame).quaternion
5765
5783
  );
5766
- return r.offset ? [...delta] : [delta[1]];
5784
+ return [delta[1]];
5767
5785
  }),
5768
5786
  ...active.flatMap((owner) => {
5769
5787
  if (flexible[owner]) return [];
5770
- const rotations = bodies[owner].relations.filter((r) => r.rotations.length);
5771
- return rotations.length ? rotations.flatMap(
5772
- (r) => beforeRelation(
5773
- candidate[owner],
5774
- r,
5775
- candidate,
5776
- owner
5777
- ).quaternion.slice(0, 3)
5778
- ) : candidate[owner].quaternion.slice(0, 3);
5788
+ const initial = bodies[owner].initial ?? identityRigidTransform;
5789
+ const unplaced = unplace(candidate[owner], owner, candidate);
5790
+ const orientationResidual = /* @__PURE__ */ __name((pose) => composeTransforms(
5791
+ invertTransform(rotation(initial.quaternion)),
5792
+ rotation(pose.quaternion)
5793
+ ).quaternion.slice(0, 3), "orientationResidual");
5794
+ return [
5795
+ ...bodies[owner].relations.length ? [] : subtract2(unplaced.position, initial.position),
5796
+ ...orientationResidual(unplaced)
5797
+ ];
5779
5798
  })
5780
5799
  ], "residual");
5781
5800
  const perturb = /* @__PURE__ */ __name((base, steps) => {
@@ -5795,10 +5814,61 @@ function solveBodies2(bodies) {
5795
5814
  };
5796
5815
  });
5797
5816
  }, "perturb");
5817
+ const settleBoundTranslations = /* @__PURE__ */ __name(() => {
5818
+ if (!active.some((owner) => !flexible[owner])) return poses;
5819
+ const free = active.flatMap((body) => [0, 1, 2].map((axis) => ({ body, axis })));
5820
+ if (!free.length) return poses;
5821
+ const shift = /* @__PURE__ */ __name((steps) => {
5822
+ const result = poses.map((pose) => ({
5823
+ ...pose,
5824
+ position: [...pose.position]
5825
+ }));
5826
+ free.forEach(({ body, axis }, i) => {
5827
+ result[body].position[axis] += steps[i];
5828
+ });
5829
+ return result;
5830
+ }, "shift");
5831
+ const preferences = /* @__PURE__ */ __name((candidate2) => unique.filter(({ owner }) => !flexible[owner]).flatMap(
5832
+ ({ owner }) => subtract2(
5833
+ baselinePose(candidate2[owner], candidate2, owner).position,
5834
+ bodies[owner].initial?.position ?? [0, 0, 0]
5835
+ )
5836
+ ), "preferences");
5837
+ const h = 1e-5;
5838
+ const initial = residual2(poses), preference = preferences(poses);
5839
+ const columns = free.map((_, i) => {
5840
+ const plus = shift(free.map((_2, j) => i === j ? h : 0));
5841
+ const minus = shift(free.map((_2, j) => i === j ? -h : 0));
5842
+ const a = residual2(plus), b = residual2(minus), p = preferences(plus), q = preferences(minus);
5843
+ return {
5844
+ constraints: a.map((v, j) => (v - b[j]) / (2 * h)),
5845
+ preferences: p.map((v, j) => (v - q[j]) / (2 * h))
5846
+ };
5847
+ });
5848
+ const step = solveTranslations(
5849
+ initial.map((value, i) => ({
5850
+ id: "bound translation",
5851
+ value: -value,
5852
+ coefficients: columns.map((column) => column.constraints[i])
5853
+ })),
5854
+ preference.map((value, i) => ({
5855
+ id: "pre-action position",
5856
+ value: -value,
5857
+ coefficients: columns.map((column) => column.preferences[i])
5858
+ })),
5859
+ free.length
5860
+ );
5861
+ const candidate = shift(step);
5862
+ return residual2(candidate).every((value) => Math.abs(value) < 1e-8) ? candidate : poses;
5863
+ }, "settleBoundTranslations");
5798
5864
  let values = residual2(poses), damping = 1e-5;
5799
5865
  const norm = /* @__PURE__ */ __name((values2) => values2.reduce((sum, x) => sum + x * x, 0), "norm");
5800
- for (let iteration = 0; iteration < 160; iteration++) {
5801
- if (values.every((v) => Math.abs(v) < 1e-8)) return poses;
5866
+ for (let iteration = 0; iteration < 168; iteration++) {
5867
+ if (iteration === 8) {
5868
+ variables = fullVariables;
5869
+ damping = 1e-5;
5870
+ }
5871
+ if (values.every((v) => Math.abs(v) < 1e-8)) return settleBoundTranslations();
5802
5872
  const h = 1e-6;
5803
5873
  const columns = variables.map((_, i) => {
5804
5874
  const shifted = residual2(
@@ -5807,7 +5877,13 @@ function solveBodies2(bodies) {
5807
5877
  variables.map((_2, j) => i === j ? h : 0)
5808
5878
  )
5809
5879
  );
5810
- return values.map((v, j) => (shifted[j] - v) / h);
5880
+ const opposite = residual2(
5881
+ perturb(
5882
+ poses,
5883
+ variables.map((_2, j) => i === j ? -h : 0)
5884
+ )
5885
+ );
5886
+ return values.map((_2, j) => (shifted[j] - opposite[j]) / (2 * h));
5811
5887
  });
5812
5888
  const multiply2 = /* @__PURE__ */ __name((a, b) => a.reduce((sum, x, i) => sum + x * b[i], 0), "multiply");
5813
5889
  const matrix = columns.map((a, i) => [
@@ -5830,10 +5906,10 @@ __name(solveBodies2, "solveBodies");
5830
5906
  function eliminate(matrix) {
5831
5907
  const n = matrix.length;
5832
5908
  for (let i = 0; i < n; i++) {
5833
- let pivot = i;
5909
+ let pivot2 = i;
5834
5910
  for (let j = i + 1; j < n; j++)
5835
- if (Math.abs(matrix[j][i]) > Math.abs(matrix[pivot][i])) pivot = j;
5836
- [matrix[i], matrix[pivot]] = [matrix[pivot], matrix[i]];
5911
+ if (Math.abs(matrix[j][i]) > Math.abs(matrix[pivot2][i])) pivot2 = j;
5912
+ [matrix[i], matrix[pivot2]] = [matrix[pivot2], matrix[i]];
5837
5913
  for (let j = i + 1; j < n; j++) {
5838
5914
  const factor = matrix[j][i] / matrix[i][i];
5839
5915
  for (let k = i; k <= n; k++) matrix[j][k] -= factor * matrix[i][k];
@@ -5977,10 +6053,10 @@ function openShell(shape, topology, thickness, removedIds, sourceVolume) {
5977
6053
  __name(openShell, "openShell");
5978
6054
  function closedShell(shape, topology, thickness, sourceVolume) {
5979
6055
  const oc = getOC8();
5980
- const offset = new oc.BRepOffsetAPI_MakeOffsetShape();
6056
+ const offset2 = new oc.BRepOffsetAPI_MakeOffsetShape();
5981
6057
  let parallel;
5982
6058
  try {
5983
- offset.PerformByJoin(
6059
+ offset2.PerformByJoin(
5984
6060
  shape.wrapped,
5985
6061
  -thickness,
5986
6062
  tolerance,
@@ -5990,9 +6066,9 @@ function closedShell(shape, topology, thickness, sourceVolume) {
5990
6066
  oc.GeomAbs_JoinType.GeomAbs_Arc,
5991
6067
  false
5992
6068
  );
5993
- if (!offset.IsDone())
6069
+ if (!offset2.IsDone())
5994
6070
  throw new Error("OpenCascade could not build the cavity offset.");
5995
- const raw = castOwnedShape3D(offset.Shape());
6071
+ const raw = castOwnedShape3D(offset2.Shape());
5996
6072
  try {
5997
6073
  parallel = singleSolid(raw);
5998
6074
  } finally {
@@ -6049,7 +6125,7 @@ function closedShell(shape, topology, thickness, sourceVolume) {
6049
6125
  }
6050
6126
  } finally {
6051
6127
  parallel?.delete();
6052
- offset.delete();
6128
+ offset2.delete();
6053
6129
  }
6054
6130
  }
6055
6131
  __name(closedShell, "closedShell");
@@ -17942,19 +18018,19 @@ function getAxisAlignedRayLoopIntersections(loop, p, dir) {
17942
18018
  }
17943
18019
  const ts7 = [];
17944
18020
  let f8;
17945
- let offset;
18021
+ let offset2;
17946
18022
  let axis;
17947
18023
  const dirIsDecreasing = dir === "left" || dir === "up";
17948
18024
  if (dir === "left" || dir === "right") {
17949
18025
  f8 = /* @__PURE__ */ __name((ps2) => toPowerBasis(ps2)[1], "f");
17950
- offset = [0, -y];
18026
+ offset2 = [0, -y];
17951
18027
  axis = 0;
17952
18028
  } else {
17953
18029
  f8 = /* @__PURE__ */ __name((ps2) => toPowerBasis(ps2)[0], "f");
17954
- offset = [-x, 0];
18030
+ offset2 = [-x, 0];
17955
18031
  axis = 1;
17956
18032
  }
17957
- const translatedPs = ps.map(translate(offset));
18033
+ const translatedPs = ps.map(translate(offset2));
17958
18034
  const poly = f8(translatedPs);
17959
18035
  const ts_ = roots(poly, 0 - DELTA, 1 + DELTA)?.map((r) => r.t) || [];
17960
18036
  for (let i2 = 0; i2 < ts_.length; i2++) {
@@ -19930,9 +20006,9 @@ import {
19930
20006
  measureShapeSurfaceProperties
19931
20007
  } from "replicad";
19932
20008
  function inspectShapeTopology(original, topology, options = {}) {
19933
- const offset = options.offset ?? 0;
20009
+ const offset2 = options.offset ?? 0;
19934
20010
  const limit = options.limit ?? 48;
19935
- if (!Number.isSafeInteger(offset) || offset < 0 || !Number.isSafeInteger(limit) || limit < 1 || limit > 200)
20011
+ if (!Number.isSafeInteger(offset2) || offset2 < 0 || !Number.isSafeInteger(limit) || limit < 1 || limit > 200)
19936
20012
  throw new Error(
19937
20013
  "Topology pages require a nonnegative offset and a limit from 1 to 200."
19938
20014
  );
@@ -19980,7 +20056,7 @@ function inspectShapeTopology(original, topology, options = {}) {
19980
20056
  (id, index) => !options.ids || options.ids.some((requested) => sameTopologyId(requested, id)) ? [{ kind, id, index }] : []
19981
20057
  )
19982
20058
  );
19983
- const page = all.slice(offset, offset + limit);
20059
+ const page = all.slice(offset2, offset2 + limit);
19984
20060
  const childIndices = /* @__PURE__ */ __name((parent, kind) => {
19985
20061
  const children = shapeSubshapes(parent, kind);
19986
20062
  try {
@@ -20059,8 +20135,8 @@ function inspectShapeTopology(original, topology, options = {}) {
20059
20135
  method: "kernel-enclosure"
20060
20136
  },
20061
20137
  total: all.length,
20062
- offset,
20063
- ...offset + page.length < all.length ? { nextOffset: offset + page.length } : {},
20138
+ offset: offset2,
20139
+ ...offset2 + page.length < all.length ? { nextOffset: offset2 + page.length } : {},
20064
20140
  items,
20065
20141
  units: "model",
20066
20142
  coordinates: "scene",
@@ -20188,6 +20264,18 @@ function inspectSurface(face) {
20188
20264
  __name(inspectSurface, "inspectSurface");
20189
20265
 
20190
20266
  // packages/core/src/library/runtime.ts
20267
+ function isAxisSelection(pivot2) {
20268
+ return pivot2.kind === "axisLine" || pivot2.kind === "axisEdge";
20269
+ }
20270
+ __name(isAxisSelection, "isAxisSelection");
20271
+ function selectionReference(pivot2) {
20272
+ return pivot2.kind === "axisLine" ? pivot2.axis : pivot2.kind === "pivotPoint" ? pivot2.point : void 0;
20273
+ }
20274
+ __name(selectionReference, "selectionReference");
20275
+ function mapSelectionReference(pivot2, map) {
20276
+ return pivot2.kind === "axisLine" ? { ...pivot2, axis: map(pivot2.axis) } : pivot2.kind === "pivotPoint" ? { ...pivot2, point: map(pivot2.point) } : pivot2;
20277
+ }
20278
+ __name(mapSelectionReference, "mapSelectionReference");
20191
20279
  var activeRelate;
20192
20280
  var valueTraces = /* @__PURE__ */ new WeakMap();
20193
20281
  var operationTraces = /* @__PURE__ */ new WeakMap();
@@ -20208,6 +20296,7 @@ function valueTrace(value) {
20208
20296
  __name(valueTrace, "valueTrace");
20209
20297
  function transformSolveContext(context, transform) {
20210
20298
  return {
20299
+ frame: composeTransforms(transform, context.frame),
20211
20300
  poses: new Map(
20212
20301
  [...context.poses].map(([model, pose]) => [
20213
20302
  model,
@@ -20436,7 +20525,7 @@ function modelTopologyReference(value) {
20436
20525
  __name(modelTopologyReference, "modelTopologyReference");
20437
20526
  function modelTopologyIds(value, kind) {
20438
20527
  const topology = value instanceof ModelTopologyElement ? value[anchorReferenceValue].topology : void 0;
20439
- const source = isModelObject(value) ? value : value instanceof ConstraintExpression ? value.self() : topology?.source;
20528
+ const source = isModelObject(value) ? value : value instanceof RelationExpression ? value.self() : topology?.source;
20440
20529
  const geometry = isModelObject(source) ? source[modelGeometry]()?.value : void 0;
20441
20530
  if (!geometry) return void 0;
20442
20531
  return topologyChildren(
@@ -20447,40 +20536,27 @@ function modelTopologyIds(value, kind) {
20447
20536
  );
20448
20537
  }
20449
20538
  __name(modelTopologyIds, "modelTopologyIds");
20450
- var ConstraintExpression = class {
20451
- constructor(kind, source, target, displacement, rotations, context, constraintId, previous) {
20452
- this.kind = kind;
20453
- this.source = source;
20454
- this.target = target;
20455
- this.displacement = displacement;
20456
- this.rotations = rotations;
20539
+ var RelationExpression = class {
20540
+ constructor(definition, context, relationId, previous, spatialOperation) {
20541
+ this.definition = definition;
20457
20542
  this.context = context;
20458
- this.constraintId = constraintId;
20543
+ this.relationId = relationId;
20544
+ this.spatialOperation = spatialOperation;
20459
20545
  valueTraces.set(this, {
20460
20546
  sourceRefs: [...previous ? valueTrace(previous).sourceRefs : []],
20461
20547
  parameters: [...previous ? valueTrace(previous).parameters : []]
20462
20548
  });
20463
20549
  }
20464
- kind;
20465
- source;
20466
- target;
20467
- displacement;
20468
- rotations;
20550
+ definition;
20469
20551
  context;
20470
- constraintId;
20552
+ relationId;
20553
+ spatialOperation;
20471
20554
  static {
20472
- __name(this, "ConstraintExpression");
20473
- }
20474
- get sourceRefs() {
20475
- return valueTrace(this).sourceRefs;
20476
- }
20477
- get parameters() {
20478
- return valueTrace(this).parameters;
20555
+ __name(this, "RelationExpression");
20479
20556
  }
20480
- spatialOperation;
20481
20557
  /** @internal */
20482
20558
  attachSource(sourceRef) {
20483
- const refs = this.sourceRefs;
20559
+ const refs = valueTrace(this).sourceRefs;
20484
20560
  if (!refs.some(
20485
20561
  (ref) => ref.file === sourceRef.file && ref.start === sourceRef.start && ref.end === sourceRef.end
20486
20562
  ))
@@ -20488,7 +20564,7 @@ var ConstraintExpression = class {
20488
20564
  }
20489
20565
  /** @internal */
20490
20566
  attachParameters(parameters) {
20491
- appendUniqueParameters(this.parameters, parameters);
20567
+ appendUniqueParameters(valueTrace(this).parameters, parameters);
20492
20568
  }
20493
20569
  /** @internal */
20494
20570
  self() {
@@ -20496,79 +20572,89 @@ var ConstraintExpression = class {
20496
20572
  }
20497
20573
  /** @internal */
20498
20574
  traceReference() {
20575
+ if (this.definition.kind === "transformation")
20576
+ return {
20577
+ kind: "transformation",
20578
+ transformationId: this.relationId,
20579
+ self: this.context?.self
20580
+ };
20499
20581
  const rebind = /* @__PURE__ */ __name((model) => model === this.context?.original ? this.context.self : model, "rebind");
20500
20582
  return {
20501
- constraintId: this.constraintId,
20502
- source: rebind(this.source.model),
20503
- target: rebind(this.target.model),
20583
+ kind: "constraint",
20584
+ constraintId: this.relationId,
20585
+ source: rebind(this.definition.source.model),
20586
+ target: rebind(this.definition.target.model),
20504
20587
  self: this.context?.self
20505
20588
  };
20506
20589
  }
20507
20590
  /** @internal */
20508
20591
  storeFor(model, original) {
20509
20592
  const isSelf = /* @__PURE__ */ __name((candidate) => candidate === model || candidate === original, "isSelf");
20510
- if (!isSelf(this.source.model) && !isSelf(this.target.model))
20511
- throw new Error(
20512
- "The constraint returned by relate() must involve self or the original receiver."
20513
- );
20514
20593
  const bind = /* @__PURE__ */ __name((reference) => ({
20515
20594
  ...reference,
20516
20595
  model: isSelf(reference.model) ? void 0 : reference.model
20517
20596
  }), "bind");
20518
- const stored = {
20519
- id: this.constraintId,
20520
- kind: this.kind,
20521
- source: bind(this.source),
20522
- target: bind(this.target),
20523
- offset: this.displacement,
20524
- rotations: this.rotations.map((action) => ({
20525
- ...action,
20526
- pivot: action.pivot.kind === "around" ? { ...action.pivot, axis: bind(action.pivot.axis) } : action.pivot
20527
- }))
20597
+ const definition = this.definition;
20598
+ if (definition.kind !== "transformation" && !isSelf(definition.source.model) && !isSelf(definition.target.model))
20599
+ throw new Error(
20600
+ "The constraint returned by relate() must involve self or the original receiver."
20601
+ );
20602
+ const stored = definition.kind === "transformation" ? {
20603
+ kind: "transformation",
20604
+ id: this.relationId,
20605
+ actions: definition.actions.map((action) => {
20606
+ const storedAction = {
20607
+ ...action,
20608
+ ..."pivot" in action ? {
20609
+ pivot: mapSelectionReference(action.pivot, bind)
20610
+ } : {}
20611
+ };
20612
+ valueTraces.set(storedAction, {
20613
+ sourceRefs: [...valueTrace(action).sourceRefs],
20614
+ parameters: [...valueTrace(action).parameters]
20615
+ });
20616
+ return storedAction;
20617
+ })
20618
+ } : {
20619
+ kind: definition.kind,
20620
+ id: this.relationId,
20621
+ source: bind(definition.source),
20622
+ target: bind(definition.target)
20528
20623
  };
20529
20624
  valueTraces.set(stored, {
20530
- sourceRefs: [...this.sourceRefs],
20531
- parameters: [...this.parameters]
20625
+ sourceRefs: [...valueTrace(this).sourceRefs],
20626
+ parameters: [...valueTrace(this).parameters]
20532
20627
  });
20533
20628
  return stored;
20534
20629
  }
20535
20630
  /** @internal */
20536
- preview() {
20631
+ preview(preceding = []) {
20537
20632
  if (!this.context) return void 0;
20538
- const pivot = this.spatialOperation?.pivot;
20539
- const selection = pivot?.kind === "around" ? {
20633
+ const pivot2 = this.spatialOperation?.pivot;
20634
+ const selection = pivot2 ? {
20540
20635
  ...this.spatialOperation,
20541
- pivot: {
20542
- ...pivot,
20543
- axis: {
20544
- ...pivot.axis,
20545
- model: pivot.axis.model === this.context.self || pivot.axis.model === this.context.original ? void 0 : pivot.axis.model
20546
- }
20547
- }
20636
+ pivot: mapSelectionReference(pivot2, (reference) => ({
20637
+ ...reference,
20638
+ model: reference.model === this.context.self || reference.model === this.context.original ? void 0 : reference.model
20639
+ }))
20548
20640
  } : this.spatialOperation;
20549
- return this.context.original[relationPreview](
20550
- this.context.self.nodeId,
20641
+ return this.context.self[previewRelation](
20551
20642
  this.storeFor(this.context.self, this.context.original),
20552
- selection
20643
+ selection,
20644
+ preceding
20553
20645
  );
20554
20646
  }
20647
+ /** @internal */
20648
+ continuationArguments() {
20649
+ return [this.definition, this.context, this.relationId, this];
20650
+ }
20555
20651
  };
20556
- var Constraint = class _Constraint extends ConstraintExpression {
20652
+ var Constraint = class _Constraint extends RelationExpression {
20557
20653
  static {
20558
20654
  __name(this, "Constraint");
20559
20655
  }
20560
- constructor(kind, source, target, displacement = void 0, rotations = [], context = activeRelate, constraintId = `constraint-${nextConstraintId++}`, previous, spatialOperation) {
20561
- super(
20562
- kind,
20563
- source,
20564
- target,
20565
- displacement,
20566
- rotations,
20567
- context,
20568
- constraintId,
20569
- previous
20570
- );
20571
- this.spatialOperation = spatialOperation;
20656
+ constructor(definition) {
20657
+ super(definition, activeRelate, `constraint-${nextConstraintId++}`);
20572
20658
  }
20573
20659
  /** @internal */
20574
20660
  static create(source, target, kind = "on") {
@@ -20578,132 +20664,210 @@ var Constraint = class _Constraint extends ConstraintExpression {
20578
20664
  throw new Error(
20579
20665
  "align() requires points, curves, or surfaces; select geometry on a solid or group."
20580
20666
  );
20581
- return new _Constraint(kind, source, target);
20582
- }
20583
- offset(x = 0, y = 0, z = 0) {
20584
- assertFiniteVector("offset", [x, y, z]);
20585
- return new _Constraint(
20586
- this.kind,
20587
- this.source,
20588
- this.target,
20589
- addVectors(this.displacement ?? origin, [x, y, z]),
20590
- this.rotations,
20591
- this.context,
20592
- this.constraintId,
20593
- this
20667
+ return new _Constraint({ kind, source, target });
20668
+ }
20669
+ };
20670
+ var Transformation = class extends RelationExpression {
20671
+ static {
20672
+ __name(this, "Transformation");
20673
+ }
20674
+ /** @internal */
20675
+ constructor(action, previous) {
20676
+ const args = previous?.continuationArguments();
20677
+ super(
20678
+ { kind: "transformation", actions: [action] },
20679
+ args ? args[1] : activeRelate,
20680
+ args?.[2] ?? `transformation-${nextConstraintId++}`,
20681
+ previous,
20682
+ "pivot" in action ? { kind: "rotate", pivot: action.pivot } : void 0
20594
20683
  );
20684
+ valueTraces.set(action, valueTrace(this));
20595
20685
  }
20596
- pivot([x = 0, y = 0, z = 0] = [0, 0, 0]) {
20597
- assertFiniteVector("pivot", [x, y, z]);
20598
- return new ConstraintPivotChain(this, { kind: "pivot", point: [x, y, z] });
20599
- }
20600
- /** @code3d.param id {kind: 'vertex', label: 'Pivot vertex'} */
20601
- pivotVertex(id) {
20602
- return new ConstraintPivotChain(this, { kind: "pivotVertex", id });
20603
- }
20604
- /** Select a positioned axis in the composition. */
20605
- around(axis) {
20606
- const reference = anchorReference(axis);
20607
- if (reference.kind !== "line")
20608
- throw new Error("around() requires an axis or straight edge.");
20609
- const geometry = reference.topology?.source[modelGeometry]()?.value ?? (reference.whole && isModelObject(reference.model) ? reference.model[modelGeometry]()?.value : void 0);
20610
- if (geometry) {
20611
- const straight = reference.topology ? withTopologyShape(
20612
- geometry.shape,
20613
- geometry.topology,
20614
- reference.topology.selection,
20615
- (shape) => shape.geomType === "LINE"
20616
- ) : geometry.shape.geomType === "LINE";
20617
- if (!straight)
20618
- throw new Error(
20619
- "around() requires a straight axis; curved edges do not define one rotation axis."
20620
- );
20621
- }
20622
- return new ConstraintAroundChain(this, { kind: "around", axis: reference });
20686
+ };
20687
+ var TransformationRotation = class extends RelationExpression {
20688
+ static {
20689
+ __name(this, "TransformationRotation");
20623
20690
  }
20624
- rotate(x = 0, y = 0, z = 0) {
20625
- return this.withRotation({ kind: "pivot", point: origin }, [x, y, z], this);
20691
+ constructor() {
20692
+ super(
20693
+ { kind: "transformation", actions: [] },
20694
+ activeRelate,
20695
+ `transformation-${nextConstraintId++}`
20696
+ );
20626
20697
  }
20627
- /** @internal */
20628
- withRotation(pivot, angles, previous) {
20698
+ withRotation(pivot2, angles, previous) {
20629
20699
  assertFiniteVector(
20630
20700
  "rotate",
20631
20701
  typeof angles === "number" ? [angles, 0, 0] : angles
20632
20702
  );
20633
- return new _Constraint(
20634
- this.kind,
20635
- this.source,
20636
- this.target,
20637
- this.displacement,
20638
- [...this.rotations, { pivot, angles }],
20639
- this.context,
20640
- this.constraintId,
20641
- previous,
20642
- { kind: "rotate", pivot }
20643
- );
20644
- }
20645
- /** @internal */
20646
- chainArguments() {
20647
- return [
20648
- this.kind,
20649
- this.source,
20650
- this.target,
20651
- this.displacement,
20652
- this.rotations,
20653
- this.context,
20654
- this.constraintId,
20655
- this
20656
- ];
20703
+ return new Transformation({ pivot: pivot2, angles }, previous);
20657
20704
  }
20658
20705
  };
20659
- var ConstraintPivotChain = class extends ConstraintExpression {
20706
+ function rotationPointReference(point3) {
20707
+ const reference = anchorReference(point3);
20708
+ if (reference.kind !== "point")
20709
+ throw new Error("pivotPoint() requires a point reference.");
20710
+ return reference;
20711
+ }
20712
+ __name(rotationPointReference, "rotationPointReference");
20713
+ function rotationAxisReference(axis) {
20714
+ const reference = anchorReference(axis);
20715
+ if (reference.kind !== "line")
20716
+ throw new Error("axisLine() requires an axis or straight edge.");
20717
+ const geometry = reference.topology?.source[modelGeometry]()?.value ?? (reference.whole && isModelObject(reference.model) ? reference.model[modelGeometry]()?.value : void 0);
20718
+ if (geometry) {
20719
+ const straight = reference.topology ? withTopologyShape(
20720
+ geometry.shape,
20721
+ geometry.topology,
20722
+ reference.topology.selection,
20723
+ (shape) => shape.geomType === "LINE"
20724
+ ) : geometry.shape.geomType === "LINE";
20725
+ if (!straight)
20726
+ throw new Error(
20727
+ "axisLine() requires a straight axis; curved edges do not define one rotation axis."
20728
+ );
20729
+ }
20730
+ return reference;
20731
+ }
20732
+ __name(rotationAxisReference, "rotationAxisReference");
20733
+ var PivotRotation = class extends RelationExpression {
20660
20734
  /** @internal */
20661
- constructor(constraint, selection) {
20662
- super(...constraint.chainArguments());
20663
- this.constraint = constraint;
20735
+ constructor(chain, selection, previous) {
20736
+ super(...(previous ?? chain).continuationArguments(), {
20737
+ kind: selection.offset ? "pivotOffset" : selection.kind,
20738
+ pivot: selection
20739
+ });
20740
+ this.chain = chain;
20664
20741
  this.selection = selection;
20665
- this.spatialOperation = { kind: selection.kind, pivot: selection };
20666
20742
  }
20667
- constraint;
20743
+ chain;
20668
20744
  selection;
20669
20745
  static {
20670
- __name(this, "ConstraintPivotChain");
20746
+ __name(this, "PivotRotation");
20671
20747
  }
20672
20748
  rotate(x = 0, y = 0, z = 0) {
20673
- return this.constraint.withRotation(this.selection, [x, y, z], this);
20749
+ return this.chain.withRotation(this.selection, [x, y, z], this);
20674
20750
  }
20675
20751
  };
20676
- var ConstraintAroundChain = class extends ConstraintExpression {
20752
+ var PivotChain = class extends PivotRotation {
20753
+ static {
20754
+ __name(this, "PivotChain");
20755
+ }
20756
+ pivotOffset(x = 0, y = 0, z = 0) {
20757
+ assertFiniteVector("pivotOffset", [x, y, z]);
20758
+ return new PivotRotation(
20759
+ this.chain,
20760
+ { ...this.selection, offset: [x, y, z] },
20761
+ this
20762
+ );
20763
+ }
20764
+ };
20765
+ var AxisRotation = class extends RelationExpression {
20677
20766
  /** @internal */
20678
- constructor(constraint, selection) {
20679
- super(...constraint.chainArguments());
20680
- this.constraint = constraint;
20767
+ constructor(chain, selection, previous) {
20768
+ super(...(previous ?? chain).continuationArguments(), {
20769
+ kind: selection.offset ? "axisOffset" : selection.kind,
20770
+ pivot: selection
20771
+ });
20772
+ this.chain = chain;
20681
20773
  this.selection = selection;
20682
- this.spatialOperation = { kind: selection.kind, pivot: selection };
20683
20774
  }
20684
- constraint;
20775
+ chain;
20685
20776
  selection;
20686
20777
  static {
20687
- __name(this, "ConstraintAroundChain");
20778
+ __name(this, "AxisRotation");
20688
20779
  }
20689
20780
  rotate(angle = 0) {
20690
- return this.constraint.withRotation(this.selection, angle, this);
20781
+ return this.chain.withRotation(this.selection, angle, this);
20691
20782
  }
20692
20783
  };
20784
+ var AxisChain = class extends AxisRotation {
20785
+ static {
20786
+ __name(this, "AxisChain");
20787
+ }
20788
+ axisOffset(x = 0, y = 0, z = 0) {
20789
+ assertFiniteVector("axisOffset", [x, y, z]);
20790
+ return new AxisRotation(
20791
+ this.chain,
20792
+ { ...this.selection, offset: [x, y, z] },
20793
+ this
20794
+ );
20795
+ }
20796
+ };
20797
+ function selectedAxis(axis, offset2) {
20798
+ return composeTransforms(axis.transform, translation(offset2 ?? origin));
20799
+ }
20800
+ __name(selectedAxis, "selectedAxis");
20801
+ function offset(x = 0, y = 0, z = 0) {
20802
+ assertFiniteVector("offset", [x, y, z]);
20803
+ return new Transformation({ offset: [x, y, z] });
20804
+ }
20805
+ __name(offset, "offset");
20806
+ function rotate2(x = 0, y = 0, z = 0) {
20807
+ assertFiniteVector("rotate", [x, y, z]);
20808
+ return new Transformation({
20809
+ pivot: { kind: "pivot", point: origin, implicit: true },
20810
+ angles: [x, y, z]
20811
+ });
20812
+ }
20813
+ __name(rotate2, "rotate");
20814
+ function pivot([x = 0, y = 0, z = 0] = origin) {
20815
+ assertFiniteVector("pivot", [x, y, z]);
20816
+ return new PivotChain(new TransformationRotation(), {
20817
+ kind: "pivot",
20818
+ point: [x, y, z]
20819
+ });
20820
+ }
20821
+ __name(pivot, "pivot");
20822
+ function pivotVertex(id) {
20823
+ assertTopologyId("vertex", id);
20824
+ return new PivotChain(new TransformationRotation(), {
20825
+ kind: "pivotVertex",
20826
+ id
20827
+ });
20828
+ }
20829
+ __name(pivotVertex, "pivotVertex");
20830
+ function pivotPoint(point3) {
20831
+ return new PivotChain(new TransformationRotation(), {
20832
+ kind: "pivotPoint",
20833
+ point: rotationPointReference(point3)
20834
+ });
20835
+ }
20836
+ __name(pivotPoint, "pivotPoint");
20837
+ function axisEdge(id) {
20838
+ assertTopologyId("edge", id);
20839
+ return new AxisChain(new TransformationRotation(), {
20840
+ kind: "axisEdge",
20841
+ id
20842
+ });
20843
+ }
20844
+ __name(axisEdge, "axisEdge");
20845
+ function axisLine(axis) {
20846
+ return new AxisChain(new TransformationRotation(), {
20847
+ kind: "axisLine",
20848
+ axis: rotationAxisReference(axis)
20849
+ });
20850
+ }
20851
+ __name(axisLine, "axisLine");
20693
20852
  var modelGeometry = /* @__PURE__ */ Symbol("modelGeometry");
20694
20853
  var referenceBounds = /* @__PURE__ */ Symbol("referenceBounds");
20695
20854
  var referenceBoundsParts = /* @__PURE__ */ Symbol("referenceBoundsParts");
20696
20855
  var modelSnapshotQueries = /* @__PURE__ */ Symbol("modelSnapshotQueries");
20697
- var relationPreview = /* @__PURE__ */ Symbol("relationPreview");
20856
+ var previewRelation = /* @__PURE__ */ Symbol("previewRelation");
20698
20857
  var RelationObject = class _RelationObject {
20699
20858
  static {
20700
20859
  __name(this, "RelationObject");
20701
20860
  }
20702
20861
  nodeId;
20703
- constraints;
20862
+ placements;
20863
+ get constraints() {
20864
+ return this.placements.filter(
20865
+ (value) => value.kind !== "transformation"
20866
+ );
20867
+ }
20704
20868
  constructor(init = {}) {
20705
20869
  this.nodeId = init.nodeId ?? `node-${nextNodeId++}`;
20706
- this.constraints = [...init.constraints ?? []];
20870
+ this.placements = [...init.placements ?? []];
20707
20871
  valueTraces.set(this, {
20708
20872
  sourceRefs: [...init.sourceRefs ?? []],
20709
20873
  parameters: [...init.parameters ?? []]
@@ -20715,6 +20879,17 @@ var RelationObject = class _RelationObject {
20715
20879
  get parameters() {
20716
20880
  return valueTrace(this).parameters;
20717
20881
  }
20882
+ edgeReference(_id) {
20883
+ throw new Error(
20884
+ "axisEdge() requires model topology. Use axisLine(lineRef) for a line reference."
20885
+ );
20886
+ }
20887
+ rotationAxis(pivot2) {
20888
+ return pivot2.kind === "axisLine" ? pivot2.axis : this.edgeReference(pivot2.id);
20889
+ }
20890
+ rotationPoint(pivot2) {
20891
+ return pivot2.kind === "pivot" ? pivot2.point : pivot2.kind === "pivotVertex" ? this.vertexPosition(pivot2.id) : pivot2.point.transform.position;
20892
+ }
20718
20893
  vertexPosition(_id) {
20719
20894
  throw new Error(
20720
20895
  "pivotVertex() requires model topology, not a sketch reference frame. Use pivot([x, y, z]) instead."
@@ -20730,15 +20905,15 @@ var RelationObject = class _RelationObject {
20730
20905
  } finally {
20731
20906
  activeRelate = previous;
20732
20907
  }
20733
- const constraints = Array.isArray(built) ? built : [built];
20734
- const stored = constraints.map((constraint) => {
20735
- if (!(constraint instanceof Constraint))
20908
+ const relations = Array.isArray(built) ? built : [built];
20909
+ const stored = relations.map((constraint) => {
20910
+ if (!(constraint instanceof Constraint) && !(constraint instanceof Transformation))
20736
20911
  throw new Error(
20737
- "relate() requires a completed Constraint; finish pivot/around with rotate()."
20912
+ "relate() requires a completed Constraint or Transformation; finish the pivot or axis selector with rotate()."
20738
20913
  );
20739
20914
  return constraint.storeFor(this, original);
20740
20915
  });
20741
- this.constraints.push(...stored);
20916
+ this.placements.push(...stored);
20742
20917
  return uniqueModels(stored.flatMap(constraintReferences));
20743
20918
  }
20744
20919
  [referenceBounds](reference, transform) {
@@ -20825,22 +21000,32 @@ var RelationObject = class _RelationObject {
20825
21000
  arrow
20826
21001
  };
20827
21002
  }
20828
- bodyRotations(constraint, indices) {
20829
- return constraint.rotations.map(({ pivot, angles }) => {
20830
- if (pivot.kind === "around") {
20831
- return pivot.axis.model ? {
20832
- body: indices.get(pivot.axis.model),
20833
- axis: pivot.axis.transform,
20834
- angle: angles * (pivot.axis.direction ?? 1)
21003
+ bodyActions(constraint, indices) {
21004
+ return constraint.actions.map((action) => {
21005
+ if ("offset" in action) return { offset: action.offset };
21006
+ const { pivot: pivot2, angles } = action;
21007
+ if (isAxisSelection(pivot2)) {
21008
+ const axis = this.rotationAxis(pivot2);
21009
+ return axis.model ? {
21010
+ body: indices.get(axis.model),
21011
+ axis: selectedAxis(axis, pivot2.offset),
21012
+ angle: angles * (axis.direction ?? 1)
20835
21013
  } : {
20836
21014
  local: axisRotation(
20837
- pivot.axis.transform,
20838
- angles * (pivot.axis.direction ?? 1)
21015
+ selectedAxis(axis, pivot2.offset),
21016
+ angles * (axis.direction ?? 1)
20839
21017
  )
20840
21018
  };
20841
21019
  }
21020
+ if (pivot2.kind === "pivotPoint" && pivot2.point.model)
21021
+ return {
21022
+ body: indices.get(pivot2.point.model),
21023
+ point: pivot2.point.transform.position,
21024
+ rotation: rotationAround(origin, angles).quaternion,
21025
+ displacement: pivot2.offset ?? origin
21026
+ };
20842
21027
  const frame = translation(
20843
- pivot.kind === "pivot" ? pivot.point : this.vertexPosition(pivot.id)
21028
+ addVectors(this.rotationPoint(pivot2), pivot2.offset ?? origin)
20844
21029
  );
20845
21030
  return {
20846
21031
  local: composeTransforms(
@@ -20851,90 +21036,329 @@ var RelationObject = class _RelationObject {
20851
21036
  });
20852
21037
  }
20853
21038
  /** @internal */
20854
- [relationPreview](nodeId, constraint, selection) {
21039
+ [previewRelation](constraint, selection, preceding = [], original) {
21040
+ let placements = [...(original ?? this).placements];
21041
+ const index = placements.findIndex((value) => value.id === constraint?.id);
21042
+ if (index < 0) {
21043
+ placements.push(
21044
+ ...preceding.map(
21045
+ (value) => value.storeFor(this, value.continuationArguments()[1].original)
21046
+ )
21047
+ );
21048
+ if (constraint) placements.push(constraint);
21049
+ } else if (constraint) {
21050
+ placements[index] = constraint;
21051
+ let end = index + 1;
21052
+ if (constraint.kind !== "transformation")
21053
+ while (end < placements.length && placements[end].kind !== "transformation")
21054
+ end++;
21055
+ placements = placements.slice(0, end);
21056
+ }
20855
21057
  const owner = this.copyRelations({
20856
- nodeId,
20857
- constraints: [...this.constraints, constraint]
21058
+ nodeId: this.nodeId,
21059
+ placements
20858
21060
  });
20859
21061
  const context = _RelationObject.createSolveContext([
20860
21062
  owner,
20861
- ...selection?.pivot.kind === "around" && selection.pivot.axis.model ? [selection.pivot.axis.model] : []
21063
+ ...selection && selectionReference(selection.pivot)?.model ? [selectionReference(selection.pivot).model] : []
20862
21064
  ]);
20863
21065
  return {
20864
21066
  object: {
20865
- nodeId,
21067
+ nodeId: this.nodeId,
20866
21068
  compositionTransform: toTransform(owner.solvePose(context)),
20867
21069
  constraints: owner.constraints.map(
20868
21070
  (value) => owner.constraintSnapshot(value, context)
20869
- )
21071
+ ),
21072
+ transformations: owner.transformationSnapshots(context),
21073
+ relationStages: owner.relationStageSnapshots(context)
20870
21074
  },
20871
- spatial: selection ? owner.constraintSpatial(constraint, selection, context) : void 0
21075
+ spatial: selection && constraint?.kind === "transformation" ? owner.relationSpatial(constraint, selection, context) : constraint?.kind === "transformation" && constraint.actions.at(-1) && "offset" in constraint.actions.at(-1) ? owner.transformationOffsetSpatial(constraint, context) : void 0
20872
21076
  };
20873
21077
  }
20874
- constraintSpatial(constraint, selection, context) {
21078
+ relationSpatial(constraint, selection, context, actionIndex) {
21079
+ context = this.placementStageContext(constraint, context);
20875
21080
  const models = [...context.poses.keys()];
20876
- const actions = this.bodyRotations(
20877
- constraint,
20878
- new Map(models.map((model, i) => [model, i]))
20879
- );
20880
- const stage = actions.length - (selection.kind === "rotate" ? 1 : 0);
21081
+ const actions = this.placementActions(constraint, context);
21082
+ const stage = actionIndex ?? (selection.kind === "rotate" ? constraint.actions.length - 1 - [...constraint.actions].reverse().findIndex((action) => "angles" in action) : actions.length);
20881
21083
  const finalPose = this.solvePose(context);
20882
- let before = finalPose;
20883
- for (let i = actions.length - 1; i >= stage; i--) {
20884
- const action = actions[i];
20885
- before = "local" in action ? composeTransforms(before, invertTransform(action.local)) : composeTransforms(
20886
- axisRotation(
20887
- composeTransforms(
20888
- models[action.body].solvePose(context),
20889
- action.axis
20890
- ),
20891
- -action.angle
20892
- ),
20893
- before
20894
- );
20895
- }
20896
- const pivot = selection.pivot;
21084
+ const following = this.followingActions(constraint, context);
21085
+ const before = beforeTransformations(
21086
+ beforeTransformations(
21087
+ finalPose,
21088
+ { actions: following },
21089
+ models.map((model) => model.solvePose(context))
21090
+ ),
21091
+ {
21092
+ actions: actions.slice(stage)
21093
+ },
21094
+ models.map((model) => model.solvePose(context))
21095
+ );
21096
+ const pivot2 = selection.pivot;
21097
+ const axis = isAxisSelection(pivot2) ? this.rotationAxis(pivot2) : void 0;
20897
21098
  let frame;
20898
- if (pivot.kind === "around") {
21099
+ if (axis) {
20899
21100
  frame = composeTransforms(
20900
- pivot.axis.model ? pivot.axis.model.solvePose(context) : before,
20901
- (pivot.axis.direction ?? 1) === 1 ? pivot.axis.transform : composeTransforms(pivot.axis.transform, rotation([1, 0, 0, 0]))
21101
+ axis.model ? axis.model.solvePose(context) : before,
21102
+ (axis.direction ?? 1) === 1 ? selectedAxis(axis, pivot2.offset) : composeTransforms(
21103
+ selectedAxis(axis, pivot2.offset),
21104
+ rotation([1, 0, 0, 0])
21105
+ )
20902
21106
  );
21107
+ } else if (pivot2.kind === "pivotPoint" && pivot2.point.model) {
21108
+ const center = composeTransforms(
21109
+ pivot2.point.model.solvePose(context),
21110
+ pivot2.point.transform
21111
+ ).position;
21112
+ frame = {
21113
+ position: addVectors(
21114
+ center,
21115
+ rotateVector(pivot2.offset ?? origin, before.quaternion)
21116
+ ),
21117
+ quaternion: before.quaternion
21118
+ };
20903
21119
  } else {
20904
21120
  const local = translation(
20905
- pivot.kind === "pivot" ? pivot.point : this.vertexPosition(pivot.id)
21121
+ addVectors(
21122
+ this.rotationPoint(pivot2),
21123
+ pivot2.offset ?? origin
21124
+ )
20906
21125
  );
20907
21126
  frame = composeTransforms(before, local);
20908
21127
  }
20909
- const angles = constraint.rotations[stage]?.angles ?? origin;
21128
+ const selected = constraint.actions[stage];
21129
+ const angles = selected && "angles" in selected ? selected.angles : origin;
20910
21130
  const rotationVector = typeof angles === "number" ? [0, angles, 0] : angles;
21131
+ const poses = models.map((model) => model.solvePose(context));
21132
+ let actionPose = afterTransformations(
21133
+ before,
21134
+ { actions: actions.slice(stage, stage + 1) },
21135
+ poses
21136
+ );
21137
+ const remainder = [...actions.slice(stage + 1), ...following];
21138
+ for (const action of remainder) {
21139
+ const next = afterTransformations(
21140
+ actionPose,
21141
+ {
21142
+ actions: [action]
21143
+ },
21144
+ poses
21145
+ );
21146
+ if ("body" in action)
21147
+ frame = composeTransforms(
21148
+ composeTransforms(next, invertTransform(actionPose)),
21149
+ frame
21150
+ );
21151
+ else if ("offset" in action) {
21152
+ frame = {
21153
+ ...frame,
21154
+ position: addVectors(
21155
+ frame.position,
21156
+ addVectors(next.position, negateVector(actionPose.position))
21157
+ )
21158
+ };
21159
+ }
21160
+ actionPose = next;
21161
+ }
20911
21162
  const localFrame = relativeTransform(frame, finalPose);
20912
21163
  return {
20913
21164
  nodeId: this.nodeId,
20914
21165
  kind: selection.kind,
20915
21166
  spatial: {
20916
21167
  origin: localFrame.position,
20917
- vector: selection.kind === "rotate" ? rotationVector : pivot.kind === "pivot" ? pivot.point : origin,
21168
+ vector: selection.kind === "rotate" ? rotationVector : selection.kind === "pivotOffset" || selection.kind === "axisOffset" ? pivot2.offset ?? origin : pivot2.kind === "pivot" ? pivot2.point : origin,
20918
21169
  frame: localFrame,
20919
21170
  rotation: rotationVector,
20920
- axisOnly: pivot.kind === "around"
21171
+ axisOnly: isAxisSelection(pivot2),
21172
+ reference: {
21173
+ ...pivot2.kind === "axisEdge" || pivot2.kind === "pivotVertex" ? { kind: pivot2.kind, id: pivot2.id } : pivot2.kind === "axisLine" || pivot2.kind === "pivotPoint" ? {
21174
+ kind: pivot2.kind,
21175
+ nodeId: selectionReference(pivot2).model?.nodeId ?? this.nodeId,
21176
+ name: selectionReference(pivot2).name
21177
+ } : { kind: pivot2.kind, point: pivot2.point },
21178
+ offset: pivot2.offset ?? origin,
21179
+ offsetExplicit: pivot2.offset !== void 0,
21180
+ explicit: !pivot2.implicit,
21181
+ rotation: axis && (axis.direction ?? 1) !== 1 ? [0, -rotationVector[1], 0] : rotationVector,
21182
+ frame: axis && (axis.direction ?? 1) !== 1 ? composeTransforms(localFrame, rotation([1, 0, 0, 0])) : localFrame
21183
+ }
20921
21184
  }
20922
21185
  };
20923
21186
  }
20924
- static createSolveContext(roots2) {
21187
+ placementActions(value, context) {
21188
+ const indices = new Map(
21189
+ [...context.poses.keys()].map((model, i) => [model, i])
21190
+ );
21191
+ return this.bodyActions(value, indices).map(
21192
+ (action) => "offset" in action ? { offset: rotateVector(action.offset, context.frame.quaternion) } : action
21193
+ );
21194
+ }
21195
+ /** Pose at the end of this segment; later constraints have their own input pose. */
21196
+ placementStageContext(placement, context) {
21197
+ const index = this.placements.indexOf(placement);
21198
+ let end = index + 1;
21199
+ if (placement.kind !== "transformation")
21200
+ while (end < this.placements.length && this.placements[end].kind !== "transformation")
21201
+ end++;
21202
+ while (end < this.placements.length && this.placements[end].kind === "transformation")
21203
+ end++;
21204
+ if (end >= this.placements.length) return context;
21205
+ return transformSolveContext(
21206
+ _RelationObject.createSolveContext(
21207
+ [this],
21208
+ /* @__PURE__ */ new Map([[this, this.placements.slice(0, end)]])
21209
+ ),
21210
+ context.frame
21211
+ );
21212
+ }
21213
+ followingActions(placement, context) {
21214
+ let start = this.placements.indexOf(placement) + 1;
21215
+ if (placement.kind !== "transformation")
21216
+ while (start < this.placements.length && this.placements[start].kind !== "transformation")
21217
+ start++;
21218
+ const values = [];
21219
+ while (start < this.placements.length && this.placements[start].kind === "transformation")
21220
+ values.push(this.placements[start++]);
21221
+ return values.flatMap((value) => this.placementActions(value, context));
21222
+ }
21223
+ relationStageSnapshots(context) {
21224
+ if (!this.placements.length) return void 0;
21225
+ const stages = [];
21226
+ let start = 0;
21227
+ while (start < this.placements.length) {
21228
+ let end = start;
21229
+ while (end < this.placements.length && this.placements[end].kind !== "transformation")
21230
+ end++;
21231
+ while (end < this.placements.length && this.placements[end].kind === "transformation")
21232
+ end++;
21233
+ const values = this.placements.slice(start, end);
21234
+ const stage = this.placementStageContext(values[0], context);
21235
+ stages.push({
21236
+ constraintIds: values.filter((value) => value.kind !== "transformation").map((value) => value.id),
21237
+ transformationIds: values.filter((value) => value.kind === "transformation").map((value) => value.id),
21238
+ compositionTransform: toTransform(this.solvePose(stage)),
21239
+ offsetFrame: toTransform(stage.frame)
21240
+ });
21241
+ start = end;
21242
+ }
21243
+ return stages;
21244
+ }
21245
+ transformationSnapshots(context) {
21246
+ const values = this.placements.filter(
21247
+ (value) => value.kind === "transformation"
21248
+ );
21249
+ if (!values.length) return void 0;
21250
+ return values.map(
21251
+ (value) => this.transformationSnapshot(
21252
+ value,
21253
+ this.placementStageContext(value, context)
21254
+ )
21255
+ );
21256
+ }
21257
+ followingReferenceFrame(frame, actions, context) {
21258
+ const models = [...context.poses.keys()];
21259
+ const poses = models.map((model) => model.solvePose(context));
21260
+ let pose = beforeTransformations(this.solvePose(context), { actions }, poses);
21261
+ for (const action of actions) {
21262
+ const next = afterTransformations(pose, { actions: [action] }, poses);
21263
+ if ("body" in action)
21264
+ frame = composeTransforms(
21265
+ composeTransforms(next, invertTransform(pose)),
21266
+ frame
21267
+ );
21268
+ pose = next;
21269
+ }
21270
+ return frame;
21271
+ }
21272
+ transformationSnapshot(value, context) {
21273
+ const models = [...context.poses.keys()];
21274
+ const actions = this.placementActions(value, context);
21275
+ const following = this.followingActions(value, context);
21276
+ const offsets = value.actions.flatMap((action, index) => {
21277
+ if (!("offset" in action)) return [];
21278
+ const frame = this.followingReferenceFrame(
21279
+ context.frame,
21280
+ [...actions.slice(index + 1), ...following],
21281
+ context
21282
+ );
21283
+ return [
21284
+ {
21285
+ value: action.offset,
21286
+ frame: toTransform(frame),
21287
+ sourceRefs: valueTrace(action).sourceRefs
21288
+ }
21289
+ ];
21290
+ });
21291
+ const rotations = value.actions.flatMap(
21292
+ (action, index) => "pivot" in action ? [
21293
+ {
21294
+ spatial: this.relationSpatial(
21295
+ value,
21296
+ { kind: "rotate", pivot: action.pivot },
21297
+ context,
21298
+ index
21299
+ ).spatial,
21300
+ sourceRefs: valueTrace(action).sourceRefs
21301
+ }
21302
+ ] : []
21303
+ );
21304
+ return {
21305
+ id: value.id,
21306
+ offsets,
21307
+ rotations,
21308
+ offsetFrame: toTransform(context.frame),
21309
+ sourceRefs: [...valueTrace(value).sourceRefs],
21310
+ parameters: [...valueTrace(value).parameters]
21311
+ };
21312
+ }
21313
+ transformationOffsetSpatial(value, context) {
21314
+ const offset2 = this.transformationSnapshot(value, context).offsets.at(-1);
21315
+ const local = relativeTransform(
21316
+ { ...offset2.frame, position: this.solvePose(context).position },
21317
+ this.solvePose(context)
21318
+ );
21319
+ return {
21320
+ nodeId: this.nodeId,
21321
+ kind: "offset",
21322
+ spatial: { origin, vector: offset2.value, frame: local }
21323
+ };
21324
+ }
21325
+ static createSolveContext(roots2, overrides = /* @__PURE__ */ new Map()) {
20925
21326
  const models = [];
20926
21327
  const indices = /* @__PURE__ */ new Map();
20927
21328
  const collect = /* @__PURE__ */ __name((model) => {
20928
21329
  if (indices.has(model)) return;
20929
21330
  indices.set(model, models.length);
20930
21331
  models.push(model);
20931
- model.constraints.flatMap(constraintReferences).forEach(collect);
21332
+ (overrides.get(model) ?? model.placements).flatMap(constraintReferences).forEach(collect);
20932
21333
  }, "collect");
20933
21334
  roots2.forEach(collect);
21335
+ const programs = models.map((model) => {
21336
+ const placements = overrides.get(model) ?? model.placements;
21337
+ let last = placements.length - 1;
21338
+ while (last >= 0 && placements[last].kind === "transformation") last--;
21339
+ let start = last;
21340
+ while (start > 0 && placements[start - 1].kind !== "transformation")
21341
+ start--;
21342
+ const initial = start > 0 ? _RelationObject.createSolveContext(
21343
+ [model],
21344
+ new Map(overrides).set(model, placements.slice(0, start))
21345
+ ).poses.get(model) : identityRigidTransform;
21346
+ return {
21347
+ initial,
21348
+ constraints: placements.slice(Math.max(0, start), last + 1).filter(
21349
+ (value) => value.kind !== "transformation"
21350
+ ),
21351
+ transformations: placements.slice(last + 1).filter(
21352
+ (value) => value.kind === "transformation"
21353
+ ).flatMap((value) => model.bodyActions(value, indices))
21354
+ };
21355
+ });
20934
21356
  const poses = solveBodies2(
20935
- models.map((model) => ({
21357
+ models.map((model, index) => ({
21358
+ initial: programs[index].initial,
21359
+ transformations: programs[index].transformations,
20936
21360
  name: model.name,
20937
- relations: model.constraints.map(
21361
+ relations: programs[index].constraints.map(
20938
21362
  (constraint) => constraint.kind === "align" ? {
20939
21363
  kind: "align",
20940
21364
  id: constraint.id,
@@ -20947,9 +21371,7 @@ var RelationObject = class _RelationObject {
20947
21371
  body: indices.get(constraint.target.model ?? model),
20948
21372
  geometry: (constraint.target.model ?? model).alignmentGeometry(constraint.target),
20949
21373
  transform: constraint.target.transform
20950
- },
20951
- offset: constraint.offset,
20952
- rotations: model.bodyRotations(constraint, indices)
21374
+ }
20953
21375
  } : {
20954
21376
  kind: "on",
20955
21377
  id: constraint.id,
@@ -20965,57 +21387,25 @@ var RelationObject = class _RelationObject {
20965
21387
  body: indices.get(constraint.target.model ?? model),
20966
21388
  transform: constraint.target.transform,
20967
21389
  facing: constraint.target.bound.facing
20968
- },
20969
- offset: constraint.offset,
20970
- rotations: model.bodyRotations(constraint, indices)
21390
+ }
20971
21391
  }
20972
21392
  )
20973
21393
  }))
20974
21394
  );
20975
21395
  return {
20976
- poses: new Map(models.map((model, index) => [model, poses[index]]))
21396
+ poses: new Map(models.map((model, index) => [model, poses[index]])),
21397
+ frame: identityRigidTransform
20977
21398
  };
20978
21399
  }
20979
21400
  constraintSnapshot(constraint, context) {
20980
21401
  const source = constraint.source.model ?? this;
20981
21402
  const target = constraint.target.model ?? this;
20982
- const models = [...context.poses.keys()];
20983
- const actions = this.bodyRotations(
20984
- constraint,
20985
- new Map(models.map((model, i) => [model, i]))
20986
- );
20987
- let before = this.solvePose(context);
20988
- for (const action of [...actions].reverse()) {
20989
- before = "local" in action ? composeTransforms(before, invertTransform(action.local)) : composeTransforms(
20990
- axisRotation(
20991
- composeTransforms(
20992
- models[action.body].solvePose(context),
20993
- action.axis
20994
- ),
20995
- -action.angle
20996
- ),
20997
- before
20998
- );
20999
- }
21000
- const sourcePose = source === this ? before : source.solvePose(context);
21001
- const targetPose = target === this ? before : target.solvePose(context);
21002
- let offsetFrame = composeTransforms(
21003
- targetPose,
21403
+ const sourcePose = source.solvePose(context);
21404
+ const contactFrame = composeTransforms(
21405
+ target.solvePose(context),
21004
21406
  constraint.target.transform
21005
21407
  );
21006
21408
  if (constraint.kind === "align") {
21007
- for (const action of actions)
21008
- if ("body" in action)
21009
- offsetFrame = composeTransforms(
21010
- axisRotation(
21011
- composeTransforms(
21012
- models[action.body].solvePose(context),
21013
- action.axis
21014
- ),
21015
- action.angle
21016
- ),
21017
- offsetFrame
21018
- );
21019
21409
  return {
21020
21410
  id: constraint.id,
21021
21411
  kind: "align",
@@ -21023,15 +21413,12 @@ var RelationObject = class _RelationObject {
21023
21413
  target: anchorSnapshot(target, constraint.target),
21024
21414
  sourceElement: source.relationElement(constraint.source),
21025
21415
  targetElement: target.relationElement(constraint.target),
21026
- offsetDirection: 1,
21027
- offset: constraint.offset ?? origin,
21028
- offsetFrame: toTransform(offsetFrame),
21029
21416
  sourceRefs: [...valueTrace(constraint).sourceRefs],
21030
21417
  parameters: [...valueTrace(constraint).parameters]
21031
21418
  };
21032
21419
  }
21033
21420
  const orientation = composeTransforms(
21034
- invertTransform(rotation(offsetFrame.quaternion)),
21421
+ invertTransform(rotation(contactFrame.quaternion)),
21035
21422
  rotation(sourcePose.quaternion)
21036
21423
  ).quaternion;
21037
21424
  const bounds = source[referenceBounds](
@@ -21047,9 +21434,9 @@ var RelationObject = class _RelationObject {
21047
21434
  {
21048
21435
  position: addVectors(
21049
21436
  sourcePose.position,
21050
- rotateVector(center, offsetFrame.quaternion)
21437
+ rotateVector(center, contactFrame.quaternion)
21051
21438
  ),
21052
- quaternion: offsetFrame.quaternion
21439
+ quaternion: contactFrame.quaternion
21053
21440
  },
21054
21441
  sourcePose
21055
21442
  );
@@ -21058,19 +21445,6 @@ var RelationObject = class _RelationObject {
21058
21445
  bounds[1][1] - bounds[0][1],
21059
21446
  bounds[1][2] - bounds[0][2]
21060
21447
  ];
21061
- for (const action of actions) {
21062
- if ("body" in action)
21063
- offsetFrame = composeTransforms(
21064
- axisRotation(
21065
- composeTransforms(
21066
- models[action.body].solvePose(context),
21067
- action.axis
21068
- ),
21069
- action.angle
21070
- ),
21071
- offsetFrame
21072
- );
21073
- }
21074
21448
  return {
21075
21449
  id: constraint.id,
21076
21450
  kind: constraint.kind,
@@ -21104,9 +21478,6 @@ var RelationObject = class _RelationObject {
21104
21478
  transform: toTransform(constraint.target.transform),
21105
21479
  bound: constraint.target.bound
21106
21480
  },
21107
- offsetDirection: source === this ? 1 : -1,
21108
- offset: constraint.offset ?? origin,
21109
- offsetFrame: toTransform(offsetFrame),
21110
21481
  sourceRefs: [...valueTrace(constraint).sourceRefs],
21111
21482
  parameters: [...valueTrace(constraint).parameters]
21112
21483
  };
@@ -21122,7 +21493,7 @@ var SketchFrame = class _SketchFrame extends RelationObject {
21122
21493
  operation = storedOperation("sketch");
21123
21494
  constructor(source, init = {}) {
21124
21495
  super({
21125
- constraints: source?.constraints,
21496
+ placements: source?.placements,
21126
21497
  sourceRefs: source?.sourceRefs,
21127
21498
  parameters: source?.parameters,
21128
21499
  ...init
@@ -21139,7 +21510,7 @@ var SketchFrame = class _SketchFrame extends RelationObject {
21139
21510
  relatedObjects() {
21140
21511
  return [
21141
21512
  ...this.source ? [this.source] : [],
21142
- ...this.constraints.flatMap(constraintReferences)
21513
+ ...this.placements.flatMap(constraintReferences)
21143
21514
  ];
21144
21515
  }
21145
21516
  attachOperationTrace(siteId, execution, order, sourceRef, outputIndex = 0) {
@@ -21190,7 +21561,7 @@ var SketchFrame = class _SketchFrame extends RelationObject {
21190
21561
  return related;
21191
21562
  }
21192
21563
  face(region) {
21193
- return sketchFaceModel(region, this.constraints, this);
21564
+ return sketchFaceModel(region, this.placements, this);
21194
21565
  }
21195
21566
  snapshot() {
21196
21567
  const context = RelationObject.createSolveContext([this]);
@@ -21199,7 +21570,9 @@ var SketchFrame = class _SketchFrame extends RelationObject {
21199
21570
  compositionTransform: toTransform(this.solvePose(context)),
21200
21571
  constraints: this.constraints.map(
21201
21572
  (constraint) => this.constraintSnapshot(constraint, context)
21202
- )
21573
+ ),
21574
+ transformations: this.transformationSnapshots(context),
21575
+ relationStages: this.relationStageSnapshots(context)
21203
21576
  };
21204
21577
  }
21205
21578
  /** Related finite models, expressed in this sketch's local editing plane. */
@@ -21463,9 +21836,9 @@ var ModelObject = class _ModelObject extends RelationObject {
21463
21836
  );
21464
21837
  }
21465
21838
  originOffset(dx = 0, dy = 0, dz = 0) {
21466
- const offset = [dx, dy, dz];
21467
- assertFiniteVector("originOffset", offset);
21468
- return this.withOrigin(offset, { kind: "originOffset" });
21839
+ const offset2 = [dx, dy, dz];
21840
+ assertFiniteVector("originOffset", offset2);
21841
+ return this.withOrigin(offset2, { kind: "originOffset" });
21469
21842
  }
21470
21843
  originPoint(point3) {
21471
21844
  const reference = anchorReference(point3);
@@ -21491,8 +21864,8 @@ var ModelObject = class _ModelObject extends RelationObject {
21491
21864
  const position2 = this.elements.center.transform.position;
21492
21865
  return this.withOrigin(position2, { kind: "originCenter" });
21493
21866
  }
21494
- withOrigin(offset, selection) {
21495
- const transform = translation(negateVector(offset));
21867
+ withOrigin(offset2, selection) {
21868
+ const transform = translation(negateVector(offset2));
21496
21869
  const operation = storedOperation(selection.kind, [
21497
21870
  { model: this, role: "source", index: 0 }
21498
21871
  ]);
@@ -21502,7 +21875,7 @@ var ModelObject = class _ModelObject extends RelationObject {
21502
21875
  role: "reference",
21503
21876
  index: 0
21504
21877
  });
21505
- operation.spatial = { origin, vector: offset };
21878
+ operation.spatial = { origin, vector: offset2 };
21506
21879
  if (selection.kind === "originVertex") {
21507
21880
  operation.selections.push({
21508
21881
  kind: "vertex",
@@ -21531,7 +21904,7 @@ var ModelObject = class _ModelObject extends RelationObject {
21531
21904
  transformElement(element, transform)
21532
21905
  ])
21533
21906
  ),
21534
- constraints: this.mapConstraintGeometry(
21907
+ placements: this.mapConstraintGeometry(
21535
21908
  (element) => transformElement(element, transform),
21536
21909
  (point3) => composeTransforms(transform, translation(point3)).position
21537
21910
  )
@@ -21567,15 +21940,37 @@ var ModelObject = class _ModelObject extends RelationObject {
21567
21940
  }
21568
21941
  mapConstraintGeometry(mapElement, mapPoint) {
21569
21942
  const mapReference = /* @__PURE__ */ __name((reference) => reference.model ? reference : { ...reference, ...mapElement(reference) }, "mapReference");
21570
- return this.constraints.map((constraint) => {
21943
+ const zero = mapPoint(origin);
21944
+ const mapVector = /* @__PURE__ */ __name((vector2) => mapPoint(vector2).map(
21945
+ (value, index) => value - zero[index]
21946
+ ), "mapVector");
21947
+ const scale3 = Math.hypot(...mapVector([1, 0, 0]));
21948
+ return this.placements.map((constraint) => {
21571
21949
  const mapped = {
21572
21950
  ...constraint,
21573
- source: mapReference(constraint.source),
21574
- target: mapReference(constraint.target),
21575
- rotations: constraint.rotations.map((action) => ({
21576
- ...action,
21577
- pivot: action.pivot.kind === "around" ? { ...action.pivot, axis: mapReference(action.pivot.axis) } : action.pivot.kind === "pivot" ? { ...action.pivot, point: mapPoint(action.pivot.point) } : action.pivot
21578
- }))
21951
+ ...constraint.kind === "transformation" ? {} : {
21952
+ source: mapReference(constraint.source),
21953
+ target: mapReference(constraint.target)
21954
+ },
21955
+ ...constraint.kind === "transformation" ? {
21956
+ actions: constraint.actions.map((action) => {
21957
+ if ("offset" in action) return action;
21958
+ const mappedAction = {
21959
+ ...action,
21960
+ pivot: action.pivot.kind === "pivot" ? { ...action.pivot, point: mapPoint(action.pivot.point) } : mapSelectionReference(action.pivot, mapReference)
21961
+ };
21962
+ if (action.pivot.offset) {
21963
+ mappedAction.pivot = {
21964
+ ...mappedAction.pivot,
21965
+ offset: isAxisSelection(action.pivot) ? selectionReference(action.pivot)?.model ? action.pivot.offset : action.pivot.offset.map(
21966
+ (value) => value * scale3
21967
+ ) : mapVector(action.pivot.offset)
21968
+ };
21969
+ }
21970
+ valueTraces.set(mappedAction, valueTrace(action));
21971
+ return mappedAction;
21972
+ })
21973
+ } : {}
21579
21974
  };
21580
21975
  valueTraces.set(mapped, valueTrace(constraint));
21581
21976
  return mapped;
@@ -21606,7 +22001,7 @@ var ModelObject = class _ModelObject extends RelationObject {
21606
22001
  {
21607
22002
  geometryAnchor: scaleElement(this.geometryAnchor, factor),
21608
22003
  elements: scaleElements(this.elements, factor),
21609
- constraints: this.mapConstraintGeometry(
22004
+ placements: this.mapConstraintGeometry(
21610
22005
  (element) => scaleElement(element, factor),
21611
22006
  (point3) => scaleFrame(translation(point3), factor).position
21612
22007
  )
@@ -21642,7 +22037,7 @@ var ModelObject = class _ModelObject extends RelationObject {
21642
22037
  name: "Extrude",
21643
22038
  geometry,
21644
22039
  material: this.materialSnapshot,
21645
- constraints: this.constraints,
22040
+ placements: this.placements,
21646
22041
  sourceRefs: this.sourceRefs,
21647
22042
  parameters: this.allParameters(),
21648
22043
  meshTolerance: this.meshTolerance,
@@ -21789,7 +22184,7 @@ var ModelObject = class _ModelObject extends RelationObject {
21789
22184
  return [
21790
22185
  ...this.children,
21791
22186
  ...this.operation.inputs.map((input) => input.model),
21792
- ...this.constraints.flatMap(constraintReferences)
22187
+ ...this.placements.flatMap(constraintReferences)
21793
22188
  ];
21794
22189
  }
21795
22190
  /** @internal */
@@ -21805,7 +22200,7 @@ var ModelObject = class _ModelObject extends RelationObject {
21805
22200
  );
21806
22201
  const parameters = uniqueParameters([
21807
22202
  ...this.parameters,
21808
- ...this.constraints.flatMap(
22203
+ ...this.placements.flatMap(
21809
22204
  (constraint) => valueTrace(constraint).parameters
21810
22205
  )
21811
22206
  ]);
@@ -21817,6 +22212,8 @@ var ModelObject = class _ModelObject extends RelationObject {
21817
22212
  compositionTransform: toTransform(pose),
21818
22213
  transform: toTransform(inComposition ? pose : identityRigidTransform),
21819
22214
  constraints,
22215
+ transformations: this.transformationSnapshots(solveContext),
22216
+ relationStages: this.relationStageSnapshots(solveContext),
21820
22217
  origin,
21821
22218
  elements: snapshotElements({
21822
22219
  ...this.elements,
@@ -21911,7 +22308,7 @@ var ModelObject = class _ModelObject extends RelationObject {
21911
22308
  name: "Loft",
21912
22309
  geometry,
21913
22310
  material: this.materialSnapshot,
21914
- constraints: this.constraints,
22311
+ placements: this.placements,
21915
22312
  sourceRefs: inputs.flatMap((input) => input.sourceRefs),
21916
22313
  parameters: uniqueParameters(
21917
22314
  inputs.flatMap((input) => input.allParameters())
@@ -21939,7 +22336,7 @@ var ModelObject = class _ModelObject extends RelationObject {
21939
22336
  geometry: evaluation.geometry,
21940
22337
  name: this.name,
21941
22338
  material: this.materialSnapshot,
21942
- constraints: this.constraints,
22339
+ placements: this.placements,
21943
22340
  sourceRefs: [this, ...others].flatMap((model) => model.sourceRefs),
21944
22341
  parameters: uniqueParameters(
21945
22342
  [this, ...others].flatMap((model) => model.allParameters())
@@ -22057,7 +22454,7 @@ var ModelObject = class _ModelObject extends RelationObject {
22057
22454
  allParameters() {
22058
22455
  return uniqueParameters([
22059
22456
  ...this.parameters,
22060
- ...this.constraints.flatMap(
22457
+ ...this.placements.flatMap(
22061
22458
  (constraint) => valueTrace(constraint).parameters
22062
22459
  )
22063
22460
  ]);
@@ -22189,19 +22586,22 @@ var ModelObject = class _ModelObject extends RelationObject {
22189
22586
  } : reference
22190
22587
  );
22191
22588
  }
22589
+ edgeReference(id) {
22590
+ return { ...rotationAxisReference(this.edge(id)), model: void 0 };
22591
+ }
22192
22592
  vertexPosition(id) {
22193
22593
  return anchorReference(this.vertex(id)).transform.position;
22194
22594
  }
22195
22595
  copyRelations(init) {
22196
22596
  return this.copy(init, this.operation);
22197
22597
  }
22198
- /** Fix the assembly frame once, using only its direct member origins. */
22598
+ /** Fix the assembly frame once in the first member's solved local coordinates. */
22199
22599
  static createAssembly(children) {
22200
22600
  const context = _ModelObject.createSolveContext(children);
22201
- const center = children.length ? boundsCenter(
22202
- pointBounds(children.map((child) => child.solvePose(context).position))
22203
- ) : origin;
22204
- return transformSolveContext(context, translation(negateVector(center)));
22601
+ return children.length ? transformSolveContext(
22602
+ context,
22603
+ invertTransform(children[0].solvePose(context))
22604
+ ) : context;
22205
22605
  }
22206
22606
  requireGeometry() {
22207
22607
  if (!this.geometry) {
@@ -22271,7 +22671,7 @@ var ModelObject = class _ModelObject extends RelationObject {
22271
22671
  material: this.materialSnapshot,
22272
22672
  children: this.children,
22273
22673
  assembly: this.assembly,
22274
- constraints: this.constraints,
22674
+ placements: this.placements,
22275
22675
  elements: this.elements,
22276
22676
  sourceRefs: this.sourceRefs,
22277
22677
  parameters: this.parameters,
@@ -22807,19 +23207,32 @@ function isConstraint(value) {
22807
23207
  return value instanceof Constraint;
22808
23208
  }
22809
23209
  __name(isConstraint, "isConstraint");
22810
- function isConstraintExpression(value) {
22811
- return value instanceof ConstraintExpression;
22812
- }
22813
- __name(isConstraintExpression, "isConstraintExpression");
22814
- function constraintPreview(value) {
22815
- return value.preview();
23210
+ function isRelationExpression(value) {
23211
+ return value instanceof RelationExpression;
23212
+ }
23213
+ __name(isRelationExpression, "isRelationExpression");
23214
+ function relationPreview(value, preceding = []) {
23215
+ return value.preview(preceding);
23216
+ }
23217
+ __name(relationPreview, "relationPreview");
23218
+ function currentRelationSelf() {
23219
+ return activeRelate?.self;
23220
+ }
23221
+ __name(currentRelationSelf, "currentRelationSelf");
23222
+ function relationSelectionPreview(self, preceding, context) {
23223
+ return self[previewRelation](
23224
+ void 0,
23225
+ void 0,
23226
+ preceding,
23227
+ (preceding[0] ?? context)?.continuationArguments()[1]?.original
23228
+ );
22816
23229
  }
22817
- __name(constraintPreview, "constraintPreview");
22818
- function instrumentConstraint(constraint, sourceRef, parameters) {
23230
+ __name(relationSelectionPreview, "relationSelectionPreview");
23231
+ function instrumentRelation(constraint, sourceRef, parameters) {
22819
23232
  constraint.attachSource(sourceRef);
22820
23233
  constraint.attachParameters(parameters);
22821
23234
  }
22822
- __name(instrumentConstraint, "instrumentConstraint");
23235
+ __name(instrumentRelation, "instrumentRelation");
22823
23236
  function instrumentModelOperation(object, instrumentation) {
22824
23237
  object.attachSource(instrumentation.sourceRef);
22825
23238
  object.attachParameters(instrumentation.parameters);
@@ -22832,10 +23245,10 @@ function instrumentModelOperation(object, instrumentation) {
22832
23245
  );
22833
23246
  }
22834
23247
  __name(instrumentModelOperation, "instrumentModelOperation");
22835
- function constraintTraceReference(constraint) {
23248
+ function relationTraceReference(constraint) {
22836
23249
  return constraint.traceReference();
22837
23250
  }
22838
- __name(constraintTraceReference, "constraintTraceReference");
23251
+ __name(relationTraceReference, "relationTraceReference");
22839
23252
  function modelObjectRuntimeInfo(object) {
22840
23253
  return {
22841
23254
  nodeId: object.nodeId,
@@ -23037,6 +23450,13 @@ function retainModelGeometry(objects) {
23037
23450
  }
23038
23451
  __name(retainModelGeometry, "retainModelGeometry");
23039
23452
  var authoringApi = Object.freeze({
23453
+ offset,
23454
+ rotate: rotate2,
23455
+ pivot,
23456
+ pivotVertex,
23457
+ pivotPoint,
23458
+ axisEdge,
23459
+ axisLine,
23040
23460
  cached,
23041
23461
  font,
23042
23462
  googleFont,
@@ -23116,7 +23536,10 @@ function disposeModelGeometryValue(geometry) {
23116
23536
  __name(disposeModelGeometryValue, "disposeModelGeometryValue");
23117
23537
  var kernelShape = cachedArtifact(
23118
23538
  (_operation, _arguments, _inputs, compute) => compute(),
23119
- { key: kernelOperationKey, lifecycle: shapeLifecycle }
23539
+ {
23540
+ key: /* @__PURE__ */ __name((operation, arguments_, inputs) => kernelOperationKey(`kernel-shape:${operation}`, arguments_, inputs), "key"),
23541
+ lifecycle: shapeLifecycle
23542
+ }
23120
23543
  );
23121
23544
  function evaluateKernelShape(operation, arguments_, inputs, compute) {
23122
23545
  return kernelShape(
@@ -23135,7 +23558,10 @@ var evaluateModelGeometry = cachedArtifact(
23135
23558
  referenceBasis: result.referenceBasis
23136
23559
  };
23137
23560
  },
23138
- { key: kernelOperationKey, lifecycle: modelGeometryLifecycle }
23561
+ {
23562
+ key: /* @__PURE__ */ __name((operation, arguments_, inputs) => kernelOperationKey(`model-geometry:${operation}`, arguments_, inputs), "key"),
23563
+ lifecycle: modelGeometryLifecycle
23564
+ }
23139
23565
  );
23140
23566
  function createModelGeometryValue(shape, topology) {
23141
23567
  try {
@@ -23203,7 +23629,7 @@ function meshUVs(shape, vertexCount) {
23203
23629
  const faces = shape.faces;
23204
23630
  const location = new (getOC12()).TopLoc_Location();
23205
23631
  const uvs = new Float32Array(vertexCount * 2);
23206
- let offset = 0;
23632
+ let offset2 = 0;
23207
23633
  try {
23208
23634
  for (const face of faces) {
23209
23635
  const triangulation = getOC12().BRep_Tool.Triangulation(
@@ -23221,8 +23647,8 @@ function meshUVs(shape, vertexCount) {
23221
23647
  const uv = triangulation.UVNode(index + 1);
23222
23648
  try {
23223
23649
  const u8 = uv.X(), v = uv.Y();
23224
- uvs[(offset + index) * 2] = u8;
23225
- uvs[(offset + index) * 2 + 1] = v;
23650
+ uvs[(offset2 + index) * 2] = u8;
23651
+ uvs[(offset2 + index) * 2 + 1] = v;
23226
23652
  minU = Math.min(minU, u8);
23227
23653
  maxU = Math.max(maxU, u8);
23228
23654
  minV = Math.min(minV, v);
@@ -23232,11 +23658,11 @@ function meshUVs(shape, vertexCount) {
23232
23658
  }
23233
23659
  }
23234
23660
  for (let index = 0; index < count; index++) {
23235
- const base = (offset + index) * 2;
23661
+ const base = (offset2 + index) * 2;
23236
23662
  uvs[base] = maxU === minU ? 0 : (uvs[base] - minU) / (maxU - minU);
23237
23663
  uvs[base + 1] = maxV === minV ? 0 : (uvs[base + 1] - minV) / (maxV - minV);
23238
23664
  }
23239
- offset += count;
23665
+ offset2 += count;
23240
23666
  } finally {
23241
23667
  triangulation.delete();
23242
23668
  }
@@ -23248,7 +23674,7 @@ function meshUVs(shape, vertexCount) {
23248
23674
  }
23249
23675
  }
23250
23676
  __name(meshUVs, "meshUVs");
23251
- function sketchFaceModel(region, constraints, source) {
23677
+ function sketchFaceModel(region, placements, source) {
23252
23678
  const curves = [region.outer, ...region.holes].map(
23253
23679
  (loop) => loop.map(
23254
23680
  (curve) => curve.kind === "line" ? ["line", curve.points] : curve.kind === "circle" ? ["circle", curve.center, curve.radius] : ["arc", curve.center, curve.radius, curve.start, curve.sweep]
@@ -23264,7 +23690,7 @@ function sketchFaceModel(region, constraints, source) {
23264
23690
  return ModelObject.create({
23265
23691
  kind: "face",
23266
23692
  name: "Sketch face",
23267
- constraints,
23693
+ placements,
23268
23694
  geometry,
23269
23695
  geometryAnchor: plane,
23270
23696
  elements: { plane },
@@ -23403,10 +23829,9 @@ function solidElements(bounds) {
23403
23829
  __name(solidElements, "solidElements");
23404
23830
  function constraintReferences(constraint) {
23405
23831
  return [
23406
- constraint.source.model,
23407
- constraint.target.model,
23408
- ...constraint.rotations.map(
23409
- (action) => action.pivot.kind === "around" ? action.pivot.axis.model : void 0
23832
+ ...constraint.kind === "transformation" ? [] : [constraint.source.model, constraint.target.model],
23833
+ ...(constraint.kind === "transformation" ? constraint.actions : []).map(
23834
+ (action) => "pivot" in action ? selectionReference(action.pivot)?.model : void 0
23410
23835
  )
23411
23836
  ].filter((model) => !!model);
23412
23837
  }
@@ -24735,6 +25160,7 @@ export {
24735
25160
  setKernelNativeMemoryCounter,
24736
25161
  clearKernelOperationCache,
24737
25162
  kernelOperationCacheStats,
25163
+ setKernelCacheBudget,
24738
25164
  setKernelArtifactStore,
24739
25165
  setKernelExternalBytes,
24740
25166
  identifyCachedFunction,
@@ -24779,6 +25205,13 @@ export {
24779
25205
  modelElementReference,
24780
25206
  modelTopologyReference,
24781
25207
  modelTopologyIds,
25208
+ offset,
25209
+ rotate2 as rotate,
25210
+ pivot,
25211
+ pivotVertex,
25212
+ pivotPoint,
25213
+ axisEdge,
25214
+ axisLine,
24782
25215
  circle,
24783
25216
  ellipse,
24784
25217
  rectangle,
@@ -24805,11 +25238,13 @@ export {
24805
25238
  intersect,
24806
25239
  isModelObject,
24807
25240
  isConstraint,
24808
- isConstraintExpression,
24809
- constraintPreview,
24810
- instrumentConstraint,
25241
+ isRelationExpression,
25242
+ relationPreview,
25243
+ currentRelationSelf,
25244
+ relationSelectionPreview,
25245
+ instrumentRelation,
24811
25246
  instrumentModelOperation,
24812
- constraintTraceReference,
25247
+ relationTraceReference,
24813
25248
  modelObjectRuntimeInfo,
24814
25249
  relatedModelObjects,
24815
25250
  planModelSnapshotQueries,
@@ -24831,4 +25266,4 @@ export {
24831
25266
  assertSketchDragConnections,
24832
25267
  solveSketchSnapshot
24833
25268
  };
24834
- //# sourceMappingURL=chunk-RPY2WVCB.js.map
25269
+ //# sourceMappingURL=chunk-T3RAUNSF.js.map