@shopware-ag/dive 1.16.7 → 1.16.9
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.
- package/build/dive.cjs +116 -21
- package/build/dive.cjs.map +1 -1
- package/build/dive.d.cts +86 -72
- package/build/dive.d.ts +86 -72
- package/build/dive.js +137 -35
- package/build/dive.js.map +1 -1
- package/package.json +1 -1
- package/src/com/actions/scene/getallscenedata.ts +2 -25
- package/src/com/types/COMLight.ts +1 -0
- package/src/dive.ts +1 -0
- package/src/group/Group.ts +16 -1
- package/src/group/__test__/Group.test.ts +97 -0
- package/src/math/degToRad/__test__/degToRad.test.ts +179 -0
- package/src/math/degToRad/degToRad.ts +5 -0
- package/src/math/index.ts +6 -0
- package/src/math/radToDeg/__test__/radToDeg.test.ts +162 -0
- package/src/math/radToDeg/radToDeg.ts +5 -0
- package/src/model/Model.ts +21 -5
- package/src/model/__test__/Model.test.ts +34 -4
- package/src/node/Node.ts +21 -8
- package/src/node/__test__/Node.test.ts +35 -3
- package/src/primitive/Primitive.ts +56 -15
- package/src/primitive/__test__/Primitive.test.ts +39 -1
- package/src/scene/root/__test__/Root.test.ts +39 -0
- package/src/types/SceneData.ts +26 -0
- package/src/types/index.ts +2 -1
package/package.json
CHANGED
|
@@ -1,30 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
type COMLight,
|
|
4
|
-
type COMModel,
|
|
5
|
-
type COMPov,
|
|
6
|
-
type COMPrimitive,
|
|
7
|
-
} from '../../types';
|
|
8
|
-
|
|
9
|
-
type SceneData = {
|
|
10
|
-
name: string;
|
|
11
|
-
mediaItem: null;
|
|
12
|
-
backgroundColor: string;
|
|
13
|
-
floorEnabled: boolean;
|
|
14
|
-
floorColor: string;
|
|
15
|
-
userCamera: {
|
|
16
|
-
position: Vector3Like;
|
|
17
|
-
target: Vector3Like;
|
|
18
|
-
};
|
|
19
|
-
spotmarks: object[];
|
|
20
|
-
lights: COMLight[];
|
|
21
|
-
objects: COMModel[];
|
|
22
|
-
cameras: COMPov[];
|
|
23
|
-
primitives: COMPrimitive[];
|
|
24
|
-
};
|
|
1
|
+
import { DIVESceneData } from '../../../types';
|
|
25
2
|
|
|
26
3
|
export default interface GET_ALL_SCENE_DATA {
|
|
27
4
|
DESCRIPTION: 'Retrieves all current scene data.';
|
|
28
5
|
PAYLOAD: object;
|
|
29
|
-
RETURN:
|
|
6
|
+
RETURN: DIVESceneData;
|
|
30
7
|
}
|
package/src/dive.ts
CHANGED
package/src/group/Group.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
BufferGeometry,
|
|
3
|
+
Line,
|
|
4
|
+
LineDashedMaterial,
|
|
5
|
+
Vector3,
|
|
6
|
+
Vector3Like,
|
|
7
|
+
} from 'three';
|
|
2
8
|
import { DIVENode } from '../node/Node';
|
|
3
9
|
import { type Object3D } from 'three';
|
|
4
10
|
import { type DIVESceneObject } from '../types';
|
|
@@ -20,6 +26,15 @@ export class DIVEGroup extends DIVENode {
|
|
|
20
26
|
this._lines = [];
|
|
21
27
|
}
|
|
22
28
|
|
|
29
|
+
public SetPosition(position: Vector3Like): void {
|
|
30
|
+
super.SetPosition(position);
|
|
31
|
+
this._members.forEach((member) => {
|
|
32
|
+
if ('isDIVENode' in member) {
|
|
33
|
+
(member as DIVENode).onMove();
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
23
38
|
public SetLinesVisibility(visible: boolean, object?: Object3D): void {
|
|
24
39
|
if (!object) {
|
|
25
40
|
this._lines.forEach((line) => {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { type Vector3Like } from 'three';
|
|
1
2
|
import { DIVECommunication } from '../../com/Communication';
|
|
3
|
+
import { type DIVENode } from '../../node/Node';
|
|
2
4
|
import { DIVEGroup } from '../Group';
|
|
3
5
|
|
|
4
6
|
jest.mock('../../com/Communication.ts', () => {
|
|
@@ -107,4 +109,99 @@ describe('dive/group/DIVEGroup', () => {
|
|
|
107
109
|
jest.spyOn(DIVECommunication, 'get').mockReturnValueOnce(undefined);
|
|
108
110
|
expect(() => group.onMove()).not.toThrow();
|
|
109
111
|
});
|
|
112
|
+
|
|
113
|
+
it('should call onMove on members with isDIVENode', () => {
|
|
114
|
+
// Create mock members
|
|
115
|
+
const diveNode1: DIVENode = {
|
|
116
|
+
isDIVENode: true,
|
|
117
|
+
onMove: jest.fn(),
|
|
118
|
+
} as unknown as DIVENode;
|
|
119
|
+
|
|
120
|
+
const diveNode2: DIVENode = {
|
|
121
|
+
isDIVENode: true,
|
|
122
|
+
onMove: jest.fn(),
|
|
123
|
+
} as unknown as DIVENode;
|
|
124
|
+
|
|
125
|
+
const member1: DIVENode = {
|
|
126
|
+
// Define other properties/methods if necessary
|
|
127
|
+
} as unknown as DIVENode;
|
|
128
|
+
|
|
129
|
+
const member2: DIVENode = {
|
|
130
|
+
// Define other properties/methods if necessary
|
|
131
|
+
} as unknown as DIVENode;
|
|
132
|
+
|
|
133
|
+
// Assign the _members array (assuming it's protected or public for testing)
|
|
134
|
+
// If _members is private, you might need to use a different approach or modify the class for testability
|
|
135
|
+
(group as any)._members = [
|
|
136
|
+
diveNode1,
|
|
137
|
+
member1,
|
|
138
|
+
diveNode2,
|
|
139
|
+
member2,
|
|
140
|
+
];
|
|
141
|
+
|
|
142
|
+
const position: Vector3Like = { x: 4, y: 5, z: 6 };
|
|
143
|
+
group.SetPosition(position);
|
|
144
|
+
|
|
145
|
+
// Check that onMove was called on diveNode1 and diveNode2
|
|
146
|
+
expect(diveNode1.onMove).toHaveBeenCalled();
|
|
147
|
+
expect(diveNode2.onMove).toHaveBeenCalled();
|
|
148
|
+
|
|
149
|
+
// Ensure onMove was not called on other members
|
|
150
|
+
// Since member1 and member2 don't have onMove, there's nothing to assert here
|
|
151
|
+
// If members have onMove, you should mock and verify they are not called
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('should not call onMove on members without isDIVENode', () => {
|
|
155
|
+
// Create mock members without isDIVENode
|
|
156
|
+
const member1: DIVENode = {
|
|
157
|
+
// Define other properties/methods if necessary
|
|
158
|
+
} as unknown as DIVENode;
|
|
159
|
+
|
|
160
|
+
const member2: DIVENode = {
|
|
161
|
+
// Define other properties/methods if necessary
|
|
162
|
+
} as unknown as DIVENode;
|
|
163
|
+
|
|
164
|
+
// Assign the _members array
|
|
165
|
+
(group as any)._members = [
|
|
166
|
+
member1,
|
|
167
|
+
member2,
|
|
168
|
+
];
|
|
169
|
+
|
|
170
|
+
const position: Vector3Like = { x: 7, y: 8, z: 9 };
|
|
171
|
+
group.SetPosition(position);
|
|
172
|
+
|
|
173
|
+
// Since members do not have onMove, there's nothing to assert
|
|
174
|
+
// If members have onMove as optional, you can spy on them to ensure they're not called
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('should handle an empty _members array without errors', () => {
|
|
178
|
+
// Assign an empty _members array
|
|
179
|
+
(group as any)._members = [];
|
|
180
|
+
|
|
181
|
+
const position: Vector3Like = { x: 10, y: 11, z: 12 };
|
|
182
|
+
expect(() => group.SetPosition(position)).not.toThrow();
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('should handle _members with mixed types correctly', () => {
|
|
186
|
+
// Create mixed members
|
|
187
|
+
const diveNode: DIVENode = {
|
|
188
|
+
isDIVENode: true,
|
|
189
|
+
onMove: jest.fn(),
|
|
190
|
+
} as unknown as DIVENode;
|
|
191
|
+
|
|
192
|
+
const member: DIVENode = {
|
|
193
|
+
// Define other properties/methods if necessary
|
|
194
|
+
} as unknown as DIVENode;
|
|
195
|
+
|
|
196
|
+
(group as any)._members = [
|
|
197
|
+
diveNode,
|
|
198
|
+
member,
|
|
199
|
+
];
|
|
200
|
+
|
|
201
|
+
const position: Vector3Like = { x: 13, y: 14, z: 15 };
|
|
202
|
+
group.SetPosition(position);
|
|
203
|
+
|
|
204
|
+
// Ensure onMove is called only on diveNode
|
|
205
|
+
expect(diveNode.onMove).toHaveBeenCalled();
|
|
206
|
+
});
|
|
110
207
|
});
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import degToRad from '../degToRad';
|
|
2
|
+
import { MathUtils } from 'three';
|
|
3
|
+
|
|
4
|
+
// Mock the 'three' module, specifically MathUtils.degToRad
|
|
5
|
+
jest.mock('three', () => ({
|
|
6
|
+
MathUtils: {
|
|
7
|
+
degToRad: jest.fn(),
|
|
8
|
+
},
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
// Type assertion for the mocked MathUtils.degToRad
|
|
12
|
+
const mockedDegToRad = MathUtils.degToRad as jest.Mock;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Test Suite for degToRad Function
|
|
16
|
+
*/
|
|
17
|
+
describe('degToRad', () => {
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
// Clear all previous mock calls and implementations before each test
|
|
20
|
+
mockedDegToRad.mockClear();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should convert 0 degrees to 0 radians', () => {
|
|
24
|
+
// Arrange
|
|
25
|
+
mockedDegToRad.mockReturnValue(0);
|
|
26
|
+
|
|
27
|
+
// Act
|
|
28
|
+
const result = degToRad(0);
|
|
29
|
+
|
|
30
|
+
// Assert
|
|
31
|
+
expect(MathUtils.degToRad).toHaveBeenCalledWith(0);
|
|
32
|
+
expect(result).toBe(0);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should convert 180 degrees to π radians', () => {
|
|
36
|
+
// Arrange
|
|
37
|
+
const degrees = 180;
|
|
38
|
+
const radians = Math.PI;
|
|
39
|
+
mockedDegToRad.mockReturnValue(radians);
|
|
40
|
+
|
|
41
|
+
// Act
|
|
42
|
+
const result = degToRad(degrees);
|
|
43
|
+
|
|
44
|
+
// Assert
|
|
45
|
+
expect(MathUtils.degToRad).toHaveBeenCalledWith(degrees);
|
|
46
|
+
expect(result).toBe(radians);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should convert 360 degrees to 2π radians', () => {
|
|
50
|
+
// Arrange
|
|
51
|
+
const degrees = 360;
|
|
52
|
+
const radians = 2 * Math.PI;
|
|
53
|
+
mockedDegToRad.mockReturnValue(radians);
|
|
54
|
+
|
|
55
|
+
// Act
|
|
56
|
+
const result = degToRad(degrees);
|
|
57
|
+
|
|
58
|
+
// Assert
|
|
59
|
+
expect(MathUtils.degToRad).toHaveBeenCalledWith(degrees);
|
|
60
|
+
expect(result).toBe(radians);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should convert 90 degrees to π/2 radians', () => {
|
|
64
|
+
// Arrange
|
|
65
|
+
const degrees = 90;
|
|
66
|
+
const radians = Math.PI / 2;
|
|
67
|
+
mockedDegToRad.mockReturnValue(radians);
|
|
68
|
+
|
|
69
|
+
// Act
|
|
70
|
+
const result = degToRad(degrees);
|
|
71
|
+
|
|
72
|
+
// Assert
|
|
73
|
+
expect(MathUtils.degToRad).toHaveBeenCalledWith(degrees);
|
|
74
|
+
expect(result).toBe(radians);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should convert 45 degrees to π/4 radians', () => {
|
|
78
|
+
// Arrange
|
|
79
|
+
const degrees = 45;
|
|
80
|
+
const radians = Math.PI / 4;
|
|
81
|
+
mockedDegToRad.mockReturnValue(radians);
|
|
82
|
+
|
|
83
|
+
// Act
|
|
84
|
+
const result = degToRad(degrees);
|
|
85
|
+
|
|
86
|
+
// Assert
|
|
87
|
+
expect(MathUtils.degToRad).toHaveBeenCalledWith(degrees);
|
|
88
|
+
expect(result).toBe(radians);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should handle multiple calls with different degrees', () => {
|
|
92
|
+
// Arrange
|
|
93
|
+
const degrees1 = 30;
|
|
94
|
+
const radians1 = Math.PI / 6;
|
|
95
|
+
const degrees2 = 60;
|
|
96
|
+
const radians2 = Math.PI / 3;
|
|
97
|
+
const degrees3 = 120;
|
|
98
|
+
const radians3 = (2 * Math.PI) / 3;
|
|
99
|
+
|
|
100
|
+
mockedDegToRad
|
|
101
|
+
.mockReturnValueOnce(radians1)
|
|
102
|
+
.mockReturnValueOnce(radians2)
|
|
103
|
+
.mockReturnValueOnce(radians3);
|
|
104
|
+
|
|
105
|
+
// Act
|
|
106
|
+
const result1 = degToRad(degrees1);
|
|
107
|
+
const result2 = degToRad(degrees2);
|
|
108
|
+
const result3 = degToRad(degrees3);
|
|
109
|
+
|
|
110
|
+
// Assert
|
|
111
|
+
expect(MathUtils.degToRad).toHaveBeenNthCalledWith(1, degrees1);
|
|
112
|
+
expect(MathUtils.degToRad).toHaveBeenNthCalledWith(2, degrees2);
|
|
113
|
+
expect(MathUtils.degToRad).toHaveBeenNthCalledWith(3, degrees3);
|
|
114
|
+
|
|
115
|
+
expect(result1).toBe(radians1);
|
|
116
|
+
expect(result2).toBe(radians2);
|
|
117
|
+
expect(result3).toBe(radians3);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('should handle edge case of degrees just below 360', () => {
|
|
121
|
+
// Arrange
|
|
122
|
+
const degrees = 359.999;
|
|
123
|
+
const radians = MathUtils.degToRad(degrees);
|
|
124
|
+
mockedDegToRad.mockReturnValue(radians);
|
|
125
|
+
|
|
126
|
+
// Act
|
|
127
|
+
const result = degToRad(degrees);
|
|
128
|
+
|
|
129
|
+
// Assert
|
|
130
|
+
expect(MathUtils.degToRad).toHaveBeenCalledWith(degrees);
|
|
131
|
+
expect(result).toBe(radians);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('should not allow negative degrees', () => {
|
|
135
|
+
// Arrange
|
|
136
|
+
const degrees = -45;
|
|
137
|
+
const radians = MathUtils.degToRad(degrees);
|
|
138
|
+
mockedDegToRad.mockReturnValue(radians);
|
|
139
|
+
|
|
140
|
+
// Act
|
|
141
|
+
const result = degToRad(degrees);
|
|
142
|
+
|
|
143
|
+
// Assert
|
|
144
|
+
expect(MathUtils.degToRad).toHaveBeenCalledWith(degrees);
|
|
145
|
+
expect(result).toBe(radians);
|
|
146
|
+
// Depending on implementation, you might want to expect an error
|
|
147
|
+
// or handle negative degrees differently. Adjust assertions accordingly.
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('should handle undefined degrees gracefully', () => {
|
|
151
|
+
// Arrange
|
|
152
|
+
const degrees = undefined;
|
|
153
|
+
// Since the function expects a number, this might throw an error or pass undefined
|
|
154
|
+
mockedDegToRad.mockReturnValue(NaN);
|
|
155
|
+
|
|
156
|
+
// Act
|
|
157
|
+
// @ts-ignore: Testing undefined input
|
|
158
|
+
const result = degToRad(degrees);
|
|
159
|
+
|
|
160
|
+
// Assert
|
|
161
|
+
expect(MathUtils.degToRad).toHaveBeenCalledWith(degrees);
|
|
162
|
+
expect(result).toBe(NaN);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('should handle null degrees gracefully', () => {
|
|
166
|
+
// Arrange
|
|
167
|
+
const degrees = null;
|
|
168
|
+
// Depending on implementation, this might throw an error or pass null
|
|
169
|
+
mockedDegToRad.mockReturnValue(NaN);
|
|
170
|
+
|
|
171
|
+
// Act
|
|
172
|
+
// @ts-ignore: Testing null input
|
|
173
|
+
const result = degToRad(degrees);
|
|
174
|
+
|
|
175
|
+
// Assert
|
|
176
|
+
expect(MathUtils.degToRad).toHaveBeenCalledWith(degrees);
|
|
177
|
+
expect(result).toBe(NaN);
|
|
178
|
+
});
|
|
179
|
+
});
|
package/src/math/index.ts
CHANGED
|
@@ -4,6 +4,8 @@ import roundExp from './round/roundExp.ts';
|
|
|
4
4
|
import signedAngleTo from './signedAngleTo/signedAngleTo.ts';
|
|
5
5
|
import toFixedExp from './toFixed/toFixedExp.ts';
|
|
6
6
|
import truncateExp from './truncate/truncateExp.ts';
|
|
7
|
+
import radToDeg from './radToDeg/radToDeg.ts';
|
|
8
|
+
import degToRad from './degToRad/degToRad.ts';
|
|
7
9
|
|
|
8
10
|
export const DIVEMath: {
|
|
9
11
|
ceilExp: typeof ceilExp;
|
|
@@ -12,6 +14,8 @@ export const DIVEMath: {
|
|
|
12
14
|
toFixedExp: typeof toFixedExp;
|
|
13
15
|
truncateExp: typeof truncateExp;
|
|
14
16
|
signedAngleTo: typeof signedAngleTo;
|
|
17
|
+
radToDeg: typeof radToDeg;
|
|
18
|
+
degToRad: typeof degToRad;
|
|
15
19
|
} = {
|
|
16
20
|
ceilExp,
|
|
17
21
|
floorExp,
|
|
@@ -19,4 +23,6 @@ export const DIVEMath: {
|
|
|
19
23
|
toFixedExp,
|
|
20
24
|
truncateExp,
|
|
21
25
|
signedAngleTo,
|
|
26
|
+
radToDeg,
|
|
27
|
+
degToRad,
|
|
22
28
|
};
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import radToDeg from '../radToDeg';
|
|
2
|
+
import { MathUtils } from 'three';
|
|
3
|
+
|
|
4
|
+
// Mock the 'three' module, specifically MathUtils.radToDeg
|
|
5
|
+
jest.mock('three', () => ({
|
|
6
|
+
MathUtils: {
|
|
7
|
+
radToDeg: jest.fn(),
|
|
8
|
+
},
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
// Type assertion for the mocked MathUtils.radToDeg
|
|
12
|
+
const mockedRadToDeg = MathUtils.radToDeg as jest.Mock;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Test Suite for radToDeg Function
|
|
16
|
+
*/
|
|
17
|
+
describe('radToDeg', () => {
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
// Clear all previous mock calls and implementations before each test
|
|
20
|
+
mockedRadToDeg.mockClear();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should convert 0 radians to 0 degrees', () => {
|
|
24
|
+
// Arrange
|
|
25
|
+
mockedRadToDeg.mockReturnValue(0);
|
|
26
|
+
|
|
27
|
+
// Act
|
|
28
|
+
const result = radToDeg(0);
|
|
29
|
+
|
|
30
|
+
// Assert
|
|
31
|
+
expect(MathUtils.radToDeg).toHaveBeenCalledWith(0);
|
|
32
|
+
expect(result).toBe(0);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should convert π radians to 180 degrees', () => {
|
|
36
|
+
// Arrange
|
|
37
|
+
const pi = Math.PI;
|
|
38
|
+
mockedRadToDeg.mockReturnValue(180);
|
|
39
|
+
|
|
40
|
+
// Act
|
|
41
|
+
const result = radToDeg(pi);
|
|
42
|
+
|
|
43
|
+
// Assert
|
|
44
|
+
expect(MathUtils.radToDeg).toHaveBeenCalledWith(pi);
|
|
45
|
+
expect(result).toBe(180);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should convert 2π radians to 0 degrees (wrapped)', () => {
|
|
49
|
+
// Arrange
|
|
50
|
+
const twoPi = 2 * Math.PI;
|
|
51
|
+
mockedRadToDeg.mockReturnValue(360);
|
|
52
|
+
|
|
53
|
+
// Act
|
|
54
|
+
const result = radToDeg(twoPi);
|
|
55
|
+
|
|
56
|
+
// Assert
|
|
57
|
+
expect(MathUtils.radToDeg).toHaveBeenCalledWith(twoPi);
|
|
58
|
+
expect(result).toBe(0);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should convert 3π/2 radians to 270 degrees', () => {
|
|
62
|
+
// Arrange
|
|
63
|
+
const threePiOver2 = (3 * Math.PI) / 2;
|
|
64
|
+
mockedRadToDeg.mockReturnValue(270);
|
|
65
|
+
|
|
66
|
+
// Act
|
|
67
|
+
const result = radToDeg(threePiOver2);
|
|
68
|
+
|
|
69
|
+
// Assert
|
|
70
|
+
expect(MathUtils.radToDeg).toHaveBeenCalledWith(threePiOver2);
|
|
71
|
+
expect(result).toBe(270);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should handle angles greater than 2π radians correctly', () => {
|
|
75
|
+
// Arrange
|
|
76
|
+
const sevenPi = 7 * Math.PI; // 1260 degrees
|
|
77
|
+
const expectedDegrees = (1260 + 360) % 360; // 180 degrees
|
|
78
|
+
mockedRadToDeg.mockReturnValue(1260);
|
|
79
|
+
|
|
80
|
+
// Act
|
|
81
|
+
const result = radToDeg(sevenPi);
|
|
82
|
+
|
|
83
|
+
// Assert
|
|
84
|
+
expect(MathUtils.radToDeg).toHaveBeenCalledWith(sevenPi);
|
|
85
|
+
expect(result).toBe(expectedDegrees);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should handle fractional radians correctly', () => {
|
|
89
|
+
// Arrange
|
|
90
|
+
const fractionalRadians = Math.PI / 4; // 45 degrees
|
|
91
|
+
mockedRadToDeg.mockReturnValue(45);
|
|
92
|
+
|
|
93
|
+
// Act
|
|
94
|
+
const result = radToDeg(fractionalRadians);
|
|
95
|
+
|
|
96
|
+
// Assert
|
|
97
|
+
expect(MathUtils.radToDeg).toHaveBeenCalledWith(fractionalRadians);
|
|
98
|
+
expect(result).toBe(45);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should wrap negative angles correctly', () => {
|
|
102
|
+
// Since the function does not handle negative inputs, this test ensures it behaves as expected
|
|
103
|
+
// However, based on initial requirements, negative degrees are not allowed and inputs are non-negative
|
|
104
|
+
// This test is optional and based on how you want the function to behave
|
|
105
|
+
const negativeRadians = -Math.PI / 2; // -90 degrees
|
|
106
|
+
mockedRadToDeg.mockReturnValue(-90);
|
|
107
|
+
const expectedDegrees = (-90 + 360) % 360; // 270 degrees
|
|
108
|
+
|
|
109
|
+
const result = radToDeg(negativeRadians);
|
|
110
|
+
|
|
111
|
+
expect(MathUtils.radToDeg).toHaveBeenCalledWith(negativeRadians);
|
|
112
|
+
expect(result).toBe(expectedDegrees);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should handle multiple full rotations correctly', () => {
|
|
116
|
+
// Arrange
|
|
117
|
+
const multipleRotations = 5 * 2 * Math.PI; // 360 * 5 = 1800 degrees
|
|
118
|
+
const expectedDegrees = (1800 + 360) % 360; // 0 degrees
|
|
119
|
+
mockedRadToDeg.mockReturnValue(1800);
|
|
120
|
+
|
|
121
|
+
// Act
|
|
122
|
+
const result = radToDeg(multipleRotations);
|
|
123
|
+
|
|
124
|
+
// Assert
|
|
125
|
+
expect(MathUtils.radToDeg).toHaveBeenCalledWith(multipleRotations);
|
|
126
|
+
expect(result).toBe(0);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should handle mixed angles', () => {
|
|
130
|
+
// Arrange
|
|
131
|
+
const angles = [
|
|
132
|
+
Math.PI / 6,
|
|
133
|
+
Math.PI,
|
|
134
|
+
(5 * Math.PI) / 3,
|
|
135
|
+
];
|
|
136
|
+
const mockReturns = [
|
|
137
|
+
30,
|
|
138
|
+
180,
|
|
139
|
+
300,
|
|
140
|
+
];
|
|
141
|
+
const expectedDegrees = [
|
|
142
|
+
(30 + 360) % 360,
|
|
143
|
+
(180 + 360) % 360,
|
|
144
|
+
(300 + 360) % 360,
|
|
145
|
+
]; // [30, 180, 300]
|
|
146
|
+
|
|
147
|
+
mockedRadToDeg
|
|
148
|
+
.mockReturnValueOnce(mockReturns[0])
|
|
149
|
+
.mockReturnValueOnce(mockReturns[1])
|
|
150
|
+
.mockReturnValueOnce(mockReturns[2]);
|
|
151
|
+
|
|
152
|
+
// Act & Assert for each angle
|
|
153
|
+
angles.forEach((angle, index) => {
|
|
154
|
+
const result = radToDeg(angle);
|
|
155
|
+
expect(MathUtils.radToDeg).toHaveBeenNthCalledWith(
|
|
156
|
+
index + 1,
|
|
157
|
+
angle,
|
|
158
|
+
);
|
|
159
|
+
expect(result).toBe(expectedDegrees[index]);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
});
|
package/src/model/Model.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { GLTF } from 'three/examples/jsm/Addons.js';
|
|
|
4
4
|
import { findSceneRecursive } from '../helper/findSceneRecursive/findSceneRecursive';
|
|
5
5
|
import { type COMMaterial } from '../com/types';
|
|
6
6
|
import { DIVENode } from '../node/Node';
|
|
7
|
+
import { DIVECommunication } from '../com/Communication';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* A basic model class.
|
|
@@ -23,6 +24,7 @@ export class DIVEModel extends DIVENode {
|
|
|
23
24
|
|
|
24
25
|
public SetModel(gltf: GLTF): void {
|
|
25
26
|
this.clear();
|
|
27
|
+
this._boundingBox.makeEmpty();
|
|
26
28
|
|
|
27
29
|
gltf.scene.traverse((child) => {
|
|
28
30
|
child.castShadow = true;
|
|
@@ -110,11 +112,25 @@ export class DIVEModel extends DIVENode {
|
|
|
110
112
|
}
|
|
111
113
|
|
|
112
114
|
public PlaceOnFloor(): void {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
// calculate and temporary save world position
|
|
116
|
+
const worldPos = this.getWorldPosition(this._positionWorldBuffer);
|
|
117
|
+
const oldWorldPos = worldPos.clone();
|
|
118
|
+
|
|
119
|
+
// calculate the bottom center of the bounding box and set it to world posion
|
|
120
|
+
worldPos.y = -this._boundingBox.min.y * this.scale.y;
|
|
121
|
+
|
|
122
|
+
// skip any action when the position did not change
|
|
123
|
+
if (worldPos.y === oldWorldPos.y) return;
|
|
124
|
+
|
|
125
|
+
DIVECommunication.get(this.userData.id)?.PerformAction(
|
|
126
|
+
'UPDATE_OBJECT',
|
|
127
|
+
{
|
|
128
|
+
id: this.userData.id,
|
|
129
|
+
position: worldPos,
|
|
130
|
+
rotation: this.rotation,
|
|
131
|
+
scale: this.scale,
|
|
132
|
+
},
|
|
133
|
+
);
|
|
118
134
|
}
|
|
119
135
|
|
|
120
136
|
public DropIt(): void {
|
|
@@ -11,12 +11,17 @@ import {
|
|
|
11
11
|
Color,
|
|
12
12
|
} from 'three';
|
|
13
13
|
import { type COMMaterial } from '../../com/types';
|
|
14
|
+
import { isTypeOnlyImportOrExportDeclaration } from 'typescript';
|
|
14
15
|
|
|
15
16
|
const intersectObjectsMock = jest.fn();
|
|
16
17
|
|
|
17
18
|
jest.mock('three', () => {
|
|
18
19
|
return {
|
|
19
|
-
Vector3: jest.fn(function (
|
|
20
|
+
Vector3: jest.fn(function (
|
|
21
|
+
x: number = 0,
|
|
22
|
+
y: number = 0,
|
|
23
|
+
z: number = 0,
|
|
24
|
+
) {
|
|
20
25
|
this.x = x;
|
|
21
26
|
this.y = y;
|
|
22
27
|
this.z = z;
|
|
@@ -107,6 +112,9 @@ jest.mock('three', () => {
|
|
|
107
112
|
this.traverse = jest.fn((callback) => {
|
|
108
113
|
callback(this.children[0]);
|
|
109
114
|
});
|
|
115
|
+
this.getWorldPosition = jest.fn(() => {
|
|
116
|
+
return this.position.clone();
|
|
117
|
+
});
|
|
110
118
|
return this;
|
|
111
119
|
}),
|
|
112
120
|
Box3: jest.fn(function () {
|
|
@@ -116,6 +124,7 @@ jest.mock('three', () => {
|
|
|
116
124
|
return new Vector3(0, 0, 0);
|
|
117
125
|
});
|
|
118
126
|
this.expandByObject = jest.fn();
|
|
127
|
+
this.makeEmpty = jest.fn();
|
|
119
128
|
|
|
120
129
|
return this;
|
|
121
130
|
}),
|
|
@@ -226,16 +235,37 @@ describe('dive/model/DIVEModel', () => {
|
|
|
226
235
|
});
|
|
227
236
|
|
|
228
237
|
it('should place on floor', () => {
|
|
238
|
+
const com = DIVECommunication.get('id')!;
|
|
239
|
+
const spyPerformAction = jest.spyOn(com, 'PerformAction');
|
|
240
|
+
|
|
229
241
|
model.userData.id = 'something';
|
|
230
242
|
|
|
231
|
-
|
|
243
|
+
model['_boundingBox'] = {
|
|
244
|
+
min: new Vector3(0, -1, 0),
|
|
245
|
+
} as unknown as Box3;
|
|
232
246
|
|
|
233
247
|
expect(() => model.PlaceOnFloor()).not.toThrow();
|
|
234
|
-
expect(
|
|
248
|
+
expect(spyPerformAction).toHaveBeenCalledWith(
|
|
249
|
+
'UPDATE_OBJECT',
|
|
250
|
+
expect.objectContaining({
|
|
251
|
+
position: expect.objectContaining({
|
|
252
|
+
y: 1,
|
|
253
|
+
}),
|
|
254
|
+
}),
|
|
255
|
+
);
|
|
235
256
|
|
|
257
|
+
// skip any action when the position did not change
|
|
258
|
+
spyPerformAction.mockClear();
|
|
259
|
+
model.position.y = 1;
|
|
260
|
+
expect(() => model.PlaceOnFloor()).not.toThrow();
|
|
261
|
+
expect(spyPerformAction).not.toHaveBeenCalled();
|
|
262
|
+
|
|
263
|
+
// mock that the communication is not available
|
|
264
|
+
spyPerformAction.mockClear();
|
|
236
265
|
jest.spyOn(DIVECommunication, 'get').mockReturnValueOnce(undefined);
|
|
266
|
+
model.position.y = 0;
|
|
237
267
|
expect(() => model.PlaceOnFloor()).not.toThrow();
|
|
238
|
-
expect(
|
|
268
|
+
expect(spyPerformAction).not.toHaveBeenCalled();
|
|
239
269
|
});
|
|
240
270
|
|
|
241
271
|
it('should drop it', () => {
|