@things-factory/figure-ui 10.1.75 → 10.1.76

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 (235) hide show
  1. package/client/figure-assistant.ts +3 -3
  2. package/client/graphql/{v3-store.ts → store.ts} +6 -6
  3. package/client/modeller/{v3-ai-proposal.ts → ai-proposal.ts} +8 -8
  4. package/client/modeller/{v3-align.ts → align.ts} +14 -14
  5. package/client/modeller/{v3-canvas.ts → canvas.ts} +13 -13
  6. package/client/modeller/{v3-clips.ts → clips.ts} +5 -5
  7. package/client/modeller/{v3-controls.ts → controls.ts} +2 -2
  8. package/client/modeller/{v3-editor.ts → editor.ts} +117 -117
  9. package/client/modeller/{figure-v3-capability.ts → figure-capability-panel.ts} +7 -7
  10. package/client/modeller/{figure-v3-clips.ts → figure-clips.ts} +10 -10
  11. package/client/modeller/{figure-v3-controls.ts → figure-controls.ts} +9 -9
  12. package/client/modeller/{v3-figure-facts.ts → figure-facts.ts} +11 -11
  13. package/client/modeller/{figure-v3-first-save.ts → figure-first-save.ts} +3 -3
  14. package/client/modeller/figure-history-panel.ts +1 -1
  15. package/client/modeller/{figure-v3-links.ts → figure-links.ts} +35 -35
  16. package/client/modeller/{figure-v3-material.ts → figure-material-panel.ts} +20 -20
  17. package/client/modeller/{figure-v3-parts.ts → figure-parts.ts} +15 -15
  18. package/client/modeller/{figure-v3-preview.ts → figure-preview.ts} +14 -14
  19. package/client/modeller/{figure-v3-properties.ts → figure-properties.ts} +25 -25
  20. package/client/modeller/{figure-v3-recipe-catalog.ts → figure-recipe-catalog.ts} +10 -10
  21. package/client/modeller/{figure-v3-verdict.ts → figure-verdict.ts} +24 -24
  22. package/client/modeller/gate-findings.ts +2 -2
  23. package/client/modeller/{v3-gizmo.ts → gizmo.ts} +11 -11
  24. package/client/modeller/{v3-joint-drag.ts → joint-drag.ts} +11 -11
  25. package/client/modeller/{v3-joint-fan.ts → joint-fan.ts} +13 -13
  26. package/client/modeller/{v3-links.ts → links.ts} +59 -59
  27. package/client/modeller/{v3-live.ts → live.ts} +9 -9
  28. package/client/modeller/{v3-maturity.ts → maturity.ts} +65 -65
  29. package/client/modeller/modeller-route.ts +1 -1
  30. package/client/modeller/{v3-occupancy.ts → occupancy.ts} +6 -6
  31. package/client/modeller/{v3-palette.ts → palette.ts} +3 -3
  32. package/client/modeller/{v3-preview-model.ts → preview-model.ts} +8 -8
  33. package/client/modeller/{v3-product-session.ts → product-session.ts} +25 -25
  34. package/client/modeller/{v3-recipe-source.ts → recipe-source.ts} +2 -2
  35. package/client/modeller/{v3-recipes.ts → recipes.ts} +9 -9
  36. package/client/modeller/{v3-renderer.ts → renderer.ts} +16 -16
  37. package/client/modeller/{v3-stage-model.ts → stage-model.ts} +15 -15
  38. package/client/modeller/{v3-thumbnail.ts → thumbnail.ts} +8 -8
  39. package/client/pages/figure-list-page.ts +4 -4
  40. package/client/pages/{figure-v3-modeller-page.ts → figure-modeller-page.ts} +37 -37
  41. package/client/route.ts +1 -5
  42. package/dist-client/figure-assistant.js +3 -3
  43. package/dist-client/figure-assistant.js.map +1 -1
  44. package/dist-client/graphql/store.d.ts +3 -0
  45. package/dist-client/graphql/store.js +39 -0
  46. package/dist-client/graphql/store.js.map +1 -0
  47. package/dist-client/modeller/ai-proposal.d.ts +51 -0
  48. package/dist-client/modeller/ai-proposal.js +64 -0
  49. package/dist-client/modeller/ai-proposal.js.map +1 -0
  50. package/dist-client/modeller/align.d.ts +30 -0
  51. package/dist-client/modeller/align.js +75 -0
  52. package/dist-client/modeller/align.js.map +1 -0
  53. package/dist-client/modeller/canvas.d.ts +72 -0
  54. package/dist-client/modeller/canvas.js +379 -0
  55. package/dist-client/modeller/canvas.js.map +1 -0
  56. package/dist-client/modeller/clips.d.ts +39 -0
  57. package/dist-client/modeller/clips.js +58 -0
  58. package/dist-client/modeller/clips.js.map +1 -0
  59. package/dist-client/modeller/controls.d.ts +28 -0
  60. package/dist-client/modeller/controls.js +28 -0
  61. package/dist-client/modeller/controls.js.map +1 -0
  62. package/dist-client/modeller/editor.d.ts +203 -0
  63. package/dist-client/modeller/editor.js +1526 -0
  64. package/dist-client/modeller/editor.js.map +1 -0
  65. package/dist-client/modeller/fields.d.ts +45 -0
  66. package/dist-client/modeller/fields.js +83 -0
  67. package/dist-client/modeller/fields.js.map +1 -0
  68. package/dist-client/modeller/figure-capability-panel.d.ts +22 -0
  69. package/dist-client/modeller/figure-capability-panel.js +141 -0
  70. package/dist-client/modeller/figure-capability-panel.js.map +1 -0
  71. package/dist-client/modeller/figure-clips.d.ts +31 -0
  72. package/dist-client/modeller/figure-clips.js +268 -0
  73. package/dist-client/modeller/figure-clips.js.map +1 -0
  74. package/dist-client/modeller/figure-controls.d.ts +36 -0
  75. package/dist-client/modeller/figure-controls.js +226 -0
  76. package/dist-client/modeller/figure-controls.js.map +1 -0
  77. package/dist-client/modeller/figure-facts.d.ts +35 -0
  78. package/dist-client/modeller/figure-facts.js +55 -0
  79. package/dist-client/modeller/figure-facts.js.map +1 -0
  80. package/dist-client/modeller/figure-first-save.d.ts +20 -0
  81. package/dist-client/modeller/figure-first-save.js +118 -0
  82. package/dist-client/modeller/figure-first-save.js.map +1 -0
  83. package/dist-client/modeller/figure-history-panel.d.ts +1 -1
  84. package/dist-client/modeller/figure-history-panel.js.map +1 -1
  85. package/dist-client/modeller/figure-links.d.ts +77 -0
  86. package/dist-client/modeller/figure-links.js +649 -0
  87. package/dist-client/modeller/figure-links.js.map +1 -0
  88. package/dist-client/modeller/figure-material-panel.d.ts +39 -0
  89. package/dist-client/modeller/figure-material-panel.js +323 -0
  90. package/dist-client/modeller/figure-material-panel.js.map +1 -0
  91. package/dist-client/modeller/figure-parts.d.ts +69 -0
  92. package/dist-client/modeller/figure-parts.js +559 -0
  93. package/dist-client/modeller/figure-parts.js.map +1 -0
  94. package/dist-client/modeller/figure-preview.d.ts +34 -0
  95. package/dist-client/modeller/figure-preview.js +346 -0
  96. package/dist-client/modeller/figure-preview.js.map +1 -0
  97. package/dist-client/modeller/figure-properties.d.ts +45 -0
  98. package/dist-client/modeller/figure-properties.js +239 -0
  99. package/dist-client/modeller/figure-properties.js.map +1 -0
  100. package/dist-client/modeller/figure-recipe-catalog.d.ts +38 -0
  101. package/dist-client/modeller/figure-recipe-catalog.js +399 -0
  102. package/dist-client/modeller/figure-recipe-catalog.js.map +1 -0
  103. package/dist-client/modeller/figure-verdict.d.ts +64 -0
  104. package/dist-client/modeller/figure-verdict.js +787 -0
  105. package/dist-client/modeller/figure-verdict.js.map +1 -0
  106. package/dist-client/modeller/first-save.d.ts +17 -0
  107. package/dist-client/modeller/first-save.js +26 -0
  108. package/dist-client/modeller/first-save.js.map +1 -0
  109. package/dist-client/modeller/gate-findings.d.ts +2 -2
  110. package/dist-client/modeller/gate-findings.js +2 -2
  111. package/dist-client/modeller/gate-findings.js.map +1 -1
  112. package/dist-client/modeller/gizmo.d.ts +72 -0
  113. package/dist-client/modeller/gizmo.js +111 -0
  114. package/dist-client/modeller/gizmo.js.map +1 -0
  115. package/dist-client/modeller/help.d.ts +18 -0
  116. package/dist-client/modeller/help.js +37 -0
  117. package/dist-client/modeller/help.js.map +1 -0
  118. package/dist-client/modeller/joint-drag.d.ts +19 -0
  119. package/dist-client/modeller/joint-drag.js +97 -0
  120. package/dist-client/modeller/joint-drag.js.map +1 -0
  121. package/dist-client/modeller/joint-fan.d.ts +9 -0
  122. package/dist-client/modeller/joint-fan.js +276 -0
  123. package/dist-client/modeller/joint-fan.js.map +1 -0
  124. package/dist-client/modeller/keys.d.ts +22 -0
  125. package/dist-client/modeller/keys.js +26 -0
  126. package/dist-client/modeller/keys.js.map +1 -0
  127. package/dist-client/modeller/links.d.ts +267 -0
  128. package/dist-client/modeller/links.js +480 -0
  129. package/dist-client/modeller/links.js.map +1 -0
  130. package/dist-client/modeller/live.d.ts +37 -0
  131. package/dist-client/modeller/live.js +68 -0
  132. package/dist-client/modeller/live.js.map +1 -0
  133. package/dist-client/modeller/maturity.d.ts +120 -0
  134. package/dist-client/modeller/maturity.js +313 -0
  135. package/dist-client/modeller/maturity.js.map +1 -0
  136. package/dist-client/modeller/modeller-route.d.ts +1 -1
  137. package/dist-client/modeller/modeller-route.js +1 -1
  138. package/dist-client/modeller/modeller-route.js.map +1 -1
  139. package/dist-client/modeller/occupancy.d.ts +18 -0
  140. package/dist-client/modeller/occupancy.js +24 -0
  141. package/dist-client/modeller/occupancy.js.map +1 -0
  142. package/dist-client/modeller/palette.d.ts +7 -0
  143. package/dist-client/modeller/palette.js +46 -0
  144. package/dist-client/modeller/palette.js.map +1 -0
  145. package/dist-client/modeller/preview-model.d.ts +47 -0
  146. package/dist-client/modeller/preview-model.js +68 -0
  147. package/dist-client/modeller/preview-model.js.map +1 -0
  148. package/dist-client/modeller/product-session.d.ts +510 -0
  149. package/dist-client/modeller/product-session.js +157 -0
  150. package/dist-client/modeller/product-session.js.map +1 -0
  151. package/dist-client/modeller/recipe-source.d.ts +8 -0
  152. package/dist-client/modeller/recipe-source.js +14 -0
  153. package/dist-client/modeller/recipe-source.js.map +1 -0
  154. package/dist-client/modeller/recipes.d.ts +37 -0
  155. package/dist-client/modeller/recipes.js +103 -0
  156. package/dist-client/modeller/recipes.js.map +1 -0
  157. package/dist-client/modeller/refusals.d.ts +68 -0
  158. package/dist-client/modeller/refusals.js +142 -0
  159. package/dist-client/modeller/refusals.js.map +1 -0
  160. package/dist-client/modeller/renderer.d.ts +40 -0
  161. package/dist-client/modeller/renderer.js +73 -0
  162. package/dist-client/modeller/renderer.js.map +1 -0
  163. package/dist-client/modeller/stage-model.d.ts +39 -0
  164. package/dist-client/modeller/stage-model.js +147 -0
  165. package/dist-client/modeller/stage-model.js.map +1 -0
  166. package/dist-client/modeller/thumbnail.d.ts +3 -0
  167. package/dist-client/modeller/thumbnail.js +75 -0
  168. package/dist-client/modeller/thumbnail.js.map +1 -0
  169. package/dist-client/pages/figure-list-page.d.ts +1 -1
  170. package/dist-client/pages/figure-list-page.js +4 -4
  171. package/dist-client/pages/figure-list-page.js.map +1 -1
  172. package/dist-client/pages/figure-modeller-page.d.ts +102 -0
  173. package/dist-client/pages/figure-modeller-page.js +548 -0
  174. package/dist-client/pages/figure-modeller-page.js.map +1 -0
  175. package/dist-client/route.d.ts +1 -1
  176. package/dist-client/route.js +1 -5
  177. package/dist-client/route.js.map +1 -1
  178. package/dist-client/tsconfig.tsbuildinfo +1 -1
  179. package/docs/product-cutover.md +25 -0
  180. package/package.json +5 -5
  181. package/test/{v3-ai-proposal.test.ts → ai-proposal.test.ts} +6 -6
  182. package/test/{v3-align.test.ts → align.test.ts} +8 -8
  183. package/test/browser/modeller-page.js +30 -30
  184. package/test/browser/modeller.check.mjs +14 -14
  185. package/test/browser/serve.mjs +1 -1
  186. package/test/browser/thumbnail-page.js +6 -6
  187. package/test/{v3-clips.test.ts → clips.test.ts} +18 -18
  188. package/test/{v3-controls.test.ts → controls.test.ts} +15 -15
  189. package/test/{v3-fields.test.ts → fields.test.ts} +13 -13
  190. package/test/figure-assistant-surface.test.ts +3 -3
  191. package/test/{v3-first-save.test.ts → first-save.test.ts} +10 -10
  192. package/test/fixtures/joint-column.json +2 -2
  193. package/test/fixtures/joint-roller.json +2 -2
  194. package/test/fixtures/robot-arm-6dof.json +2 -2
  195. package/test/{v3-gizmo.test.ts → gizmo.test.ts} +33 -33
  196. package/test/{v3-joint-drag.test.ts → joint-drag.test.ts} +17 -17
  197. package/test/{v3-joint-fan.test.ts → joint-fan.test.ts} +18 -18
  198. package/test/joints.ts +6 -6
  199. package/test/{v3-keys.test.ts → keys.test.ts} +2 -2
  200. package/test/{v3-layout.test.ts → layout.test.ts} +8 -8
  201. package/test/{v3-links.test.ts → links.test.ts} +201 -201
  202. package/test/{v3-live.test.ts → live.test.ts} +18 -18
  203. package/test/{v3-material.test.ts → material.test.ts} +12 -12
  204. package/test/{v3-maturity.test.ts → maturity.test.ts} +74 -74
  205. package/test/modeller-route.test.ts +5 -6
  206. package/test/{v3-palette-icons.test.ts → palette-icons.test.ts} +1 -1
  207. package/test/{v3-parts-wiring.test.ts → parts-wiring.test.ts} +5 -5
  208. package/test/{v3-preview.test.ts → preview.test.ts} +12 -12
  209. package/test/{v3-product-session.test.ts → product-session.test.ts} +17 -17
  210. package/test/{v3-product-write.test.ts → product-write.test.ts} +20 -20
  211. package/test/{v3-properties.test.ts → properties.test.ts} +24 -24
  212. package/test/{v3-recipes.test.ts → recipes.test.ts} +9 -9
  213. package/test/{v3-refusals.test.ts → refusals.test.ts} +43 -43
  214. package/test/{v3-release-panel.test.ts → release-panel.test.ts} +21 -21
  215. package/test/renderer.test.ts +24 -0
  216. package/test/resolve-ts-siblings.mjs +1 -1
  217. package/test/{v3-scene-canvas.test.ts → scene-canvas.test.ts} +6 -6
  218. package/test/{v3-stage-model.test.ts → stage-model.test.ts} +5 -5
  219. package/test/{v3-theme.test.ts → theme.test.ts} +4 -4
  220. package/test/{v3-thumbnail.test.ts → thumbnail.test.ts} +4 -4
  221. package/test/{v3-verdict.test.ts → verdict.test.ts} +17 -17
  222. package/test/{v3-view.test.ts → view.test.ts} +2 -2
  223. package/things-factory.config.js +2 -3
  224. package/translations/en.json +2 -2
  225. package/translations/ja.json +2 -2
  226. package/translations/ko.json +2 -2
  227. package/translations/ms.json +2 -2
  228. package/translations/zh.json +2 -2
  229. package/docs/v3-product-cutover.md +0 -25
  230. package/test/v3-renderer.test.ts +0 -24
  231. /package/client/modeller/{v3-fields.ts → fields.ts} +0 -0
  232. /package/client/modeller/{v3-first-save.ts → first-save.ts} +0 -0
  233. /package/client/modeller/{v3-help.ts → help.ts} +0 -0
  234. /package/client/modeller/{v3-keys.ts → keys.ts} +0 -0
  235. /package/client/modeller/{v3-refusals.ts → refusals.ts} +0 -0
