Generate the group's polygon from the exact curves, not from edges;

so now we've got the exact curve loops, with their direction
standardized so that we can tell which direction is out. We still
need the polygon in any case, since that's a convenient way to find
each curve's winding number.

And remove some more leftover code from mesh sweeps.

[git-p4: depot-paths = "//depot/solvespace/": change = 1897]
This commit is contained in:
Jonathan Westhues 2009-01-18 19:33:15 -08:00
parent 7a874c20c0
commit 0e623c90c0
10 changed files with 164 additions and 114 deletions

View File

@ -192,6 +192,12 @@ void Entity::GeneratePolyCurves(SPolyCurveList *spcl) {
double r = CircleGetRadiusNum(); double r = CircleGetRadiusNum();
double thetaa, thetab, dtheta; double thetaa, thetab, dtheta;
if(r < LENGTH_EPS) {
// If a circle or an arc gets dragged through zero radius,
// then we just don't generate anything.
break;
}
if(type == CIRCLE) { if(type == CIRCLE) {
thetaa = 0; thetaa = 0;
thetab = 2*PI; thetab = 2*PI;

7
dsc.h
View File

@ -134,6 +134,13 @@ public:
n = dest; n = dest;
// and elemsAllocated is untouched, because we didn't resize // and elemsAllocated is untouched, because we didn't resize
} }
void Reverse(void) {
int i;
for(i = 0; i < (n/2); i++) {
SWAP(T, elem[i], elem[(n-1)-i]);
}
}
}; };
// A list, where each element has an integer identifier. The list is kept // A list, where each element has an integer identifier. The list is kept

View File

