@operato/scene-storage 10.0.0-beta.43 → 10.0.0-beta.46

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 (50) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/box-3d.d.ts +2 -0
  3. package/dist/box-3d.js +103 -64
  4. package/dist/box-3d.js.map +1 -1
  5. package/dist/crane-3d.d.ts +10 -0
  6. package/dist/crane-3d.js +34 -5
  7. package/dist/crane-3d.js.map +1 -1
  8. package/dist/crane.d.ts +136 -6
  9. package/dist/crane.js +567 -46
  10. package/dist/crane.js.map +1 -1
  11. package/dist/pallet-3d.d.ts +2 -0
  12. package/dist/pallet-3d.js +103 -53
  13. package/dist/pallet-3d.js.map +1 -1
  14. package/dist/parcel-3d.d.ts +1 -0
  15. package/dist/parcel-3d.js +18 -1
  16. package/dist/parcel-3d.js.map +1 -1
  17. package/dist/rack-grid-3d.js +26 -8
  18. package/dist/rack-grid-3d.js.map +1 -1
  19. package/dist/rack-grid.d.ts +94 -10
  20. package/dist/rack-grid.js +468 -86
  21. package/dist/rack-grid.js.map +1 -1
  22. package/dist/storage-rack-3d.js +1 -1
  23. package/dist/storage-rack-3d.js.map +1 -1
  24. package/dist/storage-rack.d.ts +31 -6
  25. package/dist/storage-rack.js +96 -14
  26. package/dist/storage-rack.js.map +1 -1
  27. package/package.json +3 -3
  28. package/src/box-3d.ts +121 -68
  29. package/src/crane-3d.ts +34 -4
  30. package/src/crane.ts +615 -55
  31. package/src/pallet-3d.ts +122 -55
  32. package/src/parcel-3d.ts +19 -1
  33. package/src/rack-grid-3d.ts +31 -8
  34. package/src/rack-grid.ts +488 -82
  35. package/src/storage-rack-3d.ts +1 -1
  36. package/src/storage-rack.ts +96 -14
  37. package/test/test-coord-alignment.ts +2 -2
  38. package/test/test-crane-bay-match.ts +130 -0
  39. package/test/test-crane-binding-resolve.ts +168 -0
  40. package/test/test-crane-duration.ts +90 -0
  41. package/test/test-crane-rotation-reach.ts +218 -0
  42. package/test/test-rack-grid-3d-alignment.ts +235 -0
  43. package/test/test-rack-grid-3d-attach-real.ts +375 -0
  44. package/test/test-rack-grid-cell.ts +2 -2
  45. package/test/test-rack-grid-location.ts +2 -2
  46. package/test/test-rack-grid-occupied-slots.ts +165 -0
  47. package/test/test-rack-grid-picking-position.ts +154 -0
  48. package/test/test-rack-grid-slot-api.ts +483 -0
  49. package/test/test-slot-ids-enumeration.ts +137 -0
  50. package/tsconfig.tsbuildinfo +1 -1
package/dist/pallet-3d.js CHANGED
@@ -19,6 +19,51 @@
19
19
  import * as THREE from 'three';
20
20
  import * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js';
21
21
  import { RealObjectGroup } from '@hatiolab/things-scene';
22
+ // ── Material cache by bodyColor (shared across all Pallet3D instances) ──
23
+ // 동일 bodyColor 의 pallet 수십~수백 개 → GPU material 인스턴스 *1개로 통합*.
24
+ const woodBodyMaterials = new Map();
25
+ const woodStringerMaterials = new Map();
26
+ const plasticDeckMaterials = new Map();
27
+ const plasticFootMaterials = new Map();
28
+ function getWoodBodyMaterial(bodyColor) {
29
+ let m = woodBodyMaterials.get(bodyColor);
30
+ if (!m) {
31
+ m = new THREE.MeshStandardMaterial({ color: bodyColor, metalness: 0.0, roughness: 0.85 });
32
+ woodBodyMaterials.set(bodyColor, m);
33
+ }
34
+ return m;
35
+ }
36
+ function getWoodStringerMaterial(bodyColor) {
37
+ let m = woodStringerMaterials.get(bodyColor);
38
+ if (!m) {
39
+ const tint = new THREE.Color(bodyColor).multiplyScalar(0.85);
40
+ m = new THREE.MeshStandardMaterial({ color: tint, metalness: 0.0, roughness: 0.9 });
41
+ woodStringerMaterials.set(bodyColor, m);
42
+ }
43
+ return m;
44
+ }
45
+ function getPlasticDeckMaterial(bodyColor) {
46
+ let m = plasticDeckMaterials.get(bodyColor);
47
+ if (!m) {
48
+ m = new THREE.MeshStandardMaterial({ color: bodyColor, metalness: 0.1, roughness: 0.55 });
49
+ plasticDeckMaterials.set(bodyColor, m);
50
+ }
51
+ return m;
52
+ }
53
+ function getPlasticFootMaterial(bodyColor) {
54
+ let m = plasticFootMaterials.get(bodyColor);
55
+ if (!m) {
56
+ const tint = new THREE.Color(bodyColor).multiplyScalar(0.85);
57
+ m = new THREE.MeshStandardMaterial({ color: tint, metalness: 0.1, roughness: 0.65 });
58
+ plasticFootMaterials.set(bodyColor, m);
59
+ }
60
+ return m;
61
+ }
62
+ // ── Geometry cache by size (shared across all Pallet3D instances) ──
63
+ // 동일 width × height × depth 의 pallet → merged geometry *1세트*만 GPU 에 업로드.
64
+ // Translation / merge 비용도 동일 size pallet 끼리 *1회*만 발생.
65
+ const woodGeoCache = new Map();
66
+ const plasticGeoCache = new Map();
22
67
  export class Pallet3D extends RealObjectGroup {
23
68
  build() {
24
69
  super.build();
@@ -34,26 +79,30 @@ export class Pallet3D extends RealObjectGroup {
34
79
  }
35
80
  /** Wood EUR-style: 7 top slats + 3 stringers + 5 bottom slats. */
36
81
  buildWood(width, height, depth, bodyColor) {
82
+ const { top, stringer, bottom } = this.getWoodGeometries(width, height, depth);
83
+ const woodMaterial = getWoodBodyMaterial(bodyColor);
84
+ const stringerMaterial = getWoodStringerMaterial(bodyColor);
85
+ const topSlatMesh = new THREE.Mesh(top, woodMaterial);
86
+ topSlatMesh.castShadow = true;
87
+ topSlatMesh.receiveShadow = true;
88
+ this.object3d.add(topSlatMesh);
89
+ const stringerMesh = new THREE.Mesh(stringer, stringerMaterial);
90
+ stringerMesh.castShadow = true;
91
+ this.object3d.add(stringerMesh);
92
+ const botSlatMesh = new THREE.Mesh(bottom, woodMaterial);
93
+ botSlatMesh.receiveShadow = true;
94
+ this.object3d.add(botSlatMesh);
95
+ }
96
+ getWoodGeometries(width, height, depth) {
97
+ const key = `${width}|${height}|${depth}`;
98
+ let cached = woodGeoCache.get(key);
99
+ if (cached)
100
+ return cached;
37
101
  const baseY = -depth / 2;
38
102
  const slatThickness = depth * 0.15;
39
103
  const stringerThickness = depth * 0.45;
40
104
  const bottomSlatThickness = depth * 0.13;
41
- const woodMaterial = new THREE.MeshStandardMaterial({
42
- color: bodyColor,
43
- metalness: 0.0,
44
- roughness: 0.85
45
- });
46
- const stringerColor = new THREE.Color(bodyColor).multiplyScalar(0.85);
47
- const stringerMaterial = new THREE.MeshStandardMaterial({
48
- color: stringerColor,
49
- metalness: 0.0,
50
- roughness: 0.9
51
- });
52
- // ── Top + bottom slats — same count, same z-positions, paired vertically ─
53
- // EUR-pallet style: 5 boards on top, 5 below (under the same z ranges so
54
- // they read as a single skeleton rather than two unrelated grids).
55
105
  const slatCount = 5;
56
- const slatW = width;
57
106
  const slatD = (height * 0.92) / (slatCount + (slatCount - 1) * 0.4);
58
107
  const gapD = slatD * 0.4;
59
108
  const totalSpan = slatCount * slatD + (slatCount - 1) * gapD;
@@ -64,15 +113,11 @@ export class Pallet3D extends RealObjectGroup {
64
113
  }
65
114
  const topSlatGeos = [];
66
115
  for (const z of slatPositions) {
67
- const slat = new THREE.BoxGeometry(slatW, slatThickness, slatD);
116
+ const slat = new THREE.BoxGeometry(width, slatThickness, slatD);
68
117
  slat.translate(0, baseY + depth - slatThickness / 2, z);
69
118
  topSlatGeos.push(slat);
70
119
  }
71
- const topSlatMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(topSlatGeos), woodMaterial);
72
- topSlatMesh.castShadow = true;
73
- topSlatMesh.receiveShadow = true;
74
- this.object3d.add(topSlatMesh);
75
- // ── Stringers (3 perpendicular blocks between top and bottom decks) ─
120
+ const top = BufferGeometryUtils.mergeGeometries(topSlatGeos);
76
121
  const stringerCount = 3;
77
122
  const stringerW = width * 0.07;
78
123
  const stringerY = baseY + bottomSlatThickness + stringerThickness / 2;
@@ -84,45 +129,48 @@ export class Pallet3D extends RealObjectGroup {
84
129
  stringer.translate(x, stringerY, 0);
85
130
  stringerGeos.push(stringer);
86
131
  }
87
- const stringerMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(stringerGeos), stringerMaterial);
88
- stringerMesh.castShadow = true;
89
- this.object3d.add(stringerMesh);
90
- // ── Bottom slats — same z-positions as top so the deck reads as paired ─
132
+ const stringer = BufferGeometryUtils.mergeGeometries(stringerGeos);
91
133
  const botSlatGeos = [];
92
134
  for (const z of slatPositions) {
93
135
  const slat = new THREE.BoxGeometry(width, bottomSlatThickness, slatD);
94
136
  slat.translate(0, baseY + bottomSlatThickness / 2, z);
95
137
  botSlatGeos.push(slat);
96
138
  }
97
- const botSlatMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(botSlatGeos), woodMaterial);
98
- botSlatMesh.receiveShadow = true;
99
- this.object3d.add(botSlatMesh);
139
+ const bottom = BufferGeometryUtils.mergeGeometries(botSlatGeos);
140
+ cached = { top, stringer, bottom };
141
+ woodGeoCache.set(key, cached);
142
+ return cached;
100
143
  }
101
144
  /** Plastic molded: solid top deck + ribbed underside / feet. */
