Revamp the edge classification for Booleans. I no longer make a
separate polygon of coincident (with same or opposite normal) faces; I instead test all the edges against the other shell, and have extended the classify-against-shell stuff to handle those cases. And the normals are now perturbed a bit numerically, to either side of the edge, to distinguish tangency from a coincident surface. This seems to work fairly well, although things still tend to fail when the piecewise linear tolerance is too coarse. [git-p4: depot-paths = "//depot/solvespace/": change = 1964]
This commit is contained in:
parent
24891c0141
commit
ae35b3595c
@ -93,7 +93,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
|
|||||||
{ 1, "&Vertical\tV", MNU_VERTICAL, 'V', mCon },
|
{ 1, "&Vertical\tV", MNU_VERTICAL, 'V', mCon },
|
||||||
{ 1, NULL, 0, NULL },
|
{ 1, NULL, 0, NULL },
|
||||||
{ 1, "&On Point / Curve / Plane\tO", MNU_ON_ENTITY, 'O', mCon },
|
{ 1, "&On Point / Curve / Plane\tO", MNU_ON_ENTITY, 'O', mCon },
|
||||||
{ 1, "E&qual Length / Radius\tQ", MNU_EQUAL, 'Q', mCon },
|
{ 1, "E&qual Length / Radius / Angle\tQ", MNU_EQUAL, 'Q', mCon },
|
||||||
{ 1, "Length Ra&tio\tZ", MNU_RATIO, 'Z', mCon },
|
{ 1, "Length Ra&tio\tZ", MNU_RATIO, 'Z', mCon },
|
||||||
{ 1, "At &Midpoint\tM", MNU_AT_MIDPOINT, 'M', mCon },
|
{ 1, "At &Midpoint\tM", MNU_AT_MIDPOINT, 'M', mCon },
|
||||||
{ 1, "S&ymmetric\tY", MNU_SYMMETRIC, 'Y', mCon },
|
{ 1, "S&ymmetric\tY", MNU_SYMMETRIC, 'Y', mCon },
|
||||||
|
@ -199,6 +199,13 @@ void SolveSpace::AfterNewFile(void) {
|
|||||||
// and the naked edges
|
// and the naked edges
|
||||||
nakedEdges.Clear();
|
nakedEdges.Clear();
|
||||||
|
|
||||||
|
// GenerateAll() expects the view to be valid, because it uses that to
|
||||||
|
// fill in default values for extrusion depths etc. (which won't matter
|
||||||
|
// here, but just don't let it work on garbage)
|
||||||
|
SS.GW.offset = Vector::From(0, 0, 0);
|
||||||
|
SS.GW.projRight = Vector::From(1, 0, 0);
|
||||||
|
SS.GW.projUp = Vector::From(0, 1, 0);
|
||||||
|
|
||||||
ReloadAllImported();
|
ReloadAllImported();
|
||||||
GenerateAll(-1, -1);
|
GenerateAll(-1, -1);
|
||||||
|
|
||||||
|
247
srf/boolean.cpp
247
srf/boolean.cpp
@ -158,26 +158,12 @@ void SSurface::TrimFromEdgeList(SEdgeList *el) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For each edge, we record the membership of the regions to its left and
|
static bool KeepRegion(int type, bool opA, int shell, int orig)
|
||||||
// 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,
|
bool inShell = (shell == SShell::INSIDE),
|
||||||
inSame = (tag & INSIDE(COINCIDENT_SAME, dir)) != 0,
|
inSame = (shell == SShell::COINC_SAME),
|
||||||
inOpp = (tag & INSIDE(COINCIDENT_OPPOSITE, dir)) != 0,
|
inOpp = (shell == SShell::COINC_OPP),
|
||||||
inOrig = (tag & INSIDE(ORIG, dir)) != 0;
|
inOrig = (orig == SShell::INSIDE);
|
||||||
|
|
||||||
bool inFace = inSame || inOpp;
|
bool inFace = inSame || inOpp;
|
||||||
|
|
||||||
@ -204,10 +190,12 @@ static bool KeepRegion(int type, bool opA, int tag, int dir)
|
|||||||
default: oops();
|
default: oops();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static bool KeepEdge(int type, bool opA, int tag)
|
static bool KeepEdge(int type, bool opA,
|
||||||
|
int indir_shell, int outdir_shell,
|
||||||
|
int indir_orig, int outdir_orig)
|
||||||
{
|
{
|
||||||
bool keepIn = KeepRegion(type, opA, tag, INDIR),
|
bool keepIn = KeepRegion(type, opA, indir_shell, indir_orig),
|
||||||
keepOut = KeepRegion(type, opA, tag, OUTDIR);
|
keepOut = KeepRegion(type, opA, outdir_shell, outdir_orig);
|
||||||
|
|
||||||
// If the regions to the left and right of this edge are both in or both
|
// 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.
|
// out, then this edge is not useful and should be discarded.
|
||||||
@ -215,37 +203,33 @@ static bool KeepEdge(int type, bool opA, int tag)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int TagByClassifiedEdge(int bspclass, int reg)
|
static void TagByClassifiedEdge(int bspclass, int *indir, int *outdir)
|
||||||
{
|
{
|
||||||
switch(bspclass) {
|
switch(bspclass) {
|
||||||
case SBspUv::INSIDE:
|
case SBspUv::INSIDE:
|
||||||
return INSIDE(reg, OUTDIR) | INSIDE(reg, INDIR);
|
*indir = SShell::INSIDE;
|
||||||
|
*outdir = SShell::INSIDE;
|
||||||
|
break;
|
||||||
|
|
||||||
case SBspUv::OUTSIDE:
|
case SBspUv::OUTSIDE:
|
||||||
return 0;
|
*indir = SShell::OUTSIDE;
|
||||||
|
*outdir = SShell::OUTSIDE;
|
||||||
|
break;
|
||||||
|
|
||||||
case SBspUv::EDGE_PARALLEL:
|
case SBspUv::EDGE_PARALLEL:
|
||||||
return INSIDE(reg, INDIR);
|
*indir = SShell::INSIDE;
|
||||||
|
*outdir = SShell::OUTSIDE;
|
||||||
|
break;
|
||||||
|
|
||||||
case SBspUv::EDGE_ANTIPARALLEL:
|
case SBspUv::EDGE_ANTIPARALLEL:
|
||||||
return INSIDE(reg, OUTDIR);
|
*indir = SShell::OUTSIDE;
|
||||||
|
*outdir = SShell::INSIDE;
|
||||||
|
break;
|
||||||
|
|
||||||
default: oops();
|
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" : "");
|
|
||||||
}
|
|
||||||
|
|
||||||
void DEBUGEDGELIST(SEdgeList *sel, SSurface *surf) {
|
void DEBUGEDGELIST(SEdgeList *sel, SSurface *surf) {
|
||||||
dbp("print %d edges", sel->l.n);
|
dbp("print %d edges", sel->l.n);
|
||||||
SEdge *se;
|
SEdge *se;
|
||||||
@ -264,6 +248,54 @@ void DEBUGEDGELIST(SEdgeList *sel, SSurface *surf) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *REGION(int d) {
|
||||||
|
switch(d) {
|
||||||
|
case SShell::INSIDE: return "inside";
|
||||||
|
case SShell::OUTSIDE: return "outside";
|
||||||
|
case SShell::COINC_SAME: return "same";
|
||||||
|
case SShell::COINC_OPP: return "opp";
|
||||||
|
default: return "xxx";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SSurface::EdgeNormalsWithinSurface(Point2d auv, Point2d buv,
|
||||||
|
Vector *pt,
|
||||||
|
Vector *enin, Vector *enout,
|
||||||
|
Vector *surfn,
|
||||||
|
DWORD auxA, SShell *shell)
|
||||||
|
{
|
||||||
|
// the midpoint of the edge
|
||||||
|
Point2d muv = (auv.Plus(buv)).ScaledBy(0.5);
|
||||||
|
// a vector parallel to the edge
|
||||||
|
Point2d abuv = buv.Minus(auv).WithMagnitude(0.01);
|
||||||
|
// the edge's inner normal
|
||||||
|
Point2d enuv = abuv.Normal();
|
||||||
|
|
||||||
|
*pt = PointAt(muv);
|
||||||
|
*surfn = NormalAt(muv.x, muv.y);
|
||||||
|
|
||||||
|
// If this edge just approximates a curve, then refine our midpoint so
|
||||||
|
// so that it actually lies on that curve too. Otherwise stuff like
|
||||||
|
// point-on-face tests will fail, since the point won't actually lie
|
||||||
|
// on the other face.
|
||||||
|
hSCurve hc = { auxA };
|
||||||
|
SCurve *sc = shell->curve.FindById(hc);
|
||||||
|
if(sc->isExact && sc->exact.deg != 1) {
|
||||||
|
double t;
|
||||||
|
sc->exact.ClosestPointTo(*pt, &t, false);
|
||||||
|
*pt = sc->exact.PointAt(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the inner and outer normals of this edge (within the srf),
|
||||||
|
// in xyz space. These are not necessarily antiparallel, if the
|
||||||
|
// surface is curved.
|
||||||
|
Vector pin = PointAt(muv.Plus(enuv)),
|
||||||
|
pout = PointAt(muv.Minus(enuv));
|
||||||
|
*enin = pin.Minus(*pt),
|
||||||
|
*enout = pout.Minus(*pt);
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// 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
|
||||||
@ -294,7 +326,8 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *agnst, SShell *parent,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build up our original trim polygon; remember the coordinates could
|
// Build up our original trim polygon; remember the coordinates could
|
||||||
// be changed if we just flipped the surface normal.
|
// be changed if we just flipped the surface normal, and we are using
|
||||||
|
// the split curves (not the original curves).
|
||||||
SEdgeList orig;
|
SEdgeList orig;
|
||||||
ZERO(&orig);
|
ZERO(&orig);
|
||||||
ret.MakeEdgesInto(into, &orig, true);
|
ret.MakeEdgesInto(into, &orig, true);
|
||||||
@ -302,22 +335,6 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *agnst, SShell *parent,
|
|||||||
// which means that we can't necessarily use the old BSP...
|
// which means that we can't necessarily use the old BSP...
|
||||||
SBspUv *origBsp = SBspUv::From(&orig);
|
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
|
|
||||||
// and with opposite normal separately.
|
|
||||||
SEdgeList sameNormal, oppositeNormal;
|
|
||||||
ZERO(&sameNormal);
|
|
||||||
ZERO(&oppositeNormal);
|
|
||||||
agnst->MakeCoincidentEdgesInto(&ret, true, &sameNormal, into);
|
|
||||||
agnst->MakeCoincidentEdgesInto(&ret, false, &oppositeNormal, into);
|
|
||||||
// and cull parallel or anti-parallel pairs, which may occur if multiple
|
|
||||||
// surfaces are coincident with ours
|
|
||||||
sameNormal.CullExtraneousEdges();
|
|
||||||
oppositeNormal.CullExtraneousEdges();
|
|
||||||
// and build the trees for quick in-polygon testing
|
|
||||||
SBspUv *sameBsp = SBspUv::From(&sameNormal);
|
|
||||||
SBspUv *oppositeBsp = SBspUv::From(&oppositeNormal);
|
|
||||||
|
|
||||||
// And now intersect the other shell against us
|
// And now intersect the other shell against us
|
||||||
SEdgeList inter;
|
SEdgeList inter;
|
||||||
ZERO(&inter);
|
ZERO(&inter);
|
||||||
@ -376,42 +393,22 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *agnst, SShell *parent,
|
|||||||
Point2d auv = (se->a).ProjectXy(),
|
Point2d auv = (se->a).ProjectXy(),
|
||||||
buv = (se->b).ProjectXy();
|
buv = (se->b).ProjectXy();
|
||||||
|
|
||||||
int c_same = sameBsp->ClassifyEdge(auv, buv);
|
Vector pt, enin, enout, surfn;
|
||||||
int c_opp = oppositeBsp->ClassifyEdge(auv, buv);
|
ret.EdgeNormalsWithinSurface(auv, buv, &pt, &enin, &enout, &surfn,
|
||||||
|
se->auxA, into);
|
||||||
|
|
||||||
// Get the midpoint of this edge
|
int indir_shell, outdir_shell, indir_orig, outdir_orig;
|
||||||
Point2d am = (auv.Plus(buv)).ScaledBy(0.5);
|
|
||||||
Vector pt = ret.PointAt(am.x, am.y);
|
|
||||||
// and the outer normal from the trim polygon (within the surface)
|
|
||||||
Vector surf_n = ret.NormalAt(am.x, am.y);
|
|
||||||
Vector ea = ret.PointAt(auv.x, auv.y),
|
|
||||||
eb = ret.PointAt(buv.x, buv.y);
|
|
||||||
Vector edge_n = surf_n.Cross((eb.Minus(ea)));
|
|
||||||
|
|
||||||
int c_shell = agnst->ClassifyPoint(pt, edge_n, surf_n);
|
indir_orig = SShell::INSIDE;
|
||||||
|
outdir_orig = SShell::OUTSIDE;
|
||||||
|
|
||||||
int tag = 0;
|
agnst->ClassifyEdge(&indir_shell, &outdir_shell,
|
||||||
tag |= INSIDE(ORIG, INDIR);
|
ret.PointAt(auv), ret.PointAt(buv), pt,
|
||||||
tag |= TagByClassifiedEdge(c_same, COINCIDENT_SAME);
|
enin, enout, surfn);
|
||||||
tag |= TagByClassifiedEdge(c_opp, COINCIDENT_OPPOSITE);
|
|
||||||
|
|
||||||
if(c_shell == SShell::INSIDE) {
|
if(KeepEdge(type, opA, indir_shell, outdir_shell,
|
||||||
tag |= INSIDE(SHELL, INDIR) | INSIDE(SHELL, OUTDIR);
|
indir_orig, outdir_orig))
|
||||||
} else if(c_shell == SShell::OUTSIDE) {
|
{
|
||||||
tag |= 0;
|
|
||||||
} else if(c_shell == SShell::SURF_PARALLEL) {
|
|
||||||
tag |= INSIDE(SHELL, INDIR);
|
|
||||||
} 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)) {
|
|
||||||
final.AddEdge(se->a, se->b, se->auxA, se->auxB);
|
final.AddEdge(se->a, se->b, se->auxA, se->auxB);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -420,23 +417,22 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *agnst, SShell *parent,
|
|||||||
Point2d auv = (se->a).ProjectXy(),
|
Point2d auv = (se->a).ProjectXy(),
|
||||||
buv = (se->b).ProjectXy();
|
buv = (se->b).ProjectXy();
|
||||||
|
|
||||||
|
Vector pt, enin, enout, surfn;
|
||||||
|
ret.EdgeNormalsWithinSurface(auv, buv, &pt, &enin, &enout, &surfn,
|
||||||
|
se->auxA, into);
|
||||||
|
|
||||||
|
int indir_shell, outdir_shell, indir_orig, outdir_orig;
|
||||||
|
|
||||||
int c_this = origBsp->ClassifyEdge(auv, buv);
|
int c_this = origBsp->ClassifyEdge(auv, buv);
|
||||||
int c_same = sameBsp->ClassifyEdge(auv, buv);
|
TagByClassifiedEdge(c_this, &indir_orig, &outdir_orig);
|
||||||
int c_opp = oppositeBsp->ClassifyEdge(auv, buv);
|
|
||||||
|
|
||||||
int tag = 0;
|
agnst->ClassifyEdge(&indir_shell, &outdir_shell,
|
||||||
tag |= TagByClassifiedEdge(c_this, ORIG);
|
ret.PointAt(auv), ret.PointAt(buv), pt,
|
||||||
tag |= TagByClassifiedEdge(c_same, COINCIDENT_SAME);
|
enin, enout, surfn);
|
||||||
tag |= TagByClassifiedEdge(c_opp, COINCIDENT_OPPOSITE);
|
|
||||||
|
|
||||||
if(type == SShell::AS_DIFFERENCE && !opA) {
|
if(KeepEdge(type, opA, indir_shell, outdir_shell,
|
||||||
// The second operand of a difference gets turned inside out
|
indir_orig, outdir_orig))
|
||||||
tag |= INSIDE(SHELL, INDIR);
|
{
|
||||||
} else {
|
|
||||||
tag |= INSIDE(SHELL, OUTDIR);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -455,12 +451,11 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *agnst, SShell *parent,
|
|||||||
final.l.ClearTags();
|
final.l.ClearTags();
|
||||||
if(!final.AssemblePolygon(&poly, NULL, true)) {
|
if(!final.AssemblePolygon(&poly, NULL, true)) {
|
||||||
into->booleanFailed = true;
|
into->booleanFailed = true;
|
||||||
|
dbp("failed: I=%d", I);
|
||||||
DEBUGEDGELIST(&final, &ret);
|
DEBUGEDGELIST(&final, &ret);
|
||||||
}
|
}
|
||||||
poly.Clear();
|
poly.Clear();
|
||||||
|
|
||||||
sameNormal.Clear();
|
|
||||||
oppositeNormal.Clear();
|
|
||||||
final.Clear();
|
final.Clear();
|
||||||
inter.Clear();
|
inter.Clear();
|
||||||
orig.Clear();
|
orig.Clear();
|
||||||
@ -496,6 +491,7 @@ void SShell::MakeIntersectionCurvesAgainst(SShell *agnst, SShell *into) {
|
|||||||
void SShell::CleanupAfterBoolean(void) {
|
void SShell::CleanupAfterBoolean(void) {
|
||||||
SSurface *ss;
|
SSurface *ss;
|
||||||
for(ss = surface.First(); ss; ss = surface.NextAfter(ss)) {
|
for(ss = surface.First(); ss; ss = surface.NextAfter(ss)) {
|
||||||
|
ss->edges.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -576,8 +572,8 @@ void SShell::MakeFromAssemblyOf(SShell *a, SShell *b) {
|
|||||||
void SShell::MakeFromBoolean(SShell *a, SShell *b, int type) {
|
void SShell::MakeFromBoolean(SShell *a, SShell *b, int type) {
|
||||||
booleanFailed = false;
|
booleanFailed = false;
|
||||||
|
|
||||||
a->MakeClassifyingBsps();
|
a->MakeClassifyingBsps(NULL);
|
||||||
b->MakeClassifyingBsps();
|
b->MakeClassifyingBsps(NULL);
|
||||||
|
|
||||||
// Copy over all the original curves, splitting them so that a
|
// Copy over all the original curves, splitting them so that a
|
||||||
// piecwise linear segment never crosses a surface from the other
|
// piecwise linear segment never crosses a surface from the other
|
||||||
@ -605,11 +601,21 @@ void SShell::MakeFromBoolean(SShell *a, SShell *b, int type) {
|
|||||||
sc->RemoveShortSegments(srfA, srfB);
|
sc->RemoveShortSegments(srfA, srfB);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(b->surface.n == 0 || a->surface.n == 0) {
|
// And clean up the piecewise linear things we made as a calculation aid
|
||||||
|
a->CleanupAfterBoolean();
|
||||||
|
b->CleanupAfterBoolean();
|
||||||
|
// Remake the classifying BSPs with the split (and short-segment-removed)
|
||||||
|
// curves
|
||||||
|
a->MakeClassifyingBsps(this);
|
||||||
|
b->MakeClassifyingBsps(this);
|
||||||
|
|
||||||
// Then trim and copy the surfaces
|
// Then trim and copy the surfaces
|
||||||
|
if(b->surface.n == 0 || a->surface.n == 0) {
|
||||||
|
I = 1023123;
|
||||||
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 {
|
||||||
|
I = 0;
|
||||||
a->CopySurfacesTrimAgainst(b, this, type, true);
|
a->CopySurfacesTrimAgainst(b, this, type, true);
|
||||||
b->CopySurfacesTrimAgainst(a, this, type, false);
|
b->CopySurfacesTrimAgainst(a, this, type, false);
|
||||||
}
|
}
|
||||||
@ -627,21 +633,23 @@ void SShell::MakeFromBoolean(SShell *a, SShell *b, int type) {
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// All of the BSP routines that we use to perform and accelerate polygon ops.
|
// All of the BSP routines that we use to perform and accelerate polygon ops.
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
void SShell::MakeClassifyingBsps(void) {
|
void SShell::MakeClassifyingBsps(SShell *useCurvesFrom) {
|
||||||
SSurface *ss;
|
SSurface *ss;
|
||||||
for(ss = surface.First(); ss; ss = surface.NextAfter(ss)) {
|
for(ss = surface.First(); ss; ss = surface.NextAfter(ss)) {
|
||||||
ss->MakeClassifyingBsp(this);
|
ss->MakeClassifyingBsp(this, useCurvesFrom);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SSurface::MakeClassifyingBsp(SShell *shell) {
|
void SSurface::MakeClassifyingBsp(SShell *shell, SShell *useCurvesFrom) {
|
||||||
SEdgeList el;
|
SEdgeList el;
|
||||||
ZERO(&el);
|
ZERO(&el);
|
||||||
|
|
||||||
MakeEdgesInto(shell, &el, true);
|
MakeEdgesInto(shell, &el, true, useCurvesFrom);
|
||||||
bsp = SBspUv::From(&el);
|
bsp = SBspUv::From(&el);
|
||||||
|
|
||||||
el.Clear();
|
el.Clear();
|
||||||
|
|
||||||
|
ZERO(&edges);
|
||||||
|
MakeEdgesInto(shell, &edges, false, useCurvesFrom);
|
||||||
}
|
}
|
||||||
|
|
||||||
SBspUv *SBspUv::Alloc(void) {
|
SBspUv *SBspUv::Alloc(void) {
|
||||||
@ -733,7 +741,7 @@ SBspUv *SBspUv::InsertEdge(Point2d ea, Point2d eb) {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SBspUv::ClassifyPoint(Point2d p, Point2d eb) {
|
int SBspUv::ClassifyPoint(Point2d p, Point2d eb, Point2d *ia, Point2d *ib) {
|
||||||
if(!this) return OUTSIDE;
|
if(!this) return OUTSIDE;
|
||||||
|
|
||||||
Point2d n = ((b.Minus(a)).Normal()).WithMagnitude(1);
|
Point2d n = ((b.Minus(a)).Normal()).WithMagnitude(1);
|
||||||
@ -746,6 +754,11 @@ int SBspUv::ClassifyPoint(Point2d p, Point2d eb) {
|
|||||||
while(f) {
|
while(f) {
|
||||||
Point2d ba = (f->b).Minus(f->a);
|
Point2d ba = (f->b).Minus(f->a);
|
||||||
if(p.DistanceToLine(f->a, ba, true) < LENGTH_EPS) {
|
if(p.DistanceToLine(f->a, ba, true) < LENGTH_EPS) {
|
||||||
|
if(ia && ib) {
|
||||||
|
// Tell the caller which edge it happens to lie on.
|
||||||
|
*ia = f->a;
|
||||||
|
*ib = f->b;
|
||||||
|
}
|
||||||
if(eb.DistanceToLine(f->a, ba, false) < LENGTH_EPS) {
|
if(eb.DistanceToLine(f->a, ba, false) < LENGTH_EPS) {
|
||||||
if(ba.Dot(eb.Minus(p)) > 0) {
|
if(ba.Dot(eb.Minus(p)) > 0) {
|
||||||
return EDGE_PARALLEL;
|
return EDGE_PARALLEL;
|
||||||
@ -759,16 +772,16 @@ int SBspUv::ClassifyPoint(Point2d p, Point2d eb) {
|
|||||||
f = f->more;
|
f = f->more;
|
||||||
}
|
}
|
||||||
// Pick arbitrarily which side to send it down, doesn't matter
|
// Pick arbitrarily which side to send it down, doesn't matter
|
||||||
int c1 = neg ? neg->ClassifyPoint(p, eb) : OUTSIDE;
|
int c1 = neg ? neg->ClassifyPoint(p, eb, ia, ib) : OUTSIDE;
|
||||||
int c2 = pos ? pos->ClassifyPoint(p, eb) : INSIDE;
|
int c2 = pos ? pos->ClassifyPoint(p, eb, ia, ib) : INSIDE;
|
||||||
if(c1 != c2) {
|
if(c1 != c2) {
|
||||||
dbp("MISMATCH: %d %d %08x %08x", c1, c2, neg, pos);
|
dbp("MISMATCH: %d %d %08x %08x", c1, c2, neg, pos);
|
||||||
}
|
}
|
||||||
return c1;
|
return c1;
|
||||||
} else if(dp > 0) {
|
} else if(dp > 0) {
|
||||||
return pos ? pos->ClassifyPoint(p, eb) : INSIDE;
|
return pos ? pos->ClassifyPoint(p, eb, ia, ib) : INSIDE;
|
||||||
} else {
|
} else {
|
||||||
return neg ? neg->ClassifyPoint(p, eb) : OUTSIDE;
|
return neg ? neg->ClassifyPoint(p, eb, ia, ib) : OUTSIDE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +130,7 @@ Vector SBezier::TangentAt(double t) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SBezier::ClosestPointTo(Vector p, double *t) {
|
void SBezier::ClosestPointTo(Vector p, double *t, bool converge) {
|
||||||
int i;
|
int i;
|
||||||
double minDist = VERY_POSITIVE;
|
double minDist = VERY_POSITIVE;
|
||||||
*t = 0;
|
*t = 0;
|
||||||
@ -147,7 +147,7 @@ void SBezier::ClosestPointTo(Vector p, double *t) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Vector p0;
|
Vector p0;
|
||||||
for(i = 0; i < 15; i++) {
|
for(i = 0; i < (converge ? 15 : 3); i++) {
|
||||||
p0 = PointAt(*t);
|
p0 = PointAt(*t);
|
||||||
if(p0.Equals(p, RATPOLY_EPS)) {
|
if(p0.Equals(p, RATPOLY_EPS)) {
|
||||||
return;
|
return;
|
||||||
@ -157,7 +157,9 @@ void SBezier::ClosestPointTo(Vector p, double *t) {
|
|||||||
Vector pc = p.ClosestPointOnLine(p0, dp);
|
Vector pc = p.ClosestPointOnLine(p0, dp);
|
||||||
*t += (pc.Minus(p0)).DivPivoting(dp);
|
*t += (pc.Minus(p0)).DivPivoting(dp);
|
||||||
}
|
}
|
||||||
|
if(converge) {
|
||||||
dbp("didn't converge (closest point on bezier curve)");
|
dbp("didn't converge (closest point on bezier curve)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SBezier::SplitAt(double t, SBezier *bef, SBezier *aft) {
|
void SBezier::SplitAt(double t, SBezier *bef, SBezier *aft) {
|
||||||
@ -242,6 +244,9 @@ void SBezier::MakePwlWorker(List<Vector> *l, double ta, double tb) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector SSurface::PointAt(Point2d puv) {
|
||||||
|
return PointAt(puv.x, puv.y);
|
||||||
|
}
|
||||||
Vector SSurface::PointAt(double u, double v) {
|
Vector SSurface::PointAt(double u, double v) {
|
||||||
Vector num = Vector::From(0, 0, 0);
|
Vector num = Vector::From(0, 0, 0);
|
||||||
double den = 0;
|
double den = 0;
|
||||||
@ -294,12 +299,18 @@ void SSurface::TangentsAt(double u, double v, Vector *tu, Vector *tv) {
|
|||||||
*tv = tv->ScaledBy(1.0/(den*den));
|
*tv = tv->ScaledBy(1.0/(den*den));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector SSurface::NormalAt(Point2d puv) {
|
||||||
|
return NormalAt(puv.x, puv.y);
|
||||||
|
}
|
||||||
Vector SSurface::NormalAt(double u, double v) {
|
Vector SSurface::NormalAt(double u, double v) {
|
||||||
Vector tu, tv;
|
Vector tu, tv;
|
||||||
TangentsAt(u, v, &tu, &tv);
|
TangentsAt(u, v, &tu, &tv);
|
||||||
return tu.Cross(tv);
|
return tu.Cross(tv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SSurface::ClosestPointTo(Vector p, Point2d *puv, bool converge) {
|
||||||
|
ClosestPointTo(p, &(puv->x), &(puv->y), converge);
|
||||||
|
}
|
||||||
void SSurface::ClosestPointTo(Vector p, double *u, double *v, bool converge) {
|
void SSurface::ClosestPointTo(Vector p, double *u, double *v, bool converge) {
|
||||||
// A few special cases first; when control points are coincident the
|
// A few special cases first; when control points are coincident the
|
||||||
// derivative goes to zero at the conrol points, and would result in
|
// derivative goes to zero at the conrol points, and would result in
|
||||||
@ -394,6 +405,48 @@ bool SSurface::PointIntersectingLine(Vector p0, Vector p1, double *u, double *v)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector SSurface::ClosestPointOnThisAndSurface(SSurface *srf2, Vector p) {
|
||||||
|
// This is untested.
|
||||||
|
int i, j;
|
||||||
|
Point2d puv[2];
|
||||||
|
SSurface *srf[2] = { this, srf2 };
|
||||||
|
|
||||||
|
for(j = 0; j < 2; j++) {
|
||||||
|
(srf[j])->ClosestPointTo(p, &(puv[j]), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0; i < 4; i++) {
|
||||||
|
Vector tu[2], tv[2], cp[2], n[2];
|
||||||
|
double d[2];
|
||||||
|
|
||||||
|
for(j = 0; j < 2; j++) {
|
||||||
|
(srf[j])->TangentsAt(puv[j].x, puv[j].y, &(tu[j]), &(tv[j]));
|
||||||
|
|
||||||
|
cp[j] = (srf[j])->PointAt(puv[j]);
|
||||||
|
|
||||||
|
n[j] = ((tu[j]).Cross(tv[j])).WithMagnitude(1);
|
||||||
|
d[j] = (n[j]).Dot(cp[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector p0 = Vector::AtIntersectionOfPlanes(n[0], d[0], n[1], d[1]),
|
||||||
|
dp = (n[0]).Cross(n[1]);
|
||||||
|
|
||||||
|
Vector pc = p.ClosestPointOnLine(p0, dp);
|
||||||
|
|
||||||
|
// Adjust our guess and iterate
|
||||||
|
for(j = 0; j < 2; j++) {
|
||||||
|
Vector dc = pc.Minus(cp[j]);
|
||||||
|
double du = dc.Dot(tu[j]), dv = dc.Dot(tv[j]);
|
||||||
|
puv[j].x += du / ((tu[j]).MagSquared());
|
||||||
|
puv[j].y += dv / ((tv[j]).MagSquared());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this converged, then the two points are actually equal.
|
||||||
|
return ((srf[0])->PointAt(puv[0])).Plus(
|
||||||
|
((srf[1])->PointAt(puv[1]))).ScaledBy(0.5);
|
||||||
|
}
|
||||||
|
|
||||||
void SSurface::PointOnSurfaces(SSurface *s1, SSurface *s2,
|
void SSurface::PointOnSurfaces(SSurface *s1, SSurface *s2,
|
||||||
double *up, double *vp)
|
double *up, double *vp)
|
||||||
{
|
{
|
||||||
|
@ -31,7 +31,8 @@ public:
|
|||||||
|
|
||||||
Point2d IntersectionWith(Point2d a, Point2d b);
|
Point2d IntersectionWith(Point2d a, Point2d b);
|
||||||
SBspUv *InsertEdge(Point2d a, Point2d b);
|
SBspUv *InsertEdge(Point2d a, Point2d b);
|
||||||
int ClassifyPoint(Point2d p, Point2d eb);
|
int ClassifyPoint(Point2d p, Point2d eb,
|
||||||
|
Point2d *ia=NULL, Point2d *ib=NULL);
|
||||||
int ClassifyEdge(Point2d ea, Point2d eb);
|
int ClassifyEdge(Point2d ea, Point2d eb);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -62,7 +63,7 @@ public:
|
|||||||
|
|
||||||
Vector PointAt(double t);
|
Vector PointAt(double t);
|
||||||
Vector TangentAt(double t);
|
Vector TangentAt(double t);
|
||||||
void ClosestPointTo(Vector p, double *t);
|
void ClosestPointTo(Vector p, double *t, bool converge=true);
|
||||||
void SplitAt(double t, SBezier *bef, SBezier *aft);
|
void SplitAt(double t, SBezier *bef, SBezier *aft);
|
||||||
|
|
||||||
Vector Start(void);
|
Vector Start(void);
|
||||||
@ -184,9 +185,10 @@ public:
|
|||||||
int tag;
|
int tag;
|
||||||
Vector p;
|
Vector p;
|
||||||
SSurface *srf;
|
SSurface *srf;
|
||||||
hSSurface hsrf;
|
Point2d pinter;
|
||||||
Vector surfNormal; // of the intersecting surface, at pinter
|
Vector surfNormal; // of the intersecting surface, at pinter
|
||||||
bool onEdge; // pinter is on edge of trim poly
|
bool onEdge; // pinter is on edge of trim poly
|
||||||
|
Point2d edgeA, edgeB; // the edge that pinter is on
|
||||||
};
|
};
|
||||||
|
|
||||||
// A rational polynomial surface in Bezier form.
|
// A rational polynomial surface in Bezier form.
|
||||||
@ -209,6 +211,7 @@ public:
|
|||||||
|
|
||||||
// For testing whether a point (u, v) on the surface lies inside the trim
|
// For testing whether a point (u, v) on the surface lies inside the trim
|
||||||
SBspUv *bsp;
|
SBspUv *bsp;
|
||||||
|
SEdgeList edges;
|
||||||
|
|
||||||
static SSurface FromExtrusionOf(SBezier *spc, Vector t0, Vector t1);
|
static SSurface FromExtrusionOf(SBezier *spc, Vector t0, Vector t1);
|
||||||
static SSurface FromRevolutionOf(SBezier *sb, Vector pt, Vector axis,
|
static SSurface FromRevolutionOf(SBezier *sb, Vector pt, Vector axis,
|
||||||
@ -217,6 +220,10 @@ public:
|
|||||||
static SSurface FromTransformationOf(SSurface *a, Vector t, Quaternion q,
|
static SSurface FromTransformationOf(SSurface *a, Vector t, Quaternion q,
|
||||||
bool includingTrims);
|
bool includingTrims);
|
||||||
|
|
||||||
|
void EdgeNormalsWithinSurface(Point2d auv, Point2d buv,
|
||||||
|
Vector *pt, Vector *enin, Vector *enout,
|
||||||
|
Vector *surfn,
|
||||||
|
DWORD auxA, SShell *shell);
|
||||||
SSurface MakeCopyTrimAgainst(SShell *against, SShell *parent, SShell *into,
|
SSurface MakeCopyTrimAgainst(SShell *against, SShell *parent, SShell *into,
|
||||||
int type, bool opA);
|
int type, bool opA);
|
||||||
void TrimFromEdgeList(SEdgeList *el);
|
void TrimFromEdgeList(SEdgeList *el);
|
||||||
@ -244,11 +251,15 @@ public:
|
|||||||
List<Inter> *l, bool segment,
|
List<Inter> *l, bool segment,
|
||||||
SSurface *sorig);
|
SSurface *sorig);
|
||||||
|
|
||||||
|
void ClosestPointTo(Vector p, Point2d *puv, bool converge=true);
|
||||||
void ClosestPointTo(Vector p, double *u, double *v, bool converge=true);
|
void ClosestPointTo(Vector p, double *u, double *v, bool converge=true);
|
||||||
bool PointIntersectingLine(Vector p0, Vector p1, double *u, double *v);
|
bool PointIntersectingLine(Vector p0, Vector p1, double *u, double *v);
|
||||||
|
Vector ClosestPointOnThisAndSurface(SSurface *srf2, Vector p);
|
||||||
void PointOnSurfaces(SSurface *s1, SSurface *s2, double *u, double *v);
|
void PointOnSurfaces(SSurface *s1, SSurface *s2, double *u, double *v);
|
||||||
Vector PointAt(double u, double v);
|
Vector PointAt(double u, double v);
|
||||||
|
Vector PointAt(Point2d puv);
|
||||||
void TangentsAt(double u, double v, Vector *tu, Vector *tv);
|
void TangentsAt(double u, double v, Vector *tu, Vector *tv);
|
||||||
|
Vector NormalAt(Point2d puv);
|
||||||
Vector NormalAt(double u, double v);
|
Vector NormalAt(double u, double v);
|
||||||
bool LineEntirelyOutsideBbox(Vector a, Vector b, bool segment);
|
bool LineEntirelyOutsideBbox(Vector a, Vector b, bool segment);
|
||||||
void GetAxisAlignedBounding(Vector *ptMax, Vector *ptMin);
|
void GetAxisAlignedBounding(Vector *ptMax, Vector *ptMin);
|
||||||
@ -263,7 +274,7 @@ public:
|
|||||||
void MakeEdgesInto(SShell *shell, SEdgeList *sel, bool asUv,
|
void MakeEdgesInto(SShell *shell, SEdgeList *sel, bool asUv,
|
||||||
SShell *useCurvesFrom=NULL);
|
SShell *useCurvesFrom=NULL);
|
||||||
void MakeSectionEdgesInto(SShell *shell, SEdgeList *sel, SBezierList *sbl);
|
void MakeSectionEdgesInto(SShell *shell, SEdgeList *sel, SBezierList *sbl);
|
||||||
void MakeClassifyingBsp(SShell *shell);
|
void MakeClassifyingBsp(SShell *shell, SShell *useCurvesFrom);
|
||||||
double ChordToleranceForEdge(Vector a, Vector b);
|
double ChordToleranceForEdge(Vector a, Vector b);
|
||||||
void MakeTriangulationGridInto(List<double> *l, double vs, double vf,
|
void MakeTriangulationGridInto(List<double> *l, double vs, double vf,
|
||||||
bool swapped);
|
bool swapped);
|
||||||
@ -294,7 +305,7 @@ public:
|
|||||||
void CopyCurvesSplitAgainst(bool opA, SShell *agnst, SShell *into);
|
void CopyCurvesSplitAgainst(bool opA, SShell *agnst, SShell *into);
|
||||||
void CopySurfacesTrimAgainst(SShell *against, SShell *into, int t, bool a);
|
void CopySurfacesTrimAgainst(SShell *against, SShell *into, int t, bool a);
|
||||||
void MakeIntersectionCurvesAgainst(SShell *against, SShell *into);
|
void MakeIntersectionCurvesAgainst(SShell *against, SShell *into);
|
||||||
void MakeClassifyingBsps(void);
|
void MakeClassifyingBsps(SShell *useCurvesFrom);
|
||||||
void AllPointsIntersecting(Vector a, Vector b, List<SInter> *il,
|
void AllPointsIntersecting(Vector a, Vector b, List<SInter> *il,
|
||||||
bool seg, bool trimmed, bool inclTangent);
|
bool seg, bool trimmed, bool inclTangent);
|
||||||
void MakeCoincidentEdgesInto(SSurface *proto, bool sameNormal,
|
void MakeCoincidentEdgesInto(SSurface *proto, bool sameNormal,
|
||||||
@ -302,16 +313,19 @@ public:
|
|||||||
void RewriteSurfaceHandlesForCurves(SShell *a, SShell *b);
|
void RewriteSurfaceHandlesForCurves(SShell *a, SShell *b);
|
||||||
void CleanupAfterBoolean(void);
|
void CleanupAfterBoolean(void);
|
||||||
|
|
||||||
|
// Definitions when classifying regions of a surface; it is either inside,
|
||||||
|
// outside, or coincident (with parallel or antiparallel normal) with a
|
||||||
|
// shell.
|
||||||
static const int INSIDE = 100;
|
static const int INSIDE = 100;
|
||||||
static const int OUTSIDE = 200;
|
static const int OUTSIDE = 200;
|
||||||
static const int SURF_PARALLEL = 300;
|
static const int COINC_SAME = 300;
|
||||||
static const int SURF_ANTIPARALLEL = 400;
|
static const int COINC_OPP = 400;
|
||||||
static const int EDGE_PARALLEL = 500;
|
static const double DOTP_TOL;
|
||||||
static const int EDGE_ANTIPARALLEL = 600;
|
int ClassifyRegion(Vector edge_n, Vector inter_surf_n, Vector edge_surf_n);
|
||||||
static const int EDGE_TANGENT = 700;
|
bool ClassifyEdge(int *indir, int *outdir,
|
||||||
|
Vector ea, Vector eb,
|
||||||
int ClassifyPoint(Vector p, Vector edge_n, Vector surf_n);
|
Vector p,
|
||||||
|
Vector edge_n_in, Vector edge_n_out, Vector surf_n);
|
||||||
|
|
||||||
void MakeFromCopyOf(SShell *a);
|
void MakeFromCopyOf(SShell *a);
|
||||||
void MakeFromTransformationOf(SShell *a, Vector trans, Quaternion q);
|
void MakeFromTransformationOf(SShell *a, Vector trans, Quaternion q);
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
#include "solvespace.h"
|
#include "solvespace.h"
|
||||||
|
|
||||||
|
// Dot product tolerance for perpendicular.
|
||||||
|
const double SShell::DOTP_TOL = 1e-3;
|
||||||
|
|
||||||
extern int FLAG;
|
extern int FLAG;
|
||||||
|
|
||||||
void SSurface::AddExactIntersectionCurve(SBezier *sb, SSurface *srfB,
|
void SSurface::AddExactIntersectionCurve(SBezier *sb, SSurface *srfB,
|
||||||
@ -220,9 +223,25 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
|
|||||||
List<Vector> lv;
|
List<Vector> lv;
|
||||||
ZERO(&lv);
|
ZERO(&lv);
|
||||||
|
|
||||||
Vector axisa = axis.ScaledBy((b->ctrl[0][0]).Dot(axis)),
|
double a_axis0 = ( ctrl[0][0]).Dot(axis),
|
||||||
axisb = axis.ScaledBy((b->ctrl[0][1]).Dot(axis)),
|
a_axis1 = ( ctrl[0][1]).Dot(axis),
|
||||||
axisc = (axisa.Plus(axisb)).ScaledBy(0.5);
|
b_axis0 = (b->ctrl[0][0]).Dot(axis),
|
||||||
|
b_axis1 = (b->ctrl[0][1]).Dot(axis);
|
||||||
|
|
||||||
|
if(a_axis0 > a_axis1) SWAP(double, a_axis0, a_axis1);
|
||||||
|
if(b_axis0 > b_axis1) SWAP(double, b_axis0, b_axis1);
|
||||||
|
|
||||||
|
double ab_axis0 = max(a_axis0, b_axis0),
|
||||||
|
ab_axis1 = min(a_axis1, b_axis1);
|
||||||
|
|
||||||
|
if(fabs(ab_axis0 - ab_axis1) < LENGTH_EPS) {
|
||||||
|
// The line would be zero-length
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector axis0 = axis.ScaledBy(ab_axis0),
|
||||||
|
axis1 = axis.ScaledBy(ab_axis1),
|
||||||
|
axisc = (axis0.Plus(axis1)).ScaledBy(0.5);
|
||||||
|
|
||||||
oft.MakePwlInto(&lv);
|
oft.MakePwlInto(&lv);
|
||||||
|
|
||||||
@ -250,7 +269,7 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
|
|||||||
p = b->PointAt(ub, vb);
|
p = b->PointAt(ub, vb);
|
||||||
|
|
||||||
SBezier bezier;
|
SBezier bezier;
|
||||||
bezier = SBezier::From(p.Plus(axisa), p.Plus(axisb));
|
bezier = SBezier::From(p.Plus(axis0), p.Plus(axis1));
|
||||||
AddExactIntersectionCurve(&bezier, b, agnstA, agnstB, into);
|
AddExactIntersectionCurve(&bezier, b, agnstA, agnstB, into);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -608,8 +627,8 @@ void SSurface::AllPointsIntersecting(Vector a, Vector b,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// And that it lies inside our trim region
|
// And that it lies inside our trim region
|
||||||
Point2d dummy = { 0, 0 };
|
Point2d dummy = { 0, 0 }, ia = { 0, 0 }, ib = { 0, 0 };
|
||||||
int c = bsp->ClassifyPoint(puv, dummy);
|
int c = bsp->ClassifyPoint(puv, dummy, &ia, &ib);
|
||||||
if(trimmed && c == SBspUv::OUTSIDE) {
|
if(trimmed && c == SBspUv::OUTSIDE) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -618,9 +637,11 @@ void SSurface::AllPointsIntersecting(Vector a, Vector b,
|
|||||||
SInter si;
|
SInter si;
|
||||||
si.p = pxyz;
|
si.p = pxyz;
|
||||||
si.surfNormal = NormalAt(puv.x, puv.y);
|
si.surfNormal = NormalAt(puv.x, puv.y);
|
||||||
si.hsrf = h;
|
si.pinter = puv;
|
||||||
si.srf = this;
|
si.srf = this;
|
||||||
si.onEdge = (c != SBspUv::INSIDE);
|
si.onEdge = (c != SBspUv::INSIDE);
|
||||||
|
si.edgeA = ia;
|
||||||
|
si.edgeB = ib;
|
||||||
l->Add(&si);
|
l->Add(&si);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -637,6 +658,26 @@ void SShell::AllPointsIntersecting(Vector a, Vector b,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int SShell::ClassifyRegion(Vector edge_n, Vector inter_surf_n,
|
||||||
|
Vector edge_surf_n)
|
||||||
|
{
|
||||||
|
double dot = inter_surf_n.Dot(edge_n);
|
||||||
|
if(fabs(dot) < DOTP_TOL) {
|
||||||
|
// The edge's surface and the edge-on-face surface
|
||||||
|
// are coincident. Test the edge's surface normal
|
||||||
|
// to see if it's with same or opposite normals.
|
||||||
|
if(inter_surf_n.Dot(edge_surf_n) > 0) {
|
||||||
|
return COINC_SAME;
|
||||||
|
} else {
|
||||||
|
return COINC_OPP;
|
||||||
|
}
|
||||||
|
} else if(dot > 0) {
|
||||||
|
return OUTSIDE;
|
||||||
|
} else {
|
||||||
|
return INSIDE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Does the given point lie on our shell? There are many cases; inside and
|
// Does the given point lie on our shell? There are many cases; inside and
|
||||||
// outside are obvious, but then there's all the edge-on-edge and edge-on-face
|
// outside are obvious, but then there's all the edge-on-edge and edge-on-face
|
||||||
@ -646,15 +687,101 @@ void SShell::AllPointsIntersecting(Vector a, Vector b,
|
|||||||
// using the closest intersection point. If the ray hits a surface on edge,
|
// using the closest intersection point. If the ray hits a surface on edge,
|
||||||
// then just reattempt in a different random direction.
|
// then just reattempt in a different random direction.
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
int SShell::ClassifyPoint(Vector p, Vector edge_n, Vector surf_n) {
|
bool SShell::ClassifyEdge(int *indir, int *outdir,
|
||||||
|
Vector ea, Vector eb,
|
||||||
|
Vector p,
|
||||||
|
Vector edge_n_in, Vector edge_n_out, Vector surf_n)
|
||||||
|
{
|
||||||
List<SInter> l;
|
List<SInter> l;
|
||||||
ZERO(&l);
|
ZERO(&l);
|
||||||
|
|
||||||
srand(0);
|
srand(0);
|
||||||
|
|
||||||
int ret, cnt = 0, edge_inters;
|
// First, check for edge-on-edge
|
||||||
double edge_dotp[2];
|
int edge_inters = 0;
|
||||||
|
Vector inter_surf_n[2], inter_edge_n[2];
|
||||||
|
SSurface *srf;
|
||||||
|
for(srf = surface.First(); srf; srf = surface.NextAfter(srf)) {
|
||||||
|
if(srf->LineEntirelyOutsideBbox(ea, eb, true)) continue;
|
||||||
|
|
||||||
|
SEdgeList *sel = &(srf->edges);
|
||||||
|
SEdge *se;
|
||||||
|
for(se = sel->l.First(); se; se = sel->l.NextAfter(se)) {
|
||||||
|
if((ea.Equals(se->a) && eb.Equals(se->b)) ||
|
||||||
|
(eb.Equals(se->a) && ea.Equals(se->b)) ||
|
||||||
|
p.OnLineSegment(se->a, se->b))
|
||||||
|
{
|
||||||
|
if(edge_inters < 2) {
|
||||||
|
// Edge-on-edge case
|
||||||
|
Point2d pm;
|
||||||
|
srf->ClosestPointTo(p, &pm, false);
|
||||||
|
// A vector normal to the surface, at the intersection point
|
||||||
|
inter_surf_n[edge_inters] = srf->NormalAt(pm);
|
||||||
|
// A vector normal to the intersecting edge (but within the
|
||||||
|
// intersecting surface) at the intersection point, pointing
|
||||||
|
// out.
|
||||||
|
inter_edge_n[edge_inters] =
|
||||||
|
(inter_surf_n[edge_inters]).Cross((se->b).Minus((se->a)));
|
||||||
|
}
|
||||||
|
|
||||||
|
edge_inters++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(edge_inters == 2) {
|
||||||
|
// TODO, make this use the appropriate curved normals
|
||||||
|
double dotp[2];
|
||||||
|
for(int i = 0; i < 2; i++) {
|
||||||
|
dotp[i] = edge_n_out.Dot(inter_surf_n[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fabs(dotp[1]) < DOTP_TOL) {
|
||||||
|
SWAP(double, dotp[0], dotp[1]);
|
||||||
|
SWAP(Vector, inter_surf_n[0], inter_surf_n[1]);
|
||||||
|
SWAP(Vector, inter_edge_n[0], inter_edge_n[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
int coinc = (surf_n.Dot(inter_surf_n[0])) > 0 ? COINC_SAME : COINC_OPP;
|
||||||
|
|
||||||
|
if(fabs(dotp[0]) < DOTP_TOL && fabs(dotp[1]) < DOTP_TOL) {
|
||||||
|
// This is actually an edge on face case, just that the face
|
||||||
|
// is split into two pieces joining at our edge.
|
||||||
|
*indir = coinc;
|
||||||
|
*outdir = coinc;
|
||||||
|
} else if(fabs(dotp[0]) < DOTP_TOL && dotp[1] > DOTP_TOL) {
|
||||||
|
if(edge_n_out.Dot(inter_edge_n[0]) > 0) {
|
||||||
|
*indir = coinc;
|
||||||
|
*outdir = OUTSIDE;
|
||||||
|
} else {
|
||||||
|
*indir = INSIDE;
|
||||||
|
*outdir = coinc;
|
||||||
|
}
|
||||||
|
} else if(fabs(dotp[0]) < DOTP_TOL && dotp[1] < -DOTP_TOL) {
|
||||||
|
if(edge_n_out.Dot(inter_edge_n[0]) > 0) {
|
||||||
|
*indir = coinc;
|
||||||
|
*outdir = INSIDE;
|
||||||
|
} else {
|
||||||
|
*indir = OUTSIDE;
|
||||||
|
*outdir = coinc;
|
||||||
|
}
|
||||||
|
} else if(dotp[0] > DOTP_TOL && dotp[1] > DOTP_TOL) {
|
||||||
|
*indir = INSIDE;
|
||||||
|
*outdir = OUTSIDE;
|
||||||
|
} else if(dotp[0] < -DOTP_TOL && dotp[1] < -DOTP_TOL) {
|
||||||
|
*indir = OUTSIDE;
|
||||||
|
*outdir = INSIDE;
|
||||||
|
} else {
|
||||||
|
// Edge is tangent to the shell at shell's edge, so can't be
|
||||||
|
// a boundary of the surface.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(edge_inters != 0) dbp("bad, edge_inters=%d", edge_inters);
|
||||||
|
|
||||||
|
int cnt = 0;
|
||||||
for(;;) {
|
for(;;) {
|
||||||
// Cast a ray in a random direction (two-sided so that we test if
|
// 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
|
// the point lies on a surface, but use only one side for in/out
|
||||||
@ -663,8 +790,10 @@ int SShell::ClassifyPoint(Vector p, Vector edge_n, Vector surf_n) {
|
|||||||
AllPointsIntersecting(
|
AllPointsIntersecting(
|
||||||
p.Minus(ray), p.Plus(ray), &l, false, true, false);
|
p.Minus(ray), p.Plus(ray), &l, false, true, false);
|
||||||
|
|
||||||
|
// no intersections means it's outside
|
||||||
|
*indir = OUTSIDE;
|
||||||
|
*outdir = OUTSIDE;
|
||||||
double dmin = VERY_POSITIVE;
|
double dmin = VERY_POSITIVE;
|
||||||
ret = OUTSIDE; // no intersections means it's outside
|
|
||||||
bool onEdge = false;
|
bool onEdge = false;
|
||||||
edge_inters = 0;
|
edge_inters = 0;
|
||||||
|
|
||||||
@ -678,9 +807,9 @@ int SShell::ClassifyPoint(Vector p, Vector edge_n, Vector surf_n) {
|
|||||||
|
|
||||||
double d = ((si->p).Minus(p)).Magnitude();
|
double d = ((si->p).Minus(p)).Magnitude();
|
||||||
|
|
||||||
// Handle edge-on-edge
|
// We actually should never hit this case; it should have been
|
||||||
if(d < LENGTH_EPS && si->onEdge && edge_inters < 2) {
|
// handled above.
|
||||||
edge_dotp[edge_inters] = (si->surfNormal).Dot(edge_n);
|
if(d < LENGTH_EPS && si->onEdge) {
|
||||||
edge_inters++;
|
edge_inters++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -689,21 +818,24 @@ int SShell::ClassifyPoint(Vector p, Vector edge_n, Vector surf_n) {
|
|||||||
|
|
||||||
if(d < LENGTH_EPS) {
|
if(d < LENGTH_EPS) {
|
||||||
// Edge-on-face (unless edge-on-edge above supercedes)
|
// Edge-on-face (unless edge-on-edge above supercedes)
|
||||||
double dot = (si->surfNormal).Dot(edge_n);
|
Point2d pin, pout;
|
||||||
if(fabs(dot) < LENGTH_EPS && 0) {
|
(si->srf)->ClosestPointTo(p.Plus(edge_n_in), &pin, false);
|
||||||
// TODO revamp this
|
(si->srf)->ClosestPointTo(p.Plus(edge_n_out), &pout, false);
|
||||||
} else if(dot > 0) {
|
|
||||||
ret = SURF_PARALLEL;
|
Vector surf_n_in = (si->srf)->NormalAt(pin),
|
||||||
} else {
|
surf_n_out = (si->srf)->NormalAt(pout);
|
||||||
ret = SURF_ANTIPARALLEL;
|
|
||||||
}
|
*indir = ClassifyRegion(edge_n_in, surf_n_in, surf_n);
|
||||||
|
*outdir = ClassifyRegion(edge_n_out, surf_n_out, surf_n);
|
||||||
} else {
|
} else {
|
||||||
// Edge does not lie on surface; either strictly inside
|
// Edge does not lie on surface; either strictly inside
|
||||||
// or strictly outside
|
// or strictly outside
|
||||||
if((si->surfNormal).Dot(ray) > 0) {
|
if((si->surfNormal).Dot(ray) > 0) {
|
||||||
ret = INSIDE;
|
*indir = INSIDE;
|
||||||
|
*outdir = INSIDE;
|
||||||
} else {
|
} else {
|
||||||
ret = OUTSIDE;
|
*indir = OUTSIDE;
|
||||||
|
*outdir = OUTSIDE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onEdge = si->onEdge;
|
onEdge = si->onEdge;
|
||||||
@ -714,27 +846,16 @@ int SShell::ClassifyPoint(Vector p, Vector edge_n, Vector surf_n) {
|
|||||||
// If the point being tested lies exactly on an edge of the shell,
|
// If the point being tested lies exactly on an edge of the shell,
|
||||||
// then our ray always lies on edge, and that's okay. Otherwise
|
// then our ray always lies on edge, and that's okay. Otherwise
|
||||||
// try again in a different random direction.
|
// try again in a different random direction.
|
||||||
if((edge_inters == 2) || !onEdge) break;
|
if(!onEdge) break;
|
||||||
if(cnt++ > 5) {
|
if(cnt++ > 5) {
|
||||||
dbp("can't find a ray that doesn't hit on edge!");
|
dbp("can't find a ray that doesn't hit on edge!");
|
||||||
dbp("on edge = %d, edge_inters = %d", onEdge, edge_inters);
|
dbp("on edge = %d, edge_inters = %d", onEdge, edge_inters);
|
||||||
|
SS.nakedEdges.AddEdge(ea, eb);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(edge_inters == 2) {
|
return true;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
5
util.cpp
5
util.cpp
@ -460,6 +460,8 @@ double Vector::DistanceToLine(Vector p0, Vector dp) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Vector::OnLineSegment(Vector a, Vector b) {
|
bool Vector::OnLineSegment(Vector a, Vector b) {
|
||||||
|
if(this->Equals(a) || this->Equals(b)) return true;
|
||||||
|
|
||||||
Vector d = b.Minus(a);
|
Vector d = b.Minus(a);
|
||||||
|
|
||||||
double m = d.MagSquared();
|
double m = d.MagSquared();
|
||||||
@ -468,7 +470,7 @@ bool Vector::OnLineSegment(Vector a, Vector b) {
|
|||||||
if(distsq >= LENGTH_EPS*LENGTH_EPS) return false;
|
if(distsq >= LENGTH_EPS*LENGTH_EPS) return false;
|
||||||
|
|
||||||
double t = (this->Minus(a)).DivPivoting(d);
|
double t = (this->Minus(a)).DivPivoting(d);
|
||||||
// On-endpoint must be tested for separately.
|
// On-endpoint already tested
|
||||||
if(t < 0 || t > 1) return false;
|
if(t < 0 || t > 1) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -508,6 +510,7 @@ Vector Vector::ScaledBy(double v) {
|
|||||||
Vector Vector::WithMagnitude(double v) {
|
Vector Vector::WithMagnitude(double v) {
|
||||||
double m = Magnitude();
|
double m = Magnitude();
|
||||||
if(m == 0) {
|
if(m == 0) {
|
||||||
|
dbp("Vector::WithMagnitude of zero vector!");
|
||||||
return From(v, 0, 0);
|
return From(v, 0, 0);
|
||||||
} else {
|
} else {
|
||||||
return ScaledBy(v/m);
|
return ScaledBy(v/m);
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
|
|
||||||
marching algorithm for surface intersection
|
|
||||||
tangent intersections
|
tangent intersections
|
||||||
|
plane detection for solid of revolution
|
||||||
|
|
||||||
-----
|
-----
|
||||||
line styles (color, thickness)
|
line styles (color, thickness)
|
||||||
|
marching algorithm for surface intersection
|
||||||
loop detection
|
loop detection
|
||||||
IGES and STEP export
|
IGES and STEP export
|
||||||
incremental regen of entities?
|
incremental regen of entities?
|
||||||
|
Loading…
Reference in New Issue
Block a user