Repository: greenm01/poly2tri Branch: master Commit: a922022da4c0 Files: 19 Total size: 104.4 KB Directory structure: gitextract_iis_kvpo/ ├── AUTHORS ├── LICENSE ├── README ├── poly2tri/ │ ├── common/ │ │ ├── shapes.cc │ │ ├── shapes.h │ │ └── utils.h │ ├── poly2tri.h │ └── sweep/ │ ├── advancing_front.cc │ ├── advancing_front.h │ ├── cdt.cc │ ├── cdt.h │ ├── sweep.cc │ ├── sweep.h │ ├── sweep_context.cc │ └── sweep_context.h ├── testbed/ │ ├── data/ │ │ └── polygon.dae │ └── main.cc ├── waf └── wscript ================================================ FILE CONTENTS ================================================ ================================================ FILE: AUTHORS ================================================ Primary Contributors: Mason Green (C++, Python) Thomas hln (Java) Other Contributors: ================================================ FILE: LICENSE ================================================ 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. ================================================ FILE: README ================================================ ##################################################################################### ### ### Since there are no Input validation of the data given for triangulation you need ### to think about this. Poly2Tri does not support repeat points within epsilon. ### ### * If you have a cyclic function that generates random points make sure you don't ### add the same coordinate twice. ### ### * If you are given input and aren't sure same point exist twice you need to ### check for this yourself. ### ### * Only simple polygons are supported. You may add holes or interior Steiner points ### ### * Interior holes must not touch other holes, nor touch the polyline boundary ### ### * Use the library in this order: ### ### 1) Initialize CDT with a simple polyline (this defines the constrained edges) ### 2) Add holes if necessary (also simple polylines) ### 3) Add Steiner points ### 4) Triangulate ### ### Make sure you understand the preceding notice before posting an issue. If you have ### an issue not covered by the above, include your data-set with the problem. ### ### The only easy day was yesterday; have a nice day. ### ###################################################################################### =========================== TESTBED 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 ================================================ FILE: poly2tri/common/shapes.cc ================================================ /* * 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; } } ================================================ FILE: poly2tri/common/shapes.h ================================================ /* * 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 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 ================================================ FILE: poly2tri/common/utils.h ================================================ /* * 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 PI_div2 = 1.57079632679489661923; 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; } */ bool InScanArea(Point& pa, Point& pb, Point& pc, Point& pd) { double oadb = (pa.x - pb.x)*(pd.y - pb.y) - (pd.x - pb.x)*(pa.y - pb.y); if (oadb >= -EPSILON) { return false; } double oadc = (pa.x - pc.x)*(pd.y - pc.y) - (pd.x - pc.x)*(pa.y - pc.y); if (oadc <= EPSILON) { return false; } return true; } } #endif ================================================ FILE: poly2tri/poly2tri.h ================================================ /* * 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 ================================================ FILE: poly2tri/sweep/advancing_front.cc ================================================ /* * 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() { } } ================================================ FILE: poly2tri/sweep/advancing_front.h ================================================ /* * 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 ================================================ FILE: poly2tri/sweep/cdt.cc ================================================ /* * 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_; } } ================================================ FILE: poly2tri/sweep/cdt.h ================================================ /* * 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 ================================================ FILE: poly2tri/sweep/sweep.cc ================================================ /* * 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) { 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 { std::runtime_error("EdgeEvent - collinear points not supported"); assert(0); } return; } Point* p2 = triangle->PointCW(point); Orientation o2 = Orient2d(eq, *p2, ep); if (o2 == COLLINEAR) { 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 { std::runtime_error("EdgeEvent - collinear points not supported"); assert(0); } 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) { // if HoleAngle exceeds 90 degrees then break. if (LargeHole_DontFill(node)) break; Fill(tcx, *node); node = node->next; } // Fill left holes node = n.prev; while (node->prev) { // if HoleAngle exceeds 90 degrees then break. if (LargeHole_DontFill(node)) 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); } } } // True if HoleAngle exceeds 90 degrees. // LargeHole_DontFill checks if the advancing front has a large hole. // A "Large hole" is a triangle formed by a sequence of points in the advancing // front where three neighbor points form a triangle. // And angle between left-top, bottom, and right-top points is more than 90 degrees. // The first part of the algorithm reviews only three neighbor points, e.g. named A, B, C. // Additional part of this logic reviews a sequence of 5 points - // additionally reviews one point before and one after the sequence of three (A, B, C), // e.g. named X and Y. // In this case, angles are XBC and ABY and this if angles are negative or more // than 90 degrees LargeHole_DontFill returns true. // But there is a configuration when ABC has a negative angle but XBC or ABY is less // than 90 degrees and positive. // Then function LargeHole_DontFill return false and initiates filling. // This filling creates a triangle ABC and adds it to the advancing front. // But in the case when angle ABC is negative this triangle goes inside the advancing front // and can intersect previously created triangles. // This triangle leads to making wrong advancing front and problems in triangulation in the future. // Looks like such a triangle should not be created. // The simplest way to check and fix it is to check an angle ABC. // If it is negative LargeHole_DontFill should return true and // not initiate creating the ABC triangle in the advancing front. // X______A Y // \ / // \ / // \ B / // | / // | / // |/ // C bool Sweep::LargeHole_DontFill(Node* node) { Node* nextNode = node->next; Node* prevNode = node->prev; if (!AngleExceeds90Degrees(node->point, nextNode->point, prevNode->point)) return false; if (AngleIsNegative(node->point, nextNode->point, prevNode->point)) return true; // Check additional points on front. Node* next2Node = nextNode->next; // "..Plus.." because only want angles on same side as point being added. if ((next2Node != NULL) && !AngleExceedsPlus90DegreesOrIsNegative(node->point, next2Node->point, prevNode->point)) return false; Node* prev2Node = prevNode->prev; // "..Plus.." because only want angles on same side as point being added. if ((prev2Node != NULL) && !AngleExceedsPlus90DegreesOrIsNegative(node->point, nextNode->point, prev2Node->point)) return false; return true; } bool Sweep::AngleIsNegative(Point* origin, Point* pa, Point* pb) { const double angle = Angle(*origin, *pa, *pb); return angle < 0; } bool Sweep::AngleExceeds90Degrees(Point* origin, Point* pa, Point* pb) { double angle = Angle(*origin, *pa, *pb); bool exceeds90Degrees = ((angle > PI_div2) || (angle < -PI_div2)); return exceeds90Degrees; } bool Sweep::AngleExceedsPlus90DegreesOrIsNegative(Point* origin, Point* pa, Point* pb) { double angle = Angle(*origin, *pa, *pb); bool exceedsPlus90DegreesOrIsNegative = (angle > PI_div2) || (angle < 0); return exceedsPlus90DegreesOrIsNegative; } double Sweep::Angle(Point& origin, Point& pa, Point& pb) { /* 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 px = origin.x; double py = origin.y; double ax = pa.x- px; double ay = pa.y - py; double bx = pb.x - px; double by = pb.y - py; double x = ax * by - ay * bx; double y = ax * bx + ay * by; double angle = atan2(x, y); return angle; } 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"); assert(0); } } 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]; } } } ================================================ FILE: poly2tri/sweep/sweep.h ================================================ /* * 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 hln, 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); // Decision-making about when to Fill hole. // Contributed by ToolmakerSteve2 bool LargeHole_DontFill(Node* node); bool AngleIsNegative(Point* origin, Point* pa, Point* pb); bool AngleExceeds90Degrees(Point* origin, Point* pa, Point* pb); bool AngleExceedsPlus90DegreesOrIsNegative(Point* origin, Point* pa, Point* pb); double Angle(Point& origin, Point& pa, Point& pb); /** * * @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 ================================================ FILE: poly2tri/sweep/sweep_context.cc ================================================ /* * 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) : front_(0), head_(0), tail_(0), af_head_(0), af_middle_(0), af_tail_(0) { 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) { std::vector triangles; triangles.push_back(&triangle); while(!triangles.empty()){ Triangle *t = triangles.back(); triangles.pop_back(); if (t != NULL && !t->IsInterior()) { t->IsInterior(true); triangles_.push_back(t); for (int i = 0; i < 3; i++) { if (!t->constrained_edge[i]) triangles.push_back(t->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]; } } } ================================================ FILE: poly2tri/sweep/sweep_context.h ================================================ /* * 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 ================================================ FILE: testbed/data/polygon.dae ================================================ Google SketchUp 7.1.4871 2009-12-21T01:51:00Z 2009-12-21T01:51:00Z Z_UP -112.9393 -5.134218 -5.952325e-030 -115.783 -26.39329 -4.065249e-030 -116.1693 -15.52189 -5.174618e-030 -111.8239 -36.52552 -2.74901e-030 -106.4564 3.601217 -6.310887e-030 -104.7373 -44.7788 -1.373963e-030 -66.76277 70.05491 -5.637993e-014 -68.21937 53.83701 -4.127297e-014 -68.55863 62.04185 -5.054886e-014 -65.7681 45.99956 -2.91844e-014 -62.95416 77.33014 -5.83688e-014 -61.37187 39.0636 -1.510696e-014 -57.39237 83.37173 -5.637993e-014 -55.33028 33.5018 0 -50.4564 87.76796 -5.054886e-014 -54.18848 31.40854 0 -52.54381 29.68213 0 -50.50835 28.44022 0 -48.22082 27.76744 0 -42.61895 90.21923 -4.127297e-014 -45.8371 27.70964 0 -43.51965 28.27076 0 -41.4264 29.41256 0 -34.41412 90.55849 -2.91844e-014 -38.42946 47.11159 0 -39.69999 31.05723 0 -38.45807 33.09269 0 -36.86605 53.9672 0 -33.69118 60.24126 0 -26.40105 88.76263 -1.510696e-014 -29.09332 65.56134 0 -23.3454 69.61164 0 -19.12582 84.95402 1.877284e-029 -16.7886 72.15173 0 -13.74167 81.56284 1.269697e-029 -9.81216 73.03084 0 -8.03964 78.73864 7.636926e-030 -2.830188 72.19677 0 -2.079244 76.51087 3.64551e-030 3.742859 69.69904 0 4.077324 74.9028 7.643719e-031 9.516804 65.68592 0 10.36582 73.93119 -9.764253e-031 14.1489 60.39562 0 16.72063 73.60619 -1.558716e-030 17.36419 54.14218 0 23.07544 73.93119 -9.764253e-031 19.14039 41.22425 0 92.15467 80.62257 -5.238305e-014 88.83104 50.24595 2.085226e-030 88.0531 85.73196 -5.423092e-014 92.70476 55.53018 -1.403599e-014 94.79408 74.62572 -4.696535e-014 95.07883 61.63694 -2.711546e-014 95.79146 68.15007 -3.834705e-014 -138.3173 50.47565 -5.441105e-014 -137.432 24.73624 -3.697808e-014 -140.3168 37.52194 -4.746962e-014 -131.7115 61.79639 -5.68373e-014 -130.0639 13.89615 -2.439509e-014 -121.4177 69.91023 -5.441105e-014 -119.2369 6.508783 -1.147007e-014 -108.8671 73.6891 -4.746962e-014 -95.8047 72.60761 -3.697808e-014 -95.3204 -50.22474 -9.478654e-032 -84.0465 66.81613 -2.439509e-014 -84.63247 -52.25073 9.446271e-031 -73.87579 -50.62886 1.627355e-030 -75.22729 57.11985 -1.147007e-014 -70.57319 44.86685 0 -64.26037 -45.54158 1.876598e-030 -69.61521 31.86262 0 -64.96236 19.68153 0 -57.00677 9.350201 0 -56.86785 -37.56115 1.664318e-030 -51.7342 -16.73615 -6.04464e-035 -46.41893 1.739326 0 -52.52979 -27.58529 1.014396e-030 -43.24859 -39.15101 -3.163366e-031 -34.09116 -2.50966 0 -28.50562 -58.04747 -5.308165e-031 -21.06243 -3.038659 0 -8.827904 -71.73027 -6.242584e-031 -8.430764 0.1969107 0 14.01921 -78.97189 -5.882796e-031 2.739249 6.924362 0 11.50623 16.57672 0 17.1313 28.34049 0 37.98603 -79.12265 -4.261077e-031 29.36394 74.9028 7.643719e-031 35.52051 76.51087 3.64551e-030 41.48091 78.73864 7.636926e-030 60.92244 -72.16905 -1.522917e-031 47.18293 81.56284 1.269697e-029 52.56709 84.95402 1.877284e-029 57.67648 89.0556 -1.403599e-014 63.67333 91.69501 -2.711546e-014 80.77074 -58.7349 2.086037e-031 70.14898 92.69238 -3.834705e-014 76.66211 91.97975 -4.696535e-014 82.76887 89.60568 -5.238305e-014 95.75028 -40.02542 6.242015e-031 100.8951 29.53631 1.822036e-030 104.5172 -17.71909 1.057217e-030 106.285 6.182927 1.468804e-030 -38.28861 40.08139 0 -37.78529 35.38022 0 -37.72749 37.76394 0 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1 5.652422e-017 -2.025185e-016 -1

