@bitbybit-dev/base 0.20.2 → 0.20.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.
Files changed (47) hide show
  1. package/LICENSE +1 -1
  2. package/lib/api/inputs/base-inputs.d.ts +8 -0
  3. package/lib/api/inputs/index.d.ts +3 -0
  4. package/lib/api/inputs/index.js +3 -0
  5. package/lib/api/inputs/inputs.d.ts +3 -0
  6. package/lib/api/inputs/inputs.js +3 -0
  7. package/lib/api/inputs/line-inputs.d.ts +240 -0
  8. package/lib/api/inputs/line-inputs.js +247 -0
  9. package/lib/api/inputs/mesh-inputs.d.ts +82 -0
  10. package/lib/api/inputs/mesh-inputs.js +83 -0
  11. package/lib/api/inputs/point-inputs.d.ts +153 -0
  12. package/lib/api/inputs/point-inputs.js +188 -0
  13. package/lib/api/inputs/polyline-inputs.d.ts +206 -0
  14. package/lib/api/inputs/polyline-inputs.js +229 -0
  15. package/lib/api/inputs/text-inputs.d.ts +1 -1
  16. package/lib/api/inputs/transforms-inputs.d.ts +18 -0
  17. package/lib/api/inputs/transforms-inputs.js +29 -0
  18. package/lib/api/inputs/vector-inputs.d.ts +8 -0
  19. package/lib/api/inputs/vector-inputs.js +8 -0
  20. package/lib/api/models/index.d.ts +1 -0
  21. package/lib/api/models/index.js +1 -0
  22. package/lib/api/models/point/bucket.d.ts +1 -0
  23. package/lib/api/models/point/bucket.js +1 -0
  24. package/lib/api/models/point/hex-grid-data.d.ts +8 -0
  25. package/lib/api/models/point/hex-grid-data.js +2 -0
  26. package/lib/api/models/point/index.d.ts +1 -0
  27. package/lib/api/models/point/index.js +1 -0
  28. package/lib/api/services/dates.js +45 -15
  29. package/lib/api/services/index.d.ts +3 -0
  30. package/lib/api/services/index.js +3 -0
  31. package/lib/api/services/line.d.ts +158 -0
  32. package/lib/api/services/line.js +334 -0
  33. package/lib/api/services/lists.d.ts +1 -1
  34. package/lib/api/services/lists.js +1 -2
  35. package/lib/api/services/mesh.d.ts +66 -0
  36. package/lib/api/services/mesh.js +235 -0
  37. package/lib/api/services/point.d.ts +96 -1
  38. package/lib/api/services/point.js +540 -1
  39. package/lib/api/services/polyline.d.ts +149 -0
  40. package/lib/api/services/polyline.js +446 -0
  41. package/lib/api/services/transforms.d.ts +26 -1
  42. package/lib/api/services/transforms.js +66 -3
  43. package/lib/api/services/vector.d.ts +18 -0
  44. package/lib/api/services/vector.js +27 -0
  45. package/lib/api/unit-test-helper.d.ts +20 -0
  46. package/lib/api/unit-test-helper.js +130 -0
  47. 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.2",
3
+ "version": "0.20.4",
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
  }