@hatiolab/figure-model 0.1.75 → 0.1.77

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 (141) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +60 -138
  3. package/dist/index.d.ts +2 -13
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +4 -26
  6. package/dist/index.js.map +1 -1
  7. package/dist/v3-asset-types.d.ts +1 -2
  8. package/dist/v3-asset-types.d.ts.map +1 -1
  9. package/dist/v3-asset.js +1 -1
  10. package/dist/v3-asset.js.map +1 -1
  11. package/dist/v3-authoring-actions.d.ts +9 -12
  12. package/dist/v3-authoring-actions.d.ts.map +1 -1
  13. package/dist/v3-authoring-actions.js +4 -5
  14. package/dist/v3-authoring-actions.js.map +1 -1
  15. package/dist/v3-capabilities.js +4 -1
  16. package/dist/v3-capabilities.js.map +1 -1
  17. package/dist/v3-cost.d.ts.map +1 -1
  18. package/dist/v3-cost.js +3 -5
  19. package/dist/v3-cost.js.map +1 -1
  20. package/dist/v3-create.d.ts +1 -1
  21. package/dist/v3-create.d.ts.map +1 -1
  22. package/dist/v3-editor-workspace.d.ts +38 -0
  23. package/dist/v3-editor-workspace.d.ts.map +1 -1
  24. package/dist/v3-editor-workspace.js +14 -0
  25. package/dist/v3-editor-workspace.js.map +1 -1
  26. package/dist/v3-examples.js +1 -1
  27. package/dist/v3-gate.d.ts +7 -8
  28. package/dist/v3-gate.d.ts.map +1 -1
  29. package/dist/v3-gate.js +11 -14
  30. package/dist/v3-gate.js.map +1 -1
  31. package/dist/v3-graph.d.ts +1 -2
  32. package/dist/v3-graph.d.ts.map +1 -1
  33. package/dist/v3-graph.js +5 -7
  34. package/dist/v3-graph.js.map +1 -1
  35. package/dist/v3-instance-size.d.ts +3 -5
  36. package/dist/v3-instance-size.d.ts.map +1 -1
  37. package/dist/v3-instance-size.js +2 -4
  38. package/dist/v3-instance-size.js.map +1 -1
  39. package/dist/v3-kernel-version.d.ts +1 -1
  40. package/dist/v3-kernel-version.js +1 -1
  41. package/dist/v3-part-edit.d.ts +4 -5
  42. package/dist/v3-part-edit.d.ts.map +1 -1
  43. package/dist/v3-part-edit.js +2 -2
  44. package/dist/v3-part-edit.js.map +1 -1
  45. package/dist/v3-proposal.d.ts +42 -0
  46. package/dist/v3-proposal.d.ts.map +1 -0
  47. package/dist/v3-proposal.js +179 -0
  48. package/dist/v3-proposal.js.map +1 -0
  49. package/dist/v3-release-contract.js +1 -1
  50. package/dist/v3-release-contract.js.map +1 -1
  51. package/dist/v3-score.d.ts +4 -5
  52. package/dist/v3-score.d.ts.map +1 -1
  53. package/dist/v3-score.js +7 -8
  54. package/dist/v3-score.js.map +1 -1
  55. package/dist/v3-words.d.ts +87 -0
  56. package/dist/v3-words.d.ts.map +1 -0
  57. package/dist/v3-words.js +87 -0
  58. package/dist/v3-words.js.map +1 -0
  59. package/docs/assembly-constraints-v3.md +13 -23
  60. package/docs/v3-asset-persistence.md +4 -4
  61. package/docs/v3-common-kernel-experiment.md +1 -1
  62. package/docs/v3-core-semantics.md +3 -3
  63. package/docs/v3-cutover.md +5 -3
  64. package/docs/v3-design.md +13 -26
  65. package/docs/v3-editor-integration.md +2 -2
  66. package/docs/v3-full-conveyor-validation.md +3 -3
  67. package/docs/v3-layout-system.md +1 -1
  68. package/docs/v3-minimal-model.md +0 -1
  69. package/docs/v3-motion-contract.md +21 -56
  70. package/docs/v3-runtime-extraction.md +7 -7
  71. package/docs/v3-sample-validation-expanded.md +1 -1
  72. package/docs/v3-sample-validation.md +1 -1
  73. package/docs/v3-shape-dimension-contract.md +23 -57
  74. package/docs/v3-status.md +5 -5
  75. package/docs/v3-storage-integration.md +4 -4
  76. package/package.json +2 -20
  77. package/dist/blueprint-shape.d.ts +0 -10
  78. package/dist/blueprint-shape.d.ts.map +0 -1
  79. package/dist/blueprint-shape.js +0 -194
  80. package/dist/blueprint-shape.js.map +0 -1
  81. package/dist/blueprint.d.ts +0 -73
  82. package/dist/blueprint.d.ts.map +0 -1
  83. package/dist/blueprint.js +0 -388
  84. package/dist/blueprint.js.map +0 -1
  85. package/dist/cost-chart.d.ts +0 -35
  86. package/dist/cost-chart.d.ts.map +0 -1
  87. package/dist/cost-chart.js +0 -150
  88. package/dist/cost-chart.js.map +0 -1
  89. package/dist/cost.d.ts +0 -162
  90. package/dist/cost.d.ts.map +0 -1
  91. package/dist/cost.js +0 -194
  92. package/dist/cost.js.map +0 -1
  93. package/dist/gate.d.ts +0 -65
  94. package/dist/gate.d.ts.map +0 -1
  95. package/dist/gate.js +0 -284
  96. package/dist/gate.js.map +0 -1
  97. package/dist/grouping.d.ts +0 -121
  98. package/dist/grouping.d.ts.map +0 -1
  99. package/dist/grouping.js +0 -224
  100. package/dist/grouping.js.map +0 -1
  101. package/dist/keys.d.ts +0 -45
  102. package/dist/keys.d.ts.map +0 -1
  103. package/dist/keys.js +0 -84
  104. package/dist/keys.js.map +0 -1
  105. package/dist/origin.d.ts +0 -33
  106. package/dist/origin.d.ts.map +0 -1
  107. package/dist/origin.js +0 -46
  108. package/dist/origin.js.map +0 -1
  109. package/dist/release-contract.d.ts +0 -38
  110. package/dist/release-contract.d.ts.map +0 -1
  111. package/dist/release-contract.js +0 -246
  112. package/dist/release-contract.js.map +0 -1
  113. package/dist/sizing.d.ts +0 -231
  114. package/dist/sizing.d.ts.map +0 -1
  115. package/dist/sizing.js +0 -550
  116. package/dist/sizing.js.map +0 -1
  117. package/dist/types.d.ts +0 -1233
  118. package/dist/types.d.ts.map +0 -1
  119. package/dist/types.js +0 -547
  120. package/dist/types.js.map +0 -1
  121. package/dist/v3-from-v2.d.ts +0 -203
  122. package/dist/v3-from-v2.d.ts.map +0 -1
  123. package/dist/v3-from-v2.js +0 -2053
  124. package/dist/v3-from-v2.js.map +0 -1
  125. package/dist/v3-mesh-compare.d.ts +0 -24
  126. package/dist/v3-mesh-compare.d.ts.map +0 -1
  127. package/dist/v3-mesh-compare.js +0 -55
  128. package/dist/v3-mesh-compare.js.map +0 -1
  129. package/dist/validate.d.ts +0 -7
  130. package/dist/validate.d.ts.map +0 -1
  131. package/dist/validate.js +0 -1119
  132. package/dist/validate.js.map +0 -1
  133. package/dist/visual-evidence.d.ts +0 -21
  134. package/dist/visual-evidence.d.ts.map +0 -1
  135. package/dist/visual-evidence.js +0 -52
  136. package/dist/visual-evidence.js.map +0 -1
  137. package/docs/design.md +0 -193
  138. package/docs/format-survey.md +0 -108
  139. package/docs/format.md +0 -704
  140. package/docs/sizing.md +0 -183
  141. package/docs/validation.md +0 -357
