okgeometry-api 1.1.6 → 1.1.7
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/dist/Arc.js +1 -1
- package/dist/Arc.js.map +1 -1
- package/dist/Circle.js +1 -1
- package/dist/Circle.js.map +1 -1
- package/dist/Line.js +1 -1
- package/dist/Line.js.map +1 -1
- package/dist/Mesh.d.ts +101 -4
- package/dist/Mesh.d.ts.map +1 -1
- package/dist/Mesh.js +103 -5
- package/dist/Mesh.js.map +1 -1
- package/dist/NurbsCurve.js +1 -1
- package/dist/NurbsCurve.js.map +1 -1
- package/dist/NurbsSurface.d.ts.map +1 -1
- package/dist/NurbsSurface.js +10 -7
- package/dist/NurbsSurface.js.map +1 -1
- package/dist/PolyCurve.js +1 -1
- package/dist/PolyCurve.js.map +1 -1
- package/dist/Polyline.js +1 -1
- package/dist/Polyline.js.map +1 -1
- package/dist/Ray.js +1 -1
- package/dist/Ray.js.map +1 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +1 -3
- package/dist/engine.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/wasm-base64.d.ts +1 -1
- package/dist/wasm-base64.d.ts.map +1 -1
- package/dist/wasm-base64.js +1 -1
- package/dist/wasm-base64.js.map +1 -1
- package/package.json +7 -6
- package/src/Arc.ts +117 -117
- package/src/Circle.ts +153 -153
- package/src/Line.ts +144 -144
- package/src/Mesh.ts +671 -452
- package/src/NurbsCurve.ts +240 -240
- package/src/NurbsSurface.ts +249 -245
- package/src/PolyCurve.ts +306 -306
- package/src/Polyline.ts +153 -153
- package/src/Ray.ts +90 -90
- package/src/engine.ts +9 -11
- package/src/index.ts +6 -0
- package/src/wasm-base64.ts +1 -1
- package/wasm/README.md +0 -104
- package/wasm/okgeometrycore.d.ts +0 -754
- package/wasm/okgeometrycore.js +0 -2005
- package/wasm/okgeometrycore_bg.d.ts +0 -3
- package/wasm/okgeometrycore_bg.js +0 -1686
- package/wasm/okgeometrycore_bg.wasm +0 -0
- package/wasm/okgeometrycore_bg.wasm.d.ts +0 -100
- package/wasm/package.json +0 -19
package/src/PolyCurve.ts
CHANGED
|
@@ -1,306 +1,306 @@
|
|
|
1
|
-
import { ensureInit } from "./engine.js";
|
|
2
|
-
import { Point } from "./Point.js";
|
|
3
|
-
import { Line } from "./Line.js";
|
|
4
|
-
import { Arc } from "./Arc.js";
|
|
5
|
-
import { Vec3 } from "./Vec3.js";
|
|
6
|
-
import { NurbsCurve } from "./NurbsCurve.js";
|
|
7
|
-
import type { Plane } from "./Plane.js";
|
|
8
|
-
import type { CurveSegment, RotationAxis } from "./types.js";
|
|
9
|
-
import { SegmentTypeCode } from "./types.js";
|
|
10
|
-
import { pointsToCoords, coordsToPoints } from "./BufferCodec.js";
|
|
11
|
-
import * as wasm from "../wasm/
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Composite curve made of sequential Line and Arc segments.
|
|
15
|
-
*/
|
|
16
|
-
export class PolyCurve {
|
|
17
|
-
public readonly segments: CurveSegment[];
|
|
18
|
-
|
|
19
|
-
constructor(segments: CurveSegment[]) {
|
|
20
|
-
this.segments = segments;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/** Create a PolyCurve by connecting points with line segments */
|
|
24
|
-
static byPoints(points: Point[]): PolyCurve {
|
|
25
|
-
if (points.length < 2) return new PolyCurve([]);
|
|
26
|
-
const segs: CurveSegment[] = [];
|
|
27
|
-
for (let i = 0; i < points.length - 1; i++) {
|
|
28
|
-
segs.push(new Line(points[i], points[i + 1]));
|
|
29
|
-
}
|
|
30
|
-
return new PolyCurve(segs);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
get startPoint(): Point {
|
|
34
|
-
if (this.segments.length === 0) return Point.ORIGIN;
|
|
35
|
-
const s = this.segments[0];
|
|
36
|
-
return s instanceof Line ? s.start : s.pointAt(0);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
get endPoint(): Point {
|
|
40
|
-
if (this.segments.length === 0) return Point.ORIGIN;
|
|
41
|
-
const s = this.segments[this.segments.length - 1];
|
|
42
|
-
return s instanceof Line ? s.end : s.pointAt(1);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** Returns all constituent curves (Line and Arc segments) in order. */
|
|
46
|
-
get curves(): CurveSegment[] {
|
|
47
|
-
return this.segments;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Sample all segments into a flat array of points for rendering.
|
|
52
|
-
* Line segments produce 2 points; Arc segments are sampled with `arcSamples` points.
|
|
53
|
-
* Consecutive duplicate junction points are removed.
|
|
54
|
-
*/
|
|
55
|
-
sample(arcSamples = 32): Point[] {
|
|
56
|
-
const pts: Point[] = [];
|
|
57
|
-
for (const seg of this.segments) {
|
|
58
|
-
let segPts: Point[];
|
|
59
|
-
if (seg instanceof Line) {
|
|
60
|
-
segPts = [seg.start, seg.end];
|
|
61
|
-
} else {
|
|
62
|
-
// Arc: sample into multiple points for smooth rendering
|
|
63
|
-
segPts = seg.sample(arcSamples);
|
|
64
|
-
}
|
|
65
|
-
for (const p of segPts) {
|
|
66
|
-
// Skip duplicate junction points
|
|
67
|
-
if (pts.length > 0 && pts[pts.length - 1].equals(p)) continue;
|
|
68
|
-
pts.push(p);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return pts;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/** Evaluate a point on the curve at normalized parameter t (0 = start, 1 = end). */
|
|
75
|
-
pointAt(t: number): Point {
|
|
76
|
-
if (this.segments.length === 0) return Point.ORIGIN;
|
|
77
|
-
if (t <= 0) return this.startPoint;
|
|
78
|
-
if (t >= 1) return this.endPoint;
|
|
79
|
-
|
|
80
|
-
const lengths = this.segments.map(s => s.length());
|
|
81
|
-
const total = lengths.reduce((a, b) => a + b, 0);
|
|
82
|
-
if (total === 0) return this.startPoint;
|
|
83
|
-
|
|
84
|
-
let target = t * total;
|
|
85
|
-
for (let i = 0; i < this.segments.length; i++) {
|
|
86
|
-
if (target <= lengths[i] || i === this.segments.length - 1) {
|
|
87
|
-
const localT = lengths[i] > 0 ? target / lengths[i] : 0;
|
|
88
|
-
return this.segments[i].pointAt(Math.min(localT, 1));
|
|
89
|
-
}
|
|
90
|
-
target -= lengths[i];
|
|
91
|
-
}
|
|
92
|
-
return this.endPoint;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
isClosed(eps = 1e-10): boolean {
|
|
96
|
-
return this.startPoint.equals(this.endPoint, eps);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
length(): number {
|
|
100
|
-
return this.segments.reduce((sum, s) => sum + s.length(), 0);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
rotate(axis: RotationAxis, angle: number): PolyCurve {
|
|
104
|
-
return new PolyCurve(this.segments.map(s => s.rotate(axis, angle)));
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
translate(offset: Vec3): PolyCurve {
|
|
108
|
-
return new PolyCurve(this.segments.map(s => s.translate(offset)));
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
projectOntoPlane(plane: Plane, direction?: Vec3, arcSamples = 32): PolyCurve {
|
|
112
|
-
const proj = (p: Point) => direction ? plane.projectPointAlongDirection(p, direction) : plane.projectPoint(p);
|
|
113
|
-
const segs: CurveSegment[] = [];
|
|
114
|
-
for (const s of this.segments) {
|
|
115
|
-
if (s instanceof Line) {
|
|
116
|
-
segs.push(new Line(proj(s.start), proj(s.end)));
|
|
117
|
-
} else {
|
|
118
|
-
// Arc projects to elliptical arc — approximate with line segments
|
|
119
|
-
const pts = s.sample(arcSamples);
|
|
120
|
-
for (let i = 0; i < pts.length - 1; i++) {
|
|
121
|
-
segs.push(new Line(proj(pts[i]), proj(pts[i + 1])));
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
return new PolyCurve(segs);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Fillet corners of this PolyCurve with arcs of the given radius.
|
|
130
|
-
* Extracts the vertex points, calls WASM fillet_corners, returns a new PolyCurve.
|
|
131
|
-
*/
|
|
132
|
-
fillet(radius: number, normal?: Vec3): PolyCurve {
|
|
133
|
-
ensureInit();
|
|
134
|
-
if (this.segments.length < 2) {
|
|
135
|
-
throw new Error("PolyCurve.fillet() requires at least 2 segments");
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Extract all vertex points from segments
|
|
139
|
-
const pts: Point[] = [];
|
|
140
|
-
for (const seg of this.segments) {
|
|
141
|
-
if (seg instanceof Line) {
|
|
142
|
-
if (pts.length === 0) pts.push(seg.start);
|
|
143
|
-
pts.push(seg.end);
|
|
144
|
-
} else {
|
|
145
|
-
// Arc: use start and end points
|
|
146
|
-
if (pts.length === 0) pts.push(seg.pointAt(0));
|
|
147
|
-
pts.push(seg.pointAt(1));
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
if (pts.length < 3) {
|
|
151
|
-
throw new Error("PolyCurve.fillet() requires at least 3 vertices");
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const coords = new Float64Array(pts.length * 3);
|
|
155
|
-
for (let i = 0; i < pts.length; i++) {
|
|
156
|
-
coords[i * 3] = pts[i].x;
|
|
157
|
-
coords[i * 3 + 1] = pts[i].y;
|
|
158
|
-
coords[i * 3 + 2] = pts[i].z;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const nx = normal?.x ?? 0, ny = normal?.y ?? 0, nz = normal?.z ?? 0;
|
|
162
|
-
const buf = wasm.fillet_polycurve(coords, radius, nx, ny, nz);
|
|
163
|
-
if (buf.length < 1) {
|
|
164
|
-
throw new Error("PolyCurve.fillet() failed - WASM returned empty result");
|
|
165
|
-
}
|
|
166
|
-
return PolyCurve.fromSegmentData(buf);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/** Decode a PolyCurve from WASM segment buffer [count, type, ...data, ...] */
|
|
170
|
-
static fromSegmentData(buf: Float64Array | number[]): PolyCurve {
|
|
171
|
-
const count = buf[0];
|
|
172
|
-
const segs: CurveSegment[] = [];
|
|
173
|
-
let idx = 1;
|
|
174
|
-
for (let i = 0; i < count; i++) {
|
|
175
|
-
const type = buf[idx++];
|
|
176
|
-
if (type === SegmentTypeCode.Line) {
|
|
177
|
-
// Line: 6 floats
|
|
178
|
-
const s = new Point(buf[idx], buf[idx + 1], buf[idx + 2]);
|
|
179
|
-
const e = new Point(buf[idx + 3], buf[idx + 4], buf[idx + 5]);
|
|
180
|
-
segs.push(new Line(s, e));
|
|
181
|
-
idx += 6;
|
|
182
|
-
} else if (type === SegmentTypeCode.Arc) {
|
|
183
|
-
// Arc: 9 floats
|
|
184
|
-
const center = new Point(buf[idx], buf[idx + 1], buf[idx + 2]);
|
|
185
|
-
const normal = new Vec3(buf[idx + 3], buf[idx + 4], buf[idx + 5]);
|
|
186
|
-
const radius = buf[idx + 6];
|
|
187
|
-
const startAngle = buf[idx + 7];
|
|
188
|
-
const endAngle = buf[idx + 8];
|
|
189
|
-
segs.push(new Arc(center, radius, startAngle, endAngle, normal));
|
|
190
|
-
idx += 9;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
return new PolyCurve(segs);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Chamfer corners of this PolyCurve by cutting each corner with a straight segment.
|
|
198
|
-
* Returns a new PolyCurve of line segments only.
|
|
199
|
-
*/
|
|
200
|
-
chamfer(distance: number): PolyCurve {
|
|
201
|
-
ensureInit();
|
|
202
|
-
if (this.segments.length < 2) {
|
|
203
|
-
throw new Error("PolyCurve.chamfer() requires at least 2 segments");
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const pts: Point[] = [];
|
|
207
|
-
for (const seg of this.segments) {
|
|
208
|
-
if (seg instanceof Line) {
|
|
209
|
-
if (pts.length === 0) pts.push(seg.start);
|
|
210
|
-
pts.push(seg.end);
|
|
211
|
-
} else {
|
|
212
|
-
if (pts.length === 0) pts.push(seg.pointAt(0));
|
|
213
|
-
pts.push(seg.pointAt(1));
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
if (pts.length < 3) {
|
|
217
|
-
throw new Error("PolyCurve.chamfer() requires at least 3 vertices");
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const coords = new Float64Array(pts.length * 3);
|
|
221
|
-
for (let i = 0; i < pts.length; i++) {
|
|
222
|
-
coords[i * 3] = pts[i].x;
|
|
223
|
-
coords[i * 3 + 1] = pts[i].y;
|
|
224
|
-
coords[i * 3 + 2] = pts[i].z;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
const buf = wasm.chamfer_polycurve(coords, distance);
|
|
228
|
-
if (buf.length < 1) {
|
|
229
|
-
throw new Error("PolyCurve.chamfer() failed - WASM returned empty result");
|
|
230
|
-
}
|
|
231
|
-
return PolyCurve.fromSegmentData(buf);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Offset this PolyCurve by a distance in the given plane.
|
|
236
|
-
* Extracts vertex points, offsets as a polyline, returns a new PolyCurve of line segments.
|
|
237
|
-
*/
|
|
238
|
-
offset(distance: number, normal?: Vec3): PolyCurve {
|
|
239
|
-
ensureInit();
|
|
240
|
-
if (this.segments.length < 1) {
|
|
241
|
-
throw new Error("PolyCurve.offset() requires at least 1 segment");
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
const pts: Point[] = [];
|
|
245
|
-
for (const seg of this.segments) {
|
|
246
|
-
if (seg instanceof Line) {
|
|
247
|
-
if (pts.length === 0) pts.push(seg.start);
|
|
248
|
-
pts.push(seg.end);
|
|
249
|
-
} else {
|
|
250
|
-
if (pts.length === 0) pts.push(seg.pointAt(0));
|
|
251
|
-
pts.push(seg.pointAt(1));
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
if (pts.length < 2) {
|
|
255
|
-
throw new Error("PolyCurve.offset() requires at least 2 vertices");
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
const coords = new Float64Array(pts.length * 3);
|
|
259
|
-
for (let i = 0; i < pts.length; i++) {
|
|
260
|
-
coords[i * 3] = pts[i].x;
|
|
261
|
-
coords[i * 3 + 1] = pts[i].y;
|
|
262
|
-
coords[i * 3 + 2] = pts[i].z;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
const nx = normal?.x ?? 0, ny = normal?.y ?? 0, nz = normal?.z ?? 0;
|
|
266
|
-
const result = wasm.offset_polyline_curve(coords, distance, nx, ny, nz);
|
|
267
|
-
if (result.length < 6) {
|
|
268
|
-
throw new Error("PolyCurve.offset() failed - WASM returned insufficient data");
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// Convert flat coords back to line segments
|
|
272
|
-
const segs: CurveSegment[] = [];
|
|
273
|
-
for (let i = 0; i < result.length - 3; i += 3) {
|
|
274
|
-
const s = new Point(result[i], result[i + 1], result[i + 2]);
|
|
275
|
-
const e = new Point(result[i + 3], result[i + 4], result[i + 5]);
|
|
276
|
-
segs.push(new Line(s, e));
|
|
277
|
-
}
|
|
278
|
-
return new PolyCurve(segs);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Convert to an exact NURBS curve representation via WASM.
|
|
283
|
-
* Lines become degree-1 NURBS, Arcs become degree-2 rational NURBS.
|
|
284
|
-
* All segments are degree-elevated to a common degree and joined.
|
|
285
|
-
*/
|
|
286
|
-
toNurbs(): NurbsCurve {
|
|
287
|
-
ensureInit();
|
|
288
|
-
const segData: number[] = [this.segments.length];
|
|
289
|
-
for (const seg of this.segments) {
|
|
290
|
-
if (seg instanceof Line) {
|
|
291
|
-
segData.push(SegmentTypeCode.Line);
|
|
292
|
-
segData.push(seg.start.x, seg.start.y, seg.start.z);
|
|
293
|
-
segData.push(seg.end.x, seg.end.y, seg.end.z);
|
|
294
|
-
} else {
|
|
295
|
-
// Arc
|
|
296
|
-
segData.push(SegmentTypeCode.Arc);
|
|
297
|
-
segData.push(seg.center.x, seg.center.y, seg.center.z);
|
|
298
|
-
segData.push(seg.normal.x, seg.normal.y, seg.normal.z);
|
|
299
|
-
segData.push(seg.radius, seg.startAngle, seg.endAngle);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
const buf = wasm.polycurve_to_nurbs(new Float64Array(segData));
|
|
303
|
-
if (buf.length < 2) throw new Error("Failed to convert PolyCurve to NURBS");
|
|
304
|
-
return NurbsCurve.fromData(buf);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
1
|
+
import { ensureInit } from "./engine.js";
|
|
2
|
+
import { Point } from "./Point.js";
|
|
3
|
+
import { Line } from "./Line.js";
|
|
4
|
+
import { Arc } from "./Arc.js";
|
|
5
|
+
import { Vec3 } from "./Vec3.js";
|
|
6
|
+
import { NurbsCurve } from "./NurbsCurve.js";
|
|
7
|
+
import type { Plane } from "./Plane.js";
|
|
8
|
+
import type { CurveSegment, RotationAxis } from "./types.js";
|
|
9
|
+
import { SegmentTypeCode } from "./types.js";
|
|
10
|
+
import { pointsToCoords, coordsToPoints } from "./BufferCodec.js";
|
|
11
|
+
import * as wasm from "../wasm/okgeometrycore.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Composite curve made of sequential Line and Arc segments.
|
|
15
|
+
*/
|
|
16
|
+
export class PolyCurve {
|
|
17
|
+
public readonly segments: CurveSegment[];
|
|
18
|
+
|
|
19
|
+
constructor(segments: CurveSegment[]) {
|
|
20
|
+
this.segments = segments;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Create a PolyCurve by connecting points with line segments */
|
|
24
|
+
static byPoints(points: Point[]): PolyCurve {
|
|
25
|
+
if (points.length < 2) return new PolyCurve([]);
|
|
26
|
+
const segs: CurveSegment[] = [];
|
|
27
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
28
|
+
segs.push(new Line(points[i], points[i + 1]));
|
|
29
|
+
}
|
|
30
|
+
return new PolyCurve(segs);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
get startPoint(): Point {
|
|
34
|
+
if (this.segments.length === 0) return Point.ORIGIN;
|
|
35
|
+
const s = this.segments[0];
|
|
36
|
+
return s instanceof Line ? s.start : s.pointAt(0);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
get endPoint(): Point {
|
|
40
|
+
if (this.segments.length === 0) return Point.ORIGIN;
|
|
41
|
+
const s = this.segments[this.segments.length - 1];
|
|
42
|
+
return s instanceof Line ? s.end : s.pointAt(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Returns all constituent curves (Line and Arc segments) in order. */
|
|
46
|
+
get curves(): CurveSegment[] {
|
|
47
|
+
return this.segments;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Sample all segments into a flat array of points for rendering.
|
|
52
|
+
* Line segments produce 2 points; Arc segments are sampled with `arcSamples` points.
|
|
53
|
+
* Consecutive duplicate junction points are removed.
|
|
54
|
+
*/
|
|
55
|
+
sample(arcSamples = 32): Point[] {
|
|
56
|
+
const pts: Point[] = [];
|
|
57
|
+
for (const seg of this.segments) {
|
|
58
|
+
let segPts: Point[];
|
|
59
|
+
if (seg instanceof Line) {
|
|
60
|
+
segPts = [seg.start, seg.end];
|
|
61
|
+
} else {
|
|
62
|
+
// Arc: sample into multiple points for smooth rendering
|
|
63
|
+
segPts = seg.sample(arcSamples);
|
|
64
|
+
}
|
|
65
|
+
for (const p of segPts) {
|
|
66
|
+
// Skip duplicate junction points
|
|
67
|
+
if (pts.length > 0 && pts[pts.length - 1].equals(p)) continue;
|
|
68
|
+
pts.push(p);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return pts;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Evaluate a point on the curve at normalized parameter t (0 = start, 1 = end). */
|
|
75
|
+
pointAt(t: number): Point {
|
|
76
|
+
if (this.segments.length === 0) return Point.ORIGIN;
|
|
77
|
+
if (t <= 0) return this.startPoint;
|
|
78
|
+
if (t >= 1) return this.endPoint;
|
|
79
|
+
|
|
80
|
+
const lengths = this.segments.map(s => s.length());
|
|
81
|
+
const total = lengths.reduce((a, b) => a + b, 0);
|
|
82
|
+
if (total === 0) return this.startPoint;
|
|
83
|
+
|
|
84
|
+
let target = t * total;
|
|
85
|
+
for (let i = 0; i < this.segments.length; i++) {
|
|
86
|
+
if (target <= lengths[i] || i === this.segments.length - 1) {
|
|
87
|
+
const localT = lengths[i] > 0 ? target / lengths[i] : 0;
|
|
88
|
+
return this.segments[i].pointAt(Math.min(localT, 1));
|
|
89
|
+
}
|
|
90
|
+
target -= lengths[i];
|
|
91
|
+
}
|
|
92
|
+
return this.endPoint;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
isClosed(eps = 1e-10): boolean {
|
|
96
|
+
return this.startPoint.equals(this.endPoint, eps);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
length(): number {
|
|
100
|
+
return this.segments.reduce((sum, s) => sum + s.length(), 0);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
rotate(axis: RotationAxis, angle: number): PolyCurve {
|
|
104
|
+
return new PolyCurve(this.segments.map(s => s.rotate(axis, angle)));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
translate(offset: Vec3): PolyCurve {
|
|
108
|
+
return new PolyCurve(this.segments.map(s => s.translate(offset)));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
projectOntoPlane(plane: Plane, direction?: Vec3, arcSamples = 32): PolyCurve {
|
|
112
|
+
const proj = (p: Point) => direction ? plane.projectPointAlongDirection(p, direction) : plane.projectPoint(p);
|
|
113
|
+
const segs: CurveSegment[] = [];
|
|
114
|
+
for (const s of this.segments) {
|
|
115
|
+
if (s instanceof Line) {
|
|
116
|
+
segs.push(new Line(proj(s.start), proj(s.end)));
|
|
117
|
+
} else {
|
|
118
|
+
// Arc projects to elliptical arc — approximate with line segments
|
|
119
|
+
const pts = s.sample(arcSamples);
|
|
120
|
+
for (let i = 0; i < pts.length - 1; i++) {
|
|
121
|
+
segs.push(new Line(proj(pts[i]), proj(pts[i + 1])));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return new PolyCurve(segs);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Fillet corners of this PolyCurve with arcs of the given radius.
|
|
130
|
+
* Extracts the vertex points, calls WASM fillet_corners, returns a new PolyCurve.
|
|
131
|
+
*/
|
|
132
|
+
fillet(radius: number, normal?: Vec3): PolyCurve {
|
|
133
|
+
ensureInit();
|
|
134
|
+
if (this.segments.length < 2) {
|
|
135
|
+
throw new Error("PolyCurve.fillet() requires at least 2 segments");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Extract all vertex points from segments
|
|
139
|
+
const pts: Point[] = [];
|
|
140
|
+
for (const seg of this.segments) {
|
|
141
|
+
if (seg instanceof Line) {
|
|
142
|
+
if (pts.length === 0) pts.push(seg.start);
|
|
143
|
+
pts.push(seg.end);
|
|
144
|
+
} else {
|
|
145
|
+
// Arc: use start and end points
|
|
146
|
+
if (pts.length === 0) pts.push(seg.pointAt(0));
|
|
147
|
+
pts.push(seg.pointAt(1));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (pts.length < 3) {
|
|
151
|
+
throw new Error("PolyCurve.fillet() requires at least 3 vertices");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const coords = new Float64Array(pts.length * 3);
|
|
155
|
+
for (let i = 0; i < pts.length; i++) {
|
|
156
|
+
coords[i * 3] = pts[i].x;
|
|
157
|
+
coords[i * 3 + 1] = pts[i].y;
|
|
158
|
+
coords[i * 3 + 2] = pts[i].z;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const nx = normal?.x ?? 0, ny = normal?.y ?? 0, nz = normal?.z ?? 0;
|
|
162
|
+
const buf = wasm.fillet_polycurve(coords, radius, nx, ny, nz);
|
|
163
|
+
if (buf.length < 1) {
|
|
164
|
+
throw new Error("PolyCurve.fillet() failed - WASM returned empty result");
|
|
165
|
+
}
|
|
166
|
+
return PolyCurve.fromSegmentData(buf);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Decode a PolyCurve from WASM segment buffer [count, type, ...data, ...] */
|
|
170
|
+
static fromSegmentData(buf: Float64Array | number[]): PolyCurve {
|
|
171
|
+
const count = buf[0];
|
|
172
|
+
const segs: CurveSegment[] = [];
|
|
173
|
+
let idx = 1;
|
|
174
|
+
for (let i = 0; i < count; i++) {
|
|
175
|
+
const type = buf[idx++];
|
|
176
|
+
if (type === SegmentTypeCode.Line) {
|
|
177
|
+
// Line: 6 floats
|
|
178
|
+
const s = new Point(buf[idx], buf[idx + 1], buf[idx + 2]);
|
|
179
|
+
const e = new Point(buf[idx + 3], buf[idx + 4], buf[idx + 5]);
|
|
180
|
+
segs.push(new Line(s, e));
|
|
181
|
+
idx += 6;
|
|
182
|
+
} else if (type === SegmentTypeCode.Arc) {
|
|
183
|
+
// Arc: 9 floats
|
|
184
|
+
const center = new Point(buf[idx], buf[idx + 1], buf[idx + 2]);
|
|
185
|
+
const normal = new Vec3(buf[idx + 3], buf[idx + 4], buf[idx + 5]);
|
|
186
|
+
const radius = buf[idx + 6];
|
|
187
|
+
const startAngle = buf[idx + 7];
|
|
188
|
+
const endAngle = buf[idx + 8];
|
|
189
|
+
segs.push(new Arc(center, radius, startAngle, endAngle, normal));
|
|
190
|
+
idx += 9;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return new PolyCurve(segs);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Chamfer corners of this PolyCurve by cutting each corner with a straight segment.
|
|
198
|
+
* Returns a new PolyCurve of line segments only.
|
|
199
|
+
*/
|
|
200
|
+
chamfer(distance: number): PolyCurve {
|
|
201
|
+
ensureInit();
|
|
202
|
+
if (this.segments.length < 2) {
|
|
203
|
+
throw new Error("PolyCurve.chamfer() requires at least 2 segments");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const pts: Point[] = [];
|
|
207
|
+
for (const seg of this.segments) {
|
|
208
|
+
if (seg instanceof Line) {
|
|
209
|
+
if (pts.length === 0) pts.push(seg.start);
|
|
210
|
+
pts.push(seg.end);
|
|
211
|
+
} else {
|
|
212
|
+
if (pts.length === 0) pts.push(seg.pointAt(0));
|
|
213
|
+
pts.push(seg.pointAt(1));
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (pts.length < 3) {
|
|
217
|
+
throw new Error("PolyCurve.chamfer() requires at least 3 vertices");
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const coords = new Float64Array(pts.length * 3);
|
|
221
|
+
for (let i = 0; i < pts.length; i++) {
|
|
222
|
+
coords[i * 3] = pts[i].x;
|
|
223
|
+
coords[i * 3 + 1] = pts[i].y;
|
|
224
|
+
coords[i * 3 + 2] = pts[i].z;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const buf = wasm.chamfer_polycurve(coords, distance);
|
|
228
|
+
if (buf.length < 1) {
|
|
229
|
+
throw new Error("PolyCurve.chamfer() failed - WASM returned empty result");
|
|
230
|
+
}
|
|
231
|
+
return PolyCurve.fromSegmentData(buf);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Offset this PolyCurve by a distance in the given plane.
|
|
236
|
+
* Extracts vertex points, offsets as a polyline, returns a new PolyCurve of line segments.
|
|
237
|
+
*/
|
|
238
|
+
offset(distance: number, normal?: Vec3): PolyCurve {
|
|
239
|
+
ensureInit();
|
|
240
|
+
if (this.segments.length < 1) {
|
|
241
|
+
throw new Error("PolyCurve.offset() requires at least 1 segment");
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const pts: Point[] = [];
|
|
245
|
+
for (const seg of this.segments) {
|
|
246
|
+
if (seg instanceof Line) {
|
|
247
|
+
if (pts.length === 0) pts.push(seg.start);
|
|
248
|
+
pts.push(seg.end);
|
|
249
|
+
} else {
|
|
250
|
+
if (pts.length === 0) pts.push(seg.pointAt(0));
|
|
251
|
+
pts.push(seg.pointAt(1));
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if (pts.length < 2) {
|
|
255
|
+
throw new Error("PolyCurve.offset() requires at least 2 vertices");
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const coords = new Float64Array(pts.length * 3);
|
|
259
|
+
for (let i = 0; i < pts.length; i++) {
|
|
260
|
+
coords[i * 3] = pts[i].x;
|
|
261
|
+
coords[i * 3 + 1] = pts[i].y;
|
|
262
|
+
coords[i * 3 + 2] = pts[i].z;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const nx = normal?.x ?? 0, ny = normal?.y ?? 0, nz = normal?.z ?? 0;
|
|
266
|
+
const result = wasm.offset_polyline_curve(coords, distance, nx, ny, nz);
|
|
267
|
+
if (result.length < 6) {
|
|
268
|
+
throw new Error("PolyCurve.offset() failed - WASM returned insufficient data");
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Convert flat coords back to line segments
|
|
272
|
+
const segs: CurveSegment[] = [];
|
|
273
|
+
for (let i = 0; i < result.length - 3; i += 3) {
|
|
274
|
+
const s = new Point(result[i], result[i + 1], result[i + 2]);
|
|
275
|
+
const e = new Point(result[i + 3], result[i + 4], result[i + 5]);
|
|
276
|
+
segs.push(new Line(s, e));
|
|
277
|
+
}
|
|
278
|
+
return new PolyCurve(segs);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Convert to an exact NURBS curve representation via WASM.
|
|
283
|
+
* Lines become degree-1 NURBS, Arcs become degree-2 rational NURBS.
|
|
284
|
+
* All segments are degree-elevated to a common degree and joined.
|
|
285
|
+
*/
|
|
286
|
+
toNurbs(): NurbsCurve {
|
|
287
|
+
ensureInit();
|
|
288
|
+
const segData: number[] = [this.segments.length];
|
|
289
|
+
for (const seg of this.segments) {
|
|
290
|
+
if (seg instanceof Line) {
|
|
291
|
+
segData.push(SegmentTypeCode.Line);
|
|
292
|
+
segData.push(seg.start.x, seg.start.y, seg.start.z);
|
|
293
|
+
segData.push(seg.end.x, seg.end.y, seg.end.z);
|
|
294
|
+
} else {
|
|
295
|
+
// Arc
|
|
296
|
+
segData.push(SegmentTypeCode.Arc);
|
|
297
|
+
segData.push(seg.center.x, seg.center.y, seg.center.z);
|
|
298
|
+
segData.push(seg.normal.x, seg.normal.y, seg.normal.z);
|
|
299
|
+
segData.push(seg.radius, seg.startAngle, seg.endAngle);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
const buf = wasm.polycurve_to_nurbs(new Float64Array(segData));
|
|
303
|
+
if (buf.length < 2) throw new Error("Failed to convert PolyCurve to NURBS");
|
|
304
|
+
return NurbsCurve.fromData(buf);
|
|
305
|
+
}
|
|
306
|
+
}
|