@shopware-ag/dive 1.1.2 → 1.3.0

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 (47) hide show
  1. package/README.md +1 -0
  2. package/build/dive.cjs +333 -60
  3. package/build/dive.cjs.map +1 -1
  4. package/build/dive.d.cts +73 -6
  5. package/build/dive.d.ts +73 -6
  6. package/build/dive.js +335 -62
  7. package/build/dive.js.map +1 -1
  8. package/package.json +1 -1
  9. package/src/__test__/DIVE.test.ts +2 -0
  10. package/src/com/Communication.ts +21 -3
  11. package/src/com/__test__/Communication.test.ts +44 -6
  12. package/src/com/actions/index.ts +2 -0
  13. package/src/com/actions/object/getobjects.ts +2 -2
  14. package/src/com/actions/object/model/dropit.ts +4 -0
  15. package/src/dive.ts +7 -0
  16. package/src/gizmo/Gizmo.ts +130 -0
  17. package/src/gizmo/handles/AxisHandle.ts +124 -0
  18. package/src/gizmo/handles/RadialHandle.ts +119 -0
  19. package/src/gizmo/handles/ScaleHandle.ts +152 -0
  20. package/src/gizmo/plane/GizmoPlane.ts +85 -0
  21. package/src/gizmo/rotate/RotateGizmo.ts +95 -0
  22. package/src/gizmo/scale/ScaleGizmo.ts +97 -0
  23. package/src/gizmo/translate/TranslateGizmo.ts +88 -0
  24. package/src/helper/findSceneRecursive/__test__/findSceneRecursive.test.ts +40 -0
  25. package/src/helper/findSceneRecursive/findSceneRecursive.ts +16 -0
  26. package/src/interface/Draggable.ts +34 -0
  27. package/src/interface/Hoverable.ts +33 -0
  28. package/src/interface/Moveable.ts +0 -2
  29. package/src/interface/Selectable.ts +6 -0
  30. package/src/interface/__test__/Interfaces.test.ts +56 -0
  31. package/src/math/index.ts +3 -0
  32. package/src/math/signedAngleTo/__test__/signedAngleTo.test.ts +14 -0
  33. package/src/math/signedAngleTo/signedAngleTo.ts +13 -0
  34. package/src/model/Model.ts +35 -1
  35. package/src/model/__test__/Model.test.ts +141 -8
  36. package/src/scene/root/lightroot/LightRoot.ts +17 -3
  37. package/src/scene/root/lightroot/__test__/LightRoot.test.ts +12 -3
  38. package/src/scene/root/modelroot/ModelRoot.ts +17 -3
  39. package/src/scene/root/modelroot/__test__/ModelRoot.test.ts +13 -14
  40. package/src/toolbox/BaseTool.ts +254 -4
  41. package/src/toolbox/Toolbox.ts +6 -0
  42. package/src/toolbox/__test__/BaseTool.test.ts +389 -0
  43. package/src/toolbox/__test__/Toolbox.test.ts +8 -0
  44. package/src/toolbox/select/SelectTool.ts +29 -65
  45. package/src/toolbox/select/__test__/SelectTool.test.ts +57 -25
  46. package/src/toolbox/transform/TransformTool.ts +48 -0
  47. /package/src/helper/getObjectDelta/__test__/{getObjectDelta.spec.ts → getObjectDelta.test.ts} +0 -0
package/build/dive.js CHANGED
@@ -261,6 +261,10 @@ var _DIVECommunication = class _DIVECommunication {
261
261
  returnValue = this.setBackground(payload);
262
262
  break;
263
263
  }
