2009-01-15 03:55:42 +00:00
|
|
|
#include "../solvespace.h"
|
|
|
|
|
|
|
|
double Bernstein(int k, int deg, double t)
|
|
|
|
{
|
|
|
|
switch(deg) {
|
|
|
|
case 1:
|
|
|
|
if(k == 0) {
|
|
|
|
return (1 - t);
|
|
|
|
} else if(k = 1) {
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
if(k == 0) {
|
|
|
|
return (1 - t)*(1 - t);
|
|
|
|
} else if(k == 1) {
|
|
|
|
return 2*(1 - t)*t;
|
|
|
|
} else if(k == 2) {
|
|
|
|
return t*t;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
if(k == 0) {
|
|
|
|
return (1 - t)*(1 - t)*(1 - t);
|
|
|
|
} else if(k == 1) {
|
|
|
|
return 3*(1 - t)*(1 - t)*t;
|
|
|
|
} else if(k == 2) {
|
|
|
|
return 3*(1 - t)*t*t;
|
|
|
|
} else if(k == 3) {
|
|
|
|
return t*t*t;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
oops();
|
|
|
|
}
|
|
|
|
|
2009-01-19 03:51:00 +00:00
|
|
|
SBezier SBezier::From(Vector p0, Vector p1) {
|
|
|
|
SBezier ret;
|
2009-01-15 03:55:42 +00:00
|
|
|
ZERO(&ret);
|
|
|
|
ret.deg = 1;
|
|
|
|
ret.weight[0] = ret.weight[1] = 1;
|
|
|
|
ret.ctrl[0] = p0;
|
|
|
|
ret.ctrl[1] = p1;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-01-19 03:51:00 +00:00
|
|
|
SBezier SBezier::From(Vector p0, Vector p1, Vector p2) {
|
|
|
|
SBezier ret;
|
2009-01-15 03:55:42 +00:00
|
|
|
ZERO(&ret);
|
|
|
|
ret.deg = 2;
|
|
|
|
ret.weight[0] = ret.weight[1] = ret.weight[2] = 1;
|
|
|
|
ret.ctrl[0] = p0;
|
|
|
|
ret.ctrl[1] = p1;
|
|
|
|
ret.ctrl[2] = p2;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-01-19 03:51:00 +00:00
|
|
|
SBezier SBezier::From(Vector p0, Vector p1, Vector p2, Vector p3) {
|
|
|
|
SBezier ret;
|
2009-01-15 03:55:42 +00:00
|
|
|
ZERO(&ret);
|
|
|
|
ret.deg = 3;
|
|
|
|
ret.weight[0] = ret.weight[1] = ret.weight[2] = ret.weight[3] = 1;
|
|
|
|
ret.ctrl[0] = p0;
|
|
|
|
ret.ctrl[1] = p1;
|
|
|
|
ret.ctrl[2] = p2;
|
|
|
|
ret.ctrl[3] = p3;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-01-19 03:51:00 +00:00
|
|
|
Vector SBezier::Start(void) {
|
2009-01-15 03:55:42 +00:00
|
|
|
return ctrl[0];
|
|
|
|
}
|
|
|
|
|
2009-01-19 03:51:00 +00:00
|
|
|
Vector SBezier::Finish(void) {
|
2009-01-15 03:55:42 +00:00
|
|
|
return ctrl[deg];
|
|
|
|
}
|
|
|
|
|
2009-01-19 03:51:00 +00:00
|
|
|
Vector SBezier::PointAt(double t) {
|
2009-01-15 03:55:42 +00:00
|
|
|
Vector pt = Vector::From(0, 0, 0);
|
|
|
|
double d = 0;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for(i = 0; i <= deg; i++) {
|
|
|
|
double B = Bernstein(i, deg, t);
|
|
|
|
pt = pt.Plus(ctrl[i].ScaledBy(B*weight[i]));
|
|
|
|
d += weight[i]*B;
|
|
|
|
}
|
|
|
|
pt = pt.ScaledBy(1.0/d);
|
|
|
|
return pt;
|
|
|
|
}
|
|
|
|
|
2009-01-19 03:51:00 +00:00
|
|
|
void SBezier::MakePwlInto(List<Vector> *l) {
|
2009-01-15 03:55:42 +00:00
|
|
|
l->Add(&(ctrl[0]));
|
|
|
|
MakePwlWorker(l, 0.0, 1.0);
|
|
|
|
}
|
|
|
|
|
2009-01-19 03:51:00 +00:00
|
|
|
void SBezier::MakePwlWorker(List<Vector> *l, double ta, double tb) {
|
2009-01-19 03:33:15 +00:00
|
|
|
Vector pa = PointAt(ta);
|
|
|
|
Vector pb = PointAt(tb);
|
2009-01-15 03:55:42 +00:00
|
|
|
|
|
|
|
// Can't test in the middle, or certain cubics would break.
|
|
|
|
double tm1 = (2*ta + tb) / 3;
|
|
|
|
double tm2 = (ta + 2*tb) / 3;
|
|
|
|
|
2009-01-19 03:33:15 +00:00
|
|
|
Vector pm1 = PointAt(tm1);
|
|
|
|
Vector pm2 = PointAt(tm2);
|
2009-01-15 03:55:42 +00:00
|
|
|
|
|
|
|
double d = max(pm1.DistanceToLine(pa, pb.Minus(pa)),
|
|
|
|
pm2.DistanceToLine(pa, pb.Minus(pa)));
|
|
|
|
|
|
|
|
double tol = SS.chordTol/SS.GW.scale;
|
|
|
|
|
|
|
|
double step = 1.0/SS.maxSegments;
|
|
|
|
if((tb - ta) < step || d < tol) {
|
|
|
|
// A previous call has already added the beginning of our interval.
|
|
|
|
l->Add(&pb);
|
|
|
|
} else {
|
|
|
|
double tm = (ta + tb) / 2;
|
|
|
|
MakePwlWorker(l, ta, tm);
|
|
|
|
MakePwlWorker(l, tm, tb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-19 03:51:00 +00:00
|
|
|
void SBezier::Reverse(void) {
|
2009-01-17 05:28:49 +00:00
|
|
|
int i;
|
|
|
|
for(i = 0; i < (deg+1)/2; i++) {
|
|
|
|
SWAP(Vector, ctrl[i], ctrl[deg-i]);
|
|
|
|
SWAP(double, weight[i], weight[deg-i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-19 03:51:00 +00:00
|
|
|
void SBezierList::Clear(void) {
|
2009-01-15 03:55:42 +00:00
|
|
|
l.Clear();
|
|
|
|
}
|
|
|
|
|
2009-01-19 03:51:00 +00:00
|
|
|
SBezierLoop SBezierLoop::FromCurves(SBezierList *sbl,
|
|
|
|
bool *allClosed, SEdge *errorAt)
|
2009-01-17 05:28:49 +00:00
|
|
|
{
|
2009-01-19 03:51:00 +00:00
|
|
|
SBezierLoop loop;
|
2009-01-17 05:28:49 +00:00
|
|
|
ZERO(&loop);
|
|
|
|
|
2009-01-19 03:51:00 +00:00
|
|
|
if(sbl->l.n < 1) return loop;
|
|
|
|
sbl->l.ClearTags();
|
2009-01-17 05:28:49 +00:00
|
|
|
|
2009-01-19 03:51:00 +00:00
|
|
|
SBezier *first = &(sbl->l.elem[0]);
|
2009-01-17 05:28:49 +00:00
|
|
|
first->tag = 1;
|
|
|
|
loop.l.Add(first);
|
|
|
|
Vector start = first->Start();
|
|
|
|
Vector hanging = first->Finish();
|
|
|
|
|
2009-01-19 03:51:00 +00:00
|
|
|
sbl->l.RemoveTagged();
|
2009-01-17 05:28:49 +00:00
|
|
|
|
2009-01-19 03:51:00 +00:00
|
|
|
while(sbl->l.n > 0 && !hanging.Equals(start)) {
|
2009-01-17 05:28:49 +00:00
|
|
|
int i;
|
2009-01-19 03:33:15 +00:00
|
|
|
bool foundNext = false;
|
2009-01-19 03:51:00 +00:00
|
|
|
for(i = 0; i < sbl->l.n; i++) {
|
|
|
|
SBezier *test = &(sbl->l.elem[i]);
|
2009-01-17 05:28:49 +00:00
|
|
|
|
|
|
|
if((test->Finish()).Equals(hanging)) {
|
|
|
|
test->Reverse();
|
2009-01-19 03:33:15 +00:00
|
|
|
// and let the next test catch it
|
2009-01-17 05:28:49 +00:00
|
|
|
}
|
|
|
|
if((test->Start()).Equals(hanging)) {
|
|
|
|
test->tag = 1;
|
|
|
|
loop.l.Add(test);
|
|
|
|
hanging = test->Finish();
|
2009-01-19 03:51:00 +00:00
|
|
|
sbl->l.RemoveTagged();
|
2009-01-19 03:33:15 +00:00
|
|
|
foundNext = true;
|
2009-01-17 05:28:49 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2009-01-19 03:33:15 +00:00
|
|
|
if(!foundNext) {
|
|
|
|
// The loop completed without finding the hanging edge, so
|
|
|
|
// it's an open loop
|
|
|
|
errorAt->a = hanging;
|
|
|
|
errorAt->b = start;
|
|
|
|
*allClosed = false;
|
2009-01-17 05:28:49 +00:00
|
|
|
return loop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(hanging.Equals(start)) {
|
2009-01-19 03:33:15 +00:00
|
|
|
*allClosed = true;
|
|
|
|
} else {
|
|
|
|
// We ran out of edges without forming a closed loop.
|
|
|
|
errorAt->a = hanging;
|
|
|
|
errorAt->b = start;
|
|
|
|
*allClosed = false;
|
2009-01-17 05:28:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return loop;
|
|
|
|
}
|
|
|
|
|
2009-01-19 03:51:00 +00:00
|
|
|
void SBezierLoop::Reverse(void) {
|
2009-01-19 03:33:15 +00:00
|
|
|
l.Reverse();
|
|
|
|
}
|
|
|
|
|
2009-01-19 03:51:00 +00:00
|
|
|
void SBezierLoop::MakePwlInto(SContour *sc) {
|
2009-01-19 03:33:15 +00:00
|
|
|
List<Vector> lv;
|
|
|
|
ZERO(&lv);
|
|
|
|
|
|
|
|
int i, j;
|
|
|
|
for(i = 0; i < l.n; i++) {
|
2009-01-19 03:51:00 +00:00
|
|
|
SBezier *sb = &(l.elem[i]);
|
|
|
|
sb->MakePwlInto(&lv);
|
2009-01-19 03:33:15 +00:00
|
|
|
|
|
|
|
// 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];
|
|
|
|
}
|
|
|
|
|
2009-01-19 03:51:00 +00:00
|
|
|
SBezierLoopSet SBezierLoopSet::From(SBezierList *sbl, SPolygon *poly,
|
|
|
|
bool *allClosed, SEdge *errorAt)
|
2009-01-19 03:33:15 +00:00
|
|
|
{
|
|
|
|
int i;
|
2009-01-19 03:51:00 +00:00
|
|
|
SBezierLoopSet ret;
|
2009-01-19 03:33:15 +00:00
|
|
|
ZERO(&ret);
|
|
|
|
|
2009-01-19 03:51:00 +00:00
|
|
|
while(sbl->l.n > 0) {
|
2009-01-19 03:33:15 +00:00
|
|
|
bool thisClosed;
|
2009-01-19 03:51:00 +00:00
|
|
|
SBezierLoop loop;
|
|
|
|
loop = SBezierLoop::FromCurves(sbl, &thisClosed, errorAt);
|
2009-01-19 03:33:15 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-01-19 03:51:00 +00:00
|
|
|
void SBezierLoopSet::Clear(void) {
|
2009-01-19 03:33:15 +00:00
|
|
|
int i;
|
|
|
|
for(i = 0; i < l.n; i++) {
|
|
|
|
(l.elem[i]).Clear();
|
|
|
|
}
|
|
|
|
l.Clear();
|
|
|
|
}
|
|
|
|
|
2009-01-19 03:51:00 +00:00
|
|
|
SSurface SSurface::FromExtrusionOf(SBezier *sb, Vector t0, Vector t1) {
|
2009-01-17 05:28:49 +00:00
|
|
|
SSurface ret;
|
|
|
|
ZERO(&ret);
|
|
|
|
|
2009-01-19 03:51:00 +00:00
|
|
|
ret.degm = sb->deg;
|
2009-01-17 05:28:49 +00:00
|
|
|
ret.degn = 1;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for(i = 0; i <= ret.degm; i++) {
|
2009-01-19 03:51:00 +00:00
|
|
|
ret.ctrl[i][0] = (sb->ctrl[i]).Plus(t0);
|
|
|
|
ret.weight[i][0] = sb->weight[i];
|
2009-01-17 05:28:49 +00:00
|
|
|
|
2009-01-19 03:51:00 +00:00
|
|
|
ret.ctrl[i][1] = (sb->ctrl[i]).Plus(t1);
|
|
|
|
ret.weight[i][1] = sb->weight[i];
|
2009-01-17 05:28:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-01-19 03:51:00 +00:00
|
|
|
SShell SShell::FromExtrusionOf(SBezierList *sbl, Vector t0, Vector t1) {
|
2009-01-17 05:28:49 +00:00
|
|
|
SShell ret;
|
|
|
|
ZERO(&ret);
|
|
|
|
|
2009-01-19 03:33:15 +00:00
|
|
|
// Group the input curves into loops, not necessarily in the right order.
|
|
|
|
|
2009-01-17 05:28:49 +00:00
|
|
|
|
2009-01-19 03:33:15 +00:00
|
|
|
// Find the plane that contains our input section.
|
2009-01-17 05:28:49 +00:00
|
|
|
|
|
|
|
// 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
|
|
|
|
// be correct for outlines/holes, so that we generate correct normals.
|
|
|
|
|
|
|
|
// Now generate all the surfaces, top/bottom planes plus extrusions.
|
|
|
|
|
|
|
|
// And now all the curves, trimming the top and bottom and their extrusion
|
|
|
|
|
|
|
|
// And the lines, trimming adjacent extrusion surfaces.
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|