@code3d/core 0.0.1-alpha.0 → 0.0.1-alpha.11

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 (154) hide show
  1. package/README.md +43 -807
  2. package/THIRD_PARTY.md +40 -0
  3. package/bld/THIRD_PARTY_NOTICES.txt +182 -0
  4. package/bld/chunks/chunk-7FIFPADJ.js +1 -0
  5. package/bld/chunks/chunk-7FIFPADJ.js.map +7 -0
  6. package/bld/chunks/chunk-CL3E2DE4.js +19 -0
  7. package/bld/chunks/chunk-CL3E2DE4.js.map +7 -0
  8. package/bld/chunks/chunk-HZRQUHM2.js +30 -0
  9. package/bld/chunks/chunk-HZRQUHM2.js.map +7 -0
  10. package/bld/chunks/chunk-JBVIAMJ6.js +26474 -0
  11. package/bld/chunks/chunk-JBVIAMJ6.js.map +7 -0
  12. package/bld/chunks/chunk-QK6ZET47.js +31 -0
  13. package/bld/chunks/chunk-QK6ZET47.js.map +7 -0
  14. package/bld/library/bound-solver.d.ts +29 -5
  15. package/bld/library/bound-solver.d.ts.map +1 -1
  16. package/bld/library/cached.d.ts +35 -0
  17. package/bld/library/cached.d.ts.map +1 -0
  18. package/bld/library/font.d.ts +39 -0
  19. package/bld/library/font.d.ts.map +1 -0
  20. package/bld/library/google-font.d.ts +14 -0
  21. package/bld/library/google-font.d.ts.map +1 -0
  22. package/bld/library/index.d.ts +10 -2
  23. package/bld/library/index.d.ts.map +1 -1
  24. package/bld/library/index.js +94 -3
  25. package/bld/library/index.js.map +7 -1
  26. package/bld/library/inspect.d.ts +100 -0
  27. package/bld/library/inspect.d.ts.map +1 -0
  28. package/bld/library/kernel-artifact-codec.d.ts.map +1 -1
  29. package/bld/library/kernel-cache.d.ts +28 -7
  30. package/bld/library/kernel-cache.d.ts.map +1 -1
  31. package/bld/library/loft.d.ts.map +1 -1
  32. package/bld/library/material.d.ts +1 -1
  33. package/bld/library/material.d.ts.map +1 -1
  34. package/bld/library/open-cascade.d.ts +4 -0
  35. package/bld/library/open-cascade.d.ts.map +1 -0
  36. package/bld/library/relation-solver.d.ts +19 -8
  37. package/bld/library/relation-solver.d.ts.map +1 -1
  38. package/bld/library/replicad.d.ts +3 -2
  39. package/bld/library/replicad.d.ts.map +1 -1
  40. package/bld/library/replicad.js +10 -24
  41. package/bld/library/replicad.js.map +7 -1
  42. package/bld/library/retained-memory.d.ts.map +1 -1
  43. package/bld/library/runtime.d.ts +518 -155
  44. package/bld/library/runtime.d.ts.map +1 -1
  45. package/bld/library/sketch-solver.d.ts +1 -1
  46. package/bld/library/sketch-solver.d.ts.map +1 -1
  47. package/bld/library/sketch.d.ts +21 -1
  48. package/bld/library/sketch.d.ts.map +1 -1
  49. package/bld/library/text.d.ts +53 -0
  50. package/bld/library/text.d.ts.map +1 -0
  51. package/bld/library/three.js +3 -3
  52. package/bld/library/three.js.map +7 -1
  53. package/bld/library/topology.d.ts +6 -2
  54. package/bld/library/topology.d.ts.map +1 -1
  55. package/bld/node/index.d.ts.map +1 -1
  56. package/bld/node/index.js +96 -14
  57. package/bld/node/index.js.map +7 -1
  58. package/bld/node/replicad.js +13 -3
  59. package/bld/node/replicad.js.map +7 -1
  60. package/bld/tooling/index.d.ts +18 -15
  61. package/bld/tooling/index.d.ts.map +1 -1
  62. package/bld/tooling/index.js +165 -19
  63. package/bld/tooling/index.js.map +7 -1
  64. package/docs/api.md +764 -0
  65. package/docs/custom-primitives.mdx +58 -0
  66. package/docs/local-coordinates.md +187 -0
  67. package/docs/origins-and-rotation.mdx +105 -0
  68. package/docs/relations.mdx +450 -0
  69. package/docs/runtime.md +297 -0
  70. package/docs/shells.mdx +70 -0
  71. package/docs/sketches.md +83 -0
  72. package/docs/text.md +23 -0
  73. package/docs/topology.md +152 -0
  74. package/docs/values.md +119 -0
  75. package/package.json +20 -7
  76. package/src/library/bound-solver.ts +103 -73
  77. package/src/library/cached.ts +153 -0
  78. package/src/library/font.ts +186 -0
  79. package/src/library/google-font.ts +97 -0
  80. package/src/library/index.ts +53 -5
  81. package/src/library/inspect.ts +178 -0
  82. package/src/library/kernel-artifact-codec.ts +129 -20
  83. package/src/library/kernel-cache.ts +175 -39
  84. package/src/library/loft.ts +9 -1
  85. package/src/library/material.ts +3 -4
  86. package/src/library/open-cascade.ts +13 -0
  87. package/src/library/relation-solver.ts +230 -113
  88. package/src/library/replicad.ts +5 -5
  89. package/src/library/retained-memory.ts +6 -1
  90. package/src/library/runtime.ts +3769 -1178
  91. package/src/library/shell.ts +3 -3
  92. package/src/library/sketch-solver.ts +3 -1
  93. package/src/library/sketch.ts +112 -13
  94. package/src/library/text.ts +477 -0
  95. package/src/library/topology.ts +61 -17
  96. package/src/library/tsconfig.json +2 -1
  97. package/src/node/index.ts +14 -3
  98. package/src/node/tsconfig.json +2 -1
  99. package/src/tooling/index.ts +64 -51
  100. package/src/tooling/tsconfig.json +2 -1
  101. package/bld/library/alignment-geometry.js +0 -322
  102. package/bld/library/alignment-geometry.js.map +0 -1
  103. package/bld/library/bound-solver.js +0 -252
  104. package/bld/library/bound-solver.js.map +0 -1
  105. package/bld/library/extrude.js +0 -28
  106. package/bld/library/extrude.js.map +0 -1
  107. package/bld/library/kernel-artifact-codec.js +0 -148
  108. package/bld/library/kernel-artifact-codec.js.map +0 -1
  109. package/bld/library/kernel-cache.js +0 -245
  110. package/bld/library/kernel-cache.js.map +0 -1
  111. package/bld/library/kernel-shapes.js +0 -108
  112. package/bld/library/kernel-shapes.js.map +0 -1
  113. package/bld/library/loft.js +0 -161
  114. package/bld/library/loft.js.map +0 -1
  115. package/bld/library/material.js +0 -176
  116. package/bld/library/material.js.map +0 -1
  117. package/bld/library/model-color.js +0 -36
  118. package/bld/library/model-color.js.map +0 -1
  119. package/bld/library/open-cascade-error.js +0 -22
  120. package/bld/library/open-cascade-error.js.map +0 -1
  121. package/bld/library/relation-solver.js +0 -258
  122. package/bld/library/relation-solver.js.map +0 -1
  123. package/bld/library/retained-memory.js +0 -27
  124. package/bld/library/retained-memory.js.map +0 -1
  125. package/bld/library/runtime.js +0 -2954
  126. package/bld/library/runtime.js.map +0 -1
  127. package/bld/library/shell.js +0 -221
  128. package/bld/library/shell.js.map +0 -1
  129. package/bld/library/sketch-curve-intersections.js +0 -117
  130. package/bld/library/sketch-curve-intersections.js.map +0 -1
  131. package/bld/library/sketch-curves.js +0 -89
  132. package/bld/library/sketch-curves.js.map +0 -1
  133. package/bld/library/sketch-drag-rules.js +0 -522
  134. package/bld/library/sketch-drag-rules.js.map +0 -1
  135. package/bld/library/sketch-face.js +0 -72
  136. package/bld/library/sketch-face.js.map +0 -1
  137. package/bld/library/sketch-incidence.js +0 -114
  138. package/bld/library/sketch-incidence.js.map +0 -1
  139. package/bld/library/sketch-precision.js +0 -55
  140. package/bld/library/sketch-precision.js.map +0 -1
  141. package/bld/library/sketch-regions.js +0 -193
  142. package/bld/library/sketch-regions.js.map +0 -1
  143. package/bld/library/sketch-solver.js +0 -578
  144. package/bld/library/sketch-solver.js.map +0 -1
  145. package/bld/library/sketch.js +0 -650
  146. package/bld/library/sketch.js.map +0 -1
  147. package/bld/library/spatial.js +0 -195
  148. package/bld/library/spatial.js.map +0 -1
  149. package/bld/library/topology-id.js +0 -67
  150. package/bld/library/topology-id.js.map +0 -1
  151. package/bld/library/topology-inspection.js +0 -267
  152. package/bld/library/topology-inspection.js.map +0 -1
  153. package/bld/library/topology.js +0 -455
  154. package/bld/library/topology.js.map +0 -1
