2014-04-28 18:14:20 +00:00
|
|
|
#include "BoundingBox.hpp"
|
2013-07-16 19:04:14 +00:00
|
|
|
#include "ExPolygon.hpp"
|
2020-09-14 14:27:55 +00:00
|
|
|
#include "Exception.hpp"
|
2014-01-09 18:56:12 +00:00
|
|
|
#include "Geometry.hpp"
|
2013-07-16 19:04:14 +00:00
|
|
|
#include "Polygon.hpp"
|
2014-01-09 16:26:39 +00:00
|
|
|
#include "Line.hpp"
|
2013-11-21 14:12:06 +00:00
|
|
|
#include "ClipperUtils.hpp"
|
2016-09-30 13:23:18 +00:00
|
|
|
#include "SVG.hpp"
|
2014-04-28 18:14:20 +00:00
|
|
|
#include <algorithm>
|
2016-03-20 19:20:32 +00:00
|
|
|
#include <cassert>
|
2014-04-25 10:40:21 +00:00
|
|
|
#include <list>
|
2013-07-16 19:04:14 +00:00
|
|
|
|
|
|
|
namespace Slic3r {
|
|
|
|
|
2014-01-09 16:26:39 +00:00
|
|
|
ExPolygon::operator Points() const
|
|
|
|
{
|
|
|
|
Points points;
|
|
|
|
Polygons pp = *this;
|
|
|
|
for (Polygons::const_iterator poly = pp.begin(); poly != pp.end(); ++poly) {
|
|
|
|
for (Points::const_iterator point = poly->points.begin(); point != poly->points.end(); ++point)
|
|
|
|
points.push_back(*point);
|
|
|
|
}
|
|
|
|
return points;
|
|
|
|
}
|
|
|
|
|
2013-11-21 14:12:06 +00:00
|
|
|
ExPolygon::operator Polygons() const
|
|
|
|
{
|
2016-11-07 21:49:11 +00:00
|
|
|
return to_polygons(*this);
|
2013-11-21 14:12:06 +00:00
|
|
|
}
|
|
|
|
|
2016-09-30 13:23:18 +00:00
|
|
|
ExPolygon::operator Polylines() const
|
|
|
|
{
|
2016-11-07 21:49:11 +00:00
|
|
|
return to_polylines(*this);
|
2016-09-30 13:23:18 +00:00
|
|
|
}
|
|
|
|
|
Removed Point::scale(),translate(),coincides_with(),distance_to(),
distance_to_squared(),perp_distance_to(),negative(),vector_to(),
translate(), distance_to() etc,
replaced with the Eigen equivalents.
2018-08-17 12:14:24 +00:00
|
|
|
void ExPolygon::scale(double factor)
|
2013-07-16 19:04:14 +00:00
|
|
|
{
|
|
|
|
contour.scale(factor);
|
Removed Point::scale(),translate(),coincides_with(),distance_to(),
distance_to_squared(),perp_distance_to(),negative(),vector_to(),
translate(), distance_to() etc,
replaced with the Eigen equivalents.
2018-08-17 12:14:24 +00:00
|
|
|
for (Polygon &hole : holes)
|
|
|
|
hole.scale(factor);
|
2013-07-16 19:04:14 +00:00
|
|
|
}
|
|
|
|
|
2020-11-17 14:18:19 +00:00
|
|
|
void ExPolygon::translate(const Point &p)
|
2013-07-16 19:04:14 +00:00
|
|
|
{
|
2020-11-17 14:18:19 +00:00
|
|
|
contour.translate(p);
|
Removed Point::scale(),translate(),coincides_with(),distance_to(),
distance_to_squared(),perp_distance_to(),negative(),vector_to(),
translate(), distance_to() etc,
replaced with the Eigen equivalents.
2018-08-17 12:14:24 +00:00
|
|
|
for (Polygon &hole : holes)
|
2020-11-17 14:18:19 +00:00
|
|
|
hole.translate(p);
|
2013-07-16 19:04:14 +00:00
|
|
|
}
|
|
|
|
|
Removed Point::scale(),translate(),coincides_with(),distance_to(),
distance_to_squared(),perp_distance_to(),negative(),vector_to(),
translate(), distance_to() etc,
replaced with the Eigen equivalents.
2018-08-17 12:14:24 +00:00
|
|
|
void ExPolygon::rotate(double angle)
|
2016-04-10 17:12:32 +00:00
|
|
|
{
|
|
|
|
contour.rotate(angle);
|
Removed Point::scale(),translate(),coincides_with(),distance_to(),
distance_to_squared(),perp_distance_to(),negative(),vector_to(),
translate(), distance_to() etc,
replaced with the Eigen equivalents.
2018-08-17 12:14:24 +00:00
|
|
|
for (Polygon &hole : holes)
|
|
|
|
hole.rotate(angle);
|
2016-04-10 17:12:32 +00:00
|
|
|
}
|
|
|
|
|
Removed Point::scale(),translate(),coincides_with(),distance_to(),
distance_to_squared(),perp_distance_to(),negative(),vector_to(),
translate(), distance_to() etc,
replaced with the Eigen equivalents.
2018-08-17 12:14:24 +00:00
|
|
|
void ExPolygon::rotate(double angle, const Point ¢er)
|
2013-07-16 19:04:14 +00:00
|
|
|
{
|
|
|
|
contour.rotate(angle, center);
|
Removed Point::scale(),translate(),coincides_with(),distance_to(),
distance_to_squared(),perp_distance_to(),negative(),vector_to(),
translate(), distance_to() etc,
replaced with the Eigen equivalents.
2018-08-17 12:14:24 +00:00
|
|
|
for (Polygon &hole : holes)
|
|
|
|
hole.rotate(angle, center);
|
2013-07-16 19:04:14 +00:00
|
|
|
}
|
|
|
|
|
Removed Point::scale(),translate(),coincides_with(),distance_to(),
distance_to_squared(),perp_distance_to(),negative(),vector_to(),
translate(), distance_to() etc,
replaced with the Eigen equivalents.
2018-08-17 12:14:24 +00:00
|
|
|
double ExPolygon::area() const
|
2013-08-26 20:50:26 +00:00
|
|
|
{
|
|
|
|
double a = this->contour.area();
|
Removed Point::scale(),translate(),coincides_with(),distance_to(),
distance_to_squared(),perp_distance_to(),negative(),vector_to(),
translate(), distance_to() etc,
replaced with the Eigen equivalents.
2018-08-17 12:14:24 +00:00
|
|
|
for (const Polygon &hole : holes)
|
|
|
|
a -= - hole.area(); // holes have negative area
|
2013-08-26 20:50:26 +00:00
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
Removed Point::scale(),translate(),coincides_with(),distance_to(),
distance_to_squared(),perp_distance_to(),negative(),vector_to(),
translate(), distance_to() etc,
replaced with the Eigen equivalents.
2018-08-17 12:14:24 +00:00
|
|
|
bool ExPolygon::is_valid() const
|
2013-08-26 21:27:51 +00:00
|
|
|
{
|
|
|
|
if (!this->contour.is_valid() || !this->contour.is_counter_clockwise()) return false;
|
|
|
|
for (Polygons::const_iterator it = this->holes.begin(); it != this->holes.end(); ++it) {
|
|
|
|
if (!(*it).is_valid() || (*it).is_counter_clockwise()) return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-02-03 14:12:53 +00:00
|
|
|
void ExPolygon::douglas_peucker(double tolerance)
|
|
|
|
{
|
|
|
|
this->contour.douglas_peucker(tolerance);
|
|
|
|
for (Polygon &poly : this->holes)
|
|
|
|
poly.douglas_peucker(tolerance);
|
|
|
|
}
|
|
|
|
|
Removed Point::scale(),translate(),coincides_with(),distance_to(),
distance_to_squared(),perp_distance_to(),negative(),vector_to(),
translate(), distance_to() etc,
replaced with the Eigen equivalents.
2018-08-17 12:14:24 +00:00
|
|
|
bool ExPolygon::contains(const Line &line) const
|
2013-11-21 14:12:06 +00:00
|
|
|
{
|
Removed Point::scale(),translate(),coincides_with(),distance_to(),
distance_to_squared(),perp_distance_to(),negative(),vector_to(),
translate(), distance_to() etc,
replaced with the Eigen equivalents.
2018-08-17 12:14:24 +00:00
|
|
|
return this->contains(Polyline(line.a, line.b));
|
2014-05-13 18:06:01 +00:00
|
|
|
}
|
|
|
|
|
Removed Point::scale(),translate(),coincides_with(),distance_to(),
distance_to_squared(),perp_distance_to(),negative(),vector_to(),
translate(), distance_to() etc,
replaced with the Eigen equivalents.
2018-08-17 12:14:24 +00:00
|
|
|
bool ExPolygon::contains(const Polyline &polyline) const
|
2014-05-13 18:06:01 +00:00
|
|
|
{
|
2016-12-13 18:22:23 +00:00
|
|
|
return diff_pl((Polylines)polyline, *this).empty();
|
2013-11-21 14:12:06 +00:00
|
|
|
}
|
|
|
|
|
Removed Point::scale(),translate(),coincides_with(),distance_to(),
distance_to_squared(),perp_distance_to(),negative(),vector_to(),
translate(), distance_to() etc,
replaced with the Eigen equivalents.
2018-08-17 12:14:24 +00:00
|
|
|
bool ExPolygon::contains(const Polylines &polylines) const
|
2016-09-30 13:23:18 +00:00
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
BoundingBox bbox = get_extents(polylines);
|
|
|
|
bbox.merge(get_extents(*this));
|
2016-10-21 08:18:01 +00:00
|
|
|
SVG svg(debug_out_path("ExPolygon_contains.svg"), bbox);
|
2016-09-30 13:23:18 +00:00
|
|
|
svg.draw(*this);
|
|
|
|
svg.draw_outline(*this);
|
|
|
|
svg.draw(polylines, "blue");
|
|
|
|
#endif
|
2016-12-13 18:22:23 +00:00
|
|
|
Polylines pl_out = diff_pl(polylines, *this);
|
2016-09-30 13:23:18 +00:00
|
|
|
#if 0
|
|
|
|
svg.draw(pl_out, "red");
|
|
|
|
#endif
|
|
|
|
return pl_out.empty();
|
|
|
|
}
|
|
|
|
|
Removed Point::scale(),translate(),coincides_with(),distance_to(),
distance_to_squared(),perp_distance_to(),negative(),vector_to(),
translate(), distance_to() etc,
replaced with the Eigen equivalents.
2018-08-17 12:14:24 +00:00
|
|
|
bool ExPolygon::contains(const Point &point) const
|
2013-11-21 15:21:42 +00:00
|
|
|
{
|
2014-11-23 19:14:13 +00:00
|
|
|
if (!this->contour.contains(point)) return false;
|
2013-11-21 15:21:42 +00:00
|
|
|
for (Polygons::const_iterator it = this->holes.begin(); it != this->holes.end(); ++it) {
|
2014-11-23 19:14:13 +00:00
|
|
|
if (it->contains(point)) return false;
|
2013-11-21 15:21:42 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-01-06 19:52:36 +00:00
|
|
|
// inclusive version of contains() that also checks whether point is on boundaries
|
Removed Point::scale(),translate(),coincides_with(),distance_to(),
distance_to_squared(),perp_distance_to(),negative(),vector_to(),
translate(), distance_to() etc,
replaced with the Eigen equivalents.
2018-08-17 12:14:24 +00:00
|
|
|
bool ExPolygon::contains_b(const Point &point) const
|
2015-01-06 19:52:36 +00:00
|
|
|
{
|
|
|
|
return this->contains(point) || this->has_boundary_point(point);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
ExPolygon::has_boundary_point(const Point &point) const
|
|
|
|
{
|
|
|
|
if (this->contour.has_boundary_point(point)) return true;
|
|
|
|
for (Polygons::const_iterator h = this->holes.begin(); h != this->holes.end(); ++h) {
|
|
|
|
if (h->has_boundary_point(point)) return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-01-22 14:32:36 +00:00
|
|
|
bool ExPolygon::overlaps(const ExPolygon &other) const
|
2016-09-30 13:23:18 +00:00
|
|
|
{
|
2016-09-30 14:11:19 +00:00
|
|
|
#if 0
|
|
|
|
BoundingBox bbox = get_extents(other);
|
|
|
|
bbox.merge(get_extents(*this));
|
|
|
|
static int iRun = 0;
|
2016-10-21 08:18:01 +00:00
|
|
|
SVG svg(debug_out_path("ExPolygon_overlaps-%d.svg", iRun ++), bbox);
|
2016-09-30 14:11:19 +00:00
|
|
|
svg.draw(*this);
|
|
|
|
svg.draw_outline(*this);
|
|
|
|
svg.draw_outline(other, "blue");
|
|
|
|
#endif
|
2016-12-13 18:22:23 +00:00
|
|
|
Polylines pl_out = intersection_pl((Polylines)other, *this);
|
2016-09-30 14:11:19 +00:00
|
|
|
#if 0
|
|
|
|
svg.draw(pl_out, "red");
|
|
|
|
#endif
|
2016-09-30 13:23:18 +00:00
|
|
|
if (! pl_out.empty())
|
|
|
|
return true;
|
2021-01-22 14:32:36 +00:00
|
|
|
//FIXME ExPolygon::overlaps() shall be commutative, it is not!
|
2016-09-30 13:23:18 +00:00
|
|
|
return ! other.contour.points.empty() && this->contains_b(other.contour.points.front());
|
|
|
|
}
|
|
|
|
|
2018-05-18 07:52:09 +00:00
|
|
|
void ExPolygon::simplify_p(double tolerance, Polygons* polygons) const
|
2015-07-06 23:17:31 +00:00
|
|
|
{
|
|
|
|
Polygons pp = this->simplify_p(tolerance);
|
|
|
|
polygons->insert(polygons->end(), pp.begin(), pp.end());
|
|
|
|
}
|
|
|
|
|
2018-05-18 07:52:09 +00:00
|
|
|
Polygons ExPolygon::simplify_p(double tolerance) const
|
2013-11-21 19:25:24 +00:00
|
|
|
{
|
2014-01-16 18:02:50 +00:00
|
|
|
Polygons pp;
|
|
|
|
pp.reserve(this->holes.size() + 1);
|
2013-11-22 01:16:10 +00:00
|
|
|
// contour
|
2015-01-30 17:33:20 +00:00
|
|
|
{
|
|
|
|
Polygon p = this->contour;
|
|
|
|
p.points.push_back(p.points.front());
|
|
|
|
p.points = MultiPoint::_douglas_peucker(p.points, tolerance);
|
|
|
|
p.points.pop_back();
|
2018-05-18 07:52:09 +00:00
|
|
|
pp.emplace_back(std::move(p));
|
2015-01-30 17:33:20 +00:00
|
|
|
}
|
2013-11-22 01:16:10 +00:00
|
|
|
// holes
|
2018-05-18 07:52:09 +00:00
|
|
|
for (Polygon p : this->holes) {
|
2015-01-30 17:33:20 +00:00
|
|
|
p.points.push_back(p.points.front());
|
2013-11-22 01:16:10 +00:00
|
|
|
p.points = MultiPoint::_douglas_peucker(p.points, tolerance);
|
2015-01-30 17:33:20 +00:00
|
|
|
p.points.pop_back();
|
2018-05-18 07:52:09 +00:00
|
|
|
pp.emplace_back(std::move(p));
|
2013-11-22 01:16:10 +00:00
|
|
|
}
|
2018-05-18 07:52:09 +00:00
|
|
|
return simplify_polygons(pp);
|
2013-11-21 19:25:24 +00:00
|
|
|
}
|
|
|
|
|
2018-05-18 07:52:09 +00:00
|
|
|
ExPolygons ExPolygon::simplify(double tolerance) const
|
2013-11-21 19:25:24 +00:00
|
|
|
{
|
2018-05-18 07:52:09 +00:00
|
|
|
return union_ex(this->simplify_p(tolerance));
|
2013-11-21 19:25:24 +00:00
|
|
|
}
|
|
|
|
|
2018-05-18 07:52:09 +00:00
|
|
|
void ExPolygon::simplify(double tolerance, ExPolygons* expolygons) const
|
2013-11-21 19:25:24 +00:00
|
|
|
{
|
2018-05-18 07:52:09 +00:00
|
|
|
append(*expolygons, this->simplify(tolerance));
|
2013-11-21 19:25:24 +00:00
|
|
|
}
|
|
|
|
|
2014-01-09 16:26:39 +00:00
|
|
|
void
|
2016-03-19 14:33:58 +00:00
|
|
|
ExPolygon::medial_axis(double max_width, double min_width, ThickPolylines* polylines) const
|
2014-01-09 16:26:39 +00:00
|
|
|
{
|
2014-01-09 18:56:12 +00:00
|
|
|
// init helper object
|
2016-05-20 04:24:05 +00:00
|
|
|
Slic3r::Geometry::MedialAxis ma(max_width, min_width, this);
|
|
|
|
ma.lines = this->lines();
|
2014-01-09 18:56:12 +00:00
|
|
|
|
2016-05-20 04:24:05 +00:00
|
|
|
// compute the Voronoi diagram and extract medial axis polylines
|
2016-03-19 14:33:58 +00:00
|
|
|
ThickPolylines pp;
|
2015-07-23 13:53:02 +00:00
|
|
|
ma.build(&pp);
|
2014-01-09 16:26:39 +00:00
|
|
|
|
2016-03-19 14:33:58 +00:00
|
|
|
/*
|
|
|
|
SVG svg("medial_axis.svg");
|
|
|
|
svg.draw(*this);
|
|
|
|
svg.draw(pp);
|
|
|
|
svg.Close();
|
|
|
|
*/
|
2015-05-21 23:46:01 +00:00
|
|
|
|
2016-05-20 04:24:05 +00:00
|
|
|
/* Find the maximum width returned; we're going to use this for validating and
|
|
|
|
filtering the output segments. */
|
2016-03-26 17:10:17 +00:00
|
|
|
double max_w = 0;
|
2016-05-20 04:24:05 +00:00
|
|
|
for (ThickPolylines::const_iterator it = pp.begin(); it != pp.end(); ++it)
|
|
|
|
max_w = fmaxf(max_w, *std::max_element(it->width.begin(), it->width.end()));
|
2016-03-26 17:10:17 +00:00
|
|
|
|
2016-05-20 04:24:05 +00:00
|
|
|
/* Loop through all returned polylines in order to extend their endpoints to the
|
|
|
|
expolygon boundaries */
|
2016-03-20 19:20:32 +00:00
|
|
|
bool removed = false;
|
2015-07-23 13:53:02 +00:00
|
|
|
for (size_t i = 0; i < pp.size(); ++i) {
|
2016-03-19 14:33:58 +00:00
|
|
|
ThickPolyline& polyline = pp[i];
|
|
|
|
|
|
|
|
// extend initial and final segments of each polyline if they're actual endpoints
|
|
|
|
/* We assign new endpoints to temporary variables because in case of a single-line
|
|
|
|
polyline, after we extend the start point it will be caught by the intersection()
|
|
|
|
call, so we keep the inner point until we perform the second intersection() as well */
|
|
|
|
Point new_front = polyline.points.front();
|
|
|
|
Point new_back = polyline.points.back();
|
2016-03-20 19:20:32 +00:00
|
|
|
if (polyline.endpoints.first && !this->has_boundary_point(new_front)) {
|
Removed Point::scale(),translate(),coincides_with(),distance_to(),
distance_to_squared(),perp_distance_to(),negative(),vector_to(),
translate(), distance_to() etc,
replaced with the Eigen equivalents.
2018-08-17 12:14:24 +00:00
|
|
|
Vec2d p1 = polyline.points.front().cast<double>();
|
|
|
|
Vec2d p2 = polyline.points[1].cast<double>();
|
2016-03-19 14:33:58 +00:00
|
|
|
// prevent the line from touching on the other side, otherwise intersection() might return that solution
|
Removed Point::scale(),translate(),coincides_with(),distance_to(),
distance_to_squared(),perp_distance_to(),negative(),vector_to(),
translate(), distance_to() etc,
replaced with the Eigen equivalents.
2018-08-17 12:14:24 +00:00
|
|
|
if (polyline.points.size() == 2)
|
|
|
|
p2 = (p1 + p2) * 0.5;
|
|
|
|
// Extend the start of the segment.
|
|
|
|
p1 -= (p2 - p1).normalized() * max_width;
|
|
|
|
this->contour.intersection(Line(p1.cast<coord_t>(), p2.cast<coord_t>()), &new_front);
|
2016-03-19 14:33:58 +00:00
|
|
|
}
|
2016-03-20 19:20:32 +00:00
|
|
|
if (polyline.endpoints.second && !this->has_boundary_point(new_back)) {
|
Removed Point::scale(),translate(),coincides_with(),distance_to(),
distance_to_squared(),perp_distance_to(),negative(),vector_to(),
translate(), distance_to() etc,
replaced with the Eigen equivalents.
2018-08-17 12:14:24 +00:00
|
|
|
Vec2d p1 = (polyline.points.end() - 2)->cast<double>();
|
|
|
|
Vec2d p2 = polyline.points.back().cast<double>();
|
2016-03-19 14:33:58 +00:00
|
|
|
// prevent the line from touching on the other side, otherwise intersection() might return that solution
|
Removed Point::scale(),translate(),coincides_with(),distance_to(),
distance_to_squared(),perp_distance_to(),negative(),vector_to(),
translate(), distance_to() etc,
replaced with the Eigen equivalents.
2018-08-17 12:14:24 +00:00
|
|
|
if (polyline.points.size() == 2)
|
|
|
|
p1 = (p1 + p2) * 0.5;
|
|
|
|
// Extend the start of the segment.
|
|
|
|
p2 += (p2 - p1).normalized() * max_width;
|
|
|
|
this->contour.intersection(Line(p1.cast<coord_t>(), p2.cast<coord_t>()), &new_back);
|
2016-03-19 14:33:58 +00:00
|
|
|
}
|
|
|
|
polyline.points.front() = new_front;
|
|
|
|
polyline.points.back() = new_back;
|
|
|
|
|
|
|
|
/* remove too short polylines
|
|
|
|
(we can't do this check before endpoints extension and clipping because we don't
|
|
|
|
know how long will the endpoints be extended since it depends on polygon thickness
|
2016-03-26 17:10:17 +00:00
|
|
|
which is variable - extension will be <= max_width/2 on each side) */
|
2016-03-20 19:20:32 +00:00
|
|
|
if ((polyline.endpoints.first || polyline.endpoints.second)
|
2016-03-26 17:10:17 +00:00
|
|
|
&& polyline.length() < max_w*2) {
|
2015-07-23 13:53:02 +00:00
|
|
|
pp.erase(pp.begin() + i);
|
2015-05-21 23:46:01 +00:00
|
|
|
--i;
|
2016-03-20 19:20:32 +00:00
|
|
|
removed = true;
|
2016-03-19 14:33:58 +00:00
|
|
|
continue;
|
2015-05-21 23:46:01 +00:00
|
|
|
}
|
|
|
|
}
|
2015-07-23 13:53:02 +00:00
|
|
|
|
2016-03-20 19:20:32 +00:00
|
|
|
/* If we removed any short polylines we now try to connect consecutive polylines
|
|
|
|
in order to allow loop detection. Note that this algorithm is greedier than
|
|
|
|
MedialAxis::process_edge_neighbors() as it will connect random pairs of
|
|
|
|
polylines even when more than two start from the same point. This has no
|
|
|
|
drawbacks since we optimize later using nearest-neighbor which would do the
|
|
|
|
same, but should we use a more sophisticated optimization algorithm we should
|
|
|
|
not connect polylines when more than two meet. */
|
|
|
|
if (removed) {
|
|
|
|
for (size_t i = 0; i < pp.size(); ++i) {
|
|
|
|
ThickPolyline& polyline = pp[i];
|
|
|
|
if (polyline.endpoints.first && polyline.endpoints.second) continue; // optimization
|
|
|
|
|
|
|
|
// find another polyline starting here
|
|
|
|
for (size_t j = i+1; j < pp.size(); ++j) {
|
|
|
|
ThickPolyline& other = pp[j];
|
2018-08-15 11:51:40 +00:00
|
|
|
if (polyline.last_point() == other.last_point()) {
|
2016-03-20 19:20:32 +00:00
|
|
|
other.reverse();
|
2018-08-15 11:51:40 +00:00
|
|
|
} else if (polyline.first_point() == other.last_point()) {
|
2016-03-20 19:20:32 +00:00
|
|
|
polyline.reverse();
|
|
|
|
other.reverse();
|
2018-08-15 11:51:40 +00:00
|
|
|
} else if (polyline.first_point() == other.first_point()) {
|
2016-03-20 19:20:32 +00:00
|
|
|
polyline.reverse();
|
2018-08-15 11:51:40 +00:00
|
|
|
} else if (polyline.last_point() != other.first_point()) {
|
2016-03-20 19:20:32 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
polyline.points.insert(polyline.points.end(), other.points.begin() + 1, other.points.end());
|
|
|
|
polyline.width.insert(polyline.width.end(), other.width.begin(), other.width.end());
|
|
|
|
polyline.endpoints.second = other.endpoints.second;
|
|
|
|
assert(polyline.width.size() == polyline.points.size()*2 - 2);
|
|
|
|
|
|
|
|
pp.erase(pp.begin() + j);
|
|
|
|
j = i; // restart search from i+1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-23 13:53:02 +00:00
|
|
|
polylines->insert(polylines->end(), pp.begin(), pp.end());
|
2014-01-09 16:26:39 +00:00
|
|
|
}
|
|
|
|
|
2016-03-19 14:33:58 +00:00
|
|
|
void
|
|
|
|
ExPolygon::medial_axis(double max_width, double min_width, Polylines* polylines) const
|
|
|
|
{
|
|
|
|
ThickPolylines tp;
|
|
|
|
this->medial_axis(max_width, min_width, &tp);
|
|
|
|
polylines->insert(polylines->end(), tp.begin(), tp.end());
|
|
|
|
}
|
|
|
|
|
2019-01-24 18:08:58 +00:00
|
|
|
Lines ExPolygon::lines() const
|
2015-01-06 19:52:36 +00:00
|
|
|
{
|
2015-01-19 17:53:04 +00:00
|
|
|
Lines lines = this->contour.lines();
|
|
|
|
for (Polygons::const_iterator h = this->holes.begin(); h != this->holes.end(); ++h) {
|
|
|
|
Lines hole_lines = h->lines();
|
|
|
|
lines.insert(lines.end(), hole_lines.begin(), hole_lines.end());
|
|
|
|
}
|
2015-01-06 19:52:36 +00:00
|
|
|
return lines;
|
|
|
|
}
|
|
|
|
|
2016-09-13 11:30:00 +00:00
|
|
|
BoundingBox get_extents(const ExPolygon &expolygon)
|
|
|
|
{
|
|
|
|
return get_extents(expolygon.contour);
|
|
|
|
}
|
|
|
|
|
|
|
|
BoundingBox get_extents(const ExPolygons &expolygons)
|
|
|
|
{
|
|
|
|
BoundingBox bbox;
|
|
|
|
if (! expolygons.empty()) {
|
2017-04-05 07:52:06 +00:00
|
|
|
for (size_t i = 0; i < expolygons.size(); ++ i)
|
|
|
|
if (! expolygons[i].contour.points.empty())
|
|
|
|
bbox.merge(get_extents(expolygons[i]));
|
2016-09-13 11:30:00 +00:00
|
|
|
}
|
|
|
|
return bbox;
|
2013-07-16 19:04:14 +00:00
|
|
|
}
|
2016-09-13 11:30:00 +00:00
|
|
|
|
2016-11-07 21:49:11 +00:00
|
|
|
BoundingBox get_extents_rotated(const ExPolygon &expolygon, double angle)
|
|
|
|
{
|
|
|
|
return get_extents_rotated(expolygon.contour, angle);
|
|
|
|
}
|
|
|
|
|
|
|
|
BoundingBox get_extents_rotated(const ExPolygons &expolygons, double angle)
|
|
|
|
{
|
|
|
|
BoundingBox bbox;
|
|
|
|
if (! expolygons.empty()) {
|
|
|
|
bbox = get_extents_rotated(expolygons.front().contour, angle);
|
|
|
|
for (size_t i = 1; i < expolygons.size(); ++ i)
|
|
|
|
bbox.merge(get_extents_rotated(expolygons[i].contour, angle));
|
|
|
|
}
|
|
|
|
return bbox;
|
|
|
|
}
|
|
|
|
|
2016-11-09 14:39:12 +00:00
|
|
|
extern std::vector<BoundingBox> get_extents_vector(const ExPolygons &polygons)
|
|
|
|
{
|
|
|
|
std::vector<BoundingBox> out;
|
|
|
|
out.reserve(polygons.size());
|
|
|
|
for (ExPolygons::const_iterator it = polygons.begin(); it != polygons.end(); ++ it)
|
|
|
|
out.push_back(get_extents(*it));
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
2016-10-20 11:04:23 +00:00
|
|
|
bool remove_sticks(ExPolygon &poly)
|
|
|
|
{
|
|
|
|
return remove_sticks(poly.contour) || remove_sticks(poly.holes);
|
|
|
|
}
|
|
|
|
|
2021-05-03 18:37:14 +00:00
|
|
|
bool remove_small_and_small_holes(ExPolygons &expolygons, double min_area)
|
|
|
|
{
|
|
|
|
bool modified = false;
|
|
|
|
size_t free_idx = 0;
|
|
|
|
for (size_t expoly_idx = 0; expoly_idx < expolygons.size(); ++expoly_idx) {
|
|
|
|
if (std::abs(expolygons[expoly_idx].area()) >= min_area) {
|
|
|
|
// Expolygon is big enough, so also check all its holes
|
|
|
|
modified |= remove_small(expolygons[expoly_idx].holes, min_area);
|
|
|
|
if (free_idx < expoly_idx) {
|
|
|
|
std::swap(expolygons[expoly_idx].contour, expolygons[free_idx].contour);
|
|
|
|
std::swap(expolygons[expoly_idx].holes, expolygons[free_idx].holes);
|
|
|
|
}
|
|
|
|
++free_idx;
|
|
|
|
} else
|
|
|
|
modified = true;
|
|
|
|
}
|
|
|
|
if (free_idx < expolygons.size())
|
|
|
|
expolygons.erase(expolygons.begin() + free_idx, expolygons.end());
|
|
|
|
return modified;
|
|
|
|
}
|
|
|
|
|
2020-02-08 20:36:29 +00:00
|
|
|
void keep_largest_contour_only(ExPolygons &polygons)
|
|
|
|
{
|
|
|
|
if (polygons.size() > 1) {
|
|
|
|
double max_area = 0.;
|
|
|
|
ExPolygon* max_area_polygon = nullptr;
|
|
|
|
for (ExPolygon& p : polygons) {
|
|
|
|
double a = p.contour.area();
|
|
|
|
if (a > max_area) {
|
|
|
|
max_area = a;
|
|
|
|
max_area_polygon = &p;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert(max_area_polygon != nullptr);
|
|
|
|
ExPolygon p(std::move(*max_area_polygon));
|
|
|
|
polygons.clear();
|
|
|
|
polygons.emplace_back(std::move(p));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-13 11:30:00 +00:00
|
|
|
} // namespace Slic3r
|