@shopware-ag/dive 1.0.7 → 1.0.8

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 (97) hide show
  1. package/package.json +6 -3
  2. package/src/__test__/DIVE.test.ts +244 -0
  3. package/src/animation/AnimationSystem.ts +14 -0
  4. package/src/animation/__test__/AnimationSystem.test.ts +19 -0
  5. package/src/axiscamera/AxisCamera.ts +50 -0
  6. package/src/axiscamera/__test__/AxisCamera.test.ts +41 -0
  7. package/src/camera/PerspectiveCamera.ts +43 -0
  8. package/src/camera/__test__/PerspectiveCamera.test.ts +27 -0
  9. package/src/com/Communication.ts +382 -0
  10. package/src/com/__test__/Communication.test.ts +612 -0
  11. package/src/com/actions/camera/getcameratransform.ts +9 -0
  12. package/src/com/actions/camera/movecamera.ts +15 -0
  13. package/src/com/actions/camera/resetcamera.ts +4 -0
  14. package/src/com/actions/camera/setcameralayer.ts +4 -0
  15. package/src/com/actions/camera/setcameratransform.ts +9 -0
  16. package/src/com/actions/camera/zoomcamera.ts +4 -0
  17. package/src/com/actions/index.ts +41 -0
  18. package/src/com/actions/media/generatemedia.ts +15 -0
  19. package/src/com/actions/object/addobject.ts +6 -0
  20. package/src/com/actions/object/deleteobject.ts +6 -0
  21. package/src/com/actions/object/getallobjects.ts +6 -0
  22. package/src/com/actions/object/getobjects.ts +6 -0
  23. package/src/com/actions/object/model/modelloaded.ts +4 -0
  24. package/src/com/actions/object/model/placeonfloor.ts +4 -0
  25. package/src/com/actions/object/selectobject.ts +6 -0
  26. package/src/com/actions/object/updateobject.ts +6 -0
  27. package/src/com/actions/scene/getallscenedata.ts +23 -0
  28. package/src/com/actions/scene/setbackground.ts +4 -0
  29. package/src/com/actions/scene/updatescene.ts +9 -0
  30. package/src/com/actions/toolbox/select/setgizmomode.ts +4 -0
  31. package/src/com/index.ts +4 -0
  32. package/src/com/types.ts +30 -0
  33. package/src/constant/AxisHelperColors.ts +7 -0
  34. package/src/constant/GridColors.ts +2 -0
  35. package/src/constant/VisibilityLayerMask.ts +5 -0
  36. package/src/controls/OrbitControls.ts +145 -0
  37. package/src/controls/__test__/OrbitControls.test.ts +181 -0
  38. package/src/grid/Grid.ts +22 -0
  39. package/src/grid/__test__/Grid.test.ts +19 -0
  40. package/src/helper/applyMixins/__test__/applyMixins.test.ts +27 -0
  41. package/src/helper/applyMixins/applyMixins.ts +15 -0
  42. package/src/helper/getObjectDelta/__test__/getObjectDelta.spec.ts +152 -0
  43. package/src/helper/getObjectDelta/getObjectDelta.ts +101 -0
  44. package/src/interface/Moveable.ts +13 -0
  45. package/src/interface/Rotatable.ts +10 -0
  46. package/src/interface/Scalable.ts +10 -0
  47. package/src/interface/Selectable.ts +11 -0
  48. package/src/interface/__test__/Interfaces.test.ts +13 -0
  49. package/src/light/AmbientLight.ts +29 -0
  50. package/src/light/PointLight.ts +63 -0
  51. package/src/light/SceneLight.ts +60 -0
  52. package/src/light/__test__/AmbientLight.test.ts +44 -0
  53. package/src/light/__test__/PointLight.test.ts +98 -0
  54. package/src/light/__test__/SceneLight.test.ts +122 -0
  55. package/src/loadingmanager/LoadingManager.ts +44 -0
  56. package/src/loadingmanager/__test__/LoadingManager.test.ts +52 -0
  57. package/src/math/__test__/DIVEMath.test.ts +12 -0
  58. package/src/math/ceil/__test__/ceilExp.test.ts +12 -0
  59. package/src/math/ceil/ceilExp.ts +6 -0
  60. package/src/math/floor/__test__/floorExp.test.ts +14 -0
  61. package/src/math/floor/floorExp.ts +6 -0
  62. package/src/math/helper/__test__/shift.test.ts +12 -0
  63. package/src/math/helper/shift.ts +4 -0
  64. package/src/math/index.ts +19 -0
  65. package/src/math/round/__test__/roundExp.test.ts +14 -0
  66. package/src/math/round/roundExp.ts +7 -0
  67. package/src/math/toFixed/__test__/toFixedExp.test.ts +14 -0
  68. package/src/math/toFixed/toFixedExp.ts +6 -0
  69. package/src/math/truncate/__test__/truncateExp.test.ts +14 -0
  70. package/src/math/truncate/truncateExp.ts +6 -0
  71. package/src/mediacreator/MediaCreator.ts +65 -0
  72. package/src/mediacreator/__test__/MediaCreator.test.ts +113 -0
  73. package/src/model/Model.ts +72 -0
  74. package/src/model/__test__/Model.test.ts +163 -0
  75. package/src/primitive/floor/Floor.ts +34 -0
  76. package/src/primitive/floor/__test__/Floor.test.ts +21 -0
  77. package/src/renderer/Renderer.ts +165 -0
  78. package/src/renderer/__test__/Renderer.test.ts +169 -0
  79. package/src/scene/Scene.ts +49 -0
  80. package/src/scene/__test__/Scene.test.ts +70 -0
  81. package/src/scene/root/Root.ts +107 -0
  82. package/src/scene/root/__test__/Root.test.ts +129 -0
  83. package/src/scene/root/lightroot/LightRoot.ts +84 -0
  84. package/src/scene/root/lightroot/__test__/LightRoot.test.ts +137 -0
  85. package/src/scene/root/modelroot/ModelRoot.ts +82 -0
  86. package/src/scene/root/modelroot/__test__/ModelRoot.test.ts +185 -0
  87. package/src/toolbox/BaseTool.ts +18 -0
  88. package/src/toolbox/Toolbox.ts +76 -0
  89. package/src/toolbox/__test__/Toolbox.test.ts +109 -0
  90. package/src/toolbox/select/SelectTool.ts +123 -0
  91. package/src/toolbox/select/__test__/SelectTool.test.ts +190 -0
  92. package/build/dive.cjs +0 -1551
  93. package/build/dive.cjs.map +0 -1
  94. package/build/dive.d.cts +0 -558
  95. package/build/dive.d.ts +0 -558
  96. package/build/dive.js +0 -1516
  97. package/build/dive.js.map +0 -1
