@hatiolab/figure-model 0.1.0
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/CHANGELOG.md +45 -0
- package/README.md +163 -0
- package/dist/blueprint.d.ts +32 -0
- package/dist/blueprint.d.ts.map +1 -0
- package/dist/blueprint.js +163 -0
- package/dist/blueprint.js.map +1 -0
- package/dist/cost-chart.d.ts +35 -0
- package/dist/cost-chart.d.ts.map +1 -0
- package/dist/cost-chart.js +150 -0
- package/dist/cost-chart.js.map +1 -0
- package/dist/cost.d.ts +160 -0
- package/dist/cost.d.ts.map +1 -0
- package/dist/cost.js +170 -0
- package/dist/cost.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/keys.d.ts +38 -0
- package/dist/keys.d.ts.map +1 -0
- package/dist/keys.js +57 -0
- package/dist/keys.js.map +1 -0
- package/dist/types.d.ts +360 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +162 -0
- package/dist/types.js.map +1 -0
- package/dist/validate.d.ts +13 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +327 -0
- package/dist/validate.js.map +1 -0
- package/docs/design.md +192 -0
- package/docs/format-survey.md +108 -0
- package/docs/format.md +295 -0
- package/docs/validation.md +159 -0
- package/package.json +80 -0
package/dist/validate.js
ADDED
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright © HatioLab Inc. All rights reserved.
|
|
3
|
+
*
|
|
4
|
+
* Figure 정본 검증.
|
|
5
|
+
*
|
|
6
|
+
* 두 갈래로 돌려준다.
|
|
7
|
+
*
|
|
8
|
+
* errors 형식·정합성이 깨진 것. **컴파일할 수 없다.**
|
|
9
|
+
* violations 형식은 맞으나 정책을 넘은 것. **컴파일을 막지 않는다.**
|
|
10
|
+
*
|
|
11
|
+
* 둘을 가르는 기준은 「이대로 세울 수 있는가」다. 치수가 없으면 세울 수 없으므로
|
|
12
|
+
* error 다. 재질이 넷이면 세울 수는 있고 다만 draw call 이 는다 — violation 이다.
|
|
13
|
+
*
|
|
14
|
+
* 정책 위반을 막지 않는 것은 뜻이 있다. 막으면 사용자가 우회하고, **숫자로 보여
|
|
15
|
+
* 주면 아낀다.** 랜드마크 설비 하나는 정교해도 되며, 그 대가가 도면 예산에 드러나면
|
|
16
|
+
* 된다.
|
|
17
|
+
*
|
|
18
|
+
* 결측을 기본값으로 갈음하지 않는다. 값이 없는 것과 0 인 것은 다르고, 갈음하면
|
|
19
|
+
* 저작자가 빠뜨린 것을 알 수 없다.
|
|
20
|
+
*/
|
|
21
|
+
import { ANIMATION_KINDS, AXES, DETAIL_LEVELS, LABEL_WHENS, LIMITS, MATERIAL_PRESETS, MATERIAL_SLOTS, PART_LIMIT, PIVOT_REQUIRED, PRIMITIVE_KINDS, SEGMENTED_PRIMITIVES, SEGMENT_PRESETS, SIZING_RULES, SURFACE_KINDS } from "./types.js";
|
|
22
|
+
import { groupKeyOf } from "./keys.js";
|
|
23
|
+
function isObject(v) {
|
|
24
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
25
|
+
}
|
|
26
|
+
function isFiniteNumber(v) {
|
|
27
|
+
return typeof v === 'number' && Number.isFinite(v);
|
|
28
|
+
}
|
|
29
|
+
function isNonEmptyString(v) {
|
|
30
|
+
return typeof v === 'string' && v.trim().length > 0;
|
|
31
|
+
}
|
|
32
|
+
class Collector {
|
|
33
|
+
errors = [];
|
|
34
|
+
violations = [];
|
|
35
|
+
error(code, path, message) {
|
|
36
|
+
this.errors.push({ code, path, message });
|
|
37
|
+
}
|
|
38
|
+
violate(code, message, actual, limit) {
|
|
39
|
+
const v = { code, message };
|
|
40
|
+
if (actual !== undefined)
|
|
41
|
+
v.actual = actual;
|
|
42
|
+
if (limit !== undefined)
|
|
43
|
+
v.limit = limit;
|
|
44
|
+
this.violations.push(v);
|
|
45
|
+
}
|
|
46
|
+
/** 유한한 수인지. 아니면 error 를 남기고 false. */
|
|
47
|
+
finite(v, path, label) {
|
|
48
|
+
if (isFiniteNumber(v))
|
|
49
|
+
return true;
|
|
50
|
+
this.error('not-finite', path, `${label}는 유한한 수여야 한다`);
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
/** 양수인지. */
|
|
54
|
+
positive(v, path, label) {
|
|
55
|
+
if (!this.finite(v, path, label))
|
|
56
|
+
return false;
|
|
57
|
+
if (v > 0)
|
|
58
|
+
return true;
|
|
59
|
+
this.error('not-positive', path, `${label}는 0 보다 커야 한다`);
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
/** 열거 값인지. */
|
|
63
|
+
oneOf(v, allowed, path, label) {
|
|
64
|
+
if (typeof v === 'string' && allowed.includes(v))
|
|
65
|
+
return true;
|
|
66
|
+
this.error('not-allowed', path, `${label}는 ${allowed.join(' · ')} 중 하나여야 한다`);
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function validateVec3(c, v, path, label) {
|
|
71
|
+
if (!isObject(v)) {
|
|
72
|
+
c.error('not-object', path, `${label}는 { x, y, z } 여야 한다`);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
c.finite(v.x, `${path}.x`, `${label}.x`);
|
|
76
|
+
c.finite(v.y, `${path}.y`, `${label}.y`);
|
|
77
|
+
c.finite(v.z, `${path}.z`, `${label}.z`);
|
|
78
|
+
}
|
|
79
|
+
function validateTransform(c, part, path) {
|
|
80
|
+
const t = part.transform;
|
|
81
|
+
if (!isObject(t)) {
|
|
82
|
+
c.error('missing-transform', `${path}.transform`, 'transform 이 없다');
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
c.finite(t.x, `${path}.transform.x`, 'x');
|
|
86
|
+
c.finite(t.y, `${path}.transform.y`, 'y');
|
|
87
|
+
c.finite(t.z, `${path}.transform.z`, 'z');
|
|
88
|
+
// 평면 단면(extrude)은 depth 가 0 일 수 있으나 width·height 는 있어야 한다.
|
|
89
|
+
c.positive(t.width, `${path}.transform.width`, 'width');
|
|
90
|
+
c.positive(t.height, `${path}.transform.height`, 'height');
|
|
91
|
+
if (t.depth !== undefined)
|
|
92
|
+
c.finite(t.depth, `${path}.transform.depth`, 'depth');
|
|
93
|
+
else
|
|
94
|
+
c.error('missing-depth', `${path}.transform.depth`, 'depth 가 없다 — 0 으로 갈음하지 않는다');
|
|
95
|
+
if (t.rotation !== undefined) {
|
|
96
|
+
if (!isObject(t.rotation)) {
|
|
97
|
+
c.error('not-object', `${path}.transform.rotation`, 'rotation 은 객체여야 한다');
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
for (const axis of AXES) {
|
|
101
|
+
const value = t.rotation[axis];
|
|
102
|
+
if (value !== undefined)
|
|
103
|
+
c.finite(value, `${path}.transform.rotation.${axis}`, `rotation.${axis}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function validatePart(c, raw, index, seen) {
|
|
109
|
+
const path = `parts[${index}]`;
|
|
110
|
+
if (!isObject(raw)) {
|
|
111
|
+
c.error('not-object', path, '부품은 객체여야 한다');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
// 이름 — 저장되는 식별자다
|
|
115
|
+
if (!isNonEmptyString(raw.name)) {
|
|
116
|
+
c.error('missing-name', `${path}.name`, '부품 이름이 없다 — 바인딩·애니메이션이 이 이름을 가리킨다');
|
|
117
|
+
}
|
|
118
|
+
else if (seen.has(raw.name)) {
|
|
119
|
+
c.error('duplicate-name', `${path}.name`, `부품 이름이 겹친다: ${raw.name}`);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
seen.add(raw.name);
|
|
123
|
+
}
|
|
124
|
+
const isPrimitive = c.oneOf(raw.primitive, PRIMITIVE_KINDS, `${path}.primitive`, 'primitive');
|
|
125
|
+
validateTransform(c, raw, path);
|
|
126
|
+
// 분할 수 — 곡면이 있는 프리미티브만 뜻이 있다
|
|
127
|
+
if (raw.segments !== undefined) {
|
|
128
|
+
if (!isFiniteNumber(raw.segments) || !Number.isInteger(raw.segments)) {
|
|
129
|
+
c.error('not-integer', `${path}.segments`, 'segments 는 정수여야 한다');
|
|
130
|
+
}
|
|
131
|
+
else if (raw.segments < LIMITS.minSegments) {
|
|
132
|
+
c.error('too-few-segments', `${path}.segments`, `segments 는 ${LIMITS.minSegments} 이상이어야 한다`);
|
|
133
|
+
}
|
|
134
|
+
else if (!SEGMENT_PRESETS.includes(raw.segments)) {
|
|
135
|
+
c.violate('segments-off-preset', `${raw.name ?? path}: 분할 수 ${raw.segments} 는 프리셋(${SEGMENT_PRESETS.join(' · ')}) 이 아니다`, raw.segments);
|
|
136
|
+
}
|
|
137
|
+
if (isPrimitive && !SEGMENTED_PRIMITIVES.includes(raw.primitive)) {
|
|
138
|
+
c.violate('segments-ignored', `${raw.name ?? path}: ${raw.primitive} 는 분할 수를 쓰지 않는다`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// extrude 계열의 단면
|
|
142
|
+
if (raw.primitive === 'polygon') {
|
|
143
|
+
const shape = raw.shape;
|
|
144
|
+
const pathPoints = isObject(shape) ? shape.path : undefined;
|
|
145
|
+
if (!Array.isArray(pathPoints) || pathPoints.length < 3) {
|
|
146
|
+
c.error('missing-shape-path', `${path}.shape.path`, 'polygon 은 3 점 이상의 path 가 필요하다');
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
pathPoints.forEach((p, i) => {
|
|
150
|
+
if (!isObject(p) || !isFiniteNumber(p.x) || !isFiniteNumber(p.y)) {
|
|
151
|
+
c.error('bad-shape-point', `${path}.shape.path[${i}]`, 'path 의 점은 { x, y } 여야 한다');
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// 재질 — 색은 토큰으로 온다
|
|
157
|
+
if (!isObject(raw.material)) {
|
|
158
|
+
c.error('missing-material', `${path}.material`, 'material 이 없다');
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
const m = raw.material;
|
|
162
|
+
if (!isNonEmptyString(m.token) && m.preset === undefined) {
|
|
163
|
+
c.error('material-unresolvable', `${path}.material`, 'token 이나 preset 중 하나는 있어야 한다');
|
|
164
|
+
}
|
|
165
|
+
if (m.preset !== undefined)
|
|
166
|
+
c.oneOf(m.preset, MATERIAL_PRESETS, `${path}.material.preset`, 'preset');
|
|
167
|
+
if (m.flatShading !== undefined && typeof m.flatShading !== 'boolean') {
|
|
168
|
+
c.error('not-boolean', `${path}.material.flatShading`, 'flatShading 은 참·거짓이어야 한다');
|
|
169
|
+
}
|
|
170
|
+
if (m.transparent !== undefined && typeof m.transparent !== 'boolean') {
|
|
171
|
+
c.error('not-boolean', `${path}.material.transparent`, 'transparent 는 참·거짓이어야 한다');
|
|
172
|
+
}
|
|
173
|
+
// 팔레트 밖 표면 — 미감의 예외다. 형태만 여기서 본다.
|
|
174
|
+
if (m.surface !== undefined) {
|
|
175
|
+
const u = m.surface;
|
|
176
|
+
if (!isObject(u)) {
|
|
177
|
+
c.error('not-object', `${path}.material.surface`, 'surface 는 객체여야 한다');
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
c.oneOf(u.kind, SURFACE_KINDS, `${path}.material.surface.kind`, 'kind');
|
|
181
|
+
if (!isNonEmptyString(u.ref)) {
|
|
182
|
+
c.error('missing-surface-ref', `${path}.material.surface.ref`, 'ref 가 없다 — 무엇을 쓸지 모른다');
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (raw.slot !== undefined)
|
|
188
|
+
c.oneOf(raw.slot, MATERIAL_SLOTS, `${path}.slot`, 'slot');
|
|
189
|
+
if (raw.sizing !== undefined)
|
|
190
|
+
c.oneOf(raw.sizing, SIZING_RULES, `${path}.sizing`, 'sizing');
|
|
191
|
+
// repeat — sizing 이 repeat 면 필수
|
|
192
|
+
if (raw.sizing === 'repeat') {
|
|
193
|
+
if (!isObject(raw.repeat)) {
|
|
194
|
+
c.error('missing-repeat', `${path}.repeat`, "sizing 이 'repeat' 이면 repeat 가 필요하다");
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
c.oneOf(raw.repeat.axis, AXES, `${path}.repeat.axis`, 'repeat.axis');
|
|
198
|
+
c.positive(raw.repeat.gap, `${path}.repeat.gap`, 'repeat.gap');
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
else if (raw.repeat !== undefined) {
|
|
202
|
+
c.violate('repeat-ignored', `${raw.name ?? path}: sizing 이 'repeat' 가 아니면 repeat 는 쓰이지 않는다`);
|
|
203
|
+
}
|
|
204
|
+
// 애니메이션 포인트
|
|
205
|
+
if (raw.animation !== undefined) {
|
|
206
|
+
if (!isObject(raw.animation)) {
|
|
207
|
+
c.error('not-object', `${path}.animation`, 'animation 은 객체여야 한다');
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
const a = raw.animation;
|
|
211
|
+
const kindOk = c.oneOf(a.kind, ANIMATION_KINDS, `${path}.animation.kind`, 'animation.kind');
|
|
212
|
+
if (a.axis !== undefined)
|
|
213
|
+
c.oneOf(a.axis, AXES, `${path}.animation.axis`, 'animation.axis');
|
|
214
|
+
if (kindOk && a.kind === 'rotation' && a.axis === undefined) {
|
|
215
|
+
c.error('missing-axis', `${path}.animation.axis`, '회전은 축이 있어야 한다');
|
|
216
|
+
}
|
|
217
|
+
// 피봇을 적지 않으면 부품 자기 중심으로 돈다 — geometry.center() 때문
|
|
218
|
+
if (kindOk && PIVOT_REQUIRED.includes(a.kind)) {
|
|
219
|
+
if (a.pivot === undefined) {
|
|
220
|
+
c.error('missing-pivot', `${path}.animation.pivot`, '회전은 피봇이 있어야 한다 — 없으면 부품 자기 중심으로 돈다');
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
validateVec3(c, a.pivot, `${path}.animation.pivot`, 'pivot');
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
else if (a.pivot !== undefined) {
|
|
227
|
+
validateVec3(c, a.pivot, `${path}.animation.pivot`, 'pivot');
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// 라벨
|
|
232
|
+
if (raw.label !== undefined) {
|
|
233
|
+
if (!isObject(raw.label)) {
|
|
234
|
+
c.error('not-object', `${path}.label`, 'label 은 객체여야 한다');
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
if (!isNonEmptyString(raw.label.name)) {
|
|
238
|
+
c.error('missing-label-name', `${path}.label.name`, 'label.name 이 없다');
|
|
239
|
+
}
|
|
240
|
+
if (!isNonEmptyString(raw.label.source)) {
|
|
241
|
+
c.error('missing-label-source', `${path}.label.source`, 'label.source 가 없다 — 무엇을 보일지 모른다');
|
|
242
|
+
}
|
|
243
|
+
c.oneOf(raw.label.when, LABEL_WHENS, `${path}.label.when`, 'label.when');
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function checkPolicy(c, source) {
|
|
248
|
+
const parts = source.parts;
|
|
249
|
+
// 등급 — 없으면 예산을 잡을 수 없다. 조용히 넘기지 않는다.
|
|
250
|
+
if (source.detailLevel === undefined) {
|
|
251
|
+
c.violate('detail-level-missing', '디테일 등급이 없다 — 부품 수 예산을 잡을 수 없다');
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
const limit = PART_LIMIT[source.detailLevel];
|
|
255
|
+
if (parts.length > limit) {
|
|
256
|
+
c.violate('too-many-parts', `등급 ${source.detailLevel} 의 부품 수 한도를 넘었다`, parts.length, limit);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
const animated = parts.filter(p => p.animation !== undefined);
|
|
260
|
+
if (animated.length > LIMITS.animationPoints) {
|
|
261
|
+
c.violate('too-many-animation-points', '애니메이션 포인트가 많다 — 묶음 수와 프레임당 갱신이 는다', animated.length, LIMITS.animationPoints);
|
|
262
|
+
}
|
|
263
|
+
// 묶음 수 = 재질·슬롯 조합 수 + 애니메이션 포인트 수
|
|
264
|
+
const staticGroups = new Set(parts.filter(p => p.animation === undefined).map(groupKeyOf));
|
|
265
|
+
if (staticGroups.size > LIMITS.materialGroups) {
|
|
266
|
+
c.violate('too-many-material-groups', '재질·슬롯 조합이 많다 — 자산당 draw call 이 는다', staticGroups.size, LIMITS.materialGroups);
|
|
267
|
+
}
|
|
268
|
+
const transparent = new Set(parts.filter(p => p.material?.transparent).map(groupKeyOf));
|
|
269
|
+
if (transparent.size > LIMITS.transparentMaterials) {
|
|
270
|
+
c.violate('too-many-transparent', '투명 재질이 많다 — 정렬 비용이 크다', transparent.size, LIMITS.transparentMaterials);
|
|
271
|
+
}
|
|
272
|
+
// 팔레트 밖 표면 — 단색 규칙의 예외다. **종류 수**를 센다(같은 ref 는 한 텍스처다).
|
|
273
|
+
const surfaces = new Set(parts
|
|
274
|
+
.map(p => p.material?.surface)
|
|
275
|
+
.filter((u) => u !== undefined)
|
|
276
|
+
.map(u => `${u.kind}:${u.ref}`));
|
|
277
|
+
if (surfaces.size > LIMITS.surfaceTextures) {
|
|
278
|
+
c.violate('too-many-surface-textures', '팔레트 밖 표면이 많다 — 단색 규칙을 벗어나고 텍스처마다 GPU 메모리를 쓴다', surfaces.size, LIMITS.surfaceTextures);
|
|
279
|
+
}
|
|
280
|
+
const always = parts.filter(p => p.label?.when === 'always');
|
|
281
|
+
if (always.length > 1) {
|
|
282
|
+
c.violate('too-many-always-labels', "'always' 라벨이 여럿이다 — 대량 배치에서 DOM·텍스처가 는다", always.length, 1);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* 정본을 검증한다.
|
|
287
|
+
*
|
|
288
|
+
* 바깥에서 온 데이터(DB · API · 파일)를 받으므로 `unknown` 을 받는다.
|
|
289
|
+
* `errors` 가 비어 있으면 `source` 는 `FigureSource` 로 다룰 수 있다.
|
|
290
|
+
*/
|
|
291
|
+
export function validate(source) {
|
|
292
|
+
const c = new Collector();
|
|
293
|
+
if (!isObject(source)) {
|
|
294
|
+
c.error('not-object', '', '정본은 객체여야 한다');
|
|
295
|
+
return { errors: c.errors, violations: c.violations };
|
|
296
|
+
}
|
|
297
|
+
if (!isNonEmptyString(source.type)) {
|
|
298
|
+
c.error('missing-type', 'type', 'type 이 없다 — 저장되는 식별자다');
|
|
299
|
+
}
|
|
300
|
+
if (!isObject(source.base)) {
|
|
301
|
+
c.error('missing-base', 'base', 'base 가 없다 — 배치 비율의 근거다');
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
c.positive(source.base.width, 'base.width', 'base.width');
|
|
305
|
+
c.positive(source.base.height, 'base.height', 'base.height');
|
|
306
|
+
c.positive(source.base.depth, 'base.depth', 'base.depth');
|
|
307
|
+
}
|
|
308
|
+
if (source.anchor !== undefined)
|
|
309
|
+
validateVec3(c, source.anchor, 'anchor', 'anchor');
|
|
310
|
+
if (source.detailLevel !== undefined) {
|
|
311
|
+
c.oneOf(source.detailLevel, DETAIL_LEVELS, 'detailLevel', 'detailLevel');
|
|
312
|
+
}
|
|
313
|
+
if (source.styleKit !== undefined && !isNonEmptyString(source.styleKit)) {
|
|
314
|
+
c.error('bad-style-kit', 'styleKit', 'styleKit 은 비어 있지 않은 문자여야 한다');
|
|
315
|
+
}
|
|
316
|
+
if (!Array.isArray(source.parts) || source.parts.length === 0) {
|
|
317
|
+
c.error('missing-parts', 'parts', 'parts 가 없다 — 부품이 하나는 있어야 한다');
|
|
318
|
+
return { errors: c.errors, violations: c.violations };
|
|
319
|
+
}
|
|
320
|
+
const seen = new Set();
|
|
321
|
+
source.parts.forEach((part, i) => validatePart(c, part, i, seen));
|
|
322
|
+
// 정책 검사는 형식이 성립할 때만 뜻이 있다
|
|
323
|
+
if (c.errors.length === 0)
|
|
324
|
+
checkPolicy(c, source);
|
|
325
|
+
return { errors: c.errors, violations: c.violations };
|
|
326
|
+
}
|
|
327
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"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"}
|
package/docs/design.md
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# Figure Blueprint — things-scene 에서 작동될 수 있는 모델
|
|
2
|
+
|
|
3
|
+
- 상태: 계약 정의 + 코어 구현 (2026-09-02)
|
|
4
|
+
- 연관: `things-factory/docs/design/component-authoring-tool.md` (저작도구 전체 설계)
|
|
5
|
+
|
|
6
|
+
저작도구(things-factory)가 만든 Figure 를 **things-scene 이 실제로 세울 수 있는
|
|
7
|
+
형태**로 정의한다. 이 문서는 엔진 쪽 계약만 다룬다.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 1. 무엇을 세울 수 있나 — 지금 사실
|
|
12
|
+
|
|
13
|
+
### 프리미티브와 상태 키
|
|
14
|
+
|
|
15
|
+
저작에 쓸 프리미티브는 두 계열이고 상태 키가 다르다.
|
|
16
|
+
|
|
17
|
+
| 프리미티브 | 계열 | 치수 상태 키 | 근거 |
|
|
18
|
+
| ---------- | ------- | ---------------------------- | -------------------------- |
|
|
19
|
+
| `cube` | mesh | `width` · `height` · `depth` | `factories/cube.ts:22` |
|
|
20
|
+
| `wall` | mesh | `width` · `height` · `depth` | `factories/wall.ts:29` |
|
|
21
|
+
| `cylinder` | mesh | `rx` · `ry` · `depth` | `factories/cylinder.ts:21` |
|
|
22
|
+
| `sphere` | mesh | `rx` · `ry` · `rz` | `factories/sphere.ts:21` |
|
|
23
|
+
| `rect` | extrude | `width` · `height` · `round` | `factories/rect.ts:11` |
|
|
24
|
+
| `polygon` | extrude | `path` · `round` | `factories/polygon.ts:30` |
|
|
25
|
+
|
|
26
|
+
공통으로 `zPos`(바닥에서 높이) · `depth`(두께) · `material3d`(재질)를 갖는다
|
|
27
|
+
(`real-object.ts:489` · `:617`).
|
|
28
|
+
|
|
29
|
+
### 이미 되는 것
|
|
30
|
+
|
|
31
|
+
| | 무엇 | 어디 |
|
|
32
|
+
| ---------------- | --------------------------------------------------------------------- | -------------------------------------------- |
|
|
33
|
+
| 재질 공유 | 인스턴스 무관. `base + 색 변형` | `threed/material-bank.ts` |
|
|
34
|
+
| 타입 런타임 등록 | `Component.register(type, class)` | `types/scene-component.ts:29` |
|
|
35
|
+
| 애니메이션 | fade · heartbeat · moving · outline · rotation · vibration · waypoint | `animation/animations/` |
|
|
36
|
+
| 데이터 바인딩 | `mappings` · `accessor` · `data` | `components/component.ts:78` · `:722` |
|
|
37
|
+
| 이벤트 | `state.eventHandlers` | `components/component.ts:263` |
|
|
38
|
+
| 정사영 톱뷰 | `setOrthoView('top')` | `threed/managers/camera-manager.ts:97` |
|
|
39
|
+
| 변환 기즈모 | translate · rotate · scale + 회전 스냅 | `threed/managers/gizmo-manager.ts:4` · `:86` |
|
|
40
|
+
|
|
41
|
+
### 안 되는 것 — 신설이 필요하다
|
|
42
|
+
|
|
43
|
+
| | 무엇 | 왜 필요한가 |
|
|
44
|
+
| --- | --------------------------------------------------------------------- | --------------------------- |
|
|
45
|
+
| 1 | **분할 수가 하드코딩** — cylinder 25, sphere 32×16 | 로우폴리 · geometry 캐시 키 |
|
|
46
|
+
| 2 | **`Material3D.flatShading` 미노출** — `material-bank.ts:32` 에는 있다 | 로우폴리 미학 |
|
|
47
|
+
| 3 | **geometry 공유 없음** | 대량 배치 |
|
|
48
|
+
| 4 | **팔레트 토큰 해석 없음** — `fillStyle` 은 값 그대로 | 스타일 추종 |
|
|
49
|
+
| 5 | **톱뷰 윤곽 계산 없음** | 2D 표현 |
|
|
50
|
+
| 6 | **부품 병합 없음** | draw call · `fill()` 호출 |
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 2. 병목 — 2D 와 3D 가 같다
|
|
55
|
+
|
|
56
|
+
| | 3D | 2D |
|
|
57
|
+
| -------------------------- | -------------- | -------------------- |
|
|
58
|
+
| 병목 | **draw call** | **`fill()` 호출 수** |
|
|
59
|
+
| 자산 500 개 × 부품 8 개 | 4000 draw call | 4000 fill |
|
|
60
|
+
| 재질(색) 3 종으로 병합하면 | 1500 | 1500 |
|
|
61
|
+
|
|
62
|
+
**같은 병합이 양쪽에 이득이다.** 그래서 청사진은 2D 와 3D 를 **한 묶음 구조**로
|
|
63
|
+
다룬다 — 재질별로 묶고, 각 묶음이 3D geometry 와 2D 윤곽을 함께 갖는다.
|
|
64
|
+
|
|
65
|
+
삼각형 수는 병목이 아니다. 등급 M(자산당 1000) × 500 = 50 만이고 GPU 는 이 정도를
|
|
66
|
+
신경 쓰지 않는다. **재는 것은 묶음 수다.**
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 3. 계약
|
|
71
|
+
|
|
72
|
+
### 정본 (`FigureSource`) — 저작 결과, 편집 가능
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
FigureSource
|
|
76
|
+
type 컴포넌트 타입 이름. 저장되는 식별자
|
|
77
|
+
base 기준 치수 { width, height, depth } — 배치 비율의 근거
|
|
78
|
+
anchor? 기준점
|
|
79
|
+
detailLevel? 'S' | 'M' | 'L'
|
|
80
|
+
styleKit? StyleKit 참조
|
|
81
|
+
parts[] FigurePart
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
FigurePart
|
|
86
|
+
name 부품 이름. 저장되는 식별자 — 바인딩·애니메이션·슬롯의 지시자
|
|
87
|
+
primitive 'cube' | 'cylinder' | 'sphere' | 'wall' | 'rect' | 'polygon'
|
|
88
|
+
transform { x, y, z, width, height, depth, rotation? }
|
|
89
|
+
segments? 분할 수 (mesh 계열)
|
|
90
|
+
material { token?, preset?, flatShading? } ← 색은 토큰
|
|
91
|
+
slot? 'cap' | 'side' 상태 색이 연동될 재질 슬롯
|
|
92
|
+
sizing? 'scale' | 'fixed' | 'stretch' | 'repeat'
|
|
93
|
+
repeat? { axis, gap } sizing === 'repeat' 일 때
|
|
94
|
+
animation? { kind, axis?, pivot? } 애니메이션 포인트
|
|
95
|
+
label? { name, source, when } 텍스트 표현 자리
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 청사진 (`FigureBlueprint`) — 보드가 소비, 파생·캐시
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
FigureBlueprint
|
|
102
|
+
version 스키마 버전
|
|
103
|
+
type 정본과 같다
|
|
104
|
+
base, anchor
|
|
105
|
+
budget { triangles, groups }
|
|
106
|
+
groups[] BlueprintGroup 재질별로 병합된 묶음 (2D·3D 공통)
|
|
107
|
+
points[] BlueprintPoint 애니메이션 포인트
|
|
108
|
+
labels[] BlueprintLabel
|
|
109
|
+
violations[] Violation 제약 위반 — 막지 않고 알린다
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
BlueprintGroup
|
|
114
|
+
materialKey MaterialBank 키
|
|
115
|
+
material { token?, preset?, flatShading? }
|
|
116
|
+
slot? 이 묶음이 상태에 연동되는 슬롯
|
|
117
|
+
animated 이 묶음이 애니메이션 포인트에 걸렸나 (걸리면 단독 묶음)
|
|
118
|
+
members[] { name, geometryKey, primitive, transform, sizing, repeat?, triangles }
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
`members` 가 **병합 대상**이다. 3D 는 이들의 geometry 를 합치고, 2D 는 이들의 윤곽을
|
|
122
|
+
한 path 로 합쳐 한 번에 채운다.
|
|
123
|
+
|
|
124
|
+
### 병합 규칙
|
|
125
|
+
|
|
126
|
+
같은 묶음에 들어가는 조건은 셋을 모두 만족할 때다.
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
1. materialKey 가 같다
|
|
130
|
+
2. slot 이 같다 ← 다르면 상태 색이 따로 바뀌어야 한다
|
|
131
|
+
3. 애니메이션 포인트가 없다 ← 있으면 따로 움직여야 한다
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
애니메이션이 걸린 부품은 **부품마다 단독 묶음**이 된다.
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
자산당 묶음 수 = 재질·슬롯 조합 수 + 애니메이션 포인트 수
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### 키 규약
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
geometryKey {primitive}|{정규화 치수}|s{segments}
|
|
144
|
+
cylinder|300x300x800|s12
|
|
145
|
+
cube|200x100x400
|
|
146
|
+
|
|
147
|
+
materialKey {token}|{preset}|{flatShading}
|
|
148
|
+
palette.primary|metal|f
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**치수를 정규화한다.** 비례 그리드(저작도구 §4.11)로 치수가 이산적이므로 키 조합 수가
|
|
152
|
+
작고 캐시 적중률이 높다. 정규화는 그 전제를 엔진 쪽에서 한 번 더 보장한다.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## 4. 제약 검증 — 막지 않고 알린다
|
|
157
|
+
|
|
158
|
+
| 제약 | 값 |
|
|
159
|
+
| -------------------- | ---------------------- |
|
|
160
|
+
| 재질·슬롯 조합 수 | ≤ 3 |
|
|
161
|
+
| 애니메이션 포인트 수 | ≤ 2 |
|
|
162
|
+
| 부품 수 | S ≤ 3 · M ≤ 8 · L ≤ 20 |
|
|
163
|
+
| 투명 재질 | ≤ 1 |
|
|
164
|
+
|
|
165
|
+
위반은 `violations[]` 로 돌려준다. **컴파일을 막지 않는다** — 저작도구가 숫자로
|
|
166
|
+
보여 주고 사용자가 등급을 올리거나 줄인다.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## 5. 이번에 구현한 것
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
src/figure/types.ts 계약
|
|
174
|
+
src/figure/blueprint.ts 정본 → 청사진 컴파일 (병합 · 키 · 예산 · 검증)
|
|
175
|
+
src/figure/index.ts 재출력
|
|
176
|
+
test/unit/figure/test-blueprint.js
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**순수 함수다.** 씬을 세우지 않고 시험한다 — `model/arrange.ts` · `utils/polygon-shape.ts`
|
|
180
|
+
와 같은 방식이다.
|
|
181
|
+
|
|
182
|
+
## 6. 남은 것
|
|
183
|
+
|
|
184
|
+
| | 무엇 |
|
|
185
|
+
| --- | ------------------------------------------------------------ |
|
|
186
|
+
| 1 | `segments` 상태 — 하드코딩된 분할 수를 뺀다 |
|
|
187
|
+
| 2 | `Material3D.flatShading` 노출 |
|
|
188
|
+
| 3 | `GeometryBank` — `material-bank` 를 본뜬다 |
|
|
189
|
+
| 4 | 톱뷰 윤곽 계산 — `RealObject.topOutline` |
|
|
190
|
+
| 5 | 팔레트 토큰 해석 |
|
|
191
|
+
| 6 | `FigureInstance` 컴포넌트 — 청사진을 세운다 |
|
|
192
|
+
| 7 | 부품 병합 실제 구현 (BufferGeometry 합치기 · 2D path 합치기) |
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# 기존 3D 모델 포맷 검토 — 왜 자체 형식인가
|
|
2
|
+
|
|
3
|
+
자체 형식을 만들기 전에 쓸 수 있는 표준이 있는지 먼저 본다. 결론은 **정본으로 쓸
|
|
4
|
+
만한 것이 없다**이고, 이 문서는 그 근거다.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 우리가 담아야 하는 것
|
|
9
|
+
|
|
10
|
+
형상만이 아니다. 세 층이 필요하다.
|
|
11
|
+
|
|
12
|
+
| 층 | 무엇 | 표준 포맷에 있나 |
|
|
13
|
+
| ------------- | ----------------------------------------------------------------------------- | ---------------- |
|
|
14
|
+
| ① 부품 그래프 | 프리미티브 · 치수 · 재질 · 변환 | **있다** |
|
|
15
|
+
| ② 반응 규칙 | 크기 반응(`scale`·`fixed`·`stretch`·`repeat`) · 애니메이션 포인트 · 상태 슬롯 | **없다** |
|
|
16
|
+
| ③ 결합 지점 | 태그 · 라벨(무엇을·언제) · 기준점 | **없다** |
|
|
17
|
+
|
|
18
|
+
표준 3D 포맷은 **형상 교환**을 위해 만들어졌다. 우리가 필요한 것은 형상보다
|
|
19
|
+
**②③** 이고, 그것이 없으면 저작 결과가 보드에서 살아 움직이지 않는다.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 후보별 검토
|
|
24
|
+
|
|
25
|
+
### glTF 2.0
|
|
26
|
+
|
|
27
|
+
| | |
|
|
28
|
+
| ------------- | ------------------------------------------------------------------------- |
|
|
29
|
+
| 강점 | 사실상 표준. 웹 로더가 성숙. 재질·애니메이션 규격이 있다 |
|
|
30
|
+
| **막히는 것** | **구운 메시다.** 정점 배열이라 치수·분할 수·프리미티브 종류가 남지 않는다 |
|
|
31
|
+
|
|
32
|
+
정본으로 쓰면 넷이 무너진다.
|
|
33
|
+
|
|
34
|
+
- **StyleKit 추종 불가** — 스타일을 바꿔도 이미 구운 것은 안 바뀐다
|
|
35
|
+
- **톱뷰 계산 불가** — 형상을 모르니 2D 윤곽을 계산할 수 없다 (스냅샷으로 떨어진다)
|
|
36
|
+
- **변경 반영 불가** — 고쳐서 배치된 것에 반영할 수 없다
|
|
37
|
+
- **`parts` 이름 · 상태 슬롯 소실** — 우리 의미가 정점 배열에 남지 않는다
|
|
38
|
+
|
|
39
|
+
`extras` / `extensions` 로 ②③을 끼워 넣을 수는 있다. 그러나 그러면 **glTF 를 쓰는
|
|
40
|
+
것이 아니라 glTF 안에 우리 형식을 넣는 것**이고, 형상은 여전히 구워져 있어 ①의
|
|
41
|
+
이득(파라메트릭 편집·공유)이 없다.
|
|
42
|
+
|
|
43
|
+
**결론 — 내보내기에는 쓴다. 정본으로는 못 쓴다.**
|
|
44
|
+
|
|
45
|
+
### USD / USDZ
|
|
46
|
+
|
|
47
|
+
| | |
|
|
48
|
+
| ------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
|
49
|
+
| 강점 | 파라메트릭 · 레이어링 · 참조(composition arcs)를 제대로 갖췄다. 우리가 필요한 「정의 하나, 인스턴스 여럿」이 언어 차원에 있다 |
|
|
50
|
+
| **막히는 것** | 스펙이 크다. 웹 런타임 파서가 무겁고, 우리가 쓸 부분은 전체의 아주 일부다 |
|
|
51
|
+
|
|
52
|
+
②의 크기 반응 규칙은 USD 에도 없다. 결국 스키마를 얹어야 하는데, **그 대가로 무거운
|
|
53
|
+
파서를 끌어온다.** 로우폴리 자산 하나가 몇 KB 인 것에 비해 균형이 맞지 않는다.
|
|
54
|
+
|
|
55
|
+
**결론 — 오버킬.**
|
|
56
|
+
|
|
57
|
+
### three.js `Object3D.toJSON()`
|
|
58
|
+
|
|
59
|
+
geometry 가 구워지고 three 전용이다. glTF 와 같은 문제에 표준성까지 없다.
|
|
60
|
+
|
|
61
|
+
**결론 — 안 된다.**
|
|
62
|
+
|
|
63
|
+
### OpenSCAD · CSG 스크립트
|
|
64
|
+
|
|
65
|
+
파라메트릭이라는 점은 맞다. 그러나 **부울 연산 중심**이고 우리는 부울을 범위 밖으로
|
|
66
|
+
두었다(로우폴리 미학의 도구가 아니고, geometry 캐시가 깨진다). 형상을 코드로 적는
|
|
67
|
+
것은 저작도구의 사용자(현장 엔지니어)와 맞지 않는다.
|
|
68
|
+
|
|
69
|
+
**결론 — 방향이 다르다.**
|
|
70
|
+
|
|
71
|
+
### SVG `<symbol>` / `<use>`
|
|
72
|
+
|
|
73
|
+
「정의 하나, 인스턴스 여럿 + viewBox 비율」 구조가 우리가 원하는 것과 가깝다. 다만
|
|
74
|
+
**2D 전용**이다.
|
|
75
|
+
|
|
76
|
+
**결론 — 개념만 참고.**
|
|
77
|
+
|
|
78
|
+
### things-scene 모델 JSON
|
|
79
|
+
|
|
80
|
+
| | |
|
|
81
|
+
| --------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
82
|
+
| 강점 | 이미 파라메트릭이다. 컴포넌트 타입 + state 로 치수·재질·분할이 값으로 남는다. `mappings`·`eventHandlers`·`animation` 이 이미 state 키에 있다 |
|
|
83
|
+
| 부족한 것 | ②의 크기 반응 규칙과 ③의 라벨·태그 선언이 없다. 병합·묶음 개념이 없다 |
|
|
84
|
+
|
|
85
|
+
**가장 가깝다.** 그리고 부족한 것은 **얹으면 되는 것**이지 구조가 어긋나는 것이
|
|
86
|
+
아니다.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 결론
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
정본 자체 형식 (FigureSource)
|
|
94
|
+
things-scene 모델 JSON 과 친화적으로 — 치수·재질·분할이 값으로 남는다
|
|
95
|
+
②반응 규칙 · ③결합 지점을 얹는다
|
|
96
|
+
|
|
97
|
+
산출물 자체 형식 (FigureBlueprint)
|
|
98
|
+
정본에서 파생. 재질별 병합 · 키 참조 · 예산
|
|
99
|
+
|
|
100
|
+
내보내기 glTF 외부 뷰어·교환용. 단방향
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**자체 형식을 만드는 근거는 「표준이 부족해서」가 아니라 「담을 것이 다른 층에
|
|
104
|
+
있어서」다.** 표준 포맷은 형상을 옮기려고 만들어졌고, 우리는 **반응하는 자산**을
|
|
105
|
+
정의하려 한다.
|
|
106
|
+
|
|
107
|
+
의존을 0 으로 두는 이유도 여기에 있다 — 이 형식은 특정 렌더러의 것이 아니다. 서버가
|
|
108
|
+
검증하고, 저작도구가 만들고, 보드가 소비한다.
|