@hatiolab/figure-model 0.1.76 → 0.1.78

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 (135) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +60 -138
  3. package/dist/index.d.ts +1 -13
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +3 -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-examples.js +1 -1
  23. package/dist/v3-gate.d.ts +7 -8
  24. package/dist/v3-gate.d.ts.map +1 -1
  25. package/dist/v3-gate.js +11 -14
  26. package/dist/v3-gate.js.map +1 -1
  27. package/dist/v3-graph.d.ts +1 -2
  28. package/dist/v3-graph.d.ts.map +1 -1
  29. package/dist/v3-graph.js +5 -7
  30. package/dist/v3-graph.js.map +1 -1
  31. package/dist/v3-instance-size.d.ts +3 -5
  32. package/dist/v3-instance-size.d.ts.map +1 -1
  33. package/dist/v3-instance-size.js +2 -4
  34. package/dist/v3-instance-size.js.map +1 -1
  35. package/dist/v3-kernel-version.d.ts +1 -1
  36. package/dist/v3-kernel-version.js +1 -1
  37. package/dist/v3-part-edit.d.ts +4 -5
  38. package/dist/v3-part-edit.d.ts.map +1 -1
  39. package/dist/v3-part-edit.js +2 -2
  40. package/dist/v3-part-edit.js.map +1 -1
  41. package/dist/v3-proposal.js +1 -1
  42. package/dist/v3-proposal.js.map +1 -1
  43. package/dist/v3-release-contract.js +1 -1
  44. package/dist/v3-release-contract.js.map +1 -1
  45. package/dist/v3-score.d.ts +4 -5
  46. package/dist/v3-score.d.ts.map +1 -1
  47. package/dist/v3-score.js +7 -8
  48. package/dist/v3-score.js.map +1 -1
  49. package/dist/v3-words.d.ts +105 -0
  50. package/dist/v3-words.d.ts.map +1 -0
  51. package/dist/v3-words.js +107 -0
  52. package/dist/v3-words.js.map +1 -0
  53. package/docs/assembly-constraints-v3.md +13 -23
  54. package/docs/v3-asset-persistence.md +4 -4
  55. package/docs/v3-common-kernel-experiment.md +1 -1
  56. package/docs/v3-core-semantics.md +3 -3
  57. package/docs/v3-cutover.md +5 -3
  58. package/docs/v3-design.md +13 -26
  59. package/docs/v3-editor-integration.md +2 -2
  60. package/docs/v3-full-conveyor-validation.md +3 -3
  61. package/docs/v3-layout-system.md +1 -1
  62. package/docs/v3-minimal-model.md +0 -1
  63. package/docs/v3-motion-contract.md +21 -56
  64. package/docs/v3-runtime-extraction.md +7 -7
  65. package/docs/v3-sample-validation-expanded.md +1 -1
  66. package/docs/v3-sample-validation.md +1 -1
  67. package/docs/v3-shape-dimension-contract.md +23 -57
  68. package/docs/v3-status.md +5 -5
  69. package/docs/v3-storage-integration.md +4 -4
  70. package/package.json +2 -20
  71. package/dist/blueprint-shape.d.ts +0 -10
  72. package/dist/blueprint-shape.d.ts.map +0 -1
  73. package/dist/blueprint-shape.js +0 -194
  74. package/dist/blueprint-shape.js.map +0 -1
  75. package/dist/blueprint.d.ts +0 -73
  76. package/dist/blueprint.d.ts.map +0 -1
  77. package/dist/blueprint.js +0 -388
  78. package/dist/blueprint.js.map +0 -1
  79. package/dist/cost-chart.d.ts +0 -35
  80. package/dist/cost-chart.d.ts.map +0 -1
  81. package/dist/cost-chart.js +0 -150
  82. package/dist/cost-chart.js.map +0 -1
  83. package/dist/cost.d.ts +0 -162
  84. package/dist/cost.d.ts.map +0 -1
  85. package/dist/cost.js +0 -194
  86. package/dist/cost.js.map +0 -1
  87. package/dist/gate.d.ts +0 -65
  88. package/dist/gate.d.ts.map +0 -1
  89. package/dist/gate.js +0 -284
  90. package/dist/gate.js.map +0 -1
  91. package/dist/grouping.d.ts +0 -121
  92. package/dist/grouping.d.ts.map +0 -1
  93. package/dist/grouping.js +0 -224
  94. package/dist/grouping.js.map +0 -1
  95. package/dist/keys.d.ts +0 -45
  96. package/dist/keys.d.ts.map +0 -1
  97. package/dist/keys.js +0 -84
  98. package/dist/keys.js.map +0 -1
  99. package/dist/origin.d.ts +0 -33
  100. package/dist/origin.d.ts.map +0 -1
  101. package/dist/origin.js +0 -46
  102. package/dist/origin.js.map +0 -1
  103. package/dist/release-contract.d.ts +0 -38
  104. package/dist/release-contract.d.ts.map +0 -1
  105. package/dist/release-contract.js +0 -246
  106. package/dist/release-contract.js.map +0 -1
  107. package/dist/sizing.d.ts +0 -231
  108. package/dist/sizing.d.ts.map +0 -1
  109. package/dist/sizing.js +0 -550
  110. package/dist/sizing.js.map +0 -1
  111. package/dist/types.d.ts +0 -1233
  112. package/dist/types.d.ts.map +0 -1
  113. package/dist/types.js +0 -547
  114. package/dist/types.js.map +0 -1
  115. package/dist/v3-from-v2.d.ts +0 -203
  116. package/dist/v3-from-v2.d.ts.map +0 -1
  117. package/dist/v3-from-v2.js +0 -2053
  118. package/dist/v3-from-v2.js.map +0 -1
  119. package/dist/v3-mesh-compare.d.ts +0 -24
  120. package/dist/v3-mesh-compare.d.ts.map +0 -1
  121. package/dist/v3-mesh-compare.js +0 -55
  122. package/dist/v3-mesh-compare.js.map +0 -1
  123. package/dist/validate.d.ts +0 -7
  124. package/dist/validate.d.ts.map +0 -1
  125. package/dist/validate.js +0 -1119
  126. package/dist/validate.js.map +0 -1
  127. package/dist/visual-evidence.d.ts +0 -21
  128. package/dist/visual-evidence.d.ts.map +0 -1
  129. package/dist/visual-evidence.js +0 -52
  130. package/dist/visual-evidence.js.map +0 -1
  131. package/docs/design.md +0 -193
  132. package/docs/format-survey.md +0 -108
  133. package/docs/format.md +0 -704
  134. package/docs/sizing.md +0 -183
  135. package/docs/validation.md +0 -357