@@ -0,0 +1,382 @@
1
+ import { Color, MeshStandardMaterial, MathUtils } from "three";
2
+ import DIVEScene from "../scene/Scene.ts";
3
+ import { Actions } from "./actions/index.ts";
4
+ import { COMLight, COMModel, COMEntity, COMPov } from "./types.ts";
5
+ import DIVEToolbox from "../toolbox/Toolbox.ts";
6
+ import DIVEMediaCreator from "../mediacreator/MediaCreator.ts";
7
+ import DIVEOrbitControls from "../controls/OrbitControls.ts";
8
+ import { DIVESelectable } from "../interface/Selectable.ts";
9
+ import DIVESelectTool from "../toolbox/select/SelectTool.ts";
10
+
11
+ type EventListener<Action extends keyof Actions> = (payload: Actions[Action]['PAYLOAD']) => void;
12
+
13
+ type Unsubscribe = () => boolean;
14
+
15
+ /**
16
+ * Main class for communicating with DIVE.
17
+ *
18
+ * You can subscribe to actions and perform them from outside and inside DIVE.
19
+ *
20
+ * ```ts
21
+ * import { DIVE } from "@shopware-ag/dive";
22
+ *
23
+ * const dive = new DIVE();
24
+ *
25
+ * dive.Communication.Subscribe('GET_ALL_SCENE_DATA', () => {
26
+ * // do something
27
+ * }));
28
+ *
29
+ * dive.Communication.PerformAction('GET_ALL_SCENE_DATA', {});
30
+ * ```
31
+ *
32
+ * @module
33
+ */
34
+
35
+ export default class DIVECommunication {
36
+ private static __instances: DIVECommunication[] = [];
37
+
38
+ public static get(id: string): DIVECommunication | undefined {
39
+ return this.__instances.find((instance) => Array.from(instance.registered.values()).find((object) => object.id === id));
40
+ }
41
+
42
+ private id: string;
43
+ private scene: DIVEScene;
44
+ private controller: DIVEOrbitControls;
45
+ private toolbox: DIVEToolbox;
46
+ private mediaGenerator: DIVEMediaCreator;
47
+
48
+ private registered: Map<string, COMEntity> = new Map();
49
+
50
+ // private listeners: { [key: string]: EventListener[] } = {};
51
+ private listeners: Map<keyof Actions, EventListener<keyof Actions>[]> = new Map();
52
+
53
+ constructor(scene: DIVEScene, controls: DIVEOrbitControls, toolbox: DIVEToolbox, mediaGenerator: DIVEMediaCreator) {
54
+ this.id = MathUtils.generateUUID();
55
+ this.scene = scene;
56
+ this.controller = controls;
57
+ this.toolbox = toolbox;
58
+ this.mediaGenerator = mediaGenerator;
59
+
60
+ DIVECommunication.__instances.push(this);
61
+ }
62
+
63
+ public DestroyInstance(): boolean {
64
+ const existingIndex = DIVECommunication.__instances.findIndex((entry) => entry.id === this.id);
65
+ if (existingIndex === -1) return false;
66
+ DIVECommunication.__instances.splice(existingIndex, 1);
67
+ return true;
68
+ }
69
+
70
+ public PerformAction<Action extends keyof Actions>(action: Action, payload: Actions[Action]['PAYLOAD']): Actions[Action]['RETURN'] {
71
+ let returnValue: Actions[Action]['RETURN'] = false;
72
+
73
+ switch (action) {
74
+ case 'GET_ALL_SCENE_DATA': {
75
+ returnValue = this.getAllSceneData(payload as Actions['GET_ALL_SCENE_DATA']['PAYLOAD']);
76
+ break;
77
+ }
78
+ case 'GET_ALL_OBJECTS': {
79
+ returnValue = this.getAllObjects(payload as Actions['GET_ALL_OBJECTS']['PAYLOAD']);
80
+ break;
81
+ }
82
+ case 'GET_OBJECTS': {
83
+ returnValue = this.getObjects(payload as Actions['GET_OBJECTS']['PAYLOAD']);
84
+ break;
85
+ }
86
+ case 'ADD_OBJECT': {
87
+ returnValue = this.addObject(payload as Actions['ADD_OBJECT']['PAYLOAD']);
88
+ break;
89
+ }
90
+ case 'UPDATE_OBJECT': {
91
+ returnValue = this.updateObject(payload as Actions['UPDATE_OBJECT']['PAYLOAD']);
92
+ break;
93
+ }
94
+ case 'DELETE_OBJECT': {
95
+ returnValue = this.deleteObject(payload as Actions['DELETE_OBJECT']['PAYLOAD']);
96
+ break;
97
+ }
98
+ case 'SELECT_OBJECT': {
99
+ returnValue = this.selectObject(payload as Actions['SELECT_OBJECT']['PAYLOAD']);
100
+ break;
101
+ }
102
+ case 'SET_BACKGROUND': {
103
+ returnValue = this.setBackground(payload as Actions['SET_BACKGROUND']['PAYLOAD']);
104
+ break;
105
+ }
106
+ case 'PLACE_ON_FLOOR': {
107
+ returnValue = this.placeOnFloor(payload as Actions['PLACE_ON_FLOOR']['PAYLOAD']);
108
+ break;
109
+ }
110
+ case 'SET_CAMERA_TRANSFORM': {
111
+ returnValue = this.setCameraTransform(payload as Actions['SET_CAMERA_TRANSFORM']['PAYLOAD']);
112
+ break;
113
+ }
114
+ case 'GET_CAMERA_TRANSFORM': {
115
+ returnValue = this.getCameraTransform(payload as Actions['GET_CAMERA_TRANSFORM']['PAYLOAD']);
116
+ break;
117
+ }
118
+ case 'MOVE_CAMERA': {
119
+ returnValue = this.moveCamera(payload as Actions['MOVE_CAMERA']['PAYLOAD']);
120
+ break;
121
+ }
122
+ case 'RESET_CAMERA': {
123
+ returnValue = this.resetCamera(payload as Actions['RESET_CAMERA']['PAYLOAD']);
124
+ break;
125
+ }
126
+ case 'SET_CAMERA_LAYER': {
127
+ returnValue = this.setCameraLayer(payload as Actions['SET_CAMERA_LAYER']['PAYLOAD']);
128
+ break;
129
+ }
130
+ case 'ZOOM_CAMERA': {
131
+ returnValue = this.zoomCamera(payload as Actions['ZOOM_CAMERA']['PAYLOAD']);
132
+ break;
133
+ }
134
+ case 'SET_GIZMO_MODE': {
135
+ returnValue = this.setGizmoMode(payload as Actions['SET_GIZMO_MODE']['PAYLOAD']);
136
+ break;
137
+ }
138
+ case 'MODEL_LOADED': {
139
+ returnValue = this.modelLoaded(payload as Actions['MODEL_LOADED']['PAYLOAD']);
140
+ break;
141
+ }
142
+ case 'UPDATE_SCENE': {
143
+ returnValue = this.updateScene(payload as Actions['UPDATE_SCENE']['PAYLOAD']);
144
+ break;
145
+ }
146
+ case 'GENERATE_MEDIA': {
147
+ returnValue = this.generateMedia(payload as Actions['GENERATE_MEDIA']['PAYLOAD']);
148
+ break;
149
+ }
150
+ }
151
+
152
+ this.dispatch(action, payload);
153
+
154
+ return returnValue;
155
+ }
156
+
157
+ public Subscribe<Action extends keyof Actions>(type: Action, listener: EventListener<Action>): Unsubscribe {
158
+ if (!this.listeners.get(type)) this.listeners.set(type, []);
159
+
160
+ // casting to any because of typescript not finding between Action and typeof Actions being equal in this case
161
+ this.listeners.get(type)!.push(listener as EventListener<keyof Actions>);
162
+
163
+ return () => {
164
+ const listenerArray = this.listeners.get(type);
165
+ if (!listenerArray) return false;
166
+
167
+ const existingIndex = listenerArray.findIndex((entry) => entry === listener);
168
+ if (existingIndex === -1) return false;
169
+
170
+ listenerArray.splice(existingIndex, 1);
171
+ return true;
172
+ };
173
+ }
174
+
175
+ private dispatch<Action extends keyof Actions>(type: Action, payload: Actions[Action]['PAYLOAD']): void {
176
+ const listenerArray = this.listeners.get(type);
177
+ if (!listenerArray) return;
178
+
179
+ listenerArray.forEach((listener) => listener(payload))
180
+ }
181
+
182
+ private getAllSceneData(payload: Actions['GET_ALL_SCENE_DATA']['PAYLOAD']): Actions['GET_ALL_SCENE_DATA']['RETURN'] {
183
+ const sceneData = {
184
+ name: this.scene.name,
185
+ mediaItem: null,
186
+ backgroundColor: '#' + (this.scene.background as Color).getHexString(),
187
+ floorEnabled: this.scene.Root.Floor.visible,
188
+ floorColor: '#' + (this.scene.Root.Floor.material as MeshStandardMaterial).color.getHexString(),
189
+ userCamera: {
190
+ position: this.controller.object.position.clone(),
191
+ target: this.controller.target.clone(),
192
+ },
193
+ spotmarks: [],
194
+ lights: Array.from(this.registered.values()).filter((object) => object.entityType === 'light') as COMLight[],
195
+ objects: Array.from(this.registered.values()).filter((object) => object.entityType === 'model') as COMModel[],
196
+ cameras: Array.from(this.registered.values()).filter((object) => object.entityType === 'pov') as COMPov[],
197
+ };
198
+ Object.assign(payload, sceneData);
199
+ return sceneData;
200
+ }
201
+
202
+ private getAllObjects(payload: Actions['GET_ALL_OBJECTS']['PAYLOAD']): Actions['GET_ALL_OBJECTS']['RETURN'] {
203
+ Object.assign(payload, this.registered);
204
+ return this.registered;
205
+ }
206
+
207
+ private getObjects(payload: Actions['GET_OBJECTS']['PAYLOAD']): Actions['GET_OBJECTS']['RETURN'] {
208
+ this.registered.forEach((object) => {
209
+ if (payload.ids && payload.ids.length > 0 && !payload.ids.includes(object.id)) return;
210
+ payload.map.set(object.id, object);
211
+ });
212
+
213
+ return payload.map;
214
+ }
215
+
216
+ private addObject(payload: Actions['ADD_OBJECT']['PAYLOAD']): Actions['ADD_OBJECT']['RETURN'] {
217
+ if (this.registered.get(payload.id)) return false;
218
+
219
+ this.registered.set(payload.id, payload);
220
+
221
+ this.scene.AddSceneObject(payload);
222
+
223
+ return true;
224
+ }
225
+
226
+ private updateObject(payload: Actions['UPDATE_OBJECT']['PAYLOAD']): Actions['UPDATE_OBJECT']['RETURN'] {
227
+ const objectToUpdate = this.registered.get(payload.id);
228
+ if (!objectToUpdate) return false;
229
+
230
+ this.registered.set(payload.id, { ...objectToUpdate, ...payload });
231
+
232
+ const updatedObject = this.registered.get(payload.id)!;
233
+ this.scene.UpdateSceneObject(updatedObject);
234
+
235
+ Object.assign(payload, updatedObject);
236
+
237
+ return true;
238
+ }
239
+
240
+ private deleteObject(payload: Actions['DELETE_OBJECT']['PAYLOAD']): Actions['DELETE_OBJECT']['RETURN'] {
241
+ const deletedObject = this.registered.get(payload.id);
242
+ if (!deletedObject) return false;
243
+
244
+ // copy object to payload to use later
245
+ Object.assign(payload, deletedObject);
246
+
247
+ this.registered.delete(payload.id);
248
+
249
+ this.scene.DeleteSceneObject(deletedObject);
250
+
251
+ return true;
252
+ }
253
+
254
+ private selectObject(payload: Actions['SELECT_OBJECT']['PAYLOAD']): Actions['SELECT_OBJECT']['RETURN'] {
255
+ const object = this.registered.get(payload.id);
256
+ if (!object) return false;
257
+
258
+ const sceneObject = this.scene.GetSceneObject(object);
259
+ if (!sceneObject) return false;
260
+
261
+ if (!('isSelectable' in sceneObject)) return false;
262
+
263
+ this.toolbox.UseTool('select');
264
+ (this.toolbox.GetActiveTool() as DIVESelectTool).Select(sceneObject as DIVESelectable);
265
+
266
+ // copy object to payload to use later
267
+ Object.assign(payload, object);
268
+
269
+ return true;
270
+ }
271
+
272
+ private setBackground(payload: Actions['SET_BACKGROUND']['PAYLOAD']): Actions['SET_BACKGROUND']['RETURN'] {
273
+ this.scene.SetBackground(payload.color);
274
+
275
+ return true;
276
+ }
277
+
278
+ private placeOnFloor(payload: Actions['PLACE_ON_FLOOR']['PAYLOAD']): Actions['PLACE_ON_FLOOR']['RETURN'] {
279
+ if (!this.registered.get(payload.id)) return false;
280
+
281
+ this.scene.PlaceOnFloor(payload);
282
+
283
+ return true;
284
+ }
285
+
286
+ private setCameraTransform(payload: Actions['SET_CAMERA_TRANSFORM']['PAYLOAD']): Actions['SET_CAMERA_TRANSFORM']['RETURN'] {
287
+ this.controller.object.position.copy(payload.position);
288
+ this.controller.target.copy(payload.target);
289
+ this.controller.update();
290
+
291
+ return true;
292
+ }
293
+
294
+ private getCameraTransform(payload: Actions['GET_CAMERA_TRANSFORM']['PAYLOAD']): Actions['GET_CAMERA_TRANSFORM']['RETURN'] {
295
+ const transform = {
296
+ position: this.controller.object.position.clone(),
297
+ target: this.controller.target.clone()
298
+ };
299
+ Object.assign(payload, transform);
300
+
301
+ return transform;
302
+ }
303
+
304
+ private moveCamera(payload: Actions['MOVE_CAMERA']['PAYLOAD']): Actions['MOVE_CAMERA']['RETURN'] {
305
+ let position = { x: 0, y: 0, z: 0 };
306
+ let target = { x: 0, y: 0, z: 0 };
307
+ if ('id' in payload) {
308
+ position = (this.registered.get(payload.id) as COMPov).position;
309
+ target = (this.registered.get(payload.id) as COMPov).target;
310
+ } else {
311
+ position = payload.position;
312
+ target = payload.target;
313
+ }
314
+ this.controller.MoveTo(position, target, payload.duration, payload.locked);
315
+
316
+ return true;
317
+ }
318
+
319
+ private setCameraLayer(payload: Actions['SET_CAMERA_LAYER']['PAYLOAD']): Actions['SET_CAMERA_LAYER']['RETURN'] {
320
+ this.controller.object.SetCameraLayer(payload.layer);
321
+
322
+ return true;
323
+ }
324
+
325
+ private resetCamera(payload: Actions['RESET_CAMERA']['PAYLOAD']): Actions['RESET_CAMERA']['RETURN'] {
326
+ this.controller.RevertLast(payload.duration);
327
+
328
+ return true;
329
+ }
330
+
331
+ private zoomCamera(payload: Actions['ZOOM_CAMERA']['PAYLOAD']): Actions['ZOOM_CAMERA']['RETURN'] {
332
+ if (payload.direction === 'IN') this.controller.ZoomIn(payload.by);
333
+ if (payload.direction === 'OUT') this.controller.ZoomOut(payload.by);
334
+
335
+ return true;
336
+ }
337
+
338
+ private setGizmoMode(payload: Actions['SET_GIZMO_MODE']['PAYLOAD']): Actions['SET_GIZMO_MODE']['RETURN'] {
339
+ this.toolbox.SetGizmoMode(payload.mode);
340
+ return true;
341
+ }
342
+
343
+ private modelLoaded(payload: Actions['MODEL_LOADED']['PAYLOAD']): Actions['MODEL_LOADED']['RETURN'] {
344
+ (this.registered.get(payload.id) as COMModel).loaded = true;
345
+ return true;
346
+ }
347
+
348
+ private updateScene(payload: Actions['UPDATE_SCENE']['PAYLOAD']): Actions['UPDATE_SCENE']['RETURN'] {
349
+ if (payload.name !== undefined) this.scene.name = payload.name;
350
+ if (payload.backgroundColor !== undefined) this.scene.SetBackground(payload.backgroundColor);
351
+
352
+ if (payload.floorEnabled !== undefined) this.scene.Root.Floor.SetVisibility(payload.floorEnabled);
353
+ if (payload.floorColor !== undefined) this.scene.Root.Floor.SetColor(payload.floorColor);
354
+
355
+
356
+ // fill payload with current values
357
+ // TODO optmize this
358
+ payload.name = this.scene.name;
359
+ payload.backgroundColor = '#' + (this.scene.background as Color).getHexString();
360
+ payload.floorEnabled = this.scene.Root.Floor.visible;
361
+ payload.floorColor = '#' + (this.scene.Root.Floor.material as MeshStandardMaterial).color.getHexString();
362
+
363
+ return true;
364
+ }
365
+
366
+ private generateMedia(payload: Actions['GENERATE_MEDIA']['PAYLOAD']): Actions['GENERATE_MEDIA']['RETURN'] {
367
+ let position = { x: 0, y: 0, z: 0 };
368
+ let target = { x: 0, y: 0, z: 0 };
369
+ if ('id' in payload) {
370
+ position = (this.registered.get(payload.id) as COMPov).position;
371
+ target = (this.registered.get(payload.id) as COMPov).target;
372
+ } else {
373
+ position = payload.position;
374
+ target = payload.target;
375
+ }
376
+
377
+ payload.dataUri = this.mediaGenerator.GenerateMedia(position, target, payload.width, payload.height);
378
+
379
+ return true;
380
+ }
381
+ }
382
+