brepjs 8.8.2 → 8.8.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/dist/2d/curves.d.ts.map +1 -1
- package/dist/2d/lib/Curve2D.d.ts.map +1 -1
- package/dist/2d/lib/approximations.d.ts.map +1 -1
- package/dist/2d/lib/makeCurves.d.ts.map +1 -1
- package/dist/2d/lib/ocWrapper.d.ts.map +1 -1
- package/dist/2d.cjs +2 -2
- package/dist/2d.js +3 -3
- package/dist/Blueprint-BmFJ4caY.cjs +1439 -0
- package/dist/Blueprint-DsoGiJNJ.js +1440 -0
- package/dist/{boolean2D-q5FOdOQW.cjs → boolean2D-BQk8LNmZ.cjs} +212 -127
- package/dist/{boolean2D-Dgnuy63w.js → boolean2D-D5O0F3J8.js} +212 -127
- package/dist/{booleanFns-CFit7JYt.cjs → booleanFns-CVM3dOTP.cjs} +210 -130
- package/dist/{booleanFns--Orezl-b.js → booleanFns-DOyKxL7q.js} +210 -130
- package/dist/brepjs.cjs +457 -304
- package/dist/brepjs.js +551 -398
- package/dist/core/disposal.d.ts +44 -3
- package/dist/core/disposal.d.ts.map +1 -1
- package/dist/core/geometryHelpers.d.ts.map +1 -1
- package/dist/core/kernelCall.d.ts +20 -0
- package/dist/core/kernelCall.d.ts.map +1 -1
- package/dist/core/memory.d.ts +1 -1
- package/dist/core/memory.d.ts.map +1 -1
- package/dist/core.cjs +4 -1
- package/dist/core.d.ts +1 -1
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +11 -8
- package/dist/{cornerFinder-KNTFoGrm.js → cornerFinder-DH6EwYfL.js} +1 -1
- package/dist/{cornerFinder-v4un1Fr9.cjs → cornerFinder-XAV2ywVS.cjs} +1 -1
- package/dist/curveFns-BHRYwxBM.js +281 -0
- package/dist/{curveFns-6ovDM_sR.cjs → curveFns-BsAHC3Qv.cjs} +137 -36
- package/dist/{drawFns-WgXeXHH1.cjs → drawFns-CsmUF97U.cjs} +181 -101
- package/dist/{drawFns-XwroLxdb.js → drawFns-hD05g0ZQ.js} +181 -101
- package/dist/faceFns-DNQss51F.cjs +358 -0
- package/dist/faceFns-q5CR9pOW.js +359 -0
- package/dist/{helpers-CRfqaW0Y.cjs → helpers-aylLv0_I.cjs} +13 -10
- package/dist/{helpers-CtBCzEqs.js → helpers-tNdaX01G.js} +13 -10
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/io/importFns.d.ts.map +1 -1
- package/dist/io.cjs +131 -63
- package/dist/io.js +131 -63
- package/dist/loft-CjEEqz2P.cjs +530 -0
- package/dist/loft-DTRcYrq2.js +531 -0
- package/dist/measurement-B6_cxjpw.cjs +200 -0
- package/dist/measurement-BXqFvcGh.js +201 -0
- package/dist/measurement.cjs +1 -1
- package/dist/measurement.js +1 -1
- package/dist/{meshFns-CPNNlpbw.cjs → meshFns-CTc1CRkF.cjs} +1 -1
- package/dist/{meshFns-DAmWVyEp.js → meshFns-DDFl7gLN.js} +1 -1
- package/dist/operations/exporterFns.d.ts.map +1 -1
- package/dist/operations/exporterUtils.d.ts +3 -3
- package/dist/operations/exporterUtils.d.ts.map +1 -1
- package/dist/operations/exporters.d.ts.map +1 -1
- package/dist/operations/extrude.d.ts.map +1 -1
- package/dist/operations/extrudeFns.d.ts.map +1 -1
- package/dist/operations/loft.d.ts.map +1 -1
- package/dist/operations/multiSweepFns.d.ts.map +1 -1
- package/dist/{operations-vN0tcoaU.js → operations-jRE2QbPo.js} +261 -166
- package/dist/{operations-BQ25CPI8.cjs → operations-pxjbW4Er.cjs} +261 -166
- package/dist/operations.cjs +2 -2
- package/dist/operations.js +2 -2
- package/dist/query/shapeDistanceFilter.d.ts.map +1 -1
- package/dist/query.cjs +66 -14
- package/dist/query.js +67 -15
- package/dist/{shapeFns-C785aeVn.cjs → shapeFns-D4CRxxmF.cjs} +61 -7
- package/dist/{shapeFns-ClpALED4.js → shapeFns-DNnBK8fG.js} +61 -7
- package/dist/{shapeTypes-DnwCo942.js → shapeTypes-Bi_9RZa2.js} +50 -19
- package/dist/{shapeTypes-CIijJxCz.cjs → shapeTypes-CWuX602K.cjs} +32 -1
- package/dist/sketching/CompoundSketch.d.ts.map +1 -1
- package/dist/sketching/Sketch.d.ts.map +1 -1
- package/dist/sketching/Sketcher.d.ts.map +1 -1
- package/dist/sketching/Sketcher2d.d.ts.map +1 -1
- package/dist/sketching/cannedSketches.d.ts.map +1 -1
- package/dist/sketching/draw.d.ts.map +1 -1
- package/dist/sketching.cjs +2 -2
- package/dist/sketching.js +2 -2
- package/dist/surfaceBuilders-CLal3WlK.cjs +429 -0
- package/dist/surfaceBuilders-W9Y25CIb.js +430 -0
- package/dist/topology/curveBuilders.d.ts.map +1 -1
- package/dist/topology/shapeFns.d.ts.map +1 -1
- package/dist/topology/solidBuilders.d.ts.map +1 -1
- package/dist/topology/surfaceBuilders.d.ts.map +1 -1
- package/dist/{topology-CqyxpmEh.js → topology-CMM6vAzx.js} +6 -6
- package/dist/{topology-zG8maSDK.cjs → topology-CNw-wsmG.cjs} +6 -6
- package/dist/topology.cjs +6 -6
- package/dist/topology.js +6 -6
- package/package.json +4 -1
- package/dist/Blueprint-BmbNUnGI.cjs +0 -1185
- package/dist/Blueprint-C-JJkkwL.js +0 -1186
- package/dist/curveFns-BhQECv8e.js +0 -180
- package/dist/faceFns-3PDjBeW7.js +0 -272
- package/dist/faceFns-CxaLWOjc.cjs +0 -271
- package/dist/loft-CVb-IjEI.cjs +0 -372
- package/dist/loft-DMFjK6lk.js +0 -373
- package/dist/measurement-CecYIt3s.cjs +0 -134
- package/dist/measurement-DHDLAH7-.js +0 -135
- package/dist/surfaceBuilders-CC0ZQGix.cjs +0 -289
- package/dist/surfaceBuilders-CrJtFu2a.js +0 -290
|
@@ -1,1185 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
const vectors = require("./vectors-CGLqemPY.cjs");
|
|
3
|
-
const occtBoundary = require("./occtBoundary-Cqfsau2c.cjs");
|
|
4
|
-
const shapeTypes = require("./shapeTypes-CIijJxCz.cjs");
|
|
5
|
-
const faceFns = require("./faceFns-CxaLWOjc.cjs");
|
|
6
|
-
const curveFns = require("./curveFns-6ovDM_sR.cjs");
|
|
7
|
-
const errors = require("./errors-NNmTtM5u.cjs");
|
|
8
|
-
const surfaceBuilders = require("./surfaceBuilders-CC0ZQGix.cjs");
|
|
9
|
-
const helpers = require("./helpers-CRfqaW0Y.cjs");
|
|
10
|
-
const vecOps = require("./vecOps-CjRL1jau.cjs");
|
|
11
|
-
const result = require("./result.cjs");
|
|
12
|
-
function makePlane(plane, origin) {
|
|
13
|
-
if (plane && typeof plane !== "string") {
|
|
14
|
-
return { ...plane };
|
|
15
|
-
} else {
|
|
16
|
-
return vectors.resolvePlane(plane ?? "XY", origin);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
function mirror(shape, inputPlane, origin) {
|
|
20
|
-
const oc = occtBoundary.getKernel().oc;
|
|
21
|
-
const [r, gc] = shapeTypes.localGC();
|
|
22
|
-
let originVec;
|
|
23
|
-
let directionVec;
|
|
24
|
-
if (typeof inputPlane === "string") {
|
|
25
|
-
const plane = vectors.resolvePlane(inputPlane, origin);
|
|
26
|
-
originVec = plane.origin;
|
|
27
|
-
directionVec = plane.zDir;
|
|
28
|
-
} else if (inputPlane && typeof inputPlane === "object" && "origin" in inputPlane && "zDir" in inputPlane) {
|
|
29
|
-
originVec = origin ? occtBoundary.toVec3(origin) : inputPlane.origin;
|
|
30
|
-
directionVec = inputPlane.zDir;
|
|
31
|
-
} else if (inputPlane) {
|
|
32
|
-
originVec = origin ? occtBoundary.toVec3(origin) : [0, 0, 0];
|
|
33
|
-
directionVec = occtBoundary.toVec3(inputPlane);
|
|
34
|
-
} else {
|
|
35
|
-
const plane = vectors.resolvePlane("YZ", origin);
|
|
36
|
-
originVec = plane.origin;
|
|
37
|
-
directionVec = plane.zDir;
|
|
38
|
-
}
|
|
39
|
-
const mirrorAxis = r(occtBoundary.makeOcAx2(originVec, directionVec));
|
|
40
|
-
const trsf = r(new oc.gp_Trsf_1());
|
|
41
|
-
trsf.SetMirror_3(mirrorAxis);
|
|
42
|
-
const transformer = r(new oc.BRepBuilderAPI_Transform_2(shape, trsf, true));
|
|
43
|
-
const newShape = transformer.ModifiedShape(shape);
|
|
44
|
-
gc();
|
|
45
|
-
return newShape;
|
|
46
|
-
}
|
|
47
|
-
function isPoint2D(point) {
|
|
48
|
-
return Array.isArray(point) && point.length === 2 && typeof point[0] === "number" && typeof point[1] === "number";
|
|
49
|
-
}
|
|
50
|
-
function precisionRound(number, precision) {
|
|
51
|
-
const factor = Math.pow(10, precision);
|
|
52
|
-
const n = precision < 0 ? number : 0.01 / factor + number;
|
|
53
|
-
return Math.round(n * factor) / factor;
|
|
54
|
-
}
|
|
55
|
-
function round2(v) {
|
|
56
|
-
return Math.round(v * 100) / 100;
|
|
57
|
-
}
|
|
58
|
-
const reprPnt = ([x, y]) => {
|
|
59
|
-
return `(${round2(x)},${round2(y)})`;
|
|
60
|
-
};
|
|
61
|
-
const asFixed = (p, precision = 1e-9) => {
|
|
62
|
-
let num = p;
|
|
63
|
-
if (Math.abs(p) < precision) num = 0;
|
|
64
|
-
return num.toFixed(-Math.log10(precision));
|
|
65
|
-
};
|
|
66
|
-
const removeDuplicatePoints = (points, precision = 1e-9) => {
|
|
67
|
-
return Array.from(
|
|
68
|
-
new Map(
|
|
69
|
-
points.map(([p0, p1]) => [
|
|
70
|
-
`[${asFixed(p0, precision)},${asFixed(p1, precision)}]`,
|
|
71
|
-
[p0, p1]
|
|
72
|
-
])
|
|
73
|
-
).values()
|
|
74
|
-
);
|
|
75
|
-
};
|
|
76
|
-
const pnt = ([x, y]) => {
|
|
77
|
-
const oc = occtBoundary.getKernel().oc;
|
|
78
|
-
return new oc.gp_Pnt2d_3(x, y);
|
|
79
|
-
};
|
|
80
|
-
const direction2d = ([x, y]) => {
|
|
81
|
-
const oc = occtBoundary.getKernel().oc;
|
|
82
|
-
return new oc.gp_Dir2d_4(x, y);
|
|
83
|
-
};
|
|
84
|
-
const vec = ([x, y]) => {
|
|
85
|
-
const oc = occtBoundary.getKernel().oc;
|
|
86
|
-
return new oc.gp_Vec2d_4(x, y);
|
|
87
|
-
};
|
|
88
|
-
const axis2d = (point, direction) => {
|
|
89
|
-
const oc = occtBoundary.getKernel().oc;
|
|
90
|
-
const [r, gc] = shapeTypes.localGC();
|
|
91
|
-
const axis = new oc.gp_Ax2d_2(r(pnt(point)), r(direction2d(direction)));
|
|
92
|
-
gc();
|
|
93
|
-
return axis;
|
|
94
|
-
};
|
|
95
|
-
class BoundingBox2d {
|
|
96
|
-
_wrapped;
|
|
97
|
-
_deleted = false;
|
|
98
|
-
constructor(wrapped) {
|
|
99
|
-
const oc = occtBoundary.getKernel().oc;
|
|
100
|
-
this._wrapped = wrapped ?? new oc.Bnd_Box2d();
|
|
101
|
-
shapeTypes.registerForCleanup(this, this._wrapped);
|
|
102
|
-
}
|
|
103
|
-
get wrapped() {
|
|
104
|
-
if (this._deleted) throw new Error("This object has been deleted");
|
|
105
|
-
return this._wrapped;
|
|
106
|
-
}
|
|
107
|
-
delete() {
|
|
108
|
-
if (!this._deleted) {
|
|
109
|
-
this._deleted = true;
|
|
110
|
-
shapeTypes.unregisterFromCleanup(this._wrapped);
|
|
111
|
-
this._wrapped.delete();
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
/** Return a human-readable string of the form `(xMin,yMin) - (xMax,yMax)`. */
|
|
115
|
-
get repr() {
|
|
116
|
-
const [min, max] = this.bounds;
|
|
117
|
-
return `${reprPnt(min)} - ${reprPnt(max)}`;
|
|
118
|
-
}
|
|
119
|
-
/** Return the `[min, max]` corner points of the bounding box. */
|
|
120
|
-
get bounds() {
|
|
121
|
-
const xMin = { current: 0 };
|
|
122
|
-
const yMin = { current: 0 };
|
|
123
|
-
const xMax = { current: 0 };
|
|
124
|
-
const yMax = { current: 0 };
|
|
125
|
-
this.wrapped.Get(xMin, yMin, xMax, yMax);
|
|
126
|
-
return [
|
|
127
|
-
[xMin.current, yMin.current],
|
|
128
|
-
[xMax.current, yMax.current]
|
|
129
|
-
];
|
|
130
|
-
}
|
|
131
|
-
/** Return the center point of the bounding box. */
|
|
132
|
-
get center() {
|
|
133
|
-
const [[xmin, ymin], [xmax, ymax]] = this.bounds;
|
|
134
|
-
return [xmin + (xmax - xmin) / 2, ymin + (ymax - ymin) / 2];
|
|
135
|
-
}
|
|
136
|
-
/** Return the width (x-extent) of the bounding box. */
|
|
137
|
-
get width() {
|
|
138
|
-
const [[xmin], [xmax]] = this.bounds;
|
|
139
|
-
return Math.abs(xmax - xmin);
|
|
140
|
-
}
|
|
141
|
-
/** Return the height (y-extent) of the bounding box. */
|
|
142
|
-
get height() {
|
|
143
|
-
const [[, ymin], [, ymax]] = this.bounds;
|
|
144
|
-
return Math.abs(ymax - ymin);
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* Return a point guaranteed to lie outside the bounding box.
|
|
148
|
-
*
|
|
149
|
-
* @param paddingPercent - Extra padding as a percentage of the box dimensions.
|
|
150
|
-
*/
|
|
151
|
-
outsidePoint(paddingPercent = 1) {
|
|
152
|
-
const [min, max] = this.bounds;
|
|
153
|
-
const width = max[0] - min[0];
|
|
154
|
-
const height = max[1] - min[1];
|
|
155
|
-
return [
|
|
156
|
-
max[0] + width / 100 * paddingPercent,
|
|
157
|
-
max[1] + height / 100 * paddingPercent * 0.9
|
|
158
|
-
];
|
|
159
|
-
}
|
|
160
|
-
/** Expand this bounding box to include `other`. */
|
|
161
|
-
add(other) {
|
|
162
|
-
this.wrapped.Add_1(other.wrapped);
|
|
163
|
-
}
|
|
164
|
-
/** Test whether this bounding box and `other` are completely disjoint. */
|
|
165
|
-
isOut(other) {
|
|
166
|
-
return this.wrapped.IsOut_4(other.wrapped);
|
|
167
|
-
}
|
|
168
|
-
/** Test whether the given point lies inside (or on the boundary of) this box. */
|
|
169
|
-
containsPoint(other) {
|
|
170
|
-
const r = shapeTypes.gcWithScope();
|
|
171
|
-
const point = r(pnt(other));
|
|
172
|
-
return !this.wrapped.IsOut_1(point);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
function deserializeCurve2D(data) {
|
|
176
|
-
const oc = occtBoundary.getKernel().oc;
|
|
177
|
-
const handle = oc.GeomToolsWrapper.Read(data);
|
|
178
|
-
return new Curve2D(handle);
|
|
179
|
-
}
|
|
180
|
-
class Curve2D {
|
|
181
|
-
_wrapped;
|
|
182
|
-
_deleted = false;
|
|
183
|
-
_boundingBox;
|
|
184
|
-
_firstPoint = null;
|
|
185
|
-
_lastPoint = null;
|
|
186
|
-
constructor(handle) {
|
|
187
|
-
const oc = occtBoundary.getKernel().oc;
|
|
188
|
-
const inner = handle.get();
|
|
189
|
-
this._wrapped = new oc.Handle_Geom2d_Curve_2(inner);
|
|
190
|
-
this._boundingBox = null;
|
|
191
|
-
shapeTypes.registerForCleanup(this, this._wrapped);
|
|
192
|
-
}
|
|
193
|
-
get wrapped() {
|
|
194
|
-
if (this._deleted) throw new Error("This object has been deleted");
|
|
195
|
-
return this._wrapped;
|
|
196
|
-
}
|
|
197
|
-
delete() {
|
|
198
|
-
if (!this._deleted) {
|
|
199
|
-
this._deleted = true;
|
|
200
|
-
shapeTypes.unregisterFromCleanup(this._wrapped);
|
|
201
|
-
this._wrapped.delete();
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
/** Compute (and cache) the 2D bounding box of this curve. */
|
|
205
|
-
get boundingBox() {
|
|
206
|
-
if (this._boundingBox) return this._boundingBox;
|
|
207
|
-
const oc = occtBoundary.getKernel().oc;
|
|
208
|
-
const boundBox = new oc.Bnd_Box2d();
|
|
209
|
-
oc.BndLib_Add2dCurve.Add_3(this.wrapped, 1e-6, boundBox);
|
|
210
|
-
this._boundingBox = new BoundingBox2d(boundBox);
|
|
211
|
-
return this._boundingBox;
|
|
212
|
-
}
|
|
213
|
-
/** Return a human-readable representation, e.g. `LINE (0,0) - (1,1)`. */
|
|
214
|
-
get repr() {
|
|
215
|
-
return `${this.geomType} ${reprPnt(this.firstPoint)} - ${reprPnt(this.lastPoint)}`;
|
|
216
|
-
}
|
|
217
|
-
/** Access the underlying OCCT `Geom2d_Curve` (unwrapped from its handle). */
|
|
218
|
-
get innerCurve() {
|
|
219
|
-
return this.wrapped.get();
|
|
220
|
-
}
|
|
221
|
-
/** Serialize this curve to a string that can be restored with {@link deserializeCurve2D}. */
|
|
222
|
-
serialize() {
|
|
223
|
-
const oc = occtBoundary.getKernel().oc;
|
|
224
|
-
return oc.GeomToolsWrapper.Write(this.wrapped);
|
|
225
|
-
}
|
|
226
|
-
/** Evaluate the curve at the given parameter, returning the 2D point. */
|
|
227
|
-
value(parameter) {
|
|
228
|
-
const p = this.innerCurve.Value(parameter);
|
|
229
|
-
const v = [p.X(), p.Y()];
|
|
230
|
-
p.delete();
|
|
231
|
-
return v;
|
|
232
|
-
}
|
|
233
|
-
/** Return the point at the start of the curve (cached after first access). */
|
|
234
|
-
get firstPoint() {
|
|
235
|
-
if (this._firstPoint === null) {
|
|
236
|
-
this._firstPoint = this.value(this.firstParameter);
|
|
237
|
-
}
|
|
238
|
-
return this._firstPoint;
|
|
239
|
-
}
|
|
240
|
-
/** Return the point at the end of the curve (cached after first access). */
|
|
241
|
-
get lastPoint() {
|
|
242
|
-
if (this._lastPoint === null) {
|
|
243
|
-
this._lastPoint = this.value(this.lastParameter);
|
|
244
|
-
}
|
|
245
|
-
return this._lastPoint;
|
|
246
|
-
}
|
|
247
|
-
/** Return the parameter value at the start of the curve. */
|
|
248
|
-
get firstParameter() {
|
|
249
|
-
return this.innerCurve.FirstParameter();
|
|
250
|
-
}
|
|
251
|
-
/** Return the parameter value at the end of the curve. */
|
|
252
|
-
get lastParameter() {
|
|
253
|
-
return this.innerCurve.LastParameter();
|
|
254
|
-
}
|
|
255
|
-
/** Create a `Geom2dAdaptor_Curve` for algorithmic queries (caller must delete). */
|
|
256
|
-
adaptor() {
|
|
257
|
-
const oc = occtBoundary.getKernel().oc;
|
|
258
|
-
return new oc.Geom2dAdaptor_Curve_2(this.wrapped);
|
|
259
|
-
}
|
|
260
|
-
/** Return the geometric type of this curve (e.g. `LINE`, `CIRCLE`, `BSPLINE_CURVE`). */
|
|
261
|
-
get geomType() {
|
|
262
|
-
const adaptor = this.adaptor();
|
|
263
|
-
const curveType = errors.unwrap(curveFns.findCurveType(adaptor.GetType()));
|
|
264
|
-
adaptor.delete();
|
|
265
|
-
return curveType;
|
|
266
|
-
}
|
|
267
|
-
/** Create an independent deep copy of this curve. */
|
|
268
|
-
clone() {
|
|
269
|
-
const cloned = new Curve2D(this.innerCurve.Copy());
|
|
270
|
-
cloned._firstPoint = this._firstPoint;
|
|
271
|
-
cloned._lastPoint = this._lastPoint;
|
|
272
|
-
return cloned;
|
|
273
|
-
}
|
|
274
|
-
/** Reverse the orientation of this curve in place. */
|
|
275
|
-
reverse() {
|
|
276
|
-
this.innerCurve.Reverse();
|
|
277
|
-
const tmp = this._firstPoint;
|
|
278
|
-
this._firstPoint = this._lastPoint;
|
|
279
|
-
this._lastPoint = tmp;
|
|
280
|
-
}
|
|
281
|
-
distanceFromPoint(point) {
|
|
282
|
-
const oc = occtBoundary.getKernel().oc;
|
|
283
|
-
const r = shapeTypes.gcWithScope();
|
|
284
|
-
const projector = r(new oc.Geom2dAPI_ProjectPointOnCurve_2(r(pnt(point)), this.wrapped));
|
|
285
|
-
let curveToPoint;
|
|
286
|
-
try {
|
|
287
|
-
curveToPoint = projector.LowerDistance();
|
|
288
|
-
} catch {
|
|
289
|
-
curveToPoint = Infinity;
|
|
290
|
-
}
|
|
291
|
-
return Math.min(
|
|
292
|
-
curveToPoint,
|
|
293
|
-
helpers.distance2d(point, this.firstPoint),
|
|
294
|
-
helpers.distance2d(point, this.lastPoint)
|
|
295
|
-
);
|
|
296
|
-
}
|
|
297
|
-
distanceFromCurve(curve) {
|
|
298
|
-
const oc = occtBoundary.getKernel().oc;
|
|
299
|
-
const r = shapeTypes.gcWithScope();
|
|
300
|
-
let curveDistance;
|
|
301
|
-
const projector = r(
|
|
302
|
-
new oc.Geom2dAPI_ExtremaCurveCurve(
|
|
303
|
-
this.wrapped,
|
|
304
|
-
curve.wrapped,
|
|
305
|
-
this.firstParameter,
|
|
306
|
-
this.lastParameter,
|
|
307
|
-
curve.firstParameter,
|
|
308
|
-
curve.lastParameter
|
|
309
|
-
)
|
|
310
|
-
);
|
|
311
|
-
try {
|
|
312
|
-
curveDistance = projector.LowerDistance();
|
|
313
|
-
} catch {
|
|
314
|
-
curveDistance = Infinity;
|
|
315
|
-
}
|
|
316
|
-
return Math.min(
|
|
317
|
-
curveDistance,
|
|
318
|
-
this.distanceFromPoint(curve.firstPoint),
|
|
319
|
-
this.distanceFromPoint(curve.lastPoint),
|
|
320
|
-
curve.distanceFromPoint(this.firstPoint),
|
|
321
|
-
curve.distanceFromPoint(this.lastPoint)
|
|
322
|
-
);
|
|
323
|
-
}
|
|
324
|
-
/** Compute the minimum distance from this curve to a point or another curve. */
|
|
325
|
-
distanceFrom(element) {
|
|
326
|
-
if (isPoint2D(element)) {
|
|
327
|
-
return this.distanceFromPoint(element);
|
|
328
|
-
}
|
|
329
|
-
return this.distanceFromCurve(element);
|
|
330
|
-
}
|
|
331
|
-
/** Test whether a point lies on the curve within a tight tolerance (1e-9). */
|
|
332
|
-
isOnCurve(point) {
|
|
333
|
-
return this.distanceFromPoint(point) < 1e-9;
|
|
334
|
-
}
|
|
335
|
-
/**
|
|
336
|
-
* Project a point onto the curve and return its parameter value.
|
|
337
|
-
*
|
|
338
|
-
* @returns `Ok(parameter)` when the point is on the curve, or an error result otherwise.
|
|
339
|
-
*/
|
|
340
|
-
parameter(point, precision = 1e-9) {
|
|
341
|
-
const oc = occtBoundary.getKernel().oc;
|
|
342
|
-
const r = shapeTypes.gcWithScope();
|
|
343
|
-
let lowerDistance;
|
|
344
|
-
let lowerDistanceParameter;
|
|
345
|
-
try {
|
|
346
|
-
const projector = r(new oc.Geom2dAPI_ProjectPointOnCurve_2(r(pnt(point)), this.wrapped));
|
|
347
|
-
lowerDistance = projector.LowerDistance();
|
|
348
|
-
lowerDistanceParameter = projector.LowerDistanceParameter();
|
|
349
|
-
} catch {
|
|
350
|
-
if (helpers.samePoint(point, this.firstPoint, precision)) return errors.ok(this.firstParameter);
|
|
351
|
-
if (helpers.samePoint(point, this.lastPoint, precision)) return errors.ok(this.lastParameter);
|
|
352
|
-
return errors.err(errors.computationError("PARAMETER_NOT_FOUND", "Failed to find parameter"));
|
|
353
|
-
}
|
|
354
|
-
if (lowerDistance > precision) {
|
|
355
|
-
return errors.err(
|
|
356
|
-
errors.computationError(
|
|
357
|
-
"POINT_NOT_ON_CURVE",
|
|
358
|
-
`Point ${reprPnt(point)} not on curve ${this.repr}, ${lowerDistance.toFixed(9)}`
|
|
359
|
-
)
|
|
360
|
-
);
|
|
361
|
-
}
|
|
362
|
-
return errors.ok(lowerDistanceParameter);
|
|
363
|
-
}
|
|
364
|
-
/**
|
|
365
|
-
* Compute the tangent vector at a parameter position or at the projection of a point.
|
|
366
|
-
*
|
|
367
|
-
* @param index - A normalized parameter (0..1) or a Point2D to project onto the curve.
|
|
368
|
-
*/
|
|
369
|
-
tangentAt(index) {
|
|
370
|
-
const oc = occtBoundary.getKernel().oc;
|
|
371
|
-
const [r, gc] = shapeTypes.localGC();
|
|
372
|
-
let param;
|
|
373
|
-
if (Array.isArray(index)) {
|
|
374
|
-
param = errors.unwrap(this.parameter(index));
|
|
375
|
-
} else {
|
|
376
|
-
const paramLength = this.innerCurve.LastParameter() - this.innerCurve.FirstParameter();
|
|
377
|
-
param = paramLength * index + Number(this.innerCurve.FirstParameter());
|
|
378
|
-
}
|
|
379
|
-
const point = r(new oc.gp_Pnt2d_1());
|
|
380
|
-
const dir = r(new oc.gp_Vec2d_1());
|
|
381
|
-
this.innerCurve.D1(param, point, dir);
|
|
382
|
-
const tgtVec = [dir.X(), dir.Y()];
|
|
383
|
-
gc();
|
|
384
|
-
return tgtVec;
|
|
385
|
-
}
|
|
386
|
-
/**
|
|
387
|
-
* Split this curve at the given points or parameter values.
|
|
388
|
-
*
|
|
389
|
-
* @returns An array of sub-curves whose union covers the original curve.
|
|
390
|
-
*/
|
|
391
|
-
splitAt(points, precision = 1e-9) {
|
|
392
|
-
const oc = occtBoundary.getKernel().oc;
|
|
393
|
-
const r = shapeTypes.gcWithScope();
|
|
394
|
-
let parameters = points.map((point) => {
|
|
395
|
-
if (isPoint2D(point)) return errors.unwrap(this.parameter(point, precision));
|
|
396
|
-
return point;
|
|
397
|
-
});
|
|
398
|
-
parameters = Array.from(
|
|
399
|
-
new Map(parameters.map((p) => [precisionRound(p, -Math.log10(precision)), p])).values()
|
|
400
|
-
).sort((a, b) => a - b);
|
|
401
|
-
const firstParam = this.firstParameter;
|
|
402
|
-
const lastParam = this.lastParameter;
|
|
403
|
-
if (firstParam > lastParam) {
|
|
404
|
-
parameters.reverse();
|
|
405
|
-
}
|
|
406
|
-
if (Math.abs(parameters[0] - firstParam) < precision * 100) parameters = parameters.slice(1);
|
|
407
|
-
if (!parameters.length) return [this];
|
|
408
|
-
if (
|
|
409
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
410
|
-
Math.abs(parameters[parameters.length - 1] - lastParam) < precision * 100
|
|
411
|
-
)
|
|
412
|
-
parameters = parameters.slice(0, -1);
|
|
413
|
-
if (!parameters.length) return [this];
|
|
414
|
-
return surfaceBuilders.zip([
|
|
415
|
-
[firstParam, ...parameters],
|
|
416
|
-
[...parameters, lastParam]
|
|
417
|
-
]).map(([first, last]) => {
|
|
418
|
-
try {
|
|
419
|
-
if (this.geomType === "BEZIER_CURVE") {
|
|
420
|
-
const curveCopy = new oc.Geom2d_BezierCurve_1(r(this.adaptor()).Bezier().get().Poles_2());
|
|
421
|
-
curveCopy.Segment(first, last);
|
|
422
|
-
return new Curve2D(new oc.Handle_Geom2d_Curve_2(curveCopy));
|
|
423
|
-
}
|
|
424
|
-
if (this.geomType === "BSPLINE_CURVE") {
|
|
425
|
-
const adapted = r(this.adaptor()).BSpline().get();
|
|
426
|
-
const curveCopy = new oc.Geom2d_BSplineCurve_1(
|
|
427
|
-
adapted.Poles_2(),
|
|
428
|
-
adapted.Knots_2(),
|
|
429
|
-
adapted.Multiplicities_2(),
|
|
430
|
-
adapted.Degree(),
|
|
431
|
-
adapted.IsPeriodic()
|
|
432
|
-
);
|
|
433
|
-
curveCopy.Segment(first, last, precision);
|
|
434
|
-
return new Curve2D(new oc.Handle_Geom2d_Curve_2(curveCopy));
|
|
435
|
-
}
|
|
436
|
-
const trimmed = new oc.Geom2d_TrimmedCurve(this.wrapped, first, last, true, true);
|
|
437
|
-
return new Curve2D(new oc.Handle_Geom2d_Curve_2(trimmed));
|
|
438
|
-
} catch (e) {
|
|
439
|
-
throw new Error(
|
|
440
|
-
`Failed to split the curve: ${e instanceof Error ? e.message : String(e)}`,
|
|
441
|
-
{ cause: e }
|
|
442
|
-
);
|
|
443
|
-
}
|
|
444
|
-
});
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
const approximateAsBSpline = (adaptor, tolerance = 1e-4, continuity = "C0", maxSegments = 200) => {
|
|
448
|
-
const oc = occtBoundary.getKernel().oc;
|
|
449
|
-
const r = shapeTypes.gcWithScope();
|
|
450
|
-
const continuities = {
|
|
451
|
-
C0: oc.GeomAbs_Shape.GeomAbs_C0,
|
|
452
|
-
C1: oc.GeomAbs_Shape.GeomAbs_C1,
|
|
453
|
-
C2: oc.GeomAbs_Shape.GeomAbs_C2,
|
|
454
|
-
C3: oc.GeomAbs_Shape.GeomAbs_C3
|
|
455
|
-
};
|
|
456
|
-
const convert = r(
|
|
457
|
-
new oc.Geom2dConvert_ApproxCurve_2(
|
|
458
|
-
adaptor.ShallowCopy(),
|
|
459
|
-
tolerance,
|
|
460
|
-
continuities[continuity],
|
|
461
|
-
maxSegments,
|
|
462
|
-
3
|
|
463
|
-
)
|
|
464
|
-
);
|
|
465
|
-
return new Curve2D(convert.Curve());
|
|
466
|
-
};
|
|
467
|
-
const BSplineToBezier = (adaptor) => {
|
|
468
|
-
if (errors.unwrap(curveFns.findCurveType(adaptor.GetType())) !== "BSPLINE_CURVE")
|
|
469
|
-
result.bug("BSplineToBezier", "You can only convert a Bspline");
|
|
470
|
-
const handle = adaptor.BSpline();
|
|
471
|
-
const oc = occtBoundary.getKernel().oc;
|
|
472
|
-
const convert = new oc.Geom2dConvert_BSplineCurveToBezierCurve_1(handle);
|
|
473
|
-
function* bezierCurves() {
|
|
474
|
-
const nArcs = convert.NbArcs();
|
|
475
|
-
if (!nArcs) return;
|
|
476
|
-
for (let i = 1; i <= nArcs; i++) {
|
|
477
|
-
const arc = convert.Arc(i);
|
|
478
|
-
yield new Curve2D(arc);
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
const curves = Array.from(bezierCurves());
|
|
482
|
-
convert.delete();
|
|
483
|
-
return curves;
|
|
484
|
-
};
|
|
485
|
-
function approximateAsSvgCompatibleCurve(curves, options = {
|
|
486
|
-
tolerance: 1e-4,
|
|
487
|
-
continuity: "C0",
|
|
488
|
-
maxSegments: 300
|
|
489
|
-
}) {
|
|
490
|
-
const r = shapeTypes.gcWithScope();
|
|
491
|
-
return curves.flatMap((curve) => {
|
|
492
|
-
const adaptor = r(curve.adaptor());
|
|
493
|
-
const curveType = errors.unwrap(curveFns.findCurveType(adaptor.GetType()));
|
|
494
|
-
if (curveType === "ELLIPSE" || curveType === "CIRCLE" && helpers.samePoint(curve.firstPoint, curve.lastPoint)) {
|
|
495
|
-
return curve.splitAt([0.5]);
|
|
496
|
-
}
|
|
497
|
-
if (["LINE", "ELLIPSE", "CIRCLE"].includes(curveType)) {
|
|
498
|
-
return curve;
|
|
499
|
-
}
|
|
500
|
-
if (curveType === "BEZIER_CURVE") {
|
|
501
|
-
const b = adaptor.Bezier().get();
|
|
502
|
-
const deg = b.Degree();
|
|
503
|
-
if ([1, 2, 3].includes(deg)) {
|
|
504
|
-
return curve;
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
if (curveType === "BSPLINE_CURVE") {
|
|
508
|
-
const c = BSplineToBezier(adaptor);
|
|
509
|
-
return approximateAsSvgCompatibleCurve(c, options);
|
|
510
|
-
}
|
|
511
|
-
const bspline = approximateAsBSpline(
|
|
512
|
-
adaptor,
|
|
513
|
-
options.tolerance,
|
|
514
|
-
options.continuity,
|
|
515
|
-
options.maxSegments
|
|
516
|
-
);
|
|
517
|
-
return approximateAsSvgCompatibleCurve(BSplineToBezier(r(bspline.adaptor())), options);
|
|
518
|
-
});
|
|
519
|
-
}
|
|
520
|
-
const make2dSegmentCurve = (startPoint, endPoint) => {
|
|
521
|
-
const oc = occtBoundary.getKernel().oc;
|
|
522
|
-
const [r, gc] = shapeTypes.localGC();
|
|
523
|
-
const segment = r(new oc.GCE2d_MakeSegment_1(r(pnt(startPoint)), r(pnt(endPoint)))).Value();
|
|
524
|
-
const curve = new Curve2D(segment);
|
|
525
|
-
if (!helpers.samePoint(curve.firstPoint, startPoint)) {
|
|
526
|
-
curve.reverse();
|
|
527
|
-
}
|
|
528
|
-
gc();
|
|
529
|
-
return curve;
|
|
530
|
-
};
|
|
531
|
-
const make2dThreePointArc = (startPoint, midPoint, endPoint) => {
|
|
532
|
-
const oc = occtBoundary.getKernel().oc;
|
|
533
|
-
const [r, gc] = shapeTypes.localGC();
|
|
534
|
-
const segment = r(
|
|
535
|
-
new oc.GCE2d_MakeArcOfCircle_4(r(pnt(startPoint)), r(pnt(midPoint)), r(pnt(endPoint)))
|
|
536
|
-
).Value();
|
|
537
|
-
gc();
|
|
538
|
-
const curve = new Curve2D(segment);
|
|
539
|
-
if (!helpers.samePoint(curve.firstPoint, startPoint)) {
|
|
540
|
-
curve.wrapped.get().SetTrim(curve.lastParameter, curve.firstParameter, true, true);
|
|
541
|
-
}
|
|
542
|
-
return curve;
|
|
543
|
-
};
|
|
544
|
-
const make2dTangentArc = (startPoint, tangent, endPoint) => {
|
|
545
|
-
const oc = occtBoundary.getKernel().oc;
|
|
546
|
-
const [r, gc] = shapeTypes.localGC();
|
|
547
|
-
const segment = r(
|
|
548
|
-
new oc.GCE2d_MakeArcOfCircle_5(r(pnt(startPoint)), r(vec(tangent)), r(pnt(endPoint)))
|
|
549
|
-
).Value();
|
|
550
|
-
gc();
|
|
551
|
-
const curve = new Curve2D(segment);
|
|
552
|
-
if (!helpers.samePoint(curve.firstPoint, startPoint)) {
|
|
553
|
-
curve.wrapped.get().SetTrim(curve.lastParameter, curve.firstParameter, true, true);
|
|
554
|
-
}
|
|
555
|
-
return curve;
|
|
556
|
-
};
|
|
557
|
-
const make2dCircle = (radius, center = [0, 0]) => {
|
|
558
|
-
const oc = occtBoundary.getKernel().oc;
|
|
559
|
-
const [r, gc] = shapeTypes.localGC();
|
|
560
|
-
const segment = r(new oc.GCE2d_MakeCircle_7(r(pnt(center)), radius, true)).Value();
|
|
561
|
-
gc();
|
|
562
|
-
return new Curve2D(segment);
|
|
563
|
-
};
|
|
564
|
-
const make2dEllipse = (majorRadius, minorRadius, xDir = [1, 0], center = [0, 0], direct = true) => {
|
|
565
|
-
const oc = occtBoundary.getKernel().oc;
|
|
566
|
-
const [r, gc] = shapeTypes.localGC();
|
|
567
|
-
const ellipse = r(new oc.gp_Elips2d_2(r(axis2d(center, xDir)), majorRadius, minorRadius, direct));
|
|
568
|
-
const segment = r(new oc.GCE2d_MakeEllipse_1(ellipse)).Value();
|
|
569
|
-
gc();
|
|
570
|
-
return new Curve2D(segment);
|
|
571
|
-
};
|
|
572
|
-
const make2dEllipseArc = (majorRadius, minorRadius, startAngle, endAngle, center = [0, 0], xDir, direct = true) => {
|
|
573
|
-
const oc = occtBoundary.getKernel().oc;
|
|
574
|
-
const [r, gc] = shapeTypes.localGC();
|
|
575
|
-
const ellipse = r(new oc.gp_Elips2d_2(r(axis2d(center, xDir)), majorRadius, minorRadius, true));
|
|
576
|
-
const segment = r(new oc.GCE2d_MakeArcOfEllipse_1(ellipse, startAngle, endAngle, direct)).Value();
|
|
577
|
-
gc();
|
|
578
|
-
return new Curve2D(segment);
|
|
579
|
-
};
|
|
580
|
-
const make2dBezierCurve = (startPoint, controls, endPoint) => {
|
|
581
|
-
const oc = occtBoundary.getKernel().oc;
|
|
582
|
-
const [r, gc] = shapeTypes.localGC();
|
|
583
|
-
const arrayOfPoints = r(new oc.TColgp_Array1OfPnt2d_2(1, controls.length + 2));
|
|
584
|
-
arrayOfPoints.SetValue(1, r(pnt(startPoint)));
|
|
585
|
-
controls.forEach((p, i) => {
|
|
586
|
-
arrayOfPoints.SetValue(i + 2, r(pnt(p)));
|
|
587
|
-
});
|
|
588
|
-
arrayOfPoints.SetValue(controls.length + 2, r(pnt(endPoint)));
|
|
589
|
-
const bezCurve = new oc.Geom2d_BezierCurve_1(arrayOfPoints);
|
|
590
|
-
gc();
|
|
591
|
-
return new Curve2D(new oc.Handle_Geom2d_Curve_2(bezCurve));
|
|
592
|
-
};
|
|
593
|
-
function make2dInerpolatedBSplineCurve(points, {
|
|
594
|
-
tolerance = 1e-3,
|
|
595
|
-
smoothing = null,
|
|
596
|
-
degMax = 3,
|
|
597
|
-
degMin = 1
|
|
598
|
-
} = {}) {
|
|
599
|
-
const r = shapeTypes.gcWithScope();
|
|
600
|
-
const oc = occtBoundary.getKernel().oc;
|
|
601
|
-
const pnts = r(new oc.TColgp_Array1OfPnt2d_2(1, points.length));
|
|
602
|
-
points.forEach((point, index) => {
|
|
603
|
-
pnts.SetValue(index + 1, r(pnt(point)));
|
|
604
|
-
});
|
|
605
|
-
let splineBuilder;
|
|
606
|
-
if (smoothing) {
|
|
607
|
-
splineBuilder = r(
|
|
608
|
-
new oc.Geom2dAPI_PointsToBSpline_6(
|
|
609
|
-
pnts,
|
|
610
|
-
smoothing[0],
|
|
611
|
-
smoothing[1],
|
|
612
|
-
smoothing[2],
|
|
613
|
-
degMax,
|
|
614
|
-
oc.GeomAbs_Shape.GeomAbs_C2,
|
|
615
|
-
tolerance
|
|
616
|
-
)
|
|
617
|
-
);
|
|
618
|
-
} else {
|
|
619
|
-
splineBuilder = r(
|
|
620
|
-
new oc.Geom2dAPI_PointsToBSpline_2(
|
|
621
|
-
pnts,
|
|
622
|
-
degMin,
|
|
623
|
-
degMax,
|
|
624
|
-
oc.GeomAbs_Shape.GeomAbs_C2,
|
|
625
|
-
tolerance
|
|
626
|
-
)
|
|
627
|
-
);
|
|
628
|
-
}
|
|
629
|
-
if (!splineBuilder.IsDone()) {
|
|
630
|
-
return errors.err(errors.computationError("BSPLINE_2D_FAILED", "B-spline approximation failed"));
|
|
631
|
-
}
|
|
632
|
-
return errors.ok(new Curve2D(splineBuilder.Curve()));
|
|
633
|
-
}
|
|
634
|
-
const make2dArcFromCenter = (startPoint, endPoint, center, longArc = false) => {
|
|
635
|
-
const midChord = helpers.scalarMultiply2d(helpers.add2d(startPoint, endPoint), 0.5);
|
|
636
|
-
const orientedRadius = helpers.distance2d(center, startPoint) * (longArc ? -1 : 1);
|
|
637
|
-
const midChordDir = helpers.normalize2d(helpers.subtract2d(midChord, center));
|
|
638
|
-
return make2dThreePointArc(
|
|
639
|
-
startPoint,
|
|
640
|
-
helpers.add2d(helpers.scalarMultiply2d(midChordDir, orientedRadius), center),
|
|
641
|
-
endPoint
|
|
642
|
-
);
|
|
643
|
-
};
|
|
644
|
-
function round5(v) {
|
|
645
|
-
return Math.round(v * 1e5) / 1e5;
|
|
646
|
-
}
|
|
647
|
-
const fromPnt = (pnt2) => `${round2(pnt2.X())} ${round2(pnt2.Y())}`;
|
|
648
|
-
const adaptedCurveToPathElem = (adaptor, lastPoint) => {
|
|
649
|
-
const oc = occtBoundary.getKernel().oc;
|
|
650
|
-
const r = shapeTypes.gcWithScope();
|
|
651
|
-
const curveType = errors.unwrap(curveFns.findCurveType(adaptor.GetType()));
|
|
652
|
-
const [endX, endY] = lastPoint;
|
|
653
|
-
const endpoint = `${round5(endX)} ${round5(endY)}`;
|
|
654
|
-
if (curveType === "LINE") {
|
|
655
|
-
return `L ${endpoint}`;
|
|
656
|
-
}
|
|
657
|
-
if (curveType === "BEZIER_CURVE") {
|
|
658
|
-
const bezierHandle = r(adaptor.Bezier());
|
|
659
|
-
const curve = bezierHandle.get();
|
|
660
|
-
const deg = curve.Degree();
|
|
661
|
-
if (deg === 1) {
|
|
662
|
-
return `L ${endpoint}`;
|
|
663
|
-
}
|
|
664
|
-
if (deg === 2) {
|
|
665
|
-
const pole2 = r(curve.Pole(2));
|
|
666
|
-
return `Q ${fromPnt(pole2)} ${endpoint}`;
|
|
667
|
-
}
|
|
668
|
-
if (deg === 3) {
|
|
669
|
-
const pole2 = r(curve.Pole(2));
|
|
670
|
-
const pole3 = r(curve.Pole(3));
|
|
671
|
-
const p1 = fromPnt(pole2);
|
|
672
|
-
const p2 = fromPnt(pole3);
|
|
673
|
-
return `C ${p1} ${p2} ${endpoint}`;
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
if (curveType === "CIRCLE") {
|
|
677
|
-
const curve = r(adaptor.Circle());
|
|
678
|
-
const radius = curve.Radius();
|
|
679
|
-
const p1 = adaptor.FirstParameter();
|
|
680
|
-
const p2 = adaptor.LastParameter();
|
|
681
|
-
const paramAngle = (p2 - p1) * vecOps.RAD2DEG;
|
|
682
|
-
const end = paramAngle !== 360 ? endpoint : `${round5(endX)} ${round5(endY + 1e-4)}`;
|
|
683
|
-
return `A ${radius} ${radius} 0 ${Math.abs(paramAngle) > 180 ? "1" : "0"} ${curve.IsDirect() ? "1" : "0"} ${end}`;
|
|
684
|
-
}
|
|
685
|
-
if (curveType === "ELLIPSE") {
|
|
686
|
-
const curve = r(adaptor.Ellipse());
|
|
687
|
-
const rx = curve.MajorRadius();
|
|
688
|
-
const ry = curve.MinorRadius();
|
|
689
|
-
const p1 = adaptor.FirstParameter();
|
|
690
|
-
const p2 = adaptor.LastParameter();
|
|
691
|
-
const paramAngle = (p2 - p1) * vecOps.RAD2DEG;
|
|
692
|
-
const end = paramAngle !== 360 ? endpoint : `${round5(endX)} ${round5(endY + 1e-4)}`;
|
|
693
|
-
const dir0 = r(new oc.gp_Dir2d_1());
|
|
694
|
-
const xAxis = r(curve.XAxis());
|
|
695
|
-
const xDir = r(xAxis.Direction());
|
|
696
|
-
const angle = 180 - xDir.Angle(dir0) * vecOps.RAD2DEG;
|
|
697
|
-
return `A ${round5(rx)} ${round5(ry)} ${round5(angle)} ${Math.abs(paramAngle) > 180 ? "1" : "0"} ${curve.IsDirect() ? "1" : "0"} ${end}`;
|
|
698
|
-
}
|
|
699
|
-
result.bug("adaptedCurveToPathElem", `Unsupported curve type: ${curveType}`);
|
|
700
|
-
};
|
|
701
|
-
const curvesBoundingBox = (curves) => {
|
|
702
|
-
const oc = occtBoundary.getKernel().oc;
|
|
703
|
-
const boundBox = new oc.Bnd_Box2d();
|
|
704
|
-
curves.forEach((c) => {
|
|
705
|
-
oc.BndLib_Add2dCurve.Add_3(c.wrapped, 1e-6, boundBox);
|
|
706
|
-
});
|
|
707
|
-
return new BoundingBox2d(boundBox);
|
|
708
|
-
};
|
|
709
|
-
function curvesAsEdgesOnPlane(curves, plane) {
|
|
710
|
-
const [r, gc] = shapeTypes.localGC();
|
|
711
|
-
const ax = r(occtBoundary.makeOcAx2(plane.origin, plane.zDir, plane.xDir));
|
|
712
|
-
const oc = occtBoundary.getKernel().oc;
|
|
713
|
-
const edges = curves.map((curve) => {
|
|
714
|
-
const curve3d = r(oc.GeomLib.To3d(ax, curve.wrapped));
|
|
715
|
-
const edgeBuilder = r(new oc.BRepBuilderAPI_MakeEdge_24(curve3d));
|
|
716
|
-
return shapeTypes.createEdge(edgeBuilder.Edge());
|
|
717
|
-
});
|
|
718
|
-
gc();
|
|
719
|
-
return edges;
|
|
720
|
-
}
|
|
721
|
-
const curvesAsEdgesOnSurface = (curves, geomSurf) => {
|
|
722
|
-
const [r, gc] = shapeTypes.localGC();
|
|
723
|
-
const oc = occtBoundary.getKernel().oc;
|
|
724
|
-
const modifiedCurves = curves.map((curve) => {
|
|
725
|
-
const edgeBuilder = r(new oc.BRepBuilderAPI_MakeEdge_30(curve.wrapped, geomSurf));
|
|
726
|
-
return shapeTypes.createEdge(edgeBuilder.Edge());
|
|
727
|
-
});
|
|
728
|
-
gc();
|
|
729
|
-
return modifiedCurves;
|
|
730
|
-
};
|
|
731
|
-
const transformCurves = (curves, transformation) => {
|
|
732
|
-
const oc = occtBoundary.getKernel().oc;
|
|
733
|
-
const modifiedCurves = curves.map((curve) => {
|
|
734
|
-
if (!transformation) return curve.clone();
|
|
735
|
-
return new Curve2D(oc.GeomLib.GTransform(curve.wrapped, transformation));
|
|
736
|
-
});
|
|
737
|
-
return modifiedCurves;
|
|
738
|
-
};
|
|
739
|
-
const stretchTransform2d = (ratio, direction, origin = [0, 0]) => {
|
|
740
|
-
const oc = occtBoundary.getKernel().oc;
|
|
741
|
-
const ax = axis2d(origin, direction);
|
|
742
|
-
const transform = new oc.gp_GTrsf2d_1();
|
|
743
|
-
transform.SetAffinity(ax, ratio);
|
|
744
|
-
ax.delete();
|
|
745
|
-
return transform;
|
|
746
|
-
};
|
|
747
|
-
const translationTransform2d = (translation) => {
|
|
748
|
-
const oc = occtBoundary.getKernel().oc;
|
|
749
|
-
const [r, gc] = shapeTypes.localGC();
|
|
750
|
-
const rotation = new oc.gp_Trsf2d_1();
|
|
751
|
-
rotation.SetTranslation_1(r(vec(translation)));
|
|
752
|
-
const transform = new oc.gp_GTrsf2d_2(rotation);
|
|
753
|
-
gc();
|
|
754
|
-
return transform;
|
|
755
|
-
};
|
|
756
|
-
const mirrorTransform2d = (centerOrDirection, origin = [0, 0], mode = "center") => {
|
|
757
|
-
const oc = occtBoundary.getKernel().oc;
|
|
758
|
-
const [r, gc] = shapeTypes.localGC();
|
|
759
|
-
const rotation = new oc.gp_Trsf2d_1();
|
|
760
|
-
if (mode === "center") {
|
|
761
|
-
rotation.SetMirror_1(r(pnt(centerOrDirection)));
|
|
762
|
-
} else {
|
|
763
|
-
rotation.SetMirror_2(r(axis2d(origin, centerOrDirection)));
|
|
764
|
-
}
|
|
765
|
-
const transform = new oc.gp_GTrsf2d_2(rotation);
|
|
766
|
-
gc();
|
|
767
|
-
return transform;
|
|
768
|
-
};
|
|
769
|
-
const rotateTransform2d = (angle, center = [0, 0]) => {
|
|
770
|
-
const oc = occtBoundary.getKernel().oc;
|
|
771
|
-
const [r, gc] = shapeTypes.localGC();
|
|
772
|
-
const rotation = new oc.gp_Trsf2d_1();
|
|
773
|
-
rotation.SetRotation(r(pnt(center)), angle);
|
|
774
|
-
const transform = new oc.gp_GTrsf2d_2(rotation);
|
|
775
|
-
gc();
|
|
776
|
-
return transform;
|
|
777
|
-
};
|
|
778
|
-
const scaleTransform2d = (scaleFactor, center = [0, 0]) => {
|
|
779
|
-
const oc = occtBoundary.getKernel().oc;
|
|
780
|
-
const [r, gc] = shapeTypes.localGC();
|
|
781
|
-
const scaling = new oc.gp_Trsf2d_1();
|
|
782
|
-
scaling.SetScale(r(pnt(center)), scaleFactor);
|
|
783
|
-
const transform = new oc.gp_GTrsf2d_2(scaling);
|
|
784
|
-
gc();
|
|
785
|
-
return transform;
|
|
786
|
-
};
|
|
787
|
-
function curvesAsEdgesOnFace(curves, face, scale = "original") {
|
|
788
|
-
const [r, gc] = shapeTypes.localGC();
|
|
789
|
-
const oc = occtBoundary.getKernel().oc;
|
|
790
|
-
let geomSurf = r(oc.BRep_Tool.Surface_2(face.wrapped));
|
|
791
|
-
const bounds = faceFns.uvBounds(face);
|
|
792
|
-
let transformation = null;
|
|
793
|
-
const uAxis = r(axis2d([0, 0], [0, 1]));
|
|
794
|
-
const _vAxis = r(axis2d([0, 0], [1, 0]));
|
|
795
|
-
if (scale === "original" && faceFns.faceGeomType(face) !== "PLANE") {
|
|
796
|
-
if (faceFns.faceGeomType(face) !== "CYLINDRE")
|
|
797
|
-
return errors.err(
|
|
798
|
-
errors.validationError(
|
|
799
|
-
"UNSUPPORTED_FACE_TYPE",
|
|
800
|
-
"Only planar and cylindrical faces can be unwrapped for sketching"
|
|
801
|
-
)
|
|
802
|
-
);
|
|
803
|
-
const cylinder = r(geomSurf.get().Cylinder());
|
|
804
|
-
if (!cylinder.Direct()) {
|
|
805
|
-
geomSurf = geomSurf.get().UReversed();
|
|
806
|
-
}
|
|
807
|
-
const radius = cylinder.Radius();
|
|
808
|
-
transformation = stretchTransform2d(1 / radius, [0, 1]);
|
|
809
|
-
}
|
|
810
|
-
if (scale === "bounds") {
|
|
811
|
-
transformation = r(new oc.gp_GTrsf2d_1());
|
|
812
|
-
transformation.SetAffinity(uAxis, bounds.uMax - bounds.uMin);
|
|
813
|
-
if (bounds.uMin !== 0) {
|
|
814
|
-
const trans = r(new oc.gp_GTrsf2d_1());
|
|
815
|
-
trans.SetTranslationPart(new oc.gp_XY_2(0, -bounds.uMin));
|
|
816
|
-
transformation.Multiply(trans);
|
|
817
|
-
}
|
|
818
|
-
const vTransformation = r(new oc.gp_GTrsf2d_1());
|
|
819
|
-
vTransformation.SetAffinity(_vAxis, bounds.vMax - bounds.vMin);
|
|
820
|
-
transformation.Multiply(vTransformation);
|
|
821
|
-
if (bounds.vMin !== 0) {
|
|
822
|
-
const trans = r(new oc.gp_GTrsf2d_1());
|
|
823
|
-
trans.SetTranslationPart(r(new oc.gp_XY_2(0, -bounds.vMin)));
|
|
824
|
-
transformation.Multiply(trans);
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
const modifiedCurves = transformCurves(curves, transformation);
|
|
828
|
-
const edges = curvesAsEdgesOnSurface(modifiedCurves, geomSurf);
|
|
829
|
-
gc();
|
|
830
|
-
return errors.ok(edges);
|
|
831
|
-
}
|
|
832
|
-
function edgeToCurve(e, face) {
|
|
833
|
-
const oc = occtBoundary.getKernel().oc;
|
|
834
|
-
const r = shapeTypes.gcWithScope();
|
|
835
|
-
const adaptor = r(new oc.BRepAdaptor_Curve2d_2(e.wrapped, face.wrapped));
|
|
836
|
-
const trimmed = new oc.Geom2d_TrimmedCurve(
|
|
837
|
-
adaptor.Curve(),
|
|
838
|
-
adaptor.FirstParameter(),
|
|
839
|
-
adaptor.LastParameter(),
|
|
840
|
-
true,
|
|
841
|
-
true
|
|
842
|
-
);
|
|
843
|
-
if (curveFns.getOrientation(e) === "backward") {
|
|
844
|
-
trimmed.Reverse();
|
|
845
|
-
}
|
|
846
|
-
return new Curve2D(new oc.Handle_Geom2d_Curve_2(trimmed));
|
|
847
|
-
}
|
|
848
|
-
const viewbox = (bbox, margin = 1) => {
|
|
849
|
-
const minX = bbox.bounds[0][0] - margin;
|
|
850
|
-
const minY = -bbox.bounds[1][1] - margin;
|
|
851
|
-
return `${minX} ${minY} ${bbox.width + 2 * margin} ${bbox.height + 2 * margin}`;
|
|
852
|
-
};
|
|
853
|
-
const asSVG = (body, boundingBox, margin = 1) => {
|
|
854
|
-
const vbox = viewbox(boundingBox, margin);
|
|
855
|
-
return `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="${vbox}" fill="none" stroke="black" stroke-width="0.6%" vector-effect="non-scaling-stroke">
|
|
856
|
-
${body}
|
|
857
|
-
</svg>`;
|
|
858
|
-
};
|
|
859
|
-
function assembleWire(listOfEdges) {
|
|
860
|
-
const oc = occtBoundary.getKernel().oc;
|
|
861
|
-
const builder = new oc.BRepBuilderAPI_MakeWire_1();
|
|
862
|
-
listOfEdges.forEach((e) => {
|
|
863
|
-
builder.Add_1(e.wrapped);
|
|
864
|
-
});
|
|
865
|
-
return shapeTypes.createWire(builder.Wire());
|
|
866
|
-
}
|
|
867
|
-
class Blueprint {
|
|
868
|
-
/** Ordered 2D curve segments that compose this blueprint. */
|
|
869
|
-
curves;
|
|
870
|
-
_boundingBox;
|
|
871
|
-
_orientation;
|
|
872
|
-
_guessedOrientation;
|
|
873
|
-
/** Create a blueprint from an ordered array of 2D curves.
|
|
874
|
-
*
|
|
875
|
-
* @throws Error if the curves array is empty.
|
|
876
|
-
*/
|
|
877
|
-
constructor(curves) {
|
|
878
|
-
if (curves.length === 0) {
|
|
879
|
-
throw new Error("Blueprint requires at least one curve");
|
|
880
|
-
}
|
|
881
|
-
this.curves = curves;
|
|
882
|
-
this._boundingBox = null;
|
|
883
|
-
this._orientation = null;
|
|
884
|
-
this._guessedOrientation = null;
|
|
885
|
-
}
|
|
886
|
-
/** Release WASM resources held by the underlying curves and bounding box. */
|
|
887
|
-
delete() {
|
|
888
|
-
this.curves.forEach((c) => {
|
|
889
|
-
c.delete();
|
|
890
|
-
});
|
|
891
|
-
if (this._boundingBox) this._boundingBox.delete();
|
|
892
|
-
}
|
|
893
|
-
/** Return a deep copy of this blueprint. */
|
|
894
|
-
clone() {
|
|
895
|
-
return new Blueprint(this.curves.map((c) => c.clone()));
|
|
896
|
-
}
|
|
897
|
-
/** Return a multi-line string representation for debugging. */
|
|
898
|
-
get repr() {
|
|
899
|
-
return ["Blueprint", ...this.curves.map((c) => c.repr)].join("\n");
|
|
900
|
-
}
|
|
901
|
-
/** Compute (and cache) the axis-aligned bounding box of all curves. */
|
|
902
|
-
get boundingBox() {
|
|
903
|
-
if (!this._boundingBox) {
|
|
904
|
-
this._boundingBox = curvesBoundingBox(this.curves);
|
|
905
|
-
}
|
|
906
|
-
return this._boundingBox;
|
|
907
|
-
}
|
|
908
|
-
/** Determine the winding direction of the blueprint via the shoelace formula.
|
|
909
|
-
*
|
|
910
|
-
* @remarks Uses an approximation based on curve midpoints for non-linear
|
|
911
|
-
* segments. The result is cached after the first call.
|
|
912
|
-
*/
|
|
913
|
-
get orientation() {
|
|
914
|
-
if (this._orientation) return this._orientation;
|
|
915
|
-
if (this._guessedOrientation) return this._guessedOrientation;
|
|
916
|
-
const vertices = this.curves.flatMap((c) => {
|
|
917
|
-
if (c.geomType !== "LINE") {
|
|
918
|
-
return [c.firstPoint, c.value(0.5)];
|
|
919
|
-
}
|
|
920
|
-
return [c.firstPoint];
|
|
921
|
-
});
|
|
922
|
-
const approximateArea = vertices.map((v1, i) => {
|
|
923
|
-
const v2 = vertices[(i + 1) % vertices.length];
|
|
924
|
-
return (v2[0] - v1[0]) * (v2[1] + v1[1]);
|
|
925
|
-
}).reduce((a, b) => a + b, 0);
|
|
926
|
-
this._guessedOrientation = approximateArea > 0 ? "clockwise" : "counterClockwise";
|
|
927
|
-
return this._guessedOrientation;
|
|
928
|
-
}
|
|
929
|
-
/**
|
|
930
|
-
* Stretch the blueprint along a direction by a given ratio.
|
|
931
|
-
*
|
|
932
|
-
* @param ratio - Stretch factor (1 = unchanged).
|
|
933
|
-
* @param direction - Unit direction vector to stretch along.
|
|
934
|
-
* @param origin - Fixed point of the stretch (defaults to the origin).
|
|
935
|
-
* @returns A new stretched Blueprint.
|
|
936
|
-
*/
|
|
937
|
-
stretch(ratio, direction, origin = [0, 0]) {
|
|
938
|
-
const curves = transformCurves(this.curves, stretchTransform2d(ratio, direction, origin));
|
|
939
|
-
return new Blueprint(curves);
|
|
940
|
-
}
|
|
941
|
-
/**
|
|
942
|
-
* Uniformly scale the blueprint around a center point.
|
|
943
|
-
*
|
|
944
|
-
* @param scaleFactor - Scale multiplier (>1 enlarges, <1 shrinks).
|
|
945
|
-
* @param center - Center of scaling (defaults to the bounding box center).
|
|
946
|
-
* @returns A new scaled Blueprint.
|
|
947
|
-
*/
|
|
948
|
-
scale(scaleFactor, center) {
|
|
949
|
-
const centerPoint = center || this.boundingBox.center;
|
|
950
|
-
const curves = transformCurves(this.curves, scaleTransform2d(scaleFactor, centerPoint));
|
|
951
|
-
return new Blueprint(curves);
|
|
952
|
-
}
|
|
953
|
-
/**
|
|
954
|
-
* Rotate the blueprint by an angle in degrees.
|
|
955
|
-
*
|
|
956
|
-
* @param angle - Rotation angle in degrees (positive = counter-clockwise).
|
|
957
|
-
* @param center - Center of rotation (defaults to the origin).
|
|
958
|
-
* @returns A new rotated Blueprint.
|
|
959
|
-
*/
|
|
960
|
-
rotate(angle, center) {
|
|
961
|
-
const curves = transformCurves(this.curves, rotateTransform2d(angle * vecOps.DEG2RAD, center));
|
|
962
|
-
return new Blueprint(curves);
|
|
963
|
-
}
|
|
964
|
-
translate(xDistOrPoint, yDist = 0) {
|
|
965
|
-
const translationVector = isPoint2D(xDistOrPoint) ? xDistOrPoint : [xDistOrPoint, yDist];
|
|
966
|
-
const curves = transformCurves(this.curves, translationTransform2d(translationVector));
|
|
967
|
-
return new Blueprint(curves);
|
|
968
|
-
}
|
|
969
|
-
/**
|
|
970
|
-
* Mirror the blueprint across a point or plane.
|
|
971
|
-
*
|
|
972
|
-
* @param centerOrDirection - Mirror center (center mode) or plane normal (plane mode).
|
|
973
|
-
* @param origin - Origin for plane-mode mirroring.
|
|
974
|
-
* @param mode - `'center'` for point symmetry, `'plane'` for reflection across an axis.
|
|
975
|
-
* @returns A new mirrored Blueprint.
|
|
976
|
-
*/
|
|
977
|
-
mirror(centerOrDirection, origin = [0, 0], mode = "center") {
|
|
978
|
-
const curves = transformCurves(this.curves, mirrorTransform2d(centerOrDirection, origin, mode));
|
|
979
|
-
return new Blueprint(curves);
|
|
980
|
-
}
|
|
981
|
-
/**
|
|
982
|
-
* Project this 2D blueprint onto a 3D plane, producing a wire and metadata.
|
|
983
|
-
*
|
|
984
|
-
* @param inputPlane - Named plane (`"XY"`, `"XZ"`, etc.) or a custom Plane.
|
|
985
|
-
* @param origin - Origin offset; a number sets the offset along the plane normal.
|
|
986
|
-
* @returns Sketch data containing the projected wire and default orientation.
|
|
987
|
-
*/
|
|
988
|
-
sketchOnPlane(inputPlane, origin) {
|
|
989
|
-
const plane = inputPlane && typeof inputPlane !== "string" ? { ...inputPlane } : makePlane(inputPlane, origin);
|
|
990
|
-
const edges = curvesAsEdgesOnPlane(this.curves, plane);
|
|
991
|
-
const wire = assembleWire(edges);
|
|
992
|
-
return {
|
|
993
|
-
wire,
|
|
994
|
-
defaultOrigin: plane.origin,
|
|
995
|
-
defaultDirection: plane.zDir
|
|
996
|
-
};
|
|
997
|
-
}
|
|
998
|
-
/**
|
|
999
|
-
* Map this 2D blueprint onto a 3D face's UV surface.
|
|
1000
|
-
*
|
|
1001
|
-
* @param face - Target face to project onto.
|
|
1002
|
-
* @param scaleMode - How UV coordinates are interpreted (`'original'`, `'bounds'`, or `'native'`).
|
|
1003
|
-
* @returns Sketch data containing the wire mapped onto the face.
|
|
1004
|
-
*/
|
|
1005
|
-
sketchOnFace(face, scaleMode) {
|
|
1006
|
-
const oc = occtBoundary.getKernel().oc;
|
|
1007
|
-
const edges = errors.unwrap(curvesAsEdgesOnFace(this.curves, face, scaleMode));
|
|
1008
|
-
const wire = assembleWire(edges);
|
|
1009
|
-
oc.BRepLib.BuildCurves3d_2(wire.wrapped);
|
|
1010
|
-
const wireFixer = new oc.ShapeFix_Wire_2(wire.wrapped, face.wrapped, 1e-9);
|
|
1011
|
-
wireFixer.FixEdgeCurves();
|
|
1012
|
-
wireFixer.delete();
|
|
1013
|
-
return { wire, baseFace: face };
|
|
1014
|
-
}
|
|
1015
|
-
/**
|
|
1016
|
-
* Create a face on a target face's surface defined by this blueprint's profile.
|
|
1017
|
-
*
|
|
1018
|
-
* @param face - The face whose surface the sub-face lies on.
|
|
1019
|
-
* @param origin - Optional UV origin offset (defaults to the face center).
|
|
1020
|
-
* @returns A new Face bounded by the blueprint's profile.
|
|
1021
|
-
*/
|
|
1022
|
-
subFace(face, origin) {
|
|
1023
|
-
const originPoint = origin || [...faceFns.faceCenter(face)];
|
|
1024
|
-
const originVec3 = occtBoundary.toVec3(originPoint);
|
|
1025
|
-
const sketch = this.translate(faceFns.uvCoordinates(face, originVec3)).sketchOnFace(face, "original");
|
|
1026
|
-
return errors.unwrap(surfaceBuilders.makeFace(sketch.wire));
|
|
1027
|
-
}
|
|
1028
|
-
/**
|
|
1029
|
-
* Cut a prism-shaped hole through a solid along a face using this blueprint.
|
|
1030
|
-
*
|
|
1031
|
-
* @param shape - The solid to punch through.
|
|
1032
|
-
* @param face - The face on which the hole profile is placed.
|
|
1033
|
-
* @param options - Optional hole parameters.
|
|
1034
|
-
* @param options.height - Hole depth; `null` (default) cuts through the entire solid.
|
|
1035
|
-
* @param options.origin - UV origin on the face for the blueprint placement.
|
|
1036
|
-
* @param options.draftAngle - Taper angle in degrees (0 = straight hole).
|
|
1037
|
-
* @returns The modified shape with the hole removed.
|
|
1038
|
-
*/
|
|
1039
|
-
punchHole(shape, face, {
|
|
1040
|
-
height = null,
|
|
1041
|
-
origin = null,
|
|
1042
|
-
draftAngle = 0
|
|
1043
|
-
} = {}) {
|
|
1044
|
-
const oc = occtBoundary.getKernel().oc;
|
|
1045
|
-
const gc = shapeTypes.gcWithScope();
|
|
1046
|
-
const foundFace = errors.unwrap(helpers.getSingleFace(face, shape));
|
|
1047
|
-
const hole = this.subFace(foundFace, origin);
|
|
1048
|
-
const maker = gc(
|
|
1049
|
-
new oc.BRepFeat_MakeDPrism_1(
|
|
1050
|
-
shape.wrapped,
|
|
1051
|
-
hole.wrapped,
|
|
1052
|
-
foundFace.wrapped,
|
|
1053
|
-
draftAngle * vecOps.DEG2RAD,
|
|
1054
|
-
0,
|
|
1055
|
-
false
|
|
1056
|
-
)
|
|
1057
|
-
);
|
|
1058
|
-
if (height) {
|
|
1059
|
-
maker.Perform_1(height);
|
|
1060
|
-
} else {
|
|
1061
|
-
maker.PerformThruAll();
|
|
1062
|
-
}
|
|
1063
|
-
return errors.unwrap(faceFns.cast(maker.Shape()));
|
|
1064
|
-
}
|
|
1065
|
-
/** Convert the blueprint to an SVG path `d` attribute string. */
|
|
1066
|
-
toSVGPathD() {
|
|
1067
|
-
const r = shapeTypes.gcWithScope();
|
|
1068
|
-
const bp = this.clone().mirror([1, 0], [0, 0], "plane");
|
|
1069
|
-
const compatibleCurves = approximateAsSvgCompatibleCurve(bp.curves);
|
|
1070
|
-
const path = compatibleCurves.flatMap((c) => {
|
|
1071
|
-
return adaptedCurveToPathElem(r(c.adaptor()), c.lastPoint);
|
|
1072
|
-
});
|
|
1073
|
-
const [startX, startY] = bp.curves[0].firstPoint;
|
|
1074
|
-
return `M ${round5(startX)} ${round5(startY)} ${path.join(" ")}${bp.isClosed() ? " Z" : ""}`;
|
|
1075
|
-
}
|
|
1076
|
-
/** Wrap the SVG path data in a `<path>` element string. */
|
|
1077
|
-
toSVGPath() {
|
|
1078
|
-
return `<path d="${this.toSVGPathD()}" />`;
|
|
1079
|
-
}
|
|
1080
|
-
/**
|
|
1081
|
-
* Compute the SVG `viewBox` attribute for this blueprint.
|
|
1082
|
-
*
|
|
1083
|
-
* @param margin - Extra padding around the bounding box in drawing units.
|
|
1084
|
-
*/
|
|
1085
|
-
toSVGViewBox(margin = 1) {
|
|
1086
|
-
return viewbox(this.boundingBox, margin);
|
|
1087
|
-
}
|
|
1088
|
-
/** Return the SVG path `d` strings for this blueprint as an array. */
|
|
1089
|
-
toSVGPaths() {
|
|
1090
|
-
return [this.toSVGPathD()];
|
|
1091
|
-
}
|
|
1092
|
-
/**
|
|
1093
|
-
* Render a complete SVG document string for this blueprint.
|
|
1094
|
-
*
|
|
1095
|
-
* @param margin - Extra padding around the bounding box in drawing units.
|
|
1096
|
-
*/
|
|
1097
|
-
toSVG(margin = 1) {
|
|
1098
|
-
return asSVG(this.toSVGPath(), this.boundingBox, margin);
|
|
1099
|
-
}
|
|
1100
|
-
/** Get the start point of the first curve. */
|
|
1101
|
-
get firstPoint() {
|
|
1102
|
-
return this.curves[0].firstPoint;
|
|
1103
|
-
}
|
|
1104
|
-
/** Get the end point of the last curve. */
|
|
1105
|
-
get lastPoint() {
|
|
1106
|
-
return this.curves[this.curves.length - 1].lastPoint;
|
|
1107
|
-
}
|
|
1108
|
-
/**
|
|
1109
|
-
* Test whether a 2D point lies inside this closed blueprint.
|
|
1110
|
-
*
|
|
1111
|
-
* Uses ray-casting (intersection counting) against a segment from the point
|
|
1112
|
-
* to a location guaranteed to be outside the bounding box.
|
|
1113
|
-
*
|
|
1114
|
-
* @remarks Returns `false` for points on the boundary.
|
|
1115
|
-
* @returns `true` if the point is strictly inside the blueprint.
|
|
1116
|
-
*/
|
|
1117
|
-
isInside(point) {
|
|
1118
|
-
if (!this.boundingBox.containsPoint(point)) return false;
|
|
1119
|
-
const oc = occtBoundary.getKernel().oc;
|
|
1120
|
-
const intersector = new oc.Geom2dAPI_InterCurveCurve_1();
|
|
1121
|
-
try {
|
|
1122
|
-
const segment = make2dSegmentCurve(point, this.boundingBox.outsidePoint());
|
|
1123
|
-
let crossCounts = 0;
|
|
1124
|
-
const onCurve = this.curves.find((c) => c.isOnCurve(point));
|
|
1125
|
-
if (onCurve) return false;
|
|
1126
|
-
this.curves.forEach((c) => {
|
|
1127
|
-
if (c.boundingBox.isOut(segment.boundingBox)) return;
|
|
1128
|
-
intersector.Init_1(segment.wrapped, c.wrapped, 1e-9);
|
|
1129
|
-
crossCounts += Number(intersector.NbPoints());
|
|
1130
|
-
});
|
|
1131
|
-
return !!(crossCounts % 2);
|
|
1132
|
-
} finally {
|
|
1133
|
-
intersector.delete();
|
|
1134
|
-
}
|
|
1135
|
-
}
|
|
1136
|
-
/** Check whether the first and last points coincide (the profile is closed). */
|
|
1137
|
-
isClosed() {
|
|
1138
|
-
return helpers.samePoint(this.firstPoint, this.lastPoint);
|
|
1139
|
-
}
|
|
1140
|
-
/**
|
|
1141
|
-
* Test whether this blueprint's curves intersect with another blueprint's curves.
|
|
1142
|
-
*
|
|
1143
|
-
* @remarks Uses bounding-box pre-filtering for early rejection.
|
|
1144
|
-
*/
|
|
1145
|
-
intersects(other) {
|
|
1146
|
-
if (this.boundingBox.isOut(other.boundingBox)) return false;
|
|
1147
|
-
const oc = occtBoundary.getKernel().oc;
|
|
1148
|
-
const intersector = new oc.Geom2dAPI_InterCurveCurve_1();
|
|
1149
|
-
try {
|
|
1150
|
-
for (const myCurve of this.curves) {
|
|
1151
|
-
for (const otherCurve of other.curves) {
|
|
1152
|
-
if (myCurve.boundingBox.isOut(otherCurve.boundingBox)) continue;
|
|
1153
|
-
intersector.Init_1(myCurve.wrapped, otherCurve.wrapped, 1e-9);
|
|
1154
|
-
if (intersector.NbPoints() || intersector.NbSegments()) return true;
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
return false;
|
|
1158
|
-
} finally {
|
|
1159
|
-
intersector.delete();
|
|
1160
|
-
}
|
|
1161
|
-
}
|
|
1162
|
-
}
|
|
1163
|
-
exports.Blueprint = Blueprint;
|
|
1164
|
-
exports.BoundingBox2d = BoundingBox2d;
|
|
1165
|
-
exports.Curve2D = Curve2D;
|
|
1166
|
-
exports.approximateAsBSpline = approximateAsBSpline;
|
|
1167
|
-
exports.approximateAsSvgCompatibleCurve = approximateAsSvgCompatibleCurve;
|
|
1168
|
-
exports.asSVG = asSVG;
|
|
1169
|
-
exports.axis2d = axis2d;
|
|
1170
|
-
exports.deserializeCurve2D = deserializeCurve2D;
|
|
1171
|
-
exports.edgeToCurve = edgeToCurve;
|
|
1172
|
-
exports.isPoint2D = isPoint2D;
|
|
1173
|
-
exports.make2dArcFromCenter = make2dArcFromCenter;
|
|
1174
|
-
exports.make2dBezierCurve = make2dBezierCurve;
|
|
1175
|
-
exports.make2dCircle = make2dCircle;
|
|
1176
|
-
exports.make2dEllipse = make2dEllipse;
|
|
1177
|
-
exports.make2dEllipseArc = make2dEllipseArc;
|
|
1178
|
-
exports.make2dInerpolatedBSplineCurve = make2dInerpolatedBSplineCurve;
|
|
1179
|
-
exports.make2dSegmentCurve = make2dSegmentCurve;
|
|
1180
|
-
exports.make2dTangentArc = make2dTangentArc;
|
|
1181
|
-
exports.make2dThreePointArc = make2dThreePointArc;
|
|
1182
|
-
exports.makePlane = makePlane;
|
|
1183
|
-
exports.mirror = mirror;
|
|
1184
|
-
exports.removeDuplicatePoints = removeDuplicatePoints;
|
|
1185
|
-
exports.viewbox = viewbox;
|