@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/README.md CHANGED
@@ -172,6 +172,7 @@ In the following you find a list of all available actions to perform on DIVEComm
172
172
  | [ZOOM_CAMERA](./src/com/actions/camera/zoomcamera.ts) | Zoom in or out
173
173
  | [GENERATE_MEDIA](./src/com/actions/media/generatemedia.ts) | Generate a screenshot with the specified parameters
174
174
  | [MODEL_LOADED](./src/com/actions/object/model/modelloaded.ts) | Is performed when a model file is completely loaded
175
+ | [DROP_IT](./src/com/actions/object/model/dropit.ts) | Places the model onto the nextg underlying object's bounding box
175
176
  | [PLACE_ON_FLOOR](./src/com/actions/object/model/placeonfloor.ts) | Set a model onto to the floor
176
177
  | [ADD_OBJECT](./src/com/actions/object/addobject.ts) | Add an object to the scene
177
178
  | [UPDATE_OBJECT](./src/com/actions/object/updateobject.ts) | Update an existing object
package/build/dive.cjs CHANGED
@@ -296,6 +296,10 @@ var _DIVECommunication = class _DIVECommunication {
296
296
  returnValue = this.setBackground(payload);
297
297
  break;
298
298
  }
299
+ case "DROP_IT": {
300
+ returnValue = this.dropIt(payload);
301
+ break;
302
+ }
299
303
  case "PLACE_ON_FLOOR": {
300
304
  returnValue = this.placeOnFloor(payload);
301
305
  break;
@@ -385,11 +389,13 @@ var _DIVECommunication = class _DIVECommunication {
385
389
  return this.registered;
386
390
  }
387
391
  getObjects(payload) {
392
+ if (payload.ids.length === 0) return [];
393
+ const objects = [];
388
394
  this.registered.forEach((object) => {
389
- if (payload.ids && payload.ids.length > 0 && !payload.ids.includes(object.id)) return;
390
- payload.map.set(object.id, object);
395
+ if (!payload.ids.includes(object.id)) return;
396
+ objects.push(object);
391
397
  });
392
- return payload.map;
398
+ return objects;
393
399
  }
394
400
  addObject(payload) {
395
401
  if (this.registered.get(payload.id)) return false;
@@ -429,6 +435,13 @@ var _DIVECommunication = class _DIVECommunication {
429
435
  this.scene.SetBackground(payload.color);
430
436
  return true;
431
437
  }
438
+ dropIt(payload) {
439
+ const object = this.registered.get(payload.id);
440
+ if (!object) return false;
441
+ const model = this.scene.GetSceneObject(object);
442
+ model.DropIt();
443
+ return true;
444
+ }
432
445
  placeOnFloor(payload) {
433
446
  if (!this.registered.get(payload.id)) return false;
434
447
  this.scene.PlaceOnFloor(payload);
@@ -636,7 +649,6 @@ var DIVELightRoot = class extends import_three6.Object3D {
636
649
  if (light.visible !== void 0 && light.visible !== null) sceneObject.visible = light.visible;
637
650
  }
638
651
  DeleteLight(light) {
639
- var _a;
640
652
  if (light.id === void 0) {
641
653
  console.warn("LightRoot.DeleteLight: light.id is undefined");
642
654
  return;
@@ -646,9 +658,19 @@ var DIVELightRoot = class extends import_three6.Object3D {
646
658
  console.warn(`LightRoot.DeleteLight: Light with id ${light.id} not found`);
647
659
  return;
648
660
  }
649
- if ("isMoveable" in sceneObject) {
650
- (_a = sceneObject.gizmo) == null ? void 0 : _a.detach();
651
- }
661
+ const findScene = (object) => {
662
+ if (object.parent !== null) {
663
+ return findScene(object.parent);
664
+ }
665
+ ;
666
+ return object;
667
+ };
668
+ const scene = findScene(sceneObject);
669
+ scene.children.find((object) => {
670
+ if ("isTransformControls" in object) {
671
+ object.detach();
672
+ }
673
+ });
652
674
  this.remove(sceneObject);
653
675
  }
654
676
  };
@@ -658,6 +680,16 @@ var import_three8 = require("three");
658
680
 
659
681
  // src/model/Model.ts
660
682
  var import_three7 = require("three");
683
+
684
+ // src/helper/findSceneRecursive/findSceneRecursive.ts
685
+ var findSceneRecursive = (object) => {
686
+ if (object.parent) {
687
+ return findSceneRecursive(object.parent);
688
+ }
689
+ return object;
690
+ };
691
+
692
+ // src/model/Model.ts
661
693
  var DIVEModel = class extends import_three7.Object3D {
662
694
  constructor() {
663
695
  super();
@@ -696,6 +728,30 @@ var DIVEModel = class extends import_three7.Object3D {
696
728
  this.position.y = -this.boundingBox.min.y * this.scale.y;
697
729
  (_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 });
698
730
  }
731
+ DropIt() {
732
+ var _a;
733
+ if (!this.parent) {
734
+ console.warn("DIVEModel: DropIt() called on a model that is not in the scene.", this);
735
+ return;
736
+ }
737
+ const bottomY = this.boundingBox.min.y * this.scale.y;
738
+ const bbBottomCenter = this.localToWorld(this.boundingBox.getCenter(new import_three7.Vector3()).multiply(this.scale));
739
+ bbBottomCenter.y = bottomY + this.position.y;
740
+ const raycaster = new import_three7.Raycaster(bbBottomCenter, new import_three7.Vector3(0, -1, 0));
741
+ raycaster.layers.mask = PRODUCT_LAYER_MASK;
742
+ const intersections = raycaster.intersectObjects(findSceneRecursive(this).Root.children, true);
743
+ if (intersections.length > 0) {
744
+ const mesh = intersections[0].object;
745
+ mesh.geometry.computeBoundingBox();
746
+ const meshBB = mesh.geometry.boundingBox;
747
+ const worldPos = mesh.localToWorld(meshBB.max.clone());
748
+ const oldPos = this.position.clone();
749
+ const newPos = this.position.clone().setY(worldPos.y).add(new import_three7.Vector3(0, bottomY, 0));
750
+ this.position.copy(newPos);
751
+ if (this.position.y === oldPos.y) return;
752
+ (_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 });
753
+ }
754
+ }
699
755
  onMove() {
700
756
  var _a;
701
757
  (_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 });
@@ -771,7 +827,6 @@ var DIVEModelRoot = class extends import_three8.Object3D {
771
827
  if (object.visible !== void 0) sceneObject.visible = object.visible;
772
828
  }
773
829
  DeleteModel(object) {
774
- var _a;
775
830
  if (object.id === void 0) {
776
831
  console.warn(`ModelRoot.DeleteModel: object.id is undefined`);
777
832
  return;
@@ -781,9 +836,19 @@ var DIVEModelRoot = class extends import_three8.Object3D {
781
836
  console.warn(`ModelRoot.DeleteModel: Model with id ${object.id} not found`);
782
837
  return;
783
838
  }
784
- if ("isMoveable" in sceneObject) {
785
- (_a = sceneObject.gizmo) == null ? void 0 : _a.detach();
786
- }
839
+ const findScene = (object2) => {
840
+ if (object2.parent !== null) {
841
+ return findScene(object2.parent);
842
+ }
843
+ ;
844
+ return object2;
845
+ };
846
+ const scene = findScene(sceneObject);
847
+ scene.children.find((object2) => {
848
+ if ("isTransformControls" in object2) {
849
+ object2.detach();
850
+ }
851
+ });
787
852
  this.remove(sceneObject);
788
853
  }
789
854
  PlaceOnFloor(object) {
@@ -1096,84 +1161,282 @@ var DIVEMediaCreator = class {
1096
1161
  }
1097
1162
  };
1098
1163
 
1099
- // src/toolbox/select/SelectTool.ts
1164
+ // src/interface/Selectable.ts
1165
+ function isSelectable(object) {
1166
+ return "isSelectable" in object;
1167
+ }
1168
+
1169
+ // src/toolbox/BaseTool.ts
1100
1170
  var import_three15 = require("three");
1101
- var import_Addons3 = require("three/examples/jsm/Addons.js");
1171
+
1172
+ // src/interface/Draggable.ts
1173
+ var isDraggable = (object) => {
1174
+ return "isDraggable" in object;
1175
+ };
1176
+ var findDraggableInterface = (child) => {
1177
+ if (child === void 0) return void 0;
1178
+ if (child.parent === null) {
1179
+ return void 0;
1180
+ }
1181
+ if (isDraggable(child)) {
1182
+ return child;
1183
+ }
1184
+ return findDraggableInterface(child.parent);
1185
+ };
1186
+
1187
+ // src/interface/Hoverable.ts
1188
+ var isHoverable = (object) => {
1189
+ return "isHoverable" in object;
1190
+ };
1191
+ var findHoverableInterface = (child) => {
1192
+ if (child === void 0) return void 0;
1193
+ if (child.parent === null) {
1194
+ return void 0;
1195
+ }
1196
+ if (isHoverable(child)) {
1197
+ return child;
1198
+ }
1199
+ return findHoverableInterface(child.parent);
1200
+ };
1102
1201
 
1103
1202
  // src/toolbox/BaseTool.ts
1104
1203
  var DIVEBaseTool = class {
1105
- constructor() {
1204
+ constructor(scene, controller) {
1205
+ this.POINTER_DRAG_THRESHOLD = 1e-3;
1106
1206
  this.name = "BaseTool";
1207
+ this._canvas = controller.domElement;
1208
+ this._scene = scene;
1209
+ this._controller = controller;
1210
+ this._pointer = new import_three15.Vector2();
1211
+ this._pointerPrimaryDown = false;
1212
+ this._pointerMiddleDown = false;
1213
+ this._pointerSecondaryDown = false;
1214
+ this._lastPointerDown = new import_three15.Vector2();
1215
+ this._lastPointerUp = new import_three15.Vector2();
1216
+ this._raycaster = new import_three15.Raycaster();
1217
+ this._raycaster.layers.mask = PRODUCT_LAYER_MASK | UI_LAYER_MASK;
1218
+ this._intersects = [];
1219
+ this._hovered = null;
1220
+ this._dragging = false;
1221
+ this._dragStart = new import_three15.Vector3();
1222
+ this._dragCurrent = new import_three15.Vector3();
1223
+ this._dragEnd = new import_three15.Vector3();
1224
+ this._dragDelta = new import_three15.Vector3();
1225
+ this._draggable = null;
1226
+ this._dragRaycastOnObjects = null;
1227
+ }
1228
+ get _pointerAnyDown() {
1229
+ return this._pointerPrimaryDown || this._pointerMiddleDown || this._pointerSecondaryDown;
1107
1230
  }
1108
1231
  Activate() {
1109
1232
  }
1110
1233
  Deactivate() {
1111
1234
  }
1112
1235
  onPointerDown(e) {
1236
+ var _a;
1237
+ switch (e.button) {
1238
+ case 0:
1239
+ this._pointerPrimaryDown = true;
1240
+ break;
1241
+ case 1:
1242
+ this._pointerMiddleDown = true;
1243
+ break;
1244
+ case 2:
1245
+ this._pointerSecondaryDown = true;
1246
+ break;
1247
+ }
1248
+ this._lastPointerDown.copy(this._pointer);
1249
+ this._draggable = findDraggableInterface((_a = this._intersects[0]) == null ? void 0 : _a.object) || null;
1250
+ }
1251
+ onDragStart(e) {
1252
+ if (!this._draggable) return;
1253
+ if (this._dragRaycastOnObjects !== null) {
1254
+ this._intersects = this._raycaster.intersectObjects(this._dragRaycastOnObjects, true);
1255
+ }
1256
+ if (this._intersects.length === 0) return;
1257
+ this._dragStart.copy(this._intersects[0].point.clone());
1258
+ this._dragCurrent.copy(this._intersects[0].point.clone());
1259
+ this._dragEnd.copy(this._dragStart.clone());
1260
+ this._dragDelta.set(0, 0, 0);
1261
+ if (this._draggable && this._draggable.onDragStart) {
1262
+ this._draggable.onDragStart({
1263
+ dragStart: this._dragStart,
1264
+ dragCurrent: this._dragCurrent,
1265
+ dragEnd: this._dragEnd,
1266
+ dragDelta: this._dragDelta
1267
+ });
1268
+ this._dragging = true;
1269
+ this._controller.enabled = false;
1270
+ }
1271
+ }
1272
+ onPointerMove(e) {
1273
+ var _a;
1274
+ this._pointer.x = e.offsetX / this._canvas.clientWidth * 2 - 1;
1275
+ this._pointer.y = -(e.offsetY / this._canvas.clientHeight) * 2 + 1;
1276
+ this._raycaster.setFromCamera(this._pointer, this._controller.object);
1277
+ this._intersects = this.raycast(this._scene.children);
1278
+ const hoverable = findHoverableInterface((_a = this._intersects[0]) == null ? void 0 : _a.object);
1279
+ if (this._intersects[0] && hoverable) {
1280
+ if (!this._hovered) {
1281
+ if (hoverable.onPointerEnter) hoverable.onPointerEnter(this._intersects[0]);
1282
+ this._hovered = hoverable;
1283
+ return;
1284
+ }
1285
+ if (this._hovered.uuid !== hoverable.uuid) {
1286
+ if (this._hovered.onPointerLeave) this._hovered.onPointerLeave();
1287
+ if (hoverable.onPointerEnter) hoverable.onPointerEnter(this._intersects[0]);
1288
+ this._hovered = hoverable;
1289
+ return;
1290
+ }
1291
+ if (hoverable.onPointerOver) hoverable.onPointerOver(this._intersects[0]);
1292
+ this._hovered = hoverable;
1293
+ } else {
1294
+ if (this._hovered) {
1295
+ if (this._hovered.onPointerLeave) this._hovered.onPointerLeave();
1296
+ }
1297
+ this._hovered = null;
1298
+ }
1299
+ if (this._pointerAnyDown) {
1300
+ if (!this._dragging) {
1301
+ this.onDragStart(e);
1302
+ }
1303
+ this.onDrag(e);
1304
+ }
1305
+ }
1306
+ onDrag(e) {
1307
+ if (this._dragRaycastOnObjects !== null) {
1308
+ this._intersects = this._raycaster.intersectObjects(this._dragRaycastOnObjects, true);
1309
+ }
1310
+ const intersect = this._intersects[0];
1311
+ if (!intersect) return;
1312
+ this._dragCurrent.copy(intersect.point.clone());
1313
+ this._dragEnd.copy(intersect.point.clone());
1314
+ this._dragDelta.subVectors(this._dragCurrent.clone(), this._dragStart.clone());
1315
+ if (this._draggable && this._draggable.onDrag) {
1316
+ this._draggable.onDrag({
1317
+ dragStart: this._dragStart,
1318
+ dragCurrent: this._dragCurrent,
1319
+ dragEnd: this._dragEnd,
1320
+ dragDelta: this._dragDelta
1321
+ });
1322
+ }
1113
1323
  }
1114
1324
  onPointerUp(e) {
1325
+ if (this.pointerWasDragged() || this._dragging) {
1326
+ if (this._draggable) {
1327
+ this.onDragEnd(e);
1328
+ }
1329
+ } else {
1330
+ this.onClick(e);
1331
+ }
1332
+ switch (e.button) {
1333
+ case 0:
1334
+ this._pointerPrimaryDown = false;
1335
+ break;
1336
+ case 1:
1337
+ this._pointerMiddleDown = false;
1338
+ break;
1339
+ case 2:
1340
+ this._pointerSecondaryDown = false;
1341
+ break;
1342
+ }
1343
+ this._lastPointerUp.copy(this._pointer);
1344
+ }
1345
+ onClick(e) {
1346
+ }
1347
+ onDragEnd(e) {
1348
+ const intersect = this._intersects[0];
1349
+ if (intersect) {
1350
+ this._dragEnd.copy(intersect.point.clone());
1351
+ this._dragCurrent.copy(intersect.point.clone());
1352
+ this._dragDelta.subVectors(this._dragCurrent.clone(), this._dragStart.clone());
1353
+ }
1354
+ if (this._draggable && this._draggable.onDragEnd) {
1355
+ this._draggable.onDragEnd({
1356
+ dragStart: this._dragStart,
1357
+ dragCurrent: this._dragCurrent,
1358
+ dragEnd: this._dragEnd,
1359
+ dragDelta: this._dragDelta
1360
+ });
1361
+ }
1362
+ this._draggable = null;
1363
+ this._dragging = false;
1364
+ this._dragStart.set(0, 0, 0);
1365
+ this._dragCurrent.set(0, 0, 0);
1366
+ this._dragEnd.set(0, 0, 0);
1367
+ this._dragDelta.set(0, 0, 0);
1368
+ this._controller.enabled = true;
1115
1369
  }
1116
1370
  onWheel(e) {
1117
1371
  }
1372
+ raycast(objects) {
1373
+ if (objects !== void 0) return this._raycaster.intersectObjects(objects, true);
1374
+ return this._raycaster.intersectObjects(this._scene.children, true);
1375
+ }
1376
+ pointerWasDragged() {
1377
+ return this._lastPointerDown.distanceTo(this._pointer) > this.POINTER_DRAG_THRESHOLD;
1378
+ }
1118
1379
  };
1119
1380
 
1120
- // src/toolbox/select/SelectTool.ts
1121
- var DIVESelectTool = class extends DIVEBaseTool {
1381
+ // src/toolbox/transform/TransformTool.ts
1382
+ var import_Addons3 = require("three/examples/jsm/Addons");
1383
+ var DIVETransformTool = class extends DIVEBaseTool {
1122
1384
  constructor(scene, controller) {
1123
- super();
1124
- this.name = "SelectTool";
1125
- this.canvas = controller.domElement;
1126
- this.scene = scene;
1127
- this.controller = controller;
1128
- this.raycaster = new import_three15.Raycaster();
1129
- this.raycaster.layers.mask = PRODUCT_LAYER_MASK | HELPER_LAYER_MASK;
1130
- this.gizmo = new import_Addons3.TransformControls(this.controller.object, this.canvas);
1131
- this.gizmo.layers.mask = UI_LAYER_MASK;
1132
- this.gizmo.getRaycaster().layers.mask = UI_LAYER_MASK & this.controller.object.layers.mask;
1133
- this.gizmo.traverse((child) => {
1134
- child.layers.mask = UI_LAYER_MASK;
1135
- });
1136
- this.gizmo.addEventListener("objectChange", () => {
1137
- if (!this.gizmo.object) return;
1138
- if (!("onMove" in this.gizmo.object)) return;
1139
- if (typeof this.gizmo.object.onMove !== "function") return;
1140
- this.gizmo.object.onMove();
1141
- });
1142
- this.controller.object.onSetCameraLayer = (mask) => {
1143
- this.gizmo.getRaycaster().layers.mask = UI_LAYER_MASK & mask;
1144
- };
1145
- this.gizmo.addEventListener("dragging-changed", function(event) {
1146
- controller.enabled = !event.value;
1147
- });
1148
- this.scene.add(this.gizmo);
1385
+ super(scene, controller);
1386
+ this.name = "DIVETransformTool";
1387
+ this._gizmo = new import_Addons3.TransformControls(this._controller.object, this._controller.domElement);
1388
+ this._gizmo.mode = "translate";
1389
+ scene.add(this._gizmo);
1149
1390
  }
1150
1391
  Activate() {
1151
1392
  }
1152
1393
  SetGizmoMode(mode) {
1153
- this.gizmo.setMode(mode);
1394
+ this._gizmo.mode = mode;
1395
+ }
1396
+ // public onPointerDown(e: PointerEvent): void {
1397
+ // super.onPointerDown(e);
1398
+ // // if (this._hovered) {
1399
+ // // this._dragRaycastOnObjects = this._gizmo.gizmoPlane.children;
1400
+ // // }
1401
+ // }
1402
+ // protected raycast(): Intersection[] {
1403
+ // return super.raycast(this._gizmo.gizmoNode.children);
1404
+ // }
1405
+ };
1406
+
1407
+ // src/toolbox/select/SelectTool.ts
1408
+ var DIVESelectTool = class extends DIVETransformTool {
1409
+ constructor(scene, controller) {
1410
+ super(scene, controller);
1411
+ this.name = "SelectTool";
1412
+ }
1413
+ Activate() {
1154
1414
  }
1155
1415
  Select(selectable) {
1156
1416
  if (selectable.onSelect) selectable.onSelect();
1157
1417
  if ("isMoveable" in selectable) {
1158
1418
  const movable = selectable;
1159
- movable.gizmo = this.gizmo;
1160
- this.gizmo.attach(movable);
1419
+ this._gizmo.attach(movable);
1161
1420
  }
1162
1421
  }
1163
1422
  Deselect(selectable) {
1164
1423
  if (selectable.onDeselect) selectable.onDeselect();
1165
- if ("isMoveable" in selectable) selectable.gizmo = null;
1166
- this.gizmo.detach();
1424
+ this._gizmo.detach();
1167
1425
  }
1168
- onPointerUp(e) {
1169
- const pointerPos = new import_three15.Vector2(e.offsetX / this.canvas.clientWidth * 2 - 1, e.offsetY / this.canvas.clientHeight * -2 + 1);
1170
- this.raycaster.setFromCamera(pointerPos, this.controller.object);
1171
- const first = this.raycastFirst();
1426
+ onClick(e) {
1427
+ super.onClick(e);
1428
+ const first = this._raycaster.intersectObjects(this._scene.Root.children, true)[0];
1172
1429
  const selectable = this.findSelectableInterface(first == null ? void 0 : first.object);
1173
1430
  if (!first || !selectable) {
1174
- if (this.gizmo.object) this.Deselect(this.gizmo.object);
1431
+ if (this._gizmo.object) {
1432
+ this.Deselect(this._gizmo.object);
1433
+ }
1175
1434
  return;
1176
1435
  }
1436
+ if (this._gizmo.object) {
1437
+ if (this._gizmo.object.uuid === selectable.uuid) return;
1438
+ this.Deselect(this._gizmo.object);
1439
+ }
1177
1440
  this.Select(selectable);
1178
1441
  }
1179
1442
  findSelectableInterface(child) {
@@ -1181,17 +1444,11 @@ var DIVESelectTool = class extends DIVEBaseTool {
1181
1444
  if (child.parent === null) {
1182
1445
  return void 0;
1183
1446
  }
1184
- if ("isSelectable" in child) {
1447
+ if (isSelectable(child)) {
1185
1448
  return child;
1186
1449
  }
1187
1450
  return this.findSelectableInterface(child.parent);
1188
1451
  }
1189
- raycastFirst() {
1190
- return this.raycastAll()[0];
1191
- }
1192
- raycastAll() {
1193
- return this.raycaster.intersectObjects(this.scene.Root.children, true);
1194
- }
1195
1452
  };
1196
1453
 
1197
1454
  // src/toolbox/Toolbox.ts
@@ -1200,10 +1457,12 @@ var DIVEToolbox = class {
1200
1457
  this.removeListenersCallback = () => {
1201
1458
  };
1202
1459
  this.selectTool = new DIVESelectTool(scene, controller);
1460
+ controller.domElement.addEventListener("pointermove", this.onPointerMove.bind(this));
1203
1461
  controller.domElement.addEventListener("pointerdown", this.onPointerDown.bind(this));
1204
1462
  controller.domElement.addEventListener("pointerup", this.onPointerUp.bind(this));
1205
1463
  controller.domElement.addEventListener("wheel", this.onWheel.bind(this));
1206
1464
  this.removeListenersCallback = () => {
1465
+ controller.domElement.removeEventListener("pointermove", this.onPointerMove.bind(this));
1207
1466
  controller.domElement.removeEventListener("pointerdown", this.onPointerDown.bind(this));
1208
1467
  controller.domElement.removeEventListener("pointerup", this.onPointerUp.bind(this));
1209
1468
  controller.domElement.removeEventListener("wheel", this.onWheel.bind(this));
@@ -1233,6 +1492,9 @@ var DIVEToolbox = class {
1233
1492
  SetGizmoMode(mode) {
1234
1493
  this.selectTool.SetGizmoMode(mode);
1235
1494
  }
1495
+ onPointerMove(e) {
1496
+ this.activeTool.onPointerMove(e);
1497
+ }
1236
1498
  onPointerDown(e) {
1237
1499
  this.activeTool.onPointerDown(e);
1238
1500
  }
@@ -1383,6 +1645,11 @@ function roundExponential(number, decimals = 0) {
1383
1645
  return shift(Math.round(n), -decimals);
1384
1646
  }
1385
1647
 
1648
+ // src/math/signedAngleTo/signedAngleTo.ts
1649
+ function signedAngleTo(vecA, vecB, planeNormal) {
1650
+ return Math.atan2(vecA.clone().cross(vecB).dot(planeNormal), vecB.clone().dot(vecA));
1651
+ }
1652
+
1386
1653
  // src/math/toFixed/toFixedExp.ts
1387
1654
  function toFixedExp(number, decimals = 0) {
1388
1655
  const n = shift(number, +decimals);
@@ -1401,7 +1668,8 @@ var DIVEMath = {
1401
1668
  floorExp,
1402
1669
  roundExp: roundExponential,
1403
1670
  toFixedExp,
1404
- truncateExp
1671
+ truncateExp,
1672
+ signedAngleTo
1405
1673
  };
1406
1674
 
1407
1675
  // src/dive.ts
@@ -1477,6 +1745,11 @@ var DIVE = class {
1477
1745
  this.addResizeObserver();
1478
1746
  }
1479
1747
  this.renderer.StartRenderer(this.scene, this.perspectiveCamera);
1748
+ window.DIVE = {
1749
+ PrintScene: () => {
1750
+ console.log(this.scene);
1751
+ }
1752
+ };
1480
1753
  }
1481
1754
  // methods
1482
1755
  OnResize(width, height) {