@shopware-ag/dive 1.2.0 → 1.3.1

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 (39) hide show
  1. package/build/dive.cjs +288 -60
  2. package/build/dive.cjs.map +1 -1
  3. package/build/dive.d.cts +64 -6
  4. package/build/dive.d.ts +64 -6
  5. package/build/dive.js +289 -61
  6. package/build/dive.js.map +1 -1
  7. package/package.json +1 -1
  8. package/src/__test__/DIVE.test.ts +2 -0
  9. package/src/com/Communication.ts +6 -3
  10. package/src/com/__test__/Communication.test.ts +3 -6
  11. package/src/com/actions/object/getobjects.ts +2 -2
  12. package/src/dive.ts +7 -0
  13. package/src/gizmo/Gizmo.ts +130 -0
  14. package/src/gizmo/handles/AxisHandle.ts +124 -0
  15. package/src/gizmo/handles/RadialHandle.ts +119 -0
  16. package/src/gizmo/handles/ScaleHandle.ts +152 -0
  17. package/src/gizmo/plane/GizmoPlane.ts +85 -0
  18. package/src/gizmo/rotate/RotateGizmo.ts +95 -0
  19. package/src/gizmo/scale/ScaleGizmo.ts +97 -0
  20. package/src/gizmo/translate/TranslateGizmo.ts +88 -0
  21. package/src/interface/Draggable.ts +34 -0
  22. package/src/interface/Hoverable.ts +33 -0
  23. package/src/interface/Moveable.ts +0 -2
  24. package/src/interface/Selectable.ts +6 -0
  25. package/src/interface/__test__/Interfaces.test.ts +56 -0
  26. package/src/math/index.ts +3 -0
  27. package/src/math/signedAngleTo/__test__/signedAngleTo.test.ts +14 -0
  28. package/src/math/signedAngleTo/signedAngleTo.ts +13 -0
  29. package/src/scene/root/lightroot/LightRoot.ts +17 -3
  30. package/src/scene/root/lightroot/__test__/LightRoot.test.ts +12 -3
  31. package/src/scene/root/modelroot/ModelRoot.ts +17 -3
  32. package/src/scene/root/modelroot/__test__/ModelRoot.test.ts +13 -14
  33. package/src/toolbox/BaseTool.ts +254 -4
  34. package/src/toolbox/Toolbox.ts +6 -0
  35. package/src/toolbox/__test__/BaseTool.test.ts +389 -0
  36. package/src/toolbox/__test__/Toolbox.test.ts +8 -0
  37. package/src/toolbox/select/SelectTool.ts +29 -65
  38. package/src/toolbox/select/__test__/SelectTool.test.ts +57 -25
  39. package/src/toolbox/transform/TransformTool.ts +48 -0
