diff --git a/srf/boolean.cpp b/srf/boolean.cpp index 3c0f1677..8975f5c7 100644 --- a/srf/boolean.cpp +++ b/srf/boolean.cpp @@ -13,11 +13,11 @@ void SShell::MakeFromDifferenceOf(SShell *a, SShell *b) { static Vector LineStart, LineDirection; static int ByTAlongLine(const void *av, const void *bv) { - Vector *a = (Vector *)av, - *b = (Vector *)bv; + SInter *a = (SInter *)av, + *b = (SInter *)bv; - double ta = (a->Minus(LineStart)).DivPivoting(LineDirection), - tb = (b->Minus(LineStart)).DivPivoting(LineDirection); + double ta = (a->p.Minus(LineStart)).DivPivoting(LineDirection), + tb = (b->p.Minus(LineStart)).DivPivoting(LineDirection); return (ta > tb) ? 1 : -1; } @@ -34,7 +34,7 @@ SCurve SCurve::MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB) { p = pts.NextAfter(p); for(; p; p = pts.NextAfter(p)) { - List il; + List il; ZERO(&il); // Find all the intersections with the two passed shells @@ -48,12 +48,13 @@ SCurve SCurve::MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB) { LineDirection = p->Minus(prev); qsort(il.elem, il.n, sizeof(il.elem[0]), ByTAlongLine); - Vector *pi; + SInter *pi; for(pi = il.First(); pi; pi = il.NextAfter(pi)) { - ret.pts.Add(pi); + ret.pts.Add(&(pi->p)); } } + il.Clear(); ret.pts.Add(p); prev = *p; } @@ -197,31 +198,23 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *agnst, SShell *parent, SEdgeList final; ZERO(&final); - if(I == 2) dbp("INTERBSP: %d", inter.l.n); SBspUv *interbsp = SBspUv::From(&inter); - if(I == 2) dbp("INTEROVER"); - SEdge *se; - N = 0; for(se = orig.l.First(); se; se = orig.l.NextAfter(se)) { - int c = interbsp->ClassifyEdge(se->a.ProjectXy(), se->b.ProjectXy()); + Vector ea = PointAt(se->a.x, se->a.y), + eb = PointAt(se->b.x, se->b.y); + int c = agnst->ClassifyPoint(ea.Plus(eb).ScaledBy(0.5)); - if(I == 2) dbp("edge from %.3f %.3f %.3f to %.3f %.3f %.3f", - CO(PointAt(se->a.x, se->a.y)), CO(PointAt(se->b.x, se->b.y))); - - if(c == SBspUv::OUTSIDE) { - if(I == 2) dbp(" keep"); + if(c == SShell::OUTSIDE) { final.AddEdge(se->a, se->b, se->auxA, se->auxB); - } else { - if(I == 2) dbp(" don't keep, %d", c); } - N++; } for(se = inter.l.First(); se; se = inter.l.NextAfter(se)) { + int c = bsp->ClassifyEdge(se->a.ProjectXy(), se->b.ProjectXy()); - if(I == 2) { + if(I == 4) { Vector mid = (se->a).Plus(se->b).ScaledBy(0.5); Vector arrow = (se->b).Minus(se->a); SWAP(double, arrow.x, arrow.y); @@ -229,13 +222,14 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *agnst, SShell *parent, arrow = arrow.WithMagnitude(0.03); arrow = arrow.Plus(mid); - SS.nakedEdges.AddEdge(PointAt(se->a.x, se->a.y), - PointAt(se->b.x, se->b.y)); - SS.nakedEdges.AddEdge(PointAt(mid.x, mid.y), - PointAt(arrow.x, arrow.y)); + if(c == SBspUv::INSIDE) { + SS.nakedEdges.AddEdge(PointAt(se->a.x, se->a.y), + PointAt(se->b.x, se->b.y)); + SS.nakedEdges.AddEdge(PointAt(mid.x, mid.y), + PointAt(arrow.x, arrow.y)); + } } - int c = bsp->ClassifyEdge(se->a.ProjectXy(), se->b.ProjectXy()); if(c == SBspUv::INSIDE) { final.AddEdge(se->b, se->a, se->auxA, !se->auxB); } @@ -303,7 +297,7 @@ void SShell::MakeFromBoolean(SShell *a, SShell *b, int type) { a->CopySurfacesTrimAgainst(b, this, type, true); b->CopySurfacesTrimAgainst(a, this, type, false); } else { - I = -1; + I = 0; a->CopySurfacesTrimAgainst(b, this, type, true); b->CopySurfacesTrimAgainst(a, this, type, false); } @@ -369,10 +363,6 @@ SBspUv *SBspUv::From(SEdgeList *el) { } SBspUv *SBspUv::InsertEdge(Point2d ea, Point2d eb) { - if(I == 2) { - dbp("insert edge %.3f %.3f to %.3f %.3f", ea.x, ea.y, eb.x, eb.y); - } - if(!this) { SBspUv *ret = Alloc(); ret->a = ea; @@ -434,12 +424,7 @@ int SBspUv::ClassifyPoint(Point2d p, Point2d eb) { double dp = p.Dot(n) - d; - if(I == 2 && N == 5) { - dbp("point %.3f %.3f has d=%.3f", p.x, p.y, dp); - } - if(fabs(dp) < LENGTH_EPS) { - if(I == 2 && N == 5) dbp(" on line"); SBspUv *f = this; while(f) { Point2d ba = (f->b).Minus(f->a); @@ -457,12 +442,15 @@ int SBspUv::ClassifyPoint(Point2d p, Point2d eb) { f = f->more; } // Pick arbitrarily which side to send it down, doesn't matter - return neg ? neg->ClassifyPoint(p, eb) : OUTSIDE; + int c1 = neg ? neg->ClassifyPoint(p, eb) : OUTSIDE; + int c2 = pos ? pos->ClassifyPoint(p, eb) : INSIDE; + if(c1 != c2) { + dbp("MISMATCH: %d %d %08x %08x", c1, c2, neg, pos); + } + return c1; } else if(dp > 0) { - if(I == 2 && N == 5) dbp(" pos"); return pos ? pos->ClassifyPoint(p, eb) : INSIDE; } else { - if(I == 2 && N == 5) dbp(" neg"); return neg ? neg->ClassifyPoint(p, eb) : OUTSIDE; } } diff --git a/srf/surface.h b/srf/surface.h index 544d331a..cf5a59ba 100644 --- a/srf/surface.h +++ b/srf/surface.h @@ -145,6 +145,14 @@ public: static STrimBy STrimBy::EntireCurve(SShell *shell, hSCurve hsc, bool bkwds); }; +// An intersection point between a line and a surface +class SInter { +public: + Vector p; + double dot; // between line and surface's normal + hSSurface surface; +}; + // A rational polynomial surface in Bezier form. class SSurface { public: @@ -172,7 +180,7 @@ public: void TrimFromEdgeList(SEdgeList *el); void IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB, SShell *into); - void AllPointsIntersecting(Vector a, Vector b, List *l); + void AllPointsIntersecting(Vector a, Vector b, List *l); void ClosestPointTo(Vector p, double *u, double *v); Vector PointAt(double u, double v); @@ -205,9 +213,15 @@ public: void CopySurfacesTrimAgainst(SShell *against, SShell *into, int t, bool a); void MakeIntersectionCurvesAgainst(SShell *against, SShell *into); void MakeClassifyingBsps(void); - void AllPointsIntersecting(Vector a, Vector b, List *il); + void AllPointsIntersecting(Vector a, Vector b, List *il); void CleanupAfterBoolean(void); + static const int INSIDE = 100; + static const int OUTSIDE = 200; + static const int ON_SURFACE = 300; + int ClassifyPoint(Vector p); + + void MakeFromCopyOf(SShell *a); void MakeFromTransformationOf(SShell *a, Vector trans, Quaternion q); diff --git a/srf/surfinter.cpp b/srf/surfinter.cpp index 54f08ea6..a4334230 100644 --- a/srf/surfinter.cpp +++ b/srf/surfinter.cpp @@ -56,7 +56,7 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB, // cases, just giving up for now } -void SSurface::AllPointsIntersecting(Vector a, Vector b, List *l) { +void SSurface::AllPointsIntersecting(Vector a, Vector b, List *l) { if(degm == 1 && degn == 1) { // line-plane intersection Vector p = ctrl[0][0]; @@ -74,16 +74,51 @@ void SSurface::AllPointsIntersecting(Vector a, Vector b, List *l) { Point2d puv, dummy = { 0, 0 }; ClosestPointTo(pi, &(puv.x), &(puv.y)); if(bsp->ClassifyPoint(puv, dummy) != SBspUv::OUTSIDE) { - l->Add(&pi); + SInter si; + si.p = pi; + si.dot = NormalAt(puv.x, puv.y).Dot(b.Minus(a)); + si.surface = h; + l->Add(&si); } } } } -void SShell::AllPointsIntersecting(Vector a, Vector b, List *il) { +void SShell::AllPointsIntersecting(Vector a, Vector b, List *il) { SSurface *ss; for(ss = surface.First(); ss; ss = surface.NextAfter(ss)) { ss->AllPointsIntersecting(a, b, il); } } +int SShell::ClassifyPoint(Vector p) { + List l; + ZERO(&l); + + Vector d = Vector::From(1e5, 0, 0); // direction is arbitrary + // but it does need to be a one-sided ray + AllPointsIntersecting(p, p.Plus(d), &l); + + double dmin = VERY_POSITIVE; + int ret = OUTSIDE; // no intersections means it's outside + + SInter *si; + for(si = l.First(); si; si = l.NextAfter(si)) { + double d = ((si->p).Minus(p)).Magnitude(); + + if(d < dmin) { + dmin = d; + if(d < LENGTH_EPS) { + ret = ON_SURFACE; + } else if(si->dot > 0) { + ret = INSIDE; + } else { + ret = OUTSIDE; + } + } + } + + l.Clear(); + return ret; +} + diff --git a/util.cpp b/util.cpp index 6b43f0aa..0a59e424 100644 --- a/util.cpp +++ b/util.cpp @@ -667,7 +667,7 @@ double Point2d::Dot(Point2d p) { double Point2d::DistanceToLine(Point2d p0, Point2d dp, bool segment) { double m = dp.x*dp.x + dp.y*dp.y; - if(m < 0.05) return 1e12; + if(m < LENGTH_EPS*LENGTH_EPS) return VERY_POSITIVE; // Let our line be p = p0 + t*dp, for a scalar t from 0 to 1 double t = (dp.x*(x - p0.x) + dp.y*(y - p0.y))/m;