@things-factory/figure-ui 10.1.65 → 10.1.67

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 (176) hide show
  1. package/client/graphql/index.ts +0 -45
  2. package/client/modeller/figure-history-panel.ts +6 -145
  3. package/client/modeller/figure-v3-capability.ts +2 -2
  4. package/client/modeller/figure-v3-clips.ts +2 -2
  5. package/client/modeller/figure-v3-controls.ts +2 -2
  6. package/client/modeller/figure-v3-first-save.ts +1 -1
  7. package/client/modeller/figure-v3-links.ts +6 -6
  8. package/client/modeller/figure-v3-material.ts +11 -11
  9. package/client/modeller/figure-v3-parts.ts +7 -10
  10. package/client/modeller/figure-v3-preview.ts +3 -3
  11. package/client/modeller/figure-v3-properties.ts +5 -6
  12. package/client/modeller/figure-v3-verdict.ts +9 -10
  13. package/client/modeller/figure-view.ts +2 -3
  14. package/client/modeller/modeller-route.ts +2 -3
  15. package/client/modeller/scene-budget.ts +0 -14
  16. package/client/modeller/side-panel-styles.ts +5 -5
  17. package/client/modeller/v3-align.ts +16 -6
  18. package/client/modeller/v3-canvas.ts +12 -12
  19. package/client/modeller/v3-clips.ts +1 -1
  20. package/client/modeller/v3-controls.ts +1 -1
  21. package/client/modeller/v3-editor.ts +21 -21
  22. package/client/modeller/v3-fields.ts +1 -1
  23. package/client/modeller/v3-figure-facts.ts +1 -1
  24. package/client/modeller/v3-first-save.ts +3 -4
  25. package/client/modeller/v3-gizmo.ts +5 -5
  26. package/client/modeller/v3-help.ts +1 -1
  27. package/client/modeller/v3-joint-fan.ts +4 -5
  28. package/client/modeller/v3-keys.ts +1 -1
  29. package/client/modeller/v3-links.ts +9 -12
  30. package/client/modeller/v3-maturity.ts +22 -26
  31. package/client/modeller/v3-refusals.ts +17 -0
  32. package/client/modeller/v3-renderer.ts +1 -1
  33. package/client/modeller/v3-stage-model.ts +2 -2
  34. package/client/modeller/v3-thumbnail.ts +1 -3
  35. package/client/pages/figure-v3-modeller-page.ts +8 -11
  36. package/client/types.ts +2 -31
  37. package/dist-client/graphql/index.js +0 -45
  38. package/dist-client/graphql/index.js.map +1 -1
  39. package/dist-client/modeller/figure-history-panel.d.ts +4 -37
  40. package/dist-client/modeller/figure-history-panel.js +8 -154
  41. package/dist-client/modeller/figure-history-panel.js.map +1 -1
  42. package/dist-client/modeller/figure-v3-capability.d.ts +1 -1
  43. package/dist-client/modeller/figure-v3-capability.js +1 -1
  44. package/dist-client/modeller/figure-v3-capability.js.map +1 -1
  45. package/dist-client/modeller/figure-v3-clips.d.ts +2 -2
  46. package/dist-client/modeller/figure-v3-clips.js +2 -2
  47. package/dist-client/modeller/figure-v3-clips.js.map +1 -1
  48. package/dist-client/modeller/figure-v3-controls.d.ts +1 -1
  49. package/dist-client/modeller/figure-v3-controls.js +2 -2
  50. package/dist-client/modeller/figure-v3-controls.js.map +1 -1
  51. package/dist-client/modeller/figure-v3-first-save.d.ts +1 -1
  52. package/dist-client/modeller/figure-v3-first-save.js +1 -1
  53. package/dist-client/modeller/figure-v3-first-save.js.map +1 -1
  54. package/dist-client/modeller/figure-v3-links.d.ts +5 -5
  55. package/dist-client/modeller/figure-v3-links.js +6 -6
  56. package/dist-client/modeller/figure-v3-links.js.map +1 -1
  57. package/dist-client/modeller/figure-v3-material.d.ts +6 -6
  58. package/dist-client/modeller/figure-v3-material.js +11 -11
  59. package/dist-client/modeller/figure-v3-material.js.map +1 -1
  60. package/dist-client/modeller/figure-v3-parts.d.ts +6 -8
  61. package/dist-client/modeller/figure-v3-parts.js +1 -1
  62. package/dist-client/modeller/figure-v3-parts.js.map +1 -1
  63. package/dist-client/modeller/figure-v3-preview.js.map +1 -1
  64. package/dist-client/modeller/figure-v3-properties.d.ts +4 -5
  65. package/dist-client/modeller/figure-v3-properties.js +5 -6
  66. package/dist-client/modeller/figure-v3-properties.js.map +1 -1
  67. package/dist-client/modeller/figure-v3-verdict.d.ts +2 -2
  68. package/dist-client/modeller/figure-v3-verdict.js +9 -10
  69. package/dist-client/modeller/figure-v3-verdict.js.map +1 -1
  70. package/dist-client/modeller/figure-view.d.ts +2 -3
  71. package/dist-client/modeller/figure-view.js.map +1 -1
  72. package/dist-client/modeller/modeller-route.d.ts +2 -3
  73. package/dist-client/modeller/modeller-route.js +2 -3
  74. package/dist-client/modeller/modeller-route.js.map +1 -1
  75. package/dist-client/modeller/scene-budget.d.ts +0 -11
  76. package/dist-client/modeller/scene-budget.js +0 -12
  77. package/dist-client/modeller/scene-budget.js.map +1 -1
  78. package/dist-client/modeller/side-panel-styles.d.ts +2 -2
  79. package/dist-client/modeller/side-panel-styles.js +5 -5
  80. package/dist-client/modeller/side-panel-styles.js.map +1 -1
  81. package/dist-client/modeller/v3-align.d.ts +9 -5
  82. package/dist-client/modeller/v3-align.js +13 -3
  83. package/dist-client/modeller/v3-align.js.map +1 -1
  84. package/dist-client/modeller/v3-canvas.d.ts +4 -4
  85. package/dist-client/modeller/v3-canvas.js +9 -9
  86. package/dist-client/modeller/v3-canvas.js.map +1 -1
  87. package/dist-client/modeller/v3-clips.js.map +1 -1
  88. package/dist-client/modeller/v3-controls.js.map +1 -1
  89. package/dist-client/modeller/v3-editor.d.ts +13 -13
  90. package/dist-client/modeller/v3-editor.js +19 -18
  91. package/dist-client/modeller/v3-editor.js.map +1 -1
  92. package/dist-client/modeller/v3-fields.js.map +1 -1
  93. package/dist-client/modeller/v3-figure-facts.d.ts +1 -1
  94. package/dist-client/modeller/v3-figure-facts.js +1 -1
  95. package/dist-client/modeller/v3-figure-facts.js.map +1 -1
  96. package/dist-client/modeller/v3-first-save.js +3 -4
  97. package/dist-client/modeller/v3-first-save.js.map +1 -1
  98. package/dist-client/modeller/v3-gizmo.d.ts +5 -5
  99. package/dist-client/modeller/v3-gizmo.js +1 -1
  100. package/dist-client/modeller/v3-gizmo.js.map +1 -1
  101. package/dist-client/modeller/v3-help.d.ts +1 -1
  102. package/dist-client/modeller/v3-help.js +1 -1
  103. package/dist-client/modeller/v3-help.js.map +1 -1
  104. package/dist-client/modeller/v3-joint-fan.js +4 -5
  105. package/dist-client/modeller/v3-joint-fan.js.map +1 -1
  106. package/dist-client/modeller/v3-keys.js.map +1 -1
  107. package/dist-client/modeller/v3-links.d.ts +5 -7
  108. package/dist-client/modeller/v3-links.js +6 -8
  109. package/dist-client/modeller/v3-links.js.map +1 -1
  110. package/dist-client/modeller/v3-maturity.d.ts +7 -7
  111. package/dist-client/modeller/v3-maturity.js +20 -24
  112. package/dist-client/modeller/v3-maturity.js.map +1 -1
  113. package/dist-client/modeller/v3-refusals.d.ts +6 -0
  114. package/dist-client/modeller/v3-refusals.js +18 -0
  115. package/dist-client/modeller/v3-refusals.js.map +1 -1
  116. package/dist-client/modeller/v3-renderer.js +1 -1
  117. package/dist-client/modeller/v3-renderer.js.map +1 -1
  118. package/dist-client/modeller/v3-stage-model.js.map +1 -1
  119. package/dist-client/modeller/v3-thumbnail.js +1 -3
  120. package/dist-client/modeller/v3-thumbnail.js.map +1 -1
  121. package/dist-client/pages/figure-v3-modeller-page.d.ts +4 -5
  122. package/dist-client/pages/figure-v3-modeller-page.js +8 -11
  123. package/dist-client/pages/figure-v3-modeller-page.js.map +1 -1
  124. package/dist-client/route.d.ts +1 -1
  125. package/dist-client/tsconfig.fui.tsbuildinfo +1 -1
  126. package/dist-client/tsconfig.tsbuildinfo +1 -1
  127. package/dist-client/types.d.ts +2 -29
  128. package/dist-client/types.js +3 -0
  129. package/dist-client/types.js.map +1 -1
  130. package/docs/v3-product-cutover.md +14 -24
  131. package/package.json +5 -5
  132. package/test/browser/modeller-page.js +6 -7
  133. package/test/browser/modeller.check.mjs +9 -16
  134. package/test/fixtures/joint-column.json +1138 -0
  135. package/test/fixtures/joint-roller.json +872 -0
  136. package/test/fixtures/robot-arm-6dof.json +2776 -0
  137. package/test/i18n-prefix-guard.test.ts +0 -31
  138. package/test/modeller-route.test.ts +2 -3
  139. package/test/v3-align.test.ts +9 -4
  140. package/test/v3-clips.test.ts +1 -1
  141. package/test/v3-controls.test.ts +1 -1
  142. package/test/v3-fields.test.ts +4 -4
  143. package/test/v3-first-save.test.ts +1 -1
  144. package/test/v3-gizmo.test.ts +5 -5
  145. package/test/v3-joint-fan.test.ts +12 -42
  146. package/test/v3-keys.test.ts +1 -1
  147. package/test/v3-layout.test.ts +4 -5
  148. package/test/v3-links.test.ts +3 -3
  149. package/test/v3-material.test.ts +3 -3
  150. package/test/v3-maturity.test.ts +8 -9
  151. package/test/v3-parts-wiring.test.ts +3 -3
  152. package/test/v3-preview.test.ts +1 -1
  153. package/test/v3-product-write.test.ts +1 -1
  154. package/test/v3-properties.test.ts +3 -3
  155. package/test/v3-refusals.test.ts +12 -0
  156. package/test/v3-release-panel.test.ts +5 -5
  157. package/test/v3-scene-canvas.test.ts +2 -2
  158. package/test/v3-theme.test.ts +2 -2
  159. package/test/v3-verdict.test.ts +2 -2
  160. package/test/v3-view.test.ts +2 -2
  161. package/translations/en.json +2 -143
  162. package/translations/ja.json +2 -143
  163. package/translations/ko.json +2 -143
  164. package/translations/ms.json +2 -143
  165. package/translations/zh.json +2 -143
  166. package/client/modeller/figure-source.ts +0 -437
  167. package/client/modeller/part-edits.ts +0 -312
  168. package/dist-client/modeller/figure-source.d.ts +0 -238
  169. package/dist-client/modeller/figure-source.js +0 -193
  170. package/dist-client/modeller/figure-source.js.map +0 -1
  171. package/dist-client/modeller/part-edits.d.ts +0 -89
  172. package/dist-client/modeller/part-edits.js +0 -270
  173. package/dist-client/modeller/part-edits.js.map +0 -1
  174. package/test/expect.ts +0 -57
  175. package/test/figure-source.test.ts +0 -511
  176. package/test/part-edits.test.ts +0 -350
