Removal of not numerically robust libraries "poly2tree" and "polypartition".
Adjustment of GUI/3DBed.cpp,hpp to use the more stable triangulation algoritm derived from SGI glut. Fix of an extremely slow bridging calculation, caused by an extremely slow bridged area detection function, of which the results were never used. Fixes "slicing fails or takes too long #5974"
This commit is contained in:
parent
820c18923b
commit
2e55898d78
@ -242,7 +242,7 @@ include_directories(${LIBDIR})
|
||||
# For generated header files
|
||||
include_directories(${LIBDIR_BIN}/platform)
|
||||
# For libslic3r.h
|
||||
include_directories(${LIBDIR}/clipper ${LIBDIR}/polypartition)
|
||||
include_directories(${LIBDIR}/clipper)
|
||||
|
||||
if(WIN32)
|
||||
add_definitions(-D_USE_MATH_DEFINES -D_WIN32 -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS)
|
||||
|
@ -22,8 +22,6 @@
|
||||
* qhull: libqhull-dev does not contain libqhullcpp => link errors. Until it is fixed, we will use the builtin version. https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=925540
|
||||
* semver: One module C library, author expects to use clib for installation. No packages.
|
||||
* Shiny: no packages
|
||||
* poly2tree: Obsolete, candidate for removal
|
||||
* polypartition: Obsolete, candidate for removal
|
||||
|
||||
## Header only
|
||||
* igl
|
||||
|
@ -9,8 +9,6 @@ add_subdirectory(boost)
|
||||
add_subdirectory(clipper)
|
||||
add_subdirectory(miniz)
|
||||
add_subdirectory(glu-libtess)
|
||||
add_subdirectory(polypartition)
|
||||
add_subdirectory(poly2tri)
|
||||
add_subdirectory(qhull)
|
||||
add_subdirectory(Shiny)
|
||||
add_subdirectory(semver)
|
||||
|
@ -207,6 +207,62 @@ std::vector<double> BridgeDetector::bridge_direction_candidates() const
|
||||
return angles;
|
||||
}
|
||||
|
||||
/*
|
||||
static void get_trapezoids(const ExPolygon &expoly, Polygons* polygons) const
|
||||
{
|
||||
ExPolygons expp;
|
||||
expp.push_back(expoly);
|
||||
boost::polygon::get_trapezoids(*polygons, expp);
|
||||
}
|
||||
|
||||
void ExPolygon::get_trapezoids(ExPolygon clone, Polygons* polygons, double angle) const
|
||||
{
|
||||
clone.rotate(PI/2 - angle, Point(0,0));
|
||||
clone.get_trapezoids(polygons);
|
||||
for (Polygons::iterator polygon = polygons->begin(); polygon != polygons->end(); ++polygon)
|
||||
polygon->rotate(-(PI/2 - angle), Point(0,0));
|
||||
}
|
||||
*/
|
||||
|
||||
// This algorithm may return more trapezoids than necessary
|
||||
// (i.e. it may break a single trapezoid in several because
|
||||
// other parts of the object have x coordinates in the middle)
|
||||
static void get_trapezoids2(const ExPolygon &expoly, Polygons* polygons)
|
||||
{
|
||||
Polygons src_polygons = to_polygons(expoly);
|
||||
// get all points of this ExPolygon
|
||||
const Points pp = to_points(src_polygons);
|
||||
|
||||
// build our bounding box
|
||||
BoundingBox bb(pp);
|
||||
|
||||
// get all x coordinates
|
||||
std::vector<coord_t> xx;
|
||||
xx.reserve(pp.size());
|
||||
for (Points::const_iterator p = pp.begin(); p != pp.end(); ++p)
|
||||
xx.push_back(p->x());
|
||||
std::sort(xx.begin(), xx.end());
|
||||
|
||||
// find trapezoids by looping from first to next-to-last coordinate
|
||||
for (std::vector<coord_t>::const_iterator x = xx.begin(); x != xx.end()-1; ++x) {
|
||||
coord_t next_x = *(x + 1);
|
||||
if (*x != next_x)
|
||||
// intersect with rectangle
|
||||
// append results to return value
|
||||
polygons_append(*polygons, intersection({ { { *x, bb.min.y() }, { next_x, bb.min.y() }, { next_x, bb.max.y() }, { *x, bb.max.y() } } }, src_polygons));
|
||||
}
|
||||
}
|
||||
|
||||
static void get_trapezoids2(const ExPolygon &expoly, Polygons* polygons, double angle)
|
||||
{
|
||||
ExPolygon clone = expoly;
|
||||
clone.rotate(PI/2 - angle, Point(0,0));
|
||||
get_trapezoids2(clone, polygons);
|
||||
for (Polygon &polygon : *polygons)
|
||||
polygon.rotate(-(PI/2 - angle), Point(0,0));
|
||||
}
|
||||
|
||||
// Coverage is currently only used by the unit tests. It is extremely slow and unreliable!
|
||||
Polygons BridgeDetector::coverage(double angle) const
|
||||
{
|
||||
if (angle == -1)
|
||||
@ -228,7 +284,7 @@ Polygons BridgeDetector::coverage(double angle) const
|
||||
for (ExPolygon &expoly : offset_ex(expolygon, 0.5f * float(this->spacing))) {
|
||||
// Compute trapezoids according to a vertical orientation
|
||||
Polygons trapezoids;
|
||||
expoly.get_trapezoids2(&trapezoids, PI/2.0);
|
||||
get_trapezoids2(expoly, &trapezoids, PI/2.0);
|
||||
for (const Polygon &trapezoid : trapezoids) {
|
||||
// not nice, we need a more robust non-numeric check
|
||||
size_t n_supported = 0;
|
||||
|
@ -32,6 +32,7 @@ public:
|
||||
BridgeDetector(const ExPolygons &_expolygons, const ExPolygons &_lower_slices, coord_t _extrusion_width);
|
||||
// If bridge_direction_override != 0, then the angle is used instead of auto-detect.
|
||||
bool detect_angle(double bridge_direction_override = 0.);
|
||||
// Coverage is currently only used by the unit tests. It is extremely slow and unreliable!
|
||||
Polygons coverage(double angle = -1) const;
|
||||
void unsupported_edges(double angle, Polylines* unsupported) const;
|
||||
Polylines unsupported_edges(double angle = -1) const;
|
||||
|
@ -309,8 +309,6 @@ target_link_libraries(libslic3r
|
||||
nowide
|
||||
${EXPAT_LIBRARIES}
|
||||
glu-libtess
|
||||
polypartition
|
||||
poly2tri
|
||||
qhull
|
||||
semver
|
||||
TBB::tbb
|
||||
|
@ -6,8 +6,6 @@
|
||||
#include "Line.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "SVG.hpp"
|
||||
#include "polypartition.h"
|
||||
#include "poly2tri/poly2tri.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <list>
|
||||
@ -318,284 +316,6 @@ ExPolygon::medial_axis(double max_width, double min_width, Polylines* polylines)
|
||||
polylines->insert(polylines->end(), tp.begin(), tp.end());
|
||||
}
|
||||
|
||||
/*
|
||||
void ExPolygon::get_trapezoids(Polygons* polygons) const
|
||||
{
|
||||
ExPolygons expp;
|
||||
expp.push_back(*this);
|
||||
boost::polygon::get_trapezoids(*polygons, expp);
|
||||
}
|
||||
|
||||
void ExPolygon::get_trapezoids(Polygons* polygons, double angle) const
|
||||
{
|
||||
ExPolygon clone = *this;
|
||||
clone.rotate(PI/2 - angle, Point(0,0));
|
||||
clone.get_trapezoids(polygons);
|
||||
for (Polygons::iterator polygon = polygons->begin(); polygon != polygons->end(); ++polygon)
|
||||
polygon->rotate(-(PI/2 - angle), Point(0,0));
|
||||
}
|
||||
*/
|
||||
|
||||
// This algorithm may return more trapezoids than necessary
|
||||
// (i.e. it may break a single trapezoid in several because
|
||||
// other parts of the object have x coordinates in the middle)
|
||||
void ExPolygon::get_trapezoids2(Polygons* polygons) const
|
||||
{
|
||||
// get all points of this ExPolygon
|
||||
Points pp = *this;
|
||||
|
||||
// build our bounding box
|
||||
BoundingBox bb(pp);
|
||||
|
||||
// get all x coordinates
|
||||
std::vector<coord_t> xx;
|
||||
xx.reserve(pp.size());
|
||||
for (Points::const_iterator p = pp.begin(); p != pp.end(); ++p)
|
||||
xx.push_back(p->x());
|
||||
std::sort(xx.begin(), xx.end());
|
||||
|
||||
// find trapezoids by looping from first to next-to-last coordinate
|
||||
for (std::vector<coord_t>::const_iterator x = xx.begin(); x != xx.end()-1; ++x) {
|
||||
coord_t next_x = *(x + 1);
|
||||
if (*x != next_x)
|
||||
// intersect with rectangle
|
||||
// append results to return value
|
||||
polygons_append(*polygons, intersection({ { { *x, bb.min.y() }, { next_x, bb.min.y() }, { next_x, bb.max.y() }, { *x, bb.max.y() } } }, to_polygons(*this)));
|
||||
}
|
||||
}
|
||||
|
||||
void ExPolygon::get_trapezoids2(Polygons* polygons, double angle) const
|
||||
{
|
||||
ExPolygon clone = *this;
|
||||
clone.rotate(PI/2 - angle, Point(0,0));
|
||||
clone.get_trapezoids2(polygons);
|
||||
for (Polygons::iterator polygon = polygons->begin(); polygon != polygons->end(); ++polygon)
|
||||
polygon->rotate(-(PI/2 - angle), Point(0,0));
|
||||
}
|
||||
|
||||
// While this triangulates successfully, it's NOT a constrained triangulation
|
||||
// as it will create more vertices on the boundaries than the ones supplied.
|
||||
void ExPolygon::triangulate(Polygons* polygons) const
|
||||
{
|
||||
// first make trapezoids
|
||||
Polygons trapezoids;
|
||||
this->get_trapezoids2(&trapezoids);
|
||||
|
||||
// then triangulate each trapezoid
|
||||
for (Polygons::iterator polygon = trapezoids.begin(); polygon != trapezoids.end(); ++polygon)
|
||||
polygon->triangulate_convex(polygons);
|
||||
}
|
||||
|
||||
/*
|
||||
void ExPolygon::triangulate_pp(Polygons* polygons) const
|
||||
{
|
||||
// convert polygons
|
||||
std::list<TPPLPoly> input;
|
||||
|
||||
ExPolygons expp = union_ex(simplify_polygons(to_polygons(*this), true));
|
||||
|
||||
for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) {
|
||||
// contour
|
||||
{
|
||||
TPPLPoly p;
|
||||
p.Init(int(ex->contour.points.size()));
|
||||
//printf("%zu\n0\n", ex->contour.points.size());
|
||||
for (const Point &point : ex->contour.points) {
|
||||
size_t i = &point - &ex->contour.points.front();
|
||||
p[i].x = point(0);
|
||||
p[i].y = point(1);
|
||||
//printf("%ld %ld\n", point->x(), point->y());
|
||||
}
|
||||
p.SetHole(false);
|
||||
input.push_back(p);
|
||||
}
|
||||
|
||||
// holes
|
||||
for (Polygons::const_iterator hole = ex->holes.begin(); hole != ex->holes.end(); ++hole) {
|
||||
TPPLPoly p;
|
||||
p.Init(hole->points.size());
|
||||
//printf("%zu\n1\n", hole->points.size());
|
||||
for (const Point &point : hole->points) {
|
||||
size_t i = &point - &hole->points.front();
|
||||
p[i].x = point(0);
|
||||
p[i].y = point(1);
|
||||
//printf("%ld %ld\n", point->x(), point->y());
|
||||
}
|
||||
p.SetHole(true);
|
||||
input.push_back(p);
|
||||
}
|
||||
}
|
||||
|
||||
// perform triangulation
|
||||
std::list<TPPLPoly> output;
|
||||
int res = TPPLPartition().Triangulate_MONO(&input, &output);
|
||||
if (res != 1)
|
||||
throw Slic3r::RuntimeError("Triangulation failed");
|
||||
|
||||
// convert output polygons
|
||||
for (std::list<TPPLPoly>::iterator poly = output.begin(); poly != output.end(); ++poly) {
|
||||
long num_points = poly->GetNumPoints();
|
||||
Polygon p;
|
||||
p.points.resize(num_points);
|
||||
for (long i = 0; i < num_points; ++i) {
|
||||
p.points[i](0) = coord_t((*poly)[i].x);
|
||||
p.points[i](1) = coord_t((*poly)[i].y);
|
||||
}
|
||||
polygons->push_back(p);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
std::list<TPPLPoly> expoly_to_polypartition_input(const ExPolygon &ex)
|
||||
{
|
||||
std::list<TPPLPoly> input;
|
||||
// contour
|
||||
{
|
||||
input.emplace_back();
|
||||
TPPLPoly &p = input.back();
|
||||
p.Init(int(ex.contour.points.size()));
|
||||
for (const Point &point : ex.contour.points) {
|
||||
size_t i = &point - &ex.contour.points.front();
|
||||
p[i].x = point(0);
|
||||
p[i].y = point(1);
|
||||
}
|
||||
p.SetHole(false);
|
||||
}
|
||||
// holes
|
||||
for (const Polygon &hole : ex.holes) {
|
||||
input.emplace_back();
|
||||
TPPLPoly &p = input.back();
|
||||
p.Init(hole.points.size());
|
||||
for (const Point &point : hole.points) {
|
||||
size_t i = &point - &hole.points.front();
|
||||
p[i].x = point(0);
|
||||
p[i].y = point(1);
|
||||
}
|
||||
p.SetHole(true);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
std::list<TPPLPoly> expoly_to_polypartition_input(const ExPolygons &expps)
|
||||
{
|
||||
std::list<TPPLPoly> input;
|
||||
for (const ExPolygon &ex : expps) {
|
||||
// contour
|
||||
{
|
||||
input.emplace_back();
|
||||
TPPLPoly &p = input.back();
|
||||
p.Init(int(ex.contour.points.size()));
|
||||
for (const Point &point : ex.contour.points) {
|
||||
size_t i = &point - &ex.contour.points.front();
|
||||
p[i].x = point(0);
|
||||
p[i].y = point(1);
|
||||
}
|
||||
p.SetHole(false);
|
||||
}
|
||||
// holes
|
||||
for (const Polygon &hole : ex.holes) {
|
||||
input.emplace_back();
|
||||
TPPLPoly &p = input.back();
|
||||
p.Init(hole.points.size());
|
||||
for (const Point &point : hole.points) {
|
||||
size_t i = &point - &hole.points.front();
|
||||
p[i].x = point(0);
|
||||
p[i].y = point(1);
|
||||
}
|
||||
p.SetHole(true);
|
||||
}
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
std::vector<Point> polypartition_output_to_triangles(const std::list<TPPLPoly> &output)
|
||||
{
|
||||
size_t num_triangles = 0;
|
||||
for (const TPPLPoly &poly : output)
|
||||
if (poly.GetNumPoints() >= 3)
|
||||
num_triangles += (size_t)poly.GetNumPoints() - 2;
|
||||
std::vector<Point> triangles;
|
||||
triangles.reserve(triangles.size() + num_triangles * 3);
|
||||
for (const TPPLPoly &poly : output) {
|
||||
long num_points = poly.GetNumPoints();
|
||||
if (num_points >= 3) {
|
||||
const TPPLPoint *pt0 = &poly[0];
|
||||
const TPPLPoint *pt1 = nullptr;
|
||||
const TPPLPoint *pt2 = &poly[1];
|
||||
for (long i = 2; i < num_points; ++ i) {
|
||||
pt1 = pt2;
|
||||
pt2 = &poly[i];
|
||||
triangles.emplace_back(coord_t(pt0->x), coord_t(pt0->y));
|
||||
triangles.emplace_back(coord_t(pt1->x), coord_t(pt1->y));
|
||||
triangles.emplace_back(coord_t(pt2->x), coord_t(pt2->y));
|
||||
}
|
||||
}
|
||||
}
|
||||
return triangles;
|
||||
}
|
||||
|
||||
void ExPolygon::triangulate_pp(Points *triangles) const
|
||||
{
|
||||
ExPolygons expp = union_ex(simplify_polygons(to_polygons(*this), true));
|
||||
std::list<TPPLPoly> input = expoly_to_polypartition_input(expp);
|
||||
// perform triangulation
|
||||
std::list<TPPLPoly> output;
|
||||
int res = TPPLPartition().Triangulate_MONO(&input, &output);
|
||||
// int TPPLPartition::Triangulate_EC(TPPLPolyList *inpolys, TPPLPolyList *triangles) {
|
||||
if (res != 1)
|
||||
throw Slic3r::RuntimeError("Triangulation failed");
|
||||
*triangles = polypartition_output_to_triangles(output);
|
||||
}
|
||||
|
||||
// Uses the Poly2tri library maintained by Jan Niklas Hasse @jhasse // https://github.com/jhasse/poly2tri
|
||||
// See https://github.com/jhasse/poly2tri/blob/master/README.md for the limitations of the library!
|
||||
// No duplicate points are allowed, no very close points, holes must not touch outer contour etc.
|
||||
void ExPolygon::triangulate_p2t(Polygons* polygons) const
|
||||
{
|
||||
ExPolygons expp = simplify_polygons_ex(*this, true);
|
||||
|
||||
for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) {
|
||||
// TODO: prevent duplicate points
|
||||
|
||||
// contour
|
||||
std::vector<p2t::Point*> ContourPoints;
|
||||
for (const Point &pt : ex->contour.points)
|
||||
// We should delete each p2t::Point object
|
||||
ContourPoints.push_back(new p2t::Point(pt(0), pt(1)));
|
||||
p2t::CDT cdt(ContourPoints);
|
||||
|
||||
// holes
|
||||
for (Polygons::const_iterator hole = ex->holes.begin(); hole != ex->holes.end(); ++hole) {
|
||||
std::vector<p2t::Point*> points;
|
||||
for (const Point &pt : hole->points)
|
||||
// will be destructed in SweepContext::~SweepContext
|
||||
points.push_back(new p2t::Point(pt(0), pt(1)));
|
||||
cdt.AddHole(points);
|
||||
}
|
||||
|
||||
// perform triangulation
|
||||
try {
|
||||
cdt.Triangulate();
|
||||
std::vector<p2t::Triangle*> triangles = cdt.GetTriangles();
|
||||
|
||||
for (std::vector<p2t::Triangle*>::const_iterator triangle = triangles.begin(); triangle != triangles.end(); ++triangle) {
|
||||
Polygon p;
|
||||
for (int i = 0; i <= 2; ++i) {
|
||||
p2t::Point* point = (*triangle)->GetPoint(i);
|
||||
p.points.push_back(Point(point->x, point->y));
|
||||
}
|
||||
polygons->push_back(p);
|
||||
}
|
||||
} catch (const Slic3r::RuntimeError & /* err */) {
|
||||
assert(false);
|
||||
// just ignore, don't triangulate
|
||||
}
|
||||
|
||||
for (p2t::Point *ptr : ContourPoints)
|
||||
delete ptr;
|
||||
}
|
||||
}
|
||||
|
||||
Lines ExPolygon::lines() const
|
||||
{
|
||||
Lines lines = this->contour.lines();
|
||||
|
@ -6,9 +6,6 @@
|
||||
#include "Polyline.hpp"
|
||||
#include <vector>
|
||||
|
||||
// polygon class of the polypartition library
|
||||
class TPPLPoly;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class ExPolygon;
|
||||
@ -70,14 +67,6 @@ public:
|
||||
void simplify(double tolerance, ExPolygons* expolygons) const;
|
||||
void medial_axis(double max_width, double min_width, ThickPolylines* polylines) const;
|
||||
void medial_axis(double max_width, double min_width, Polylines* polylines) const;
|
||||
// void get_trapezoids(Polygons* polygons) const;
|
||||
// void get_trapezoids(Polygons* polygons, double angle) const;
|
||||
void get_trapezoids2(Polygons* polygons) const;
|
||||
void get_trapezoids2(Polygons* polygons, double angle) const;
|
||||
void triangulate(Polygons* polygons) const;
|
||||
// Triangulate into triples of points.
|
||||
void triangulate_pp(Points *triangles) const;
|
||||
void triangulate_p2t(Polygons* polygons) const;
|
||||
Lines lines() const;
|
||||
|
||||
// Number of contours (outer contour with holes).
|
||||
@ -349,10 +338,6 @@ extern std::vector<BoundingBox> get_extents_vector(const ExPolygons &polygons);
|
||||
extern bool remove_sticks(ExPolygon &poly);
|
||||
extern void keep_largest_contour_only(ExPolygons &polygons);
|
||||
|
||||
extern std::list<TPPLPoly> expoly_to_polypartition_input(const ExPolygons &expp);
|
||||
extern std::list<TPPLPoly> expoly_to_polypartition_input(const ExPolygon &ex);
|
||||
extern std::vector<Point> polypartition_output_to_triangles(const std::list<TPPLPoly> &output);
|
||||
|
||||
inline double area(const ExPolygons &polys)
|
||||
{
|
||||
double s = 0.;
|
||||
|
@ -46,7 +46,7 @@ public:
|
||||
|
||||
// collection of expolygons representing the bridged areas (thus not
|
||||
// needing support material)
|
||||
Polygons bridged;
|
||||
// Polygons bridged;
|
||||
|
||||
// collection of polylines representing the unsupported bridge edges
|
||||
Polylines unsupported_bridge_edges;
|
||||
|
@ -278,7 +278,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
|
||||
if (bd.detect_angle(custom_angle)) {
|
||||
bridges[idx_last].bridge_angle = bd.angle;
|
||||
if (this->layer()->object()->config().support_material) {
|
||||
polygons_append(this->bridged, bd.coverage());
|
||||
// polygons_append(this->bridged, bd.coverage());
|
||||
append(this->unsupported_bridge_edges, bd.unsupported_edges());
|
||||
}
|
||||
} else if (custom_angle > 0) {
|
||||
|
@ -1,17 +0,0 @@
|
||||
project(poly2tri)
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
|
||||
add_library(poly2tri STATIC
|
||||
common/shapes.cc
|
||||
common/shapes.h
|
||||
common/utils.h
|
||||
poly2tri.h
|
||||
sweep/advancing_front.cc
|
||||
sweep/advancing_front.h
|
||||
sweep/cdt.cc
|
||||
sweep/cdt.h
|
||||
sweep/sweep.cc
|
||||
sweep/sweep.h
|
||||
sweep/sweep_context.cc
|
||||
sweep/sweep_context.h
|
||||
)
|
@ -1,368 +0,0 @@
|
||||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2018, 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.
|
||||
*/
|
||||
#include "shapes.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
namespace p2t {
|
||||
|
||||
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;
|
||||
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(const 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, const Point& p)
|
||||
{
|
||||
Point *cw = t.PointCW(p);
|
||||
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);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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(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(const 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);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// The point counter-clockwise to given point
|
||||
Point* Triangle::PointCCW(const 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);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// The neighbor clockwise to given point
|
||||
Triangle* Triangle::NeighborCW(const 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(const Point& point)
|
||||
{
|
||||
if (&point == points_[0]) {
|
||||
return neighbors_[2];
|
||||
} else if (&point == points_[1]) {
|
||||
return neighbors_[0];
|
||||
}
|
||||
return neighbors_[1];
|
||||
}
|
||||
|
||||
bool Triangle::GetConstrainedEdgeCCW(const 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(const 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(const 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(const 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(const 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(const 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(const 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(const 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(const Point& opoint)
|
||||
{
|
||||
if (&opoint == points_[0]) {
|
||||
return *neighbors_[0];
|
||||
} else if (&opoint == points_[1]) {
|
||||
return *neighbors_[1];
|
||||
}
|
||||
return *neighbors_[2];
|
||||
}
|
||||
|
||||
void Triangle::DebugPrint()
|
||||
{
|
||||
std::cout << *points_[0] << " " << *points_[1] << " " << *points_[2] << std::endl;
|
||||
}
|
||||
|
||||
}
|
@ -1,325 +0,0 @@
|
||||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2018, 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.
|
||||
*/
|
||||
|
||||
// Include guard
|
||||
#ifndef SHAPES_H
|
||||
#define SHAPES_H
|
||||
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
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*> 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()
|
||||
{
|
||||
const double len = Length();
|
||||
x /= len;
|
||||
y /= len;
|
||||
return len;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream&, const Point&);
|
||||
|
||||
// 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 (std::abs(p1.y - p2.y) < 1e-10) {
|
||||
if (p1.x > p2.x) {
|
||||
q = &p1;
|
||||
p = &p2;
|
||||
} else if (std::abs(p1.x - p2.x) < 1e-10) {
|
||||
// Repeat points
|
||||
throw std::runtime_error("Edge::Edge: p1 == p2");
|
||||
}
|
||||
}
|
||||
|
||||
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(int index);
|
||||
Point* PointCW(const Point& point);
|
||||
Point* PointCCW(const Point& point);
|
||||
Point* OppositePoint(Triangle& t, const Point& p);
|
||||
|
||||
Triangle* GetNeighbor(int index);
|
||||
void MarkNeighbor(Point* p1, Point* p2, Triangle* t);
|
||||
void MarkNeighbor(Triangle& t);
|
||||
|
||||
void MarkConstrainedEdge(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(const Point& point);
|
||||
Triangle* NeighborCCW(const Point& point);
|
||||
bool GetConstrainedEdgeCCW(const Point& p);
|
||||
bool GetConstrainedEdgeCW(const Point& p);
|
||||
void SetConstrainedEdgeCCW(const Point& p, bool ce);
|
||||
void SetConstrainedEdgeCW(const Point& p, bool ce);
|
||||
bool GetDelunayEdgeCCW(const Point& p);
|
||||
bool GetDelunayEdgeCW(const Point& p);
|
||||
void SetDelunayEdgeCCW(const Point& p, bool e);
|
||||
void SetDelunayEdgeCW(const Point& p, bool e);
|
||||
|
||||
bool Contains(const Point* p);
|
||||
bool Contains(const Edge& e);
|
||||
bool Contains(const Point* p, const 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(const Triangle *triangle);
|
||||
void ClearNeighbors();
|
||||
void ClearDelunayEdges();
|
||||
|
||||
inline bool IsInterior();
|
||||
inline void IsInterior(bool b);
|
||||
|
||||
Triangle& NeighborAcross(const 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(double s, const Point& a)
|
||||
{
|
||||
return Point(-s * a.y, s * a.x);
|
||||
}
|
||||
|
||||
inline Point* Triangle::GetPoint(int index)
|
||||
{
|
||||
return points_[index];
|
||||
}
|
||||
|
||||
inline Triangle* Triangle::GetNeighbor(int index)
|
||||
{
|
||||
return neighbors_[index];
|
||||
}
|
||||
|
||||
inline bool Triangle::Contains(const 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(const Point* p, const Point* q)
|
||||
{
|
||||
return Contains(p) && Contains(q);
|
||||
}
|
||||
|
||||
inline bool Triangle::IsInterior()
|
||||
{
|
||||
return interior_;
|
||||
}
|
||||
|
||||
inline void Triangle::IsInterior(bool b)
|
||||
{
|
||||
interior_ = b;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,131 +0,0 @@
|
||||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2018, 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.
|
||||
*/
|
||||
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
|
||||
// Otherwise #defines like M_PI are undeclared under Visual Studio
|
||||
#ifndef _USE_MATH_DEFINES
|
||||
#define _USE_MATH_DEFINES
|
||||
#endif /* _USE_MATH_DEFINES */
|
||||
|
||||
#include "shapes.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <exception>
|
||||
|
||||
// C99 removes M_PI from math.h
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846264338327
|
||||
#endif
|
||||
|
||||
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<br>
|
||||
* Positive if CCW<br>
|
||||
* Negative if CW<br>
|
||||
* 0 if collinear<br>
|
||||
* <pre>
|
||||
* A[P1,P2,P3] = (x1*y2 - y1*x2) + (x2*y3 - y2*x3) + (x3*y1 - y3*x1)
|
||||
* = (x1-x3)*(y2-y3) - (y1-y3)*(x2-x3)
|
||||
* </pre>
|
||||
*/
|
||||
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) {
|
||||
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(const Point& pa, const Point& pb, const Point& pc, const 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
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2018, 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.
|
||||
*/
|
||||
|
||||
#ifndef POLY2TRI_H
|
||||
#define POLY2TRI_H
|
||||
|
||||
#include "common/shapes.h"
|
||||
#include "sweep/cdt.h"
|
||||
|
||||
#endif
|
@ -1,110 +0,0 @@
|
||||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2018, 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.
|
||||
*/
|
||||
#include "advancing_front.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace p2t {
|
||||
|
||||
AdvancingFront::AdvancingFront(Node& head, Node& tail)
|
||||
{
|
||||
head_ = &head;
|
||||
tail_ = &tail;
|
||||
search_node_ = &head;
|
||||
}
|
||||
|
||||
Node* AdvancingFront::LocateNode(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(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()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
@ -1,118 +0,0 @@
|
||||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2018, 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.
|
||||
*/
|
||||
|
||||
#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(double x);
|
||||
|
||||
Node* LocatePoint(const Point* point);
|
||||
|
||||
private:
|
||||
|
||||
Node* head_, *tail_, *search_node_;
|
||||
|
||||
Node* FindSearchNode(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
|
@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2018, 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.
|
||||
*/
|
||||
#include "cdt.h"
|
||||
|
||||
namespace p2t {
|
||||
|
||||
CDT::CDT(const std::vector<Point*>& polyline)
|
||||
{
|
||||
sweep_context_ = new SweepContext(polyline);
|
||||
sweep_ = new Sweep;
|
||||
}
|
||||
|
||||
void CDT::AddHole(const std::vector<Point*>& polyline)
|
||||
{
|
||||
sweep_context_->AddHole(polyline);
|
||||
}
|
||||
|
||||
void CDT::AddPoint(Point* point) {
|
||||
sweep_context_->AddPoint(point);
|
||||
}
|
||||
|
||||
void CDT::Triangulate()
|
||||
{
|
||||
sweep_->Triangulate(*sweep_context_);
|
||||
}
|
||||
|
||||
std::vector<p2t::Triangle*> CDT::GetTriangles()
|
||||
{
|
||||
return sweep_context_->GetTriangles();
|
||||
}
|
||||
|
||||
std::list<p2t::Triangle*> CDT::GetMap()
|
||||
{
|
||||
return sweep_context_->GetMap();
|
||||
}
|
||||
|
||||
CDT::~CDT()
|
||||
{
|
||||
delete sweep_context_;
|
||||
delete sweep_;
|
||||
}
|
||||
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2018, 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.
|
||||
*/
|
||||
|
||||
#ifndef CDT_H
|
||||
#define CDT_H
|
||||
|
||||
#include "advancing_front.h"
|
||||
#include "sweep_context.h"
|
||||
#include "sweep.h"
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Mason Green <mason.green@gmail.com>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace p2t {
|
||||
|
||||
class CDT
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor - add polyline with non repeating points
|
||||
*
|
||||
* @param polyline
|
||||
*/
|
||||
CDT(const std::vector<Point*>& polyline);
|
||||
|
||||
/**
|
||||
* Destructor - clean up memory
|
||||
*/
|
||||
~CDT();
|
||||
|
||||
/**
|
||||
* Add a hole
|
||||
*
|
||||
* @param polyline
|
||||
*/
|
||||
void AddHole(const std::vector<Point*>& 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<Triangle*> GetTriangles();
|
||||
|
||||
/**
|
||||
* Get triangle map
|
||||
*/
|
||||
std::list<Triangle*> GetMap();
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Internals
|
||||
*/
|
||||
|
||||
SweepContext* sweep_context_;
|
||||
Sweep* sweep_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,796 +0,0 @@
|
||||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2018, 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.
|
||||
*/
|
||||
#include "sweep.h"
|
||||
#include "sweep_context.h"
|
||||
#include "advancing_front.h"
|
||||
#include "../common/utils.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace p2t {
|
||||
|
||||
// Triangulate simple polygon with holes
|
||||
void Sweep::Triangulate(SweepContext& tcx)
|
||||
{
|
||||
tcx.InitTriangulation();
|
||||
tcx.CreateAdvancingFront();
|
||||
// Sweep points; build mesh
|
||||
SweepPoints(tcx);
|
||||
// Clean up
|
||||
FinalizationPolygon(tcx);
|
||||
}
|
||||
|
||||
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 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)
|
||||
{
|
||||
const 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) {
|
||||
const double angle = BasinAngle(n);
|
||||
if (angle < PI_3div4) {
|
||||
FillBasin(tcx, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// True if HoleAngle exceeds 90 degrees.
|
||||
bool Sweep::LargeHole_DontFill(const Node* node) const {
|
||||
|
||||
const Node* nextNode = node->next;
|
||||
const Node* prevNode = node->prev;
|
||||
if (!AngleExceeds90Degrees(node->point, nextNode->point, prevNode->point))
|
||||
return false;
|
||||
|
||||
// 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))
|
||||
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))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
bool Sweep::AngleExceedsPlus90DegreesOrIsNegative(const Point* origin, const Point* pa, const Point* pb) const {
|
||||
const double angle = Angle(origin, pa, pb);
|
||||
return (angle > PI_div2) || (angle < 0);
|
||||
}
|
||||
|
||||
double Sweep::Angle(const Point* origin, const Point* pa, const Point* pb) const {
|
||||
/* 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
|
||||
*/
|
||||
const double px = origin->x;
|
||||
const double py = origin->y;
|
||||
const double ax = pa->x- px;
|
||||
const double ay = pa->y - py;
|
||||
const double bx = pb->x - px;
|
||||
const double by = pb->y - py;
|
||||
const double x = ax * by - ay * bx;
|
||||
const double y = ax * bx + ay * by;
|
||||
return atan2(x, y);
|
||||
}
|
||||
|
||||
double Sweep::BasinAngle(const Node& node) const
|
||||
{
|
||||
const double ax = node.point->x - node.next->next->point->x;
|
||||
const double ay = node.point->y - node.next->next->point->y;
|
||||
return atan2(ay, ax);
|
||||
}
|
||||
|
||||
double Sweep::HoleAngle(const Node& node) const
|
||||
{
|
||||
/* 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
|
||||
*/
|
||||
const double ax = node.next->point->x - node.point->x;
|
||||
const double ay = node.next->point->y - node.point->y;
|
||||
const double bx = node.prev->point->x - node.point->x;
|
||||
const 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(const Point& pa, const Point& pb, const Point& pc, const Point& pd) const
|
||||
{
|
||||
const double adx = pa.x - pd.x;
|
||||
const double ady = pa.y - pd.y;
|
||||
const double bdx = pb.x - pd.x;
|
||||
const double bdy = pb.y - pd.y;
|
||||
|
||||
const double adxbdy = adx * bdy;
|
||||
const double bdxady = bdx * ady;
|
||||
const double oabd = adxbdy - bdxady;
|
||||
|
||||
if (oabd <= 0)
|
||||
return false;
|
||||
|
||||
const double cdx = pc.x - pd.x;
|
||||
const double cdy = pc.y - pd.y;
|
||||
|
||||
const double cdxady = cdx * ady;
|
||||
const double adxcdy = adx * cdy;
|
||||
const double ocad = cdxady - adxcdy;
|
||||
|
||||
if (ocad <= 0)
|
||||
return false;
|
||||
|
||||
const double bdxcdy = bdx * cdy;
|
||||
const double cdxbdy = cdx * bdy;
|
||||
|
||||
const double alift = adx * adx + ady * ady;
|
||||
const double blift = bdx * bdx + bdy * bdy;
|
||||
const double clift = cdx * cdx + cdy * cdy;
|
||||
|
||||
const double det = alift * (bdxcdy - cdxbdy) + blift * ocad + clift * oabd;
|
||||
|
||||
return det > 0;
|
||||
}
|
||||
|
||||
void Sweep::RotateTrianglePair(Triangle& t, Point& p, Triangle& ot, Point& op) const
|
||||
{
|
||||
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 (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);
|
||||
}
|
||||
throw std::runtime_error("[Unsupported] Opposing point on constrained edge");
|
||||
}
|
||||
|
||||
void Sweep::FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& flip_triangle,
|
||||
Triangle& t, Point& p)
|
||||
{
|
||||
Triangle& ot = t.NeighborAcross(p);
|
||||
Point& op = *ot.OppositePoint(t, p);
|
||||
|
||||
if (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(size_t i = 0; i < nodes_.size(); i++) {
|
||||
delete nodes_[i];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,285 +0,0 @@
|
||||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2018, 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.
|
||||
*/
|
||||
/**
|
||||
* Sweep-line, Constrained Delauney Triangulation (CDT) See: Domiter, V. and
|
||||
* Zalik, B.(2008)'Sweep-line algorithm for constrained Delaunay triangulation',
|
||||
* International Journal of Geographical Information Science
|
||||
*
|
||||
* "FlipScan" Constrained Edge Algorithm invented by Thomas ?hl?n, thahlen@gmail.com
|
||||
*/
|
||||
|
||||
#ifndef SWEEP_H
|
||||
#define SWEEP_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
* <b>Requirement</b>:<br>
|
||||
* 1. a,b and c form a triangle.<br>
|
||||
* 2. a and d is know to be on opposite side of bc<br>
|
||||
* <pre>
|
||||
* a
|
||||
* +
|
||||
* / \
|
||||
* / \
|
||||
* b/ \c
|
||||
* +-------+
|
||||
* / d \
|
||||
* / \
|
||||
* </pre>
|
||||
* <b>Fact</b>: d has to be in area B to have a chance to be inside the circle formed by
|
||||
* a,b and c<br>
|
||||
* d is outside B if orient2d(a,b,d) or orient2d(c,a,d) is CW<br>
|
||||
* 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(const Point& pa, const Point& pb, const Point& pc, const Point& pd) const;
|
||||
|
||||
/**
|
||||
* Rotates a triangle pair one vertex CW
|
||||
*<pre>
|
||||
* n2 n2
|
||||
* P +-----+ P +-----+
|
||||
* | t /| |\ t |
|
||||
* | / | | \ |
|
||||
* n1| / |n3 n1| \ |n3
|
||||
* | / | after CW | \ |
|
||||
* |/ oT | | oT \|
|
||||
* +-----+ oP +-----+
|
||||
* n4 n4
|
||||
* </pre>
|
||||
*/
|
||||
void RotateTrianglePair(Triangle& t, Point& p, Triangle& ot, Point& op) const;
|
||||
|
||||
/**
|
||||
* 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(const Node* node) 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;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param node - middle node
|
||||
* @return the angle between 3 front nodes
|
||||
*/
|
||||
double HoleAngle(const Node& node) const;
|
||||
|
||||
/**
|
||||
* The basin angle is decided against the horizontal line [1,0]
|
||||
*/
|
||||
double BasinAngle(const Node& node) const;
|
||||
|
||||
/**
|
||||
* Fills a basin that has formed on the Advancing Front to the right
|
||||
* of given node.<br>
|
||||
* 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<br>
|
||||
* 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<Node*> nodes_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,210 +0,0 @@
|
||||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2018, 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.
|
||||
*/
|
||||
#include "sweep_context.h"
|
||||
#include <algorithm>
|
||||
#include "advancing_front.h"
|
||||
|
||||
namespace p2t {
|
||||
|
||||
SweepContext::SweepContext(const std::vector<Point*>& polyline) : points_(polyline),
|
||||
front_(0),
|
||||
head_(0),
|
||||
tail_(0),
|
||||
af_head_(0),
|
||||
af_middle_(0),
|
||||
af_tail_(0)
|
||||
{
|
||||
InitEdges(points_);
|
||||
}
|
||||
|
||||
void SweepContext::AddHole(const std::vector<Point*>& 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<Triangle*> &SweepContext::GetTriangles()
|
||||
{
|
||||
return triangles_;
|
||||
}
|
||||
|
||||
std::list<Triangle*> &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(const std::vector<Point*>& polyline)
|
||||
{
|
||||
size_t num_points = polyline.size();
|
||||
for (size_t i = 0; i < num_points; i++) {
|
||||
size_t j = i < num_points - 1 ? i + 1 : 0;
|
||||
edge_list.push_back(new Edge(*polyline[i], *polyline[j]));
|
||||
}
|
||||
}
|
||||
|
||||
Point* SweepContext::GetPoint(size_t index)
|
||||
{
|
||||
return points_[index];
|
||||
}
|
||||
|
||||
void SweepContext::AddToMap(Triangle* triangle)
|
||||
{
|
||||
map_.push_back(triangle);
|
||||
}
|
||||
|
||||
Node& SweepContext::LocateNode(const Point& point)
|
||||
{
|
||||
// TODO implement search tree
|
||||
return *front_->LocateNode(point.x);
|
||||
}
|
||||
|
||||
void SweepContext::CreateAdvancingFront()
|
||||
{
|
||||
|
||||
// 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<Triangle *> 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<Triangle*> 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];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,186 +0,0 @@
|
||||
/*
|
||||
* Poly2Tri Copyright (c) 2009-2018, 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.
|
||||
*/
|
||||
|
||||
#ifndef SWEEP_CONTEXT_H
|
||||
#define SWEEP_CONTEXT_H
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
|
||||
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(const std::vector<Point*>& polyline);
|
||||
/// Destructor
|
||||
~SweepContext();
|
||||
|
||||
void set_head(Point* p1);
|
||||
|
||||
Point* head() const;
|
||||
|
||||
void set_tail(Point* p1);
|
||||
|
||||
Point* tail() const;
|
||||
|
||||
size_t point_count() const;
|
||||
|
||||
Node& LocateNode(const Point& point);
|
||||
|
||||
void RemoveNode(Node* node);
|
||||
|
||||
void CreateAdvancingFront();
|
||||
|
||||
/// 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(size_t index);
|
||||
|
||||
Point* GetPoints();
|
||||
|
||||
void RemoveFromMap(Triangle* triangle);
|
||||
|
||||
void AddHole(const std::vector<Point*>& polyline);
|
||||
|
||||
void AddPoint(Point* point);
|
||||
|
||||
AdvancingFront* front() const;
|
||||
|
||||
void MeshClean(Triangle& triangle);
|
||||
|
||||
std::vector<Triangle*> &GetTriangles();
|
||||
std::list<Triangle*> &GetMap();
|
||||
|
||||
std::vector<Edge*> 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<Triangle*> triangles_;
|
||||
std::list<Triangle*> map_;
|
||||
std::vector<Point*> 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(const std::vector<Point*>& polyline);
|
||||
|
||||
};
|
||||
|
||||
inline AdvancingFront* SweepContext::front() const
|
||||
{
|
||||
return front_;
|
||||
}
|
||||
|
||||
inline size_t SweepContext::point_count() const
|
||||
{
|
||||
return points_.size();
|
||||
}
|
||||
|
||||
inline void SweepContext::set_head(Point* p1)
|
||||
{
|
||||
head_ = p1;
|
||||
}
|
||||
|
||||
inline Point* SweepContext::head() const
|
||||
{
|
||||
return head_;
|
||||
}
|
||||
|
||||
inline void SweepContext::set_tail(Point* p1)
|
||||
{
|
||||
tail_ = p1;
|
||||
}
|
||||
|
||||
inline Point* SweepContext::tail() const
|
||||
{
|
||||
return tail_;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,7 +0,0 @@
|
||||
project(polypartition)
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
|
||||
add_library(polypartition STATIC
|
||||
polypartition.cpp
|
||||
polypartition.h
|
||||
)
|
File diff suppressed because it is too large
Load Diff
@ -1,379 +0,0 @@
|
||||
//Copyright (C) 2011 by Ivan Fratric
|
||||
//
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
//
|
||||
//The above copyright notice and this permission notice shall be included in
|
||||
//all copies or substantial portions of the Software.
|
||||
//
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
//THE SOFTWARE.
|
||||
|
||||
#ifndef POLYPARTITION_H
|
||||
#define POLYPARTITION_H
|
||||
|
||||
#include <list>
|
||||
#include <set>
|
||||
|
||||
typedef double tppl_float;
|
||||
|
||||
#define TPPL_CCW 1
|
||||
#define TPPL_CW -1
|
||||
|
||||
//2D point structure
|
||||
struct TPPLPoint {
|
||||
tppl_float x;
|
||||
tppl_float y;
|
||||
// User-specified vertex identifier. Note that this isn't used internally
|
||||
// by the library, but will be faithfully copied around.
|
||||
int id;
|
||||
|
||||
TPPLPoint operator + (const TPPLPoint& p) const {
|
||||
TPPLPoint r;
|
||||
r.x = x + p.x;
|
||||
r.y = y + p.y;
|
||||
return r;
|
||||
}
|
||||
|
||||
TPPLPoint operator - (const TPPLPoint& p) const {
|
||||
TPPLPoint r;
|
||||
r.x = x - p.x;
|
||||
r.y = y - p.y;
|
||||
return r;
|
||||
}
|
||||
|
||||
TPPLPoint operator * (const tppl_float f ) const {
|
||||
TPPLPoint r;
|
||||
r.x = x*f;
|
||||
r.y = y*f;
|
||||
return r;
|
||||
}
|
||||
|
||||
TPPLPoint operator / (const tppl_float f ) const {
|
||||
TPPLPoint r;
|
||||
r.x = x/f;
|
||||
r.y = y/f;
|
||||
return r;
|
||||
}
|
||||
|
||||
bool operator==(const TPPLPoint& p) const {
|
||||
if((x == p.x)&&(y==p.y)) return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
bool operator!=(const TPPLPoint& p) const {
|
||||
if((x == p.x)&&(y==p.y)) return false;
|
||||
else return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//Polygon implemented as an array of points with a 'hole' flag
|
||||
class TPPLPoly {
|
||||
protected:
|
||||
|
||||
TPPLPoint *points;
|
||||
long numpoints;
|
||||
bool hole;
|
||||
|
||||
public:
|
||||
|
||||
//constructors/destructors
|
||||
TPPLPoly();
|
||||
~TPPLPoly();
|
||||
|
||||
TPPLPoly(const TPPLPoly &src);
|
||||
TPPLPoly& operator=(const TPPLPoly &src);
|
||||
|
||||
//getters and setters
|
||||
long GetNumPoints() const {
|
||||
return numpoints;
|
||||
}
|
||||
|
||||
bool IsHole() const {
|
||||
return hole;
|
||||
}
|
||||
|
||||
void SetHole(bool hole) {
|
||||
this->hole = hole;
|
||||
}
|
||||
|
||||
TPPLPoint &GetPoint(long i) {
|
||||
return points[i];
|
||||
}
|
||||
|
||||
const TPPLPoint &GetPoint(long i) const {
|
||||
return points[i];
|
||||
}
|
||||
|
||||
TPPLPoint *GetPoints() {
|
||||
return points;
|
||||
}
|
||||
|
||||
TPPLPoint& operator[] (int i) {
|
||||
return points[i];
|
||||
}
|
||||
|
||||
const TPPLPoint& operator[] (int i) const {
|
||||
return points[i];
|
||||
}
|
||||
|
||||
//clears the polygon points
|
||||
void Clear();
|
||||
|
||||
//inits the polygon with numpoints vertices
|
||||
void Init(long numpoints);
|
||||
|
||||
//creates a triangle with points p1,p2,p3
|
||||
void Triangle(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3);
|
||||
|
||||
//inverts the orfer of vertices
|
||||
void Invert();
|
||||
|
||||
//returns the orientation of the polygon
|
||||
//possible values:
|
||||
// TPPL_CCW : polygon vertices are in counter-clockwise order
|
||||
// TPPL_CW : polygon vertices are in clockwise order
|
||||
// 0 : the polygon has no (measurable) area
|
||||
int GetOrientation() const;
|
||||
|
||||
//sets the polygon orientation
|
||||
//orientation can be
|
||||
// TPPL_CCW : sets vertices in counter-clockwise order
|
||||
// TPPL_CW : sets vertices in clockwise order
|
||||
void SetOrientation(int orientation);
|
||||
|
||||
//checks whether a polygon is valid or not
|
||||
inline bool Valid() const { return this->numpoints >= 3; }
|
||||
};
|
||||
|
||||
#ifdef TPPL_ALLOCATOR
|
||||
typedef std::list<TPPLPoly, TPPL_ALLOCATOR(TPPLPoly)> TPPLPolyList;
|
||||
#else
|
||||
typedef std::list<TPPLPoly> TPPLPolyList;
|
||||
#endif
|
||||
|
||||
class TPPLPartition {
|
||||
protected:
|
||||
struct PartitionVertex {
|
||||
bool isActive;
|
||||
bool isConvex;
|
||||
bool isEar;
|
||||
|
||||
TPPLPoint p;
|
||||
tppl_float angle;
|
||||
PartitionVertex *previous;
|
||||
PartitionVertex *next;
|
||||
|
||||
PartitionVertex();
|
||||
};
|
||||
|
||||
struct MonotoneVertex {
|
||||
TPPLPoint p;
|
||||
long previous;
|
||||
long next;
|
||||
};
|
||||
|
||||
class VertexSorter{
|
||||
MonotoneVertex *vertices;
|
||||
public:
|
||||
VertexSorter(MonotoneVertex *v) : vertices(v) {}
|
||||
bool operator() (long index1, long index2);
|
||||
};
|
||||
|
||||
struct Diagonal {
|
||||
long index1;
|
||||
long index2;
|
||||
};
|
||||
|
||||
#ifdef TPPL_ALLOCATOR
|
||||
typedef std::list<Diagonal, TPPL_ALLOCATOR(Diagonal)> DiagonalList;
|
||||
#else
|
||||
typedef std::list<Diagonal> DiagonalList;
|
||||
#endif
|
||||
|
||||
//dynamic programming state for minimum-weight triangulation
|
||||
struct DPState {
|
||||
bool visible;
|
||||
tppl_float weight;
|
||||
long bestvertex;
|
||||
};
|
||||
|
||||
//dynamic programming state for convex partitioning
|
||||
struct DPState2 {
|
||||
bool visible;
|
||||
long weight;
|
||||
DiagonalList pairs;
|
||||
};
|
||||
|
||||
//edge that intersects the scanline
|
||||
struct ScanLineEdge {
|
||||
mutable long index;
|
||||
TPPLPoint p1;
|
||||
TPPLPoint p2;
|
||||
|
||||
//determines if the edge is to the left of another edge
|
||||
bool operator< (const ScanLineEdge & other) const;
|
||||
|
||||
bool IsConvex(const TPPLPoint& p1, const TPPLPoint& p2, const TPPLPoint& p3) const;
|
||||
};
|
||||
|
||||
//standard helper functions
|
||||
bool IsConvex(TPPLPoint& p1, TPPLPoint& p2, TPPLPoint& p3);
|
||||
bool IsReflex(TPPLPoint& p1, TPPLPoint& p2, TPPLPoint& p3);
|
||||
bool IsInside(TPPLPoint& p1, TPPLPoint& p2, TPPLPoint& p3, TPPLPoint &p);
|
||||
|
||||
bool InCone(TPPLPoint &p1, TPPLPoint &p2, TPPLPoint &p3, TPPLPoint &p);
|
||||
bool InCone(PartitionVertex *v, TPPLPoint &p);
|
||||
|
||||
int Intersects(TPPLPoint &p11, TPPLPoint &p12, TPPLPoint &p21, TPPLPoint &p22);
|
||||
|
||||
TPPLPoint Normalize(const TPPLPoint &p);
|
||||
tppl_float Distance(const TPPLPoint &p1, const TPPLPoint &p2);
|
||||
|
||||
//helper functions for Triangulate_EC
|
||||
void UpdateVertexReflexity(PartitionVertex *v);
|
||||
void UpdateVertex(PartitionVertex *v,PartitionVertex *vertices, long numvertices);
|
||||
|
||||
//helper functions for ConvexPartition_OPT
|
||||
void UpdateState(long a, long b, long w, long i, long j, DPState2 **dpstates);
|
||||
void TypeA(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates);
|
||||
void TypeB(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates);
|
||||
|
||||
//helper functions for MonotonePartition
|
||||
bool Below(TPPLPoint &p1, TPPLPoint &p2);
|
||||
void AddDiagonal(MonotoneVertex *vertices, long *numvertices, long index1, long index2,
|
||||
char *vertextypes, std::set<ScanLineEdge>::iterator *edgeTreeIterators,
|
||||
std::set<ScanLineEdge> *edgeTree, long *helpers);
|
||||
|
||||
//triangulates a monotone polygon, used in Triangulate_MONO
|
||||
int TriangulateMonotone(TPPLPoly *inPoly, TPPLPolyList *triangles);
|
||||
|
||||
public:
|
||||
|
||||
//simple heuristic procedure for removing holes from a list of polygons
|
||||
//works by creating a diagonal from the rightmost hole vertex to some visible vertex
|
||||
//time complexity: O(h*(n^2)), h is the number of holes, n is the number of vertices
|
||||
//space complexity: O(n)
|
||||
//params:
|
||||
// inpolys : a list of polygons that can contain holes
|
||||
// vertices of all non-hole polys have to be in counter-clockwise order
|
||||
// vertices of all hole polys have to be in clockwise order
|
||||
// outpolys : a list of polygons without holes
|
||||
//returns 1 on success, 0 on failure
|
||||
int RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys);
|
||||
|
||||
//triangulates a polygon by ear clipping
|
||||
//time complexity O(n^2), n is the number of vertices
|
||||
//space complexity: O(n)
|
||||
//params:
|
||||
// poly : an input polygon to be triangulated
|
||||
// vertices have to be in counter-clockwise order
|
||||
// triangles : a list of triangles (result)
|
||||
//returns 1 on success, 0 on failure
|
||||
int Triangulate_EC(TPPLPoly *poly, TPPLPolyList *triangles);
|
||||
|
||||
//triangulates a list of polygons that may contain holes by ear clipping algorithm
|
||||
//first calls RemoveHoles to get rid of the holes, and then Triangulate_EC for each resulting polygon
|
||||
//time complexity: O(h*(n^2)), h is the number of holes, n is the number of vertices
|
||||
//space complexity: O(n)
|
||||
//params:
|
||||
// inpolys : a list of polygons to be triangulated (can contain holes)
|
||||
// vertices of all non-hole polys have to be in counter-clockwise order
|
||||
// vertices of all hole polys have to be in clockwise order
|
||||
// triangles : a list of triangles (result)
|
||||
//returns 1 on success, 0 on failure
|
||||
int Triangulate_EC(TPPLPolyList *inpolys, TPPLPolyList *triangles);
|
||||
|
||||
//creates an optimal polygon triangulation in terms of minimal edge length
|
||||
//time complexity: O(n^3), n is the number of vertices
|
||||
//space complexity: O(n^2)
|
||||
//params:
|
||||
// poly : an input polygon to be triangulated
|
||||
// vertices have to be in counter-clockwise order
|
||||
// triangles : a list of triangles (result)
|
||||
//returns 1 on success, 0 on failure
|
||||
int Triangulate_OPT(TPPLPoly *poly, TPPLPolyList *triangles);
|
||||
|
||||
//triangulates a polygons by firstly partitioning it into monotone polygons
|
||||
//time complexity: O(n*log(n)), n is the number of vertices
|
||||
//space complexity: O(n)
|
||||
//params:
|
||||
// poly : an input polygon to be triangulated
|
||||
// vertices have to be in counter-clockwise order
|
||||
// triangles : a list of triangles (result)
|
||||
//returns 1 on success, 0 on failure
|
||||
int Triangulate_MONO(TPPLPoly *poly, TPPLPolyList *triangles);
|
||||
|
||||
//triangulates a list of polygons by firstly partitioning them into monotone polygons
|
||||
//time complexity: O(n*log(n)), n is the number of vertices
|
||||
//space complexity: O(n)
|
||||
//params:
|
||||
// inpolys : a list of polygons to be triangulated (can contain holes)
|
||||
// vertices of all non-hole polys have to be in counter-clockwise order
|
||||
// vertices of all hole polys have to be in clockwise order
|
||||
// triangles : a list of triangles (result)
|
||||
//returns 1 on success, 0 on failure
|
||||
int Triangulate_MONO(TPPLPolyList *inpolys, TPPLPolyList *triangles);
|
||||
|
||||
//creates a monotone partition of a list of polygons that can contain holes
|
||||
//time complexity: O(n*log(n)), n is the number of vertices
|
||||
//space complexity: O(n)
|
||||
//params:
|
||||
// inpolys : a list of polygons to be triangulated (can contain holes)
|
||||
// vertices of all non-hole polys have to be in counter-clockwise order
|
||||
// vertices of all hole polys have to be in clockwise order
|
||||
// monotonePolys : a list of monotone polygons (result)
|
||||
//returns 1 on success, 0 on failure
|
||||
int MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monotonePolys);
|
||||
|
||||
//partitions a polygon into convex polygons by using Hertel-Mehlhorn algorithm
|
||||
//the algorithm gives at most four times the number of parts as the optimal algorithm
|
||||
//however, in practice it works much better than that and often gives optimal partition
|
||||
//uses triangulation obtained by ear clipping as intermediate result
|
||||
//time complexity O(n^2), n is the number of vertices
|
||||
//space complexity: O(n)
|
||||
//params:
|
||||
// poly : an input polygon to be partitioned
|
||||
// vertices have to be in counter-clockwise order
|
||||
// parts : resulting list of convex polygons
|
||||
//returns 1 on success, 0 on failure
|
||||
int ConvexPartition_HM(TPPLPoly *poly, TPPLPolyList *parts);
|
||||
|
||||
//partitions a list of polygons into convex parts by using Hertel-Mehlhorn algorithm
|
||||
//the algorithm gives at most four times the number of parts as the optimal algorithm
|
||||
//however, in practice it works much better than that and often gives optimal partition
|
||||
//uses triangulation obtained by ear clipping as intermediate result
|
||||
//time complexity O(n^2), n is the number of vertices
|
||||
//space complexity: O(n)
|
||||
//params:
|
||||
// inpolys : an input list of polygons to be partitioned
|
||||
// vertices of all non-hole polys have to be in counter-clockwise order
|
||||
// vertices of all hole polys have to be in clockwise order
|
||||
// parts : resulting list of convex polygons
|
||||
//returns 1 on success, 0 on failure
|
||||
int ConvexPartition_HM(TPPLPolyList *inpolys, TPPLPolyList *parts);
|
||||
|
||||
//optimal convex partitioning (in terms of number of resulting convex polygons)
|
||||
//using the Keil-Snoeyink algorithm
|
||||
//M. Keil, J. Snoeyink, "On the time bound for convex decomposition of simple polygons", 1998
|
||||
//time complexity O(n^3), n is the number of vertices
|
||||
//space complexity: O(n^3)
|
||||
// poly : an input polygon to be partitioned
|
||||
// vertices have to be in counter-clockwise order
|
||||
// parts : resulting list of convex polygons
|
||||
//returns 1 on success, 0 on failure
|
||||
int ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
@ -6,6 +6,7 @@
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
#include "libslic3r/Geometry.hpp"
|
||||
#include "libslic3r/Tesselate.hpp"
|
||||
|
||||
#include "GUI_App.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
@ -23,59 +24,36 @@ static const float GROUND_Z = -0.02f;
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords)
|
||||
bool GeometryBuffer::set_from_triangles(const std::vector<Vec2f> &triangles, float z)
|
||||
{
|
||||
if (triangles.empty()) {
|
||||
m_vertices.clear();
|
||||
unsigned int v_size = 3 * (unsigned int)triangles.size();
|
||||
|
||||
if (v_size == 0)
|
||||
return false;
|
||||
|
||||
m_vertices = std::vector<Vertex>(v_size, Vertex());
|
||||
|
||||
float min_x = unscale<float>(triangles[0].points[0](0));
|
||||
float min_y = unscale<float>(triangles[0].points[0](1));
|
||||
float max_x = min_x;
|
||||
float max_y = min_y;
|
||||
|
||||
unsigned int v_count = 0;
|
||||
for (const Polygon& t : triangles) {
|
||||
for (unsigned int i = 0; i < 3; ++i) {
|
||||
Vertex& v = m_vertices[v_count];
|
||||
|
||||
const Point& p = t.points[i];
|
||||
float x = unscale<float>(p(0));
|
||||
float y = unscale<float>(p(1));
|
||||
|
||||
v.position[0] = x;
|
||||
v.position[1] = y;
|
||||
v.position[2] = z;
|
||||
|
||||
if (generate_tex_coords) {
|
||||
v.tex_coords[0] = x;
|
||||
v.tex_coords[1] = y;
|
||||
|
||||
min_x = std::min(min_x, x);
|
||||
max_x = std::max(max_x, x);
|
||||
min_y = std::min(min_y, y);
|
||||
max_y = std::max(max_y, y);
|
||||
}
|
||||
|
||||
++v_count;
|
||||
}
|
||||
assert(triangles.size() % 3 == 0);
|
||||
m_vertices = std::vector<Vertex>(triangles.size(), Vertex());
|
||||
|
||||
Vec2f min(unscaled<float>(triangles.front()));
|
||||
Vec2f max = min;
|
||||
|
||||
for (size_t v_count = 0; v_count < triangles.size(); ++ v_count) {
|
||||
const Vec2f &p = triangles[v_count];
|
||||
Vertex &v = m_vertices[v_count];
|
||||
v.position = Vec3f(p.x(), p.y(), z);
|
||||
v.tex_coords = p;
|
||||
min = min.cwiseMin(p).eval();
|
||||
max = max.cwiseMax(p).eval();
|
||||
}
|
||||
|
||||
if (generate_tex_coords) {
|
||||
float size_x = max_x - min_x;
|
||||
float size_y = max_y - min_y;
|
||||
|
||||
if ((size_x != 0.0f) && (size_y != 0.0f)) {
|
||||
float inv_size_x = 1.0f / size_x;
|
||||
float inv_size_y = -1.0f / size_y;
|
||||
Vec2f size = max - min;
|
||||
if (size.x() != 0.f && size.y() != 0.f) {
|
||||
Vec2f inv_size = size.cwiseInverse();
|
||||
inv_size.y() *= -1;
|
||||
for (Vertex& v : m_vertices) {
|
||||
v.tex_coords[0] = (v.tex_coords[0] - min_x) * inv_size_x;
|
||||
v.tex_coords[1] = (v.tex_coords[1] - min_y) * inv_size_y;
|
||||
}
|
||||
v.tex_coords -= min;
|
||||
v.tex_coords.x() *= inv_size.x();
|
||||
v.tex_coords.y() *= inv_size.y();
|
||||
}
|
||||
}
|
||||
|
||||
@ -290,11 +268,8 @@ void Bed3D::calc_bounding_boxes() const
|
||||
|
||||
void Bed3D::calc_triangles(const ExPolygon& poly)
|
||||
{
|
||||
Polygons triangles;
|
||||
poly.triangulate_p2t(&triangles);
|
||||
|
||||
if (!m_triangles.set_from_triangles(triangles, GROUND_Z, true))
|
||||
printf("Unable to create bed triangles\n");
|
||||
if (! m_triangles.set_from_triangles(triangulate_expolygon_2f(poly, NORMALS_UP), GROUND_Z))
|
||||
BOOST_LOG_TRIVIAL(error) << "Unable to create bed triangles";
|
||||
}
|
||||
|
||||
void Bed3D::calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox)
|
||||
@ -321,7 +296,7 @@ void Bed3D::calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox)
|
||||
std::copy(contour_lines.begin(), contour_lines.end(), std::back_inserter(gridlines));
|
||||
|
||||
if (!m_gridlines.set_from_lines(gridlines, GROUND_Z))
|
||||
printf("Unable to create bed grid lines\n");
|
||||
BOOST_LOG_TRIVIAL(error) << "Unable to create bed grid lines\n";
|
||||
}
|
||||
|
||||
|
||||
|
@ -17,20 +17,14 @@ class GeometryBuffer
|
||||
{
|
||||
struct Vertex
|
||||
{
|
||||
float position[3];
|
||||
float tex_coords[2];
|
||||
|
||||
Vertex()
|
||||
{
|
||||
position[0] = 0.0f; position[1] = 0.0f; position[2] = 0.0f;
|
||||
tex_coords[0] = 0.0f; tex_coords[1] = 0.0f;
|
||||
}
|
||||
Vec3f position = Vec3f::Zero();
|
||||
Vec2f tex_coords = Vec2f::Zero();
|
||||
};
|
||||
|
||||
std::vector<Vertex> m_vertices;
|
||||
|
||||
public:
|
||||
bool set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords);
|
||||
bool set_from_triangles(const std::vector<Vec2f> &triangles, float z);
|
||||
bool set_from_lines(const Lines& lines, float z);
|
||||
|
||||
const float* get_vertices_data() const;
|
||||
|
@ -99,8 +99,6 @@ void CopyrightsDialog::fill_entries()
|
||||
"2002 - 2007, Marcelo E.Magallon; "
|
||||
"2002, Lev Povalahev" , "http://glew.sourceforge.net/" },
|
||||
{ "Libigl" , "2013 Alec Jacobson and others" , "https://libigl.github.io/" },
|
||||
{ "Poly2Tri" , "2009-2018, Poly2Tri Contributors" , "https://github.com/jhasse/poly2tri" },
|
||||
{ "PolyPartition" , "2011 Ivan Fratric" , "https://github.com/ivanfratric/polypartition" },
|
||||
{ "Qhull" , "1993-2015 C.B.Barber Arlington and "
|
||||
"University of Minnesota" , "http://qhull.org/" },
|
||||
{ "SemVer" , "2015-2017 Tomas Aparicio" , "https://semver.org/" },
|
||||
|
@ -2,7 +2,6 @@
|
||||
#include "GLCanvas3D.hpp"
|
||||
|
||||
#include "admesh/stl.h"
|
||||
#include "polypartition.h"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/GCode/ThumbnailData.hpp"
|
||||
|
@ -5,7 +5,7 @@ use warnings;
|
||||
|
||||
use List::Util qw(first sum);
|
||||
use Slic3r::XS;
|
||||
use Test::More tests => 31;
|
||||
use Test::More tests => 21;
|
||||
|
||||
use constant PI => 4 * atan2(1, 1);
|
||||
|
||||
@ -105,32 +105,4 @@ is $expolygon->area, 100*100-20*20, 'area';
|
||||
is_deeply $collection->[0]->clone->pp, $collection->[0]->pp, 'clone collection item';
|
||||
}
|
||||
|
||||
{
|
||||
my $expolygon = Slic3r::ExPolygon->new($square);
|
||||
my $polygons = $expolygon->get_trapezoids2(PI/2);
|
||||
is scalar(@$polygons), 1, 'correct number of trapezoids returned';
|
||||
is scalar(@{$polygons->[0]}), 4, 'trapezoid has 4 points';
|
||||
is $polygons->[0]->area, $expolygon->area, 'trapezoid has correct area';
|
||||
}
|
||||
|
||||
{
|
||||
my $polygons = $expolygon->get_trapezoids2(PI/2);
|
||||
is scalar(@$polygons), 4, 'correct number of trapezoids returned';
|
||||
|
||||
# trapezoid polygons might have more than 4 points in case of collinear segments
|
||||
$polygons = [ map @{$_->simplify(1)}, @$polygons ];
|
||||
ok !defined(first { @$_ != 4 } @$polygons), 'all trapezoids have 4 points';
|
||||
|
||||
is scalar(grep { $_->area == 40*100 } @$polygons), 2, 'trapezoids have expected area';
|
||||
is scalar(grep { $_->area == 20*40 } @$polygons), 2, 'trapezoids have expected area';
|
||||
}
|
||||
|
||||
{
|
||||
my $expolygon = Slic3r::ExPolygon->new([ [0,100],[100,0],[200,0],[300,100],[200,200],[100,200] ]);
|
||||
my $polygons = $expolygon->get_trapezoids2(PI/2);
|
||||
is scalar(@$polygons), 3, 'correct number of trapezoids returned';
|
||||
is scalar(grep { $_->area == 100*200/2 } @$polygons), 2, 'trapezoids have expected area';
|
||||
is scalar(grep { $_->area == 100*200 } @$polygons), 1, 'trapezoids have expected area';
|
||||
}
|
||||
|
||||
__END__
|
||||
|
@ -31,10 +31,6 @@
|
||||
Polygons simplify_p(double tolerance);
|
||||
Polylines medial_axis(double max_width, double min_width)
|
||||
%code{% THIS->medial_axis(max_width, min_width, &RETVAL); %};
|
||||
Polygons get_trapezoids2(double angle)
|
||||
%code{% THIS->get_trapezoids2(&RETVAL, angle); %};
|
||||
Polygons triangulate()
|
||||
%code{% THIS->triangulate(&RETVAL); %};
|
||||
%{
|
||||
|
||||
ExPolygon*
|
||||
|
@ -18,8 +18,6 @@
|
||||
%code%{ RETVAL = &THIS->thin_fills; %};
|
||||
Ref<SurfaceCollection> fill_surfaces()
|
||||
%code%{ RETVAL = &THIS->fill_surfaces; %};
|
||||
Polygons bridged()
|
||||
%code%{ RETVAL = THIS->bridged; %};
|
||||
Ref<ExtrusionEntityCollection> perimeters()
|
||||
%code%{ RETVAL = &THIS->perimeters; %};
|
||||
Ref<ExtrusionEntityCollection> fills()
|
||||
|
Loading…
Reference in New Issue
Block a user