mirror of
https://github.com/jhasse/poly2tri.git
synced 2024-11-30 01:03:30 +01:00
Merge pull request #22 from pierre-dejoue/master
Add documentation and code patches for maintenability
This commit is contained in:
commit
e9938d9c68
@ -7,7 +7,8 @@ option(P2T_BUILD_TESTS "Build tests" OFF)
|
|||||||
option(P2T_BUILD_TESTBED "Build the testbed application" OFF)
|
option(P2T_BUILD_TESTBED "Build the testbed application" OFF)
|
||||||
|
|
||||||
file(GLOB SOURCES poly2tri/common/*.cc poly2tri/sweep/*.cc)
|
file(GLOB SOURCES poly2tri/common/*.cc poly2tri/sweep/*.cc)
|
||||||
add_library(poly2tri ${SOURCES})
|
file(GLOB HEADERS poly2tri/*.h poly2tri/common/*.h poly2tri/sweep/*.h)
|
||||||
|
add_library(poly2tri ${SOURCES} ${HEADERS})
|
||||||
target_include_directories(poly2tri INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
|
target_include_directories(poly2tri INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
if(P2T_BUILD_TESTS)
|
if(P2T_BUILD_TESTS)
|
||||||
|
43
README.md
43
README.md
@ -52,6 +52,8 @@ python waf configure
|
|||||||
python waf build
|
python waf build
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Alternatively, the testbed can be built using cmake. See below.
|
||||||
|
|
||||||
Running the Examples
|
Running the Examples
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
@ -59,7 +61,7 @@ Load data points from a file:
|
|||||||
```
|
```
|
||||||
p2t <filename> <center_x> <center_y> <zoom>
|
p2t <filename> <center_x> <center_y> <zoom>
|
||||||
```
|
```
|
||||||
Random distribution of points inside a consrained box:
|
Random distribution of points inside a constrained box:
|
||||||
```
|
```
|
||||||
p2t random <num_points> <box_radius> <zoom>
|
p2t random <num_points> <box_radius> <zoom>
|
||||||
```
|
```
|
||||||
@ -71,3 +73,42 @@ Examples:
|
|||||||
./build/p2t random 10 100 5.0
|
./build/p2t random 10 100 5.0
|
||||||
./build/p2t random 1000 20000 0.025
|
./build/p2t random 1000 20000 0.025
|
||||||
```
|
```
|
||||||
|
|
||||||
|
BUILD WITH CMAKE
|
||||||
|
================
|
||||||
|
|
||||||
|
Build the library
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
```
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake -GNinja
|
||||||
|
cmake --build .
|
||||||
|
```
|
||||||
|
|
||||||
|
Build and run the unit tests
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
```
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake -GNinja -DP2T_BUILD_TESTS=ON
|
||||||
|
cmake --build .
|
||||||
|
ctest --output-on-failure
|
||||||
|
```
|
||||||
|
|
||||||
|
Build the testbed
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
```
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake -GNinja -DP2T_BUILD_TESTBED=ON
|
||||||
|
cmake --build .
|
||||||
|
```
|
||||||
|
|
||||||
|
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)
|
||||||
|
BIN
doc/FlipScan.png
Normal file
BIN
doc/FlipScan.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 223 KiB |
@ -243,6 +243,17 @@ Point* Triangle::PointCCW(const Point& point)
|
|||||||
return nullptr;
|
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
|
// The neighbor clockwise to given point
|
||||||
Triangle* Triangle::NeighborCW(const Point& point)
|
Triangle* Triangle::NeighborCW(const Point& point)
|
||||||
{
|
{
|
||||||
@ -349,23 +360,6 @@ void Triangle::SetDelunayEdgeCW(const Point& p, bool e)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The neighbor across to given point
|
|
||||||
Triangle& Triangle::NeighborAcross(const Point& opoint)
|
|
||||||
{
|
|
||||||
Triangle* neighbor = nullptr;
|
|
||||||
if (&opoint == points_[0]) {
|
|
||||||
neighbor = neighbors_[0];
|
|
||||||
} else if (&opoint == points_[1]) {
|
|
||||||
neighbor = neighbors_[1];
|
|
||||||
} else {
|
|
||||||
neighbor = neighbors_[2];
|
|
||||||
}
|
|
||||||
if (neighbor == nullptr) {
|
|
||||||
throw std::runtime_error("NeighborAcross - null neighbor");
|
|
||||||
}
|
|
||||||
return *neighbor;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Triangle::DebugPrint()
|
void Triangle::DebugPrint()
|
||||||
{
|
{
|
||||||
std::cout << *points_[0] << " " << *points_[1] << " " << *points_[2] << std::endl;
|
std::cout << *points_[0] << " " << *points_[1] << " " << *points_[2] << std::endl;
|
||||||
|
@ -176,6 +176,7 @@ void MarkConstrainedEdge(Point* p, Point* q);
|
|||||||
int Index(const Point* p);
|
int Index(const Point* p);
|
||||||
int EdgeIndex(const Point* p1, const Point* p2);
|
int EdgeIndex(const Point* p1, const Point* p2);
|
||||||
|
|
||||||
|
Triangle* NeighborAcross(const Point& point);
|
||||||
Triangle* NeighborCW(const Point& point);
|
Triangle* NeighborCW(const Point& point);
|
||||||
Triangle* NeighborCCW(const Point& point);
|
Triangle* NeighborCCW(const Point& point);
|
||||||
bool GetConstrainedEdgeCCW(const Point& p);
|
bool GetConstrainedEdgeCCW(const Point& p);
|
||||||
@ -203,8 +204,6 @@ void ClearDelunayEdges();
|
|||||||
inline bool IsInterior();
|
inline bool IsInterior();
|
||||||
inline void IsInterior(bool b);
|
inline void IsInterior(bool b);
|
||||||
|
|
||||||
Triangle& NeighborAcross(const Point& opoint);
|
|
||||||
|
|
||||||
void DebugPrint();
|
void DebugPrint();
|
||||||
|
|
||||||
bool CircumcicleContains(const Point&) const;
|
bool CircumcicleContains(const Point&) const;
|
||||||
|
@ -108,6 +108,9 @@ void Sweep::EdgeEvent(SweepContext& tcx, Edge* edge, Node* node)
|
|||||||
|
|
||||||
void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangle, Point& point)
|
void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangle, Point& point)
|
||||||
{
|
{
|
||||||
|
if (triangle == nullptr) {
|
||||||
|
throw std::runtime_error("EdgeEvent - null triangle");
|
||||||
|
}
|
||||||
if (IsEdgeSideOfTriangle(*triangle, ep, eq)) {
|
if (IsEdgeSideOfTriangle(*triangle, ep, eq)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -120,7 +123,7 @@ void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangl
|
|||||||
// We are modifying the constraint maybe it would be better to
|
// 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
|
// not change the given constraint and just keep a variable for the new constraint
|
||||||
tcx.edge_event.constrained_edge->q = p1;
|
tcx.edge_event.constrained_edge->q = p1;
|
||||||
triangle = &triangle->NeighborAcross(point);
|
triangle = triangle->NeighborAcross(point);
|
||||||
EdgeEvent(tcx, ep, *p1, triangle, *p1);
|
EdgeEvent(tcx, ep, *p1, triangle, *p1);
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error("EdgeEvent - collinear points not supported");
|
throw std::runtime_error("EdgeEvent - collinear points not supported");
|
||||||
@ -136,7 +139,7 @@ void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangl
|
|||||||
// We are modifying the constraint maybe it would be better to
|
// 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
|
// not change the given constraint and just keep a variable for the new constraint
|
||||||
tcx.edge_event.constrained_edge->q = p2;
|
tcx.edge_event.constrained_edge->q = p2;
|
||||||
triangle = &triangle->NeighborAcross(point);
|
triangle = triangle->NeighborAcross(point);
|
||||||
EdgeEvent(tcx, ep, *p2, triangle, *p2);
|
EdgeEvent(tcx, ep, *p2, triangle, *p2);
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error("EdgeEvent - collinear points not supported");
|
throw std::runtime_error("EdgeEvent - collinear points not supported");
|
||||||
@ -155,6 +158,7 @@ void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangl
|
|||||||
EdgeEvent(tcx, ep, eq, triangle, point);
|
EdgeEvent(tcx, ep, eq, triangle, point);
|
||||||
} else {
|
} else {
|
||||||
// This triangle crosses constraint so lets flippin start!
|
// This triangle crosses constraint so lets flippin start!
|
||||||
|
assert(triangle);
|
||||||
FlipEdgeEvent(tcx, ep, eq, triangle, point);
|
FlipEdgeEvent(tcx, ep, eq, triangle, point);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -215,7 +219,6 @@ void Sweep::Fill(SweepContext& tcx, Node& node)
|
|||||||
if (!Legalize(tcx, *triangle)) {
|
if (!Legalize(tcx, *triangle)) {
|
||||||
tcx.MapTriangleToNodes(*triangle);
|
tcx.MapTriangleToNodes(*triangle);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sweep::FillAdvancingFront(SweepContext& tcx, Node& n)
|
void Sweep::FillAdvancingFront(SweepContext& tcx, Node& n)
|
||||||
@ -610,7 +613,6 @@ void Sweep::FillRightConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sweep::FillRightConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
|
void Sweep::FillRightConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
|
||||||
@ -691,12 +693,17 @@ void Sweep::FillLeftConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sweep::FlipEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* t, Point& p)
|
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);
|
Point& op = *ot.OppositePoint(*t, p);
|
||||||
|
|
||||||
if (InScanArea(p, *t->PointCCW(p), *t->PointCW(p), op)) {
|
if (InScanArea(p, *t->PointCCW(p), *t->PointCW(p), op)) {
|
||||||
@ -762,7 +769,11 @@ Point& Sweep::NextFlipPoint(Point& ep, Point& eq, Triangle& ot, Point& op)
|
|||||||
void Sweep::FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& flip_triangle,
|
void Sweep::FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& flip_triangle,
|
||||||
Triangle& t, Point& p)
|
Triangle& t, Point& p)
|
||||||
{
|
{
|
||||||
Triangle& ot = t.NeighborAcross(p);
|
Triangle* ot_ptr = t.NeighborAcross(p);
|
||||||
|
if (ot_ptr == nullptr) {
|
||||||
|
throw std::runtime_error("FlipScanEdgeEvent - null neighbor across");
|
||||||
|
}
|
||||||
|
Triangle& ot = *ot_ptr;
|
||||||
Point& op = *ot.OppositePoint(t, p);
|
Point& op = *ot.OppositePoint(t, p);
|
||||||
|
|
||||||
if (InScanArea(eq, *flip_triangle.PointCCW(eq), *flip_triangle.PointCW(eq), op)) {
|
if (InScanArea(eq, *flip_triangle.PointCCW(eq), *flip_triangle.PointCW(eq), op)) {
|
||||||
@ -790,5 +801,4 @@ Sweep::~Sweep() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // namespace p2t
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
* Zalik, B.(2008)'Sweep-line algorithm for constrained Delaunay triangulation',
|
* Zalik, B.(2008)'Sweep-line algorithm for constrained Delaunay triangulation',
|
||||||
* International Journal of Geographical Information Science
|
* 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
|
#ifndef SWEEP_H
|
||||||
|
@ -87,8 +87,8 @@ void SweepContext::InitTriangulation()
|
|||||||
|
|
||||||
double dx = kAlpha * (xmax - xmin);
|
double dx = kAlpha * (xmax - xmin);
|
||||||
double dy = kAlpha * (ymax - ymin);
|
double dy = kAlpha * (ymax - ymin);
|
||||||
head_ = new Point(xmax + dx, ymin - dy);
|
head_ = new Point(xmin - dx, ymin - dy);
|
||||||
tail_ = new Point(xmin - dx, ymin - dy);
|
tail_ = new Point(xmax + dx, ymin - dy);
|
||||||
|
|
||||||
// Sort points along y-axis
|
// Sort points along y-axis
|
||||||
std::sort(points_.begin(), points_.end(), cmp);
|
std::sort(points_.begin(), points_.end(), cmp);
|
||||||
@ -124,7 +124,7 @@ void SweepContext::CreateAdvancingFront()
|
|||||||
{
|
{
|
||||||
|
|
||||||
// Initial triangle
|
// Initial triangle
|
||||||
Triangle* triangle = new Triangle(*points_[0], *tail_, *head_);
|
Triangle* triangle = new Triangle(*points_[0], *head_, *tail_);
|
||||||
|
|
||||||
map_.push_back(triangle);
|
map_.push_back(triangle);
|
||||||
|
|
||||||
|
@ -28,18 +28,22 @@
|
|||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
#include <cstdlib>
|
#include <poly2tri/poly2tri.h>
|
||||||
#include <GLFW/glfw3.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <fstream>
|
|
||||||
#include <string>
|
|
||||||
#include <sstream>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <iterator>
|
|
||||||
#include <iostream>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
#include "../poly2tri/poly2tri.h"
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <ctime>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <iterator>
|
||||||
|
#include <list>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
using namespace p2t;
|
using namespace p2t;
|
||||||
|
|
||||||
void Init();
|
void Init();
|
||||||
|
@ -3,9 +3,11 @@
|
|||||||
#endif
|
#endif
|
||||||
#define BOOST_TEST_MODULE Poly2triTest
|
#define BOOST_TEST_MODULE Poly2triTest
|
||||||
|
|
||||||
|
#include <poly2tri/poly2tri.h>
|
||||||
|
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
#include <boost/test/unit_test.hpp>
|
#include <boost/test/unit_test.hpp>
|
||||||
#include <poly2tri/poly2tri.h>
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
@ -19,7 +21,7 @@ BOOST_AUTO_TEST_CASE(BasicTest)
|
|||||||
new p2t::Point(1, 1),
|
new p2t::Point(1, 1),
|
||||||
};
|
};
|
||||||
p2t::CDT cdt{ polyline };
|
p2t::CDT cdt{ polyline };
|
||||||
cdt.Triangulate();
|
BOOST_CHECK_NO_THROW(cdt.Triangulate());
|
||||||
const auto result = cdt.GetTriangles();
|
const auto result = cdt.GetTriangles();
|
||||||
BOOST_REQUIRE_EQUAL(result.size(), 1);
|
BOOST_REQUIRE_EQUAL(result.size(), 1);
|
||||||
BOOST_CHECK_EQUAL(*result[0]->GetPoint(0), *polyline[0]);
|
BOOST_CHECK_EQUAL(*result[0]->GetPoint(0), *polyline[0]);
|
||||||
@ -36,7 +38,7 @@ BOOST_AUTO_TEST_CASE(QuadTest)
|
|||||||
std::vector<p2t::Point*> polyline{ new p2t::Point(0, 0), new p2t::Point(0, 1),
|
std::vector<p2t::Point*> polyline{ new p2t::Point(0, 0), new p2t::Point(0, 1),
|
||||||
new p2t::Point(1, 1), new p2t::Point(1, 0) };
|
new p2t::Point(1, 1), new p2t::Point(1, 0) };
|
||||||
p2t::CDT cdt{ polyline };
|
p2t::CDT cdt{ polyline };
|
||||||
cdt.Triangulate();
|
BOOST_CHECK_NO_THROW(cdt.Triangulate());
|
||||||
const auto result = cdt.GetTriangles();
|
const auto result = cdt.GetTriangles();
|
||||||
BOOST_REQUIRE_EQUAL(result.size(), 2);
|
BOOST_REQUIRE_EQUAL(result.size(), 2);
|
||||||
BOOST_CHECK(p2t::IsDelaunay(result));
|
BOOST_CHECK(p2t::IsDelaunay(result));
|
||||||
@ -45,7 +47,7 @@ BOOST_AUTO_TEST_CASE(QuadTest)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(QuadTestThrow)
|
BOOST_AUTO_TEST_CASE(NarrowQuadTest)
|
||||||
{
|
{
|
||||||
// Very narrow quad that demonstrates a failure case during triangulation
|
// Very narrow quad that demonstrates a failure case during triangulation
|
||||||
std::vector<p2t::Point*> polyline {
|
std::vector<p2t::Point*> polyline {
|
||||||
@ -90,7 +92,7 @@ BOOST_AUTO_TEST_CASE(TestbedFilesTest)
|
|||||||
polyline.push_back(new p2t::Point(x, y));
|
polyline.push_back(new p2t::Point(x, y));
|
||||||
}
|
}
|
||||||
p2t::CDT cdt{ polyline };
|
p2t::CDT cdt{ polyline };
|
||||||
cdt.Triangulate();
|
BOOST_CHECK_NO_THROW(cdt.Triangulate());
|
||||||
const auto result = cdt.GetTriangles();
|
const auto result = cdt.GetTriangles();
|
||||||
BOOST_REQUIRE(result.size() * 3 > polyline.size());
|
BOOST_REQUIRE(result.size() * 3 > polyline.size());
|
||||||
BOOST_CHECK_MESSAGE(p2t::IsDelaunay(result), filename + std::to_string(polyline.size()));
|
BOOST_CHECK_MESSAGE(p2t::IsDelaunay(result), filename + std::to_string(polyline.size()));
|
||||||
|
Loading…
Reference in New Issue
Block a user