@code3d/core 0.0.1-alpha.4 → 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-OQLJDJIF.js → chunk-3RIMOCPP.js} +2 -2
  3. package/bld/chunks/{chunk-PAIFBNU4.js → chunk-KYW4AWKO.js} +2 -2
  4. package/bld/chunks/{chunk-J77FMYFY.js → chunk-T3RAUNSF.js} +941 -514
  5. package/bld/chunks/chunk-T3RAUNSF.js.map +7 -0
  6. package/bld/chunks/{chunk-ZMT6EFHL.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 -78
  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 -436
  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-J77FMYFY.js.map +0 -7
  38. /package/bld/chunks/{chunk-OQLJDJIF.js.map → chunk-3RIMOCPP.js.map} +0 -0
  39. /package/bld/chunks/{chunk-PAIFBNU4.js.map → chunk-KYW4AWKO.js.map} +0 -0
  40. /package/bld/chunks/{chunk-ZMT6EFHL.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,63 +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
- const lastRotation = constraint.rotations.at(-1);
20981
- const rotationSpatial = lastRotation ? this.constraintSpatial(
20982
- constraint,
20983
- { kind: "rotate", pivot: lastRotation.pivot },
20984
- context
20985
- ).spatial : void 0;
20986
21401
  const source = constraint.source.model ?? this;
20987
21402
  const target = constraint.target.model ?? this;
20988
- const models = [...context.poses.keys()];
20989
- const actions = this.bodyRotations(
20990
- constraint,
20991
- new Map(models.map((model, i) => [model, i]))
20992
- );
20993
- let before = this.solvePose(context);
20994
- for (const action of [...actions].reverse()) {
20995
- before = "local" in action ? composeTransforms(before, invertTransform(action.local)) : composeTransforms(
20996
- axisRotation(
20997
- composeTransforms(
20998
- models[action.body].solvePose(context),
20999
- action.axis
21000
- ),
21001
- -action.angle
21002
- ),
21003
- before
21004
- );
21005
- }
21006
- const sourcePose = source === this ? before : source.solvePose(context);
21007
- const targetPose = target === this ? before : target.solvePose(context);
21008
- let offsetFrame = composeTransforms(
21009
- targetPose,
21403
+ const sourcePose = source.solvePose(context);
21404
+ const contactFrame = composeTransforms(
21405
+ target.solvePose(context),
21010
21406
  constraint.target.transform
21011
21407
  );
21012
21408
  if (constraint.kind === "align") {
21013
- for (const action of actions)
21014
- if ("body" in action)
21015
- offsetFrame = composeTransforms(
21016
- axisRotation(
21017
- composeTransforms(
21018
- models[action.body].solvePose(context),
21019
- action.axis
21020
- ),
21021
- action.angle
21022
- ),
21023
- offsetFrame
21024
- );
21025
21409
  return {
21026
21410
  id: constraint.id,
21027
21411
  kind: "align",
@@ -21029,16 +21413,12 @@ var RelationObject = class _RelationObject {
21029
21413
  target: anchorSnapshot(target, constraint.target),
21030
21414
  sourceElement: source.relationElement(constraint.source),
21031
21415
  targetElement: target.relationElement(constraint.target),
21032
- offsetDirection: 1,
21033
- offset: constraint.offset ?? origin,
21034
- offsetFrame: toTransform(offsetFrame),
21035
- rotation: rotationSpatial,
21036
21416
  sourceRefs: [...valueTrace(constraint).sourceRefs],
21037
21417
  parameters: [...valueTrace(constraint).parameters]
21038
21418
  };
21039
21419
  }
21040
21420
  const orientation = composeTransforms(
21041
- invertTransform(rotation(offsetFrame.quaternion)),
21421
+ invertTransform(rotation(contactFrame.quaternion)),
21042
21422
  rotation(sourcePose.quaternion)
21043
21423
  ).quaternion;
21044
21424
  const bounds = source[referenceBounds](
@@ -21054,9 +21434,9 @@ var RelationObject = class _RelationObject {
21054
21434
  {
21055
21435
  position: addVectors(
21056
21436
  sourcePose.position,
21057
- rotateVector(center, offsetFrame.quaternion)
21437
+ rotateVector(center, contactFrame.quaternion)
21058
21438
  ),
21059
- quaternion: offsetFrame.quaternion
21439
+ quaternion: contactFrame.quaternion
21060
21440
  },
21061
21441
  sourcePose
21062
21442
  );
@@ -21065,19 +21445,6 @@ var RelationObject = class _RelationObject {
21065
21445
  bounds[1][1] - bounds[0][1],
21066
21446
  bounds[1][2] - bounds[0][2]
21067
21447
  ];
21068
- for (const action of actions) {
21069
- if ("body" in action)
21070
- offsetFrame = composeTransforms(
21071
- axisRotation(
21072
- composeTransforms(
21073
- models[action.body].solvePose(context),
21074
- action.axis
21075
- ),
21076
- action.angle
21077
- ),
21078
- offsetFrame
21079
- );
21080
- }
21081
21448
  return {
21082
21449
  id: constraint.id,
21083
21450
  kind: constraint.kind,
@@ -21111,10 +21478,6 @@ var RelationObject = class _RelationObject {
21111
21478
  transform: toTransform(constraint.target.transform),
21112
21479
  bound: constraint.target.bound
21113
21480
  },
21114
- offsetDirection: source === this ? 1 : -1,
21115
- offset: constraint.offset ?? origin,
21116
- offsetFrame: toTransform(offsetFrame),
21117
- rotation: rotationSpatial,
21118
21481
  sourceRefs: [...valueTrace(constraint).sourceRefs],
21119
21482
  parameters: [...valueTrace(constraint).parameters]
21120
21483
  };
@@ -21130,7 +21493,7 @@ var SketchFrame = class _SketchFrame extends RelationObject {
21130
21493
  operation = storedOperation("sketch");
21131
21494
  constructor(source, init = {}) {
21132
21495
  super({
21133
- constraints: source?.constraints,
21496
+ placements: source?.placements,
21134
21497
  sourceRefs: source?.sourceRefs,
21135
21498
  parameters: source?.parameters,
21136
21499
  ...init
@@ -21147,7 +21510,7 @@ var SketchFrame = class _SketchFrame extends RelationObject {
21147
21510
  relatedObjects() {
21148
21511
  return [
21149
21512
  ...this.source ? [this.source] : [],
21150
- ...this.constraints.flatMap(constraintReferences)
21513
+ ...this.placements.flatMap(constraintReferences)
21151
21514
  ];
21152
21515
  }
21153
21516
  attachOperationTrace(siteId, execution, order, sourceRef, outputIndex = 0) {
@@ -21198,7 +21561,7 @@ var SketchFrame = class _SketchFrame extends RelationObject {
21198
21561
  return related;
21199
21562
  }
21200
21563
  face(region) {
21201
- return sketchFaceModel(region, this.constraints, this);
21564
+ return sketchFaceModel(region, this.placements, this);
21202
21565
  }
21203
21566
  snapshot() {
21204
21567
  const context = RelationObject.createSolveContext([this]);
@@ -21207,7 +21570,9 @@ var SketchFrame = class _SketchFrame extends RelationObject {
21207
21570
  compositionTransform: toTransform(this.solvePose(context)),
21208
21571
  constraints: this.constraints.map(
21209
21572
  (constraint) => this.constraintSnapshot(constraint, context)
21210
- )
21573
+ ),
21574
+ transformations: this.transformationSnapshots(context),
21575
+ relationStages: this.relationStageSnapshots(context)
21211
21576
  };
21212
21577
  }
21213
21578
  /** Related finite models, expressed in this sketch's local editing plane. */
@@ -21471,9 +21836,9 @@ var ModelObject = class _ModelObject extends RelationObject {
21471
21836
  );
21472
21837
  }
21473
21838
  originOffset(dx = 0, dy = 0, dz = 0) {
21474
- const offset = [dx, dy, dz];
21475
- assertFiniteVector("originOffset", offset);
21476
- return this.withOrigin(offset, { kind: "originOffset" });
21839
+ const offset2 = [dx, dy, dz];
21840
+ assertFiniteVector("originOffset", offset2);
21841
+ return this.withOrigin(offset2, { kind: "originOffset" });
21477
21842
  }
21478
21843
  originPoint(point3) {
21479
21844
  const reference = anchorReference(point3);
@@ -21499,8 +21864,8 @@ var ModelObject = class _ModelObject extends RelationObject {
21499
21864
  const position2 = this.elements.center.transform.position;
21500
21865
  return this.withOrigin(position2, { kind: "originCenter" });
21501
21866
  }
21502
- withOrigin(offset, selection) {
21503
- const transform = translation(negateVector(offset));
21867
+ withOrigin(offset2, selection) {
21868
+ const transform = translation(negateVector(offset2));
21504
21869
  const operation = storedOperation(selection.kind, [
21505
21870
  { model: this, role: "source", index: 0 }
21506
21871
  ]);
@@ -21510,7 +21875,7 @@ var ModelObject = class _ModelObject extends RelationObject {
21510
21875
  role: "reference",
21511
21876
  index: 0
21512
21877
  });
21513
- operation.spatial = { origin, vector: offset };
21878
+ operation.spatial = { origin, vector: offset2 };
21514
21879
  if (selection.kind === "originVertex") {
21515
21880
  operation.selections.push({
21516
21881
  kind: "vertex",
@@ -21539,7 +21904,7 @@ var ModelObject = class _ModelObject extends RelationObject {
21539
21904
  transformElement(element, transform)
21540
21905
  ])
21541
21906
  ),
21542
- constraints: this.mapConstraintGeometry(
21907
+ placements: this.mapConstraintGeometry(
21543
21908
  (element) => transformElement(element, transform),
21544
21909
  (point3) => composeTransforms(transform, translation(point3)).position
21545
21910
  )
@@ -21575,15 +21940,37 @@ var ModelObject = class _ModelObject extends RelationObject {
21575
21940
  }
21576
21941
  mapConstraintGeometry(mapElement, mapPoint) {
21577
21942
  const mapReference = /* @__PURE__ */ __name((reference) => reference.model ? reference : { ...reference, ...mapElement(reference) }, "mapReference");
21578
- 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) => {
21579
21949
  const mapped = {
21580
21950
  ...constraint,
21581
- source: mapReference(constraint.source),
21582
- target: mapReference(constraint.target),
21583
- rotations: constraint.rotations.map((action) => ({
21584
- ...action,
21585
- 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
21586
- }))
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
+ } : {}
21587
21974
  };
21588
21975
  valueTraces.set(mapped, valueTrace(constraint));
21589
21976
  return mapped;
@@ -21614,7 +22001,7 @@ var ModelObject = class _ModelObject extends RelationObject {
21614
22001
  {
21615
22002
  geometryAnchor: scaleElement(this.geometryAnchor, factor),
21616
22003
  elements: scaleElements(this.elements, factor),
21617
- constraints: this.mapConstraintGeometry(
22004
+ placements: this.mapConstraintGeometry(
21618
22005
  (element) => scaleElement(element, factor),
21619
22006
  (point3) => scaleFrame(translation(point3), factor).position
21620
22007
  )
@@ -21650,7 +22037,7 @@ var ModelObject = class _ModelObject extends RelationObject {
21650
22037
  name: "Extrude",
21651
22038
  geometry,
21652
22039
  material: this.materialSnapshot,
21653
- constraints: this.constraints,
22040
+ placements: this.placements,
21654
22041
  sourceRefs: this.sourceRefs,
21655
22042
  parameters: this.allParameters(),
21656
22043
  meshTolerance: this.meshTolerance,
@@ -21797,7 +22184,7 @@ var ModelObject = class _ModelObject extends RelationObject {
21797
22184
  return [
21798
22185
  ...this.children,
21799
22186
  ...this.operation.inputs.map((input) => input.model),
21800
- ...this.constraints.flatMap(constraintReferences)
22187
+ ...this.placements.flatMap(constraintReferences)
21801
22188
  ];
21802
22189
  }
21803
22190
  /** @internal */
@@ -21813,7 +22200,7 @@ var ModelObject = class _ModelObject extends RelationObject {
21813
22200
  );
21814
22201
  const parameters = uniqueParameters([
21815
22202
  ...this.parameters,
21816
- ...this.constraints.flatMap(
22203
+ ...this.placements.flatMap(
21817
22204
  (constraint) => valueTrace(constraint).parameters
21818
22205
  )
21819
22206
  ]);
@@ -21825,6 +22212,8 @@ var ModelObject = class _ModelObject extends RelationObject {
21825
22212
  compositionTransform: toTransform(pose),
21826
22213
  transform: toTransform(inComposition ? pose : identityRigidTransform),
21827
22214
  constraints,
22215
+ transformations: this.transformationSnapshots(solveContext),
22216
+ relationStages: this.relationStageSnapshots(solveContext),
21828
22217
  origin,
21829
22218
  elements: snapshotElements({
21830
22219
  ...this.elements,
@@ -21919,7 +22308,7 @@ var ModelObject = class _ModelObject extends RelationObject {
21919
22308
  name: "Loft",
21920
22309
  geometry,
21921
22310
  material: this.materialSnapshot,
21922
- constraints: this.constraints,
22311
+ placements: this.placements,
21923
22312
  sourceRefs: inputs.flatMap((input) => input.sourceRefs),
21924
22313
  parameters: uniqueParameters(
21925
22314
  inputs.flatMap((input) => input.allParameters())
@@ -21947,7 +22336,7 @@ var ModelObject = class _ModelObject extends RelationObject {
21947
22336
  geometry: evaluation.geometry,
21948
22337
  name: this.name,
21949
22338
  material: this.materialSnapshot,
21950
- constraints: this.constraints,
22339
+ placements: this.placements,
21951
22340
  sourceRefs: [this, ...others].flatMap((model) => model.sourceRefs),
21952
22341
  parameters: uniqueParameters(
21953
22342
  [this, ...others].flatMap((model) => model.allParameters())
@@ -22065,7 +22454,7 @@ var ModelObject = class _ModelObject extends RelationObject {
22065
22454
  allParameters() {
22066
22455
  return uniqueParameters([
22067
22456
  ...this.parameters,
22068
- ...this.constraints.flatMap(
22457
+ ...this.placements.flatMap(
22069
22458
  (constraint) => valueTrace(constraint).parameters
22070
22459
  )
22071
22460
  ]);
@@ -22197,19 +22586,22 @@ var ModelObject = class _ModelObject extends RelationObject {
22197
22586
  } : reference
22198
22587
  );
22199
22588
  }
22589
+ edgeReference(id) {
22590
+ return { ...rotationAxisReference(this.edge(id)), model: void 0 };
22591
+ }
22200
22592
  vertexPosition(id) {
22201
22593
  return anchorReference(this.vertex(id)).transform.position;
22202
22594
  }
22203
22595
  copyRelations(init) {
22204
22596
  return this.copy(init, this.operation);
22205
22597
  }
22206
- /** 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. */
22207
22599
  static createAssembly(children) {
22208
22600
  const context = _ModelObject.createSolveContext(children);
22209
- const center = children.length ? boundsCenter(
22210
- pointBounds(children.map((child) => child.solvePose(context).position))
22211
- ) : origin;
22212
- return transformSolveContext(context, translation(negateVector(center)));
22601
+ return children.length ? transformSolveContext(
22602
+ context,
22603
+ invertTransform(children[0].solvePose(context))
22604
+ ) : context;
22213
22605
  }
22214
22606
  requireGeometry() {
22215
22607
  if (!this.geometry) {
@@ -22279,7 +22671,7 @@ var ModelObject = class _ModelObject extends RelationObject {
22279
22671
  material: this.materialSnapshot,
22280
22672
  children: this.children,
22281
22673
  assembly: this.assembly,
22282
- constraints: this.constraints,
22674
+ placements: this.placements,
22283
22675
  elements: this.elements,
22284
22676
  sourceRefs: this.sourceRefs,
22285
22677
  parameters: this.parameters,
@@ -22815,19 +23207,32 @@ function isConstraint(value) {
22815
23207
  return value instanceof Constraint;
22816
23208
  }
22817
23209
  __name(isConstraint, "isConstraint");
22818
- function isConstraintExpression(value) {
22819
- return value instanceof ConstraintExpression;
22820
- }
22821
- __name(isConstraintExpression, "isConstraintExpression");
22822
- function constraintPreview(value) {
22823
- 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
+ );
22824
23229
  }
22825
- __name(constraintPreview, "constraintPreview");
22826
- function instrumentConstraint(constraint, sourceRef, parameters) {
23230
+ __name(relationSelectionPreview, "relationSelectionPreview");
23231
+ function instrumentRelation(constraint, sourceRef, parameters) {
22827
23232
  constraint.attachSource(sourceRef);
22828
23233
  constraint.attachParameters(parameters);
22829
23234
  }
22830
- __name(instrumentConstraint, "instrumentConstraint");
23235
+ __name(instrumentRelation, "instrumentRelation");
22831
23236
  function instrumentModelOperation(object, instrumentation) {
22832
23237
  object.attachSource(instrumentation.sourceRef);
22833
23238
  object.attachParameters(instrumentation.parameters);
@@ -22840,10 +23245,10 @@ function instrumentModelOperation(object, instrumentation) {
22840
23245
  );
22841
23246
  }
22842
23247
  __name(instrumentModelOperation, "instrumentModelOperation");
22843
- function constraintTraceReference(constraint) {
23248
+ function relationTraceReference(constraint) {
22844
23249
  return constraint.traceReference();
22845
23250
  }
22846
- __name(constraintTraceReference, "constraintTraceReference");
23251
+ __name(relationTraceReference, "relationTraceReference");
22847
23252
  function modelObjectRuntimeInfo(object) {
22848
23253
  return {
22849
23254
  nodeId: object.nodeId,
@@ -23045,6 +23450,13 @@ function retainModelGeometry(objects) {
23045
23450
  }
23046
23451
  __name(retainModelGeometry, "retainModelGeometry");
23047
23452
  var authoringApi = Object.freeze({
23453
+ offset,
23454
+ rotate: rotate2,
23455
+ pivot,
23456
+ pivotVertex,
23457
+ pivotPoint,
23458
+ axisEdge,
23459
+ axisLine,
23048
23460
  cached,
23049
23461
  font,
23050
23462
  googleFont,
@@ -23124,7 +23536,10 @@ function disposeModelGeometryValue(geometry) {
23124
23536
  __name(disposeModelGeometryValue, "disposeModelGeometryValue");
23125
23537
  var kernelShape = cachedArtifact(
23126
23538
  (_operation, _arguments, _inputs, compute) => compute(),
23127
- { key: kernelOperationKey, lifecycle: shapeLifecycle }
23539
+ {
23540
+ key: /* @__PURE__ */ __name((operation, arguments_, inputs) => kernelOperationKey(`kernel-shape:${operation}`, arguments_, inputs), "key"),
23541
+ lifecycle: shapeLifecycle
23542
+ }
23128
23543
  );
23129
23544
  function evaluateKernelShape(operation, arguments_, inputs, compute) {
23130
23545
  return kernelShape(
@@ -23143,7 +23558,10 @@ var evaluateModelGeometry = cachedArtifact(
23143
23558
  referenceBasis: result.referenceBasis
23144
23559
  };
23145
23560
  },
23146
- { key: kernelOperationKey, lifecycle: modelGeometryLifecycle }
23561
+ {
23562
+ key: /* @__PURE__ */ __name((operation, arguments_, inputs) => kernelOperationKey(`model-geometry:${operation}`, arguments_, inputs), "key"),
23563
+ lifecycle: modelGeometryLifecycle
23564
+ }
23147
23565
  );
23148
23566
  function createModelGeometryValue(shape, topology) {
23149
23567
  try {
@@ -23211,7 +23629,7 @@ function meshUVs(shape, vertexCount) {
23211
23629
  const faces = shape.faces;
23212
23630
  const location = new (getOC12()).TopLoc_Location();
23213
23631
  const uvs = new Float32Array(vertexCount * 2);
23214
- let offset = 0;
23632
+ let offset2 = 0;
23215
23633
  try {
23216
23634
  for (const face of faces) {
23217
23635
  const triangulation = getOC12().BRep_Tool.Triangulation(
@@ -23229,8 +23647,8 @@ function meshUVs(shape, vertexCount) {
23229
23647
  const uv = triangulation.UVNode(index + 1);
23230
23648
  try {
23231
23649
  const u8 = uv.X(), v = uv.Y();
23232
- uvs[(offset + index) * 2] = u8;
23233
- uvs[(offset + index) * 2 + 1] = v;
23650
+ uvs[(offset2 + index) * 2] = u8;
23651
+ uvs[(offset2 + index) * 2 + 1] = v;
23234
23652
  minU = Math.min(minU, u8);
23235
23653
  maxU = Math.max(maxU, u8);
23236
23654
  minV = Math.min(minV, v);
@@ -23240,11 +23658,11 @@ function meshUVs(shape, vertexCount) {
23240
23658
  }
23241
23659
  }
23242
23660
  for (let index = 0; index < count; index++) {
23243
- const base = (offset + index) * 2;
23661
+ const base = (offset2 + index) * 2;
23244
23662
  uvs[base] = maxU === minU ? 0 : (uvs[base] - minU) / (maxU - minU);
23245
23663
  uvs[base + 1] = maxV === minV ? 0 : (uvs[base + 1] - minV) / (maxV - minV);
23246
23664
  }
23247
- offset += count;
23665
+ offset2 += count;
23248
23666
  } finally {
23249
23667
  triangulation.delete();
23250
23668
  }
@@ -23256,7 +23674,7 @@ function meshUVs(shape, vertexCount) {
23256
23674
  }
23257
23675
  }
23258
23676
  __name(meshUVs, "meshUVs");
23259
- function sketchFaceModel(region, constraints, source) {
23677
+ function sketchFaceModel(region, placements, source) {
23260
23678
  const curves = [region.outer, ...region.holes].map(
23261
23679
  (loop) => loop.map(
23262
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]
@@ -23272,7 +23690,7 @@ function sketchFaceModel(region, constraints, source) {
23272
23690
  return ModelObject.create({
23273
23691
  kind: "face",
23274
23692
  name: "Sketch face",
23275
- constraints,
23693
+ placements,
23276
23694
  geometry,
23277
23695
  geometryAnchor: plane,
23278
23696
  elements: { plane },
@@ -23411,10 +23829,9 @@ function solidElements(bounds) {
23411
23829
  __name(solidElements, "solidElements");
23412
23830
  function constraintReferences(constraint) {
23413
23831
  return [
23414
- constraint.source.model,
23415
- constraint.target.model,
23416
- ...constraint.rotations.map(
23417
- (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
23418
23835
  )
23419
23836
  ].filter((model) => !!model);
23420
23837
  }
@@ -24743,6 +25160,7 @@ export {
24743
25160
  setKernelNativeMemoryCounter,
24744
25161
  clearKernelOperationCache,
24745
25162
  kernelOperationCacheStats,
25163
+ setKernelCacheBudget,
24746
25164
  setKernelArtifactStore,
24747
25165
  setKernelExternalBytes,
24748
25166
  identifyCachedFunction,
@@ -24787,6 +25205,13 @@ export {
24787
25205
  modelElementReference,
24788
25206
  modelTopologyReference,
24789
25207
  modelTopologyIds,
25208
+ offset,
25209
+ rotate2 as rotate,
25210
+ pivot,
25211
+ pivotVertex,
25212
+ pivotPoint,
25213
+ axisEdge,
25214
+ axisLine,
24790
25215
  circle,
24791
25216
  ellipse,
24792
25217
  rectangle,
@@ -24813,11 +25238,13 @@ export {
24813
25238
  intersect,
24814
25239
  isModelObject,
24815
25240
  isConstraint,
24816
- isConstraintExpression,
24817
- constraintPreview,
24818
- instrumentConstraint,
25241
+ isRelationExpression,
25242
+ relationPreview,
25243
+ currentRelationSelf,
25244
+ relationSelectionPreview,
25245
+ instrumentRelation,
24819
25246
  instrumentModelOperation,
24820
- constraintTraceReference,
25247
+ relationTraceReference,
24821
25248
  modelObjectRuntimeInfo,
24822
25249
  relatedModelObjects,
24823
25250
  planModelSnapshotQueries,
@@ -24839,4 +25266,4 @@ export {
24839
25266
  assertSketchDragConnections,
24840
25267
  solveSketchSnapshot
24841
25268
  };
24842
- //# sourceMappingURL=chunk-J77FMYFY.js.map
25269
+ //# sourceMappingURL=chunk-T3RAUNSF.js.map