summaryrefslogtreecommitdiffstats
path: root/Polygon.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Polygon.cpp')
-rw-r--r--Polygon.cpp205
1 files changed, 201 insertions, 4 deletions
diff --git a/Polygon.cpp b/Polygon.cpp
index 9340135..3425727 100644
--- a/Polygon.cpp
+++ b/Polygon.cpp
@@ -1,8 +1,9 @@
#include "Polygon.h"
+#include <list>
#include <math.h>
-double Polygon::area() const {
+double Polygon::signedArea() const {
double d = 0.0;
for(const_iterator it = begin(); it != end(); it++) {
@@ -12,7 +13,17 @@ double Polygon::area() const {
d += (it2->getX()+it->getX())*(it2->getY()-it->getY());
}
- return fabs(d/2);
+ return d/2;
+}
+
+Polygon::Direction Polygon::getDirection() const {
+ double area = signedArea();
+
+ return (area > 0) ? Triangle::CW : (area < 0) ? Triangle::CCW : Triangle::Unknown;
+}
+
+double Polygon::area() const {
+ return fabs(signedArea());
}
double Polygon::perimeter() const {
@@ -103,8 +114,8 @@ bool Polygon::contains(const Vertex &v) const {
bool Polygon::intersects(const Line &l) const {
Line line;
- for(Polygon::const_iterator it = begin(); it != end(); it++) {
- Polygon::const_iterator it2 = it+1;
+ for(const_iterator it = begin(); it != end(); it++) {
+ const_iterator it2 = it+1;
if(it2 == end()) it2 = begin();
line.setVertex1(*it);
@@ -116,3 +127,189 @@ bool Polygon::intersects(const Line &l) const {
return false;
}
+
+bool Polygon::isConcave(const Direction &dir, const Vertex &v1, const Vertex &v2, const Vertex &v3) const {
+ switch(dir) {
+ case Triangle::CW:
+ return (v1.getX()-v2.getX())*(v3.getY()-v2.getY()) > (v3.getX()-v2.getX())*(v1.getY()-v2.getY());
+ case Triangle::CCW:
+ return (v1.getX()-v2.getX())*(v3.getY()-v2.getY()) < (v3.getX()-v2.getX())*(v1.getY()-v2.getY());
+ }
+
+ return false;
+}
+
+bool Polygon::intersections(std::vector<Intersection> *intersections) const {
+ bool ret = false;
+ size_t s = size();
+
+ for(size_t i = 0; i+2 < s; i++) {
+ Line l(at(i), at(i+1));
+
+ for(size_t j = i+2; j < s; j++) {
+ if(i == (j+1)%s) continue;
+
+ Line l2(at(j), at((j+1)%s));
+
+ Vertex v;
+ if(l.intersects(l2, &v) == INTERSECTION_SEGMENT_SEGMENT) {
+ if(intersections) {
+ Intersection in = {i, i+1, j, ((j+1)%s), v};
+
+ intersections->push_back(in);
+ ret = true;
+ }
+ else {
+ return true;
+ }
+ }
+ }
+ }
+
+ return ret;
+}
+
+bool Polygon::isSimple() const {
+ return !intersections();
+}
+
+bool Polygon::simplify(Polygon &polygon) const {
+ std::vector<Intersection> ins;
+
+ if(!intersections(&ins)) return false;
+
+ int s = size();
+ int start = 0;
+
+ for(int i = 1; i < s; i++) {
+ if(at(i).getX() < at(start).getX())
+ start = i;
+ else if(at(i).getX() == at(start).getX() && at(i).getY() < at(start).getY())
+ start = i;
+ }
+
+ int dir = Triangle(at((s+start-1)%s), at(start), at((start+1)%s)).getDirection();
+
+ int i2 = start;
+ const Vertex *v2 = &at(i2);
+ bool intersected;
+
+ do {
+ intersected = false;
+
+ int i = i2;
+
+ if(dir == Triangle::CW)
+ i2 = (i2+1)%s;
+ else
+ i2 = (s+i2-1)%s;
+
+ const Vertex *v1 = v2;
+ v2 = &at(i2);
+
+ Vertex *v = NULL;
+ int v3, v4;
+
+ for(std::vector<Intersection>::iterator in = ins.begin(); in != ins.end(); ++in) {
+ if(v2->distanceSq(in->v) >= v2->distanceSq(*v1) || v1 == &in->v)
+ continue;
+
+ if(v) {
+ if(v1->distanceSq(in->v) >= v1->distanceSq(*v))
+ continue;
+ }
+
+ if((i == in->va1 && i2 == in->va2) || (i == in->va2 && i2 == in->va1)) {
+ v = &in->v;
+ v3 = in->vb1;
+ v4 = in->vb2;
+ }
+ else if((i == in->vb1 && i2 == in->vb2) || (i == in->vb2 && i2 == in->vb1)) {
+ v = &in->v;
+ v3 = in->va1;
+ v4 = in->va2;
+ }
+ }
+
+ if(v) {
+ v2 = v;
+ intersected = true;
+
+ if(isConcave(Triangle::CW, *v1, *v2, at(v4))) {
+ i2 = v3;
+ dir = Triangle::CW;
+ }
+ else {
+ i2 = v4;
+ dir = Triangle::CCW;
+ }
+ }
+
+ polygon.push_back(*v2);
+ } while(i2 != start || intersected);
+
+ return true;
+}
+
+// FIXME: has still some problems...
+void Polygon::doTriangulate(std::vector<Triangle> &triangles) const {
+ size_t s = size();
+
+ if(s < 3) return;
+
+ std::vector<size_t> p;
+ std::list<size_t> concave;
+ Direction dir = getDirection();
+
+ for(size_t i = 0; i < s; i++) {
+ p.push_back((i+1)%s);
+
+ if(isConcave(dir, at(i), at((i+1)%s), at((i+2)%s)))
+ concave.push_back((i+1)%s);
+ }
+
+ for(size_t i = 0; i < p.size() && p.size() > 3; i++) {
+ size_t s2 = p.size();
+
+ const Vertex *v0 = &at(p[i]);
+ const Vertex *v1 = &at(p[(i+1)%s2]);
+ const Vertex *v2 = &at(p[(i+2)%s2]);
+ const Vertex *v3 = &at(p[(i+3)%s2]);
+ const Vertex *v4 = &at(p[(i+4)%s2]);
+
+ if(isConcave(dir, *v1, *v2, *v3))
+ continue;
+
+ Triangle t(*v1, *v2, *v3);
+
+ std::list<size_t>::iterator it = concave.begin();
+ for(; it != concave.end(); it++) {
+ if(*it != p[(i+1)%s2] && *it != p[(i+3)%s2] && t.contains(at(*it)))
+ break;
+ }
+
+ if(it != concave.end())
+ continue;
+
+ triangles.push_back(t);
+
+ if(isConcave(dir, *v2, *v3, *v4) && !isConcave(dir, *v1, *v3, *v4))
+ concave.remove(p[(i+3)%s2]);
+
+ if(isConcave(dir, *v0, *v1, *v2) && !isConcave(dir, *v0, *v1, *v3)) {
+ concave.remove(p[(i+1)%s2]);
+ }
+
+ p.erase(p.begin()+(i+2)%s2);
+ i = 0;
+ }
+
+ triangles.push_back(Triangle(at(p[0]), at(p[1]), at(p[2])));
+}
+
+void Polygon::triangulate(std::vector<Triangle> &triangles) const {
+ Polygon p;
+
+ if(simplify(p)) p.doTriangulate(triangles);
+ else doTriangulate(triangles);
+}