264
+ case "DROP_IT": {
265
+ returnValue = this.dropIt(payload);
266
+ break;
267
+ }
264
268
  case "PLACE_ON_FLOOR": {
265
269
  returnValue = this.placeOnFloor(payload);
266
270
  break;
@@ -350,11 +354,13 @@ var _DIVECommunication = class _DIVECommunication {
350
354
  return this.registered;
351
355
  }
352
356
  getObjects(payload) {
357
+ if (payload.ids.length === 0) return [];
358
+ const objects = [];
353
359
  this.registered.forEach((object) => {
354
- if (payload.ids && payload.ids.length > 0 && !payload.ids.includes(object.id)) return;
355
- payload.map.set(object.id, object);
360
+ if (!payload.ids.includes(object.id)) return;
361
+ objects.push(object);
356
362
  });
357
- return payload.map;
363
+ return objects;
358
364
  }
359
365
  addObject(payload) {
360
366
  if (this.registered.get(payload.id)) return false;
@@ -394,6 +400,13 @@ var _DIVECommunication = class _DIVECommunication {
394
400
  this.scene.SetBackground(payload.color);
395
401
  return true;
396
402
  }
403
+ dropIt(payload) {
404
+ const object = this.registered.get(payload.id);
405
+ if (!object) return false;
406
+ const model = this.scene.GetSceneObject(object);
407
+ model.DropIt();
408
+ return true;
409
+ }
397
410
  placeOnFloor(payload) {
398
411
  if (!this.registered.get(payload.id)) return false;
399
412
  this.scene.PlaceOnFloor(payload);
@@ -601,7 +614,6 @@ var DIVELightRoot = class extends Object3D4 {
601
614
  if (light.visible !== void 0 && light.visible !== null) sceneObject.visible = light.visible;
602
615
  }
603
616
  DeleteLight(light) {
604
- var _a;
605
617
  if (light.id === void 0) {
606
618
  console.warn("LightRoot.DeleteLight: light.id is undefined");
607
619
  return;
@@ -611,9 +623,19 @@ var DIVELightRoot = class extends Object3D4 {
611
623
  console.warn(`LightRoot.DeleteLight: Light with id ${light.id} not found`);
612
624
  return;
613
625
  }
614
- if ("isMoveable" in sceneObject) {
615
- (_a = sceneObject.gizmo) == null ? void 0 : _a.detach();
616
- }
626
+ const findScene = (object) => {
627
+ if (object.parent !== null) {
628
+ return findScene(object.parent);
629
+ }
630
+ ;
631
+ return object;
632
+ };
633
+ const scene = findScene(sceneObject);
634
+ scene.children.find((object) => {
635
+ if ("isTransformControls" in object) {
636
+ object.detach();
637
+ }
638
+ });
617
639
  this.remove(sceneObject);
618
640
  }
619
641
  };
@@ -622,7 +644,17 @@ var DIVELightRoot = class extends Object3D4 {
622
644
  import { Object3D as Object3D6 } from "three";
623
645
 
624
646
  // src/model/Model.ts
625
- import { Box3, Object3D as Object3D5, Vector3 } from "three";
647
+ import { Box3, Object3D as Object3D5, Raycaster, Vector3 } from "three";
648
+
649
+ // src/helper/findSceneRecursive/findSceneRecursive.ts
650
+ var findSceneRecursive = (object) => {
651
+ if (object.parent) {
652
+ return findSceneRecursive(object.parent);
653
+ }
654
+ return object;
655
+ };
656
+
657
+ // src/model/Model.ts
626
658
  var DIVEModel = class extends Object3D5 {
627
659
  constructor() {
628
660
  super();
@@ -661,6 +693,30 @@ var DIVEModel = class extends Object3D5 {
661
693
  this.position.y = -this.boundingBox.min.y * this.scale.y;
662
694
  (_a = DIVECommunication.get(this.userData.id)) == null ? void 0 : _a.PerformAction("UPDATE_OBJECT", { id: this.userData.id, position: this.position, rotation: this.rotation, scale: this.scale });
663
695
  }
696
+ DropIt() {
697
+ var _a;
698
+ if (!this.parent) {
699
+ console.warn("DIVEModel: DropIt() called on a model that is not in the scene.", this);
700
+ return;
701
+ }
702
+ const bottomY = this.boundingBox.min.y * this.scale.y;
703
+ const bbBottomCenter = this.localToWorld(this.boundingBox.getCenter(new Vector3()).multiply(this.scale));
704
+ bbBottomCenter.y = bottomY + this.position.y;
705
+ const raycaster = new Raycaster(bbBottomCenter, new Vector3(0, -1, 0));
706
+ raycaster.layers.mask = PRODUCT_LAYER_MASK;
707
+ const intersections = raycaster.intersectObjects(findSceneRecursive(this).Root.children, true);
708
+ if (intersections.length > 0) {
709
+ const mesh = intersections[0].object;
710
+ mesh.geometry.computeBoundingBox();
711
+ const meshBB = mesh.geometry.boundingBox;
712
+ const worldPos = mesh.localToWorld(meshBB.max.clone());
713
+ const oldPos = this.position.clone();
714
+ const newPos = this.position.clone().setY(worldPos.y).add(new Vector3(0, bottomY, 0));
715
+ this.position.copy(newPos);
716
+ if (this.position.y === oldPos.y) return;
717
+ (_a = DIVECommunication.get(this.userData.id)) == null ? void 0 : _a.PerformAction("UPDATE_OBJECT", { id: this.userData.id, position: this.position, rotation: this.rotation, scale: this.scale });
718
+ }
719
+ }
664
720
  onMove() {
665
721
  var _a;
666
722
  (_a = DIVECommunication.get(this.userData.id)) == null ? void 0 : _a.PerformAction("UPDATE_OBJECT", { id: this.userData.id, position: this.position, rotation: this.rotation, scale: this.scale });
@@ -736,7 +792,6 @@ var DIVEModelRoot = class extends Object3D6 {
736
792
  if (object.visible !== void 0) sceneObject.visible = object.visible;
737
793
  }
738
794
  DeleteModel(object) {
739
- var _a;
740
795
  if (object.id === void 0) {
741
796
  console.warn(`ModelRoot.DeleteModel: object.id is undefined`);
742
797
  return;
@@ -746,9 +801,19 @@ var DIVEModelRoot = class extends Object3D6 {
746
801
  console.warn(`ModelRoot.DeleteModel: Model with id ${object.id} not found`);
747
802
  return;
748
803
  }
749
- if ("isMoveable" in sceneObject) {
750
- (_a = sceneObject.gizmo) == null ? void 0 : _a.detach();
751
- }
804
+ const findScene = (object2) => {
805
+ if (object2.parent !== null) {
806
+ return findScene(object2.parent);
807
+ }
808
+ ;
809
+ return object2;
810
+ };
811
+ const scene = findScene(sceneObject);
812
+ scene.children.find((object2) => {
813
+ if ("isTransformControls" in object2) {
814
+ object2.detach();
815
+ }
816
+ });
752
817
  this.remove(sceneObject);
753
818
  }
754
819
  PlaceOnFloor(object) {
@@ -1061,84 +1126,282 @@ var DIVEMediaCreator = class {
1061
1126
  }
1062
1127
  };
1063
1128
 
1064
- // src/toolbox/select/SelectTool.ts
1065
- import { Raycaster, Vector2 } from "three";
1066
- import { TransformControls } from "three/examples/jsm/Addons.js";
1129
+ // src/interface/Selectable.ts
1130
+ function isSelectable(object) {
1131
+ return "isSelectable" in object;
1132
+ }
1133
+
1134
+ // src/toolbox/BaseTool.ts
1135
+ import { Raycaster as Raycaster2, Vector2, Vector3 as Vector32 } from "three";
1136
+
1137
+ // src/interface/Draggable.ts
1138
+ var isDraggable = (object) => {
1139
+ return "isDraggable" in object;
1140
+ };
1141
+ var findDraggableInterface = (child) => {
1142
+ if (child === void 0) return void 0;
1143
+ if (child.parent === null) {
1144
+ return void 0;
1145
+ }
1146
+ if (isDraggable(child)) {
1147
+ return child;
1148
+ }
1149
+ return findDraggableInterface(child.parent);
1150
+ };
1151
+
1152
+ // src/interface/Hoverable.ts
1153
+ var isHoverable = (object) => {
1154
+ return "isHoverable" in object;
1155
+ };
1156
+ var findHoverableInterface = (child) => {
1157
+ if (child === void 0) return void 0;
1158
+ if (child.parent === null) {
1159
+ return void 0;
1160
+ }
1161
+ if (isHoverable(child)) {
1162
+ return child;
1163
+ }
1164
+ return findHoverableInterface(child.parent);
1165
+ };
1067
1166
 
1068
1167
  // src/toolbox/BaseTool.ts
1069
1168
  var DIVEBaseTool = class {
1070
- constructor() {
1169
+ constructor(scene, controller) {
1170
+ this.POINTER_DRAG_THRESHOLD = 1e-3;
1071
1171
  this.name = "BaseTool";
1172
+ this._canvas = controller.domElement;
1173
+ this._scene = scene;
1174
+ this._controller = controller;
1175
+ this._pointer = new Vector2();
1176
+ this._pointerPrimaryDown = false;
1177
+ this._pointerMiddleDown = false;
1178
+ this._pointerSecondaryDown = false;
1179
+ this._lastPointerDown = new Vector2();
1180
+ this._lastPointerUp = new Vector2();
1181
+ this._raycaster = new Raycaster2();
1182
+ this._raycaster.layers.mask = PRODUCT_LAYER_MASK | UI_LAYER_MASK;
1183
+ this._intersects = [];
1184
+ this._hovered = null;
1185
+ this._dragging = false;
1186
+ this._dragStart = new Vector32();
1187
+ this._dragCurrent = new Vector32();
1188
+ this._dragEnd = new Vector32();
1189
+ this._dragDelta = new Vector32();
1190
+ this._draggable = null;
1191
+ this._dragRaycastOnObjects = null;
1192
+ }
1193
+ get _pointerAnyDown() {
1194
+ return this._pointerPrimaryDown || this._pointerMiddleDown || this._pointerSecondaryDown;
1072
1195
  }
1073
1196
  Activate() {
1074
1197
  }
1075
1198
  Deactivate() {
1076
1199
  }
1077
1200
  onPointerDown(e) {
1201
+ var _a;
1202
+ switch (e.button) {
1203
+ case 0:
1204
+ this._pointerPrimaryDown = true;
1205
+ break;
1206
+ case 1:
1207
+ this._pointerMiddleDown = true;
1208
+ break;
1209
+ case 2:
1210
+ this._pointerSecondaryDown = true;
1211
+ break;
1212
+ }
1213
+ this._lastPointerDown.copy(this._pointer);
1214
+ this._draggable = findDraggableInterface((_a = this._intersects[0]) == null ? void 0 : _a.object) || null;
1215
+ }
1216
+ onDragStart(e) {
1217
+ if (!this._draggable) return;
1218
+ if (this._dragRaycastOnObjects !== null) {
1219
+ this._intersects = this._raycaster.intersectObjects(this._dragRaycastOnObjects, true);
1220
+ }
1221
+ if (this._intersects.length === 0) return;
1222
+ this._dragStart.copy(this._intersects[0].point.clone());
1223
+ this._dragCurrent.copy(this._intersects[0].point.clone());
1224
+ this._dragEnd.copy(this._dragStart.clone());
1225
+ this._dragDelta.set(0, 0, 0);
1226
+ if (this._draggable && this._draggable.onDragStart) {
1227
+ this._draggable.onDragStart({
1228
+ dragStart: this._dragStart,
1229
+ dragCurrent: this._dragCurrent,
1230
+ dragEnd: this._dragEnd,
1231
+ dragDelta: this._dragDelta
1232
+ });
1233
+ this._dragging = true;
1234
+ this._controller.enabled = false;
1235
+ }
1236
+ }
1237
+ onPointerMove(e) {
1238
+ var _a;
1239
+ this._pointer.x = e.offsetX / this._canvas.clientWidth * 2 - 1;
1240
+ this._pointer.y = -(e.offsetY / this._canvas.clientHeight) * 2 + 1;
1241
+ this._raycaster.setFromCamera(this._pointer, this._controller.object);
1242
+ this._intersects = this.raycast(this._scene.children);
1243
+ const hoverable = findHoverableInterface((_a = this._intersects[0]) == null ? void 0 : _a.object);
1244
+ if (this._intersects[0] && hoverable) {
1245
+ if (!this._hovered) {
1246
+ if (hoverable.onPointerEnter) hoverable.onPointerEnter(this._intersects[0]);
1247
+ this._hovered = hoverable;
1248
+ return;
1249
+ }
1250
+ if (this._hovered.uuid !== hoverable.uuid) {
1251
+ if (this._hovered.onPointerLeave) this._hovered.onPointerLeave();
1252
+ if (hoverable.onPointerEnter) hoverable.onPointerEnter(this._intersects[0]);
1253
+ this._hovered = hoverable;
1254
+ return;
1255
+ }
1256
+ if (hoverable.onPointerOver) hoverable.onPointerOver(this._intersects[0]);
1257
+ this._hovered = hoverable;
1258
+ } else {
1259
+ if (this._hovered) {
1260
+ if (this._hovered.onPointerLeave) this._hovered.onPointerLeave();
1261
+ }
1262
+ this._hovered = null;
1263
+ }
1264
+ if (this._pointerAnyDown) {
1265
+ if (!this._dragging) {
1266
+ this.onDragStart(e);
1267
+ }
1268
+ this.onDrag(e);
1269
+ }
1270
+ }
1271
+ onDrag(e) {
1272
+ if (this._dragRaycastOnObjects !== null) {
1273
+ this._intersects = this._raycaster.intersectObjects(this._dragRaycastOnObjects, true);
1274
+ }
1275
+ const intersect = this._intersects[0];
1276
+ if (!intersect) return;
1277
+ this._dragCurrent.copy(intersect.point.clone());
1278
+ this._dragEnd.copy(intersect.point.clone());
1279
+ this._dragDelta.subVectors(this._dragCurrent.clone(), this._dragStart.clone());
1280
+ if (this._draggable && this._draggable.onDrag) {
1281
+ this._draggable.onDrag({
1282
+ dragStart: this._dragStart,
1283
+ dragCurrent: this._dragCurrent,
1284
+ dragEnd: this._dragEnd,
1285
+ dragDelta: this._dragDelta
1286
+ });
1287
+ }
1078
1288
  }
1079
1289
  onPointerUp(e) {
1290
+ if (this.pointerWasDragged() || this._dragging) {
1291
+ if (this._draggable) {
1292
+ this.onDragEnd(e);
1293
+ }
1294
+ } else {
1295
+ this.onClick(e);
1296
+ }
1297
+ switch (e.button) {
1298
+ case 0:
1299
+ this._pointerPrimaryDown = false;
1300
+ break;
1301
+ case 1:
1302
+ this._pointerMiddleDown = false;
1303
+ break;
1304
+ case 2:
1305
+ this._pointerSecondaryDown = false;
1306
+ break;
1307
+ }
1308
+ this._lastPointerUp.copy(this._pointer);
1309
+ }
1310
+ onClick(e) {
1311
+ }
1312
+ onDragEnd(e) {
1313
+ const intersect = this._intersects[0];
1314
+ if (intersect) {
1315
+ this._dragEnd.copy(intersect.point.clone());
1316
+ this._dragCurrent.copy(intersect.point.clone());
1317
+ this._dragDelta.subVectors(this._dragCurrent.clone(), this._dragStart.clone());
1318
+ }
1319
+ if (this._draggable && this._draggable.onDragEnd) {
1320
+ this._draggable.onDragEnd({
1321
+ dragStart: this._dragStart,
1322
+ dragCurrent: this._dragCurrent,
1323
+ dragEnd: this._dragEnd,
1324
+ dragDelta: this._dragDelta
1325
+ });
1326
+ }
1327
+ this._draggable = null;
1328
+ this._dragging = false;
1329
+ this._dragStart.set(0, 0, 0);
1330
+ this._dragCurrent.set(0, 0, 0);
1331
+ this._dragEnd.set(0, 0, 0);
1332
+ this._dragDelta.set(0, 0, 0);
1333
+ this._controller.enabled = true;
1080
1334
  }
1081
1335
  onWheel(e) {
1082
1336
  }
1337
+ raycast(objects) {
1338
+ if (objects !== void 0) return this._raycaster.intersectObjects(objects, true);
1339
+ return this._raycaster.intersectObjects(this._scene.children, true);
1340
+ }
1341
+ pointerWasDragged() {
1342
+ return this._lastPointerDown.distanceTo(this._pointer) > this.POINTER_DRAG_THRESHOLD;
1343
+ }
1083
1344
  };
1084
1345
 
1085
- // src/toolbox/select/SelectTool.ts
1086
- var DIVESelectTool = class extends DIVEBaseTool {
1346
+ // src/toolbox/transform/TransformTool.ts
1347
+ import { TransformControls } from "three/examples/jsm/Addons";
1348
+ var DIVETransformTool = class extends DIVEBaseTool {
1087
1349
  constructor(scene, controller) {
1088
- super();
1089
- this.name = "SelectTool";
1090
- this.canvas = controller.domElement;
1091
- this.scene = scene;
1092
- this.controller = controller;
1093
- this.raycaster = new Raycaster();
1094
- this.raycaster.layers.mask = PRODUCT_LAYER_MASK | HELPER_LAYER_MASK;
1095
- this.gizmo = new TransformControls(this.controller.object, this.canvas);
1096
- this.gizmo.layers.mask = UI_LAYER_MASK;
1097
- this.gizmo.getRaycaster().layers.mask = UI_LAYER_MASK & this.controller.object.layers.mask;
1098
- this.gizmo.traverse((child) => {
1099
- child.layers.mask = UI_LAYER_MASK;
1100
- });
1101
- this.gizmo.addEventListener("objectChange", () => {
1102
- if (!this.gizmo.object) return;
1103
- if (!("onMove" in this.gizmo.object)) return;
1104
- if (typeof this.gizmo.object.onMove !== "function") return;
1105
- this.gizmo.object.onMove();
1106
- });
1107
- this.controller.object.onSetCameraLayer = (mask) => {
1108
- this.gizmo.getRaycaster().layers.mask = UI_LAYER_MASK & mask;
1109
- };
1110
- this.gizmo.addEventListener("dragging-changed", function(event) {
1111
- controller.enabled = !event.value;
1112
- });
1113
- this.scene.add(this.gizmo);
1350
+ super(scene, controller);
1351
+ this.name = "DIVETransformTool";
1352
+ this._gizmo = new TransformControls(this._controller.object, this._controller.domElement);
1353
+ this._gizmo.mode = "translate";
1354
+ scene.add(this._gizmo);
1114
1355
  }
1115
1356
  Activate() {
1116
1357
  }
1117
1358
  SetGizmoMode(mode) {
1118
- this.gizmo.setMode(mode);
1359
+ this._gizmo.mode = mode;
1360
+ }
1361
+ // public onPointerDown(e: PointerEvent): void {
1362
+ // super.onPointerDown(e);
1363
+ // // if (this._hovered) {
1364
+ // // this._dragRaycastOnObjects = this._gizmo.gizmoPlane.children;
1365
+ // // }
1366
+ // }
1367
+ // protected raycast(): Intersection[] {
1368
+ // return super.raycast(this._gizmo.gizmoNode.children);
1369
+ // }
1370
+ };
1371
+
1372
+ // src/toolbox/select/SelectTool.ts
1373
+ var DIVESelectTool = class extends DIVETransformTool {
1374
+ constructor(scene, controller) {
1375
+ super(scene, controller);
1376
+ this.name = "SelectTool";
1377
+ }
1378
+ Activate() {
1119
1379
  }
1120
1380
  Select(selectable) {
1121
1381
  if (selectable.onSelect) selectable.onSelect();
1122
1382
  if ("isMoveable" in selectable) {
1123
1383
  const movable = selectable;
1124
- movable.gizmo = this.gizmo;
1125
- this.gizmo.attach(movable);
1384
+ this._gizmo.attach(movable);
1126
1385
  }
1127
1386
  }
1128
1387
  Deselect(selectable) {
1129
1388
  if (selectable.onDeselect) selectable.onDeselect();
1130
- if ("isMoveable" in selectable) selectable.gizmo = null;
1131
- this.gizmo.detach();
1389
+ this._gizmo.detach();
1132
1390
  }
1133
- onPointerUp(e) {
1134
- const pointerPos = new Vector2(e.offsetX / this.canvas.clientWidth * 2 - 1, e.offsetY / this.canvas.clientHeight * -2 + 1);
1135
- this.raycaster.setFromCamera(pointerPos, this.controller.object);
1136
- const first = this.raycastFirst();
1391
+ onClick(e) {
1392
+ super.onClick(e);
1393
+ const first = this._raycaster.intersectObjects(this._scene.Root.children, true)[0];
1137
1394
  const selectable = this.findSelectableInterface(first == null ? void 0 : first.object);
1138
1395
  if (!first || !selectable) {
1139
- if (this.gizmo.object) this.Deselect(this.gizmo.object);
1396
+ if (this._gizmo.object) {
1397
+ this.Deselect(this._gizmo.object);
1398
+ }
1140
1399
  return;
1141
1400
  }
1401
+ if (this._gizmo.object) {
1402
+ if (this._gizmo.object.uuid === selectable.uuid) return;
1403
+ this.Deselect(this._gizmo.object);
1404
+ }
1142
1405
  this.Select(selectable);
1143
1406
  }
1144
1407
  findSelectableInterface(child) {
@@ -1146,17 +1409,11 @@ var DIVESelectTool = class extends DIVEBaseTool {
1146
1409
  if (child.parent === null) {
1147
1410
  return void 0;
1148
1411
  }
1149
- if ("isSelectable" in child) {
1412
+ if (isSelectable(child)) {
1150
1413
  return child;
1151
1414
  }
1152
1415
  return this.findSelectableInterface(child.parent);
1153
1416
  }
1154
- raycastFirst() {
1155
- return this.raycastAll()[0];
1156
- }
1157
- raycastAll() {
1158
- return this.raycaster.intersectObjects(this.scene.Root.children, true);
1159
- }
1160
1417
  };
1161
1418
 
1162
1419
  // src/toolbox/Toolbox.ts
@@ -1165,10 +1422,12 @@ var DIVEToolbox = class {
1165
1422
  this.removeListenersCallback = () => {
1166
1423
  };
1167
1424
  this.selectTool = new DIVESelectTool(scene, controller);
1425
+ controller.domElement.addEventListener("pointermove", this.onPointerMove.bind(this));
1168
1426
  controller.domElement.addEventListener("pointerdown", this.onPointerDown.bind(this));
1169
1427
  controller.domElement.addEventListener("pointerup", this.onPointerUp.bind(this));
1170
1428
  controller.domElement.addEventListener("wheel", this.onWheel.bind(this));
1171
1429
  this.removeListenersCallback = () => {
1430
+ controller.domElement.removeEventListener("pointermove", this.onPointerMove.bind(this));
1172
1431
  controller.domElement.removeEventListener("pointerdown", this.onPointerDown.bind(this));
1173
1432
  controller.domElement.removeEventListener("pointerup", this.onPointerUp.bind(this));
1174
1433
  controller.domElement.removeEventListener("wheel", this.onWheel.bind(this));
@@ -1198,6 +1457,9 @@ var DIVEToolbox = class {
1198
1457
  SetGizmoMode(mode) {
1199
1458
  this.selectTool.SetGizmoMode(mode);
1200
1459
  }
1460
+ onPointerMove(e) {
1461
+ this.activeTool.onPointerMove(e);
1462
+ }
1201
1463
  onPointerDown(e) {
1202
1464
  this.activeTool.onPointerDown(e);
1203
1465
  }
@@ -1348,6 +1610,11 @@ function roundExponential(number, decimals = 0) {
1348
1610
  return shift(Math.round(n), -decimals);
1349
1611
  }
1350
1612
 
1613
+ // src/math/signedAngleTo/signedAngleTo.ts
1614
+ function signedAngleTo(vecA, vecB, planeNormal) {
1615
+ return Math.atan2(vecA.clone().cross(vecB).dot(planeNormal), vecB.clone().dot(vecA));
1616
+ }
1617
+
1351
1618
  // src/math/toFixed/toFixedExp.ts
1352
1619
  function toFixedExp(number, decimals = 0) {
1353
1620
  const n = shift(number, +decimals);
@@ -1366,7 +1633,8 @@ var DIVEMath = {
1366
1633
  floorExp,
1367
1634
  roundExp: roundExponential,
1368
1635
  toFixedExp,
1369
- truncateExp
1636
+ truncateExp,
1637
+ signedAngleTo
1370
1638
  };
1371
1639
 
1372
1640
  // src/dive.ts
@@ -1442,6 +1710,11 @@ var DIVE = class {
1442
1710
  this.addResizeObserver();
1443
1711
  }
1444
1712
  this.renderer.StartRenderer(this.scene, this.perspectiveCamera);
1713
+ window.DIVE = {
1714
+ PrintScene: () => {
1715
+ console.log(this.scene);
1716
+ }
1717
+ };
1445
1718
  }
1446
1719
  // methods
1447
1720
  OnResize(width, height) {