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 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) {
thetaa = 0;
thetab = 2*PI;

7
dsc.h
View File

@ -134,6 +134,13 @@ public:
n = dest;
// 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

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,
// and then regenerate the mesh based on the solved stuff.
SolveGroup(g->h, andFindFree);
g->GeneratePolygon();
g->GenerateLoops();
g->GenerateMesh();
g->clean = true;
} else {

View File

@ -2,112 +2,50 @@
#define gs (SS.GW.gs)
bool Group::AssemblePolygon(SPolygon *p, SEdge *error) {
SEdgeList edges; ZERO(&edges);
bool Group::AssembleLoops(void) {
SPolyCurveList spcl;
ZERO(&spcl);
int i;
for(i = 0; i < SS.entity.n; i++) {
Entity *e = &(SS.entity.elem[i]);
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();
return ret;
bool allClosed;
curveLoops = SPolyCurveLoops::From(&spcl, &poly,
&allClosed, &(polyError.notClosedAt));
spcl.Clear();
return allClosed;
}
void Group::GeneratePolygon(void) {
void Group::GenerateLoops(void) {
poly.Clear();
curveLoops.Clear();
if(type == DRAWING_3D || type == DRAWING_WORKPLANE ||
type == ROTATE || type == TRANSLATE || type == IMPORTED)
{
if(AssemblePolygon(&poly, &(polyError.notClosedAt))) {
if(AssembleLoops()) {
polyError.how = POLY_GOOD;
poly.normal = poly.ComputeNormal();
poly.FixContourDirections();
if(!poly.AllPointsInPlane(&(polyError.notCoplanarAt))) {
// The edges aren't all coplanar; so not a good polygon
polyError.how = POLY_NOT_COPLANAR;
poly.Clear();
curveLoops.Clear();
}
} else {
polyError.how = POLY_NOT_CLOSED;
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,
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);
}
return (area < 0);
}
@ -224,13 +223,7 @@ bool SContour::AllPointsInPlane(Vector n, double d, Vector *notCoplanarAt) {
}
void SContour::Reverse(void) {
int i;
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;
}
l.Reverse();
}
@ -277,6 +270,9 @@ int SPolygon::WindingNumberForPoint(Vector p) {
}
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.
int i, j;
for(i = 0; i < l.n; i++) {
@ -296,6 +292,7 @@ void SPolygon::FixContourDirections(void) {
bool clockwise = sc->IsClockwiseProjdToNormal(normal);
if(clockwise && outer || (!clockwise && !outer)) {
sc->Reverse();
sc->tag = 1;
}
}
}

View File

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

View File

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

View File

@ -77,7 +77,7 @@ Vector SPolyCurve::Finish(void) {
return ctrl[deg];
}
Vector SPolyCurve::EvalAt(double t) {
Vector SPolyCurve::PointAt(double t) {
Vector pt = Vector::From(0, 0, 0);
double d = 0;
@ -97,15 +97,15 @@ void SPolyCurve::MakePwlInto(List<Vector> *l) {
}
void SPolyCurve::MakePwlWorker(List<Vector> *l, double ta, double tb) {
Vector pa = EvalAt(ta);
Vector pb = EvalAt(tb);
Vector pa = PointAt(ta);
Vector pb = PointAt(tb);
// Can't test in the middle, or certain cubics would break.
double tm1 = (2*ta + tb) / 3;
double tm2 = (ta + 2*tb) / 3;
Vector pm1 = EvalAt(tm1);
Vector pm2 = EvalAt(tm2);
Vector pm1 = PointAt(tm1);
Vector pm2 = PointAt(tm2);
double d = max(pm1.DistanceToLine(pa, pb.Minus(pa)),
pm2.DistanceToLine(pa, pb.Minus(pa)));
@ -135,7 +135,8 @@ void SPolyCurveList::Clear(void) {
l.Clear();
}
SPolyCurveLoop SPolyCurveLoop::FromCurves(SPolyCurveList *spcl, bool *notClosed)
SPolyCurveLoop SPolyCurveLoop::FromCurves(SPolyCurveList *spcl,
bool *allClosed, SEdge *errorAt)
{
SPolyCurveLoop loop;
ZERO(&loop);
@ -153,35 +154,114 @@ SPolyCurveLoop SPolyCurveLoop::FromCurves(SPolyCurveList *spcl, bool *notClosed)
while(spcl->l.n > 0 && !hanging.Equals(start)) {
int i;
bool foundNext = false;
for(i = 0; i < spcl->l.n; i++) {
SPolyCurve *test = &(spcl->l.elem[i]);
if((test->Finish()).Equals(hanging)) {
test->Reverse();
// and let the next test catch it
}
if((test->Start()).Equals(hanging)) {
test->tag = 1;
loop.l.Add(test);
hanging = test->Finish();
spcl->l.RemoveTagged();
foundNext = true;
break;
}
}
if(i >= spcl->l.n) {
// Didn't find the next curve in the loop
*notClosed = true;
if(!foundNext) {
// The loop completed without finding the hanging edge, so
// it's an open loop
errorAt->a = hanging;
errorAt->b = start;
*allClosed = false;
return loop;
}
}
if(hanging.Equals(start)) {
*notClosed = false;
} else {
*notClosed = true;
*allClosed = true;
} else {
// We ran out of edges without forming a closed loop.
errorAt->a = hanging;
errorAt->b = start;
*allClosed = false;
}
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 ret;
ZERO(&ret);
@ -205,11 +285,10 @@ SShell SShell::FromExtrusionOf(SPolyCurveList *spcl, Vector t0, Vector t1) {
SShell 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)
// direction.
// Find the plane that contains our input section.
// 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

View File

@ -2,8 +2,9 @@
#ifndef __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 BernsteinDerivative(int k, int deg, double t);
class hSSurface {
public:
@ -24,7 +25,7 @@ public:
Vector ctrl[4];
double weight[4];
Vector EvalAt(double t);
Vector PointAt(double t);
Vector Start(void);
Vector Finish(void);
void MakePwlInto(List<Vector> *l);
@ -48,11 +49,24 @@ class SPolyCurveLoop {
public:
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
class SCurve {
@ -84,12 +98,17 @@ public:
int degm, degn;
Vector ctrl[4][4];
double weight[4][4];
Vector out00; // outer normal at ctrl[0][0]
List<STrimBy> trim;
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);
};

View File

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