@@ -2,13 +2,13 @@ import { css, html, LitElement, nothing, unsafeCSS } from 'lit'
2
2
  import { customElement, property, query, state } from 'lit/decorators.js'
3
3
 
4
4
  import { i18next, localize } from '@operato/i18n'
5
- import { compileV3Asset } from '@hatiolab/figure-model'
6
- import type { V3Asset } from '@hatiolab/figure-model'
7
- import { create, registerFigureV3, unregisterFigureV3 } from '@hatiolab/things-scene'
5
+ import { compileFigureAsset } from '@hatiolab/figure-model'
6
+ import type { FigureAsset } from '@hatiolab/figure-model'
7
+ import { create, registerFigure, unregisterFigure } from '@hatiolab/things-scene'
8
8
 
9
9
  import { DEFAULT_VIEW, previewBoardDepth, sceneLook, WORKBENCH, type ViewSettings } from './figure-view.js'
10
10
  import { dialShape } from './angle-dial.js'
11
- import { authoredSizeOf, PREVIEW_FACTOR, PREVIEW_WAYS, previewClampsOf, previewInstances } from './v3-preview-model.js'
11
+ import { authoredSizeOf, PREVIEW_FACTOR, PREVIEW_WAYS, previewClampsOf, previewInstances } from './preview-model.js'
12
12
 