0 1 2 1 0 3 3 0 4 3 4 5 6 7 8 7 6 9 9 6 10 9 10 11 11 10 12 11 12 13 13 12 14 13 14 15 15 14 16 16 14 17 17 14 18 18 14 19 18 19 20 20 19 21 21 19 22 22 19 23 22 23 24 22 24 25 25 24 26 24 23 27 27 23 28 28 23 29 28 29 30 30 29 31 31 29 32 31 32 33 33 32 34 33 34 35 35 34 36 35 36 37 37 36 38 37 38 39 39 38 40 39 40 41 41 40 42 41 42 43 43 42 44 43 44 45 45 44 46 45 46 47 48 49 50 49 48 51 51 48 52 51 52 53 53 52 54 55 56 57 56 55 58 56 58 59 59 58 60 59 60 61 61 60 62 61 62 4 4 62 63 4 63 5 5 63 64 64 63 65 64 65 66 66 65 67 67 65 68 67 68 69 67 69 70 70 69 71 70 71 72 70 72 73 70 73 74 74 73 75 75 73 76 74 75 77 75 76 78 78 76 79 78 79 80 80 79 81 80 81 82 82 81 83 82 83 84 84 83 85 84 85 86 84 86 87 84 87 88 88 87 47 88 47 46 88 46 89 88 89 90 88 90 91 88 91 92 92 91 93 92 93 94 92 94 95 92 95 96 92 96 97 97 96 98 97 98 99 97 99 100 97 100 101 101 100 49 49 100 50 101 49 102 101 102 103 103 102 104 26 105 106 105 26 24 106 105 107

