@immugio/three-math-extensions 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +32 -0
- package/README +1 -0
- package/cjs/Line2D.js +527 -0
- package/cjs/Line3D.js +373 -0
- package/cjs/Point2.js +2 -0
- package/cjs/Point3.js +2 -0
- package/cjs/Vec2.js +19 -0
- package/cjs/Vec3.js +57 -0
- package/cjs/index.js +11 -0
- package/jest.config.js +6 -0
- package/package.json +44 -0
- package/src/Line2D.ts +615 -0
- package/src/Line3D.ts +448 -0
- package/src/Point2.ts +4 -0
- package/src/Point3.ts +6 -0
- package/src/Vec2.ts +18 -0
- package/src/Vec3.ts +70 -0
- package/src/__tests__/Line2D.spec.ts +329 -0
- package/src/__tests__/Line3D.join.spec.ts +28 -0
- package/src/__tests__/Line3D.joinLines.spec.ts +72 -0
- package/src/__tests__/Line3D.spec.ts +321 -0
- package/src/index.ts +4 -0
- package/tsconfig-cjs.json +9 -0
- package/tsconfig.json +13 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import { Line3D } from "../Line3D";
|
|
2
|
+
import { Vec3 } from "../Vec3";
|
|
3
|
+
|
|
4
|
+
const defaultLine = () => new Line3D(new Vec3(-10, 0, 0), new Vec3(10, 0, 0));
|
|
5
|
+
|
|
6
|
+
describe("Line3d", () => {
|
|
7
|
+
it("should create a line with expected values", () => {
|
|
8
|
+
const line = new Line3D(new Vec3(1, 2, 3), new Vec3(4, 5, 6));
|
|
9
|
+
expect(line.start).toEqual(new Vec3(1, 2, 3));
|
|
10
|
+
expect(line.end).toEqual(new Vec3(4, 5, 6));
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("should return the expected center", () => {
|
|
14
|
+
const line = defaultLine();
|
|
15
|
+
expect(line.center).toEqual(new Vec3(0, 0, 0));
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("should resize should resize the line by the given length", () => {
|
|
19
|
+
const line = defaultLine();
|
|
20
|
+
const originalCenter = line.center;
|
|
21
|
+
expect(line.length).toEqual(20);
|
|
22
|
+
|
|
23
|
+
const resizeDistance = 2;
|
|
24
|
+
line.resize(resizeDistance);
|
|
25
|
+
|
|
26
|
+
expect(line.length).toEqual(22);
|
|
27
|
+
expect(line.center).toEqual(originalCenter);
|
|
28
|
+
expect(line.start.x).toEqual(-11);
|
|
29
|
+
expect(line.end.x).toEqual(11);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("should setLength to required", () => {
|
|
33
|
+
const line = new Line3D(new Vec3(0, 0, 0), new Vec3(10, 0, 0));
|
|
34
|
+
const originalCenter = line.center;
|
|
35
|
+
expect(line.length).toEqual(10);
|
|
36
|
+
|
|
37
|
+
const newSize = 2;
|
|
38
|
+
line.setLength(newSize);
|
|
39
|
+
|
|
40
|
+
expect(line.length).toEqual(newSize);
|
|
41
|
+
expect(line.center).toEqual(originalCenter);
|
|
42
|
+
expect(line.start.x).toEqual(4);
|
|
43
|
+
expect(line.end.x).toEqual(6);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("should project the line on another", () => {
|
|
47
|
+
// Arrange
|
|
48
|
+
const other = new Line3D(new Vec3(0, 0, 0), new Vec3(10, 0, 0));
|
|
49
|
+
const line = new Line3D(new Vec3(-1, 1, 0), new Vec3(10, 1, 0));
|
|
50
|
+
|
|
51
|
+
// Act
|
|
52
|
+
const projected = line.projectOn(other, false);
|
|
53
|
+
|
|
54
|
+
// Assert
|
|
55
|
+
expect(projected.start).toEqual(new Vec3(-1, 0, 0));
|
|
56
|
+
expect(projected.end).toEqual(new Vec3(10, 0, 0));
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("should project and clamp the line on another", () => {
|
|
60
|
+
// Arrange
|
|
61
|
+
const other = new Line3D(new Vec3(0, 0, 0), new Vec3(10, 0, 0));
|
|
62
|
+
const line = new Line3D(new Vec3(-1, 1, 0), new Vec3(10, 1, 0));
|
|
63
|
+
|
|
64
|
+
// Act
|
|
65
|
+
const projected = line.projectOn(other, true);
|
|
66
|
+
|
|
67
|
+
// Assert
|
|
68
|
+
expect(projected.start).toEqual(new Vec3(0, 0, 0));
|
|
69
|
+
expect(projected.end).toEqual(new Vec3(10, 0, 0));
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("should contain point", () => {
|
|
73
|
+
// Arrange
|
|
74
|
+
const line = new Line3D(new Vec3(0, 0, 0), new Vec3(10, 0, 0));
|
|
75
|
+
|
|
76
|
+
// Act
|
|
77
|
+
const contains = line.containsPoint(new Vec3(5, 0, 0));
|
|
78
|
+
|
|
79
|
+
// Assert
|
|
80
|
+
expect(contains).toBeTruthy();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("should not contain point", () => {
|
|
84
|
+
// Arrange
|
|
85
|
+
const line = new Line3D(new Vec3(0, 0, 0), new Vec3(10, 0, 0));
|
|
86
|
+
|
|
87
|
+
// Act
|
|
88
|
+
const contains = line.containsPoint(new Vec3(5, 1, 0));
|
|
89
|
+
|
|
90
|
+
// Assert
|
|
91
|
+
expect(contains).toBeFalsy();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it.each([
|
|
95
|
+
["lines are the same", new Line3D(new Vec3(0, 0, 0), new Vec3(10, 0, 0)), new Line3D(new Vec3(0, 0, 0), new Vec3(10, 0, 0)), true],
|
|
96
|
+
["overlap from right", new Line3D(new Vec3(0, 0, 0), new Vec3(10, 0, 0)), new Line3D(new Vec3(5, 0, 0), new Vec3(11, 0, 0)), true],
|
|
97
|
+
["overlap from left", new Line3D(new Vec3(0, 0, 0), new Vec3(10, 0, 0)), new Line3D(new Vec3(-5, 0, 0), new Vec3(5, 0, 0)), true],
|
|
98
|
+
["second inside the first", new Line3D(new Vec3(0, 0, 0), new Vec3(10, 0, 0)), new Line3D(new Vec3(5, 0, 0), new Vec3(6, 0, 0)), true],
|
|
99
|
+
["fist inside the second", new Line3D(new Vec3(5, 0, 0), new Vec3(6, 0, 0)), new Line3D(new Vec3(0, 0, 0), new Vec3(10, 0, 0)), true],
|
|
100
|
+
["no overlap", new Line3D(new Vec3(0, 0, 0), new Vec3(10, 0, 0)), new Line3D(new Vec3(11, 0, 0), new Vec3(12, 0, 0)), false],
|
|
101
|
+
["no overlap, but touching ends", new Line3D(new Vec3(0, 0, 0), new Vec3(10, 0, 0)), new Line3D(new Vec3(10, 0, 0), new Vec3(12, 0, 0)), false],
|
|
102
|
+
["not parallel", new Line3D(new Vec3(0, 0, 0), new Vec3(10, 0, 0)), new Line3D(new Vec3(5, 0, 0), new Vec3(10, 1, 0)), false],
|
|
103
|
+
])("should detect overlap of two lines - %s", (reason, line1, line2, expected) => {
|
|
104
|
+
// Act
|
|
105
|
+
const result = line1.overlaps(line2);
|
|
106
|
+
|
|
107
|
+
// Assert
|
|
108
|
+
expect(result).toEqual(expected);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("should trim the line and return two offcuts, clip well within source", () => {
|
|
112
|
+
// Arrange
|
|
113
|
+
const source = new Line3D(new Vec3(0, 0, 0), new Vec3(10, 0, 0));
|
|
114
|
+
const clip = new Line3D(new Vec3(3, 0, 0), new Vec3(7, 0, 0));
|
|
115
|
+
|
|
116
|
+
// Act
|
|
117
|
+
const result = source.clipLine(clip);
|
|
118
|
+
const groupResult = source.clipLines([clip]);
|
|
119
|
+
|
|
120
|
+
// Assert
|
|
121
|
+
expect(result.length).toEqual(2);
|
|
122
|
+
|
|
123
|
+
expect(result[0].start).toEqual(new Vec3(0, 0, 0));
|
|
124
|
+
expect(result[0].end).toEqual(new Vec3(3, 0, 0));
|
|
125
|
+
|
|
126
|
+
expect(result[1].start).toEqual(new Vec3(7, 0, 0));
|
|
127
|
+
expect(result[1].end).toEqual(new Vec3(10, 0, 0));
|
|
128
|
+
|
|
129
|
+
expect(groupResult.sort((a, b) => a.start.x - b.start.x)).toEqual(result.sort((a, b) => a.start.x - b.start.x));
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("should trim the lines even if the clips are overlapping and return two offcuts", () => {
|
|
133
|
+
// Arrange
|
|
134
|
+
const source = new Line3D(new Vec3(0, 0, 0), new Vec3(10, 0, 0));
|
|
135
|
+
const clips = [
|
|
136
|
+
new Line3D(new Vec3(-3, 0, 0), new Vec3(2, 0, 0)),
|
|
137
|
+
new Line3D(new Vec3(3, 0, 0), new Vec3(6, 0, 0)),
|
|
138
|
+
new Line3D(new Vec3(4, 0, 0), new Vec3(7, 0, 0)),
|
|
139
|
+
];
|
|
140
|
+
|
|
141
|
+
// Act
|
|
142
|
+
const result = source.clipLines(clips);
|
|
143
|
+
|
|
144
|
+
// Assert
|
|
145
|
+
result.sort((a, b) => a.start.x - b.start.x); // Order from the clipping is not guaranteed
|
|
146
|
+
expect(result).toEqual([
|
|
147
|
+
new Line3D(new Vec3(2, 0, 0), new Vec3(3, 0, 0)),
|
|
148
|
+
new Line3D(new Vec3(7, 0, 0), new Vec3(10, 0, 0)),
|
|
149
|
+
]);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it.each([
|
|
153
|
+
{ tolerance: 0 },
|
|
154
|
+
{ tolerance: 1 },
|
|
155
|
+
])("should trim the line and return two offcuts even if the source and trim run in opposite direction and are not perfectly parallel when sufficient tolerance is provided", ({ tolerance }) => {
|
|
156
|
+
// Arrange
|
|
157
|
+
const source = new Line3D(new Vec3(
|
|
158
|
+
1907.0952296605503,
|
|
159
|
+
0,
|
|
160
|
+
5258.129694575135
|
|
161
|
+
), new Vec3(
|
|
162
|
+
1907.0952296605506,
|
|
163
|
+
0,
|
|
164
|
+
4302.1493774205865
|
|
165
|
+
));
|
|
166
|
+
const clip = new Line3D(new Vec3(
|
|
167
|
+
1907.0952296605503,
|
|
168
|
+
0,
|
|
169
|
+
4762.083899346474
|
|
170
|
+
), new Vec3(
|
|
171
|
+
1907.0952296605503,
|
|
172
|
+
0,
|
|
173
|
+
4954.083899346474
|
|
174
|
+
));
|
|
175
|
+
|
|
176
|
+
// Act
|
|
177
|
+
const result = source.clipLine(clip, tolerance);
|
|
178
|
+
|
|
179
|
+
// Assert
|
|
180
|
+
if (tolerance === 0) {
|
|
181
|
+
expect(result).toEqual([source]);
|
|
182
|
+
} else {
|
|
183
|
+
expect(result).toEqual([
|
|
184
|
+
new Line3D(new Vec3(1907.0952296605503, 0, 5258.129694575135), new Vec3(1907.0952296605503, 0, 4954.083899346474)),
|
|
185
|
+
new Line3D(new Vec3(1907.0952296605503, 0, 4762.083899346474), new Vec3(1907.0952296605506, 0, 4302.1493774205865)),
|
|
186
|
+
]);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
it("should trim the line and return right side offcuts, clip is completely contained in source", () => {
|
|
192
|
+
// Arrange
|
|
193
|
+
const source = new Line3D(new Vec3(0, 0, 0), new Vec3(10, 0, 0));
|
|
194
|
+
const clip = new Line3D(new Vec3(0, 0, 0), new Vec3(7, 0, 0));
|
|
195
|
+
|
|
196
|
+
// Act
|
|
197
|
+
const result = source.clipLine(clip);
|
|
198
|
+
const groupResult = source.clipLines([clip]);
|
|
199
|
+
|
|
200
|
+
// Assert
|
|
201
|
+
expect(result.length).toEqual(1);
|
|
202
|
+
expect(result[0].start).toEqual(new Vec3(7, 0, 0));
|
|
203
|
+
expect(result[0].end).toEqual(new Vec3(10, 0, 0));
|
|
204
|
+
expect(result).toEqual(groupResult);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it("should trim the line and return right side offcuts, clip overlapping from left", () => {
|
|
208
|
+
// Arrange
|
|
209
|
+
const source = new Line3D(new Vec3(0, 0, 0), new Vec3(10, 0, 0));
|
|
210
|
+
const clip = new Line3D(new Vec3(-1, 0, 0), new Vec3(7, 0, 0));
|
|
211
|
+
|
|
212
|
+
// Act
|
|
213
|
+
const result = source.clipLine(clip);
|
|
214
|
+
const groupResult = source.clipLines([clip]);
|
|
215
|
+
|
|
216
|
+
// Assert
|
|
217
|
+
expect(result.length).toEqual(1);
|
|
218
|
+
expect(result[0].start).toEqual(new Vec3(7, 0, 0));
|
|
219
|
+
expect(result[0].end).toEqual(new Vec3(10, 0, 0));
|
|
220
|
+
expect(result).toEqual(groupResult);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it("should trim the line and return left side off-cut, clip is completely contained in source", () => {
|
|
224
|
+
// Arrange
|
|
225
|
+
const source = new Line3D(new Vec3(0, 0, 0), new Vec3(10, 0, 0));
|
|
226
|
+
const clip = new Line3D(new Vec3(5, 0, 0), new Vec3(10, 0, 0));
|
|
227
|
+
|
|
228
|
+
// Act
|
|
229
|
+
const result = source.clipLine(clip);
|
|
230
|
+
const groupResult = source.clipLines([clip]);
|
|
231
|
+
|
|
232
|
+
// Assert
|
|
233
|
+
expect(result.length).toEqual(1);
|
|
234
|
+
expect(result[0].start).toEqual(new Vec3(0, 0, 0));
|
|
235
|
+
expect(result[0].end).toEqual(new Vec3(5, 0, 0));
|
|
236
|
+
expect(result).toEqual(groupResult);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it("should trim the line and return left side off-cut, clip overlapping from right", () => {
|
|
240
|
+
// Arrange
|
|
241
|
+
const source = new Line3D(new Vec3(0, 0, 0), new Vec3(10, 0, 0));
|
|
242
|
+
const clip = new Line3D(new Vec3(5, 0, 0), new Vec3(15, 0, 0));
|
|
243
|
+
|
|
244
|
+
// Act
|
|
245
|
+
const result = source.clipLine(clip);
|
|
246
|
+
const groupResult = source.clipLines([clip]);
|
|
247
|
+
|
|
248
|
+
// Assert
|
|
249
|
+
expect(result.length).toEqual(1);
|
|
250
|
+
expect(result[0].start).toEqual(new Vec3(0, 0, 0));
|
|
251
|
+
expect(result[0].end).toEqual(new Vec3(5, 0, 0));
|
|
252
|
+
expect(result).toEqual(groupResult);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it("should trim the line and return no off-cut as it matches the source entirely", () => {
|
|
256
|
+
// Arrange
|
|
257
|
+
const source = new Line3D(new Vec3(0, 0, 0), new Vec3(10, 0, 0));
|
|
258
|
+
const clip = new Line3D(new Vec3(0, 0, 0), new Vec3(10, 0, 0));
|
|
259
|
+
|
|
260
|
+
// Act
|
|
261
|
+
const result = source.clipLine(clip);
|
|
262
|
+
const groupResult = source.clipLines([clip]);
|
|
263
|
+
|
|
264
|
+
// Assert
|
|
265
|
+
expect(result.length).toEqual(0);
|
|
266
|
+
expect(result).toEqual(groupResult);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it("should trim the line and return no off-cut as it overlaps the source", () => {
|
|
270
|
+
// Arrange
|
|
271
|
+
const source = new Line3D(new Vec3(0, 0, 0), new Vec3(10, 0, 0));
|
|
272
|
+
const clip = new Line3D(new Vec3(-1, 0, 0), new Vec3(11, 0, 0));
|
|
273
|
+
|
|
274
|
+
// Act
|
|
275
|
+
const result = source.clipLine(clip);
|
|
276
|
+
const groupResult = source.clipLines([clip]);
|
|
277
|
+
|
|
278
|
+
// Assert
|
|
279
|
+
expect(result.length).toEqual(0);
|
|
280
|
+
expect(result).toEqual(groupResult);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it("should trim the line with opposite direction", () => {
|
|
284
|
+
// Arrange
|
|
285
|
+
const source = new Line3D(new Vec3(6, 0, 0), new Vec3(10, 0, 0));
|
|
286
|
+
const clip = new Line3D(new Vec3(7, 0, 0), new Vec3(4, 0, 0));
|
|
287
|
+
|
|
288
|
+
// Act
|
|
289
|
+
const result = source.clipLine(clip);
|
|
290
|
+
const groupResult = source.clipLines([clip]);
|
|
291
|
+
|
|
292
|
+
// Assert
|
|
293
|
+
expect(result).toEqual([new Line3D(new Vec3(7, 0, 0), new Vec3(10, 0, 0))]);
|
|
294
|
+
expect(result).toEqual(groupResult);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it.each([
|
|
298
|
+
// Arrange
|
|
299
|
+
{ a: new Line3D(new Vec3(0, 0, 0), new Vec3(10, 0, 0)), b: new Line3D(new Vec3(0, 0, 0), new Vec3(10, 0, 0)), expected: true },
|
|
300
|
+
{ a: new Line3D(new Vec3(10, 0, 0), new Vec3(0, 0, 0)), b: new Line3D(new Vec3(0, 0, 0), new Vec3(10, 0, 0)), expected: true },
|
|
301
|
+
{ a: new Line3D(new Vec3(0, 0, 0), new Vec3(10, 1, 0)), b: new Line3D(new Vec3(0, 0, 0), new Vec3(10, 0, 0)), expected: false },
|
|
302
|
+
])("should determine if the lines are parallel %j", ({ a, b, expected }) => {
|
|
303
|
+
// Act
|
|
304
|
+
const areParallel = a.isParallelTo(b);
|
|
305
|
+
// Assert
|
|
306
|
+
expect(areParallel).toBe(expected);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it("chunk should split the line in to multiple with expected length", () => {
|
|
310
|
+
const size = 10;
|
|
311
|
+
const line = new Line3D(new Vec3(0, 0, 0), new Vec3(88, 0, 0));
|
|
312
|
+
const chunks = line.chunk(size);
|
|
313
|
+
expect(chunks.length).toEqual(9);
|
|
314
|
+
|
|
315
|
+
expect(chunks[0].length).toEqual(size);
|
|
316
|
+
expect(chunks[0].start.equals(line.start)).toBe(true);
|
|
317
|
+
|
|
318
|
+
expect(chunks[chunks.length - 1].length).toEqual(8);
|
|
319
|
+
expect(chunks[chunks.length - 1].end.equals(line.end)).toBe(true);
|
|
320
|
+
});
|
|
321
|
+
});
|
package/src/index.ts
ADDED
package/tsconfig.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"outDir": "./esm",
|
|
4
|
+
"declaration": true,
|
|
5
|
+
"declarationDir": "./types",
|
|
6
|
+
"importHelpers": true,
|
|
7
|
+
"target": "ESNext",
|
|
8
|
+
"module": "ESNext",
|
|
9
|
+
"moduleResolution": "node",
|
|
10
|
+
"lib": [ "esnext", "dom" ]
|
|
11
|
+
},
|
|
12
|
+
"exclude": ["src/**/*.test.ts", "src/**/*.spec.ts"]
|
|
13
|
+
}
|