13
13
  /**
14
14
  * The figure placed on a board.
@@ -17,7 +17,7 @@ import { authoredSizeOf, PREVIEW_FACTOR, PREVIEW_WAYS, previewClampsOf, previewI
17
17
  * different size, so here the figure being authored is registered as a board registers it and placed as
18
18
  * `FigureInstance`s: the size it was made, and each of the seven ways of growing one, two or three axes twice as
19
19
  * large, side by side at one scale, four to a row. The board draws them as it will on a real board --
20
- * placement, lights, environment, the same V3 renderer.
20
+ * placement, lights, environment, the same renderer.
21
21
  *
22
22
  * The controls: each value that poses the figure with a slider (and a dial for an angle), each clip's
23
23
  * speed, and 「이 자세를 기본으로」 to keep the pose as the one it stands in. Nothing here is edited; the canvas edits.
@@ -34,8 +34,8 @@ interface SceneHandle {
34
34
  }
35
35
  }
36
36
 
37
- @customElement('figure-v3-preview')
38
- export class FigureV3Preview extends localize(i18next)(LitElement) {
37
+ @customElement('figure-preview')
38
+ export class FigurePreview extends localize(i18next)(LitElement) {
39
39
  static styles = css`
40
40
  :host {
41
41
  position: relative;
@@ -149,7 +149,7 @@ export class FigureV3Preview extends localize(i18next)(LitElement) {
149
149
  `
150
150
 
151
151
  /** The figure being authored, as the editor holds it now. */
152
- @property({ attribute: false }) asset?: V3Asset
152
+ @property({ attribute: false }) asset?: FigureAsset
153
153
  /** Which figure: the type it is registered under here is this one's own, never a real type a board uses. */
154
154
  @property() context = ''
155
155
  @property({ attribute: false }) view: ViewSettings = DEFAULT_VIEW
@@ -192,7 +192,7 @@ export class FigureV3Preview extends localize(i18next)(LitElement) {
192
192
  ${controls.length + clips.length ? this.renderDrivers(asset!, controls, clips) : nothing}`}`
193
193
  }
194
194
 