package/docs/sizing.md DELETED
@@ -1,183 +0,0 @@
1
- # 크기 반응 — 인스턴스가 커질 때 부품이 어떻게 되나
2
-
3
- 저작한 형상 하나가 도면에 여러 크기로 놓인다. 컨베이어를 2400 으로도, 7200 으로도
4
- 놓는다. 그때 부품마다 다르게 반응해야 한다 — 프레임은 길어지고, 모터는 그대로이고,
5
- 롤러는 개수가 늘고, 다리는 늘어나 프레임을 떠받친다.
6
-
7
- 이 문서는 그 반응을 **빠짐없이** 적는다. 낱개 규칙을 모아 놓은 것이 아니라, 「인스턴스가
8
- 축마다 다른 배율을 받을 때 부품 하나에 일어날 수 있는 일」의 전부다.
9
-
10
- ## 물음은 축마다 둘이다
11
-
12
- 인스턴스 배율은 축마다 다르다 — `sx` · `sy` · `sz`. 부품 하나에 대해 축마다 물을 것이
13
- 둘 있다.
14
-
15
- ```
16
- 크기 이 축의 길이가 어떻게 되나
17
- 자리 이 축의 중심이 어디로 가나
18
- ```
19
-
20
- 세 축 × 둘 = 여섯 가지 답이 부품 하나의 반응을 완전히 정한다. **`anchor` 의 한 값이 그
21
- 축의 둘을 함께 정한다** — 갈라 놓으면 「크기는 지키고 자리는 비례」처럼 성립하지 않는
22
- 조합이 생긴다. 실제로 그것 때문에 크레들의 V 패드 둘이 ±80 에서 ±240 으로 벌어졌다.
23
-
24
- ## 값 여섯 개가 전부다
25
-
26
- | `anchor` | 크기 | 자리 | 붙잡는 자리 |
27
- | ---------- | ------------- | --------------------------- | ----------- |
28
- | `scale` | `× s` | `× s` | 없음 |
29
- | `min` | 그대로 | 아래 면에서 잰 거리를 지킨다 | 한 곳 |
30
- | `center` | 그대로 | 가운데에서 잰 거리를 지킨다 | 한 곳 |
31
- | `max` | 그대로 | 위 면에서 잰 거리를 지킨다 | 한 곳 |
32
- | `span` | 남는 만큼 채움 | 양쪽에서 잰 거리를 지킨다 | 두 곳 |
33
- | `aspect-x` `aspect-y` `aspect-z` | 가리킨 축의 배율 | 가리킨 축의 배율 | 다른 축 |
34
-
35
- 「아래·위」는 축의 −·+ 쪽이다. x 는 왼쪽·오른쪽, y 는 밑·위, z 는 뒤·앞이다.
36
-
37
- ### 이 여섯이 왜 전부인가
38
-
39
- 크기가 될 수 있는 것은 넷뿐이다 — 배율을 그대로 받거나(`scale`), 지키거나(`min` ·
40
- `center` · `max`), 남는 만큼 채우거나(`span`), 다른 축을 따라가거나(`aspect-`). 자리는
41
- 기준면이 어디냐로 갈리고 기준면은 셋뿐이다 — 아래 · 가운데 · 위.
42
-
43
- 빠진 조합 둘을 굳이 확인해 두면 이렇다.
44
-
45
- - **크기는 배율, 자리는 붙잡기.** `scale` 이 이미 그것이다. 바닥에 붙여 놓은 부품은
46
- `scale` 로 두면 바닥에 남는다 — 상자 바닥이 `−h·s` 로 가고 부품 밑면도 그리로 간다.
47
- - **크기는 그대로, 자리는 비례.** 한동안 `center` 가 이것이었고 **틀렸다.** 가운데
48
- 가까이 저작한 것이 상자가 커질 때마다 밖으로 밀려 이웃과 갈라진다.
49
-
50
- ## `sizing` 은 축별 기본값을 채우는 짧은 표기다
51
-
52
- 부품마다 세 축을 다 적게 하면 저작이 못 견딘다. `sizing` 한 낱말이 세 축을 채우고,
53
- `anchor` 가 축마다 그것을 덮는다.
54
-
55
- | `sizing` | x · y · z 에 채워지는 것 |
56
- | --------- | --------------------------------------------------------- |
57
- | `scale` | 세 축 다 `scale` |
58
- | `fixed` | 세 축 다 **가까운 면** |
59
- | `stretch` | 가장 긴 축은 `span`, 나머지는 가까운 면 |
60
- | `repeat` | 늘어나는 축은 `center`, 나머지는 가까운 면 |
61
-
62
- **가까운 면**은 부품의 면에서 상자의 면까지 재서 짧은 쪽이다. 중심에서 재지 않는다 —
63
- 중심으로 재면 굵은 부품과 얇은 부품이 같은 자리에서 다른 답을 낸다. 양쪽이 같으면
64
- `center` 다.
65
-
66
- 표본 열넷 중 대부분이 이 기본값만으로 제대로 선다. 그것이 이 기본값이 사람이 이미
67
- 뜻하는 것이라는 증거다.
68
-
69
- ## 축을 넘는 규칙 둘
70
-
71
- 축마다 따로 정하는 것으로 안 되는 것이 둘 있다. 둘 다 **여러 축이 한 배율을 나눠
72
- 받아야** 하는 경우다.
73
-
74
- ### `keepRound` — 곡면의 단면은 원이다
75
-
76
- `cylinder` · `sphere` 는 단면이 원이다. 축마다 다른 배율을 받으면 타원이 된다 —
77
- 원기둥이라고 적어 놓고 원기둥이 아닌 것을 그리는 것이다.
78
-
79
- 그래서 단면의 두 축(구는 세 축)이 **기하평균**을 함께 받는다. 작은 쪽에 맞추면 한 축만
80
- 늘인 인스턴스에서 지름이 묶여, 형태가 아니라 **크기**를 지키게 된다.
81
-
82
- 기본이 켜짐이고 값을 치른다 — 합친 형상은 변환을 하나만 받으므로 원을 지키는 곡면 부품
83
- 하나가 draw call 하나다. 표본 열넷에서 139 가 168 이 되었다. `keepRound: false` 로 끄면
84
- 다시 합쳐진다.
85
-
86
- ```
87
- 켜 둔다 화면의 주역 롤 · 바퀴 · 탱크 · 기둥 · 파이프
88
- 끈다 기계가 안 깎은 것 사람의 팔다리 · 머리
89
- 끈다 찌그러져도 모를 것 볼트 머리 · 작은 스터드
90
- ```
91
-
92
- 사람에게는 기계가 깎은 원통이 없다. 작업자를 전부 끄면 16 부품이 13 call 에서 4 call 이
93
- 되는데, 그 4 는 쓰는 재질 수이므로 더 내려갈 자리가 없다는 뜻이다.
94
-
95
- ### `aspect-` — 비율을 지킨다
96
-
97
- 두 부품이 같은 것을 반대 방향에서 요구했다.
98
-
99
- ```
100
- 드라이룸 출입구 높이가 비례하고 **폭이 높이를 따라간다**
101
- 문은 방이 넓어졌다고 넓어지지 않는다. 높아져서 넓어진다
102
-
103
- 전극 롤 크래들 지름이 **z 를 따라간다**
104
- z 가 기계의 깊이 900 이고 롤이 그 중 700 이다. 그 깊이가 지름의 자리다
105
- 길이는 따로다 — x 가 웹 폭이다
106
- ```
107
-
108
- 축마다 제 배율을 받는 것으로는 둘 다 못 말한다. `aspect-y` 는 「이 축은 y 가 **끝낸**
109
- 배율을 받는다」이고, 끝낸 배율은 그 축의 규칙이 정한다 — `scale` 이면 인스턴스 배율,
110
- 붙잡은 축이면 1, `span` 이면 채운 만큼이다.
111
-
112
- 자기 자신이나 또 다른 `aspect-` 를 가리킬 수는 없다(`bad-aspect-axis`). 따라갈 배율이
113
- 정해지지 않는다.
114
-
115
- **상자로 자르지 않는다.** 자르면 크레들 롤이 막힌다 — 지름은 기준 상자의 높이를 넘어서
116
- 자란다. 기준 상자는 배치 비율의 분모이고 담장이 아니다(컨베이어의 발판이 이미 상자보다
117
- 넓다). 값은 이렇다: 이미 상자를 꽉 채운 부품은 한 축만 커진 상자에서 비율을 지킬 수 없다.
118
- 그런 부품은 여유를 두고 저작하거나 `span` 으로 채운다.
119
-
120
- ## `repeat` — 줄이 늘어나나 커지나
121
-
122
- 같은 「반복」이 두 가지다. 그 갈림을 늘어나는 축의 `anchor` 가 말한다.
123
-
124
- ```
125
- center 줄이 상자를 채운다 컨베이어 롤러 — 길어지면 개수가 늘고 한 칸은 그대로다
126
- scale 줄이 상자와 함께 큰다 셀트레이 셀 — 커지면 셀이 커지고 개수는 그대로다
127
- ```
128
-
129
- 셀 개수는 그 트레이가 몇 셀짜리냐이지 화면에 얼마나 크게 그렸느냐가 아니다.
130
-
131
- ## 저작할 때 묻는 순서
132
-
133
- ```
134
- 1 이 부품은 상자와 함께 커지나
135
- 그렇다 → sizing: 'scale' · 끝
136
- 2 어느 축이 상자를 채우나
137
- 길이 방향 하나 → sizing: 'stretch'
138
- 둘 이상 → sizing: 'fixed' + anchor 에 'span'
139
- 3 채우지 않는 축은 무엇을 붙잡나
140
- 적지 않으면 가까운 면. 가까운 면이 틀리면 min · center · max 로 적는다
141
- 4 다른 부품과 한 뭉치인가
142
- **같은 면을 붙잡게 한다.** 서로 다른 면을 잡으면 배율마다 사이가 벌어진다
143
- 5 비율을 지켜야 하나
144
- aspect- 로 기준 축을 가리킨다
145
- 6 곡면인데 찌그러져도 되나
146
- 되면 keepRound: false — draw call 하나를 아낀다
147
- ```
148
-
149
- 4 번이 가장 자주 틀린다. 롤프레스의 두 롤이 `min` 과 `max` 로 갈려 3 배에서 5200
150
- 벌어졌고, 시그널 타워의 램프가 기둥과 다른 면을 잡아 공중으로 갔다.
151
-
152
- ## 못 하는 말 하나 — 부품에 부모가 없다
153
-
154
- 「선반에 달린 램프」 · 「하우징에 붙은 HMI」를 형식이 직접 말하지 못한다. 붙잡는 것은
155
- 언제나 **기준 상자의 면**이고 다른 부품이 아니다.
156
-
157
- 지금은 둘이 **같은 면을 붙잡게** 해서 우회한다. 상자의 같은 면에서 잰 거리를 둘 다
158
- 지키면 서로의 거리도 지켜지기 때문이다. 그래서 롤프레스의 하우징이 `span` 으로 상자
159
- 면까지 닿게 만들어 놓았다 — HMI 가 잡을 면과 하우징이 잡을 면을 같게 하려고.
160
-
161
- 우회가 안 되는 자리도 있었다. 충방전기의 채널 램프는 선반과 같은 피치로 반복되는데, 두
162
- 줄이 저마다 상자를 채우므로 줄 사이의 45 가 배율만큼 벌어졌다. 램프를 선반 앞턱에 물려
163
- 같은 줄 중심을 쓰게 해서 넘겼다.
164
-
165
- 부모가 생기면 이 우회들이 없어진다.
166
-
167
- ## 무엇으로 확인하나
168
-
169
- ```bash
170
- node test/browser/check-figure-scaling.mjs # things-factory
171
- ```
172
-
173
- 보는 것 셋이다.
174
-
175
- - **한 덩어리로 남나.** 닿는 부품을 이어 덩어리를 만들고, 1 배에서 한 덩어리이던 것이
176
- 늘려도 한 덩어리인지 본다. 쌍으로 세면 안 된다 — AGV 바퀴가 차체 바닥과 26 벌어졌다고
177
- 네 건이 났는데 그 사이는 underbody 가 메우고 있었다.
178
- - **일곱 조합 전부.** x · y · z · x-y · x-z · y-z · x-y-z. 세 축을 같이 늘린 것만 보면
179
- 축마다 따로 걸리는 규칙을 못 본다. 오늘 잡은 결함 둘이 다 한 축만 늘렸을 때 드러났다.
180
- - **z-fighting.** 같은 평면 · **같은 법선** · 겹치는 발자국 셋이 다 맞을 때만이다. 맞대어
181
- 놓은 것은 서로 등을 져서 뒷면이 잘려 나가므로 멀쩡하다.
182
-
183
- 미리보기도 여덟을 나란히 놓는다 — 저작한 크기와 일곱 조합이다.
@@ -1,357 +0,0 @@
1
- # 검증 — 오류와 위반
2
-
3
- `validate(source)` 는 두 벌을 돌려준다.
4
-
5
- ```ts
6
- const { errors, violations } = validate(source)
7
- ```
8
-
9
- | | 뜻 | 컴파일 |
10
- | ------------ | --------------------------------------------------- | ----------- |
11
- | `errors` | 형식·정합성이 깨졌다. 무엇을 세워야 할지 알 수 없다 | **막는다** |
12
- | `violations` | 형식은 맞다. 정책을 넘었을 뿐이다 | 막지 않는다 |
13
-
14
- 가르는 기준은 **「이 상태로 세울 수 있나」** 하나다. `size.y` 가 없으면 세울 수 없다 —
15
- 오류다. 재질이 넷이면 세울 수는 있고 다만 draw call 이 는다 — 위반이다.
16
-
17
- **정책은 형식이 성립할 때만 본다.** `errors` 가 하나라도 있으면 `violations` 는 비어
18
- 있다. 망가진 입력에 대고 「재질이 많다」고 말해 봐야 도움이 안 되기 때문이다.
19
-
20
- ## 코드는 목록으로 잠겨 있다
21
-
22
- `ERROR_CODES` · `VIOLATION_CODES` 가 기준이 되는 원본이고 `FigureError.code` · `Violation.code` 가
23
- 그 값으로 좁혀져 있다. **목록에 없는 코드는 타입 검사가 막는다.** 이 문서와 목록이
24
- 어긋나면 `src/docs.test.ts` 가 깨진다.
25
-
26
- 소비처는 코드로 분기한다. 문구(`message`)는 사람이 읽는 것이라 바뀔 수 있으니
27
- 코드에 의존한다.
28
-
29
- ---
30
-
31
- ## 오류 (`errors`)
32
-
33
- `{ code, path, message }` — `path` 는 어디서 났는지 가리킨다. 예: `parts[2].transform.size.x`.
34
-
35
- ### 형태
36
-
37
- | 코드 | 언제 |
38
- | -------------- | ----------------------------------------------------------------------------------------------------------------------------- |
39
- | `not-object` | 객체여야 할 자리에 객체가 아닌 것이 왔다 (원본 데이터 자체, 부품, `transform.rotation`, `material`, `animation`, `label`, `Vec3`) |
40
- | `not-finite` | 수여야 할 자리가 수가 아니거나 `NaN`·`Infinity` 다 |
41
- | `not-positive` | 0 보다 커야 하는데 아니다 (`base` 의 세 변) |
42
- | `not-integer` | `segments` 가 정수가 아니다 |
43
- | `out-of-range` | 아래위가 정해진 값이 그 밖이다 (`material.emissive.intensity` 는 0~1, `animation.turnsPerSecond` 는 0 이상) |
44
- | `not-boolean` | `material.flatShading` · `material.transparent` · `material.emissive.on` 이 참·거짓이 아니다 |
45
- | `not-allowed` | 열거 값이 목록 밖이다 (`primitive` · `preset` · `slot` · `sizing` · `axis` · `detailLevel` · `animation.kind` · `label.when`) |
46
-
47
- ### 원본의 필수 항목
48
-
49
- | 코드 | 언제 | 왜 |
50
- | --------------- | ----------------------------------------- | --------------------------------------------------------- |
51
- | `missing-type` | `type` 이 없거나 빈 문자다 | **저장되는 식별자다.** 씬이 이 이름으로 청사진을 찾는다 |
52
- | `source-version-unsupported` | `version` 이 2 가 아니거나 없다 | 판 1 은 y 를 상자 중심에서 쟀다. 판을 적지 않은 원본도 판 1 이다. 옮긴 원본만 읽는다 |
53
- | `missing-base` | `base` 가 없다 | 배치 크기가 이 비율에서 나온다 |
54
- | `missing-parts` | `parts` 가 없거나 비었다 | 부품이 하나는 있어야 한다. 여기서 검사를 멈춘다 |
55
- | `bad-style-kit` | `styleKit` 이 빈 문자이거나 문자가 아니다 | |
56
-
57
- ### 부품
58
-
59
- | 코드 | 언제 | 왜 |
60
- | ------------------- | ---------------------------------------------- | ---------------------------------------------------------------------- |
61
- | `missing-name` | `name` 이 없거나 빈 문자다 | **바인딩·애니메이션·슬롯이 이 이름을 가리킨다** |
62
- | `duplicate-name` | 이름이 겹친다 | 겹치면 어느 부품을 가리키는지 정해지지 않는다 |
63
- | `missing-transform` | `transform` 이 없다 | |
64
- | `missing-position` | `transform.position` 이 없다 | |
65
- | `missing-size` | `transform.size` 나 그 안의 `y` 가 없다 | **0 으로 갈음하지 않는다.** 두께를 모르는 것과 두께가 0 인 것은 다르다 |
66
- | `too-few-segments` | `segments` 가 `LIMITS.minSegments`(3) 미만이다 | 2 이하는 면이 생기지 않는다 |
67
-
68
- ### 단면
69
-
70
- | 코드 | 언제 |
71
- | -------------------- | -------------------------------------------------------- |
72
- | `missing-shape-path` | `polygon` 인데 `shape.path` 가 없거나 점이 3 개 미만이다 |
73
- | `hollow-too-thick` | 벽이나 바닥이 치수를 다 먹는다 — 팔 안쪽이 남지 않는다 |
74
- | `hollow-not-convex` | 오목한 `polygon` 은 아직 못 판다 — 안쪽 테가 제 몸을 가로지른다 |
75
- | `bad-aspect-axis` | `aspect-` 가 자기 자신이나 또 다른 `aspect-` 축을 가리킨다 — 따라갈 배율이 정해지지 않는다 |
76
- | `bad-shape-point` | `shape.path` 의 점이 `{ x, y }` 가 아니다 |
77
-
78
- ### 재질
79
-
80
- | 코드 | 언제 | 왜 |
81
- | ----------------------- | --------------------------- | -------------------------------------- |
82
- | `missing-material` | `material` 이 없다 | |
83
- | `material-unresolvable` | `token` 도 `preset` 도 없다 | 무슨 색·무슨 질감인지 정할 근거가 없다 |
84
-
85
- ### 크기 반응
86
-
87
- | 코드 | 언제 |
88
- | ---------------- | -------------------------------------------- |
89
- | `missing-repeat` | `sizing` 이 `'repeat'` 인데 `repeat` 가 없다 |
90
-
91
- ### 움직임
92
-
93
- | 코드 | 언제 | 왜 |
94
- | ------------------------ | --------------------------------------------- | ---------------------------------------------------------------- |
95
- | `missing-channels` | clip 에 `channels` 가 없거나 비었다 | 무엇을 움직이는지 모른다 |
96
- | `unknown-channel-target` | `channels[].target` 이 없는 부품을 가리킨다 | 조용히 아무것도 안 움직인다 |
97
- | `too-few-keys` | `keys` 가 둘 미만이다 | 하나짜리는 움직임이 아니라 자세 하나다. `transform` 이 그 자리다 |
98
- | `keys-out-of-order` | 키의 시각이 앞의 것보다 크지 않다 | 표본추출이 뜻을 잃는다. 화면에서는 「이상하게 움직인다」로만 보인다 |
99
-
100
- clip 의 이름은 부품과 같은 코드를 쓴다 — 없으면 `missing-name`, 겹치면 `duplicate-name`.
101
- 경로와 `interpolation` 이 목록 밖이면 `not-allowed`, 키의 시각이 음수면 `not-positive` 다.
102
-
103
- **같은 검사가 파라미터의 곡선에도 그대로 돈다.** 채널 모양은 어느 쪽이든 같아야 한다.
104
-
105
- ### 예외 표면
106
-
107
- | 코드 | 언제 |
108
- | --------------------- | --------------------------------------------------- |
109
- | `missing-surface-ref` | `material.surface.ref` 가 없다 — 무엇을 쓸지 모른다 |
110
-
111
- ### 라벨
112
-
113
- | 코드 | 언제 |
114
- | ---------------------- | --------------------------------------------- |
115
- | `missing-label-name` | `label.name` 이 없다 |
116
- | `missing-label-source` | `label.source` 가 없다 — 무엇을 보일지 모른다 |
117
-
118
- ### 능력 (`capabilities`)
119
-
120
- 배치된 도형이 씬 컴포넌트로서 부여받는 능력의 선언이다. **이름도 여기서 본다** — 한동안
121
- 아니었다.
122
-
123
- 목록이 things-scene 에만 있던 동안 이 검사가 이름을 못 봤고, AI 가 낸
124
- `capabilities: ['hoist']` 가 **검증을 지나고 발행을 지나** 렌더링에서 죽었다. 저작자는 자산을
125
- 내보내고 나서야 그것이 안 선다는 것을 알았다. 게이트는 사실이 사는 곳에 있어야 한다 —
126
- `base` 상자 때와 같은 구멍이었다.
127
-
128
- 쓸 수 있는 이름은 넷이다(`FIGURE_CAPABILITIES`). 자세한 것은 `docs/format.md`.
129
-
130
- | 코드 | 뜻 |
131
- | ------------------------ | -------------------------------------------------------------- |
132
- | `bad-capability` | 배열이 아니거나, 이름이 비어 있거나, **목록 밖의 이름이거나**, 혼자 설 수 없는 것을 혼자 적었다 |
133
- | `duplicate-capability` | 같은 능력을 두 번 선언했다 |
134
- | `bad-part-capability` | 부품의 `capability` 모양이 틀렸다 — 역할·accepts·capacity |
135
-
136
- 거절할 때는 **쓸 수 있는 넷을 함께 말한다.** 「틀렸다」만으로는 AI 도 사람도 다음 수가 없다.
137
-
138
- `Transferable` 은 자리를 찾는 일을 `Capacity` 에게 맡기므로 혼자 못 선다. 씬이 조립할 때도
139
- 같은 이유로 던지는데, **그때는 이미 발행된 뒤다** — 그래서 여기서 본다.
140
-
141
- ### 부모와 관절 (`parent` · `joints`)
142
-
143
- > ADR-0066 (`operato-twin/design/04-decisions.md`)
144
-
145
- | 코드 | 언제 | 왜 |
146
- | --------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------ |
147
- | `unknown-parent` | `parent` 가 부품 이름이 아니거나 없는 부품을 가리킨다 | 붙을 자리가 없다 |
148
- | `parent-cycle` | 부모를 따라가면 제자리로 돌아온다 | figure 의 틀에 닿지 않아 어느 틀에서 움직이는지 정할 수 없다 |
149
- | `unknown-joint-child` | 관절의 `child` 가 없는 부품이다 | 움직일 것이 없다 |
150
- | `joint-child-taken` | 한 부품이 두 관절의 `child` 다 | 한 부품이 부모에 대해 움직이는 방법은 하나다 |
151
- | `bad-joint-axis` | `axis` 가 0 벡터다 | 어느 쪽으로 움직이는지 없다 |
152
-
153
- 관절 이름이 없으면 `missing-name`, 다른 관절이나 **부품** 이름과 겹치면 `duplicate-name` 이다 — channel 의 `target` 이
154
- 두 가지를 뜻하면 안 된다. `type` 이 목록 밖이면 `not-allowed`. `limits` 는 `max` 가 `min` 이하면 `bad-range`, **0 을
155
- 품지 않으면 `out-of-range`** 다 — 그린 자세가 관절 값 0 이다. continuous 관절에 `limits` 를 적으면 `not-allowed`.
156
-
157
- **관절을 모는 channel** 은 `target` 이 관절 이름인 것이다. `path` 나 `pivot` 을 적으면 `not-allowed`(관절이 이미
158
- 말했다), 키의 `value` 가 수가 아니면 `not-finite`, `limits` 밖이면 `out-of-range` 다. `target` 이 부품도 관절도 아니면
159
- `unknown-channel-target`.
160
-
161
- ### 파라미터 (`parameters`)
162
-
163
- > ADR-0051 (`operato-twin/design/04-decisions.md`)
164
-
165
- 값이 만드는 자세다 — 호이스트가 얼마나 내려왔나, 포크가 얼마나 올라갔나. **애니메이션이 아니다.**
166
- 저것은 시각이 흐르는 것이고 이것은 지금 값이 그만큼인 것이다.
167
-
168
- 두 층으로 되어 있다. `clip` 은 **glTF 애니메이션과 같은 모양**이라 내보내면 평범한 애니메이션이
169
- 나가고, 그 위에 얹는 `range` 가 우리 것이다 — 「이 곡선은 값이 몬다」와 「그 값은 mm 로 몇부터
170
- 몇까지다」. 못 받는 쪽은 선언만 잃고 형상은 잃지 않는다.
171
-
172
- 곡선의 값 축은 0~1 이다(glTF morph target `weights` · Unity 파라미터와 같은 정규화). 저작자와
173
- 인스턴스가 주고받는 것은 `range` 의 단위이고 런타임이 그 사이를 맵한다.
174
-
175
- | 코드 | 뜻 |
176
- | --------------- | ------------------------------------------------------------------ |
177
- | `missing-range` | `range` 나 `range.unit` 이 없다 — 인스턴스가 어느 값을 줄지 모른다 |
178
- | `bad-range` | `max` 가 `min` 이하이거나, `default` 가 그 밖이다 |
179
- | `out-of-range` | `clip` 의 키 `at` 이 1 을 넘는다 — `at` 은 초가 아니라 값의 구간(0~1)이다 |
180
- | `not-positive` | `clip.duration` 이 음수다 — 없거나 0 이면 즉시다 |
181
-
182
- **키의 `at` 은 값의 구간에 앉는다.** 1 을 넘는 키는 어떤 값으로도 닿지 않는 자세라서 거절한다. 구간을
183
- 다 덮을 필요는 없다 — 첫 키 앞과 마지막 키 뒤는 끝 키의 자세에 머문다. 전이 시간은 `clip.duration`
184
- (초, 0→1 전 구간)이 따로 말한다.
185
-
186
- 이름은 `animations` 의 clip 이름과도 겹칠 수 없다. 인스턴스는 둘을 이름으로 부르므로 겹치면 어느 쪽에
187
- 값을 준 것인지 알 수 없다.
188
-
189
- clip 에 `drive` 칸이 있으면 `not-allowed` 다. 값으로 움직이는 자세가 `drive: 'hold'` 로 애니메이션 안에 있던
190
- 원본은 판 2 로 옮길 때 파라미터로 함께 옮겼다(키는 마지막 키의 시각으로 나눠 값의 구간에, `duration` 없음).
191
- 형식은 옛 모양을 읽지 않는다.
192
-
193
- 부품의 `capability` 는 **그 능력을 어느 부품이 받쳐 주나**를 말한다. 역할은 셋이다.
194
-
195
- | 역할 | 씬에서 |
196
- | -------- | ---------------------------- |
197
- | slot | `Capacity.slots` 의 한 자리 |
198
- | port-in | `FlowNode.inboundPorts` |
199
- | port-out | `FlowNode.outboundPorts` |
200
-
201
- `roles` 는 **배열이다.** 한 부품이 여럿을 맡을 수 있다 — AGV 의 상판은 물건을 놓는 자리이면서
202
- 그 자리로 물건이 들어오고 나간다. 하나만 받으면 저작자가 같은 위치에 부품을 두 개 만들게 되고,
203
- 실물에 없는 구조를 형식이 만들어 낸다. 빈 배열은 막는다. 역할이 없으면 그냥 그리는 부품이므로
204
- `capability` 를 적지 않는 것이 맞다.
205
-
206
- ```json
207
- { "name": "deck", "capability": { "roles": ["slot", "port-in", "port-out"], "accepts": ["PALLET"] } }
208
- ```
209
-
210
- 닻은 **그려지지 않는다.** 삼각형·묶음·부품 수에 들어가지 않고, 그래서 `material` 도 요구하지 않는다.
211
-
212
- 그리지 않는다고 안 재는 것은 아니다. 발행 게이트의 크기 검사는 **자리(slot)를 그리는 부품과
213
- 함께 세운다** — 선반이 커질 때 자리가 따라가지 않으면 그 위의 재고가 허공에 뜨고, 자리는
214
- 화면에 안 보이므로 저작자가 눈으로는 영영 못 찾는다. 문(port)은 몸 밖에 두는 것이 정상이라
215
- 세지 않는다.
216
-
217
- ---
218
-
219
- ## 위반 (`violations`)
220
-
221
- `{ code, message, actual?, limit? }` — 한도를 넘은 것은 `actual` 과 `limit` 을 함께
222
- 낸다. 저작 화면이 「8 개 중 3 개 한도」처럼 보일 수 있게 하려는 것이다.
223
-
224
- ### 쓰이지 않는 값
225
-
226
- | 코드 | 언제 |
227
- | --------------------- | ------------------------------------------------------------------------------------- |
228
- | `segments-off-preset` | `segments` 가 프리셋(8 · 12 · 16 · 24 · 32 · 48) 밖이다. 값은 쓰이지만 geometry 키가 그만큼 늘어난다 |
229
- | `segments-ignored` | 곡면이 없는 프리미티브에 `segments` 를 주었다 |
230
- | `repeat-ignored` | `sizing` 이 `'repeat'` 가 아닌데 `repeat` 를 주었다 |
231
- | `keep-round-ignored` | 단면이 원이 아닌 프리미티브에 `keepRound` 를 주었다 |
232
- | `hollow-ignored` | 밀어 만드는 것이 아닌 프리미티브에 `shape.hollow` 를 주었다 |
233
- | `pivot-ignored` | `rotation` 이 아닌 채널에 `pivot` 을 주었다 — 아무 일도 안 일어난다 |
234
-
235
- ### 예산
236
-
237
- | 코드 | 한도 | 왜 |
238
- | --------------------------- | -------------------------------------------- | --------------------------------------------------------------------------------------------- |
239
- | `detail-level-missing` | — | 등급이 없으면 부품 수 예산을 잡을 수 없다 |
240
- | `too-many-parts` | `PART_LIMIT[detailLevel]` (S 3 · M 8 · L 20) | |
241
- | `too-many-independent-parts` | `LIMITS.independentParts` (12) | 따로 변환되는 부품마다 묶음이 하나 늘고, 그만큼 한 화면에 세울 대수가 준다. clip 과 parameter 가 움직이는 부품을 함께 센다 |
242
- | `too-many-material-groups` | `LIMITS.materialGroups` (3) | **자산당 draw call 이 는다.** 세는 단위는 `groupKeyOf` — 컴파일러가 합치는 단위와 같은 식이다 |
243
- | `too-many-transparent` | `LIMITS.transparentMaterials` (1) | 투명 재질은 별도 정렬 경로를 탄다 |
244
- | `too-many-always-labels` | 1 | `'always'` 라벨은 대량 배치에서 DOM·텍스처를 늘린다 |
245
- | `too-many-surface-textures` | `LIMITS.surfaceTextures` (1) | 무늬·그라디언트는 **재질을 나눠 쓸 수 없다** — 인스턴스 수만큼 재질이 생긴다 |
246
- | `capacity-with-repeat` | — | 반복하는 자리에 `capacity` 를 적었다 — 칸 수는 **인스턴스 크기**가 정하므로 쓰이지 않는다 |
247
-
248
- ### 기준 상자
249
-
250
- > ADR-0044 (`operato-twin/design/04-decisions.md`)
251
-
252
- `base` 는 **저작자가 선언하는 점유 부피**다. 부품을 감싸는 최소 상자가 아니다 — 감싸는 상자면
253
- 계산해서 나오는 값이라 저작자가 쓸 것이 아니고, 의도를 담을 자리가 없어진다. 컨베이어가 앞에
254
- 두어야 하는 접근 공간, 랙의 칸 pitch 가 그 여백에 담긴다. **남는 여백은 선언이므로 보지 않는다.**
255
-
256
- 넘치는 것과 기준면에 안 닿는 것은 **다른 결함이라 코드를 나눈다.** 하나로 뭉치면 저작자가 상자를
257
- 키우는 쪽으로 고치는데, 뒤쪽이 필요로 하는 것은 부품을 제자리로 옮기는 것이다.
258
-
259
- | 코드 | 언제 | 왜 |
260
- | --------------------------- | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------- |
261
- | `part-outside-base` | 부품이 상자 밖으로 나간다(x·z 는 중심에서 `±base/2`, y 는 밑면 0 에서 `base.y`). 회전을 포함한 실제 부피로 잰다 | `base` 는 **나누는 수**다(`instanceScale = width/base.x`). 넘치면 그 수가 거짓말을 하고 도면에서 크기와 높이가 둘 다 틀린다 |
262
- | `parts-off-placement-face` | 바닥 기반인데 상자 밑면에 안 닿는다 · 천정 기반인데 윗면에 안 닿는다 | 「선다」·「매달린다」고 선언해 놓고 떠 있다. **원점 규약이 어긋난 것**이지 상자가 큰 것이 아니다 |
263
-
264
- 허용 오차는 `SKIN`(0.5) 하나다 — 발행 관문이 「덩어리가 갈라졌나」를 재는 값과 같다. 오차 상수가
265
- 둘이면 한쪽이 닿았다고 한 면을 다른 쪽이 떨어졌다고 한다.
266
-
267
- **중심 기반(`center`)에는 기준면 요구가 없다.** 떠 있는 것이 그 배치의 뜻이다.
268
-
269
- 밑면 아래로 지나가는 것은 **닿은 것으로 본다.** 지나간 사실은 `part-outside-base` 가 이미
270
- 말하므로, 같은 일을 두 코드가 두 번 말하지 않는다.
271
-
272
- **이 둘은 발행을 막는다.** 이미 발행된 것은 그대로 서 있고, 고치기 전에는 다시 발행하지 못한다.
273
-
274
- ### type 이름
275
-
276
- `type` 은 도면이 `{ type, left, top, width, height }` 로 적는 **저장되는 식별자**다. 발행된 이름은 이미 놓인
277
- 도면들과의 약속이라 바꿀 수 없다. 그래서 규칙은 **초안 저장이 아니라 발행을 막는다** — 이름을 정하기 전에
278
- 만들어 보는 것은 막을 이유가 없다.
279
-
280
- | 코드 | 언제 | 왜 |
281
- | --------------------- | ---------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
282
- | `type-not-identifier` | 대문자 `SCREAMING_SNAKE_CASE`(`TYPE_PATTERN`) 가 아니거나 3~32 자 밖이다 | 대문자가 figure 의 이름공간이다. 씬의 내장 컴포넌트는 소문자라, 이 모양이면 겹칠 수 없다 |
283
- | `type-is-placeholder` | 이름을 짓기 전의 임시 이름(`FIGURE_` + 8 자, `placeholderType()`)이다 | 모양은 맞아서 위 규칙으로는 안 걸린다. **아직 사람이 이름을 안 지은 것**이다 |
284
-
285
- 임시 이름은 저작 화면이 만들고(`placeholderType`) 게이트와 AI 도구가 알아본다(`isPlaceholderType`). 만드는
286
- 쪽과 알아보는 쪽이 같은 규칙을 쓰도록 형식이 갖는다. 범주 접두(`EQ_` · `VEH_` …)는 규칙으로 세우지 않는다.
287
-
288
- 형식이 깨진 원본에서는 이름을 보지 않는다 — 다른 정책 위반과 같은 단계다.
289
-
290
- ### draw call 을 세는 법
291
-
292
- ```
293
- draw call = 합쳐진 묶음 하나당 1
294
- + 단독으로 서는 부품 하나당 1
295
- + repeat 부품은 사본 하나당 1
296
- ```
297
-
298
- **단독으로 서는 것이 셋이다.**
299
-
300
- | 무엇 | 왜 |
301
- | --- | --- |
302
- | clip 이나 parameter 가 움직이는 부품 | 합치면 그 부품만 따로 움직일 수 없다 |
303
- | `sizing` 이 `scale` 이 아닌 부품 | 합친 geometry 는 변환을 하나만 받는다 |
304
- | `repeat` 부품 | 한 부품이 여러 mesh 다 |
305
-
306
- 한동안 이 자리가 「재질·슬롯 조합 수」였다. 그 값은 컴파일이 실제로 만드는 묶음보다
307
- 작아서, 저작자에게 자산이 싸다고 알려 주는 숫자만 비용을 안 재고 있었다.
308
-
309
- ```
310
- 컨베이어 3 으로 셌고 32 를 그렸다
311
- 셀 트레이 2 로 셌고 9 를 그렸다
312
- ```
313
-
314
- `repeat` 개수는 **저작한 크기(배율 1)** 기준이다. 줄은 인스턴스가 받은 상자를 채우므로
315
- 크게 놓으면 더 그린다.
316
-
317
-
318
- `groupKeyOf` 에 드는 것 — `token` · `preset` · `flatShading` · `transparent` · `slot`.
319
- 투명 여부와 flatShading 이 키에 드는 이유는 셰이더와 정렬 경로가 달라 실제로 합칠 수
320
- 없기 때문이다.
321
-
322
- **검사와 컴파일이 같은 함수를 쓴다**(`src/keys.ts`). 다르면 「한도를 지켰다」는 말이
323
- 실제 묶음 수와 어긋나고, 그 어긋남은 오류 없이 성능으로만 나타난다.
324
-
325
- ---
326
-
327
- ## 컴파일
328
-
329
- ```ts
330
- const blueprint = compile(source)
331
- ```
332
-
333
- `errors` 가 있으면 `FigureCompileError` 를 던진다. 예외에 `errors` 목록이 담겨 있다.
334
-
335
- `violations` 는 던지지 않고 **청사진에 실려 나간다**(`blueprint.violations`). 저작
336
- 화면이 그것을 보인다.
337
-
338
- ## 문은 둘이다 — 저작할 때와 세울 때
339
-
340
- ```ts
341
- validate(source) // 저작 형식을 본다. 사람이 고치는 것
342
- blueprintProblems(blueprint) // 그 결과물을 본다. 씬이 등록할 때
343
- ```
344
-
345
- `validate` 는 저작 중에 돌고, 무엇이 왜 틀렸는지 저작자에게 말하는 것이 일이다.
346
-
347
- `blueprintProblems` 는 **씬에 들어가기 직전**에 한 번 돈다. 서버나 파일에서 온 청사진이
348
- 이 형식대로 생겼는지 묻는 자리다. 그리는 시점이 아니라 등록 시점에 보는 이유는, 잘못된
349
- 청사진이 **프레임마다 조용히 실패하는 것**보다 등록에서 한 번 크게 실패하는 편이 낫기
350
- 때문이다.
351
-
352
- 여기서 특히 보는 것은 **그려지지 않는 것들**이다. 자리(`anchors`)와 파라미터가 그렇다 —
353
- 틀려도 화면에는 아무 일도 안 일어난다. 물건을 앉히려는 순간, 값을 주는 순간에야 드러난다.
354
-
355
- `range` 를 여기서 한 번 더 보는 이유가 그것이다. 그 값이 **런타임의 나누는 수**라서
356
- (`(값 − min) / (max − min)`), `max` 가 `min` 이하면 mm 를 0~1 로 맵할 수 없고 **준 값이
357
- 통째로 조용히 버려진다.**