From 577cdf225525de5c06a6b3fa81dfb1cabc316908 Mon Sep 17 00:00:00 2001 From: Jonathan Westhues Date: Tue, 17 Feb 2009 03:17:12 -0800 Subject: [PATCH] More coincident fixing; test for edge-on-edge, fix some gross stupidity. [git-p4: depot-paths = "//depot/solvespace/": change = 1915] --- srf/boolean.cpp | 76 ++++++++++++++++++++++++++++++----------------- srf/surface.h | 8 +++-- srf/surfinter.cpp | 42 +++++++++++++++++++------- 3 files changed, 87 insertions(+), 39 deletions(-) diff --git a/srf/boolean.cpp b/srf/boolean.cpp index 1f6483b..414d1d2 100644 --- a/srf/boolean.cpp +++ b/srf/boolean.cpp @@ -193,10 +193,10 @@ static int TagByClassifiedEdge(int bspclass, int reg) return 0; case SBspUv::EDGE_PARALLEL: - return INSIDE(reg, OUTDIR); + return INSIDE(reg, INDIR); case SBspUv::EDGE_ANTIPARALLEL: - return INSIDE(reg, INDIR); + return INSIDE(reg, OUTDIR); default: oops(); } @@ -214,6 +214,23 @@ void DBPEDGE(int tag) { (tag & INSIDE(ORIG, OUTDIR)) ? "orig" : ""); } +void DEBUGEDGELIST(SEdgeList *sel, SSurface *surf) { + SEdge *se; + for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) { + Vector mid = (se->a).Plus(se->b).ScaledBy(0.5); + Vector arrow = (se->b).Minus(se->a); + SWAP(double, arrow.x, arrow.y); + arrow.x *= -1; + arrow = arrow.WithMagnitude(0.03); + arrow = arrow.Plus(mid); + + SS.nakedEdges.AddEdge(surf->PointAt(se->a.x, se->a.y), + surf->PointAt(se->b.x, se->b.y)); + SS.nakedEdges.AddEdge(surf->PointAt(mid.x, mid.y), + surf->PointAt(arrow.x, arrow.y)); + } +} + //----------------------------------------------------------------------------- // Trim this surface against the specified shell, in the way that's appropriate // for the specified Boolean operation type (and which operand we are). We @@ -243,11 +260,14 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *agnst, SShell *parent, ret.Reverse(); } - // Build up our original trim polygon + // Build up our original trim polygon; remember the coordinates could + // be changed if we just flipped the surface normal. SEdgeList orig; ZERO(&orig); ret.MakeEdgesInto(into, &orig, true); ret.trim.Clear(); + // which means that we can't necessarily use the old BSP... + SBspUv *origBsp = SBspUv::From(&orig); // Find any surfaces from the other shell that are coincident with ours, // and get the intersection polygons, grouping surfaces with the same @@ -295,7 +315,10 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *agnst, SShell *parent, Vector tn = ret.NormalAt(ta.x, ta.y); Vector sn = ss->NormalAt(auv.x, auv.y); - bool bkwds = false; + // We are subtracting the portion of our surface that + // lies in the shell, so the in-plane edge normal should + // point opposite to the surface normal. + bool bkwds = true; if((tn.Cross(b.Minus(a))).Dot(sn) < 0) bkwds = !bkwds; if(type == SShell::AS_DIFFERENCE && !opA) bkwds = !bkwds; if(bkwds) { @@ -339,10 +362,16 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *agnst, SShell *parent, tag |= INSIDE(SHELL, INDIR) | INSIDE(SHELL, OUTDIR); } else if(c_shell == SShell::OUTSIDE) { tag |= 0; - } else if(c_shell == SShell::ON_PARALLEL) { + } else if(c_shell == SShell::SURF_PARALLEL) { tag |= INSIDE(SHELL, INDIR); - } else if(c_shell == SShell::ON_ANTIPARALLEL) { + } else if(c_shell == SShell::SURF_ANTIPARALLEL) { tag |= INSIDE(SHELL, OUTDIR); + } else if(c_shell == SShell::EDGE_PARALLEL) { + tag |= INSIDE(SHELL, INDIR); + } else if(c_shell == SShell::EDGE_ANTIPARALLEL) { + tag |= INSIDE(SHELL, OUTDIR); + } else if(c_shell == SShell::EDGE_TANGENT) { + continue; } if(KeepEdge(type, opA, tag)) { @@ -354,7 +383,7 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *agnst, SShell *parent, Point2d auv = (se->a).ProjectXy(), buv = (se->b).ProjectXy(); - int c_this = bsp->ClassifyEdge(auv, buv); + int c_this = origBsp->ClassifyEdge(auv, buv); int c_same = sameBsp->ClassifyEdge(auv, buv); int c_opp = oppositeBsp->ClassifyEdge(auv, buv); @@ -370,15 +399,12 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *agnst, SShell *parent, tag |= INSIDE(SHELL, OUTDIR); } - if(I == 0) DBPEDGE(tag); - if(KeepEdge(type, opA, tag)) { - final.AddEdge(se->b, se->a, se->auxA, !se->auxB); + final.AddEdge(se->a, se->b, se->auxA, se->auxB); } } - // If our surface intersects an edge, then it will intersect two surfaces - // from the shell at that edge, so we'll get a duplicate. Cull those. + // Cull extraneous edges; duplicates or anti-parallel pairs final.l.ClearTags(); int i, j; for(i = 0; i < final.l.n; i++) { @@ -386,26 +412,22 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *agnst, SShell *parent, for(j = i+1; j < final.l.n; j++) { SEdge *set = &(final.l.elem[j]); if((set->a).Equals(se->a) && (set->b).Equals(se->b)) { + // Two parallel edges exist; so keep only the first one. This + // can happen if our surface intersects the shell at an edge, + // so that we get two copies of the intersection edge. + set->tag = 1; + } + if((set->a).Equals(se->b) && (set->b).Equals(se->a)) { + // Two anti-parallel edges exist; so keep neither. + se->tag = 1; set->tag = 1; } } - - if(I == 0) { - Vector mid = (se->a).Plus(se->b).ScaledBy(0.5); - Vector arrow = (se->b).Minus(se->a); - SWAP(double, arrow.x, arrow.y); - arrow.x *= -1; - arrow = arrow.WithMagnitude(0.03); - arrow = arrow.Plus(mid); - - SS.nakedEdges.AddEdge(ret.PointAt(se->a.x, se->a.y), - ret.PointAt(se->b.x, se->b.y)); - SS.nakedEdges.AddEdge(ret.PointAt(mid.x, mid.y), - ret.PointAt(arrow.x, arrow.y)); - } } final.l.RemoveTagged(); +// if(I == 1) DEBUGEDGELIST(&final, &ret); + // Use our reassembled edges to trim the new surface. ret.TrimFromEdgeList(&final); @@ -468,8 +490,8 @@ void SShell::MakeFromBoolean(SShell *a, SShell *b, int type) { a->CopySurfacesTrimAgainst(b, this, type, true); b->CopySurfacesTrimAgainst(a, this, type, false); } else { - I = 0; a->CopySurfacesTrimAgainst(b, this, type, true); + I = 0; b->CopySurfacesTrimAgainst(a, this, type, false); } diff --git a/srf/surface.h b/srf/surface.h index 827f6a0..c390fb0 100644 --- a/srf/surface.h +++ b/srf/surface.h @@ -224,8 +224,12 @@ public: static const int INSIDE = 100; static const int OUTSIDE = 200; - static const int ON_PARALLEL = 300; - static const int ON_ANTIPARALLEL = 400; + static const int SURF_PARALLEL = 300; + static const int SURF_ANTIPARALLEL = 400; + static const int EDGE_PARALLEL = 500; + static const int EDGE_ANTIPARALLEL = 600; + static const int EDGE_TANGENT = 700; + int ClassifyPoint(Vector p, Vector out); diff --git a/srf/surfinter.cpp b/srf/surfinter.cpp index c63903b..7d55a9e 100644 --- a/srf/surfinter.cpp +++ b/srf/surfinter.cpp @@ -110,7 +110,9 @@ int SShell::ClassifyPoint(Vector p, Vector pout) { srand(0); - int ret, cnt = 0; + int ret, cnt = 0, edge_inters; + double edge_dotp[2]; + for(;;) { // Cast a ray in a random direction (two-sided so that we test if // the point lies on a surface, but use only one side for in/out @@ -122,6 +124,7 @@ int SShell::ClassifyPoint(Vector p, Vector pout) { double dmin = VERY_POSITIVE; ret = OUTSIDE; // no intersections means it's outside bool onEdge = false; + edge_inters = 0; SInter *si; for(si = l.First(); si; si = l.NextAfter(si)) { @@ -133,18 +136,24 @@ int SShell::ClassifyPoint(Vector p, Vector pout) { double d = ((si->p).Minus(p)).Magnitude(); + // Handle edge-on-edge + if(d < LENGTH_EPS && si->onEdge && edge_inters < 2) { + edge_dotp[edge_inters] = (si->surfNormal).Dot(pout); + edge_inters++; + } + if(d < dmin) { dmin = d; if(d < LENGTH_EPS) { - // Lies on the surface + // Edge-on-face (unless edge-on-edge above supercedes) if((si->surfNormal).Dot(pout) > 0) { - ret = ON_PARALLEL; + ret = SURF_PARALLEL; } else { - ret = ON_ANTIPARALLEL; + ret = SURF_ANTIPARALLEL; } } else { - // Does not lie on this surface; inside or out, depending - // on the normal + // Edge does not lie on surface; either strictly inside + // or strictly outside if((si->surfNormal).Dot(ray) > 0) { ret = INSIDE; } else { @@ -157,15 +166,28 @@ int SShell::ClassifyPoint(Vector p, Vector pout) { l.Clear(); // If the point being tested lies exactly on an edge of the shell, - // then our ray always lies on edge, and that's okay. - if(ret == ON_PARALLEL || ret == ON_ANTIPARALLEL || !onEdge) break; - if(cnt++ > 10) { + // then our ray always lies on edge, and that's okay. Otherwise + // try again in a different random direction. + if((edge_inters == 2) || !onEdge) break; + if(cnt++ > 20) { dbp("can't find a ray that doesn't hit on edge!"); break; } } - return ret; + if(edge_inters == 2) { + double tol = 1e-3; + + if(edge_dotp[0] > -tol && edge_dotp[1] > -tol) { + return EDGE_PARALLEL; + } else if(edge_dotp[0] < tol && edge_dotp[1] < tol) { + return EDGE_ANTIPARALLEL; + } else { + return EDGE_TANGENT; + } + } else { + return ret; + } } //-----------------------------------------------------------------------------