From 1ebd116dff21ca8e347676288bfda3056af18a8c Mon Sep 17 00:00:00 2001 From: aramis_acg Date: Tue, 17 Jan 2012 01:17:50 +0000 Subject: [PATCH] + add poly2tri to assimp repository. git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@1115 67173fc5-114c-0410-ac8e-9d2fd5bffc1f --- contrib/poly2tri/AUTHORS | 8 + contrib/poly2tri/LICENSE | 27 + contrib/poly2tri/README | 51 ++ contrib/poly2tri/poly2tri/common/shapes.cc | 367 +++++++++ contrib/poly2tri/poly2tri/common/shapes.h | 327 ++++++++ contrib/poly2tri/poly2tri/common/utils.h | 105 +++ contrib/poly2tri/poly2tri/poly2tri.h | 39 + .../poly2tri/sweep/advancing_front.cc | 109 +++ .../poly2tri/poly2tri/sweep/advancing_front.h | 118 +++ contrib/poly2tri/poly2tri/sweep/cdt.cc | 72 ++ contrib/poly2tri/poly2tri/sweep/cdt.h | 105 +++ contrib/poly2tri/poly2tri/sweep/sweep.cc | 764 ++++++++++++++++++ contrib/poly2tri/poly2tri/sweep/sweep.h | 278 +++++++ .../poly2tri/poly2tri/sweep/sweep_context.cc | 202 +++++ .../poly2tri/poly2tri/sweep/sweep_context.h | 186 +++++ contrib/poly2tri_patch.txt | 67 ++ 16 files changed, 2825 insertions(+) create mode 100644 contrib/poly2tri/AUTHORS create mode 100644 contrib/poly2tri/LICENSE create mode 100644 contrib/poly2tri/README create mode 100644 contrib/poly2tri/poly2tri/common/shapes.cc create mode 100644 contrib/poly2tri/poly2tri/common/shapes.h create mode 100644 contrib/poly2tri/poly2tri/common/utils.h create mode 100644 contrib/poly2tri/poly2tri/poly2tri.h create mode 100644 contrib/poly2tri/poly2tri/sweep/advancing_front.cc create mode 100644 contrib/poly2tri/poly2tri/sweep/advancing_front.h create mode 100644 contrib/poly2tri/poly2tri/sweep/cdt.cc create mode 100644 contrib/poly2tri/poly2tri/sweep/cdt.h create mode 100644 contrib/poly2tri/poly2tri/sweep/sweep.cc create mode 100644 contrib/poly2tri/poly2tri/sweep/sweep.h create mode 100644 contrib/poly2tri/poly2tri/sweep/sweep_context.cc create mode 100644 contrib/poly2tri/poly2tri/sweep/sweep_context.h create mode 100644 contrib/poly2tri_patch.txt diff --git a/contrib/poly2tri/AUTHORS b/contrib/poly2tri/AUTHORS new file mode 100644 index 000000000..1736f14bb --- /dev/null +++ b/contrib/poly2tri/AUTHORS @@ -0,0 +1,8 @@ +Primary Contributors: + + Mason Green (C++, Python) + Thomas Åhlén (Java) + +Other Contributors: + + diff --git a/contrib/poly2tri/LICENSE b/contrib/poly2tri/LICENSE new file mode 100644 index 000000000..9417c0836 --- /dev/null +++ b/contrib/poly2tri/LICENSE @@ -0,0 +1,27 @@ +Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors +http://code.google.com/p/poly2tri/ + +All rights reserved. +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +* Neither the name of Poly2Tri nor the names of its contributors may be + used to endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/contrib/poly2tri/README b/contrib/poly2tri/README new file mode 100644 index 000000000..2857e2983 --- /dev/null +++ b/contrib/poly2tri/README @@ -0,0 +1,51 @@ +================== +INSTALLATION GUIDE +================== + +------------ +Dependencies +------------ + + Core poly2tri lib: + - Standard Template Library (STL) + + Testbed: + - gcc + - OpenGL + - GLFW (http://glfw.sf.net) + - Python + +Waf (http://code.google.com/p/waf/) is used to compile the testbed. +A waf script (86kb) is included in the repositoty. + +---------------------------------------------- +Building the Testbed +---------------------------------------------- + +Posix/MSYS environment: + + ./waf configure + ./waf build + +Windows command line: + + python waf configure + python waf build + +---------------------------------------------- +Running the Examples +---------------------------------------------- + +Load data points from a file: +p2t + +Random distribution of points inside a consrained box: +p2t random + +Examples: + + ./p2t dude.dat 300 500 2 + ./p2t nazca_monkey.dat 0 0 9 + + ./p2t random 10 100 5.0 + ./p2t random 1000 20000 0.025 \ No newline at end of file diff --git a/contrib/poly2tri/poly2tri/common/shapes.cc b/contrib/poly2tri/poly2tri/common/shapes.cc new file mode 100644 index 000000000..77bafa150 --- /dev/null +++ b/contrib/poly2tri/poly2tri/common/shapes.cc @@ -0,0 +1,367 @@ +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "shapes.h" +#include + +namespace p2t { + +Triangle::Triangle(Point& a, Point& b, Point& c) +{ + points_[0] = &a; points_[1] = &b; points_[2] = &c; + neighbors_[0] = NULL; neighbors_[1] = NULL; neighbors_[2] = NULL; + constrained_edge[0] = constrained_edge[1] = constrained_edge[2] = false; + delaunay_edge[0] = delaunay_edge[1] = delaunay_edge[2] = false; + interior_ = false; +} + +// Update neighbor pointers +void Triangle::MarkNeighbor(Point* p1, Point* p2, Triangle* t) +{ + if ((p1 == points_[2] && p2 == points_[1]) || (p1 == points_[1] && p2 == points_[2])) + neighbors_[0] = t; + else if ((p1 == points_[0] && p2 == points_[2]) || (p1 == points_[2] && p2 == points_[0])) + neighbors_[1] = t; + else if ((p1 == points_[0] && p2 == points_[1]) || (p1 == points_[1] && p2 == points_[0])) + neighbors_[2] = t; + else + assert(0); +} + +// Exhaustive search to update neighbor pointers +void Triangle::MarkNeighbor(Triangle& t) +{ + if (t.Contains(points_[1], points_[2])) { + neighbors_[0] = &t; + t.MarkNeighbor(points_[1], points_[2], this); + } else if (t.Contains(points_[0], points_[2])) { + neighbors_[1] = &t; + t.MarkNeighbor(points_[0], points_[2], this); + } else if (t.Contains(points_[0], points_[1])) { + neighbors_[2] = &t; + t.MarkNeighbor(points_[0], points_[1], this); + } +} + +/** + * Clears all references to all other triangles and points + */ +void Triangle::Clear() +{ + Triangle *t; + for( int i=0; i<3; i++ ) + { + t = neighbors_[i]; + if( t != NULL ) + { + t->ClearNeighbor( this ); + } + } + ClearNeighbors(); + points_[0]=points_[1]=points_[2] = NULL; +} + +void Triangle::ClearNeighbor(Triangle *triangle ) +{ + if( neighbors_[0] == triangle ) + { + neighbors_[0] = NULL; + } + else if( neighbors_[1] == triangle ) + { + neighbors_[1] = NULL; + } + else + { + neighbors_[2] = NULL; + } +} + +void Triangle::ClearNeighbors() +{ + neighbors_[0] = NULL; + neighbors_[1] = NULL; + neighbors_[2] = NULL; +} + +void Triangle::ClearDelunayEdges() +{ + delaunay_edge[0] = delaunay_edge[1] = delaunay_edge[2] = false; +} + +Point* Triangle::OppositePoint(Triangle& t, Point& p) +{ + Point *cw = t.PointCW(p); + double x = cw->x; + double y = cw->y; + x = p.x; + y = p.y; + return PointCW(*cw); +} + +// Legalized triangle by rotating clockwise around point(0) +void Triangle::Legalize(Point& point) +{ + points_[1] = points_[0]; + points_[0] = points_[2]; + points_[2] = &point; +} + +// Legalize triagnle by rotating clockwise around oPoint +void Triangle::Legalize(Point& opoint, Point& npoint) +{ + if (&opoint == points_[0]) { + points_[1] = points_[0]; + points_[0] = points_[2]; + points_[2] = &npoint; + } else if (&opoint == points_[1]) { + points_[2] = points_[1]; + points_[1] = points_[0]; + points_[0] = &npoint; + } else if (&opoint == points_[2]) { + points_[0] = points_[2]; + points_[2] = points_[1]; + points_[1] = &npoint; + } else { + assert(0); + } +} + +int Triangle::Index(const Point* p) +{ + if (p == points_[0]) { + return 0; + } else if (p == points_[1]) { + return 1; + } else if (p == points_[2]) { + return 2; + } + assert(0); +} + +int Triangle::EdgeIndex(const Point* p1, const Point* p2) +{ + if (points_[0] == p1) { + if (points_[1] == p2) { + return 2; + } else if (points_[2] == p2) { + return 1; + } + } else if (points_[1] == p1) { + if (points_[2] == p2) { + return 0; + } else if (points_[0] == p2) { + return 2; + } + } else if (points_[2] == p1) { + if (points_[0] == p2) { + return 1; + } else if (points_[1] == p2) { + return 0; + } + } + return -1; +} + +void Triangle::MarkConstrainedEdge(const int index) +{ + constrained_edge[index] = true; +} + +void Triangle::MarkConstrainedEdge(Edge& edge) +{ + MarkConstrainedEdge(edge.p, edge.q); +} + +// Mark edge as constrained +void Triangle::MarkConstrainedEdge(Point* p, Point* q) +{ + if ((q == points_[0] && p == points_[1]) || (q == points_[1] && p == points_[0])) { + constrained_edge[2] = true; + } else if ((q == points_[0] && p == points_[2]) || (q == points_[2] && p == points_[0])) { + constrained_edge[1] = true; + } else if ((q == points_[1] && p == points_[2]) || (q == points_[2] && p == points_[1])) { + constrained_edge[0] = true; + } +} + +// The point counter-clockwise to given point +Point* Triangle::PointCW(Point& point) +{ + if (&point == points_[0]) { + return points_[2]; + } else if (&point == points_[1]) { + return points_[0]; + } else if (&point == points_[2]) { + return points_[1]; + } + assert(0); +} + +// The point counter-clockwise to given point +Point* Triangle::PointCCW(Point& point) +{ + if (&point == points_[0]) { + return points_[1]; + } else if (&point == points_[1]) { + return points_[2]; + } else if (&point == points_[2]) { + return points_[0]; + } + assert(0); +} + +// The neighbor clockwise to given point +Triangle* Triangle::NeighborCW(Point& point) +{ + if (&point == points_[0]) { + return neighbors_[1]; + } else if (&point == points_[1]) { + return neighbors_[2]; + } + return neighbors_[0]; +} + +// The neighbor counter-clockwise to given point +Triangle* Triangle::NeighborCCW(Point& point) +{ + if (&point == points_[0]) { + return neighbors_[2]; + } else if (&point == points_[1]) { + return neighbors_[0]; + } + return neighbors_[1]; +} + +bool Triangle::GetConstrainedEdgeCCW(Point& p) +{ + if (&p == points_[0]) { + return constrained_edge[2]; + } else if (&p == points_[1]) { + return constrained_edge[0]; + } + return constrained_edge[1]; +} + +bool Triangle::GetConstrainedEdgeCW(Point& p) +{ + if (&p == points_[0]) { + return constrained_edge[1]; + } else if (&p == points_[1]) { + return constrained_edge[2]; + } + return constrained_edge[0]; +} + +void Triangle::SetConstrainedEdgeCCW(Point& p, bool ce) +{ + if (&p == points_[0]) { + constrained_edge[2] = ce; + } else if (&p == points_[1]) { + constrained_edge[0] = ce; + } else { + constrained_edge[1] = ce; + } +} + +void Triangle::SetConstrainedEdgeCW(Point& p, bool ce) +{ + if (&p == points_[0]) { + constrained_edge[1] = ce; + } else if (&p == points_[1]) { + constrained_edge[2] = ce; + } else { + constrained_edge[0] = ce; + } +} + +bool Triangle::GetDelunayEdgeCCW(Point& p) +{ + if (&p == points_[0]) { + return delaunay_edge[2]; + } else if (&p == points_[1]) { + return delaunay_edge[0]; + } + return delaunay_edge[1]; +} + +bool Triangle::GetDelunayEdgeCW(Point& p) +{ + if (&p == points_[0]) { + return delaunay_edge[1]; + } else if (&p == points_[1]) { + return delaunay_edge[2]; + } + return delaunay_edge[0]; +} + +void Triangle::SetDelunayEdgeCCW(Point& p, bool e) +{ + if (&p == points_[0]) { + delaunay_edge[2] = e; + } else if (&p == points_[1]) { + delaunay_edge[0] = e; + } else { + delaunay_edge[1] = e; + } +} + +void Triangle::SetDelunayEdgeCW(Point& p, bool e) +{ + if (&p == points_[0]) { + delaunay_edge[1] = e; + } else if (&p == points_[1]) { + delaunay_edge[2] = e; + } else { + delaunay_edge[0] = e; + } +} + +// The neighbor across to given point +Triangle& Triangle::NeighborAcross(Point& opoint) +{ + if (&opoint == points_[0]) { + return *neighbors_[0]; + } else if (&opoint == points_[1]) { + return *neighbors_[1]; + } + return *neighbors_[2]; +} + +void Triangle::DebugPrint() +{ + using namespace std; + cout << points_[0]->x << "," << points_[0]->y << " "; + cout << points_[1]->x << "," << points_[1]->y << " "; + cout << points_[2]->x << "," << points_[2]->y << endl; +} + +} + diff --git a/contrib/poly2tri/poly2tri/common/shapes.h b/contrib/poly2tri/poly2tri/common/shapes.h new file mode 100644 index 000000000..7c63d09e3 --- /dev/null +++ b/contrib/poly2tri/poly2tri/common/shapes.h @@ -0,0 +1,327 @@ +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Include guard +#ifndef SHAPES_H +#define SHAPES_H + +#include +#include +#include +#include + +namespace p2t { + +struct Edge; + +struct Point { + + double x, y; + + /// Default constructor does nothing (for performance). + Point() + { + x = 0.0; + y = 0.0; + } + + /// The edges this point constitutes an upper ending point + std::vector edge_list; + + /// Construct using coordinates. + Point(double x, double y) : x(x), y(y) {} + + /// Set this point to all zeros. + void set_zero() + { + x = 0.0; + y = 0.0; + } + + /// Set this point to some specified coordinates. + void set(double x_, double y_) + { + x = x_; + y = y_; + } + + /// Negate this point. + Point operator -() const + { + Point v; + v.set(-x, -y); + return v; + } + + /// Add a point to this point. + void operator +=(const Point& v) + { + x += v.x; + y += v.y; + } + + /// Subtract a point from this point. + void operator -=(const Point& v) + { + x -= v.x; + y -= v.y; + } + + /// Multiply this point by a scalar. + void operator *=(double a) + { + x *= a; + y *= a; + } + + /// Get the length of this point (the norm). + double Length() const + { + return sqrt(x * x + y * y); + } + + /// Convert this point into a unit point. Returns the Length. + double Normalize() + { + double len = Length(); + x /= len; + y /= len; + return len; + } + +}; + +// Represents a simple polygon's edge +struct Edge { + + Point* p, *q; + + /// Constructor + Edge(Point& p1, Point& p2) : p(&p1), q(&p2) + { + if (p1.y > p2.y) { + q = &p1; + p = &p2; + } else if (p1.y == p2.y) { + if (p1.x > p2.x) { + q = &p1; + p = &p2; + } else if (p1.x == p2.x) { + // Repeat points + // ASSIMP_CHANGE (aramis_acg) + throw std::runtime_error("repeat points"); + //assert(false); + } + } + + q->edge_list.push_back(this); + } +}; + +// Triangle-based data structures are know to have better performance than quad-edge structures +// See: J. Shewchuk, "Triangle: Engineering a 2D Quality Mesh Generator and Delaunay Triangulator" +// "Triangulations in CGAL" +class Triangle { +public: + +/// Constructor +Triangle(Point& a, Point& b, Point& c); + +/// Flags to determine if an edge is a Constrained edge +bool constrained_edge[3]; +/// Flags to determine if an edge is a Delauney edge +bool delaunay_edge[3]; + +Point* GetPoint(const int& index); +Point* PointCW(Point& point); +Point* PointCCW(Point& point); +Point* OppositePoint(Triangle& t, Point& p); + +Triangle* GetNeighbor(const int& index); +void MarkNeighbor(Point* p1, Point* p2, Triangle* t); +void MarkNeighbor(Triangle& t); + +void MarkConstrainedEdge(const int index); +void MarkConstrainedEdge(Edge& edge); +void MarkConstrainedEdge(Point* p, Point* q); + +int Index(const Point* p); +int EdgeIndex(const Point* p1, const Point* p2); + +Triangle* NeighborCW(Point& point); +Triangle* NeighborCCW(Point& point); +bool GetConstrainedEdgeCCW(Point& p); +bool GetConstrainedEdgeCW(Point& p); +void SetConstrainedEdgeCCW(Point& p, bool ce); +void SetConstrainedEdgeCW(Point& p, bool ce); +bool GetDelunayEdgeCCW(Point& p); +bool GetDelunayEdgeCW(Point& p); +void SetDelunayEdgeCCW(Point& p, bool e); +void SetDelunayEdgeCW(Point& p, bool e); + +bool Contains(Point* p); +bool Contains(const Edge& e); +bool Contains(Point* p, Point* q); +void Legalize(Point& point); +void Legalize(Point& opoint, Point& npoint); +/** + * Clears all references to all other triangles and points + */ +void Clear(); +void ClearNeighbor(Triangle *triangle ); +void ClearNeighbors(); +void ClearDelunayEdges(); + +inline bool IsInterior(); +inline void IsInterior(bool b); + +Triangle& NeighborAcross(Point& opoint); + +void DebugPrint(); + +private: + +/// Triangle points +Point* points_[3]; +/// Neighbor list +Triangle* neighbors_[3]; + +/// Has this triangle been marked as an interior triangle? +bool interior_; +}; + +inline bool cmp(const Point* a, const Point* b) +{ + if (a->y < b->y) { + return true; + } else if (a->y == b->y) { + // Make sure q is point with greater x value + if (a->x < b->x) { + return true; + } + } + return false; +} + +/// Add two points_ component-wise. +inline Point operator +(const Point& a, const Point& b) +{ + return Point(a.x + b.x, a.y + b.y); +} + +/// Subtract two points_ component-wise. +inline Point operator -(const Point& a, const Point& b) +{ + return Point(a.x - b.x, a.y - b.y); +} + +/// Multiply point by scalar +inline Point operator *(double s, const Point& a) +{ + return Point(s * a.x, s * a.y); +} + +inline bool operator ==(const Point& a, const Point& b) +{ + return a.x == b.x && a.y == b.y; +} + +inline bool operator !=(const Point& a, const Point& b) +{ + return a.x != b.x && a.y != b.y; +} + +/// Peform the dot product on two vectors. +inline double Dot(const Point& a, const Point& b) +{ + return a.x * b.x + a.y * b.y; +} + +/// Perform the cross product on two vectors. In 2D this produces a scalar. +inline double Cross(const Point& a, const Point& b) +{ + return a.x * b.y - a.y * b.x; +} + +/// Perform the cross product on a point and a scalar. In 2D this produces +/// a point. +inline Point Cross(const Point& a, double s) +{ + return Point(s * a.y, -s * a.x); +} + +/// Perform the cross product on a scalar and a point. In 2D this produces +/// a point. +inline Point Cross(const double s, const Point& a) +{ + return Point(-s * a.y, s * a.x); +} + +inline Point* Triangle::GetPoint(const int& index) +{ + return points_[index]; +} + +inline Triangle* Triangle::GetNeighbor(const int& index) +{ + return neighbors_[index]; +} + +inline bool Triangle::Contains(Point* p) +{ + return p == points_[0] || p == points_[1] || p == points_[2]; +} + +inline bool Triangle::Contains(const Edge& e) +{ + return Contains(e.p) && Contains(e.q); +} + +inline bool Triangle::Contains(Point* p, Point* q) +{ + return Contains(p) && Contains(q); +} + +inline bool Triangle::IsInterior() +{ + return interior_; +} + +inline void Triangle::IsInterior(bool b) +{ + interior_ = b; +} + +} + +#endif + + diff --git a/contrib/poly2tri/poly2tri/common/utils.h b/contrib/poly2tri/poly2tri/common/utils.h new file mode 100644 index 000000000..88d0c9e74 --- /dev/null +++ b/contrib/poly2tri/poly2tri/common/utils.h @@ -0,0 +1,105 @@ +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UTILS_H +#define UTILS_H + +// Otherwise #defines like M_PI are undeclared under Visual Studio +#define _USE_MATH_DEFINES + +#include +#include + +namespace p2t { + +const double PI_3div4 = 3 * M_PI / 4; +const double EPSILON = 1e-12; + +enum Orientation { CW, CCW, COLLINEAR }; + +/** + * Forumla to calculate signed area
+ * Positive if CCW
+ * Negative if CW
+ * 0 if collinear
+ *
+ * A[P1,P2,P3]  =  (x1*y2 - y1*x2) + (x2*y3 - y2*x3) + (x3*y1 - y3*x1)
+ *              =  (x1-x3)*(y2-y3) - (y1-y3)*(x2-x3)
+ * 
+ */ +Orientation Orient2d(Point& pa, Point& pb, Point& pc) +{ + double detleft = (pa.x - pc.x) * (pb.y - pc.y); + double detright = (pa.y - pc.y) * (pb.x - pc.x); + double val = detleft - detright; + if (val > -EPSILON && val < EPSILON) { + return COLLINEAR; + } else if (val > 0) { + return CCW; + } + return CW; +} + +bool InScanArea(Point& pa, Point& pb, Point& pc, Point& pd) +{ + double pdx = pd.x; + double pdy = pd.y; + double adx = pa.x - pdx; + double ady = pa.y - pdy; + double bdx = pb.x - pdx; + double bdy = pb.y - pdy; + + double adxbdy = adx * bdy; + double bdxady = bdx * ady; + double oabd = adxbdy - bdxady; + + if (oabd <= EPSILON) { + return false; + } + + double cdx = pc.x - pdx; + double cdy = pc.y - pdy; + + double cdxady = cdx * ady; + double adxcdy = adx * cdy; + double ocad = cdxady - adxcdy; + + if (ocad <= EPSILON) { + return false; + } + + return true; +} + +} + +#endif + diff --git a/contrib/poly2tri/poly2tri/poly2tri.h b/contrib/poly2tri/poly2tri/poly2tri.h new file mode 100644 index 000000000..487755e2e --- /dev/null +++ b/contrib/poly2tri/poly2tri/poly2tri.h @@ -0,0 +1,39 @@ +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef POLY2TRI_H +#define POLY2TRI_H + +#include "common/shapes.h" +#include "sweep/cdt.h" + +#endif + diff --git a/contrib/poly2tri/poly2tri/sweep/advancing_front.cc b/contrib/poly2tri/poly2tri/sweep/advancing_front.cc new file mode 100644 index 000000000..019df4a6e --- /dev/null +++ b/contrib/poly2tri/poly2tri/sweep/advancing_front.cc @@ -0,0 +1,109 @@ +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "advancing_front.h" + +namespace p2t { + +AdvancingFront::AdvancingFront(Node& head, Node& tail) +{ + head_ = &head; + tail_ = &tail; + search_node_ = &head; +} + +Node* AdvancingFront::LocateNode(const double& x) +{ + Node* node = search_node_; + + if (x < node->value) { + while ((node = node->prev) != NULL) { + if (x >= node->value) { + search_node_ = node; + return node; + } + } + } else { + while ((node = node->next) != NULL) { + if (x < node->value) { + search_node_ = node->prev; + return node->prev; + } + } + } + return NULL; +} + +Node* AdvancingFront::FindSearchNode(const double& x) +{ + (void)x; // suppress compiler warnings "unused parameter 'x'" + // TODO: implement BST index + return search_node_; +} + +Node* AdvancingFront::LocatePoint(const Point* point) +{ + const double px = point->x; + Node* node = FindSearchNode(px); + const double nx = node->point->x; + + if (px == nx) { + if (point != node->point) { + // We might have two nodes with same x value for a short time + if (point == node->prev->point) { + node = node->prev; + } else if (point == node->next->point) { + node = node->next; + } else { + assert(0); + } + } + } else if (px < nx) { + while ((node = node->prev) != NULL) { + if (point == node->point) { + break; + } + } + } else { + while ((node = node->next) != NULL) { + if (point == node->point) + break; + } + } + if(node) search_node_ = node; + return node; +} + +AdvancingFront::~AdvancingFront() +{ +} + +} + diff --git a/contrib/poly2tri/poly2tri/sweep/advancing_front.h b/contrib/poly2tri/poly2tri/sweep/advancing_front.h new file mode 100644 index 000000000..bab73d449 --- /dev/null +++ b/contrib/poly2tri/poly2tri/sweep/advancing_front.h @@ -0,0 +1,118 @@ +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ADVANCED_FRONT_H +#define ADVANCED_FRONT_H + +#include "../common/shapes.h" + +namespace p2t { + +struct Node; + +// Advancing front node +struct Node { + Point* point; + Triangle* triangle; + + Node* next; + Node* prev; + + double value; + + Node(Point& p) : point(&p), triangle(NULL), next(NULL), prev(NULL), value(p.x) + { + } + + Node(Point& p, Triangle& t) : point(&p), triangle(&t), next(NULL), prev(NULL), value(p.x) + { + } + +}; + +// Advancing front +class AdvancingFront { +public: + +AdvancingFront(Node& head, Node& tail); +// Destructor +~AdvancingFront(); + +Node* head(); +void set_head(Node* node); +Node* tail(); +void set_tail(Node* node); +Node* search(); +void set_search(Node* node); + +/// Locate insertion point along advancing front +Node* LocateNode(const double& x); + +Node* LocatePoint(const Point* point); + +private: + +Node* head_, *tail_, *search_node_; + +Node* FindSearchNode(const double& x); +}; + +inline Node* AdvancingFront::head() +{ + return head_; +} +inline void AdvancingFront::set_head(Node* node) +{ + head_ = node; +} + +inline Node* AdvancingFront::tail() +{ + return tail_; +} +inline void AdvancingFront::set_tail(Node* node) +{ + tail_ = node; +} + +inline Node* AdvancingFront::search() +{ + return search_node_; +} + +inline void AdvancingFront::set_search(Node* node) +{ + search_node_ = node; +} + +} + +#endif diff --git a/contrib/poly2tri/poly2tri/sweep/cdt.cc b/contrib/poly2tri/poly2tri/sweep/cdt.cc new file mode 100644 index 000000000..f013e47e3 --- /dev/null +++ b/contrib/poly2tri/poly2tri/sweep/cdt.cc @@ -0,0 +1,72 @@ +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "cdt.h" + +namespace p2t { + +CDT::CDT(std::vector polyline) +{ + sweep_context_ = new SweepContext(polyline); + sweep_ = new Sweep; +} + +void CDT::AddHole(std::vector polyline) +{ + sweep_context_->AddHole(polyline); +} + +void CDT::AddPoint(Point* point) { + sweep_context_->AddPoint(point); +} + +void CDT::Triangulate() +{ + sweep_->Triangulate(*sweep_context_); +} + +std::vector CDT::GetTriangles() +{ + return sweep_context_->GetTriangles(); +} + +std::list CDT::GetMap() +{ + return sweep_context_->GetMap(); +} + +CDT::~CDT() +{ + delete sweep_context_; + delete sweep_; +} + +} + diff --git a/contrib/poly2tri/poly2tri/sweep/cdt.h b/contrib/poly2tri/poly2tri/sweep/cdt.h new file mode 100644 index 000000000..baf6ca7dd --- /dev/null +++ b/contrib/poly2tri/poly2tri/sweep/cdt.h @@ -0,0 +1,105 @@ +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CDT_H +#define CDT_H + +#include "advancing_front.h" +#include "sweep_context.h" +#include "sweep.h" + +/** + * + * @author Mason Green + * + */ + +namespace p2t { + +class CDT +{ +public: + + /** + * Constructor - add polyline with non repeating points + * + * @param polyline + */ + CDT(std::vector polyline); + + /** + * Destructor - clean up memory + */ + ~CDT(); + + /** + * Add a hole + * + * @param polyline + */ + void AddHole(std::vector polyline); + + /** + * Add a steiner point + * + * @param point + */ + void AddPoint(Point* point); + + /** + * Triangulate - do this AFTER you've added the polyline, holes, and Steiner points + */ + void Triangulate(); + + /** + * Get CDT triangles + */ + std::vector GetTriangles(); + + /** + * Get triangle map + */ + std::list GetMap(); + + private: + + /** + * Internals + */ + + SweepContext* sweep_context_; + Sweep* sweep_; + +}; + +} + +#endif diff --git a/contrib/poly2tri/poly2tri/sweep/sweep.cc b/contrib/poly2tri/poly2tri/sweep/sweep.cc new file mode 100644 index 000000000..573ca372d --- /dev/null +++ b/contrib/poly2tri/poly2tri/sweep/sweep.cc @@ -0,0 +1,764 @@ +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include "sweep.h" +#include "sweep_context.h" +#include "advancing_front.h" +#include "../common/utils.h" + +namespace p2t { + +// Triangulate simple polygon with holes +void Sweep::Triangulate(SweepContext& tcx) +{ + tcx.InitTriangulation(); + tcx.CreateAdvancingFront(nodes_); + // Sweep points; build mesh + SweepPoints(tcx); + // Clean up + FinalizationPolygon(tcx); +} + +void Sweep::SweepPoints(SweepContext& tcx) +{ + for (int i = 1; i < tcx.point_count(); i++) { + Point& point = *tcx.GetPoint(i); + Node* node = &PointEvent(tcx, point); + for (unsigned int i = 0; i < point.edge_list.size(); i++) { + EdgeEvent(tcx, point.edge_list[i], node); + } + } +} + +void Sweep::FinalizationPolygon(SweepContext& tcx) +{ + // Get an Internal triangle to start with + Triangle* t = tcx.front()->head()->next->triangle; + Point* p = tcx.front()->head()->next->point; + while (!t->GetConstrainedEdgeCW(*p)) { + t = t->NeighborCCW(*p); + } + + // Collect interior triangles constrained by edges + tcx.MeshClean(*t); +} + +Node& Sweep::PointEvent(SweepContext& tcx, Point& point) +{ + Node& node = tcx.LocateNode(point); + Node& new_node = NewFrontTriangle(tcx, point, node); + + // Only need to check +epsilon since point never have smaller + // x value than node due to how we fetch nodes from the front + if (point.x <= node.point->x + EPSILON) { + Fill(tcx, node); + } + + //tcx.AddNode(new_node); + + FillAdvancingFront(tcx, new_node); + return new_node; +} + +void Sweep::EdgeEvent(SweepContext& tcx, Edge* edge, Node* node) +{ + tcx.edge_event.constrained_edge = edge; + tcx.edge_event.right = (edge->p->x > edge->q->x); + + if (IsEdgeSideOfTriangle(*node->triangle, *edge->p, *edge->q)) { + return; + } + + // For now we will do all needed filling + // TODO: integrate with flip process might give some better performance + // but for now this avoid the issue with cases that needs both flips and fills + FillEdgeEvent(tcx, edge, node); + EdgeEvent(tcx, *edge->p, *edge->q, node->triangle, *edge->q); +} + +void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangle, Point& point) +{ + if (IsEdgeSideOfTriangle(*triangle, ep, eq)) { + return; + } + + Point* p1 = triangle->PointCCW(point); + Orientation o1 = Orient2d(eq, *p1, ep); + if (o1 == COLLINEAR) { + // ASSIMP_CHANGE (aramis_acg) + throw std::runtime_error("EdgeEvent - collinear points not supported"); + if( triangle->Contains(&eq, p1)) { + triangle->MarkConstrainedEdge(&eq, p1 ); + // We are modifying the constraint maybe it would be better to + // not change the given constraint and just keep a variable for the new constraint + tcx.edge_event.constrained_edge->q = p1; + triangle = &triangle->NeighborAcross(point); + EdgeEvent( tcx, ep, *p1, triangle, *p1 ); + } else { + // ASSIMP_CHANGE (aramis_acg) + std::runtime_error("EdgeEvent - collinear points not supported"); + } + return; + } + + Point* p2 = triangle->PointCW(point); + Orientation o2 = Orient2d(eq, *p2, ep); + if (o2 == COLLINEAR) { + // ASSIMP_CHANGE (aramis_acg) + throw std::runtime_error("EdgeEvent - collinear points not supported"); + + if( triangle->Contains(&eq, p2)) { + triangle->MarkConstrainedEdge(&eq, p2 ); + // We are modifying the constraint maybe it would be better to + // not change the given constraint and just keep a variable for the new constraint + tcx.edge_event.constrained_edge->q = p2; + triangle = &triangle->NeighborAcross(point); + EdgeEvent( tcx, ep, *p2, triangle, *p2 ); + } else { + // ASSIMP_CHANGE (aramis_acg) + throw std::runtime_error("EdgeEvent - collinear points not supported"); + } + return; + } + + if (o1 == o2) { + // Need to decide if we are rotating CW or CCW to get to a triangle + // that will cross edge + if (o1 == CW) { + triangle = triangle->NeighborCCW(point); + } else{ + triangle = triangle->NeighborCW(point); + } + EdgeEvent(tcx, ep, eq, triangle, point); + } else { + // This triangle crosses constraint so lets flippin start! + FlipEdgeEvent(tcx, ep, eq, triangle, point); + } +} + +bool Sweep::IsEdgeSideOfTriangle(Triangle& triangle, Point& ep, Point& eq) +{ + int index = triangle.EdgeIndex(&ep, &eq); + + if (index != -1) { + triangle.MarkConstrainedEdge(index); + Triangle* t = triangle.GetNeighbor(index); + if (t) { + t->MarkConstrainedEdge(&ep, &eq); + } + return true; + } + return false; +} + +Node& Sweep::NewFrontTriangle(SweepContext& tcx, Point& point, Node& node) +{ + Triangle* triangle = new Triangle(point, *node.point, *node.next->point); + + triangle->MarkNeighbor(*node.triangle); + tcx.AddToMap(triangle); + + Node* new_node = new Node(point); + nodes_.push_back(new_node); + + new_node->next = node.next; + new_node->prev = &node; + node.next->prev = new_node; + node.next = new_node; + + if (!Legalize(tcx, *triangle)) { + tcx.MapTriangleToNodes(*triangle); + } + + return *new_node; +} + +void Sweep::Fill(SweepContext& tcx, Node& node) +{ + Triangle* triangle = new Triangle(*node.prev->point, *node.point, *node.next->point); + + // TODO: should copy the constrained_edge value from neighbor triangles + // for now constrained_edge values are copied during the legalize + triangle->MarkNeighbor(*node.prev->triangle); + triangle->MarkNeighbor(*node.triangle); + + tcx.AddToMap(triangle); + + // Update the advancing front + node.prev->next = node.next; + node.next->prev = node.prev; + + // If it was legalized the triangle has already been mapped + if (!Legalize(tcx, *triangle)) { + tcx.MapTriangleToNodes(*triangle); + } + +} + +void Sweep::FillAdvancingFront(SweepContext& tcx, Node& n) +{ + + // Fill right holes + Node* node = n.next; + + while (node->next) { + double angle = HoleAngle(*node); + if (angle > M_PI_2 || angle < -M_PI_2) break; + Fill(tcx, *node); + node = node->next; + } + + // Fill left holes + node = n.prev; + + while (node->prev) { + double angle = HoleAngle(*node); + if (angle > M_PI_2 || angle < -M_PI_2) break; + Fill(tcx, *node); + node = node->prev; + } + + // Fill right basins + if (n.next && n.next->next) { + double angle = BasinAngle(n); + if (angle < PI_3div4) { + FillBasin(tcx, n); + } + } +} + +double Sweep::BasinAngle(Node& node) +{ + double ax = node.point->x - node.next->next->point->x; + double ay = node.point->y - node.next->next->point->y; + return atan2(ay, ax); +} + +double Sweep::HoleAngle(Node& node) +{ + /* Complex plane + * ab = cosA +i*sinA + * ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx) + * atan2(y,x) computes the principal value of the argument function + * applied to the complex number x+iy + * Where x = ax*bx + ay*by + * y = ax*by - ay*bx + */ + double ax = node.next->point->x - node.point->x; + double ay = node.next->point->y - node.point->y; + double bx = node.prev->point->x - node.point->x; + double by = node.prev->point->y - node.point->y; + return atan2(ax * by - ay * bx, ax * bx + ay * by); +} + +bool Sweep::Legalize(SweepContext& tcx, Triangle& t) +{ + // To legalize a triangle we start by finding if any of the three edges + // violate the Delaunay condition + for (int i = 0; i < 3; i++) { + if (t.delaunay_edge[i]) + continue; + + Triangle* ot = t.GetNeighbor(i); + + if (ot) { + Point* p = t.GetPoint(i); + Point* op = ot->OppositePoint(t, *p); + int oi = ot->Index(op); + + // If this is a Constrained Edge or a Delaunay Edge(only during recursive legalization) + // then we should not try to legalize + if (ot->constrained_edge[oi] || ot->delaunay_edge[oi]) { + t.constrained_edge[i] = ot->constrained_edge[oi]; + continue; + } + + bool inside = Incircle(*p, *t.PointCCW(*p), *t.PointCW(*p), *op); + + if (inside) { + // Lets mark this shared edge as Delaunay + t.delaunay_edge[i] = true; + ot->delaunay_edge[oi] = true; + + // Lets rotate shared edge one vertex CW to legalize it + RotateTrianglePair(t, *p, *ot, *op); + + // We now got one valid Delaunay Edge shared by two triangles + // This gives us 4 new edges to check for Delaunay + + // Make sure that triangle to node mapping is done only one time for a specific triangle + bool not_legalized = !Legalize(tcx, t); + if (not_legalized) { + tcx.MapTriangleToNodes(t); + } + + not_legalized = !Legalize(tcx, *ot); + if (not_legalized) + tcx.MapTriangleToNodes(*ot); + + // Reset the Delaunay edges, since they only are valid Delaunay edges + // until we add a new triangle or point. + // XXX: need to think about this. Can these edges be tried after we + // return to previous recursive level? + t.delaunay_edge[i] = false; + ot->delaunay_edge[oi] = false; + + // If triangle have been legalized no need to check the other edges since + // the recursive legalization will handles those so we can end here. + return true; + } + } + } + return false; +} + +bool Sweep::Incircle(Point& pa, Point& pb, Point& pc, Point& pd) +{ + double adx = pa.x - pd.x; + double ady = pa.y - pd.y; + double bdx = pb.x - pd.x; + double bdy = pb.y - pd.y; + + double adxbdy = adx * bdy; + double bdxady = bdx * ady; + double oabd = adxbdy - bdxady; + + if (oabd <= 0) + return false; + + double cdx = pc.x - pd.x; + double cdy = pc.y - pd.y; + + double cdxady = cdx * ady; + double adxcdy = adx * cdy; + double ocad = cdxady - adxcdy; + + if (ocad <= 0) + return false; + + double bdxcdy = bdx * cdy; + double cdxbdy = cdx * bdy; + + double alift = adx * adx + ady * ady; + double blift = bdx * bdx + bdy * bdy; + double clift = cdx * cdx + cdy * cdy; + + double det = alift * (bdxcdy - cdxbdy) + blift * ocad + clift * oabd; + + return det > 0; +} + +void Sweep::RotateTrianglePair(Triangle& t, Point& p, Triangle& ot, Point& op) +{ + Triangle* n1, *n2, *n3, *n4; + n1 = t.NeighborCCW(p); + n2 = t.NeighborCW(p); + n3 = ot.NeighborCCW(op); + n4 = ot.NeighborCW(op); + + bool ce1, ce2, ce3, ce4; + ce1 = t.GetConstrainedEdgeCCW(p); + ce2 = t.GetConstrainedEdgeCW(p); + ce3 = ot.GetConstrainedEdgeCCW(op); + ce4 = ot.GetConstrainedEdgeCW(op); + + bool de1, de2, de3, de4; + de1 = t.GetDelunayEdgeCCW(p); + de2 = t.GetDelunayEdgeCW(p); + de3 = ot.GetDelunayEdgeCCW(op); + de4 = ot.GetDelunayEdgeCW(op); + + t.Legalize(p, op); + ot.Legalize(op, p); + + // Remap delaunay_edge + ot.SetDelunayEdgeCCW(p, de1); + t.SetDelunayEdgeCW(p, de2); + t.SetDelunayEdgeCCW(op, de3); + ot.SetDelunayEdgeCW(op, de4); + + // Remap constrained_edge + ot.SetConstrainedEdgeCCW(p, ce1); + t.SetConstrainedEdgeCW(p, ce2); + t.SetConstrainedEdgeCCW(op, ce3); + ot.SetConstrainedEdgeCW(op, ce4); + + // Remap neighbors + // XXX: might optimize the markNeighbor by keeping track of + // what side should be assigned to what neighbor after the + // rotation. Now mark neighbor does lots of testing to find + // the right side. + t.ClearNeighbors(); + ot.ClearNeighbors(); + if (n1) ot.MarkNeighbor(*n1); + if (n2) t.MarkNeighbor(*n2); + if (n3) t.MarkNeighbor(*n3); + if (n4) ot.MarkNeighbor(*n4); + t.MarkNeighbor(ot); +} + +void Sweep::FillBasin(SweepContext& tcx, Node& node) +{ + if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) { + tcx.basin.left_node = node.next->next; + } else { + tcx.basin.left_node = node.next; + } + + // Find the bottom and right node + tcx.basin.bottom_node = tcx.basin.left_node; + while (tcx.basin.bottom_node->next + && tcx.basin.bottom_node->point->y >= tcx.basin.bottom_node->next->point->y) { + tcx.basin.bottom_node = tcx.basin.bottom_node->next; + } + if (tcx.basin.bottom_node == tcx.basin.left_node) { + // No valid basin + return; + } + + tcx.basin.right_node = tcx.basin.bottom_node; + while (tcx.basin.right_node->next + && tcx.basin.right_node->point->y < tcx.basin.right_node->next->point->y) { + tcx.basin.right_node = tcx.basin.right_node->next; + } + if (tcx.basin.right_node == tcx.basin.bottom_node) { + // No valid basins + return; + } + + tcx.basin.width = tcx.basin.right_node->point->x - tcx.basin.left_node->point->x; + tcx.basin.left_highest = tcx.basin.left_node->point->y > tcx.basin.right_node->point->y; + + FillBasinReq(tcx, tcx.basin.bottom_node); +} + +void Sweep::FillBasinReq(SweepContext& tcx, Node* node) +{ + // if shallow stop filling + if (IsShallow(tcx, *node)) { + return; + } + + Fill(tcx, *node); + + if (node->prev == tcx.basin.left_node && node->next == tcx.basin.right_node) { + return; + } else if (node->prev == tcx.basin.left_node) { + Orientation o = Orient2d(*node->point, *node->next->point, *node->next->next->point); + if (o == CW) { + return; + } + node = node->next; + } else if (node->next == tcx.basin.right_node) { + Orientation o = Orient2d(*node->point, *node->prev->point, *node->prev->prev->point); + if (o == CCW) { + return; + } + node = node->prev; + } else { + // Continue with the neighbor node with lowest Y value + if (node->prev->point->y < node->next->point->y) { + node = node->prev; + } else { + node = node->next; + } + } + + FillBasinReq(tcx, node); +} + +bool Sweep::IsShallow(SweepContext& tcx, Node& node) +{ + double height; + + if (tcx.basin.left_highest) { + height = tcx.basin.left_node->point->y - node.point->y; + } else { + height = tcx.basin.right_node->point->y - node.point->y; + } + + // if shallow stop filling + if (tcx.basin.width > height) { + return true; + } + return false; +} + +void Sweep::FillEdgeEvent(SweepContext& tcx, Edge* edge, Node* node) +{ + if (tcx.edge_event.right) { + FillRightAboveEdgeEvent(tcx, edge, node); + } else { + FillLeftAboveEdgeEvent(tcx, edge, node); + } +} + +void Sweep::FillRightAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node) +{ + while (node->next->point->x < edge->p->x) { + // Check if next node is below the edge + if (Orient2d(*edge->q, *node->next->point, *edge->p) == CCW) { + FillRightBelowEdgeEvent(tcx, edge, *node); + } else { + node = node->next; + } + } +} + +void Sweep::FillRightBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) +{ + if (node.point->x < edge->p->x) { + if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) { + // Concave + FillRightConcaveEdgeEvent(tcx, edge, node); + } else{ + // Convex + FillRightConvexEdgeEvent(tcx, edge, node); + // Retry this one + FillRightBelowEdgeEvent(tcx, edge, node); + } + } +} + +void Sweep::FillRightConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) +{ + Fill(tcx, *node.next); + if (node.next->point != edge->p) { + // Next above or below edge? + if (Orient2d(*edge->q, *node.next->point, *edge->p) == CCW) { + // Below + if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) { + // Next is concave + FillRightConcaveEdgeEvent(tcx, edge, node); + } else { + // Next is convex + } + } + } + +} + +void Sweep::FillRightConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) +{ + // Next concave or convex? + if (Orient2d(*node.next->point, *node.next->next->point, *node.next->next->next->point) == CCW) { + // Concave + FillRightConcaveEdgeEvent(tcx, edge, *node.next); + } else{ + // Convex + // Next above or below edge? + if (Orient2d(*edge->q, *node.next->next->point, *edge->p) == CCW) { + // Below + FillRightConvexEdgeEvent(tcx, edge, *node.next); + } else{ + // Above + } + } +} + +void Sweep::FillLeftAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node) +{ + while (node->prev->point->x > edge->p->x) { + // Check if next node is below the edge + if (Orient2d(*edge->q, *node->prev->point, *edge->p) == CW) { + FillLeftBelowEdgeEvent(tcx, edge, *node); + } else { + node = node->prev; + } + } +} + +void Sweep::FillLeftBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) +{ + if (node.point->x > edge->p->x) { + if (Orient2d(*node.point, *node.prev->point, *node.prev->prev->point) == CW) { + // Concave + FillLeftConcaveEdgeEvent(tcx, edge, node); + } else { + // Convex + FillLeftConvexEdgeEvent(tcx, edge, node); + // Retry this one + FillLeftBelowEdgeEvent(tcx, edge, node); + } + } +} + +void Sweep::FillLeftConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) +{ + // Next concave or convex? + if (Orient2d(*node.prev->point, *node.prev->prev->point, *node.prev->prev->prev->point) == CW) { + // Concave + FillLeftConcaveEdgeEvent(tcx, edge, *node.prev); + } else{ + // Convex + // Next above or below edge? + if (Orient2d(*edge->q, *node.prev->prev->point, *edge->p) == CW) { + // Below + FillLeftConvexEdgeEvent(tcx, edge, *node.prev); + } else{ + // Above + } + } +} + +void Sweep::FillLeftConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) +{ + Fill(tcx, *node.prev); + if (node.prev->point != edge->p) { + // Next above or below edge? + if (Orient2d(*edge->q, *node.prev->point, *edge->p) == CW) { + // Below + if (Orient2d(*node.point, *node.prev->point, *node.prev->prev->point) == CW) { + // Next is concave + FillLeftConcaveEdgeEvent(tcx, edge, node); + } else{ + // Next is convex + } + } + } + +} + +void Sweep::FlipEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* t, Point& p) +{ + Triangle& ot = t->NeighborAcross(p); + Point& op = *ot.OppositePoint(*t, p); + + if (&ot == NULL) { + // If we want to integrate the fillEdgeEvent do it here + // With current implementation we should never get here + //throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle"); + assert(0); + } + + if (InScanArea(p, *t->PointCCW(p), *t->PointCW(p), op)) { + // Lets rotate shared edge one vertex CW + RotateTrianglePair(*t, p, ot, op); + tcx.MapTriangleToNodes(*t); + tcx.MapTriangleToNodes(ot); + + if (p == eq && op == ep) { + if (eq == *tcx.edge_event.constrained_edge->q && ep == *tcx.edge_event.constrained_edge->p) { + t->MarkConstrainedEdge(&ep, &eq); + ot.MarkConstrainedEdge(&ep, &eq); + Legalize(tcx, *t); + Legalize(tcx, ot); + } else { + // XXX: I think one of the triangles should be legalized here? + } + } else { + Orientation o = Orient2d(eq, op, ep); + t = &NextFlipTriangle(tcx, (int)o, *t, ot, p, op); + FlipEdgeEvent(tcx, ep, eq, t, p); + } + } else { + Point& newP = NextFlipPoint(ep, eq, ot, op); + FlipScanEdgeEvent(tcx, ep, eq, *t, ot, newP); + EdgeEvent(tcx, ep, eq, t, p); + } +} + +Triangle& Sweep::NextFlipTriangle(SweepContext& tcx, int o, Triangle& t, Triangle& ot, Point& p, Point& op) +{ + if (o == CCW) { + // ot is not crossing edge after flip + int edge_index = ot.EdgeIndex(&p, &op); + ot.delaunay_edge[edge_index] = true; + Legalize(tcx, ot); + ot.ClearDelunayEdges(); + return t; + } + + // t is not crossing edge after flip + int edge_index = t.EdgeIndex(&p, &op); + + t.delaunay_edge[edge_index] = true; + Legalize(tcx, t); + t.ClearDelunayEdges(); + return ot; +} + +Point& Sweep::NextFlipPoint(Point& ep, Point& eq, Triangle& ot, Point& op) +{ + Orientation o2d = Orient2d(eq, op, ep); + if (o2d == CW) { + // Right + return *ot.PointCCW(op); + } else if (o2d == CCW) { + // Left + return *ot.PointCW(op); + } else{ + //throw new RuntimeException("[Unsupported] Opposing point on constrained edge"); + // ASSIMP_CHANGE (aramis_acg) + throw std::runtime_error("[Unsupported] Opposing point on constrained edge"); + } +} + +void Sweep::FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& flip_triangle, + Triangle& t, Point& p) +{ + Triangle& ot = t.NeighborAcross(p); + Point& op = *ot.OppositePoint(t, p); + + if (&t.NeighborAcross(p) == NULL) { + // If we want to integrate the fillEdgeEvent do it here + // With current implementation we should never get here + //throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle"); + assert(0); + } + + if (InScanArea(eq, *flip_triangle.PointCCW(eq), *flip_triangle.PointCW(eq), op)) { + // flip with new edge op->eq + FlipEdgeEvent(tcx, eq, op, &ot, op); + // TODO: Actually I just figured out that it should be possible to + // improve this by getting the next ot and op before the the above + // flip and continue the flipScanEdgeEvent here + // set new ot and op here and loop back to inScanArea test + // also need to set a new flip_triangle first + // Turns out at first glance that this is somewhat complicated + // so it will have to wait. + } else{ + Point& newP = NextFlipPoint(ep, eq, ot, op); + FlipScanEdgeEvent(tcx, ep, eq, flip_triangle, ot, newP); + } +} + +Sweep::~Sweep() { + + // Clean up memory + for(int i = 0; i < nodes_.size(); i++) { + delete nodes_[i]; + } + +} + +} + diff --git a/contrib/poly2tri/poly2tri/sweep/sweep.h b/contrib/poly2tri/poly2tri/sweep/sweep.h new file mode 100644 index 000000000..bd98adfc5 --- /dev/null +++ b/contrib/poly2tri/poly2tri/sweep/sweep.h @@ -0,0 +1,278 @@ +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/** + * Sweep-line, Constrained Delauney Triangulation (CDT) See: Domiter, V. and + * Zalik, B.(2008)'Sweep-line algorithm for constrained Delaunay triangulation', + * International Journal of Geographical Information Science + * + * "FlipScan" Constrained Edge Algorithm invented by Thomas Åhlén, thahlen@gmail.com + */ + +#ifndef SWEEP_H +#define SWEEP_H + +#include + +namespace p2t { + +class SweepContext; +struct Node; +struct Point; +struct Edge; +class Triangle; + +class Sweep +{ +public: + + /** + * Triangulate + * + * @param tcx + */ + void Triangulate(SweepContext& tcx); + + /** + * Destructor - clean up memory + */ + ~Sweep(); + +private: + + /** + * Start sweeping the Y-sorted point set from bottom to top + * + * @param tcx + */ + void SweepPoints(SweepContext& tcx); + + /** + * Find closes node to the left of the new point and + * create a new triangle. If needed new holes and basins + * will be filled to. + * + * @param tcx + * @param point + * @return + */ + Node& PointEvent(SweepContext& tcx, Point& point); + + /** + * + * + * @param tcx + * @param edge + * @param node + */ + void EdgeEvent(SweepContext& tcx, Edge* edge, Node* node); + + void EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangle, Point& point); + + /** + * Creates a new front triangle and legalize it + * + * @param tcx + * @param point + * @param node + * @return + */ + Node& NewFrontTriangle(SweepContext& tcx, Point& point, Node& node); + + /** + * Adds a triangle to the advancing front to fill a hole. + * @param tcx + * @param node - middle node, that is the bottom of the hole + */ + void Fill(SweepContext& tcx, Node& node); + + /** + * Returns true if triangle was legalized + */ + bool Legalize(SweepContext& tcx, Triangle& t); + + /** + * Requirement:
+ * 1. a,b and c form a triangle.
+ * 2. a and d is know to be on opposite side of bc
+ *
+   *                a
+   *                +
+   *               / \
+   *              /   \
+   *            b/     \c
+   *            +-------+
+   *           /    d    \
+   *          /           \
+   * 
+ * Fact: d has to be in area B to have a chance to be inside the circle formed by + * a,b and c
+ * d is outside B if orient2d(a,b,d) or orient2d(c,a,d) is CW
+ * This preknowledge gives us a way to optimize the incircle test + * @param a - triangle point, opposite d + * @param b - triangle point + * @param c - triangle point + * @param d - point opposite a + * @return true if d is inside circle, false if on circle edge + */ + bool Incircle(Point& pa, Point& pb, Point& pc, Point& pd); + + /** + * Rotates a triangle pair one vertex CW + *
+   *       n2                    n2
+   *  P +-----+             P +-----+
+   *    | t  /|               |\  t |
+   *    |   / |               | \   |
+   *  n1|  /  |n3           n1|  \  |n3
+   *    | /   |    after CW   |   \ |
+   *    |/ oT |               | oT \|
+   *    +-----+ oP            +-----+
+   *       n4                    n4
+   * 
+ */ + void RotateTrianglePair(Triangle& t, Point& p, Triangle& ot, Point& op); + + /** + * Fills holes in the Advancing Front + * + * + * @param tcx + * @param n + */ + void FillAdvancingFront(SweepContext& tcx, Node& n); + + /** + * + * @param node - middle node + * @return the angle between 3 front nodes + */ + double HoleAngle(Node& node); + + /** + * The basin angle is decided against the horizontal line [1,0] + */ + double BasinAngle(Node& node); + + /** + * Fills a basin that has formed on the Advancing Front to the right + * of given node.
+ * First we decide a left,bottom and right node that forms the + * boundaries of the basin. Then we do a reqursive fill. + * + * @param tcx + * @param node - starting node, this or next node will be left node + */ + void FillBasin(SweepContext& tcx, Node& node); + + /** + * Recursive algorithm to fill a Basin with triangles + * + * @param tcx + * @param node - bottom_node + * @param cnt - counter used to alternate on even and odd numbers + */ + void FillBasinReq(SweepContext& tcx, Node* node); + + bool IsShallow(SweepContext& tcx, Node& node); + + bool IsEdgeSideOfTriangle(Triangle& triangle, Point& ep, Point& eq); + + void FillEdgeEvent(SweepContext& tcx, Edge* edge, Node* node); + + void FillRightAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node); + + void FillRightBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node); + + void FillRightConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node); + + void FillRightConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node); + + void FillLeftAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node); + + void FillLeftBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node); + + void FillLeftConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node); + + void FillLeftConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node); + + void FlipEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* t, Point& p); + + /** + * After a flip we have two triangles and know that only one will still be + * intersecting the edge. So decide which to contiune with and legalize the other + * + * @param tcx + * @param o - should be the result of an orient2d( eq, op, ep ) + * @param t - triangle 1 + * @param ot - triangle 2 + * @param p - a point shared by both triangles + * @param op - another point shared by both triangles + * @return returns the triangle still intersecting the edge + */ + Triangle& NextFlipTriangle(SweepContext& tcx, int o, Triangle& t, Triangle& ot, Point& p, Point& op); + + /** + * When we need to traverse from one triangle to the next we need + * the point in current triangle that is the opposite point to the next + * triangle. + * + * @param ep + * @param eq + * @param ot + * @param op + * @return + */ + Point& NextFlipPoint(Point& ep, Point& eq, Triangle& ot, Point& op); + + /** + * Scan part of the FlipScan algorithm
+ * When a triangle pair isn't flippable we will scan for the next + * point that is inside the flip triangle scan area. When found + * we generate a new flipEdgeEvent + * + * @param tcx + * @param ep - last point on the edge we are traversing + * @param eq - first point on the edge we are traversing + * @param flipTriangle - the current triangle sharing the point eq with edge + * @param t + * @param p + */ + void FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& flip_triangle, Triangle& t, Point& p); + + void FinalizationPolygon(SweepContext& tcx); + + std::vector nodes_; + +}; + +} + +#endif diff --git a/contrib/poly2tri/poly2tri/sweep/sweep_context.cc b/contrib/poly2tri/poly2tri/sweep/sweep_context.cc new file mode 100644 index 000000000..c9dd5a8c4 --- /dev/null +++ b/contrib/poly2tri/poly2tri/sweep/sweep_context.cc @@ -0,0 +1,202 @@ +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "sweep_context.h" +#include +#include "advancing_front.h" + +namespace p2t { + +SweepContext::SweepContext(std::vector polyline) +{ + basin = Basin(); + edge_event = EdgeEvent(); + + points_ = polyline; + + InitEdges(points_); +} + +void SweepContext::AddHole(std::vector polyline) +{ + InitEdges(polyline); + for(unsigned int i = 0; i < polyline.size(); i++) { + points_.push_back(polyline[i]); + } +} + +void SweepContext::AddPoint(Point* point) { + points_.push_back(point); +} + +std::vector SweepContext::GetTriangles() +{ + return triangles_; +} + +std::list SweepContext::GetMap() +{ + return map_; +} + +void SweepContext::InitTriangulation() +{ + double xmax(points_[0]->x), xmin(points_[0]->x); + double ymax(points_[0]->y), ymin(points_[0]->y); + + // Calculate bounds. + for (unsigned int i = 0; i < points_.size(); i++) { + Point& p = *points_[i]; + if (p.x > xmax) + xmax = p.x; + if (p.x < xmin) + xmin = p.x; + if (p.y > ymax) + ymax = p.y; + if (p.y < ymin) + ymin = p.y; + } + + double dx = kAlpha * (xmax - xmin); + double dy = kAlpha * (ymax - ymin); + head_ = new Point(xmax + dx, ymin - dy); + tail_ = new Point(xmin - dx, ymin - dy); + + // Sort points along y-axis + std::sort(points_.begin(), points_.end(), cmp); + +} + +void SweepContext::InitEdges(std::vector polyline) +{ + int num_points = polyline.size(); + for (int i = 0; i < num_points; i++) { + int j = i < num_points - 1 ? i + 1 : 0; + edge_list.push_back(new Edge(*polyline[i], *polyline[j])); + } +} + +Point* SweepContext::GetPoint(const int& index) +{ + return points_[index]; +} + +void SweepContext::AddToMap(Triangle* triangle) +{ + map_.push_back(triangle); +} + +Node& SweepContext::LocateNode(Point& point) +{ + // TODO implement search tree + return *front_->LocateNode(point.x); +} + +void SweepContext::CreateAdvancingFront(std::vector nodes) +{ + + (void) nodes; + // Initial triangle + Triangle* triangle = new Triangle(*points_[0], *tail_, *head_); + + map_.push_back(triangle); + + af_head_ = new Node(*triangle->GetPoint(1), *triangle); + af_middle_ = new Node(*triangle->GetPoint(0), *triangle); + af_tail_ = new Node(*triangle->GetPoint(2)); + front_ = new AdvancingFront(*af_head_, *af_tail_); + + // TODO: More intuitive if head is middles next and not previous? + // so swap head and tail + af_head_->next = af_middle_; + af_middle_->next = af_tail_; + af_middle_->prev = af_head_; + af_tail_->prev = af_middle_; +} + +void SweepContext::RemoveNode(Node* node) +{ + delete node; +} + +void SweepContext::MapTriangleToNodes(Triangle& t) +{ + for (int i = 0; i < 3; i++) { + if (!t.GetNeighbor(i)) { + Node* n = front_->LocatePoint(t.PointCW(*t.GetPoint(i))); + if (n) + n->triangle = &t; + } + } +} + +void SweepContext::RemoveFromMap(Triangle* triangle) +{ + map_.remove(triangle); +} + +void SweepContext::MeshClean(Triangle& triangle) +{ + if (&triangle != NULL && !triangle.IsInterior()) { + triangle.IsInterior(true); + triangles_.push_back(&triangle); + for (int i = 0; i < 3; i++) { + if (!triangle.constrained_edge[i]) + MeshClean(*triangle.GetNeighbor(i)); + } + } +} + +SweepContext::~SweepContext() +{ + + // Clean up memory + + delete head_; + delete tail_; + delete front_; + delete af_head_; + delete af_middle_; + delete af_tail_; + + typedef std::list type_list; + + for(type_list::iterator iter = map_.begin(); iter != map_.end(); ++iter) { + Triangle* ptr = *iter; + delete ptr; + } + + for(unsigned int i = 0; i < edge_list.size(); i++) { + delete edge_list[i]; + } + +} + +} diff --git a/contrib/poly2tri/poly2tri/sweep/sweep_context.h b/contrib/poly2tri/poly2tri/sweep/sweep_context.h new file mode 100644 index 000000000..266408dc2 --- /dev/null +++ b/contrib/poly2tri/poly2tri/sweep/sweep_context.h @@ -0,0 +1,186 @@ +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SWEEP_CONTEXT_H +#define SWEEP_CONTEXT_H + +#include +#include +#include + +namespace p2t { + +// Inital triangle factor, seed triangle will extend 30% of +// PointSet width to both left and right. +const double kAlpha = 0.3; + +struct Point; +class Triangle; +struct Node; +struct Edge; +class AdvancingFront; + +class SweepContext { +public: + +/// Constructor +SweepContext(std::vector polyline); +/// Destructor +~SweepContext(); + +void set_head(Point* p1); + +Point* head(); + +void set_tail(Point* p1); + +Point* tail(); + +int point_count(); + +Node& LocateNode(Point& point); + +void RemoveNode(Node* node); + +void CreateAdvancingFront(std::vector nodes); + +/// Try to map a node to all sides of this triangle that don't have a neighbor +void MapTriangleToNodes(Triangle& t); + +void AddToMap(Triangle* triangle); + +Point* GetPoint(const int& index); + +Point* GetPoints(); + +void RemoveFromMap(Triangle* triangle); + +void AddHole(std::vector polyline); + +void AddPoint(Point* point); + +AdvancingFront* front(); + +void MeshClean(Triangle& triangle); + +std::vector GetTriangles(); +std::list GetMap(); + +std::vector edge_list; + +struct Basin { + Node* left_node; + Node* bottom_node; + Node* right_node; + double width; + bool left_highest; + + Basin() : left_node(NULL), bottom_node(NULL), right_node(NULL), width(0.0), left_highest(false) + { + } + + void Clear() + { + left_node = NULL; + bottom_node = NULL; + right_node = NULL; + width = 0.0; + left_highest = false; + } +}; + +struct EdgeEvent { + Edge* constrained_edge; + bool right; + + EdgeEvent() : constrained_edge(NULL), right(false) + { + } +}; + +Basin basin; +EdgeEvent edge_event; + +private: + +friend class Sweep; + +std::vector triangles_; +std::list map_; +std::vector points_; + +// Advancing front +AdvancingFront* front_; +// head point used with advancing front +Point* head_; +// tail point used with advancing front +Point* tail_; + +Node *af_head_, *af_middle_, *af_tail_; + +void InitTriangulation(); +void InitEdges(std::vector polyline); + +}; + +inline AdvancingFront* SweepContext::front() +{ + return front_; +} + +inline int SweepContext::point_count() +{ + return points_.size(); +} + +inline void SweepContext::set_head(Point* p1) +{ + head_ = p1; +} + +inline Point* SweepContext::head() +{ + return head_; +} + +inline void SweepContext::set_tail(Point* p1) +{ + tail_ = p1; +} + +inline Point* SweepContext::tail() +{ + return tail_; +} + +} + +#endif diff --git a/contrib/poly2tri_patch.txt b/contrib/poly2tri_patch.txt new file mode 100644 index 000000000..f8d38b909 --- /dev/null +++ b/contrib/poly2tri_patch.txt @@ -0,0 +1,67 @@ +diff -r 5de9623d6a50 poly2tri/common/shapes.h +--- a/poly2tri/common/shapes.h Mon Aug 08 22:26:41 2011 -0400 ++++ b/poly2tri/common/shapes.h Tue Jan 17 02:14:32 2012 +0100 +@@ -136,7 +136,9 @@ + p = &p2; + } else if (p1.x == p2.x) { + // Repeat points +- assert(false); ++ // ASSIMP_CHANGE (aramis_acg) ++ throw std::runtime_error("repeat points"); ++ //assert(false); + } + } + +diff -r 5de9623d6a50 poly2tri/sweep/sweep.cc +--- a/poly2tri/sweep/sweep.cc Mon Aug 08 22:26:41 2011 -0400 ++++ b/poly2tri/sweep/sweep.cc Tue Jan 17 02:14:32 2012 +0100 +@@ -113,6 +113,8 @@ + Point* p1 = triangle->PointCCW(point); + Orientation o1 = Orient2d(eq, *p1, ep); + if (o1 == COLLINEAR) { ++ // ASSIMP_CHANGE (aramis_acg) ++ throw std::runtime_error("EdgeEvent - collinear points not supported"); + if( triangle->Contains(&eq, p1)) { + triangle->MarkConstrainedEdge(&eq, p1 ); + // We are modifying the constraint maybe it would be better to +@@ -121,8 +123,8 @@ + triangle = &triangle->NeighborAcross(point); + EdgeEvent( tcx, ep, *p1, triangle, *p1 ); + } else { ++ // ASSIMP_CHANGE (aramis_acg) + std::runtime_error("EdgeEvent - collinear points not supported"); +- assert(0); + } + return; + } +@@ -130,6 +132,9 @@ + Point* p2 = triangle->PointCW(point); + Orientation o2 = Orient2d(eq, *p2, ep); + if (o2 == COLLINEAR) { ++ // ASSIMP_CHANGE (aramis_acg) ++ throw std::runtime_error("EdgeEvent - collinear points not supported"); ++ + if( triangle->Contains(&eq, p2)) { + triangle->MarkConstrainedEdge(&eq, p2 ); + // We are modifying the constraint maybe it would be better to +@@ -138,8 +143,8 @@ + triangle = &triangle->NeighborAcross(point); + EdgeEvent( tcx, ep, *p2, triangle, *p2 ); + } else { +- std::runtime_error("EdgeEvent - collinear points not supported"); +- assert(0); ++ // ASSIMP_CHANGE (aramis_acg) ++ throw std::runtime_error("EdgeEvent - collinear points not supported"); + } + return; + } +@@ -712,7 +717,8 @@ + return *ot.PointCW(op); + } else{ + //throw new RuntimeException("[Unsupported] Opposing point on constrained edge"); +- assert(0); ++ // ASSIMP_CHANGE (aramis_acg) ++ throw std::runtime_error("[Unsupported] Opposing point on constrained edge"); + } + } +