@@ -0,0 +1,95 @@
1
+ import { Euler, Object3D, Vector3 } from "three";
2
+ import { AxesColorBlue, AxesColorGreen, AxesColorRed } from "../../constant/AxisHelperColors";
3
+ import DIVEOrbitControls from "../../controls/OrbitControls";
4
+ import { DIVERadialHandle } from "../handles/RadialHandle";
5
+ import { DIVEGizmo, DIVEGizmoAxis } from "../Gizmo";
6
+ import { DraggableEvent } from "../../toolbox/BaseTool";
7
+ import { DIVEMath } from "../../math";
8
+
9
+ export class DIVERotateGizmo extends Object3D {
10
+ public children: DIVERadialHandle[];
11
+
12
+ private _controller: DIVEOrbitControls;
13
+
14
+ private _startRot: Euler | null;
15
+
16
+ constructor(controller: DIVEOrbitControls) {
17
+ super();
18
+
19
+ this.name = "DIVERotateGizmo";
20
+
21
+ this.children = [];
22
+
23
+ this._startRot = null;
24
+
25
+ this._controller = controller;
26
+
27
+ this.add(new DIVERadialHandle('x', 1, Math.PI / 2, new Vector3(1, 0, 0), AxesColorRed));
28
+ this.add(new DIVERadialHandle('y', 1, -Math.PI / 2, new Vector3(0, 1, 0), AxesColorGreen));
29
+ this.add(new DIVERadialHandle('z', 1, Math.PI / 2, new Vector3(0, 0, 1), AxesColorBlue));
30
+ }
31
+
32
+ public reset(): void {
33
+ this.children.forEach((child) => {
34
+ child.reset();
35
+ });
36
+ }
37
+
38
+ private handleHighlight(axis: DIVEGizmoAxis, value: boolean, dragged: boolean): void {
39
+ // Set highlight state for all handles.
40
+ this.children.forEach((child) => {
41
+ if (dragged) {
42
+ // Dragging has priority when it comes to highlighting.
43
+ child.highlight = child.axis === axis && dragged;
44
+ } else {
45
+ // If nothing is dragged, decide on hovered state.
46
+ child.highlight = child.axis === axis && value;
47
+ }
48
+ });
49
+ }
50
+
51
+ public onHandleHover(handle: DIVERadialHandle, value: boolean): void {
52
+ // If _startRot is set, it means there is a drag operation in progress.
53
+ // While dragging, we don't want to change the hover state.
54
+ if (this._startRot) return;
55
+
56
+ if (!this.parent) return;
57
+ if (!this.parent.parent) return;
58
+ (this.parent.parent as DIVEGizmo).onHover('rotate', handle.axis, value);
59
+
60
+ this.handleHighlight(handle.axis, value, false);
61
+ }
62
+
63
+ public onHandleDragStart(handle: DIVERadialHandle): void {
64
+ if (!this.parent) return;
65
+ if (!this.parent.parent) return;
66
+
67
+ const object = (this.parent.parent as DIVEGizmo).object;
68
+ if (!object) return;
69
+
70
+ this._startRot = object.rotation.clone();
71
+ this.handleHighlight(handle.axis, true, true);
72
+ }
73
+
74
+ public onHandleDrag(handle: DIVERadialHandle, e: DraggableEvent): void {
75
+ if (!this._startRot) return;
76
+ if (!this.parent) return;
77
+ if (!this.parent.parent) return;
78
+ if (!('onChange' in this.parent.parent)) return;
79
+
80
+ const currentVector = e.dragCurrent.clone().sub(this.parent.parent.position).normalize();
81
+ const startVector = e.dragStart.clone().sub(this.parent.parent.position).normalize();
82
+ const signedAngle = DIVEMath.signedAngleTo(startVector, currentVector, handle.forwardVector);
83
+ const euler = new Euler(
84
+ this._startRot.x + handle.forwardVector.x * signedAngle,
85
+ this._startRot.y + handle.forwardVector.y * signedAngle,
86
+ this._startRot.z + handle.forwardVector.z * signedAngle,
87
+ );
88
+ (this.parent.parent as DIVEGizmo).onChange(undefined, euler);
89
+ }
90
+
91
+ public onHandleDragEnd(handle: DIVERadialHandle): void {
92
+ this._startRot = null;
93
+ this.handleHighlight(handle.axis, false, false);
94
+ }
95
+ }
@@ -0,0 +1,97 @@
1
+ import { Object3D, Vector3 } from "three";
2
+ import { AxesColorBlue, AxesColorGreen, AxesColorRed } from "../../constant/AxisHelperColors";
3
+ import { DIVEHoverable } from "../../interface/Hoverable";
4
+ import DIVEOrbitControls from "../../controls/OrbitControls";
5
+ import { DIVEScaleHandle } from "../handles/ScaleHandle";
6
+ import { DraggableEvent } from "../../toolbox/BaseTool";
7
+ import { DIVEGizmoAxis, DIVEGizmo } from "../Gizmo";
8
+
9
+ export class DIVEScaleGizmo extends Object3D implements DIVEHoverable {
10
+ readonly isHoverable: true = true;
11
+
12
+ public children: DIVEScaleHandle[];
13
+
14
+ private _controller: DIVEOrbitControls;
15
+
16
+ private _startScale: Vector3 | null;
17
+
18
+ constructor(controller: DIVEOrbitControls) {
19
+ super();
20
+
21
+ this.name = "DIVEScaleGizmo";
22
+
23
+ this.children = [];
24
+
25
+ this._startScale = null;
26
+
27
+ this._controller = controller;
28
+
29
+ this.add(new DIVEScaleHandle('x', 1, new Vector3(1, 0, 0), AxesColorRed));
30
+ this.add(new DIVEScaleHandle('y', 1, new Vector3(0, 1, 0), AxesColorGreen));
31
+ this.add(new DIVEScaleHandle('z', 1, new Vector3(0, 0, 1), AxesColorBlue));
32
+ }
33
+
34
+ public reset(): void {
35
+ this.children.forEach((child) => {
36
+ child.reset();
37
+ });
38
+ }
39
+
40
+ public update(scale: Vector3): void {
41
+ this.children.forEach((child) => {
42
+ child.update(scale);
43
+ });
44
+ }
45
+
46
+ private handleHighlight(axis: DIVEGizmoAxis, value: boolean, dragged: boolean): void {
47
+ // Set highlight state for all handles.
48
+ this.children.forEach((child) => {
49
+ if (dragged) {
50
+ // Dragging has priority when it comes to highlighting.
51
+ child.highlight = child.axis === axis && dragged;
52
+ } else {
53
+ // If nothing is dragged, decide on hovered state.
54
+ child.highlight = child.axis === axis && value;
55
+ }
56
+ });
57
+ }
58
+
59
+ public onHoverAxis(handle: DIVEScaleHandle, value: boolean): void {
60
+ // If _startScale is set, it means there is a drag operation in progress.
61
+ // While dragging, we don't want to change the hover state.
62
+ if (this._startScale) return;
63
+
64
+ if (!this.parent) return;
65
+ if (!this.parent.parent) return;
66
+ (this.parent.parent as DIVEGizmo).onHover('translate', handle.axis, value);
67
+
68
+ this.handleHighlight(handle.axis, value, false);
69
+ }
70
+
71
+ public onAxisDragStart(handle: DIVEScaleHandle): void {
72
+ if (!this.parent) return;
73
+ if (!this.parent.parent) return;
74
+
75
+ const object = (this.parent.parent as DIVEGizmo).object;
76
+ if (!object) return;
77
+
78
+ this._startScale = object.scale.clone();
79
+ this.handleHighlight(handle.axis, true, true);
80
+ }
81
+
82
+ public onAxisDrag(axis: DIVEScaleHandle, e: DraggableEvent): void {
83
+ if (!this._startScale) return;
84
+
85
+ if (!this.parent) return;
86
+ if (!this.parent.parent) return;
87
+ if (!('onChange' in this.parent.parent)) return;
88
+
89
+ const delta = e.dragDelta.clone().projectOnVector(axis.forwardVector);
90
+ (this.parent.parent as DIVEGizmo).onChange(undefined, undefined, this._startScale.clone().add(delta));
91
+ }
92
+
93
+ public onAxisDragEnd(handle: DIVEScaleHandle): void {
94
+ this._startScale = null;
95
+ this.handleHighlight(handle.axis, false, false);
96
+ }
97
+ }
@@ -0,0 +1,88 @@
1
+ import { Object3D, Vector3 } from "three";
2
+ import { AxesColorBlue, AxesColorGreen, AxesColorRed } from "../../constant/AxisHelperColors";
3
+ import DIVEOrbitControls from "../../controls/OrbitControls";
4
+ import { DIVEAxisHandle } from "../handles/AxisHandle";
5
+ import { DIVEGizmo, DIVEGizmoAxis } from "../Gizmo";
6
+ import { DraggableEvent } from "../../toolbox/BaseTool";
7
+
8
+ export class DIVETranslateGizmo extends Object3D {
9
+ private _controller: DIVEOrbitControls;
10
+
11
+ public children: DIVEAxisHandle[];
12
+
13
+ private _startPos: Vector3 | null;
14
+
15
+ constructor(controller: DIVEOrbitControls) {
16
+ super();
17
+
18
+ this.name = "DIVETranslateGizmo";
19
+
20
+ this.children = [];
21
+
22
+ this._startPos = null;
23
+
24
+ this._controller = controller;
25
+
26
+ this.add(new DIVEAxisHandle('x', 1, new Vector3(1, 0, 0), AxesColorRed));
27
+ this.add(new DIVEAxisHandle('y', 1, new Vector3(0, 1, 0), AxesColorGreen));
28
+ this.add(new DIVEAxisHandle('z', 1, new Vector3(0, 0, 1), AxesColorBlue));
29
+ }
30
+
31
+ public reset(): void {
32
+ this.children.forEach((child) => {
33
+ child.reset();
34
+ });
35
+ }
36
+
37
+ private handleHighlight(axis: DIVEGizmoAxis, value: boolean, dragged: boolean): void {
38
+ // Set highlight state for all handles.
39
+ this.children.forEach((child) => {
40
+ if (dragged) {
41
+ // Dragging has priority when it comes to highlighting.
42
+ child.highlight = child.axis === axis && dragged;
43
+ } else {
44
+ // If nothing is dragged, decide on hovered state.
45
+ child.highlight = child.axis === axis && value;
46
+ }
47
+ });
48
+ }
49
+
50
+ public onHandleHover(handle: DIVEAxisHandle, value: boolean): void {
51
+ // If _startPos is set, it means there is a drag operation in progress.
52
+ // While dragging, we don't want to change the hover state.
53
+ if (this._startPos) return;
54
+
55
+ if (!this.parent) return;
56
+ if (!this.parent.parent) return;
57
+ (this.parent.parent as DIVEGizmo).onHover('translate', handle.axis, value);
58
+
59
+ this.handleHighlight(handle.axis, value, false);
60
+ }
61
+
62
+ public onHandleDragStart(handle: DIVEAxisHandle): void {
63
+ if (!this.parent) return;
64
+ if (!this.parent.parent) return;
65
+
66
+ const object = (this.parent.parent as DIVEGizmo).object;
67
+ if (!object) return;
68
+
69
+ this._startPos = object.position.clone();
70
+ this.handleHighlight(handle.axis, true, true);
71
+ }
72
+
73
+ public onHandleDrag(handle: DIVEAxisHandle, e: DraggableEvent): void {
74
+ if (!this._startPos) return;
75
+
76
+ if (!this.parent) return;
77
+ if (!this.parent.parent) return;
78
+ if (!('onChange' in this.parent.parent)) return;
79
+
80
+ const delta = e.dragDelta.clone().projectOnVector(handle.forwardVector);
81
+ (this.parent.parent as DIVEGizmo).onChange(this._startPos.clone().add(delta));
82
+ }
83
+
84
+ public onHandleDragEnd(handle: DIVEAxisHandle): void {
85
+ this._startPos = null;
86
+ this.handleHighlight(handle.axis, false, false);
87
+ }
88
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Interface for objects that can be hovered in the scene.
3
+ *
4
+ * @module
5
+ */
6
+
7
+ import { type Object3D } from "three";
8
+ import { type DraggableEvent } from "../toolbox/BaseTool";
9
+
10
+ export interface DIVEDraggable {
11
+ isDraggable: true;
12
+ onDragStart?: (e: DraggableEvent) => void;
13
+ onDrag?: (e: DraggableEvent) => void;
14
+ onDragEnd?: (e: DraggableEvent) => void;
15
+ }
16
+
17
+ export const isDraggable = (object: Object3D): object is Object3D & DIVEDraggable => {
18
+ return 'isDraggable' in object;
19
+ };
20
+
21
+ export const findDraggableInterface = (child: Object3D): (Object3D & DIVEDraggable) | undefined => {
22
+ if (child === undefined) return undefined;
23
+
24
+ if (child.parent === null) {
25
+ // in this case it is the scene itself
26
+ return undefined;
27
+ }
28
+
29
+ if (isDraggable(child)) {
30
+ return child as (Object3D & DIVEDraggable);
31
+ }
32
+
33
+ return findDraggableInterface(child.parent);
34
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Interface for objects that can be hovered in the scene.
3
+ *
4
+ * @module
5
+ */
6
+
7
+ import { type Object3D, type Intersection } from "three";
8
+
9
+ export interface DIVEHoverable {
10
+ isHoverable: true;
11
+ onPointerEnter?: (i: Intersection) => void;
12
+ onPointerOver?: (i: Intersection) => void;
13
+ onPointerLeave?: () => void;
14
+ }
15
+
16
+ export const isHoverable = (object: Object3D): object is Object3D & DIVEHoverable => {
17
+ return 'isHoverable' in object;
18
+ };
19
+
20
+ export const findHoverableInterface = (child: Object3D): (Object3D & DIVEHoverable) | undefined => {
21
+ if (child === undefined) return undefined;
22
+
23
+ if (child.parent === null) {
24
+ // in this case it is the scene itself
25
+ return undefined;
26
+ }
27
+
28
+ if (isHoverable(child)) {
29
+ return child as (Object3D & DIVEHoverable);
30
+ }
31
+
32
+ return findHoverableInterface(child.parent);
33
+ }
@@ -1,4 +1,3 @@
1
- import type { TransformControls } from "three/examples/jsm/Addons.js";
2
1
 
3
2
  /**
4
3
  * Interface for objects that can be moved in the scene.
@@ -8,6 +7,5 @@ import type { TransformControls } from "three/examples/jsm/Addons.js";
8
7
 
9
8
  export interface DIVEMoveable {
10
9
  isMoveable: true;
11
- gizmo: TransformControls | null;
12
10
  onMove?: () => void;
13
11
  }
@@ -4,8 +4,14 @@
4
4
  * @module
5
5
  */
6
6
 
7
+ import { Object3D } from "three";
8
+
7
9
  export interface DIVESelectable {
8
10
  isSelectable: true;
9
11
  onSelect?: () => void;
10
12
  onDeselect?: () => void;
13
+ }
14
+
15
+ export function isSelectable(object: Object3D): object is Object3D & DIVESelectable {
16
+ return 'isSelectable' in object;
11
17
  }
@@ -1,7 +1,10 @@
1
+ import { type Object3D } from 'three';
1
2
  import * as Moveable_DEF from '../Moveable';
2
3
  import * as Rotatable_DEF from '../Rotatable';
3
4
  import * as Scalable_DEF from '../Scalable';
4
5
  import * as Selectable_DEF from '../Selectable';
6
+ import * as Draggable_DEF from '../Draggable';
7
+ import * as Hoverable_DEF from '../Hoverable';
5
8
 
6
9
  describe('interfaces', () => {
7
10
  it('should be defined', () => {
@@ -9,5 +12,58 @@ describe('interfaces', () => {
9
12
  expect(Rotatable_DEF).toBeDefined();
10
13
  expect(Scalable_DEF).toBeDefined();
11
14
  expect(Selectable_DEF).toBeDefined();
15
+ expect(Draggable_DEF).toBeDefined();
16
+ expect(Hoverable_DEF).toBeDefined();
17
+ });
18
+
19
+ it('should identify Selectable', () => {
20
+ const Selectable = { isSelectable: true };
21
+ expect(Selectable_DEF.isSelectable(Selectable as unknown as Object3D)).toBe(true);
22
+ });
23
+
24
+ it('should identify Draggable', () => {
25
+ const Draggable = { isDraggable: true };
26
+ expect(Draggable_DEF.isDraggable(Draggable as unknown as Object3D)).toBe(true);
27
+ });
28
+
29
+ it('should find Draggable', () => {
30
+ let Draggable = {
31
+ isDraggable: true,
32
+ } as unknown as Object3D & Draggable_DEF.DIVEDraggable;
33
+ expect(Draggable_DEF.findDraggableInterface(Draggable as unknown as Object3D)).toBe(Draggable);
34
+
35
+ let parent = {
36
+ isDraggable: true,
37
+ }
38
+ Draggable = {
39
+ parent: parent,
40
+ } as unknown as Object3D & Draggable_DEF.DIVEDraggable;
41
+ expect(Draggable_DEF.findDraggableInterface(Draggable as unknown as Object3D)).toBe(parent);
42
+
43
+ Draggable = { isDraggable: true, parent: null } as unknown as Object3D & Draggable_DEF.DIVEDraggable;
44
+ expect(Draggable_DEF.findDraggableInterface(Draggable as unknown as Object3D)).toBe(undefined);
45
+ });
46
+
47
+ it('should identify Hoverable', () => {
48
+ const Hoverable = { isHoverable: true };
49
+ expect(Hoverable_DEF.isHoverable(Hoverable as unknown as Object3D)).toBe(true);
50
+ });
51
+
52
+ it('should find Hoverable', () => {
53
+ let Hoverable = {
54
+ isHoverable: true,
55
+ } as unknown as Object3D & Hoverable_DEF.DIVEHoverable;
56
+ expect(Hoverable_DEF.findHoverableInterface(Hoverable as unknown as Object3D)).toBe(Hoverable);
57
+
58
+ let parent = {
59
+ isHoverable: true,
60
+ }
61
+ Hoverable = {
62
+ parent: parent,
63
+ } as unknown as Object3D & Hoverable_DEF.DIVEHoverable;
64
+ expect(Hoverable_DEF.findHoverableInterface(Hoverable as unknown as Object3D)).toBe(parent);
65
+
66
+ Hoverable = { isHoverable: true, parent: null } as unknown as Object3D & Hoverable_DEF.DIVEHoverable;
67
+ expect(Hoverable_DEF.findHoverableInterface(Hoverable as unknown as Object3D)).toBe(undefined);
12
68
  });
13
69
  });
package/src/math/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import ceilExp from "./ceil/ceilExp.ts";
2
2
  import floorExp from "./floor/floorExp.ts";
3
3
  import roundExp from "./round/roundExp.ts";
4
+ import signedAngleTo from "./signedAngleTo/signedAngleTo.ts";
4
5
  import toFixedExp from "./toFixed/toFixedExp.ts";
5
6
  import truncateExp from "./truncate/truncateExp.ts";
6
7
 
@@ -10,10 +11,12 @@ export const DIVEMath: {
10
11
  roundExp: typeof roundExp;
11
12
  toFixedExp: typeof toFixedExp;
12
13
  truncateExp: typeof truncateExp;
14
+ signedAngleTo: typeof signedAngleTo;
13
15
  } = {
14
16
  ceilExp,
15
17
  floorExp,
16
18
  roundExp,
17
19
  toFixedExp,
18
20
  truncateExp,
21
+ signedAngleTo,
19
22
  }
@@ -0,0 +1,14 @@
1
+ import { Vector3 } from 'three';
2
+ import signedAngleTo from '../signedAngleTo';
3
+
4
+ describe('dive/math/signedAngleTo', () => {
5
+ it('should signedAngleTo', () => {
6
+ const planeNormal = new Vector3(0, 0, 1);
7
+ const a = new Vector3(1, 0, 0);
8
+ expect(signedAngleTo(a, new Vector3(1, 0, 0), planeNormal)).toBe(0);
9
+ expect(signedAngleTo(a, new Vector3(0, 1, 0), planeNormal)).toBe(Math.PI / 2);
10
+ expect(signedAngleTo(a, new Vector3(-1, 0, 0), planeNormal)).toBe(Math.PI);
11
+ expect(signedAngleTo(a, new Vector3(0, -1, 0), planeNormal)).toBe(-Math.PI / 2);
12
+ expect(signedAngleTo(a, planeNormal, planeNormal)).toBe(0);
13
+ });
14
+ });
@@ -0,0 +1,13 @@
1
+ import { Vector3 } from "three";
2
+
3
+ /**
4
+ * Calculate the signed angle between two vectors. Only works when the vectors are on the same plane.
5
+ * @param vecB Start Vector
6
+ * @param vecA Target Vector
7
+ * @param planeNormal The vector's plane normal
8
+ * @returns Signed angle in radians
9
+ */
10
+
11
+ export default function signedAngleTo(vecA: Vector3, vecB: Vector3, planeNormal: Vector3): number {
12
+ return Math.atan2(vecA.clone().cross(vecB).dot(planeNormal), vecB.clone().dot(vecA));
13
+ }
@@ -2,8 +2,9 @@ import { Color, Object3D } from "three";
2
2
  import { type COMLight } from "../../../com/types.ts";
3
3
  import DIVEAmbientLight from "../../../light/AmbientLight.ts";
4
4
  import DIVEPointLight from "../../../light/PointLight.ts";
5
- import type { DIVEMoveable } from "../../../interface/Moveable.ts";
6
5
  import DIVESceneLight from "../../../light/SceneLight.ts";
6
+ import { type TransformControls } from "three/examples/jsm/Addons";
7
+ import type DIVEScene from "../../Scene.ts";
7
8
 
8
9
  /**
9
10
  * A basic scene node to hold all lights.
@@ -76,10 +77,23 @@ export default class DIVELightRoot extends Object3D {
76
77
  return;
77
78
  }
78
79
 
79
- if ('isMoveable' in sceneObject) {
80
- (sceneObject as unknown as DIVEMoveable).gizmo?.detach();
80
+ // _______________________________________________________
81
+ // this is only neccessary due to using the old TransformControls instead of the new DIVEGizmo
82
+ const findScene = (object: Object3D): DIVEScene => {
83
+ if (object.parent !== null) {
84
+ return findScene(object.parent);
85
+ };
86
+ return object as DIVEScene;
81
87
  }
82
88
 
89
+ const scene = findScene(sceneObject);
90
+ scene.children.find((object) => {
91
+ if ('isTransformControls' in object) {
92
+ (object as TransformControls).detach();
93
+ }
94
+ });
95
+ // _______________________________________________________
96
+
83
97
  this.remove(sceneObject);
84
98
  }
85
99
  }
@@ -2,6 +2,7 @@ import { TransformControls } from 'three/examples/jsm/Addons';
2
2
  import { COMLight } from '../../../../com';
3
3
  import { DIVEMoveable } from '../../../../interface/Moveable';
4
4
  import DIVELightRoot from '../LightRoot';
5
+ import type DIVEScene from '../../../Scene';
5
6
 
6
7
  const mock_SetPosition = jest.fn();
7
8
  const mock_SetIntensity = jest.fn();
@@ -115,6 +116,17 @@ describe('dive/scene/root/lightroot/DIVELightRoot', () => {
115
116
 
116
117
  it('should delete light', () => {
117
118
  const lightRoot = new DIVELightRoot();
119
+
120
+ const sceneParent = {
121
+ parent: null,
122
+ children: [
123
+ {
124
+ isTransformControls: true,
125
+ detach: jest.fn(),
126
+ }
127
+ ],
128
+ }
129
+ lightRoot.parent = sceneParent as unknown as DIVEScene;
118
130
  expect(() => lightRoot.DeleteLight({ id: undefined })).not.toThrow();
119
131
 
120
132
  expect(() => lightRoot.DeleteLight({ id: 'test_id' })).not.toThrow();
@@ -128,9 +140,6 @@ describe('dive/scene/root/lightroot/DIVELightRoot', () => {
128
140
 
129
141
  lightRoot.UpdateLight({ id: 'test_id', type: 'point', position: { x: 1, y: 2, z: 3 }, intensity: 0.5, color: 0x123456 });
130
142
  (lightRoot.children[0] as unknown as DIVEMoveable).isMoveable = true;
131
- (lightRoot.children[0] as unknown as DIVEMoveable).gizmo = {
132
- detach: jest.fn(),
133
- } as unknown as TransformControls;
134
143
  expect(() => lightRoot.DeleteLight({ id: 'test_id' })).not.toThrow();
135
144
  expect(lightRoot.children).toHaveLength(0);
136
145
  });
@@ -3,7 +3,8 @@ import { COMModel } from "../../../com/types.ts";
3
3
  import Model from "../../../model/Model.ts";
4
4
  import DIVELoadingManager from "../../../loadingmanager/LoadingManager.ts";
5
5
  import DIVECommunication from "../../../com/Communication.ts";
6
- import type { DIVEMoveable } from "../../../interface/Moveable.ts";
6
+ import { type TransformControls } from "three/examples/jsm/Addons";
7
+ import type DIVEScene from "../../Scene.ts";
7
8
 
8
9
  /**
9
10
  * A basic scene node to hold all models.
@@ -65,10 +66,23 @@ export default class DIVEModelRoot extends Object3D {
65
66
  return;
66
67
  }
67
68
 
68
- if ('isMoveable' in sceneObject) {
69
- (sceneObject as unknown as DIVEMoveable).gizmo?.detach();
69
+ // _______________________________________________________
70
+ // this is only neccessary due to using the old TransformControls instead of the new DIVEGizmo
71
+ const findScene = (object: Object3D): DIVEScene => {
72
+ if (object.parent !== null) {
73
+ return findScene(object.parent);
74
+ };
75
+ return object as DIVEScene;
70
76
  }
71
77
 
78
+ const scene = findScene(sceneObject);
79
+ scene.children.find((object) => {
80
+ if ('isTransformControls' in object) {
81
+ (object as TransformControls).detach();
82
+ }
83
+ });
84
+ // _______________________________________________________
85
+
72
86
  this.remove(sceneObject);
73
87
  }
74
88
 
@@ -1,7 +1,7 @@
1
1
  import DIVEModelRoot from '../ModelRoot';
2
2
  import DIVECommunication from '../../../../com/Communication';
3
- import { TransformControls } from 'three/examples/jsm/Addons';
4
3
  import { DIVEMoveable } from '../../../../interface/Moveable';
4
+ import type DIVEScene from '../../../Scene';
5
5
 
6
6
  const mock_LoadGLTF = jest.fn().mockResolvedValue({});
7
7
  const mock_SetPosition = jest.fn();
@@ -154,6 +154,18 @@ describe('dive/scene/root/modelroot/DIVEModelRoot', () => {
154
154
 
155
155
  it('should delete model', async () => {
156
156
  const modelRoot = new DIVEModelRoot();
157
+
158
+ const sceneParent = {
159
+ parent: null,
160
+ children: [
161
+ {
162
+ isTransformControls: true,
163
+ detach: jest.fn(),
164
+ }
165
+ ],
166
+ }
167
+ modelRoot.parent = sceneParent as unknown as DIVEScene;
168
+
157
169
  expect(() => modelRoot.DeleteModel({ id: undefined })).not.toThrow();
158
170
  expect(consoleWarnSpy).toHaveBeenCalled();
159
171
  consoleWarnSpy.mockClear();
@@ -168,19 +180,6 @@ describe('dive/scene/root/modelroot/DIVEModelRoot', () => {
168
180
  })).not.toThrow();
169
181
  (modelRoot.children[0] as unknown as DIVEMoveable).isMoveable = true;
170
182
  expect(() => modelRoot.DeleteModel({ id: 'test_id' })).not.toThrow();
171
-
172
- await expect(() => modelRoot.UpdateModel({
173
- id: 'test_id',
174
- uri: 'not a real uri',
175
- position: { x: 1, y: 2, z: 3 },
176
- rotation: { x: 1, y: 2, z: 3 },
177
- scale: { x: 1, y: 2, z: 3 },
178
- })).not.toThrow();
179
- (modelRoot.children[0] as unknown as DIVEMoveable).isMoveable = true;
180
- (modelRoot.children[0] as unknown as DIVEMoveable).gizmo = {
181
- detach: jest.fn(),
182
- } as unknown as TransformControls;
183
- expect(() => modelRoot.DeleteModel({ id: 'test_id' })).not.toThrow();
184
183
  expect(modelRoot.children).toHaveLength(0);
185
184
  });
186
185
  });