291 lines
7.2 KiB
C++
291 lines
7.2 KiB
C++
|
//-----------------------------------------------------------------------------
|
||
|
// Anything involving curves and sets of curves (except for the real math,
|
||
|
// which is in ratpoly.cpp).
|
||
|
//-----------------------------------------------------------------------------
|
||
|
#include "../solvespace.h"
|
||
|
|
||
|
SBezier SBezier::From(Vector p0, Vector p1) {
|
||
|
SBezier ret;
|
||
|
ZERO(&ret);
|
||
|
ret.deg = 1;
|
||
|
ret.weight[0] = ret.weight[1] = 1;
|
||
|
ret.ctrl[0] = p0;
|
||
|
ret.ctrl[1] = p1;
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
SBezier SBezier::From(Vector p0, Vector p1, Vector p2) {
|
||
|
SBezier ret;
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
SBezier SBezier::From(Vector p0, Vector p1, Vector p2, Vector p3) {
|
||
|
SBezier ret;
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
Vector SBezier::Start(void) {
|
||
|
return ctrl[0];
|
||
|
}
|
||
|
|
||
|
Vector SBezier::Finish(void) {
|
||
|
return ctrl[deg];
|
||
|
}
|
||
|
|
||
|
void SBezier::Reverse(void) {
|
||
|
int i;
|
||
|
for(i = 0; i < (deg+1)/2; i++) {
|
||
|
SWAP(Vector, ctrl[i], ctrl[deg-i]);
|
||
|
SWAP(double, weight[i], weight[deg-i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
bool SBezier::Equals(SBezier *b) {
|
||
|
// We just test of identical degree and control points, even though two
|
||
|
// curves could still be coincident (even sharing endpoints).
|
||
|
if(deg != b->deg) return false;
|
||
|
int i;
|
||
|
for(i = 0; i <= deg; i++) {
|
||
|
if(!(ctrl[i]).Equals(b->ctrl[i])) return false;
|
||
|
if(fabs(weight[i] - b->weight[i]) > LENGTH_EPS) return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void SBezierList::Clear(void) {
|
||
|
l.Clear();
|
||
|
}
|
||
|
|
||
|
|
||
|
SBezierLoop SBezierLoop::FromCurves(SBezierList *sbl,
|
||
|
bool *allClosed, SEdge *errorAt)
|
||
|
{
|
||
|
SBezierLoop loop;
|
||
|
ZERO(&loop);
|
||
|
|
||
|
if(sbl->l.n < 1) return loop;
|
||
|
sbl->l.ClearTags();
|
||
|
|
||
|
SBezier *first = &(sbl->l.elem[0]);
|
||
|
first->tag = 1;
|
||
|
loop.l.Add(first);
|
||
|
Vector start = first->Start();
|
||
|
Vector hanging = first->Finish();
|
||
|
|
||
|
sbl->l.RemoveTagged();
|
||
|
|
||
|
while(sbl->l.n > 0 && !hanging.Equals(start)) {
|
||
|
int i;
|
||
|
bool foundNext = false;
|
||
|
for(i = 0; i < sbl->l.n; i++) {
|
||
|
SBezier *test = &(sbl->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();
|
||
|
sbl->l.RemoveTagged();
|
||
|
foundNext = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
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)) {
|
||
|
*allClosed = true;
|
||
|
} else {
|
||
|
// We ran out of edges without forming a closed loop.
|
||
|
errorAt->a = hanging;
|
||
|
errorAt->b = start;
|
||
|
*allClosed = false;
|
||
|
}
|
||
|
|
||
|
return loop;
|
||
|
}
|
||
|
|
||
|
void SBezierLoop::Reverse(void) {
|
||
|
l.Reverse();
|
||
|
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();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SBezierLoop::MakePwlInto(SContour *sc) {
|
||
|
List<Vector> lv;
|
||
|
ZERO(&lv);
|
||
|
|
||
|
int i, j;
|
||
|
for(i = 0; i < l.n; i++) {
|
||
|
SBezier *sb = &(l.elem[i]);
|
||
|
sb->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];
|
||
|
}
|
||
|
|
||
|
|
||
|
SBezierLoopSet SBezierLoopSet::From(SBezierList *sbl, SPolygon *poly,
|
||
|
bool *allClosed, SEdge *errorAt)
|
||
|
{
|
||
|
int i;
|
||
|
SBezierLoopSet ret;
|
||
|
ZERO(&ret);
|
||
|
|
||
|
while(sbl->l.n > 0) {
|
||
|
bool thisClosed;
|
||
|
SBezierLoop loop;
|
||
|
loop = SBezierLoop::FromCurves(sbl, &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;
|
||
|
if(poly->l.n > 0) {
|
||
|
ret.point = poly->AnyPoint();
|
||
|
} else {
|
||
|
ret.point = Vector::From(0, 0, 0);
|
||
|
}
|
||
|
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 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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SBezierLoopSet::Clear(void) {
|
||
|
int i;
|
||
|
for(i = 0; i < l.n; i++) {
|
||
|
(l.elem[i]).Clear();
|
||
|
}
|
||
|
l.Clear();
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
ret.surfA = a->surfA;
|
||
|
ret.surfB = a->surfB;
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
void SCurve::Clear(void) {
|
||
|
pts.Clear();
|
||
|
}
|
||
|
|
||
|
STrimBy STrimBy::EntireCurve(SShell *shell, hSCurve hsc, bool backwards) {
|
||
|
STrimBy stb;
|
||
|
ZERO(&stb);
|
||
|
stb.curve = hsc;
|
||
|
SCurve *sc = shell->curve.FindById(hsc);
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
return stb;
|
||
|
}
|
||
|
|