@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
@@ -0,0 +1,235 @@
1
+ /**
2
+ * Contains various mesh helper methods that are not necessarily present in higher level CAD kernels that bitbybit is using.
3
+ */
4
+ export class MeshBitByBit {
5
+ constructor(vector, polyline) {
6
+ this.vector = vector;
7
+ this.polyline = polyline;
8
+ }
9
+ /**
10
+ * Computes the signed distance from a point to a plane.
11
+ * @param inputs a point and a plane
12
+ * @returns signed distance
13
+ * @group base
14
+ * @shortname signed dist to plane
15
+ * @drawable false
16
+ */
17
+ signedDistanceToPlane(inputs) {
18
+ return this.vector.dot({ first: inputs.plane.normal, second: inputs.point }) - inputs.plane.d;
19
+ }
20
+ /**
21
+ * Calculates the triangle plane from triangle.
22
+ * @param inputs triangle and tolerance
23
+ * @returns triangle plane
24
+ * @group traingle
25
+ * @shortname triangle plane
26
+ * @drawable false
27
+ */
28
+ calculateTrianglePlane(inputs) {
29
+ const EPSILON_SQ = Math.pow((inputs.tolerance || 1e-7), 2);
30
+ const edge1 = this.vector.sub({ first: inputs.triangle[1], second: inputs.triangle[0] });
31
+ const edge2 = this.vector.sub({ first: inputs.triangle[2], second: inputs.triangle[0] });
32
+ const normal = this.vector.cross({ first: edge1, second: edge2 });
33
+ if (this.vector.lengthSq({ vector: normal }) < EPSILON_SQ) {
34
+ return undefined; // Degenerate triangle
35
+ }
36
+ // Defensive copy if normalize modifies in-place
37
+ const normalizedNormal = this.vector.normalized({ vector: normal });
38
+ const d = this.vector.dot({ first: normalizedNormal, second: inputs.triangle[0] });
39
+ return { normal: normalizedNormal, d: d };
40
+ }
41
+ /**
42
+ * Calculates the intersection of two triangles.
43
+ * @param inputs first triangle, second triangle, and tolerance
44
+ * @returns intersection segment or undefined if no intersection
45
+ * @group traingle
46
+ * @shortname triangle-triangle int
47
+ * @drawable false
48
+ */
49
+ triangleTriangleIntersection(inputs) {
50
+ const t1 = inputs.triangle1;
51
+ const t2 = inputs.triangle2;
52
+ const EPSILON = inputs.tolerance || 1e-7;
53
+ const p1 = t1[0], p2 = t1[1], p3 = t1[2];
54
+ const q1 = t2[0], q2 = t2[1], q3 = t2[2];
55
+ const plane1 = this.calculateTrianglePlane({ triangle: t1, tolerance: EPSILON });
56
+ const plane2 = this.calculateTrianglePlane({ triangle: t2, tolerance: EPSILON });
57
+ if (!plane1 || !plane2)
58
+ return undefined;
59
+ const distQ_Plane1 = [
60
+ this.signedDistanceToPlane({ point: q1, plane: plane1 }),
61
+ this.signedDistanceToPlane({ point: q2, plane: plane1 }),
62
+ this.signedDistanceToPlane({ point: q3, plane: plane1 }),
63
+ ];
64
+ if ((distQ_Plane1[0] > EPSILON && distQ_Plane1[1] > EPSILON && distQ_Plane1[2] > EPSILON) ||
65
+ (distQ_Plane1[0] < -EPSILON && distQ_Plane1[1] < -EPSILON && distQ_Plane1[2] < -EPSILON)) {
66
+ return undefined;
67
+ }
68
+ const distP_Plane2 = [
69
+ this.signedDistanceToPlane({ point: p1, plane: plane2 }),
70
+ this.signedDistanceToPlane({ point: p2, plane: plane2 }),
71
+ this.signedDistanceToPlane({ point: p3, plane: plane2 }),
72
+ ];
73
+ if ((distP_Plane2[0] > EPSILON && distP_Plane2[1] > EPSILON && distP_Plane2[2] > EPSILON) ||
74
+ (distP_Plane2[0] < -EPSILON && distP_Plane2[1] < -EPSILON && distP_Plane2[2] < -EPSILON)) {
75
+ return undefined;
76
+ }
77
+ const allDistPZero = distP_Plane2.every(d => Math.abs(d) < EPSILON);
78
+ const allDistQZero = distQ_Plane1.every(d => Math.abs(d) < EPSILON);
79
+ if (allDistPZero && allDistQZero) {
80
+ return undefined; // Explicitly not handling coplanar intersection areas
81
+ }
82
+ const lineDir = this.vector.cross({ first: plane1.normal, second: plane2.normal });
83
+ const det = this.vector.dot({ first: lineDir, second: lineDir }); // det = |lineDir|^2
84
+ if (det < EPSILON * EPSILON) {
85
+ return undefined; // Planes parallel, no line intersection (coplanar case handled above)
86
+ }
87
+ // --- Calculate Interval Projections ---
88
+ // Store the 3D points that define the intervals on the line
89
+ const t1_intersection_points_3d = [];
90
+ const t2_intersection_points_3d = [];
91
+ const edges1 = [[p1, p2], [p2, p3], [p3, p1]];
92
+ const dists1 = distP_Plane2;
93
+ for (let i = 0; i < 3; ++i) {
94
+ const u = edges1[i][0];
95
+ const v = edges1[i][1];
96
+ const du = dists1[i];
97
+ const dv = dists1[(i + 1) % 3];
98
+ if (Math.abs(du) < EPSILON)
99
+ t1_intersection_points_3d.push(u); // Start vertex is on plane2
100
+ // Removed the redundant check for dv here, handled by next edge start
101
+ if ((du * dv) < 0 && Math.abs(du - dv) > EPSILON) { // Edge crosses plane2
102
+ const t = du / (du - dv);
103
+ t1_intersection_points_3d.push(this.computeIntersectionPoint(u, v, t));
104
+ }
105
+ }
106
+ const edges2 = [[q1, q2], [q2, q3], [q3, q1]];
107
+ const dists2 = distQ_Plane1;
108
+ for (let i = 0; i < 3; ++i) {
109
+ const u = edges2[i][0];
110
+ const v = edges2[i][1];
111
+ const du = dists2[i];
112
+ const dv = dists2[(i + 1) % 3];
113
+ if (Math.abs(du) < EPSILON)
114
+ t2_intersection_points_3d.push(u); // Start vertex is on plane1
115
+ // Removed redundant check for dv
116
+ if ((du * dv) < 0 && Math.abs(du - dv) > EPSILON) { // Edge crosses plane1
117
+ const t = du / (du - dv);
118
+ t2_intersection_points_3d.push(this.computeIntersectionPoint(u, v, t));
119
+ }
120
+ }
121
+ // We expect exactly two points for each triangle in the standard piercing case.
122
+ // Handle potential duplicates or edge cases if more points are generated (e.g., edge lies on plane)
123
+ // A simple check for the common case:
124
+ if (t1_intersection_points_3d.length < 2 || t2_intersection_points_3d.length < 2) {
125
+ // This can happen if triangles touch at a vertex or edge without crossing planes,
126
+ // or due to numerical precision near edges/vertices.
127
+ return undefined; // Treat touch as no intersection segment
128
+ }
129
+ // Calculate a robust origin ON the intersection line
130
+ const n1 = plane1.normal;
131
+ const n2 = plane2.normal;
132
+ const d1 = plane1.d;
133
+ const d2 = plane2.d;
134
+ // Point P = ( (d1 * N2 - d2 * N1) x D ) / (D dot D)
135
+ const term1 = this.vector.mul({ vector: n2, scalar: d1 });
136
+ const term2 = this.vector.mul({ vector: n1, scalar: d2 });
137
+ const termSub = this.vector.sub({ first: term1, second: term2 });
138
+ const crossTerm = this.vector.cross({ first: termSub, second: lineDir });
139
+ const lineOrigin = this.vector.mul({ vector: crossTerm, scalar: 1.0 / det });
140
+ // Project the 3D intersection points onto the lineDir, relative to lineOrigin
141
+ const t1_params = t1_intersection_points_3d.map(p => this.vector.dot({ first: this.vector.sub({ first: p, second: lineOrigin }), second: lineDir }));
142
+ const t2_params = t2_intersection_points_3d.map(p => this.vector.dot({ first: this.vector.sub({ first: p, second: lineOrigin }), second: lineDir }));
143
+ // Find the intervals
144
+ const t1Interval = [Math.min(...t1_params), Math.max(...t1_params)];
145
+ const t2Interval = [Math.min(...t2_params), Math.max(...t2_params)];
146
+ // Find the overlap of the two intervals
147
+ const intersectionMinParam = Math.max(t1Interval[0], t2Interval[0]);
148
+ const intersectionMaxParam = Math.min(t1Interval[1], t2Interval[1]);
149
+ // Check if the overlap is valid
150
+ if (intersectionMinParam < intersectionMaxParam - (EPSILON * det)) { // Let's use scaled epsilon for robustness against small det values.
151
+ // Convert the final parameters back to 3D points using the lineOrigin
152
+ // P = lineOrigin + dir * (param / det)
153
+ const point1 = this.vector.add({ first: lineOrigin, second: this.vector.mul({ vector: lineDir, scalar: intersectionMinParam / det }) });
154
+ const point2 = this.vector.add({ first: lineOrigin, second: this.vector.mul({ vector: lineDir, scalar: intersectionMaxParam / det }) });
155
+ // Check if the resulting segment has non-zero length
156
+ const segVec = this.vector.sub({ first: point1, second: point2 });
157
+ if (this.vector.lengthSq({ vector: segVec }) > EPSILON * EPSILON) {
158
+ return [point1, point2];
159
+ }
160
+ else {
161
+ return undefined; // Degenerate segment
162
+ }
163
+ }
164
+ else {
165
+ return undefined; // Intervals do not overlap
166
+ }
167
+ }
168
+ /**
169
+ * Computes the intersection segments of two meshes.
170
+ * @param inputs first mesh, second mesh, and tolerance
171
+ * @returns array of intersection segments
172
+ * @group mesh
173
+ * @shortname mesh-mesh int segments
174
+ * @drawable false
175
+ */
176
+ meshMeshIntersectionSegments(inputs) {
177
+ const mesh1 = inputs.mesh1;
178
+ const mesh2 = inputs.mesh2;
179
+ const intersectionSegments = [];
180
+ for (let i = 0; i < mesh1.length; ++i) {
181
+ for (let j = 0; j < mesh2.length; ++j) {
182
+ const triangle1 = mesh1[i];
183
+ const triangle2 = mesh2[j];
184
+ const segment = this.triangleTriangleIntersection({ triangle1, triangle2, tolerance: inputs.tolerance });
185
+ if (segment) {
186
+ intersectionSegments.push(segment);
187
+ }
188
+ }
189
+ }
190
+ return intersectionSegments;
191
+ }
192
+ /**
193
+ * Computes the intersection polylines of two meshes.
194
+ * @param inputs first mesh, second mesh, and tolerance
195
+ * @returns array of intersection polylines
196
+ * @group mesh
197
+ * @shortname mesh-mesh int polylines
198
+ * @drawable true
199
+ */
200
+ meshMeshIntersectionPolylines(inputs) {
201
+ const segments = this.meshMeshIntersectionSegments(inputs);
202
+ return this.polyline.sortSegmentsIntoPolylines({ segments, tolerance: inputs.tolerance });
203
+ }
204
+ /**
205
+ * Computes the intersection points of two meshes.
206
+ * @param inputs first mesh, second mesh, and tolerance
207
+ * @returns array of intersection points
208
+ * @group mesh
209
+ * @shortname mesh-mesh int points
210
+ * @drawable false
211
+ */
212
+ meshMeshIntersectionPoints(inputs) {
213
+ const polylines = this.meshMeshIntersectionPolylines(inputs);
214
+ return polylines.map(polyline => {
215
+ if (polyline.isClosed) {
216
+ return [...polyline.points, polyline.points[0]];
217
+ }
218
+ else {
219
+ return polyline.points;
220
+ }
221
+ });
222
+ }
223
+ computeIntersectionPoint(u, v, t) {
224
+ return this.vector.add({
225
+ first: u,
226
+ second: this.vector.mul({
227
+ vector: this.vector.sub({
228
+ first: v,
229
+ second: u
230
+ }),
231
+ scalar: t
232
+ })
233
+ });
234
+ }
235
+ }
@@ -2,6 +2,8 @@ import { GeometryHelper } from "./geometry-helper";
2
2
  import * as Inputs from "../inputs";