@@ -1,15 +1,14 @@
1
1
  # V3 shape-dimension contract — per-axis radii, elliptic sections, and dimensions that follow state
2
2
 
3
3
  Status: **contract, fixed 2026-09-22 by the V3 designer** (ADR-0087, stage-review rulings 3 and 5). §1 (per-axis
4
- dimensions) implemented the same day: `cylinder-shape@2`, `rounded-box@2`, `hollow-box@2`, the converter writing
5
- them, the sampler reading them. §2–3 implemented the same night: dependence on state is tracked per value in the
4
+ dimensions) implemented the same day: `cylinder-shape@2`, `rounded-box@2`, `hollow-box@2`, and the sampler reading
5
+ them. §2–3 implemented the same night: dependence on state is tracked per value in the
6
6
  compiler and printed as `dependence` beside `outputTypes`; every operator carries a `state` policy (`follows` or
7
7
  `refuses`; an operator without one fails with `STATE_POLICY`); `occupancy.extent` may not depend on state
8
8
  (`OCCUPANCY_STATE`); `inspectV3Asset` covers the state range and reports `proven` (corners; no state-dependent
9
9
  rotation), `sampled` (a rotation depends on state; corners plus per-input sweeps, step stated, not a proof) or
10
10
  `defaults` (one state, as asked). The `bounded` method of §3 (swept envelope) is not implemented; such assets report
11
- `sampled`. The converter writes a two-key scale channel as dimensions × affine(parameter) for percent or ratio
12
- parameters (`STATE_DIMENSION` note). This is one rule for what a shape's dimensions are and what they may depend on. It is not three
11
+ `sampled`. This is one rule for what a shape's dimensions are and what they may depend on. It is not three
13
12
  exceptions bolted on for the three assets that needed them.
14
13
 
15
14
  ## What a person sees
@@ -19,11 +18,11 @@ cradle gets thinner: its radius shrinks, its length does not. The edge plates th
19
18
  stay on its surface. Nothing else moves. At 0 % the roll is exactly the drawn roll.
20
19
 
21
20
  An author stretches a kiosk to twice its width. The bezel's corners stay the same round in front view where the
22
- section did not stretch, and go wider than they are tall where it did, because V2 drew them that way. Nothing pops
23
- back to a circle on its own.
21
+ section did not stretch, and go wider than they are tall where it did, because each corner radius follows its own
22
+ axis. Nothing pops back to a circle on its own.
24
23
 
25
24
  An author opens the worker figure and stretches it sideways. The legs, which the author chose not to keep round,
26
- become oval like V2 drew them. The renderer draws the oval it is given. It never "fixes" it to a circle.
25
+ become oval. The renderer draws the oval it is given. It never "fixes" it to a circle.
27
26
 
28
27
  ## 1. Every round thing has per-axis dimensions
29
28
 
@@ -32,7 +31,7 @@ A section is round only when its two cross dimensions are equal. The providers w
32
31
  | operator | evaluated provider | dimensions | round when | status |
33
32
  |---|---|---|---|---|
34
33
  | `cylinder-shape@2` | `ellipticCylinder` | `radiusX`, `radiusZ`, `length` (axis Y) | `radiusX = radiusZ` | implemented 2026-09-22 |
35
- | `frustum-shape@2` | — | `topRadiusX`, `topRadiusZ`, `bottomRadiusX`, `bottomRadiusZ`, `height` | pairs equal | not implemented; no V2 shape needs it (V2 draws no frustum) |
34
+ | `frustum-shape@2` | — | `topRadiusX`, `topRadiusZ`, `bottomRadiusX`, `bottomRadiusZ`, `height` | pairs equal | not implemented |
36
35
  | `sphere-shape@1` | `sphere` | `radiusX`, `radiusY`, `radiusZ` (already so) | all equal | implemented |
37
36
  | `rounded-box@2` | `roundedBoxXZ` | `width`, `height`, `depth`, `roundX`, `roundZ` | `roundX = roundZ` | implemented 2026-09-22 |
38
37
  | `hollow-box@2` | `hollowBoxXZ` | as `hollow-box@1` with `roundX`, `roundZ` in place of `round` | same | implemented 2026-09-22 |