1 1 1 1
================================================ FILE: testbed/main.cc ================================================ /* * 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 #include #include #include #include #include #include #include using namespace std; #include "../poly2tri/poly2tri.h" using namespace p2t; void Init(); void ShutDown(int return_code); void MainLoop(const double zoom); void Draw(const double zoom); void DrawMap(const double zoom); void ConstrainedColor(bool constrain); double StringToDouble(const std::string& s); double Random(double (*fun)(double), double xmin, double xmax); double Fun(double x); /// Dude hole examples vector CreateHeadHole(); vector CreateChestHole(); /// Stalactite hole example vector CreateStalactiteHole(); float rotate_y = 0, rotate_z = 0; const float rotations_per_tick = .2; /// Screen center x double cx = 0.0; /// Screen center y double cy = 0.0; /// Constrained triangles vector triangles; /// Triangle map list map; /// Polylines vector< vector > polylines; /// Draw the entire triangle map? bool draw_map = false; /// Create a random distribution of points? bool random_distribution = false; template void FreeClear( C & cntr ) { for ( typename C::iterator it = cntr.begin(); it != cntr.end(); ++it ) { delete * it; } cntr.clear(); } int main(int argc, char* argv[]) { int num_points = 0; double max, min; double zoom; if (argc != 5) { cout << "-== USAGE ==-" << endl; cout << "Load Data File: p2t filename center_x center_y zoom" << endl; cout << "Example: ./build/p2t testbed/data/dude.dat 500 500 1" << endl; return 1; } if(string(argv[1]) == "random") { num_points = atoi(argv[2]); random_distribution = true; char* pEnd; max = strtod(argv[3], &pEnd); min = -max; cx = cy = 0; zoom = atof(argv[4]); } else { zoom = atof(argv[4]); cx = atof(argv[2]); cy = atof(argv[3]); } vector polyline; if(random_distribution) { // Create a simple bounding box polyline.push_back(new Point(min,min)); polyline.push_back(new Point(min,max)); polyline.push_back(new Point(max,max)); polyline.push_back(new Point(max,min)); } else { // Load pointset from file // Parse and tokenize data file string line; ifstream myfile(argv[1]); if (myfile.is_open()) { while (!myfile.eof()) { getline(myfile, line); if (line.size() == 0) { break; } istringstream iss(line); vector tokens; copy(istream_iterator(iss), istream_iterator(), back_inserter >(tokens)); double x = StringToDouble(tokens[0]); double y = StringToDouble(tokens[1]); polyline.push_back(new Point(x, y)); num_points++; } myfile.close(); } else { cout << "File not opened" << endl; } } cout << "Number of constrained edges = " << polyline.size() << endl; polylines.push_back(polyline); Init(); /* * Perform triangulation! */ double init_time = glfwGetTime(); /* * STEP 1: Create CDT and add primary polyline * NOTE: polyline must be a simple polygon. The polyline's points * constitute constrained edges. No repeat points!!! */ CDT* cdt = new CDT(polyline); /* * STEP 2: Add holes or Steiner points if necessary */ string s(argv[1]); if(s.find("dude.dat", 0) != string::npos) { // Add head hole vector head_hole = CreateHeadHole(); num_points += head_hole.size(); cdt->AddHole(head_hole); // Add chest hole vector chest_hole = CreateChestHole(); num_points += chest_hole.size(); cdt->AddHole(chest_hole); polylines.push_back(head_hole); polylines.push_back(chest_hole); } else if (s.find("stalactite.dat", 0) != string::npos) { vector stalactite_hole = CreateStalactiteHole(); num_points += stalactite_hole.size(); cdt->AddHole(stalactite_hole); polylines.push_back(stalactite_hole); } else if (random_distribution) { max-=(1e-4); min+=(1e-4); for(int i = 0; i < num_points; i++) { double x = Random(Fun, min, max); double y = Random(Fun, min, max); cdt->AddPoint(new Point(x, y)); } } /* * STEP 3: Triangulate! */ cdt->Triangulate(); double dt = glfwGetTime() - init_time; triangles = cdt->GetTriangles(); map = cdt->GetMap(); cout << "Number of points = " << num_points << endl; cout << "Number of triangles = " << triangles.size() << endl; cout << "Elapsed time (ms) = " << dt*1000.0 << endl; MainLoop(zoom); // Cleanup delete cdt; // Free points for(int i = 0; i < polylines.size(); i++) { vector poly = polylines[i]; FreeClear(poly); } ShutDown(0); return 0; } void Init() { const int window_width = 800, window_height = 600; if (glfwInit() != GL_TRUE) ShutDown(1); // 800 x 600, 16 bit color, no depth, alpha or stencil buffers, windowed if (glfwOpenWindow(window_width, window_height, 5, 6, 5, 0, 0, 0, GLFW_WINDOW) != GL_TRUE) ShutDown(1); glfwSetWindowTitle("Poly2Tri - C++"); glfwSwapInterval(1); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glClearColor(0.0, 0.0, 0.0, 0.0); glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); } void ShutDown(int return_code) { glfwTerminate(); exit(return_code); } void MainLoop(const double zoom) { // the time of the previous frame double old_time = glfwGetTime(); // this just loops as long as the program runs bool running = true; while (running) { // calculate time elapsed, and the amount by which stuff rotates double current_time = glfwGetTime(), delta_rotate = (current_time - old_time) * rotations_per_tick * 360; old_time = current_time; // escape to quit, arrow keys to rotate view // Check if ESC key was pressed or window was closed running = !glfwGetKey(GLFW_KEY_ESC) && glfwGetWindowParam(GLFW_OPENED); if (glfwGetKey(GLFW_KEY_LEFT) == GLFW_PRESS) rotate_y += delta_rotate; if (glfwGetKey(GLFW_KEY_RIGHT) == GLFW_PRESS) rotate_y -= delta_rotate; // z axis always rotates rotate_z += delta_rotate; // Draw the scene if (draw_map) { DrawMap(zoom); } else { Draw(zoom); } // swap back and front buffers glfwSwapBuffers(); } } void ResetZoom(double zoom, double cx, double cy, double width, double height) { double left = -width / zoom; double right = width / zoom; double bottom = -height / zoom; double top = height / zoom; // Reset viewport glLoadIdentity(); glMatrixMode(GL_PROJECTION); glLoadIdentity(); // Reset ortho view glOrtho(left, right, bottom, top, 1, -1); glTranslatef(-cx, -cy, 0); glMatrixMode(GL_MODELVIEW); glDisable(GL_DEPTH_TEST); glLoadIdentity(); // Clear the screen glClear(GL_COLOR_BUFFER_BIT); } void Draw(const double zoom) { // reset zoom Point center = Point(cx, cy); ResetZoom(zoom, center.x, center.y, 800, 600); for (int i = 0; i < triangles.size(); i++) { Triangle& t = *triangles[i]; Point& a = *t.GetPoint(0); Point& b = *t.GetPoint(1); Point& c = *t.GetPoint(2); // Red glColor3f(1, 0, 0); glBegin(GL_LINE_LOOP); glVertex2f(a.x, a.y); glVertex2f(b.x, b.y); glVertex2f(c.x, c.y); glEnd(); } // green glColor3f(0, 1, 0); for(int i = 0; i < polylines.size(); i++) { vector poly = polylines[i]; glBegin(GL_LINE_LOOP); for(int j = 0; j < poly.size(); j++) { glVertex2f(poly[j]->x, poly[j]->y); } glEnd(); } } void DrawMap(const double zoom) { // reset zoom Point center = Point(cx, cy); ResetZoom(zoom, center.x, center.y, 800, 600); list::iterator it; for (it = map.begin(); it != map.end(); it++) { Triangle& t = **it; Point& a = *t.GetPoint(0); Point& b = *t.GetPoint(1); Point& c = *t.GetPoint(2); ConstrainedColor(t.constrained_edge[2]); glBegin(GL_LINES); glVertex2f(a.x, a.y); glVertex2f(b.x, b.y); glEnd( ); ConstrainedColor(t.constrained_edge[0]); glBegin(GL_LINES); glVertex2f(b.x, b.y); glVertex2f(c.x, c.y); glEnd( ); ConstrainedColor(t.constrained_edge[1]); glBegin(GL_LINES); glVertex2f(c.x, c.y); glVertex2f(a.x, a.y); glEnd( ); } } void ConstrainedColor(bool constrain) { if (constrain) { // Green glColor3f(0, 1, 0); } else { // Red glColor3f(1, 0, 0); } } vector CreateHeadHole() { vector head_hole; head_hole.push_back(new Point(325, 437)); head_hole.push_back(new Point(320, 423)); head_hole.push_back(new Point(329, 413)); head_hole.push_back(new Point(332, 423)); return head_hole; } vector CreateChestHole() { vector chest_hole; chest_hole.push_back(new Point(320.72342,480)); chest_hole.push_back(new Point(338.90617,465.96863)); chest_hole.push_back(new Point(347.99754,480.61584)); chest_hole.push_back(new Point(329.8148,510.41534)); chest_hole.push_back(new Point(339.91632,480.11077)); chest_hole.push_back(new Point(334.86556,478.09046)); return chest_hole; } vector CreateStalactiteHole() { vector hole; hole.push_back(new Point(980, 1636)); hole.push_back(new Point(950, 1600)); hole.push_back(new Point(650, 1230)); hole.push_back(new Point(625, 1247)); hole.push_back(new Point(600, 1250)); hole.push_back(new Point(591, 1350)); hole.push_back(new Point(550, 2050)); return hole; } double StringToDouble(const std::string& s) { std::istringstream i(s); double x; if (!(i >> x)) return 0; return x; } double Fun(double x) { return 2.5 + sin(10 * x) / x; } double Random(double (*fun)(double), double xmin = 0, double xmax = 1) { static double (*Fun)(double) = NULL, YMin, YMax; static bool First = true; // Initialises random generator for first call if (First) { First = false; srand((unsigned) time(NULL)); } // Evaluates maximum of function if (fun != Fun) { Fun = fun; YMin = 0, YMax = Fun(xmin); for (int iX = 1; iX < RAND_MAX; iX++) { double X = xmin + (xmax - xmin) * iX / RAND_MAX; double Y = Fun(X); YMax = Y > YMax ? Y : YMax; } } // Gets random values for X & Y double X = xmin + (xmax - xmin) * rand() / RAND_MAX; double Y = YMin + (YMax - YMin) * rand() / RAND_MAX; // Returns if valid and try again if not valid return Y < fun(X) ? X : Random(Fun, xmin, xmax); } ================================================ FILE: wscript ================================================ #! /usr/bin/env python # encoding: utf-8 # waf 1.6.10 VERSION='0.3.3' import sys APPNAME='p2t' top = '.' out = 'build' CPP_SOURCES = ['poly2tri/common/shapes.cc', 'poly2tri/sweep/cdt.cc', 'poly2tri/sweep/advancing_front.cc', 'poly2tri/sweep/sweep_context.cc', 'poly2tri/sweep/sweep.cc', 'testbed/main.cc'] from waflib.Tools.compiler_cxx import cxx_compiler cxx_compiler['win32'] = ['g++'] #Platform specific libs if sys.platform == 'win32': # MS Windows sys_libs = ['glfw', 'opengl32'] elif sys.platform == 'darwin': # Apple OSX sys_libs = ['glfw', 'OpenGL'] else: # GNU/Linux, BSD, etc sys_libs = ['glfw', 'GL'] def options(opt): print(' set_options') opt.load('compiler_cxx') def configure(conf): print(' calling the configuration') conf.load('compiler_cxx') conf.env.CXXFLAGS = ['-O3', '-ffast-math'] conf.env.DEFINES_P2T = ['P2T'] conf.env.LIB_P2T = sys_libs def build(bld): print(' building') bld.program(features = 'cxx cxxprogram', source=CPP_SOURCES, target = 'p2t', uselib = 'P2T')