From ab10e38d44b26de3ee64b60db0d154278d1ee8ac Mon Sep 17 00:00:00 2001 From: phkahler <14852918+phkahler@users.noreply.github.com> Date: Wed, 14 Oct 2020 20:27:56 -0400 Subject: [PATCH] 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. --- src/srf/boolean.cpp | 50 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/src/srf/boolean.cpp b/src/srf/boolean.cpp index b0b175da..7789b8de 100644 --- a/src/srf/boolean.cpp +++ b/src/srf/boolean.cpp @@ -20,6 +20,32 @@ void SShell::MakeFromIntersectionOf(SShell *a, SShell *b) { MakeFromBoolean(a, b, SSurface::CombineAs::INTERSECTION); } +// We will be inserting existing verticies into curves to split them +// todo: this is only using the ends of exact curves, and it is only +// using them to split existing curves, not new intersections. +// It resolves some issues but we could do better. We will need to +// reorder things so the surface intersection curves exist prior to +// splitting any curves at all in order to have their verticies too. +// Better still would be to get curve/surface intersection to work +// more reliably at the edges - maybe do curve/curve tests as part +// of the curve-surface intersection test. +static void FindVertsOnCurve(List *l, const SCurve *curve, SShell *sh) { + for(auto sc : sh->curve) { + if(!sc.isExact) continue; + for(int i=0; i<2; i++) { + Vector pt = sc.exact.ctrl[ i==0 ? 0 : sc.exact.deg ]; + double t; + curve->exact.ClosestPointTo(pt, &t, /*must converge=*/ false); + double d = pt.Minus(curve->exact.PointAt(t)).Magnitude(); + if((t>LENGTH_EPS) && (t<(1.0-LENGTH_EPS)) && (d < LENGTH_EPS)) { + SInter inter; + inter.p = pt; + l->Add(&inter); + } + } + } +} + //----------------------------------------------------------------------------- // Take our original pwl curve. Wherever an edge intersects a surface within // either agnstA or agnstB, split the piecewise linear element. Then refine @@ -35,12 +61,19 @@ SCurve SCurve::MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB, ret = *this; ret.pts = {}; + // First find any vertex that lies on our curve. + List vertpts = {}; + if(agnstA) + FindVertsOnCurve(&vertpts, this, agnstA); + if(agnstB) + FindVertsOnCurve(&vertpts, this, agnstB); + const SCurvePt *p = pts.First(); ssassert(p != NULL, "Cannot split an empty curve"); SCurvePt prev = *p; ret.pts.Add(p); p = pts.NextAfter(p); - + for(; p; p = pts.NextAfter(p)) { List il = {}; @@ -100,12 +133,22 @@ SCurve SCurve::MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB, pi->p = (pi->srf)->PointAt(puv); } il.RemoveTagged(); + } + // Now add any vertex that is on this segment + const Vector lineStart = prev.p; + const Vector lineDirection = (p->p).Minus(prev.p); + for(auto vtx : vertpts) { + double t = (vtx.p.Minus(lineStart)).DivProjected(lineDirection); + if((0.0 < t) && (t < 1.0)) { + il.Add(&vtx); + } + } + if(!il.IsEmpty()) { + SInter *pi; // And now sort them in order along the line. Note that we must // do that after refining, in case the refining would make two // points switch places. - const Vector lineStart = prev.p; - const Vector lineDirection = (p->p).Minus(prev.p); std::sort(il.begin(), il.end(), [&](const SInter &a, const SInter &b) { double ta = (a.p.Minus(lineStart)).DivProjected(lineDirection); double tb = (b.p.Minus(lineStart)).DivProjected(lineDirection); @@ -133,6 +176,7 @@ SCurve SCurve::MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB, ret.pts.Add(p); prev = *p; } + vertpts.Clear(); return ret; }