Skip to content

Commit ab10e38

Browse files
committed
Add vertexes to curve intersection list in addition to surface intersections.
Sometimes a vertex can be used to split a curve where surface intersections can't. Those unsplit curves can cause boolean failures.
1 parent 6b5936b commit ab10e38

File tree

1 file changed

+47
-3
lines changed

1 file changed

+47
-3
lines changed

src/srf/boolean.cpp

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,32 @@ void SShell::MakeFromIntersectionOf(SShell *a, SShell *b) {
2020
MakeFromBoolean(a, b, SSurface::CombineAs::INTERSECTION);
2121
}
2222

23+
// We will be inserting existing verticies into curves to split them
24+
// todo: this is only using the ends of exact curves, and it is only
25+
// using them to split existing curves, not new intersections.
26+
// It resolves some issues but we could do better. We will need to
27+
// reorder things so the surface intersection curves exist prior to
28+
// splitting any curves at all in order to have their verticies too.
29+
// Better still would be to get curve/surface intersection to work
30+
// more reliably at the edges - maybe do curve/curve tests as part
31+
// of the curve-surface intersection test.
32+
static void FindVertsOnCurve(List<SInter> *l, const SCurve *curve, SShell *sh) {
33+
for(auto sc : sh->curve) {
34+
if(!sc.isExact) continue;
35+
for(int i=0; i<2; i++) {
36+
Vector pt = sc.exact.ctrl[ i==0 ? 0 : sc.exact.deg ];
37+
double t;
38+
curve->exact.ClosestPointTo(pt, &t, /*must converge=*/ false);
39+
double d = pt.Minus(curve->exact.PointAt(t)).Magnitude();
40+
if((t>LENGTH_EPS) && (t<(1.0-LENGTH_EPS)) && (d < LENGTH_EPS)) {
41+
SInter inter;
42+
inter.p = pt;
43+
l->Add(&inter);
44+
}
45+
}
46+
}
47+
}
48+
2349
//-----------------------------------------------------------------------------
2450
// Take our original pwl curve. Wherever an edge intersects a surface within
2551
// either agnstA or agnstB, split the piecewise linear element. Then refine
@@ -35,12 +61,19 @@ SCurve SCurve::MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB,
3561
ret = *this;
3662
ret.pts = {};
3763

64+
// First find any vertex that lies on our curve.
65+
List<SInter> vertpts = {};
66+
if(agnstA)
67+
FindVertsOnCurve(&vertpts, this, agnstA);
68+
if(agnstB)
69+
FindVertsOnCurve(&vertpts, this, agnstB);
70+
3871
const SCurvePt *p = pts.First();
3972
ssassert(p != NULL, "Cannot split an empty curve");
4073
SCurvePt prev = *p;
4174
ret.pts.Add(p);
4275
p = pts.NextAfter(p);
43-
76+
4477
for(; p; p = pts.NextAfter(p)) {
4578
List<SInter> il = {};
4679

@@ -100,12 +133,22 @@ SCurve SCurve::MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB,
100133
pi->p = (pi->srf)->PointAt(puv);
101134
}
102135
il.RemoveTagged();
136+
}
137+
// Now add any vertex that is on this segment
138+
const Vector lineStart = prev.p;
139+
const Vector lineDirection = (p->p).Minus(prev.p);
140+
for(auto vtx : vertpts) {
141+
double t = (vtx.p.Minus(lineStart)).DivProjected(lineDirection);
142+
if((0.0 < t) && (t < 1.0)) {
143+
il.Add(&vtx);
144+
}
145+
}
146+
if(!il.IsEmpty()) {
147+
SInter *pi;
103148

104149
// And now sort them in order along the line. Note that we must
105150
// do that after refining, in case the refining would make two
106151
// points switch places.
107-
const Vector lineStart = prev.p;
108-
const Vector lineDirection = (p->p).Minus(prev.p);
109152
std::sort(il.begin(), il.end(), [&](const SInter &a, const SInter &b) {
110153
double ta = (a.p.Minus(lineStart)).DivProjected(lineDirection);
111154
double tb = (b.p.Minus(lineStart)).DivProjected(lineDirection);
@@ -133,6 +176,7 @@ SCurve SCurve::MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB,
133176
ret.pts.Add(p);
134177
prev = *p;
135178
}
179+
vertpts.Clear();
136180
return ret;
137181
}
138182

0 commit comments

Comments
 (0)