@ -222,7 +222,7 @@ void SolveSpace::GenerateAll(int first, int last, bool andFindFree) {
// The group falls inside the range, so really solve it, // The group falls inside the range, so really solve it,
// and then regenerate the mesh based on the solved stuff. // and then regenerate the mesh based on the solved stuff.
SolveGroup(g->h, andFindFree); SolveGroup(g->h, andFindFree);
g->GeneratePolygon(); g->GenerateLoops();
g->GenerateMesh(); g->GenerateMesh();
g->clean = true; g->clean = true;
} else { } else {

View File

@ -2,112 +2,50 @@
#define gs (SS.GW.gs) #define gs (SS.GW.gs)
bool Group::AssemblePolygon(SPolygon *p, SEdge *error) { bool Group::AssembleLoops(void) {
SEdgeList edges; ZERO(&edges); SPolyCurveList spcl;
ZERO(&spcl);
int i; int i;
for(i = 0; i < SS.entity.n; i++) { for(i = 0; i < SS.entity.n; i++) {
Entity *e = &(SS.entity.elem[i]); Entity *e = &(SS.entity.elem[i]);
if(e->group.v != h.v) continue; if(e->group.v != h.v) continue;
if(e->construction) continue;
e->GenerateEdges(&edges); e->GeneratePolyCurves(&spcl);
} }
bool ret = edges.AssemblePolygon(p, error);
edges.Clear(); bool allClosed;
return ret; curveLoops = SPolyCurveLoops::From(&spcl, &poly,
&allClosed, &(polyError.notClosedAt));
spcl.Clear();
return allClosed;
} }
void Group::GeneratePolygon(void) { void Group::GenerateLoops(void) {
poly.Clear(); poly.Clear();
curveLoops.Clear();
if(type == DRAWING_3D || type == DRAWING_WORKPLANE || if(type == DRAWING_3D || type == DRAWING_WORKPLANE ||
type == ROTATE || type == TRANSLATE || type == IMPORTED) type == ROTATE || type == TRANSLATE || type == IMPORTED)
{ {
if(AssemblePolygon(&poly, &(polyError.notClosedAt))) { if(AssembleLoops()) {
polyError.how = POLY_GOOD; polyError.how = POLY_GOOD;
poly.normal = poly.ComputeNormal();
poly.FixContourDirections();
if(!poly.AllPointsInPlane(&(polyError.notCoplanarAt))) { if(!poly.AllPointsInPlane(&(polyError.notCoplanarAt))) {
// The edges aren't all coplanar; so not a good polygon // The edges aren't all coplanar; so not a good polygon
polyError.how = POLY_NOT_COPLANAR; polyError.how = POLY_NOT_COPLANAR;
poly.Clear(); poly.Clear();
curveLoops.Clear();
} }
} else { } else {
polyError.how = POLY_NOT_CLOSED; polyError.how = POLY_NOT_CLOSED;
poly.Clear(); poly.Clear();
curveLoops.Clear();
} }
} }
} }
void Group::GetTrajectory(hGroup hg, SContour *traj, SPolygon *section) {
if(section->IsEmpty()) return;
SEdgeList edges; ZERO(&edges);
int i, j;
for(i = 0; i < SS.entity.n; i++) {
Entity *e = &(SS.entity.elem[i]);
if(e->group.v != hg.v) continue;
e->GenerateEdges(&edges);
}
Vector pn = (section->normal).WithMagnitude(1);
double pd = pn.Dot(section->AnyPoint());
// Find the start of the trajectory
Vector first, last;
for(i = 0; i < edges.l.n; i++) {
SEdge *se = &(edges.l.elem[i]);
bool startA = true, startB = true;
for(j = 0; j < edges.l.n; j++) {
if(i == j) continue;
SEdge *set = &(edges.l.elem[j]);
if((set->a).Equals(se->a)) startA = false;
if((set->b).Equals(se->a)) startA = false;
if((set->a).Equals(se->b)) startB = false;
if((set->b).Equals(se->b)) startB = false;
}
if(startA || startB) {
// It's possible for both to be true, if only one segment exists
if(startA) {
first = se->a;
last = se->b;
} else {
first = se->b;
last = se->a;
}
se->tag = 1;
break;
}
}
if(i >= edges.l.n) goto cleanup;
edges.AssembleContour(first, last, traj, NULL);
if(traj->l.n < 1) goto cleanup;
// Starting and ending points of the trajectory
Vector ps, pf;
ps = traj->l.elem[0].p;
pf = traj->l.elem[traj->l.n - 1].p;
// Distances of those points to the section plane
double ds = fabs(pn.Dot(ps) - pd), df = fabs(pn.Dot(pf) - pd);
if(ds < LENGTH_EPS && df < LENGTH_EPS) {
if(section->WindingNumberForPoint(pf) > 0) {
// Both the start and finish lie on the section plane; let the
// start be the one that's somewhere within the section. Use
// winding > 0, not odd/even, since it's natural e.g. to sweep
// a ring to make a pipe, and draw the trajectory through the
// center of the ring.
traj->Reverse();
}
} else if(ds > df) {
// The starting point is the endpoint that's closer to the plane
traj->Reverse();
}
cleanup:
edges.Clear();
}
void Group::AddQuadWithNormal(STriMeta meta, Vector out, void Group::AddQuadWithNormal(STriMeta meta, Vector out,
Vector a, Vector b, Vector c, Vector d) Vector a, Vector b, Vector c, Vector d)
{ {

View File

@ -181,7 +181,6 @@ bool SContour::IsClockwiseProjdToNormal(Vector n) {
area += ((v0 + v1)/2)*(u1 - u0); area += ((v0 + v1)/2)*(u1 - u0);
} }
return (area < 0); return (area < 0);
} }
@ -224,13 +223,7 @@ bool SContour::AllPointsInPlane(Vector n, double d, Vector *notCoplanarAt) {
} }
void SContour::Reverse(void) { void SContour::Reverse(void) {
int i; l.Reverse();
for(i = 0; i < (l.n / 2); i++) {
int i2 = (l.n - 1) - i;
SPoint t = l.elem[i2];
l.elem[i2] = l.elem[i];
l.elem[i] = t;
}
} }
@ -277,6 +270,9 @@ int SPolygon::WindingNumberForPoint(Vector p) {
} }
void SPolygon::FixContourDirections(void) { void SPolygon::FixContourDirections(void) {
// At output, the contour's tag will be 1 if we reversed it, else 0.
l.ClearTags();
// Outside curve looks counterclockwise, projected against our normal. // Outside curve looks counterclockwise, projected against our normal.
int i, j; int i, j;
for(i = 0; i < l.n; i++) { for(i = 0; i < l.n; i++) {
@ -296,6 +292,7 @@ void SPolygon::FixContourDirections(void) {
bool clockwise = sc->IsClockwiseProjdToNormal(normal); bool clockwise = sc->IsClockwiseProjdToNormal(normal);
if(clockwise && outer || (!clockwise && !outer)) { if(clockwise && outer || (!clockwise && !outer)) {
sc->Reverse(); sc->Reverse();
sc->tag = 1;
} }
} }
} }

View File

@ -34,6 +34,7 @@ public:
class SContour { class SContour {
public: public:
int tag;
List<SPoint> l; List<SPoint> l;
void AddPoint(Vector p); void AddPoint(Vector p);

View File

@ -137,6 +137,7 @@ public:
} predef; } predef;
SPolygon poly; SPolygon poly;
SPolyCurveLoops curveLoops;
static const int POLY_GOOD = 0; static const int POLY_GOOD = 0;
static const int POLY_NOT_CLOSED = 1; static const int POLY_NOT_CLOSED = 1;
static const int POLY_NOT_COPLANAR = 2; static const int POLY_NOT_COPLANAR = 2;
@ -198,12 +199,12 @@ public:
void AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index); void AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index);
void GenerateEquations(IdList<Equation,hEquation> *l); void GenerateEquations(IdList<Equation,hEquation> *l);
// Assembling piecewise linear sections into polygons // Assembling the curves into loops, and into a piecewise linear polygon
bool AssemblePolygon(SPolygon *p, SEdge *error); // at the same time.
void GeneratePolygon(void); bool AssembleLoops(void);
void GenerateLoops(void);
// And the mesh stuff // And the mesh stuff
SMesh *PreviousGroupMesh(void); SMesh *PreviousGroupMesh(void);
void GetTrajectory(hGroup hg, SContour *traj, SPolygon *section);
void AddQuadWithNormal(STriMeta meta, Vector out, void AddQuadWithNormal(STriMeta meta, Vector out,
Vector a, Vector b, Vector c, Vector d); Vector a, Vector b, Vector c, Vector d);
void GenerateMeshForStepAndRepeat(void); void GenerateMeshForStepAndRepeat(void);