@@ -41,38 +40,32 @@ The evaluated provider name is distinct from the `@1` one so a renderer can neve
41
40
  single radius. Features on `ellipticCylinder` are `start` and `end` only; `radialBottom` has no single meaning on an
42
41
  ellipse.
43
42
 
44
- - `@1` of each provider stays valid and means the equal case; the converter writes `@2` only where V2 drew an
45
- unequal section. Nothing that exists today is invalidated.
43
+ - `@1` of each provider stays valid and means the equal case. `@2` is needed only for an unequal section.
46
44
  - **The renderer draws what it is given.** An elliptic section is an ellipse; an elliptic corner is the quadratic
47
45
  Bézier from `(hw − roundX, −hd)` to `(hw, −hd + roundZ)` with the corner `(hw, −hd)` as control point, which is
48
- what things-scene's `roundedRect` draws and what `src/v3-mesh-compare.ts` samples. It is not a circular or
49
- elliptic arc: an arc differs from the Bézier by up to 0.06 × round at the corner's middle. A renderer that snaps a
50
- section to a circle is wrong, and the mesh-level comparison of the completion criterion is what catches it.
46
+ what things-scene's `roundedRect` draws. It is not a circular or elliptic arc: an arc differs from the Bézier by
47
+ up to 0.06 × round at the corner's middle. A renderer that snaps a section to a circle is wrong.
51
48
  - Validity: every dimension positive (a corner radius may be 0), `roundX ≤ width/2`, `roundZ ≤ depth/2`. A
52
- declared corner larger than the section is refused at compile; the converter normalises V2's declared value to
53
- what V2 drew (ruling 2) and reports it.
49
+ declared corner larger than the section is refused at compile.
54
50
 
55
- ### What `keepRound` becomes
51
+ ### A round section under non-uniform sizing
56
52
 
57
- V2's `keepRound` was a rule about **what happens to a round section under non-uniform sizing**: keep it round by
58
- the geometric mean of the two factors. In V3 that is not a flag; it is two possible expressions for two dimensions:
53
+ What happens to a round section when the instance is sized differently along its two cross axes is not a flag. It
54
+ is two possible expressions for two dimensions:
59
55
 
60
56
  ```
61
- kept round radiusX = radiusZ = r₀ · geomean(fx, fz) (V2 keepRound on, the default)
62
- follows axes radiusX = r₀ · fx, radiusZ = r₀ · fz (V2 keepRound off)
57
+ kept round radiusX = radiusZ = r₀ · geomean(fx, fz)
58
+ follows axes radiusX = r₀ · fx, radiusZ = r₀ · fz
63
59
  ```
64
60
 
65
- Both are ordinary length expressions. The converter chooses by the V2 flag and writes the expression; the
66
- `ELLIPTIC_SECTION` refusal is now a note on the report (implemented 2026-09-22), and `CORNER_PER_AXIS` notes a
67
- corner written with two semi-axes. The same two shapes apply to corners: V2 scaled a rounded box's finished mesh
68
- per axis, so a corner drawn with radius `r₀` becomes `roundX = r₀·fx`, `roundZ = r₀·fz`. That is what
69
- `ROUND_NOT_SCALED` (40 parts in the last run) was reporting as a loss; with `@2` corners it is no longer a loss.
61
+ Both are ordinary length expressions; the author chooses which one to write. The same two shapes apply to corners:
62
+ a corner drawn with radius `r₀` that follows the axes is `roundX = r₀·fx`, `roundZ = r₀·fz`.
70
63
 
71
64
  ## 2. A dimension may depend on state
72
65
 
73
66
  A state input (`role: 'state'`) may be an argument of a shape or length node. A consumed roll's radius is
74
67
  `r₀ − fed · k`; a lamp's glow radius grows with the gate. This is the core-semantics §2 invariant ("state changes the
75
- actual shape dimension") made concrete, and it is what a V2 `scale` channel means: `dims × affine(p)`.
68
+ actual shape dimension") made concrete. A dimension that scales with a state input is `dims × affine(p)`.
76
69
 
77
70
  ### Dependence is tracked in the type, apart from unit and frame
78
71
 
@@ -114,30 +107,14 @@ range, and says how it knows:
114
107
  - The endpoint-only shortcut is **wrong** for a rotating long part: inside at both end angles, out in the middle.
115
108
  That case takes the `bounded` row, not the `proven` one.
116
109
 
117
- ## 4. What the converter writes with this contract
118
-
119
- - V2 cylinder with `keepRound: false` and differing cross factors → `cylinder-shape@2` with `radiusX`, `radiusZ`
120
- following their own axes. WORKER converts; `ELLIPTIC_SECTION` is retired.
121
- - V2 rect or hollow rect whose section follows the instance → `rounded-box@2` / `hollow-box@2` with per-axis corner
122
- radii `r·fx`, `r·fz`. `ROUND_NOT_SCALED` is retired.
123
- - V2 `scale` channel on a part, two linear keys, from a parameter → each scaled dimension becomes
124
- `dim × affine(p)`; the part's centre is unchanged (V2 scaled about the part centre). The dependence set of every
125
- affected length includes the parameter. DRY_ROOM's lamps and ROLL_CRADLE's roll convert; `ANIMATION_CHANNEL`
126
- stays only for channels the contract does not cover (pivots, compound axes, curves other than two linear keys).
127
- - The report marks every state-dependent dimension it wrote, and the occupancy inspection of a converted asset
128
- runs over the parameter ranges as §3 says.
129
-
130
- ## 5. Completion criterion for this contract
110
+ ## 4. Completion criterion for this contract
131
111
 
132
112
  - The four providers' `@2` forms compile, evaluate, and their `@1` forms are unchanged (the operator-list guard
133
113
  gains the four names).
134
114
  - Dependence appears in `outputTypes`; a synthetic new operator with no policy fails to compile when handed a
135
115
  state-dependent value; `fit-pitch@1` and `fixed-count@1` still refuse it; a shape provider accepts it.
136
- - The three assets convert and match V2 at box level at 8 sizes × states, and the WORKER's legs match as ovals.
137
116
  - An occupancy check on the roll cradle reports `proven` over the `fed` range, and a synthetic rotating long part
