Additional poking at Booleans. At least this is a halfway rational
way to think about the cases; I'm classifying the regions to the left and right of each edge, and keeping the edges if those regions (2d, surfaces) classify different. Still screws up with edge-on-edge intersections; but if I make the surface intersection stuff handle that, then might be more straightforward to use that info. [git-p4: depot-paths = "//depot/solvespace/": change = 1914]solver
parent
90842131ff
commit
c6b429b9ce
204
srf/boolean.cpp
204
srf/boolean.cpp
|
@ -126,6 +126,94 @@ void SSurface::TrimFromEdgeList(SEdgeList *el) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For each edge, we record the membership of the regions to its left and
|
||||||
|
// right, which we call the "in direction" and "out direction" (wrt its
|
||||||
|
// outer normal)
|
||||||
|
#define INDIR (0)
|
||||||
|
#define OUTDIR (8)
|
||||||
|
// Regions of interest are the other shell itself, the coincident faces of the
|
||||||
|
// shell (same or opposite normal) and the original surface.
|
||||||
|
#define SHELL (0)
|
||||||
|
#define COINCIDENT_SAME (1)
|
||||||
|
#define COINCIDENT_OPPOSITE (2)
|
||||||
|
#define ORIG (3)
|
||||||
|
// Macro for building bit to test
|
||||||
|
#define INSIDE(reg, dir) (1 << ((reg)+(dir)))
|
||||||
|
|
||||||
|
static bool KeepRegion(int type, bool opA, int tag, int dir)
|
||||||
|
{
|
||||||
|
bool inShell = (tag & INSIDE(SHELL, dir)) != 0,
|
||||||
|
inSame = (tag & INSIDE(COINCIDENT_SAME, dir)) != 0,
|
||||||
|
inOpp = (tag & INSIDE(COINCIDENT_OPPOSITE, dir)) != 0,
|
||||||
|
inOrig = (tag & INSIDE(ORIG, dir)) != 0;
|
||||||
|
|
||||||
|
bool inFace = inSame || inOpp;
|
||||||
|
|
||||||
|
// If these are correct, then they should be independent of inShell
|
||||||
|
// if inFace is true.
|
||||||
|
if(!inOrig) return false;
|
||||||
|
switch(type) {
|
||||||
|
case SShell::AS_UNION:
|
||||||
|
if(opA) {
|
||||||
|
return (!inShell && !inFace);
|
||||||
|
} else {
|
||||||
|
return (!inShell && !inFace) || inSame;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SShell::AS_DIFFERENCE:
|
||||||
|
if(opA) {
|
||||||
|
return (!inShell && !inFace);
|
||||||
|
} else {
|
||||||
|
return (inShell && !inFace) || inSame;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: oops();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static bool KeepEdge(int type, bool opA, int tag)
|
||||||
|
{
|
||||||
|
bool keepIn = KeepRegion(type, opA, tag, INDIR),
|
||||||
|
keepOut = KeepRegion(type, opA, tag, OUTDIR);
|
||||||
|
|
||||||
|
// If the regions to the left and right of this edge are both in or both
|
||||||
|
// out, then this edge is not useful and should be discarded.
|
||||||
|
if(keepIn && !keepOut) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int TagByClassifiedEdge(int bspclass, int reg)
|
||||||
|
{
|
||||||
|
switch(bspclass) {
|
||||||
|
case SBspUv::INSIDE:
|
||||||
|
return INSIDE(reg, OUTDIR) | INSIDE(reg, INDIR);
|
||||||
|
|
||||||
|
case SBspUv::OUTSIDE:
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case SBspUv::EDGE_PARALLEL:
|
||||||
|
return INSIDE(reg, OUTDIR);
|
||||||
|
|
||||||
|
case SBspUv::EDGE_ANTIPARALLEL:
|
||||||
|
return INSIDE(reg, INDIR);
|
||||||
|
|
||||||
|
default: oops();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DBPEDGE(int tag) {
|
||||||
|
dbp("edge: indir %s %s %s %s ; outdir %s %s %s %s",
|
||||||
|
(tag & INSIDE(SHELL, INDIR)) ? "shell" : "",
|
||||||
|
(tag & INSIDE(COINCIDENT_SAME, INDIR)) ? "coinc-same" : "",
|
||||||
|
(tag & INSIDE(COINCIDENT_OPPOSITE, INDIR)) ? "coinc-opp" : "",
|
||||||
|
(tag & INSIDE(ORIG, INDIR)) ? "orig" : "",
|
||||||
|
(tag & INSIDE(SHELL, OUTDIR)) ? "shell" : "",
|
||||||
|
(tag & INSIDE(COINCIDENT_SAME, OUTDIR)) ? "coinc-same" : "",
|
||||||
|
(tag & INSIDE(COINCIDENT_OPPOSITE, OUTDIR)) ? "coinc-opp" : "",
|
||||||
|
(tag & INSIDE(ORIG, OUTDIR)) ? "orig" : "");
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Trim this surface against the specified shell, in the way that's appropriate
|
// 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
|
// for the specified Boolean operation type (and which operand we are). We
|
||||||
|
@ -150,6 +238,11 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *agnst, SShell *parent,
|
||||||
ret.trim.Add(&stn);
|
ret.trim.Add(&stn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(type == SShell::AS_DIFFERENCE && !opA) {
|
||||||
|
// The second operand of a Boolean difference gets turned inside out
|
||||||
|
ret.Reverse();
|
||||||
|
}
|
||||||
|
|
||||||
// Build up our original trim polygon
|
// Build up our original trim polygon
|
||||||
SEdgeList orig;
|
SEdgeList orig;
|
||||||
ZERO(&orig);
|
ZERO(&orig);
|
||||||
|
@ -162,8 +255,8 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *agnst, SShell *parent,
|
||||||
SEdgeList sameNormal, oppositeNormal;
|
SEdgeList sameNormal, oppositeNormal;
|
||||||
ZERO(&sameNormal);
|
ZERO(&sameNormal);
|
||||||
ZERO(&oppositeNormal);
|
ZERO(&oppositeNormal);
|
||||||
agnst->MakeCoincidentEdgesInto(this, true, &sameNormal);
|
agnst->MakeCoincidentEdgesInto(&ret, true, &sameNormal);
|
||||||
agnst->MakeCoincidentEdgesInto(this, false, &oppositeNormal);
|
agnst->MakeCoincidentEdgesInto(&ret, false, &oppositeNormal);
|
||||||
// and build the trees for quick in-polygon testing
|
// and build the trees for quick in-polygon testing
|
||||||
SBspUv *sameBsp = SBspUv::From(&sameNormal);
|
SBspUv *sameBsp = SBspUv::From(&sameNormal);
|
||||||
SBspUv *oppositeBsp = SBspUv::From(&oppositeNormal);
|
SBspUv *oppositeBsp = SBspUv::From(&oppositeNormal);
|
||||||
|
@ -196,16 +289,19 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *agnst, SShell *parent,
|
||||||
if(c != SBspUv::OUTSIDE) {
|
if(c != SBspUv::OUTSIDE) {
|
||||||
Vector ta = Vector::From(0, 0, 0);
|
Vector ta = Vector::From(0, 0, 0);
|
||||||
Vector tb = Vector::From(0, 0, 0);
|
Vector tb = Vector::From(0, 0, 0);
|
||||||
ClosestPointTo(a, &(ta.x), &(ta.y));
|
ret.ClosestPointTo(a, &(ta.x), &(ta.y));
|
||||||
ClosestPointTo(b, &(tb.x), &(tb.y));
|
ret.ClosestPointTo(b, &(tb.x), &(tb.y));
|
||||||
|
|
||||||
Vector tn = NormalAt(ta.x, ta.y);
|
Vector tn = ret.NormalAt(ta.x, ta.y);
|
||||||
Vector sn = ss->NormalAt(auv.x, auv.y);
|
Vector sn = ss->NormalAt(auv.x, auv.y);
|
||||||
|
|
||||||
if((tn.Cross(b.Minus(a))).Dot(sn) > 0) {
|
bool bkwds = false;
|
||||||
inter.AddEdge(ta, tb, sc->h.v, 0);
|
if((tn.Cross(b.Minus(a))).Dot(sn) < 0) bkwds = !bkwds;
|
||||||
} else {
|
if(type == SShell::AS_DIFFERENCE && !opA) bkwds = !bkwds;
|
||||||
|
if(bkwds) {
|
||||||
inter.AddEdge(tb, ta, sc->h.v, 1);
|
inter.AddEdge(tb, ta, sc->h.v, 1);
|
||||||
|
} else {
|
||||||
|
inter.AddEdge(ta, tb, sc->h.v, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -225,36 +321,31 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *agnst, SShell *parent,
|
||||||
|
|
||||||
// Get the midpoint of this edge
|
// Get the midpoint of this edge
|
||||||
Point2d am = (auv.Plus(buv)).ScaledBy(0.5);
|
Point2d am = (auv.Plus(buv)).ScaledBy(0.5);
|
||||||
Vector pt = PointAt(am.x, am.y);
|
Vector pt = ret.PointAt(am.x, am.y);
|
||||||
// and the outer normal from the trim polygon (within the surface)
|
// and the outer normal from the trim polygon (within the surface)
|
||||||
Vector n = NormalAt(am.x, am.y);
|
Vector n = ret.NormalAt(am.x, am.y);
|
||||||
Vector ea = PointAt(auv.x, auv.y),
|
Vector ea = ret.PointAt(auv.x, auv.y),
|
||||||
eb = PointAt(buv.x, buv.y);
|
eb = ret.PointAt(buv.x, buv.y);
|
||||||
Vector ptout = n.Cross((eb.Minus(ea)));
|
Vector ptout = n.Cross((eb.Minus(ea)));
|
||||||
|
|
||||||
int c_shell = agnst->ClassifyPoint(pt, ptout);
|
int c_shell = agnst->ClassifyPoint(pt, ptout);
|
||||||
|
|
||||||
bool keep;
|
int tag = 0;
|
||||||
if(c_opp != SBspUv::OUTSIDE) {
|
tag |= INSIDE(ORIG, INDIR);
|
||||||
// Edge lies on coincident (opposite normals) surface of agnst
|
tag |= TagByClassifiedEdge(c_same, COINCIDENT_SAME);
|
||||||
keep = (c_opp == SBspUv::OUTSIDE ) ||
|
tag |= TagByClassifiedEdge(c_opp, COINCIDENT_OPPOSITE);
|
||||||
(c_opp == SBspUv::EDGE_ANTIPARALLEL );
|
|
||||||
|
|
||||||
} else if(c_same != SBspUv::OUTSIDE) {
|
if(c_shell == SShell::INSIDE) {
|
||||||
// Edge lies on coincident (same normals) surface of agnst
|
tag |= INSIDE(SHELL, INDIR) | INSIDE(SHELL, OUTDIR);
|
||||||
if(opA) {
|
} else if(c_shell == SShell::OUTSIDE) {
|
||||||
keep = true;
|
tag |= 0;
|
||||||
} else {
|
} else if(c_shell == SShell::ON_PARALLEL) {
|
||||||
keep = false;
|
tag |= INSIDE(SHELL, INDIR);
|
||||||
}
|
} else if(c_shell == SShell::ON_ANTIPARALLEL) {
|
||||||
|
tag |= INSIDE(SHELL, OUTDIR);
|
||||||
} else {
|
|
||||||
// Edge does not lie on a coincident surface
|
|
||||||
keep = (c_shell == SShell::OUTSIDE ) ||
|
|
||||||
(c_shell == SShell::ON_ANTIPARALLEL );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(keep) {
|
if(KeepEdge(type, opA, tag)) {
|
||||||
final.AddEdge(se->a, se->b, se->auxA, se->auxB);
|
final.AddEdge(se->a, se->b, se->auxA, se->auxB);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -267,25 +358,38 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *agnst, SShell *parent,
|
||||||
int c_same = sameBsp->ClassifyEdge(auv, buv);
|
int c_same = sameBsp->ClassifyEdge(auv, buv);
|
||||||
int c_opp = oppositeBsp->ClassifyEdge(auv, buv);
|
int c_opp = oppositeBsp->ClassifyEdge(auv, buv);
|
||||||
|
|
||||||
bool keep;
|
int tag = 0;
|
||||||
if(c_opp != SBspUv::OUTSIDE) {
|
tag |= TagByClassifiedEdge(c_this, ORIG);
|
||||||
keep = (c_this == SBspUv::INSIDE);
|
tag |= TagByClassifiedEdge(c_same, COINCIDENT_SAME);
|
||||||
} else if(c_same != SBspUv::OUTSIDE) {
|
tag |= TagByClassifiedEdge(c_opp, COINCIDENT_OPPOSITE);
|
||||||
if(opA) {
|
|
||||||
keep = false;
|
if(type == SShell::AS_DIFFERENCE && !opA) {
|
||||||
} else {
|
// The second operand of a difference gets turned inside out
|
||||||
keep = (c_this == SBspUv::INSIDE);
|
tag |= INSIDE(SHELL, INDIR);
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
keep = (c_this == SBspUv::INSIDE);
|
tag |= INSIDE(SHELL, OUTDIR);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(keep) {
|
if(I == 0) DBPEDGE(tag);
|
||||||
|
|
||||||
|
if(KeepEdge(type, opA, tag)) {
|
||||||
final.AddEdge(se->b, se->a, se->auxA, !se->auxB);
|
final.AddEdge(se->b, se->a, se->auxA, !se->auxB);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(se = final.l.First(); se; se = final.l.NextAfter(se)) {
|
// 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.
|
||||||
|
final.l.ClearTags();
|
||||||
|
int i, j;
|
||||||
|
for(i = 0; i < final.l.n; i++) {
|
||||||
|
se = &(final.l.elem[i]);
|
||||||
|
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)) {
|
||||||
|
set->tag = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(I == 0) {
|
if(I == 0) {
|
||||||
Vector mid = (se->a).Plus(se->b).ScaledBy(0.5);
|
Vector mid = (se->a).Plus(se->b).ScaledBy(0.5);
|
||||||
Vector arrow = (se->b).Minus(se->a);
|
Vector arrow = (se->b).Minus(se->a);
|
||||||
|
@ -294,13 +398,15 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *agnst, SShell *parent,
|
||||||
arrow = arrow.WithMagnitude(0.03);
|
arrow = arrow.WithMagnitude(0.03);
|
||||||
arrow = arrow.Plus(mid);
|
arrow = arrow.Plus(mid);
|
||||||
|
|
||||||
SS.nakedEdges.AddEdge(PointAt(se->a.x, se->a.y),
|
SS.nakedEdges.AddEdge(ret.PointAt(se->a.x, se->a.y),
|
||||||
PointAt(se->b.x, se->b.y));
|
ret.PointAt(se->b.x, se->b.y));
|
||||||
SS.nakedEdges.AddEdge(PointAt(mid.x, mid.y),
|
SS.nakedEdges.AddEdge(ret.PointAt(mid.x, mid.y),
|
||||||
PointAt(arrow.x, arrow.y));
|
ret.PointAt(arrow.x, arrow.y));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
final.l.RemoveTagged();
|
||||||
|
|
||||||
|
// Use our reassembled edges to trim the new surface.
|
||||||
ret.TrimFromEdgeList(&final);
|
ret.TrimFromEdgeList(&final);
|
||||||
|
|
||||||
sameNormal.Clear();
|
sameNormal.Clear();
|
||||||
|
@ -355,10 +461,10 @@ void SShell::MakeFromBoolean(SShell *a, SShell *b, int type) {
|
||||||
// Generate the intersection curves for each surface in A against all
|
// Generate the intersection curves for each surface in A against all
|
||||||
// the surfaces in B (which is all of the intersection curves).
|
// the surfaces in B (which is all of the intersection curves).
|
||||||
a->MakeIntersectionCurvesAgainst(b, this);
|
a->MakeIntersectionCurvesAgainst(b, this);
|
||||||
|
|
||||||
if(a->surface.n == 0 || b->surface.n == 0) {
|
I = 100;
|
||||||
|
if(b->surface.n == 0 || a->surface.n == 0) {
|
||||||
// Then trim and copy the surfaces
|
// Then trim and copy the surfaces
|
||||||
I = 100;
|
|
||||||
a->CopySurfacesTrimAgainst(b, this, type, true);
|
a->CopySurfacesTrimAgainst(b, this, type, true);
|
||||||
b->CopySurfacesTrimAgainst(a, this, type, false);
|
b->CopySurfacesTrimAgainst(a, this, type, false);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -617,32 +617,53 @@ void SSurface::TriangulateInto(SShell *shell, SMesh *sm) {
|
||||||
|
|
||||||
SPolygon poly;
|
SPolygon poly;
|
||||||
ZERO(&poly);
|
ZERO(&poly);
|
||||||
if(!el.AssemblePolygon(&poly, NULL, true)) {
|
if(el.AssemblePolygon(&poly, NULL, true)) {
|
||||||
|
int i, start = sm->l.n;
|
||||||
|
poly.UvTriangulateInto(sm);
|
||||||
|
|
||||||
|
STriMeta meta = { face, color };
|
||||||
|
for(i = start; i < sm->l.n; i++) {
|
||||||
|
STriangle *st = &(sm->l.elem[i]);
|
||||||
|
st->meta = meta;
|
||||||
|
st->an = NormalAt(st->a.x, st->a.y);
|
||||||
|
st->bn = NormalAt(st->b.x, st->b.y);
|
||||||
|
st->cn = NormalAt(st->c.x, st->c.y);
|
||||||
|
st->a = PointAt(st->a.x, st->a.y);
|
||||||
|
st->b = PointAt(st->b.x, st->b.y);
|
||||||
|
st->c = PointAt(st->c.x, st->c.y);
|
||||||
|
// Works out that my chosen contour direction is inconsistent with
|
||||||
|
// the triangle direction, sigh.
|
||||||
|
st->FlipNormal();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
dbp("failed to assemble polygon to trim nurbs surface in uv space");
|
dbp("failed to assemble polygon to trim nurbs surface in uv space");
|
||||||
}
|
}
|
||||||
|
|
||||||
int i, start = sm->l.n;
|
|
||||||
poly.UvTriangulateInto(sm);
|
|
||||||
|
|
||||||
STriMeta meta = { face, color };
|
|
||||||
for(i = start; i < sm->l.n; i++) {
|
|
||||||
STriangle *st = &(sm->l.elem[i]);
|
|
||||||
st->meta = meta;
|
|
||||||
st->an = NormalAt(st->a.x, st->a.y);
|
|
||||||
st->bn = NormalAt(st->b.x, st->b.y);
|
|
||||||
st->cn = NormalAt(st->c.x, st->c.y);
|
|
||||||
st->a = PointAt(st->a.x, st->a.y);
|
|
||||||
st->b = PointAt(st->b.x, st->b.y);
|
|
||||||
st->c = PointAt(st->c.x, st->c.y);
|
|
||||||
// Works out that my chosen contour direction is inconsistent with
|
|
||||||
// the triangle direction, sigh.
|
|
||||||
st->FlipNormal();
|
|
||||||
}
|
|
||||||
|
|
||||||
el.Clear();
|
el.Clear();
|
||||||
poly.Clear();
|
poly.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Reverse the parametrisation of one of our dimensions, which flips the
|
||||||
|
// normal. We therefore must reverse all our trim curves too. The uv
|
||||||
|
// coordinates change, but trim curves are stored as xyz so nothing happens
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void SSurface::Reverse(void) {
|
||||||
|
int i, j;
|
||||||
|
for(i = 0; i < (degm+1)/2; i++) {
|
||||||
|
for(j = 0; j <= degn; j++) {
|
||||||
|
SWAP(Vector, ctrl[i][j], ctrl[degm-i][j]);
|
||||||
|
SWAP(double, weight[i][j], weight[degm-i][j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STrimBy *stb;
|
||||||
|
for(stb = trim.First(); stb; stb = trim.NextAfter(stb)) {
|
||||||
|
stb->backwards = !stb->backwards;
|
||||||
|
SWAP(Vector, stb->start, stb->finish);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SSurface::Clear(void) {
|
void SSurface::Clear(void) {
|
||||||
trim.Clear();
|
trim.Clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,6 +195,7 @@ public:
|
||||||
void MakeEdgesInto(SShell *shell, SEdgeList *sel, bool asUv);
|
void MakeEdgesInto(SShell *shell, SEdgeList *sel, bool asUv);
|
||||||
void MakeClassifyingBsp(SShell *shell);
|
void MakeClassifyingBsp(SShell *shell);
|
||||||
|
|
||||||
|
void Reverse(void);
|
||||||
void Clear(void);
|
void Clear(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue