Make surfaces of revolution with control points on the axis

triangulate correctly; don't screw up generating them, and make
sure that the ratpoly stuff doesn't blow up near the singularity.

[git-p4: depot-paths = "//depot/solvespace/": change = 1953]
This commit is contained in:
Jonathan Westhues 2009-05-18 00:18:32 -08:00
parent 40ed1b7ac1
commit 1d88f96e13
3 changed files with 105 additions and 53 deletions

View File

@ -301,17 +301,26 @@ Vector SSurface::NormalAt(double u, double v) {
} }
void SSurface::ClosestPointTo(Vector p, double *u, double *v, bool converge) { void SSurface::ClosestPointTo(Vector p, double *u, double *v, bool converge) {
int i, j; // A few special cases first; when control points are coincident the
// derivative goes to zero at the conrol points, and would result in
// nonconvergence. We avoid that here, and also guarantee a consistent
// (u, v) (of the infinitely many possible in one parameter).
if(p.Equals(ctrl[0] [0] )) { *u = 0; *v = 0; return; }
if(p.Equals(ctrl[degm][0] )) { *u = 1; *v = 0; return; }
if(p.Equals(ctrl[degm][degn])) { *u = 1; *v = 1; return; }
if(p.Equals(ctrl[0] [degn])) { *u = 0; *v = 1; return; }
// Search for a reasonable initial guess
int i, j;
if(degm == 1 && degn == 1) { if(degm == 1 && degn == 1) {
*u = *v = 0; // a plane, perfect no matter what the initial guess *u = *v = 0; // a plane, perfect no matter what the initial guess
} else { } else {
double minDist = VERY_POSITIVE; double minDist = VERY_POSITIVE;
double res = (max(degm, degn) == 2) ? 7.0 : 20.0; int res = (max(degm, degn) == 2) ? 7 : 20;
for(i = 0; i < (int)res; i++) { for(i = 0; i < res; i++) {
for(j = 0; j <= (int)res; j++) { for(j = 0; j < res; j++) {
double tryu = (i/res), tryv = (j/res); double tryu = (i + 0.5)/res, tryv = (j + 0.5)/res;
Vector tryp = PointAt(tryu, tryv); Vector tryp = PointAt(tryu, tryv);
double d = (tryp.Minus(p)).Magnitude(); double d = (tryp.Minus(p)).Magnitude();
if(d < minDist) { if(d < minDist) {
@ -323,7 +332,7 @@ void SSurface::ClosestPointTo(Vector p, double *u, double *v, bool converge) {
} }
} }
// Initial guess is in u, v // 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 ? 15 : 3); i++) {
p0 = PointAt(*u, *v); p0 = PointAt(*u, *v);

View File

@ -80,17 +80,25 @@ SSurface SSurface::FromRevolutionOf(SBezier *sb, Vector pt, Vector axis,
Vector ps = p.RotatedAbout(pt, axis, thetas), Vector ps = p.RotatedAbout(pt, axis, thetas),
pf = p.RotatedAbout(pt, axis, thetaf); pf = p.RotatedAbout(pt, axis, thetaf);
Vector c = ps.ClosestPointOnLine(pt, axis); Vector ct;
if(ps.Equals(pf)) {
// Degenerate case: a control point lies on the axis of revolution,
// so we get three coincident control points.
ct = ps;
} else {
// Normal case, the control point sweeps out a circle.
Vector c = ps.ClosestPointOnLine(pt, axis);
Vector rs = ps.Minus(c), Vector rs = ps.Minus(c),
rf = pf.Minus(c); rf = pf.Minus(c);
Vector ts = axis.Cross(rs), Vector ts = axis.Cross(rs),
tf = axis.Cross(rf); tf = axis.Cross(rf);
Vector ct = Vector::AtIntersectionOfLines(ps, ps.Plus(ts), ct = Vector::AtIntersectionOfLines(ps, ps.Plus(ts),
pf, pf.Plus(tf), pf, pf.Plus(tf),
NULL, NULL, NULL); NULL, NULL, NULL);
}
ret.ctrl[i][0] = ps; ret.ctrl[i][0] = ps;
ret.ctrl[i][1] = ct; ret.ctrl[i][1] = ct;
@ -489,20 +497,36 @@ void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis,
int color) int color)
{ {
ZERO(this); ZERO(this);
SBezierLoop *sbl;
// Normalize the axis direction so that the direction of revolution // Normalize the axis direction so that the direction of revolution
// ends up parallel to the normal of the sketch, on the side of the // ends up parallel to the normal of the sketch, on the side of the
// axis where the sketch is. // axis where the sketch is.
Vector pto = sbls->point, Vector pto;
ptc = pto.ClosestPointOnLine(pt, axis), double md = VERY_NEGATIVE;
for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) {
SBezier *sb;
for(sb = sbl->l.First(); sb; sb = sbl->l.NextAfter(sb)) {
// Choose the point farthest from the axis; we'll get garbage
// if we choose a point that lies on the axis, for example.
// (And our surface will be self-intersecting if the sketch
// spans the axis, so don't worry about that.)
Vector p = sb->Start();
double d = p.DistanceToLine(pt, axis);
if(d > md) {
md = d;
pto = p;
}
}
}
Vector ptc = pto.ClosestPointOnLine(pt, axis),
up = (pto.Minus(ptc)).WithMagnitude(1), up = (pto.Minus(ptc)).WithMagnitude(1),
vp = (sbls->normal).Cross(up); vp = (sbls->normal).Cross(up);
if(vp.Dot(axis) < 0) { if(vp.Dot(axis) < 0) {
axis = axis.ScaledBy(-1); axis = axis.ScaledBy(-1);
} }
SBezierLoop *sbl; // Now we actually build and trim the surfaces.
for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) { for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) {
int i, j; int i, j;
SBezier *sb, *prev; SBezier *sb, *prev;
@ -516,11 +540,20 @@ void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis,
for(sb = sbl->l.First(); sb; sb = sbl->l.NextAfter(sb)) { for(sb = sbl->l.First(); sb; sb = sbl->l.NextAfter(sb)) {
Revolved revs; Revolved revs;
for(j = 0; j < 4; j++) { for(j = 0; j < 4; j++) {
SSurface ss = SSurface::FromRevolutionOf(sb, pt, axis, if(sb->deg == 1 &&
(PI/2)*j, (sb->ctrl[0]).DistanceToLine(pt, axis) < LENGTH_EPS &&
(PI/2)*(j+1)); (sb->ctrl[1]).DistanceToLine(pt, axis) < LENGTH_EPS)
ss.color = color; {
revs.d[j] = surface.AddAndAssignId(&ss); // This is a line on the axis of revolution; it does
// not contribute a surface.
revs.d[j].v = 0;
} else {
SSurface ss = SSurface::FromRevolutionOf(sb, pt, axis,
(PI/2)*j,
(PI/2)*(j+1));
ss.color = color;
revs.d[j] = surface.AddAndAssignId(&ss);
}
} }
hsl.Add(&revs); hsl.Add(&revs);
} }
@ -533,44 +566,54 @@ void SShell::MakeFromRevolutionOf(SBezierLoopSet *sbls, Vector pt, Vector axis,
prev = &(sbl->l.elem[WRAP(i-1, sbl->l.n)]); prev = &(sbl->l.elem[WRAP(i-1, sbl->l.n)]);
for(j = 0; j < 4; j++) { for(j = 0; j < 4; j++) {
SCurve sc;
Quaternion qs = Quaternion::From(axis, (PI/2)*j); Quaternion qs = Quaternion::From(axis, (PI/2)*j);
// we want Q*(x - p) + p = Q*x + (p - Q*p) // we want Q*(x - p) + p = Q*x + (p - Q*p)
Vector ts = pt.Minus(qs.Rotate(pt)); Vector ts = pt.Minus(qs.Rotate(pt));
SCurve sc; // If this input curve generate a surface, then trim that
ZERO(&sc); // surface with the rotated version of the input curve.
sc.isExact = true; if(revs.d[j].v) {
sc.exact = sb->TransformedBy(ts, qs); ZERO(&sc);
(sc.exact).MakePwlInto(&(sc.pts)); sc.isExact = true;
sc.surfA = revs.d[j]; sc.exact = sb->TransformedBy(ts, qs);
sc.surfB = revs.d[WRAP(j-1, 4)]; (sc.exact).MakePwlInto(&(sc.pts));
sc.surfA = revs.d[j];
sc.surfB = revs.d[WRAP(j-1, 4)];
hSCurve hcb = curve.AddAndAssignId(&sc); hSCurve hcb = curve.AddAndAssignId(&sc);
STrimBy stb; STrimBy stb;
stb = STrimBy::EntireCurve(this, hcb, true); stb = STrimBy::EntireCurve(this, hcb, true);
(surface.FindById(sc.surfA))->trim.Add(&stb); (surface.FindById(sc.surfA))->trim.Add(&stb);
stb = STrimBy::EntireCurve(this, hcb, false); stb = STrimBy::EntireCurve(this, hcb, false);
(surface.FindById(sc.surfB))->trim.Add(&stb); (surface.FindById(sc.surfB))->trim.Add(&stb);
}
SSurface *ss = surface.FindById(sc.surfA); // And if this input curve and the one after it both generated
// surfaces, then trim both of those by the appropriate
// circle.
if(revs.d[j].v && revsp.d[j].v) {
SSurface *ss = surface.FindById(revs.d[j]);
ZERO(&sc); ZERO(&sc);
sc.isExact = true; sc.isExact = true;
sc.exact = SBezier::From(ss->ctrl[0][0], sc.exact = SBezier::From(ss->ctrl[0][0],
ss->ctrl[0][1], ss->ctrl[0][1],
ss->ctrl[0][2]); ss->ctrl[0][2]);
sc.exact.weight[1] = ss->weight[0][1]; sc.exact.weight[1] = ss->weight[0][1];
(sc.exact).MakePwlInto(&(sc.pts)); (sc.exact).MakePwlInto(&(sc.pts));
sc.surfA = revs.d[j]; sc.surfA = revs.d[j];
sc.surfB = revsp.d[j]; sc.surfB = revsp.d[j];
hSCurve hcc = curve.AddAndAssignId(&sc); hSCurve hcc = curve.AddAndAssignId(&sc);
stb = STrimBy::EntireCurve(this, hcc, false); STrimBy stb;
(surface.FindById(sc.surfA))->trim.Add(&stb); stb = STrimBy::EntireCurve(this, hcc, false);
stb = STrimBy::EntireCurve(this, hcc, true); (surface.FindById(sc.surfA))->trim.Add(&stb);
(surface.FindById(sc.surfB))->trim.Add(&stb); stb = STrimBy::EntireCurve(this, hcc, true);
(surface.FindById(sc.surfB))->trim.Add(&stb);
}
} }
} }

View File

@ -1,7 +1,7 @@
marching algorithm for surface intersection marching algorithm for surface intersection
tangent intersections tangent intersections
surfaces with coincident control points put back the meshes
assembly assembly
----- -----