Fix stupidity in Point2d::DistanceToLine, and classify line

segments in Boolean against the shell, not the intersection
polygon. (We just cast a ray, and use the surface-line intersection
function that already existed.) That's slow, but can be
accelerated later.

[git-p4: depot-paths = "//depot/solvespace/": change = 1911]
solver
Jonathan Westhues 2009-02-01 05:01:28 -08:00
parent 9ffe95ea65
commit d0ab8270d9
4 changed files with 82 additions and 45 deletions

View File

@ -13,11 +13,11 @@ void SShell::MakeFromDifferenceOf(SShell *a, SShell *b) {
static Vector LineStart, LineDirection; static Vector LineStart, LineDirection;
static int ByTAlongLine(const void *av, const void *bv) static int ByTAlongLine(const void *av, const void *bv)
{ {
Vector *a = (Vector *)av, SInter *a = (SInter *)av,
*b = (Vector *)bv; *b = (SInter *)bv;
double ta = (a->Minus(LineStart)).DivPivoting(LineDirection), double ta = (a->p.Minus(LineStart)).DivPivoting(LineDirection),
tb = (b->Minus(LineStart)).DivPivoting(LineDirection); tb = (b->p.Minus(LineStart)).DivPivoting(LineDirection);
return (ta > tb) ? 1 : -1; return (ta > tb) ? 1 : -1;
} }
@ -34,7 +34,7 @@ SCurve SCurve::MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB) {
p = pts.NextAfter(p); p = pts.NextAfter(p);
for(; p; p = pts.NextAfter(p)) { for(; p; p = pts.NextAfter(p)) {
List<Vector> il; List<SInter> il;
ZERO(&il); ZERO(&il);
// Find all the intersections with the two passed shells // Find all the intersections with the two passed shells
@ -48,12 +48,13 @@ SCurve SCurve::MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB) {
LineDirection = p->Minus(prev); LineDirection = p->Minus(prev);
qsort(il.elem, il.n, sizeof(il.elem[0]), ByTAlongLine); qsort(il.elem, il.n, sizeof(il.elem[0]), ByTAlongLine);
Vector *pi; SInter *pi;
for(pi = il.First(); pi; pi = il.NextAfter(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); ret.pts.Add(p);
prev = *p; prev = *p;
} }
@ -197,31 +198,23 @@ SSurface SSurface::MakeCopyTrimAgainst(SShell *agnst, SShell *parent,
SEdgeList final; SEdgeList final;
ZERO(&final); ZERO(&final);
if(I == 2) dbp("INTERBSP: %d", inter.l.n);
SBspUv *interbsp = SBspUv::From(&inter); SBspUv *interbsp = SBspUv::From(&inter);
if(I == 2) dbp("INTEROVER");
SEdge *se; SEdge *se;
N = 0;
for(se = orig.l.First(); se; se = orig.l.NextAfter(se)) { 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", if(c == SShell::OUTSIDE) {
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");
final.AddEdge(se->a, se->b, se->auxA, se->auxB); 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)) { 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 mid = (se->a).Plus(se->b).ScaledBy(0.5);
Vector arrow = (se->b).Minus(se->a); Vector arrow = (se->b).Minus(se->a);
SWAP(double, arrow.x, arrow.y); SWAP(double, arrow.x, arrow.y);
@ -229,13 +222,14 @@ 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);
if(c == SBspUv::INSIDE) {
SS.nakedEdges.AddEdge(PointAt(se->a.x, se->a.y), SS.nakedEdges.AddEdge(PointAt(se->a.x, se->a.y),
PointAt(se->b.x, se->b.y)); PointAt(se->b.x, se->b.y));
SS.nakedEdges.AddEdge(PointAt(mid.x, mid.y), SS.nakedEdges.AddEdge(PointAt(mid.x, mid.y),
PointAt(arrow.x, arrow.y)); PointAt(arrow.x, arrow.y));
} }
}
int c = bsp->ClassifyEdge(se->a.ProjectXy(), se->b.ProjectXy());
if(c == SBspUv::INSIDE) { if(c == SBspUv::INSIDE) {
final.AddEdge(se->b, se->a, se->auxA, !se->auxB); 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); a->CopySurfacesTrimAgainst(b, this, type, true);
b->CopySurfacesTrimAgainst(a, this, type, false); b->CopySurfacesTrimAgainst(a, this, type, false);
} else { } else {
I = -1; 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);
} }
@ -369,10 +363,6 @@ SBspUv *SBspUv::From(SEdgeList *el) {
} }
SBspUv *SBspUv::InsertEdge(Point2d ea, Point2d eb) { 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) { if(!this) {
SBspUv *ret = Alloc(); SBspUv *ret = Alloc();
ret->a = ea; ret->a = ea;
@ -434,12 +424,7 @@ int SBspUv::ClassifyPoint(Point2d p, Point2d eb) {
double dp = p.Dot(n) - d; 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(fabs(dp) < LENGTH_EPS) {
if(I == 2 && N == 5) dbp(" on line");
SBspUv *f = this; SBspUv *f = this;
while(f) { while(f) {
Point2d ba = (f->b).Minus(f->a); Point2d ba = (f->b).Minus(f->a);
@ -457,12 +442,15 @@ 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
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) { } else if(dp > 0) {
if(I == 2 && N == 5) dbp(" pos");
return pos ? pos->ClassifyPoint(p, eb) : INSIDE; return pos ? pos->ClassifyPoint(p, eb) : INSIDE;
} else { } else {
if(I == 2 && N == 5) dbp(" neg");
return neg ? neg->ClassifyPoint(p, eb) : OUTSIDE; return neg ? neg->ClassifyPoint(p, eb) : OUTSIDE;
} }
} }

View File

@ -145,6 +145,14 @@ public:
static STrimBy STrimBy::EntireCurve(SShell *shell, hSCurve hsc, bool bkwds); 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. // A rational polynomial surface in Bezier form.
class SSurface { class SSurface {
public: public:
@ -172,7 +180,7 @@ public:
void TrimFromEdgeList(SEdgeList *el); void TrimFromEdgeList(SEdgeList *el);
void IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB, void IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
SShell *into); SShell *into);
void AllPointsIntersecting(Vector a, Vector b, List<Vector> *l); void AllPointsIntersecting(Vector a, Vector b, List<SInter> *l);
void ClosestPointTo(Vector p, double *u, double *v); void ClosestPointTo(Vector p, double *u, double *v);
Vector PointAt(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 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(void);
void AllPointsIntersecting(Vector a, Vector b, List<Vector> *il); void AllPointsIntersecting(Vector a, Vector b, List<SInter> *il);
void CleanupAfterBoolean(void); 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 MakeFromCopyOf(SShell *a);
void MakeFromTransformationOf(SShell *a, Vector trans, Quaternion q); void MakeFromTransformationOf(SShell *a, Vector trans, Quaternion q);

View File

@ -56,7 +56,7 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
// cases, just giving up for now // cases, just giving up for now
} }
void SSurface::AllPointsIntersecting(Vector a, Vector b, List<Vector> *l) { void SSurface::AllPointsIntersecting(Vector a, Vector b, List<SInter> *l) {
if(degm == 1 && degn == 1) { if(degm == 1 && degn == 1) {
// line-plane intersection // line-plane intersection
Vector p = ctrl[0][0]; Vector p = ctrl[0][0];
@ -74,16 +74,51 @@ void SSurface::AllPointsIntersecting(Vector a, Vector b, List<Vector> *l) {
Point2d puv, dummy = { 0, 0 }; Point2d puv, dummy = { 0, 0 };
ClosestPointTo(pi, &(puv.x), &(puv.y)); ClosestPointTo(pi, &(puv.x), &(puv.y));
if(bsp->ClassifyPoint(puv, dummy) != SBspUv::OUTSIDE) { 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<Vector> *il) { void SShell::AllPointsIntersecting(Vector a, Vector b, List<SInter> *il) {
SSurface *ss; SSurface *ss;
for(ss = surface.First(); ss; ss = surface.NextAfter(ss)) { for(ss = surface.First(); ss; ss = surface.NextAfter(ss)) {
ss->AllPointsIntersecting(a, b, il); ss->AllPointsIntersecting(a, b, il);
} }
} }
int SShell::ClassifyPoint(Vector p) {
List<SInter> 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;
}

View File

@ -667,7 +667,7 @@ double Point2d::Dot(Point2d p) {
double Point2d::DistanceToLine(Point2d p0, Point2d dp, bool segment) { double Point2d::DistanceToLine(Point2d p0, Point2d dp, bool segment) {
double m = dp.x*dp.x + dp.y*dp.y; 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 // 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; double t = (dp.x*(x - p0.x) + dp.y*(y - p0.y))/m;