@hatiolab/figure-model 0.1.30 → 0.1.32

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 (143) hide show
  1. package/README.md +2 -0
  2. package/dist/index.d.ts +12 -0
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +11 -0
  5. package/dist/index.js.map +1 -1
  6. package/dist/release-contract.d.ts +2 -2
  7. package/dist/release-contract.d.ts.map +1 -1
  8. package/dist/release-contract.js +45 -3
  9. package/dist/release-contract.js.map +1 -1
  10. package/dist/types.d.ts +15 -4
  11. package/dist/types.d.ts.map +1 -1
  12. package/dist/types.js +15 -4
  13. package/dist/types.js.map +1 -1
  14. package/dist/v3-asset-types.d.ts +116 -0
  15. package/dist/v3-asset-types.d.ts.map +1 -0
  16. package/dist/v3-asset-types.js +2 -0
  17. package/dist/v3-asset-types.js.map +1 -0
  18. package/dist/v3-asset.d.ts +8 -0
  19. package/dist/v3-asset.d.ts.map +1 -0
  20. package/dist/v3-asset.js +135 -0
  21. package/dist/v3-asset.js.map +1 -0
  22. package/dist/v3-authoring-session.d.ts +119 -0
  23. package/dist/v3-authoring-session.d.ts.map +1 -0
  24. package/dist/v3-authoring-session.js +132 -0
  25. package/dist/v3-authoring-session.js.map +1 -0
  26. package/dist/v3-capabilities.d.ts +3 -0
  27. package/dist/v3-capabilities.d.ts.map +1 -0
  28. package/dist/v3-capabilities.js +221 -0
  29. package/dist/v3-capabilities.js.map +1 -0
  30. package/dist/v3-contract.d.ts +7 -0
  31. package/dist/v3-contract.d.ts.map +1 -0
  32. package/dist/v3-contract.js +12 -0
  33. package/dist/v3-contract.js.map +1 -0
  34. package/dist/v3-create.d.ts +61 -0
  35. package/dist/v3-create.d.ts.map +1 -0
  36. package/dist/v3-create.js +151 -0
  37. package/dist/v3-create.js.map +1 -0
  38. package/dist/v3-document.d.ts +5 -0
  39. package/dist/v3-document.d.ts.map +1 -0
  40. package/dist/v3-document.js +31 -0
  41. package/dist/v3-document.js.map +1 -0
  42. package/dist/v3-editor-workspace.d.ts +367 -0
  43. package/dist/v3-editor-workspace.d.ts.map +1 -0
  44. package/dist/v3-editor-workspace.js +179 -0
  45. package/dist/v3-editor-workspace.js.map +1 -0
  46. package/dist/v3-graph-types.d.ts +91 -0
  47. package/dist/v3-graph-types.d.ts.map +1 -0
  48. package/dist/v3-graph-types.js +2 -0
  49. package/dist/v3-graph-types.js.map +1 -0
  50. package/dist/v3-graph.d.ts +5 -0
  51. package/dist/v3-graph.d.ts.map +1 -0
  52. package/dist/v3-graph.js +631 -0
  53. package/dist/v3-graph.js.map +1 -0
  54. package/dist/v3-json.d.ts +13 -0
  55. package/dist/v3-json.d.ts.map +1 -0
  56. package/dist/v3-json.js +94 -0
  57. package/dist/v3-json.js.map +1 -0
  58. package/dist/v3-part-edit.d.ts +12 -0
  59. package/dist/v3-part-edit.d.ts.map +1 -0
  60. package/dist/v3-part-edit.js +61 -0
  61. package/dist/v3-part-edit.js.map +1 -0
  62. package/docs/assembly-constraints-v3.md +141 -0
  63. package/docs/prototypes/README.md +73 -0
  64. package/docs/prototypes/render-dryroom.py +60 -0
  65. package/docs/prototypes/render-full-dryroom.mjs +86 -0
  66. package/docs/prototypes/shapeassembly-probe-results.json +325 -0
  67. package/docs/prototypes/shapeassembly-probe.py +84 -0
  68. package/docs/prototypes/v3-asset-samples.mjs +15 -0
  69. package/docs/prototypes/v3-asset-store.mjs +52 -0
  70. package/docs/prototypes/v3-asset-store.test.mjs +59 -0
  71. package/docs/prototypes/v3-asset.mjs +1 -0
  72. package/docs/prototypes/v3-asset.schema.json +44 -0
  73. package/docs/prototypes/v3-asset.test.mjs +68 -0
  74. package/docs/prototypes/v3-authoring-session.mjs +1 -0
  75. package/docs/prototypes/v3-authoring-session.test.mjs +65 -0
  76. package/docs/prototypes/v3-capability-plan.mjs +1 -0
  77. package/docs/prototypes/v3-capability-plan.test.mjs +56 -0
  78. package/docs/prototypes/v3-capability-samples.mjs +23 -0
  79. package/docs/prototypes/v3-document.mjs +2 -0
  80. package/docs/prototypes/v3-document.schema.json +66 -0
  81. package/docs/prototypes/v3-document.test.mjs +55 -0
  82. package/docs/prototypes/v3-dryroom-mesh.mjs +60 -0
  83. package/docs/prototypes/v3-dryroom-mesh.test.mjs +30 -0
  84. package/docs/prototypes/v3-editor-browser.mjs +97 -0
  85. package/docs/prototypes/v3-editor-workspace.mjs +1 -0
  86. package/docs/prototypes/v3-editor-workspace.test.mjs +35 -0
  87. package/docs/prototypes/v3-feature-pattern.test.mjs +131 -0
  88. package/docs/prototypes/v3-full-conveyor.mjs +60 -0
  89. package/docs/prototypes/v3-full-conveyor.test.mjs +61 -0
  90. package/docs/prototypes/v3-full-dryroom.mjs +67 -0
  91. package/docs/prototypes/v3-full-dryroom.test.mjs +54 -0
  92. package/docs/prototypes/v3-full-forklift.mjs +40 -0
  93. package/docs/prototypes/v3-full-forklift.test.mjs +37 -0
  94. package/docs/prototypes/v3-full-robot.mjs +35 -0
  95. package/docs/prototypes/v3-full-robot.test.mjs +68 -0
  96. package/docs/prototypes/v3-kernel.mjs +147 -0
  97. package/docs/prototypes/v3-kernel.test.mjs +144 -0
  98. package/docs/prototypes/v3-layout.test.mjs +281 -0
  99. package/docs/prototypes/v3-linear-comparison.py +183 -0
  100. package/docs/prototypes/v3-more-samples.test.mjs +182 -0
  101. package/docs/prototypes/v3-payload-collision.mjs +54 -0
  102. package/docs/prototypes/v3-payload-collision.test.mjs +68 -0
  103. package/docs/prototypes/v3-payload-fit.mjs +58 -0
  104. package/docs/prototypes/v3-payload-fit.test.mjs +59 -0
  105. package/docs/prototypes/v3-repeated-binding.mjs +13 -0
  106. package/docs/prototypes/v3-repeated-binding.test.mjs +46 -0
  107. package/docs/prototypes/v3-runtime-browser.mjs +76 -0
  108. package/docs/prototypes/v3-samples.test.mjs +144 -0
  109. package/docs/prototypes/v3-semantics.test.mjs +55 -0
  110. package/docs/prototypes/v3-spatial-plan.test.mjs +102 -0
  111. package/docs/prototypes/v3-typed-plan.mjs +3 -0
  112. package/docs/prototypes/v3-typed-plan.test.mjs +70 -0
  113. package/docs/prototypes/v3-typed-repeat.test.mjs +96 -0
  114. package/docs/v3-asset-persistence.md +66 -0
  115. package/docs/v3-authoring-transactions.md +57 -0
  116. package/docs/v3-capability-bindings.md +109 -0
  117. package/docs/v3-common-kernel-experiment.md +116 -0
  118. package/docs/v3-core-semantics.md +303 -0
  119. package/docs/v3-cutover.md +27 -0
  120. package/docs/v3-design.md +388 -0
  121. package/docs/v3-document-contract.md +55 -0
  122. package/docs/v3-dryroom-profile-validation.md +87 -0
  123. package/docs/v3-editor-integration.md +59 -0
  124. package/docs/v3-full-conveyor-validation.md +69 -0
  125. package/docs/v3-full-dryroom-validation.md +55 -0
  126. package/docs/v3-full-forklift-validation.md +41 -0
  127. package/docs/v3-full-robot-validation.md +64 -0
  128. package/docs/v3-layout-system.md +318 -0
  129. package/docs/v3-minimal-model.md +255 -0
  130. package/docs/v3-operator-contracts.md +148 -0
  131. package/docs/v3-payload-collision-validation.md +42 -0
  132. package/docs/v3-payload-contact-validation.md +56 -0
  133. package/docs/v3-repeated-binding-validation.md +42 -0
  134. package/docs/v3-research-review.md +160 -0
  135. package/docs/v3-runtime-extraction.md +107 -0
  136. package/docs/v3-sample-validation-expanded.md +106 -0
  137. package/docs/v3-sample-validation.md +97 -0
  138. package/docs/v3-shapeassembly-reproduction.md +99 -0
  139. package/docs/v3-spatial-contracts.md +84 -0
  140. package/docs/v3-status.md +41 -0
  141. package/docs/v3-storage-integration.md +58 -0
  142. package/docs/v3-typed-repeat-contracts.md +79 -0
  143. package/package.json +1 -1
