More coincident fixing; test for edge-on-edge, fix some gross

stupidity.

[git-p4: depot-paths = "//depot/solvespace/": change = 1915]
solver
Jonathan Westhues 2009-02-17 03:17:12 -08:00
parent c6b429b9ce
commit 577cdf2255
3 changed files with 87 additions and 39 deletions

View File

@ -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);
}

View File

@ -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);

View File

@ -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;
}
}
//-----------------------------------------------------------------------------