3
3
  import { Transforms } from "./transforms";
4
4
  import { Vector } from "./vector";
5
+ import * as Models from "../models";
6
+ import { Lists } from "./lists";
5
7
  /**
6
8
  * Contains various methods for points. Point in bitbybit is simply an array containing 3 numbers for [x, y, z].
7
9
  * Because of this form Point can be interchanged with Vector, which also is an array in [x, y, z] form.
@@ -11,7 +13,8 @@ export declare class Point {
11
13
  private readonly geometryHelper;
12
14
  private readonly transforms;
13
15
  private readonly vector;
14
- constructor(geometryHelper: GeometryHelper, transforms: Transforms, vector: Vector);
16
+ private readonly lists;
17
+ constructor(geometryHelper: GeometryHelper, transforms: Transforms, vector: Vector, lists: Lists);
15
18
  /**
16
19
  * Transforms the single point
17
20
  * @param inputs Contains a point and the transformations to apply
@@ -75,6 +78,15 @@ export declare class Point {
75
78
  * @drawable true
76
79
  */
77
80
  scalePointsCenterXYZ(inputs: Inputs.Point.ScalePointsCenterXYZDto): Inputs.Base.Point3[];
81
+ /**
82
+ * Stretch multiple points by providing center point, direction and uniform scale factor
83
+ * @param inputs Contains points, center point, direction and scale factor
84
+ * @returns Stretched points
85
+ * @group transforms
86
+ * @shortname stretch points dir from center
87
+ * @drawable true
88
+ */
89
+ stretchPointsDirFromCenter(inputs: Inputs.Point.StretchPointsDirFromCenterDto): Inputs.Base.Point3[];
78
90
  /**
79
91
  * Rotate multiple points by providing center point, axis and degrees of rotation
80
92
  * @param inputs Contains points, axis, center point and angle of rotation
@@ -220,6 +232,64 @@ export declare class Point {
220
232
  * @drawable true
221
233
  */
