@things-factory/figure-ui 10.1.65 → 10.1.67

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 (176) hide show
  1. package/client/graphql/index.ts +0 -45
  2. package/client/modeller/figure-history-panel.ts +6 -145
  3. package/client/modeller/figure-v3-capability.ts +2 -2
  4. package/client/modeller/figure-v3-clips.ts +2 -2
  5. package/client/modeller/figure-v3-controls.ts +2 -2
  6. package/client/modeller/figure-v3-first-save.ts +1 -1
  7. package/client/modeller/figure-v3-links.ts +6 -6
  8. package/client/modeller/figure-v3-material.ts +11 -11
  9. package/client/modeller/figure-v3-parts.ts +7 -10
  10. package/client/modeller/figure-v3-preview.ts +3 -3
  11. package/client/modeller/figure-v3-properties.ts +5 -6
  12. package/client/modeller/figure-v3-verdict.ts +9 -10
  13. package/client/modeller/figure-view.ts +2 -3
  14. package/client/modeller/modeller-route.ts +2 -3
  15. package/client/modeller/scene-budget.ts +0 -14
  16. package/client/modeller/side-panel-styles.ts +5 -5
  17. package/client/modeller/v3-align.ts +16 -6
  18. package/client/modeller/v3-canvas.ts +12 -12
  19. package/client/modeller/v3-clips.ts +1 -1
  20. package/client/modeller/v3-controls.ts +1 -1
  21. package/client/modeller/v3-editor.ts +21 -21
  22. package/client/modeller/v3-fields.ts +1 -1
  23. package/client/modeller/v3-figure-facts.ts +1 -1
  24. package/client/modeller/v3-first-save.ts +3 -4
  25. package/client/modeller/v3-gizmo.ts +5 -5
  26. package/client/modeller/v3-help.ts +1 -1
  27. package/client/modeller/v3-joint-fan.ts +4 -5
  28. package/client/modeller/v3-keys.ts +1 -1
  29. package/client/modeller/v3-links.ts +9 -12
  30. package/client/modeller/v3-maturity.ts +22 -26
  31. package/client/modeller/v3-refusals.ts +17 -0
  32. package/client/modeller/v3-renderer.ts +1 -1
  33. package/client/modeller/v3-stage-model.ts +2 -2
  34. package/client/modeller/v3-thumbnail.ts +1 -3
  35. package/client/pages/figure-v3-modeller-page.ts +8 -11
  36. package/client/types.ts +2 -31
  37. package/dist-client/graphql/index.js +0 -45
  38. package/dist-client/graphql/index.js.map +1 -1
  39. package/dist-client/modeller/figure-history-panel.d.ts +4 -37
  40. package/dist-client/modeller/figure-history-panel.js +8 -154
  41. package/dist-client/modeller/figure-history-panel.js.map +1 -1
  42. package/dist-client/modeller/figure-v3-capability.d.ts +1 -1
  43. package/dist-client/modeller/figure-v3-capability.js +1 -1
  44. package/dist-client/modeller/figure-v3-capability.js.map +1 -1
  45. package/dist-client/modeller/figure-v3-clips.d.ts +2 -2
  46. package/dist-client/modeller/figure-v3-clips.js +2 -2
  47. package/dist-client/modeller/figure-v3-clips.js.map +1 -1
  48. package/dist-client/modeller/figure-v3-controls.d.ts +1 -1
  49. package/dist-client/modeller/figure-v3-controls.js +2 -2
  50. package/dist-client/modeller/figure-v3-controls.js.map +1 -1
  51. package/dist-client/modeller/figure-v3-first-save.d.ts +1 -1
  52. package/dist-client/modeller/figure-v3-first-save.js +1 -1
  53. package/dist-client/modeller/figure-v3-first-save.js.map +1 -1
  54. package/dist-client/modeller/figure-v3-links.d.ts +5 -5
  55. package/dist-client/modeller/figure-v3-links.js +6 -6
  56. package/dist-client/modeller/figure-v3-links.js.map +1 -1
  57. package/dist-client/modeller/figure-v3-material.d.ts +6 -6
  58. package/dist-client/modeller/figure-v3-material.js +11 -11
  59. package/dist-client/modeller/figure-v3-material.js.map +1 -1
  60. package/dist-client/modeller/figure-v3-parts.d.ts +6 -8
  61. package/dist-client/modeller/figure-v3-parts.js +1 -1
  62. package/dist-client/modeller/figure-v3-parts.js.map +1 -1
  63. package/dist-client/modeller/figure-v3-preview.js.map +1 -1
  64. package/dist-client/modeller/figure-v3-properties.d.ts +4 -5
  65. package/dist-client/modeller/figure-v3-properties.js +5 -6
  66. package/dist-client/modeller/figure-v3-properties.js.map +1 -1
  67. package/dist-client/modeller/figure-v3-verdict.d.ts +2 -2
  68. package/dist-client/modeller/figure-v3-verdict.js +9 -10
  69. package/dist-client/modeller/figure-v3-verdict.js.map +1 -1
  70. package/dist-client/modeller/figure-view.d.ts +2 -3
  71. package/dist-client/modeller/figure-view.js.map +1 -1
  72. package/dist-client/modeller/modeller-route.d.ts +2 -3
  73. package/dist-client/modeller/modeller-route.js +2 -3
  74. package/dist-client/modeller/modeller-route.js.map +1 -1
  75. package/dist-client/modeller/scene-budget.d.ts +0 -11
  76. package/dist-client/modeller/scene-budget.js +0 -12
  77. package/dist-client/modeller/scene-budget.js.map +1 -1
  78. package/dist-client/modeller/side-panel-styles.d.ts +2 -2
  79. package/dist-client/modeller/side-panel-styles.js +5 -5
  80. package/dist-client/modeller/side-panel-styles.js.map +1 -1
  81. package/dist-client/modeller/v3-align.d.ts +9 -5
  82. package/dist-client/modeller/v3-align.js +13 -3
  83. package/dist-client/modeller/v3-align.js.map +1 -1
  84. package/dist-client/modeller/v3-canvas.d.ts +4 -4
  85. package/dist-client/modeller/v3-canvas.js +9 -9
  86. package/dist-client/modeller/v3-canvas.js.map +1 -1
  87. package/dist-client/modeller/v3-clips.js.map +1 -1
  88. package/dist-client/modeller/v3-controls.js.map +1 -1
  89. package/dist-client/modeller/v3-editor.d.ts +13 -13
  90. package/dist-client/modeller/v3-editor.js +19 -18
  91. package/dist-client/modeller/v3-editor.js.map +1 -1
  92. package/dist-client/modeller/v3-fields.js.map +1 -1
  93. package/dist-client/modeller/v3-figure-facts.d.ts +1 -1
  94. package/dist-client/modeller/v3-figure-facts.js +1 -1
  95. package/dist-client/modeller/v3-figure-facts.js.map +1 -1
  96. package/dist-client/modeller/v3-first-save.js +3 -4
  97. package/dist-client/modeller/v3-first-save.js.map +1 -1
  98. package/dist-client/modeller/v3-gizmo.d.ts +5 -5
  99. package/dist-client/modeller/v3-gizmo.js +1 -1
  100. package/dist-client/modeller/v3-gizmo.js.map +1 -1
  101. package/dist-client/modeller/v3-help.d.ts +1 -1
  102. package/dist-client/modeller/v3-help.js +1 -1
  103. package/dist-client/modeller/v3-help.js.map +1 -1
  104. package/dist-client/modeller/v3-joint-fan.js +4 -5
  105. package/dist-client/modeller/v3-joint-fan.js.map +1 -1
  106. package/dist-client/modeller/v3-keys.js.map +1 -1
  107. package/dist-client/modeller/v3-links.d.ts +5 -7
  108. package/dist-client/modeller/v3-links.js +6 -8
  109. package/dist-client/modeller/v3-links.js.map +1 -1
  110. package/dist-client/modeller/v3-maturity.d.ts +7 -7
  111. package/dist-client/modeller/v3-maturity.js +20 -24
  112. package/dist-client/modeller/v3-maturity.js.map +1 -1
  113. package/dist-client/modeller/v3-refusals.d.ts +6 -0
  114. package/dist-client/modeller/v3-refusals.js +18 -0
  115. package/dist-client/modeller/v3-refusals.js.map +1 -1
  116. package/dist-client/modeller/v3-renderer.js +1 -1
  117. package/dist-client/modeller/v3-renderer.js.map +1 -1
  118. package/dist-client/modeller/v3-stage-model.js.map +1 -1
  119. package/dist-client/modeller/v3-thumbnail.js +1 -3
  120. package/dist-client/modeller/v3-thumbnail.js.map +1 -1
  121. package/dist-client/pages/figure-v3-modeller-page.d.ts +4 -5
  122. package/dist-client/pages/figure-v3-modeller-page.js +8 -11
  123. package/dist-client/pages/figure-v3-modeller-page.js.map +1 -1
  124. package/dist-client/route.d.ts +1 -1
  125. package/dist-client/tsconfig.fui.tsbuildinfo +1 -1
  126. package/dist-client/tsconfig.tsbuildinfo +1 -1
  127. package/dist-client/types.d.ts +2 -29
  128. package/dist-client/types.js +3 -0
  129. package/dist-client/types.js.map +1 -1
  130. package/docs/v3-product-cutover.md +14 -24
  131. package/package.json +5 -5
  132. package/test/browser/modeller-page.js +6 -7
  133. package/test/browser/modeller.check.mjs +9 -16
  134. package/test/fixtures/joint-column.json +1138 -0
  135. package/test/fixtures/joint-roller.json +872 -0
  136. package/test/fixtures/robot-arm-6dof.json +2776 -0
  137. package/test/i18n-prefix-guard.test.ts +0 -31
  138. package/test/modeller-route.test.ts +2 -3
  139. package/test/v3-align.test.ts +9 -4
  140. package/test/v3-clips.test.ts +1 -1
  141. package/test/v3-controls.test.ts +1 -1
  142. package/test/v3-fields.test.ts +4 -4
  143. package/test/v3-first-save.test.ts +1 -1
  144. package/test/v3-gizmo.test.ts +5 -5
  145. package/test/v3-joint-fan.test.ts +12 -42
  146. package/test/v3-keys.test.ts +1 -1
  147. package/test/v3-layout.test.ts +4 -5
  148. package/test/v3-links.test.ts +3 -3
  149. package/test/v3-material.test.ts +3 -3
  150. package/test/v3-maturity.test.ts +8 -9
  151. package/test/v3-parts-wiring.test.ts +3 -3
  152. package/test/v3-preview.test.ts +1 -1
  153. package/test/v3-product-write.test.ts +1 -1
  154. package/test/v3-properties.test.ts +3 -3
  155. package/test/v3-refusals.test.ts +12 -0
  156. package/test/v3-release-panel.test.ts +5 -5
  157. package/test/v3-scene-canvas.test.ts +2 -2
  158. package/test/v3-theme.test.ts +2 -2
  159. package/test/v3-verdict.test.ts +2 -2
  160. package/test/v3-view.test.ts +2 -2
  161. package/translations/en.json +2 -143
  162. package/translations/ja.json +2 -143
  163. package/translations/ko.json +2 -143
  164. package/translations/ms.json +2 -143
  165. package/translations/zh.json +2 -143
  166. package/client/modeller/figure-source.ts +0 -437
  167. package/client/modeller/part-edits.ts +0 -312
  168. package/dist-client/modeller/figure-source.d.ts +0 -238
  169. package/dist-client/modeller/figure-source.js +0 -193
  170. package/dist-client/modeller/figure-source.js.map +0 -1
  171. package/dist-client/modeller/part-edits.d.ts +0 -89
  172. package/dist-client/modeller/part-edits.js +0 -270
  173. package/dist-client/modeller/part-edits.js.map +0 -1
  174. package/test/expect.ts +0 -57
  175. package/test/figure-source.test.ts +0 -511
  176. package/test/part-edits.test.ts +0 -350