@@ -0,0 +1,450 @@
1
+ ---
2
+ title: Positioning with relations
3
+ description: Position geometry with bound contacts, geometric alignment, and explicit rotations.
4
+ sidebar:
5
+ order: 4
6
+ ---
7
+
8
+ import ModelExample from '../../web/src/components/ModelExample.astro';
9
+ import SourceCode from '../../web/src/components/SourceCode.astro';
10
+ import ArrowIcon from '../../web/src/components/ArrowIcon.astro';
11
+ import {appUrl, sitePath} from '../../web/src/lib/site';
12
+ import source from '../../app/examples/constraints/combined-constraints.ts?raw';
13
+ import transformations from '../../app/examples/constraints/transformations.ts?raw';
14
+
15
+ A relation describes how one part sits against another. It is attached to a
16
+ model value and resolved when the parts are composed or their relative geometry is queried. The part keeps its own
17
+ [local coordinates](local-coordinates.md): `originOffset()` changes
18
+ those geometry coordinates, while a relation's `offset()` participates in
19
+ placement. Consecutive constraints are solved together; independent transformations
20
+ then move that joint result.
21
+
22
+ <ModelExample id="relate" />
23
+
24
+ ## Touch a directional bound
25
+
26
+ `part.on(base.up)` places the part above the base by translating its matching
27
+ bound into contact. The six target directions are `up` (+Y), `down` (−Y),
28
+ `right` (+X), `left` (−X), `front` (+Z), and `back` (−Z), measured in the base's
29
+ local frame. They describe its finite bounding extent, not particular faces.
30
+
31
+ The source can also be a selected vertex, edge, or surface. Only that selected
32
+ geometry contributes its extent. A tilted shape still has a support boundary
33
+ in the target direction; `on` never rotates it. Without an offset, its position
34
+ along the boundary plane stays unchanged.
35
+
36
+ An explicit `offset(x, y, z)` translates self from the original solution in the
37
+ **target reference frame**. Zero leaves the result unchanged; it never adds
38
+ centering constraints. Use explicit point or axis alignment to center parts.
39
+ Consecutive offsets in the same frame add. Offsets and rotations execute in
40
+ call order: a translation before an external-axis rotation moves with that
41
+ rotation; the same translation after it does not.
42
+ `base.up.flip()` reverses the side of contact while keeping that frame and the
43
+ geometry unchanged. Arbitrary models or topology elements are not valid targets.
44
+
45
+ ## Combine conditions
46
+
47
+ Return an array from `relate()` to solve several relations together:
48
+
49
+ <SourceCode source={source} file="constraints/combined-constraints.ts" />
50
+
51
+ <p>
52
+ <a
53
+ href={appUrl('constraints/combined-constraints.ts')}
54
+ target="_blank"
55
+ rel="noopener"
56
+ >
57
+ {'Open this example in the App '}
58
+ <ArrowIcon />
59
+ </a>
60
+ </p>
61
+
62
+ The two contacts place the second box at `[15, -15, 0]`. One determines X,
63
+ the other determines Y, and the free Z position stays unchanged. Conflicting
64
+ position conditions report an error. `on` only solves translation; rotations
65
+ are always authored explicitly.
66
+
67
+ ## Align underlying geometry
68
+
69
+ Use `align` when the relationship should determine geometry and direction:
70
+
71
+ ```ts
72
+ const post = cylinder(5, 20).relate(self => [
73
+ self.axis.align(base.axis),
74
+ self.on(base.up),
75
+ ]);
76
+ ```
77
+
78
+ Points coincide or lie on a curve or surface. Curves coincide with one another,
79
+ or lie entirely on a surface. Surfaces coincide with matching normal sense.
80
+ Unlike a bound contact, alignment can rotate self. Lines, circles, ellipses,
81
+ planes, cylinders, and spheres are supported; other underlying geometry types
82
+ report unsupported.
83
+
84
+ Edges and faces contribute their complete supporting geometry. A point can lie
85
+ on the extension of a short straight edge; two equal-radius arcs can share a
86
+ circle even when their trimmed ranges differ. Endpoints and parameter origins
87
+ are not paired. Use `start`, `midpoint`, `end`, or a vertex when more specific
88
+ positioning is needed. Multiple solutions can remain.
89
+
90
+ Curve–curve alignment uses the same positive direction. Use `target.reverse()`
91
+ to reverse a line, axis, or edge reference, and `target.flip()` to reverse a
92
+ surface's normal sense. These change direction metadata while preserving the
93
+ reference axes. Point membership ignores direction; a curve lying on a surface
94
+ does not gain an arbitrary heading in that surface.
95
+
96
+ Constraints do not have offset or rotation methods. To adjust their solved pose,
97
+ add an independent `offset(x, y, z)` array entry. Its direction uses the fixed
98
+ composition axes, regardless of which side of `on` or `align` names self.
99
+ Zero leaves the preceding solution unchanged.
100
+
101
+ <ModelExample id="expose" />
102
+
103
+ The pin function exposes its mounting face, tip face and centerline. The cap
104
+ aligns its axis to the pin and touches its tip. These two relations express the
105
+ actual fit; changing the pin's height does not require recomputing the cap position.
106
+
107
+ Relation curves keep their actual shape and serve as the arrow shaft. Only
108
+ the arrowhead is added at the directed endpoint (or seam for a closed curve),
109
+ following its tangent.
110
+ `reverse()` uses the opposite endpoint and tangent. Axes use one positive
111
+ arrow while inspecting a relation; passive axis references have arrows at
112
+ both ends.
113
+ Arrowheads keep the same screen size as you zoom. Unsupported geometry,
114
+ geometric incompatibility, and numerical nonconvergence have separate errors.
115
+
116
+ To adjust a multi-constraint result visually, select self. The App edits or adds
117
+ an independent transformation after the joint constraint segment.
118
+
119
+ ## Align coordinate systems
120
+
121
+ All models, including groups, expose `frame` and `origin` references.
122
+ `model.origin` is the same reference as `model.frame.origin`; neither reference
123
+ adds geometry to the result.
124
+
125
+ ```ts
126
+ const positioned = part.relate(self => self.origin.align(base.origin));
127
+ const mounted = part.relate(self => self.frame.align(base.frame));
128
+ ```
129
+
130
+ Origin alignment leaves orientation free. Frame alignment coincides the origins
131
+ and the corresponding X/Y/Z directions, fixing all six relative degrees of
132
+ freedom. It uses the target's solved placement, including its relations to other
133
+ models. The target can remain a reference dependency without being included in
134
+ the output group. Both constraints participate in the same joint solve as other
135
+ `align` and `on` constraints.
136
+
137
+ Named frames work the same way: `assembly.expose({mount: part.frame})` provides
138
+ `assembly.mount` and `assembly.mount.origin` in the assembly's coordinates.
139
+
140
+ ## Choose a rotation pivot or axis
141
+
142
+ ```ts
143
+ import {pivot} from '@code3d/core';
144
+
145
+ const placed = part.relate(self => [
146
+ self.on(base.up),
147
+ pivot([50, 0, 0]).rotate(0, 0, 45),
148
+ ]);
149
+ ```
150
+
151
+ The pivot is expressed in self's local coordinates. Use `pivotVertex(id)` to
152
+ choose one of self's vertices, or omit the pivot and call `rotate(x, y, z)`
153
+ to rotate around self's origin. Angles are degrees, applied X, then Y, then Z.
154
+ `axisLine(base.axis).rotate(angle)` instead uses a positioned line axis; external
155
+ axes follow their own composition placement. Each selection applies only to
156
+ the next rotation.
157
+
158
+ To move a reference without losing it, use
159
+ `pivotVertex(id).pivotOffset(dx, dy, dz).rotate(x, y, z)` or
160
+ `axisLine(base.axis).axisOffset(dx, dy, dz).rotate(angle)`. `pivot([...])` also
161
+ accepts `pivotOffset`. The point displacement uses self's local axes; the axis
162
+ displacement uses the selected axis frame. Moving along the axis itself has no
163
+ effect on the rotation. These are reference modifiers, followed by one `rotate`;
164
+ completed transformations remain separate array entries.
165
+
166
+ Rotations follow the preceding joint constraint solution. Later transformations
167
+ compose in order; a later constraint starts a new segment from that pose.
168
+ Groups move their assembled children as one rigid part.
169
+
170
+ ### Bend a loft with explicit rotations
171
+
172
+ <ModelExample id="loft" />
173
+
174
+ The octagonal and rectangular sections first touch the circle's `up` bound,
175
+ then turn 45° and 90° around a pivot 50 units along self's X axis.
176
+ Inspect `sections` to see the positioned profiles, or `loft` to see the solid
177
+ through them. Editing `pivot` or `rotate` changes the same source used here.
178
+
179
+ ## Transform a joint result
180
+
181
+ Import `offset`, `rotate`, `pivot`, `pivotVertex`, `pivotPoint`, `axisEdge`, or `axisLine` from Core to build
182
+ an independent `Transformation` inside `relate`. It acts on the joint solution
183
+ and can move the part away from its contacts:
184
+
185
+ <SourceCode source={transformations} file="constraints/transformations.ts" />
186
+
187
+ <p>
188
+ <a
189
+ href={appUrl('constraints/transformations.ts')}
190
+ target="_blank"
191
+ rel="noopener"
192
+ >
193
+ {'Open the transformation example '}
194
+ <ArrowIcon />
195
+ </a>
196
+ </p>
197
+
198
+ Independent `offset(x, y, z)` uses the composition's fixed axes. Rotating the
199
+ part does not rotate those offset directions. Independent `rotate(x, y, z)`
200
+ uses self's current origin and local axes; `pivot([x, y, z])` changes that center,
201
+ `pivotVertex(id)` chooses a self vertex; `pivotPoint(pointRef)` selects a local or external point while keeping self XYZ axes. `axisEdge(id).rotate(angle)` chooses a straight self edge, and `axisLine(lineRef).rotate(angle)` uses
202
+ the referenced axis in its solved placement. Pivot selections apply to the next
203
+ rotation. Completed transformations cannot be chained: write `[offset(...), rotate(...)]`,
204
+ not `offset(...).rotate(...)`. Only pivot and axis selections expose `rotate` to
205
+ complete that one operation. These constructors describe relative placement;
206
+ they do not add `model.offset()` or change standalone geometry.
207
+
208
+ Transformation array entries execute in order. A later constraint
209
+ starts a new joint segment: `[align, on, offset, rotate]` solves the two constraints
210
+ then transforms the result; `[align, offset, on]` solves align, moves the result,
211
+ then solves on from that pose. The later segment preserves its unconstrained
212
+ position and orientation from its input. Previous segments do not remain hard
213
+ conditions on that later result. Successive `relate` calls continue the same
214
+ sequence; constraints without an intervening transformation remain one segment.
215
+
216
+ The fixed composition frame belongs to this assembly. A nested group's existing
217
+ placement and origin rules carry it into the enclosing group; it is not the
218
+ viewport's world frame. Call order remains meaningful, especially around a fixed
219
+ external axis. A fixed-axis translation and a rotation about the current self
220
+ origin can happen to commute without changing the sequencing rule.
221
+
222
+ ## Choose which part moves
223
+
224
+ `relate` returns a new value and passes it to the callback as `self`. Every
225
+ constraint must involve that value, even when written as `base.on(self.up)`.
226
+ The parameter can have any name; it is its value that identifies the new part.
227
+ External variables always refer to their existing model, so a part can be
228
+ placed against the original value from which it was derived:
229
+
230
+ ```ts
231
+ const side = box(2, 250, 250);
232
+ const leftSide = side;
233
+ const rightSide = side.relate(self => [
234
+ self.on(leftSide.right),
235
+ offset(160, 0, 0),
236
+ ]);
237
+ const assembly = group([leftSide, rightSide]);
238
+ ```
239
+
240
+ The two panel centers are 162 units apart, with a 160-unit gap. `side` and
241
+ `leftSide` remain unchanged; no intermediate copy is needed.
242
+
243
+ This identity rule also applies to selected elements and rotation references:
244
+ `pivotPoint(self.center)` follows the new part's center, while
245
+ `pivotPoint(side.center)` rotates around the original part's center.
246
+ `axisLine(self.axis)` and `axisLine(side.axis)` distinguish the same two owners.
247
+ Use `self.surface(id)`, `self.edge(id)`, or `self.vertex(id)` to constrain an
248
+ element of the new part. A reference selected before the callback stays attached
249
+ to its original model.
250
+
251
+ ## Inspect the right scope
252
+
253
+ The related part on its own still displays in its local frame. Select the
254
+ `group` to see the part positioned against the base, or place the cursor in
255
+ the relation to inspect both participants. Related expressions inside the callback keep the actual participants visible.
256
+ `on` and `align` inspectors show the relevant anchors and finite support bounds.
257
+ Unrelated values keep their ordinary preview, and mixed collections preview all
258
+ of their members. An inner call's own inspector or ordinary result takes
259
+ precedence over the enclosing callback.
260
+
261
+ The callback parameter in `relate(part => ...)` is the new related model
262
+ value. Place the cursor on its declaration or a use of `part` inside the
263
+ callback to inspect its completed placement segment. Its tools edit the nearest
264
+ following offset or rotation, with the preview including later operations in that segment.
265
+ Even before you write a consuming
266
+ `group` or `loft`, the relation context shows the participants in their solved
267
+ positions. Selecting the related model's standalone binding shows it in its local
268
+ frame. Operations preserve the observable meaning of existing model values.
269
+
270
+ In a `relate` array, `on` and `align` show the joint solution of their continuous
271
+ constraint segment. Each independent `offset` or `rotate` shows its own result.
272
+ The pivot and axis selectors show the preceding pose with the selected reference.
273
+ Later steps do not move this preview or its handles. Previous segments provide
274
+ the incoming pose; later segments are excluded. An invalid segment reports a
275
+ preview error while keeping the valid final model available.
276
+
277
+ <p>
278
+ <a href={sitePath('examples/relate/')}>
279
+ Step through contact, target, offset, and rotation in the relation preview →
280
+ </a>
281
+ </p>
282
+
283
+ ## Read the relation highlights
284
+
285
+ The viewport distinguishes focused targets, other targets, and ambient geometry.
286
+ An inspector declares the complete scene. Targets that are the actual selected
287
+ values receive focus; generated results do not inherit focus from their inputs.
288
+ With focus present, other target markers use 70% of their normal opacity. Without
289
+ focus, all target markers keep their normal opacity. Ambient geometry stays gray.
290
+
291
+ On the `relate` method name, the completed part is the target and this call's
292
+ actual participants are ambient. Unrelated downstream parts are omitted. Inside
293
+ its callback, related values use the same participants and the selected relation
294
+ stage. Unrelated values use ordinary object preview; a mixed collection falls
295
+ through as a whole so no unrelated member disappears.
296
+
297
+ `on` and `align` have their own inspectors for the call, receiver and target.
298
+ Selecting a reference emphasizes that reference. Selecting the method name shows
299
+ the participating references without inventing a focused side. In
300
+ `base.on(self.up)`, selecting `self.up` therefore focuses self's bound. Independent
301
+ `offset`, `pivot`, `pivotVertex`, `pivotPoint`, `axisEdge`, `axisLine` and `rotate`
302
+ expressions use the relate stage; their editing handles remain separate from
303
+ passive inspection. See [source inspection](runtime.md#source-inspection) for
304
+ custom JSDoc inspectors and closure contexts.
305
+
306
+ Model surfaces remain translucent so you can see overlapping references.
307
+ Their opacity is capped for the current role instead of multiplied by the
308
+ material's existing opacity. A model that is already more transparent keeps
309
+ its material; the marker groups still show which side is selected.
310
+
311
+ A bound face adds translucent fill in the same color as its bounding box.
312
+ If that box is already visible, the face does not add another corner outline.
313
+ When no box is visible, it supplies its own corner brackets. The source of
314
+ `on` shows its complete measured bounding box, since contact is calculated
315
+ from that whole extent. For a selected vertex, edge, or surface, this box
316
+ encloses only the selected geometry. Bounds describe finite extents rather
317
+ than selecting a real topology face.
318
+
319
+ Face arrows, axis arrowheads, point markers, and spatial controls keep their
320
+ screen size through zoom and viewport resizing. Axis shafts and curved
321
+ highlights still follow their actual geometry.
322
+
323
+ ## Group or fuse
324
+
325
+ Use `group([base, post])` for separate parts. Use `union([base, post])`
326
+ when you want a single fused solid. Boolean operations also resolve operand
327
+ relations before evaluating geometry.
328
+
329
+ ## Make visual adjustments
330
+
331
+ Enter an `offset` call in the source to use the position tool. Supported
332
+ parameter changes write back to the defining source values. A unique upstream
333
+ parameter takes priority when the tool can safely preview its uses. If it is
334
+ shared, other affected objects preview the change as well.
335
+
336
+ Without a safe upstream parameter, dragging preserves the offset expression
337
+ and adjusts it by an increment. For example, an X movement of `2` changes
338
+ `offset((i - 2) * 8, 0, 0)` to `offset((i - 2) * 8 + 2, 0, 0)`.
339
+ Another movement of `3` combines that increment into `+ 5`. Returning by `5`
340
+ removes it. The tool does not change the inner `8` or keep nesting offset calls.
341
+
342
+ Selecting self shows the complete placement segment. Choosing a tool keeps a
343
+ matching current call, or checks the immediately following transformation. If
344
+ that transformation matches, it activates the existing call; otherwise it inserts
345
+ before that transformation. It never skips a different transformation to find
346
+ a matching one farther ahead. With no following transformation, it appends. In a multi-constraint array,
347
+ they target independent transformations after the jointly solved constraints.
348
+ The search stops at the next constraint segment. App inserts the corresponding
349
+ Core import when needed. It preserves existing aliases and avoids local name
350
+ collisions.
351
+
352
+ The toolbar starts with no active tool when selecting a model, self, or a blank
353
+ array position. Choose a tool explicitly, or place the caret in an existing
354
+ offset or rotation call to activate its tool. When focus moves into the viewport,
355
+ the editor keeps a static caret and word marker at the current source position.
356
+ The active tool's existing call is underlined; if the tool needs to insert a call,
357
+ the underline appears on that new code after the edit. Marked code scrolls into
358
+ view, including horizontally, without taking focus from the viewport or an input.
359
+ Parameter and topology tools use the same source markers and navigation.
360
+ Clicking a tool with an existing call moves the caret to the complete expression
361
+ end, such as `offset()|` or `axisEdge(1).rotate(25)|`, and shows that step immediately. If a new call is needed, the viewport shows the state just
362
+ before the actual insertion position. The chosen tool stays active, and clicking
363
+ alone does not change the source. New array calls put the caret in the insertion
364
+ gap: before the closing bracket at the end, or before the next item in the middle.
365
+ From an array gap, the same rule checks only the immediately following item;
366
+ a mismatch keeps the insertion position.
367
+
368
+ The start of a self expression, such as `|self.axis.align(...)`, also counts as
369
+ self context. Plain rotate and pivot selectors match point rotation; axis selectors match
370
+ axis rotation. Choosing one does not activate a call of the other type.
371
+
372
+ When an `offset` or `rotate` is selected, the matching tool edits that call.
373
+ Choosing another tool checks the next transformation first. A match activates it;
374
+ a mismatch inserts immediately after the selected call, before later operations. For independent transformations this inserts a new array
375
+ item; a single return value becomes an array. It never chains one completed
376
+ transformation into another. Handles and previews use the frame of that exact
377
+ edit, excluding later operations. A spread argument is replaced
378
+ with the current three values when editing that call.
379
+
380
+ A safe shared parameter can update multiple instances of the same call. If it
381
+ also controls a different operation, dragging adjusts the selected expression
382
+ instead. A shared expression changes all of its runtime occurrences; it is not
383
+ an override for only the clicked part.
384
+
385
+ Release to commit the preview, or press `Esc` while dragging to cancel without
386
+ closing the panel. Use Undo after an edit has been committed. Numeric panel
387
+ input has a different meaning when an expression appears as a placeholder:
388
+ it [replaces the whole argument](../../web/src/content/docs/docs/guides/model-tools.mdx#what-a-panel-can-edit).
389
+
390
+ Use the position toolbar above the panel to choose translation or a rotation
391
+ variant. In either rotation tool, hold Alt to move its reference instead of
392
+ rotating the part. Point or axis candidates appear while the corresponding
393
+ rotation tool is active; click one directly to choose the reference. Point selection
394
+ retains a vertex ID; axis selection uses a straight edge or a named axis of self.
395
+ Replacing a reference within the same rotation type retains its offset and angles;
396
+ choosing a reference for the other rotation type adds a new zero-angle operation.
397
+
398
+ To change the standalone geometry and its origin, see
399
+ [origins and rotation](origins-and-rotation.mdx).
400
+
401
+ See [code and geometry](../../web/src/content/docs/docs/concepts/code-and-geometry.md) for the distinction
402
+ between a model value and its runtime context.
403
+
404
+ ## Measure before building a part
405
+
406
+ Use `distance(a, b, axis?)` when another part's dimensions depend on existing
407
+ geometry. The query resolves the inputs' current relations and returns an
408
+ ordinary number; a group is not required before measuring.
409
+
410
+ <ModelExample id="distance" />
411
+
412
+ In this example, change `gap` to resize the opening and its beam. The beam reads
413
+ the X gap between the two facing bounds and the support's Z depth, then builds
414
+ with those dimensions. Its height remains fixed. The Arguments presets show
415
+ two sizes of the same assembly.
416
+
417
+ Omit the axis for the shortest spatial distance, or provide `x`, `y`, `z`, a
418
+ direction vector, or a straight edge/axis reference for the gap between projected
419
+ intervals. Use topology faces when their actual trimmed shape matters; a
420
+ directional bound measures its rectangular extent. See the
421
+ [measurement reference](api.md#measurements) for supported inputs
422
+ and coordinate rules.
423
+
424
+ Measurements follow source order. Adding a later relation does not retroactively
425
+ change a computed length. For a repeated part already in a group, expose the
426
+ intended occurrence and measure that reference.
427
+
428
+ Place the cursor anywhere inside a `distance(...)` call to inspect the measurement
429
+ in the viewport. Both measured objects are emphasized against dimmed related
430
+ models, with a distance line and numeric label. Whole solids and groups retain
431
+ their model appearance instead of receiving a face-selection overlay. The preview uses the solved
432
+ positions at that call, even before a `group` exists. A projected measurement is
433
+ marked with its axis: the line spans the projection intervals, so its endpoints
434
+ need not lie on the selected geometry. The line is placed within the shared
435
+ transverse bounds when possible: for a point inside a face’s projection, it starts
436
+ at the point and reaches the face along the measurement axis. Overlapping
437
+ intervals on the measurement axis show zero.
438
+
439
+ Inside the call, selecting a model variable emphasizes that model and dims the
440
+ other whole-model operand. Selecting an
441
+ element reference emphasizes that element while the other measured element stays
442
+ visible at secondary emphasis. An explicitly selected bound also shows its normal
443
+ direction; other measured bounds keep only their face and corner markers. The
444
+ measurement line stays visible as you move between arguments.
445
+
446
+ Measurements use a gray dashed line with short ticks at both ends and a neutral
447
+ numeric label. The X, Y, or Z letter uses the matching viewport axis color; custom
448
+ directions keep a neutral `axis` label. Dash spacing, endpoint ticks, and text
449
+ keep their screen size as you zoom and are included in annotated image exports. This preview is passive; measurement
450
+ parameter controls and element-picking tools are not yet available.