138
117
  reports `bounded`, with a violation found at a middle angle that the endpoints alone would have missed.
139
- - The mesh-level comparison of the completion criterion covers sections and corners, so an elliptic section drawn
140
- round would fail it.
141
118
 
142
119
  ## Not in this contract
143
120
 
@@ -145,10 +122,9 @@ range, and says how it knows:
145
122
  - Shape deformation beyond dimensions: bending, twisting, free-form profiles that change with state. Those would be
146
123
  new providers with their own contracts.
147
124
 
148
- ## 4. 표시 여부 (visibility) — 안 보이는 것과 작은 것은 다르다
125
+ ## 5. 표시 여부 (visibility) — 안 보이는 것과 작은 것은 다르다
149
126
 
150
- **V3 설계자 결정 2026-09-22.** V2 는 부품을 숨길 때 크기를 0 으로 만든다. V3 는 그 방법을 쓰지
151
- 않는다. 0 을 아주 작은 양수로 바꾸는 것도 쓰지 않는다 — 확대·그림자·포인터 선택에서 차이가
127
+ **V3 설계자 결정 2026-09-22.** 부품을 숨기려고 크기를 0 으로 만들지 않는다. 0 을 아주 작은 양수로 바꾸는 것도 쓰지 않는다 — 확대·그림자·포인터 선택에서 차이가
152
128
  생기고, 「안 보임」이 「아주 작음」이 되어 버린다.
153
129
 
154
130
  ### 규칙
@@ -172,19 +148,9 @@ range, and says how it knows:
172
148
 
173
149
  | 목록 | 무엇이 들어가는가 | 누가 쓰는가 |
174
150
  | --- | --- | --- |
175
- | `v3WorldBoxesOf` | 그리는 몸통만 (`visible === false` 제외) | renderer, V2 비교 |
151
+ | `v3WorldBoxesOf` | 그리는 몸통만 (`visible === false` 제외) | renderer |
176
152
  | `v3OccupiedBoxesOf` | 놓인 몸통 전부 (숨긴 것 포함) | occupancy 검사 |
177
153
 
178
- ### 변환기가 V2 의 scale 0 을 옮기는 방법
179
-
180
- 채널이 **모든 변하는 축에서 0 으로 시작하고, 처음 0 이 아닌 key 부터 끝까지 정확히 1** 이면
181
- (= 부품 제 크기) visibility 로 옮긴다. 부품은 제 치수를 계속 갖고, flag 가 그 key 의 `at` 에서
182
- 켜진다. 그 외 — 숨기면서 크기도 바꾸는 채널 — 은 `SCALE_ZERO` 로 거절한다.
183
-
184
- 이것은 **의도적 재저작이다**(`method: 're-authored'`, `RE_AUTHORED_VISIBILITY` note). 0 과
185
- 문턱 사이에서 V2 는 자라나는 부품을 그리고 V3 는 안 그린다. DRY_ROOM 에서 실측했다: 문턱은
186
- 파라미터 범위의 1 %, 그 밖 모든 상태에서 V2 와 같고 차이는 그 1 % 안에서만 난다.
187
-
188
154
  ## When `proven` may be said (added 2026-09-23, after two counterexamples from the V3 designer)
189
155
 
190
156
  `proven` means the gate evaluated the corners of the state box and nothing else. That is only a proof when every
package/docs/v3-status.md CHANGED
@@ -2,14 +2,14 @@
2
2
 
3
3
  > **2026-09-22 — this file is a progress note, not the document of record.** The decision and the order of work are
4
4
  > `operato-application/adr/ADR-0087.md`; the current state is the app's
5
- > `operato-application/packages/operato-figure/docs/CURRENT_STATUS.md` (section "figure-model V2 → V3"). Where this
5
+ > `operato-application/packages/operato-figure/docs/CURRENT_STATUS.md`. Where this
6
6
  > file disagrees with those two, they win.
7
7
 
8
8
  기준일: 2026-09-21. **현재 상태를 판단하는 문서다.** 개별 연구·검증 문서의 과거 단계 수치나 '다음 단계'보다 이 현황을 우선한다.
9
9
 
10
10
  ## 제품 방향
11
11
 
12
- 모델러·AI·저장·렌더러를 **V3 하나로 전환**한다. V2 호환성·병행 운영은 요구사항이 아니다. 기존 자산은 V3로 변환하거나 재작성한다. 원본 복구본은 데이터 교체를 위한 안전 조치일 뿐 V2 실행 지원을 뜻하지 않는다. [전환 결정](v3-cutover.md)
12
+ 모델러·AI·저장·렌더러는 **V3(`figure-v3-asset-1`) 하나**를 쓴다. V2와 V1은 2026-09-26 코드와 명세에서 제거했다(ADR-0087 전환을 제거로 마쳤다). [전환 결정](v3-cutover.md)
13
13
 
14
14
  ## 완료 범위와 미완료 범위
15
15
 
@@ -27,7 +27,7 @@
27
27
 
28
28
  ## 검증 근거
29
29
 
30
- - `npm test`: 소스 **455개 통과**. 기존 검사와 신규 V3 검사를 합친 수치이며 V3 기능 수가 아니다.
30
+ - `node --test src/*.test.ts`: 소스 **286개 통과**(2026-09-26, V2·V1 제거 뒤). 시험 수이며 V3 기능 수가 아니다.
31
31
  - `node --test docs/prototypes/v3-*.test.mjs`: 실험 **143개 통과**.
32
32
  - `npm run build`: 타입 선언·JS 생성 성공.
33
33
  - `node docs/prototypes/v3-runtime-browser.mjs`: Chrome에서 빌드된 모듈 실행, 네 대표 모델(드라이룸·컨베이어·로봇암·포크리프트)의 계산값/형상/재질/binding을 Node와 비교.
@@ -43,8 +43,8 @@
43
43
  4. [자산 계약](v3-asset-persistence.md), [저장 검증](v3-storage-integration.md).
44
44
  5. [V3 단일 전환](v3-cutover.md).
45
45
 