102
145
  buildPlastic(width, height, depth, bodyColor) {
146
+ const { deck, feet, brace } = this.getPlasticGeometries(width, height, depth);
147
+ const deckMaterial = getPlasticDeckMaterial(bodyColor);
148
+ const footMaterial = getPlasticFootMaterial(bodyColor);
103
149
  const baseY = -depth / 2;
104
150
  const deckThickness = depth * 0.30;
105
- const footH = depth * 0.55;
106
- const footW = width * 0.12;
107
- const deckMaterial = new THREE.MeshStandardMaterial({
108
- color: bodyColor,
109
- metalness: 0.1,
110
- roughness: 0.55
111
- });
112
- const footColor = new THREE.Color(bodyColor).multiplyScalar(0.85);
113
- const footMaterial = new THREE.MeshStandardMaterial({
114
- color: footColor,
115
- metalness: 0.1,
116
- roughness: 0.65
117
- });
118
- // ── Solid top deck ───────────────────────────────────────────────
119
- const deckGeo = new THREE.BoxGeometry(width * 0.98, deckThickness, height * 0.98);
120
- const deckMesh = new THREE.Mesh(deckGeo, deckMaterial);
151
+ const deckMesh = new THREE.Mesh(deck, deckMaterial);
121
152
  deckMesh.position.set(0, baseY + depth - deckThickness / 2, 0);
122
153
  deckMesh.castShadow = true;
123
154
  deckMesh.receiveShadow = true;
124
155
  this.object3d.add(deckMesh);
125
- // ── 9 feet (3×3 grid — typical plastic pallet underside) ─────────
156
+ const footMesh = new THREE.Mesh(feet, footMaterial);
157
+ footMesh.castShadow = true;
158
+ this.object3d.add(footMesh);
159
+ const braceMesh = new THREE.Mesh(brace, footMaterial);
160
+ this.object3d.add(braceMesh);
161
+ }
162
+ getPlasticGeometries(width, height, depth) {
163
+ const key = `${width}|${height}|${depth}`;
164
+ let cached = plasticGeoCache.get(key);
165
+ if (cached)
166
+ return cached;
167
+ const baseY = -depth / 2;
168
+ const deckThickness = depth * 0.30;
169
+ const footH = depth * 0.55;
170
+ const footW = width * 0.12;
171
+ // Deck geo — local space; mesh position applied at instantiation.
172
+ const deck = new THREE.BoxGeometry(width * 0.98, deckThickness, height * 0.98);
173
+ // 9 feet (3×3 grid) → merged geometry with translations baked in.
126
174
  const footGeos = [];
127
175
  for (let i = -1; i <= 1; i++) {
128
176
  for (let j = -1; j <= 1; j++) {
@@ -133,17 +181,19 @@ export class Pallet3D extends RealObjectGroup {
133
181
  footGeos.push(foot);
134
182
  }
135
183
  }
136
- const footMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(footGeos), footMaterial);
137
- footMesh.castShadow = true;
138
- this.object3d.add(footMesh);
139
- // ── Cross-bracing along underside (suggests molded reinforcement) ─
184
+ const feet = BufferGeometryUtils.mergeGeometries(footGeos);
185
+ // 3 braces → merged into single mesh (이전에 mesh 3개 분리 = drawcall 3개).
140
186
  const braceH = depth * 0.10;
141
- const braceGeo = new THREE.BoxGeometry(width * 0.95, braceH, height * 0.04);
187
+ const braceGeos = [];
142
188
  for (const zSign of [-1, 0, 1]) {
143
- const brace = new THREE.Mesh(braceGeo.clone(), footMaterial);
144
- brace.position.set(0, baseY + footH - braceH / 2, zSign * height * 0.4);
145
- this.object3d.add(brace);
189
+ const b = new THREE.BoxGeometry(width * 0.95, braceH, height * 0.04);
190
+ b.translate(0, baseY + footH - braceH / 2, zSign * height * 0.4);
191
+ braceGeos.push(b);
146
192
  }
193
+ const brace = BufferGeometryUtils.mergeGeometries(braceGeos);
194
+ cached = { deck, feet, brace };
195
+ plasticGeoCache.set(key, cached);
196
+ return cached;
147
197
  }
148
198
  updateDimension() { }
