@operato/scene-visualizer 9.2.2 → 10.0.0-beta.10

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 (249) hide show
  1. package/dist/carrier.d.ts +263 -0
  2. package/dist/carrier.js +272 -0
  3. package/dist/carrier.js.map +1 -0
  4. package/dist/desk.d.ts +238 -3
  5. package/dist/desk.js +1 -2
  6. package/dist/desk.js.map +1 -1
  7. package/dist/editors/index.d.ts +3 -0
  8. package/dist/editors/index.js +15 -0
  9. package/dist/editors/index.js.map +1 -1
  10. package/dist/editors/property-editor-gltf-fill-targets.d.ts +20 -0
  11. package/dist/editors/property-editor-gltf-fill-targets.js +313 -0
  12. package/dist/editors/property-editor-gltf-fill-targets.js.map +1 -0
  13. package/dist/editors/property-editor-gltf-info.d.ts +25 -3
  14. package/dist/editors/property-editor-gltf-info.js +333 -73
  15. package/dist/editors/property-editor-gltf-info.js.map +1 -1
  16. package/dist/editors/property-editor-gltf-play-targets.d.ts +25 -0
  17. package/dist/editors/property-editor-gltf-play-targets.js +388 -0
  18. package/dist/editors/property-editor-gltf-play-targets.js.map +1 -0
  19. package/dist/editors/property-editor-location-increase-pattern.js +87 -95
  20. package/dist/editors/property-editor-location-increase-pattern.js.map +1 -1
  21. package/dist/editors/property-editor-stocker-location.d.ts +13 -0
  22. package/dist/editors/property-editor-stocker-location.js +151 -0
  23. package/dist/editors/property-editor-stocker-location.js.map +1 -0
  24. package/dist/editors/property-editor-stocker-ports.d.ts +8 -0
  25. package/dist/editors/property-editor-stocker-ports.js +112 -0
  26. package/dist/editors/property-editor-stocker-ports.js.map +1 -0
  27. package/dist/effects/outline.js +1 -1
  28. package/dist/effects/outline.js.map +1 -1
  29. package/dist/index.d.ts +8 -17
  30. package/dist/index.js +10 -17
  31. package/dist/index.js.map +1 -1
  32. package/dist/rack-table-3d.d.ts +16 -0
  33. package/dist/rack-table-3d.js +95 -0
  34. package/dist/rack-table-3d.js.map +1 -0
  35. package/dist/rack-table-cell.d.ts +238 -3
  36. package/dist/rack-table-cell.js +44 -51
  37. package/dist/rack-table-cell.js.map +1 -1
  38. package/dist/rack-table-location.d.ts +37 -0
  39. package/dist/rack-table-location.js +227 -0
  40. package/dist/rack-table-location.js.map +1 -0
  41. package/dist/rack-table.d.ts +13 -29
  42. package/dist/rack-table.js +121 -380
  43. package/dist/rack-table.js.map +1 -1
  44. package/dist/rack.d.ts +16 -5
  45. package/dist/rack.js +106 -19
  46. package/dist/rack.js.map +1 -1
  47. package/dist/signal-tower.d.ts +492 -0
  48. package/dist/signal-tower.js +275 -0
  49. package/dist/signal-tower.js.map +1 -0
  50. package/dist/stock-hub.d.ts +25 -0
  51. package/dist/stock-hub.js +147 -0
  52. package/dist/stock-hub.js.map +1 -0
  53. package/dist/stock.d.ts +52 -8
  54. package/dist/stock.js +223 -120
  55. package/dist/stock.js.map +1 -1
  56. package/dist/stocker-3d.d.ts +23 -0
  57. package/dist/stocker-3d.js +352 -0
  58. package/dist/stocker-3d.js.map +1 -0
  59. package/dist/stocker-port-3d.d.ts +14 -0
  60. package/dist/stocker-port-3d.js +80 -0
  61. package/dist/stocker-port-3d.js.map +1 -0
  62. package/dist/stocker-port.d.ts +254 -0
  63. package/dist/stocker-port.js +123 -0
  64. package/dist/stocker-port.js.map +1 -0
  65. package/dist/stocker.d.ts +340 -0
  66. package/dist/stocker.js +370 -0
  67. package/dist/stocker.js.map +1 -0
  68. package/dist/tank.d.ts +492 -0
  69. package/dist/tank.js +312 -0
  70. package/dist/tank.js.map +1 -0
  71. package/dist/templates/carrier.d.ts +19 -0
  72. package/dist/templates/carrier.js +20 -0
  73. package/dist/templates/carrier.js.map +1 -0
  74. package/dist/templates/cube.js +1 -1
  75. package/dist/templates/cube.js.map +1 -1
  76. package/dist/templates/cylinder.js +3 -3
  77. package/dist/templates/cylinder.js.map +1 -1
  78. package/dist/templates/index.d.ts +38 -38
  79. package/dist/templates/index.js +15 -1
  80. package/dist/templates/index.js.map +1 -1
  81. package/dist/templates/rack-table.d.ts +2 -0
  82. package/dist/templates/rack-table.js +4 -2
  83. package/dist/templates/rack-table.js.map +1 -1
  84. package/dist/templates/signal-tower.d.ts +21 -0
  85. package/dist/templates/signal-tower.js +22 -0
  86. package/dist/templates/signal-tower.js.map +1 -0
  87. package/dist/templates/sphere.d.ts +1 -0
  88. package/dist/templates/sphere.js +5 -4
  89. package/dist/templates/sphere.js.map +1 -1
  90. package/dist/templates/stock-hub.d.ts +14 -0
  91. package/dist/templates/stock-hub.js +15 -0
  92. package/dist/templates/stock-hub.js.map +1 -0
  93. package/dist/templates/stocker-port.d.ts +17 -0
  94. package/dist/templates/stocker-port.js +17 -0
  95. package/dist/templates/stocker-port.js.map +1 -0
  96. package/dist/templates/stocker.d.ts +27 -0
  97. package/dist/templates/stocker.js +38 -0
  98. package/dist/templates/stocker.js.map +1 -0
  99. package/dist/templates/tank.d.ts +21 -0
  100. package/dist/templates/tank.js +22 -0
  101. package/dist/templates/tank.js.map +1 -0
  102. package/dist/templates/vehicle.d.ts +19 -0
  103. package/dist/templates/vehicle.js +20 -0
  104. package/dist/templates/vehicle.js.map +1 -0
  105. package/dist/templates/visualizer.js +1 -1
  106. package/dist/templates/visualizer.js.map +1 -1
  107. package/dist/vehicle.d.ts +248 -0
  108. package/dist/vehicle.js +133 -0
  109. package/dist/vehicle.js.map +1 -0
  110. package/dist/visualizer.d.ts +5 -5
  111. package/dist/visualizer.js +72 -68
  112. package/dist/visualizer.js.map +1 -1
  113. package/icons/carrier.png +0 -0
  114. package/icons/signal-tower.png +0 -0
  115. package/icons/stock-hub.png +0 -0
  116. package/icons/tank.png +0 -0
  117. package/icons/vehicle.png +0 -0
  118. package/package.json +16 -18
  119. package/translations/en.json +6 -0
  120. package/translations/ja.json +5 -0
  121. package/translations/ko.json +6 -1
  122. package/translations/ms.json +5 -0
  123. package/translations/zh.json +5 -0
  124. package/dist/banner.d.ts +0 -15
  125. package/dist/banner.js +0 -76
  126. package/dist/banner.js.map +0 -1
  127. package/dist/camera.d.ts +0 -20
  128. package/dist/camera.js +0 -108
  129. package/dist/camera.js.map +0 -1
  130. package/dist/cube.d.ts +0 -13
  131. package/dist/cube.js +0 -38
  132. package/dist/cube.js.map +0 -1
  133. package/dist/cylinder.d.ts +0 -11
  134. package/dist/cylinder.js +0 -38
  135. package/dist/cylinder.js.map +0 -1
  136. package/dist/ellipse.d.ts +0 -5
  137. package/dist/ellipse.js +0 -22
  138. package/dist/ellipse.js.map +0 -1
  139. package/dist/gltf-object.d.ts +0 -20
  140. package/dist/gltf-object.js +0 -104
  141. package/dist/gltf-object.js.map +0 -1
  142. package/dist/html-overlay-element.d.ts +0 -1
  143. package/dist/html-overlay-element.js +0 -12
  144. package/dist/html-overlay-element.js.map +0 -1
  145. package/dist/light.d.ts +0 -15
  146. package/dist/light.js +0 -135
  147. package/dist/light.js.map +0 -1
  148. package/dist/polygon.d.ts +0 -17
  149. package/dist/polygon.js +0 -64
  150. package/dist/polygon.js.map +0 -1
  151. package/dist/rect.d.ts +0 -5
  152. package/dist/rect.js +0 -36
  153. package/dist/rect.js.map +0 -1
  154. package/dist/scene/component.d.ts +0 -1
  155. package/dist/scene/component.js +0 -29
  156. package/dist/scene/component.js.map +0 -1
  157. package/dist/sphere.d.ts +0 -11
  158. package/dist/sphere.js +0 -38
  159. package/dist/sphere.js.map +0 -1
  160. package/dist/sprite.d.ts +0 -9
  161. package/dist/sprite.js +0 -28
  162. package/dist/sprite.js.map +0 -1
  163. package/dist/text.d.ts +0 -1
  164. package/dist/text.js +0 -9
  165. package/dist/text.js.map +0 -1
  166. package/dist/three-container-editor.d.ts +0 -22
  167. package/dist/three-container-editor.js +0 -132
  168. package/dist/three-container-editor.js.map +0 -1
  169. package/dist/three-container.d.ts +0 -85
  170. package/dist/three-container.js +0 -565
  171. package/dist/three-container.js.map +0 -1
  172. package/dist/three-controls.d.ts +0 -11
  173. package/dist/three-controls.js +0 -616
  174. package/dist/three-controls.js.map +0 -1
  175. package/dist/three-layout.d.ts +0 -8
  176. package/dist/three-layout.js +0 -20
  177. package/dist/three-layout.js.map +0 -1
  178. package/dist/three-space.d.ts +0 -85
  179. package/dist/three-space.js +0 -570
  180. package/dist/three-space.js.map +0 -1
  181. package/dist/threed/common.d.ts +0 -22
  182. package/dist/threed/common.js +0 -19
  183. package/dist/threed/common.js.map +0 -1
  184. package/dist/threed/floor/floor.d.ts +0 -3
  185. package/dist/threed/floor/floor.js +0 -51
  186. package/dist/threed/floor/floor.js.map +0 -1
  187. package/dist/threed/html/elements.d.ts +0 -2
  188. package/dist/threed/html/elements.js +0 -21
  189. package/dist/threed/html/elements.js.map +0 -1
  190. package/dist/threed/index.d.ts +0 -15
  191. package/dist/threed/index.js +0 -16
  192. package/dist/threed/index.js.map +0 -1
  193. package/dist/threed/real-object-camera-meshed.d.ts +0 -12
  194. package/dist/threed/real-object-camera-meshed.js +0 -49
  195. package/dist/threed/real-object-camera-meshed.js.map +0 -1
  196. package/dist/threed/real-object-camera.d.ts +0 -9
  197. package/dist/threed/real-object-camera.js +0 -31
  198. package/dist/threed/real-object-camera.js.map +0 -1
  199. package/dist/threed/real-object-dom-element.d.ts +0 -9
  200. package/dist/threed/real-object-dom-element.js +0 -40
  201. package/dist/threed/real-object-dom-element.js.map +0 -1
  202. package/dist/threed/real-object-dummy.d.ts +0 -6
  203. package/dist/threed/real-object-dummy.js +0 -11
  204. package/dist/threed/real-object-dummy.js.map +0 -1
  205. package/dist/threed/real-object-extrude.d.ts +0 -21
  206. package/dist/threed/real-object-extrude.js +0 -173
  207. package/dist/threed/real-object-extrude.js.map +0 -1
  208. package/dist/threed/real-object-gltf.d.ts +0 -16
  209. package/dist/threed/real-object-gltf.js +0 -101
  210. package/dist/threed/real-object-gltf.js.map +0 -1
  211. package/dist/threed/real-object-group.d.ts +0 -5
  212. package/dist/threed/real-object-group.js +0 -11
  213. package/dist/threed/real-object-group.js.map +0 -1
  214. package/dist/threed/real-object-mesh.d.ts +0 -13
  215. package/dist/threed/real-object-mesh.js +0 -75
  216. package/dist/threed/real-object-mesh.js.map +0 -1
  217. package/dist/threed/real-object-plane.d.ts +0 -5
  218. package/dist/threed/real-object-plane.js +0 -22
  219. package/dist/threed/real-object-plane.js.map +0 -1
  220. package/dist/threed/real-object-scene.d.ts +0 -21
  221. package/dist/threed/real-object-scene.js +0 -67
  222. package/dist/threed/real-object-scene.js.map +0 -1
  223. package/dist/threed/real-object-sprite-2d.d.ts +0 -14
  224. package/dist/threed/real-object-sprite-2d.js +0 -45
  225. package/dist/threed/real-object-sprite-2d.js.map +0 -1
  226. package/dist/threed/real-object-sprite.d.ts +0 -11
  227. package/dist/threed/real-object-sprite.js +0 -50
  228. package/dist/threed/real-object-sprite.js.map +0 -1
  229. package/dist/threed/real-object-text.d.ts +0 -15
  230. package/dist/threed/real-object-text.js +0 -64
  231. package/dist/threed/real-object-text.js.map +0 -1
  232. package/dist/threed/real-object.d.ts +0 -64
  233. package/dist/threed/real-object.js +0 -260
  234. package/dist/threed/real-object.js.map +0 -1
  235. package/dist/threed/texture/canvas-texture.d.ts +0 -4
  236. package/dist/threed/texture/canvas-texture.js +0 -49
  237. package/dist/threed/texture/canvas-texture.js.map +0 -1
  238. package/dist/threed/texture/text-texture.d.ts +0 -8
  239. package/dist/threed/texture/text-texture.js +0 -79
  240. package/dist/threed/texture/text-texture.js.map +0 -1
  241. package/dist/threed/three-dimensional-container.d.ts +0 -8
  242. package/dist/threed/three-dimensional-container.js +0 -2
  243. package/dist/threed/three-dimensional-container.js.map +0 -1
  244. package/dist/threed/utils/bound-uv-generator.d.ts +0 -16
  245. package/dist/threed/utils/bound-uv-generator.js +0 -42
  246. package/dist/threed/utils/bound-uv-generator.js.map +0 -1
  247. package/dist/wall.d.ts +0 -13
  248. package/dist/wall.js +0 -45
  249. package/dist/wall.js.map +0 -1
