Now we are actually marching. There seems to be either a numerical

problem or a tendency to generate backwards edges or both, need to
debug that. But it generates the curve, and begins to work.

And change the edge classification. Now instead of testing for
point-on-surface using the results of the raycasting, test for
point-on-surface as a separate step. That stops us from picking up
the additional numerical error from the surface-line intersection,
which may be significant if the ray is parallel or almost parallel
to the surface.

[git-p4: depot-paths = "//depot/solvespace/": change = 1991]
This commit is contained in:
Jonathan Westhues 2009-06-21 01:02:36 -08:00
parent 666ea1c047
commit d3dcd8fb23
6 changed files with 145 additions and 47 deletions

View File

@ -240,11 +240,11 @@ void Entity::GenerateBezierCurves(SBezierList *sbl) {
ArcGetAngles(&thetaa, &thetab, &dtheta); ArcGetAngles(&thetaa, &thetab, &dtheta);
} }
int i, n; int i, n;
if(dtheta > 3*PI/2) { if(dtheta > (3*PI/2 + 0.01)) {
n = 4; n = 4;
} else if(dtheta > PI) { } else if(dtheta > (PI + 0.01)) {
n = 3; n = 3;
} else if(dtheta > PI/2) { } else if(dtheta > (PI/2 + 0.01)) {
n = 2; n = 2;
} else { } else {
n = 1; n = 1;

2
dsc.h
View File

@ -74,7 +74,7 @@ public:
Vector DotInToCsys(Vector u, Vector v, Vector n); Vector DotInToCsys(Vector u, Vector v, Vector n);
Vector ScaleOutOfCsys(Vector u, Vector v, Vector n); Vector ScaleOutOfCsys(Vector u, Vector v, Vector n);
double DistanceToLine(Vector p0, Vector dp); double DistanceToLine(Vector p0, Vector dp);
bool OnLineSegment(Vector a, Vector b); bool OnLineSegment(Vector a, Vector b, double tol=LENGTH_EPS);
Vector ClosestPointOnLine(Vector p0, Vector dp); Vector ClosestPointOnLine(Vector p0, Vector dp);
double Magnitude(void); double Magnitude(void);
double MagSquared(void); double MagSquared(void);

View File

@ -40,6 +40,7 @@ public:
int ear; int ear;
Vector p; Vector p;
Vector auxv;
}; };
class SPointList { class SPointList {

View File

@ -147,7 +147,7 @@ void SBezier::ClosestPointTo(Vector p, double *t, bool converge) {
} }
Vector p0; Vector p0;
for(i = 0; i < (converge ? 15 : 3); i++) { for(i = 0; i < (converge ? 15 : 5); i++) {
p0 = PointAt(*t); p0 = PointAt(*t);
if(p0.Equals(p, RATPOLY_EPS)) { if(p0.Equals(p, RATPOLY_EPS)) {
return; return;
@ -345,7 +345,7 @@ void SSurface::ClosestPointTo(Vector p, double *u, double *v, bool converge) {
// Initial guess is in u, v; refine by Newton iteration. // Initial guess is in u, v; refine by Newton iteration.
Vector p0; Vector p0;
for(i = 0; i < (converge ? 15 : 3); i++) { for(i = 0; i < (converge ? 25 : 5); i++) {
p0 = PointAt(*u, *v); p0 = PointAt(*u, *v);
if(converge) { if(converge) {
if(p0.Equals(p, RATPOLY_EPS)) { if(p0.Equals(p, RATPOLY_EPS)) {
@ -443,6 +443,10 @@ Vector SSurface::ClosestPointOnThisAndSurface(SSurface *srf2, Vector p) {
puv[j].y += dv / ((tv[j]).MagSquared()); puv[j].y += dv / ((tv[j]).MagSquared());
} }
} }
if(i >= 10) {
dbp("this and srf, didn't converge, d=%g",
(puv[0].Minus(puv[1])).Magnitude());
}
// If this converged, then the two points are actually equal. // If this converged, then the two points are actually equal.
return ((srf[0])->PointAt(puv[0])).Plus( return ((srf[0])->PointAt(puv[0])).Plus(
@ -461,7 +465,7 @@ void SSurface::PointOnSurfaces(SSurface *s1, SSurface *s2,
(srf[2])->ClosestPointTo(p, &(u[2]), &(v[2]), false); (srf[2])->ClosestPointTo(p, &(u[2]), &(v[2]), false);
int i, j; int i, j;
for(i = 0; i < 15; i++) { for(i = 0; i < 20; i++) {
// Approximate each surface by a plane // Approximate each surface by a plane
Vector p[3], tu[3], tv[3], n[3]; Vector p[3], tu[3], tv[3], n[3];
double d[3]; double d[3];

View File

@ -51,7 +51,7 @@ void SSurface::AddExactIntersectionCurve(SBezier *sb, SSurface *srfB,
split = sc.MakeCopySplitAgainst(agnstA, agnstB, this, srfB); split = sc.MakeCopySplitAgainst(agnstA, agnstB, this, srfB);
sc.Clear(); sc.Clear();
} }
if(0 && sb->deg == 1) { if(0 && sb->deg == 1) {
dbp(" "); dbp(" ");
SCurvePt *prev = NULL, *v; SCurvePt *prev = NULL, *v;
@ -192,7 +192,6 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
Vector al = along.ScaledBy(0.5); Vector al = along.ScaledBy(0.5);
SBezier bezier; SBezier bezier;
bezier = SBezier::From((si->p).Minus(al), (si->p).Plus(al)); bezier = SBezier::From((si->p).Minus(al), (si->p).Plus(al));
AddExactIntersectionCurve(&bezier, b, agnstA, agnstB, into); AddExactIntersectionCurve(&bezier, b, agnstA, agnstB, into);
} }
@ -313,10 +312,21 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
for(si = lsi.First(); si; si = lsi.NextAfter(si)) { for(si = lsi.First(); si; si = lsi.NextAfter(si)) {
Vector p = si->p; Vector p = si->p;
double u, v; double u, v;
srfA->ClosestPointTo(p, &u, &v); srfB->ClosestPointTo(p, &u, &v);
srfA->PointOnSurfaces(srfB, other, &u, &v); srfB->PointOnSurfaces(srfA, other, &u, &v);
p = srfA->PointAt(u, v); p = srfB->PointAt(u, v);
if(!spl.ContainsPoint(p)) spl.Add(p); if(!spl.ContainsPoint(p)) {
SPoint sp;
sp.p = p;
// We also need the edge normal, so that we know in
// which direction to march.
srfA->ClosestPointTo(p, &u, &v);
Vector n = srfA->NormalAt(u, v);
sp.auxv = n.Cross((se->b).Minus(se->a));
sp.auxv = (sp.auxv).WithMagnitude(1);
spl.l.Add(&sp);
}
} }
lsi.Clear(); lsi.Clear();
} }
@ -324,9 +334,7 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
el.Clear(); el.Clear();
} }
SPoint *sp; while(spl.l.n >= 2) {
if(spl.l.n == 2) {
SCurve sc; SCurve sc;
ZERO(&sc); ZERO(&sc);
sc.surfA = h; sc.surfA = h;
@ -334,16 +342,82 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
sc.isExact = false; sc.isExact = false;
sc.source = SCurve::FROM_INTERSECTION; sc.source = SCurve::FROM_INTERSECTION;
SCurvePt scpt; Vector start = spl.l.elem[0].p,
scpt.p = (spl.l.elem[0].p); startv = spl.l.elem[0].auxv;
sc.pts.Add(&scpt); spl.l.ClearTags();
scpt.p = (spl.l.elem[1].p); spl.l.elem[0].tag = 1;
sc.pts.Add(&scpt); spl.l.RemoveTagged();
SCurvePt padd;
ZERO(&padd);
padd.vertex = true;
padd.p = start;
sc.pts.Add(&padd);
Point2d pa, pb;
Vector np, npc;
// Better to start with a too-small step, so that we don't miss
// features of the curve entirely.
bool fwd;
double tol, step = SS.ChordTolMm();
for(a = 0; a < 100; a++) {
ClosestPointTo(start, &pa);
b->ClosestPointTo(start, &pb);
Vector na = NormalAt(pa).WithMagnitude(1),
nb = b->NormalAt(pb).WithMagnitude(1);
if(a == 0) {
Vector dp = nb.Cross(na);
if(dp.Dot(startv) < 0) {
// We want to march in the more inward direction.
fwd = true;
} else {
fwd = false;
}
}
int i = 0;
do {
if(i++ > 20) break;
Vector dp = nb.Cross(na);
if(!fwd) dp = dp.ScaledBy(-1);
dp = dp.WithMagnitude(step);
np = start.Plus(dp);
npc = ClosestPointOnThisAndSurface(b, np);
tol = (npc.Minus(np)).Magnitude();
if(tol > SS.ChordTolMm()*0.8) {
step *= 0.95;
} else {
step /= 0.95;
}
} while(tol > SS.ChordTolMm() || tol < SS.ChordTolMm()/2);
SPoint *sp;
for(sp = spl.l.First(); sp; sp = spl.l.NextAfter(sp)) {
if((sp->p).OnLineSegment(start, npc, 2*SS.ChordTolMm())) {
sp->tag = 1;
a = 1000;
npc = sp->p;
}
}
padd.p = npc;
padd.vertex = (a == 1000);
sc.pts.Add(&padd);
start = npc;
}
spl.l.RemoveTagged();
// And now we split and insert the curve
SCurve split = sc.MakeCopySplitAgainst(agnstA, agnstB, this, b); SCurve split = sc.MakeCopySplitAgainst(agnstA, agnstB, this, b);
sc.Clear(); sc.Clear();
into->curve.AddAndAssignId(&split); into->curve.AddAndAssignId(&split);
break;
} }
spl.Clear(); spl.Clear();
} }
@ -848,13 +922,46 @@ bool SShell::ClassifyEdge(int *indir, int *outdir,
} }
if(edge_inters != 0) dbp("bad, edge_inters=%d", edge_inters); if(edge_inters != 0) dbp("bad, edge_inters=%d", edge_inters);
// Next, check for edge-on-surface. The ray-casting for edge-inside-shell
// would catch this too, but test separately, for speed (since many edges
// are on surface) and for numerical stability, so we don't pick up
// the additional error from the line intersection.
for(srf = surface.First(); srf; srf = surface.NextAfter(srf)) {
if(srf->LineEntirelyOutsideBbox(ea, eb, true)) continue;
Point2d puv;
srf->ClosestPointTo(p, &(puv.x), &(puv.y), false);
Vector pp = srf->PointAt(puv);
if((pp.Minus(p)).Magnitude() > LENGTH_EPS) continue;
Point2d dummy = { 0, 0 }, ia = { 0, 0 }, ib = { 0, 0 };
int c = srf->bsp->ClassifyPoint(puv, dummy, &ia, &ib);
if(c == SBspUv::OUTSIDE) continue;
// Edge-on-face (unless edge-on-edge above superceded)
Point2d pin, pout;
srf->ClosestPointTo(p.Plus(edge_n_in), &pin, false);
srf->ClosestPointTo(p.Plus(edge_n_out), &pout, false);
Vector surf_n_in = srf->NormalAt(pin),
surf_n_out = srf->NormalAt(pout);
*indir = ClassifyRegion(edge_n_in, surf_n_in, surf_n);
*outdir = ClassifyRegion(edge_n_out, surf_n_out, surf_n);
return true;
}
// Edge is not on face or on edge; so it's either inside or outside
// the shell, and we'll determine which by raycasting.
int cnt = 0; 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
// testing) // testing)
Vector ray = Vector::From(Random(1), Random(1), Random(1)); Vector ray = Vector::From(Random(1), Random(1), Random(1));
AllPointsIntersecting( AllPointsIntersecting(
p.Minus(ray), p.Plus(ray), &l, false, true, false); p.Minus(ray), p.Plus(ray), &l, false, true, false);
@ -883,28 +990,14 @@ bool SShell::ClassifyEdge(int *indir, int *outdir,
if(d < dmin) { if(d < dmin) {
dmin = d; dmin = d;
// Edge does not lie on surface; either strictly inside
if(d < LENGTH_EPS) { // or strictly outside
// Edge-on-face (unless edge-on-edge above supercedes) if((si->surfNormal).Dot(ray) > 0) {
Point2d pin, pout; *indir = INSIDE;
(si->srf)->ClosestPointTo(p.Plus(edge_n_in), &pin, false); *outdir = INSIDE;
(si->srf)->ClosestPointTo(p.Plus(edge_n_out), &pout, false);
Vector surf_n_in = (si->srf)->NormalAt(pin),
surf_n_out = (si->srf)->NormalAt(pout);
*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 *indir = OUTSIDE;
// or strictly outside *outdir = OUTSIDE;
if((si->surfNormal).Dot(ray) > 0) {
*indir = INSIDE;
*outdir = INSIDE;
} else {
*indir = OUTSIDE;
*outdir = OUTSIDE;
}
} }
onEdge = si->onEdge; onEdge = si->onEdge;
} }

View File

@ -459,15 +459,15 @@ double Vector::DistanceToLine(Vector p0, Vector dp) {
return ((this->Minus(p0)).Cross(dp)).Magnitude() / m; return ((this->Minus(p0)).Cross(dp)).Magnitude() / m;
} }
bool Vector::OnLineSegment(Vector a, Vector b) { bool Vector::OnLineSegment(Vector a, Vector b, double tol) {
if(this->Equals(a) || this->Equals(b)) return true; if(this->Equals(a, tol) || this->Equals(b, tol)) return true;
Vector d = b.Minus(a); Vector d = b.Minus(a);
double m = d.MagSquared(); double m = d.MagSquared();
double distsq = ((this->Minus(a)).Cross(d)).MagSquared() / m; double distsq = ((this->Minus(a)).Cross(d)).MagSquared() / m;
if(distsq >= LENGTH_EPS*LENGTH_EPS) return false; if(distsq >= tol*tol) return false;
double t = (this->Minus(a)).DivPivoting(d); double t = (this->Minus(a)).DivPivoting(d);
// On-endpoint already tested // On-endpoint already tested