@@ -1,350 +0,0 @@
1
- import assert from 'node:assert/strict'
2
- import { describe, it, test } from 'node:test'
3
- import { readFileSync } from 'node:fs'
4
- import { fileURLToPath } from 'node:url'
5
-
6
- import { expect } from './expect.ts'
7
-
8
- import {
9
- addPart,
10
- alignPartToEnd,
11
- alignPartToStart,
12
- canMirror,
13
- centerPart,
14
- duplicatePart,
15
- gridStep,
16
- mirrorPart,
17
- movePart,
18
- newPart,
19
- niceStep,
20
- partLimit,
21
- removePart,
22
- roundPart,
23
- snapPart,
24
- updatePart
25
- } from '../client/modeller/part-edits.ts'
26
- import { toFigureSource } from '../dist-client/modeller/figure-source.js'
27
- import type { FigureDraft, PartModel } from '../dist-client/modeller/figure-source.js'
28
- import { validate } from '@hatiolab/figure-model'
29
-
30
- /**
31
- * 씬 모델 용어로 부품을 편집하는 규칙.
32
- *
33
- * 편집 중의 기준 데이터는 씬 모델이므로 편집도 씬의 용어로 한다. 그런데 저장되는 것은
34
- * FigureSource 다. 그래서 두 가지를 확인한다 — **씬 용어에서 규칙이 맞는지**, 그리고
35
- * **변환했을 때 검증을 통과하는지**. 앞의 것만 보면 화면에서는 멀쩡한데 저장이 거부되는
36
- * 상황이 생긴다.
37
- */
38
-
39
- /** 기준 상자 3000 x 2000 x 800 — 세 축이 모두 달라야 축을 바꿔 써도 테스트가 잡아낸다. */
40
- const DRAFT: FigureDraft = {
41
- width: 3000,
42
- height: 2000,
43
- depth: 800,
44
- figureType: 'AGV',
45
- detailLevel: 'L'
46
- }
47
-
48
- const DECK: PartModel = {
49
- type: 'figure-part',
50
- name: 'deck',
51
- primitive: 'cube',
52
- left: 1200,
53
- top: 900,
54
- width: 800,
55
- height: 600,
56
- zPos: 500,
57
- depth: 200,
58
- token: 'palette.primary'
59
- }
60
-
61
- /*
62
- * 기즈모에서 온 숫자.
63
- *
64
- * 화면에서 끌면 씬이 비율을 곱한 값을 돌려준다. 그 값이 그대로 부품에 들어가면 저장 형식에
65
- * 소수 넷째 자리가 남고, 크기 칸은 잘려서 보인다.
66
- */
67
- describe('씬에서 온 부품을 밀리미터로 맞춘다', () => {
68
- const part = (over: Partial<PartModel> = {}): PartModel => ({ ...DECK, ...over })
69
-
70
- it('끈 크기와 자리를 정수로 맞춘다', () => {
71
- const dragged = part({ left: 384.99999999999994, top: 380, zPos: 930, width: 368.98743869823886, height: 140, depth: 722.4180046035418 })
72
- const fixed = roundPart(dragged)
73
-
74
- expect(fixed.left).toBe(385)
75
- expect(fixed.width).toBe(369)
76
- expect(fixed.depth).toBe(722)
77
- expect(fixed.top).toBe(380)
78
- })
79
-
80
- it('크기는 1 밑으로 안 내려간다 — 0 이면 부피가 사라지고 배율이 0 으로 나뉜다', () => {
81
- const tiny = roundPart(part({ width: 0.2, height: 0.4, depth: 0 }))
82
- expect(tiny.width).toBe(1)
83
- expect(tiny.height).toBe(1)
84
- expect(tiny.depth).toBe(1)
85
- })
86
-
87
- it('맞춘 값을 다시 맞춰도 같다', () => {
88
- const once = roundPart(part({ left: 12.5, width: 7.5 }))
89
- expect(roundPart(once)).toEqual(once)
90
- })
91
-
92
- it('크기·자리 밖의 값은 건드리지 않는다', () => {
93
- const kept = roundPart(part({ name: 'arm', token: 'palette.dark', segments: 12, zPos: 3.4 }))
94
- expect(kept.name).toBe('arm')
95
- expect(kept.token).toBe('palette.dark')
96
- expect(kept.segments).toBe(12)
97
- expect(kept.zPos).toBe(3)
98
- })
99
- })
100
-
101
- describe('부품 생성', () => {
102
- it('기준 상자의 3분의 1 크기로 생성된다 — 추가하자마자 보여야 한다', () => {
103
- const part = newPart(DRAFT, [], 'cube')
104
-
105
- expect(part.width).toBe(1000)
106
- expect(part.height).toBe(667)
107
- expect(part.depth).toBe(267)
108
- })
109
-
110
- it('바닥 가운데에 놓인다', () => {
111
- const part = newPart(DRAFT, [], 'cube')
112
-
113
- expect(part.zPos).toBe(0)
114
- // 위치도 크기도 정수로 맞추므로 0.5 까지는 어긋난다
115
- expect(Math.abs(part.left + part.width / 2 - DRAFT.width / 2)).toBeLessThanOrEqual(0.5)
116
- expect(Math.abs(part.top + part.height / 2 - DRAFT.height / 2)).toBeLessThanOrEqual(0.5)
117
- })
118
-
119
- it('편집 캔버스에 그대로 놓을 수 있는 씬 컴포넌트 타입을 갖는다', () => {
120
- expect(newPart(DRAFT, [], 'cube').type).toBe('figure-part')
121
- })
122
-
123
- it('이름이 중복되지 않는다 — 저장되는 식별자다', () => {
124
- let parts: PartModel[] = []
125
- for (let i = 0; i < 3; i++) parts = addPart(DRAFT, parts, 'cube')
126
-
127
- expect(parts.map(part => part.name)).toEqual(['cube', 'cube-2', 'cube-3'])
128
- })
129
-
130
- it('팔레트 토큰을 기존 부품에서 물려받는다 — 토큰이 늘면 재질 그룹이 하나씩 는다', () => {
131
- const parts = addPart(DRAFT, [{ ...DECK, token: 'palette.dark' }], 'sphere')
132
- expect(parts[1].token).toBe('palette.dark')
133
- })
134
-
135
- it('곡면 도형은 분할 수를 갖고 생성된다', () => {
136
- expect(newPart(DRAFT, [], 'cylinder').segments).toBe(12)
137
- expect(newPart(DRAFT, [], 'cube').segments).toBeUndefined()
138
- })
139
-
140
- it('polygon 은 삼각형으로 생성된다 — 점이 3개보다 적으면 검증에서 막힌다', () => {
141
- expect(newPart(DRAFT, [], 'polygon').path).toHaveLength(3)
142
- })
143
-
144
- /*
145
- * 둘째 부품이 어디에 서는가.
146
- *
147
- * 언제나 바닥 한가운데였을 때, 둘째 부품은 첫 부품 **속에** 들어갔다 — 같은 자리에 같은
148
- * 크기라 화면에는 덩어리 하나뿐이고 저작자는 무엇이 늘었는지 못 봤다.
149
- */
150
- describe('고른 부품 위에 얹는다', () => {
151
- /* 갑판을 바닥에 내려 둔다 — 그 위에 3분의 1 짜리가 들어가야 얹는 갈래를 볼 수 있다. */
152
- const FLOOR_DECK = { ...DECK, zPos: 0 }
153
-
154
- it('윗면에 서고 발자국이 가운데로 맞는다', () => {
155
- const part = newPart(DRAFT, [FLOOR_DECK], 'cube', FLOOR_DECK)
156
-
157
- expect(part.zPos).toBe(FLOOR_DECK.zPos + FLOOR_DECK.depth)
158
- // 자리도 크기도 정수로 맞추므로 0.5 까지는 어긋난다
159
- const middle = { x: FLOOR_DECK.left + FLOOR_DECK.width / 2, y: FLOOR_DECK.top + FLOOR_DECK.height / 2 }
160
- expect(Math.abs(part.left + part.width / 2 - middle.x)).toBeLessThanOrEqual(0.5)
161
- expect(Math.abs(part.top + part.height / 2 - middle.y)).toBeLessThanOrEqual(0.5)
162
- })
163
-
164
- it('얹으면 상자를 넘을 때는 바닥 한가운데로 간다 — 추가가 발행을 막지 않는다', () => {
165
- /* 갑판이 이미 높다: 500 + 200 위에 267 을 더 쌓으면 상자 높이 800 을 넘는다. */
166
- const part = newPart(DRAFT, [DECK], 'cube', DECK)
167
-
168
- expect(part.zPos).toBe(0)
169
- expect(Math.abs(part.left + part.width / 2 - DRAFT.width / 2)).toBeLessThanOrEqual(0.5)
170
- })
171
-
172
- it('가장자리 부품 위에서도 상자 안에 남는다', () => {
173
- const corner = { ...DECK, left: 0, top: 0, width: 200, height: 200, zPos: 0, depth: 100 }
174
- const part = newPart(DRAFT, [corner], 'cube', corner)
175
-
176
- expect(part.left).toBe(0)
177
- expect(part.top).toBe(0)
178
- expect(part.left + part.width).toBeLessThanOrEqual(DRAFT.width)
179
- expect(part.top + part.height).toBeLessThanOrEqual(DRAFT.height)
180
- })
181
-
182
- it('고른 것이 없으면 전과 같이 바닥 한가운데다', () => {
183
- const alone = newPart(DRAFT, [DECK], 'cube')
184
- expect(alone.zPos).toBe(0)
185
- expect(Math.abs(alone.left + alone.width / 2 - DRAFT.width / 2)).toBeLessThanOrEqual(0.5)
186
- })
187
-
188
- it('쌓은 둘이 서로 겹치지 않는다 — 이 규칙이 지키는 것이 그것이다', () => {
189
- const [, stacked] = addPart(DRAFT, [FLOOR_DECK], 'cylinder', FLOOR_DECK)
190
- expect(stacked!.zPos).toBeGreaterThanOrEqual(FLOOR_DECK.zPos + FLOOR_DECK.depth)
191
- })
192
- })
193
-
194
- /*
195
- 화면에서 멀쩡해 보여도 저장이 거부되면 사용자는 이유를 알 수 없다. 새로 만든 부품은
196
- 손대지 않은 상태로도 검증을 통과해야 한다.
197
- */
198
- it('새로 만든 부품만으로도 검증을 통과한다', () => {
199
- for (const kind of ['cube', 'wall', 'cylinder', 'sphere', 'rect', 'polygon'] as const) {
200
- const source = toFigureSource(DRAFT, [newPart(DRAFT, [], kind)])
201
- expect(validate(source).errors).toEqual([])
202
- }
203
- })
204
- })
205
-
206
- describe('목록 편집', () => {
207
- const PARTS = [DECK, { ...DECK, name: 'mast' }, { ...DECK, name: 'wheel' }]
208
-
209
- it('삭제한 것만 빠진다', () => {
210
- expect(removePart(PARTS, 1).map(part => part.name)).toEqual(['deck', 'wheel'])
211
- })
212
-
213
- it('복제본이 바로 뒤에 오고 이름이 새로 붙는다', () => {
214
- const next = duplicatePart(PARTS, 0)
215
- expect(next.map(part => part.name)).toEqual(['deck', 'deck-copy', 'mast', 'wheel'])
216
- })
217
-
218
- it('복제본이 원본과 객체를 공유하지 않는다 — 수정하면 따로 움직여야 한다', () => {
219
- const next = duplicatePart([{ ...DECK, path: [{ x: 0, y: 0 }] }], 0)
220
- next[1].path![0].x = 99
221
-
222
- expect(next[0].path![0].x).toBe(0)
223
- })
224
-
225
- it('순서를 바꾼다', () => {
226
- expect(movePart(PARTS, 2, 0).map(part => part.name)).toEqual(['wheel', 'deck', 'mast'])
227
- })
228
-
229
- it('범위를 벗어나면 그대로 둔다', () => {
230
- expect(movePart(PARTS, 0, -1)).toBe(PARTS)
231
- expect(movePart(PARTS, 2, 3)).toBe(PARTS)
232
- })
233
-
234
- it('원본을 수정하지 않는다', () => {
235
- const before = JSON.stringify(PARTS)
236
- removePart(PARTS, 0)
237
- duplicatePart(PARTS, 0)
238
- updatePart(PARTS, 0, { ...DECK, left: 0 })
239
-
240
- expect(JSON.stringify(PARTS)).toBe(before)
241
- })
242
-
243
- it('부품 수 한도는 디테일 등급이 결정한다', () => {
244
- expect(partLimit({ ...DRAFT, detailLevel: 'S' })).toBeLessThan(partLimit({ ...DRAFT, detailLevel: 'L' }))
245
- })
246
- })
247
-
248
- /*
249
- 정밀 배치 도구.
250
-
251
- 씬 모델은 원점이 **모서리**다(left·top 이 0 에서 시작하고 zPos 는 밑면이다).
252
- FigureSource 는 원점이 중심이다. 이전 구현은 FigureSource 기준이라 '가운데로'가 곧
253
- '0 으로'였는데, 씬 용어에서는 그렇지 않다 — 이 절이 그 차이를 확인한다.
254
- */
255
- describe('정밀 배치 도구', () => {
256
- it('가운데로 옮기면 변환했을 때 원점에 온다', () => {
257
- const moved = centerPart(DRAFT, [{ ...DECK, left: 0 }], 0, 'x')
258
-
259
- expect(moved[0].left).toBe(1100)
260
- expect(toFigureSource(DRAFT, moved).parts[0].transform.position.x).toBe(0)
261
- })
262
-
263
- it('축마다 다른 속성을 바꾼다 — z 는 top 이고 y 는 zPos 다', () => {
264
- expect(centerPart(DRAFT, [DECK], 0, 'z')[0].top).toBe(700)
265
- expect(centerPart(DRAFT, [DECK], 0, 'y')[0].zPos).toBe(300)
266
- })
267
-
268
- it('바닥에 붙이면 zPos 가 0 이다 — 밑면이 곧 상자 바닥이다', () => {
269
- expect(alignPartToStart([DECK], 0, 'y')[0].zPos).toBe(0)
270
- })
271
-
272
- it('끝에 붙이면 부품의 끝이 상자의 끝과 만난다', () => {
273
- const moved = alignPartToEnd(DRAFT, [DECK], 0, 'x')
274
- expect(moved[0].left + moved[0].width).toBe(DRAFT.width)
275
- })
276
-
277
- it('대칭 복제가 상자 안에서 반대쪽으로 간다', () => {
278
- const parts = mirrorPart(DRAFT, [{ ...DECK, left: 200 }], 0, 'x')
279
-
280
- expect(parts).toHaveLength(2)
281
- // 왼쪽 여백 200 → 오른쪽 여백도 200 이어야 한다
282
- expect(parts[1].left).toBe(DRAFT.width - 200 - DECK.width)
283
- })
284
-
285
- it('대칭 복제본이 어느 쪽인지 이름에 드러난다', () => {
286
- expect(mirrorPart(DRAFT, [{ ...DECK, left: 200 }], 0, 'x')[1].name).toBe('deck-x')
287
- })
288
-
289
- it('대칭 복제가 원본을 건드리지 않는다', () => {
290
- const parts = mirrorPart(DRAFT, [{ ...DECK, left: 200 }], 0, 'x')
291
- expect(parts[0].left).toBe(200)
292
- })
293
-
294
- /*
295
- 이미 가운데면 사본이 같은 자리에 생긴다. 화면에는 아무 변화가 없어 보이는데 부품 수만
296
- 늘고 겹친 면에서 z-fighting 이 일어난다.
297
- */
298
- it('이미 가운데면 대칭 복제를 막는다', () => {
299
- const centered = centerPart(DRAFT, [DECK], 0, 'x')
300
-
301
- expect(canMirror(DRAFT, centered, 0, 'x')).toBe(false)
302
- expect(mirrorPart(DRAFT, centered, 0, 'x')).toHaveLength(1)
303
- })
304
-
305
- it('가운데가 아니면 대칭 복제할 수 있다', () => {
306
- expect(canMirror(DRAFT, [{ ...DECK, left: 200 }], 0, 'x')).toBe(true)
307
- })
308
-
309
- it('격자에 맞추면 위치와 크기가 격자 배수가 된다', () => {
310
- const step = gridStep(DRAFT)
311
- const snapped = snapPart(DRAFT, [{ ...DECK, left: 1213, width: 787 }], 0)
312
-
313
- expect(snapped[0].left % step).toBe(0)
314
- expect(snapped[0].width % step).toBe(0)
315
- })
316
-
317
- it('격자에 맞춰도 크기가 0 이 되지 않는다 — 사라진 것처럼 보인다', () => {
318
- const snapped = snapPart(DRAFT, [{ ...DECK, width: 1, height: 1, depth: 1 }], 0)
319
-
320
- expect(snapped[0].width).toBeGreaterThan(0)
321
- expect(snapped[0].height).toBeGreaterThan(0)
322
- expect(snapped[0].depth).toBeGreaterThan(0)
323
- })
324
-
325
- it('없는 인덱스를 주면 목록을 그대로 반환한다', () => {
326
- const parts = [DECK]
327
- expect(centerPart(DRAFT, parts, 7, 'x')).toBe(parts)
328
- expect(snapPart(DRAFT, parts, 7)).toBe(parts)
329
- expect(mirrorPart(DRAFT, parts, 7, 'x')).toBe(parts)
330
- expect(canMirror(DRAFT, parts, 7, 'x')).toBe(false)
331
- })
332
- })
333
-
334
- describe('격자 간격', () => {
335
- it('읽기 좋은 값이 나온다 — 27·33 같은 간격은 좌표를 읽을 수 없게 만든다', () => {
336
- expect(niceStep(3000)).toBe(500)
337
- expect(niceStep(1000)).toBe(200)
338
- expect(niceStep(137)).toBe(20)
339
- expect(niceStep(4)).toBe(1)
340
- })
341
-
342
- it('아무리 작아도 0 이 되지 않는다 — 0 으로 나누면 NaN 이 된다', () => {
343
- expect(niceStep(0)).toBeGreaterThan(0)
344
- expect(niceStep(-100)).toBeGreaterThan(0)
345
- })
346
-
347
- it('기준 상자의 긴 쪽을 기준으로 잡는다 — 3D 바닥 격자와 같아야 한다', () => {
348
- expect(gridStep(DRAFT)).toBe(niceStep(3000))
349
- })
350
- })