@bitbybit-dev/base 0.20.2 → 0.20.3
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/LICENSE +1 -1
- package/lib/api/inputs/base-inputs.d.ts +8 -0
- package/lib/api/inputs/index.d.ts +3 -0
- package/lib/api/inputs/index.js +3 -0
- package/lib/api/inputs/inputs.d.ts +3 -0
- package/lib/api/inputs/inputs.js +3 -0
- package/lib/api/inputs/line-inputs.d.ts +240 -0
- package/lib/api/inputs/line-inputs.js +247 -0
- package/lib/api/inputs/mesh-inputs.d.ts +82 -0
- package/lib/api/inputs/mesh-inputs.js +83 -0
- package/lib/api/inputs/point-inputs.d.ts +153 -0
- package/lib/api/inputs/point-inputs.js +188 -0
- package/lib/api/inputs/polyline-inputs.d.ts +206 -0
- package/lib/api/inputs/polyline-inputs.js +229 -0
- package/lib/api/inputs/text-inputs.d.ts +1 -1
- package/lib/api/inputs/transforms-inputs.d.ts +18 -0
- package/lib/api/inputs/transforms-inputs.js +29 -0
- package/lib/api/inputs/vector-inputs.d.ts +8 -0
- package/lib/api/inputs/vector-inputs.js +8 -0
- package/lib/api/models/index.d.ts +1 -0
- package/lib/api/models/index.js +1 -0
- package/lib/api/models/point/bucket.d.ts +1 -0
- package/lib/api/models/point/bucket.js +1 -0
- package/lib/api/models/point/hex-grid-data.d.ts +8 -0
- package/lib/api/models/point/hex-grid-data.js +2 -0
- package/lib/api/models/point/index.d.ts +1 -0
- package/lib/api/models/point/index.js +1 -0
- package/lib/api/services/dates.js +45 -15
- package/lib/api/services/index.d.ts +3 -0
- package/lib/api/services/index.js +3 -0
- package/lib/api/services/line.d.ts +149 -0
- package/lib/api/services/line.js +320 -0
- package/lib/api/services/lists.d.ts +1 -1
- package/lib/api/services/lists.js +1 -2
- package/lib/api/services/mesh.d.ts +66 -0
- package/lib/api/services/mesh.js +235 -0
- package/lib/api/services/point.d.ts +96 -1
- package/lib/api/services/point.js +540 -1
- package/lib/api/services/polyline.d.ts +149 -0
- package/lib/api/services/polyline.js +444 -0
- package/lib/api/services/transforms.d.ts +26 -1
- package/lib/api/services/transforms.js +66 -3
- package/lib/api/services/vector.d.ts +18 -0
- package/lib/api/services/vector.js +27 -0
- package/lib/api/unit-test-helper.d.ts +20 -0
- package/lib/api/unit-test-helper.js +130 -0
- package/package.json +2 -2
|
@@ -109,6 +109,23 @@ export class Transforms {
|
|
|
109
109
|
scaleXYZ(inputs) {
|
|
110
110
|
return [this.scaling(inputs.scaleXyz[0], inputs.scaleXyz[1], inputs.scaleXyz[2])];
|
|
111
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Creates a stretch transformation along a specific direction, relative to a center point.
|
|
114
|
+
* This scales points along the given direction vector while leaving points in the
|
|
115
|
+
* plane perpendicular to the direction (passing through the center) unchanged.
|
|
116
|
+
* @param inputs Defines the center, direction, and scale factor for the stretch.
|
|
117
|
+
* @returns Array of transformations: [Translate To Origin, Stretch, Translate Back].
|
|
118
|
+
* @group scale
|
|
119
|
+
* @shortname stretch dir center
|
|
120
|
+
* @drawable false
|
|
121
|
+
*/
|
|
122
|
+
stretchDirFromCenter(inputs) {
|
|
123
|
+
return [
|
|
124
|
+
this.translation(-inputs.center[0], -inputs.center[1], -inputs.center[2]),
|
|
125
|
+
this.stretchDirection(inputs.direction, inputs.scale),
|
|
126
|
+
this.translation(inputs.center[0], inputs.center[1], inputs.center[2]),
|
|
127
|
+
];
|
|
128
|
+
}
|
|
112
129
|
/**
|
|
113
130
|
* Creates uniform scale transformation
|
|
114
131
|
* @param inputs Scale Dto
|
|
@@ -157,15 +174,22 @@ export class Transforms {
|
|
|
157
174
|
translationsXYZ(inputs) {
|
|
158
175
|
return inputs.translations.map(translation => [this.translation(translation[0], translation[1], translation[2])]);
|
|
159
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* Creates the identity transformation
|
|
179
|
+
* @returns transformation
|
|
180
|
+
* @group identity
|
|
181
|
+
* @shortname identity
|
|
182
|
+
* @drawable false
|
|
183
|
+
*/
|
|
184
|
+
identity() {
|
|
185
|
+
return [1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0];
|
|
186
|
+
}
|
|
160
187
|
translation(x, y, z) {
|
|
161
188
|
return [1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, x, y, z, 1.0];
|
|
162
189
|
}
|
|
163
190
|
scaling(x, y, z) {
|
|
164
191
|
return [x, 0.0, 0.0, 0.0, 0.0, y, 0.0, 0.0, 0.0, 0.0, z, 0.0, 0.0, 0.0, 0.0, 1.0];
|
|
165
192
|
}
|
|
166
|
-
identity() {
|
|
167
|
-
return [1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0];
|
|
168
|
-
}
|
|
169
193
|
rotationAxis(axis, angle) {
|
|
170
194
|
const s = Math.sin(-angle);
|
|
171
195
|
const c = Math.cos(-angle);
|
|
@@ -253,4 +277,43 @@ export class Transforms {
|
|
|
253
277
|
m[15] = 1.0;
|
|
254
278
|
return m;
|
|
255
279
|
}
|
|
280
|
+
/**
|
|
281
|
+
* Creates a 4x4 matrix that scales along a given direction vector.
|
|
282
|
+
* @param direction The direction vector (will be normalized).
|
|
283
|
+
* @param scale The scale factor along the direction.
|
|
284
|
+
* @returns A 4x4 column-major transformation matrix.
|
|
285
|
+
*/
|
|
286
|
+
stretchDirection(direction, scale) {
|
|
287
|
+
const d = this.vector.normalized({ vector: direction });
|
|
288
|
+
const [dx, dy, dz] = d;
|
|
289
|
+
// Handle potential zero vector after normalization (if input was zero)
|
|
290
|
+
if (isNaN(dx) || (dx === 0 && dy === 0 && dz === 0)) {
|
|
291
|
+
console.warn("Stretch direction vector is zero or invalid. Returning identity matrix.");
|
|
292
|
+
return this.identity();
|
|
293
|
+
}
|
|
294
|
+
const s = scale;
|
|
295
|
+
const sMinus1 = s - 1.0;
|
|
296
|
+
// Calculate elements of the 3x3 directional scaling part
|
|
297
|
+
const m11 = 1.0 + sMinus1 * dx * dx;
|
|
298
|
+
const m12 = sMinus1 * dx * dy;
|
|
299
|
+
const m13 = sMinus1 * dx * dz;
|
|
300
|
+
// m14 = 0
|
|
301
|
+
const m21 = sMinus1 * dy * dx;
|
|
302
|
+
const m22 = 1.0 + sMinus1 * dy * dy;
|
|
303
|
+
const m23 = sMinus1 * dy * dz;
|
|
304
|
+
// m24 = 0
|
|
305
|
+
const m31 = sMinus1 * dz * dx;
|
|
306
|
+
const m32 = sMinus1 * dz * dy;
|
|
307
|
+
const m33 = 1.0 + sMinus1 * dz * dz;
|
|
308
|
+
// m34 = 0
|
|
309
|
+
// m41, m42, m43 = 0, m44 = 1
|
|
310
|
+
// Assemble the 4x4 matrix in COLUMN-MAJOR order
|
|
311
|
+
const m = [
|
|
312
|
+
m11, m21, m31, 0.0,
|
|
313
|
+
m12, m22, m32, 0.0,
|
|
314
|
+
m13, m23, m33, 0.0,
|
|
315
|
+
0.0, 0.0, 0.0, 1.0 // Column 4
|
|
316
|
+
];
|
|
317
|
+
return m;
|
|
318
|
+
}
|
|
256
319
|
}
|
|
@@ -326,4 +326,22 @@ export declare class Vector {
|
|
|
326
326
|
* @drawable false
|
|
327
327
|
*/
|
|
328
328
|
sum(inputs: Inputs.Vector.VectorDto): number;
|
|
329
|
+
/**
|
|
330
|
+
* Computes the squared length of the vector
|
|
331
|
+
* @param inputs Vector to compute the length
|
|
332
|
+
* @returns Number that is squared length of the vector
|
|
333
|
+
* @group base
|
|
334
|
+
* @shortname length squared
|
|
335
|
+
* @drawable false
|
|
336
|
+
*/
|
|
337
|
+
lengthSq(inputs: Inputs.Vector.Vector3Dto): number;
|
|
338
|
+
/**
|
|
339
|
+
* Computes the length of the vector
|
|
340
|
+
* @param inputs Vector to compute the length
|
|
341
|
+
* @returns Number that is length of the vector
|
|
342
|
+
* @group base
|
|
343
|
+
* @shortname length
|
|
344
|
+
* @drawable false
|
|
345
|
+
*/
|
|
346
|
+
length(inputs: Inputs.Vector.Vector3Dto): number;
|
|
329
347
|
}
|
|
@@ -328,6 +328,10 @@ export class Vector {
|
|
|
328
328
|
* @drawable false
|
|
329
329
|
*/
|
|
330
330
|
normalized(inputs) {
|
|
331
|
+
const len = this.length({ vector: inputs.vector });
|
|
332
|
+
if (len <= 1e-8) {
|
|
333
|
+
return undefined;
|
|
334
|
+
}
|
|
331
335
|
return this.div({ scalar: this.norm(inputs), vector: inputs.vector });
|
|
332
336
|
}
|
|
333
337
|
/**
|
|
@@ -476,4 +480,27 @@ export class Vector {
|
|
|
476
480
|
sum(inputs) {
|
|
477
481
|
return inputs.vector.reduce((a, b) => a + b, 0);
|
|
478
482
|
}
|
|
483
|
+
/**
|
|
484
|
+
* Computes the squared length of the vector
|
|
485
|
+
* @param inputs Vector to compute the length
|
|
486
|
+
* @returns Number that is squared length of the vector
|
|
487
|
+
* @group base
|
|
488
|
+
* @shortname length squared
|
|
489
|
+
* @drawable false
|
|
490
|
+
*/
|
|
491
|
+
lengthSq(inputs) {
|
|
492
|
+
const v = inputs.vector;
|
|
493
|
+
return v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Computes the length of the vector
|
|
497
|
+
* @param inputs Vector to compute the length
|
|
498
|
+
* @returns Number that is length of the vector
|
|
499
|
+
* @group base
|
|
500
|
+
* @shortname length
|
|
501
|
+
* @drawable false
|
|
502
|
+
*/
|
|
503
|
+
length(inputs) {
|
|
504
|
+
return Math.sqrt(this.lengthSq(inputs));
|
|
505
|
+
}
|
|
479
506
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as Inputs from "./inputs";
|
|
2
|
+
import { Vector } from "./services";
|
|
3
|
+
export declare const TOLERANCE = 1e-7;
|
|
4
|
+
export declare class UnitTestHelper {
|
|
5
|
+
vector: Vector;
|
|
6
|
+
constructor();
|
|
7
|
+
expectPointCloseTo(received: Inputs.Base.Point3 | Inputs.Base.Vector3 | undefined, expected: Inputs.Base.Point3 | Inputs.Base.Vector3): void;
|
|
8
|
+
expectPointsCloseTo(received: Inputs.Base.Point3[] | Inputs.Base.Vector3[], expected: Inputs.Base.Point3[] | Inputs.Base.Vector3[]): void;
|
|
9
|
+
expectLineCloseTo(received: Inputs.Base.Line3 | undefined, expected: Inputs.Base.Line3): void;
|
|
10
|
+
expectLinesCloseTo(received: Inputs.Base.Line3[], expected: Inputs.Base.Line3[]): void;
|
|
11
|
+
expectSegmentCloseTo(received: Inputs.Base.Segment3 | undefined, expected: Inputs.Base.Segment3, precision?: number): void;
|
|
12
|
+
expectPlaneCloseTo(received: Inputs.Base.TrianglePlane3 | undefined, expected: Inputs.Base.TrianglePlane3, precision?: number): void;
|
|
13
|
+
expectMatrixCloseTo(received: Inputs.Base.TransformMatrix | undefined, expected: Inputs.Base.TransformMatrix): void;
|
|
14
|
+
expectMatrixesCloseTo(received: Inputs.Base.TransformMatrixes | undefined, expected: Inputs.Base.TransformMatrixes): void;
|
|
15
|
+
/** Helper to compare two arrays of points for near-equality, ignoring order */
|
|
16
|
+
expectPointArraysCloseTo(actual: Inputs.Base.Point3[] | undefined, expected: Inputs.Base.Point3[], tolerance?: number): void;
|
|
17
|
+
sortPoints(points: Inputs.Base.Point3[]): Inputs.Base.Point3[];
|
|
18
|
+
sortPolylinesForComparison(polylines: Inputs.Base.Polyline3[]): Inputs.Base.Polyline3[];
|
|
19
|
+
expectFloatArraysClose(actual: number[], expected: number[], precision: number): void;
|
|
20
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { GeometryHelper, MathBitByBit, Vector } from "./services";
|
|
2
|
+
export const TOLERANCE = 1e-7;
|
|
3
|
+
export class UnitTestHelper {
|
|
4
|
+
constructor() {
|
|
5
|
+
const math = new MathBitByBit();
|
|
6
|
+
const geometryHelper = new GeometryHelper();
|
|
7
|
+
this.vector = new Vector(math, geometryHelper);
|
|
8
|
+
}
|
|
9
|
+
expectPointCloseTo(received, expected) {
|
|
10
|
+
expect(received).toBeDefined();
|
|
11
|
+
if (!received)
|
|
12
|
+
return;
|
|
13
|
+
expect(received.length).toEqual(expected.length);
|
|
14
|
+
expect(received[0]).toBeCloseTo(expected[0], TOLERANCE);
|
|
15
|
+
expect(received[1]).toBeCloseTo(expected[1], TOLERANCE);
|
|
16
|
+
if (expected.length > 2 && received.length > 2) {
|
|
17
|
+
expect(received[2]).toBeCloseTo(expected[2], TOLERANCE);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
expectPointsCloseTo(received, expected) {
|
|
21
|
+
expect(received.length).toEqual(expected.length);
|
|
22
|
+
received.forEach((p, i) => this.expectPointCloseTo(p, expected[i]));
|
|
23
|
+
}
|
|
24
|
+
expectLineCloseTo(received, expected) {
|
|
25
|
+
expect(received).toBeDefined();
|
|
26
|
+
if (!received)
|
|
27
|
+
return;
|
|
28
|
+
this.expectPointCloseTo(received.start, expected.start);
|
|
29
|
+
this.expectPointCloseTo(received.end, expected.end);
|
|
30
|
+
}
|
|
31
|
+
expectLinesCloseTo(received, expected) {
|
|
32
|
+
expect(received.length).toEqual(expected.length);
|
|
33
|
+
received.forEach((l, i) => this.expectLineCloseTo(l, expected[i]));
|
|
34
|
+
}
|
|
35
|
+
expectSegmentCloseTo(received, expected, precision = TOLERANCE) {
|
|
36
|
+
expect(received).toBeDefined();
|
|
37
|
+
if (!received)
|
|
38
|
+
return;
|
|
39
|
+
expect(received).toHaveLength(2);
|
|
40
|
+
const order1Matches = Math.abs(this.vector.dist({ first: received[0], second: expected[0] })) < precision &&
|
|
41
|
+
Math.abs(this.vector.dist({ first: received[1], second: expected[1] })) < precision;
|
|
42
|
+
const order2Matches = Math.abs(this.vector.dist({ first: received[0], second: expected[1] })) < precision &&
|
|
43
|
+
Math.abs(this.vector.dist({ first: received[1], second: expected[0] })) < precision;
|
|
44
|
+
expect(order1Matches || order2Matches).toBe(true);
|
|
45
|
+
}
|
|
46
|
+
expectPlaneCloseTo(received, expected, precision = TOLERANCE) {
|
|
47
|
+
expect(received).toBeDefined();
|
|
48
|
+
if (!received)
|
|
49
|
+
return;
|
|
50
|
+
const normalDir1 = this.vector.sub({ first: received.normal, second: expected.normal });
|
|
51
|
+
const normalDir2 = this.vector.add({ first: received.normal, second: expected.normal });
|
|
52
|
+
const dir1Match = this.vector.lengthSq({ vector: normalDir1 }) < precision * precision;
|
|
53
|
+
const dir2Match = this.vector.lengthSq({ vector: normalDir2 }) < precision * precision;
|
|
54
|
+
expect(dir1Match || dir2Match).toBe(true);
|
|
55
|
+
expect(received.d).toBeCloseTo(dir1Match ? expected.d : -expected.d, precision);
|
|
56
|
+
}
|
|
57
|
+
expectMatrixCloseTo(received, expected) {
|
|
58
|
+
expect(received).toBeDefined();
|
|
59
|
+
if (!received)
|
|
60
|
+
return;
|
|
61
|
+
expect(received).toHaveLength(16);
|
|
62
|
+
expect(expected).toHaveLength(16);
|
|
63
|
+
for (let i = 0; i < 16; i++) {
|
|
64
|
+
expect(received[i]).toBeCloseTo(expected[i], TOLERANCE);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
expectMatrixesCloseTo(received, expected) {
|
|
68
|
+
expect(received).toBeDefined();
|
|
69
|
+
if (!received)
|
|
70
|
+
return;
|
|
71
|
+
expect(received.length).toEqual(expected.length);
|
|
72
|
+
received.forEach((matrix, i) => this.expectMatrixCloseTo(matrix, expected[i]));
|
|
73
|
+
}
|
|
74
|
+
/** Helper to compare two arrays of points for near-equality, ignoring order */
|
|
75
|
+
expectPointArraysCloseTo(actual, expected, tolerance = 1e-6) {
|
|
76
|
+
// Use expectPointsClose helper for individual point comparison if needed
|
|
77
|
+
const expectPointsClose = (act, exp, tol = 1e-6) => {
|
|
78
|
+
const precision = Math.max(0, Math.ceil(-Math.log10(tol)) - 1);
|
|
79
|
+
if (exp === undefined) {
|
|
80
|
+
expect(act).toBeUndefined();
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
expect(act).toBeDefined();
|
|
84
|
+
if (act) {
|
|
85
|
+
expect(act[0]).toBeCloseTo(exp[0], precision);
|
|
86
|
+
expect(act[1]).toBeCloseTo(exp[1], precision);
|
|
87
|
+
expect(act[2]).toBeCloseTo(exp[2], precision);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
expect(actual).toBeDefined();
|
|
92
|
+
// Proceed only if actual is defined
|
|
93
|
+
if (actual) {
|
|
94
|
+
expect(actual.length).toEqual(expected.length);
|
|
95
|
+
// Sort both arrays to compare independent of order
|
|
96
|
+
const sortedActual = this.sortPoints(actual);
|
|
97
|
+
const sortedExpected = this.sortPoints(expected);
|
|
98
|
+
for (let i = 0; i < sortedExpected.length; i++) {
|
|
99
|
+
expectPointsClose(sortedActual[i], sortedExpected[i], tolerance);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
sortPoints(points) {
|
|
104
|
+
return [...points].sort((a, b) => {
|
|
105
|
+
if (a[0] !== b[0])
|
|
106
|
+
return a[0] - b[0];
|
|
107
|
+
if (a[1] !== b[1])
|
|
108
|
+
return a[1] - b[1];
|
|
109
|
+
return a[2] - b[2];
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
sortPolylinesForComparison(polylines) {
|
|
113
|
+
return polylines.sort((a, b) => {
|
|
114
|
+
const pA = a.points[0];
|
|
115
|
+
const pB = b.points[0];
|
|
116
|
+
if (pA[0] !== pB[0])
|
|
117
|
+
return pA[0] - pB[0];
|
|
118
|
+
if (pA[1] !== pB[1])
|
|
119
|
+
return pA[1] - pB[1];
|
|
120
|
+
return pA[2] - pB[2];
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
expectFloatArraysClose(actual, expected, precision) {
|
|
124
|
+
expect(actual.length).toBe(expected.length);
|
|
125
|
+
actual.forEach((val, index) => {
|
|
126
|
+
expect(val).toBeCloseTo(expected[index], precision);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
;
|
|
130
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitbybit-dev/base",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.3",
|
|
4
4
|
"description": "Bit By Bit Developers Base CAD Library to Program Geometry",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
"node_modules/(?!@bitbybit-dev)/"
|
|
87
87
|
],
|
|
88
88
|
"collectCoverageFrom": [
|
|
89
|
-
"lib/api/**/*"
|
|
89
|
+
"lib/api/services/**/*"
|
|
90
90
|
]
|
|
91
91
|
}
|
|
92
92
|
}
|