brepjs 18.123.0 → 18.124.0
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.cjs +6 -6
- package/dist/2d.js +6 -6
- package/dist/{adjacencyFns-YdbeJQOQ.cjs → adjacencyFns-BJ6jcQ-N.cjs} +2 -2
- package/dist/{adjacencyFns-BjQlu_ta.js → adjacencyFns-CnehwLNj.js} +2 -2
- package/dist/{blueprint-D15K1m3D.js → blueprint-BGWbpQ-w.js} +5 -5
- package/dist/{blueprint-DRdJngbT.cjs → blueprint-BSfG0Uik.cjs} +5 -5
- package/dist/{blueprintFns-BYBj5nAC.js → blueprintFns-Bt1hKlL7.js} +2 -2
- package/dist/{blueprintFns-DjhpKV3P.cjs → blueprintFns-C0-nGqlW.cjs} +2 -2
- package/dist/{blueprintSketcher-BrqUJtEJ.js → blueprintSketcher-BTGRoQ_G.js} +9 -5
- package/dist/{blueprintSketcher-BwrJrjxs.cjs → blueprintSketcher-Bd5ZS9p6.cjs} +9 -5
- package/dist/{boolean2D-DzfMpynm.cjs → boolean2D-Cr6oLsbf.cjs} +4 -4
- package/dist/{boolean2D-D68EbJPa.js → boolean2D-zZ9-qR5a.js} +4 -4
- package/dist/brepjs.cjs +27 -27
- package/dist/brepjs.js +27 -27
- package/dist/{cameraFns-BcMH4Nmd.js → cameraFns-CGMawqfi.js} +3 -3
- package/dist/{cameraFns-BoJrdghV.cjs → cameraFns-j2I9G3L_.cjs} +3 -3
- package/dist/core.cjs +2 -2
- package/dist/core.js +2 -2
- package/dist/{cornerFinder-Cbx8rGjg.cjs → cornerFinder-CPwSLgpM.cjs} +1 -1
- package/dist/{cornerFinder-DG-lilYY.js → cornerFinder-CaXBdZz9.js} +1 -1
- package/dist/{curveFns-BuqWKNvF.cjs → curveFns-BBLaLG1U.cjs} +1 -1
- package/dist/{curveFns-o05jCVtr.js → curveFns-BoHbhtqX.js} +1 -1
- package/dist/{drawFns-q-Ooe6d4.js → drawFns-BrRbAeYB.js} +12 -12
- package/dist/{drawFns-BiOerjkt.cjs → drawFns-DOkSdo8E.cjs} +12 -12
- package/dist/{faceFns-Dgiv4Jhb.js → faceFns-Ce7Fq6j5.js} +2 -2
- package/dist/{faceFns-DeTAgvu_.cjs → faceFns-UozswcwP.cjs} +2 -2
- package/dist/{healingFns-BDfz7tDU.js → healingFns-Dn0EFSHm.js} +4 -4
- package/dist/{healingFns-BvB2hVj5.cjs → healingFns-mKogaVOk.cjs} +4 -4
- package/dist/{helpers-DEN_M6k_.js → helpers-CZsK8STz.js} +6 -6
- package/dist/{helpers-B6br6W9E.cjs → helpers-DuTAcGVX.cjs} +6 -6
- package/dist/{importFns--rKvxKFm.js → importFns-CE4byS-8.js} +2 -2
- package/dist/{importFns-3RGNUkR-.cjs → importFns-Ch2YwieP.cjs} +2 -2
- package/dist/io.cjs +2 -2
- package/dist/io.js +2 -2
- package/dist/{loftFns-Cufcyuqt.cjs → loftFns-BYiUcAxk.cjs} +1 -1
- package/dist/{loftFns-BVkAi4-a.js → loftFns-DQVg8qxM.js} +1 -1
- package/dist/{measureFns-DcgzCTfb.js → measureFns-BbLLBg5q.js} +3 -3
- package/dist/{measureFns-CG3O8Mzp.cjs → measureFns-Cao9gY1L.cjs} +3 -3
- package/dist/measurement.cjs +1 -1
- package/dist/measurement.js +1 -1
- package/dist/{meshFns-DhFraVIQ.js → meshFns-B6NveFwc.js} +3 -3
- package/dist/{meshFns-BLqWnKyU.cjs → meshFns-CnYIUU9b.cjs} +3 -3
- package/dist/operations.cjs +2 -2
- package/dist/operations.js +2 -2
- package/dist/{originTrackingFns-4c8Sw44l.js → originTrackingFns-DXcggmgf.js} +2 -2
- package/dist/{originTrackingFns-7tC4igk0.cjs → originTrackingFns-uz38yuMp.cjs} +2 -2
- package/dist/{primitiveFns-D695dXsj.js → primitiveFns-CmmTWOkO.js} +4 -4
- package/dist/{primitiveFns-CFibG_KT.cjs → primitiveFns-DmEPf6av.cjs} +4 -4
- package/dist/projection.cjs +1 -1
- package/dist/projection.js +1 -1
- package/dist/query.cjs +2 -2
- package/dist/query.js +2 -2
- package/dist/{refResolveFns-u-3Okd3D.cjs → refResolveFns-CnPEaqHw.cjs} +6 -6
- package/dist/{refResolveFns-BwEBGVGr.js → refResolveFns-DqY7Cd00.js} +6 -6
- package/dist/{shapeFns-BqmsBoBy.cjs → shapeFns-BLZ1dDSz.cjs} +2 -2
- package/dist/{shapeFns-BvPYFFCd.js → shapeFns-mYczuWOy.js} +2 -2
- package/dist/shapeRef.cjs +1 -1
- package/dist/shapeRef.js +1 -1
- package/dist/{shapeTypes-D9TjxkdO.cjs → shapeTypes-DZKuRt24.cjs} +384 -13
- package/dist/{shapeTypes-u60nsijI.js → shapeTypes-bHlssytH.js} +384 -13
- package/dist/sketching.cjs +3 -3
- package/dist/sketching.js +3 -3
- package/dist/{solidBuilders-BRrbDIpu.cjs → solidBuilders-5tA9O1SM.cjs} +5 -5
- package/dist/{solidBuilders-DOafY0bO.js → solidBuilders-9s-j-ZiW.js} +5 -5
- package/dist/{surfaceBuilders-B4Yn-xOW.cjs → surfaceBuilders-0x7j0ibk.cjs} +3 -3
- package/dist/{surfaceBuilders-BnkTJeD4.js → surfaceBuilders-B7UnwkQz.js} +3 -3
- package/dist/text.cjs +2 -2
- package/dist/text.js +2 -2
- package/dist/{textBlueprints-DPldA7fV.cjs → textBlueprints-BPbIpA4-.cjs} +7 -7
- package/dist/{textBlueprints-B1JdMBb0.js → textBlueprints-DE3M-E9o.js} +7 -7
- package/dist/{textMetrics-YfWZZTIk.js → textMetrics-D3Bly1w4.js} +1 -1
- package/dist/{textMetrics-19pWWvqT.cjs → textMetrics-rRuWxQG_.cjs} +1 -1
- package/dist/{threadFns-B2YuXmV6.js → threadFns-BAjFNk2_.js} +7 -7
- package/dist/{threadFns-BDMSW3Od.cjs → threadFns-BMT6ziG_.cjs} +7 -7
- package/dist/topology.cjs +9 -9
- package/dist/topology.js +9 -9
- package/dist/{topologyQueryFns-DiuoNPqI.js → topologyQueryFns-DVhWxBwT.js} +1 -1
- package/dist/{topologyQueryFns-CUbx6oRW.cjs → topologyQueryFns-DZUpJz2p.cjs} +1 -1
- package/dist/{validityTypes-phPr-BRp.js → validityTypes-19mfqWVv.js} +1 -1
- package/dist/{validityTypes-DuiGeQhe.cjs → validityTypes-CCnRq3A9.cjs} +1 -1
- package/package.json +1 -1
|
@@ -11770,7 +11770,7 @@ function meshVertices(shape) {
|
|
|
11770
11770
|
function spinePath(spine, segments) {
|
|
11771
11771
|
const shape = asShape$1(spine);
|
|
11772
11772
|
const params = shape.node?.params;
|
|
11773
|
-
const recorded = params?.["path"] ?? params?.["points"];
|
|
11773
|
+
const recorded = params?.["path"] ?? params?.["points"] ?? params?.["ring"];
|
|
11774
11774
|
if (recorded && recorded.length >= 2) return recorded.map((p) => [
|
|
11775
11775
|
p[0],
|
|
11776
11776
|
p[1],
|
|
@@ -12117,12 +12117,21 @@ function makeSweepOps(module) {
|
|
|
12117
12117
|
}),
|
|
12118
12118
|
...sweepFamilyEntries(module),
|
|
12119
12119
|
draftPrism: (shape, face, _baseFace, height, angleDeg, fuse) => draftPrismOp(module, shape, face, height, angleDeg, fuse),
|
|
12120
|
-
buildExtrusionLaw: (profile, length, endFactor) =>
|
|
12121
|
-
|
|
12122
|
-
|
|
12123
|
-
|
|
12124
|
-
|
|
12125
|
-
|
|
12120
|
+
buildExtrusionLaw: (profile, length, endFactor) => {
|
|
12121
|
+
const law = {
|
|
12122
|
+
type: "extrusionLaw",
|
|
12123
|
+
profile,
|
|
12124
|
+
length,
|
|
12125
|
+
endFactor,
|
|
12126
|
+
Trim: (first, last, _tol) => ({
|
|
12127
|
+
...law,
|
|
12128
|
+
trimFirst: first,
|
|
12129
|
+
trimLast: last
|
|
12130
|
+
}),
|
|
12131
|
+
delete: () => {}
|
|
12132
|
+
};
|
|
12133
|
+
return law;
|
|
12134
|
+
},
|
|
12126
12135
|
loftBatch: () => notImplemented("loftBatch"),
|
|
12127
12136
|
extrudeBatch: (entries) => entries.map((e) => extrudeOp(module, e.face, e.direction, e.length))
|
|
12128
12137
|
};
|
|
@@ -14598,6 +14607,320 @@ function makeRepairOps(_module) {
|
|
|
14598
14607
|
//#endregion
|
|
14599
14608
|
//#region src/kernel/manifold/kernel2dNative.ts
|
|
14600
14609
|
var CHORD_TOL = .004;
|
|
14610
|
+
/**
|
|
14611
|
+
* Closest parameter on a native curve to (x, y), in that curve's own
|
|
14612
|
+
* parametrization: signed distance for a line, angle for a conic, [0,1] for a
|
|
14613
|
+
* bezier. Exact where the geometry allows it, and a bounded golden-section
|
|
14614
|
+
* search where it does not (general ellipse, bezier) — both of which are
|
|
14615
|
+
* unimodal in distance over a single arc, so the search cannot land on a
|
|
14616
|
+
* spurious local minimum.
|
|
14617
|
+
*/
|
|
14618
|
+
function closestParamOn(c, x, y) {
|
|
14619
|
+
if (c.k === "line") {
|
|
14620
|
+
const dx = c.p2[0] - c.p1[0];
|
|
14621
|
+
const dy = c.p2[1] - c.p1[1];
|
|
14622
|
+
const len = Math.hypot(dx, dy);
|
|
14623
|
+
if (len === 0) return 0;
|
|
14624
|
+
const t = ((x - c.p1[0]) * dx + (y - c.p1[1]) * dy) / len;
|
|
14625
|
+
return Math.min(Math.max(t, 0), len);
|
|
14626
|
+
}
|
|
14627
|
+
if (c.k === "conic") {
|
|
14628
|
+
const ru = Math.hypot(c.u[0], c.u[1]);
|
|
14629
|
+
const rv = Math.hypot(c.v[0], c.v[1]);
|
|
14630
|
+
if (Math.abs(ru - rv) < 1e-12 && Math.abs(c.u[0] * c.v[0] + c.u[1] * c.v[1]) < 1e-12) {
|
|
14631
|
+
const px = x - c.c[0];
|
|
14632
|
+
const py = y - c.c[1];
|
|
14633
|
+
return clampToArc(Math.atan2((px * c.v[0] + py * c.v[1]) / (rv * rv || 1), (px * c.u[0] + py * c.u[1]) / (ru * ru || 1)), c.a0, c.a1);
|
|
14634
|
+
}
|
|
14635
|
+
return bracketedMin(c.a0, c.a1, (t) => distSqAt(c, t, x, y), stepsFor(c, CHORD_TOL));
|
|
14636
|
+
}
|
|
14637
|
+
return bracketedMin(0, 1, (t) => distSqAt(c, t, x, y), stepsFor(c, CHORD_TOL));
|
|
14638
|
+
}
|
|
14639
|
+
/**
|
|
14640
|
+
* Global minimum of f over [lo, hi].
|
|
14641
|
+
*
|
|
14642
|
+
* Distance to an eccentric ellipse or a cubic-or-higher bezier is NOT unimodal
|
|
14643
|
+
* — a point inside a flat ellipse has one minimum per end, and a self-nearing
|
|
14644
|
+
* bezier can have several. So scan coarsely for the best sample, then refine
|
|
14645
|
+
* only inside the bracket around it, which is unimodal by construction.
|
|
14646
|
+
*/
|
|
14647
|
+
function bracketedMin(lo, hi, f, steps) {
|
|
14648
|
+
const N = Math.max(8, steps);
|
|
14649
|
+
let bestI = 0;
|
|
14650
|
+
let best = Infinity;
|
|
14651
|
+
for (let i = 0; i <= N; i++) {
|
|
14652
|
+
const v = f(lo + (hi - lo) * i / N);
|
|
14653
|
+
if (v < best) {
|
|
14654
|
+
best = v;
|
|
14655
|
+
bestI = i;
|
|
14656
|
+
}
|
|
14657
|
+
}
|
|
14658
|
+
return goldenMin(lo + (hi - lo) * Math.max(0, bestI - 1) / N, lo + (hi - lo) * Math.min(N, bestI + 1) / N, f);
|
|
14659
|
+
}
|
|
14660
|
+
/** Wrap an angle into [a0, a1] where possible, else clamp to the nearer end. */
|
|
14661
|
+
function clampToArc(a, a0, a1) {
|
|
14662
|
+
if (a1 - a0 >= 2 * Math.PI - 1e-12) return a;
|
|
14663
|
+
let t = a;
|
|
14664
|
+
while (t < a0) t += 2 * Math.PI;
|
|
14665
|
+
while (t > a0 + 2 * Math.PI) t -= 2 * Math.PI;
|
|
14666
|
+
if (t <= a1) return t;
|
|
14667
|
+
return t - a1 < a0 + 2 * Math.PI - t ? a1 : a0;
|
|
14668
|
+
}
|
|
14669
|
+
function distSqAt(c, t, x, y) {
|
|
14670
|
+
const p = nativeAt(c, t);
|
|
14671
|
+
const dx = p[0] - x;
|
|
14672
|
+
const dy = p[1] - y;
|
|
14673
|
+
return dx * dx + dy * dy;
|
|
14674
|
+
}
|
|
14675
|
+
function nativeAt(c, t) {
|
|
14676
|
+
if (c.k === "line") {
|
|
14677
|
+
const dx = c.p2[0] - c.p1[0];
|
|
14678
|
+
const dy = c.p2[1] - c.p1[1];
|
|
14679
|
+
const len = Math.hypot(dx, dy) || 1;
|
|
14680
|
+
return [c.p1[0] + dx * t / len, c.p1[1] + dy * t / len];
|
|
14681
|
+
}
|
|
14682
|
+
if (c.k === "bezier") return bezierAt(c.pts, t);
|
|
14683
|
+
return [c.c[0] + c.u[0] * Math.cos(t) + c.v[0] * Math.sin(t), c.c[1] + c.u[1] * Math.cos(t) + c.v[1] * Math.sin(t)];
|
|
14684
|
+
}
|
|
14685
|
+
/** Golden-section minimum of a unimodal f over [lo, hi]. */
|
|
14686
|
+
function goldenMin(lo, hi, f) {
|
|
14687
|
+
const phi = (Math.sqrt(5) - 1) / 2;
|
|
14688
|
+
let a = lo;
|
|
14689
|
+
let b = hi;
|
|
14690
|
+
let c1 = b - phi * (b - a);
|
|
14691
|
+
let d1 = a + phi * (b - a);
|
|
14692
|
+
for (let i = 0; i < 80 && b - a > 1e-12; i++) if (f(c1) < f(d1)) {
|
|
14693
|
+
b = d1;
|
|
14694
|
+
d1 = c1;
|
|
14695
|
+
c1 = b - phi * (b - a);
|
|
14696
|
+
} else {
|
|
14697
|
+
a = c1;
|
|
14698
|
+
c1 = d1;
|
|
14699
|
+
d1 = a + phi * (b - a);
|
|
14700
|
+
}
|
|
14701
|
+
return (a + b) / 2;
|
|
14702
|
+
}
|
|
14703
|
+
/**
|
|
14704
|
+
* Curve-curve intersection over the shim's own polyline discretization.
|
|
14705
|
+
*
|
|
14706
|
+
* Manifold has no exact curve algebra, so candidate crossings come from
|
|
14707
|
+
* segment pairs and are then refined by alternating projection: each point is
|
|
14708
|
+
* projected onto the other curve until it stops moving. That converges to a
|
|
14709
|
+
* genuine crossing when the segments really cross, and to the closest approach
|
|
14710
|
+
* when they only nearly touch — which the tolerance check then rejects.
|
|
14711
|
+
*/
|
|
14712
|
+
function intersectNative(a, b, tol) {
|
|
14713
|
+
const pa = sample(a);
|
|
14714
|
+
const pb = sample(b);
|
|
14715
|
+
const points = [];
|
|
14716
|
+
const params = [];
|
|
14717
|
+
for (let i = 0; i + 1 < pa.length; i++) for (let j = 0; j + 1 < pb.length; j++) {
|
|
14718
|
+
const hit = segSeg(pa[i], pa[i + 1], pb[j], pb[j + 1]);
|
|
14719
|
+
if (!hit) continue;
|
|
14720
|
+
let p = hit;
|
|
14721
|
+
for (let k = 0; k < 24; k++) {
|
|
14722
|
+
const qa = nativeAt(a, closestParamOn(a, p[0], p[1]));
|
|
14723
|
+
const qb = nativeAt(b, closestParamOn(b, qa[0], qa[1]));
|
|
14724
|
+
const moved = Math.hypot(qb[0] - p[0], qb[1] - p[1]);
|
|
14725
|
+
p = [(qa[0] + qb[0]) / 2, (qa[1] + qb[1]) / 2];
|
|
14726
|
+
if (moved < 1e-12) break;
|
|
14727
|
+
}
|
|
14728
|
+
const ta = closestParamOn(a, p[0], p[1]);
|
|
14729
|
+
const tb = closestParamOn(b, p[0], p[1]);
|
|
14730
|
+
const d = sub2(nativeAt(a, ta), nativeAt(b, tb));
|
|
14731
|
+
if (Math.hypot(d[0], d[1]) > Math.max(tol, 1e-7)) continue;
|
|
14732
|
+
if (points.some((q) => Math.hypot(q[0] - p[0], q[1] - p[1]) <= Math.max(tol, 1e-9))) continue;
|
|
14733
|
+
points.push(p);
|
|
14734
|
+
params.push(ta);
|
|
14735
|
+
}
|
|
14736
|
+
return dropOverlapPoints(points, params, contactRuns(a, b, tol, points, params), tol);
|
|
14737
|
+
}
|
|
14738
|
+
/**
|
|
14739
|
+
* Spans of `a` that lie on `b` within tolerance.
|
|
14740
|
+
*
|
|
14741
|
+
* Coincident spans and tangential touches never register as segment crossings —
|
|
14742
|
+
* collinear chords are parallel, and a tangency grazes without passing through.
|
|
14743
|
+
* Walking a's parameter range finds both: a run with length is a common
|
|
14744
|
+
* segment, an isolated dip is a tangential point (appended to `points`).
|
|
14745
|
+
*/
|
|
14746
|
+
function contactRuns(a, b, tol, points, params) {
|
|
14747
|
+
const { first, last } = nativeBounds(a);
|
|
14748
|
+
const M = 192;
|
|
14749
|
+
const on = [];
|
|
14750
|
+
for (let i = 0; i <= M; i++) {
|
|
14751
|
+
const p = nativeAt(a, first + (last - first) * i / M);
|
|
14752
|
+
const tb = closestParamOn(b, p[0], p[1]);
|
|
14753
|
+
on.push(Math.sqrt(distSqAt(b, tb, p[0], p[1])) <= Math.max(tol, 1e-9));
|
|
14754
|
+
}
|
|
14755
|
+
const segments = [];
|
|
14756
|
+
let runStart = -1;
|
|
14757
|
+
for (let i = 0; i <= 193; i++) {
|
|
14758
|
+
const inside = i <= M && on[i] === true;
|
|
14759
|
+
if (inside && runStart < 0) runStart = i;
|
|
14760
|
+
if (inside || runStart < 0) continue;
|
|
14761
|
+
const t0 = first + (last - first) * runStart / M;
|
|
14762
|
+
const t1 = first + (last - first) * (i - 1) / M;
|
|
14763
|
+
let physical = 0;
|
|
14764
|
+
for (let k = runStart; k < i - 1; k++) {
|
|
14765
|
+
const q0 = nativeAt(a, first + (last - first) * k / M);
|
|
14766
|
+
const q1 = nativeAt(a, first + (last - first) * (k + 1) / M);
|
|
14767
|
+
physical += Math.hypot(q1[0] - q0[0], q1[1] - q0[1]);
|
|
14768
|
+
}
|
|
14769
|
+
if (physical > Math.max(tol, CHORD_TOL)) segments.push(trimNative(a, t0, t1));
|
|
14770
|
+
else {
|
|
14771
|
+
const p = nativeAt(a, t0);
|
|
14772
|
+
if (!points.some((q) => Math.hypot(q[0] - p[0], q[1] - p[1]) <= Math.max(tol, 1e-9))) {
|
|
14773
|
+
points.push(p);
|
|
14774
|
+
params.push(t0);
|
|
14775
|
+
}
|
|
14776
|
+
}
|
|
14777
|
+
runStart = -1;
|
|
14778
|
+
}
|
|
14779
|
+
return segments;
|
|
14780
|
+
}
|
|
14781
|
+
/**
|
|
14782
|
+
* A coincident span makes the crossing search fire at every sampled chord pair
|
|
14783
|
+
* along it — 79 "intersections" for two identical circles. Those points are the
|
|
14784
|
+
* overlap, not isolated crossings, so drop any that land on a reported segment.
|
|
14785
|
+
*/
|
|
14786
|
+
function dropOverlapPoints(points, params, segments, tol) {
|
|
14787
|
+
if (segments.length === 0) return {
|
|
14788
|
+
points,
|
|
14789
|
+
params,
|
|
14790
|
+
segments
|
|
14791
|
+
};
|
|
14792
|
+
const keep = [];
|
|
14793
|
+
for (let i = 0; i < points.length; i++) {
|
|
14794
|
+
const p = points[i];
|
|
14795
|
+
if (!segments.some((seg) => {
|
|
14796
|
+
const t = closestParamOn(seg, p[0], p[1]);
|
|
14797
|
+
return Math.sqrt(distSqAt(seg, t, p[0], p[1])) <= Math.max(tol, CHORD_TOL);
|
|
14798
|
+
})) keep.push(i);
|
|
14799
|
+
}
|
|
14800
|
+
return {
|
|
14801
|
+
points: keep.map((i) => points[i]),
|
|
14802
|
+
params: keep.map((i) => params[i]),
|
|
14803
|
+
segments
|
|
14804
|
+
};
|
|
14805
|
+
}
|
|
14806
|
+
function sub2(a, b) {
|
|
14807
|
+
return [a[0] - b[0], a[1] - b[1]];
|
|
14808
|
+
}
|
|
14809
|
+
/** Intersection point of two segments, or null when they do not cross. */
|
|
14810
|
+
function segSeg(p1, p2, p3, p4) {
|
|
14811
|
+
const d1x = p2[0] - p1[0];
|
|
14812
|
+
const d1y = p2[1] - p1[1];
|
|
14813
|
+
const d2x = p4[0] - p3[0];
|
|
14814
|
+
const d2y = p4[1] - p3[1];
|
|
14815
|
+
const den = d1x * d2y - d1y * d2x;
|
|
14816
|
+
if (Math.abs(den) < 1e-15) return null;
|
|
14817
|
+
const t = ((p3[0] - p1[0]) * d2y - (p3[1] - p1[1]) * d2x) / den;
|
|
14818
|
+
const u = ((p3[0] - p1[0]) * d1y - (p3[1] - p1[1]) * d1x) / den;
|
|
14819
|
+
if (t < -1e-9 || t > 1.000000001 || u < -1e-9 || u > 1.000000001) return null;
|
|
14820
|
+
return [p1[0] + d1x * t, p1[1] + d1y * t];
|
|
14821
|
+
}
|
|
14822
|
+
/**
|
|
14823
|
+
* How many parameter steps resolve `c` to within `tol`.
|
|
14824
|
+
*
|
|
14825
|
+
* A fixed step count is wrong at both ends: wasteful on a short line and blind
|
|
14826
|
+
* to a feature narrower than one interval on a long or wiggly curve. Deriving
|
|
14827
|
+
* it from the curve's own extent — control-polygon length for a bezier, arc
|
|
14828
|
+
* length for a conic — ties the resolution to the geometry and the caller's
|
|
14829
|
+
* tolerance, the same way conicSegments already does for discretization.
|
|
14830
|
+
*
|
|
14831
|
+
* This bounds the miss, it does not remove it: any sampling scheme can still
|
|
14832
|
+
* step over a feature narrower than its own interval. Exactly resolving that
|
|
14833
|
+
* needs polynomial root-finding on the distance function, which is a different
|
|
14834
|
+
* and much larger piece of machinery than this shim carries.
|
|
14835
|
+
*/
|
|
14836
|
+
function stepsFor(c, tol) {
|
|
14837
|
+
const t = Math.max(tol, 1e-9);
|
|
14838
|
+
if (c.k === "line") return 2;
|
|
14839
|
+
let extent;
|
|
14840
|
+
if (c.k === "conic") {
|
|
14841
|
+
const r = Math.max(Math.hypot(c.u[0], c.u[1]), Math.hypot(c.v[0], c.v[1]));
|
|
14842
|
+
extent = Math.abs(c.a1 - c.a0) * r;
|
|
14843
|
+
} else {
|
|
14844
|
+
extent = 0;
|
|
14845
|
+
for (let i = 0; i + 1 < c.pts.length; i++) {
|
|
14846
|
+
const a = c.pts[i];
|
|
14847
|
+
const b = c.pts[i + 1];
|
|
14848
|
+
extent += Math.hypot(b[0] - a[0], b[1] - a[1]);
|
|
14849
|
+
}
|
|
14850
|
+
}
|
|
14851
|
+
return Math.min(2e4, Math.max(64, Math.ceil(2 * extent / t)));
|
|
14852
|
+
}
|
|
14853
|
+
/** Parameter range of a native curve, matching getCurve2dBounds. */
|
|
14854
|
+
function nativeBounds(c) {
|
|
14855
|
+
if (c.k === "conic") return {
|
|
14856
|
+
first: c.a0,
|
|
14857
|
+
last: c.a1
|
|
14858
|
+
};
|
|
14859
|
+
if (c.k === "line") return {
|
|
14860
|
+
first: 0,
|
|
14861
|
+
last: Math.hypot(c.p2[0] - c.p1[0], c.p2[1] - c.p1[1])
|
|
14862
|
+
};
|
|
14863
|
+
return {
|
|
14864
|
+
first: 0,
|
|
14865
|
+
last: 1
|
|
14866
|
+
};
|
|
14867
|
+
}
|
|
14868
|
+
/** Restrict a native curve to [from, to] in its own parametrization. */
|
|
14869
|
+
function trimNative(c, from, to) {
|
|
14870
|
+
if (c.k === "conic") return {
|
|
14871
|
+
...c,
|
|
14872
|
+
a0: from,
|
|
14873
|
+
a1: to
|
|
14874
|
+
};
|
|
14875
|
+
if (c.k === "line") {
|
|
14876
|
+
const p1 = nativeAt(c, from);
|
|
14877
|
+
const p2 = nativeAt(c, to);
|
|
14878
|
+
return {
|
|
14879
|
+
...c,
|
|
14880
|
+
p1,
|
|
14881
|
+
p2
|
|
14882
|
+
};
|
|
14883
|
+
}
|
|
14884
|
+
const right = deCasteljauRight(c.pts, from);
|
|
14885
|
+
const span = 1 - from;
|
|
14886
|
+
const t2 = span <= 1e-15 ? 1 : (to - from) / span;
|
|
14887
|
+
return {
|
|
14888
|
+
...c,
|
|
14889
|
+
pts: deCasteljauLeft(right, t2)
|
|
14890
|
+
};
|
|
14891
|
+
}
|
|
14892
|
+
function deCasteljauLeft(pts, t) {
|
|
14893
|
+
const out = [];
|
|
14894
|
+
let cur = pts.map((p) => [p[0], p[1]]);
|
|
14895
|
+
out.push(cur[0]);
|
|
14896
|
+
while (cur.length > 1) {
|
|
14897
|
+
const next = [];
|
|
14898
|
+
for (let i = 0; i + 1 < cur.length; i++) {
|
|
14899
|
+
const a = cur[i];
|
|
14900
|
+
const b = cur[i + 1];
|
|
14901
|
+
next.push([a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t]);
|
|
14902
|
+
}
|
|
14903
|
+
cur = next;
|
|
14904
|
+
out.push(cur[0]);
|
|
14905
|
+
}
|
|
14906
|
+
return out;
|
|
14907
|
+
}
|
|
14908
|
+
function deCasteljauRight(pts, t) {
|
|
14909
|
+
const out = [];
|
|
14910
|
+
let cur = pts.map((p) => [p[0], p[1]]);
|
|
14911
|
+
out.push(cur[cur.length - 1]);
|
|
14912
|
+
while (cur.length > 1) {
|
|
14913
|
+
const next = [];
|
|
14914
|
+
for (let i = 0; i + 1 < cur.length; i++) {
|
|
14915
|
+
const a = cur[i];
|
|
14916
|
+
const b = cur[i + 1];
|
|
14917
|
+
next.push([a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t]);
|
|
14918
|
+
}
|
|
14919
|
+
cur = next;
|
|
14920
|
+
out.push(cur[cur.length - 1]);
|
|
14921
|
+
}
|
|
14922
|
+
return out.reverse();
|
|
14923
|
+
}
|
|
14601
14924
|
function isNative(x) {
|
|
14602
14925
|
return !!x && typeof x === "object" && x.__nativeC2d === true;
|
|
14603
14926
|
}
|
|
@@ -15159,14 +15482,62 @@ function makeNativeKernel2DOps(module, occt) {
|
|
|
15159
15482
|
},
|
|
15160
15483
|
makeBSpline2d: (points, options) => occtOr("makeBSpline2d").makeBSpline2d(points, options),
|
|
15161
15484
|
offsetCurve2d: (c, offset) => occtOr("offsetCurve2d").offsetCurve2d(toOcct(c), offset),
|
|
15162
|
-
intersectCurves2d: (c1, c2, tol) =>
|
|
15163
|
-
|
|
15164
|
-
|
|
15485
|
+
intersectCurves2d: (c1, c2, tol) => {
|
|
15486
|
+
if (!isNative(c1) || !isNative(c2)) return occtOr("intersectCurves2d").intersectCurves2d(toOcct(c1), toOcct(c2), tol);
|
|
15487
|
+
const { points, segments } = intersectNative(asC(c1), asC(c2), tol);
|
|
15488
|
+
return {
|
|
15489
|
+
points: points.map((p) => [p[0], p[1]]),
|
|
15490
|
+
segments
|
|
15491
|
+
};
|
|
15492
|
+
},
|
|
15493
|
+
projectPointOnCurve2d: (c, x, y) => {
|
|
15494
|
+
if (!isNative(c)) return occtOr("projectPointOnCurve2d").projectPointOnCurve2d(toOcct(c), x, y);
|
|
15495
|
+
const n = asC(c);
|
|
15496
|
+
const param = closestParamOn(n, x, y);
|
|
15497
|
+
return {
|
|
15498
|
+
param,
|
|
15499
|
+
distance: Math.sqrt(distSqAt(n, param, x, y))
|
|
15500
|
+
};
|
|
15501
|
+
},
|
|
15502
|
+
distanceBetweenCurves2d: (c1, c2, s1, e1, s2, e2) => {
|
|
15503
|
+
if (!isNative(c1) || !isNative(c2)) return occtOr("distanceBetweenCurves2d").distanceBetweenCurves2d(toOcct(c1), toOcct(c2), s1, e1, s2, e2);
|
|
15504
|
+
const a = asC(c1);
|
|
15505
|
+
const b = asC(c2);
|
|
15506
|
+
const N = 256;
|
|
15507
|
+
let best = Infinity;
|
|
15508
|
+
for (let i = 0; i <= N; i++) {
|
|
15509
|
+
const p = nativeAt(a, s1 + (e1 - s1) * i / N);
|
|
15510
|
+
const q = closestParamOn(b, p[0], p[1]);
|
|
15511
|
+
best = Math.min(best, Math.sqrt(distSqAt(b, Math.min(Math.max(q, Math.min(s2, e2)), Math.max(s2, e2)), p[0], p[1])));
|
|
15512
|
+
}
|
|
15513
|
+
return best;
|
|
15514
|
+
},
|
|
15165
15515
|
approximateCurve2dAsBSpline: (c, tol, cont, maxSeg) => occtOr("approximateCurve2dAsBSpline").approximateCurve2dAsBSpline(toOcct(c), tol, cont, maxSeg),
|
|
15166
15516
|
decomposeBSpline2dToBeziers: (c) => occtOr("decomposeBSpline2dToBeziers").decomposeBSpline2dToBeziers(toOcct(c)),
|
|
15167
|
-
serializeCurve2d: (c) =>
|
|
15168
|
-
|
|
15169
|
-
|
|
15517
|
+
serializeCurve2d: (c) => isNative(c) ? JSON.stringify({
|
|
15518
|
+
__nativeC2d: 1,
|
|
15519
|
+
c: asC(c)
|
|
15520
|
+
}) : occtOr("serializeCurve2d").serializeCurve2d(toOcct(c)),
|
|
15521
|
+
deserializeCurve2d: (data) => {
|
|
15522
|
+
if (data.startsWith("{\"__nativeC2d\"")) return {
|
|
15523
|
+
__nativeC2d: true,
|
|
15524
|
+
...JSON.parse(data).c
|
|
15525
|
+
};
|
|
15526
|
+
return occtOr("deserializeCurve2d").deserializeCurve2d(data);
|
|
15527
|
+
},
|
|
15528
|
+
splitCurve2d: (c, params) => {
|
|
15529
|
+
if (!isNative(c)) return occtOr("splitCurve2d").splitCurve2d(toOcct(c), params);
|
|
15530
|
+
const n = asC(c);
|
|
15531
|
+
const { first, last } = nativeBounds(n);
|
|
15532
|
+
const cuts = [
|
|
15533
|
+
first,
|
|
15534
|
+
...params.filter((t) => t > first && t < last).sort((a, b) => a - b),
|
|
15535
|
+
last
|
|
15536
|
+
];
|
|
15537
|
+
const out = [];
|
|
15538
|
+
for (let i = 0; i + 1 < cuts.length; i++) out.push(trimNative(n, cuts[i] ?? 0, cuts[i + 1] ?? 0));
|
|
15539
|
+
return out;
|
|
15540
|
+
},
|
|
15170
15541
|
getCurve2dEllipseData: (c) => occtOr("getCurve2dEllipseData").getCurve2dEllipseData(toOcct(c)),
|
|
15171
15542
|
getCurve2dBezierPoles: (c) => occtOr("getCurve2dBezierPoles").getCurve2dBezierPoles(toOcct(c)),
|
|
15172
15543
|
getCurve2dBezierDegree: (c) => occtOr("getCurve2dBezierDegree").getCurve2dBezierDegree(toOcct(c)),
|