195
- private renderDrivers(asset: V3Asset, controls: V3Asset['document']['model']['inputs'], clips: string[]) {
195
+ private renderDrivers(asset: FigureAsset, controls: FigureAsset['document']['model']['inputs'], clips: string[]) {
196
196
  const changed = controls.filter(c => this.giving[c.id] !== undefined && this.giving[c.id] !== asset.stateDefaults[c.id])
197
197
  return html`<div clips>
198
198
  ${changed.length
@@ -252,7 +252,7 @@ export class FigureV3Preview extends localize(i18next)(LitElement) {
252
252
  }
253
253
 
254
254
  private poseAsDefault() {
255
- this.dispatchEvent(new CustomEvent('v3-part-edit', { detail: { edit: { kind: 'set-state-defaults', values: { ...this.giving } } }, bubbles: true, composed: true }))
255
+ this.dispatchEvent(new CustomEvent('part-edit', { detail: { edit: { kind: 'set-state-defaults', values: { ...this.giving } } }, bubbles: true, composed: true }))
256
256
  this.giving = {}
257
257
  }
258
258
 
@@ -297,7 +297,7 @@ export class FigureV3Preview extends localize(i18next)(LitElement) {
297
297
  const signature = JSON.stringify([asset, view.placement, view.ceilingHeight])
298
298
  if (this.scene && this.standing === signature) return
299
299
  try {
300
- compileV3Asset(asset)
300
+ compileFigureAsset(asset)
301
301
  } catch {
302
302
  this.cannot = i18next.t('figure.text.preview-blocked-by-errors')
303
303
  this.teardown()
@@ -307,8 +307,8 @@ export class FigureV3Preview extends localize(i18next)(LitElement) {
307
307
  this.driving = {}
308
308
  try {
309
309
  /* Registered as a board registers it, under a type of this preview's own: a real type's drawings stay as they are. */
310
- const type = `figure-v3-preview-${this.context || 'new'}`
311
- registerFigureV3(type, asset)
310
+ const type = `figure-preview-${this.context || 'new'}`
311
+ registerFigure(type, asset)
312
312
  this.registered = type
313
313
  const size = authoredSizeOf(asset)
314
314
  const { components, board } = previewInstances(type, size)
@@ -343,7 +343,7 @@ export class FigureV3Preview extends localize(i18next)(LitElement) {
343
343
  this.scene?.dispose?.()
344
344
  this.scene = undefined
345
345
  this.standing = undefined
346
- if (this.registered) unregisterFigureV3(this.registered)
346
+ if (this.registered) unregisterFigure(this.registered)
347
347
  this.registered = undefined
348
348
  }
349
349
  }
@@ -5,16 +5,16 @@ import { customElement, property, state } from 'lit/decorators.js'
5
5
  import { live } from 'lit/directives/live.js'
6
6
 
7
7
  import { i18next, localize } from '@operato/i18n'
8
- import { V3_FACES } from '@hatiolab/figure-model'
9
- import type { V3AuthoringAction, V3Face } from '@hatiolab/figure-model'
8
+ import { FIGURE_FACES } from '@hatiolab/figure-model'
9
+ import type { FigureAuthoringAction, FigureFace } from '@hatiolab/figure-model'
10
10
 
11
- import { occupancyFromParts } from './v3-occupancy.js'
12
- import { tagsOf, v3FigureFacts, type Placement, type V3FigureFacts } from './v3-figure-facts.js'
11
+ import { occupancyFromParts } from './occupancy.js'
12
+ import { tagsOf, figureFigureFacts, type Placement, type FigureFigureFacts } from './figure-facts.js'
13
13
  import { SidePanelStyles } from './side-panel-styles.js'
14
14
  import { ScrollbarStyles } from '@operato/styles'
15
15
 
16
16
  const PLACEMENTS: Placement[] = ['floor', 'ceiling', 'center', 'wall']
17
- const FACES = Object.keys(V3_FACES) as V3Face[]
17
+ const FACES = Object.keys(FIGURE_FACES) as FigureFace[]
18
18
 
19
19
  /**
20
20
  * 속성 — the modeller's figure properties.
@@ -23,16 +23,16 @@ const FACES = Object.keys(V3_FACES) as V3Face[]
23
23
  * catalogue files it.
24
24
  *
25
25
  * Two kinds of change leave this tab, and it does neither itself:
26
- * - `v3-command` carries an authoring command (rename, declare the room, the mounting face). The page runs it
26
+ * - `command` carries an authoring command (rename, declare the room, the mounting face). The page runs it
27
27
  * through the session, so it is one step of the same undo history as every other edit, and is saved with it.
28
- * - `v3-catalog` carries a change to the row (category, tags). Those are not in the asset; the page writes them
28
+ * - `catalog` carries a change to the row (category, tags). Those are not in the asset; the page writes them
29
29
  * to the saved row at once, so they need a saved figure.
30
30
  *
31
- * Not here yet, because V3 has no place for them: editing the declared room by hand (V3 declares it from the
32
- * parts, as graph expressions), and the detail level. Both wait on a V3 ruling.
31
+ * Not here yet, because has no place for them: editing the declared room by hand (declares it from the
32
+ * parts, as graph expressions), and the detail level. Both wait on a ruling.
33
33
  */
34
- @customElement('figure-v3-properties')
35
- export class FigureV3Properties extends localize(i18next)(LitElement) {
34
+ @customElement('figure-properties')
35
+ export class FigureProperties extends localize(i18next)(LitElement) {
36
36
  static styles = [ScrollbarStyles, SidePanelStyles, css`
37
37
  :host {
38
38
  display: block;
@@ -66,11 +66,11 @@ export class FigureV3Properties extends localize(i18next)(LitElement) {
66
66
  @property({ type: Array }) figureTags?: string[]
67
67
  /** The part and face picked for the mounting face, before 「이 면으로」 is pressed. */
68
68
  @state() private pickedPart?: string
69
- @state() private pickedFace?: V3Face
69
+ @state() private pickedFace?: FigureFace
70
70
 
71
- private facts(): V3FigureFacts | undefined {
71
+ private facts(): FigureFigureFacts | undefined {
72
72
  try {
73
- return this.source ? v3FigureFacts(this.source) : undefined
73
+ return this.source ? figureFigureFacts(this.source) : undefined
74
74
  } catch {
75
75
  /* A source the format refuses has no facts to show; the verdict tab says why. */
76
76
  return undefined
@@ -96,7 +96,7 @@ export class FigureV3Properties extends localize(i18next)(LitElement) {
96
96
  </section>`
97
97
  }
98
98
 
99
- private renderName(facts: V3FigureFacts) {
99
+ private renderName(facts: FigureFigureFacts) {
100
100
  return html`
101
101
  <div field>
102
102
  <label>${i18next.t('figure.label.name')}</label>
@@ -118,10 +118,10 @@ export class FigureV3Properties extends localize(i18next)(LitElement) {
118
118
  }
119
119
 
120
120
  /**
121
- * The face the figure is placed by. In V3 it is part of the room declaration, so choosing one declares the room
122
- * again from the parts — the only way V3 declares it — with the new face. The hint says so.
121
+ * The face the figure is placed by. In it is part of the room declaration, so choosing one declares the room
122
+ * again from the parts — the only way declares it — with the new face. The hint says so.
123
123
  */
124
- private renderPlacement(facts: V3FigureFacts) {
124
+ private renderPlacement(facts: FigureFigureFacts) {
125
125
  const current = facts.placement ?? 'floor'
126
126
  return html`
127
127
  <div field>
@@ -145,7 +145,7 @@ export class FigureV3Properties extends localize(i18next)(LitElement) {
145
145
  `
146
146
  }
147
147
 
148
- private renderSpace(facts: V3FigureFacts) {
148
+ private renderSpace(facts: FigureFigureFacts) {
149
149
  return html`
150
150
  <div field>
151
151
  <label>${i18next.t('figure.label.declared-space')}</label>
@@ -159,11 +159,11 @@ export class FigureV3Properties extends localize(i18next)(LitElement) {
159
159
  `
160
160
  }
161
161
 
162
- private renderMounting(facts: V3FigureFacts) {
162
+ private renderMounting(facts: FigureFigureFacts) {
163
163
  const mounting = facts.mounting
164
164
  /* Chosen, or what is set, or where the figure meets its plane -- not the first part listed (walk 1). */
165
165
  const part = [this.pickedPart, mounting?.part, facts.suggested?.part, facts.parts[0]].find(one => one && facts.parts.includes(one))
166
- const face: V3Face = this.pickedFace ?? mounting?.face ?? facts.suggested?.face ?? 'bottom'
166
+ const face: FigureFace = this.pickedFace ?? mounting?.face ?? facts.suggested?.face ?? 'bottom'
167
167
  return html`
168
168
  <div field>
169
169
  <label>${i18next.t('figure.label.mounting-face')}</label>
@@ -171,7 +171,7 @@ export class FigureV3Properties extends localize(i18next)(LitElement) {
171
171
  <select ?disabled=${!facts.declared} @change=${(e: Event) => (this.pickedPart = (e.target as HTMLSelectElement).value)}>
172
172
  ${facts.parts.map(one => html`<option value=${one} ?selected=${one === part}>${one}</option>`)}
173
173
  </select>
174
- <select ?disabled=${!facts.declared} @change=${(e: Event) => (this.pickedFace = (e.target as HTMLSelectElement).value as V3Face)}>
174
+ <select ?disabled=${!facts.declared} @change=${(e: Event) => (this.pickedFace = (e.target as HTMLSelectElement).value as FigureFace)}>
175
175
  ${FACES.map(one => html`<option value=${one} ?selected=${one === face}>${i18next.t('figure.label.face-' + one)}</option>`)}
176
176
  </select>
177
177
  </div>
@@ -224,11 +224,11 @@ export class FigureV3Properties extends localize(i18next)(LitElement) {
224
224
  this.command(occupancyFromParts(this.source, placement))
225
225
  }
226
226
 
227
- private command(action: V3AuthoringAction) {
228
- this.dispatchEvent(new CustomEvent('v3-command', { detail: { action }, bubbles: true, composed: true }))
227
+ private command(action: FigureAuthoringAction) {
228
+ this.dispatchEvent(new CustomEvent('command', { detail: { action }, bubbles: true, composed: true }))
229
229
  }
230
230
 
231
231
  private catalog(patch: { category?: string; tags?: string[] }) {
232
- this.dispatchEvent(new CustomEvent('v3-catalog', { detail: patch, bubbles: true, composed: true }))
232
+ this.dispatchEvent(new CustomEvent('catalog', { detail: patch, bubbles: true, composed: true }))
233
233
  }
234
234
  }
@@ -6,19 +6,19 @@ import { customElement, property, state } from 'lit/decorators.js'
6
6
 
7
7
  import { i18next, localize } from '@operato/i18n'
8
8
  import { ScrollbarStyles } from '@operato/styles'
9
- import { describeV3Recipe } from '@hatiolab/figure-model'
10
- import type { V3Recipe, V3RecipeValue } from '@hatiolab/figure-model'
9
+ import { describeFigureRecipe } from '@hatiolab/figure-model'
10
+ import type { FigureRecipe, FigureRecipeValue } from '@hatiolab/figure-model'
11
11
 
12
- import { recipeFieldOf, recipeReady, recipeValueOf, recipeWord, recipeWorkings, recipesMatching, type RecipeTemplate } from './v3-recipes.js'
12
+ import { recipeFieldOf, recipeReady, recipeValueOf, recipeWord, recipeWorkings, recipesMatching, type RecipeTemplate } from './recipes.js'
13
13
 
14
14
  /**
15
15
  * The recipe catalog (ADR-0095): the templates the product ships, by category, found by words, each told in full --
16
16
  * what it puts in, how many parts, how they move, what each value does, and how to use it -- and put onto the chosen
17
- * part in one step. The numbers and the ways of moving are worked out from the recipe (`describeV3Recipe`), so they say
17
+ * part in one step. The numbers and the ways of moving are worked out from the recipe (`describeFigureRecipe`), so they say
18
18
  * what it does; only the descriptions and the guide are written.
19
19
  */
20
- @customElement('figure-v3-recipe-catalog')
21
- export class FigureV3RecipeCatalog extends localize(i18next)(LitElement) {
20
+ @customElement('figure-recipe-catalog')
21
+ export class FigureRecipeCatalog extends localize(i18next)(LitElement) {
22
22
  static styles = [
23
23
  ScrollbarStyles,
24
24
  css`
@@ -238,7 +238,7 @@ export class FigureV3RecipeCatalog extends localize(i18next)(LitElement) {
238
238
  @state() private category = ''
239
239
  @state() private query = ''
240
240
  @state() private chosen = ''
241
- @state() private values: Record<string, V3RecipeValue> = {}
241
+ @state() private values: Record<string, FigureRecipeValue> = {}
242
242
  /*
243
243
  The guide as a page \`ox-markdown\` can read: it renders what its src points to, not text, and given the text it
244
244
  fetched it as a path and drew the app's own page in its place (seen on :3300, 2026-09-26). A blob of type
@@ -314,7 +314,7 @@ export class FigureV3RecipeCatalog extends localize(i18next)(LitElement) {
314
314
  }
315
315
 
316
316
  private renderDetail(t: RecipeTemplate, language: string) {
317
- const d = describeV3Recipe(t.recipe)
317
+ const d = describeFigureRecipe(t.recipe)
318
318
  const count = d.parts.min === d.parts.max ? String(d.parts.min) : `${d.parts.min}–${d.parts.max}`
319
319
  const ready = recipeReady(t.recipe, this.selected)
320
320
  return html`
@@ -339,8 +339,8 @@ export class FigureV3RecipeCatalog extends localize(i18next)(LitElement) {
339
339
  }
340
340
 
341
341
  /** Each value: its name, what it does, and the field to set it -- a share typed as a percentage. */
342
- private renderForm(recipe: V3Recipe, language: string) {
343
- const set = (name: string, value: V3RecipeValue) => (this.values = { ...this.values, [name]: value })
342
+ private renderForm(recipe: FigureRecipe, language: string) {
343
+ const set = (name: string, value: FigureRecipeValue) => (this.values = { ...this.values, [name]: value })
344
344
  return html`<div form>
345
345
  <h3>${i18next.t('figure.label.recipe-values')}</h3>
346
346
  ${recipe.params.map(param => {
@@ -5,15 +5,15 @@ import { customElement, property, state } from 'lit/decorators.js'
5
5
 
6
6
  import { i18next, localize } from '@operato/i18n'
7
7
  import { ScrollbarStyles } from '@operato/styles'
8
- import { LIMITS, parseV3Asset, V3_PER_INSTANCE, v3CostOf } from '@hatiolab/figure-model'
8
+ import { LIMITS, parseFigureAsset, FIGURE_PER_INSTANCE, figureCostOf } from '@hatiolab/figure-model'
9
9
 
10
10
  import { inspectFigureDraft } from '../graphql/index.js'
11
11
  import type { FigureInspection } from '../types.js'
12
12
  import { gateFindingStyles, renderGateFinding } from './gate-findings.js'
13
13
  import { MEASURED } from './scene-budget.js'
14
14
  import { SidePanelStyles } from './side-panel-styles.js'
15
- import { budgetOf, CURVE_SIZE, curveLayout, GRADE_SCALE, shortNumber, v3ReportOf } from './v3-maturity.js'
16
- import type { V3BudgetUse, V3MaturityAxis, V3Report } from './v3-maturity.js'
15
+ import { budgetOf, CURVE_SIZE, curveLayout, GRADE_SCALE, shortNumber, figureReportOf } from './maturity.js'
16
+ import type { FigureBudgetUse, FigureMaturityAxis, FigureReport } from './maturity.js'
17
17
 
18
18
  /** How long an edit settles before the server is asked again. One question per pause, not one per keystroke. */
19
19
  export const VERDICT_SETTLE_MS = 400
@@ -28,7 +28,7 @@ export const VERDICT_SETTLE_MS = 400
28
28
  *
29
29
  * ## What is computed where
30
30
  *
31
- * The numbers come from `v3-maturity.ts`, which reads the evaluated asset the way the V3 renderer draws it — the
31
+ * The numbers come from `maturity.ts`, which reads the evaluated asset the way the renderer draws it — the
32
32
  * screen calls and shows, it does not count. The count uses the same `capacityFrom` the catalogue cards use, so the
33
33
  * tab and the card cannot disagree.
34
34
  *
@@ -38,16 +38,16 @@ export const VERDICT_SETTLE_MS = 400
38
38
  *
39
39
  * ## The grade
40
40
  *
41
- * figure-model's `v3ScoreOf`, which the catalogue uses too: headroom against placing `LIMITS.screenInstances` of the
41
+ * figure-model's `figureScoreOf`, which the catalogue uses too: headroom against placing `LIMITS.screenInstances` of the
42
42
  * figure on one screen (chief architect, 2026-09-24) -- the screen's measured groups and its triangle target shared
43
43
  * per instance -- and the rule limits for independently moved parts and transparent materials, decided by the
44
- * tightest. The two budgets V3 has no field for are left out and the tab says so.
44
+ * tightest. The two budgets has no field for are left out and the tab says so.
45
45
  */
46
46
  /** A per-instance share of the screen is not a whole number; what an instance may draw is the whole part of it. */
47
- const limitOf = (use: V3BudgetUse) => Math.floor(use.limit).toLocaleString()
47
+ const limitOf = (use: FigureBudgetUse) => Math.floor(use.limit).toLocaleString()
48
48
 
49
- @customElement('figure-v3-verdict')
50
- export class FigureV3Verdict extends localize(i18next)(LitElement) {
49
+ @customElement('figure-verdict')
50
+ export class FigureVerdict extends localize(i18next)(LitElement) {
51
51
  static styles = [
52
52
  ScrollbarStyles,
53
53
  SidePanelStyles,
@@ -384,7 +384,7 @@ export class FigureV3Verdict extends localize(i18next)(LitElement) {
384
384
  @property({ type: String }) figureType?: string
385
385
 
386
386
  /** The report over the source being edited. Absent when nothing is drawn, or the format refuses the source. */
387
- @state() private report?: V3Report
387
+ @state() private report?: FigureReport
388
388
  /** Bodies in the asset, drawn or hidden. With none there is nothing to fit the space to, so the button is not offered. */
389
389
  @state() private bodies = 0
390
390
  /** Whether the trend chart is open at its own size. */
@@ -408,13 +408,13 @@ export class FigureV3Verdict extends localize(i18next)(LitElement) {
408
408
  }
409
409
 
410
410
  private measure() {
411
- let report: V3Report | undefined
411
+ let report: FigureReport | undefined
412
412
  let bodies = 0
413
413
  try {
414
- const asset = this.source ? parseV3Asset(this.source) : undefined
415
- const cost = asset ? v3CostOf(asset) : undefined
414
+ const asset = this.source ? parseFigureAsset(this.source) : undefined
415
+ const cost = asset ? figureCostOf(asset) : undefined
416
416
  bodies = cost ? cost.groups + cost.hidden : 0
417
- report = asset ? v3ReportOf(asset) : undefined
417
+ report = asset ? figureReportOf(asset) : undefined
418
418
  } catch {
419
419
  /* A source the format refuses has no report; the server's finding below says why. */
420
420
  report = undefined
@@ -481,7 +481,7 @@ export class FigureV3Verdict extends localize(i18next)(LitElement) {
481
481
  * The count's denominator is measured (2026-09-18, Apple M5) and says so with the machine and the date; the
482
482
  * triangle side is still the design document's target and says that.
483
483
  */
484
- private renderVerdict(report: V3Report) {
484
+ private renderVerdict(report: FigureReport) {
485
485
  const { capacity, score } = report
486
486
  return html`
487
487
  <section>
@@ -508,8 +508,8 @@ export class FigureV3Verdict extends localize(i18next)(LitElement) {
508
508
  <p basis>
509
509
  ${i18next.t('figure.text.grade-target', {
510
510
  target: LIMITS.screenInstances,
511
- groups: Math.floor(V3_PER_INSTANCE.groups),
512
- triangles: Math.floor(V3_PER_INSTANCE.triangles).toLocaleString()
511
+ groups: Math.floor(FIGURE_PER_INSTANCE.groups),
512
+ triangles: Math.floor(FIGURE_PER_INSTANCE.triangles).toLocaleString()
513
513
  })}
514
514
  </p>
515
515
  ${this.renderGradeScale(score.grade)}
@@ -544,7 +544,7 @@ export class FigureV3Verdict extends localize(i18next)(LitElement) {
544
544
  *
545
545
  * Scale is left out of the list: it leads the tab, and the same number twice reads as two different things.
546
546
  */
547
- private renderAxes(report: V3Report) {
547
+ private renderAxes(report: FigureReport) {
548
548
  const measured = report.axes.filter(axis => axis.ratio !== undefined)
549
549
  const unmeasured = report.axes.filter(axis => axis.ratio === undefined)
550
550
  return html`
@@ -566,7 +566,7 @@ export class FigureV3Verdict extends localize(i18next)(LitElement) {
566
566
  }
567
567
 
568
568
  /** The radar: rings at a third, two thirds and full, short names so the left edge is not cut. */
569
- private renderRadar(axes: V3MaturityAxis[]) {
569
+ private renderRadar(axes: FigureMaturityAxis[]) {
570
570
  const cx = 60
571
571
  const cy = 46
572
572
  const r = 28
@@ -600,7 +600,7 @@ export class FigureV3Verdict extends localize(i18next)(LitElement) {
600
600
  `
601
601
  }
602
602
 
603
- private renderAxis(axis: V3MaturityAxis) {
603
+ private renderAxis(axis: FigureMaturityAxis) {
604
604
  const name = i18next.t('figure.label.axis-' + axis.key)
605
605
  if (axis.blocked) {
606
606
  return html`
@@ -623,7 +623,7 @@ export class FigureV3Verdict extends localize(i18next)(LitElement) {
623
623
  }
624
624
 
625
625
  /** The budget bars. The one closest to its limit is coloured — it decided the grade. */
626
- private renderBudgets(report: V3Report) {
626
+ private renderBudgets(report: FigureReport) {
627
627
  const { score } = report
628
628
  return html`
629
629
  <section>
@@ -639,12 +639,12 @@ export class FigureV3Verdict extends localize(i18next)(LitElement) {
639
639
  `
640
640
  )}
641
641
  </ul>
642
- <p quiet>${i18next.t('figure.text.budgets-not-in-v3')}</p>
642
+ <p quiet>${i18next.t('figure.text.budgets-not-in')}</p>
643
643
  </section>
644
644
  `
645
645
  }
646
646
 
647
- private renderMetrics(report: V3Report) {
647
+ private renderMetrics(report: FigureReport) {
648
648
  return html`
649
649
  <section>
650
650
  <h3>${i18next.t('figure.label.weight')}</h3>
@@ -673,7 +673,7 @@ export class FigureV3Verdict extends localize(i18next)(LitElement) {
673
673
  * The trend at its own size: both axes logarithmic, the growing lines against the flat ones, and a
674
674
  * legend with each line's value at the last count.
675
675
  */
676
- private renderSheet(report: V3Report) {
676
+ private renderSheet(report: FigureReport) {
677
677
  if (!this.expanded) return nothing
678
678
  const layout = curveLayout(report.curve)
679
679
  if (!layout) return nothing
@@ -6,8 +6,8 @@ import type { FigureGateFinding } from '../types.js'
6
6
  /**
7
7
  * One release-gate finding as a line on screen — the measured fact, where, why it matters, and what to change.
8
8
  *
9
- * Two places draw these: the release panel (`figure-history-panel`) and the V3 modeller's verdict while editing
10
- * (`figure-v3-verdict`). They show the same findings from the same server door, so they draw them one way; a copy
9
+ * Two places draw these: the release panel (`figure-history-panel`) and the modeller's verdict while editing
10
+ * (`figure-verdict`). They show the same findings from the same server door, so they draw them one way; a copy
11
11
  * in each would drift the first time one of them is touched. The caller passes its own action buttons, because
12
12
  * what can be done about a finding depends on the screen it is shown on.
13
13
  */
@@ -1,13 +1,13 @@
1
- import { v3AttachmentsOf } from '@hatiolab/figure-model'
2
- import type { V3Asset, V3InputChange } from '@hatiolab/figure-model'
1
+ import { figureAttachmentsOf } from '@hatiolab/figure-model'
2
+ import type { FigureAsset, FigureInputChange } from '@hatiolab/figure-model'
3
3
 
4
4
  /**
5
- * What the gizmo may write for a part: move · turn · size, on a V3 asset.
5
+ * What the gizmo may write for a part: move · turn · size, on a 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 placed by
10
+ * `addFigureBox`, `<part>.dimension.*` from the editor's shape palette. A part fastened to another, following an instance size or placed by
11
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
  */
@@ -61,7 +61,7 @@ export function gizmoReachOf(asset: AssetLike, part: string): GizmoReach {
61
61
  !!pose &&
62
62
  pose.op === 'rigid@1' &&
63
63
  !nodes.some(node => node.id === `${part}.motion`) &&
64
- v3AttachmentsOf(asset as unknown as V3Asset)[part] === null
64
+ figureAttachmentsOf(asset as unknown as FigureAsset)[part] === null
65
65
  const posed = direct || reseated
66
66
  const shape = nodes.find(node => node.id === `${part}.shape`)
67
67
  const mine = (ids: string[]) => ids.every(id => id.startsWith(`${part}.`) && design.has(id))
@@ -111,10 +111,10 @@ export interface GizmoResult {
111
111
  * The input changes a drag makes — one list, applied as one step. Millimetres to the millimetre and degrees to a
112
112
  * tenth (a drag otherwise stores 722.4180046035418 mm). A size never goes below 1 mm.
113
113
  */
114
- export function gizmoChanges(part: string, reach: GizmoReach, result: GizmoResult, sizes: Record<string, number>): V3InputChange[] {
115
- const changes: V3InputChange[] = []
116
- const put = (input: string, unit: V3InputChange['unit'], value: number) =>
117
- changes.push({ kind: 'set-design', input, unit, value } as V3InputChange)
114
+ export function gizmoChanges(part: string, reach: GizmoReach, result: GizmoResult, sizes: Record<string, number>): FigureInputChange[] {
115
+ const changes: FigureInputChange[] = []
116
+ const put = (input: string, unit: FigureInputChange['unit'], value: number) =>
117
+ changes.push({ kind: 'set-design', input, unit, value } as FigureInputChange)
118
118
  if (result.position && reach.translate)
119
119
  AXES.forEach((axis, i) => put(`${part}.position.${axis}`, 'mm', Math.round(result.position![i]!)))
120
120
  if (result.rotation && reach.rotate)
@@ -144,7 +144,7 @@ export function gizmoChanges(part: string, reach: GizmoReach, result: GizmoResul
144
144
  * face): the part's own Y position moves by half the change. A post typed 2200 tall sank 933 mm under the floor
145
145
  * (2026-09-26). Only for a part placed by its own numbers and standing unturned; otherwise its height is not up and down.
146
146
  */
147
- export function keepBottomChanges(part: string, reach: GizmoReach, input: string, value: number, values: Record<string, number>): V3InputChange[] {
147
+ export function keepBottomChanges(part: string, reach: GizmoReach, input: string, value: number, values: Record<string, number>): FigureInputChange[] {
148
148
  if (!reach.translate) return []
149
149
  const height = reach.sizes?.[1] ?? reach.ownSizes?.[1] ?? reach.round?.along[0]
150
150
  if (!height || height !== input) return []
@@ -152,5 +152,5 @@ export function keepBottomChanges(part: string, reach: GizmoReach, input: string
152
152
  const before = values[input],
153
153
  y = values[`${part}.position.y`]
154
154
  if (before === undefined || y === undefined || !Number.isFinite(value)) return []
155
- return [{ kind: 'set-design', input: `${part}.position.y`, unit: 'mm', value: Math.round((y + (value - before) / 2) * 10) / 10 } as V3InputChange]
155
+ return [{ kind: 'set-design', input: `${part}.position.y`, unit: 'mm', value: Math.round((y + (value - before) / 2) * 10) / 10 } as FigureInputChange]
156
156
  }
@@ -1,9 +1,9 @@
1
1
  import * as THREE from 'three'
2
- import { compileV3Asset } from '@hatiolab/figure-model'
3
- import type { V3Asset, V3Joint } from '@hatiolab/figure-model'
2
+ import { compileFigureAsset } from '@hatiolab/figure-model'
3
+ import type { FigureAsset, FigureJoint } from '@hatiolab/figure-model'
4
4
 
5
- import type { GizmoResult } from './v3-gizmo.js'
6
- import type { V3RenderPart } from './v3-renderer.js'
5
+ import type { GizmoResult } from './gizmo.js'
6
+ import type { FigureRenderPart } from './renderer.js'
7
7
 
8
8
  /*
9
9
  * A gizmo drag on a part that hangs from a joint edits the joint, not the part (ADR-0093; r3 §3: the modeller changes
@@ -19,12 +19,12 @@ import type { V3RenderPart } from './v3-renderer.js'
19
19
 
20
20
  export type JointDrag =
21
21
  | { kind: 'value'; input: string; unit: 'deg' | 'mm' | 'ratio'; value: number }
22
- | { kind: 'joint'; joint: V3Joint }
22
+ | { kind: 'joint'; joint: FigureJoint }
23
23
  | { kind: 'refused'; reason: 'mimic' | 'fixed-turn' | 'origin-follows' | 'turned-slide' | 'off-axis'; joint: string; leader?: string; axis?: string }
24
24
 
25
25
  type Axis = 'x' | 'y' | 'z'
26
26
  const AXES: Axis[] = ['x', 'y', 'z']
27
- /** The axis each face faces along, and which way (as figure-model's `V3_FACES`). */
27
+ /** The axis each face faces along, and which way (as figure-model's `FIGURE_FACES`). */
28
28
  const FACE_AXIS: Record<string, Axis> = { left: 'x', right: 'x', bottom: 'y', top: 'y', back: 'z', front: 'z' }
29
29
  const FACE_SIGN: Record<string, number> = { left: -1, right: 1, bottom: -1, top: 1, back: -1, front: 1 }
30
30
 
@@ -34,17 +34,17 @@ const unit = (axis: string) => new THREE.Vector3(/x/i.test(axis) ? 1 : 0, /y/i.t
34
34
  const round = (v: number, step: number) => Number((Math.round(v / step) * step).toFixed(Math.max(0, -Math.floor(Math.log10(step)))))
35
35
 
36
36
  /** The part's own quarter turn in the joint frame, as the record says it. */
37
- const turnMatrix = (j: V3Joint) => eulerMatrix(AXES.map(a => j.origin?.turn?.[a] ?? 0))
37
+ const turnMatrix = (j: FigureJoint) => eulerMatrix(AXES.map(a => j.origin?.turn?.[a] ?? 0))
38
38
 
39
39
  /** The joint frame's rotation in the figure: the part's rotation now, less its own turn and less the joint's. */
40
- function jointFrameRotation(j: V3Joint, part: V3RenderPart, angle: number): THREE.Matrix4 {
40
+ function jointFrameRotation(j: FigureJoint, part: FigureRenderPart, angle: number): THREE.Matrix4 {
41
41
  const now = matrixOf(part.pose.r as number[][])
42
42
  const own = turnMatrix(j)
43
43
  const turn = j.type === 'revolute' ? new THREE.Matrix4().makeRotationAxis(unit(j.axis!), THREE.MathUtils.degToRad(angle)) : new THREE.Matrix4()
44
44
  return now.multiply(own.invert()).multiply(turn.invert())
45
45
  }
46
46
 
47
- export function jointDragOf(asset: V3Asset, id: string, part: V3RenderPart, result: GizmoResult, values: Record<string, number>): JointDrag | null {
47
+ export function jointDragOf(asset: FigureAsset, id: string, part: FigureRenderPart, result: GizmoResult, values: Record<string, number>): JointDrag | null {
48
48
  const j = asset.joints?.find(joint => joint.body1.part === id)
49
49
  /* A size is the part's own, not the joint's: a scale drag goes to the part's measures (the editor drops the shift). */
50
50
  if (!j || result.scale || (!result.rotation && !result.position)) return null
@@ -81,7 +81,7 @@ export function jointDragOf(asset: V3Asset, id: string, part: V3RenderPart, resu
81
81
  /* How far one unit of the value moves the part along its axis, measured on the figure itself (a ratio travels a length). */
82
82
  const along = unit(j.axis!).applyMatrix4(frame).normalize()
83
83
  const at = (v: number) => {
84
- const g = compileV3Asset(asset).evaluate({ ...values, [stateId]: v }).geometry.find(g => g.id === id && 'pose' in g) as { pose: { t: number[] } } | undefined
84
+ const g = compileFigureAsset(asset).evaluate({ ...values, [stateId]: v }).geometry.find(g => g.id === id && 'pose' in g) as { pose: { t: number[] } } | undefined
85
85
  return g ? new THREE.Vector3(...(g.pose.t as [number, number, number])).dot(along) : 0
86
86
  }
87
87
  const step = j.travel ? 0.01 : 1
@@ -97,7 +97,7 @@ export function jointDragOf(asset: V3Asset, id: string, part: V3RenderPart, resu
97
97
  const normal: Axis = 'plane' in j.body0 ? 'y' : FACE_AXIS[j.body0.face]!
98
98
  /* Which way a larger gap moves the part: out of the face it meets (on the plane: up when it stands, down when it hangs). */
99
99
  const outward = 'plane' in j.body0 ? (j.body1.face === 'bottom' ? 1 : -1) : ((j.origin?.facing ?? 'meet') === 'meet' ? 1 : -1) * FACE_SIGN[j.body0.face]!
100
- const origin: NonNullable<V3Joint['origin']> = { ...(j.origin ?? {}) }
100
+ const origin: NonNullable<FigureJoint['origin']> = { ...(j.origin ?? {}) }
101
101
  const along = local[normal]
102
102
  if (Math.abs(along) >= 0.5) {
103
103
  const gap = origin.gap ?? 0
@@ -1,7 +1,7 @@
1
- import { compileV3Asset, compileV3Graph, v3WorldBoxesOf, v3WritersOf } from '@hatiolab/figure-model'
2
- import type { V3Asset } from '@hatiolab/figure-model'
1
+ import { compileFigureAsset, compileFigureGraph, figureWorldBoxesOf, figureWritersOf } from '@hatiolab/figure-model'
2
+ import type { FigureAsset } from '@hatiolab/figure-model'
3
3
 
4
- import type { V3RenderPart } from './v3-renderer.js'
4
+ import type { FigureRenderPart } from './renderer.js'
5
5
 
6
6
  /*
7
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
@@ -68,7 +68,7 @@ interface Turn {
68
68
  control: { id: string; min: number; max: number }
69
69
  }
70
70
 
71
- function turnOf(asset: V3Asset, part: string, values: Record<string, unknown>, box: WorldBox | undefined): Turn | null {
71
+ function turnOf(asset: FigureAsset, part: string, values: Record<string, unknown>, box: WorldBox | undefined): Turn | null {
72
72
  const m = asset.document.model as unknown as { nodes: Node[]; constants: { id: string; value: number }[]; inputs: { id: string; role?: string; min?: number; max?: number }[] }
73
73
  const node = (id: string) => m.nodes.find(n => n.id === id)
74
74
  const constant = (ref: string) => m.constants.find(c => c.id === ref)?.value
@@ -183,11 +183,11 @@ function turnOf(asset: V3Asset, part: string, values: Record<string, unknown>, b
183
183
  * The fan for the part, as a body to stand on the board, or null when the part does not turn. `values` are the
184
184
  * inputs the figure is drawn with now (a pose in hand included), so the fan follows the arm as it is posed.
185
185
  */
186
- export function jointFanOf(asset: V3Asset, part: string, values: Record<string, number> = {}): V3RenderPart | null {
186
+ export function jointFanOf(asset: FigureAsset, part: string, values: Record<string, number> = {}): FigureRenderPart | null {
187
187
  if (!part) return null
188
- let graph: ReturnType<typeof compileV3Graph>
188
+ let graph: ReturnType<typeof compileFigureGraph>
189
189
  try {
190
- graph = compileV3Graph(asset.document.model as never)
190
+ graph = compileFigureGraph(asset.document.model as never)
191
191
  } catch {
192
192
  return null
193
193
  }
@@ -201,11 +201,11 @@ export function jointFanOf(asset: V3Asset, part: string, values: Record<string,
201
201
  }
202
202
  let box: WorldBox | undefined
203
203
  try {
204
- box = v3WorldBoxesOf(
205
- compileV3Asset({ ...asset, designInputs: { ...asset.designInputs, ...pick(values, asset.designInputs) }, stateDefaults: { ...asset.stateDefaults, ...pick(values, asset.stateDefaults) } })
204
+ box = figureWorldBoxesOf(
205
+ compileFigureAsset({ ...asset, designInputs: { ...asset.designInputs, ...pick(values, asset.designInputs) }, stateDefaults: { ...asset.stateDefaults, ...pick(values, asset.stateDefaults) } })
206
206
  .evaluate()
207
207
  .geometry.filter(g => g.id === part),
208
- v3WritersOf(asset)
208
+ figureWritersOf(asset)
209
209
  )[0]
210
210
  } catch {
211
211
  box = undefined
@@ -261,7 +261,7 @@ export function jointFanOf(asset: V3Asset, part: string, values: Record<string,
261
261
  material: { token: '__joint-fan', transparent: true },
262
262
  /** Which control turns it: the joint the fan shows. */
263
263
  turns: turn.control.id
264
- } as unknown as V3RenderPart
264
+ } as unknown as FigureRenderPart
265
265
  }
266
266
 
267
267
  /** The values in hand that belong to a record's keys (design inputs, or state defaults). */
@@ -270,9 +270,9 @@ function pick(values: Record<string, number>, of: Record<string, number>): Recor
270
270
  }
271
271
 
272
272
  /** A part's largest own measure (a box side, a diameter, a length) at the inputs given; 0 when it cannot be read. */
273
- function largestMeasureOf(asset: V3Asset, part: string, inputs: Record<string, number>): number {
273
+ function largestMeasureOf(asset: FigureAsset, part: string, inputs: Record<string, number>): number {
274
274
  try {
275
- const body = compileV3Asset({ ...asset, designInputs: pick(inputs, asset.designInputs), stateDefaults: pick(inputs, asset.stateDefaults) })
275
+ const body = compileFigureAsset({ ...asset, designInputs: pick(inputs, asset.designInputs), stateDefaults: pick(inputs, asset.stateDefaults) })
276
276
  .evaluate()
277
277
  .geometry.find(g => g.id === part) as { shape?: { dimensions?: Record<string, number> } } | undefined
278
278
  const dims = Object.entries(body?.shape?.dimensions ?? {}).map(([k, v]) => (k.startsWith('radius') ? v * 2 : v))