@@ -0,0 +1,275 @@
1
+ /*
2
+ * Copyright © HatioLab Inc. All rights reserved.
3
+ */
4
+ import { __decorate } from "tslib";
5
+ import { RealObjectGroup, RectPath, Shape, ValueHolder, sceneComponent } from '@hatiolab/things-scene';
6
+ import * as THREE from 'three';
7
+ import * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js';
8
+ const NATURE = {
9
+ mutable: false,
10
+ resizable: true,
11
+ rotatable: true,
12
+ properties: [
13
+ {
14
+ type: 'number',
15
+ label: 'value',
16
+ name: 'value',
17
+ property: 'value'
18
+ },
19
+ {
20
+ type: 'checkbox',
21
+ label: 'blink',
22
+ name: 'blink',
23
+ property: 'blink'
24
+ }
25
+ ],
26
+ help: 'scene/component/signal-tower'
27
+ };
28
+ /* ── 색상 상수 ── */
29
+ const LIGHT_COLORS = {
30
+ red: { on: '#cc0000', off: '#1a0505' },
31
+ yellow: { on: '#cc8800', off: '#1a1400' },
32
+ green: { on: '#00bb00', off: '#051a05' }
33
+ };
34
+ const BODY_COLOR = '#333333';
35
+ const POLE_COLOR = '#888888';
36
+ const RING_COLOR = '#222222';
37
+ /* ── 2D 렌더 ── */
38
+ const BLINK_PERIOD = 500; // ms
39
+ function isBlinkOn() {
40
+ return Math.floor(Date.now() / BLINK_PERIOD) % 2 === 0;
41
+ }
42
+ function renderSignalTower(ctx, x, y, w, h, value, blink) {
43
+ const sep = Math.max(1, h * 0.01);
44
+ const poleH = h * 0.08;
45
+ const lightH = (h - sep * 2 - poleH) / 3;
46
+ const blinkVisible = !blink || isBlinkOn();
47
+ const lights = [
48
+ { color: LIGHT_COLORS.red, on: !!(value & 4) && blinkVisible },
49
+ { color: LIGHT_COLORS.yellow, on: !!(value & 2) && blinkVisible },
50
+ { color: LIGHT_COLORS.green, on: !!(value & 1) && blinkVisible }
51
+ ];
52
+ let cy = y;
53
+ for (let i = 0; i < 3; i++) {
54
+ const light = lights[i];
55
+ // light band — full width
56
+ ctx.fillStyle = light.on ? light.color.on : light.color.off;
57
+ ctx.fillRect(x, cy, w, lightH);
58
+ // ON glow overlay
59
+ if (light.on) {
60
+ const cx = x + w / 2;
61
+ const my = cy + lightH / 2;
62
+ const grad = ctx.createRadialGradient(cx, my, 0, cx, my, w * 0.6);
63
+ grad.addColorStop(0, '#ffffff88');
64
+ grad.addColorStop(1, '#ffffff00');
65
+ ctx.fillStyle = grad;
66
+ ctx.fillRect(x, cy, w, lightH);
67
+ }
68
+ cy += lightH;
69
+ // thin separator between lights
70
+ if (i < 2) {
71
+ ctx.fillStyle = RING_COLOR;
72
+ ctx.fillRect(x, cy, w, sep);
73
+ cy += sep;
74
+ }
75
+ }
76
+ // pole stub
77
+ const poleW = w * 0.3;
78
+ ctx.fillStyle = POLE_COLOR;
79
+ ctx.fillRect(x + (w - poleW) / 2, cy, poleW, poleH);
80
+ }
81
+ /* ── 3D 렌더 ── */
82
+ export class SignalTower3D extends RealObjectGroup {
83
+ _lightMeshes = [];
84
+ build() {
85
+ super.build();
86
+ const { width, height } = this.component.bounds;
87
+ const { depth } = this.component.state;
88
+ const d = depth || width * 5;
89
+ const radius = Math.min(width, height) / 2;
90
+ // 경광등 부분: width 기반 고정 크기
91
+ const lightH = radius * 1.8;
92
+ const ringH = radius * 0.12;
93
+ const capH = radius * 0.25;
94
+ const baseH = radius * 0.15;
95
+ // 경광등 헤드 총 높이 (등3 + 링4 + 캡)
96
+ const headH = lightH * 3 + ringH * 4 + capH;
97
+ // 폴: 전체 depth에서 헤드+베이스를 뺀 나머지
98
+ const poleH = Math.max(radius * 0.5, d - headH - baseH);
99
+ // ── static parts (merged) ──
100
+ const statics = [];
101
+ // base
102
+ const baseGeo = new THREE.CylinderGeometry(radius * 1.4, radius * 1.4, baseH, 24);
103
+ baseGeo.translate(0, baseH / 2, 0);
104
+ statics.push(baseGeo);
105
+ // pole
106
+ const poleGeo = new THREE.CylinderGeometry(radius * 0.25, radius * 0.25, poleH, 12);
107
+ poleGeo.translate(0, baseH + poleH / 2, 0);
108
+ statics.push(poleGeo);
109
+ let y = baseH + poleH;
110
+ // ring between pole and first light
111
+ const ring0Geo = new THREE.CylinderGeometry(radius * 1.05, radius * 1.05, ringH, 24);
112
+ ring0Geo.translate(0, y + ringH / 2, 0);
113
+ statics.push(ring0Geo);
114
+ y += ringH;
115
+ // rings between lights
116
+ const lightOrder = ['green', 'yellow', 'red'];
117
+ for (let i = 0; i < 3; i++) {
118
+ y += lightH;
119
+ if (i < 2) {
120
+ const rGeo = new THREE.CylinderGeometry(radius * 1.05, radius * 1.05, ringH, 24);
121
+ rGeo.translate(0, y + ringH / 2, 0);
122
+ statics.push(rGeo);
123
+ y += ringH;
124
+ }
125
+ }
126
+ // cap
127
+ const capGeo = new THREE.CylinderGeometry(radius * 0.5, radius * 1.05, capH, 24);
128
+ capGeo.translate(0, y + capH / 2, 0);
129
+ statics.push(capGeo);
130
+ const mergedGeo = BufferGeometryUtils.mergeGeometries(statics);
131
+ const staticMat = new THREE.MeshStandardMaterial({
132
+ color: BODY_COLOR,
133
+ roughness: 0.6,
134
+ metalness: 0.3
135
+ });
136
+ const staticMesh = new THREE.Mesh(mergedGeo, staticMat);
137
+ staticMesh.castShadow = true;
138
+ this.object3d.add(staticMesh);
139
+ // ── light meshes (individual for emissive control) ──
140
+ this._lightMeshes = [];
141
+ let ly = baseH + poleH + ringH;
142
+ const value = this.component.value;
143
+ const bitMap = [1, 2, 4]; // green, yellow, red
144
+ for (let i = 0; i < 3; i++) {
145
+ const colorKey = lightOrder[i];
146
+ const isOn = !!(value & bitMap[i]);
147
+ const lightGeo = new THREE.CylinderGeometry(radius, radius, lightH, 24);
148
+ const lightMat = new THREE.MeshStandardMaterial({
149
+ color: isOn ? 0x000000 : LIGHT_COLORS[colorKey].off,
150
+ emissive: isOn ? new THREE.Color(LIGHT_COLORS[colorKey].on) : new THREE.Color(0x000000),
151
+ emissiveIntensity: isOn ? 1.0 : 0,
152
+ roughness: isOn ? 0.1 : 0.7,
153
+ metalness: 0.1
154
+ });
155
+ const mesh = new THREE.Mesh(lightGeo, lightMat);
156
+ mesh.position.y = ly + lightH / 2;
157
+ mesh.castShadow = true;
158
+ this.object3d.add(mesh);
159
+ this._lightMeshes.push(mesh);
160
+ ly += lightH + (i < 2 ? ringH : 0);
161
+ }
162
+ // center vertically
163
+ const totalH = baseH + poleH + headH;
164
+ this.object3d.children.forEach(child => {
165
+ child.position.y -= totalH / 2;
166
+ });
167
+ }
168
+ updateLights(forceOff = false) {
169
+ const value = this.component.value;
170
+ const lightOrder = ['green', 'yellow', 'red'];
171
+ const bitMap = [1, 2, 4]; // green, yellow, red
172
+ for (let i = 0; i < this._lightMeshes.length; i++) {
173
+ const mat = this._lightMeshes[i].material;
174
+ const colorKey = lightOrder[i];
175
+ const isOn = !!(value & bitMap[i]) && !forceOff;
176
+ mat.color.set(isOn ? 0x000000 : LIGHT_COLORS[colorKey].off);
177
+ mat.emissive.set(isOn ? LIGHT_COLORS[colorKey].on : 0x000000);
178
+ mat.emissiveIntensity = isOn ? 1.0 : 0;
179
+ mat.roughness = isOn ? 0.1 : 0.7;
180
+ mat.needsUpdate = true;
181
+ }
182
+ }
183
+ blinkTick() {
184
+ this.updateLights(!isBlinkOn());
185
+ }
186
+ update() {
187
+ super.update();
188
+ this.updateLights();
189
+ }
190
+ updateDimension() {
191
+ this.clear();
192
+ this.build();
193
+ }
194
+ onchange(after, before) {
195
+ super.onchange(after, before);
196
+ if ('value' in after) {
197
+ this.updateLights();
198
+ }
199
+ }
200
+ }
201
+ /* ── Component 클래스 ── */
202
+ const MixedShape = ValueHolder(RectPath(Shape));
203
+ let SignalTower = class SignalTower extends MixedShape {
204
+ _blinkRaf = null;
205
+ _lastBlinkOn = false;
206
+ is3dish() {
207
+ return true;
208
+ }
209
+ buildRealObject() {
210
+ return new SignalTower3D(this);
211
+ }
212
+ get nature() {
213
+ return NATURE;
214
+ }
215
+ async ready() {
216
+ await super.ready();
217
+ if (this.getState('blink')) {
218
+ this._startBlink();
219
+ }
220
+ }
221
+ dispose() {
222
+ this._stopBlink();
223
+ super.dispose();
224
+ }
225
+ _startBlink() {
226
+ if (this._blinkRaf)
227
+ return;
228
+ const tick = () => {
229
+ const on = isBlinkOn();
230
+ if (on !== this._lastBlinkOn) {
231
+ this._lastBlinkOn = on;
232
+ this.invalidate();
233
+ if (this._realObject) {
234
+ ;
235
+ this._realObject.blinkTick();
236
+ }
237
+ }
238
+ this._blinkRaf = requestAnimationFrame(tick);
239
+ };
240
+ this._blinkRaf = requestAnimationFrame(tick);
241
+ }
242
+ _stopBlink() {
243
+ if (!this._blinkRaf)
244
+ return;
245
+ cancelAnimationFrame(this._blinkRaf);
246
+ this._blinkRaf = null;
247
+ // 정상 점등 상태로 복원
248
+ this.invalidate();
249
+ if (this._realObject) {
250
+ ;
251
+ this._realObject.updateLights();
252
+ }
253
+ }
254
+ onchange(after, before) {
255
+ super.onchange(after, before);
256
+ if ('value' in after || 'blink' in after) {
257
+ this.invalidate();
258
+ if (this.getState('blink')) {
259
+ this._startBlink();
260
+ }
261
+ else {
262
+ this._stopBlink();
263
+ }
264
+ }
265
+ }
266
+ render(ctx) {
267
+ const { left, top, width, height } = this.bounds;
268
+ renderSignalTower(ctx, left, top, width, height, this.value, !!this.getState('blink'));
269
+ }
270
+ };
271
+ SignalTower = __decorate([
272
+ sceneComponent('signal-tower')
273
+ ], SignalTower);
274
+ export { SignalTower };
275
+ //# sourceMappingURL=signal-tower.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signal-tower.js","sourceRoot":"","sources":["../src/signal-tower.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n */\n\nimport {\n Component,\n ComponentNature,\n RealObjectGroup,\n RealObject,\n RectPath,\n Shape,\n ValueHolder,\n sceneComponent\n} from '@hatiolab/things-scene'\nimport * as THREE from 'three'\nimport * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js'\n\nconst NATURE: ComponentNature = {\n mutable: false,\n resizable: true,\n rotatable: true,\n properties: [\n {\n type: 'number',\n label: 'value',\n name: 'value',\n property: 'value'\n },\n {\n type: 'checkbox',\n label: 'blink',\n name: 'blink',\n property: 'blink'\n }\n ],\n help: 'scene/component/signal-tower'\n}\n\n/* ── 색상 상수 ── */\nconst LIGHT_COLORS = {\n red: { on: '#cc0000', off: '#1a0505' },\n yellow: { on: '#cc8800', off: '#1a1400' },\n green: { on: '#00bb00', off: '#051a05' }\n} as const\n\nconst BODY_COLOR = '#333333'\nconst POLE_COLOR = '#888888'\nconst RING_COLOR = '#222222'\n\n/* ── 2D 렌더 ── */\n\nconst BLINK_PERIOD = 500 // ms\n\nfunction isBlinkOn() {\n return Math.floor(Date.now() / BLINK_PERIOD) % 2 === 0\n}\n\nfunction renderSignalTower(ctx: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, value: number, blink: boolean) {\n const sep = Math.max(1, h * 0.01)\n const poleH = h * 0.08\n const lightH = (h - sep * 2 - poleH) / 3\n\n const blinkVisible = !blink || isBlinkOn()\n const lights = [\n { color: LIGHT_COLORS.red, on: !!(value & 4) && blinkVisible },\n { color: LIGHT_COLORS.yellow, on: !!(value & 2) && blinkVisible },\n { color: LIGHT_COLORS.green, on: !!(value & 1) && blinkVisible }\n ]\n\n let cy = y\n\n for (let i = 0; i < 3; i++) {\n const light = lights[i]\n\n // light band — full width\n ctx.fillStyle = light.on ? light.color.on : light.color.off\n ctx.fillRect(x, cy, w, lightH)\n\n // ON glow overlay\n if (light.on) {\n const cx = x + w / 2\n const my = cy + lightH / 2\n const grad = ctx.createRadialGradient(cx, my, 0, cx, my, w * 0.6)\n grad.addColorStop(0, '#ffffff88')\n grad.addColorStop(1, '#ffffff00')\n ctx.fillStyle = grad\n ctx.fillRect(x, cy, w, lightH)\n }\n\n cy += lightH\n\n // thin separator between lights\n if (i < 2) {\n ctx.fillStyle = RING_COLOR\n ctx.fillRect(x, cy, w, sep)\n cy += sep\n }\n }\n\n // pole stub\n const poleW = w * 0.3\n ctx.fillStyle = POLE_COLOR\n ctx.fillRect(x + (w - poleW) / 2, cy, poleW, poleH)\n}\n\n/* ── 3D 렌더 ── */\n\nexport class SignalTower3D extends RealObjectGroup {\n private _lightMeshes: THREE.Mesh[] = []\n\n build() {\n super.build()\n\n const { width, height } = this.component.bounds\n const { depth } = this.component.state\n const d = depth || width * 5\n\n const radius = Math.min(width, height) / 2\n\n // 경광등 부분: width 기반 고정 크기\n const lightH = radius * 1.8\n const ringH = radius * 0.12\n const capH = radius * 0.25\n const baseH = radius * 0.15\n\n // 경광등 헤드 총 높이 (등3 + 링4 + 캡)\n const headH = lightH * 3 + ringH * 4 + capH\n\n // 폴: 전체 depth에서 헤드+베이스를 뺀 나머지\n const poleH = Math.max(radius * 0.5, d - headH - baseH)\n\n // ── static parts (merged) ──\n const statics: THREE.BufferGeometry[] = []\n\n // base\n const baseGeo = new THREE.CylinderGeometry(radius * 1.4, radius * 1.4, baseH, 24)\n baseGeo.translate(0, baseH / 2, 0)\n statics.push(baseGeo)\n\n // pole\n const poleGeo = new THREE.CylinderGeometry(radius * 0.25, radius * 0.25, poleH, 12)\n poleGeo.translate(0, baseH + poleH / 2, 0)\n statics.push(poleGeo)\n\n let y = baseH + poleH\n\n // ring between pole and first light\n const ring0Geo = new THREE.CylinderGeometry(radius * 1.05, radius * 1.05, ringH, 24)\n ring0Geo.translate(0, y + ringH / 2, 0)\n statics.push(ring0Geo)\n y += ringH\n\n // rings between lights\n const lightOrder = ['green', 'yellow', 'red'] as const\n for (let i = 0; i < 3; i++) {\n y += lightH\n if (i < 2) {\n const rGeo = new THREE.CylinderGeometry(radius * 1.05, radius * 1.05, ringH, 24)\n rGeo.translate(0, y + ringH / 2, 0)\n statics.push(rGeo)\n y += ringH\n }\n }\n\n // cap\n const capGeo = new THREE.CylinderGeometry(radius * 0.5, radius * 1.05, capH, 24)\n capGeo.translate(0, y + capH / 2, 0)\n statics.push(capGeo)\n\n const mergedGeo = BufferGeometryUtils.mergeGeometries(statics)\n const staticMat = new THREE.MeshStandardMaterial({\n color: BODY_COLOR,\n roughness: 0.6,\n metalness: 0.3\n })\n const staticMesh = new THREE.Mesh(mergedGeo, staticMat)\n staticMesh.castShadow = true\n this.object3d.add(staticMesh)\n\n // ── light meshes (individual for emissive control) ──\n this._lightMeshes = []\n let ly = baseH + poleH + ringH\n const value = (this.component as unknown as SignalTower).value\n const bitMap = [1, 2, 4] // green, yellow, red\n\n for (let i = 0; i < 3; i++) {\n const colorKey = lightOrder[i]\n const isOn = !!(value & bitMap[i])\n\n const lightGeo = new THREE.CylinderGeometry(radius, radius, lightH, 24)\n const lightMat = new THREE.MeshStandardMaterial({\n color: isOn ? 0x000000 : LIGHT_COLORS[colorKey].off,\n emissive: isOn ? new THREE.Color(LIGHT_COLORS[colorKey].on) : new THREE.Color(0x000000),\n emissiveIntensity: isOn ? 1.0 : 0,\n roughness: isOn ? 0.1 : 0.7,\n metalness: 0.1\n })\n\n const mesh = new THREE.Mesh(lightGeo, lightMat)\n mesh.position.y = ly + lightH / 2\n mesh.castShadow = true\n this.object3d.add(mesh)\n this._lightMeshes.push(mesh)\n\n ly += lightH + (i < 2 ? ringH : 0)\n }\n\n // center vertically\n const totalH = baseH + poleH + headH\n this.object3d.children.forEach(child => {\n child.position.y -= totalH / 2\n })\n }\n\n updateLights(forceOff = false) {\n const value = (this.component as unknown as SignalTower).value\n const lightOrder = ['green', 'yellow', 'red'] as const\n const bitMap = [1, 2, 4] // green, yellow, red\n\n for (let i = 0; i < this._lightMeshes.length; i++) {\n const mat = this._lightMeshes[i].material as THREE.MeshStandardMaterial\n const colorKey = lightOrder[i]\n const isOn = !!(value & bitMap[i]) && !forceOff\n\n mat.color.set(isOn ? 0x000000 : LIGHT_COLORS[colorKey].off)\n mat.emissive.set(isOn ? LIGHT_COLORS[colorKey].on : 0x000000)\n mat.emissiveIntensity = isOn ? 1.0 : 0\n mat.roughness = isOn ? 0.1 : 0.7\n mat.needsUpdate = true\n }\n }\n\n blinkTick() {\n this.updateLights(!isBlinkOn())\n }\n\n update() {\n super.update()\n this.updateLights()\n }\n\n updateDimension() {\n this.clear()\n this.build()\n }\n\n onchange(after: Record<string, unknown>, before: Record<string, unknown>) {\n super.onchange(after, before)\n\n if ('value' in after) {\n this.updateLights()\n }\n }\n}\n\n/* ── Component 클래스 ── */\n\nconst MixedShape = ValueHolder(RectPath(Shape))\n\n@sceneComponent('signal-tower')\nexport class SignalTower extends MixedShape {\n private _blinkRaf: number | null = null\n private _lastBlinkOn: boolean = false\n\n is3dish() {\n return true\n }\n\n buildRealObject(): RealObject | undefined {\n return new SignalTower3D(this as any)\n }\n\n get nature() {\n return NATURE\n }\n\n async ready() {\n await super.ready()\n if (this.getState('blink')) {\n this._startBlink()\n }\n }\n\n dispose() {\n this._stopBlink()\n super.dispose()\n }\n\n private _startBlink() {\n if (this._blinkRaf) return\n\n const tick = () => {\n const on = isBlinkOn()\n if (on !== this._lastBlinkOn) {\n this._lastBlinkOn = on\n this.invalidate()\n if (this._realObject) {\n ;(this._realObject as SignalTower3D).blinkTick()\n }\n }\n this._blinkRaf = requestAnimationFrame(tick)\n }\n\n this._blinkRaf = requestAnimationFrame(tick)\n }\n\n private _stopBlink() {\n if (!this._blinkRaf) return\n cancelAnimationFrame(this._blinkRaf)\n this._blinkRaf = null\n // 정상 점등 상태로 복원\n this.invalidate()\n if (this._realObject) {\n ;(this._realObject as SignalTower3D).updateLights()\n }\n }\n\n onchange(after: Record<string, unknown>, before: Record<string, unknown>) {\n super.onchange(after, before)\n\n if ('value' in after || 'blink' in after) {\n this.invalidate()\n\n if (this.getState('blink')) {\n this._startBlink()\n } else {\n this._stopBlink()\n }\n }\n }\n\n render(ctx: CanvasRenderingContext2D) {\n const { left, top, width, height } = this.bounds\n\n renderSignalTower(ctx, left, top, width, height, this.value, !!this.getState('blink'))\n }\n\n}\n"]}
@@ -0,0 +1,25 @@
1
+ import { Component, ComponentNature, Properties, RealObject, ServiceComponent } from '@hatiolab/things-scene';
2
+ import * as THREE from 'three';
3
+ import { type StockMaterialProvider } from './stock.js';
4
+ export declare class StockHub extends ServiceComponent implements StockMaterialProvider {
5
+ static _icon?: HTMLImageElement;
6
+ static get icon(): HTMLImageElement;
7
+ _stock_materials: THREE.Material[];
8
+ _default_material?: THREE.Material;
9
+ _empty_material?: THREE.Material;
10
+ private _legendTarget?;
11
+ _objects: {
12
+ [id: string]: RealObject;
13
+ };
14
+ get nature(): ComponentNature;
15
+ get serviceName(): string;
16
+ get hideEmptyStock(): boolean;
17
+ get legendTarget(): Component | undefined;
18
+ putObject(id: string, object: RealObject): void;
19
+ getObject(id: string): RealObject;
20
+ dispose(): void;
21
+ onchangeData(): void;
22
+ onchange(after: Properties, before: Properties): void;
23
+ private _onLegendChanged;
24
+ private _resetMaterials;
25
+ }
@@ -0,0 +1,147 @@
1
+ /*
2
+ * Copyright © HatioLab Inc. All rights reserved.
3
+ */
4
+ var StockHub_1;
5
+ import { __decorate } from "tslib";
6
+ import { ServiceComponent, sceneComponent } from '@hatiolab/things-scene';
7
+ const STOCK_HUB_ICON_SRC = 'data:image/svg+xml;base64,' +
8
+ btoa(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" fill="#d09050">
9
+ <rect x="8" y="28" width="18" height="28" rx="2" opacity="0.8"/>
10
+ <rect x="23" y="18" width="18" height="38" rx="2" opacity="0.6"/>
11
+ <rect x="38" y="8" width="18" height="48" rx="2" opacity="0.9"/>
12
+ <rect x="4" y="56" width="56" height="4" rx="1"/>
13
+ </svg>`);
14
+ const NATURE = {
15
+ mutable: false,
16
+ resizable: true,
17
+ rotatable: true,
18
+ properties: [
19
+ {
20
+ type: 'string',
21
+ label: 'location-field',
22
+ name: 'locationField',
23
+ placeholder: 'location'
24
+ },
25
+ {
26
+ type: 'id-input',
27
+ label: 'legend-target',
28
+ name: 'legendTarget',
29
+ property: {
30
+ component: 'legend'
31
+ }
32
+ },
33
+ {
34
+ type: 'checkbox',
35
+ label: 'hide-empty-stock',
36
+ name: 'hideEmptyStock'
37
+ }
38
+ ],
39
+ help: 'scene/component/stock-hub'
40
+ };
41
+ let StockHub = class StockHub extends ServiceComponent {
42
+ static { StockHub_1 = this; }
43
+ static _icon;
44
+ static get icon() {
45
+ if (!StockHub_1._icon) {
46
+ StockHub_1._icon = new Image();
47
+ StockHub_1._icon.src = STOCK_HUB_ICON_SRC;
48
+ }
49
+ return StockHub_1._icon;
50
+ }
51
+ // === StockMaterialProvider ===
52
+ _stock_materials = [];
53
+ _default_material;
54
+ _empty_material;
55
+ _legendTarget;
56
+ // === Stock 레지스트리 ===
57
+ _objects = {};
58
+ get nature() {
59
+ return NATURE;
60
+ }
61
+ get serviceName() {
62
+ return 'stock';
63
+ }
64
+ get hideEmptyStock() {
65
+ return !!this.getState('hideEmptyStock');
66
+ }
67
+ get legendTarget() {
68
+ const { legendTarget } = this.state;
69
+ if (!this._legendTarget && legendTarget) {
70
+ this._legendTarget = this.root.findById?.(legendTarget);
71
+ this._legendTarget?.on('change', this._onLegendChanged, this);
72
+ }
73
+ return this._legendTarget;
74
+ }
75
+ /* === Stock 레지스트리 메서드 === */
76
+ putObject(id, object) {
77
+ id && (this._objects[id] = object);
78
+ }
79
+ getObject(id) {
80
+ return this._objects[id];
81
+ }
82
+ /* === 라이프사이클 === */
83
+ dispose() {
84
+ this._legendTarget?.off('change', this._onLegendChanged, this);
85
+ delete this._legendTarget;
86
+ this._resetMaterials();
87
+ this._objects = {};
88
+ super.dispose();
89
+ }
90
+ /* === 데이터 분배 === */
91
+ onchangeData() {
92
+ if (typeof this.data !== 'object') {
93
+ return;
94
+ }
95
+ let data = this.data;
96
+ const locationField = this.getState('locationField') || 'location';
97
+ if (data instanceof Array) {
98
+ data = data.reduce((acc, value) => {
99
+ const location = value[locationField];
100
+ if (!location) {
101
+ return acc;
102
+ }
103
+ if (acc[location]) {
104
+ acc[location]['items'].push(value);
105
+ }
106
+ else {
107
+ acc[location] = { items: [value] };
108
+ }
109
+ return acc;
110
+ }, {});
111
+ }
112
+ for (const key in data) {
113
+ if (data.hasOwnProperty(key)) {
114
+ const d = data[key];
115
+ const object = this.getObject(key);
116
+ if (object) {
117
+ ;
118
+ object.onchangeStockData(d);
119
+ }
120
+ }
121
+ }
122
+ }
123
+ /* === 이벤트 핸들러 === */
124
+ onchange(after, before) {
125
+ if ('legendTarget' in before || 'legendTarget' in after) {
126
+ this._legendTarget?.off('change', this._onLegendChanged, this);
127
+ delete this._legendTarget;
128
+ this._resetMaterials();
129
+ }
130
+ this.invalidate();
131
+ }
132
+ _onLegendChanged() {
133
+ this._resetMaterials();
134
+ this.invalidate();
135
+ }
136
+ _resetMaterials() {
137
+ this._stock_materials.forEach(m => m.dispose?.());
138
+ this._stock_materials = [];
139
+ delete this._default_material;
140
+ delete this._empty_material;
141
+ }
142
+ };
143
+ StockHub = StockHub_1 = __decorate([
144
+ sceneComponent('stock-hub')
145
+ ], StockHub);
146
+ export { StockHub };
147
+ //# sourceMappingURL=stock-hub.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stock-hub.js","sourceRoot":"","sources":["../src/stock-hub.ts"],"names":[],"mappings":"AAAA;;GAEG;;;AAEH,OAAO,EAKL,gBAAgB,EAChB,cAAc,EACf,MAAM,wBAAwB,CAAA;AAI/B,MAAM,kBAAkB,GACtB,4BAA4B;IAC5B,IAAI,CACF;;;;;OAKG,CACJ,CAAA;AAEH,MAAM,MAAM,GAAoB;IAC9B,OAAO,EAAE,KAAK;IACd,SAAS,EAAE,IAAI;IACf,SAAS,EAAE,IAAI;IACf,UAAU,EAAE;QACV;YACE,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,gBAAgB;YACvB,IAAI,EAAE,eAAe;YACrB,WAAW,EAAE,UAAU;SACxB;QACD;YACE,IAAI,EAAE,UAAU;YAChB,KAAK,EAAE,eAAe;YACtB,IAAI,EAAE,cAAc;YACpB,QAAQ,EAAE;gBACR,SAAS,EAAE,QAAQ;aACpB;SACF;QACD;YACE,IAAI,EAAE,UAAU;YAChB,KAAK,EAAE,kBAAkB;YACzB,IAAI,EAAE,gBAAgB;SACvB;KACF;IACD,IAAI,EAAE,2BAA2B;CAClC,CAAA;AAGM,IAAM,QAAQ,GAAd,MAAM,QAAS,SAAQ,gBAAgB;;IAC5C,MAAM,CAAC,KAAK,CAAmB;IAE/B,MAAM,KAAK,IAAI;QACb,IAAI,CAAC,UAAQ,CAAC,KAAK,EAAE,CAAC;YACpB,UAAQ,CAAC,KAAK,GAAG,IAAI,KAAK,EAAE,CAAA;YAC5B,UAAQ,CAAC,KAAK,CAAC,GAAG,GAAG,kBAAkB,CAAA;QACzC,CAAC;QACD,OAAO,UAAQ,CAAC,KAAK,CAAA;IACvB,CAAC;IAED,gCAAgC;IAChC,gBAAgB,GAAqB,EAAE,CAAA;IACvC,iBAAiB,CAAiB;IAClC,eAAe,CAAiB;IACxB,aAAa,CAAY;IAEjC,sBAAsB;IACtB,QAAQ,GAAiC,EAAE,CAAA;IAE3C,IAAI,MAAM;QACR,OAAO,MAAM,CAAA;IACf,CAAC;IAED,IAAI,WAAW;QACb,OAAO,OAAO,CAAA;IAChB,CAAC;IAED,IAAI,cAAc;QAChB,OAAO,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAA;IAC1C,CAAC;IAED,IAAI,YAAY;QACd,MAAM,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC,KAAK,CAAA;QAEnC,IAAI,CAAC,IAAI,CAAC,aAAa,IAAI,YAAY,EAAE,CAAC;YACxC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,YAAY,CAAC,CAAA;YACvD,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,gBAAgB,EAAE,IAAI,CAAC,CAAA;QAC/D,CAAC;QAED,OAAO,IAAI,CAAC,aAAa,CAAA;IAC3B,CAAC;IAED,6BAA6B;IAE7B,SAAS,CAAC,EAAU,EAAE,MAAkB;QACtC,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,CAAA;IACpC,CAAC;IAED,SAAS,CAAC,EAAU;QAClB,OAAO,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;IAC1B,CAAC;IAED,oBAAoB;IAEpB,OAAO;QACL,IAAI,CAAC,aAAa,EAAE,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,gBAAgB,EAAE,IAAI,CAAC,CAAA;QAC9D,OAAO,IAAI,CAAC,aAAa,CAAA;QACzB,IAAI,CAAC,eAAe,EAAE,CAAA;QACtB,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAA;QAElB,KAAK,CAAC,OAAO,EAAE,CAAA;IACjB,CAAC;IAED,oBAAoB;IAEpB,YAAY;QACV,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAClC,OAAM;QACR,CAAC;QAED,IAAI,IAAI,GAAG,IAAI,CAAC,IAAW,CAAA;QAC3B,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,UAAU,CAAA;QAElE,IAAI,IAAI,YAAY,KAAK,EAAE,CAAC;YAC1B,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAQ,EAAE,KAAU,EAAE,EAAE;gBAC1C,MAAM,QAAQ,GAAG,KAAK,CAAC,aAAa,CAAC,CAAA;gBAErC,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACd,OAAO,GAAG,CAAA;gBACZ,CAAC;gBAED,IAAI,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAClB,GAAG,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;gBACpC,CAAC;qBAAM,CAAC;oBACN,GAAG,CAAC,QAAQ,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,KAAK,CAAC,EAAE,CAAA;gBACpC,CAAC;gBAED,OAAO,GAAG,CAAA;YACZ,CAAC,EAAE,EAAE,CAAC,CAAA;QACR,CAAC;QAED,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC7B,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAA;gBACnB,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;gBAClC,IAAI,MAAM,EAAE,CAAC;oBACX,CAAC;oBAAC,MAAgB,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAA;gBACzC,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,qBAAqB;IAErB,QAAQ,CAAC,KAAiB,EAAE,MAAkB;QAC5C,IAAI,cAAc,IAAI,MAAM,IAAI,cAAc,IAAI,KAAK,EAAE,CAAC;YACxD,IAAI,CAAC,aAAa,EAAE,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,gBAAgB,EAAE,IAAI,CAAC,CAAA;YAC9D,OAAO,IAAI,CAAC,aAAa,CAAA;YACzB,IAAI,CAAC,eAAe,EAAE,CAAA;QACxB,CAAC;QAED,IAAI,CAAC,UAAU,EAAE,CAAA;IACnB,CAAC;IAEO,gBAAgB;QACtB,IAAI,CAAC,eAAe,EAAE,CAAA;QACtB,IAAI,CAAC,UAAU,EAAE,CAAA;IACnB,CAAC;IAEO,eAAe;QACrB,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;QACjD,IAAI,CAAC,gBAAgB,GAAG,EAAE,CAAA;QAC1B,OAAO,IAAI,CAAC,iBAAiB,CAAA;QAC7B,OAAO,IAAI,CAAC,eAAe,CAAA;IAC7B,CAAC;CACF,CAAA;AA9HY,QAAQ;IADpB,cAAc,CAAC,WAAW,CAAC;GACf,QAAQ,CA8HpB","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n */\n\nimport {\n Component,\n ComponentNature,\n Properties,\n RealObject,\n ServiceComponent,\n sceneComponent\n} from '@hatiolab/things-scene'\nimport * as THREE from 'three'\nimport { Stock, type StockMaterialProvider } from './stock.js'\n\nconst STOCK_HUB_ICON_SRC =\n 'data:image/svg+xml;base64,' +\n btoa(\n `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 64 64\" fill=\"#d09050\">\n <rect x=\"8\" y=\"28\" width=\"18\" height=\"28\" rx=\"2\" opacity=\"0.8\"/>\n <rect x=\"23\" y=\"18\" width=\"18\" height=\"38\" rx=\"2\" opacity=\"0.6\"/>\n <rect x=\"38\" y=\"8\" width=\"18\" height=\"48\" rx=\"2\" opacity=\"0.9\"/>\n <rect x=\"4\" y=\"56\" width=\"56\" height=\"4\" rx=\"1\"/>\n</svg>`\n )\n\nconst NATURE: ComponentNature = {\n mutable: false,\n resizable: true,\n rotatable: true,\n properties: [\n {\n type: 'string',\n label: 'location-field',\n name: 'locationField',\n placeholder: 'location'\n },\n {\n type: 'id-input',\n label: 'legend-target',\n name: 'legendTarget',\n property: {\n component: 'legend'\n }\n },\n {\n type: 'checkbox',\n label: 'hide-empty-stock',\n name: 'hideEmptyStock'\n }\n ],\n help: 'scene/component/stock-hub'\n}\n\n@sceneComponent('stock-hub')\nexport class StockHub extends ServiceComponent implements StockMaterialProvider {\n static _icon?: HTMLImageElement\n\n static get icon(): HTMLImageElement {\n if (!StockHub._icon) {\n StockHub._icon = new Image()\n StockHub._icon.src = STOCK_HUB_ICON_SRC\n }\n return StockHub._icon\n }\n\n // === StockMaterialProvider ===\n _stock_materials: THREE.Material[] = []\n _default_material?: THREE.Material\n _empty_material?: THREE.Material\n private _legendTarget?: Component\n\n // === Stock 레지스트리 ===\n _objects: { [id: string]: RealObject } = {}\n\n get nature(): ComponentNature {\n return NATURE\n }\n\n get serviceName(): string {\n return 'stock'\n }\n\n get hideEmptyStock(): boolean {\n return !!this.getState('hideEmptyStock')\n }\n\n get legendTarget(): Component | undefined {\n const { legendTarget } = this.state\n\n if (!this._legendTarget && legendTarget) {\n this._legendTarget = this.root.findById?.(legendTarget)\n this._legendTarget?.on('change', this._onLegendChanged, this)\n }\n\n return this._legendTarget\n }\n\n /* === Stock 레지스트리 메서드 === */\n\n putObject(id: string, object: RealObject): void {\n id && (this._objects[id] = object)\n }\n\n getObject(id: string): RealObject {\n return this._objects[id]\n }\n\n /* === 라이프사이클 === */\n\n dispose(): void {\n this._legendTarget?.off('change', this._onLegendChanged, this)\n delete this._legendTarget\n this._resetMaterials()\n this._objects = {}\n\n super.dispose()\n }\n\n /* === 데이터 분배 === */\n\n onchangeData(): void {\n if (typeof this.data !== 'object') {\n return\n }\n\n let data = this.data as any\n const locationField = this.getState('locationField') || 'location'\n\n if (data instanceof Array) {\n data = data.reduce((acc: any, value: any) => {\n const location = value[locationField]\n\n if (!location) {\n return acc\n }\n\n if (acc[location]) {\n acc[location]['items'].push(value)\n } else {\n acc[location] = { items: [value] }\n }\n\n return acc\n }, {})\n }\n\n for (const key in data) {\n if (data.hasOwnProperty(key)) {\n const d = data[key]\n const object = this.getObject(key)\n if (object) {\n ;(object as Stock).onchangeStockData(d)\n }\n }\n }\n }\n\n /* === 이벤트 핸들러 === */\n\n onchange(after: Properties, before: Properties): void {\n if ('legendTarget' in before || 'legendTarget' in after) {\n this._legendTarget?.off('change', this._onLegendChanged, this)\n delete this._legendTarget\n this._resetMaterials()\n }\n\n this.invalidate()\n }\n\n private _onLegendChanged(): void {\n this._resetMaterials()\n this.invalidate()\n }\n\n private _resetMaterials(): void {\n this._stock_materials.forEach(m => m.dispose?.())\n this._stock_materials = []\n delete this._default_material\n delete this._empty_material\n }\n}\n"]}
package/dist/stock.d.ts CHANGED
@@ -1,29 +1,73 @@
1
- import { Component, Model } from '@hatiolab/things-scene';
1
+ import { Component, RealObject } from '@hatiolab/things-scene';
2
2
  import * as THREE from 'three';