46
- 다음 작업은 모델러의 사용자 동작·AI·실제 저장·렌더링을 이 공용 V3 구현에 연결하는 것이다. 완료한 계층을 다시 이관하거나 V2 호환 분기를 추가하는 것이 아니다.
46
+ 다음 작업은 모델러의 사용자 동작·AI·실제 저장·렌더링을 이 공용 V3 구현에 연결하는 것이다.
47
47
 
48
48
  ## 제품 연결 작업 추가 기록 (2026-09-21)
49
49
 
50
- things-factory의 `packages/figure-ui/docs/v3-product-cutover.md`에 제품 전환 진행 상태를 기록했다. V3 제품 세션·캔버스·편집 컴포넌트·저장 API와 페이지를 작성했으나, 기본 route는 아직 교체하지 않았다. 로컬 활성 자산32개가 모두 V2이며 자산 전환·AI 도구·발행/소비처 연결이 남아 있다. UI241개 테스트 및 클라이언트/서비스 타입 검사 통과는 V3 제품 end-to-end 검증 완료를 뜻하지 않는다. 운영 DB 데이터 변경이나 배포는 하지 않았다.
50
+ things-factory의 `packages/figure-ui/docs/v3-product-cutover.md`에 제품 전환 진행 상태를 기록했다. V3 제품 세션·캔버스·편집 컴포넌트·저장 API와 페이지를 작성했으나, 기본 route는 아직 교체하지 않았다. 당시 로컬 활성 자산 32개는 아직 V3로 전환되지 않았고, 자산 전환·AI 도구·발행/소비처 연결이 남아 있었다. UI241개 테스트 및 클라이언트/서비스 타입 검사 통과는 V3 제품 end-to-end 검증 완료를 뜻하지 않는다. 운영 DB 데이터 변경이나 배포는 하지 않았다.
@@ -12,9 +12,9 @@
12
12
 
13
13
  ## 왜 기존 저장 API에 바로 연결하지 않았는가
14
14
 
15
- 제품 figure-service의 `figure-source.ts`는 V2 validate/compile/costOf/scoreOf를 호출한다. `figure-mutation.ts`의 생성·수정·되돌리기는 이 검증을 사용하며 발행은 V2 검사 경로를 사용한다. figure-ui도 draft/parts를 직접 편집한다.
15
+ 당시 제품 figure-service의 `figure-source.ts`는 이전 형식의 검증·컴파일·비용·점수 함수를 호출했다. `figure-mutation.ts`의 생성·수정·되돌리기와 발행도 그 검사 경로를 사용했다. figure-ui도 draft/parts를 직접 편집한다.
16
16
 
17
- 실험 포맷만 허용하면 저장은 되지만 기존 소비자가 읽을 수 없는 자산이 된다. 따라서 **제품 V3 경로 연결은 아직 미완료**다. 실험 코드의 직접 import, package.json 변경, V2 검증 우회는 하지 않았다.
17
+ 실험 포맷만 허용하면 저장은 되지만 기존 소비자가 읽을 수 없는 자산이 된다. 따라서 **제품 V3 경로 연결은 아직 미완료**다. 실험 코드의 직접 import, package.json 변경, 기존 검증 우회는 하지 않았다.
18
18
 
19
19
  ## 저장 계약
20
20
 
@@ -51,8 +51,8 @@ scope/id 조합을 해시한 파일명으로 사용해 사용자 식별자를
51
51
  ## 제품 연결 순서
52
52
 
53
53
  1. 입력·그래프·capability·문서·자산·편집 workspace의 정식 소스 이관은 [완료](v3-runtime-extraction.md)했다. 이 모듈들을 제품에 반영한다.
54
- 2. figure-service의 저장·조회·발행을 V3 단일 기준으로 전환하고 저장 revision CAS를 구현한다. V2 병행 운영은 요구사항이 아니다.
55
- 3. figure-ui와 렌더러·AI를 V3 단일 경로에 연결한다. 기존 자산은 V3로 변환하거나 재작성한다.
54
+ 2. figure-service의 저장·조회·발행을 V3 단일 기준으로 전환하고 저장 revision CAS를 구현한다.
55
+ 3. figure-ui와 렌더러·AI를 V3 단일 경로에 연결한다. 기존 자산은 V3로 재작성한다.
56
56
  4. 실제 인증·DB로 두 편집 세션의 충돌, 저장 실패, 재접속, 모델 전환, AI 늦은 응답을 검증한다.
57
57
 
58
58
  이 문서의 완료 범위는 저장 경계의 실행 가능한 검증이며, 제품 연결·배포 완료가 아니다.
package/package.json CHANGED
@@ -1,12 +1,11 @@
1
1
  {
2
2
  "name": "@hatiolab/figure-model",
3
- "version": "0.1.75",
4
- "description": "Figure 저작 결과의 정본 형식과 검증 — 재질별로 병합되고 이름이 붙은 부품 그래프. 3D 로 저작한 형상 하나가 씬 컴포넌트로 서는 데 필요한 것을 담는다.",
3
+ "version": "0.1.77",
4
+ "description": "The figure format (figure-v3-asset-1): a kernel that evaluates a figure from its declared relations, the authoring commands a modeller and an AI share, the release gate and the score.",
5
5
  "keywords": [
6
6
  "figure",
7
7
  "low-poly",
8
8
  "3d",
9
- "blueprint",
10
9
  "authoring",
11
10
  "things-scene",
12
11
  "digital-twin",
@@ -31,22 +30,6 @@
31
30
  "types": "./dist/index.d.ts",
32
31
  "default": "./dist/index.js"
33
32
  },
34
- "./types": {
35
- "types": "./dist/types.d.ts",
36
- "default": "./dist/types.js"
37
- },
38
- "./validate": {
39
- "types": "./dist/validate.d.ts",
40
- "default": "./dist/validate.js"
41
- },
42
- "./blueprint": {
43
- "types": "./dist/blueprint.d.ts",
44
- "default": "./dist/blueprint.js"
45
- },
46
- "./cost": {
47
- "types": "./dist/cost.d.ts",
48
- "default": "./dist/cost.js"
49
- },
50
33
  "./package.json": "./package.json"
51
34
  },