@@ -0,0 +1,388 @@
1
+ # Figure v3 — 3차원 레이아웃의 수학적 계약
2
+
3
+ > 현재 구현·검증·제품 연결 상태는 [V3 통합 현황](v3-status.md)을 따른다. 본문의 단계별 연구/검증 기록은 제품 전체 완료 선언이 아니다.
4
+
5
+ > 이 문서는 r1의 수학적 논거를 보존한다. [최소 실행 모델 r2](v3-minimal-model.md)가
6
+ > 현재 설계 후보이며, 이 문서의 독립적인 layout/motion parent 및 상위 Flex/Grid 계약은
7
+ > 확정 요구사항이 아니다. 두 안을 혼합해 구현하지 않는다.
8
+
9
+ 상태: 설계 중인 실행 계약. 구현·렌더링·실자산 검증 완료가 아니다.
10
+ 2026-09-20. `assembly-constraints-v3.md`의 초기안을 대체한다.
11
+
12
+ 상위 배치 계약: [3차원 컨테이너·Flex·Grid](v3-layout-system.md).
13
+ 이 문서는 좌표·부착·운동의 하위 계약이다. Flex/Grid의 크기 배분과 중첩은 상위 계약을 따른다.
14
+ 검증 예제: `node --test docs/prototypes/v3-layout.test.mjs`.
15
+
16
+ ## 1. 목적과 범위
17
+
18
+ Figure는 저폴리 형상을 쉽게 저작하고 많은 인스턴스를 효율적으로 배치하는 제품이다.
19
+ v3의 핵심은 **치수에 따라 달라지는 3차원 배치 함수**다. 문틀·문짝·표시등은 이 함수의
20
+ 입력 예이며 별도의 예외 알고리즘이 아니다.
21
+
22
+ 부품 맞춤 UI와 AI는 공통 레이아웃 언어로 편집한다. 모델·검사·2D·3D가 같은 평가 결과를
23
+ 쓴다. 임의 CAD, 물리 시뮬레이션, 일반 비선형 제약 해결은 초기 범위 밖이다. 지원하지 않는
24
+ 관계를 그럴듯한 결과로 대체하지 않고 지원 범위를 명시한다.
25
+
26
+ v2의 불합리한 규칙은 고칠 수 있다. 원본 자산과 확인한 의도를 보존하되, 모든 v2 동작을
27
+ 보존하는 것은 요구사항이 아니다. 관계 없는 단독 상자도 정상 모델이며, 미완성 초안의
28
+ 저장을 금지하지 않는다.
29
+
30
+ ## 2. 현재 문제의 증거와 진단 교정
31
+
32
+ | 근거 | 확인한 사실 | 한계 |
33
+ | --- | --- | --- |
34
+ | `src/sizing.ts` | anchor는 Figure 기준 상자의 축에 대한 위치·크기 반응 | 부품의 개구부·부착 좌표계를 직접 참조하지 못한다 |
35
+ | `src/types.ts`, `src/grouping.ts`, `jointOriginPosition` | parent/joint와 부모를 따라 변하는 관절 원점은 이미 있다 | 관계가 전혀 없는 것이 아니라 크기 배치 관계가 부족하다 |
36
+ | `src/gate.ts: spansAt/meets/clumpsOf` | AABB의 연결 개수 증가를 검사 | 실제 접촉·개구부 맞춤·어떤 부품에 연결됐는지 보장하지 못한다 |
37
+ | `src/gate.ts: remedyFor` | 다른 덩어리의 느리게 커지는 부품을 골라 조언 | 실제 맞댄 면을 증명하지 않고 수정 방향을 단정할 수 있다 |
38
+ | `src/gate.ts: sizingReport` | 기본적으로 기준 크기와 7종 2배 검사 | 연속 치수 범위·축소·운동 상태의 증명이 아니다 |
39
+ | anchor 후보 탐색 | 제한된 수정 조합을 탐색 | 실패했다고 v2로 해결 불가능한 것은 아니다 |
40
+ | `src/grouping.ts`, things-scene `figure-real-object.ts` | 재질·운동 frame·크기 반응으로 병합/분리 | 관계 추가가 무료 성능을 보장하지 않는다 |
41
+
42
+ 드라이룸에서 복합 수정안이 생성되지 않은 것은 관찰했다. 고정 revision의 원본을 확보해
43
+ 원인과 모든 대안을 검증한 상태는 아니다. 앞서 탐색 실패를 곧바로 모델 표현력의 불가능성으로
44
+ 설명한 것은 과도했다. v3 설계의 근거는 일반적인 조립 의도와 일관된 배치 계산의 필요성이다.
45
+
46
+ ## 3. 형식적 문제 정의
47
+
48
+ 입력:
49
+ - 자산 정본 S: 부품 geometry, material, 치수/배치 규칙, 운동 규칙.
50
+ - 인스턴스 치수 벡터 d = (width, height, depth), 단위 mm.
51
+ - 런타임 상태 q: 관절 값·표시 상태·시간.
52
+ - 지원 영역 D: d가 만족해야 하는 유한한 크기 범위와 조건.
53
+
54
+ 출력:
55
+
56
+ ```text
57
+ L(S, d) = { (id_i, s_i(d), c_i(d), R_i, features_i(d)) } 정지 레이아웃
58
+ P(L, q) = { (id_i, M_i(d,q), geometrySpec_i(d)) } 최종 자세
59
+ ```
60
+
61
+ s는 부품의 local 크기, c는 Figure 좌표의 중심, R은 회전 행렬이다.
62
+ M은 최종 rigid 배치 행렬이다. material과 geometry spec은 별도 참조다.
63
+
64
+ 초기 kernel은 다음으로 제한한다.
65
+ 1. 명시적 크기·offset·feature 식은 아핀식이다. Flex/Grid의 min/max 활성 집합이
66
+ 바뀌면 결과는 구간별 아핀이며, 전체 범위를 단일 아핀식으로 다루지 않는다.
67
+ 2. 정지 회전은 d와 무관한 상수이며 R∈SO(3)이다.
68
+ 3. 위치·크기 의존 관계는 유향 비순환 그래프다.
69
+ 4. 운동은 레이아웃 완료 후의 강체 변환이다.
70
+ 5. 반복 개수 변화는 별도의 유한한 topology 구간으로 취급한다.
71
+
72
+ 고정 topology와 고정 배분 활성 집합 안에서는 치수와 정지 위치가 d에 대한 아핀함수다.
73
+ 위상 순서는 여러 개일 수 있지만 단일 writer의 결정적 함수로 평가한 결과는 유일하다.
74
+ 일반 제약 시스템에 대한 임의 반복·수치 최적화·수렴 추정이 필요하지 않다.
75
+ 범위 밖 표현은 확장 kernel의 별도 계약이 필요하다.
76
+
77
+ ## 4. 좌표계와 부품 기하
78
+
79
+ Figure 좌표는 오른손, Y 상향, mm다. 배치 원점은 reference box의 바닥 중심이다.
80
+ 부품 좌표 원점은 중심이다. renderer 고유 축 변환은 입출력 adapter에서 한 번만 한다.
81
+
82
+ 상자형 부품의 정규화 좌표 u∈[-1/2,1/2]³에 대해 정지 위치는 다음과 같다.
83
+
84
+ ```text
85
+ x_i(u,d) = c_i(d) + R_i diag(s_i(d)) u
86
+ ```
87
+
88
+ 비정형 primitive는 `geometrySpec(d)`가 local 형상을 생성한다. 위 상자는 local 치수의
89
+ 기준이지 polygon 내부가 모두 채워졌다는 뜻이 아니다.
90
+
91
+ 회전은 정규화 quaternion으로 저장하고 RᵀR=I, det(R)=1을 검사한다.
92
+ Euler 값은 UI에서 변환한다. 잘못된 quaternion/NaN/음수 크기는 명시 오류다.
93
+ 계산값을 표시용 반올림으로 되먹이지 않는다.
94
+
95
+ ## 5. 치수 언어와 자유도 소유권
96
+
97
+ 부품의 정지 자유도는 크기 3, 위치 3, 회전 3이다. 각 자유도의 writer는 하나다.
98
+ 원본 transform과 제약이 같은 값을 서로 덮어쓰지 않는다.
99
+
100
+ 길이는 mm, 비율은 무차원이다. 허용 식은 길이 상수, 참조, 덧셈/뺄셈, 상수배다.
101
+ 두 가변 길이의 곱·나눗셈, 임의 함수, eval은 초기 언어에 없다.
102
+
103
+ ```ts
104
+ type LengthExpr =
105
+ | { op: 'mm'; value: number }
106
+ | { op: 'ref'; ref: DimensionRef }
107
+ | { op: 'add' | 'sub'; left: LengthExpr; right: LengthExpr }
108
+ | { op: 'mul'; value: LengthExpr; factor: number }
109
+ ```
110
+
111
+ DimensionRef는 instance 크기축, 선언한 dimension, 부품 크기축, feature extent축을 가리킨다.
112
+ compile은 명시적 식을 입력 참조의 아핀식으로 정규화한다. 배분 결과 참조는
113
+ Flex/Grid 연산 노드를 유지한다. 상수계수 유한성·단위·참조·식 크기 상한을 검사한다.
114
+ 한 값을 둘이 구동하면 우선순위로 해결하지 않고 중복 writer 진단을 반환한다.
115
+
116
+ 사용자에게는 ‘폭을 따라감’, ‘두께 유지’, ‘사이를 채움’으로 보인다.
117
+ 공통 template가 이 식을 생성하며 AI도 같은 API를 사용한다.
118
+
119
+ ## 6. 3차원 정렬의 일반식
120
+
121
+ 대상 feature frame의 Figure 원점 o_t, 회전 R_t, extent e_t를 알고 있다고 하자.
122
+ 대상 기준점 계수 a, local offset δ, 부품의 자기 기준점 계수 b,
123
+ 추가 정지 회전 Q를 명시한다. a,b의 일반적인 값은 -1/2,0,1/2다.
124
+
125
+ ```text
126
+ R_i = R_t Q
127
+ p_target = o_t + R_t (a ⊙ e_t + δ)
128
+ c_i = p_target − R_i (b ⊙ s_i)
129
+ ```
130
+
131
+ 그러면 다음 불변식이 성립한다.
132
+
133
+ ```text
134
+ c_i + R_i(b ⊙ s_i) = o_t + R_t(a ⊙ e_t + δ)
135
+ ```
136
+
137
+ 즉 선택한 두 기준점은 선언한 offset을 두고 항상 일치한다.
138
+ 이 식은 ‘상단에 램프’, ‘문틀 중심에 문’, ‘회전한 패널 전면에 표시기’를 동일하게 처리한다.
139
+
140
+ 단, **기준점 일치와 면 전체 밀착은 다르다.**
141
+ 면 밀착 연산은 추가로 면 법선 정렬과 접선 방향 영역 조건을 요구한다.
142
+ 외향 법선이 맞대는 경우 `R_i n_self = −R_t n_target`이어야 한다.
143
+ 임의 Q 때문에 이 조건이 깨지면 단순 점 부착으로 낮춰 성공 처리하지 않는다.
144
+
145
+ 축 정렬된 두 경계 사이를 채울 때, 같은 frame의 하한 l, 상한 h,
146
+ 좌우 여백 m₋,m₊로 다음처럼 크기와 중심을 동시에 정한다.
147
+
148
+ ```text
149
+ s = h − l − m₋ − m₊
150
+ c_local = (h + l + m₋ − m₊)/2
151
+ ```
152
+
153
+ 이 연산이 그 축의 위치와 크기를 모두 소유한다. 음수/0 크기는 범위 불성립이다.
154
+ 추가 회전된 부품을 그 영역에 넣는 일반 문제는 같은 연산으로 속이지 않는다.
155
+ 고정 회전 상자라면 support extent `|Q|s`로 포함 조건을 검사할 수 있으나
156
+ 최적 치수를 역으로 찾는 solver는 초기 kernel에 없다.
157
+
158
+ ## 7. feature: 부착 기준과 실제 개구부
159
+
160
+ feature는 owner 부품 local frame에 있는 이름 있는 좌표계/영역이다.
161
+ origin은 owner local 길이의 아핀식, orientation은 상수, extent는 길이식이다.
162
+ feature는 geometry·material·draw call을 만들지 않는다.
163
+
164
+ 출처를 구분한다.
165
+ - geometry-bound: shape의 안정된 profile/영역 id와 dimension을 공유한다.
166
+ - author-defined: 저작자가 지정한 기준 영역. 실제 구멍이라는 검증은 별도다.
167
+
168
+ 초기 범위는 bounds, mount frame, rectangular opening이다.
169
+ portal의 opening W/H와 실제 polygon을 만드는 W/H는 동일 dimension을 참조한다.
170
+ 개구부 선언과 실제 형상이 따로 수정되는 이중 정본을 금지한다.
171
+
172
+ geometry provider는 shape spec과 함께 해당 feature binding을 반환해야 한다.
173
+ 자유 polygon의 구멍을 자동 추론해 확정하지 않는다. 안정적인 영역을 지정하거나 geometry
174
+ 검증이 필요하다. 비볼록/곡선 개구부는 bounding box만으로 포함 여부를 판단하지 않는다.
175
+ 지원되는 기하 검사 범위 밖이면 `unverified-geometry-relation`으로 표시한다.
176
+
177
+ ## 8. 의존 그래프와 평가 알고리즘
178
+
179
+ 서로 다른 세 구조를 혼동하지 않는다.
180
+
181
+ | 구조 | 책임 |
182
+ | --- | --- |
183
+ | 값 의존 DAG | 치수·feature·정지 위치 계산 순서 |
184
+ | 운동 tree/forest | joint 변환의 상속과 적용 순서 |
185
+ | assembly 그룹 | 저작 선택·재사용·노출 조작 묶음 |
186
+
187
+ 부품 id에 대한 순환만 보는 대신 실제 값 참조의 cycle을 검사한다.
188
+ 같은 부품의 크기→feature→다른 부품 위치는 허용된다. 자기 크기를 그 feature에서 다시
189
+ 계산하면 cycle이다. frame의 원점/회전 의존도 DAG에 포함한다.
190
+
191
+ ```text
192
+ compile:
193
+ schema/units/IDs → single writer check → resolve references
194
+ → dependency DAG → cycle diagnostic → topological order
195
+ → affine coefficients → domain obligations → prepared blueprint
196
+
197
+ resolve(d):
198
+ check d∈D → evaluate dimensions/features/rest frames
199
+ → validate finite positive outputs → immutable resolved layout
200
+
201
+ pose(q):
202
+ validate state → evaluate joint motion along forest
203
+ → final rigid frames → draw/inspect/export
204
+ ```
205
+
206
+ 규칙 수 V와 참조 E에 대해 DAG 처리 O(V+E). 명시적 아핀식 평가는 식 수에 선형이다.
207
+ Flex/Grid는 별도의 공간 배분 비용을 더한다. 상위 계약의 복잡도 분석을 따른다.
208
+ 표시 이름/배열 순서가 결과를 바꾸지 않는다. 동일 입력·semantic version은 동일 결과를 낸다.
209
+
210
+ ## 9. 운동과 shear 방지
211
+
212
+ 정지 frame을 B_i(d)라 한다. child i의 motion parent가 p이면
213
+ `A_i(d) = inverse(B_p(d)) B_i(d)`를 정지 상대 frame으로 만든다.
214
+ joint local 원점/축을 이 frame 약속으로 정의한 강체 변화 J_i(q,d)에 대해:
215
+
216
+ ```text
217
+ W_i(d,q) = W_p(d,q) A_i(d) J_i(q,d)
218
+ ```
219
+
220
+ joint가 없으면 J=I. q=0이면 모든 J=I여서 정지 레이아웃과 정확히 일치한다.
221
+ hinge는 joint 기준점 주위 `T(h) R(axis,q) T(-h)`,
222
+ slider는 `T(axis × displacement)`다. 축은 정규화한다.
223
+
224
+ W와 B는 scale이 없는 rigid frame이다. geometry local 크기는 별도로 해결한다.
225
+ 따라서 부모의 비균등 scale과 자식 rotation을 곱해 shear를 만들지 않는다.
226
+ renderer는 이미 계산된 크기를 다시 인스턴스 배율로 확대해서도 안 된다.
227
+
228
+ layout target과 motion parent는 명시적으로 구분한다. ‘부착’ template는 보통 둘 다 지정하고,
229
+ ‘위치만 참조’는 motion을 상속하지 않는다. 하나의 joint를 여러 clip/parameter가 동시에
230
+ 구동하면 명시적 arbitration 없이 실행하지 않는다.
231
+
232
+ 초기판의 runtime q는 rigid motion/표시 상태만 바꾼다. 형상을 변형하는 q, 닫힌 운동 루프,
233
+ 움직이는 두 가지 사이의 재구속은 별도 확장이다. resize 중의 q는 현재 값을 보존하되 새 허용
234
+ 영역을 벗어나면 진단하고 사용자 정책을 적용한다. 무언의 clamp는 없다.
235
+
236
+ ## 10. 해의 존재·유일성·보장 범위
237
+
238
+ **존재**: 참조가 유효하고 DAG이며 각 primitive/feature의 정의역과 모든 양의 치수 조건을
239
+ 만족하면 위상 순서로 값을 구성할 수 있다.
240
+
241
+ **유일성**: 각 값이 상수 입력 또는 이미 결정된 값의 결정적 함수이고 writer가 하나이므로
242
+ 위상 순서에 대한 귀납으로 결과가 유일하다. 미지수를 동시에 푸는 일반 solver의 유일성
243
+ 주장이 아니다.
244
+
245
+ **부착 보존**: §6 식을 대입하면 선택 기준점의 일치는 대수적으로 성립한다.
246
+ 이는 geometry의 정확성, 면 전체 접촉, 미적 품질을 증명하지 않는다.
247
+
248
+ **범위 검증**: 초기 d가 직육면체 영역 D에 있고 값 f(d)=a·d+b이면
249
+ 각 축의 계수 부호에 따라 하한/상한에서 정확한 최솟값/최댓값을 구한다.
250
+ 3변수의 8개 꼭짓점 평가와 동치다. 이 조건은 전체 영역에서 식이 아핀일 때만 성립한다.
251
+ Flex/Grid의 활성 집합이 바뀌면 영역 분할 또는 보수적 구간 검증이 필요하다.
252
+ positive length·gap·linear inequality를 범위 전체에서
253
+ 검사할 수 있다. 혼합 affine 조건이 추가된 domain은 box에서 보수적으로 증명하며,
254
+ 증명이 안 되면 미증명으로 반환한다. 샘플 성공으로 증명 상태를 바꾸지 않는다.
255
+
256
+ 부동소수 계산에서는 epsilon과 단위 scale을 명시한다.
257
+ 초기 비교 허용오차 제안은 `ε = 1e-6 mm + 1e-9 × characteristicLength`.
258
+ 제품 조립 clearance와 수치 epsilon은 별개다. epsilon은 테스트/수치 안정성 검토로 확정한다.
259
+
260
+ 회전 관절의 swept collision, 비볼록 개구부 포함, 곡선 geometry는 위 선형 증명 범위 밖이다.
261
+ 그 항목은 전문 기하 검사 또는 범위/자세를 밝힌 샘플 검증으로 남긴다.
262
+ ‘전 크기/전 자세 안전’이라는 단일 boolean 대신 obligation별 proven/tested/unverified를 반환한다.
263
+
264
+ ## 11. repeat는 topology 연산
265
+
266
+ 고정개수와 고정pitch를 구분한다. 고정pitch의 count는 floor를 포함해 전역 affine가 아니다.
267
+ 따라서 count가 일정한 크기 구간별 layout으로 분리한다.
268
+ 같은 반복 frame의 index/pitch를 선반·램프·slot이 공유한다.
269
+
270
+ 길이 L 안에서 item width w, 고정pitch p, 양끝 margin m을 지킬 때:
271
+ `count = floor((L−2m−w)/p)+1` (L≥2m+w, p>0).
272
+ 공간 부족이면 zero item 허용 여부를 template가 선언한다. 무조건 최소1개로 만들지 않는다.
273
+ centred/end anchored 분포도 명시하고 count 전환점의 양쪽을 검사한다.
274
+
275
+ 복제 id는 template id + 안정된 index다. count 축소로 slot이 사라지면 capability 계층에
276
+ 변경을 알린다. 이 정책이 구현되기 전에는 점유된 반복 slot의 안전한 이행을 주장하지 않는다.
277
+ 최대 count/geometry 예산 초과는 진단하며 조용히 잘라내지 않는다.
278
+
279
+ ## 12. 두짝문은 일반식의 파생 예
280
+
281
+ 인공 fixture이며 실제 드라이룸의 측정값이 아니다.
282
+ opening W=1440, H=2200, 외곽 margin c=10, 중앙 gap g=10, 두께40 mm.
283
+ X 중심0, Y 하단0인 opening frame을 사용한다.
284
+
285
+ ```text
286
+ leafWidth = (W−2c−g)/2 = 705
287
+ leafHeight = H−2c = 2180
288
+ leftCenterX = −g/2−leafWidth/2 = −357.5
289
+ rightCenterX = +357.5
290
+ centerY = H/2 = 1100
291
+ travel = leafWidth+c = (W-g)/2 = 715
292
+ leftX(q) = leftCenterX−q×travel
293
+ rightX(q) = rightCenterX+q×travel
294
+ ```
295
+
296
+ 닫힌 상태는 외곽10mm, 중앙10mm를 유지한다. q=1에서 두 문 내측 끝은 각각 −W/2,+W/2다.
297
+ W가 두 배면 leafWidth 1425, 중심 ±717.5, travel 1435. H가 두 배면 높이 4380.
298
+ 이전 초안의 travel=W/2는 간격 g/2만큼 더 이동하는 오류였고, 수치 예제로 교정했다.
299
+ 두께/clearance는 고정이다. 개구부를 벗어난 문의 주차 공간은 별도 assertion으로 검사한다.
300
+
301
+ 이 예는 split 영역을 두 개 만들어 각각 채우는 것으로도 동일하게 표현된다.
302
+ lamp는 portal feature 기준점에 고정 크기로 정렬하고, readout은 feature 상대 위치와
303
+ 고정 두께를 갖는다. portal 폭이 높이를 따라야 하면 W의 식만 H의 상수배로 바꾼다.
304
+ ‘portal 예외’나 ‘lamp 예외’가 evaluator에 필요하지 않다.
305
+
306
+ ## 13. 실행 모델과 성능
307
+
308
+ ```ts
309
+ compileV3(source): PreparedFigureV3 | Diagnostics
310
+ resolveLayout(prepared, dimensions): ResolvedLayout | Diagnostics
311
+ evaluatePose(layout, state): ResolvedFigure | Diagnostics
312
+ inspectResolved(prepared, resolved, profile): Inspection
313
+ ```
314
+
315
+ Prepared는 식/순서/컨테이너/feature binding/geometry spec/운동 구조를,
316
+ Resolved는 실제 크기/rigid frame/feature/slot/port를 반환한다.
317
+ source version, blueprint version, evaluator semantic version을 분리한다.
318
+ 2D·3D는 같은 Resolved를 소비하고 관계 의미를 재구현하지 않는다.
319
+
320
+ compile은 자산 변경 시, layout은 치수 변경 시, pose는 상태 변경 시 처리한다.
321
+ 정지 frame에서 매번 관계 전체를 해석하지 않는다. feature/assembly는 scene node를 강제하지 않는다.
322
+
323
+ 동일 material·motion frame·변형 함수를 공유하는 부품은 병합 후보가 된다.
324
+ 실제 geometry 병합/instancing 경로가 구현된 경우만 비용 이득으로 계산한다.
325
+ 크기별 geometry cache는 제한/퇴출/semantic version 포함 키를 가진다.
326
+
327
+ 같은 모양·치수·상태·LOD로 1/100/500 인스턴스의 draw call, triangle, geometry 공유,
328
+ heap, layout p50/p95, frame p95를 측정한다. 관계 메타데이터만 추가했을 때
329
+ geometry/draw call이 늘지 않아야 한다. 동일 기능 p95 시간 10% 초과 악화 시 성능 리뷰를
330
+ 한다(잠정 목표, 실측값 아님). 독립 운동/투명 표현 비용이 늘면 그 차이를 명시한다.
331
+
332
+ ## 14. 저장·검사·발행
333
+
334
+ driver는 실행 의미이므로 advisory로 무시할 수 없다. required/advisory는 별도 assertion의 속성이다.
335
+ 자동 수정이 required를 advisory로 낮춰 합격시키는 것을 금지한다.
336
+
337
+ | 상태 | 처리 |
338
+ | --- | --- |
339
+ | 불명 참조/순환/중복 writer | 실행 불가 진단, 작업 초안 보관 가능, 정상 blueprint로 배포 불가 |
340
+ | domain 밖/음수 치수 | 해당 크기 사용 불가; 마지막 정상 표시라면 현재 결과가 아님을 명시 |
341
+ | required assertion 위반 | 선언한 지원 범위에서 발행 불가 |
342
+ | required assertion 미증명 | 위반 확정과 구분; 보장 정책/범위 조정 또는 추가 검증 필요 |
343
+ | AABB 분리/미적 권고 | advisory; 파손 확정으로 설명하지 않음 |
344
+ | 외부 parameter/slot 삭제 | 소비 계약 변경으로 revision 정책과 함께 처리 |
345
+
346
+ 근접성이 계약을 대신하지 않는다. geometry-bound feature에 대해 독립 검사와 수치 oracle을 쓴다.
347
+ 2배 7종은 회귀 샘플일 뿐 보장의 전부가 아니다. 축소, 범위 끝, mixed scale, seed 고정 중간점,
348
+ joint 경계, 개도 0/0.5/1, repeat 전환점을 포함한다.
349
+
350
+ 기준 상자 비율 변경만으로 영구 발행 금지하지 않는다. 모든 사용처를 알 수 있다는 전제도 없다.
351
+ consumer는 asset revision 고정과 최신판 추종을 명시적으로 지원해야 한다.
352
+
353
+ ## 15. 저작·AI·수정 transaction
354
+
355
+ 사용자 조작은 ‘이 면에 붙이기’, ‘이 공간을 나누어 채우기’, ‘두께 유지’다.
356
+ 구동되는 속성에는 참조 대상과 이유를 표시한다.
357
+ explicit 부착의 드래그는 offset 변경, flow item의 드래그는 순서/칸 변경을 기본으로 한다.
358
+ flow의 계산 위치를 수동 좌표로 덮어쓰지 않는다.
359
+ 관계 해제는 현재 값을 고정값으로 전환하고 명시적 배치로 이동하며 undo 가능하게 한다.
360
+
361
+ AI와 자동 수정은 typed edit API를 사용한다. 후보에 baseRevision/sourceHash, 대상 id,
362
+ 사전 조건, atomic patch, 관계 변경, 크기/자세별 결과 차, 비용 차와 검증 범위를 포함한다.
363
+ 원본 경합 시 재계산, requestId 멱등 적용, undo를 지원한다. 적용/저장/발행 결과를 분리한다.
364
+
365
+ 관계 미선언 시 ‘램프를 문틀 상단에 부착’ 후보를 보여주며 이름/근접 추론을 자동 확정하지 않는다.
366
+ 전역 차단 항목이 0이어야만 수정을 제공하는 조건은 폐기한다. 국소 해결과 남은 문제를 구분한다.
367
+ 의미/외형을 훼손하며 검사만 통과한 후보를 안전하다고 표시하지 않는다.
368
+
369
+ ## 16. 전환과 구현 단계
370
+
371
+ id는 불변, label은 표시 이름이다. 기존 name 기반 외부 참조는 자산 전환 시 갱신한다. V2 호환 alias 실행 계층을 유지하는 것은 요구사항이 아니다.
372
+ 변환은 원본/hash 보존, v3 후보, 기계적 변환, 의도 선택, 미검증 진단을 반환한다.
373
+ v2의 잘못된 동작을 보존할 의무는 없다. 의도적으로 바뀐 외형은 승인된 차분으로 기록한다.
374
+ 초안 저장과 발행 조건을 분리한다. 미지원 consumer에 v3를 부분 해석해 보내지 않는다.
375
+
376
+ 세부 구현 순서와 출하 조건은 [상위 레이아웃 계약 §12](v3-layout-system.md#12-출하-전-단계와-완료-기준)를 따른다.
377
+ fixture는 단독 box, 두짝문, 회전 portal, 원형 roller, 선반+lamp repeat, slot/port,
378
+ 의도적 분리, 서로 다른 개도의 portal 두 개를 포함한다.
379
+ 실제 드라이룸 revision fixture 취득은 남아 있다.
380
+ 현재 수치 예제는 설계 규칙의 검증이며 제품 불량 해결의 증거가 아니다.
381
+
382
+ ## 17. 남은 검증
383
+
384
+ - geometry profile의 안정된 feature id와 전체 직렬화 schema를 kernel/adapter 구현 전에 고정한다.
385
+ - 회전 형상의 local 치수/normal/공유 geometry를 renderer에서 확인한다.
386
+ - 임의 비볼록 geometry 검사, 비선형식, 움직이는 구조의 재구속은 별도 지원 계약이 필요하다.
387
+ - repeat의 slot 점유 이전과 consumer revision 이행은 전환 기능의 출하 조건이다.
388
+ - 미적 품질, 성능, 수치 범위의 안정성은 형식적 논증만으로 완료되지 않으며 실측이 필요하다.
@@ -0,0 +1,55 @@
1
+ # V3 그래프·capability 직렬화 계약 — 실험판
2
+
3
+ > 현재 구현·검증·제품 연결 상태는 [V3 통합 현황](v3-status.md)을 따른다. 본문의 단계별 연구/검증 기록은 제품 전체 완료 선언이 아니다.
4
+
5
+ ## 범위와 상태
6
+
7
+ 실험 그래프와 capability 선언을 저장하고 재로드해 의미 검증하는 계약을 구현했다. **전체 Figure V3 제품 파일 포맷 확정이 아니다.** 현재 전체 모델 예제의 재질·분할 수·외부 capability 메타데이터 일부는 어댑터가 공급한다. 그 데이터까지 이 포맷이 저장한다고 주장하지 않는다. 제품 DB·서버·UI는 변경하지 않았다.
8
+
9
+ 후속 [자산 저장 envelope](v3-asset-persistence.md)는 이 document를 감싸 설계 입력·동작 기본값·재질·팔레트·분할 품질을 함께 저장한다. document 단독 포맷과 자산 envelope의 지원 범위를 혼동하지 않는다.
10
+
11
+ - [JSON Schema draft-07](prototypes/v3-document.schema.json)
12
+ - [저장·읽기·의미 검증](prototypes/v3-document.mjs)
13
+ - [표면 출처 및 round-trip 테스트](prototypes/v3-document.test.mjs)
14
+
15
+ ```
16
+ {
17
+ version: "figure-v3-prototype-1",
18
+ model: { version: "r3-spatial-1", inputs, constants, nodes },
19
+ capabilities: { version: "r3-capability-3", assetFrame, bindings }
20
+ }
21
+ ```
22
+
23
+ 위 코드는 구조 설명이며 생략된 값을 포함한 유효 JSON 예제는 아니다. 재현 가능한 실제 선언은 `v3-capability-samples.mjs`의 model/declaration을 사용한다.
24
+
25
+ ## 두 단계 검증
26
+
27
+ JSON Schema는 필드 구조·자료형·기본 enum·버전을 검사한다. operator 입력 포트별 형식, 단위, 그래프 순환, 참조 소유권, 좌표계, 범위 간 관계는 의미 컴파일러가 검사한다. Schema 통과만으로 실행하거나 인증하지 않는다.
28
+
29
+ 런타임 parser는 내장 구조 검사와 의미 컴파일러를 실행한다. 외부 Schema 검증기 자체를 제품 의존성으로 추가하지 않았다. 테스트는 기존 things-factory의 Ajv 6을 사용해 Schema를 컴파일하고 두 예제를 검증한다. 이 테스트 경로는 현재 개발 환경에 종속적이다.
30
+
31
+ 직렬화는 평가 결과가 아니라 선언을 저장한다. 파생 메시, 캐시, 과거 pose, 성공 판정은 저장 대상이 아니다. 새 입력으로 다시 계산한다. 사용자 UI·AI도 최종적으로 동일 선언/입력을 바꿔야 한다.
32
+
33
+ ## 손실·오류 정책
34
+
35
+ - 알 수 없는 문서/계약 버전과 추가 필드를 거부한다. 현재 capability V3는 transform 또는 표면 참조 pose를 허용한다. V1/V2를 추측 변환하지 않는다.
36
+ - JSON 키 중복은 마지막 값으로 덮지 않고 거부한다. Unicode escape로 같은 키가 표현된 경우도 포함한다.
37
+ - 잘린 JSON, 비유한 수, undefined, 함수, Date, 순환, sparse array, accessor·숨겨진 속성 등 JSON으로 손실되는 입력을 거부한다.
38
+ - 실험 자원 제한: 1MiB UTF-8, 객체 깊이64, 그래프 node512. 제품 용량 정책으로 확정된 값이 아니다.
39
+ - round-trip은 의미·수치·선언 동일성을 대상으로 한다. 원본 JSON의 공백·키 표기·서식은 보존하지 않는다.
40
+ - 함수는 파일/DB에 직접 쓰지 않는다. 저장 API의 원자성, 권한, 버전 충돌, 마이그레이션은 제품 통합 시 별도로 구현해야 한다.
41
+
42
+ ## 실제 표면 참조
43
+
44
+ capability contacts는 `placement`와 `feature` 출력 ID로 명시한다. 실제 배치와 표면을 동일 shape에 결속시킨다. 임의의 rigid transform을 받침면으로 쓰거나 다른 shape의 feature를 가져오는 경우 실패한다.
45
+
46
+ 출처 검증과 접촉 검증은 별개다. 출력의 표면 법선은 part의 world 회전을 적용한 바깥 방향이다. feature의 local axes가 표면 법선이라고 가정하지 않는다. box 6면·roundedBox 위아래·cylinder 양 끝면만 지원하며 곡면과 아직 명세되지 않은 provider는 거부한다. 실제 접촉·충돌·안전성은 계속 `unverified`다.
47
+
48
+ ## 이번 검증
49
+
50
+ 1. 로봇암·포크리프트의 선언이 JSON Schema를 통과하고, serialize → parse → compile → evaluate 후 변경된 동작 상태 결과가 동일하다.
51
+ 2. 임의 프레임, 다른 shape, 사라진 placement, 중복 contact, cylinder 곡면을 거부한다.
52
+ 3. 로봇 상완을 -90/0/90°로 회전하면 실제 surface normal도 같은 회전을 따른다.
53
+ 4. 잘린 문서, 중복 키, 구버전, 크기 제한과 손실되는 JavaScript 값을 거부한다.
54
+
55
+ 후속 단계에서 반복 표면 참조, 제한된 payload 접촉/충돌 검사, 전체 asset 직렬화와 공통 편집 workspace를 구현했다. 현황은 [통합 현황](v3-status.md)을 따른다. 제품 편집기·DB 통합과 일반적인 공간·안전성 보장은 아직 미완료다.
@@ -0,0 +1,87 @@
1
+ # 드라이룸: 비대칭 단면 → typed feature → 실제 메시 검증
2
+
3
+ > 현재 구현·검증·제품 연결 상태는 [V3 통합 현황](v3-status.md)을 따른다. 본문의 단계별 연구/검증 기록은 제품 전체 완료 선언이 아니다.
4
+
5
+ 상태: 독립 V3 실험. 제품/DB/원본 시드 수정 없음.
6
+ `operato-application/packages/operato-figure/server/seed/figure-samples.json`의
7
+ DRY_ROOM portal-in/out과 lamp-in/out을 읽었다. 서버에서 편집된 revision은 검사하지 않았다.
8
+ shell/readout을 포함한 드라이룸 전체 변환도 아니다.
9
+
10
+ ## 1. 구현 범위
11
+
12
+ 동일 typed compiler에 `portal-profile@1`을 추가했다.
13
+ 입력은 outer left/right/bottom/top, opening left/right/top, extrusion depth의 8개 길이다.
14
+ 입력 경계로부터 실제 XY 단면과 feature를 함께 만든다. 원점은 자동 재중심화하지 않는다.
15
+ 조건은 `left < openingLeft < openingRight < right`, `bottom < openingTop < top`, `depth > 0`이다.
16
+
17
+ 이 provider는 **직교 U형 단면**이다. 제품 이름에 대한 엔진 분기는 없지만
18
+ 범용 polygon clipping/round/hollow를 해결한 것으로 주장하지 않는다.
19
+ 일반 polygon이나 경사진 문틀은 아직 지원하지 않는다.
20
+
21
+ 정점은 각 경계 교점의 이름을 id로 갖는다. feature는 top, bottomLeft, bottomRight,
22
+ openingTop, frontTop이며 모두 같은 경계값을 읽는다.
23
+ opening 아래의 빈 공간 가운데를 '실제 bottom 면'으로 노출하지 않는다.
24
+ feature 축은 provider 축과 같으며 바깥 법선 방향 frame이 아니다.
25
+
26
+ ## 2. 원본 대응
27
+
28
+ 기존 renderer의 polygon extrusion +90° X와 portal의 −90° X를 합치면
29
+ source path는 world XY로 돌아오고 두께는 중심 기준 Z축으로 놓인다.
30
+ 독립 어댑터는 이 변환을 적용한다. 원본 path 8점이 그대로 재현됨을 검사했다.
31
+ 램프의 원본 상대 위치를 top-front feature 기준 offset으로 표현했다.
32
+ 이것은 자동 의도 복원이 아니라 **이 실험에서 명시한 연결**이다.
33
+
34
+ ## 3. 실제 메시 검사
35
+
36
+ 설치된 things-scene의 Three.js를 재사용해 bevel 없는 ExtrudeGeometry를 만들었다.
37
+ 램프도 CylinderGeometry로 생성했다. product dependency/package 설정 변경 없음.
38
+ 검사기는 float 좌표를 10⁻⁶mm 격자로 묶어 모서리를 비교한다. 원본의 현재 정수 좌표에는
39
+ 충분하지만 임의 크기 형상의 보수적 수치 인증 규칙은 아니다.
40
+
41
+ | 항목 | 전 | 후 |
42
+ | --- | ---: | ---: |
43
+ | 문틀 world 하단 | −60mm | 0mm |
44
+ | world 상단 | 2900mm | 2900mm |
45
+ | 개구부 상단 | 2200mm | 2200mm |
46
+ | 개구부 폭 | 1400mm | 1400mm |
47
+ | 램프 pose | 원본 대응 | 동일 |
48
+ | 문틀 하나의 체적 | 1,359,600,000mm³ | 1,338,000,000mm³ |
49
+ | 삼각형/정점/모서리 | 28/16/42 | 28/16/42 |
50
+ | 방향/짝이 맞지 않는 모서리 | 0 | 0 |
51
+
52
+ 제거 체적은 `2 × 600 × 60 × 300 = 21,600,000mm³`로 두 다리의 아래 돌출과 일치한다.
53
+ 모든 삼각형 면적이 양수, signed volume이 양수이며 analytic volume과 일치했다.
54
+ edge incidence 2, 반대 방향 edge pairing, Euler=2를 검사했다.
55
+ 이 조건만으로 임의 mesh의 무자기교차를 증명하지 않는다. 여기서는 위 정의역이 보장하는
56
+ 단순 U형 extrusion을 대상으로 한다.
57
+
58
+ ray 검사: 개구부 중앙은 관통, 상부/다리는 충돌, y=−30의 다리는 전에는 존재하고 후에는 없다.
59
+ 하단을 2200까지 올려 개구부가 사라지는 입력은 GEOMETRY_DOMAIN으로 거절했다.
60
+ −60/0/100/1000/2199mm의 추가 하단 위치에서도 closed/oriented mesh 검사가 통과했다.
61
+
62
+ ## 4. 시각 확인
63
+
64
+ 같은 삼각형을 소프트웨어 orthographic z-buffer renderer로 그려 직접 확인했다.
65
+ 왼쪽은 원본, 오른쪽은 수정 후보, 아래는 하단 확대다. 빨간 선은 앞면 평면의 Y=0 기준선이다.
66
+ 후보에서 앞면 하단은 선과 일치하고, 두께 방향 뒷모서리는 투영 때문에 선 위에 보인다.
67
+ 상부/개구부/램프가 유지되는 것을 확인했다.
68
+
69
+ 이것은 제품 viewport 스크린샷이 아니다. 원본 재질/PBR/조명/그림자와 app integration은 미검증이다.
70
+ 렌더 도중 처음 발견한 바닥선 좌표 혼용과 painter 정렬 문제는 검증 도구에서 수정했다.
71
+ 최종 이미지는 world 기준선과 픽셀별 depth 비교를 사용한다.
72
+
73
+ ```sh
74
+ node --test docs/prototypes/v3-dryroom-mesh.test.mjs
75
+ # Pillow + NumPy가 있는 Python 환경 사용. 실제 생성 메시 JSON을 입력한다.
76
+ node docs/prototypes/v3-dryroom-mesh.mjs | python3 docs/prototypes/render-dryroom.py /tmp/figure-v3-dryroom-before-after.png
77
+ ```
78
+
79
+ 현재 이미지: `/private/tmp/figure-v3-dryroom-before-after.png` (임시 생성물, 위 명령으로 재생성).
80
+ 2개 mesh 검사 통과. 기존 검사와 함께 재실행한다.
81
+
82
+ ## 5. 설계에 대한 결론
83
+
84
+ 외곽 상자의 배율을 바꾸는 것보다, 실제 단면 경계와 feature를 정본으로 다루는 계약이
85
+ 이번 사용자 의도를 정확히 표현한다. 전체 도형을 scale하지 않고 하단만 편집할 수 있다.
86
+ 하지만 U형 하나의 성공을 모든 단면에 일반화하지 않는다.
87
+ 다음은 profile의 방향/곡선/feature 안정성 계약과 typed 반복 통합, 이후 전체 자산 렌더링 검증이다.
@@ -0,0 +1,59 @@
1
+ # V3 편집 화면 연결 검증 — 실험판
2
+
3
+ > 현재 구현·검증·제품 연결 상태는 [V3 통합 현황](v3-status.md)을 따른다. 본문의 단계별 연구/검증 기록은 제품 전체 완료 선언이 아니다.
4
+
5
+ ## 완료 범위
6
+
7
+ 저장된 V3 자산을 공통 편집 세션으로 열고, 입력 변경을 평가된 3D 장면으로 전달하는 연결 계층을 구현했다. **제품 모델러·서버·DB에는 아직 연결하지 않았다.**
8
+
9
+ - 연결 계층: [v3-editor-workspace.ts](../src/v3-editor-workspace.ts) — 실험 mjs는 re-export만 유지
10
+ - 계약 검증: [v3-editor-workspace.test.mjs](prototypes/v3-editor-workspace.test.mjs)
11
+ - 격리 브라우저 검증: [v3-editor-browser.mjs](prototypes/v3-editor-browser.mjs)
12
+
13
+ 원본은 관계 그래프와 저작 입력이다. 메시를 직접 수정한 뒤 그래프로 역추정하지 않는다. 속성창과 AI는 같은 단위·역할·revision 검사 및 편집 트랜잭션을 사용한다.
14
+
15
+ ## 편집 수명 주기
16
+
17
+ | 동작 | 계약 |
18
+ |---|---|
19
+ | 자산 열기 | 파싱·검증 성공 후 교체. 실패하면 기존 자산 유지 |
20
+ | 속성/AI 수정 | 활성 context와 revision 모두 일치해야 적용 |
21
+ | 다른 자산 열기 | 새 context, revision 0, 빈 undo 기록 |
22
+ | 빈 화면 | 자산·입력·binding·geometry를 모두 비움. 기본 상자를 만들지 않음 |
23
+ | 변경 후 이탈 | 저장하지 않은 설계 변경이 있을 때만 거부. 명시적 폐기 가능 |
24
+ | 동작 미리보기 | 설계 dirty 상태로 간주하지 않음 |
25
+ | export | 저장 문자열 반환만 수행. 저장 완료 표시하지 않음 |
26
+ | 저장 확인 | 동일 context/revision과 현재 내용 일치 확인 후 저장 기준 갱신 |
27
+
28
+ 서로 다른 모델은 둘 다 revision 0일 수 있다. 따라서 revision만 비교하면 늦게 도착한 AI 결과가 다른 모델에 적용될 수 있다. 이번 context는 workspace 안에서만 유일한 열기 세대 식별자다. 제품 저장 자산 ID나 영속적인 대화 ID를 대체하지 않는다.
29
+
30
+ 저장 확인 함수는 호출자가 실제 저장 성공을 확인했다는 전제다. 권한 검증이나 DB 쓰기를 수행하지 않는다. 저장 중 추가 편집이 발생하면 오래된 확인으로 dirty 상태를 지우지 않는다.
31
+
32
+ ## 실제 확인 결과
33
+
34
+ 2026-09-21, 전체 `v3-*.test.mjs` 138개 통과(실패 0). 이번 workspace 테스트 4개 포함. 별도로 실제 Chrome/Three.js 격리 화면에서 다음 경로를 검증했다.
35
+
36
+ 1. 포크리프트 20개 메시 열기.
37
+ 2. 포크 길이 850 → 1275mm 변경, 렌더링 입력의 실제 형상 치수 확인.
38
+ 3. 임시 메모리에 저장하고 저장 기준 갱신.
39
+ 4. 로봇으로 전환해 10개 메시만 남는지 확인.
40
+ 5. 이전 포크리프트 context의 AI 요청을 보내 `CONTEXT_CONFLICT` 확인.
41
+ 6. 저장본을 다시 열어 1275mm 복원 확인.
42
+ 7. 빈 화면으로 전환해 메시 0개·입력 0개 확인.
43
+
44
+ 위 기록은 최초 메모리 저장 검증이다. 이후 저장 버튼을 HTTP 기반 임시 파일 저장으로 연결했고 별도 프로세스 복원·저장 충돌 검증을 추가했다. 최신 범위는 [저장 연결 검증](v3-storage-integration.md)을 따른다. 브라우저 page error는 0개이며 `/private/tmp/figure-v3-editor.png`와 `/private/tmp/figure-v3-editor-empty.png`의 캔버스도 직접 확인했다. 제품 UI 연결을 뜻하지 않는다.
45
+
46
+ ## 제품 연결 시 주의점
47
+
48
+ 현재 figure-ui의 `client/modeller/figure-source.ts`는 장면 파트를 저장용 V2 source로 변환한다. `figure-inspector.ts`와 `figure-modeller-page.ts`의 update-part/update-draft 경로도 V2 draft/parts 중심이다. V3 graph를 이 경로의 `parts`로 가장해 전달하면 원본이 이중화된다.
49
+
50
+ 제품 모델러·저장·AI·렌더러를 V3 단일 경로로 전환한다. 기존 자산은 변환 또는 재작성한다. V2 병행 운영이나 호환 분기는 만들지 않는다. 제품에서는 src에서 빌드한 공용 API를 사용한다.
51
+
52
+ ## 아직 해결하지 않은 범위
53
+
54
+ - 빈 자산 생성과 상자 부품 추가, 입력 수정, 생성/수정 통합 undo/redo를 지원한다. 격리 UI 버튼까지 검증했지만 제품 버튼 연결, 다른 primitive 추가, 삭제·재질·관계 편집은 남아 있다.
55
+ - 제품 속성창·AI-dock·저장 API·인증·대화 이력 통합은 남아 있다.
56
+ - 실험 렌더러는 색상과 단순 roughness를 사용한다. preset/emissive 등 제품 재질의 완전한 시각적 재현 검증은 아니다.
57
+ - read마다 자산을 재컴파일한다. 증분 평가·렌더링 성능 검증이 필요하다.
58
+ - 이탈 경고 UI, 접근성, 좁은 화면, 네트워크 저장 실패의 제품 UX는 별도 검증해야 한다.
59
+ - 테스트 통과는 모델 안전성이나 제품 배포 완료 판정이 아니다.