2009-01-15 03:55:42 +00:00
|
|
|
#include "../solvespace.h"
|
|
|
|
|
|
|
|
double Bernstein(int k, int deg, double t)
|
|
|
|
{
|
2009-01-19 10:37:10 +00:00
|
|
|
if(k > deg || k < 0) return 0;
|
|
|
|
|
2009-01-15 03:55:42 +00:00
|
|
|
switch(deg) {
|
2009-01-19 10:37:10 +00:00
|
|
|
case 0:
|
|
|
|
return 1;
|
|
|
|
break;
|
|
|
|
|
2009-01-15 03:55:42 +00:00
|
|
|
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 10:37:10 +00:00
|
|
|
double BernsteinDerivative(int k, int deg, double t)
|
|
|
|
{
|
|
|
|
return deg*(Bernstein(k-1, deg-1, t) - Bernstein(k, deg-1, t));
|
|
|
|
}
|
|
|
|
|
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-19 10:37:10 +00:00
|
|
|
MakePwlInto(l, Vector::From(0, 0, 0));
|
2009-01-15 03:55:42 +00:00
|
|
|
}
|
2009-01-19 10:37:10 +00:00
|
|
|
void SBezier::MakePwlInto(List<Vector> *l, Vector offset) {
|
|
|
|
Vector p = (ctrl[0]).Plus(offset);
|
|
|
|
l->Add(&p);
|
2009-01-15 03:55:42 +00:00
|
|
|
|
2009-01-19 10:37:10 +00:00
|
|
|
MakePwlWorker(l, 0.0, 1.0, offset);
|
|
|
|
}
|
|
|
|
void SBezier::MakePwlWorker(List<Vector> *l, double ta, double tb, Vector off) {
|
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.
|
2009-01-19 10:37:10 +00:00
|
|
|
pb = pb.Plus(off);
|
2009-01-15 03:55:42 +00:00
|
|
|
l->Add(&pb);
|
|
|
|
} else {
|
|
|
|
double tm = (ta + tb) / 2;
|
2009-01-19 10:37:10 +00:00
|
|
|
MakePwlWorker(l, ta, tm, off);
|
|
|
|
MakePwlWorker(l, tm, tb, off);
|
2009-01-15 03:55:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-25 11:52:29 +00:00
|
|
|
void SBezier::GetBoundingProjd(Vector u, Vector orig,
|
|
|
|
double *umin, double *umax)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for(i = 0; i <= deg; i++) {
|
|
|
|
double ut = ((ctrl[i]).Minus(orig)).Dot(u);
|
|
|
|
if(ut < *umin) *umin = ut;
|
|
|
|
if(ut > *umax) *umax = ut;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-23 03:30:30 +00:00
|
|
|
SBezier SBezier::TransformedBy(Vector t, Quaternion q) {
|
|
|
|
SBezier ret = *this;
|
|
|
|
int i;
|
|
|
|
for(i = 0; i <= deg; i++) {
|
|
|
|
ret.ctrl[i] = (q.Rotate(ret.ctrl[i])).Plus(t);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
2009-01-19 10:37:10 +00:00
|
|
|
|
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 10:37:10 +00:00
|
|
|
|
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 10:37:10 +00:00
|
|
|
SBezier *sb;
|
|
|
|
for(sb = l.First(); sb; sb = l.NextAfter(sb)) {
|
|
|
|
// If we didn't reverse each curve, then the next curve in list would
|
|
|
|
// share your start, not your finish.
|
|
|
|
sb->Reverse();
|
|
|
|
}
|
2009-01-19 03:33:15 +00:00
|
|
|
}
|
|
|
|
|
2009-01-25 11:52:29 +00:00
|
|
|
void SBezierLoop::GetBoundingProjd(Vector u, Vector orig,
|
|
|
|
double *umin, double *umax)
|
|
|
|
{
|
|
|
|
SBezier *sb;
|
|
|
|
for(sb = l.First(); sb; sb = l.NextAfter(sb)) {
|
|
|
|
sb->GetBoundingProjd(u, orig, umin, umax);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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 10:37:10 +00:00
|
|
|
|
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;
|
2009-01-19 10:37:10 +00:00
|
|
|
if(poly->l.n > 0) {
|
|
|
|
ret.point = poly->AnyPoint();
|
|
|
|
} else {
|
|
|
|
ret.point = Vector::From(0, 0, 0);
|
|
|
|
}
|
2009-01-19 03:33:15 +00:00
|
|
|
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-25 11:52:29 +00:00
|
|
|
void SBezierLoopSet::GetBoundingProjd(Vector u, Vector orig,
|
|
|
|
double *umin, double *umax)
|
|
|
|
{
|
|
|
|
SBezierLoop *sbl;
|
|
|
|
for(sbl = l.First(); sbl; sbl = l.NextAfter(sbl)) {
|
|
|
|
sbl->GetBoundingProjd(u, orig, umin, umax);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-23 03:30:30 +00:00
|
|
|
SCurve SCurve::FromTransformationOf(SCurve *a, Vector t, Quaternion q) {
|
|
|
|
SCurve ret;
|
|
|
|
ZERO(&ret);
|
|
|
|
|
|
|
|
ret.h = a->h;
|
|
|
|
ret.isExact = a->isExact;
|
|
|
|
ret.exact = (a->exact).TransformedBy(t, q);
|
|
|
|
|
|
|
|
Vector *p;
|
|
|
|
for(p = a->pts.First(); p; p = a->pts.NextAfter(p)) {
|
|
|
|
Vector pp = (q.Rotate(*p)).Plus(t);
|
|
|
|
ret.pts.Add(&pp);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-01-19 10:37:10 +00:00
|
|
|
void SCurve::Clear(void) {
|
|
|
|
pts.Clear();
|
|
|
|
}
|
|
|
|
|
2009-01-25 09:19:59 +00:00
|
|
|
STrimBy STrimBy::EntireCurve(SShell *shell, hSCurve hsc, bool backwards) {
|
2009-01-19 10:37:10 +00:00
|
|
|
STrimBy stb;
|
|
|
|
ZERO(&stb);
|
|
|
|
stb.curve = hsc;
|
|
|
|
SCurve *sc = shell->curve.FindById(hsc);
|
2009-01-25 09:19:59 +00:00
|
|
|
|
|
|
|
if(backwards) {
|
|
|
|
stb.finish = sc->pts.elem[0];
|
|
|
|
stb.start = sc->pts.elem[sc->pts.n - 1];
|
|
|
|
stb.backwards = true;
|
|
|
|
} else {
|
|
|
|
stb.start = sc->pts.elem[0];
|
|
|
|
stb.finish = sc->pts.elem[sc->pts.n - 1];
|
|
|
|
stb.backwards = false;
|
|
|
|
}
|
2009-01-19 10:37:10 +00:00
|
|
|
|
|
|
|
return stb;
|
|
|
|
}
|
|
|
|
|
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-25 11:52:29 +00:00
|
|
|
SSurface SSurface::FromPlane(Vector pt, Vector u, Vector v) {
|
2009-01-19 10:37:10 +00:00
|
|
|
SSurface ret;
|
|
|
|
ZERO(&ret);
|
|
|
|
|
|
|
|
ret.degm = 1;
|
|
|
|
ret.degn = 1;
|
|
|
|
|
|
|
|
ret.weight[0][0] = ret.weight[0][1] = 1;
|
|
|
|
ret.weight[1][0] = ret.weight[1][1] = 1;
|
|
|
|
|
|
|
|
ret.ctrl[0][0] = pt;
|
|
|
|
ret.ctrl[0][1] = pt.Plus(u);
|
|
|
|
ret.ctrl[1][0] = pt.Plus(v);
|
|
|
|
ret.ctrl[1][1] = pt.Plus(v).Plus(u);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-01-23 03:30:30 +00:00
|
|
|
SSurface SSurface::FromTransformationOf(SSurface *a, Vector t, Quaternion q,
|
|
|
|
bool includingTrims)
|
|
|
|
{
|
|
|
|
SSurface ret;
|
|
|
|
ZERO(&ret);
|
|
|
|
|
|
|
|
ret.h = a->h;
|
2009-01-25 09:19:59 +00:00
|
|
|
ret.color = a->color;
|
|
|
|
ret.face = a->face;
|
2009-01-23 03:30:30 +00:00
|
|
|
|
|
|
|
ret.degm = a->degm;
|
|
|
|
ret.degn = a->degn;
|
|
|
|
int i, j;
|
|
|
|
for(i = 0; i <= 3; i++) {
|
|
|
|
for(j = 0; j <= 3; j++) {
|
|
|
|
ret.ctrl[i][j] = (q.Rotate(a->ctrl[i][j])).Plus(t);
|
|
|
|
ret.weight[i][j] = a->weight[i][j];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(includingTrims) {
|
|
|
|
STrimBy *stb;
|
|
|
|
for(stb = a->trim.First(); stb; stb = a->trim.NextAfter(stb)) {
|
|
|
|
STrimBy n = *stb;
|
|
|
|
n.start = (q.Rotate(n.start)) .Plus(t);
|
|
|
|
n.finish = (q.Rotate(n.finish)).Plus(t);
|
|
|
|
ret.trim.Add(&n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-01-19 10:37:10 +00:00
|
|
|
Vector SSurface::PointAt(double u, double v) {
|
|
|
|
Vector num = Vector::From(0, 0, 0);
|
|
|
|
double den = 0;
|
|
|
|
|
|
|
|
int i, j;
|
|
|
|
for(i = 0; i <= degm; i++) {
|
|
|
|
for(j = 0; j <= degn; j++) {
|
|
|
|
double Bi = Bernstein(i, degm, u),
|
|
|
|
Bj = Bernstein(j, degn, v);
|
|
|
|
|
|
|
|
num = num.Plus(ctrl[i][j].ScaledBy(Bi*Bj*weight[i][j]));
|
|
|
|
den += weight[i][j]*Bi*Bj;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
num = num.ScaledBy(1.0/den);
|
|
|
|
return num;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SSurface::TangentsAt(double u, double v, Vector *tu, Vector *tv) {
|
|
|
|
Vector num = Vector::From(0, 0, 0),
|
|
|
|
num_u = Vector::From(0, 0, 0),
|
|
|
|
num_v = Vector::From(0, 0, 0);
|
|
|
|
double den = 0,
|
|
|
|
den_u = 0,
|
|
|
|
den_v = 0;
|
|
|
|
|
|
|
|
int i, j;
|
|
|
|
for(i = 0; i <= degm; i++) {
|
|
|
|
for(j = 0; j <= degn; j++) {
|
|
|
|
double Bi = Bernstein(i, degm, u),
|
|
|
|
Bj = Bernstein(j, degn, v),
|
|
|
|
Bip = BernsteinDerivative(i, degm, u),
|
|
|
|
Bjp = BernsteinDerivative(j, degn, v);
|
|
|
|
|
|
|
|
num = num.Plus(ctrl[i][j].ScaledBy(Bi*Bj*weight[i][j]));
|
|
|
|
den += weight[i][j]*Bi*Bj;
|
|
|
|
|
|
|
|
num_u = num_u.Plus(ctrl[i][j].ScaledBy(Bip*Bj*weight[i][j]));
|
|
|
|
den_u += weight[i][j]*Bip*Bj;
|
|
|
|
|
|
|
|
num_v = num_v.Plus(ctrl[i][j].ScaledBy(Bi*Bjp*weight[i][j]));
|
|
|
|
den_v += weight[i][j]*Bi*Bjp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Quotient rule; f(t) = n(t)/d(t), so f' = (n'*d - n*d')/(d^2)
|
|
|
|
*tu = ((num_u.ScaledBy(den)).Minus(num.ScaledBy(den_u)));
|
|
|
|
*tu = tu->ScaledBy(1.0/(den*den));
|
|
|
|
|
|
|
|
*tv = ((num_v.ScaledBy(den)).Minus(num.ScaledBy(den_v)));
|
|
|
|
*tv = tv->ScaledBy(1.0/(den*den));
|
|
|
|
}
|
|
|
|
|
|
|
|
Vector SSurface::NormalAt(double u, double v) {
|
|
|
|
Vector tu, tv;
|
|
|
|
TangentsAt(u, v, &tu, &tv);
|
|
|
|
return tu.Cross(tv);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SSurface::ClosestPointTo(Vector p, double *u, double *v) {
|
|
|
|
int i, j;
|
|
|
|
double minDist = 1e10;
|
|
|
|
double res = 7.0;
|
|
|
|
for(i = 0; i < (int)res; i++) {
|
|
|
|
for(j = 0; j <= (int)res; j++) {
|
|
|
|
double tryu = (i/res), tryv = (j/res);
|
|
|
|
|
|
|
|
Vector tryp = PointAt(tryu, tryv);
|
|
|
|
double d = (tryp.Minus(p)).Magnitude();
|
|
|
|
if(d < minDist) {
|
|
|
|
*u = tryu;
|
|
|
|
*v = tryv;
|
|
|
|
minDist = d;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initial guess is in u, v
|
|
|
|
Vector p0;
|
|
|
|
for(i = 0; i < 50; i++) {
|
|
|
|
p0 = PointAt(*u, *v);
|
2009-01-27 05:48:40 +00:00
|
|
|
// Converge it to better than LENGTH_EPS; we want two points, each
|
|
|
|
// independently projected into uv and back, to end up equal with
|
|
|
|
// the LENGTH_EPS. Best case that requires LENGTH_EPS/2, but more
|
|
|
|
// is better and convergence should be fast by now.
|
|
|
|
if(p0.Equals(p, LENGTH_EPS/100)) {
|
2009-01-19 10:37:10 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Vector tu, tv;
|
|
|
|
TangentsAt(*u, *v, &tu, &tv);
|
|
|
|
|
|
|
|
// Project the point into a plane through p0, with basis tu, tv; a
|
|
|
|
// second-order thing would converge faster but needs second
|
|
|
|
// derivatives.
|
|
|
|
Vector dp = p.Minus(p0);
|
|
|
|
double du = dp.Dot(tu), dv = dp.Dot(tv);
|
|
|
|
*u += du / (tu.MagSquared());
|
|
|
|
*v += dv / (tu.MagSquared());
|
|
|
|
}
|
|
|
|
dbp("didn't converge");
|
|
|
|
dbp("have %.3f %.3f %.3f", CO(p0));
|
|
|
|
dbp("want %.3f %.3f %.3f", CO(p));
|
|
|
|
if(isnan(*u) || isnan(*v)) {
|
|
|
|
*u = *v = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-27 07:59:58 +00:00
|
|
|
void SSurface::GetAxisAlignedBounding(Vector *ptMax, Vector *ptMin) {
|
|
|
|
*ptMax = Vector::From(VERY_NEGATIVE, VERY_NEGATIVE, VERY_NEGATIVE);
|
|
|
|
*ptMin = Vector::From(VERY_POSITIVE, VERY_POSITIVE, VERY_POSITIVE);
|
|
|
|
|
|
|
|
int i, j;
|
|
|
|
for(i = 0; i <= degm; i++) {
|
|
|
|
for(j = 0; j <= degn; j++) {
|
|
|
|
(ctrl[i][j]).MakeMaxMin(ptMax, ptMin);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-23 03:30:30 +00:00
|
|
|
void SSurface::MakeEdgesInto(SShell *shell, SEdgeList *sel, bool asUv) {
|
2009-01-19 10:37:10 +00:00
|
|
|
STrimBy *stb;
|
|
|
|
for(stb = trim.First(); stb; stb = trim.NextAfter(stb)) {
|
|
|
|
SCurve *sc = shell->curve.FindById(stb->curve);
|
|
|
|
|
2009-01-23 03:30:30 +00:00
|
|
|
Vector prev, prevuv, ptuv;
|
2009-01-25 11:52:29 +00:00
|
|
|
bool inCurve = false, empty = true;
|
2009-01-19 10:37:10 +00:00
|
|
|
double u = 0, v = 0;
|
2009-01-25 11:52:29 +00:00
|
|
|
|
2009-01-25 09:19:59 +00:00
|
|
|
int i, first, last, increment;
|
|
|
|
if(stb->backwards) {
|
|
|
|
first = sc->pts.n - 1;
|
|
|
|
last = 0;
|
|
|
|
increment = -1;
|
|
|
|
} else {
|
|
|
|
first = 0;
|
|
|
|
last = sc->pts.n - 1;
|
|
|
|
increment = 1;
|
|
|
|
}
|
|
|
|
for(i = first; i != (last + increment); i += increment) {
|
|
|
|
Vector *pt = &(sc->pts.elem[i]);
|
2009-01-23 03:30:30 +00:00
|
|
|
if(asUv) {
|
|
|
|
ClosestPointTo(*pt, &u, &v);
|
|
|
|
ptuv = Vector::From(u, v, 0);
|
|
|
|
if(inCurve) {
|
2009-01-25 11:52:29 +00:00
|
|
|
sel->AddEdge(prevuv, ptuv, sc->h.v, stb->backwards);
|
|
|
|
empty = false;
|
2009-01-23 03:30:30 +00:00
|
|
|
}
|
|
|
|
prevuv = ptuv;
|
|
|
|
} else {
|
|
|
|
if(inCurve) {
|
2009-01-25 11:52:29 +00:00
|
|
|
sel->AddEdge(prev, *pt, sc->h.v, stb->backwards);
|
|
|
|
empty = false;
|
2009-01-23 03:30:30 +00:00
|
|
|
}
|
|
|
|
prev = *pt;
|
2009-01-19 10:37:10 +00:00
|
|
|
}
|
|
|
|
|
2009-01-25 11:52:29 +00:00
|
|
|
if(pt->Equals(stb->start)) inCurve = true;
|
|
|
|
if(pt->Equals(stb->finish)) inCurve = false;
|
|
|
|
}
|
|
|
|
if(inCurve || empty) {
|
|
|
|
dbp("trim was empty or unterminated");
|
2009-01-19 10:37:10 +00:00
|
|
|
}
|
|
|
|
}
|
2009-01-23 03:30:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void SSurface::TriangulateInto(SShell *shell, SMesh *sm) {
|
|
|
|
SEdgeList el;
|
|
|
|
ZERO(&el);
|
|
|
|
|
|
|
|
MakeEdgesInto(shell, &el, true);
|
|
|
|
|
2009-01-19 10:37:10 +00:00
|
|
|
SPolygon poly;
|
|
|
|
ZERO(&poly);
|
2009-01-25 09:19:59 +00:00
|
|
|
if(!el.AssemblePolygon(&poly, NULL, true)) {
|
2009-01-19 10:37:10 +00:00
|
|
|
dbp("failed to assemble polygon to trim nurbs surface in uv space");
|
|
|
|
}
|
|
|
|
|
|
|
|
int i, start = sm->l.n;
|
2009-01-21 05:04:38 +00:00
|
|
|
poly.UvTriangulateInto(sm);
|
|
|
|
|
2009-01-25 09:19:59 +00:00
|
|
|
STriMeta meta = { face, color };
|
2009-01-19 10:37:10 +00:00
|
|
|
for(i = start; i < sm->l.n; i++) {
|
|
|
|
STriangle *st = &(sm->l.elem[i]);
|
2009-01-21 05:04:38 +00:00
|
|
|
st->meta = meta;
|
2009-01-19 10:37:10 +00:00
|
|
|
st->an = NormalAt(st->a.x, st->a.y);
|
|
|
|
st->bn = NormalAt(st->b.x, st->b.y);
|
|
|
|
st->cn = NormalAt(st->c.x, st->c.y);
|
|
|
|
st->a = PointAt(st->a.x, st->a.y);
|
|
|
|
st->b = PointAt(st->b.x, st->b.y);
|
|
|
|
st->c = PointAt(st->c.x, st->c.y);
|
2009-01-25 09:19:59 +00:00
|
|
|
// Works out that my chosen contour direction is inconsistent with
|
|
|
|
// the triangle direction, sigh.
|
|
|
|
st->FlipNormal();
|
2009-01-19 10:37:10 +00:00
|
|
|
}
|
2009-01-22 10:02:46 +00:00
|
|
|
|
2009-01-19 10:37:10 +00:00
|
|
|
el.Clear();
|
|
|
|
poly.Clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SSurface::Clear(void) {
|
|
|
|
trim.Clear();
|
|
|
|
}
|
|
|
|
|
2009-01-25 09:19:59 +00:00
|
|
|
void SShell::MakeFromExtrusionOf(SBezierLoopSet *sbls, Vector t0, Vector t1,
|
|
|
|
int color)
|
|
|
|
{
|
2009-01-23 03:30:30 +00:00
|
|
|
ZERO(this);
|
2009-01-17 05:28:49 +00:00
|
|
|
|
2009-01-19 10:37:10 +00:00
|
|
|
// Make the extrusion direction consistent with respect to the normal
|
|
|
|
// of the sketch we're extruding.
|
|
|
|
if((t0.Minus(t1)).Dot(sbls->normal) < 0) {
|
|
|
|
SWAP(Vector, t0, t1);
|
|
|
|
}
|
2009-01-19 03:33:15 +00:00
|
|
|
|
2009-01-25 11:52:29 +00:00
|
|
|
// Define a coordinate system to contain the original sketch, and get
|
|
|
|
// a bounding box in that csys
|
|
|
|
Vector n = sbls->normal.ScaledBy(-1);
|
|
|
|
Vector u = n.Normal(0), v = n.Normal(1);
|
|
|
|
Vector orig = sbls->point;
|
|
|
|
double umax = 1e-10, umin = 1e10;
|
|
|
|
sbls->GetBoundingProjd(u, orig, &umin, &umax);
|
|
|
|
double vmax = 1e-10, vmin = 1e10;
|
|
|
|
sbls->GetBoundingProjd(v, orig, &vmin, &vmax);
|
|
|
|
// and now fix things up so that all u and v lie between 0 and 1
|
|
|
|
orig = orig.Plus(u.ScaledBy(umin));
|
|
|
|
orig = orig.Plus(v.ScaledBy(vmin));
|
|
|
|
u = u.ScaledBy(umax - umin);
|
|
|
|
v = v.ScaledBy(vmax - vmin);
|
|
|
|
|
|
|
|
// So we can now generate the top and bottom surfaces of the extrusion,
|
|
|
|
// planes within a translated (and maybe mirrored) version of that csys.
|
2009-01-19 10:37:10 +00:00
|
|
|
SSurface s0, s1;
|
2009-01-25 11:52:29 +00:00
|
|
|
s0 = SSurface::FromPlane(orig.Plus(t0), u, v);
|
2009-01-25 09:19:59 +00:00
|
|
|
s0.color = color;
|
2009-01-25 11:52:29 +00:00
|
|
|
s1 = SSurface::FromPlane(orig.Plus(t1).Plus(u), u.ScaledBy(-1), v);
|
2009-01-25 09:19:59 +00:00
|
|
|
s1.color = color;
|
2009-01-23 03:30:30 +00:00
|
|
|
hSSurface hs0 = surface.AddAndAssignId(&s0),
|
|
|
|
hs1 = surface.AddAndAssignId(&s1);
|
2009-01-19 10:37:10 +00:00
|
|
|
|
|
|
|
// Now go through the input curves. For each one, generate its surface
|
|
|
|
// of extrusion, its two translated trim curves, and one trim line. We
|
|
|
|
// go through by loops so that we can assign the lines correctly.
|
|
|
|
SBezierLoop *sbl;
|
|
|
|
for(sbl = sbls->l.First(); sbl; sbl = sbls->l.NextAfter(sbl)) {
|
|
|
|
SBezier *sb;
|
|
|
|
|
|
|
|
typedef struct {
|
2009-01-25 09:19:59 +00:00
|
|
|
hSCurve hc;
|
2009-01-19 10:37:10 +00:00
|
|
|
hSSurface hs;
|
|
|
|
} TrimLine;
|
|
|
|
List<TrimLine> trimLines;
|
|
|
|
ZERO(&trimLines);
|
|
|
|
|
|
|
|
for(sb = sbl->l.First(); sb; sb = sbl->l.NextAfter(sb)) {
|
|
|
|
// Generate the surface of extrusion of this curve, and add
|
|
|
|
// it to the list
|
|
|
|
SSurface ss = SSurface::FromExtrusionOf(sb, t0, t1);
|
2009-01-25 09:19:59 +00:00
|
|
|
ss.color = color;
|
2009-01-23 03:30:30 +00:00
|
|
|
hSSurface hsext = surface.AddAndAssignId(&ss);
|
2009-01-19 10:37:10 +00:00
|
|
|
|
|
|
|
// Translate the curve by t0 and t1 to produce two trim curves
|
|
|
|
SCurve sc;
|
|
|
|
ZERO(&sc);
|
|
|
|
sb->MakePwlInto(&(sc.pts), t0);
|
2009-01-23 03:30:30 +00:00
|
|
|
hSCurve hc0 = curve.AddAndAssignId(&sc);
|
2009-01-25 09:19:59 +00:00
|
|
|
STrimBy stb0 = STrimBy::EntireCurve(this, hc0, false);
|
2009-01-19 10:37:10 +00:00
|
|
|
|
|
|
|
ZERO(&sc);
|
|
|
|
sb->MakePwlInto(&(sc.pts), t1);
|
2009-01-23 03:30:30 +00:00
|
|
|
hSCurve hc1 = curve.AddAndAssignId(&sc);
|
2009-01-25 09:19:59 +00:00
|
|
|
STrimBy stb1 = STrimBy::EntireCurve(this, hc1, true);
|
2009-01-19 10:37:10 +00:00
|
|
|
|
|
|
|
// The translated curves trim the flat top and bottom surfaces.
|
2009-01-23 03:30:30 +00:00
|
|
|
(surface.FindById(hs0))->trim.Add(&stb0);
|
2009-01-27 07:59:58 +00:00
|
|
|
(curve.FindById(hc0))->surfA = hs0;
|
|
|
|
|
2009-01-23 03:30:30 +00:00
|
|
|
(surface.FindById(hs1))->trim.Add(&stb1);
|
2009-01-27 07:59:58 +00:00
|
|
|
(curve.FindById(hc1))->surfA = hs1;
|
2009-01-19 10:37:10 +00:00
|
|
|
|
|
|
|
// The translated curves also trim the surface of extrusion.
|
2009-01-25 09:19:59 +00:00
|
|
|
stb0 = STrimBy::EntireCurve(this, hc0, true);
|
2009-01-23 03:30:30 +00:00
|
|
|
(surface.FindById(hsext))->trim.Add(&stb0);
|
2009-01-27 07:59:58 +00:00
|
|
|
(curve.FindById(hc0))->surfB = hsext;
|
|
|
|
|
2009-01-25 09:19:59 +00:00
|
|
|
stb1 = STrimBy::EntireCurve(this, hc1, false);
|
2009-01-23 03:30:30 +00:00
|
|
|
(surface.FindById(hsext))->trim.Add(&stb1);
|
2009-01-27 07:59:58 +00:00
|
|
|
(curve.FindById(hc1))->surfB = hsext;
|
2009-01-19 10:37:10 +00:00
|
|
|
|
|
|
|
// And form the trim line
|
|
|
|
Vector pt = sb->Finish();
|
|
|
|
Vector p0 = pt.Plus(t0), p1 = pt.Plus(t1);
|
|
|
|
ZERO(&sc);
|
|
|
|
sc.pts.Add(&p0);
|
|
|
|
sc.pts.Add(&p1);
|
2009-01-23 03:30:30 +00:00
|
|
|
hSCurve hl = curve.AddAndAssignId(&sc);
|
2009-01-19 10:37:10 +00:00
|
|
|
// save this for later
|
|
|
|
TrimLine tl;
|
2009-01-25 09:19:59 +00:00
|
|
|
tl.hc = hl;
|
2009-01-19 10:37:10 +00:00
|
|
|
tl.hs = hsext;
|
|
|
|
trimLines.Add(&tl);
|
|
|
|
}
|
2009-01-17 05:28:49 +00:00
|
|
|
|
2009-01-19 10:37:10 +00:00
|
|
|
int i;
|
|
|
|
for(i = 0; i < trimLines.n; i++) {
|
|
|
|
TrimLine *tl = &(trimLines.elem[i]);
|
2009-01-23 03:30:30 +00:00
|
|
|
SSurface *ss = surface.FindById(tl->hs);
|
2009-01-17 05:28:49 +00:00
|
|
|
|
2009-01-19 10:37:10 +00:00
|
|
|
TrimLine *tlp = &(trimLines.elem[WRAP(i-1, trimLines.n)]);
|
2009-01-17 05:28:49 +00:00
|
|
|
|
2009-01-25 09:19:59 +00:00
|
|
|
STrimBy stb;
|
|
|
|
stb = STrimBy::EntireCurve(this, tl->hc, true);
|
|
|
|
ss->trim.Add(&stb);
|
|
|
|
stb = STrimBy::EntireCurve(this, tlp->hc, false);
|
|
|
|
ss->trim.Add(&stb);
|
2009-01-27 07:59:58 +00:00
|
|
|
|
|
|
|
(curve.FindById(tl->hc))->surfA = ss->h;
|
|
|
|
(curve.FindById(tlp->hc))->surfB = ss->h;
|
2009-01-19 10:37:10 +00:00
|
|
|
}
|
|
|
|
trimLines.Clear();
|
|
|
|
}
|
2009-01-23 03:30:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void SShell::MakeFromCopyOf(SShell *a) {
|
|
|
|
Vector t = Vector::From(0, 0, 0);
|
|
|
|
Quaternion q = Quaternion::From(1, 0, 0, 0);
|
|
|
|
|
|
|
|
MakeFromTransformationOf(a, t, q);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SShell::MakeFromTransformationOf(SShell *a, Vector t, Quaternion q) {
|
|
|
|
SSurface *s;
|
|
|
|
for(s = a->surface.First(); s; s = a->surface.NextAfter(s)) {
|
|
|
|
SSurface n;
|
|
|
|
n = SSurface::FromTransformationOf(s, t, q, true);
|
|
|
|
surface.Add(&n); // keeping the old ID
|
|
|
|
}
|
2009-01-17 05:28:49 +00:00
|
|
|
|
2009-01-23 03:30:30 +00:00
|
|
|
SCurve *c;
|
|
|
|
for(c = a->curve.First(); c; c = a->curve.NextAfter(c)) {
|
|
|
|
SCurve n;
|
|
|
|
n = SCurve::FromTransformationOf(c, t, q);
|
|
|
|
curve.Add(&n); // keeping the old ID
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SShell::MakeEdgesInto(SEdgeList *sel) {
|
|
|
|
SSurface *s;
|
|
|
|
for(s = surface.First(); s; s = surface.NextAfter(s)) {
|
|
|
|
s->MakeEdgesInto(this, sel, false);
|
|
|
|
}
|
2009-01-17 05:28:49 +00:00
|
|
|
}
|
|
|
|
|
2009-01-19 10:37:10 +00:00
|
|
|
void SShell::TriangulateInto(SMesh *sm) {
|
|
|
|
SSurface *s;
|
|
|
|
for(s = surface.First(); s; s = surface.NextAfter(s)) {
|
|
|
|
s->TriangulateInto(this, sm);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SShell::Clear(void) {
|
|
|
|
SSurface *s;
|
|
|
|
for(s = surface.First(); s; s = surface.NextAfter(s)) {
|
|
|
|
s->Clear();
|
|
|
|
}
|
|
|
|
surface.Clear();
|
|
|
|
|
|
|
|
SCurve *c;
|
|
|
|
for(c = curve.First(); c; c = curve.NextAfter(c)) {
|
|
|
|
c->Clear();
|
|
|
|
}
|
|
|
|
curve.Clear();
|
|
|
|
}
|
2009-01-17 05:28:49 +00:00
|
|
|
|