@@ -1,7 +1,6 @@
1
1
  /**
2
- * The one modeller. Every figure is V3 since ADR-0087 step 5 (2026-09-24, user decision: V2 is not kept), so the
3
- * catalogue, a new figure and an old address all open the same page. `figure-v3-modeller` stays as an alias so a
4
- * link written before the switch still lands.
2
+ * The one modeller. The catalogue, a new figure and an old address all open the same page. `figure-v3-modeller`
3
+ * stays as an alias so a link written under that name still lands.
5
4
  */
6
5
  export const MODELLER = 'figure-modeller'
7
6
 
@@ -1,4 +1,3 @@
1
- import type { FigureCost } from '@hatiolab/figure-model'
2
1
 
3
2
  /**
4
3
  * 한 화면이 감당하는 양 — 「몇 개까지 편한가」의 분모.
@@ -56,19 +55,6 @@ export interface FigureCapacity {
56
55
  budget: number
57
56
  }
58
57
 
59
- /**
60
- * 몇 개까지 놓을 수 있나.
61
- *
62
- * 두 예산 중 **먼저 차는 쪽**이 답이다. 평균이나 곱을 내지 않는다 — 하나만 꽉 차도
63
- * 그것이 병목이고, 저작자가 고쳐야 할 곳은 그 하나다.
64
- *
65
- * 셈은 여기서 하지만 재료는 전부 figure-model 이 낸 것이다. `cost.groups` 가 인스턴스
66
- * 하나당 draw call 이고 `cost.triangles` 가 인스턴스 하나의 삼각형 수다.
67
- */
68
- export function capacityOf(cost: FigureCost): FigureCapacity | undefined {
69
- return capacityFrom(cost.groups, cost.triangles)
70
- }
71
-
72
58
  /**
73
59
  * 저장된 값만으로 셈한다.
74
60
  *
@@ -1,14 +1,14 @@
1
1
  import { css } from 'lit'
2
2
 
3
3
  /**
4
- * The side panel's one look -- V2's inspector, for every element that draws in the V3 modeller's side.
4
+ * The side panel's one look, for every element that draws in the modeller's side.
5
5
  *
6
6
  * The side is drawn by several elements (the type field, the figure's properties, a part's values, fastening, places,
7
7
  * motion, material, controls, clips, the environment) and each had styled its own fields: three label styles and
8
8
  * three field heights in one column (user, 2026-09-24: "why are the styles and layout all different?"). They share
9
9
  * this sheet now and keep only what is theirs.
10
10
  *
11
- * The vocabulary, V2's:
11
+ * The vocabulary:
12
12
  * section one titled group; the side is a stack of them
13
13
  * h3 a group's title -- small capitals
14
14
  * div[inline-field] a short label and its one field on one line
@@ -28,7 +28,7 @@ export const SidePanelStyles = css`
28
28
  font-size: 0.76rem;
29
29
  color: var(--md-sys-color-on-surface);
30
30
  }
31
- /* The side is a stack of sections, each a titled group (V2's inspector). */
31
+ /* The side is a stack of sections, each a titled group. */
32
32
  section {
33
33
  display: grid;
34
34
  gap: 6px;
@@ -101,7 +101,7 @@ export const SidePanelStyles = css`
101
101
  div[vector-row] + div[inline-field] {
102
102
  margin-top: 5px;
103
103
  }
104
- /* A row of icon tools (align, mirror, snap), as V2's placement section had them. */
104
+ /* A row of icon tools (align, mirror, snap). */
105
105
  div[tools] {
106
106
  display: flex;
107
107
  flex-wrap: wrap;
@@ -270,7 +270,7 @@ export const SidePanelStyles = css`
270
270
  font-size: 0.76rem;
271
271
  color: var(--md-sys-color-on-surface);
272
272
  }
