@hatiolab/figure-model 0.1.21 → 0.1.23
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.
- package/dist/blueprint-shape.d.ts.map +1 -1
- package/dist/blueprint-shape.js +49 -1
- package/dist/blueprint-shape.js.map +1 -1
- package/dist/blueprint.d.ts +7 -0
- package/dist/blueprint.d.ts.map +1 -1
- package/dist/blueprint.js +73 -43
- package/dist/blueprint.js.map +1 -1
- package/dist/grouping.d.ts +43 -2
- package/dist/grouping.d.ts.map +1 -1
- package/dist/grouping.js +60 -4
- package/dist/grouping.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/origin.d.ts +33 -0
- package/dist/origin.d.ts.map +1 -0
- package/dist/origin.js +46 -0
- package/dist/origin.js.map +1 -0
- package/dist/sizing.d.ts +15 -0
- package/dist/sizing.d.ts.map +1 -1
- package/dist/sizing.js +29 -2
- package/dist/sizing.js.map +1 -1
- package/dist/types.d.ts +220 -35
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +70 -14
- package/dist/types.js.map +1 -1
- package/dist/validate.d.ts +0 -6
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +253 -20
- package/dist/validate.js.map +1 -1
- package/dist/visual-evidence.d.ts +1 -1
- package/dist/visual-evidence.d.ts.map +1 -1
- package/dist/visual-evidence.js +3 -1
- package/dist/visual-evidence.js.map +1 -1
- package/docs/format.md +221 -25
- package/docs/validation.md +92 -15
- package/package.json +2 -2
package/docs/format.md
CHANGED
|
@@ -28,8 +28,25 @@ transform.size { x, y, z } 축별 크기 [mm]
|
|
|
28
28
|
transform.rotation { x, y, z } 축별 회전(도, deg), 선택. 오일러 각 적용 순서는 Three.js/glTF 기본인 'XYZ' (Rx → Ry → Rz)다.
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
**원점은 부품의 중심**이고 `position` 은 그 원점의 자리다 — 여느 모델러와 같다.
|
|
32
|
+
|
|
33
|
+
그 자리를 재는 기준은 **`base` 상자의 밑면 가운데**다(원본 판 2, ADR-0065).
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
x 상자 중심에서
|
|
37
|
+
z 상자 중심에서
|
|
38
|
+
y 상자 밑면에서 위로
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
바닥에 선 부품은 `position.y` 가 자기 높이의 절반이다. 상자 높이(`base.y`)를 바꿔도 부품의 수는
|
|
42
|
+
그대로다 — 편집하면서 높이가 계속 달라지기 때문에 이 기준을 쓴다. 놓이는 자산에 흔한 원점(발 밑
|
|
43
|
+
가운데)과도 같다.
|
|
44
|
+
|
|
45
|
+
컴파일한 청사진은 y 도 상자 중심에서 잰다. 씬 런타임의 원점 규약이 그것이고, 원본을 그 좌표로 바꾸는
|
|
46
|
+
자리는 `origin.ts` 한 곳이다.
|
|
47
|
+
|
|
48
|
+
판 1(2026-09-17 이전)은 y 를 상자 중심에서 쟀다. 판을 적지 않은 원본도 판 1 이다. **판 2 만 읽는다** —
|
|
49
|
+
다른 판은 `source-version-unsupported` 로 거절한다.
|
|
33
50
|
|
|
34
51
|
씬 컴포넌트의 state 는 `width` · `height` · `depth` 이고 축 배정도 다르다(그쪽은
|
|
35
52
|
`depth` 가 높이다). 그 환산은 그리는 쪽 경계 한 곳에서 한다.
|
|
@@ -57,6 +74,25 @@ pitch: 150 // 롤러 중심 간 거리 150mm
|
|
|
57
74
|
| `parts` | `FigurePart[]` | ✓ | 부품. 하나는 있어야 한다 |
|
|
58
75
|
| `detailLevel` | `'S' \| 'M' \| 'L'` | | 부품 수 상한을 정한다 (3 · 8 · 20) |
|
|
59
76
|
| `styleKit` | `string` | | StyleKit 이름. 팔레트·분할 수·그리드·등급을 한 번에 받는다 |
|
|
77
|
+
| `placement` | `'floor' \| 'ceiling' \| 'center'` | | 어느 면에 붙나 — 아래 |
|
|
78
|
+
| `animations` | `AnimationClip[]` | | 시각이 흘러서 움직이는 것 — 아래 |
|
|
79
|
+
| `parameters` | `FigureParameter[]` | | 값이 바뀌어서 움직이는 것 — 아래 |
|
|
80
|
+
| `capabilities`| `FigureCapability[]` | | 흐름에서 무엇을 하나 — 아래 |
|
|
81
|
+
|
|
82
|
+
### `placement` — 어느 면에 붙나
|
|
83
|
+
|
|
84
|
+
컨베이어는 바닥에 서고 OHT 는 천장에 매달린다. 그것은 **그 종류의 사실**이지 놓는 사람이
|
|
85
|
+
매번 정할 것이 아니다. 그래서 정본이 지고 다니고, 인스턴스가 배치될 때 씬이 높이를 정한다.
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
floor 바닥에 선다. 안 적으면 이것이다 컨베이어 · 랙 · 작업대
|
|
89
|
+
ceiling 천장에 매달린다 OHT · 호이스트 · 크레인
|
|
90
|
+
center 공중에 뜬다. 기준 면이 없다 드론 · 센서
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**저작하는 좌표는 이것과 무관하다.** 어느 값이든 부품은 `base` 상자 안의 같은 자리에 적힌다
|
|
94
|
+
— `position.y` 는 언제나 상자 **중심**에서 잰다. 천장 기준 도형이라고 해서 숫자가 뒤집히지
|
|
95
|
+
않는다. 뒤집으면 저작자가 적는 수가 드롭다운 하나에 따라 다른 면을 뜻하게 된다.
|
|
60
96
|
|
|
61
97
|
## `FigurePart` — 부품
|
|
62
98
|
|
|
@@ -75,6 +111,7 @@ pitch: 150 // 롤러 중심 간 거리 150mm
|
|
|
75
111
|
| `repeat` | `{ axis, pitch }` | | `sizing` 이 `'repeat'` 일 때 필수 (`pitch` 단위: mm) |
|
|
76
112
|
| `capability`| `PartCapability` | | 부품이 갖는 닻(능력). 그리지 않고 자리(`slot`) 또는 문(`port`)으로 컴파일된다 |
|
|
77
113
|
| `label` | `LabelSpec` | | 텍스트. 아래 표 |
|
|
114
|
+
| `parent` | `string` | | 붙어 있는 부품의 이름. 없으면 figure 의 틀에 붙는다. **부모를 적는 자리는 여기 하나다** — 아래 「관절」 |
|
|
78
115
|
|
|
79
116
|
### `PrimitiveKind`
|
|
80
117
|
|
|
@@ -136,23 +173,41 @@ mesh 계열은 치수만으로 선다. extrude 계열은 2D 단면을 위로 민
|
|
|
136
173
|
한 Figure 에 `LIMITS.surfaceTextures`(1) 개까지 봐준다. 넘으면 위반이고
|
|
137
174
|
원가표(`costOf`)의 재질 수와 점수(`scoreOf`)에 그대로 드러난다.
|
|
138
175
|
|
|
139
|
-
### 움직임 —
|
|
176
|
+
### 움직임 — 두 갈래다
|
|
140
177
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
178
|
+
도형이 움직이는 까닭은 둘 중 하나다.
|
|
179
|
+
|
|
180
|
+
**시각이 흘러서 움직인다.** 컨베이어 롤러는 아무도 안 봐도 돌고 있다. 인스턴스가 주는
|
|
181
|
+
수는 **배속**이고, 1 이 보통이고 0 이면 선다. 이런 것이 `animations` 다.
|
|
182
|
+
|
|
183
|
+
**값이 바뀌어서 움직인다.** 호이스트는 재생되는 것이 아니라 **지금 1200mm 내려와 있는**
|
|
184
|
+
것이다. 인스턴스가 주는 수는 **그 물리량**이고, 그 값이 곧 자세다. 이런 것이
|
|
185
|
+
`parameters` 다.
|
|
186
|
+
|
|
187
|
+
한동안 뒤엣것이 앞엣것 안에 `drive: 'hold'` 로 들어 있었다. 형식의 판단은 맞았는데
|
|
188
|
+
**자리와 이름이 틀려서**, 「호이스트 길이를 변수로 노출하라」는 요청에 저작자도 AI 도
|
|
189
|
+
이것을 못 찾았다 — 애니메이션 칸에 있을 거라고 생각하지 않는다. 지금은 둘이 갈라져 있고,
|
|
190
|
+
`drive` 칸은 없어졌다. 업계도 같은 자리에서 가른다: Unity 의 Animation Clip 과 Blend Tree
|
|
191
|
+
파라미터, Blender 의 Action 과 Driver.
|
|
144
192
|
|
|
145
193
|
```
|
|
146
|
-
FigureSource.animations[]
|
|
147
|
-
└ channels[]
|
|
194
|
+
FigureSource.animations[] 시각이 흐르는 것
|
|
195
|
+
└ channels[] (부품, 경로) + 키프레임
|
|
196
|
+
|
|
197
|
+
FigureSource.parameters[] 값이 자세를 만드는 것
|
|
198
|
+
├ range 인스턴스와 주고받는 물리량 — mm · deg · %
|
|
199
|
+
└ clip.channels[] 같은 채널 형식이다
|
|
148
200
|
```
|
|
149
201
|
|
|
150
|
-
|
|
202
|
+
**곡선은 둘 다 glTF 그대로다.** 이 형식을 다른 3D 형식으로 내보낼 수 있어야 하고, 그러려면
|
|
203
|
+
구조가 같아야 한다. 내보내면 평범한 glTF 애니메이션이 나가고, 못 받는 쪽은 **`range` 선언만
|
|
204
|
+
잃고 형상은 잃지 않는다.**
|
|
205
|
+
|
|
206
|
+
#### `AnimationClip` — 시각이 흐르는 것
|
|
151
207
|
|
|
152
208
|
| 필드 | 타입 | 뜻 |
|
|
153
209
|
| ---------- | --------------------- | ------------------------------------------------------------ |
|
|
154
|
-
| `name` | `string` | **저장되는 식별자.** 인스턴스가 이 이름으로
|
|
155
|
-
| `drive` | `'loop' \| 'hold'` | 안 적으면 `loop` |
|
|
210
|
+
| `name` | `string` | **저장되는 식별자.** 인스턴스가 이 이름으로 배속을 준다 |
|
|
156
211
|
| `channels` | `AnimationChannel[]` | 하나는 있어야 한다 |
|
|
157
212
|
|
|
158
213
|
길이는 적지 않는다 — **마지막 키의 시각이 곧 길이다.** 따로 적으면 어긋날 수 있고,
|
|
@@ -162,7 +217,7 @@ FigureSource.animations[] clip
|
|
|
162
217
|
|
|
163
218
|
| 필드 | 타입 | 뜻 |
|
|
164
219
|
| --------------- | ---------------------------------------- | ---------------------------------------------------------- |
|
|
165
|
-
| `target` | `string` | 어느 부품인가. `FigurePart.name
|
|
220
|
+
| `target` | `string` | 어느 부품인가. `FigurePart.name`. **관절 이름이면 관절을 모는 channel 이다** — 아래 「관절」 |
|
|
166
221
|
| `path` | `'translation' \| 'rotation' \| 'scale'` | 무엇을 움직이나 |
|
|
167
222
|
| `pivot` | `Vec3` | 회전 중심. `rotation` 에서만. 안 적으면 부품 제 중심 |
|
|
168
223
|
| `interpolation` | `'linear' \| 'step'` | 안 적으면 `linear` |
|
|
@@ -187,24 +242,14 @@ scale 배율. 1 이 제 크기
|
|
|
187
242
|
| --- | --- | --- |
|
|
188
243
|
| `target` 이 부품 **이름** | 노드 인덱스 | 이 형식은 처음부터 이름으로 가리킨다 |
|
|
189
244
|
| 회전 키가 **오일러 도** | 쿼터니언 | 손으로 적는 형식이다. `transform.rotation` 과 같은 약속 |
|
|
190
|
-
| `pivot` 이 채널에 | 부모 노드를 끼운다 | 부품이 겹치지 않아 끼울 노드가
|
|
245
|
+
| `pivot` 이 채널에 | 부모 노드를 끼운다 | 부품이 겹치지 않아 끼울 노드가 없다. 부모에 대해 도는 부품은 관절(`joints`)이 그 노드다 |
|
|
191
246
|
| `weights` 없음 | 있음 | 모프 타깃이 없다 |
|
|
192
|
-
|
|
|
247
|
+
| 값이 모는 곡선을 따로 갖는다(`parameters`) | 전부 애니메이션 | 트윈은 타임라인을 재생하지 않는다 |
|
|
193
248
|
| 채널·샘플러를 합침 | 나뉨 | 나눔은 정점 데이터를 아끼는 장치다. 손으로 적을 때는 한 겹일 뿐 |
|
|
194
249
|
|
|
195
250
|
**내보낼 때 주의**: 회전을 쿼터니언으로 바꾸면 **360° 한 바퀴가 항등이 되어** 키 둘로는
|
|
196
251
|
표현이 안 된다. 180° 넘는 구간은 내보내는 쪽이 쪼개야 한다 — 표준 익스포터가 하는 일이다.
|
|
197
252
|
|
|
198
|
-
#### `drive` — 트윈이 타임라인을 재생하지 않기 때문에 있다
|
|
199
|
-
|
|
200
|
-
clip 은 `t → 변환` 함수다. `t` 를 시계가 아니라 **상태**에서 받으면 트윈이 원하는 것이
|
|
201
|
-
그대로 된다.
|
|
202
|
-
|
|
203
|
-
```
|
|
204
|
-
loop 시각이 흐른다. 인스턴스가 주는 값은 배속 도는 롤러
|
|
205
|
-
hold 시각이 멈춰 있다. 인스턴스가 주는 값은 0~1 위치 열린 정도
|
|
206
|
-
```
|
|
207
|
-
|
|
208
253
|
그래서 「진동」·「맥동」은 별도 종류가 아니라 **왕복하는 키프레임**이다.
|
|
209
254
|
|
|
210
255
|
```js
|
|
@@ -216,6 +261,145 @@ hold 시각이 멈춰 있다. 인스턴스가 주는 값은 0~1 위치 열
|
|
|
216
261
|
]}]}
|
|
217
262
|
```
|
|
218
263
|
|
|
264
|
+
#### `FigureParameter` — 값이 자세를 만드는 것
|
|
265
|
+
|
|
266
|
+
호이스트가 얼마나 내려왔나, 포크가 얼마나 올라갔나, 닙이 얼마나 닫혔나. 트윈은 「클립을
|
|
267
|
+
재생하라」가 아니라 **「지금 1200mm 내려와 있다」**를 준다.
|
|
268
|
+
|
|
269
|
+
| 필드 | 타입 | 뜻 |
|
|
270
|
+
| --------- | ---------------- | ----------------------------------------------------------- |
|
|
271
|
+
| `name` | `string` | **저장되는 식별자.** 인스턴스가 이 이름으로 값을 준다 |
|
|
272
|
+
| `label` | `string` | 화면에 보일 이름. 없으면 `name` |
|
|
273
|
+
| `range` | `ParameterRange` | 주고받는 물리량. `{ unit, min, max }` |
|
|
274
|
+
| `default` | `number` | 아무도 안 줬을 때. 없으면 `range.min` |
|
|
275
|
+
| `clip` | `{ duration?, channels }` | 자세. `AnimationClip` 과 같은 모양이되 이름이 없고, 키의 `at` 이 값의 구간이다 |
|
|
276
|
+
|
|
277
|
+
**`range` 가 이 기능의 요점이다.** 곡선의 값 축은 0~1 인데(glTF 의 morph target `weights`,
|
|
278
|
+
Unity 의 파라미터와 같다), 저작자와 인스턴스가 주고받는 것은 **mm 다.** 「길이값을 변수로
|
|
279
|
+
노출한다」는 그 사이가 있어야 성립한다. 런타임이 맵한다.
|
|
280
|
+
|
|
281
|
+
`unit` 은 형식이 해석하지 않는다. 화면이 보이고 인스턴스가 읽는 낱말이다 — `mm` · `deg` ·
|
|
282
|
+
`%` 처럼. 형식이 단위를 판정하려 들면 단위 표를 형식이 갖게 되고, 그것은 형식의 일이 아니다.
|
|
283
|
+
|
|
284
|
+
**키의 `at` 은 초가 아니라 값의 구간이다 — `0` 이상 `1` 이하.** `0` 이 `range.min`, `1` 이
|
|
285
|
+
`range.max` 의 자세다. `1` 을 넘는 키는 어떤 값으로도 닿지 않으므로 검증이 거절한다. 키가 구간을
|
|
286
|
+
다 덮을 필요는 없다 — 첫 키 앞과 마지막 키 뒤는 끝 키의 자세에 머문다(glTF 의 범위 밖 규칙과 같다).
|
|
287
|
+
|
|
288
|
+
**전이 시간은 `clip.duration` 이다.** `min` 에서 `max` 까지 전 구간을 움직이는 **초**이고, 곧 그
|
|
289
|
+
기계의 속도다. 값이 v₁ 에서 v₂ 로 바뀌면 `|v₂−v₁| × duration` 동안 직선으로 간다. **ease 는 없다.
|
|
290
|
+
기계는 연출하지 않는다.** `duration` 이 없거나 `0` 이면 즉시다. 같은 도형이라도 설비마다 속도가
|
|
291
|
+
다르면 인스턴스가 `state.parameterDurations` 로 덮는다.
|
|
292
|
+
|
|
293
|
+
키가 값 축에 있고 시간은 `duration` 한 칸에 있으므로 glTF 와는 손실 없이 오간다.
|
|
294
|
+
|
|
295
|
+
```
|
|
296
|
+
내보낼 때 glTF 키 시각 = at × (duration || 1)
|
|
297
|
+
받아들일 때 가장 늦은 키 시각으로 나눠 at 을 만들고, 그 시각을 duration 으로 둔다
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
```js
|
|
301
|
+
// 2.5 초에 걸쳐 1800mm 를 오르내리는 호이스트
|
|
302
|
+
{
|
|
303
|
+
name: 'hoist',
|
|
304
|
+
label: '갈고리 높이',
|
|
305
|
+
range: { unit: 'mm', min: 0, max: 1800 },
|
|
306
|
+
default: 0,
|
|
307
|
+
clip: { duration: 2.5, channels: [{ target: 'hook', path: 'translation', keys: [
|
|
308
|
+
{ at: 0, value: { x: 0, y: 0, z: 0 } },
|
|
309
|
+
{ at: 1, value: { x: 0, y: -1800, z: 0 } }
|
|
310
|
+
]}]}
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
clip 의 `drive` 칸은 없다. 옛 원본의 `drive: 'hold'` 는 판 2 로 옮길 때 파라미터로 함께 옮겼고, 형식은 그 칸을
|
|
315
|
+
거절한다.
|
|
316
|
+
|
|
317
|
+
이름은 **두 목록이 나눠 쓴다.** 인스턴스는 이름 하나로 값을 주므로 clip 과 파라미터가 같은
|
|
318
|
+
이름을 가지면 어느 쪽에 준 것인지 알 수 없고, 검증이 그것을 막는다.
|
|
319
|
+
|
|
320
|
+
### 관절 — `FigureSource.joints`
|
|
321
|
+
|
|
322
|
+
> ADR-0066 (`operato-twin/design/04-decisions.md`)
|
|
323
|
+
|
|
324
|
+
로봇 팔의 어깨가 돌면 팔꿈치 · 손목 · 집게가 **함께** 돈다. 부품 하나를 `pivot` 중심으로 돌리는 channel 로는
|
|
325
|
+
이것을 적을 수 없다. 부품마다 곡선이 따로라 어깨와 팔꿈치가 따로 움직일 때 회전이 합쳐지지 않는다. 관절이 그
|
|
326
|
+
부모 · 자식을 준다. URDF 의 link · joint 트리, glTF 의 노드 계층과 같은 모양이다.
|
|
327
|
+
|
|
328
|
+
**부모는 부품이 말하고, 움직임은 관절이 말한다.**
|
|
329
|
+
|
|
330
|
+
- `FigurePart.parent` 가 「이 부품은 어느 부품에 붙어 있나」다. 부모를 적는 자리는 여기 하나다.
|
|
331
|
+
- 관절은 「그 부품(`child`)이 부모에 대해 어떻게 움직이나」만 말한다. 관절에는 `parent` 칸이 없다. 관절의 부모는
|
|
332
|
+
`child` 부품의 `parent` 다.
|
|
333
|
+
- 관절 없이 `parent` 만 있는 부품은 부모와 **함께** 움직인다(고정 부착). URDF 로 내보내면 한 link 의 visual 여럿이 된다.
|
|
334
|
+
- 부모를 따라가면 figure 의 틀에 닿아야 한다. 되돌아오면 `parent-cycle` 이다. 한 부품에 관절은 하나다.
|
|
335
|
+
|
|
336
|
+
| 필드 | 타입 | 뜻 |
|
|
337
|
+
| -------- | --------------------------------------------- | ------------------------------------------------------------------------------------- |
|
|
338
|
+
| `name` | `string` | **저장되는 식별자.** channel 이 이 이름으로 관절을 몬다. 부품 이름과 겹칠 수 없다 |
|
|
339
|
+
| `child` | `string` | 움직이는 부품 |
|
|
340
|
+
| `type` | `'revolute' \| 'prismatic' \| 'continuous'` | 도는가(한계 안), 미끄러지는가(한계 안), 끝없이 도는가 |
|
|
341
|
+
| `origin` | `Vec3` | 돌거나 미끄러지는 기준점. figure 좌표(판 2 — y 는 상자 밑면에서) |
|
|
342
|
+
| `axis` | `Vec3` | 방향. 회전은 오른손 법칙. 길이는 상관없고 0 은 안 된다 |
|
|
343
|
+
| `limits` | `{ min, max }` | revolute 는 도, prismatic 은 mm. **0 을 품어야 한다.** continuous 에는 적지 않는다 |
|
|
344
|
+
|
|
345
|
+
**그린 자세가 관절 값 0 이다.** 모든 부품의 `transform` 은 지금처럼 휴지 자세를 figure 좌표로 적는다. 계층은
|
|
346
|
+
compile 이 만든다. 그래서 `limits` 는 0 을 품어야 한다 — 그린 자세가 그 기계가 취할 수 없는 자세일 수는 없다.
|
|
347
|
+
|
|
348
|
+
#### 관절을 모는 channel
|
|
349
|
+
|
|
350
|
+
값의 문은 `parameters` 하나다(ADR-0051). `parameters[].clip.channels` 의 `target` 이 관절 이름을 가리키면
|
|
351
|
+
그 channel 은 관절을 몬다. 끝없이 도는 관절은 지금처럼 loop clip(`animations`)으로 몰 수 있다.
|
|
352
|
+
|
|
353
|
+
- **`path` 도 `pivot` 도 적지 않는다.** 축과 중심은 관절이 이미 말했다.
|
|
354
|
+
- **키의 값은 관절의 좌표 하나다** — revolute · continuous 는 도, prismatic 은 mm. `limits` 밖이면 `out-of-range`.
|
|
355
|
+
- 노드의 회전(glTF 로 내보낼 때의 quaternion 포함)은 관절의 축에서 만든다.
|
|
356
|
+
- 부품을 모는 channel(`path` · `pivot`)은 그대로 쓴다. 그 부품이 관절 아래에 있으면 **그 관절의 틀 안에서** 움직인다.
|
|
357
|
+
|
|
358
|
+
```js
|
|
359
|
+
// 팔꿈치를 -150° ~ 150° 로, 3 초에 전 구간
|
|
360
|
+
{ name: 'elbow-flex', range: { unit: 'deg', min: -150, max: 150 },
|
|
361
|
+
clip: { duration: 3, channels: [{ target: 'elbow-flex', keys: [{ at: 0, value: -150 }, { at: 1, value: 150 }] }] } }
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
#### 묶음 · 닻 · 크기 반응
|
|
365
|
+
|
|
366
|
+
- **묶음은 (관절의 틀, 재질)로 합친다.** 합친 geometry 는 변환을 하나만 받으므로 다른 틀에 있는 부품은 합칠 수
|
|
367
|
+
없다. 부품의 틀은 부모를 따라 올라가며 처음 만나는 관절이다(자기 자신이 `child` 면 그 관절). 게이트의 draw call 셈도
|
|
368
|
+
같은 함수(`mergeKeyOf`)를 쓴다.
|
|
369
|
+
- **slot · port 의 닻은 자기 부품의 틀을 따라 움직인다**(`BlueprintAnchor.joint`).
|
|
370
|
+
- **크기 반응은 휴지 자세에서 먼저 푼다.** 관절의 `origin` 은 `child` 의 부모 부품이 받는 크기 반응을 따라 옮겨진다
|
|
371
|
+
(`jointOriginPosition`). 관절 노드에는 배율을 걸지 않는다 — 축마다 배율이 다른 인스턴스에서 자식을 돌려도 찌그러지지
|
|
372
|
+
않는다.
|
|
373
|
+
- **옮김의 크기는 배율을 따른다.** 부품을 옮기는 channel(`translation`)은 인스턴스 배율이 걸린 틀 안에서 더해지므로
|
|
374
|
+
축마다 인스턴스 배율만큼 늘어난다 — 높이를 두 배로 놓은 호이스트의 200mm 는 400mm 를 간다(things-scene
|
|
375
|
+
`FigureRealObject` 가 `object3d.scale` 에 인스턴스 배율을 걸고 그 안에서 옮긴다). prismatic 관절의 행정도 이것과
|
|
376
|
+
**같게** 한다: 관절의 부모 틀에서 `axis × 값` 의 각 축 성분에 그 축의 인스턴스 배율을 곱한다. 같은 figure 안에서
|
|
377
|
+
옮김의 거동이 둘이면 안 되기 때문이다(ADR-0066 결정 3).
|
|
378
|
+
|
|
379
|
+
#### 짓지 않은 것
|
|
380
|
+
|
|
381
|
+
IK(`ikChains`) · 감쇠 스프링 · `coupling` · `maxSpeed` · `acceleration` · `smoothing` 은 형식에 없다. 소비처가 생기면
|
|
382
|
+
그때 따로 판정한다. 관절이 있는지는 형식에서 읽히므로 능력 어휘(`capabilities`)에 관절용 이름을 두지 않는다.
|
|
383
|
+
|
|
384
|
+
### `FigureSource.capabilities` — 이 도형이 흐름에서 무엇을 하나
|
|
385
|
+
|
|
386
|
+
파렛트는 물건을 **담고**, 컨베이어는 **흘리고**, 신호탑은 아무것도 담지 않는다. 그리는
|
|
387
|
+
것만으로는 컨베이어가 컨베이어가 아니다.
|
|
388
|
+
|
|
389
|
+
```
|
|
390
|
+
Capacity 자리를 갖는다
|
|
391
|
+
Holdable 담긴 것을 함께 들고 그린다
|
|
392
|
+
Transferable 자리를 잡아 두고 넘겨받는다 — Capacity 나 FlowNode 가 함께 있어야 한다
|
|
393
|
+
FlowNode 흐름의 마디다. 위의 셋을 스스로 포함한다
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
**닫힌 목록이다**(`FIGURE_CAPABILITIES`). 한동안 이 목록이 씬에만 있어서 검증이 이름을 못
|
|
397
|
+
봤고, AI 가 낸 `capabilities: ['hoist']` 가 **검증을 지나고 발행을 지나** 렌더링에서 죽었다.
|
|
398
|
+
게이트는 사실이 사는 곳에 있어야 한다.
|
|
399
|
+
|
|
400
|
+
`hoist` 는 능력이 아니다. 능력은 **흐름에서 무엇을 하는가**이고, 호이스트의 연장은
|
|
401
|
+
**자세를 어떻게 구동하는가**(위의 파라미터)다. 다른 축이다.
|
|
402
|
+
|
|
219
403
|
### `LabelSpec`
|
|
220
404
|
|
|
221
405
|
| 필드 | 타입 | 뜻 |
|
|
@@ -366,6 +550,7 @@ sizing: 'fixed', anchor: { x: 'span', y: 'max', z: 'span' }
|
|
|
366
550
|
| `groups` | `BlueprintGroup[]` | 재질별로 병합된 묶음 |
|
|
367
551
|
| `animations` | `BlueprintClip[]` | 움직임. 원본에 적은 것 그대로 |
|
|
368
552
|
| `labels` | `BlueprintLabel[]` | 라벨. `at` 이 부품 이름 |
|
|
553
|
+
| `joints` | `BlueprintJoint[]` | 관절. **부모가 먼저 온다.** 원본에 관절이 없으면 칸 자체가 없다 |
|
|
369
554
|
| `violations` | `Violation[]` | 정책을 넘은 것. 저작 화면이 보인다 |
|
|
370
555
|
|
|
371
556
|
### `BlueprintGroup` — 병합 단위 = draw call 하나
|
|
@@ -375,8 +560,19 @@ sizing: 'fixed', anchor: { x: 'span', y: 'max', z: 'span' }
|
|
|
375
560
|
| `materialKey` | MaterialBank 키 |
|
|
376
561
|
| `material` | 원본 데이터의 `PartMaterial` 그대로 |
|
|
377
562
|
| `materialSlot` | 재질 슬롯 |
|
|
378
|
-
| `animated` | clip
|
|
563
|
+
| `animated` | clip 이나 parameter 가 움직이는 부품이 든 그룹인지. **그러면 부품을 하나만 갖는다** |
|
|
379
564
|
| `members` | `BlueprintMember[]` |
|
|
565
|
+
| `joint` | 이 묶음이 움직이는 관절의 틀. 없으면 figure 의 틀 |
|
|
566
|
+
|
|
567
|
+
### `BlueprintJoint` — 관절 노드
|
|
568
|
+
|
|
569
|
+
| 필드 | 뜻 |
|
|
570
|
+
| ------------------ | --------------------------------------------------------------------------- |
|
|
571
|
+
| `name` · `type` · `child` · `limits` | 원본에서 |
|
|
572
|
+
| `origin` | 휴지 자세의 기준점. **상자 중심 좌표** — 청사진의 다른 자리와 같다 |
|
|
573
|
+
| `axis` | 길이 1 로 맞춘 방향 |
|
|
574
|
+
| `parent` | 이 관절이 놓이는 관절의 틀. 없으면 figure 의 틀 |
|
|
575
|
+
| `attach` | `child` 의 부모 부품. 크기 반응에서 `origin` 이 이 부품을 따라간다. 없으면 figure 의 틀에 붙은 관절이다 |
|
|
380
576
|
|
|
381
577
|
### `BlueprintMember` — 묶음 안의 부품
|
|
382
578
|
|
package/docs/validation.md
CHANGED
|
@@ -49,6 +49,7 @@ const { errors, violations } = validate(source)
|
|
|
49
49
|
| 코드 | 언제 | 왜 |
|
|
50
50
|
| --------------- | ----------------------------------------- | --------------------------------------------------------- |
|
|
51
51
|
| `missing-type` | `type` 이 없거나 빈 문자다 | **저장되는 식별자다.** 씬이 이 이름으로 청사진을 찾는다 |
|
|
52
|
+
| `source-version-unsupported` | `version` 이 2 가 아니거나 없다 | 판 1 은 y 를 상자 중심에서 쟀다. 판을 적지 않은 원본도 판 1 이다. 옮긴 원본만 읽는다 |
|
|
52
53
|
| `missing-base` | `base` 가 없다 | 배치 크기가 이 비율에서 나온다 |
|
|
53
54
|
| `missing-parts` | `parts` 가 없거나 비었다 | 부품이 하나는 있어야 한다. 여기서 검사를 멈춘다 |
|
|
54
55
|
| `bad-style-kit` | `styleKit` 이 빈 문자이거나 문자가 아니다 | |
|
|
@@ -97,8 +98,9 @@ const { errors, violations } = validate(source)
|
|
|
97
98
|
| `keys-out-of-order` | 키의 시각이 앞의 것보다 크지 않다 | 표본추출이 뜻을 잃는다. 화면에서는 「이상하게 움직인다」로만 보인다 |
|
|
98
99
|
|
|
99
100
|
clip 의 이름은 부품과 같은 코드를 쓴다 — 없으면 `missing-name`, 겹치면 `duplicate-name`.
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
경로와 `interpolation` 이 목록 밖이면 `not-allowed`, 키의 시각이 음수면 `not-positive` 다.
|
|
102
|
+
|
|
103
|
+
**같은 검사가 파라미터의 곡선에도 그대로 돈다.** 채널 모양은 어느 쪽이든 같아야 한다.
|
|
102
104
|
|
|
103
105
|
### 예외 표면
|
|
104
106
|
|
|
@@ -115,16 +117,46 @@ clip 의 이름은 부품과 같은 코드를 쓴다 — 없으면 `missing-name
|
|
|
115
117
|
|
|
116
118
|
### 능력 (`capabilities`)
|
|
117
119
|
|
|
118
|
-
배치된 도형이 씬 컴포넌트로서 부여받는 능력의 선언이다.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
+
같은 이유로 던지는데, **그때는 이미 발행된 뒤다** — 그래서 여기서 본다.
|
|
122
140
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
|
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`.
|
|
128
160
|
|
|
129
161
|
### 파라미터 (`parameters`)
|
|
130
162
|
|
|
@@ -144,11 +176,19 @@ things-scene** 이므로(`registerCapabilityMeta`), 여기서는 **모양만**
|
|
|
144
176
|
| --------------- | ------------------------------------------------------------------ |
|
|
145
177
|
| `missing-range` | `range` 나 `range.unit` 이 없다 — 인스턴스가 어느 값을 줄지 모른다 |
|
|
146
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 전 구간)이 따로 말한다.
|
|
147
185
|
|
|
148
186
|
이름은 `animations` 의 clip 이름과도 겹칠 수 없다. 인스턴스는 둘을 이름으로 부르므로 겹치면 어느 쪽에
|
|
149
187
|
값을 준 것인지 알 수 없다.
|
|
150
188
|
|
|
151
|
-
|
|
189
|
+
clip 에 `drive` 칸이 있으면 `not-allowed` 다. 값으로 움직이는 자세가 `drive: 'hold'` 로 애니메이션 안에 있던
|
|
190
|
+
원본은 판 2 로 옮길 때 파라미터로 함께 옮겼다(키는 마지막 키의 시각으로 나눠 값의 구간에, `duration` 없음).
|
|
191
|
+
형식은 옛 모양을 읽지 않는다.
|
|
152
192
|
|
|
153
193
|
부품의 `capability` 는 **그 능력을 어느 부품이 받쳐 주나**를 말한다. 역할은 셋이다.
|
|
154
194
|
|
|
@@ -198,7 +238,7 @@ things-scene** 이므로(`registerCapabilityMeta`), 여기서는 **모양만**
|
|
|
198
238
|
| --------------------------- | -------------------------------------------- | --------------------------------------------------------------------------------------------- |
|
|
199
239
|
| `detail-level-missing` | — | 등급이 없으면 부품 수 예산을 잡을 수 없다 |
|
|
200
240
|
| `too-many-parts` | `PART_LIMIT[detailLevel]` (S 3 · M 8 · L 20) | |
|
|
201
|
-
| `too-many-animated-parts` | `LIMITS.animatedParts` (2) | 움직이는 부품마다 묶음이 하나 늘고, 프레임마다 갱신이
|
|
241
|
+
| `too-many-animated-parts` | `LIMITS.animatedParts` (2) | 움직이는 부품마다 묶음이 하나 늘고, 프레임마다 갱신이 는다. clip 과 parameter 가 움직이는 부품을 함께 센다 |
|
|
202
242
|
| `too-many-material-groups` | `LIMITS.materialGroups` (3) | **자산당 draw call 이 는다.** 세는 단위는 `groupKeyOf` — 컴파일러가 합치는 단위와 같은 식이다 |
|
|
203
243
|
| `too-many-transparent` | `LIMITS.transparentMaterials` (1) | 투명 재질은 별도 정렬 경로를 탄다 |
|
|
204
244
|
| `too-many-always-labels` | 1 | `'always'` 라벨은 대량 배치에서 DOM·텍스처를 늘린다 |
|
|
@@ -218,7 +258,7 @@ things-scene** 이므로(`registerCapabilityMeta`), 여기서는 **모양만**
|
|
|
218
258
|
|
|
219
259
|
| 코드 | 언제 | 왜 |
|
|
220
260
|
| --------------------------- | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------- |
|
|
221
|
-
| `part-outside-base` | 부품이 `±base/2
|
|
261
|
+
| `part-outside-base` | 부품이 상자 밖으로 나간다(x·z 는 중심에서 `±base/2`, y 는 밑면 0 에서 `base.y`). 회전을 포함한 실제 부피로 잰다 | `base` 는 **나누는 수**다(`instanceScale = width/base.x`). 넘치면 그 수가 거짓말을 하고 도면에서 크기와 높이가 둘 다 틀린다 |
|
|
222
262
|
| `parts-off-placement-face` | 바닥 기반인데 상자 밑면에 안 닿는다 · 천정 기반인데 윗면에 안 닿는다 | 「선다」·「매달린다」고 선언해 놓고 떠 있다. **원점 규약이 어긋난 것**이지 상자가 큰 것이 아니다 |
|
|
223
263
|
|
|
224
264
|
허용 오차는 `SKIN`(0.5) 하나다 — 발행 관문이 「덩어리가 갈라졌나」를 재는 값과 같다. 오차 상수가
|
|
@@ -231,6 +271,22 @@ things-scene** 이므로(`registerCapabilityMeta`), 여기서는 **모양만**
|
|
|
231
271
|
|
|
232
272
|
**이 둘은 발행을 막는다.** 이미 발행된 것은 그대로 서 있고, 고치기 전에는 다시 발행하지 못한다.
|
|
233
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
|
+
|
|
234
290
|
### draw call 을 세는 법
|
|
235
291
|
|
|
236
292
|
```
|
|
@@ -243,7 +299,7 @@ draw call = 합쳐진 묶음 하나당 1
|
|
|
243
299
|
|
|
244
300
|
| 무엇 | 왜 |
|
|
245
301
|
| --- | --- |
|
|
246
|
-
| clip
|
|
302
|
+
| clip 이나 parameter 가 움직이는 부품 | 합치면 그 부품만 따로 움직일 수 없다 |
|
|
247
303
|
| `sizing` 이 `scale` 이 아닌 부품 | 합친 geometry 는 변환을 하나만 받는다 |
|
|
248
304
|
| `repeat` 부품 | 한 부품이 여러 mesh 다 |
|
|
249
305
|
|
|
@@ -278,3 +334,24 @@ const blueprint = compile(source)
|
|
|
278
334
|
|
|
279
335
|
`violations` 는 던지지 않고 **청사진에 실려 나간다**(`blueprint.violations`). 저작
|
|
280
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
|
+
통째로 조용히 버려진다.**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hatiolab/figure-model",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.23",
|
|
4
4
|
"description": "Figure 저작 결과의 정본 형식과 검증 — 재질별로 병합되고 이름이 붙은 부품 그래프. 3D 로 저작한 형상 하나가 씬 컴포넌트로 서는 데 필요한 것을 담는다.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"figure",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"scripts": {
|
|
65
65
|
"build": "tsc -p tsconfig.build.json",
|
|
66
66
|
"check": "tsc -p tsconfig.json --noEmit",
|
|
67
|
-
"test": "node --test src/*.test.ts",
|
|
67
|
+
"test": "npm run check && node --test src/*.test.ts",
|
|
68
68
|
"test:coverage": "node --test --experimental-test-coverage --test-coverage-lines=100 --test-coverage-branches=95 --test-coverage-functions=100 src/*.test.ts",
|
|
69
69
|
"bench": "node src/perf.bench.ts",
|
|
70
70
|
"format": "prettier --write \"src/**/*.ts\" \"docs/**/*.md\" \"*.md\" \"*.json\"",
|