View File

@ -77,7 +77,7 @@ Vector SPolyCurve::Finish(void) {
return ctrl[deg]; return ctrl[deg];
} }
Vector SPolyCurve::EvalAt(double t) { Vector SPolyCurve::PointAt(double t) {
Vector pt = Vector::From(0, 0, 0); Vector pt = Vector::From(0, 0, 0);
double d = 0; double d = 0;
@ -97,15 +97,15 @@ void SPolyCurve::MakePwlInto(List<Vector> *l) {
} }
void SPolyCurve::MakePwlWorker(List<Vector> *l, double ta, double tb) { void SPolyCurve::MakePwlWorker(List<Vector> *l, double ta, double tb) {
Vector pa = EvalAt(ta); Vector pa = PointAt(ta);
Vector pb = EvalAt(tb); Vector pb = PointAt(tb);
// Can't test in the middle, or certain cubics would break. // Can't test in the middle, or certain cubics would break.
double tm1 = (2*ta + tb) / 3; double tm1 = (2*ta + tb) / 3;
double tm2 = (ta + 2*tb) / 3; double tm2 = (ta + 2*tb) / 3;
Vector pm1 = EvalAt(tm1); Vector pm1 = PointAt(tm1);
Vector pm2 = EvalAt(tm2); Vector pm2 = PointAt(tm2);
double d = max(pm1.DistanceToLine(pa, pb.Minus(pa)), double d = max(pm1.DistanceToLine(pa, pb.Minus(pa)),
pm2.DistanceToLine(pa, pb.Minus(pa))); pm2.DistanceToLine(pa, pb.Minus(pa)));
@ -135,7 +135,8 @@ void SPolyCurveList::Clear(void) {
l.Clear(); l.Clear();
} }
SPolyCurveLoop SPolyCurveLoop::FromCurves(SPolyCurveList *spcl, bool *notClosed) SPolyCurveLoop SPolyCurveLoop::FromCurves(SPolyCurveList *spcl,
bool *allClosed, SEdge *errorAt)
{ {
SPolyCurveLoop loop; SPolyCurveLoop loop;
ZERO(&loop); ZERO(&loop);
@ -153,35 +154,114 @@ SPolyCurveLoop SPolyCurveLoop::FromCurves(SPolyCurveList *spcl, bool *notClosed)
while(spcl->l.n > 0 && !hanging.Equals(start)) { while(spcl->l.n > 0 && !hanging.Equals(start)) {
int i; int i;
bool foundNext = false;
for(i = 0; i < spcl->l.n; i++) { for(i = 0; i < spcl->l.n; i++) {
SPolyCurve *test = &(spcl->l.elem[i]); SPolyCurve *test = &(spcl->l.elem[i]);
if((test->Finish()).Equals(hanging)) { if((test->Finish()).Equals(hanging)) {
test->Reverse(); test->Reverse();
// and let the next test catch it
} }
if((test->Start()).Equals(hanging)) { if((test->Start()).Equals(hanging)) {
test->tag = 1; test->tag = 1;
loop.l.Add(test); loop.l.Add(test);
hanging = test->Finish(); hanging = test->Finish();
spcl->l.RemoveTagged(); spcl->l.RemoveTagged();
foundNext = true;
break; break;
} }
} }
if(i >= spcl->l.n) { if(!foundNext) {
// Didn't find the next curve in the loop // The loop completed without finding the hanging edge, so
*notClosed = true; // it's an open loop
errorAt->a = hanging;
errorAt->b = start;
*allClosed = false;
return loop; return loop;
} }
} }
if(hanging.Equals(start)) { if(hanging.Equals(start)) {
*notClosed = false; *allClosed = true;
} else { } else {
*notClosed = true; // We ran out of edges without forming a closed loop.
errorAt->a = hanging;
errorAt->b = start;
*allClosed = false;
} }
return loop; return loop;
} }
void SPolyCurveLoop::Reverse(void) {
l.Reverse();
}
void SPolyCurveLoop::MakePwlInto(SContour *sc) {
List<Vector> lv;
ZERO(&lv);
int i, j;
for(i = 0; i < l.n; i++) {
SPolyCurve *spc = &(l.elem[i]);
spc->MakePwlInto(&lv);
// Each curve's piecewise linearization includes its endpoints,
// which we don't want to duplicate (creating zero-len edges).
for(j = (i == 0 ? 0 : 1); j < lv.n; j++) {
sc->AddPoint(lv.elem[j]);
}
lv.Clear();
}
// Ensure that it's exactly closed, not just within a numerical tolerance.
sc->l.elem[sc->l.n - 1] = sc->l.elem[0];
}
SPolyCurveLoops SPolyCurveLoops::From(SPolyCurveList *spcl, SPolygon *poly,
bool *allClosed, SEdge *errorAt)
{
int i;
SPolyCurveLoops ret;
ZERO(&ret);
while(spcl->l.n > 0) {
bool thisClosed;
SPolyCurveLoop loop;
loop = SPolyCurveLoop::FromCurves(spcl, &thisClosed, errorAt);
if(!thisClosed) {
ret.Clear();
*allClosed = false;
return ret;
}
ret.l.Add(&loop);
poly->AddEmptyContour();
loop.MakePwlInto(&(poly->l.elem[poly->l.n-1]));
}
poly->normal = poly->ComputeNormal();
ret.normal = poly->normal;
poly->FixContourDirections();
for(i = 0; i < poly->l.n; i++) {
if(poly->l.elem[i].tag) {
// We had to reverse this contour in order to fix the poly
// contour directions; so need to do the same with the curves.
ret.l.elem[i].Reverse();
}
}
*allClosed = true;
return ret;
}
void SPolyCurveLoops::Clear(void) {
int i;
for(i = 0; i < l.n; i++) {
(l.elem[i]).Clear();
}
l.Clear();
}
SSurface SSurface::FromExtrusionOf(SPolyCurve *spc, Vector t0, Vector t1) { SSurface SSurface::FromExtrusionOf(SPolyCurve *spc, Vector t0, Vector t1) {
SSurface ret; SSurface ret;
ZERO(&ret); ZERO(&ret);
@ -205,11 +285,10 @@ SShell SShell::FromExtrusionOf(SPolyCurveList *spcl, Vector t0, Vector t1) {
SShell ret; SShell ret;
ZERO(&ret); ZERO(&ret);
// Find the plane that contains our input section. // Group the input curves into loops, not necessarily in the right order.
// Group the input curves into loops; this will reverse some of the
// curves if necessary for consistent (but not necessarily correct yet) // Find the plane that contains our input section.
// direction.
// Generate a polygon from the curves, and use this to test how many // Generate a polygon from the curves, and use this to test how many
// times each loop is enclosed. Then set the direction (cw/ccw) to // times each loop is enclosed. Then set the direction (cw/ccw) to

View File

@ -2,8 +2,9 @@
#ifndef __SURFACE_H #ifndef __SURFACE_H
#define __SURFACE_H #define __SURFACE_H
// Utility function // Utility functions, Bernstein polynomials of order 1-3 and their derivatives.
double Bernstein(int k, int deg, double t); double Bernstein(int k, int deg, double t);
double BernsteinDerivative(int k, int deg, double t);
class hSSurface { class hSSurface {
public: public:
@ -24,7 +25,7 @@ public:
Vector ctrl[4]; Vector ctrl[4];
double weight[4]; double weight[4];
Vector EvalAt(double t); Vector PointAt(double t);
Vector Start(void); Vector Start(void);
Vector Finish(void); Vector Finish(void);
void MakePwlInto(List<Vector> *l); void MakePwlInto(List<Vector> *l);
@ -48,11 +49,24 @@ class SPolyCurveLoop {
public: public:
List<SPolyCurve> l; List<SPolyCurve> l;
bool IsClockwiseProjdToNormal(Vector n); inline void Clear(void) { l.Clear(); }
void Reverse(void);
void MakePwlInto(SContour *sc);
static SPolyCurveLoop FromCurves(SPolyCurveList *spcl, bool *notClosed); static SPolyCurveLoop FromCurves(SPolyCurveList *spcl,
bool *allClosed, SEdge *errorAt);
}; };
class SPolyCurveLoops {
public:
List<SPolyCurveLoop> l;
Vector normal;
static SPolyCurveLoops From(SPolyCurveList *spcl, SPolygon *poly,
bool *allClosed, SEdge *errorAt);
void Clear(void);
};
// Stuff for the surface trim curves: piecewise linear // Stuff for the surface trim curves: piecewise linear
class SCurve { class SCurve {
@ -84,12 +98,17 @@ public:
int degm, degn; int degm, degn;
Vector ctrl[4][4]; Vector ctrl[4][4];
double weight[4][4]; double weight[4][4];
Vector out00; // outer normal at ctrl[0][0]
List<STrimBy> trim; List<STrimBy> trim;
static SSurface FromExtrusionOf(SPolyCurve *spc, Vector t0, Vector t1); static SSurface FromExtrusionOf(SPolyCurve *spc, Vector t0, Vector t1);
void ClosestPointTo(Vector p, double *u, double *v);
Vector PointAt(double u, double v);
Vector TangentWrtUAt(double u, double v);
Vector TangentWrtVAt(double u, double v);
Vector NormalAt(double u, double v);
void TriangulateInto(SMesh *sm); void TriangulateInto(SMesh *sm);
}; };

View File

@ -49,6 +49,7 @@ void SolveSpace::PushFromCurrentOnto(UndoStack *uk) {
dest.vvMeshClean = false; dest.vvMeshClean = false;
ZERO(&(dest.solved)); ZERO(&(dest.solved));
ZERO(&(dest.poly)); ZERO(&(dest.poly));
ZERO(&(dest.curveLoops));
ZERO(&(dest.polyError)); ZERO(&(dest.polyError));
ZERO(&(dest.thisMesh)); ZERO(&(dest.thisMesh));
ZERO(&(dest.runningMesh)); ZERO(&(dest.runningMesh));
@ -91,6 +92,7 @@ void SolveSpace::PopOntoCurrentFrom(UndoStack *uk) {
for(i = 0; i < group.n; i++) { for(i = 0; i < group.n; i++) {
Group *g = &(group.elem[i]); Group *g = &(group.elem[i]);
g->poly.Clear(); g->poly.Clear();
g->curveLoops.Clear();
g->thisMesh.Clear(); g->thisMesh.Clear();
g->runningMesh.Clear(); g->runningMesh.Clear();
g->meshError.interferesAt.Clear(); g->meshError.interferesAt.Clear();