273
- /* The 「?」 beside a title: no border, it says there is more rather than asking to be pressed (V2). */
273
+ /* The 「?」 beside a title: no border, it says there is more rather than asking to be pressed. */
274
274
  button[help] {
275
275
  display: inline-flex;
276
276
  align-items: center;
@@ -1,15 +1,13 @@
1
1
  import { compileV3Asset, v3OccupiedBoxesOf, v3WritersOf } from '@hatiolab/figure-model'
2
2
  import type { V3Asset, V3InputChange, V3WorldBox } from '@hatiolab/figure-model'
3
3
 
4
- import { niceStep } from './part-edits.js'
5
4
  import type { GizmoReach } from './v3-gizmo.js'
6
5
 
7
6
  /**
8
- * 맞추기 — the V2 inspector's align tools on a V3 part (parity row ③): centre on X, centre on Z, stand on the floor,
7
+ * 맞추기 — the align tools on a part: centre on X, centre on Z, stand on the floor,
9
8
  * mirror on X or Z, snap to the grid.
10
9
  *
11
- * V2 measured against its base box, which ran from 0 to its size on each axis. A V3 asset's frame has the floor at
12
- * y 0 and X and Z centred on 0, so centring puts the part's box centre on 0 and the floor puts its lowest point on
10
+ * The asset's frame has the floor at y 0 and X and Z centred on 0, so centring puts the part's box centre on 0 and the floor puts its lowest point on
13
11
  * y 0. The part's box is its evaluated, rotated extent (`v3OccupiedBoxesOf`), so a turned part lands by what it
14
12
  * actually covers, not by its unturned size.
15
13
  *
@@ -25,7 +23,7 @@ export function boxOf(asset: V3Asset, part: string): V3WorldBox | undefined {
25
23
  return v3OccupiedBoxesOf(geometry, v3WritersOf(asset)).find(box => box.part === part)
26
24
  }
27
25
 
28
- /** The grid V2 snaps to: about five steps across the figure, on 1 · 2 · 5 multiples. */
26
+ /** The snapping grid: about five steps across the figure, on 1 · 2 · 5 multiples. */
29
27
  export function gridStepOf(asset: V3Asset): number {
30
28
  const geometry = compileV3Asset(asset).evaluate().geometry
31
29
  const boxes = v3OccupiedBoxesOf(geometry, v3WritersOf(asset))
@@ -77,8 +75,20 @@ export function mirrorChanges(asset: V3Asset, part: string, copy: string, axis:
77
75
  return [{ kind: 'set-design', input: `${copy}.position.${axis}`, unit: 'mm', value: Math.round((asset.designInputs[id] ?? 0) - 2 * box.centre[axis]) }]
78
76
  }
79
77
 
80
- /** Mirroring a part that already sits on the centre plane would put the copy inside it — V2 refuses it too. */
78
+ /** Mirroring a part that already sits on the centre plane would put the copy inside it, so it is refused. */
81
79
  export function canMirror(asset: V3Asset, part: string, axis: 'x' | 'z', reach: GizmoReach): boolean {
82
80
  const box = reach.translate ? boxOf(asset, part) : undefined
83
81
  return !!box && Math.abs(box.centre[axis]) >= 0.5
84
82
  }
83
+
84
+ /**
85
+ * A **readable** grid step for a length: about five cells, rounded up to a 1, 2 or 5 multiple as rulers and charts do.
86
+ * Divided plainly it gives 27 or 33, and coordinates snapped to that read as 54 and 81. Never 0.
87
+ */
88
+ export function niceStep(span: number): number {
89
+ const raw = Math.max(span, 1) / 5
90
+ const magnitude = Math.pow(10, Math.floor(Math.log10(raw)))
91
+ const n = raw / magnitude
92
+ const multiple = n < 1.5 ? 1 : n < 3.5 ? 2 : n < 7.5 ? 5 : 10
93
+ return Math.max(1, multiple * magnitude)
94
+ }
@@ -11,10 +11,10 @@ import { JOINT_FAN } from './v3-joint-fan.js'
11
11
  import { ownerOf } from './v3-fields.js'
12
12
 
13
13
  /*
14
- * The V3 modeller's canvas -- a things-scene board in editing mode, as the V2 modeller's was.
14
+ * The modeller's canvas -- a things-scene board in editing mode.
15
15
  *
16
- * Each body figure-model evaluates stands on the board as a `figure-v3-part` component, so everything the V2 canvas
17
- * had comes from the scene, not from code of our own: picking and the orange outline, the gizmo with its snapping and
16
+ * Each body figure-model evaluates stands on the board as a `figure-v3-part` component, so everything the canvas
17
+ * has comes from the scene, not from code of our own: picking and the orange outline, the gizmo with its snapping and
18
18
  * live readout, box and shift selection, the camera (damping, zoom toward the cursor, double-click to fit, the six
19
19
  * view presets and their keys), the studio environment, the coordinate ground, lights and shadows, supersampling.
20
20
  * An earlier canvas drew the bodies with three.js directly and lost all of that (user, 2026-09-24: "why nerf it?").
@@ -90,7 +90,7 @@ export class FigureV3Canvas extends LitElement {
90
90
  /**
91
91
  * View only: nothing that happens on the board is told out. The card's picture is taken from a second canvas stood up
92
92
  * off screen (`v3-thumbnail.ts`); the scene settles its components as it builds them, and a change told from there
93
- * would reach the editor as an edit (V2's `viewOnly`, for the same reason).
93
+ * would reach the editor as an edit.
94
94
  */
95
95
  @property({ type: Boolean }) viewOnly = false
96
96
  /** Where the selected part turns and how far (`v3-joint-fan`): shown, never picked, never saved. */
@@ -131,9 +131,9 @@ export class FigureV3Canvas extends LitElement {
131
131
 
132
132
  /**
133
133
  * The board as a picture, for the card: the 3D view at this size, or nothing when it cannot be taken. The scene's
134
- * `toDataURL` renders the 2D board (a white sheet, V2 found), and the 3D renderer keeps no drawing buffer, so the
134
+ * `toDataURL` renders the 2D board (a white sheet), and the 3D renderer keeps no drawing buffer, so the
135
135
  * scene's own `snapshot3d` draws and reads in one task. A picture that cannot be taken is not an error and is not
136
- * faked (V2's `snapshot`, lost when this canvas moved onto the scene -- no card had a picture since, user 2026-09-24).
136
+ * faked (user, 2026-09-24: no card had a picture after this canvas moved onto the scene).
137
137
  */
138
138
  async snapshot(width = 480, height = 360): Promise<string | undefined> {
139
139
  const layer = this.scene?.root as (SceneLayer & { snapshot3d?: (w?: number, h?: number, type?: string, quality?: number) => Promise<string | undefined> }) | undefined
@@ -148,7 +148,7 @@ export class FigureV3Canvas extends LitElement {
148
148
  }
149
149
  }
150
150
 
151
- /** The whole figure in view: the board's fit, then the 3D camera's own, as V2 did. */
151
+ /** The whole figure in view: the board's fit, then the 3D camera's own. */
152
152
  fit() {
153
153
  this.scene?.fit?.('ratio')
154
154
  this.scene?.root?.fitCameraToModel?.()
@@ -188,7 +188,7 @@ export class FigureV3Canvas extends LitElement {
188
188
  height: needed.height,
189
189
  depth: needed.depth,
190
190
  threed: true,
191
- /* Two samples a pixel: MSAA alone leaves steps on the silhouettes of one asset shown large (V2). */
191
+ /* Two samples a pixel: MSAA alone leaves steps on the silhouettes of one asset shown large. */
192
192
  pixelRatio: Math.min(3, (window.devicePixelRatio || 1) * 2),
193
193
  ...sceneLook(this.view ?? DEFAULT_VIEW),
194
194
  gridStep: niceGridStep(Math.max(needed.width, needed.height)),
@@ -196,7 +196,7 @@ export class FigureV3Canvas extends LitElement {
196
196
  fillStyle: PAPER,
197
197
  components: this.components()
198
198
  },
199
- /* Editing mode: the camera view buttons, gizmo modes, help and keys V2's canvas had. */
199
+ /* Editing mode: the camera view buttons, gizmo modes, help and keys. */
200
200
  mode: 1
201
201
  }) as SceneHandle
202
202
  this.standingFor = this.context
@@ -280,7 +280,7 @@ export class FigureV3Canvas extends LitElement {
280
280
  })
281
281
  /*
282
282
  A change comes up to the board with the component in the last argument's `origin` (the scene appends
283
- { origin, name, deliverer } as it bubbles). Reading the first argument as the component does nothing (V2).
283
+ { origin, name, deliverer } as it bubbles). Reading the first argument as the component does nothing.
284
284
  */
285
285
  /*
286
286
  A drag is written once, when it ends. The scene's gizmo writes the component on every move -- a size anchored on
@@ -323,8 +323,8 @@ export class FigureV3Canvas extends LitElement {
323
323
  private commit() {
324
324
  /*
325
325
  Not while a pointer is down: a pause set by a change just before the press (the arrow keys, the scene settling
326
- the pick) fired in the middle of the drag and wrote where the part was then -- a drag on a converted part became
327
- two steps, the first one a few millimetres in (browser check, 2026-09-25). What is pending waits for the release.
326
+ the pick) fired in the middle of the drag and wrote where the part was then -- a drag on a part placed by a rule
327
+ became two steps, the first one a few millimetres in (browser check, 2026-09-25). What is pending waits for the release.
328
328
  */
329
329
  if (this.pointerDown || !this.board || !this.pending.size) return
330
330
  const changed = [...this.pending]
@@ -2,7 +2,7 @@ import { driverValue } from '@hatiolab/figure-model'
2
2
  import type { V3Asset, V3Driver, V3InputChange } from '@hatiolab/figure-model'
3
3
 
4
4
  /*
5
- * The figure's clips -- V2's animations: controls played by keys over time, grouped into clips that each run on one
5
+ * The figure's clips: controls played by keys over time, grouped into clips that each run on one
6
6
  * clock. Kept apart from the element so the reading is tested without a browser.
7
7
  */
8
8
  export interface ClipKey {
@@ -1,7 +1,7 @@
1
1
  import type { V3Asset } from '@hatiolab/figure-model'
2
2
 
3
3
  /*
4
- * The figure's controls -- its state inputs, what V2 called parameters: the values a board or a connector sets to pose
4
+ * The figure's controls -- its state inputs: the values a board or a connector sets to pose
5
5
  * the figure (a lift height, a joint angle, a gripper opening). Kept apart from the element so the reading is tested
6
6
  * without a browser.
7
7
  */
@@ -29,7 +29,7 @@ import type { V3PaletteKind, V3PartRow } from './figure-v3-parts.js'
29
29
  import { paletteRecipe } from './v3-palette.js'
30
30
  import type { FigureV3Canvas } from './v3-canvas.js'
31
31
  import { nudgeOf } from './v3-stage-model.js'
32
- import { inDeclaredRoom, narrowingRetryOf, partRefusalOf, placementRefusalOf, sizeRangeRefusalOf } from './v3-refusals.js'
32
+ import { inDeclaredRoom, inputRangeRefusalOf, narrowingRetryOf, partRefusalOf, placementRefusalOf, sizeRangeRefusalOf } from './v3-refusals.js'
33
33
  import type { V3RenderPart } from './v3-renderer.js'
34
34
  import type { V3BudgetLine } from './v3-maturity.js'
35
35
  import { instanceRangeOf, instanceRangeToDeclare, proportionToFollow, rangeDiffers, rangeWith, turnsAboutAJoint, valueNow } from './v3-links.js'
@@ -40,8 +40,7 @@ import { isLive, livePartsOf, liveOverSession, NOTHING_LIVE, scrubbed, scrubStep
40
40
 
41
41
 
42
42
  /**
43
- * The side's tabs, V2's six in V2's order where V3 has them: properties, what it does (places), motion, the verdict,
44
- * the release history. A figure-level tab (verdict, history) gives way to properties when a part is chosen; a tab about
43
+ * The side's tabs: properties, what it does (places), motion, the verdict, the view, the release history. A figure-level tab (verdict, history) gives way to properties when a part is chosen; a tab about
45
44
  * parts keeps showing the newly chosen part.
46
45
  */
47
46
  type SideTab = 'properties' | 'capability' | 'motion' | 'verdict' | 'view' | 'history'
@@ -106,7 +105,7 @@ export class FigureV3Editor extends LitElement {
106
105
  min-width: 0;
107
106
  min-height: 0;
108
107
  }
109
- /* The side: tabs over one panel at a time, each using the whole height (V2's side, and why it had tabs). */
108
+ /* The side: tabs over one panel at a time, each using the whole height. */
110
109
  aside {
111
110
  display: flex;
112
111
  flex-direction: column;
@@ -228,7 +227,7 @@ export class FigureV3Editor extends LitElement {
228
227
  `]
229
228
  @property({ attribute: false }) session?: V3ProductSession
230
229
  @property({ type: Number }) revision = 0
231
- /** The verdict's count, drawn on its tab so it stays in sight while another tab is open (V2's badge). */
230
+ /** The verdict's count, drawn on its tab so it stays in sight while another tab is open. */
232
231
  @property({ type: Number }) instances?: number
233
232
  /** The cost line the parts list shows, from the maturity report. */
234
233
  @property({ attribute: false }) budget?: V3BudgetLine
@@ -244,10 +243,10 @@ export class FigureV3Editor extends LitElement {
244
243
  @state() private tab: SideTab = 'properties'
245
244
  /** The frame the bar folds - found after the first render, since the bar is drawn before it. */
246
245
  @state() private panes?: OxThreePane
247
- /** What the middle shows: the parts being edited, or the figure placed on a board at eight sizes (V2's two modes). */
246
+ /** What the middle shows: the parts being edited, or the figure placed on a board at eight sizes. */
248
247
  @state() private middle: 'edit' | 'preview' = 'edit'
249
248
  /**
250
- * How the workbench shows the figure -- V2's view settings, the board's own state names. The viewer's, kept in
249
+ * How the workbench shows the figure, in the board's own state names. The viewer's, kept in
251
250
  * their browser, never in the figure.
252
251
  */
253
252
  @state() private view: ViewSettings = FigureV3Editor.storedView()
@@ -268,8 +267,8 @@ export class FigureV3Editor extends LitElement {
268
267
  }
269
268
  }
270
269
  /**
271
- * Parts hidden on the workbench — the author's view while working, not the asset's visibility. Not saved, as in
272
- * the V2 modeller: a part hidden here is still in the figure, still checked, still drawn on a board.
270
+ * Parts hidden on the workbench — the author's view while working, not the asset's visibility. Not saved: a part
271
+ * hidden here is still in the figure, still checked, still drawn on a board.
273
272
  */
274
273
  @state() private hiddenParts = new Set<string>()
275
274
  /** A value in hand -- typed, held on a spinner, scrubbed, dragged -- drawn at once and recorded on release. */
@@ -290,7 +289,7 @@ export class FigureV3Editor extends LitElement {
290
289
  /*
291
290
  The session's history is the only one. With the canvas focused the scene took Ctrl+Z as well, undoing on its own
292
291
  board the last value the canvas had written -- which the canvas then told as a drag: an undo of a drag on a
293
- converted part wrote the drag again (2026-09-25). Heard first (capture) and kept from the scene.
292
+ part placed by a rule wrote the drag again (2026-09-25). Heard first (capture) and kept from the scene.
294
293
  */
295
294
  e.stopPropagation()
296
295
  if (action === 'translate' || action === 'rotate' || action === 'scale') canvas?.gizmo(action)
@@ -324,7 +323,8 @@ export class FigureV3Editor extends LitElement {
324
323
  this.error = ''
325
324
  this.changed()
326
325
  } catch (error) {
327
- this.error = sizeRangeRefusalOf(error, (key, params) => i18next.t(key, params)) ?? (error instanceof Error ? error.message : String(error))
326
+ const say = (key: string, params: Record<string, string>) => i18next.t(key, params)
327
+ this.error = sizeRangeRefusalOf(error, say) ?? inputRangeRefusalOf(error, say) ?? (error instanceof Error ? error.message : String(error))
328
328
  } finally {
329
329
  /* Committed or refused, the value in hand is gone: the figure as it stands is drawn again. */
330
330
  this.live = NOTHING_LIVE
@@ -408,7 +408,7 @@ export class FigureV3Editor extends LitElement {
408
408
  }
409
409
  /**
410
410
  * The list's own operations, as V3 part edits — each one step of the session's history. The copy and the moved
411
- * part stay selected, as they do in the V2 list.
411
+ * part stay selected.
412
412
  */
413
413
  private duplicatePart(id: string) {
414
414
  this.run(() => {
@@ -486,14 +486,14 @@ export class FigureV3Editor extends LitElement {
486
486
  return this.requestUpdate()
487
487
  }
488
488
  /*
489
- A part whose place is worked out (a converted one) has no numbers for the gizmo to write: the drag is added beside
489
+ A part whose place is worked out has no numbers for the gizmo to write: the drag is added beside
490
490
  its rule (`nudge-part`). A part a joint or a fastening places, or a size that is worked out, is refused -- said in
491
491
  words, not by the part silently springing back (user, 2026-09-24: 「기즈모 컨트롤도 전혀 작동이 안되는군」).
492
492
  */
493
493
  const body = view.geometry.find(g => g.id === id) as unknown as V3RenderPart | undefined
494
494
  if (!body) return this.requestUpdate()
495
495
  const { by, turn } = nudgeOf(body, result)
496
- /* A size worked out from the figure's: the drag's factor beside its rule, so it still follows (V2 scaled any part). */
496
+ /* A size worked out from the figure's: the drag's factor beside its rule, so it still follows. */
497
497
  const grow: Partial<Record<'x' | 'y' | 'z', number>> = {}
498
498
  if (result.scale) (['x', 'y', 'z'] as const).forEach((axis, i) => Math.abs(result.scale![i]! - 1) > 1e-6 && (grow[axis] = Math.round(result.scale![i]! * 1e6) / 1e6))
499
499
  if (!Object.keys(by).length && !Object.keys(turn).length && !Object.keys(grow).length) return this.requestUpdate()
@@ -525,7 +525,7 @@ export class FigureV3Editor extends LitElement {
525
525
  if (changes.length) this.session!.apply(changes)
526
526
  })
527
527
  }
528
- /** A mirrored copy: duplicate, then place the copy on the other side — two steps, as the V2 list makes it. */
528
+ /** A mirrored copy: duplicate, then place the copy on the other side — two steps. */
529
529
  private mirror(axis: 'x' | 'z') {
530
530
  this.run(() => {
531
531
  const id = this.selected
@@ -569,7 +569,7 @@ export class FigureV3Editor extends LitElement {
569
569
  </div>`
570
570
  }
571
571
  /**
572
- * A part's name — the V2 part name field. It renames every value the part owns, and what refers to them follows
572
+ * A part's name. It renames every value the part owns, and what refers to them follows
573
573
  * (figure-model's rename-part), as one step of the history.
574
574
  */
575
575
  /**
@@ -603,7 +603,7 @@ export class FigureV3Editor extends LitElement {
603
603
  : String(error)
604
604
  }
605
605
  }
606
- /** Size response, fastening and motion for the selected part (parity ④ ⑤ ⑥), as authoring commands. */
606
+ /** Size response, fastening and motion for the selected part, as authoring commands. */
607
607
  private renderLinks(unavailable: boolean, section: 'shape' | 'places' | 'motion') {
608
608
  let asset
609
609
  try {
@@ -627,7 +627,7 @@ export class FigureV3Editor extends LitElement {
627
627
  @v3-part-edit=${(e: CustomEvent) => this.run(() => this.session!.edit(e.detail.edit))}
628
628
  ></figure-v3-links>`
629
629
  }
630
- /** The figure's controls: try a value, name it, set its range and travel time, keep the pose (parity row ⑥). */
630
+ /** The figure's controls: try a value, name it, set its range and travel time, keep the pose. */
631
631
  private renderControls(view: { fields: Array<{ id: string; role?: string; value: number }> }, unavailable: boolean) {
632
632
  let asset
633
633
  try {
@@ -827,8 +827,8 @@ export class FigureV3Editor extends LitElement {
827
827
  </button>`
828
828
  }
829
829
  /**
830
- * The parts list, one row per part the author made: a repeat is one row with how many copies it makes now (V2 listed
831
- * its parts the same way; user, 2026-09-24: the conveyor's sixteen rollers were sixteen rows).
830
+ * The parts list, one row per part the author made: a repeat is one row with how many copies it makes now
831
+ * (user, 2026-09-24: the conveyor's sixteen rollers were sixteen rows).
832
832
  */
833
833
  private partRows(geometry: readonly { id: string }[]): V3PartRow[] {
834
834
  const rows = new Map<string, V3PartRow>()
@@ -859,7 +859,7 @@ export class FigureV3Editor extends LitElement {
859
859
  }
860
860
  }
861
861
  /**
862
- * A repeat: which way its copies run, how many there are now, and how far apart -- V2's `repeat: { axis, pitch }`.
862
+ * A repeat: which way its copies run, how many there are now, and how far apart.
863
863
  * The pitch of a row that fills its length is edited as a number (`set-repeat` makes it the part's own the first
864
864
  * time); a row of a fixed count says its pitch cannot be set yet (figure-model refuses it the same way).
865
865
  */
@@ -1,5 +1,5 @@
1
1
  /*
2
- * The values a part or the figure is edited by, grouped as V2's inspector showed them: position, rotation and size
2
+ * The values a part or the figure is edited by, grouped: position, rotation and size
3
3
  * as one row of three cells each (X · Y · Z read together), and the shape's own measures one to a line under their
4
4
  * own title. Kept apart from the editor so the grouping is tested without a browser.
5
5
  */
@@ -38,7 +38,7 @@ export function v3FigureFacts(source: string): V3FigureFacts {
38
38
  }
39
39
 
40
40
  /**
41
- * A tag line as a list — the V2 inspector's rule: split on commas, drop a leading `#`, keep each once. An empty
41
+ * A tag line as a list: split on commas, drop a leading `#`, keep each once. An empty
42
42
  * line is an empty list, so an author can clear every tag.
43
43
  */
44
44
  export function tagsOf(raw: string): string[] {
@@ -1,10 +1,9 @@
1
1
  import { isPlaceholderType, TYPE_LENGTH, TYPE_PATTERN } from '@hatiolab/figure-model'
2
2
 
3
3
  /*
4
- * The first save of a new figure asks for its type and name, as V2's page did. The type is the name a board places
5
- * the figure by; it freezes when the figure is released (ADR-0063 decision 4). The V3 modeller saved a new figure
6
- * under a temporary machine name instead and asked nothing -- the author had to find the field afterwards
7
- * (user, 2026-09-25: V2 features are not to go missing).
4
+ * The first save of a new figure asks for its type and name. The type is the name a board places
5
+ * the figure by; it freezes when the figure is released (ADR-0063 decision 4). The modeller once saved a new figure
6
+ * under a temporary machine name and asked nothing -- the author had to find the field afterwards (user, 2026-09-25).
8
7
  */
9
8
 
10
9
  /** A type as typed, made into the shape a type has: letters, digits and underscores, upper case. */
@@ -2,13 +2,13 @@ import { v3AttachmentsOf } from '@hatiolab/figure-model'
2
2
  import type { V3Asset, V3InputChange } from '@hatiolab/figure-model'
3
3
 
4
4
  /**
5
- * What the gizmo may write for a part — the V2 gizmo's move · turn · size, on a V3 asset (parity row ③).
5
+ * What the gizmo may write for a part: move · turn · size, on a V3 asset.
6
6
  *
7
7
  * A drag can only be written back where the part is placed by its own numbers. A part the create commands made is:
8
8
  * its pose is `rigid@1` into the asset frame from `<part>.position.*` and `<part>.rotation.*`, and a box's shape
9
9
  * (`box-shape@1` or `rounded-box@1`) takes its width, height and depth from inputs of its own — `<part>.size.*` from
10
- * `addV3Box`, `<part>.dimension.*` from the editor's shape palette. A part fastened to another, following an instance size or made by the
11
- * converter from expressions has no such numbers; the gizmo does not offer what it could not write back, rather
10
+ * `addV3Box`, `<part>.dimension.*` from the editor's shape palette. A part fastened to another, following an instance size or placed by
11
+ * expressions has no such numbers; the gizmo does not offer what it could not write back, rather
12
12
  * than move something the next evaluation would put back.
13
13
  */
14
14
  export interface GizmoReach {
@@ -23,7 +23,7 @@ export interface GizmoReach {
23
23
  */
24
24
  ownSizes?: (string | null)[]
25
25
  /**
26
- * A round part keeps its section round, as V2 kept it (`keepRound`): the radii across (X and Z) take the one factor
26
+ * A round part keeps its section round: the radii across (X and Z) take the one factor
27
27
  * the drag gave most; the length along Y takes its own. A sphere's three radii take one factor together.
28
28
  */
29
29
  round?: { across: string[]; along: string[] }
@@ -109,7 +109,7 @@ export interface GizmoResult {
109
109
 
110
110
  /**
111
111
  * The input changes a drag makes — one list, applied as one step. Millimetres to the millimetre and degrees to a
112
- * tenth, as the V2 gizmo rounds (a drag otherwise stores 722.4180046035418 mm). A size never goes below 1 mm.
112
+ * tenth (a drag otherwise stores 722.4180046035418 mm). A size never goes below 1 mm.
113
113
  */
114
114
  export function gizmoChanges(part: string, reach: GizmoReach, result: GizmoResult, sizes: Record<string, number>): V3InputChange[] {
115
115
  const changes: V3InputChange[] = []
@@ -4,7 +4,7 @@ import { html, nothing, type ReactiveController, type ReactiveControllerHost } f
4
4
  import { i18next } from '@operato/i18n'
5
5
 
6
6
  /**
7
- * V2's help toggles: a small 「?」 beside a title opens the longer explanation under it. Explanations left open on every
7
+ * Help toggles: a small 「?」 beside a title opens the longer explanation under it. Explanations left open on every
8
8
  * section made the side read as clutter (user, 2026-09-24); the author opens the one they want. What was opened stays
9
9
  * open while the author moves between parts.
10
10
  */
@@ -4,8 +4,7 @@ import type { V3Asset } from '@hatiolab/figure-model'
4
4
  import type { V3RenderPart } from './v3-renderer.js'
5
5
 
6
6
  /*
7
- * Where the selected part turns, and how far -- a translucent fan on the editing board (V2's joint gizmo, inventory
8
- * 3.38). An axis name says nothing about which way a part swings; on a six-axis arm the author went to the preview and
7
+ * Where the selected part turns, and how far -- a translucent fan on the editing board. An axis name says nothing about which way a part swings; on a six-axis arm the author went to the preview and
9
8
  * pushed each slider to its end to find out. The fan lies in the plane the part turns in, at the pivot, open from the
10
9
  * control's lowest value to its highest; the edge at the value the figure holds now is where the part is.
11
10
  *
@@ -15,7 +14,7 @@ import type { V3RenderPart } from './v3-renderer.js'
15
14
  *
16
15
  * Two ways a part turns are read:
17
16
  * - a joint record (ADR-0093): at the joint's origin, about its axis;
18
- * - a joint the converter wrote from V2 (`<joint>.motion` · `<joint>.chain`): the part is composed on the joint's
17
+ * - a joint written as values (`<joint>.motion` · `<joint>.chain`): the part is composed on the joint's
19
18
  * chain, the pivot is the joint's origin (`<joint>.frameOnParent`).
20
19
  * A slide has no fan. Nothing is drawn when the turn cannot be found -- a fan in some guessed plane would point the
21
20
  * wrong way.
@@ -117,7 +116,7 @@ function turnOf(asset: V3Asset, part: string, values: Record<string, unknown>, b
117
116
  }
118
117
 
119
118
  /*
120
- Written by the converter: a joint (`<joint>.motion`, its origin `<joint>.frameOnParent`) carries its own part and
119
+ A joint written as values (`<joint>.motion`, its origin `<joint>.frameOnParent`) carries its own part and
121
120
  everything fastened below it, through its chain (the first joint of a chain has only its attachment). A part shows
122
121
  the joint it is to that part of the mechanism (user, 2026-09-24: 「elbow-housing itself is a axis」 · 「please check
123
122
  all that logic and concept」), tested in this order on the joints as they stand now:
@@ -222,7 +221,7 @@ export function jointFanOf(asset: V3Asset, part: string, values: Record<string,
222
221
  const inPlane = sub(rel, turn.axis.map(v => v * dot(rel, turn.axis)))
223
222
  const d = norm(inPlane) ?? norm(cross(turn.axis, Math.abs(turn.axis[0]!) < 0.9 ? [1, 0, 0] : [0, 1, 0]))!
224
223
  /*
225
- Sized from what the joint turns, as V2 sized it from the joint's child: selecting the elbow housing or the forearm
224
+ Sized from what the joint turns, not from the selected part: selecting the elbow housing or the forearm
226
225
  shows the elbow's one fan, not a smaller one for the smaller part (user, 2026-09-24).
227
226
  */
228
227
  const radius = Math.max(60, Math.round(Math.max(largestMeasureOf(asset, turn.turnedPart, inputs), Math.hypot(...inPlane)) * 0.6))
@@ -1,5 +1,5 @@
1
1
  /*
2
- * The modeller's keys (V2 had undo and redo on Ctrl+Z / Ctrl+Y; these add what a person reaches for next). Read apart
2
+ * The modeller's keys: undo and redo on Ctrl+Z / Ctrl+Y, and what a person reaches for next. Read apart
3
3
  * from the element so the rule is tested without a browser.
4
4
  *
5
5
  * Typing is never taken: a key pressed in a field belongs to the field -- Backspace in a name box deletes a letter,
@@ -3,8 +3,7 @@ import type { V3SizeRange } from '@hatiolab/figure-model'
3
3
  import type { V3Asset, V3BindingPlace, V3Face, V3Joint } from '@hatiolab/figure-model'
4
4
 
5
5
  /*
6
- * What a V3 part is tied to — the readers behind the parts' size response, fastening and motion sections (parity rows
7
- * ④ ⑤ ⑥). Kept apart from the element so they can be tested without a browser.
6
+ * What a V3 part is tied to — the readers behind the parts' size response, fastening and motion sections. Kept apart from the element so they can be tested without a browser.
8
7
  */
9
8
 
10
9
  export type Axis = 'x' | 'y' | 'z'
@@ -22,11 +21,11 @@ const modelOf = (asset: V3Asset) =>
22
21
  /** What one dimension of a part is worked out from. */
23
22
  export type Follow =
24
23
  | { of: 'own' }
25
- /** `times` 1 is the instance size itself; any other is a share of it -- V2's proportional rule. */
24
+ /** `times` 1 is the instance size itself; any other is a share of it. */
26
25
  | { of: 'instance'; axis: Axis; times: number; plus?: number }
27
26
  | { of: 'part'; part: string; axis: Axis; times?: number; plus?: number }
28
27
  | { of: 'input'; input: string }
29
- /** Worked out some other way (the converter writes expressions) — shown, not offered as a choice. */
28
+ /** Worked out some other way (an expression) — shown, not offered as a choice. */
30
29
  | { of: 'expression' }
31
30
 
32
31
  /**
@@ -38,7 +37,7 @@ export function followOf(asset: V3Asset, part: string, axis: Axis): Follow {
38
37
  const m = modelOf(asset)
39
38
  /*
40
39
  A link routes the dimension through `<part>.dimension.<axis>`; before any link the shape reads its measure directly,
41
- and that may already be worked out -- the converter writes V2's rules there (ROBOT_ARM's flange filled what was left).
40
+ and that may already be worked out (ROBOT_ARM's flange fills what is left).
42
41
  */
43
42
  const node = m.nodes.find(n => n.id === `${part}.dimension.${axis}`)
44
43
  const start = node ? node.args[0] : m.nodes.find(n => n.id === `${part}.shape`)?.args[AXES.indexOf(axis)]
@@ -79,8 +78,8 @@ export function followOf(asset: V3Asset, part: string, axis: Axis): Follow {
79
78
  const onward = writer.args.filter(arg => !constant(arg))
80
79
  if (onward.length !== 1) return { of: 'expression' }
81
80
  /*
82
- Only a product is a share of its source. A sum with a number is not (V2's 「fill what is left」 is the size plus a
83
- negative gap -- ROBOT_ARM's tool flange), nor anything else; those read as worked out some other way.
81
+ Only a product is a share of its source. A sum with a number is not (「fill what is left」 is the size plus a negative
82
+ gap -- ROBOT_ARM's tool flange), nor anything else; those read as worked out some other way.
84
83
  */
85
84
  const factors = writer.args.filter(arg => constant(arg)).map(arg => constant(arg)!.value)
86
85
  if (writer.op === 'mul@1') times *= factors.reduce((a, b) => a * b, 1)
@@ -118,8 +117,7 @@ export function canFollow(asset: V3Asset, part: string): boolean {
118
117
 
119
118
  /**
120
119
  * The axes whose size can follow something, by the shape's own measures: a box's X · Y · Z; a cylinder's radius (read
121
- * as X) and length (Y) -- its shape takes those two, so it has no Z of its own (V2's sizing cards had the cylinder's
122
- * two as well).
120
+ * as X) and length (Y) -- its shape takes those two, so it has no Z of its own.
123
121
  */
124
122
  export function followableAxes(asset: V3Asset, part: string): Axis[] {
125
123
  const shape = modelOf(asset).nodes.find(n => n.id === `${part}.shape`)
@@ -234,8 +232,7 @@ export function alignOf(
234
232
  }
235
233
 
236
234
  /**
237
- * The axes on which the part's position is worked out from other values rather than being its own number — the
238
- * converter writes V2's anchor rules that way. `fix-position` makes one its own number where it is now.
235
+ * The axes on which the part's position is worked out from other values rather than being its own number. `fix-position` makes one its own number where it is now.
239
236
  */
240
237
  export function positionsWorkedOut(asset: V3Asset, part: string): Axis[] {
241
238
  const m = modelOf(asset)
@@ -417,7 +414,7 @@ export const acceptsOf = (typed: string) => [...new Set(typed.split(',').map(one
417
414
 
418
415
  /**
419
416
  * The sizes a board may give this figure: the range it declares now, and the range measured afresh -- the default
420
- * (1 mm to ten times the authored size) narrowed only where the figure cannot follow, by the converter's own functions.
417
+ * (1 mm to ten times the authored size) narrowed only where the figure cannot follow, by figure-model's own functions.
421
418
  * A range measured when a part filled what the others left (ROBOT_ARM: Y from 1,854 mm) stays narrow after the part is
422
419
  * made to grow with the figure; measuring again gives the figure its whole range back. Nothing when no size is declared.
423
420
  */