diff --git a/srf/surface.cpp b/srf/surface.cpp index 05ca22a..3443300 100644 --- a/srf/surface.cpp +++ b/srf/surface.cpp @@ -261,6 +261,26 @@ void SSurface::MakeEdgesInto(SShell *shell, SEdgeList *sel, bool asUv, } } +//----------------------------------------------------------------------------- +// Compute the exact tangent to the intersection curve between two surfaces, +// by taking the cross product of the surface normals. We choose the direction +// of this tangent so that its dot product with dir is positive. +//----------------------------------------------------------------------------- +Vector SSurface::ExactSurfaceTangentAt(Vector p, SSurface *srfA, SSurface *srfB, + Vector dir) +{ + Point2d puva, puvb; + srfA->ClosestPointTo(p, &puva); + srfB->ClosestPointTo(p, &puvb); + Vector ts = (srfA->NormalAt(puva)).Cross( + (srfB->NormalAt(puvb))); + ts = ts.WithMagnitude(1); + if(ts.Dot(dir) < 0) { + ts = ts.ScaledBy(-1); + } + return ts; +} + //----------------------------------------------------------------------------- // Report our trim curves. If a trim curve is exact and sbl is not null, then // add its exact form to sbl. Otherwise, add its piecewise linearization to @@ -293,6 +313,76 @@ void SSurface::MakeSectionEdgesInto(SShell *shell, keep_aft.SplitAt(tf, &keep_bef, &junk_aft); sbl->l.Add(&keep_bef); + } else if(sbl && !sel && !sc->isExact) { + // We must approximate this trim curve, as piecewise cubic sections. + SSurface *srfA = shell->surface.FindById(sc->surfA), + *srfB = shell->surface.FindById(sc->surfB); + + Vector s = stb->backwards ? stb->finish : stb->start, + f = stb->backwards ? stb->start : stb->finish; + + int sp, fp; + for(sp = 0; sp < sc->pts.n; sp++) { + if(s.Equals(sc->pts.elem[sp].p)) break; + } + if(sp >= sc->pts.n) return; + for(fp = sp; fp < sc->pts.n; fp++) { + if(f.Equals(sc->pts.elem[fp].p)) break; + } + if(fp >= sc->pts.n) return; + // So now the curve we want goes from elem[sp] to elem[fp] + + while(sp < fp) { + // Initially, we'll try approximating the entire trim curve + // as a single Bezier segment + int fpt = fp; + + for(;;) { + // So construct a cubic Bezier with the correct endpoints + // and tangents for the current span. + Vector st = sc->pts.elem[sp].p, + ft = sc->pts.elem[fpt].p, + sf = ft.Minus(st); + double m = sf.Magnitude() / 3; + + Vector stan = ExactSurfaceTangentAt(st, srfA, srfB, sf), + ftan = ExactSurfaceTangentAt(ft, srfA, srfB, sf); + + SBezier sb = SBezier::From(st, + st.Plus (stan.WithMagnitude(m)), + ft.Minus(ftan.WithMagnitude(m)), + ft); + + // And test how much this curve deviates from the + // intermediate points (if any). + int i; + bool tooFar = false; + for(i = sp + 1; i <= (fpt - 1); i++) { + Vector p = sc->pts.elem[i].p; + double t; + sb.ClosestPointTo(p, &t, false); + Vector pp = sb.PointAt(t); + if((pp.Minus(p)).Magnitude() > SS.ChordTolMm()/2) { + tooFar = true; + break; + } + } + + if(tooFar) { + // Deviates by too much, so try a shorter span + fpt--; + continue; + } else { + // Okay, so use this piece and break. + sbl->l.Add(&sb); + break; + } + } + + // And continue interpolating, starting wherever the curve + // we just generated finishes. + sp = fpt; + } } else { if(sel) MakeTrimEdgesInto(sel, false, sc, stb); } diff --git a/srf/surface.h b/srf/surface.h index 5fd0880..0de3b08 100644 --- a/srf/surface.h +++ b/srf/surface.h @@ -278,6 +278,8 @@ public: void MakeTrimEdgesInto(SEdgeList *sel, bool asUv, SCurve *sc, STrimBy *stb); void MakeEdgesInto(SShell *shell, SEdgeList *sel, bool asUv, SShell *useCurvesFrom=NULL); + Vector ExactSurfaceTangentAt(Vector p, SSurface *srfA, SSurface *srfB, + Vector dir); void MakeSectionEdgesInto(SShell *shell, SEdgeList *sel, SBezierList *sbl); void MakeClassifyingBsp(SShell *shell, SShell *useCurvesFrom); double ChordToleranceForEdge(Vector a, Vector b); diff --git a/srf/surfinter.cpp b/srf/surfinter.cpp index da8f966..89f67da 100644 --- a/srf/surfinter.cpp +++ b/srf/surfinter.cpp @@ -349,6 +349,12 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB, spl.l.ClearTags(); spl.l.elem[0].tag = 1; spl.l.RemoveTagged(); + + // Our chord tolerance is whatever the user specified + double maxtol = SS.ChordTolMm(); + int maxsteps = max(300, SS.maxSegments*3); + + // The curve starts at our starting point. SCurvePt padd; ZERO(&padd); padd.vertex = true; @@ -357,11 +363,11 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB, Point2d pa, pb; Vector np, npc; + bool fwd; // Better to start with a too-small step, so that we don't miss // features of the curve entirely. - bool fwd; - double tol, step = SS.ChordTolMm(); - for(a = 0; a < 100; a++) { + double tol, step = maxtol; + for(a = 0; a < maxsteps; a++) { ClosestPointTo(start, &pa); b->ClosestPointTo(start, &pb); @@ -378,10 +384,8 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB, } } - int i = 0; - do { - if(i++ > 20) break; - + int i; + for(i = 0; i < 20; i++) { Vector dp = nb.Cross(na); if(!fwd) dp = dp.ScaledBy(-1); dp = dp.WithMagnitude(step); @@ -390,24 +394,30 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB, npc = ClosestPointOnThisAndSurface(b, np); tol = (npc.Minus(np)).Magnitude(); - if(tol > SS.ChordTolMm()*0.8) { + if(tol > maxtol*0.8) { step *= 0.95; } else { step /= 0.95; } - } while(tol > SS.ChordTolMm() || tol < SS.ChordTolMm()/2); + + if((tol < maxtol) && (tol > maxtol/2)) { + // If we meet the chord tolerance test, and we're + // not too fine, then we break out. + break; + } + } SPoint *sp; for(sp = spl.l.First(); sp; sp = spl.l.NextAfter(sp)) { if((sp->p).OnLineSegment(start, npc, 2*SS.ChordTolMm())) { sp->tag = 1; - a = 1000; + a = maxsteps; npc = sp->p; } } padd.p = npc; - padd.vertex = (a == 1000); + padd.vertex = (a == maxsteps); sc.pts.Add(&padd); start = npc; @@ -419,7 +429,6 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB, SCurve split = sc.MakeCopySplitAgainst(agnstA, agnstB, this, b); sc.Clear(); into->curve.AddAndAssignId(&split); - break; } spl.Clear(); }