52
35
  "files": [
@@ -67,7 +50,6 @@
67
50
  "check": "tsc -p tsconfig.json --noEmit",
68
51
  "test": "npm run check && node --test src/*.test.ts",
69
52
  "test:coverage": "node --test --experimental-test-coverage --test-coverage-lines=100 --test-coverage-branches=95 --test-coverage-functions=100 src/*.test.ts",
70
- "bench": "node src/perf.bench.ts",
71
53
  "format": "prettier --write \"src/**/*.ts\" \"docs/**/*.md\" \"*.md\" \"*.json\"",
72
54
  "format:check": "prettier --check \"src/**/*.ts\" \"docs/**/*.md\" \"*.md\" \"*.json\"",
73
55
  "clean": "rm -rf dist",
@@ -1,10 +0,0 @@
1
- /**
2
- * 청사진의 형태를 확인한다 — **읽기 전에 한 번**, 등록 시점에.
3
- *
4
- * 그리는 시점이 아니라 등록 시점에 보는 이유는, 잘못된 청사진이 프레임마다 조용히
5
- * 실패하는 대신 등록에서 한 번 크게 실패하게 하려는 것이다.
6
- *
7
- * 문제를 찾으면 사람이 읽을 수 있는 사유 목록을 돌려준다. 비어 있으면 읽을 수 있다.
8
- */
9
- export declare function blueprintProblems(value: unknown): string[];
10
- //# sourceMappingURL=blueprint-shape.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"blueprint-shape.d.ts","sourceRoot":"","sources":["../src/blueprint-shape.ts"],"names":[],"mappings":"AAgCA;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CA+J1D"}
@@ -1,194 +0,0 @@
1
- /**
2
- * 이미 **컴파일된 청사진**의 형태를 확인한다.
3
- *
4
- * `validate` 는 저작 형식(`FigureSource`)을 본다. 이쪽은 그 결과물인 `FigureBlueprint`
5
- * 를 본다 — 서버나 파일에서 온 것이 이 형식대로 생겼는지, **읽기 전에 한 번** 묻는
6
- * 자리다.
7
- *
8
- * 그리는 시점이 아니라 등록 시점에 보는 이유는, 잘못된 청사진이 프레임마다 조용히
9
- * 실패하는 대신 등록에서 한 번 크게 실패하게 하려는 것이다.
10
- *
11
- * ## 왜 여기 있나
12
- *
13
- * 한동안 things-scene 이 이것을 손으로 들고 있었다. 「청사진이 어떻게 생겨야 하는가」는
14
- * **형식의 지식**이고, 읽는 쪽이 다시 적으면 형식이 바뀔 때 조용히 어긋난다. 실제로
15
- * `SEGMENT_PRESETS` 가 그렇게 갈려 있었다.
16
- *
17
- * 문제를 찾으면 사람이 읽을 수 있는 사유 목록을 돌려준다. 비어 있으면 읽을 수 있다.
18
- */
19
- import { BLUEPRINT_VERSION, JOINT_TYPES, PART_ROLES } from "./types.js";
20
- function isFiniteNumber(v) {
21
- return typeof v === 'number' && Number.isFinite(v);
22
- }
23
- function isFiniteVec3(v) {
24
- if (!v || typeof v !== 'object')
25
- return false;
26
- const { x, y, z } = v;
27
- return isFiniteNumber(x) && isFiniteNumber(y) && isFiniteNumber(z);
28
- }
29
- /**
30
- * 청사진의 형태를 확인한다 — **읽기 전에 한 번**, 등록 시점에.
31
- *
32
- * 그리는 시점이 아니라 등록 시점에 보는 이유는, 잘못된 청사진이 프레임마다 조용히
33
- * 실패하는 대신 등록에서 한 번 크게 실패하게 하려는 것이다.
34
- *
35
- * 문제를 찾으면 사람이 읽을 수 있는 사유 목록을 돌려준다. 비어 있으면 읽을 수 있다.
36
- */
37
- export function blueprintProblems(value) {
38
- const problems = [];
39
- const bp = value;
40
- if (!bp || typeof bp !== 'object')
41
- return ['청사진이 객체가 아니다'];
42
- if (bp.version !== BLUEPRINT_VERSION) {
43
- problems.push(`읽을 수 없는 판이다 — version ${String(bp.version)}, 이 판올림은 ${BLUEPRINT_VERSION} 만 읽는다`);
44
- }
45
- if (typeof bp.type !== 'string' || bp.type.length === 0) {
46
- problems.push('type 이 없다 — 컴포넌트 타입 이름이 곧 청사진을 찾는 키다');
47
- }
48
- const base = bp.base;
49
- if (!base || !isFiniteNumber(base.x) || !isFiniteNumber(base.y) || !isFiniteNumber(base.z)) {
50
- problems.push('base 의 x·y·z 가 온전하지 않다');
51
- }
52
- else if (!(base.x > 0) || !(base.z > 0)) {
53
- problems.push('base 의 x·z 는 0 보다 커야 한다 — 평면 배치 비율의 분모다');
54
- }
55
- if (!Array.isArray(bp.groups)) {
56
- problems.push('groups 가 배열이 아니다');
57
- return problems;
58
- }
59
- if (bp.groups.length === 0) {
60
- problems.push('groups 가 비어 있다 — 그릴 것이 없는 청사진은 등록하지 않는다');
61
- }
62
- bp.groups.forEach((group, gi) => {
63
- if (!group || !Array.isArray(group.members)) {
64
- problems.push(`groups[${gi}].members 가 배열이 아니다`);
65
- return;
66
- }
67
- if (group.members.length === 0) {
68
- problems.push(`groups[${gi}] 에 부품이 없다`);
69
- }
70
- group.members.forEach((member, mi) => {
71
- const where = `groups[${gi}].members[${mi}]`;
72
- if (typeof member?.name !== 'string' || member.name.length === 0) {
73
- problems.push(`${where}.name 이 없다 — 이름이 바인딩·이벤트·애니메이션이 붙는 자리다`);
74
- }
75
- const t = member?.transform;
76
- if (!t || !isFiniteVec3(t.position) || !isFiniteVec3(t.size)) {
77
- problems.push(`${where}.transform 의 값이 온전하지 않다`);
78
- }
79
- });
80
- });
81
- /*
82
- 자리와 문.
83
-
84
- `groups` 와 달리 없어도 된다 — 능력을 안 지는 도형이 대부분이다. 있으면 형태를 본다.
85
- 그리지 않는 것이라 **틀려도 화면에는 아무 일도 안 일어나고**, 물건을 앉히려는 순간에야
86
- 프레임마다 조용히 실패한다. 등록에서 한 번 크게 실패하는 편이 낫다.
87
- */
88
- if (bp.anchors !== undefined) {
89
- if (!Array.isArray(bp.anchors)) {
90
- problems.push('anchors 가 배열이 아니다');
91
- }
92
- else {
93
- bp.anchors.forEach((at, ai) => {
94
- const where = `anchors[${ai}]`;
95
- if (typeof at?.name !== 'string' || at.name.length === 0) {
96
- problems.push(`${where}.name 이 없다 — 이름이 곧 slot·port 의 id 다`);
97
- }
98
- if (!Array.isArray(at?.roles) || at.roles.length === 0) {
99
- problems.push(`${where}.roles 가 역할 하나 이상의 배열이 아니다`);
100
- }
101
- else if (at.roles.some(role => !PART_ROLES.includes(role))) {
102
- problems.push(`${where}.roles 에 ${PART_ROLES.join(' · ')} 밖의 값이 있다`);
103
- }
104
- const t = at?.transform;
105
- if (!t || !isFiniteVec3(t.position) || !isFiniteVec3(t.size)) {
106
- problems.push(`${where}.transform 의 값이 온전하지 않다`);
107
- }
108
- });
109
- }
110
- }
111
- /*
112
- 값이 만드는 자세.
113
-
114
- `anchors` 와 같은 갈래다 — 없어도 되고, **틀려도 세울 때는 아무 일도 안 일어난다.**
115
- 인스턴스가 값을 주는 순간에야 프레임마다 조용히 기본 자세로 돌아간다. 등록에서 한 번
116
- 크게 실패하는 편이 낫다.
117
-
118
- `range` 를 여기서 보는 이유는 그것이 **런타임의 나누는 수**라서다. `max` 가 `min`
119
- 이하면 런타임이 물리량을 0~1 로 맵할 수 없고, 그러면 준 값이 조용히 버려진다.
120
- */
121
- if (bp.parameters !== undefined) {
122
- if (!Array.isArray(bp.parameters)) {
123
- problems.push('parameters 가 배열이 아니다');
124
- }
125
- else {
126
- bp.parameters.forEach((parameter, pi) => {
127
- const where = `parameters[${pi}]`;
128
- if (typeof parameter?.name !== 'string' || parameter.name.length === 0) {
129
- problems.push(`${where}.name 이 없다 — 인스턴스가 이 이름으로 값을 준다`);
130
- }
131
- const range = parameter?.range;
132
- if (!range || !isFiniteNumber(range.min) || !isFiniteNumber(range.max)) {
133
- problems.push(`${where}.range 의 min·max 가 온전하지 않다`);
134
- }
135
- else if (!(range.max > range.min)) {
136
- problems.push(`${where}.range 의 max 가 min 보다 크지 않다 — 0~1 로 맵할 수 없다`);
137
- }
138
- if (!parameter?.clip || !Array.isArray(parameter.clip.channels)) {
139
- problems.push(`${where}.clip.channels 가 배열이 아니다 — 값이 무엇을 움직이는지 없다`);
140
- }
141
- else if (parameter.clip.duration !== undefined &&
142
- !(isFiniteNumber(parameter.clip.duration) && parameter.clip.duration >= 0)) {
143
- problems.push(`${where}.clip.duration 이 0 이상의 수가 아니다`);
144
- }
145
- });
146
- }
147
- }
148
- /*
149
- Joints (ADR-0066). Optional, like anchors. The runtime builds a node per joint and hangs groups from
150
- the node a group names, so a group or anchor naming a joint that is not there, a zero axis, or a
151
- parent that comes later would leave parts in no frame or turn them by nothing. Refused at
152
- registration instead.
153
- */
154
- if (bp.joints !== undefined) {
155
- if (!Array.isArray(bp.joints)) {
156
- problems.push('joints 가 배열이 아니다');
157
- }
158
- else {
159
- const placed = new Set();
160
- bp.joints.forEach((joint, ji) => {
161
- const where = `joints[${ji}]`;
162
- if (typeof joint?.name !== 'string' || joint.name.length === 0) {
163
- problems.push(`${where}.name 이 없다 — channel 이 이 이름으로 관절을 몬다`);
164
- return;
165
- }
166
- if (!JOINT_TYPES.includes(joint.type)) {
167
- problems.push(`${where}.type 이 ${JOINT_TYPES.join(' · ')} 가 아니다`);
168
- }
169
- if (!isFiniteVec3(joint.origin))
170
- problems.push(`${where}.origin 이 온전하지 않다`);
171
- if (!isFiniteVec3(joint.axis) || Math.hypot(joint.axis.x, joint.axis.y, joint.axis.z) === 0) {
172
- problems.push(`${where}.axis 가 0 이 아닌 벡터가 아니다`);
173
- }
174
- if (joint.parent !== undefined && !placed.has(joint.parent)) {
175
- problems.push(`${where}.parent '${joint.parent}' 가 앞에 없다 — 관절은 부모가 먼저 온다`);
176
- }
177
- placed.add(joint.name);
178
- });
179
- }
180
- }
181
- const frames = new Set(Array.isArray(bp.joints) ? bp.joints.map(joint => joint?.name) : []);
182
- bp.groups.forEach((group, gi) => {
183
- if (group?.joint !== undefined && !frames.has(group.joint)) {
184
- problems.push(`groups[${gi}].joint '${group.joint}' 가 joints 에 없다`);
185
- }
186
- });
187
- (Array.isArray(bp.anchors) ? bp.anchors : []).forEach((at, ai) => {
188
- if (at?.joint !== undefined && !frames.has(at.joint)) {
189
- problems.push(`anchors[${ai}].joint '${at.joint}' 가 joints 에 없다`);
190
- }
191
- });
192
- return problems;
193
- }
194
- //# sourceMappingURL=blueprint-shape.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"blueprint-shape.js","sourceRoot":"","sources":["../src/blueprint-shape.ts"],"names":[],"mappings":"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"}
@@ -1,73 +0,0 @@
1
- import { type ValidateResult } from './validate.ts';
2
- export { geometryKeyOf, emissiveKeyOf, materialKeyOf, usesSegments } from './keys.ts';
3
- import { type FigureBlueprint, type FigurePart, type Axis, type PartTransform, type Vec3 } from './types.ts';
4
- /** 컴파일할 수 없을 때 던진다. 오류보고없이 빈 청사진을 돌려주지 않는다. */
5
- export declare class FigureCompileError extends Error {
6
- readonly errors: ValidateResult['errors'];
7
- constructor(errors: ValidateResult['errors']);
8
- }
9
- /**
10
- * 삼각형 수 추정.
11
- *
12
- * 근거는 things-scene 의 geometry 생성 인자다.
13
- *
14
- * cube · wall BoxGeometry 면 6 × 2 = 12
15
- * cylinder CylinderGeometry(seg) 측면 2·seg + 캡 2·seg = 4·seg
16
- * 민 것(n 점) ExtrudeGeometry 캡 2·(n−2) + 측면 2·n = 4n − 4
17
- * 속 판 것 ExtrudeGeometry+구멍 4·(n + m) m 은 안쪽 테의 점 수
18
- *
19
- * ## 라운드를 안 세다가 12 배를 놓쳤다
20
- *
21
- * 한동안 `rect` 를 라운드와 무관하게 12 로 셌다. 「베벨과 라운드는 세지 않는다」고
22
- * 적어 두기까지 했다. three 로 실제로 만들어 세어 보면
23
- *
24
- * rect 각짐 12 cube 와 같다 — 민다고 비싸지지 않는다
25
- * rect round=24 140 모서리마다 8 점으로 뽑으므로 테가 36 점이 된다
26
- * 속 판 rect round=24 160 구멍은 +20 이다. 고리의 캡이 통짜 캡보다 싸다
27
- *
28
- * **비싼 것은 미는 것이 아니라 라운드다.** 그리고 라운드는 저작자가 무심코 넣는
29
- * 값이라, 예산이 12 배 적게 세고 있으면 그 선택의 대가가 화면에 안 나타난다.
30
- *
31
- * 그리는 쪽이 테를 몇 점으로 뽑는지(`SEGMENT_PRESETS[0]`)와 **같은 수**를 쓴다.
32
- * 다르면 재는 값과 그려지는 것이 갈린다.
33
- */
34
- export declare function trianglesOf(part: FigurePart): number;
35
- /** `sizing` 이 없으면 무엇으로 보나 — 비례 확대가 가장 예측 가능하다. */
36
- /**
37
- * 회전까지 반영한 부품의 축별 크기.
38
- *
39
- * 회전한 상자의 여덟 꼭짓점을 각 축에 내린 것 — 축에 맞춘 경계다. 회전 행렬은
40
- * `Rx·Ry·Rz` 이고 three 의 기본 Euler 순서('XYZ')와 같다. 그리는 쪽이 같은 순서로
41
- * 돌리므로 여기서만 다르면 답이 달라진다.
42
- */
43
- export declare function rotatedExtentOf(part: {
44
- transform: PartTransform;
45
- }): Vec3;
46
- /**
47
- * `stretch` 일 때 늘어나는 축.
48
- *
49
- * 형식에 축을 적는 자리가 없다. 대신 **부품이 가장 길게 뻗은 축**으로 정한다. 문서가
50
- * 든 예(프레임 · 벨트 · 레일)가 전부 "길어서 그 물건인 것"이라 이 규칙과 맞는다.
51
- * 정육면체에 가까운 부품에는 뜻이 없는데, 그런 부품은 애초에 `stretch` 를 쓸 자리가 아니다.
52
- *
53
- * **회전을 반영해 측정한다.** 크기만 보면 x 로 길게 그린 레일을 y 로 90° 돌려 세워도 여전히
54
- * x 라고 답한다. 그러면 레일이 길이가 아니라 두께 쪽으로 늘어난다.
55
- *
56
- * 두 축의 길이가 같으면 x → z → y 순으로 정한다. 순서를 정해 두지 않으면 같은 부품이
57
- * 소비처마다 다른 축으로 늘어난다.
58
- *
59
- * **소비처가 저마다 계산하면 안 된다.** 3D 와 2D 톱뷰와 속성 편집기가 같은 답을 내야
60
- * 하므로 여기 한 곳에서만 정한다.
61
- */
62
- export declare function stretchAxisOf(part: {
63
- transform: PartTransform;
64
- }): Axis;
65
- /**
66
- * 원본 데이터를 청사진으로 컴파일한다.
67
- *
68
- * `errors` 가 있으면 던진다 — 세울 수 없는 것을 반쯤 세운 청사진으로 돌려주면
69
- * 어디서 잘못됐는지 알 수 없다. `violations` 는 청사진에 담아 돌려준다. 정책 위반은
70
- * 컴파일을 막지 않고 숫자로 보인다.
71
- */
72
- export declare function compile(source: unknown): FigureBlueprint;
73
- //# sourceMappingURL=blueprint.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"blueprint.d.ts","sourceRoot":"","sources":["../src/blueprint.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAY,KAAK,cAAc,EAAE,MAAM,eAAe,CAAA;AAM7D,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACrF,OAAO,EAeL,KAAK,eAAe,EACpB,KAAK,UAAU,EAGf,KAAK,IAAI,EACT,KAAK,aAAa,EAClB,KAAK,IAAI,EACV,MAAM,YAAY,CAAA;AAEnB,+CAA+C;AAC/C,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAA;gBAE7B,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC;CAS7C;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CA0BpD;AAuBD,kDAAkD;AAElD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE;IAAE,SAAS,EAAE,aAAa,CAAA;CAAE,GAAG,IAAI,CAuBxE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAAE,SAAS,EAAE,aAAa,CAAA;CAAE,GAAG,IAAI,CAKtE;AAyFD;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,OAAO,GAAG,eAAe,CAwHxD"}