@flighthq/geometry 0.1.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.
- package/dist/aabb.d.ts +88 -0
- package/dist/aabb.d.ts.map +1 -0
- package/dist/aabb.js +230 -0
- package/dist/aabb.js.map +1 -0
- package/dist/boundingSphere.d.ts +62 -0
- package/dist/boundingSphere.d.ts.map +1 -0
- package/dist/boundingSphere.js +188 -0
- package/dist/boundingSphere.js.map +1 -0
- package/dist/capsule.d.ts +38 -0
- package/dist/capsule.d.ts.map +1 -0
- package/dist/capsule.js +215 -0
- package/dist/capsule.js.map +1 -0
- package/dist/frustum.d.ts +49 -0
- package/dist/frustum.d.ts.map +1 -0
- package/dist/frustum.js +149 -0
- package/dist/frustum.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/matrix.d.ts +152 -0
- package/dist/matrix.d.ts.map +1 -0
- package/dist/matrix.js +488 -0
- package/dist/matrix.js.map +1 -0
- package/dist/matrix3.d.ts +63 -0
- package/dist/matrix3.d.ts.map +1 -0
- package/dist/matrix3.js +408 -0
- package/dist/matrix3.js.map +1 -0
- package/dist/matrix3Pool.d.ts +6 -0
- package/dist/matrix3Pool.d.ts.map +1 -0
- package/dist/matrix3Pool.js +26 -0
- package/dist/matrix3Pool.js.map +1 -0
- package/dist/matrix4.d.ts +238 -0
- package/dist/matrix4.d.ts.map +1 -0
- package/dist/matrix4.js +931 -0
- package/dist/matrix4.js.map +1 -0
- package/dist/matrix4Pool.d.ts +6 -0
- package/dist/matrix4Pool.d.ts.map +1 -0
- package/dist/matrix4Pool.js +26 -0
- package/dist/matrix4Pool.js.map +1 -0
- package/dist/matrixPool.d.ts +6 -0
- package/dist/matrixPool.d.ts.map +1 -0
- package/dist/matrixPool.js +26 -0
- package/dist/matrixPool.js.map +1 -0
- package/dist/obb.d.ts +44 -0
- package/dist/obb.d.ts.map +1 -0
- package/dist/obb.js +248 -0
- package/dist/obb.js.map +1 -0
- package/dist/plane.d.ts +66 -0
- package/dist/plane.d.ts.map +1 -0
- package/dist/plane.js +140 -0
- package/dist/plane.js.map +1 -0
- package/dist/quaternion.d.ts +128 -0
- package/dist/quaternion.d.ts.map +1 -0
- package/dist/quaternion.js +508 -0
- package/dist/quaternion.js.map +1 -0
- package/dist/quaternionPool.d.ts +6 -0
- package/dist/quaternionPool.d.ts.map +1 -0
- package/dist/quaternionPool.js +29 -0
- package/dist/quaternionPool.js.map +1 -0
- package/dist/ray3d.d.ts +69 -0
- package/dist/ray3d.d.ts.map +1 -0
- package/dist/ray3d.js +264 -0
- package/dist/ray3d.js.map +1 -0
- package/dist/rectangle.d.ts +56 -0
- package/dist/rectangle.d.ts.map +1 -0
- package/dist/rectangle.js +235 -0
- package/dist/rectangle.js.map +1 -0
- package/dist/rectanglePool.d.ts +6 -0
- package/dist/rectanglePool.d.ts.map +1 -0
- package/dist/rectanglePool.js +29 -0
- package/dist/rectanglePool.js.map +1 -0
- package/dist/typedarray.d.ts +4 -0
- package/dist/typedarray.d.ts.map +1 -0
- package/dist/typedarray.js +25 -0
- package/dist/typedarray.js.map +1 -0
- package/dist/vector2.d.ts +130 -0
- package/dist/vector2.d.ts.map +1 -0
- package/dist/vector2.js +250 -0
- package/dist/vector2.js.map +1 -0
- package/dist/vector2Pool.d.ts +6 -0
- package/dist/vector2Pool.d.ts.map +1 -0
- package/dist/vector2Pool.js +20 -0
- package/dist/vector2Pool.js.map +1 -0
- package/dist/vector3.d.ts +220 -0
- package/dist/vector3.d.ts.map +1 -0
- package/dist/vector3.js +380 -0
- package/dist/vector3.js.map +1 -0
- package/dist/vector3Pool.d.ts +6 -0
- package/dist/vector3Pool.d.ts.map +1 -0
- package/dist/vector3Pool.js +28 -0
- package/dist/vector3Pool.js.map +1 -0
- package/dist/vector4.d.ts +180 -0
- package/dist/vector4.d.ts.map +1 -0
- package/dist/vector4.js +333 -0
- package/dist/vector4.js.map +1 -0
- package/dist/vector4Pool.d.ts +6 -0
- package/dist/vector4Pool.d.ts.map +1 -0
- package/dist/vector4Pool.js +29 -0
- package/dist/vector4Pool.js.map +1 -0
- package/package.json +38 -0
- package/src/aabb.test.ts +341 -0
- package/src/boundingSphere.test.ts +247 -0
- package/src/capsule.test.ts +157 -0
- package/src/frustum.test.ts +160 -0
- package/src/matrix.test.ts +1111 -0
- package/src/matrix3.test.ts +752 -0
- package/src/matrix3Pool.test.ts +73 -0
- package/src/matrix4.test.ts +1627 -0
- package/src/matrix4Pool.test.ts +71 -0
- package/src/matrixPool.test.ts +69 -0
- package/src/obb.test.ts +183 -0
- package/src/plane.test.ts +225 -0
- package/src/quaternion.test.ts +531 -0
- package/src/quaternionPool.test.ts +64 -0
- package/src/ray3d.test.ts +297 -0
- package/src/rectangle.test.ts +982 -0
- package/src/rectanglePool.test.ts +71 -0
- package/src/typedarray.test.ts +118 -0
- package/src/vector2.test.ts +958 -0
- package/src/vector2Pool.test.ts +65 -0
- package/src/vector3.test.ts +749 -0
- package/src/vector3Pool.test.ts +68 -0
- package/src/vector4.test.ts +660 -0
- package/src/vector4Pool.test.ts +71 -0
|
@@ -0,0 +1,1111 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cloneMatrix,
|
|
3
|
+
copyMatrix,
|
|
4
|
+
copyMatrixColumnFromVector3,
|
|
5
|
+
copyMatrixColumnToVector3,
|
|
6
|
+
copyMatrixRowFromVector3,
|
|
7
|
+
copyMatrixRowToVector3,
|
|
8
|
+
createGradientTransformMatrix,
|
|
9
|
+
createMatrix,
|
|
10
|
+
createRectangle,
|
|
11
|
+
createTransformMatrix,
|
|
12
|
+
createVector2,
|
|
13
|
+
createVector3,
|
|
14
|
+
equalsMatrix,
|
|
15
|
+
getRectangleBottom,
|
|
16
|
+
getRectangleRight,
|
|
17
|
+
inverseMatrix,
|
|
18
|
+
inverseMatrixTransformPoint,
|
|
19
|
+
inverseMatrixTransformPointXY,
|
|
20
|
+
inverseMatrixTransformVector,
|
|
21
|
+
inverseMatrixTransformVectorXY,
|
|
22
|
+
matrixTransformBounds,
|
|
23
|
+
matrixTransformBoundsVector2,
|
|
24
|
+
matrixTransformPoint,
|
|
25
|
+
matrixTransformPointXY,
|
|
26
|
+
matrixTransformRectangle,
|
|
27
|
+
matrixTransformVector,
|
|
28
|
+
matrixTransformVectorXY,
|
|
29
|
+
multiplyMatrix,
|
|
30
|
+
rotateMatrix,
|
|
31
|
+
scaleMatrix,
|
|
32
|
+
setGradientTransformMatrix,
|
|
33
|
+
setMatrix,
|
|
34
|
+
setMatrixFromFloat32Array,
|
|
35
|
+
setMatrixFromMatrix3,
|
|
36
|
+
setMatrixFromMatrix4,
|
|
37
|
+
setMatrixIdentity,
|
|
38
|
+
setTransformMatrix,
|
|
39
|
+
translateMatrix,
|
|
40
|
+
translateMatrixByVector,
|
|
41
|
+
translateMatrixByVectorXY,
|
|
42
|
+
writeMatrixToFloat32Array,
|
|
43
|
+
} from '@flighthq/geometry';
|
|
44
|
+
import type { Matrix, Matrix3Like, Matrix4Like } from '@flighthq/types';
|
|
45
|
+
|
|
46
|
+
describe('cloneMatrix', () => {
|
|
47
|
+
it('should clone the matrix3x2 correctly', () => {
|
|
48
|
+
const m1 = createMatrix(2, 3, 4, 5, 6, 7);
|
|
49
|
+
const m2 = cloneMatrix(m1);
|
|
50
|
+
expect(m2.a).toBe(2);
|
|
51
|
+
expect(m2.b).toBe(3);
|
|
52
|
+
expect(m2.c).toBe(4);
|
|
53
|
+
expect(m2.d).toBe(5);
|
|
54
|
+
expect(m2.tx).toBe(6);
|
|
55
|
+
expect(m2.ty).toBe(7);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe('copyMatrix', () => {
|
|
60
|
+
it('should copy matrix3x2 values from another matrix3x2', () => {
|
|
61
|
+
const m1 = createMatrix(2, 3, 4, 5, 6, 7);
|
|
62
|
+
const m2 = createMatrix();
|
|
63
|
+
copyMatrix(m2, m1);
|
|
64
|
+
expect(m2.a).toBe(2);
|
|
65
|
+
expect(m2.b).toBe(3);
|
|
66
|
+
expect(m2.c).toBe(4);
|
|
67
|
+
expect(m2.d).toBe(5);
|
|
68
|
+
expect(m2.tx).toBe(6);
|
|
69
|
+
expect(m2.ty).toBe(7);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe('copyMatrixColumnFromVector3', () => {
|
|
74
|
+
it('should copy column from a vector3 to a matrix3x2', () => {
|
|
75
|
+
const m = createMatrix();
|
|
76
|
+
const v = createVector3(1, 2, 0);
|
|
77
|
+
copyMatrixColumnFromVector3(m, 0, v); // column 0
|
|
78
|
+
expect(m.a).toBe(1);
|
|
79
|
+
expect(m.b).toBe(2);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should copy column 1 (c, d)', () => {
|
|
83
|
+
const m = createMatrix();
|
|
84
|
+
const v = createVector3(3, 4, 0);
|
|
85
|
+
copyMatrixColumnFromVector3(m, 1, v);
|
|
86
|
+
expect(m.c).toBe(3);
|
|
87
|
+
expect(m.d).toBe(4);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should copy column 2 (tx, ty)', () => {
|
|
91
|
+
const m = createMatrix();
|
|
92
|
+
const v = createVector3(5, 6, 0);
|
|
93
|
+
copyMatrixColumnFromVector3(m, 2, v);
|
|
94
|
+
expect(m.tx).toBe(5);
|
|
95
|
+
expect(m.ty).toBe(6);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should throw when column is greater than 2', () => {
|
|
99
|
+
const m = createMatrix();
|
|
100
|
+
const v = createVector3();
|
|
101
|
+
expect(() => copyMatrixColumnFromVector3(m, 3, v)).toThrow();
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('copyMatrixColumnToVector3', () => {
|
|
106
|
+
it('should copy column to a vector3 from a matrix3x2', () => {
|
|
107
|
+
const m = createMatrix(1, 2, 3, 4, 5, 6);
|
|
108
|
+
const v = createVector3();
|
|
109
|
+
copyMatrixColumnToVector3(v, 0, m); // column 0
|
|
110
|
+
expect(v.x).toBe(1);
|
|
111
|
+
expect(v.y).toBe(2);
|
|
112
|
+
expect(v.z).toBe(0);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should copy column 1 into vector3', () => {
|
|
116
|
+
const m = createMatrix(0, 0, 3, 4, 0, 0);
|
|
117
|
+
const v = createVector3();
|
|
118
|
+
copyMatrixColumnToVector3(v, 1, m);
|
|
119
|
+
expect(v.x).toBe(3);
|
|
120
|
+
expect(v.y).toBe(4);
|
|
121
|
+
expect(v.z).toBe(0);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('should copy column 2 into vector3 and set z to 1', () => {
|
|
125
|
+
const m = createMatrix(0, 0, 0, 0, 7, 8);
|
|
126
|
+
const v = createVector3();
|
|
127
|
+
copyMatrixColumnToVector3(v, 2, m);
|
|
128
|
+
expect(v.x).toBe(7);
|
|
129
|
+
expect(v.y).toBe(8);
|
|
130
|
+
expect(v.z).toBe(1);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('should throw when column is greater than 2', () => {
|
|
134
|
+
const m = createMatrix();
|
|
135
|
+
const v = createVector3();
|
|
136
|
+
expect(() => copyMatrixColumnToVector3(v, 3, m)).toThrow();
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe('copyMatrixRowFromVector3', () => {
|
|
141
|
+
it('should copy row from a vector3 to a matrix3x2', () => {
|
|
142
|
+
const m = createMatrix();
|
|
143
|
+
const v = createVector3(1, 2, 3);
|
|
144
|
+
copyMatrixRowFromVector3(m, 0, v); // row 0
|
|
145
|
+
expect(m.a).toBe(1);
|
|
146
|
+
expect(m.c).toBe(2);
|
|
147
|
+
expect(m.tx).toBe(3);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('should copy row 1 (b, d, ty)', () => {
|
|
151
|
+
const m = createMatrix();
|
|
152
|
+
const v = createVector3(2, 4, 6);
|
|
153
|
+
copyMatrixRowFromVector3(m, 1, v);
|
|
154
|
+
expect(m.b).toBe(2);
|
|
155
|
+
expect(m.d).toBe(4);
|
|
156
|
+
expect(m.ty).toBe(6);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('should throw when row is greater than 2', () => {
|
|
160
|
+
const m = createMatrix();
|
|
161
|
+
const v = createVector3();
|
|
162
|
+
expect(() => copyMatrixRowFromVector3(m, 3, v)).toThrow();
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
describe('copyMatrixRowToVector3', () => {
|
|
167
|
+
it('should copy row to a vector3 from a matrix3x2', () => {
|
|
168
|
+
const m = createMatrix(1, 2, 3, 4, 5, 6);
|
|
169
|
+
const v = createVector3();
|
|
170
|
+
copyMatrixRowToVector3(v, 0, m); // row 0
|
|
171
|
+
expect(v.x).toBe(1); // m.a
|
|
172
|
+
expect(v.y).toBe(3); // m.c
|
|
173
|
+
expect(v.z).toBe(5); // m.tx
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('should copy row 1 (b, d, ty)', () => {
|
|
177
|
+
const m = createMatrix(1, 2, 3, 4, 5, 6);
|
|
178
|
+
const v = createVector3();
|
|
179
|
+
copyMatrixRowToVector3(v, 1, m);
|
|
180
|
+
expect(v.x).toBe(2);
|
|
181
|
+
expect(v.y).toBe(4);
|
|
182
|
+
expect(v.z).toBe(6);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('should return (0, 0, 1) for row 2', () => {
|
|
186
|
+
const m = createMatrix();
|
|
187
|
+
const v = createVector3();
|
|
188
|
+
copyMatrixRowToVector3(v, 2, m);
|
|
189
|
+
expect(v.x).toBe(0);
|
|
190
|
+
expect(v.y).toBe(0);
|
|
191
|
+
expect(v.z).toBe(1);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
describe('createGradientTransformMatrix', () => {
|
|
196
|
+
it('returns a Matrix equivalent to calling setGradientTransform', () => {
|
|
197
|
+
const m1 = createGradientTransformMatrix(100, 200);
|
|
198
|
+
const m2 = createMatrix();
|
|
199
|
+
setGradientTransformMatrix(m2, 100, 200);
|
|
200
|
+
expect(equalsMatrix(m1, m2)).toBe(true);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('sets tx to tx + width / 2 and ty to ty + height / 2', () => {
|
|
204
|
+
const m = createGradientTransformMatrix(100, 200, 0, 10, 20);
|
|
205
|
+
expect(m.tx).toBeCloseTo(60); // 10 + 100/2
|
|
206
|
+
expect(m.ty).toBeCloseTo(120); // 20 + 200/2
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
describe('createMatrix', () => {
|
|
211
|
+
it('should initialize matrix3x2 with provided values', () => {
|
|
212
|
+
const m = createMatrix(2, 3, 4, 5, 6, 7);
|
|
213
|
+
expect(m.a).toBe(2);
|
|
214
|
+
expect(m.b).toBe(3);
|
|
215
|
+
expect(m.c).toBe(4);
|
|
216
|
+
expect(m.d).toBe(5);
|
|
217
|
+
expect(m.tx).toBe(6);
|
|
218
|
+
expect(m.ty).toBe(7);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('should default to identity matrix3x2 when no values are provided', () => {
|
|
222
|
+
const m = createMatrix();
|
|
223
|
+
expect(m.a).toBe(1);
|
|
224
|
+
expect(m.b).toBe(0);
|
|
225
|
+
expect(m.c).toBe(0);
|
|
226
|
+
expect(m.d).toBe(1);
|
|
227
|
+
expect(m.tx).toBe(0);
|
|
228
|
+
expect(m.ty).toBe(0);
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
describe('createTransformMatrix', () => {
|
|
233
|
+
it('should create a createMatrix and call setTransform', () => {
|
|
234
|
+
const m1 = createTransformMatrix(2, 4, 45, 10, 100);
|
|
235
|
+
const m2 = createMatrix();
|
|
236
|
+
setTransformMatrix(m2, 2, 4, 45, 10, 100);
|
|
237
|
+
expect(equalsMatrix(m1, m2)).toBe(true);
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
describe('equalsMatrix', () => {
|
|
242
|
+
it('should return false if one matrix3x2 is null and the other is not', () => {
|
|
243
|
+
const mat1 = createMatrix();
|
|
244
|
+
expect(equalsMatrix(mat1, null)).toBe(false);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('should return false if one matrix3x2 is undefined and the other is not', () => {
|
|
248
|
+
const mat1 = createMatrix();
|
|
249
|
+
expect(equalsMatrix(mat1, undefined)).toBe(false);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('should return true if both matrix3x2 objects are null', () => {
|
|
253
|
+
expect(equalsMatrix(null, null)).toBe(true);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it('should return true if both matrix3x2 objects are undefined', () => {
|
|
257
|
+
expect(equalsMatrix(undefined, undefined)).toBe(true);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it('should return true if one matrix3x2 object is undefined and the other is null', () => {
|
|
261
|
+
expect(equalsMatrix(undefined, undefined)).toBe(true);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it('should return false if both matrix3x2 objects are defined and have different values', () => {
|
|
265
|
+
const mat1 = createMatrix();
|
|
266
|
+
const mat2 = createMatrix();
|
|
267
|
+
mat2.a = 2;
|
|
268
|
+
expect(equalsMatrix(mat1, mat2)).toBe(false);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('should allow differences in translation if includeTranslation is false', () => {
|
|
272
|
+
const mat1 = createMatrix();
|
|
273
|
+
const mat2 = createMatrix();
|
|
274
|
+
mat2.tx = 100;
|
|
275
|
+
expect(equalsMatrix(mat1, mat2, false)).toBe(true);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it('should not allow differences in translation if includeTranslation is true', () => {
|
|
279
|
+
const mat1 = createMatrix();
|
|
280
|
+
const mat2 = createMatrix();
|
|
281
|
+
mat2.tx = 100;
|
|
282
|
+
expect(equalsMatrix(mat1, mat2, true)).toBe(false);
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
describe('inverseMatrix', () => {
|
|
287
|
+
it('should invert the matrix3x2 correctly', () => {
|
|
288
|
+
// Create a matrix3x2 with scaling of 2 and translation of (5, 3)
|
|
289
|
+
const m = createMatrix(2, 0, 0, 2, 5, 3);
|
|
290
|
+
|
|
291
|
+
// Apply inversion
|
|
292
|
+
let out = createMatrix();
|
|
293
|
+
inverseMatrix(out, m);
|
|
294
|
+
|
|
295
|
+
// Expected inverse matrix3x2:
|
|
296
|
+
// Scaling should be 0.5 (inverse of 2)
|
|
297
|
+
// Translation should be -2.5 (inverse of 5 scaled by 0.5) and -1.5 (inverse of 3 scaled by 0.5)
|
|
298
|
+
|
|
299
|
+
// Assert the inverse matrix3x2 values
|
|
300
|
+
expect(out.a).toBeCloseTo(0.5); // Inverse scaling on x
|
|
301
|
+
expect(out.b).toBeCloseTo(0); // No shear on x
|
|
302
|
+
expect(out.c).toBeCloseTo(0); // No shear on y
|
|
303
|
+
expect(out.d).toBeCloseTo(0.5); // Inverse scaling on y
|
|
304
|
+
expect(out.tx).toBeCloseTo(-2.5); // Inverse translation on x
|
|
305
|
+
expect(out.ty).toBeCloseTo(-1.5); // Inverse translation on y
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it('should not depend on initial out matrix3x2 values', () => {
|
|
309
|
+
const source = createMatrix(2, 1, 3, 4, 5, 6);
|
|
310
|
+
const out = createMatrix(9, 9, 9, 9, 9, 9);
|
|
311
|
+
|
|
312
|
+
inverseMatrix(out, source);
|
|
313
|
+
|
|
314
|
+
const result = createMatrix();
|
|
315
|
+
multiplyMatrix(result, source, out);
|
|
316
|
+
|
|
317
|
+
expect(result.a).toBeCloseTo(1);
|
|
318
|
+
expect(result.b).toBeCloseTo(0);
|
|
319
|
+
expect(result.c).toBeCloseTo(0);
|
|
320
|
+
expect(result.d).toBeCloseTo(1);
|
|
321
|
+
expect(result.tx).toBeCloseTo(0);
|
|
322
|
+
expect(result.ty).toBeCloseTo(0);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
it('supports out === source', () => {
|
|
326
|
+
const matrix = createMatrix(2, 1, 3, 4, 5, 6);
|
|
327
|
+
const expected = createMatrix();
|
|
328
|
+
inverseMatrix(expected, matrix);
|
|
329
|
+
|
|
330
|
+
inverseMatrix(matrix, matrix);
|
|
331
|
+
|
|
332
|
+
expect(equalsMatrix(matrix, expected)).toBe(true);
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
describe('inverseMatrixTransformPoint', () => {
|
|
337
|
+
it('should apply inverse transformation to a point', () => {
|
|
338
|
+
const m = createMatrix(2, 0, 0, 2, 0, 0);
|
|
339
|
+
const p = createVector2(2, 2);
|
|
340
|
+
const transformedvector2 = createVector2();
|
|
341
|
+
inverseMatrixTransformPoint(transformedvector2, m, p);
|
|
342
|
+
expect(transformedvector2.x).toBe(1);
|
|
343
|
+
expect(transformedvector2.y).toBe(1);
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
it('should return a new point', () => {
|
|
347
|
+
const m = createMatrix(2, 0, 0, 2, 0, 0);
|
|
348
|
+
const p = createVector2(2, 2);
|
|
349
|
+
const transformedvector2 = createVector2();
|
|
350
|
+
inverseMatrixTransformPoint(transformedvector2, m, p);
|
|
351
|
+
expect(p).not.toBe(transformedvector2);
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it('should not modify original point', () => {
|
|
355
|
+
const m = createMatrix(2, 0, 0, 2, 0, 0);
|
|
356
|
+
const p = createVector2(2, 2);
|
|
357
|
+
const out = createVector2();
|
|
358
|
+
inverseMatrixTransformPoint(out, m, p);
|
|
359
|
+
expect(p.x).toBe(2);
|
|
360
|
+
expect(p.y).toBe(2);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it('supports out === point', () => {
|
|
364
|
+
const m = createMatrix(2, 0, 0, 2, 0, 0);
|
|
365
|
+
const p = createVector2(2, 4);
|
|
366
|
+
inverseMatrixTransformPoint(p, m, p);
|
|
367
|
+
expect(p.x).toBe(1);
|
|
368
|
+
expect(p.y).toBe(2);
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
describe('inverseMatrixTransformPointXY', () => {
|
|
373
|
+
it('should apply inverse transformation to a point', () => {
|
|
374
|
+
const m = createMatrix(2, 0, 0, 2, 0, 0);
|
|
375
|
+
let transformedvector2 = createVector2();
|
|
376
|
+
inverseMatrixTransformPointXY(transformedvector2, m, 2, 2);
|
|
377
|
+
expect(transformedvector2.x).toBe(1);
|
|
378
|
+
expect(transformedvector2.y).toBe(1);
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
it('should handle singular matrices', () => {
|
|
382
|
+
const m = createMatrix(1, 2, 2, 4, 10, 20); // determinant = 0
|
|
383
|
+
const out = createVector2();
|
|
384
|
+
|
|
385
|
+
inverseMatrixTransformPointXY(out, m, 5, 5);
|
|
386
|
+
|
|
387
|
+
expect(out.x).toBe(-10);
|
|
388
|
+
expect(out.y).toBe(-20);
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
describe('inverseMatrixTransformVector', () => {
|
|
393
|
+
it('should apply inverse transformation to a point', () => {
|
|
394
|
+
const m = createMatrix(2, 0, 0, 2, 0, 0);
|
|
395
|
+
const p = createVector2(2, 2);
|
|
396
|
+
const transformedvector2 = createVector2();
|
|
397
|
+
inverseMatrixTransformVector(transformedvector2, m, p);
|
|
398
|
+
expect(transformedvector2.x).toBe(1);
|
|
399
|
+
expect(transformedvector2.y).toBe(1);
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
it('should return a new point', () => {
|
|
403
|
+
const m = createMatrix(2, 0, 0, 2, 0, 0);
|
|
404
|
+
const p = createVector2(2, 2);
|
|
405
|
+
const transformedvector2 = createVector2();
|
|
406
|
+
inverseMatrixTransformVector(transformedvector2, m, p);
|
|
407
|
+
expect(p).not.toBe(transformedvector2);
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it('should not modify original point', () => {
|
|
411
|
+
const m = createMatrix(2, 0, 0, 2, 0, 0);
|
|
412
|
+
const p = createVector2(2, 2);
|
|
413
|
+
const out = createVector2();
|
|
414
|
+
inverseMatrixTransformVector(out, m, p);
|
|
415
|
+
expect(p.x).toBe(2);
|
|
416
|
+
expect(p.y).toBe(2);
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it('supports out === vector', () => {
|
|
420
|
+
const m = createMatrix(2, 0, 0, 4, 10, 20);
|
|
421
|
+
const p = createVector2(2, 8);
|
|
422
|
+
inverseMatrixTransformVector(p, m, p);
|
|
423
|
+
expect(p.x).toBe(1);
|
|
424
|
+
expect(p.y).toBe(2);
|
|
425
|
+
});
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
describe('inverseMatrixTransformVectorXY', () => {
|
|
429
|
+
it('should apply inverse transformation to a point', () => {
|
|
430
|
+
const m = createMatrix(2, 0, 0, 2, 0, 0);
|
|
431
|
+
let transformedvector2 = createVector2();
|
|
432
|
+
inverseMatrixTransformVectorXY(transformedvector2, m, 2, 2);
|
|
433
|
+
expect(transformedvector2.x).toBe(1);
|
|
434
|
+
expect(transformedvector2.y).toBe(1);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
it('should handle singular matrices', () => {
|
|
438
|
+
const m = createMatrix(1, 2, 2, 4, 10, 20); // determinant = 0
|
|
439
|
+
const out = createVector2();
|
|
440
|
+
|
|
441
|
+
inverseMatrixTransformVectorXY(out, m, 5, 5);
|
|
442
|
+
|
|
443
|
+
expect(out.x).toBe(0);
|
|
444
|
+
expect(out.y).toBe(0);
|
|
445
|
+
});
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
describe('matrixTransformBounds', () => {
|
|
449
|
+
it('should work when ax > bx or ay > by (flipped input)', () => {
|
|
450
|
+
const m = createMatrix();
|
|
451
|
+
const out = createRectangle();
|
|
452
|
+
matrixTransformBounds(out, m, 10, 10, 0, 0);
|
|
453
|
+
expect(out.x).toBe(0);
|
|
454
|
+
expect(out.y).toBe(0);
|
|
455
|
+
expect(out.width).toBe(10);
|
|
456
|
+
expect(out.height).toBe(10);
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
it('should handle negative scaling', () => {
|
|
460
|
+
const m = createMatrix(-1, 0, 0, -1, 0, 0);
|
|
461
|
+
const out = createRectangle();
|
|
462
|
+
matrixTransformBounds(out, m, 0, 0, 10, 10);
|
|
463
|
+
expect(out.width).toBe(10);
|
|
464
|
+
expect(out.height).toBe(10);
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
it('should handle rotation', () => {
|
|
468
|
+
const m = createMatrix();
|
|
469
|
+
rotateMatrix(m, m, Math.PI / 2);
|
|
470
|
+
const out = createRectangle();
|
|
471
|
+
matrixTransformBounds(out, m, 0, 0, 10, 20);
|
|
472
|
+
expect(out.width).toBeCloseTo(20);
|
|
473
|
+
expect(out.height).toBeCloseTo(10);
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
it('should handle flipped input coordinates', () => {
|
|
477
|
+
const rect = createRectangle(10, 20, -10, -20);
|
|
478
|
+
const mat = createMatrix();
|
|
479
|
+
|
|
480
|
+
const out = createRectangle();
|
|
481
|
+
matrixTransformBounds(out, mat, rect.x, rect.y, getRectangleRight(rect), getRectangleBottom(rect));
|
|
482
|
+
|
|
483
|
+
expect(out.x).toBeCloseTo(0);
|
|
484
|
+
expect(out.y).toBeCloseTo(0);
|
|
485
|
+
expect(out.width).toBeCloseTo(10);
|
|
486
|
+
expect(out.height).toBeCloseTo(20);
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
it('should handle negative scaling (mirroring)', () => {
|
|
490
|
+
const rect = createRectangle(0, 0, 10, 20);
|
|
491
|
+
const mat = createMatrix(-1, 0, 0, -1, 0, 0);
|
|
492
|
+
|
|
493
|
+
const out = createRectangle();
|
|
494
|
+
matrixTransformBounds(out, mat, rect.x, rect.y, getRectangleRight(rect), getRectangleBottom(rect));
|
|
495
|
+
|
|
496
|
+
expect(out.width).toBeCloseTo(10);
|
|
497
|
+
expect(out.height).toBeCloseTo(20);
|
|
498
|
+
});
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
describe('matrixTransformBoundsVector2', () => {
|
|
502
|
+
it('should alias transformRectXY', () => {
|
|
503
|
+
const m = createMatrix();
|
|
504
|
+
const out = createRectangle();
|
|
505
|
+
const a = createVector2(10, 10);
|
|
506
|
+
const b = createVector2();
|
|
507
|
+
matrixTransformBoundsVector2(out, m, a, b);
|
|
508
|
+
expect(out.x).toBe(0);
|
|
509
|
+
expect(out.y).toBe(0);
|
|
510
|
+
expect(out.width).toBe(10);
|
|
511
|
+
expect(out.height).toBe(10);
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
it('supports out === a', () => {
|
|
515
|
+
const m = createMatrix(2, 0, 0, 3, 5, 7);
|
|
516
|
+
const out = createRectangle(0, 0, 0, 0);
|
|
517
|
+
const b = createVector2(10, 20);
|
|
518
|
+
matrixTransformBoundsVector2(out, m, out, b);
|
|
519
|
+
expect(out.x).toBe(5);
|
|
520
|
+
expect(out.y).toBe(7);
|
|
521
|
+
expect(out.width).toBe(20);
|
|
522
|
+
expect(out.height).toBe(60);
|
|
523
|
+
});
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
describe('matrixTransformPoint', () => {
|
|
527
|
+
it('should transform a point using the matrix3x2', () => {
|
|
528
|
+
const m = createMatrix(1, 0, 0, 1, 10, 20);
|
|
529
|
+
const p = createVector2(1, 1);
|
|
530
|
+
const transformedvector2 = createVector2();
|
|
531
|
+
matrixTransformPoint(transformedvector2, m, p);
|
|
532
|
+
expect(transformedvector2.x).toBe(11);
|
|
533
|
+
expect(transformedvector2.y).toBe(21);
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
it('should not return same point', () => {
|
|
537
|
+
const m = createMatrix(1, 0, 0, 1, 10, 20);
|
|
538
|
+
const p = createVector2(1, 1);
|
|
539
|
+
const transformedvector2 = createVector2();
|
|
540
|
+
matrixTransformPoint(transformedvector2, m, p);
|
|
541
|
+
expect(p).not.toBe(transformedvector2);
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
it('should not modify input point', () => {
|
|
545
|
+
const m = createMatrix(1, 0, 0, 1, 10, 20);
|
|
546
|
+
const p = createVector2(1, 1);
|
|
547
|
+
const out = createVector2();
|
|
548
|
+
matrixTransformPoint(out, m, p);
|
|
549
|
+
expect(p.x).toBe(1);
|
|
550
|
+
expect(p.y).toBe(1);
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
it('supports out === point', () => {
|
|
554
|
+
const m = createMatrix(2, 0, 0, 3, 10, 20);
|
|
555
|
+
const p = createVector2(1, 2);
|
|
556
|
+
matrixTransformPoint(p, m, p);
|
|
557
|
+
expect(p.x).toBe(12);
|
|
558
|
+
expect(p.y).toBe(26);
|
|
559
|
+
});
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
describe('matrixTransformPointXY', () => {
|
|
563
|
+
it('should correctly transform coordinates with translation', () => {
|
|
564
|
+
const m = createMatrix(1, 0, 0, 1, 5, 6);
|
|
565
|
+
const p = createVector2();
|
|
566
|
+
matrixTransformPointXY(p, m, 1, 2);
|
|
567
|
+
expect(p.x).toBe(6);
|
|
568
|
+
expect(p.y).toBe(8);
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
it('should handle rotation correctly', () => {
|
|
572
|
+
const m = createMatrix();
|
|
573
|
+
rotateMatrix(m, m, Math.PI / 2);
|
|
574
|
+
const p = createVector2();
|
|
575
|
+
matrixTransformPointXY(p, m, 1, 0);
|
|
576
|
+
expect(p.x).toBeCloseTo(0);
|
|
577
|
+
expect(p.y).toBeCloseTo(1);
|
|
578
|
+
});
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
describe('matrixTransformRectangle', () => {
|
|
582
|
+
it('should return the same rectangle for identity matrix3x2', () => {
|
|
583
|
+
const rect = createRectangle(0, 0, 10, 20);
|
|
584
|
+
const mat = createMatrix(); // identity by default
|
|
585
|
+
const out = createRectangle();
|
|
586
|
+
matrixTransformRectangle(out, mat, rect);
|
|
587
|
+
expect(out.x).toBeCloseTo(0);
|
|
588
|
+
expect(out.y).toBeCloseTo(0);
|
|
589
|
+
expect(out.width).toBeCloseTo(10);
|
|
590
|
+
expect(out.height).toBeCloseTo(20);
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
it('should apply translation correctly', () => {
|
|
594
|
+
const rect = createRectangle(0, 0, 10, 20);
|
|
595
|
+
const mat = createMatrix();
|
|
596
|
+
mat.tx = 5;
|
|
597
|
+
mat.ty = 7;
|
|
598
|
+
const out = createRectangle();
|
|
599
|
+
matrixTransformRectangle(out, mat, rect);
|
|
600
|
+
expect(out.x).toBeCloseTo(5);
|
|
601
|
+
expect(out.y).toBeCloseTo(7);
|
|
602
|
+
expect(out.width).toBeCloseTo(10);
|
|
603
|
+
expect(out.height).toBeCloseTo(20);
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
it('should apply uniform scaling correctly', () => {
|
|
607
|
+
const rect = createRectangle(0, 0, 10, 20);
|
|
608
|
+
const mat = createMatrix();
|
|
609
|
+
mat.a = 2; // scaleX
|
|
610
|
+
mat.d = 3; // scaleY
|
|
611
|
+
const out = createRectangle();
|
|
612
|
+
matrixTransformRectangle(out, mat, rect);
|
|
613
|
+
expect(out.x).toBeCloseTo(0);
|
|
614
|
+
expect(out.y).toBeCloseTo(0);
|
|
615
|
+
expect(out.width).toBeCloseTo(20);
|
|
616
|
+
expect(out.height).toBeCloseTo(60);
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
it('should handle rotation correctly', () => {
|
|
620
|
+
const rect = createRectangle(0, 0, 10, 20);
|
|
621
|
+
const mat = createMatrix();
|
|
622
|
+
const angle = Math.PI / 2; // 90 degrees
|
|
623
|
+
mat.a = Math.cos(angle);
|
|
624
|
+
mat.b = Math.sin(angle);
|
|
625
|
+
mat.c = -Math.sin(angle);
|
|
626
|
+
mat.d = Math.cos(angle);
|
|
627
|
+
|
|
628
|
+
const out = createRectangle();
|
|
629
|
+
matrixTransformRectangle(out, mat, rect);
|
|
630
|
+
// After 90° rotation, width and height swap in axis-aligned bounding box
|
|
631
|
+
expect(out.width).toBeCloseTo(20);
|
|
632
|
+
expect(out.height).toBeCloseTo(10);
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
it('should handle skew correctly', () => {
|
|
636
|
+
const rect = createRectangle(0, 0, 10, 10);
|
|
637
|
+
const mat = createMatrix();
|
|
638
|
+
mat.c = 1; // skew X
|
|
639
|
+
mat.b = 0.5; // skew Y
|
|
640
|
+
|
|
641
|
+
const out = createRectangle();
|
|
642
|
+
matrixTransformRectangle(out, mat, rect);
|
|
643
|
+
// For 10x10, transformed width and height increase due to skew
|
|
644
|
+
expect(out.width).toBeCloseTo(10 + 10 * 1); // 20
|
|
645
|
+
expect(out.height).toBeCloseTo(10 + 10 * 0.5); // 15
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
it('should not return same object', () => {
|
|
649
|
+
const rect = createRectangle(0, 0, 10, 10);
|
|
650
|
+
const mat = createMatrix();
|
|
651
|
+
mat.tx = 5;
|
|
652
|
+
mat.ty = 7;
|
|
653
|
+
|
|
654
|
+
const out = createRectangle();
|
|
655
|
+
matrixTransformRectangle(out, mat, rect);
|
|
656
|
+
expect(rect).not.toBe(out);
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
it('should not modify input object', () => {
|
|
660
|
+
const rect = createRectangle(0, 0, 10, 10);
|
|
661
|
+
const mat = createMatrix();
|
|
662
|
+
mat.tx = 5;
|
|
663
|
+
mat.ty = 7;
|
|
664
|
+
|
|
665
|
+
const out = createRectangle();
|
|
666
|
+
matrixTransformRectangle(out, mat, rect);
|
|
667
|
+
expect(rect.x).toBeCloseTo(0);
|
|
668
|
+
expect(rect.y).toBeCloseTo(0);
|
|
669
|
+
expect(rect.width).toBeCloseTo(10);
|
|
670
|
+
expect(rect.height).toBeCloseTo(10);
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
it('supports out === source', () => {
|
|
674
|
+
const rect = createRectangle(0, 0, 10, 20);
|
|
675
|
+
const mat = createMatrix(2, 0, 0, 3, 5, 7);
|
|
676
|
+
matrixTransformRectangle(rect, mat, rect);
|
|
677
|
+
expect(rect.x).toBeCloseTo(5);
|
|
678
|
+
expect(rect.y).toBeCloseTo(7);
|
|
679
|
+
expect(rect.width).toBeCloseTo(20);
|
|
680
|
+
expect(rect.height).toBeCloseTo(60);
|
|
681
|
+
});
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
describe('matrixTransformVector', () => {
|
|
685
|
+
it('should apply delta transformation to a point', () => {
|
|
686
|
+
const m = createMatrix(2, 0, 0, 2, 0, 0);
|
|
687
|
+
const p = createVector2(1, 1);
|
|
688
|
+
const transformedvector2 = createVector2();
|
|
689
|
+
matrixTransformVector(transformedvector2, m, p);
|
|
690
|
+
expect(transformedvector2.x).toBe(2);
|
|
691
|
+
expect(transformedvector2.y).toBe(2);
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
it('should not modify input point', () => {
|
|
695
|
+
const m = createMatrix(2, 0, 0, 2, 0, 0);
|
|
696
|
+
const p = createVector2(1, 1);
|
|
697
|
+
const out = createVector2();
|
|
698
|
+
matrixTransformVector(out, m, p);
|
|
699
|
+
expect(p.x).toBe(1);
|
|
700
|
+
expect(p.y).toBe(1);
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
it('supports out === vector', () => {
|
|
704
|
+
const m = createMatrix(2, 0, 0, 3, 10, 20);
|
|
705
|
+
const p = createVector2(1, 2);
|
|
706
|
+
matrixTransformVector(p, m, p);
|
|
707
|
+
expect(p.x).toBe(2);
|
|
708
|
+
expect(p.y).toBe(6);
|
|
709
|
+
});
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
describe('matrixTransformVectorXY', () => {
|
|
713
|
+
it('should apply delta transformation to a point', () => {
|
|
714
|
+
const m = createMatrix(2, 0, 0, 2, 0, 0);
|
|
715
|
+
const transformedvector2 = createVector2();
|
|
716
|
+
matrixTransformVectorXY(transformedvector2, m, 1, 1);
|
|
717
|
+
expect(transformedvector2.x).toBe(2);
|
|
718
|
+
expect(transformedvector2.y).toBe(2);
|
|
719
|
+
});
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
describe('multiplyMatrix', () => {
|
|
723
|
+
it('should support out === a', () => {
|
|
724
|
+
const a = createMatrix(2, 0, 0, 2, 1, 1);
|
|
725
|
+
const b = createMatrix(1, 0, 0, 1, 5, 6);
|
|
726
|
+
|
|
727
|
+
// out = a × b
|
|
728
|
+
multiplyMatrix(a, a, b);
|
|
729
|
+
|
|
730
|
+
// translation = A.linear × B.translation + A.translation
|
|
731
|
+
expect(a.tx).toBe(2 * 5 + 1); // 11
|
|
732
|
+
expect(a.ty).toBe(2 * 6 + 1); // 13
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
it('should support out === b', () => {
|
|
736
|
+
const a = createMatrix(2, 0, 0, 2, 0, 0);
|
|
737
|
+
const b = createMatrix(1, 0, 0, 1, 3, 4);
|
|
738
|
+
|
|
739
|
+
multiplyMatrix(b, a, b);
|
|
740
|
+
|
|
741
|
+
expect(b.a).toBe(2);
|
|
742
|
+
expect(b.d).toBe(2);
|
|
743
|
+
expect(b.tx).toBe(6); // 2 * 3
|
|
744
|
+
expect(b.ty).toBe(8); // 2 * 4
|
|
745
|
+
});
|
|
746
|
+
|
|
747
|
+
it('should multiply identity correctly', () => {
|
|
748
|
+
const identity = createMatrix();
|
|
749
|
+
const m = createMatrix(2, 3, 4, 5, 6, 7);
|
|
750
|
+
const out = createMatrix();
|
|
751
|
+
|
|
752
|
+
multiplyMatrix(out, identity, m);
|
|
753
|
+
expect(equalsMatrix(out, m)).toBe(true);
|
|
754
|
+
|
|
755
|
+
multiplyMatrix(out, m, identity);
|
|
756
|
+
expect(equalsMatrix(out, m)).toBe(true);
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
it('should handle negative scale factors', () => {
|
|
760
|
+
const m1 = createMatrix(2, 0, 0, 2, 0, 0);
|
|
761
|
+
const m2 = createMatrix(-1, 0, 0, -1, 0, 0);
|
|
762
|
+
|
|
763
|
+
const out = createMatrix();
|
|
764
|
+
multiplyMatrix(out, m1, m2);
|
|
765
|
+
|
|
766
|
+
expect(out.a).toBe(-2);
|
|
767
|
+
expect(out.b).toBe(0);
|
|
768
|
+
expect(out.c).toBe(0);
|
|
769
|
+
expect(out.d).toBe(-2);
|
|
770
|
+
expect(out.tx).toBe(0);
|
|
771
|
+
expect(out.ty).toBe(0);
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
it('should handle rotation multiplication', () => {
|
|
775
|
+
const angle = Math.PI / 4;
|
|
776
|
+
const r = createMatrix(Math.cos(angle), Math.sin(angle), -Math.sin(angle), Math.cos(angle), 0, 0);
|
|
777
|
+
|
|
778
|
+
const out = createMatrix();
|
|
779
|
+
multiplyMatrix(out, r, r); // r² = rotation by 90°
|
|
780
|
+
|
|
781
|
+
expect(out.a).toBeCloseTo(0, 5);
|
|
782
|
+
expect(out.b).toBeCloseTo(1, 5);
|
|
783
|
+
expect(out.c).toBeCloseTo(-1, 5);
|
|
784
|
+
expect(out.d).toBeCloseTo(0, 5);
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
it('should handle non-uniform scaling', () => {
|
|
788
|
+
const scaleY = createMatrix(1, 0, 0, 2, 0, 0);
|
|
789
|
+
const scaleX = createMatrix(2, 0, 0, 1, 0, 0);
|
|
790
|
+
|
|
791
|
+
const out = createMatrix();
|
|
792
|
+
multiplyMatrix(out, scaleY, scaleX);
|
|
793
|
+
|
|
794
|
+
expect(out.a).toBe(2);
|
|
795
|
+
expect(out.b).toBe(0);
|
|
796
|
+
expect(out.c).toBe(0);
|
|
797
|
+
expect(out.d).toBe(2);
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
it('should handle translation correctly', () => {
|
|
801
|
+
const a = createMatrix(2, 0, 0, 2, 1, 1);
|
|
802
|
+
const b = createMatrix(1, 0, 0, 1, 3, 4);
|
|
803
|
+
|
|
804
|
+
const out = createMatrix();
|
|
805
|
+
multiplyMatrix(out, a, b);
|
|
806
|
+
|
|
807
|
+
// t' = A.linear × B.translation + A.translation
|
|
808
|
+
expect(out.tx).toBe(2 * 3 + 1); // 7
|
|
809
|
+
expect(out.ty).toBe(2 * 4 + 1); // 9
|
|
810
|
+
});
|
|
811
|
+
|
|
812
|
+
it('should handle negative values consistently', () => {
|
|
813
|
+
const a = createMatrix(-1, 0, 0, -1, 0, 0);
|
|
814
|
+
const b = createMatrix(1, 0, 0, 1, -2, -3);
|
|
815
|
+
|
|
816
|
+
const out = createMatrix();
|
|
817
|
+
multiplyMatrix(out, a, b);
|
|
818
|
+
|
|
819
|
+
expect(out.tx).toBe(2);
|
|
820
|
+
expect(out.ty).toBe(3);
|
|
821
|
+
});
|
|
822
|
+
});
|
|
823
|
+
|
|
824
|
+
describe('rotateMatrix', () => {
|
|
825
|
+
it('should write rotated result to out without modifying source', () => {
|
|
826
|
+
const src = createMatrix(1, 0, 0, 1, 10, 0);
|
|
827
|
+
const out = createMatrix();
|
|
828
|
+
rotateMatrix(out, src, Math.PI / 2);
|
|
829
|
+
expect(src.tx).toBe(10);
|
|
830
|
+
expect(out.tx).toBeCloseTo(0);
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
it('should support out === source', () => {
|
|
834
|
+
const m = createMatrix(1, 0, 0, 1, 0, 0);
|
|
835
|
+
rotateMatrix(m, m, Math.PI);
|
|
836
|
+
expect(m.a).toBeCloseTo(-1);
|
|
837
|
+
expect(m.d).toBeCloseTo(-1);
|
|
838
|
+
});
|
|
839
|
+
});
|
|
840
|
+
|
|
841
|
+
describe('scaleMatrix', () => {
|
|
842
|
+
it('should write scaled result to out without modifying source', () => {
|
|
843
|
+
const src = createMatrix(2, 0, 0, 2, 5, 6);
|
|
844
|
+
const out = createMatrix();
|
|
845
|
+
scaleMatrix(out, src, 2, 3);
|
|
846
|
+
expect(src.a).toBe(2);
|
|
847
|
+
expect(out.a).toBe(4);
|
|
848
|
+
expect(out.d).toBe(6);
|
|
849
|
+
});
|
|
850
|
+
|
|
851
|
+
it('should support out === source', () => {
|
|
852
|
+
const m = createMatrix(1, 0, 0, 1, 1, 1);
|
|
853
|
+
scaleMatrix(m, m, 2, 3);
|
|
854
|
+
expect(m.a).toBe(2);
|
|
855
|
+
expect(m.d).toBe(3);
|
|
856
|
+
expect(m.tx).toBe(2);
|
|
857
|
+
expect(m.ty).toBe(3);
|
|
858
|
+
});
|
|
859
|
+
});
|
|
860
|
+
|
|
861
|
+
describe('setGradientTransformMatrix', () => {
|
|
862
|
+
it('sets a and d proportional to width and height', () => {
|
|
863
|
+
const m = createMatrix();
|
|
864
|
+
setGradientTransformMatrix(m, 1638.4, 1638.4);
|
|
865
|
+
expect(m.a).toBeCloseTo(1);
|
|
866
|
+
expect(m.d).toBeCloseTo(1);
|
|
867
|
+
});
|
|
868
|
+
|
|
869
|
+
it('sets tx to tx + width / 2 and ty to ty + height / 2', () => {
|
|
870
|
+
const m = createMatrix();
|
|
871
|
+
setGradientTransformMatrix(m, 200, 400, 0, 0, 0);
|
|
872
|
+
expect(m.tx).toBeCloseTo(100);
|
|
873
|
+
expect(m.ty).toBeCloseTo(200);
|
|
874
|
+
});
|
|
875
|
+
|
|
876
|
+
it('applies rotation to the linear part', () => {
|
|
877
|
+
const m = createMatrix();
|
|
878
|
+
setGradientTransformMatrix(m, 1638.4, 1638.4, Math.PI / 2);
|
|
879
|
+
expect(m.b).toBeCloseTo(1);
|
|
880
|
+
expect(m.c).toBeCloseTo(-1);
|
|
881
|
+
});
|
|
882
|
+
});
|
|
883
|
+
|
|
884
|
+
describe('setMatrix', () => {
|
|
885
|
+
it('should assign all matrix3x2 fields', () => {
|
|
886
|
+
const m = createMatrix();
|
|
887
|
+
setMatrix(m, 1, 2, 3, 4, 5, 6);
|
|
888
|
+
expect(m.a).toBe(1);
|
|
889
|
+
expect(m.b).toBe(2);
|
|
890
|
+
expect(m.c).toBe(3);
|
|
891
|
+
expect(m.d).toBe(4);
|
|
892
|
+
expect(m.tx).toBe(5);
|
|
893
|
+
expect(m.ty).toBe(6);
|
|
894
|
+
});
|
|
895
|
+
});
|
|
896
|
+
|
|
897
|
+
describe('setMatrixFromFloat32Array', () => {
|
|
898
|
+
it('writes the matrix from 6 values at the offset', () => {
|
|
899
|
+
const array = new Float32Array(6);
|
|
900
|
+
for (let i = 0; i < 6; i++) {
|
|
901
|
+
array[i] = i;
|
|
902
|
+
}
|
|
903
|
+
const matrix = createMatrix();
|
|
904
|
+
setMatrixFromFloat32Array(matrix, 0, array);
|
|
905
|
+
expect(matrix.a).toBe(0);
|
|
906
|
+
expect(matrix.b).toBe(1);
|
|
907
|
+
expect(matrix.c).toBe(2);
|
|
908
|
+
expect(matrix.d).toBe(3);
|
|
909
|
+
expect(matrix.tx).toBe(4);
|
|
910
|
+
expect(matrix.ty).toBe(5);
|
|
911
|
+
});
|
|
912
|
+
});
|
|
913
|
+
|
|
914
|
+
describe('setMatrixFromMatrix3', () => {
|
|
915
|
+
let mat3: Matrix3Like;
|
|
916
|
+
let mat2D: Matrix;
|
|
917
|
+
|
|
918
|
+
beforeEach(() => {
|
|
919
|
+
// Setup a basic Matrix3x3 instance for testing
|
|
920
|
+
mat3 = {
|
|
921
|
+
m: new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9]), // 3x3 matrix3x2 (row-major)
|
|
922
|
+
};
|
|
923
|
+
mat2D = createMatrix(); // Create a createMatrix instance
|
|
924
|
+
});
|
|
925
|
+
|
|
926
|
+
it('should correctly copy the first 6 values of Matrix3x3 into matrix3x2', () => {
|
|
927
|
+
setMatrixFromMatrix3(mat2D, mat3);
|
|
928
|
+
|
|
929
|
+
expect(mat2D.a).toEqual(1);
|
|
930
|
+
expect(mat2D.b).toEqual(2);
|
|
931
|
+
expect(mat2D.tx).toEqual(3);
|
|
932
|
+
expect(mat2D.c).toEqual(4);
|
|
933
|
+
expect(mat2D.d).toEqual(5);
|
|
934
|
+
expect(mat2D.ty).toEqual(6);
|
|
935
|
+
});
|
|
936
|
+
|
|
937
|
+
it('should not affect the original Matrix3x3 after calling fromMatrix3x3', () => {
|
|
938
|
+
const originalMatrix3x3 = new Float32Array(mat3.m); // Clone the original Matrix3x3
|
|
939
|
+
|
|
940
|
+
setMatrixFromMatrix3(mat2D, mat3);
|
|
941
|
+
|
|
942
|
+
// Assert that the original Matrix3x3 is untouched
|
|
943
|
+
expect(mat3.m).toEqual(originalMatrix3x3);
|
|
944
|
+
});
|
|
945
|
+
|
|
946
|
+
it('should work with matrices where all values are zeros', () => {
|
|
947
|
+
const zeroMatrix3x3: Matrix3Like = {
|
|
948
|
+
m: new Float32Array([0, 0, 0, 0, 0, 0, 0, 0, 0]), // A matrix3x2 full of zeros
|
|
949
|
+
};
|
|
950
|
+
|
|
951
|
+
setMatrixFromMatrix3(mat2D, zeroMatrix3x3);
|
|
952
|
+
|
|
953
|
+
expect(mat2D.a).toEqual(0);
|
|
954
|
+
expect(mat2D.b).toEqual(0);
|
|
955
|
+
expect(mat2D.tx).toEqual(0);
|
|
956
|
+
expect(mat2D.c).toEqual(0);
|
|
957
|
+
expect(mat2D.d).toEqual(0);
|
|
958
|
+
expect(mat2D.ty).toEqual(0);
|
|
959
|
+
});
|
|
960
|
+
|
|
961
|
+
it('should handle matrices where the translation is zero', () => {
|
|
962
|
+
const translationZeromatrix3x2: Matrix3Like = {
|
|
963
|
+
m: new Float32Array([1, 0, 0, 0, 1, 0, 0, 0, 1]), // Identity matrix3x2 (no translation)
|
|
964
|
+
};
|
|
965
|
+
|
|
966
|
+
setMatrixFromMatrix3(mat2D, translationZeromatrix3x2);
|
|
967
|
+
|
|
968
|
+
expect(mat2D.a).toEqual(1);
|
|
969
|
+
expect(mat2D.b).toEqual(0);
|
|
970
|
+
expect(mat2D.tx).toEqual(0);
|
|
971
|
+
expect(mat2D.c).toEqual(0);
|
|
972
|
+
expect(mat2D.d).toEqual(1);
|
|
973
|
+
expect(mat2D.ty).toEqual(0);
|
|
974
|
+
});
|
|
975
|
+
});
|
|
976
|
+
|
|
977
|
+
describe('setMatrixFromMatrix4', () => {
|
|
978
|
+
let mat4: Matrix4Like;
|
|
979
|
+
let mat2D: Matrix;
|
|
980
|
+
|
|
981
|
+
beforeEach(() => {
|
|
982
|
+
// Setup a basic Matrix4x4 instance for testing (column-major)
|
|
983
|
+
mat4 = {
|
|
984
|
+
m: new Float32Array([1, 4, 0, 0, 2, 5, 0, 0, 0, 0, 1, 0, 3, 6, 0, 1]), // 4x4 column-major matrix3x2
|
|
985
|
+
};
|
|
986
|
+
mat2D = createMatrix(); // Create a createMatrix instance
|
|
987
|
+
});
|
|
988
|
+
|
|
989
|
+
it('should correctly copy the 2D affine part from a column-major Matrix4x4', () => {
|
|
990
|
+
setMatrixFromMatrix4(mat2D, mat4);
|
|
991
|
+
|
|
992
|
+
// Expected 2D affine matrix3x2 values from Matrix4x4 (ignoring 3rd row/column)
|
|
993
|
+
expect(mat2D.a).toEqual(1);
|
|
994
|
+
expect(mat2D.b).toEqual(2);
|
|
995
|
+
expect(mat2D.tx).toEqual(3);
|
|
996
|
+
expect(mat2D.c).toEqual(4);
|
|
997
|
+
expect(mat2D.d).toEqual(5);
|
|
998
|
+
expect(mat2D.ty).toEqual(6);
|
|
999
|
+
});
|
|
1000
|
+
|
|
1001
|
+
it('should not affect the original Matrix4x4 after calling fromMatrix4x4', () => {
|
|
1002
|
+
const originalMatrix4x4 = new Float32Array(mat4.m); // Clone the original Matrix4x4
|
|
1003
|
+
|
|
1004
|
+
setMatrixFromMatrix4(mat2D, mat4);
|
|
1005
|
+
|
|
1006
|
+
// Assert that the original Matrix4x4 is untouched
|
|
1007
|
+
expect(mat4.m).toEqual(originalMatrix4x4);
|
|
1008
|
+
});
|
|
1009
|
+
|
|
1010
|
+
it('should handle matrices with zero values correctly', () => {
|
|
1011
|
+
const zeroMatrix4x4: Matrix4Like = {
|
|
1012
|
+
m: new Float32Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]), // Identity matrix3x2 with no scaling or translation
|
|
1013
|
+
};
|
|
1014
|
+
|
|
1015
|
+
setMatrixFromMatrix4(mat2D, zeroMatrix4x4);
|
|
1016
|
+
|
|
1017
|
+
expect(mat2D.a).toEqual(0);
|
|
1018
|
+
expect(mat2D.b).toEqual(0);
|
|
1019
|
+
expect(mat2D.tx).toEqual(0);
|
|
1020
|
+
expect(mat2D.c).toEqual(0);
|
|
1021
|
+
expect(mat2D.d).toEqual(0);
|
|
1022
|
+
expect(mat2D.ty).toEqual(0);
|
|
1023
|
+
});
|
|
1024
|
+
|
|
1025
|
+
it('should correctly handle a 2D identity matrix3x2 in Matrix4x4 format', () => {
|
|
1026
|
+
const setMatrix4Identityx4: Matrix4Like = {
|
|
1027
|
+
m: new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]), // 4x4 identity matrix3x2 (no scaling, no translation)
|
|
1028
|
+
};
|
|
1029
|
+
|
|
1030
|
+
setMatrixFromMatrix4(mat2D, setMatrix4Identityx4);
|
|
1031
|
+
|
|
1032
|
+
expect(mat2D.a).toEqual(1);
|
|
1033
|
+
expect(mat2D.b).toEqual(0);
|
|
1034
|
+
expect(mat2D.tx).toEqual(0);
|
|
1035
|
+
expect(mat2D.c).toEqual(0);
|
|
1036
|
+
expect(mat2D.d).toEqual(1);
|
|
1037
|
+
expect(mat2D.ty).toEqual(0);
|
|
1038
|
+
});
|
|
1039
|
+
});
|
|
1040
|
+
|
|
1041
|
+
describe('setMatrixIdentity', () => {
|
|
1042
|
+
it('resets a modified matrix to identity', () => {
|
|
1043
|
+
const m = createMatrix(2, 3, 4, 5, 6, 7);
|
|
1044
|
+
setMatrixIdentity(m);
|
|
1045
|
+
expect(m.a).toBe(1);
|
|
1046
|
+
expect(m.b).toBe(0);
|
|
1047
|
+
expect(m.c).toBe(0);
|
|
1048
|
+
expect(m.d).toBe(1);
|
|
1049
|
+
expect(m.tx).toBe(0);
|
|
1050
|
+
expect(m.ty).toBe(0);
|
|
1051
|
+
});
|
|
1052
|
+
});
|
|
1053
|
+
|
|
1054
|
+
describe('setTransformMatrix', () => {
|
|
1055
|
+
it('should apply rotate, scale and translation', () => {
|
|
1056
|
+
const m1 = createMatrix();
|
|
1057
|
+
rotateMatrix(m1, m1, 45);
|
|
1058
|
+
scaleMatrix(m1, m1, 2, 4);
|
|
1059
|
+
translateMatrix(m1, m1, 10, 100);
|
|
1060
|
+
const m2 = createMatrix();
|
|
1061
|
+
setTransformMatrix(m2, 2, 4, 45, 10, 100);
|
|
1062
|
+
expect(equalsMatrix(m1, m2)).toBe(true);
|
|
1063
|
+
});
|
|
1064
|
+
});
|
|
1065
|
+
|
|
1066
|
+
describe('translateMatrix', () => {
|
|
1067
|
+
it('should translate the matrix3x2 correctly', () => {
|
|
1068
|
+
const m = createMatrix();
|
|
1069
|
+
translateMatrix(m, m, 10, 20);
|
|
1070
|
+
expect(m.tx).toBe(10);
|
|
1071
|
+
expect(m.ty).toBe(20);
|
|
1072
|
+
});
|
|
1073
|
+
});
|
|
1074
|
+
|
|
1075
|
+
describe('translateMatrixByVector', () => {
|
|
1076
|
+
it('translates tx/ty by the transformed vector', () => {
|
|
1077
|
+
const m = createMatrix(2, 0, 0, 2, 5, 10);
|
|
1078
|
+
const out = createMatrix();
|
|
1079
|
+
translateMatrixByVector(out, m, { x: 3, y: 4 });
|
|
1080
|
+
expect(out.tx).toBeCloseTo(5 + 2 * 3 + 0 * 4); // 11
|
|
1081
|
+
expect(out.ty).toBeCloseTo(10 + 0 * 3 + 2 * 4); // 18
|
|
1082
|
+
});
|
|
1083
|
+
});
|
|
1084
|
+
|
|
1085
|
+
describe('translateMatrixByVectorXY', () => {
|
|
1086
|
+
it('translates tx/ty by the transformed x and y components', () => {
|
|
1087
|
+
const m = createMatrix(2, 0, 0, 2, 5, 10);
|
|
1088
|
+
const out = createMatrix();
|
|
1089
|
+
translateMatrixByVectorXY(out, m, 3, 4);
|
|
1090
|
+
expect(out.tx).toBeCloseTo(11);
|
|
1091
|
+
expect(out.ty).toBeCloseTo(18);
|
|
1092
|
+
});
|
|
1093
|
+
|
|
1094
|
+
it('supports out === source', () => {
|
|
1095
|
+
const m = createMatrix(1, 0, 0, 1, 5, 10);
|
|
1096
|
+
translateMatrixByVectorXY(m, m, 2, 3);
|
|
1097
|
+
expect(m.tx).toBeCloseTo(7);
|
|
1098
|
+
expect(m.ty).toBeCloseTo(13);
|
|
1099
|
+
});
|
|
1100
|
+
});
|
|
1101
|
+
|
|
1102
|
+
describe('writeMatrixToFloat32Array', () => {
|
|
1103
|
+
it('writes 6 values at the offset', () => {
|
|
1104
|
+
const array = new Float32Array(6);
|
|
1105
|
+
const matrix = { a: 1, b: 2, c: 3, d: 4, tx: 5, ty: 6 };
|
|
1106
|
+
writeMatrixToFloat32Array(array, 0, matrix);
|
|
1107
|
+
for (let i = 0; i < 6; i++) {
|
|
1108
|
+
expect(array[i]).toBe(i + 1);
|
|
1109
|
+
}
|
|
1110
|
+
});
|
|
1111
|
+
});
|