149
199
  onchange(after, before) {
@@ -1 +1 @@
1
- {"version":3,"file":"pallet-3d.js","sourceRoot":"","sources":["../src/pallet-3d.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * Pallet 3D — wood and plastic variants.\n *\n * LO-POLY but structurally distinguishing the two materials:\n *\n * - wood: parallel top slats with gaps between + 3 perpendicular\n * stringers (the classic EUR pallet silhouette) + parallel\n * bottom slats. The forklift entry holes between stringers are\n * the wood pallet's visual signature.\n * - plastic: solid molded top deck (with a few suggestion cutouts as visual\n * detail) + ribbed underside / feet. No discrete slats — the\n * plastic pallet's signature is the seamless one-piece look.\n *\n * Color comes from `state.bodyColor` (Legendable, driven by `material`).\n * Stringer / underside colors are slightly darker tints derived from bodyColor.\n */\n\nimport * as THREE from 'three'\nimport * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js'\nimport { RealObjectGroup } from '@hatiolab/things-scene'\n\nexport class Pallet3D extends RealObjectGroup {\n build() {\n super.build()\n\n const { width, height, depth = 150 } = this.component.state\n const material = (this.component.state.material as string) || 'wood'\n const bodyColor = (this.component.state.bodyColor as string) || '#a87644'\n\n if (material === 'plastic') {\n this.buildPlastic(width, height, depth, bodyColor)\n } else {\n this.buildWood(width, height, depth, bodyColor)\n }\n }\n\n /** Wood EUR-style: 7 top slats + 3 stringers + 5 bottom slats. */\n private buildWood(width: number, height: number, depth: number, bodyColor: string) {\n const baseY = -depth / 2\n const slatThickness = depth * 0.15\n const stringerThickness = depth * 0.45\n const bottomSlatThickness = depth * 0.13\n\n const woodMaterial = new THREE.MeshStandardMaterial({\n color: bodyColor,\n metalness: 0.0,\n roughness: 0.85\n })\n const stringerColor = new THREE.Color(bodyColor).multiplyScalar(0.85)\n const stringerMaterial = new THREE.MeshStandardMaterial({\n color: stringerColor,\n metalness: 0.0,\n roughness: 0.9\n })\n\n // ── Top + bottom slats — same count, same z-positions, paired vertically ─\n // EUR-pallet style: 5 boards on top, 5 below (under the same z ranges so\n // they read as a single skeleton rather than two unrelated grids).\n const slatCount = 5\n const slatW = width\n const slatD = (height * 0.92) / (slatCount + (slatCount - 1) * 0.4)\n const gapD = slatD * 0.4\n const totalSpan = slatCount * slatD + (slatCount - 1) * gapD\n const startZ = -totalSpan / 2\n\n const slatPositions: number[] = []\n for (let i = 0; i < slatCount; i++) {\n slatPositions.push(startZ + i * (slatD + gapD) + slatD / 2)\n }\n\n const topSlatGeos: THREE.BufferGeometry[] = []\n for (const z of slatPositions) {\n const slat = new THREE.BoxGeometry(slatW, slatThickness, slatD)\n slat.translate(0, baseY + depth - slatThickness / 2, z)\n topSlatGeos.push(slat)\n }\n const topSlatMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(topSlatGeos), woodMaterial)\n topSlatMesh.castShadow = true\n topSlatMesh.receiveShadow = true\n this.object3d.add(topSlatMesh)\n\n // ── Stringers (3 perpendicular blocks between top and bottom decks) ─\n const stringerCount = 3\n const stringerW = width * 0.07\n const stringerY = baseY + bottomSlatThickness + stringerThickness / 2\n const stringerGeos: THREE.BufferGeometry[] = []\n for (let i = 0; i < stringerCount; i++) {\n const xFrac = i / (stringerCount - 1) - 0.5\n const x = xFrac * (width * 0.85)\n const stringer = new THREE.BoxGeometry(stringerW, stringerThickness, height)\n stringer.translate(x, stringerY, 0)\n stringerGeos.push(stringer)\n }\n const stringerMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(stringerGeos), stringerMaterial)\n stringerMesh.castShadow = true\n this.object3d.add(stringerMesh)\n\n // ── Bottom slats — same z-positions as top so the deck reads as paired ─\n const botSlatGeos: THREE.BufferGeometry[] = []\n for (const z of slatPositions) {\n const slat = new THREE.BoxGeometry(width, bottomSlatThickness, slatD)\n slat.translate(0, baseY + bottomSlatThickness / 2, z)\n botSlatGeos.push(slat)\n }\n const botSlatMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(botSlatGeos), woodMaterial)\n botSlatMesh.receiveShadow = true\n this.object3d.add(botSlatMesh)\n }\n\n /** Plastic molded: solid top deck + ribbed underside / feet. */\n private buildPlastic(width: number, height: number, depth: number, bodyColor: string) {\n const baseY = -depth / 2\n const deckThickness = depth * 0.30\n const footH = depth * 0.55\n const footW = width * 0.12\n\n const deckMaterial = new THREE.MeshStandardMaterial({\n color: bodyColor,\n metalness: 0.1,\n roughness: 0.55\n })\n const footColor = new THREE.Color(bodyColor).multiplyScalar(0.85)\n const footMaterial = new THREE.MeshStandardMaterial({\n color: footColor,\n metalness: 0.1,\n roughness: 0.65\n })\n\n // ── Solid top deck ───────────────────────────────────────────────\n const deckGeo = new THREE.BoxGeometry(width * 0.98, deckThickness, height * 0.98)\n const deckMesh = new THREE.Mesh(deckGeo, deckMaterial)\n deckMesh.position.set(0, baseY + depth - deckThickness / 2, 0)\n deckMesh.castShadow = true\n deckMesh.receiveShadow = true\n this.object3d.add(deckMesh)\n\n // ── 9 feet (3×3 grid — typical plastic pallet underside) ─────────\n const footGeos: THREE.BufferGeometry[] = []\n for (let i = -1; i <= 1; i++) {\n for (let j = -1; j <= 1; j++) {\n const x = i * (width * 0.4)\n const z = j * (height * 0.4)\n const foot = new THREE.BoxGeometry(footW, footH, footW)\n foot.translate(x, baseY + footH / 2, z)\n footGeos.push(foot)\n }\n }\n const footMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(footGeos), footMaterial)\n footMesh.castShadow = true\n this.object3d.add(footMesh)\n\n // ── Cross-bracing along underside (suggests molded reinforcement) ─\n const braceH = depth * 0.10\n const braceGeo = new THREE.BoxGeometry(width * 0.95, braceH, height * 0.04)\n for (const zSign of [-1, 0, 1]) {\n const brace = new THREE.Mesh(braceGeo.clone(), footMaterial)\n brace.position.set(0, baseY + footH - braceH / 2, zSign * height * 0.4)\n this.object3d.add(brace)\n }\n }\n\n updateDimension() {}\n\n onchange(after: Record<string, unknown>, before: Record<string, unknown>) {\n if (\n 'material' in after ||\n 'bodyColor' in after ||\n 'width' in after ||\n 'height' in after ||\n 'depth' in after\n ) {\n this.update()\n return\n }\n super.onchange(after, before)\n }\n\n updateAlpha() {}\n}\n"]}
1
+ {"version":3,"file":"pallet-3d.js","sourceRoot":"","sources":["../src/pallet-3d.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * Pallet 3D — wood and plastic variants.\n *\n * LO-POLY but structurally distinguishing the two materials:\n *\n * - wood: parallel top slats with gaps between + 3 perpendicular\n * stringers (the classic EUR pallet silhouette) + parallel\n * bottom slats. The forklift entry holes between stringers are\n * the wood pallet's visual signature.\n * - plastic: solid molded top deck (with a few suggestion cutouts as visual\n * detail) + ribbed underside / feet. No discrete slats — the\n * plastic pallet's signature is the seamless one-piece look.\n *\n * Color comes from `state.bodyColor` (Legendable, driven by `material`).\n * Stringer / underside colors are slightly darker tints derived from bodyColor.\n */\n\nimport * as THREE from 'three'\nimport * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js'\nimport { RealObjectGroup } from '@hatiolab/things-scene'\n\n// ── Material cache by bodyColor (shared across all Pallet3D instances) ──\n// 동일 bodyColor 의 pallet 수십~수백 개 → GPU material 인스턴스 *1개로 통합*.\nconst woodBodyMaterials = new Map<string, THREE.MeshStandardMaterial>()\nconst woodStringerMaterials = new Map<string, THREE.MeshStandardMaterial>()\nconst plasticDeckMaterials = new Map<string, THREE.MeshStandardMaterial>()\nconst plasticFootMaterials = new Map<string, THREE.MeshStandardMaterial>()\n\nfunction getWoodBodyMaterial(bodyColor: string): THREE.MeshStandardMaterial {\n let m = woodBodyMaterials.get(bodyColor)\n if (!m) {\n m = new THREE.MeshStandardMaterial({ color: bodyColor, metalness: 0.0, roughness: 0.85 })\n woodBodyMaterials.set(bodyColor, m)\n }\n return m\n}\n\nfunction getWoodStringerMaterial(bodyColor: string): THREE.MeshStandardMaterial {\n let m = woodStringerMaterials.get(bodyColor)\n if (!m) {\n const tint = new THREE.Color(bodyColor).multiplyScalar(0.85)\n m = new THREE.MeshStandardMaterial({ color: tint, metalness: 0.0, roughness: 0.9 })\n woodStringerMaterials.set(bodyColor, m)\n }\n return m\n}\n\nfunction getPlasticDeckMaterial(bodyColor: string): THREE.MeshStandardMaterial {\n let m = plasticDeckMaterials.get(bodyColor)\n if (!m) {\n m = new THREE.MeshStandardMaterial({ color: bodyColor, metalness: 0.1, roughness: 0.55 })\n plasticDeckMaterials.set(bodyColor, m)\n }\n return m\n}\n\nfunction getPlasticFootMaterial(bodyColor: string): THREE.MeshStandardMaterial {\n let m = plasticFootMaterials.get(bodyColor)\n if (!m) {\n const tint = new THREE.Color(bodyColor).multiplyScalar(0.85)\n m = new THREE.MeshStandardMaterial({ color: tint, metalness: 0.1, roughness: 0.65 })\n plasticFootMaterials.set(bodyColor, m)\n }\n return m\n}\n\n// ── Geometry cache by size (shared across all Pallet3D instances) ──\n// 동일 width × height × depth 의 pallet → merged geometry *1세트*만 GPU 에 업로드.\n// Translation / merge 비용도 동일 size pallet 끼리 *1회*만 발생.\nconst woodGeoCache = new Map<string, { top: THREE.BufferGeometry; stringer: THREE.BufferGeometry; bottom: THREE.BufferGeometry }>()\nconst plasticGeoCache = new Map<string, { deck: THREE.BufferGeometry; feet: THREE.BufferGeometry; brace: THREE.BufferGeometry }>()\n\nexport class Pallet3D extends RealObjectGroup {\n build() {\n super.build()\n\n const { width, height, depth = 150 } = this.component.state\n const material = (this.component.state.material as string) || 'wood'\n const bodyColor = (this.component.state.bodyColor as string) || '#a87644'\n\n if (material === 'plastic') {\n this.buildPlastic(width, height, depth, bodyColor)\n } else {\n this.buildWood(width, height, depth, bodyColor)\n }\n }\n\n /** Wood EUR-style: 7 top slats + 3 stringers + 5 bottom slats. */\n private buildWood(width: number, height: number, depth: number, bodyColor: string) {\n const { top, stringer, bottom } = this.getWoodGeometries(width, height, depth)\n\n const woodMaterial = getWoodBodyMaterial(bodyColor)\n const stringerMaterial = getWoodStringerMaterial(bodyColor)\n\n const topSlatMesh = new THREE.Mesh(top, woodMaterial)\n topSlatMesh.castShadow = true\n topSlatMesh.receiveShadow = true\n this.object3d.add(topSlatMesh)\n\n const stringerMesh = new THREE.Mesh(stringer, stringerMaterial)\n stringerMesh.castShadow = true\n this.object3d.add(stringerMesh)\n\n const botSlatMesh = new THREE.Mesh(bottom, woodMaterial)\n botSlatMesh.receiveShadow = true\n this.object3d.add(botSlatMesh)\n }\n\n private getWoodGeometries(width: number, height: number, depth: number) {\n const key = `${width}|${height}|${depth}`\n let cached = woodGeoCache.get(key)\n if (cached) return cached\n\n const baseY = -depth / 2\n const slatThickness = depth * 0.15\n const stringerThickness = depth * 0.45\n const bottomSlatThickness = depth * 0.13\n\n const slatCount = 5\n const slatD = (height * 0.92) / (slatCount + (slatCount - 1) * 0.4)\n const gapD = slatD * 0.4\n const totalSpan = slatCount * slatD + (slatCount - 1) * gapD\n const startZ = -totalSpan / 2\n\n const slatPositions: number[] = []\n for (let i = 0; i < slatCount; i++) {\n slatPositions.push(startZ + i * (slatD + gapD) + slatD / 2)\n }\n\n const topSlatGeos: THREE.BufferGeometry[] = []\n for (const z of slatPositions) {\n const slat = new THREE.BoxGeometry(width, slatThickness, slatD)\n slat.translate(0, baseY + depth - slatThickness / 2, z)\n topSlatGeos.push(slat)\n }\n const top = BufferGeometryUtils.mergeGeometries(topSlatGeos)\n\n const stringerCount = 3\n const stringerW = width * 0.07\n const stringerY = baseY + bottomSlatThickness + stringerThickness / 2\n const stringerGeos: THREE.BufferGeometry[] = []\n for (let i = 0; i < stringerCount; i++) {\n const xFrac = i / (stringerCount - 1) - 0.5\n const x = xFrac * (width * 0.85)\n const stringer = new THREE.BoxGeometry(stringerW, stringerThickness, height)\n stringer.translate(x, stringerY, 0)\n stringerGeos.push(stringer)\n }\n const stringer = BufferGeometryUtils.mergeGeometries(stringerGeos)\n\n const botSlatGeos: THREE.BufferGeometry[] = []\n for (const z of slatPositions) {\n const slat = new THREE.BoxGeometry(width, bottomSlatThickness, slatD)\n slat.translate(0, baseY + bottomSlatThickness / 2, z)\n botSlatGeos.push(slat)\n }\n const bottom = BufferGeometryUtils.mergeGeometries(botSlatGeos)\n\n cached = { top, stringer, bottom }\n woodGeoCache.set(key, cached)\n return cached\n }\n\n /** Plastic molded: solid top deck + ribbed underside / feet. */\n private buildPlastic(width: number, height: number, depth: number, bodyColor: string) {\n const { deck, feet, brace } = this.getPlasticGeometries(width, height, depth)\n\n const deckMaterial = getPlasticDeckMaterial(bodyColor)\n const footMaterial = getPlasticFootMaterial(bodyColor)\n\n const baseY = -depth / 2\n const deckThickness = depth * 0.30\n\n const deckMesh = new THREE.Mesh(deck, deckMaterial)\n deckMesh.position.set(0, baseY + depth - deckThickness / 2, 0)\n deckMesh.castShadow = true\n deckMesh.receiveShadow = true\n this.object3d.add(deckMesh)\n\n const footMesh = new THREE.Mesh(feet, footMaterial)\n footMesh.castShadow = true\n this.object3d.add(footMesh)\n\n const braceMesh = new THREE.Mesh(brace, footMaterial)\n this.object3d.add(braceMesh)\n }\n\n private getPlasticGeometries(width: number, height: number, depth: number) {\n const key = `${width}|${height}|${depth}`\n let cached = plasticGeoCache.get(key)\n if (cached) return cached\n\n const baseY = -depth / 2\n const deckThickness = depth * 0.30\n const footH = depth * 0.55\n const footW = width * 0.12\n\n // Deck geo — local space; mesh position applied at instantiation.\n const deck = new THREE.BoxGeometry(width * 0.98, deckThickness, height * 0.98)\n\n // 9 feet (3×3 grid) → merged geometry with translations baked in.\n const footGeos: THREE.BufferGeometry[] = []\n for (let i = -1; i <= 1; i++) {\n for (let j = -1; j <= 1; j++) {\n const x = i * (width * 0.4)\n const z = j * (height * 0.4)\n const foot = new THREE.BoxGeometry(footW, footH, footW)\n foot.translate(x, baseY + footH / 2, z)\n footGeos.push(foot)\n }\n }\n const feet = BufferGeometryUtils.mergeGeometries(footGeos)\n\n // 3 braces → merged into single mesh (이전에 mesh 3개 분리 = drawcall 3개).\n const braceH = depth * 0.10\n const braceGeos: THREE.BufferGeometry[] = []\n for (const zSign of [-1, 0, 1]) {\n const b = new THREE.BoxGeometry(width * 0.95, braceH, height * 0.04)\n b.translate(0, baseY + footH - braceH / 2, zSign * height * 0.4)\n braceGeos.push(b)\n }\n const brace = BufferGeometryUtils.mergeGeometries(braceGeos)\n\n cached = { deck, feet, brace }\n plasticGeoCache.set(key, cached)\n return cached\n }\n\n updateDimension() {}\n\n onchange(after: Record<string, unknown>, before: Record<string, unknown>) {\n if (\n 'material' in after ||\n 'bodyColor' in after ||\n 'width' in after ||\n 'height' in after ||\n 'depth' in after\n ) {\n this.update()\n return\n }\n super.onchange(after, before)\n }\n\n updateAlpha() {}\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  import { RealObjectGroup } from '@hatiolab/things-scene';
2
2
  export declare class Parcel3D extends RealObjectGroup {
3
+ private _cachedSize?;
3
4
  build(): void;
4
5
  updateDimension(): void;
5
6
  onchange(after: Record<string, unknown>, before: Record<string, unknown>): void;
package/dist/parcel-3d.js CHANGED
@@ -73,9 +73,26 @@ function _getLabelGeo(w, t, h) {
73
73
  return g;
74
74
  }
75
75
  export class Parcel3D extends RealObjectGroup {
76
+ // 처음 build 에서 finite size 받으면 cache. 이후 *NaN 으로 rebuild* 시 cache
77
+ // 값 사용 — *생성 직후 실제 크기 유지*. fallback (100x100x150) 은 *cache 도
78
+ // 없는 첫 호출에 NaN* 일 때만 사용.
79
+ _cachedSize;
76
80
  build() {
77
81
  super.build();
78
- const { width, height, depth = 150 } = this.component.state;
82
+ // NaN guard + cache. carrier.state.height/width *crane.receive
83
+ // bounds reflow* 시점에 NaN 으로 변할 수 있음 (cross-module state proxy
84
+ // 의 _state.h undefined + 계산 결과 NaN). 처음 build 의 finite 값을 instance
85
+ // 에 cache → 이후 NaN rebuild 에도 *실제 carrier 크기로 BoxGeometry* 유지.
86
+ const finite = (v) => typeof v === 'number' && Number.isFinite(v) && v > 0;
87
+ const stateW = this.component.state.width;
88
+ const stateH = this.component.state.height;
89
+ const stateD = this.component.state.depth;
90
+ if (this._cachedSize == null && finite(stateW) && finite(stateH) && finite(stateD)) {
91
+ this._cachedSize = { w: stateW, h: stateH, d: stateD };
92
+ }
93
+ const width = finite(stateW) ? stateW : (this._cachedSize?.w ?? 100);
94
+ const height = finite(stateH) ? stateH : (this._cachedSize?.h ?? 100);
95
+ const depth = finite(stateD) ? stateD : (this._cachedSize?.d ?? 150);
79
96
  const baseY = -depth / 2;
80
97
  // ── Main body ────────────────────────────────────────────────────
81
98
  const bodyMesh = new THREE.Mesh(_getBodyGeo(width, depth, height), PARCEL_BODY_MATERIAL);
@@ -1 +1 @@
1
- {"version":3,"file":"parcel-3d.js","sourceRoot":"","sources":["../src/parcel-3d.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * Parcel 3D — a cardboard package.\n *\n * Structure:\n * - main body box (cardboard color)\n * - tape line running across the top (the visual signature — what makes\n * this read as a \"shipping parcel\" rather than a generic box)\n * - small label area on top (white rectangle suggesting a shipping label)\n *\n * Kept very simple — parcels in a logistics scene are typically present in\n * large numbers (sortation lines, fulfillment bays), so polygon count\n * matters more than it does for one-off equipment.\n */\n\nimport * as THREE from 'three'\nimport { RealObjectGroup } from '@hatiolab/things-scene'\n\nconst CARDBOARD_COLOR = 0xc8a878\nconst TAPE_COLOR = 0xddc899\nconst LABEL_COLOR = 0xeeeeee\n\n// ── Module-level shared materials ───────────────────────────────────────────\n// Parcel 인스턴스가 수백~수천 가능. instance 별 new MeshStandardMaterial 시\n// material 개수 폭증 → GPU memory + draw call 비효율. static color 라 단일\n// instance 공유. 색 변경 시 모든 Parcel 에 자동 반영 (의도).\nconst PARCEL_BODY_MATERIAL = new THREE.MeshStandardMaterial({\n color: CARDBOARD_COLOR,\n metalness: 0,\n roughness: 0.9\n})\nconst PARCEL_TAPE_MATERIAL = new THREE.MeshStandardMaterial({\n color: TAPE_COLOR,\n metalness: 0.05,\n roughness: 0.5\n})\nconst PARCEL_LABEL_MATERIAL = new THREE.MeshStandardMaterial({\n color: LABEL_COLOR,\n metalness: 0,\n roughness: 0.4\n})\n\n// ── Geometry cache — 같은 (w,h,d) 인 parcel 들 BoxGeometry 공유. 수백 parcel 의\n// GPU memory + setup cost 폭감.\nconst _BODY_GEO_CACHE = new Map<string, THREE.BoxGeometry>()\nconst _TAPE_GEO_CACHE = new Map<string, THREE.BoxGeometry>()\nconst _LABEL_GEO_CACHE = new Map<string, THREE.BoxGeometry>()\n\nfunction _key3(a: number, b: number, c: number): string {\n return `${a.toFixed(1)}-${b.toFixed(1)}-${c.toFixed(1)}`\n}\nfunction _getBodyGeo(w: number, d: number, h: number): THREE.BoxGeometry {\n const k = _key3(w, d, h)\n let g = _BODY_GEO_CACHE.get(k)\n if (!g) { g = new THREE.BoxGeometry(w, d, h); _BODY_GEO_CACHE.set(k, g) }\n return g\n}\nfunction _getTapeGeo(w: number, t: number, l: number): THREE.BoxGeometry {\n const k = _key3(w, t, l)\n let g = _TAPE_GEO_CACHE.get(k)\n if (!g) { g = new THREE.BoxGeometry(w, t, l); _TAPE_GEO_CACHE.set(k, g) }\n return g\n}\nfunction _getLabelGeo(w: number, t: number, h: number): THREE.BoxGeometry {\n const k = _key3(w, t, h)\n let g = _LABEL_GEO_CACHE.get(k)\n if (!g) { g = new THREE.BoxGeometry(w, t, h); _LABEL_GEO_CACHE.set(k, g) }\n return g\n}\n\nexport class Parcel3D extends RealObjectGroup {\n build() {\n super.build()\n\n const { width, height, depth = 150 } = this.component.state\n const baseY = -depth / 2\n\n // ── Main body ────────────────────────────────────────────────────\n const bodyMesh = new THREE.Mesh(_getBodyGeo(width, depth, height), PARCEL_BODY_MATERIAL)\n bodyMesh.position.set(0, 0, 0)\n bodyMesh.castShadow = true\n bodyMesh.receiveShadow = true\n this.object3d.add(bodyMesh)\n\n // ── Tape line on top (running along the long axis) ───────────────\n const tapeW = Math.min(width, height) * 0.10\n const tapeT = depth * 0.02\n const tapeAlongLong = width >= height\n const tapeMesh = new THREE.Mesh(\n tapeAlongLong\n ? _getTapeGeo(width * 1.005, tapeT, tapeW)\n : _getTapeGeo(tapeW, tapeT, height * 1.005),\n PARCEL_TAPE_MATERIAL\n )\n tapeMesh.position.set(0, baseY + depth + tapeT / 2 - 0.01, 0)\n // shadow 부담 줄임 — tape 는 얇아 shadow 시각 영향 미미\n this.object3d.add(tapeMesh)\n\n // ── Shipping label (small white rectangle on top) ────────────────\n const labelW = Math.min(width, height) * 0.35\n const labelH = labelW * 0.6\n const labelMesh = new THREE.Mesh(\n _getLabelGeo(labelW, depth * 0.005, labelH),\n PARCEL_LABEL_MATERIAL\n )\n if (tapeAlongLong) {\n labelMesh.position.set(width * 0.2, baseY + depth + depth * 0.0025, -height * 0.15)\n } else {\n labelMesh.position.set(width * 0.15, baseY + depth + depth * 0.0025, height * 0.2)\n }\n // shadow 부담 줄임 — label 도 얇아 shadow 시각 영향 미미\n this.object3d.add(labelMesh)\n }\n\n updateDimension() {}\n\n onchange(after: Record<string, unknown>, before: Record<string, unknown>) {\n if ('width' in after || 'height' in after || 'depth' in after) {\n this.update()\n return\n }\n super.onchange(after, before)\n }\n\n updateAlpha() {}\n}\n"]}
1
+ {"version":3,"file":"parcel-3d.js","sourceRoot":"","sources":["../src/parcel-3d.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * Parcel 3D — a cardboard package.\n *\n * Structure:\n * - main body box (cardboard color)\n * - tape line running across the top (the visual signature — what makes\n * this read as a \"shipping parcel\" rather than a generic box)\n * - small label area on top (white rectangle suggesting a shipping label)\n *\n * Kept very simple — parcels in a logistics scene are typically present in\n * large numbers (sortation lines, fulfillment bays), so polygon count\n * matters more than it does for one-off equipment.\n */\n\nimport * as THREE from 'three'\nimport { RealObjectGroup } from '@hatiolab/things-scene'\n\nconst CARDBOARD_COLOR = 0xc8a878\nconst TAPE_COLOR = 0xddc899\nconst LABEL_COLOR = 0xeeeeee\n\n// ── Module-level shared materials ───────────────────────────────────────────\n// Parcel 인스턴스가 수백~수천 가능. instance 별 new MeshStandardMaterial 시\n// material 개수 폭증 → GPU memory + draw call 비효율. static color 라 단일\n// instance 공유. 색 변경 시 모든 Parcel 에 자동 반영 (의도).\nconst PARCEL_BODY_MATERIAL = new THREE.MeshStandardMaterial({\n color: CARDBOARD_COLOR,\n metalness: 0,\n roughness: 0.9\n})\nconst PARCEL_TAPE_MATERIAL = new THREE.MeshStandardMaterial({\n color: TAPE_COLOR,\n metalness: 0.05,\n roughness: 0.5\n})\nconst PARCEL_LABEL_MATERIAL = new THREE.MeshStandardMaterial({\n color: LABEL_COLOR,\n metalness: 0,\n roughness: 0.4\n})\n\n// ── Geometry cache — 같은 (w,h,d) 인 parcel 들 BoxGeometry 공유. 수백 parcel 의\n// GPU memory + setup cost 폭감.\nconst _BODY_GEO_CACHE = new Map<string, THREE.BoxGeometry>()\nconst _TAPE_GEO_CACHE = new Map<string, THREE.BoxGeometry>()\nconst _LABEL_GEO_CACHE = new Map<string, THREE.BoxGeometry>()\n\nfunction _key3(a: number, b: number, c: number): string {\n return `${a.toFixed(1)}-${b.toFixed(1)}-${c.toFixed(1)}`\n}\nfunction _getBodyGeo(w: number, d: number, h: number): THREE.BoxGeometry {\n const k = _key3(w, d, h)\n let g = _BODY_GEO_CACHE.get(k)\n if (!g) { g = new THREE.BoxGeometry(w, d, h); _BODY_GEO_CACHE.set(k, g) }\n return g\n}\nfunction _getTapeGeo(w: number, t: number, l: number): THREE.BoxGeometry {\n const k = _key3(w, t, l)\n let g = _TAPE_GEO_CACHE.get(k)\n if (!g) { g = new THREE.BoxGeometry(w, t, l); _TAPE_GEO_CACHE.set(k, g) }\n return g\n}\nfunction _getLabelGeo(w: number, t: number, h: number): THREE.BoxGeometry {\n const k = _key3(w, t, h)\n let g = _LABEL_GEO_CACHE.get(k)\n if (!g) { g = new THREE.BoxGeometry(w, t, h); _LABEL_GEO_CACHE.set(k, g) }\n return g\n}\n\nexport class Parcel3D extends RealObjectGroup {\n // 처음 build 에서 finite size 받으면 cache. 이후 *NaN 으로 rebuild* 시 cache\n // 값 사용 — *생성 직후 실제 크기 유지*. fallback (100x100x150) 은 *cache 도\n // 없는 첫 호출에 NaN* 일 때만 사용.\n private _cachedSize?: { w: number; h: number; d: number }\n\n build() {\n super.build()\n\n // NaN guard + cache. carrier.state.height/width 가 *crane.receive 후\n // bounds reflow* 시점에 NaN 으로 변할 수 있음 (cross-module state proxy\n // 의 _state.h undefined + 계산 결과 NaN). 처음 build 의 finite 값을 instance\n // 에 cache → 이후 NaN rebuild 에도 *실제 carrier 크기로 BoxGeometry* 유지.\n const finite = (v: any) => typeof v === 'number' && Number.isFinite(v) && v > 0\n const stateW = this.component.state.width\n const stateH = this.component.state.height\n const stateD = this.component.state.depth\n if (this._cachedSize == null && finite(stateW) && finite(stateH) && finite(stateD)) {\n this._cachedSize = { w: stateW, h: stateH, d: stateD }\n }\n const width = finite(stateW) ? stateW : (this._cachedSize?.w ?? 100)\n const height = finite(stateH) ? stateH : (this._cachedSize?.h ?? 100)\n const depth = finite(stateD) ? stateD : (this._cachedSize?.d ?? 150)\n const baseY = -depth / 2\n\n // ── Main body ────────────────────────────────────────────────────\n const bodyMesh = new THREE.Mesh(_getBodyGeo(width, depth, height), PARCEL_BODY_MATERIAL)\n bodyMesh.position.set(0, 0, 0)\n bodyMesh.castShadow = true\n bodyMesh.receiveShadow = true\n this.object3d.add(bodyMesh)\n\n // ── Tape line on top (running along the long axis) ───────────────\n const tapeW = Math.min(width, height) * 0.10\n const tapeT = depth * 0.02\n const tapeAlongLong = width >= height\n const tapeMesh = new THREE.Mesh(\n tapeAlongLong\n ? _getTapeGeo(width * 1.005, tapeT, tapeW)\n : _getTapeGeo(tapeW, tapeT, height * 1.005),\n PARCEL_TAPE_MATERIAL\n )\n tapeMesh.position.set(0, baseY + depth + tapeT / 2 - 0.01, 0)\n // shadow 부담 줄임 — tape 는 얇아 shadow 시각 영향 미미\n this.object3d.add(tapeMesh)\n\n // ── Shipping label (small white rectangle on top) ────────────────\n const labelW = Math.min(width, height) * 0.35\n const labelH = labelW * 0.6\n const labelMesh = new THREE.Mesh(\n _getLabelGeo(labelW, depth * 0.005, labelH),\n PARCEL_LABEL_MATERIAL\n )\n if (tapeAlongLong) {\n labelMesh.position.set(width * 0.2, baseY + depth + depth * 0.0025, -height * 0.15)\n } else {\n labelMesh.position.set(width * 0.15, baseY + depth + depth * 0.0025, height * 0.2)\n }\n // shadow 부담 줄임 — label 도 얇아 shadow 시각 영향 미미\n this.object3d.add(labelMesh)\n }\n\n updateDimension() {}\n\n onchange(after: Record<string, unknown>, before: Record<string, unknown>) {\n if ('width' in after || 'height' in after || 'depth' in after) {\n this.update()\n return\n }\n super.onchange(after, before)\n }\n\n updateAlpha() {}\n}\n"]}
@@ -52,13 +52,15 @@ export class RackGrid3D extends RealObjectGroup {
52
52
  this._beamGroup = new THREE.Group();
53
53
  this._frameGroup.add(this._beamGroup);
54
54
  this.object3d.add(this._frameGroup);
55
- const width = rs?.width ?? 400; // 3D X
56
- const height = rs?.depth ?? 2000; // 3D Y (floor ceiling)
57
- const depth = rs?.height ?? 200; // 3D Z (front → back)
55
+ // ?? 쓰면 NaN 통과 finite + positive 검증 후 default fallback.
56
+ const finite = (v, fb) => typeof v === 'number' && Number.isFinite(v) && v > 0 ? v : fb;
57
+ const width = finite(rs?.width, 400); // 3D X
58
+ const height = finite(rs?.depth, 2000); // 3D Y (floor → ceiling)
59
+ const depth = finite(rs?.height, 200); // 3D Z (front → back)
58
60
  const cols = comp.columns;
59
61
  const rows = comp.rackRows;
60
62
  const shelves = comp.shelves;
61
- const shelfBase = Math.max(0, Math.min(rs?.shelfBaseHeight || 0, height * 0.9));
63
+ const shelfBase = Math.max(0, Math.min(finite(rs?.shelfBaseHeight, 0), height * 0.9));
62
64
  const shelfZone = height - shelfBase;
63
65
  const bayW = width / cols;
64
66
  const bayD = depth / rows;
@@ -286,10 +288,13 @@ export class RackGrid3D extends RealObjectGroup {
286
288
  const cols = comp.columns;
287
289
  const rows = comp.rackRows;
288
290
  const shelves = comp.shelves;
289
- const width = rs?.width ?? 400;
290
- const height = rs?.depth ?? 2000;
291
- const depth = rs?.height ?? 200;
292
- const shelfBase = Math.max(0, Math.min(rs?.shelfBaseHeight || 0, height * 0.9));
291
+ // state.width/depth/height NaN 인 경우 — ?? 는 nullish 만 fallback 이라 NaN 통과.
292
+ // 명시적 finite 검증 default 사용. NaN BoxGeometry 의 근본 원인.
293
+ const finite = (v, fb) => typeof v === 'number' && Number.isFinite(v) && v > 0 ? v : fb;
294
+ const width = finite(rs?.width, 400);
295
+ const height = finite(rs?.depth, 2000);
296
+ const depth = finite(rs?.height, 200);
297
+ const shelfBase = Math.max(0, Math.min(finite(rs?.shelfBaseHeight, 0), height * 0.9));
293
298
  const shelfZone = height - shelfBase;
294
299
  const bayW = width / cols;
295
300
  const bayD = depth / rows;
@@ -299,6 +304,19 @@ export class RackGrid3D extends RealObjectGroup {
299
304
  const stockW = bayW * 0.85;
300
305
  const stockD = cellY * 0.7;
301
306
  const stockH = bayD * 0.85;
307
+ // 최종 차원이 finite 한지 검증 — accessor + finite() 거쳤어도 *모든 입력이 0* 이거나
308
+ // 예상 못한 경로로 NaN/0 가능성. invalid 시 stock mesh 생성 자체를 skip + 진단 로그.
309
+ if (!Number.isFinite(stockW) || stockW <= 0 ||
310
+ !Number.isFinite(stockD) || stockD <= 0 ||
311
+ !Number.isFinite(stockH) || stockH <= 0) {
312
+ console.error('[rack-grid-3d] rebuildStockMesh: invalid stock dims — mesh 생성 skip', {
313
+ stockW, stockD, stockH,
314
+ cols, rows, shelves,
315
+ width, height, depth, shelfBase, shelfZone, cellY, bayW, bayD,
316
+ stateW: rs?.width, stateDepth: rs?.depth, stateH: rs?.height, stateShelfBase: rs?.shelfBaseHeight
317
+ });
318
+ return;
319
+ }
302
320
  const records = comp.records;
303
321
  const recordsByCell = new Map();
304
322
  for (const r of records) {
@@ -1 +1 @@
1
- {"version":3,"file":"rack-grid-3d.js","sourceRoot":"","sources":["../src/rack-grid-3d.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * RackGrid3D — *공유 corner posts + bay 별 beams + shelf planes*.\n *\n * Post (수직 기둥):\n * - (cols+1) × (rows+1) 의 grid corner 위치마다 *공유 post* 1 개.\n * - 인접 4 bay 중 *최소 1개 non-empty* 면 post 만듦. 모두 empty 면 skip.\n * - 옆 bay 와 *공유* → 두 post 겹침 X.\n *\n * Beam (수평 부재):\n * - non-empty bay 마다 front + back beam (각 shelf level).\n *\n * Shelf (반투명 판):\n * - non-empty bay 의 각 level 의 frame 안쪽.\n *\n * isEmpty source of truth: RackGrid.isBayEmpty(col, row) — cell.state.isEmpty 우선.\n */\nimport * as THREE from 'three'\nimport * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js'\nimport { RealObjectGroup } from '@hatiolab/things-scene'\nimport type RackGrid from './rack-grid.js'\nimport {\n POST_MATERIAL, BEAM_MATERIAL, SHELF_MATERIAL,\n STOCK_MATERIAL, EMPTY_STOCK_MATERIAL\n} from './rack-materials.js'\n\nexport class RackGrid3D extends RealObjectGroup {\n private _frameGroup?: THREE.Group // post + beam 묶음 (hideRackFrame 시 hidden)\n private _beamGroup?: THREE.Group // beam 만 (hideHorizontalFrame 시 hidden)\n\n build() {\n super.build()\n this._buildFrames()\n this._applyFrameVisibility()\n }\n\n /** hideRackFrame / hideHorizontalFrame 변경 시 visibility 즉시 반영. */\n applyFrameVisibility(): void {\n this._applyFrameVisibility()\n }\n\n private _applyFrameVisibility(): void {\n const rs: any = this.component.state\n const hideFrame = !!rs?.hideRackFrame\n const hideBeams = !!rs?.hideHorizontalFrame\n if (this._frameGroup) this._frameGroup.visible = !hideFrame\n // beam group 은 frame group 안에 nested — frame 이 hidden 일 때는 beam 도 자연히 hidden.\n // frame visible + beam toggle 따로\n if (this._beamGroup) this._beamGroup.visible = !hideBeams\n }\n\n private _buildFrames(): void {\n const comp = this.component as unknown as RackGrid\n const rs: any = comp.state\n // frame group — post + beamGroup 담음. hideRackFrame 시 frame group.visible=false.\n // beam group — 가로 frame 만 (hideHorizontalFrame 시 따로 토글).\n this._frameGroup = new THREE.Group()\n this._beamGroup = new THREE.Group()\n this._frameGroup.add(this._beamGroup)\n this.object3d.add(this._frameGroup)\n\n const width = (rs?.width as number) ?? 400 // 3D X\n const height = (rs?.depth as number) ?? 2000 // 3D Y (floor → ceiling)\n const depth = (rs?.height as number) ?? 200 // 3D Z (front → back)\n const cols = comp.columns\n const rows = comp.rackRows\n const shelves = comp.shelves\n const shelfBase = Math.max(0, Math.min((rs?.shelfBaseHeight as number) || 0, height * 0.9))\n const shelfZone = height - shelfBase\n\n const bayW = width / cols\n const bayD = depth / rows\n const baseY = -height / 2\n const shelfBaseY = baseY + shelfBase\n\n // Frame 굵기 — storage-rack 과 동일 비율 (공유 post / 공유 beam 적용 후엔\n // 겹침 없으므로 같은 비율 사용 가능).\n const postW = Math.min(bayW, bayD) * 0.06\n const beamH = postW * 1.2\n\n const isEmpty = (col: number, row: number): boolean => {\n if (col < 0 || col >= cols || row < 0 || row >= rows) return true\n return comp.isBayEmpty(col, row)\n }\n\n // ── 1. 공유 corner posts (hideRackFrame 면 skip) ───────\n //\n // (cols+1) × (rows+1) 의 모든 corner 위치. 4 인접 bay 중 *하나라도 non-empty*\n // 이면 post 생성. 인접 bay 가 모두 empty → post skip.\n\n const postGeos: THREE.BufferGeometry[] = []\n for (let c = 0; c <= cols; c++) {\n for (let r = 0; r <= rows; r++) {\n // 이 corner 에 인접한 4 bay (없는 위치는 isEmpty 처리)\n const anyActive =\n !isEmpty(c - 1, r - 1) ||\n !isEmpty(c, r - 1) ||\n !isEmpty(c - 1, r ) ||\n !isEmpty(c, r )\n if (!anyActive) continue\n\n const x = (c - cols / 2) * bayW\n const z = (r - rows / 2) * bayD\n const post = new THREE.BoxGeometry(postW, height, postW)\n post.translate(x, 0, z)\n postGeos.push(post)\n }\n }\n\n // ── 2. Horizontal beams (X 축 외곽 wall 만 — storage-rack 일관) ────\n //\n // storage-rack 처럼 *front + back 의 X 축 beam* 만. 내부 행 경계 beam +\n // Z 축 (좌우 side) beam 모두 제거 — *깔끔한 wall-frame 시각*.\n // 각 level 마다 front (zEdge=0) + back (zEdge=rows) 두 줄. 각 줄은 *연속\n // non-empty col 구간* 공유 통합.\n\n const beamGeos: THREE.BufferGeometry[] = []\n\n // 외곽 wall 의 가로 frame — *연속 non-empty bay 구간*만. isEmpty bay 영역엔\n // frame 없음. *내부 cross frame 은 항상 제외* — 깔끔함 유지.\n\n // X 축 beam (front + back, 각 level) — non-empty col segment 별 단일 beam\n for (const zEdge of [0, rows]) {\n const zPos = (zEdge - rows / 2) * bayD\n const adjacentRow = zEdge === 0 ? 0 : rows - 1\n for (let lv = 0; lv <= shelves; lv++) {\n const yFrac = lv / shelves\n const y = shelfBaseY + yFrac * shelfZone - beamH / 2 + (lv === 0 ? beamH : 0)\n let segStart = -1\n for (let col = 0; col <= cols; col++) {\n const active = col < cols && !isEmpty(col, adjacentRow)\n if (active && segStart === -1) segStart = col\n if (!active && segStart !== -1) {\n const startX = (segStart - cols / 2) * bayW\n const endX = (col - cols / 2) * bayW\n const beamLen = endX - startX\n const beamCenterX = (startX + endX) / 2\n const beam = new THREE.BoxGeometry(beamLen, beamH, beamH)\n beam.translate(beamCenterX, y, zPos)\n beamGeos.push(beam)\n segStart = -1\n }\n }\n }\n }\n\n // Z 축 beam (좌우 side) — *모든 level*, isEmpty row 제외 segment.\n for (const xEdge of [0, cols]) {\n const xPos = (xEdge - cols / 2) * bayW\n const adjacentCol = xEdge === 0 ? 0 : cols - 1\n for (let lv = 0; lv <= shelves; lv++) {\n const yFrac = lv / shelves\n const y = shelfBaseY + yFrac * shelfZone - beamH / 2 + (lv === 0 ? beamH : 0)\n let segStart = -1\n for (let row = 0; row <= rows; row++) {\n const active = row < rows && !isEmpty(adjacentCol, row)\n if (active && segStart === -1) segStart = row\n if (!active && segStart !== -1) {\n const startZ = (segStart - rows / 2) * bayD\n const endZ = (row - rows / 2) * bayD\n const beamLen = endZ - startZ\n const beamCenterZ = (startZ + endZ) / 2\n const beam = new THREE.BoxGeometry(beamH, beamH, beamLen)\n beam.translate(xPos, y, beamCenterZ)\n beamGeos.push(beam)\n segStart = -1\n }\n }\n }\n }\n\n // 천장 (lv=shelves) 내부 cross beam — *isEmpty 영역 제외 segment*.\n {\n const lv = shelves\n const yFrac = lv / shelves\n const y = shelfBaseY + yFrac * shelfZone - beamH / 2 + (lv === 0 ? beamH : 0)\n\n // 내부 col 경계 의 Z 축 beam (col 사이, Z 방향) — 인접 2 col 중 *해당 row* 가 non-empty\n for (let xEdge = 1; xEdge < cols; xEdge++) {\n const xPos = (xEdge - cols / 2) * bayW\n let segStart = -1\n for (let row = 0; row <= rows; row++) {\n const active = row < rows && (!isEmpty(xEdge - 1, row) || !isEmpty(xEdge, row))\n if (active && segStart === -1) segStart = row\n if (!active && segStart !== -1) {\n const startZ = (segStart - rows / 2) * bayD\n const endZ = (row - rows / 2) * bayD\n const beamLen = endZ - startZ\n const beamCenterZ = (startZ + endZ) / 2\n const beam = new THREE.BoxGeometry(beamH, beamH, beamLen)\n beam.translate(xPos, y, beamCenterZ)\n beamGeos.push(beam)\n segStart = -1\n }\n }\n }\n\n // 내부 row 경계 의 X 축 beam (row 사이, X 방향) — 인접 2 row 중 *해당 col* 가 non-empty\n for (let zEdge = 1; zEdge < rows; zEdge++) {\n const zPos = (zEdge - rows / 2) * bayD\n let segStart = -1\n for (let col = 0; col <= cols; col++) {\n const active = col < cols && (!isEmpty(col, zEdge - 1) || !isEmpty(col, zEdge))\n if (active && segStart === -1) segStart = col\n if (!active && segStart !== -1) {\n const startX = (segStart - cols / 2) * bayW\n const endX = (col - cols / 2) * bayW\n const beamLen = endX - startX\n const beamCenterX = (startX + endX) / 2\n const beam = new THREE.BoxGeometry(beamLen, beamH, beamH)\n beam.translate(beamCenterX, y, zPos)\n beamGeos.push(beam)\n segStart = -1\n }\n }\n }\n }\n\n // ── 3. Shelf planes — *단일 InstancedMesh* (성능). 이전엔 cols × rows × shelves\n // 개별 Mesh (각 draw call) — 큰 grid 에서 수천 draw call. 이제 1 mesh / 1 draw call.\n\n const shelfW = Math.max(0, bayW - 2 * postW)\n const shelfDD = Math.max(0, bayD - 2 * beamH)\n if (shelfW > 0 && shelfDD > 0) {\n const positions: Array<{ x: number; y: number; z: number }> = []\n for (let col = 0; col < cols; col++) {\n for (let row = 0; row < rows; row++) {\n if (isEmpty(col, row)) continue\n const bayCenterX = (col - cols / 2 + 0.5) * bayW\n const bayCenterZ = (row - rows / 2 + 0.5) * bayD\n for (let lv = 0; lv < shelves; lv++) {\n const yFrac = lv / shelves\n const y = shelfBaseY + yFrac * shelfZone + (lv === 0 ? beamH : 0)\n positions.push({ x: bayCenterX, y, z: bayCenterZ })\n }\n }\n }\n if (positions.length > 0) {\n const shelfGeo = new THREE.PlaneGeometry(shelfW, shelfDD)\n shelfGeo.rotateX(-Math.PI / 2)\n const shelfMesh = new THREE.InstancedMesh(shelfGeo, SHELF_MATERIAL, positions.length)\n shelfMesh.receiveShadow = true\n shelfMesh.frustumCulled = false\n const m = new THREE.Matrix4()\n const pos = new THREE.Vector3()\n const q = new THREE.Quaternion()\n const s = new THREE.Vector3(1, 1, 1)\n for (let i = 0; i < positions.length; i++) {\n pos.set(positions[i].x, positions[i].y, positions[i].z)\n m.compose(pos, q, s)\n shelfMesh.setMatrixAt(i, m)\n }\n shelfMesh.instanceMatrix.needsUpdate = true\n shelfMesh.computeBoundingSphere()\n this.object3d.add(shelfMesh)\n }\n }\n\n // ── Merge — post + beam ─────────────────────────────────\n\n if (postGeos.length > 0) {\n const merged = BufferGeometryUtils.mergeGeometries(postGeos)\n const mesh = new THREE.Mesh(merged, POST_MATERIAL)\n mesh.castShadow = true\n mesh.receiveShadow = true\n this._frameGroup!.add(mesh)\n }\n\n if (beamGeos.length > 0) {\n const merged = BufferGeometryUtils.mergeGeometries(beamGeos)\n const mesh = new THREE.Mesh(merged, BEAM_MATERIAL)\n mesh.castShadow = true\n mesh.receiveShadow = true\n this._beamGroup!.add(mesh)\n }\n\n // ── 4. Stock InstancedMesh — state.data 의 record + hideEmptyStock 분기 ────\n this.rebuildStockMesh()\n }\n\n // ── Stock visualization ─────────────────────────────────\n\n private _stockMesh?: THREE.InstancedMesh // record 있는 stock (불투명, 색)\n private _emptyStockMesh?: THREE.InstancedMesh // record 없는 stock (반투명 회색)\n\n /** Public — 후속 click 핸들러 사용. */\n get stockMesh(): THREE.InstancedMesh | undefined {\n return this._stockMesh\n }\n\n /**\n * Stock 시각화 (Plan A — InstancedMesh batched).\n * - hideEmptyStock=true : state.data 의 record 있는 cell 만 instance\n * - hideEmptyStock=false : *모든 (non-isEmpty bay) cell × shelf* 에 instance.\n * record 있으면 default 색, 없으면 *백색 반투명*.\n */\n rebuildStockMesh(): void {\n // 기존 두 mesh 모두 제거\n if (this._stockMesh) {\n this.object3d.remove(this._stockMesh)\n this._stockMesh.dispose?.()\n this._stockMesh = undefined\n }\n if (this._emptyStockMesh) {\n this.object3d.remove(this._emptyStockMesh)\n this._emptyStockMesh.dispose?.()\n this._emptyStockMesh = undefined\n }\n\n const comp = this.component as unknown as RackGrid\n const rs: any = comp.state\n const cols = comp.columns\n const rows = comp.rackRows\n const shelves = comp.shelves\n const width = (rs?.width as number) ?? 400\n const height = (rs?.depth as number) ?? 2000\n const depth = (rs?.height as number) ?? 200\n const shelfBase = Math.max(0, Math.min((rs?.shelfBaseHeight as number) || 0, height * 0.9))\n const shelfZone = height - shelfBase\n const bayW = width / cols\n const bayD = depth / rows\n const cellY = shelfZone / shelves\n const baseY = -height / 2\n const shelfBaseY = baseY + shelfBase\n\n const stockW = bayW * 0.85\n const stockD = cellY * 0.7\n const stockH = bayD * 0.85\n\n const records = comp.records\n const recordsByCell = new Map<string, any>()\n for (const r of records) {\n if (r?.cellId) recordsByCell.set(r.cellId, r)\n }\n\n const hideEmpty = !!rs?.hideEmptyStock\n\n // 두 그룹 분리: record 있는 stock (불투명, 색) vs empty stock (반투명 회색)\n const filled: Array<{ col: number; row: number; shelf: number; record: any }> = []\n const empties: Array<{ col: number; row: number; shelf: number }> = []\n\n for (let col = 0; col < cols; col++) {\n for (let row = 0; row < rows; row++) {\n if (comp.isBayEmpty(col, row)) continue\n for (let shelf = 0; shelf < shelves; shelf++) {\n const cellId = `${col}-${row}-${shelf}`\n const record = recordsByCell.get(cellId)\n if (record) {\n filled.push({ col, row, shelf, record })\n } else if (!hideEmpty) {\n empties.push({ col, row, shelf })\n }\n }\n }\n }\n\n const matrixFor = (col: number, row: number, shelf: number, target: THREE.Matrix4) => {\n const cx = (col - cols / 2 + 0.5) * bayW\n const cellBottomY = shelfBaseY + shelf * cellY\n const cy = cellBottomY + stockD / 2\n const cz = (row - rows / 2 + 0.5) * bayD\n target.compose(new THREE.Vector3(cx, cy, cz), new THREE.Quaternion(), new THREE.Vector3(1, 1, 1))\n }\n\n // ── 1. Filled stock — 불투명, legend/default 색 ──────\n if (filled.length > 0) {\n const STOCK_COLOR_DEFAULT = '#c8a878' // cardboard\n const geo = new THREE.BoxGeometry(stockW, stockD, stockH)\n const mesh = new THREE.InstancedMesh(geo, STOCK_MATERIAL, filled.length)\n mesh.frustumCulled = false\n mesh.userData.context = this\n mesh.userData._records = filled.map(i => i.record)\n\n const m = new THREE.Matrix4()\n const c = new THREE.Color()\n for (let i = 0; i < filled.length; i++) {\n const { col, row, shelf, record } = filled[i]\n matrixFor(col, row, shelf, m)\n mesh.setMatrixAt(i, m)\n const resolved = (comp as any).resolveLegendColor?.(record) ?? STOCK_COLOR_DEFAULT\n c.set(resolved)\n mesh.setColorAt(i, c)\n }\n mesh.instanceMatrix.needsUpdate = true\n if (mesh.instanceColor) mesh.instanceColor.needsUpdate = true\n mesh.computeBoundingSphere()\n mesh.computeBoundingBox?.()\n this.object3d.add(mesh)\n this._stockMesh = mesh\n }\n\n // ── 2. Empty stock — 반투명 회색 (hideEmptyStock=off 시만) ────\n if (empties.length > 0) {\n const geo = new THREE.BoxGeometry(stockW, stockD, stockH)\n const mesh = new THREE.InstancedMesh(geo, EMPTY_STOCK_MATERIAL, empties.length)\n mesh.frustumCulled = false\n mesh.userData.context = this\n\n const m = new THREE.Matrix4()\n for (let i = 0; i < empties.length; i++) {\n const { col, row, shelf } = empties[i]\n matrixFor(col, row, shelf, m)\n mesh.setMatrixAt(i, m)\n }\n mesh.instanceMatrix.needsUpdate = true\n mesh.computeBoundingSphere()\n mesh.computeBoundingBox?.()\n this.object3d.add(mesh)\n this._emptyStockMesh = mesh\n }\n }\n\n dispose() {\n // Material 은 module-level singleton 이라 dispose 안 함 (전체 application\n // lifecycle 동안 살아있음). InstancedMesh / Group 의 geometry 만 정리.\n if (this._stockMesh) {\n this.object3d.remove(this._stockMesh)\n this._stockMesh.dispose?.()\n this._stockMesh = undefined\n }\n if (this._emptyStockMesh) {\n this.object3d.remove(this._emptyStockMesh)\n this._emptyStockMesh.dispose?.()\n this._emptyStockMesh = undefined\n }\n super.dispose()\n }\n\n updateAlpha() {}\n}\n"]}
1
+ {"version":3,"file":"rack-grid-3d.js","sourceRoot":"","sources":["../src/rack-grid-3d.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * RackGrid3D — *공유 corner posts + bay 별 beams + shelf planes*.\n *\n * Post (수직 기둥):\n * - (cols+1) × (rows+1) 의 grid corner 위치마다 *공유 post* 1 개.\n * - 인접 4 bay 중 *최소 1개 non-empty* 면 post 만듦. 모두 empty 면 skip.\n * - 옆 bay 와 *공유* → 두 post 겹침 X.\n *\n * Beam (수평 부재):\n * - non-empty bay 마다 front + back beam (각 shelf level).\n *\n * Shelf (반투명 판):\n * - non-empty bay 의 각 level 의 frame 안쪽.\n *\n * isEmpty source of truth: RackGrid.isBayEmpty(col, row) — cell.state.isEmpty 우선.\n */\nimport * as THREE from 'three'\nimport * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js'\nimport { RealObjectGroup } from '@hatiolab/things-scene'\nimport type RackGrid from './rack-grid.js'\nimport {\n POST_MATERIAL, BEAM_MATERIAL, SHELF_MATERIAL,\n STOCK_MATERIAL, EMPTY_STOCK_MATERIAL\n} from './rack-materials.js'\n\nexport class RackGrid3D extends RealObjectGroup {\n private _frameGroup?: THREE.Group // post + beam 묶음 (hideRackFrame 시 hidden)\n private _beamGroup?: THREE.Group // beam 만 (hideHorizontalFrame 시 hidden)\n\n build() {\n super.build()\n this._buildFrames()\n this._applyFrameVisibility()\n }\n\n /** hideRackFrame / hideHorizontalFrame 변경 시 visibility 즉시 반영. */\n applyFrameVisibility(): void {\n this._applyFrameVisibility()\n }\n\n private _applyFrameVisibility(): void {\n const rs: any = this.component.state\n const hideFrame = !!rs?.hideRackFrame\n const hideBeams = !!rs?.hideHorizontalFrame\n if (this._frameGroup) this._frameGroup.visible = !hideFrame\n // beam group 은 frame group 안에 nested — frame 이 hidden 일 때는 beam 도 자연히 hidden.\n // frame visible + beam toggle 따로\n if (this._beamGroup) this._beamGroup.visible = !hideBeams\n }\n\n private _buildFrames(): void {\n const comp = this.component as unknown as RackGrid\n const rs: any = comp.state\n // frame group — post + beamGroup 담음. hideRackFrame 시 frame group.visible=false.\n // beam group — 가로 frame 만 (hideHorizontalFrame 시 따로 토글).\n this._frameGroup = new THREE.Group()\n this._beamGroup = new THREE.Group()\n this._frameGroup.add(this._beamGroup)\n this.object3d.add(this._frameGroup)\n\n // ?? 만 쓰면 NaN 통과 — finite + positive 검증 후 default fallback.\n const finite = (v: any, fb: number) =>\n typeof v === 'number' && Number.isFinite(v) && v > 0 ? v : fb\n const width = finite(rs?.width, 400) // 3D X\n const height = finite(rs?.depth, 2000) // 3D Y (floor → ceiling)\n const depth = finite(rs?.height, 200) // 3D Z (front → back)\n const cols = comp.columns\n const rows = comp.rackRows\n const shelves = comp.shelves\n const shelfBase = Math.max(0, Math.min(finite(rs?.shelfBaseHeight, 0), height * 0.9))\n const shelfZone = height - shelfBase\n\n const bayW = width / cols\n const bayD = depth / rows\n const baseY = -height / 2\n const shelfBaseY = baseY + shelfBase\n\n // Frame 굵기 — storage-rack 과 동일 비율 (공유 post / 공유 beam 적용 후엔\n // 겹침 없으므로 같은 비율 사용 가능).\n const postW = Math.min(bayW, bayD) * 0.06\n const beamH = postW * 1.2\n\n const isEmpty = (col: number, row: number): boolean => {\n if (col < 0 || col >= cols || row < 0 || row >= rows) return true\n return comp.isBayEmpty(col, row)\n }\n\n // ── 1. 공유 corner posts (hideRackFrame 면 skip) ───────\n //\n // (cols+1) × (rows+1) 의 모든 corner 위치. 4 인접 bay 중 *하나라도 non-empty*\n // 이면 post 생성. 인접 bay 가 모두 empty → post skip.\n\n const postGeos: THREE.BufferGeometry[] = []\n for (let c = 0; c <= cols; c++) {\n for (let r = 0; r <= rows; r++) {\n // 이 corner 에 인접한 4 bay (없는 위치는 isEmpty 처리)\n const anyActive =\n !isEmpty(c - 1, r - 1) ||\n !isEmpty(c, r - 1) ||\n !isEmpty(c - 1, r ) ||\n !isEmpty(c, r )\n if (!anyActive) continue\n\n const x = (c - cols / 2) * bayW\n const z = (r - rows / 2) * bayD\n const post = new THREE.BoxGeometry(postW, height, postW)\n post.translate(x, 0, z)\n postGeos.push(post)\n }\n }\n\n // ── 2. Horizontal beams (X 축 외곽 wall 만 — storage-rack 일관) ────\n //\n // storage-rack 처럼 *front + back 의 X 축 beam* 만. 내부 행 경계 beam +\n // Z 축 (좌우 side) beam 모두 제거 — *깔끔한 wall-frame 시각*.\n // 각 level 마다 front (zEdge=0) + back (zEdge=rows) 두 줄. 각 줄은 *연속\n // non-empty col 구간* 공유 통합.\n\n const beamGeos: THREE.BufferGeometry[] = []\n\n // 외곽 wall 의 가로 frame — *연속 non-empty bay 구간*만. isEmpty bay 영역엔\n // frame 없음. *내부 cross frame 은 항상 제외* — 깔끔함 유지.\n\n // X 축 beam (front + back, 각 level) — non-empty col segment 별 단일 beam\n for (const zEdge of [0, rows]) {\n const zPos = (zEdge - rows / 2) * bayD\n const adjacentRow = zEdge === 0 ? 0 : rows - 1\n for (let lv = 0; lv <= shelves; lv++) {\n const yFrac = lv / shelves\n const y = shelfBaseY + yFrac * shelfZone - beamH / 2 + (lv === 0 ? beamH : 0)\n let segStart = -1\n for (let col = 0; col <= cols; col++) {\n const active = col < cols && !isEmpty(col, adjacentRow)\n if (active && segStart === -1) segStart = col\n if (!active && segStart !== -1) {\n const startX = (segStart - cols / 2) * bayW\n const endX = (col - cols / 2) * bayW\n const beamLen = endX - startX\n const beamCenterX = (startX + endX) / 2\n const beam = new THREE.BoxGeometry(beamLen, beamH, beamH)\n beam.translate(beamCenterX, y, zPos)\n beamGeos.push(beam)\n segStart = -1\n }\n }\n }\n }\n\n // Z 축 beam (좌우 side) — *모든 level*, isEmpty row 제외 segment.\n for (const xEdge of [0, cols]) {\n const xPos = (xEdge - cols / 2) * bayW\n const adjacentCol = xEdge === 0 ? 0 : cols - 1\n for (let lv = 0; lv <= shelves; lv++) {\n const yFrac = lv / shelves\n const y = shelfBaseY + yFrac * shelfZone - beamH / 2 + (lv === 0 ? beamH : 0)\n let segStart = -1\n for (let row = 0; row <= rows; row++) {\n const active = row < rows && !isEmpty(adjacentCol, row)\n if (active && segStart === -1) segStart = row\n if (!active && segStart !== -1) {\n const startZ = (segStart - rows / 2) * bayD\n const endZ = (row - rows / 2) * bayD\n const beamLen = endZ - startZ\n const beamCenterZ = (startZ + endZ) / 2\n const beam = new THREE.BoxGeometry(beamH, beamH, beamLen)\n beam.translate(xPos, y, beamCenterZ)\n beamGeos.push(beam)\n segStart = -1\n }\n }\n }\n }\n\n // 천장 (lv=shelves) 내부 cross beam — *isEmpty 영역 제외 segment*.\n {\n const lv = shelves\n const yFrac = lv / shelves\n const y = shelfBaseY + yFrac * shelfZone - beamH / 2 + (lv === 0 ? beamH : 0)\n\n // 내부 col 경계 의 Z 축 beam (col 사이, Z 방향) — 인접 2 col 중 *해당 row* 가 non-empty\n for (let xEdge = 1; xEdge < cols; xEdge++) {\n const xPos = (xEdge - cols / 2) * bayW\n let segStart = -1\n for (let row = 0; row <= rows; row++) {\n const active = row < rows && (!isEmpty(xEdge - 1, row) || !isEmpty(xEdge, row))\n if (active && segStart === -1) segStart = row\n if (!active && segStart !== -1) {\n const startZ = (segStart - rows / 2) * bayD\n const endZ = (row - rows / 2) * bayD\n const beamLen = endZ - startZ\n const beamCenterZ = (startZ + endZ) / 2\n const beam = new THREE.BoxGeometry(beamH, beamH, beamLen)\n beam.translate(xPos, y, beamCenterZ)\n beamGeos.push(beam)\n segStart = -1\n }\n }\n }\n\n // 내부 row 경계 의 X 축 beam (row 사이, X 방향) — 인접 2 row 중 *해당 col* 가 non-empty\n for (let zEdge = 1; zEdge < rows; zEdge++) {\n const zPos = (zEdge - rows / 2) * bayD\n let segStart = -1\n for (let col = 0; col <= cols; col++) {\n const active = col < cols && (!isEmpty(col, zEdge - 1) || !isEmpty(col, zEdge))\n if (active && segStart === -1) segStart = col\n if (!active && segStart !== -1) {\n const startX = (segStart - cols / 2) * bayW\n const endX = (col - cols / 2) * bayW\n const beamLen = endX - startX\n const beamCenterX = (startX + endX) / 2\n const beam = new THREE.BoxGeometry(beamLen, beamH, beamH)\n beam.translate(beamCenterX, y, zPos)\n beamGeos.push(beam)\n segStart = -1\n }\n }\n }\n }\n\n // ── 3. Shelf planes — *단일 InstancedMesh* (성능). 이전엔 cols × rows × shelves\n // 개별 Mesh (각 draw call) — 큰 grid 에서 수천 draw call. 이제 1 mesh / 1 draw call.\n\n const shelfW = Math.max(0, bayW - 2 * postW)\n const shelfDD = Math.max(0, bayD - 2 * beamH)\n if (shelfW > 0 && shelfDD > 0) {\n const positions: Array<{ x: number; y: number; z: number }> = []\n for (let col = 0; col < cols; col++) {\n for (let row = 0; row < rows; row++) {\n if (isEmpty(col, row)) continue\n const bayCenterX = (col - cols / 2 + 0.5) * bayW\n const bayCenterZ = (row - rows / 2 + 0.5) * bayD\n for (let lv = 0; lv < shelves; lv++) {\n const yFrac = lv / shelves\n const y = shelfBaseY + yFrac * shelfZone + (lv === 0 ? beamH : 0)\n positions.push({ x: bayCenterX, y, z: bayCenterZ })\n }\n }\n }\n if (positions.length > 0) {\n const shelfGeo = new THREE.PlaneGeometry(shelfW, shelfDD)\n shelfGeo.rotateX(-Math.PI / 2)\n const shelfMesh = new THREE.InstancedMesh(shelfGeo, SHELF_MATERIAL, positions.length)\n shelfMesh.receiveShadow = true\n shelfMesh.frustumCulled = false\n const m = new THREE.Matrix4()\n const pos = new THREE.Vector3()\n const q = new THREE.Quaternion()\n const s = new THREE.Vector3(1, 1, 1)\n for (let i = 0; i < positions.length; i++) {\n pos.set(positions[i].x, positions[i].y, positions[i].z)\n m.compose(pos, q, s)\n shelfMesh.setMatrixAt(i, m)\n }\n shelfMesh.instanceMatrix.needsUpdate = true\n shelfMesh.computeBoundingSphere()\n this.object3d.add(shelfMesh)\n }\n }\n\n // ── Merge — post + beam ─────────────────────────────────\n\n if (postGeos.length > 0) {\n const merged = BufferGeometryUtils.mergeGeometries(postGeos)\n const mesh = new THREE.Mesh(merged, POST_MATERIAL)\n mesh.castShadow = true\n mesh.receiveShadow = true\n this._frameGroup!.add(mesh)\n }\n\n if (beamGeos.length > 0) {\n const merged = BufferGeometryUtils.mergeGeometries(beamGeos)\n const mesh = new THREE.Mesh(merged, BEAM_MATERIAL)\n mesh.castShadow = true\n mesh.receiveShadow = true\n this._beamGroup!.add(mesh)\n }\n\n // ── 4. Stock InstancedMesh — state.data 의 record + hideEmptyStock 분기 ────\n this.rebuildStockMesh()\n }\n\n // ── Stock visualization ─────────────────────────────────\n\n private _stockMesh?: THREE.InstancedMesh // record 있는 stock (불투명, 색)\n private _emptyStockMesh?: THREE.InstancedMesh // record 없는 stock (반투명 회색)\n\n /** Public — 후속 click 핸들러 사용. */\n get stockMesh(): THREE.InstancedMesh | undefined {\n return this._stockMesh\n }\n\n /**\n * Stock 시각화 (Plan A — InstancedMesh batched).\n * - hideEmptyStock=true : state.data 의 record 있는 cell 만 instance\n * - hideEmptyStock=false : *모든 (non-isEmpty bay) cell × shelf* 에 instance.\n * record 있으면 default 색, 없으면 *백색 반투명*.\n */\n rebuildStockMesh(): void {\n // 기존 두 mesh 모두 제거\n if (this._stockMesh) {\n this.object3d.remove(this._stockMesh)\n this._stockMesh.dispose?.()\n this._stockMesh = undefined\n }\n if (this._emptyStockMesh) {\n this.object3d.remove(this._emptyStockMesh)\n this._emptyStockMesh.dispose?.()\n this._emptyStockMesh = undefined\n }\n\n const comp = this.component as unknown as RackGrid\n const rs: any = comp.state\n const cols = comp.columns\n const rows = comp.rackRows\n const shelves = comp.shelves\n // state.width/depth/height 가 NaN 인 경우 — ?? 는 nullish 만 fallback 이라 NaN 통과.\n // 명시적 finite 검증 후 default 사용. NaN BoxGeometry 의 근본 원인.\n const finite = (v: any, fb: number) =>\n typeof v === 'number' && Number.isFinite(v) && v > 0 ? v : fb\n const width = finite(rs?.width, 400)\n const height = finite(rs?.depth, 2000)\n const depth = finite(rs?.height, 200)\n const shelfBase = Math.max(0, Math.min(finite(rs?.shelfBaseHeight, 0), height * 0.9))\n const shelfZone = height - shelfBase\n const bayW = width / cols\n const bayD = depth / rows\n const cellY = shelfZone / shelves\n const baseY = -height / 2\n const shelfBaseY = baseY + shelfBase\n\n const stockW = bayW * 0.85\n const stockD = cellY * 0.7\n const stockH = bayD * 0.85\n\n // 최종 차원이 finite 한지 검증 — accessor + finite() 거쳤어도 *모든 입력이 0* 이거나\n // 예상 못한 경로로 NaN/0 가능성. invalid 시 stock mesh 생성 자체를 skip + 진단 로그.\n if (\n !Number.isFinite(stockW) || stockW <= 0 ||\n !Number.isFinite(stockD) || stockD <= 0 ||\n !Number.isFinite(stockH) || stockH <= 0\n ) {\n console.error('[rack-grid-3d] rebuildStockMesh: invalid stock dims — mesh 생성 skip', {\n stockW, stockD, stockH,\n cols, rows, shelves,\n width, height, depth, shelfBase, shelfZone, cellY, bayW, bayD,\n stateW: rs?.width, stateDepth: rs?.depth, stateH: rs?.height, stateShelfBase: rs?.shelfBaseHeight\n })\n return\n }\n\n const records = comp.records\n const recordsByCell = new Map<string, any>()\n for (const r of records) {\n if (r?.cellId) recordsByCell.set(r.cellId, r)\n }\n\n const hideEmpty = !!rs?.hideEmptyStock\n\n // 두 그룹 분리: record 있는 stock (불투명, 색) vs empty stock (반투명 회색)\n const filled: Array<{ col: number; row: number; shelf: number; record: any }> = []\n const empties: Array<{ col: number; row: number; shelf: number }> = []\n\n for (let col = 0; col < cols; col++) {\n for (let row = 0; row < rows; row++) {\n if (comp.isBayEmpty(col, row)) continue\n for (let shelf = 0; shelf < shelves; shelf++) {\n const cellId = `${col}-${row}-${shelf}`\n const record = recordsByCell.get(cellId)\n if (record) {\n filled.push({ col, row, shelf, record })\n } else if (!hideEmpty) {\n empties.push({ col, row, shelf })\n }\n }\n }\n }\n\n const matrixFor = (col: number, row: number, shelf: number, target: THREE.Matrix4) => {\n const cx = (col - cols / 2 + 0.5) * bayW\n const cellBottomY = shelfBaseY + shelf * cellY\n const cy = cellBottomY + stockD / 2\n const cz = (row - rows / 2 + 0.5) * bayD\n target.compose(new THREE.Vector3(cx, cy, cz), new THREE.Quaternion(), new THREE.Vector3(1, 1, 1))\n }\n\n // ── 1. Filled stock — 불투명, legend/default 색 ──────\n if (filled.length > 0) {\n const STOCK_COLOR_DEFAULT = '#c8a878' // cardboard\n const geo = new THREE.BoxGeometry(stockW, stockD, stockH)\n const mesh = new THREE.InstancedMesh(geo, STOCK_MATERIAL, filled.length)\n mesh.frustumCulled = false\n mesh.userData.context = this\n mesh.userData._records = filled.map(i => i.record)\n\n const m = new THREE.Matrix4()\n const c = new THREE.Color()\n for (let i = 0; i < filled.length; i++) {\n const { col, row, shelf, record } = filled[i]\n matrixFor(col, row, shelf, m)\n mesh.setMatrixAt(i, m)\n const resolved = (comp as any).resolveLegendColor?.(record) ?? STOCK_COLOR_DEFAULT\n c.set(resolved)\n mesh.setColorAt(i, c)\n }\n mesh.instanceMatrix.needsUpdate = true\n if (mesh.instanceColor) mesh.instanceColor.needsUpdate = true\n mesh.computeBoundingSphere()\n mesh.computeBoundingBox?.()\n this.object3d.add(mesh)\n this._stockMesh = mesh\n }\n\n // ── 2. Empty stock — 반투명 회색 (hideEmptyStock=off 시만) ────\n if (empties.length > 0) {\n const geo = new THREE.BoxGeometry(stockW, stockD, stockH)\n const mesh = new THREE.InstancedMesh(geo, EMPTY_STOCK_MATERIAL, empties.length)\n mesh.frustumCulled = false\n mesh.userData.context = this\n\n const m = new THREE.Matrix4()\n for (let i = 0; i < empties.length; i++) {\n const { col, row, shelf } = empties[i]\n matrixFor(col, row, shelf, m)\n mesh.setMatrixAt(i, m)\n }\n mesh.instanceMatrix.needsUpdate = true\n mesh.computeBoundingSphere()\n mesh.computeBoundingBox?.()\n this.object3d.add(mesh)\n this._emptyStockMesh = mesh\n }\n }\n\n dispose() {\n // Material 은 module-level singleton 이라 dispose 안 함 (전체 application\n // lifecycle 동안 살아있음). InstancedMesh / Group 의 geometry 만 정리.\n if (this._stockMesh) {\n this.object3d.remove(this._stockMesh)\n this._stockMesh.dispose?.()\n this._stockMesh = undefined\n }\n if (this._emptyStockMesh) {\n this.object3d.remove(this._emptyStockMesh)\n this._emptyStockMesh.dispose?.()\n this._emptyStockMesh = undefined\n }\n super.dispose()\n }\n\n updateAlpha() {}\n}\n"]}