@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,149 @@
1
+ import { GeometryHelper } from "./geometry-helper";
2
+ import * as Inputs from "../inputs";
3
+ import { Point } from "./point";
4
+ import { Vector } from "./vector";
5
+ import { Line } from "./line";
6
+ /**
7
+ * Contains various methods for polyline. Polyline in bitbybit is a simple object that has points property containing an array of points.
8
+ * { points: number[][] }
9
+ */
10
+ export declare class Polyline {
11
+ private readonly vector;
12
+ private readonly point;
13
+ private readonly line;
14
+ private readonly geometryHelper;
15
+ constructor(vector: Vector, point: Point, line: Line, geometryHelper: GeometryHelper);
16
+ /**
17
+ * Gets the length of the polyline
18
+ * @param inputs a polyline
19
+ * @returns length
20
+ * @group get
21
+ * @shortname polyline length
22
+ * @drawable false
23
+ */
24
+ length(inputs: Inputs.Polyline.PolylineDto): number;
25
+ /**
26
+ * Gets the number of points in the polyline
27
+ * @param inputs a polyline
28
+ * @returns nr of points
29
+ * @group get
30
+ * @shortname nr polyline points
31
+ * @drawable false
32
+ */
33
+ countPoints(inputs: Inputs.Polyline.PolylineDto): number;
34
+ /**
35
+ * Gets the points of the polyline
36
+ * @param inputs a polyline
37
+ * @returns points
38
+ * @group get
39
+ * @shortname points
40
+ * @drawable true
41
+ */
42
+ getPoints(inputs: Inputs.Polyline.PolylineDto): Inputs.Base.Point3[];
43
+ /**
44
+ * Reverse the points of the polyline
45
+ * @param inputs a polyline
46
+ * @returns reversed polyline
47
+ * @group convert
48
+ * @shortname reverse polyline
49
+ * @drawable true
50
+ */
51
+ reverse(inputs: Inputs.Polyline.PolylineDto): Inputs.Polyline.PolylinePropertiesDto;
52
+ /**
53
+ * Transform the polyline
54
+ * @param inputs a polyline
55
+ * @returns transformed polyline
56
+ * @group transforms
57
+ * @shortname transform polyline
58
+ * @drawable true
59
+ */
60
+ transformPolyline(inputs: Inputs.Polyline.TransformPolylineDto): Inputs.Polyline.PolylinePropertiesDto;
61
+ /**
62
+ * Create the polyline
63
+ * @param inputs points and info if its closed
64
+ * @returns polyline
65
+ * @group create
66
+ * @shortname polyline
67
+ * @drawable true
68
+ */
69
+ create(inputs: Inputs.Polyline.PolylineCreateDto): Inputs.Polyline.PolylinePropertiesDto;
70
+ /**
71
+ * Create the lines from the polyline
72
+ * @param inputs polyline
73
+ * @returns lines
74
+ * @group convert
75
+ * @shortname polyline to lines
76
+ * @drawable true
77
+ */
78
+ polylineToLines(inputs: Inputs.Polyline.PolylineDto): Inputs.Base.Line3[];
79
+ /**
80
+ * Create the segments from the polyline
81
+ * @param inputs polyline
82
+ * @returns segments
83
+ * @group convert
84
+ * @shortname polyline to segments
85
+ * @drawable false
86
+ */
87
+ polylineToSegments(inputs: Inputs.Polyline.PolylineDto): Inputs.Base.Segment3[];
88
+ /**
89
+ * Finds the points of self intersection of the polyline
90
+ * @param inputs points of self intersection
91
+ * @returns polyline
92
+ * @group intersections
93
+ * @shortname polyline self intersections
94
+ * @drawable true
95
+ */
96
+ polylineSelfIntersection(inputs: Inputs.Polyline.PolylineToleranceDto): Inputs.Base.Point3[];
97
+ /**
98
+ * Finds the intersection points between two polylines
99
+ * @param inputs two polylines and tolerance
100
+ * @returns points
101
+ * @group intersection
102
+ * @shortname two polyline intersection
103
+ * @drawable true
104
+ */
105
+ twoPolylineIntersection(inputs: Inputs.Polyline.TwoPolylinesToleranceDto): Inputs.Base.Point3[];
106
+ /**
107
+ * Create the polylines from segments that are potentially connected but scrambled randomly
108
+ * @param inputs segments
109
+ * @returns polylines
110
+ * @group sort
111
+ * @shortname segments to polylines
112
+ * @drawable true
113
+ */
114
+ sortSegmentsIntoPolylines(inputs: Inputs.Polyline.SegmentsToleranceDto): Inputs.Base.Polyline3[];
115
+ /**
116
+ * Calculates the maximum possible half-line fillet radius for each corner
117
+ * of a given polyline. For a closed polyline, it includes the corners
118
+ * connecting the last segment back to the first.
119
+ *
120
+ * The calculation uses the 'half-line' constraint, meaning the fillet's
121
+ * tangent points must lie within the first half of each segment connected
122
+ * to the corner.
123
+ *
124
+ * @param inputs Defines the polyline points, whether it's closed, and an optional tolerance.
125
+ * @returns An array containing the maximum fillet radius calculated for each corner.
126
+ * The order corresponds to corners P[1]...P[n-2] for open polylines,
127
+ * and P[1]...P[n-2], P[0], P[n-1] for closed polylines.
128
+ * Returns an empty array if the polyline has fewer than 3 points.
129
+ * @group fillet
130
+ * @shortname polyline max fillet radii
131
+ * @drawable false
132
+ */
133
+ maxFilletsHalfLine(inputs: Inputs.Polyline.PolylineToleranceDto): number[];
134
+ /**
135
+ * Calculates the single safest maximum fillet radius that can be applied
136
+ * uniformly to all corners of a polyline, based on the 'half-line' constraint.
137
+ * This is determined by finding the minimum of the maximum possible fillet
138
+ * radii calculated for each individual corner.
139
+ *
140
+ * @param inputs Defines the polyline points, whether it's closed, and an optional tolerance.
141
+ * @returns The smallest value from the results of calculatePolylineMaxFillets.
142
+ * Returns 0 if the polyline has fewer than 3 points or if any
143
+ * calculated maximum radius is 0.
144
+ * @group fillet
145
+ * @shortname polyline safest fillet radius
146
+ * @drawable false
147
+ */
148
+ safestFilletRadius(inputs: Inputs.Polyline.PolylineToleranceDto): number;
149
+ }
@@ -0,0 +1,446 @@
1
+ /**
2
+ * Contains various methods for polyline. Polyline in bitbybit is a simple object that has points property containing an array of points.
3
+ * { points: number[][] }
4
+ */
5
+ export class Polyline {
6
+ constructor(vector, point, line, geometryHelper) {
7
+ this.vector = vector;
8
+ this.point = point;
9
+ this.line = line;
10
+ this.geometryHelper = geometryHelper;
11
+ }
12
+ /**
13
+ * Gets the length of the polyline
14
+ * @param inputs a polyline
15
+ * @returns length
16
+ * @group get
17
+ * @shortname polyline length
18
+ * @drawable false
19
+ */
20
+ length(inputs) {
21
+ let distanceOfPolyline = 0;
22
+ for (let i = 1; i < inputs.polyline.points.length; i++) {
23
+ const previousPoint = inputs.polyline.points[i - 1];
24
+ const currentPoint = inputs.polyline.points[i];
25
+ distanceOfPolyline += this.point.distance({ startPoint: previousPoint, endPoint: currentPoint });
26
+ }
27
+ return distanceOfPolyline;
28
+ }
29
+ /**
30
+ * Gets the number of points in the polyline
31
+ * @param inputs a polyline
32
+ * @returns nr of points
33
+ * @group get
34
+ * @shortname nr polyline points
35
+ * @drawable false
36
+ */
37
+ countPoints(inputs) {
38
+ return inputs.polyline.points.length;
39
+ }
40
+ /**
41
+ * Gets the points of the polyline
42
+ * @param inputs a polyline
43
+ * @returns points
44
+ * @group get
45
+ * @shortname points
46
+ * @drawable true
47
+ */
48
+ getPoints(inputs) {
49
+ return inputs.polyline.points;
50
+ }
51
+ /**
52
+ * Reverse the points of the polyline
53
+ * @param inputs a polyline
54
+ * @returns reversed polyline
55
+ * @group convert
56
+ * @shortname reverse polyline
57
+ * @drawable true
58
+ */
59
+ reverse(inputs) {
60
+ return { points: inputs.polyline.points.reverse() };
61
+ }
62
+ /**
63
+ * Transform the polyline
64
+ * @param inputs a polyline
65
+ * @returns transformed polyline
66
+ * @group transforms
67
+ * @shortname transform polyline
68
+ * @drawable true
69
+ */
70
+ transformPolyline(inputs) {
71
+ const transformation = inputs.transformation;
72
+ let transformedControlPoints = inputs.polyline.points;
73
+ transformedControlPoints = this.geometryHelper.transformControlPoints(transformation, transformedControlPoints);
74
+ return { points: transformedControlPoints };
75
+ }
76
+ /**
77
+ * Create the polyline
78
+ * @param inputs points and info if its closed
79
+ * @returns polyline
80
+ * @group create
81
+ * @shortname polyline
82
+ * @drawable true
83
+ */
84
+ create(inputs) {
85
+ var _a;
86
+ return {
87
+ points: inputs.points,
88
+ isClosed: (_a = inputs.isClosed) !== null && _a !== void 0 ? _a : false,
89
+ };
90
+ }
91
+ /**
92
+ * Create the lines from the polyline
93
+ * @param inputs polyline
94
+ * @returns lines
95
+ * @group convert
96
+ * @shortname polyline to lines
97
+ * @drawable true
98
+ */
99
+ polylineToLines(inputs) {
100
+ const segments = this.polylineToSegments(inputs);
101
+ return segments.map((segment) => ({
102
+ start: segment[0],
103
+ end: segment[1],
104
+ }));
105
+ }
106
+ /**
107
+ * Create the segments from the polyline
108
+ * @param inputs polyline
109
+ * @returns segments
110
+ * @group convert
111
+ * @shortname polyline to segments
112
+ * @drawable false
113
+ */
114
+ polylineToSegments(inputs) {
115
+ const polyline = inputs.polyline;
116
+ const segments = [];
117
+ const points = polyline.points;
118
+ const numPoints = points.length;
119
+ if (numPoints < 2) {
120
+ return segments;
121
+ }
122
+ // Create segments between consecutive points
123
+ for (let i = 0; i < numPoints - 1; i++) {
124
+ segments.push([points[i], points[i + 1]]);
125
+ }
126
+ // Add closing segment if the polyline is closed and has enough points
127
+ if (polyline.isClosed && numPoints >= 2) {
128
+ if (!this.point.twoPointsAlmostEqual({ point1: points[numPoints - 1], point2: points[0], tolerance: 1e-9 })) {
129
+ segments.push([points[numPoints - 1], points[0]]);
130
+ }
131
+ }
132
+ return segments;
133
+ }
134
+ /**
135
+ * Finds the points of self intersection of the polyline
136
+ * @param inputs points of self intersection
137
+ * @returns polyline
138
+ * @group intersections
139
+ * @shortname polyline self intersections
140
+ * @drawable true
141
+ */
142
+ polylineSelfIntersection(inputs) {
143
+ const { polyline, tolerance } = inputs;
144
+ const lines = this.polylineToLines({ polyline });
145
+ const numSegments = lines.length;
146
+ if (numSegments < 3) {
147
+ return [];
148
+ }
149
+ const selfIntersectionPoints = [];
150
+ const defaultTolerance = tolerance !== null && tolerance !== void 0 ? tolerance : 1e-6;
151
+ for (let i = 0; i < numSegments; i++) {
152
+ for (let j = i + 1; j < numSegments; j++) {
153
+ let areAdjacent = (j === i + 1);
154
+ if (!areAdjacent && polyline.isClosed && i === 0 && j === numSegments - 1) {
155
+ areAdjacent = true;
156
+ }
157
+ if (areAdjacent) {
158
+ continue;
159
+ }
160
+ const intersection = this.line.lineLineIntersection({
161
+ line1: lines[i],
162
+ line2: lines[j],
163
+ checkSegmentsOnly: true,
164
+ tolerance: defaultTolerance,
165
+ });
166
+ if (intersection) {
167
+ let foundClose = false;
168
+ for (const existingPoint of selfIntersectionPoints) {
169
+ if (this.point.twoPointsAlmostEqual({
170
+ point1: intersection,
171
+ point2: existingPoint,
172
+ tolerance: defaultTolerance
173
+ })) {
174
+ foundClose = true;
175
+ break;
176
+ }
177
+ }
178
+ if (!foundClose) {
179
+ selfIntersectionPoints.push(intersection);
180
+ }
181
+ }
182
+ }
183
+ }
184
+ return selfIntersectionPoints;
185
+ }
186
+ /**
187
+ * Finds the intersection points between two polylines
188
+ * @param inputs two polylines and tolerance
189
+ * @returns points
190
+ * @group intersection
191
+ * @shortname two polyline intersection
192
+ * @drawable true
193
+ */
194
+ twoPolylineIntersection(inputs) {
195
+ const { polyline1, polyline2, tolerance } = inputs;
196
+ const lines1 = this.polylineToLines({ polyline: polyline1 });
197
+ const lines2 = this.polylineToLines({ polyline: polyline2 });
198
+ const intersectionPoints = [];
199
+ const defaultTolerance = tolerance !== null && tolerance !== void 0 ? tolerance : 1e-6;
200
+ for (const seg1 of lines1) {
201
+ for (const seg2 of lines2) {
202
+ const intersection = this.line.lineLineIntersection({
203
+ line1: seg1,
204
+ line2: seg2,
205
+ checkSegmentsOnly: true,
206
+ tolerance: defaultTolerance,
207
+ });
208
+ if (intersection) {
209
+ let foundClose = false;
210
+ for (const existingPoint of intersectionPoints) {
211
+ if (this.point.twoPointsAlmostEqual({
212
+ point1: intersection,
213
+ point2: existingPoint,
214
+ tolerance: defaultTolerance
215
+ })) {
216
+ foundClose = true;
217
+ break;
218
+ }
219
+ }
220
+ if (!foundClose) {
221
+ intersectionPoints.push(intersection);
222
+ }
223
+ }
224
+ }
225
+ }
226
+ return intersectionPoints;
227
+ }
228
+ /**
229
+ * Create the polylines from segments that are potentially connected but scrambled randomly
230
+ * @param inputs segments
231
+ * @returns polylines
232
+ * @group sort
233
+ * @shortname segments to polylines
234
+ * @drawable true
235
+ */
236
+ sortSegmentsIntoPolylines(inputs) {
237
+ var _a;
238
+ const tolerance = (_a = inputs.tolerance) !== null && _a !== void 0 ? _a : 1e-5; // Default tolerance
239
+ const segments = inputs.segments;
240
+ if (!segments || segments.length === 0) {
241
+ return [];
242
+ }
243
+ const toleranceSq = tolerance * tolerance;
244
+ const numSegments = segments.length;
245
+ const used = new Array(numSegments).fill(false);
246
+ const results = [];
247
+ const endpointMap = new Map();
248
+ const invTolerance = 1.0 / tolerance;
249
+ const getGridKey = (p) => {
250
+ const ix = Math.round(p[0] * invTolerance);
251
+ const iy = Math.round(p[1] * invTolerance);
252
+ const iz = Math.round(p[2] * invTolerance);
253
+ return `${ix},${iy},${iz}`;
254
+ };
255
+ // 1. Build the spatial map
256
+ for (let i = 0; i < numSegments; i++) {
257
+ const segment = segments[i];
258
+ if (this.point.twoPointsAlmostEqual({ point1: segment[0], point2: segment[1], tolerance: tolerance })) {
259
+ used[i] = true; // Mark degenerate as used
260
+ continue;
261
+ }
262
+ const key0 = getGridKey(segment[0]);
263
+ const key1 = getGridKey(segment[1]);
264
+ const info0 = { segmentIndex: i, endpointIndex: 0, coords: segment[0] };
265
+ const info1 = { segmentIndex: i, endpointIndex: 1, coords: segment[1] };
266
+ if (!endpointMap.has(key0))
267
+ endpointMap.set(key0, []);
268
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
269
+ endpointMap.get(key0).push(info0);
270
+ if (key1 !== key0) {
271
+ if (!endpointMap.has(key1))
272
+ endpointMap.set(key1, []);
273
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
274
+ endpointMap.get(key1).push(info1);
275
+ }
276
+ else {
277
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
278
+ endpointMap.get(key0).push(info1); // Add both endpoints if same key
279
+ }
280
+ }
281
+ // --- Helper to find connecting segment ---
282
+ const findConnection = (pointToMatch) => {
283
+ const searchKeys = [];
284
+ const px = Math.round(pointToMatch[0] * invTolerance);
285
+ const py = Math.round(pointToMatch[1] * invTolerance);
286
+ const pz = Math.round(pointToMatch[2] * invTolerance);
287
+ for (let dx = -1; dx <= 1; dx++) {
288
+ for (let dy = -1; dy <= 1; dy++) {
289
+ for (let dz = -1; dz <= 1; dz++) {
290
+ searchKeys.push(`${px + dx},${py + dy},${pz + dz}`);
291
+ }
292
+ }
293
+ }
294
+ let bestMatch = undefined;
295
+ let minDistanceSq = toleranceSq;
296
+ for (const searchKey of searchKeys) {
297
+ const candidates = endpointMap.get(searchKey);
298
+ if (!candidates)
299
+ continue;
300
+ for (const candidate of candidates) {
301
+ // Only consider segments not already used in *any* polyline
302
+ if (!used[candidate.segmentIndex]) {
303
+ const diffVector = this.vector.sub({ first: candidate.coords, second: pointToMatch });
304
+ const distSq = this.vector.lengthSq({ vector: diffVector });
305
+ if (distSq < minDistanceSq) {
306
+ // Check with precise method if it's a potential best match
307
+ if (this.point.twoPointsAlmostEqual({ point1: candidate.coords, point2: pointToMatch, tolerance: tolerance })) {
308
+ bestMatch = candidate;
309
+ minDistanceSq = distSq; // Update min distance found
310
+ }
311
+ }
312
+ }
313
+ }
314
+ }
315
+ // No need for final check here, already done inside the loop
316
+ if (bestMatch && !used[bestMatch.segmentIndex]) { // Double check used status
317
+ return bestMatch;
318
+ }
319
+ return undefined;
320
+ };
321
+ // 2. Iterate and chain segments
322
+ for (let i = 0; i < numSegments; i++) {
323
+ if (used[i])
324
+ continue; // Skip if already part of a polyline
325
+ // Start a new polyline
326
+ used[i] = true; // Mark the starting segment as used
327
+ const startSegment = segments[i];
328
+ const currentPoints = [startSegment[0], startSegment[1]];
329
+ let currentHead = startSegment[0];
330
+ let currentTail = startSegment[1];
331
+ let isClosed = false;
332
+ let iterations = 0;
333
+ // Extend forward (tail)
334
+ while (iterations++ < numSegments) {
335
+ const nextMatch = findConnection(currentTail);
336
+ if (!nextMatch)
337
+ break; // No unused segment connects to the tail
338
+ // We found a potential next segment
339
+ const nextSegment = segments[nextMatch.segmentIndex];
340
+ const pointToAdd = (nextMatch.endpointIndex === 0) ? nextSegment[1] : nextSegment[0];
341
+ // Check for closure *before* adding the point
342
+ if (this.point.twoPointsAlmostEqual({ point1: pointToAdd, point2: currentHead, tolerance: tolerance })) {
343
+ isClosed = true;
344
+ // Mark the closing segment as used
345
+ used[nextMatch.segmentIndex] = true;
346
+ break; // Closed loop found
347
+ }
348
+ // Not closing, so add the point and mark the segment used
349
+ used[nextMatch.segmentIndex] = true;
350
+ currentPoints.push(pointToAdd);
351
+ currentTail = pointToAdd;
352
+ }
353
+ // Extend backward (head) - only if not already closed
354
+ iterations = 0;
355
+ if (!isClosed) {
356
+ while (iterations++ < numSegments) {
357
+ const prevMatch = findConnection(currentHead);
358
+ if (!prevMatch)
359
+ break; // No unused segment connects to the head
360
+ const prevSegment = segments[prevMatch.segmentIndex];
361
+ const pointToAdd = (prevMatch.endpointIndex === 0) ? prevSegment[1] : prevSegment[0];
362
+ // Check for closure against the current tail *before* adding
363
+ if (this.point.twoPointsAlmostEqual({ point1: pointToAdd, point2: currentTail, tolerance: tolerance })) {
364
+ isClosed = true;
365
+ // Mark the closing segment as used
366
+ used[prevMatch.segmentIndex] = true;
367
+ break; // Closed loop found
368
+ }
369
+ // Not closing, add point to beginning and mark segment used
370
+ used[prevMatch.segmentIndex] = true;
371
+ currentPoints.unshift(pointToAdd);
372
+ currentHead = pointToAdd;
373
+ }
374
+ }
375
+ // Final closure check (might be redundant now, but harmless)
376
+ // This catches cases like A->B, B->A which form a 2-point closed loop
377
+ if (!isClosed && currentPoints.length >= 2) {
378
+ isClosed = this.point.twoPointsAlmostEqual({ point1: currentHead, point2: currentTail, tolerance: tolerance });
379
+ }
380
+ // Remove duplicate point for closed loops with more than 2 points
381
+ if (isClosed && currentPoints.length > 2) {
382
+ // Check if the first and last points are indeed the ones needing merging
383
+ if (this.point.twoPointsAlmostEqual({ point1: currentPoints[currentPoints.length - 1], point2: currentPoints[0], tolerance: tolerance })) {
384
+ currentPoints.pop();
385
+ }
386
+ }
387
+ // Add the completed polyline (even if it's just the starting segment)
388
+ results.push({
389
+ points: currentPoints,
390
+ isClosed: isClosed,
391
+ });
392
+ }
393
+ return results;
394
+ }
395
+ /**
396
+ * Calculates the maximum possible half-line fillet radius for each corner
397
+ * of a given polyline. For a closed polyline, it includes the corners
398
+ * connecting the last segment back to the first.
399
+ *
400
+ * The calculation uses the 'half-line' constraint, meaning the fillet's
401
+ * tangent points must lie within the first half of each segment connected
402
+ * to the corner.
403
+ *
404
+ * @param inputs Defines the polyline points, whether it's closed, and an optional tolerance.
405
+ * @returns An array containing the maximum fillet radius calculated for each corner.
406
+ * The order corresponds to corners P[1]...P[n-2] for open polylines,
407
+ * and P[1]...P[n-2], P[0], P[n-1] for closed polylines.
408
+ * Returns an empty array if the polyline has fewer than 3 points.
409
+ * @group fillet
410
+ * @shortname polyline max fillet radii
411
+ * @drawable false
412
+ */
413
+ maxFilletsHalfLine(inputs) {
414
+ return this.point.maxFilletsHalfLine({
415
+ points: inputs.polyline.points,
416
+ checkLastWithFirst: inputs.polyline.isClosed,
417
+ tolerance: inputs.tolerance,
418
+ });
419
+ }
420
+ /**
421
+ * Calculates the single safest maximum fillet radius that can be applied
422
+ * uniformly to all corners of a polyline, based on the 'half-line' constraint.
423
+ * This is determined by finding the minimum of the maximum possible fillet
424
+ * radii calculated for each individual corner.
425
+ *
426
+ * @param inputs Defines the polyline points, whether it's closed, and an optional tolerance.
427
+ * @returns The smallest value from the results of calculatePolylineMaxFillets.
428
+ * Returns 0 if the polyline has fewer than 3 points or if any
429
+ * calculated maximum radius is 0.
430
+ * @group fillet
431
+ * @shortname polyline safest fillet radius
432
+ * @drawable false
433
+ */
434
+ safestFilletRadius(inputs) {
435
+ const allMaxRadii = this.maxFilletsHalfLine(inputs);
436
+ if (allMaxRadii.length === 0) {
437
+ // No corners, or fewer than 3 points. No fillet possible.
438
+ return 0;
439
+ }
440
+ // Find the minimum radius among all calculated maximums.
441
+ // If any corner calculation resulted in 0, the safest radius is 0.
442
+ const safestRadius = Math.min(...allMaxRadii);
443
+ // Ensure we don't return a negative radius if Math.min had weird input (shouldn't happen here)
444
+ return Math.max(0, safestRadius);
445
+ }
446
+ }
@@ -74,6 +74,17 @@ export declare class Transforms {
74
74
  * @drawable false
75
75
  */
76
76
  scaleXYZ(inputs: Inputs.Transforms.ScaleXYZDto): Base.TransformMatrixes;
77
+ /**
78
+ * Creates a stretch transformation along a specific direction, relative to a center point.
79
+ * This scales points along the given direction vector while leaving points in the
80
+ * plane perpendicular to the direction (passing through the center) unchanged.
81
+ * @param inputs Defines the center, direction, and scale factor for the stretch.
82
+ * @returns Array of transformations: [Translate To Origin, Stretch, Translate Back].
83
+ * @group scale
84
+ * @shortname stretch dir center
85
+ * @drawable false
86
+ */
87
+ stretchDirFromCenter(inputs: Inputs.Transforms.StretchDirCenterDto): Base.TransformMatrixes;
77
88
  /**
78
89
  * Creates uniform scale transformation
79
90
  * @param inputs Scale Dto
@@ -110,13 +121,27 @@ export declare class Transforms {
110
121
  * @drawable false
111
122
  */
112
123
  translationsXYZ(inputs: Inputs.Transforms.TranslationsXYZDto): Base.TransformMatrixes[];
124
+ /**
125
+ * Creates the identity transformation
126
+ * @returns transformation
127
+ * @group identity
128
+ * @shortname identity
129
+ * @drawable false
130
+ */
131
+ identity(): Base.TransformMatrix;
113
132
  private translation;
114
133
  private scaling;
115
- private identity;
116
134
  private rotationAxis;
117
135
  private rotationX;
118
136
  private rotationY;
119
137
  private rotationZ;
120
138
  private rotationYawPitchRoll;
121
139
  private rotationMatrixFromQuat;
140
+ /**
141
+ * Creates a 4x4 matrix that scales along a given direction vector.
142
+ * @param direction The direction vector (will be normalized).
143
+ * @param scale The scale factor along the direction.
144
+ * @returns A 4x4 column-major transformation matrix.
145
+ */
146
+ private stretchDirection;
122
147
  }