solvespace/polygon.cpp

203 lines
4.9 KiB
C++
Raw Normal View History

#include "solvespace.h"
bool SEdgeList::AssemblePolygon(SPolygon *dest, SEdge *errorAt) {
dest->Clear();
l.ClearTags();
for(;;) {
Vector first, last;
int i;
for(i = 0; i < l.n; i++) {
if(!l.elem[i].tag) {
first = l.elem[i].a;
last = l.elem[i].b;
l.elem[i].tag = 1;
break;
}
}
if(i >= l.n) {
return true;
}
dest->AddEmptyContour();
dest->AddPoint(first);
dest->AddPoint(last);
do {
for(i = 0; i < l.n; i++) {
SEdge *se = &(l.elem[i]);
if(se->tag) continue;
if(se->a.Equals(last)) {
dest->AddPoint(se->b);
last = se->b;
se->tag = 1;
break;
}
if(se->b.Equals(last)) {
dest->AddPoint(se->a);
last = se->a;
se->tag = 1;
break;
}
}
if(i >= l.n) {
// Couldn't assemble a closed contour; mark where.
errorAt->a = first;
errorAt->b = last;
return false;
}
} while(!last.Equals(first));
}
}
void SPolygon::Clear(void) {
int i;
for(i = 0; i < l.n; i++) {
(l.elem[i]).l.Clear();
}
l.Clear();
}
void SPolygon::AddEmptyContour(void) {
SContour c;
memset(&c, 0, sizeof(c));
l.Add(&c);
}
void SPolygon::AddPoint(Vector p) {
if(l.n < 1) oops();
SPoint sp;
sp.tag = 0;
sp.p = p;
// Add to the last contour in the list
(l.elem[l.n-1]).l.Add(&sp);
}
void SPolygon::MakeEdgesInto(SEdgeList *el) {
int i;
for(i = 0; i < l.n; i++) {
(l.elem[i]).MakeEdgesInto(el);
}
}
Vector SPolygon::ComputeNormal(void) {
if(l.n < 1) return Vector::MakeFrom(0, 0, 0);
return (l.elem[0]).ComputeNormal();
}
void SPolygon::FixContourDirections(void) {
// Outside curve looks counterclockwise, projected against our normal.
int i, j;
for(i = 0; i < l.n; i++) {
SContour *sc = &(l.elem[i]);
if(sc->l.n < 1) continue;
Vector pt = (sc->l.elem[0]).p;
bool outer = true;
for(j = 0; j < l.n; j++) {
if(i == j) continue;
SContour *sct = &(l.elem[j]);
if(sct->ContainsPointProjdToNormal(normal, pt)) {
outer = !outer;
}
}
bool clockwise = sc->IsClockwiseProjdToNormal(normal);
if(clockwise && outer || (!clockwise && !outer)) {
sc->Reverse();
}
}
}
void SContour::MakeEdgesInto(SEdgeList *el) {
int i;
for(i = 0; i < (l.n-1); i++) {
SEdge e;
e.a = l.elem[i].p;
e.b = l.elem[i+1].p;
el->l.Add(&e);
}
}
Vector SContour::ComputeNormal(void) {
Vector n = Vector::MakeFrom(0, 0, 0);
for(int i = 0; i < l.n - 2; i++) {
Vector u = (l.elem[i+1].p).Minus(l.elem[i+0].p).WithMagnitude(1);
Vector v = (l.elem[i+2].p).Minus(l.elem[i+1].p).WithMagnitude(1);
Vector nt = u.Cross(v);
if(nt.Magnitude() > n.Magnitude()) {
n = nt;
}
}
return n;
}
bool SContour::IsClockwiseProjdToNormal(Vector n) {
if(n.Magnitude() < 0.01) oops();
// An arbitrary 2d coordinate system that has n as its normal
Vector u = n.Normal(0);
Vector v = n.Normal(1);
double area = 0;
for(int i = 0; i < (l.n - 1); i++) {
double u0 = (l.elem[i ].p).Dot(u);
double v0 = (l.elem[i ].p).Dot(v);
double u1 = (l.elem[i+1].p).Dot(u);
double v1 = (l.elem[i+1].p).Dot(v);
area += ((v0 + v1)/2)*(u1 - u0);
}
return (area < 0);
}
bool SContour::ContainsPointProjdToNormal(Vector n, Vector p) {
Vector u = n.Normal(0);
Vector v = n.Normal(1);
double up = p.Dot(u);
double vp = p.Dot(v);
bool inside = false;
for(int i = 0; i < (l.n - 1); i++) {
double ua = (l.elem[i ].p).Dot(u);
double va = (l.elem[i ].p).Dot(v);
double ub = (l.elem[i+1].p).Dot(u);
double vb = (l.elem[i+1].p).Dot(v);
// Write the parametric equation of the line, standardized so that
// t = 0 has smaller v than t = 1
double u0, v0, du, dv;
if(va < vb) {
u0 = ua; v0 = va;
du = (ub - ua); dv = (vb - va);
} else {
u0 = ub; v0 = vb;
du = (ua - ub); dv = (va - vb);
}
if(dv == 0) continue; // intersects our horiz ray either 0 or 2 times
double t = (vp - v0)/dv;
double ui = u0 + t*du;
if(ui > up && t >= 0 && t < 1) inside = !inside;
}
return inside;
}
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;
}
}