diff --git a/contrib/poly2tri/LICENSE b/contrib/poly2tri/LICENSE index 9417c0836..dddc3ccc3 100644 --- a/contrib/poly2tri/LICENSE +++ b/contrib/poly2tri/LICENSE @@ -1,7 +1,6 @@ -Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors -http://code.google.com/p/poly2tri/ - +Copyright (c) 2009-2018, Poly2Tri Contributors All rights reserved. + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/contrib/poly2tri/README b/contrib/poly2tri/README deleted file mode 100644 index 883e9a581..000000000 --- a/contrib/poly2tri/README +++ /dev/null @@ -1,51 +0,0 @@ -================== -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 diff --git a/contrib/poly2tri/README.md b/contrib/poly2tri/README.md new file mode 100644 index 000000000..88a0618ea --- /dev/null +++ b/contrib/poly2tri/README.md @@ -0,0 +1,101 @@ +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) + +Unit tests: +* Boost (filesystem, test framework) + +Testbed: + +* OpenGL +* [GLFW](http://glfw.sf.net) + +Build the library +----------------- + +With the ninja build system installed: + +``` +mkdir build && cd build +cmake -GNinja .. +cmake --build . +``` + +Build and run with unit tests +---------------------------- + +With the ninja build system: + +``` +mkdir build && cd build +cmake -GNinja -DP2T_BUILD_TESTS=ON .. +cmake --build . +ctest --output-on-failure +``` + +Build with the testbed +----------------- + +``` +mkdir build && cd build +cmake -GNinja -DP2T_BUILD_TESTBED=ON .. +cmake --build . +``` + +Running the Examples +-------------------- + +Load data points from a file: +``` +build/testbed/p2t +``` +Load data points from a file and automatically fit the geometry to the window: +``` +build/testbed/p2t +``` +Random distribution of points inside a constrained box: +``` +build/testbed/p2t random +``` +Examples: +``` +build/testbed/p2t testbed/data/dude.dat 350 500 3 + +build/testbed/p2t testbed/data/nazca_monkey.dat + +build/testbed/p2t random 10 100 5.0 +build/testbed/p2t random 1000 20000 0.025 +``` + +References +========== + +- Domiter V. and Zalik B. (2008) Sweep‐line algorithm for constrained Delaunay triangulation +- FlipScan by library author Thomas Åhlén + +![FlipScan](doc/FlipScan.png) diff --git a/contrib/poly2tri/poly2tri/common/dll_symbol.h b/contrib/poly2tri/poly2tri/common/dll_symbol.h new file mode 100644 index 000000000..72ed5a75f --- /dev/null +++ b/contrib/poly2tri/poly2tri/common/dll_symbol.h @@ -0,0 +1,59 @@ +/* + * Poly2Tri Copyright (c) 2009-2022, Poly2Tri Contributors + * https://github.com/jhasse/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. + */ + +#pragma once + +#if defined(_WIN32) +# pragma warning( disable: 4273) +# define P2T_COMPILER_DLLEXPORT __declspec(dllexport) +# define P2T_COMPILER_DLLIMPORT __declspec(dllimport) +#elif defined(__GNUC__) +# define P2T_COMPILER_DLLEXPORT __attribute__ ((visibility ("default"))) +# define P2T_COMPILER_DLLIMPORT __attribute__ ((visibility ("default"))) +#else +# define P2T_COMPILER_DLLEXPORT +# define P2T_COMPILER_DLLIMPORT +#endif + +// We need to enable shard linkage explicitely +#ifdef ASSIMP_BUILD_DLL_EXPORT +# define P2T_SHARED_EXPORTS 1 +#endif + +#ifndef P2T_DLL_SYMBOL +# if defined(P2T_STATIC_EXPORTS) +# define P2T_DLL_SYMBOL +# elif defined(P2T_SHARED_EXPORTS) +# define P2T_DLL_SYMBOL P2T_COMPILER_DLLEXPORT +# else +# define P2T_DLL_SYMBOL P2T_COMPILER_DLLIMPORT +# endif +#endif diff --git a/contrib/poly2tri/poly2tri/common/shapes.cc b/contrib/poly2tri/poly2tri/common/shapes.cc index c94e11c03..359ef7586 100644 --- a/contrib/poly2tri/poly2tri/common/shapes.cc +++ b/contrib/poly2tri/poly2tri/common/shapes.cc @@ -1,6 +1,6 @@ /* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ + * Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors + * https://github.com/jhasse/poly2tri * * All rights reserved. * @@ -29,14 +29,24 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "shapes.h" + +#include #include namespace p2t { +Point::Point(double x, double y) : x(x), y(y) +{ +} + +std::ostream& operator<<(std::ostream& out, const Point& point) { + return out << point.x << "," << point.y; +} + 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; + neighbors_[0] = nullptr; neighbors_[1] = nullptr; neighbors_[2] = nullptr; constrained_edge[0] = constrained_edge[1] = constrained_edge[2] = false; delaunay_edge[0] = delaunay_edge[1] = delaunay_edge[2] = false; interior_ = false; @@ -76,39 +86,37 @@ void Triangle::MarkNeighbor(Triangle& t) void Triangle::Clear() { Triangle *t; - for( int i=0; i<3; i++ ) - { - t = neighbors_[i]; - if( t != NULL ) - { - t->ClearNeighbor( this ); - } + for (auto& neighbor : neighbors_) { + t = neighbor; + if (t != nullptr) { + t->ClearNeighbor(this); + } } ClearNeighbors(); - points_[0]=points_[1]=points_[2] = NULL; + points_[0]=points_[1]=points_[2] = nullptr; } void Triangle::ClearNeighbor(const Triangle *triangle ) { if( neighbors_[0] == triangle ) { - neighbors_[0] = NULL; + neighbors_[0] = nullptr; } else if( neighbors_[1] == triangle ) { - neighbors_[1] = NULL; + neighbors_[1] = nullptr; } else { - neighbors_[2] = NULL; + neighbors_[2] = nullptr; } } void Triangle::ClearNeighbors() { - neighbors_[0] = NULL; - neighbors_[1] = NULL; - neighbors_[2] = NULL; + neighbors_[0] = nullptr; + neighbors_[1] = nullptr; + neighbors_[2] = nullptr; } void Triangle::ClearDelunayEdges() @@ -220,7 +228,7 @@ Point* Triangle::PointCW(const Point& point) return points_[1]; } assert(0); - return NULL; + return nullptr; } // The point counter-clockwise to given point @@ -234,7 +242,18 @@ Point* Triangle::PointCCW(const Point& point) return points_[0]; } assert(0); - return NULL; + return nullptr; +} + +// The neighbor across to given point +Triangle* Triangle::NeighborAcross(const Point& point) +{ + if (&point == points_[0]) { + return neighbors_[0]; + } else if (&point == points_[1]) { + return neighbors_[1]; + } + return neighbors_[2]; } // The neighbor clockwise to given point @@ -343,23 +362,50 @@ void Triangle::SetDelunayEdgeCW(const Point& p, bool e) } } -// The neighbor across to given point -Triangle& Triangle::NeighborAcross(const 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; + std::cout << *points_[0] << " " << *points_[1] << " " << *points_[2] << std::endl; } +bool Triangle::CircumcicleContains(const Point& point) const +{ + assert(IsCounterClockwise()); + const double dx = points_[0]->x - point.x; + const double dy = points_[0]->y - point.y; + const double ex = points_[1]->x - point.x; + const double ey = points_[1]->y - point.y; + const double fx = points_[2]->x - point.x; + const double fy = points_[2]->y - point.y; + + const double ap = dx * dx + dy * dy; + const double bp = ex * ex + ey * ey; + const double cp = fx * fx + fy * fy; + + return (dx * (fy * bp - cp * ey) - dy * (fx * bp - cp * ex) + ap * (fx * ey - fy * ex)) < 0; } + +bool Triangle::IsCounterClockwise() const +{ + return (points_[1]->x - points_[0]->x) * (points_[2]->y - points_[0]->y) - + (points_[2]->x - points_[0]->x) * (points_[1]->y - points_[0]->y) > + 0; +} + +bool IsDelaunay(const std::vector& triangles) +{ + for (const auto triangle : triangles) { + for (const auto other : triangles) { + if (triangle == other) { + continue; + } + for (int i = 0; i < 3; ++i) { + if (triangle->CircumcicleContains(*other->GetPoint(i))) { + return false; + } + } + } + } + return true; +} + +} // namespace p2t diff --git a/contrib/poly2tri/poly2tri/common/shapes.h b/contrib/poly2tri/poly2tri/common/shapes.h index d3660f716..3f4d1c02f 100644 --- a/contrib/poly2tri/poly2tri/common/shapes.h +++ b/contrib/poly2tri/poly2tri/common/shapes.h @@ -1,6 +1,6 @@ /* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ + * Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors + * https://github.com/jhasse/poly2tri * * All rights reserved. * @@ -29,22 +29,24 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -// Include guard -#ifndef SHAPES_H -#define SHAPES_H +#pragma once -#include +#include "dll_symbol.h" + +#include #include #include -#include -#include -#include +#include + +#if defined(_WIN32) +# pragma warning( disable: 4251) +#endif namespace p2t { struct Edge; -struct Point { +struct P2T_DLL_SYMBOL Point { double x, y; @@ -59,7 +61,7 @@ struct Point { std::vector edge_list; /// Construct using coordinates. - Point(double x, double y) : x(x), y(y) {} + Point(double x, double y); /// Set this point to all zeros. void set_zero() @@ -121,8 +123,10 @@ struct Point { }; +P2T_DLL_SYMBOL std::ostream& operator<<(std::ostream&, const Point&); + // Represents a simple polygon's edge -struct Edge { +struct P2T_DLL_SYMBOL Edge { Point* p, *q; @@ -138,9 +142,7 @@ struct Edge { p = &p2; } else if (p1.x == p2.x) { // Repeat points - // ASSIMP_CHANGE (aramis_acg) - throw std::runtime_error(std::string("repeat points")); - //assert(false); + throw std::runtime_error("Edge::Edge: p1 == p2"); } } @@ -151,7 +153,7 @@ struct Edge { // 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 { +class P2T_DLL_SYMBOL Triangle { public: /// Constructor @@ -178,6 +180,7 @@ void MarkConstrainedEdge(Point* p, Point* q); int Index(const Point* p); int EdgeIndex(const Point* p1, const Point* p2); +Triangle* NeighborAcross(const Point& point); Triangle* NeighborCW(const Point& point); Triangle* NeighborCCW(const Point& point); bool GetConstrainedEdgeCCW(const Point& p); @@ -205,12 +208,14 @@ void ClearDelunayEdges(); inline bool IsInterior(); inline void IsInterior(bool b); -Triangle& NeighborAcross(const Point& opoint); - void DebugPrint(); +bool CircumcicleContains(const Point&) const; + private: +bool IsCounterClockwise() const; + /// Triangle points Point* points_[3]; /// Neighbor list @@ -258,7 +263,7 @@ inline bool operator ==(const Point& a, const Point& b) inline bool operator !=(const Point& a, const Point& b) { - return !(a.x == b.x) && !(a.y == b.y); + return !(a.x == b.x) || !(a.y == b.y); } /// Peform the dot product on two vectors. @@ -322,6 +327,7 @@ inline void Triangle::IsInterior(bool b) interior_ = b; } -} +/// Is this set a valid delaunay triangulation? +P2T_DLL_SYMBOL bool IsDelaunay(const std::vector&); -#endif +} diff --git a/contrib/poly2tri/poly2tri/common/utils.h b/contrib/poly2tri/poly2tri/common/utils.h index 2424c712c..c9f1c23c3 100644 --- a/contrib/poly2tri/poly2tri/common/utils.h +++ b/contrib/poly2tri/poly2tri/common/utils.h @@ -1,6 +1,6 @@ /* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ + * Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors + * https://github.com/jhasse/poly2tri * * All rights reserved. * @@ -29,14 +29,15 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef UTILS_H -#define UTILS_H +#pragma once // Otherwise #defines like M_PI are undeclared under Visual Studio #define _USE_MATH_DEFINES +#include "shapes.h" + +#include #include -#include // C99 removes M_PI from math.h #ifndef M_PI @@ -66,7 +67,11 @@ Orientation Orient2d(const Point& pa, const Point& pb, const 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) { + +// Using a tolerance here fails on concave-by-subepsilon boundaries +// if (val > -EPSILON && val < EPSILON) { +// Using == on double makes -Wfloat-equal warnings yell at us + if (std::fpclassify(val) == FP_ZERO) { return COLLINEAR; } else if (val > 0) { return CCW; @@ -123,5 +128,3 @@ bool InScanArea(const Point& pa, const Point& pb, const Point& pc, const Point& } } - -#endif diff --git a/contrib/poly2tri/poly2tri/poly2tri.h b/contrib/poly2tri/poly2tri/poly2tri.h index ba5cc159e..3d40373b0 100644 --- a/contrib/poly2tri/poly2tri/poly2tri.h +++ b/contrib/poly2tri/poly2tri/poly2tri.h @@ -1,6 +1,6 @@ /* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ + * Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors + * https://github.com/jhasse/poly2tri * * All rights reserved. * @@ -29,10 +29,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef POLY2TRI_H -#define POLY2TRI_H +#pragma once #include "common/shapes.h" #include "sweep/cdt.h" - -#endif diff --git a/contrib/poly2tri/poly2tri/sweep/advancing_front.cc b/contrib/poly2tri/poly2tri/sweep/advancing_front.cc index 9739babce..e981bad2f 100644 --- a/contrib/poly2tri/poly2tri/sweep/advancing_front.cc +++ b/contrib/poly2tri/poly2tri/sweep/advancing_front.cc @@ -1,6 +1,6 @@ /* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ + * Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors + * https://github.com/jhasse/poly2tri * * All rights reserved. * @@ -30,6 +30,8 @@ */ #include "advancing_front.h" +#include + namespace p2t { AdvancingFront::AdvancingFront(Node& head, Node& tail) @@ -44,21 +46,21 @@ Node* AdvancingFront::LocateNode(double x) Node* node = search_node_; if (x < node->value) { - while ((node = node->prev) != NULL) { + while ((node = node->prev) != nullptr) { if (x >= node->value) { search_node_ = node; return node; } } } else { - while ((node = node->next) != NULL) { + while ((node = node->next) != nullptr) { if (x < node->value) { search_node_ = node->prev; return node->prev; } } } - return NULL; + return nullptr; } Node* AdvancingFront::FindSearchNode(double x) @@ -86,13 +88,13 @@ Node* AdvancingFront::LocatePoint(const Point* point) } } } else if (px < nx) { - while ((node = node->prev) != NULL) { + while ((node = node->prev) != nullptr) { if (point == node->point) { break; } } } else { - while ((node = node->next) != NULL) { + while ((node = node->next) != nullptr) { if (point == node->point) break; } @@ -105,4 +107,4 @@ AdvancingFront::~AdvancingFront() { } -} +} // namespace p2t diff --git a/contrib/poly2tri/poly2tri/sweep/advancing_front.h b/contrib/poly2tri/poly2tri/sweep/advancing_front.h index 3bfec5368..ffd3fe71b 100644 --- a/contrib/poly2tri/poly2tri/sweep/advancing_front.h +++ b/contrib/poly2tri/poly2tri/sweep/advancing_front.h @@ -1,6 +1,6 @@ /* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ + * Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors + * https://github.com/jhasse/poly2tri * * All rights reserved. * @@ -29,8 +29,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ADVANCED_FRONT_H -#define ADVANCED_FRONT_H +#pragma once #include "../common/shapes.h" @@ -114,5 +113,3 @@ inline void AdvancingFront::set_search(Node* node) } } - -#endif diff --git a/contrib/poly2tri/poly2tri/sweep/cdt.cc b/contrib/poly2tri/poly2tri/sweep/cdt.cc index b79f5a8de..4dfe6a641 100644 --- a/contrib/poly2tri/poly2tri/sweep/cdt.cc +++ b/contrib/poly2tri/poly2tri/sweep/cdt.cc @@ -1,6 +1,6 @@ /* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ + * Poly2Tri Copyright (c) 2009-2021, Poly2Tri Contributors + * https://github.com/jhasse/poly2tri * * All rights reserved. * @@ -68,4 +68,4 @@ CDT::~CDT() delete sweep_; } -} +} // namespace p2t diff --git a/contrib/poly2tri/poly2tri/sweep/cdt.h b/contrib/poly2tri/poly2tri/sweep/cdt.h index 4a9a292d3..7c54ea976 100644 --- a/contrib/poly2tri/poly2tri/sweep/cdt.h +++ b/contrib/poly2tri/poly2tri/sweep/cdt.h @@ -1,6 +1,6 @@ /* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ + * Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors + * https://github.com/jhasse/poly2tri * * All rights reserved. * @@ -29,13 +29,14 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef CDT_H -#define CDT_H +#pragma once #include "advancing_front.h" #include "sweep_context.h" #include "sweep.h" +#include "../common/dll_symbol.h" + /** * * @author Mason Green @@ -44,7 +45,7 @@ namespace p2t { -class CDT +class P2T_DLL_SYMBOL CDT { public: @@ -101,5 +102,3 @@ public: }; } - -#endif diff --git a/contrib/poly2tri/poly2tri/sweep/sweep.cc b/contrib/poly2tri/poly2tri/sweep/sweep.cc index e1f23f11b..48e8bee8f 100644 --- a/contrib/poly2tri/poly2tri/sweep/sweep.cc +++ b/contrib/poly2tri/poly2tri/sweep/sweep.cc @@ -1,6 +1,6 @@ /* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ + * Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors + * https://github.com/jhasse/poly2tri * * All rights reserved. * @@ -28,24 +28,21 @@ * 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 { +#include +#include -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning( disable : 4702 ) -#endif // _MSC_VER +namespace p2t { // Triangulate simple polygon with holes void Sweep::Triangulate(SweepContext& tcx) { tcx.InitTriangulation(); - tcx.CreateAdvancingFront(nodes_); + tcx.CreateAdvancingFront(); // Sweep points; build mesh SweepPoints(tcx); // Clean up @@ -57,8 +54,8 @@ void Sweep::SweepPoints(SweepContext& tcx) for (size_t i = 1; i < tcx.point_count(); i++) { Point& point = *tcx.GetPoint(i); Node* node = &PointEvent(tcx, point); - for (unsigned int ii = 0; ii < point.edge_list.size(); ii++) { - EdgeEvent(tcx, point.edge_list[ii], node); + for (auto& j : point.edge_list) { + EdgeEvent(tcx, j, node); } } } @@ -68,17 +65,25 @@ 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)) { + while (t && !t->GetConstrainedEdgeCW(*p)) { t = t->NeighborCCW(*p); } // Collect interior triangles constrained by edges - tcx.MeshClean(*t); + if (t) { + tcx.MeshClean(*t); + } } Node& Sweep::PointEvent(SweepContext& tcx, Point& point) { - Node& node = tcx.LocateNode(point); + Node* node_ptr = tcx.LocateNode(point); + if (!node_ptr || !node_ptr->point || !node_ptr->next || !node_ptr->next->point) + { + throw std::runtime_error("PointEvent - null node"); + } + + Node& node = *node_ptr; Node& new_node = NewFrontTriangle(tcx, point, node); // Only need to check +epsilon since point never have smaller @@ -111,9 +116,9 @@ void Sweep::EdgeEvent(SweepContext& tcx, Edge* edge, Node* node) void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangle, Point& point) { - if (triangle == nullptr) - return; - + if (triangle == nullptr) { + throw std::runtime_error("EdgeEvent - null triangle"); + } if (IsEdgeSideOfTriangle(*triangle, ep, eq)) { return; } @@ -121,17 +126,14 @@ void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangl Point* p1 = triangle->PointCCW(point); Orientation o1 = Orient2d(eq, *p1, ep); if (o1 == COLLINEAR) { - - - if( triangle->Contains(&eq, p1)) { - triangle->MarkConstrainedEdge(&eq, p1 ); + 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 ); + triangle = triangle->NeighborAcross(point); + EdgeEvent(tcx, ep, *p1, triangle, *p1); } else { - // ASSIMP_CHANGE (aramis_acg) throw std::runtime_error("EdgeEvent - collinear points not supported"); } return; @@ -140,18 +142,14 @@ void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangl Point* p2 = triangle->PointCW(point); Orientation o2 = Orient2d(eq, *p2, ep); if (o2 == COLLINEAR) { - - - - if( triangle->Contains(&eq, p2)) { - triangle->MarkConstrainedEdge(&eq, p2 ); + 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 ); + triangle = triangle->NeighborAcross(point); + EdgeEvent(tcx, ep, *p2, triangle, *p2); } else { - // ASSIMP_CHANGE (aramis_acg) throw std::runtime_error("EdgeEvent - collinear points not supported"); } return; @@ -162,12 +160,13 @@ void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangl // that will cross edge if (o1 == CW) { triangle = triangle->NeighborCCW(point); - } else{ + } else { triangle = triangle->NeighborCW(point); } EdgeEvent(tcx, ep, eq, triangle, point); } else { // This triangle crosses constraint so lets flippin start! + assert(triangle); FlipEdgeEvent(tcx, ep, eq, triangle, point); } } @@ -228,7 +227,6 @@ void Sweep::Fill(SweepContext& tcx, Node& node) if (!Legalize(tcx, *triangle)) { tcx.MapTriangleToNodes(*triangle); } - } void Sweep::FillAdvancingFront(SweepContext& tcx, Node& n) @@ -237,7 +235,7 @@ void Sweep::FillAdvancingFront(SweepContext& tcx, Node& n) // Fill right holes Node* node = n.next; - while (node->next) { + while (node && node->next) { // if HoleAngle exceeds 90 degrees then break. if (LargeHole_DontFill(node)) break; Fill(tcx, *node); @@ -247,7 +245,7 @@ void Sweep::FillAdvancingFront(SweepContext& tcx, Node& n) // Fill left holes node = n.prev; - while (node->prev) { + while (node && node->prev) { // if HoleAngle exceeds 90 degrees then break. if (LargeHole_DontFill(node)) break; Fill(tcx, *node); @@ -264,6 +262,35 @@ void Sweep::FillAdvancingFront(SweepContext& tcx, Node& 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(const Node* node) const { const Node* nextNode = node->next; @@ -271,20 +298,28 @@ bool Sweep::LargeHole_DontFill(const Node* node) const { 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. const 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)) + if ((next2Node != nullptr) && !AngleExceedsPlus90DegreesOrIsNegative(node->point, next2Node->point, prevNode->point)) return false; const 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)) + if ((prev2Node != nullptr) && !AngleExceedsPlus90DegreesOrIsNegative(node->point, nextNode->point, prev2Node->point)) return false; return true; } +bool Sweep::AngleIsNegative(const Point* origin, const Point* pa, const Point* pb) const { + const double angle = Angle(origin, pa, pb); + return angle < 0; +} + bool Sweep::AngleExceeds90Degrees(const Point* origin, const Point* pa, const Point* pb) const { const double angle = Angle(origin, pa, pb); return ((angle > PI_div2) || (angle < -PI_div2)); @@ -306,7 +341,7 @@ double Sweep::Angle(const Point* origin, const Point* pa, const Point* pb) const */ const double px = origin->x; const double py = origin->y; - const double ax = pa->x- px; + const double ax = pa->x - px; const double ay = pa->y - py; const double bx = pb->x - px; const double by = pb->y - py; @@ -599,7 +634,7 @@ void Sweep::FillRightBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) { // Concave FillRightConcaveEdgeEvent(tcx, edge, node); - } else{ + } else { // Convex FillRightConvexEdgeEvent(tcx, edge, node); // Retry this one @@ -623,7 +658,6 @@ void Sweep::FillRightConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) } } } - } void Sweep::FillRightConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) @@ -632,13 +666,13 @@ void Sweep::FillRightConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) if (Orient2d(*node.next->point, *node.next->next->point, *node.next->next->next->point) == CCW) { // Concave FillRightConcaveEdgeEvent(tcx, edge, *node.next); - } else{ + } 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{ + } else { // Above } } @@ -677,13 +711,13 @@ void Sweep::FillLeftConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) if (Orient2d(*node.prev->point, *node.prev->prev->point, *node.prev->prev->prev->point) == CW) { // Concave FillLeftConcaveEdgeEvent(tcx, edge, *node.prev); - } else{ + } 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{ + } else { // Above } } @@ -699,17 +733,22 @@ void Sweep::FillLeftConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) if (Orient2d(*node.point, *node.prev->point, *node.prev->prev->point) == CW) { // Next is concave FillLeftConcaveEdgeEvent(tcx, edge, node); - } else{ + } else { // Next is convex } } } - } void Sweep::FlipEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* t, Point& p) { - Triangle& ot = t->NeighborAcross(p); + assert(t); + Triangle* ot_ptr = t->NeighborAcross(p); + if (ot_ptr == nullptr) + { + throw std::runtime_error("FlipEdgeEvent - null neighbor across"); + } + Triangle& ot = *ot_ptr; Point& op = *ot.OppositePoint(*t, p); if (InScanArea(p, *t->PointCCW(p), *t->PointCW(p), op)) { @@ -775,10 +814,26 @@ Point& Sweep::NextFlipPoint(Point& ep, Point& eq, Triangle& ot, Point& op) 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); + Triangle* ot_ptr = t.NeighborAcross(p); + if (ot_ptr == nullptr) { + throw std::runtime_error("FlipScanEdgeEvent - null neighbor across"); + } - if (InScanArea(eq, *flip_triangle.PointCCW(eq), *flip_triangle.PointCW(eq), op)) { + Point* op_ptr = ot_ptr->OppositePoint(t, p); + if (op_ptr == nullptr) { + throw std::runtime_error("FlipScanEdgeEvent - null opposing point"); + } + + Point* p1 = flip_triangle.PointCCW(eq); + Point* p2 = flip_triangle.PointCW(eq); + if (p1 == nullptr || p2 == nullptr) { + throw std::runtime_error("FlipScanEdgeEvent - null on either of points"); + } + + Triangle& ot = *ot_ptr; + Point& op = *op_ptr; + + if (InScanArea(eq, *p1, *p2, 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 @@ -788,7 +843,7 @@ void Sweep::FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& // 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{ + } else { Point& newP = NextFlipPoint(ep, eq, ot, op); FlipScanEdgeEvent(tcx, ep, eq, flip_triangle, ot, newP); } @@ -797,14 +852,9 @@ void Sweep::FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& Sweep::~Sweep() { // Clean up memory - for(size_t i = 0; i < nodes_.size(); i++) { - delete nodes_[i]; + for (auto& node : nodes_) { + delete node; } - } -#ifdef _MSC_VER -# pragma warning( pop ) -#endif // _MSC_VER - -} +} // namespace p2t diff --git a/contrib/poly2tri/poly2tri/sweep/sweep.h b/contrib/poly2tri/poly2tri/sweep/sweep.h index ad429fd96..ad43f2e4a 100644 --- a/contrib/poly2tri/poly2tri/sweep/sweep.h +++ b/contrib/poly2tri/poly2tri/sweep/sweep.h @@ -1,6 +1,6 @@ /* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ + * Poly2Tri Copyright (c) 2009-2018, Poly2Tri Contributors + * https://github.com/jhasse/poly2tri * * All rights reserved. * @@ -33,11 +33,10 @@ * Zalik, B.(2008)'Sweep-line algorithm for constrained Delaunay triangulation', * International Journal of Geographical Information Science * - * "FlipScan" Constrained Edge Algorithm invented by Thomas ?hl?n, thahlen@gmail.com + * "FlipScan" Constrained Edge Algorithm invented by Thomas Åhlén, thahlen@gmail.com */ -#ifndef SWEEP_H -#define SWEEP_H +#pragma once #include @@ -172,6 +171,7 @@ private: // Decision-making about when to Fill hole. // Contributed by ToolmakerSteve2 bool LargeHole_DontFill(const Node* node) const; + bool AngleIsNegative(const Point* origin, const Point* pa, const Point* pb) const; bool AngleExceeds90Degrees(const Point* origin, const Point* pa, const Point* pb) const; bool AngleExceedsPlus90DegreesOrIsNegative(const Point* origin, const Point* pa, const Point* pb) const; double Angle(const Point* origin, const Point* pa, const Point* pb) const; @@ -281,5 +281,3 @@ private: }; } - -#endif diff --git a/contrib/poly2tri/poly2tri/sweep/sweep_context.cc b/contrib/poly2tri/poly2tri/sweep/sweep_context.cc index a9f1fdf8e..7b9432feb 100644 --- a/contrib/poly2tri/poly2tri/sweep/sweep_context.cc +++ b/contrib/poly2tri/poly2tri/sweep/sweep_context.cc @@ -1,6 +1,6 @@ /* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ + * Poly2Tri Copyright (c) 2009-2022, Poly2Tri Contributors + * https://github.com/jhasse/poly2tri * * All rights reserved. * @@ -34,13 +34,13 @@ namespace p2t { -SweepContext::SweepContext(const std::vector& polyline) : points_(polyline), - front_(0), - head_(0), - tail_(0), - af_head_(0), - af_middle_(0), - af_tail_(0) +SweepContext::SweepContext(std::vector polyline) : points_(std::move(polyline)), + front_(nullptr), + head_(nullptr), + tail_(nullptr), + af_head_(nullptr), + af_middle_(nullptr), + af_tail_(nullptr) { InitEdges(points_); } @@ -48,8 +48,8 @@ SweepContext::SweepContext(const std::vector& polyline) : points_(polyli void SweepContext::AddHole(const std::vector& polyline) { InitEdges(polyline); - for(unsigned int i = 0; i < polyline.size(); i++) { - points_.push_back(polyline[i]); + for (auto i : polyline) { + points_.push_back(i); } } @@ -73,8 +73,8 @@ void SweepContext::InitTriangulation() double ymax(points_[0]->y), ymin(points_[0]->y); // Calculate bounds. - for (unsigned int i = 0; i < points_.size(); i++) { - Point& p = *points_[i]; + for (auto& point : points_) { + Point& p = *point; if (p.x > xmax) xmax = p.x; if (p.x < xmin) @@ -87,8 +87,8 @@ void SweepContext::InitTriangulation() double dx = kAlpha * (xmax - xmin); double dy = kAlpha * (ymax - ymin); - head_ = new Point(xmax + dx, ymin - dy); - tail_ = new Point(xmin - dx, ymin - dy); + head_ = new Point(xmin - dx, ymin - dy); + tail_ = new Point(xmax + dx, ymin - dy); // Sort points along y-axis std::sort(points_.begin(), points_.end(), cmp); @@ -114,18 +114,17 @@ void SweepContext::AddToMap(Triangle* triangle) map_.push_back(triangle); } -Node& SweepContext::LocateNode(const Point& point) +Node* SweepContext::LocateNode(const Point& point) { // TODO implement search tree - return *front_->LocateNode(point.x); + return front_->LocateNode(point.x); } -void SweepContext::CreateAdvancingFront(const std::vector& nodes) +void SweepContext::CreateAdvancingFront() { - (void) nodes; // Initial triangle - Triangle* triangle = new Triangle(*points_[0], *tail_, *head_); + Triangle* triangle = new Triangle(*points_[0], *head_, *tail_); map_.push_back(triangle); @@ -172,7 +171,7 @@ void SweepContext::MeshClean(Triangle& triangle) Triangle *t = triangles.back(); triangles.pop_back(); - if (t != NULL && !t->IsInterior()) { + if (t != nullptr && !t->IsInterior()) { t->IsInterior(true); triangles_.push_back(t); for (int i = 0; i < 3; i++) { @@ -195,17 +194,13 @@ SweepContext::~SweepContext() 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 (auto ptr : map_) { + delete ptr; } - for(unsigned int i = 0; i < edge_list.size(); i++) { - delete edge_list[i]; + for (auto& i : edge_list) { + delete i; } - } -} +} // namespace p2t diff --git a/contrib/poly2tri/poly2tri/sweep/sweep_context.h b/contrib/poly2tri/poly2tri/sweep/sweep_context.h index ba0d06581..11d573944 100644 --- a/contrib/poly2tri/poly2tri/sweep/sweep_context.h +++ b/contrib/poly2tri/poly2tri/sweep/sweep_context.h @@ -1,6 +1,6 @@ /* - * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors - * http://code.google.com/p/poly2tri/ + * Poly2Tri Copyright (c) 2009-2022, Poly2Tri Contributors + * https://github.com/jhasse/poly2tri * * All rights reserved. * @@ -29,8 +29,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SWEEP_CONTEXT_H -#define SWEEP_CONTEXT_H +#pragma once #include #include @@ -52,7 +51,7 @@ class SweepContext { public: /// Constructor -SweepContext(const std::vector& polyline); +explicit SweepContext(std::vector polyline); /// Destructor ~SweepContext(); @@ -66,11 +65,11 @@ Point* tail() const; size_t point_count() const; -Node& LocateNode(const Point& point); +Node* LocateNode(const Point& point); void RemoveNode(Node* node); -void CreateAdvancingFront(const std::vector& nodes); +void CreateAdvancingFront(); /// Try to map a node to all sides of this triangle that don't have a neighbor void MapTriangleToNodes(Triangle& t); @@ -103,15 +102,16 @@ struct Basin { double width; bool left_highest; - Basin() : left_node(NULL), bottom_node(NULL), right_node(NULL), width(0.0), left_highest(false) + Basin() + : left_node(nullptr), bottom_node(nullptr), right_node(nullptr), width(0.0), left_highest(false) { } void Clear() { - left_node = NULL; - bottom_node = NULL; - right_node = NULL; + left_node = nullptr; + bottom_node = nullptr; + right_node = nullptr; width = 0.0; left_highest = false; } @@ -182,5 +182,3 @@ inline Point* SweepContext::tail() const } } - -#endif