222
234
  hexGrid(inputs: Inputs.Point.HexGridCentersDto): Inputs.Base.Point3[];
235
+ /**
236
+ * Creates a pointy-top or flat-top hexagon grid, scaling hexagons to fit specified dimensions exactly.
237
+ * Returns both center points and the vertices of each (potentially scaled) hexagon.
238
+ * Hexagons are ordered column-first, then row-first.
239
+ * @param inputs Information about the desired grid dimensions and hexagon counts.
240
+ * @returns An object containing the array of center points and an array of hexagon vertex arrays.
241
+ * @group create
242
+ * @shortname scaled hex grid to fit
243
+ * @drawable false
244
+ */
245
+ hexGridScaledToFit(inputs: Inputs.Point.HexGridScaledToFitDto): Models.Point.HexGridData;
246
+ /**
247
+ * Calculates the maximum possible fillet radius at a corner formed by two line segments
248
+ * sharing an endpoint (C), such that the fillet arc is tangent to both segments
249
+ * and lies entirely within them.
250
+ * @param inputs three points and the tolerance
251
+ * @returns the maximum fillet radius
252
+ * @group fillet
253
+ * @shortname max fillet radius
254
+ * @drawable false
255
+ */
256
+ maxFilletRadius(inputs: Inputs.Point.ThreePointsToleranceDto): number;
257
+ /**
258
+ * Calculates the maximum possible fillet radius at a corner C, such that the fillet arc
259
+ * is tangent to both segments (P1-C, P2-C) and the tangent points lie within
260
+ * the first half of each segment (measured from C).
261
+ * @param inputs three points and the tolerance
262
+ * @returns the maximum fillet radius
263
+ * @group fillet
264
+ * @shortname max fillet radius half line
265
+ * @drawable false
266
+ */
267
+ maxFilletRadiusHalfLine(inputs: Inputs.Point.ThreePointsToleranceDto): number;
268
+ /**
269
+ * Calculates the maximum possible fillet radius at each corner of a polyline formed by
270
+ * formed by a series of points. The fillet radius is calculated for each internal
271
+ * corner and optionally for the closing corners if the polyline is closed.
272
+ * @param inputs Points, checkLastWithFirst flag, and tolerance
273
+ * @returns Array of maximum fillet radii for each corner
274
+ * @group fillet
275
+ * @shortname max fillets half line
276
+ * @drawable false
277
+ */
278
+ maxFilletsHalfLine(inputs: Inputs.Point.PointsMaxFilletsHalfLineDto): number[];
279
+ /**
280
+ * Calculates the single safest maximum fillet radius that can be applied
281
+ * uniformly to all corners of collection of points, based on the 'half-line' constraint.
282
+ * This is determined by finding the minimum of the maximum possible fillet
283
+ * radii calculated for each individual corner.
284
+ * @param inputs Defines the points, whether it's closed, and an optional tolerance.
285
+ * @returns The smallest value from the results of pointsMaxFilletsHalfLine.
286
+ * Returns 0 if the polyline has fewer than 3 points or if any
287
+ * calculated maximum radius is 0.
288
+ * @group fillet
289
+ * @shortname safest fillet radii points
290
+ * @drawable false
291
+ */
292
+ safestPointsMaxFilletHalfLine(inputs: Inputs.Point.PointsMaxFilletsHalfLineDto): number;
223
293
  /**
224
294
  * Removes consecutive duplicates from the point array with tolerance
225
295
  * @param inputs points, tolerance and check first and last
@@ -239,4 +309,29 @@ export declare class Point {
239
309
  */
240
310
  normalFromThreePoints(inputs: Inputs.Point.ThreePointsNormalDto): Inputs.Base.Vector3;
241
311
  private closestPointFromPointData;
312
+ /**
313
+ * Checks if two points are almost equal
314
+ * @param inputs Two points and the tolerance
315
+ * @returns true if the points are almost equal
316
+ * @group measure
317
+ * @shortname two points almost equal
318
+ * @drawable false
319
+ */
320
+ twoPointsAlmostEqual(inputs: Inputs.Point.TwoPointsToleranceDto): boolean;
321
+ /**
322
+ * Sorts points lexicographically (X, then Y, then Z)
323
+ * @param inputs points
324
+ * @returns sorted points
325
+ * @group sort
326
+ * @shortname sort points
327
+ * @drawable true
328
+ */
329
+ sortPoints(inputs: Inputs.Point.PointsDto): Inputs.Base.Point3[];
330
+ /**
331
+ * Calculates the 6 vertices of a regular flat-top hexagon.
332
+ * @param center The center point [x, y, z].
333
+ * @param radius The radius (distance from center to vertex).
334
+ * @returns An array of 6 Point3 vertices in counter-clockwise order.
335
+ */
336
+ private getRegularHexagonVertices;
242
337
  }