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:
parent
7a874c20c0
commit
0e623c90c0
@ -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
7
dsc.h
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
{
|
||||
|
13
polygon.cpp
13
polygon.cpp
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ public:
|
||||
|
||||
class SContour {
|
||||
public:
|
||||
int tag;
|
||||
List<SPoint> l;
|
||||
|
||||
void AddPoint(Vector p);
|
||||
|
11
sketch.h
11
sketch.h
@ -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);
|
||||
|
111
srf/ratpoly.cpp
111
srf/ratpoly.cpp
@ -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
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user