3
- import { RealObject } from './threed/real-object.js';
4
- import { Visualizer } from './visualizer.js';
3
+ /**
4
+ * Stock material/legend 정보를 제공하는 컴포넌트 인터페이스.
5
+ * Visualizer와 RackTable 모두 이를 구현한다.
6
+ */
7
+ export interface StockMaterialProvider {
8
+ _stock_materials: THREE.Material[];
9
+ _default_material?: THREE.Material;
10
+ _empty_material?: THREE.Material;
11
+ legendTarget?: Component;
12
+ hideEmptyStock?: boolean;
13
+ }
5
14
  export declare class Stock extends RealObject<THREE.Mesh> {
6
15
  static defaultMaterial: THREE.MeshStandardMaterial;
16
+ static defaultEmptyMaterial: THREE.MeshStandardMaterial;
7
17
  static stockGeometry: THREE.BoxGeometry;
8
18
  _hideEmptyStock: boolean;
9
19
  _focused: boolean;
10
20
  _focusedAt?: number;
11
- model: Model;
12
- constructor(component: Component, model: Model);
13
- protected getObject3dInstance(): THREE.Mesh<THREE.BufferGeometry<THREE.NormalBufferAttributes>, THREE.Material | THREE.Material[], THREE.Object3DEventMap>;
21
+ _instancedMesh?: THREE.InstancedMesh;
22
+ _instanceIndex: number;
23
+ _basePosition?: {
24
+ x: number;
25
+ y: number;
26
+ z: number;
27
+ };
28
+ _baseScale?: {
29
+ x: number;
30
+ y: number;
31
+ z: number;
32
+ };
33
+ model: any;
34
+ _data?: any;
35
+ constructor(component: Component, model: any);
36
+ get isInstanced(): boolean;
37
+ protected getObject3dInstance(): THREE.Mesh<THREE.BufferGeometry<THREE.NormalBufferAttributes, THREE.BufferGeometryEventMap>, THREE.Material | THREE.Material[], THREE.Object3DEventMap>;
38
+ /**
39
+ * 가장 가까운 StockMaterialProvider 조상을 찾는다 (RackTable 또는 Visualizer).
40
+ */
41
+ get provider(): StockMaterialProvider | undefined;
42
+ private get _legendStatus();
43
+ /**
44
+ * InstancedMesh의 해당 인스턴스 색상을 설정한다.
45
+ */
46
+ private _setInstanceColor;
47
+ /**
48
+ * 색상을 결정하고 InstancedMesh에 반영한다.
49
+ */
50
+ private _applyMaterialColor;
51
+ private _getDefaultColor;
14
52
  getMaterial(index: number): THREE.Material;
15
- get visualizer(): Visualizer | undefined;
16
53
  get stockMaterials(): THREE.Material[];
17
54
  get userDefineDefaultMaterial(): THREE.Material;
18
55
  get emptyMaterial(): THREE.Material;
19
56
  build(): void;
20
57
  createStock(w: number, h: number, d: number): void;
21
58
  onchangeStockData(data: any): void;
59
+ /**
60
+ * InstancedMesh 모드에서의 데이터 변경 처리.
61
+ * 개별 mesh의 material 대신 instanceColor를 변경한다.
62
+ */
63
+ private _onchangeStockDataInstanced;
64
+ private _aggregate;
22
65
  onBeforeRender: () => void;
23
- onmouseup(e: MouseEvent, visualizer: Visualizer, callback: (arg: {
66
+ onmouseup(e: MouseEvent, _context: unknown, callback: (arg: {
24
67
  data: any;
25
68
  location: string;
26
69
  }) => void): void;
70
+ private _getCurrentColorHex;
27
71
  updateTransform(): void;
28
72
  updateDimension(): void;
29
73
  updateAlpha(): void;