Clean up the marching step size / chord tol stuff, and add code to

export an inexact curve by approximating it with piecwise cubic
segments (whose endpoints lie exactly on the curve, and with exact
tangent directions at the endpoints).

[git-p4: depot-paths = "//depot/solvespace/": change = 1995]
solver
Jonathan Westhues 2009-06-21 18:54:09 -08:00
parent 684ba7deb1
commit bd36221219
3 changed files with 113 additions and 12 deletions

View File

@ -261,6 +261,26 @@ void SSurface::MakeEdgesInto(SShell *shell, SEdgeList *sel, bool asUv,
}
}
//-----------------------------------------------------------------------------
// Compute the exact tangent to the intersection curve between two surfaces,
// by taking the cross product of the surface normals. We choose the direction
// of this tangent so that its dot product with dir is positive.
//-----------------------------------------------------------------------------
Vector SSurface::ExactSurfaceTangentAt(Vector p, SSurface *srfA, SSurface *srfB,
Vector dir)
{
Point2d puva, puvb;
srfA->ClosestPointTo(p, &puva);
srfB->ClosestPointTo(p, &puvb);
Vector ts = (srfA->NormalAt(puva)).Cross(
(srfB->NormalAt(puvb)));
ts = ts.WithMagnitude(1);
if(ts.Dot(dir) < 0) {
ts = ts.ScaledBy(-1);
}
return ts;
}
//-----------------------------------------------------------------------------
// Report our trim curves. If a trim curve is exact and sbl is not null, then
// add its exact form to sbl. Otherwise, add its piecewise linearization to
@ -293,6 +313,76 @@ void SSurface::MakeSectionEdgesInto(SShell *shell,
keep_aft.SplitAt(tf, &keep_bef, &junk_aft);
sbl->l.Add(&keep_bef);
} else if(sbl && !sel && !sc->isExact) {
// We must approximate this trim curve, as piecewise cubic sections.
SSurface *srfA = shell->surface.FindById(sc->surfA),
*srfB = shell->surface.FindById(sc->surfB);
Vector s = stb->backwards ? stb->finish : stb->start,
f = stb->backwards ? stb->start : stb->finish;
int sp, fp;
for(sp = 0; sp < sc->pts.n; sp++) {
if(s.Equals(sc->pts.elem[sp].p)) break;
}
if(sp >= sc->pts.n) return;
for(fp = sp; fp < sc->pts.n; fp++) {
if(f.Equals(sc->pts.elem[fp].p)) break;
}
if(fp >= sc->pts.n) return;
// So now the curve we want goes from elem[sp] to elem[fp]
while(sp < fp) {
// Initially, we'll try approximating the entire trim curve
// as a single Bezier segment
int fpt = fp;
for(;;) {
// So construct a cubic Bezier with the correct endpoints
// and tangents for the current span.
Vector st = sc->pts.elem[sp].p,
ft = sc->pts.elem[fpt].p,
sf = ft.Minus(st);
double m = sf.Magnitude() / 3;
Vector stan = ExactSurfaceTangentAt(st, srfA, srfB, sf),
ftan = ExactSurfaceTangentAt(ft, srfA, srfB, sf);
SBezier sb = SBezier::From(st,
st.Plus (stan.WithMagnitude(m)),
ft.Minus(ftan.WithMagnitude(m)),
ft);
// And test how much this curve deviates from the
// intermediate points (if any).
int i;
bool tooFar = false;
for(i = sp + 1; i <= (fpt - 1); i++) {
Vector p = sc->pts.elem[i].p;
double t;
sb.ClosestPointTo(p, &t, false);
Vector pp = sb.PointAt(t);
if((pp.Minus(p)).Magnitude() > SS.ChordTolMm()/2) {
tooFar = true;
break;
}
}
if(tooFar) {
// Deviates by too much, so try a shorter span
fpt--;
continue;
} else {
// Okay, so use this piece and break.
sbl->l.Add(&sb);
break;
}
}
// And continue interpolating, starting wherever the curve
// we just generated finishes.
sp = fpt;
}
} else {
if(sel) MakeTrimEdgesInto(sel, false, sc, stb);
}

View File

@ -278,6 +278,8 @@ public:
void MakeTrimEdgesInto(SEdgeList *sel, bool asUv, SCurve *sc, STrimBy *stb);
void MakeEdgesInto(SShell *shell, SEdgeList *sel, bool asUv,
SShell *useCurvesFrom=NULL);
Vector ExactSurfaceTangentAt(Vector p, SSurface *srfA, SSurface *srfB,
Vector dir);
void MakeSectionEdgesInto(SShell *shell, SEdgeList *sel, SBezierList *sbl);
void MakeClassifyingBsp(SShell *shell, SShell *useCurvesFrom);
double ChordToleranceForEdge(Vector a, Vector b);

View File

@ -349,6 +349,12 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
spl.l.ClearTags();
spl.l.elem[0].tag = 1;
spl.l.RemoveTagged();
// Our chord tolerance is whatever the user specified
double maxtol = SS.ChordTolMm();
int maxsteps = max(300, SS.maxSegments*3);
// The curve starts at our starting point.
SCurvePt padd;
ZERO(&padd);
padd.vertex = true;
@ -357,11 +363,11 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
Point2d pa, pb;
Vector np, npc;
bool fwd;
// 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++) {
double tol, step = maxtol;
for(a = 0; a < maxsteps; a++) {
ClosestPointTo(start, &pa);
b->ClosestPointTo(start, &pb);
@ -378,10 +384,8 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
}
}
int i = 0;
do {
if(i++ > 20) break;
int i;
for(i = 0; i < 20; i++) {
Vector dp = nb.Cross(na);
if(!fwd) dp = dp.ScaledBy(-1);
dp = dp.WithMagnitude(step);
@ -390,24 +394,30 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
npc = ClosestPointOnThisAndSurface(b, np);
tol = (npc.Minus(np)).Magnitude();
if(tol > SS.ChordTolMm()*0.8) {
if(tol > maxtol*0.8) {
step *= 0.95;
} else {
step /= 0.95;
}
} while(tol > SS.ChordTolMm() || tol < SS.ChordTolMm()/2);
if((tol < maxtol) && (tol > maxtol/2)) {
// If we meet the chord tolerance test, and we're
// not too fine, then we break out.
break;
}
}
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;
a = maxsteps;
npc = sp->p;
}
}
padd.p = npc;
padd.vertex = (a == 1000);
padd.vertex = (a == maxsteps);
sc.pts.Add(&padd);
start = npc;
@ -419,7 +429,6 @@ void SSurface::IntersectAgainst(SSurface *b, SShell *agnstA, SShell *agnstB,
SCurve split = sc.MakeCopySplitAgainst(agnstA, agnstB, this, b);
sc.Clear();
into->curve.AddAndAssignId(&split);
break;
}
spl.Clear();
}