fluidcad 0.0.2 → 0.0.4
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/README.md +7 -7
- package/lib/dist/common/shape-factory.d.ts +1 -1
- package/lib/dist/oc/boolean-ops.d.ts +2 -2
- package/lib/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -2
- package/lib/dist/constraints/constraint.d.ts +0 -11
- package/lib/dist/constraints/constraint.js +0 -51
- package/lib/dist/constraints/outside.d.ts +0 -3
- package/lib/dist/constraints/outside.js +0 -4
- package/lib/dist/core/2d/aslot.d.ts +0 -12
- package/lib/dist/core/2d/aslot.js +0 -40
- package/lib/dist/core/2d/crect.d.ts +0 -12
- package/lib/dist/core/2d/crect.js +0 -74
- package/lib/dist/core/2d/face-maker.d.ts +0 -13
- package/lib/dist/core/2d/face-maker.js +0 -119
- package/lib/dist/core/2d/face-maker2.d.ts +0 -6
- package/lib/dist/core/2d/face-maker2.js +0 -54
- package/lib/dist/core/region.d.ts +0 -13
- package/lib/dist/core/region.js +0 -18
- package/lib/dist/features/2d/amove.d.ts +0 -14
- package/lib/dist/features/2d/amove.js +0 -33
- package/lib/dist/features/2d/constraints/constraint.d.ts +0 -11
- package/lib/dist/features/2d/constraints/constraint.js +0 -49
- package/lib/dist/features/2d/constraints/outside.d.ts +0 -3
- package/lib/dist/features/2d/constraints/outside.js +0 -4
- package/lib/dist/features/2d/tarc-two-circles.d.ts +0 -18
- package/lib/dist/features/2d/tarc-two-circles.js +0 -61
- package/lib/dist/features/2d/tcircle-three-tan.d.ts +0 -13
- package/lib/dist/features/2d/tcircle-three-tan.js +0 -33
- package/lib/dist/features/2d/tcircle-two-tan.d.ts +0 -13
- package/lib/dist/features/2d/tcircle-two-tan.js +0 -33
- package/lib/dist/features/Extrude.d.ts +0 -1
- package/lib/dist/features/Extrude.js +0 -1
- package/lib/dist/features/cut-base.d.ts +0 -49
- package/lib/dist/features/cut-base.js +0 -312
- package/lib/dist/features/cut-symmetric.d.ts +0 -23
- package/lib/dist/features/cut-symmetric.js +0 -119
- package/lib/dist/features/cut-two-distances.d.ts +0 -24
- package/lib/dist/features/cut-two-distances.js +0 -110
- package/lib/dist/features/cut.d.ts +0 -27
- package/lib/dist/features/cut.js +0 -101
- package/lib/dist/features/extrude-symmetric.d.ts +0 -28
- package/lib/dist/features/extrude-symmetric.js +0 -177
- package/lib/dist/features/region2d.d.ts +0 -25
- package/lib/dist/features/region2d.js +0 -185
- package/lib/dist/features/repeat-circular2d.d.ts +0 -17
- package/lib/dist/features/repeat-circular2d.js +0 -90
- package/lib/dist/features/repeat-linear2d.d.ts +0 -17
- package/lib/dist/features/repeat-linear2d.js +0 -114
- package/lib/dist/features/revolve-options.d.ts +0 -6
- package/lib/dist/features/revolve-options.js +0 -1
- package/lib/dist/features/split.d.ts +0 -14
- package/lib/dist/features/split.js +0 -74
- package/lib/dist/features/state-scene-object.d.ts +0 -15
- package/lib/dist/features/state-scene-object.js +0 -44
- package/lib/dist/features/state-select.d.ts +0 -21
- package/lib/dist/features/state-select.js +0 -73
- package/lib/dist/features/translate2d.d.ts +0 -16
- package/lib/dist/features/translate2d.js +0 -61
- package/lib/dist/filters/all-filter.d.ts +0 -8
- package/lib/dist/filters/all-filter.js +0 -12
- package/lib/dist/filters/near-point-filter.d.ts +0 -11
- package/lib/dist/filters/near-point-filter.js +0 -33
- package/lib/dist/helpers/resolve-filters.d.ts +0 -6
- package/lib/dist/helpers/resolve-filters.js +0 -25
- package/lib/dist/math/axis.test.d.ts +0 -1
- package/lib/dist/math/axis.test.js +0 -287
- package/lib/dist/math/coordinate-system.test.d.ts +0 -1
- package/lib/dist/math/coordinate-system.test.js +0 -308
- package/lib/dist/math/matrix4.test.d.ts +0 -1
- package/lib/dist/math/matrix4.test.js +0 -357
- package/lib/dist/math/plane.test.d.ts +0 -1
- package/lib/dist/math/plane.test.js +0 -398
- package/lib/dist/math/point.test.d.ts +0 -1
- package/lib/dist/math/point.test.js +0 -385
- package/lib/dist/math/quaternion.test.d.ts +0 -1
- package/lib/dist/math/quaternion.test.js +0 -278
- package/lib/dist/math/vector3d.test.d.ts +0 -1
- package/lib/dist/math/vector3d.test.js +0 -276
- package/lib/dist/oc/constraint-resolver.d.ts +0 -7
- package/lib/dist/oc/constraint-resolver.js +0 -31
- package/lib/dist/oc/constraints/curve-constraint-solver.d.ts +0 -1
- package/lib/dist/oc/constraints/curve-constraint-solver.js +0 -2
- package/lib/dist/oc/constraints/geometric-constraint-solver.d.ts +0 -1
- package/lib/dist/oc/constraints/geometric-constraint-solver.js +0 -5
- package/lib/dist/oc/face-maker.d.ts +0 -14
- package/lib/dist/oc/face-maker.js +0 -191
- package/lib/dist/oc/measure.d.ts +0 -21
- package/lib/dist/oc/measure.js +0 -256
- package/lib/dist/oc/tangent-circle-solver.d.ts +0 -17
- package/lib/dist/oc/tangent-circle-solver.js +0 -72
- package/lib/dist/oc/tangent-line-solver.d.ts +0 -17
- package/lib/dist/oc/tangent-line-solver.js +0 -83
- package/lib/dist/oc/tangent-solver.d.ts +0 -14
- package/lib/dist/oc/tangent-solver.js +0 -199
- package/lib/dist/rendering/builder-context.d.ts +0 -16
- package/lib/dist/rendering/builder-context.js +0 -63
- package/lib/dist/tests/extrude.test.d.ts +0 -1
- package/lib/dist/tests/extrude.test.js +0 -48
- package/lib/dist/tests/features/copy.test.d.ts +0 -1
- package/lib/dist/tests/features/copy.test.js +0 -158
- package/lib/dist/tests/features/dispose.test.d.ts +0 -1
- package/lib/dist/tests/features/dispose.test.js +0 -189
- package/lib/dist/tests/features/part-pick.test.d.ts +0 -1
- package/lib/dist/tests/features/part-pick.test.js +0 -73
- package/lib/dist/tests/features/part-repeat.test.d.ts +0 -1
- package/lib/dist/tests/features/part-repeat.test.js +0 -109
- package/server/dist/fluidcad-server.d.ts +0 -32
- package/server/dist/fluidcad-server.js +0 -150
- package/server/dist/index.d.ts +0 -1
- package/server/dist/index.js +0 -290
- package/server/dist/routes/actions.d.ts +0 -3
- package/server/dist/routes/actions.js +0 -100
- package/server/dist/routes/export.d.ts +0 -3
- package/server/dist/routes/export.js +0 -55
- package/server/dist/routes/properties.d.ts +0 -3
- package/server/dist/routes/properties.js +0 -46
- package/server/dist/routes/screenshot.d.ts +0 -2
- package/server/dist/routes/screenshot.js +0 -76
- package/server/dist/vite-manager.d.ts +0 -10
- package/server/dist/vite-manager.js +0 -64
- package/server/dist/ws-protocol.d.ts +0 -138
- package/server/dist/ws-protocol.js +0 -4
|
@@ -1,385 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { Point, Point2D, isPointLike, isPoint2DLike } from "./point.js";
|
|
3
|
-
import { Vector3d } from "./vector3d.js";
|
|
4
|
-
import { Axis } from "./axis.js";
|
|
5
|
-
describe("Point", () => {
|
|
6
|
-
describe("constructor", () => {
|
|
7
|
-
it("creates a point with x, y, z coordinates", () => {
|
|
8
|
-
const p = new Point(1, 2, 3);
|
|
9
|
-
expect(p.x).toBe(1);
|
|
10
|
-
expect(p.y).toBe(2);
|
|
11
|
-
expect(p.z).toBe(3);
|
|
12
|
-
});
|
|
13
|
-
});
|
|
14
|
-
describe("equals", () => {
|
|
15
|
-
it("returns true for identical points", () => {
|
|
16
|
-
const p1 = new Point(1, 2, 3);
|
|
17
|
-
const p2 = new Point(1, 2, 3);
|
|
18
|
-
expect(p1.equals(p2)).toBe(true);
|
|
19
|
-
});
|
|
20
|
-
it("returns false for different points", () => {
|
|
21
|
-
const p1 = new Point(1, 2, 3);
|
|
22
|
-
const p2 = new Point(1, 2, 4);
|
|
23
|
-
expect(p1.equals(p2)).toBe(false);
|
|
24
|
-
});
|
|
25
|
-
it("supports tolerance", () => {
|
|
26
|
-
const p1 = new Point(1, 2, 3);
|
|
27
|
-
const p2 = new Point(1.001, 2.001, 3.001);
|
|
28
|
-
expect(p1.equals(p2, 0.01)).toBe(true);
|
|
29
|
-
expect(p1.equals(p2, 0.0001)).toBe(false);
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
describe("distanceTo", () => {
|
|
33
|
-
it("computes distance to another point", () => {
|
|
34
|
-
const p1 = new Point(0, 0, 0);
|
|
35
|
-
const p2 = new Point(3, 4, 0);
|
|
36
|
-
expect(p1.distanceTo(p2)).toBe(5);
|
|
37
|
-
});
|
|
38
|
-
it("computes distance to a vector", () => {
|
|
39
|
-
const p = new Point(0, 0, 0);
|
|
40
|
-
const v = new Vector3d(3, 4, 0);
|
|
41
|
-
expect(p.distanceTo(v)).toBe(5);
|
|
42
|
-
});
|
|
43
|
-
it("returns 0 for same point", () => {
|
|
44
|
-
const p1 = new Point(1, 2, 3);
|
|
45
|
-
const p2 = new Point(1, 2, 3);
|
|
46
|
-
expect(p1.distanceTo(p2)).toBe(0);
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
describe("add", () => {
|
|
50
|
-
it("adds a vector to point", () => {
|
|
51
|
-
const p = new Point(1, 2, 3);
|
|
52
|
-
const v = new Vector3d(4, 5, 6);
|
|
53
|
-
const result = p.add(v);
|
|
54
|
-
expect(result.x).toBe(5);
|
|
55
|
-
expect(result.y).toBe(7);
|
|
56
|
-
expect(result.z).toBe(9);
|
|
57
|
-
});
|
|
58
|
-
it("is immutable", () => {
|
|
59
|
-
const p = new Point(1, 2, 3);
|
|
60
|
-
const v = new Vector3d(4, 5, 6);
|
|
61
|
-
p.add(v);
|
|
62
|
-
expect(p.x).toBe(1);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
describe("subtract", () => {
|
|
66
|
-
it("subtracts a vector from point", () => {
|
|
67
|
-
const p = new Point(5, 7, 9);
|
|
68
|
-
const v = new Vector3d(1, 2, 3);
|
|
69
|
-
const result = p.subtract(v);
|
|
70
|
-
expect(result.x).toBe(4);
|
|
71
|
-
expect(result.y).toBe(5);
|
|
72
|
-
expect(result.z).toBe(6);
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
describe("multiply", () => {
|
|
76
|
-
it("multiplies point by scalar", () => {
|
|
77
|
-
const p = new Point(1, 2, 3);
|
|
78
|
-
const result = p.multiply(2);
|
|
79
|
-
expect(result.x).toBe(2);
|
|
80
|
-
expect(result.y).toBe(4);
|
|
81
|
-
expect(result.z).toBe(6);
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
describe("translate", () => {
|
|
85
|
-
it("translates point by dx, dy, dz", () => {
|
|
86
|
-
const p = new Point(1, 2, 3);
|
|
87
|
-
const result = p.translate(10, 20, 30);
|
|
88
|
-
expect(result.x).toBe(11);
|
|
89
|
-
expect(result.y).toBe(22);
|
|
90
|
-
expect(result.z).toBe(33);
|
|
91
|
-
});
|
|
92
|
-
it("defaults dz to 0", () => {
|
|
93
|
-
const p = new Point(1, 2, 3);
|
|
94
|
-
const result = p.translate(10, 20);
|
|
95
|
-
expect(result.z).toBe(3);
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
describe("translateX/Y/Z", () => {
|
|
99
|
-
it("translateX moves along X axis", () => {
|
|
100
|
-
const p = new Point(1, 2, 3);
|
|
101
|
-
const result = p.translateX(10);
|
|
102
|
-
expect(result.x).toBe(11);
|
|
103
|
-
expect(result.y).toBe(2);
|
|
104
|
-
expect(result.z).toBe(3);
|
|
105
|
-
});
|
|
106
|
-
it("translateY moves along Y axis", () => {
|
|
107
|
-
const p = new Point(1, 2, 3);
|
|
108
|
-
const result = p.translateY(10);
|
|
109
|
-
expect(result.x).toBe(1);
|
|
110
|
-
expect(result.y).toBe(12);
|
|
111
|
-
expect(result.z).toBe(3);
|
|
112
|
-
});
|
|
113
|
-
it("translateZ moves along Z axis", () => {
|
|
114
|
-
const p = new Point(1, 2, 3);
|
|
115
|
-
const result = p.translateZ(10);
|
|
116
|
-
expect(result.x).toBe(1);
|
|
117
|
-
expect(result.y).toBe(2);
|
|
118
|
-
expect(result.z).toBe(13);
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
describe("vectorTo", () => {
|
|
122
|
-
it("returns vector from this point to another", () => {
|
|
123
|
-
const p1 = new Point(1, 2, 3);
|
|
124
|
-
const p2 = new Point(4, 6, 8);
|
|
125
|
-
const v = p1.vectorTo(p2);
|
|
126
|
-
expect(v.x).toBe(3);
|
|
127
|
-
expect(v.y).toBe(4);
|
|
128
|
-
expect(v.z).toBe(5);
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
describe("lerp", () => {
|
|
132
|
-
it("interpolates between points", () => {
|
|
133
|
-
const p1 = new Point(0, 0, 0);
|
|
134
|
-
const p2 = new Point(10, 10, 10);
|
|
135
|
-
const result = p1.lerp(p2, 0.5);
|
|
136
|
-
expect(result.x).toBe(5);
|
|
137
|
-
expect(result.y).toBe(5);
|
|
138
|
-
expect(result.z).toBe(5);
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
describe("toVector3d", () => {
|
|
142
|
-
it("converts point to vector", () => {
|
|
143
|
-
const p = new Point(1, 2, 3);
|
|
144
|
-
const v = p.toVector3d();
|
|
145
|
-
expect(v).toBeInstanceOf(Vector3d);
|
|
146
|
-
expect(v.x).toBe(1);
|
|
147
|
-
expect(v.y).toBe(2);
|
|
148
|
-
expect(v.z).toBe(3);
|
|
149
|
-
});
|
|
150
|
-
});
|
|
151
|
-
describe("toPoint2D", () => {
|
|
152
|
-
it("converts to Point2D (drops z)", () => {
|
|
153
|
-
const p = new Point(1, 2, 3);
|
|
154
|
-
const p2d = p.toPoint2D();
|
|
155
|
-
expect(p2d).toBeInstanceOf(Point2D);
|
|
156
|
-
expect(p2d.x).toBe(1);
|
|
157
|
-
expect(p2d.y).toBe(2);
|
|
158
|
-
});
|
|
159
|
-
});
|
|
160
|
-
describe("static methods", () => {
|
|
161
|
-
it("origin returns (0,0,0)", () => {
|
|
162
|
-
const p = Point.origin();
|
|
163
|
-
expect(p.x).toBe(0);
|
|
164
|
-
expect(p.y).toBe(0);
|
|
165
|
-
expect(p.z).toBe(0);
|
|
166
|
-
});
|
|
167
|
-
it("fromArray creates point from array", () => {
|
|
168
|
-
const p = Point.fromArray([1, 2, 3]);
|
|
169
|
-
expect(p.x).toBe(1);
|
|
170
|
-
expect(p.y).toBe(2);
|
|
171
|
-
expect(p.z).toBe(3);
|
|
172
|
-
});
|
|
173
|
-
it("fromVector3d creates point from vector", () => {
|
|
174
|
-
const v = new Vector3d(1, 2, 3);
|
|
175
|
-
const p = Point.fromVector3d(v);
|
|
176
|
-
expect(p.x).toBe(1);
|
|
177
|
-
expect(p.y).toBe(2);
|
|
178
|
-
expect(p.z).toBe(3);
|
|
179
|
-
});
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
|
-
describe("Point2D", () => {
|
|
183
|
-
describe("constructor", () => {
|
|
184
|
-
it("creates a point with x, y coordinates", () => {
|
|
185
|
-
const p = new Point2D(1, 2);
|
|
186
|
-
expect(p.x).toBe(1);
|
|
187
|
-
expect(p.y).toBe(2);
|
|
188
|
-
});
|
|
189
|
-
});
|
|
190
|
-
describe("equals", () => {
|
|
191
|
-
it("returns true for identical points", () => {
|
|
192
|
-
const p1 = new Point2D(1, 2);
|
|
193
|
-
const p2 = new Point2D(1, 2);
|
|
194
|
-
expect(p1.equals(p2)).toBe(true);
|
|
195
|
-
});
|
|
196
|
-
it("supports tolerance", () => {
|
|
197
|
-
const p1 = new Point2D(1, 2);
|
|
198
|
-
const p2 = new Point2D(1.001, 2.001);
|
|
199
|
-
expect(p1.equals(p2, 0.01)).toBe(true);
|
|
200
|
-
});
|
|
201
|
-
});
|
|
202
|
-
describe("distanceTo", () => {
|
|
203
|
-
it("computes distance correctly", () => {
|
|
204
|
-
const p1 = new Point2D(0, 0);
|
|
205
|
-
const p2 = new Point2D(3, 4);
|
|
206
|
-
expect(p1.distanceTo(p2)).toBe(5);
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
describe("add/subtract", () => {
|
|
210
|
-
it("adds points correctly", () => {
|
|
211
|
-
const p1 = new Point2D(1, 2);
|
|
212
|
-
const p2 = new Point2D(3, 4);
|
|
213
|
-
const result = p1.add(p2);
|
|
214
|
-
expect(result.x).toBe(4);
|
|
215
|
-
expect(result.y).toBe(6);
|
|
216
|
-
});
|
|
217
|
-
it("subtracts points correctly", () => {
|
|
218
|
-
const p1 = new Point2D(5, 7);
|
|
219
|
-
const p2 = new Point2D(2, 3);
|
|
220
|
-
const result = p1.subtract(p2);
|
|
221
|
-
expect(result.x).toBe(3);
|
|
222
|
-
expect(result.y).toBe(4);
|
|
223
|
-
});
|
|
224
|
-
});
|
|
225
|
-
describe("multiply", () => {
|
|
226
|
-
it("multiplies component-wise", () => {
|
|
227
|
-
const p1 = new Point2D(2, 3);
|
|
228
|
-
const p2 = new Point2D(4, 5);
|
|
229
|
-
const result = p1.multiply(p2);
|
|
230
|
-
expect(result.x).toBe(8);
|
|
231
|
-
expect(result.y).toBe(15);
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
|
-
describe("multiplyScalar", () => {
|
|
235
|
-
it("multiplies by scalar", () => {
|
|
236
|
-
const p = new Point2D(2, 3);
|
|
237
|
-
const result = p.multiplyScalar(2);
|
|
238
|
-
expect(result.x).toBe(4);
|
|
239
|
-
expect(result.y).toBe(6);
|
|
240
|
-
});
|
|
241
|
-
});
|
|
242
|
-
describe("normalize", () => {
|
|
243
|
-
it("returns unit vector", () => {
|
|
244
|
-
const p = new Point2D(3, 4);
|
|
245
|
-
const result = p.normalize();
|
|
246
|
-
expect(result.length()).toBeCloseTo(1);
|
|
247
|
-
});
|
|
248
|
-
it("returns zero for zero point", () => {
|
|
249
|
-
const p = new Point2D(0, 0);
|
|
250
|
-
const result = p.normalize();
|
|
251
|
-
expect(result.x).toBe(0);
|
|
252
|
-
expect(result.y).toBe(0);
|
|
253
|
-
});
|
|
254
|
-
});
|
|
255
|
-
describe("rotate", () => {
|
|
256
|
-
it("rotates around origin", () => {
|
|
257
|
-
const p = new Point2D(1, 0);
|
|
258
|
-
const result = p.rotate(Math.PI / 2);
|
|
259
|
-
expect(result.x).toBeCloseTo(0);
|
|
260
|
-
expect(result.y).toBeCloseTo(1);
|
|
261
|
-
});
|
|
262
|
-
it("rotates around custom pivot", () => {
|
|
263
|
-
const p = new Point2D(2, 0);
|
|
264
|
-
const pivot = new Point2D(1, 0);
|
|
265
|
-
const result = p.rotate(Math.PI / 2, pivot);
|
|
266
|
-
expect(result.x).toBeCloseTo(1);
|
|
267
|
-
expect(result.y).toBeCloseTo(1);
|
|
268
|
-
});
|
|
269
|
-
});
|
|
270
|
-
describe("toPoint", () => {
|
|
271
|
-
it("converts to Point with z=0 by default", () => {
|
|
272
|
-
const p2d = new Point2D(1, 2);
|
|
273
|
-
const p = p2d.toPoint();
|
|
274
|
-
expect(p.x).toBe(1);
|
|
275
|
-
expect(p.y).toBe(2);
|
|
276
|
-
expect(p.z).toBe(0);
|
|
277
|
-
});
|
|
278
|
-
it("converts to Point with custom z", () => {
|
|
279
|
-
const p2d = new Point2D(1, 2);
|
|
280
|
-
const p = p2d.toPoint(5);
|
|
281
|
-
expect(p.z).toBe(5);
|
|
282
|
-
});
|
|
283
|
-
});
|
|
284
|
-
describe("mirrorAroundPoint", () => {
|
|
285
|
-
it("mirrors point around origin", () => {
|
|
286
|
-
const p = new Point2D(2, 3);
|
|
287
|
-
const result = p.mirrorAroundPoint(Point2D.origin());
|
|
288
|
-
expect(result.x).toBe(-2);
|
|
289
|
-
expect(result.y).toBe(-3);
|
|
290
|
-
});
|
|
291
|
-
it("mirrors point around arbitrary pivot", () => {
|
|
292
|
-
const p = new Point2D(4, 2);
|
|
293
|
-
const pivot = new Point2D(2, 2);
|
|
294
|
-
const result = p.mirrorAroundPoint(pivot);
|
|
295
|
-
expect(result.x).toBe(0);
|
|
296
|
-
expect(result.y).toBe(2);
|
|
297
|
-
});
|
|
298
|
-
it("returns same point when mirroring around itself", () => {
|
|
299
|
-
const p = new Point2D(1, 1);
|
|
300
|
-
const result = p.mirrorAroundPoint(p);
|
|
301
|
-
expect(result.x).toBe(1);
|
|
302
|
-
expect(result.y).toBe(1);
|
|
303
|
-
});
|
|
304
|
-
});
|
|
305
|
-
describe("mirrorAroundAxis", () => {
|
|
306
|
-
it("mirrors point around Y-axis", () => {
|
|
307
|
-
const p = new Point2D(2, 3);
|
|
308
|
-
const yAxis = new Axis(Point.origin(), new Vector3d(0, 1, 0));
|
|
309
|
-
const result = p.mirrorAroundAxis(yAxis);
|
|
310
|
-
expect(result.x).toBeCloseTo(-2);
|
|
311
|
-
expect(result.y).toBeCloseTo(3);
|
|
312
|
-
});
|
|
313
|
-
it("mirrors point around X-axis", () => {
|
|
314
|
-
const p = new Point2D(2, 3);
|
|
315
|
-
const xAxis = new Axis(Point.origin(), new Vector3d(1, 0, 0));
|
|
316
|
-
const result = p.mirrorAroundAxis(xAxis);
|
|
317
|
-
expect(result.x).toBeCloseTo(2);
|
|
318
|
-
expect(result.y).toBeCloseTo(-3);
|
|
319
|
-
});
|
|
320
|
-
it("mirrors point around diagonal axis (y=x)", () => {
|
|
321
|
-
const p = new Point2D(1, 0);
|
|
322
|
-
const diagonalAxis = new Axis(Point.origin(), new Vector3d(1, 1, 0));
|
|
323
|
-
const result = p.mirrorAroundAxis(diagonalAxis);
|
|
324
|
-
expect(result.x).toBeCloseTo(0);
|
|
325
|
-
expect(result.y).toBeCloseTo(1);
|
|
326
|
-
});
|
|
327
|
-
it("point on axis remains unchanged", () => {
|
|
328
|
-
const p = new Point2D(2, 2);
|
|
329
|
-
const diagonalAxis = new Axis(Point.origin(), new Vector3d(1, 1, 0));
|
|
330
|
-
const result = p.mirrorAroundAxis(diagonalAxis);
|
|
331
|
-
expect(result.x).toBeCloseTo(2);
|
|
332
|
-
expect(result.y).toBeCloseTo(2);
|
|
333
|
-
});
|
|
334
|
-
it("mirrors around offset axis", () => {
|
|
335
|
-
const p = new Point2D(3, 1);
|
|
336
|
-
// Vertical axis at x=2
|
|
337
|
-
const axis = new Axis(new Point(2, 0, 0), new Vector3d(0, 1, 0));
|
|
338
|
-
const result = p.mirrorAroundAxis(axis);
|
|
339
|
-
expect(result.x).toBeCloseTo(1);
|
|
340
|
-
expect(result.y).toBeCloseTo(1);
|
|
341
|
-
});
|
|
342
|
-
});
|
|
343
|
-
describe("static methods", () => {
|
|
344
|
-
it("origin returns (0,0)", () => {
|
|
345
|
-
const p = Point2D.origin();
|
|
346
|
-
expect(p.x).toBe(0);
|
|
347
|
-
expect(p.y).toBe(0);
|
|
348
|
-
});
|
|
349
|
-
it("fromArray creates point from array", () => {
|
|
350
|
-
const p = Point2D.fromArray([1, 2]);
|
|
351
|
-
expect(p.x).toBe(1);
|
|
352
|
-
expect(p.y).toBe(2);
|
|
353
|
-
});
|
|
354
|
-
});
|
|
355
|
-
});
|
|
356
|
-
describe("helper functions", () => {
|
|
357
|
-
describe("isPointLike", () => {
|
|
358
|
-
it("returns true for Point", () => {
|
|
359
|
-
expect(isPointLike(new Point(1, 2, 3))).toBe(true);
|
|
360
|
-
});
|
|
361
|
-
it("returns true for array of 3", () => {
|
|
362
|
-
expect(isPointLike([1, 2, 3])).toBe(true);
|
|
363
|
-
});
|
|
364
|
-
it("returns true for object with x,y,z", () => {
|
|
365
|
-
expect(isPointLike({ x: 1, y: 2, z: 3 })).toBe(true);
|
|
366
|
-
});
|
|
367
|
-
it("returns false for Point2D", () => {
|
|
368
|
-
expect(isPointLike(new Point2D(1, 2))).toBe(false);
|
|
369
|
-
});
|
|
370
|
-
});
|
|
371
|
-
describe("isPoint2DLike", () => {
|
|
372
|
-
it("returns true for Point2D", () => {
|
|
373
|
-
expect(isPoint2DLike(new Point2D(1, 2))).toBe(true);
|
|
374
|
-
});
|
|
375
|
-
it("returns true for array of 2", () => {
|
|
376
|
-
expect(isPoint2DLike([1, 2])).toBe(true);
|
|
377
|
-
});
|
|
378
|
-
it("returns true for object with x,y only", () => {
|
|
379
|
-
expect(isPoint2DLike({ x: 1, y: 2 })).toBe(true);
|
|
380
|
-
});
|
|
381
|
-
it("returns false for Point", () => {
|
|
382
|
-
expect(isPoint2DLike(new Point(1, 2, 3))).toBe(false);
|
|
383
|
-
});
|
|
384
|
-
});
|
|
385
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,278 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { Quaternion } from "./quaternion.js";
|
|
3
|
-
import { Vector3d } from "./vector3d.js";
|
|
4
|
-
describe("Quaternion", () => {
|
|
5
|
-
describe("constructor", () => {
|
|
6
|
-
it("creates quaternion with x, y, z, w components", () => {
|
|
7
|
-
const q = new Quaternion(1, 2, 3, 4);
|
|
8
|
-
expect(q.x).toBe(1);
|
|
9
|
-
expect(q.y).toBe(2);
|
|
10
|
-
expect(q.z).toBe(3);
|
|
11
|
-
expect(q.w).toBe(4);
|
|
12
|
-
});
|
|
13
|
-
});
|
|
14
|
-
describe("identity", () => {
|
|
15
|
-
it("returns identity quaternion (0,0,0,1)", () => {
|
|
16
|
-
const q = Quaternion.identity();
|
|
17
|
-
expect(q.x).toBe(0);
|
|
18
|
-
expect(q.y).toBe(0);
|
|
19
|
-
expect(q.z).toBe(0);
|
|
20
|
-
expect(q.w).toBe(1);
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
describe("length", () => {
|
|
24
|
-
it("computes length correctly", () => {
|
|
25
|
-
const q = new Quaternion(1, 2, 2, 0);
|
|
26
|
-
expect(q.length()).toBe(3);
|
|
27
|
-
});
|
|
28
|
-
it("identity has length 1", () => {
|
|
29
|
-
const q = Quaternion.identity();
|
|
30
|
-
expect(q.length()).toBe(1);
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
describe("normalize", () => {
|
|
34
|
-
it("returns unit quaternion", () => {
|
|
35
|
-
const q = new Quaternion(1, 2, 3, 4);
|
|
36
|
-
const normalized = q.normalize();
|
|
37
|
-
expect(normalized.length()).toBeCloseTo(1);
|
|
38
|
-
});
|
|
39
|
-
it("returns identity for zero quaternion", () => {
|
|
40
|
-
const q = new Quaternion(0, 0, 0, 0);
|
|
41
|
-
const normalized = q.normalize();
|
|
42
|
-
expect(normalized.w).toBe(1);
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
describe("conjugate", () => {
|
|
46
|
-
it("negates xyz, keeps w", () => {
|
|
47
|
-
const q = new Quaternion(1, 2, 3, 4);
|
|
48
|
-
const conj = q.conjugate();
|
|
49
|
-
expect(conj.x).toBe(-1);
|
|
50
|
-
expect(conj.y).toBe(-2);
|
|
51
|
-
expect(conj.z).toBe(-3);
|
|
52
|
-
expect(conj.w).toBe(4);
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
describe("inverse", () => {
|
|
56
|
-
it("returns inverse quaternion", () => {
|
|
57
|
-
const q = Quaternion.fromAxisAngle(Vector3d.unitZ(), Math.PI / 4);
|
|
58
|
-
const inv = q.inverse();
|
|
59
|
-
const result = q.multiply(inv);
|
|
60
|
-
expect(result.x).toBeCloseTo(0);
|
|
61
|
-
expect(result.y).toBeCloseTo(0);
|
|
62
|
-
expect(result.z).toBeCloseTo(0);
|
|
63
|
-
expect(result.w).toBeCloseTo(1);
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
describe("multiply", () => {
|
|
67
|
-
it("identity * q = q", () => {
|
|
68
|
-
const q = Quaternion.fromAxisAngle(Vector3d.unitZ(), Math.PI / 4);
|
|
69
|
-
const result = Quaternion.identity().multiply(q);
|
|
70
|
-
expect(result.equals(q, 1e-10)).toBe(true);
|
|
71
|
-
});
|
|
72
|
-
it("q * identity = q", () => {
|
|
73
|
-
const q = Quaternion.fromAxisAngle(Vector3d.unitZ(), Math.PI / 4);
|
|
74
|
-
const result = q.multiply(Quaternion.identity());
|
|
75
|
-
expect(result.equals(q, 1e-10)).toBe(true);
|
|
76
|
-
});
|
|
77
|
-
it("combines rotations correctly", () => {
|
|
78
|
-
const q1 = Quaternion.fromAxisAngle(Vector3d.unitZ(), Math.PI / 2);
|
|
79
|
-
const q2 = Quaternion.fromAxisAngle(Vector3d.unitZ(), Math.PI / 2);
|
|
80
|
-
const combined = q1.multiply(q2);
|
|
81
|
-
// Combined should be 180 degree rotation around Z
|
|
82
|
-
const v = new Vector3d(1, 0, 0);
|
|
83
|
-
const rotated = combined.rotateVector(v);
|
|
84
|
-
expect(rotated.x).toBeCloseTo(-1);
|
|
85
|
-
expect(rotated.y).toBeCloseTo(0);
|
|
86
|
-
expect(rotated.z).toBeCloseTo(0);
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
describe("rotateVector", () => {
|
|
90
|
-
it("rotates vector around Z axis", () => {
|
|
91
|
-
const q = Quaternion.fromAxisAngle(Vector3d.unitZ(), Math.PI / 2);
|
|
92
|
-
const v = new Vector3d(1, 0, 0);
|
|
93
|
-
const result = q.rotateVector(v);
|
|
94
|
-
expect(result.x).toBeCloseTo(0);
|
|
95
|
-
expect(result.y).toBeCloseTo(1);
|
|
96
|
-
expect(result.z).toBeCloseTo(0);
|
|
97
|
-
});
|
|
98
|
-
it("rotates vector around X axis", () => {
|
|
99
|
-
const q = Quaternion.fromAxisAngle(Vector3d.unitX(), Math.PI / 2);
|
|
100
|
-
const v = new Vector3d(0, 1, 0);
|
|
101
|
-
const result = q.rotateVector(v);
|
|
102
|
-
expect(result.x).toBeCloseTo(0);
|
|
103
|
-
expect(result.y).toBeCloseTo(0);
|
|
104
|
-
expect(result.z).toBeCloseTo(1);
|
|
105
|
-
});
|
|
106
|
-
it("rotates vector around Y axis", () => {
|
|
107
|
-
const q = Quaternion.fromAxisAngle(Vector3d.unitY(), Math.PI / 2);
|
|
108
|
-
const v = new Vector3d(1, 0, 0);
|
|
109
|
-
const result = q.rotateVector(v);
|
|
110
|
-
expect(result.x).toBeCloseTo(0);
|
|
111
|
-
expect(result.y).toBeCloseTo(0);
|
|
112
|
-
expect(result.z).toBeCloseTo(-1);
|
|
113
|
-
});
|
|
114
|
-
it("identity does not rotate", () => {
|
|
115
|
-
const q = Quaternion.identity();
|
|
116
|
-
const v = new Vector3d(1, 2, 3);
|
|
117
|
-
const result = q.rotateVector(v);
|
|
118
|
-
expect(result.x).toBeCloseTo(1);
|
|
119
|
-
expect(result.y).toBeCloseTo(2);
|
|
120
|
-
expect(result.z).toBeCloseTo(3);
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
describe("fromAxisAngle", () => {
|
|
124
|
-
it("creates quaternion from axis and angle", () => {
|
|
125
|
-
const q = Quaternion.fromAxisAngle(Vector3d.unitZ(), Math.PI);
|
|
126
|
-
const v = new Vector3d(1, 0, 0);
|
|
127
|
-
const rotated = q.rotateVector(v);
|
|
128
|
-
expect(rotated.x).toBeCloseTo(-1);
|
|
129
|
-
expect(rotated.y).toBeCloseTo(0);
|
|
130
|
-
});
|
|
131
|
-
it("zero angle gives identity", () => {
|
|
132
|
-
const q = Quaternion.fromAxisAngle(Vector3d.unitZ(), 0);
|
|
133
|
-
expect(q.x).toBeCloseTo(0);
|
|
134
|
-
expect(q.y).toBeCloseTo(0);
|
|
135
|
-
expect(q.z).toBeCloseTo(0);
|
|
136
|
-
expect(q.w).toBeCloseTo(1);
|
|
137
|
-
});
|
|
138
|
-
});
|
|
139
|
-
describe("toAxisAngle", () => {
|
|
140
|
-
it("extracts axis and angle", () => {
|
|
141
|
-
const axis = new Vector3d(0, 0, 1);
|
|
142
|
-
const angle = Math.PI / 3;
|
|
143
|
-
const q = Quaternion.fromAxisAngle(axis, angle);
|
|
144
|
-
const result = q.toAxisAngle();
|
|
145
|
-
expect(result.angle).toBeCloseTo(angle);
|
|
146
|
-
expect(result.axis.x).toBeCloseTo(0);
|
|
147
|
-
expect(result.axis.y).toBeCloseTo(0);
|
|
148
|
-
expect(result.axis.z).toBeCloseTo(1);
|
|
149
|
-
});
|
|
150
|
-
it("handles identity quaternion", () => {
|
|
151
|
-
const q = Quaternion.identity();
|
|
152
|
-
const result = q.toAxisAngle();
|
|
153
|
-
expect(result.angle).toBeCloseTo(0);
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
describe("fromEulerAngles", () => {
|
|
157
|
-
it("creates quaternion from euler angles", () => {
|
|
158
|
-
const q = Quaternion.fromEulerAngles(0, 0, Math.PI / 2);
|
|
159
|
-
const v = new Vector3d(1, 0, 0);
|
|
160
|
-
const rotated = q.rotateVector(v);
|
|
161
|
-
expect(rotated.x).toBeCloseTo(0);
|
|
162
|
-
expect(rotated.y).toBeCloseTo(1);
|
|
163
|
-
expect(rotated.z).toBeCloseTo(0);
|
|
164
|
-
});
|
|
165
|
-
it("zero angles give identity", () => {
|
|
166
|
-
const q = Quaternion.fromEulerAngles(0, 0, 0);
|
|
167
|
-
expect(q.w).toBeCloseTo(1);
|
|
168
|
-
expect(q.x).toBeCloseTo(0);
|
|
169
|
-
expect(q.y).toBeCloseTo(0);
|
|
170
|
-
expect(q.z).toBeCloseTo(0);
|
|
171
|
-
});
|
|
172
|
-
});
|
|
173
|
-
describe("toEulerAngles", () => {
|
|
174
|
-
it("extracts euler angles", () => {
|
|
175
|
-
const q = Quaternion.fromEulerAngles(0.1, 0.2, 0.3);
|
|
176
|
-
const euler = q.toEulerAngles();
|
|
177
|
-
expect(euler.x).toBeCloseTo(0.1, 4);
|
|
178
|
-
expect(euler.y).toBeCloseTo(0.2, 4);
|
|
179
|
-
expect(euler.z).toBeCloseTo(0.3, 4);
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
|
-
describe("slerp", () => {
|
|
183
|
-
it("returns start at t=0", () => {
|
|
184
|
-
const q1 = Quaternion.fromAxisAngle(Vector3d.unitZ(), 0);
|
|
185
|
-
const q2 = Quaternion.fromAxisAngle(Vector3d.unitZ(), Math.PI);
|
|
186
|
-
const result = q1.slerp(q2, 0);
|
|
187
|
-
expect(result.equals(q1, 1e-10)).toBe(true);
|
|
188
|
-
});
|
|
189
|
-
it("returns end at t=1", () => {
|
|
190
|
-
const q1 = Quaternion.fromAxisAngle(Vector3d.unitZ(), 0);
|
|
191
|
-
const q2 = Quaternion.fromAxisAngle(Vector3d.unitZ(), Math.PI / 2);
|
|
192
|
-
const result = q1.slerp(q2, 1);
|
|
193
|
-
expect(result.x).toBeCloseTo(q2.x);
|
|
194
|
-
expect(result.y).toBeCloseTo(q2.y);
|
|
195
|
-
expect(result.z).toBeCloseTo(q2.z);
|
|
196
|
-
expect(result.w).toBeCloseTo(q2.w);
|
|
197
|
-
});
|
|
198
|
-
it("interpolates at t=0.5", () => {
|
|
199
|
-
const q1 = Quaternion.fromAxisAngle(Vector3d.unitZ(), 0);
|
|
200
|
-
const q2 = Quaternion.fromAxisAngle(Vector3d.unitZ(), Math.PI / 2);
|
|
201
|
-
const result = q1.slerp(q2, 0.5);
|
|
202
|
-
// Should be 45 degree rotation
|
|
203
|
-
const v = new Vector3d(1, 0, 0);
|
|
204
|
-
const rotated = result.rotateVector(v);
|
|
205
|
-
expect(rotated.x).toBeCloseTo(Math.cos(Math.PI / 4));
|
|
206
|
-
expect(rotated.y).toBeCloseTo(Math.sin(Math.PI / 4));
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
describe("equals", () => {
|
|
210
|
-
it("returns true for identical quaternions", () => {
|
|
211
|
-
const q1 = new Quaternion(1, 2, 3, 4);
|
|
212
|
-
const q2 = new Quaternion(1, 2, 3, 4);
|
|
213
|
-
expect(q1.equals(q2)).toBe(true);
|
|
214
|
-
});
|
|
215
|
-
it("returns false for different quaternions", () => {
|
|
216
|
-
const q1 = new Quaternion(1, 2, 3, 4);
|
|
217
|
-
const q2 = new Quaternion(1, 2, 3, 5);
|
|
218
|
-
expect(q1.equals(q2)).toBe(false);
|
|
219
|
-
});
|
|
220
|
-
it("supports tolerance", () => {
|
|
221
|
-
const q1 = new Quaternion(1, 2, 3, 4);
|
|
222
|
-
const q2 = new Quaternion(1.001, 2.001, 3.001, 4.001);
|
|
223
|
-
expect(q1.equals(q2, 0.01)).toBe(true);
|
|
224
|
-
});
|
|
225
|
-
});
|
|
226
|
-
describe("toMatrix", () => {
|
|
227
|
-
it("returns identity matrix for identity quaternion", () => {
|
|
228
|
-
const q = Quaternion.identity();
|
|
229
|
-
const m = q.toMatrix();
|
|
230
|
-
expect(m[0]).toBeCloseTo(1);
|
|
231
|
-
expect(m[5]).toBeCloseTo(1);
|
|
232
|
-
expect(m[10]).toBeCloseTo(1);
|
|
233
|
-
expect(m[15]).toBeCloseTo(1);
|
|
234
|
-
});
|
|
235
|
-
it("rotation matrix rotates correctly", () => {
|
|
236
|
-
const q = Quaternion.fromAxisAngle(Vector3d.unitZ(), Math.PI / 2);
|
|
237
|
-
const m = q.toMatrix();
|
|
238
|
-
// Apply matrix to vector (1,0,0)
|
|
239
|
-
const x = m[0] * 1 + m[4] * 0 + m[8] * 0;
|
|
240
|
-
const y = m[1] * 1 + m[5] * 0 + m[9] * 0;
|
|
241
|
-
const z = m[2] * 1 + m[6] * 0 + m[10] * 0;
|
|
242
|
-
expect(x).toBeCloseTo(0);
|
|
243
|
-
expect(y).toBeCloseTo(1);
|
|
244
|
-
expect(z).toBeCloseTo(0);
|
|
245
|
-
});
|
|
246
|
-
});
|
|
247
|
-
describe("fromRotationMatrix", () => {
|
|
248
|
-
it("reconstructs quaternion from its matrix", () => {
|
|
249
|
-
const original = Quaternion.fromAxisAngle(new Vector3d(1, 1, 1).normalize(), Math.PI / 3);
|
|
250
|
-
const matrix = original.toMatrix();
|
|
251
|
-
const reconstructed = Quaternion.fromRotationMatrix(matrix);
|
|
252
|
-
// Quaternions q and -q represent the same rotation
|
|
253
|
-
const sameRotation = original.equals(reconstructed, 1e-10) ||
|
|
254
|
-
original.equals(new Quaternion(-reconstructed.x, -reconstructed.y, -reconstructed.z, -reconstructed.w), 1e-10);
|
|
255
|
-
expect(sameRotation).toBe(true);
|
|
256
|
-
});
|
|
257
|
-
});
|
|
258
|
-
describe("dot", () => {
|
|
259
|
-
it("computes dot product", () => {
|
|
260
|
-
const q1 = new Quaternion(1, 0, 0, 0);
|
|
261
|
-
const q2 = new Quaternion(1, 0, 0, 0);
|
|
262
|
-
expect(q1.dot(q2)).toBe(1);
|
|
263
|
-
});
|
|
264
|
-
it("returns 0 for perpendicular quaternions", () => {
|
|
265
|
-
const q1 = new Quaternion(1, 0, 0, 0);
|
|
266
|
-
const q2 = new Quaternion(0, 1, 0, 0);
|
|
267
|
-
expect(q1.dot(q2)).toBe(0);
|
|
268
|
-
});
|
|
269
|
-
});
|
|
270
|
-
describe("clone", () => {
|
|
271
|
-
it("creates independent copy", () => {
|
|
272
|
-
const q = new Quaternion(1, 2, 3, 4);
|
|
273
|
-
const clone = q.clone();
|
|
274
|
-
expect(clone.equals(q)).toBe(true);
|
|
275
|
-
expect(clone).not.toBe(q);
|
|
276
|
-
});
|
|
277
|
-
});
|
|
278
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|