ENH: fix for STUDIO-881

Thanks prusa

Signed-off-by: salt.wei <salt.wei@bambulab.com>
Change-Id: I2e1c1088d29dd5401016ca41d3ed6dec87e0acd1
This commit is contained in:
salt.wei 2023-03-13 17:33:02 +08:00 committed by Lane.Wei
parent b4ffa91cb4
commit 61b271f379
31 changed files with 703 additions and 471 deletions

View file

@ -53,8 +53,6 @@ set(lisbslic3r_sources
enum_bitmask.hpp enum_bitmask.hpp
ExPolygon.cpp ExPolygon.cpp
ExPolygon.hpp ExPolygon.hpp
ExPolygonCollection.cpp
ExPolygonCollection.hpp
Extruder.cpp Extruder.cpp
Extruder.hpp Extruder.hpp
ExtrusionEntity.cpp ExtrusionEntity.cpp

View file

@ -660,6 +660,8 @@ Slic3r::Polygons diff(const Slic3r::Surfaces &subject, const Slic3r::Polygons &c
{ return _clipper(ClipperLib::ctDifference, ClipperUtils::SurfacesProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); } { return _clipper(ClipperLib::ctDifference, ClipperUtils::SurfacesProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); }
Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset) Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset)
{ return _clipper(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::SinglePathProvider(clip.points), do_safety_offset); } { return _clipper(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::SinglePathProvider(clip.points), do_safety_offset); }
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset)
{ return _clipper(ClipperLib::ctIntersection, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonProvider(clip), do_safety_offset); }
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
{ return _clipper(ClipperLib::ctIntersection, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); } { return _clipper(ClipperLib::ctIntersection, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); }
Slic3r::Polygons intersection(const Slic3r::ExPolygon &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset) Slic3r::Polygons intersection(const Slic3r::ExPolygon &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset)
@ -746,10 +748,16 @@ Slic3r::ExPolygons intersection_ex(const Slic3r::Polygons &subject, const Slic3r
{ return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); } { return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); }
Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygon &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygon &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
{ return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::ExPolygonProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); } { return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::ExPolygonProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); }
Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygon& subject, const Slic3r::ExPolygon& clip, ApplySafetyOffset do_safety_offset)
{ return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::ExPolygonProvider(subject), ClipperUtils::ExPolygonProvider(clip), do_safety_offset); }
Slic3r::ExPolygons intersection_ex(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset) Slic3r::ExPolygons intersection_ex(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset)
{ return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); } { return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); }
Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
{ return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); } { return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); }
Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygons& subject, const Slic3r::ExPolygon& clip, ApplySafetyOffset do_safety_offset)
{ return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::ExPolygonProvider(clip), do_safety_offset);}
Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygon& subject, const Slic3r::ExPolygons& clip, ApplySafetyOffset do_safety_offset)
{ return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::ExPolygonProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset);}
Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset) Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset)
{ return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); } { return _clipper_ex(ClipperLib::ctIntersection, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); }
Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
@ -859,6 +867,8 @@ Slic3r::Polylines diff_pl(const Slic3r::Polygons &subject, const Slic3r::Polygon
{ return _clipper_pl_closed(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip)); } { return _clipper_pl_closed(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip)); }
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip) Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip)
{ return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::PolylinesProvider(subject), ClipperUtils::SinglePathProvider(clip.points)); } { return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::PolylinesProvider(subject), ClipperUtils::SinglePathProvider(clip.points)); }
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip)
{ return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::PolylinesProvider(subject), ClipperUtils::ExPolygonProvider(clip)); }
Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip) Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip)
{ return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::PolygonsProvider(clip)); } { return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::PolygonsProvider(clip)); }
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip) Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip)

View file

@ -472,6 +472,7 @@ inline Slic3r::Lines diff_ln(const Slic3r::Lines &subject, const Slic3r::Polygon
// Safety offset is applied to the clipping polygons only. // Safety offset is applied to the clipping polygons only.
Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polygons intersection(const Slic3r::ExPolygon &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons intersection(const Slic3r::ExPolygon &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polygons intersection(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons intersection(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
@ -482,28 +483,23 @@ Slic3r::Polygons intersection(const Slic3r::Surfaces &subject, const Slic3r::E
Slic3r::Polygons intersection(const Slic3r::Polygons& subject, const Slic3r::Polygon& clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons intersection(const Slic3r::Polygons& subject, const Slic3r::Polygon& clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygon &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygon &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygon& subject, const Slic3r::ExPolygon& clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons intersection_ex(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons intersection_ex(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygons& subject, const Slic3r::ExPolygon& clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygon& subject, const Slic3r::ExPolygons& clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons intersection_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::ExPolygons intersection_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip); Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip);
Slic3r::Polylines intersection_pl(const Slic3r::Polylines& subject, const Slic3r::ExPolygon& clip);
Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip); Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip);
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip); Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip);
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &clip); Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &clip);
Slic3r::Polylines intersection_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip); Slic3r::Polylines intersection_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip);
// BBS
inline Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygon& subject, const Slic3r::ExPolygons& clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No)
{
Slic3r::ExPolygons subject_temp;
subject_temp.push_back(subject);
return intersection_ex(subject_temp, clip, do_safety_offset);
}
inline Slic3r::Lines intersection_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip) inline Slic3r::Lines intersection_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip)
{ {
return _clipper_ln(ClipperLib::ctIntersection, subject, clip); return _clipper_ln(ClipperLib::ctIntersection, subject, clip);

View file

@ -136,11 +136,6 @@ void EdgeGrid::Grid::create(const ExPolygons &expolygons, coord_t resolution)
create_from_m_contours(resolution); create_from_m_contours(resolution);
} }
void EdgeGrid::Grid::create(const ExPolygonCollection &expolygons, coord_t resolution)
{
create(expolygons.expolygons, resolution);
}
// m_contours has been initialized. Now fill in the edge grid. // m_contours has been initialized. Now fill in the edge grid.
void EdgeGrid::Grid::create_from_m_contours(coord_t resolution) void EdgeGrid::Grid::create_from_m_contours(coord_t resolution)
{ {

View file

@ -7,7 +7,6 @@
#include "Point.hpp" #include "Point.hpp"
#include "BoundingBox.hpp" #include "BoundingBox.hpp"
#include "ExPolygon.hpp" #include "ExPolygon.hpp"
#include "ExPolygonCollection.hpp"
namespace Slic3r { namespace Slic3r {
namespace EdgeGrid { namespace EdgeGrid {
@ -112,7 +111,6 @@ public:
void create(const std::vector<Points> &polygons, coord_t resolution) { this->create(polygons, resolution, false); } void create(const std::vector<Points> &polygons, coord_t resolution) { this->create(polygons, resolution, false); }
void create(const ExPolygon &expoly, coord_t resolution); void create(const ExPolygon &expoly, coord_t resolution);
void create(const ExPolygons &expolygons, coord_t resolution); void create(const ExPolygons &expolygons, coord_t resolution);
void create(const ExPolygonCollection &expolygons, coord_t resolution);
const std::vector<Contour>& contours() const { return m_contours; } const std::vector<Contour>& contours() const { return m_contours; }
@ -123,7 +121,6 @@ public:
bool intersect(const Polygons &polygons) { for (size_t i = 0; i < polygons.size(); ++ i) if (intersect(polygons[i])) return true; return false; } bool intersect(const Polygons &polygons) { for (size_t i = 0; i < polygons.size(); ++ i) if (intersect(polygons[i])) return true; return false; }
bool intersect(const ExPolygon &expoly) { if (intersect(expoly.contour)) return true; for (size_t i = 0; i < expoly.holes.size(); ++ i) if (intersect(expoly.holes[i])) return true; return false; } bool intersect(const ExPolygon &expoly) { if (intersect(expoly.contour)) return true; for (size_t i = 0; i < expoly.holes.size(); ++ i) if (intersect(expoly.holes[i])) return true; return false; }
bool intersect(const ExPolygons &expolygons) { for (size_t i = 0; i < expolygons.size(); ++ i) if (intersect(expolygons[i])) return true; return false; } bool intersect(const ExPolygons &expolygons) { for (size_t i = 0; i < expolygons.size(); ++ i) if (intersect(expolygons[i])) return true; return false; }
bool intersect(const ExPolygonCollection &expolygons) { return intersect(expolygons.expolygons); }
// Test, whether a point is inside a contour. // Test, whether a point is inside a contour.
bool inside(const Point &pt); bool inside(const Point &pt);
@ -391,7 +388,7 @@ protected:
// Referencing the source contours. // Referencing the source contours.
// This format allows one to work with any Slic3r fixed point contour format // This format allows one to work with any Slic3r fixed point contour format
// (Polygon, ExPolygon, ExPolygonCollection etc). // (Polygon, ExPolygon, ExPolygons etc).
std::vector<Contour> m_contours; std::vector<Contour> m_contours;
// Referencing a contour and a line segment of m_contours. // Referencing a contour and a line segment of m_contours.

View file

@ -12,27 +12,6 @@
namespace Slic3r { namespace Slic3r {
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;
}
ExPolygon::operator Polygons() const
{
return to_polygons(*this);
}
ExPolygon::operator Polylines() const
{
return to_polylines(*this);
}
void ExPolygon::scale(double factor) void ExPolygon::scale(double factor)
{ {
contour.scale(factor); contour.scale(factor);
@ -40,6 +19,13 @@ void ExPolygon::scale(double factor)
hole.scale(factor); hole.scale(factor);
} }
void ExPolygon::scale(double factor_x, double factor_y)
{
contour.scale(factor_x, factor_y);
for (Polygon &hole : holes)
hole.scale(factor_x, factor_y);
}
void ExPolygon::translate(const Point &p) void ExPolygon::translate(const Point &p)
{ {
contour.translate(p); contour.translate(p);
@ -118,34 +104,53 @@ bool ExPolygon::contains(const Polylines &polylines) const
return pl_out.empty(); return pl_out.empty();
} }
bool ExPolygon::contains(const Point &point) const bool ExPolygon::contains(const Point &point, bool border_result /* = true */) const
{ {
if (! this->contour.contains(point)) if (! Slic3r::contains(contour, point, border_result))
// Outside the outer contour, not on the contour boundary.
return false; return false;
for (const Polygon &hole : this->holes) for (const Polygon &hole : this->holes)
if (hole.contains(point)) if (Slic3r::contains(hole, point, ! border_result))
// Inside a hole, not on the hole boundary.
return false; return false;
return true; return true;
} }
// inclusive version of contains() that also checks whether point is on boundaries bool ExPolygon::on_boundary(const Point &point, double eps) const
bool ExPolygon::contains_b(const Point &point) const
{ {
return this->contains(point) || this->has_boundary_point(point); if (this->contour.on_boundary(point, eps))
return true;
for (const Polygon &hole : this->holes)
if (hole.on_boundary(point, eps))
return true;
return false;
} }
bool // Projection of a point onto the polygon.
ExPolygon::has_boundary_point(const Point &point) const Point ExPolygon::point_projection(const Point &point) const
{ {
if (this->contour.has_boundary_point(point)) return true; if (this->holes.empty()) {
for (Polygons::const_iterator h = this->holes.begin(); h != this->holes.end(); ++h) { return this->contour.point_projection(point);
if (h->has_boundary_point(point)) return true; } else {
double dist_min2 = std::numeric_limits<double>::max();
Point closest_pt_min;
for (size_t i = 0; i < this->num_contours(); ++ i) {
Point closest_pt = this->contour_or_hole(i).point_projection(point);
double d2 = (closest_pt - point).cast<double>().squaredNorm();
if (d2 < dist_min2) {
dist_min2 = d2;
closest_pt_min = closest_pt;
}
}
return closest_pt_min;
} }
return false;
} }
bool ExPolygon::overlaps(const ExPolygon &other) const bool ExPolygon::overlaps(const ExPolygon &other) const
{ {
if (this->empty() || other.empty())
return false;
#if 0 #if 0
BoundingBox bbox = get_extents(other); BoundingBox bbox = get_extents(other);
bbox.merge(get_extents(*this)); bbox.merge(get_extents(*this));
@ -155,61 +160,62 @@ bool ExPolygon::overlaps(const ExPolygon &other) const
svg.draw_outline(*this); svg.draw_outline(*this);
svg.draw_outline(other, "blue"); svg.draw_outline(other, "blue");
#endif #endif
Polylines pl_out = intersection_pl((Polylines)other, *this);
Polylines pl_out = intersection_pl(to_polylines(other), *this);
#if 0 #if 0
svg.draw(pl_out, "red"); svg.draw(pl_out, "red");
#endif #endif
if (! pl_out.empty())
return true; // See unit test SCENARIO("Clipper diff with polyline", "[Clipper]")
//FIXME ExPolygon::overlaps() shall be commutative, it is not! // for in which case the intersection_pl produces any intersection.
return ! other.contour.points.empty() && this->contains_b(other.contour.points.front()); return ! pl_out.empty() ||
// If *this is completely inside other, then pl_out is empty, but the expolygons overlap. Test for that situation.
other.contains(this->contour.points.front());
} }
void ExPolygon::simplify_p(double tolerance, Polygons* polygons, SimplifyMethod method) const void ExPolygon::simplify_p(double tolerance, Polygons* polygons) const
{ {
Polygons pp = this->simplify_p(tolerance, method); Polygons pp = this->simplify_p(tolerance);
polygons->insert(polygons->end(), pp.begin(), pp.end()); polygons->insert(polygons->end(), pp.begin(), pp.end());
} }
Polygons ExPolygon::simplify_p(double tolerance, SimplifyMethod method) const Polygons ExPolygon::simplify_p(double tolerance) const
{ {
Polygons pp; Polygons pp;
pp.reserve(this->holes.size() + 1); pp.reserve(this->holes.size() + 1);
std::map<int, std::function<Points(const Points&, const double)>> method_list = { {SimplifyMethodDP, MultiPoint::_douglas_peucker}, {SimplifyMethodVisvalingam, MultiPoint::visivalingam},{SimplifyMethodConcave, MultiPoint::concave_hull_2d} };
// contour // contour
{ {
Polygon p = this->contour; Polygon p = this->contour;
p.points.push_back(p.points.front()); p.points.push_back(p.points.front());
p.points = method_list[method](p.points, tolerance); p.points = MultiPoint::_douglas_peucker(p.points, tolerance);
p.points.pop_back(); p.points.pop_back();
pp.emplace_back(std::move(p)); pp.emplace_back(std::move(p));
} }
// holes // holes
for (Polygon p : this->holes) { for (Polygon p : this->holes) {
p.points.push_back(p.points.front()); p.points.push_back(p.points.front());
p.points = method_list[method](p.points, tolerance); p.points = MultiPoint::_douglas_peucker(p.points, tolerance);
p.points.pop_back(); p.points.pop_back();
pp.emplace_back(std::move(p)); pp.emplace_back(std::move(p));
} }
return simplify_polygons(pp); return simplify_polygons(pp);
} }
ExPolygons ExPolygon::simplify(double tolerance, SimplifyMethod method) const ExPolygons ExPolygon::simplify(double tolerance) const
{ {
return union_ex(this->simplify_p(tolerance, method)); return union_ex(this->simplify_p(tolerance));
} }
void ExPolygon::simplify(double tolerance, ExPolygons* expolygons, SimplifyMethod method) const void ExPolygon::simplify(double tolerance, ExPolygons* expolygons) const
{ {
append(*expolygons, this->simplify(tolerance, method)); append(*expolygons, this->simplify(tolerance));
} }
void void ExPolygon::medial_axis(double min_width, double max_width, ThickPolylines* polylines) const
ExPolygon::medial_axis(double max_width, double min_width, ThickPolylines* polylines) const
{ {
// init helper object // init helper object
Slic3r::Geometry::MedialAxis ma(max_width, min_width, this); Slic3r::Geometry::MedialAxis ma(min_width, max_width, *this);
ma.lines = this->lines();
// compute the Voronoi diagram and extract medial axis polylines // compute the Voronoi diagram and extract medial axis polylines
ThickPolylines pp; ThickPolylines pp;
@ -240,7 +246,7 @@ ExPolygon::medial_axis(double max_width, double min_width, ThickPolylines* polyl
call, so we keep the inner point until we perform the second intersection() as well */ call, so we keep the inner point until we perform the second intersection() as well */
Point new_front = polyline.points.front(); Point new_front = polyline.points.front();
Point new_back = polyline.points.back(); Point new_back = polyline.points.back();
if (polyline.endpoints.first && !this->has_boundary_point(new_front)) { if (polyline.endpoints.first && !this->on_boundary(new_front, SCALED_EPSILON)) {
Vec2d p1 = polyline.points.front().cast<double>(); Vec2d p1 = polyline.points.front().cast<double>();
Vec2d p2 = polyline.points[1].cast<double>(); Vec2d p2 = polyline.points[1].cast<double>();
// prevent the line from touching on the other side, otherwise intersection() might return that solution // prevent the line from touching on the other side, otherwise intersection() might return that solution
@ -250,7 +256,7 @@ ExPolygon::medial_axis(double max_width, double min_width, ThickPolylines* polyl
p1 -= (p2 - p1).normalized() * max_width; p1 -= (p2 - p1).normalized() * max_width;
this->contour.intersection(Line(p1.cast<coord_t>(), p2.cast<coord_t>()), &new_front); this->contour.intersection(Line(p1.cast<coord_t>(), p2.cast<coord_t>()), &new_front);
} }
if (polyline.endpoints.second && !this->has_boundary_point(new_back)) { if (polyline.endpoints.second && !this->on_boundary(new_back, SCALED_EPSILON)) {
Vec2d p1 = (polyline.points.end() - 2)->cast<double>(); Vec2d p1 = (polyline.points.end() - 2)->cast<double>();
Vec2d p2 = polyline.points.back().cast<double>(); Vec2d p2 = polyline.points.back().cast<double>();
// prevent the line from touching on the other side, otherwise intersection() might return that solution // prevent the line from touching on the other side, otherwise intersection() might return that solution
@ -316,12 +322,13 @@ ExPolygon::medial_axis(double max_width, double min_width, ThickPolylines* polyl
polylines->insert(polylines->end(), pp.begin(), pp.end()); polylines->insert(polylines->end(), pp.begin(), pp.end());
} }
void void ExPolygon::medial_axis(double min_width, double max_width, Polylines* polylines) const
ExPolygon::medial_axis(double max_width, double min_width, Polylines* polylines) const
{ {
ThickPolylines tp; ThickPolylines tp;
this->medial_axis(max_width, min_width, &tp); this->medial_axis(min_width, max_width, &tp);
polylines->insert(polylines->end(), tp.begin(), tp.end()); polylines->reserve(polylines->size() + tp.size());
for (auto &pl : tp)
polylines->emplace_back(pl.points);
} }
Lines ExPolygon::lines() const Lines ExPolygon::lines() const
@ -334,6 +341,18 @@ Lines ExPolygon::lines() const
return lines; return lines;
} }
// Do expolygons match? If they match, they must have the same topology,
// however their contours may be rotated.
bool expolygons_match(const ExPolygon &l, const ExPolygon &r)
{
if (l.holes.size() != r.holes.size() || ! polygons_match(l.contour, r.contour))
return false;
for (size_t hole_idx = 0; hole_idx < l.holes.size(); ++ hole_idx)
if (! polygons_match(l.holes[hole_idx], r.holes[hole_idx]))
return false;
return true;
}
BoundingBox get_extents(const ExPolygon &expolygon) BoundingBox get_extents(const ExPolygon &expolygon)
{ {
return get_extents(expolygon.contour); return get_extents(expolygon.contour);

View file

@ -1,6 +1,7 @@
#ifndef slic3r_ExPolygon_hpp_ #ifndef slic3r_ExPolygon_hpp_
#define slic3r_ExPolygon_hpp_ #define slic3r_ExPolygon_hpp_
#include "Point.hpp"
#include "libslic3r.h" #include "libslic3r.h"
#include "Polygon.hpp" #include "Polygon.hpp"
#include "Polyline.hpp" #include "Polyline.hpp"
@ -9,13 +10,7 @@
namespace Slic3r { namespace Slic3r {
class ExPolygon; class ExPolygon;
typedef std::vector<ExPolygon> ExPolygons; using ExPolygons = std::vector<ExPolygon>;
typedef enum SimplifyMethod_ {
SimplifyMethodDP=0,
SimplifyMethodVisvalingam,
SimplifyMethodConcave
}SimplifyMethod;
class ExPolygon class ExPolygon
{ {
@ -37,14 +32,12 @@ public:
ExPolygon& operator=(const ExPolygon &other) = default; ExPolygon& operator=(const ExPolygon &other) = default;
ExPolygon& operator=(ExPolygon &&other) = default; ExPolygon& operator=(ExPolygon &&other) = default;
Polygon contour; Polygon contour; //CCW
Polygons holes; Polygons holes; //CW
operator Points() const;
operator Polygons() const;
operator Polylines() const;
void clear() { contour.points.clear(); holes.clear(); } void clear() { contour.points.clear(); holes.clear(); }
void scale(double factor); void scale(double factor);
void scale(double factor_x, double factor_y);
void translate(double x, double y) { this->translate(Point(coord_t(x), coord_t(y))); } void translate(double x, double y) { this->translate(Point(coord_t(x), coord_t(y))); }
void translate(const Point &vector); void translate(const Point &vector);
void rotate(double angle); void rotate(double angle);
@ -58,21 +51,29 @@ public:
bool contains(const Line &line) const; bool contains(const Line &line) const;
bool contains(const Polyline &polyline) const; bool contains(const Polyline &polyline) const;
bool contains(const Polylines &polylines) const; bool contains(const Polylines &polylines) const;
bool contains(const Point &point) const; bool contains(const Point &point, bool border_result = true) const;
bool contains_b(const Point &point) const; // Approximate on boundary test.
bool has_boundary_point(const Point &point) const; bool on_boundary(const Point &point, double eps) const;
// Projection of a point onto the polygon.
Point point_projection(const Point &point) const;
// Does this expolygon overlap another expolygon? // Does this expolygon overlap another expolygon?
// Either the ExPolygons intersect, or one is fully inside the other, // Either the ExPolygons intersect, or one is fully inside the other,
// and it is not inside a hole of the other expolygon. // and it is not inside a hole of the other expolygon.
// The test may not be commutative if the two expolygons touch by a boundary only,
// see unit test SCENARIO("Clipper diff with polyline", "[Clipper]").
// Namely expolygons touching at a vertical boundary are considered overlapping, while expolygons touching
// at a horizontal boundary are NOT considered overlapping.
bool overlaps(const ExPolygon &other) const; bool overlaps(const ExPolygon &other) const;
void simplify_p(double tolerance, Polygons* polygons, SimplifyMethod method = SimplifyMethodDP) const; void simplify_p(double tolerance, Polygons* polygons) const;
Polygons simplify_p(double tolerance, SimplifyMethod method = SimplifyMethodDP) const; Polygons simplify_p(double tolerance) const;
ExPolygons simplify(double tolerance, SimplifyMethod method = SimplifyMethodDP) const; ExPolygons simplify(double tolerance) const;
void simplify(double tolerance, ExPolygons* expolygons, SimplifyMethod method = SimplifyMethodDP) const; void simplify(double tolerance, ExPolygons* expolygons) const;
void medial_axis(double max_width, double min_width, ThickPolylines* polylines) const; void medial_axis(double min_width, double max_width, ThickPolylines* polylines) const;
void medial_axis(double max_width, double min_width, Polylines* polylines) const; void medial_axis(double min_width, double max_width, Polylines* polylines) const;
Polylines medial_axis(double min_width, double max_width) const
{ Polylines out; this->medial_axis(min_width, max_width, &out); return out; }
Lines lines() const; Lines lines() const;
// Number of contours (outer contour with holes). // Number of contours (outer contour with holes).
@ -84,6 +85,25 @@ public:
inline bool operator==(const ExPolygon &lhs, const ExPolygon &rhs) { return lhs.contour == rhs.contour && lhs.holes == rhs.holes; } inline bool operator==(const ExPolygon &lhs, const ExPolygon &rhs) { return lhs.contour == rhs.contour && lhs.holes == rhs.holes; }
inline bool operator!=(const ExPolygon &lhs, const ExPolygon &rhs) { return lhs.contour != rhs.contour || lhs.holes != rhs.holes; } inline bool operator!=(const ExPolygon &lhs, const ExPolygon &rhs) { return lhs.contour != rhs.contour || lhs.holes != rhs.holes; }
inline size_t count_points(const ExPolygons &expolys)
{
size_t n_points = 0;
for (const auto &expoly : expolys) {
n_points += expoly.contour.points.size();
for (const auto &hole : expoly.holes)
n_points += hole.points.size();
}
return n_points;
}
inline size_t count_points(const ExPolygon &expoly)
{
size_t n_points = expoly.contour.points.size();
for (const auto &hole : expoly.holes)
n_points += hole.points.size();
return n_points;
}
// Count a nuber of polygons stored inside the vector of expolygons. // Count a nuber of polygons stored inside the vector of expolygons.
// Useful for allocating space for polygons when converting expolygons to polygons. // Useful for allocating space for polygons when converting expolygons to polygons.
inline size_t number_polygons(const ExPolygons &expolys) inline size_t number_polygons(const ExPolygons &expolys)
@ -96,11 +116,8 @@ inline size_t number_polygons(const ExPolygons &expolys)
inline Lines to_lines(const ExPolygon &src) inline Lines to_lines(const ExPolygon &src)
{ {
size_t n_lines = src.contour.points.size();
for (size_t i = 0; i < src.holes.size(); ++ i)
n_lines += src.holes[i].points.size();
Lines lines; Lines lines;
lines.reserve(n_lines); lines.reserve(count_points(src));
for (size_t i = 0; i <= src.holes.size(); ++ i) { for (size_t i = 0; i <= src.holes.size(); ++ i) {
const Polygon &poly = (i == 0) ? src.contour : src.holes[i - 1]; const Polygon &poly = (i == 0) ? src.contour : src.holes[i - 1];
for (Points::const_iterator it = poly.points.begin(); it != poly.points.end()-1; ++it) for (Points::const_iterator it = poly.points.begin(); it != poly.points.end()-1; ++it)
@ -112,14 +129,8 @@ inline Lines to_lines(const ExPolygon &src)
inline Lines to_lines(const ExPolygons &src) inline Lines to_lines(const ExPolygons &src)
{ {
size_t n_lines = 0;
for (ExPolygons::const_iterator it_expoly = src.begin(); it_expoly != src.end(); ++ it_expoly) {
n_lines += it_expoly->contour.points.size();
for (size_t i = 0; i < it_expoly->holes.size(); ++ i)
n_lines += it_expoly->holes[i].points.size();
}
Lines lines; Lines lines;
lines.reserve(n_lines); lines.reserve(count_points(src));
for (ExPolygons::const_iterator it_expoly = src.begin(); it_expoly != src.end(); ++ it_expoly) { for (ExPolygons::const_iterator it_expoly = src.begin(); it_expoly != src.end(); ++ it_expoly) {
for (size_t i = 0; i <= it_expoly->holes.size(); ++ i) { for (size_t i = 0; i <= it_expoly->holes.size(); ++ i) {
const Points &points = ((i == 0) ? it_expoly->contour : it_expoly->holes[i - 1]).points; const Points &points = ((i == 0) ? it_expoly->contour : it_expoly->holes[i - 1]).points;
@ -131,6 +142,70 @@ inline Lines to_lines(const ExPolygons &src)
return lines; return lines;
} }
// Line is from point index(see to_points) to next point.
// Next point of last point in polygon is first polygon point.
inline Linesf to_linesf(const ExPolygons &src, uint32_t count_lines = 0)
{
assert(count_lines == 0 || count_lines == count_points(src));
if (count_lines == 0) count_lines = count_points(src);
Linesf lines;
lines.reserve(count_lines);
Vec2d prev_pd;
auto to_lines = [&lines, &prev_pd](const Points &pts) {
assert(pts.size() >= 3);
if (pts.size() < 2) return;
bool is_first = true;
for (const Point &p : pts) {
Vec2d pd = p.cast<double>();
if (is_first) is_first = false;
else lines.emplace_back(prev_pd, pd);
prev_pd = pd;
}
lines.emplace_back(prev_pd, pts.front().cast<double>());
};
for (const ExPolygon& expoly: src) {
to_lines(expoly.contour.points);
for (const Polygon &hole : expoly.holes)
to_lines(hole.points);
}
assert(lines.size() == count_lines);
return lines;
}
inline Linesf to_unscaled_linesf(const ExPolygons &src)
{
Linesf lines;
lines.reserve(count_points(src));
for (ExPolygons::const_iterator it_expoly = src.begin(); it_expoly != src.end(); ++ it_expoly) {
for (size_t i = 0; i <= it_expoly->holes.size(); ++ i) {
const Points &points = ((i == 0) ? it_expoly->contour : it_expoly->holes[i - 1]).points;
Vec2d unscaled_a = unscaled(points.front());
Vec2d unscaled_b = unscaled_a;
for (Points::const_iterator it = points.begin()+1; it != points.end(); ++it){
unscaled_b = unscaled(*(it));
lines.push_back(Linef(unscaled_a, unscaled_b));
unscaled_a = unscaled_b;
}
lines.push_back(Linef(unscaled_a, unscaled(points.front())));
}
}
return lines;
}
inline Points to_points(const ExPolygons &src)
{
Points points;
size_t count = count_points(src);
points.reserve(count);
for (const ExPolygon &expolygon : src) {
append(points, expolygon.contour.points);
for (const Polygon &hole : expolygon.holes)
append(points, hole.points);
}
return points;
}
inline Polylines to_polylines(const ExPolygon &src) inline Polylines to_polylines(const ExPolygon &src)
{ {
Polylines polylines; Polylines polylines;
@ -175,10 +250,10 @@ inline Polylines to_polylines(ExPolygon &&src)
Polyline &pl = polylines[idx ++]; Polyline &pl = polylines[idx ++];
pl.points = std::move(src.contour.points); pl.points = std::move(src.contour.points);
pl.points.push_back(pl.points.front()); pl.points.push_back(pl.points.front());
for (Polygons::const_iterator ith = src.holes.begin(); ith != src.holes.end(); ++ith) { for (auto ith = src.holes.begin(); ith != src.holes.end(); ++ith) {
Polyline &pl = polylines[idx ++]; Polyline &pl = polylines[idx ++];
pl.points = std::move(ith->points); pl.points = std::move(ith->points);
pl.points.push_back(ith->points.front()); pl.points.push_back(pl.points.front());
} }
assert(idx == polylines.size()); assert(idx == polylines.size());
return polylines; return polylines;
@ -189,14 +264,14 @@ inline Polylines to_polylines(ExPolygons &&src)
Polylines polylines; Polylines polylines;
polylines.assign(number_polygons(src), Polyline()); polylines.assign(number_polygons(src), Polyline());
size_t idx = 0; size_t idx = 0;
for (ExPolygons::const_iterator it = src.begin(); it != src.end(); ++it) { for (auto it = src.begin(); it != src.end(); ++it) {
Polyline &pl = polylines[idx ++]; Polyline &pl = polylines[idx ++];
pl.points = std::move(it->contour.points); pl.points = std::move(it->contour.points);
pl.points.push_back(pl.points.front()); pl.points.push_back(pl.points.front());
for (Polygons::const_iterator ith = it->holes.begin(); ith != it->holes.end(); ++ith) { for (auto ith = it->holes.begin(); ith != it->holes.end(); ++ith) {
Polyline &pl = polylines[idx ++]; Polyline &pl = polylines[idx ++];
pl.points = std::move(ith->points); pl.points = std::move(ith->points);
pl.points.push_back(ith->points.front()); pl.points.push_back(pl.points.front());
} }
} }
assert(idx == polylines.size()); assert(idx == polylines.size());
@ -250,8 +325,9 @@ inline Polygons to_polygons(ExPolygon &&src)
Polygons polygons; Polygons polygons;
polygons.reserve(src.holes.size() + 1); polygons.reserve(src.holes.size() + 1);
polygons.push_back(std::move(src.contour)); polygons.push_back(std::move(src.contour));
std::move(std::begin(src.holes), std::end(src.holes), std::back_inserter(polygons)); polygons.insert(polygons.end(),
src.holes.clear(); std::make_move_iterator(src.holes.begin()),
std::make_move_iterator(src.holes.end()));
return polygons; return polygons;
} }
@ -259,10 +335,11 @@ inline Polygons to_polygons(ExPolygons &&src)
{ {
Polygons polygons; Polygons polygons;
polygons.reserve(number_polygons(src)); polygons.reserve(number_polygons(src));
for (ExPolygons::iterator it = src.begin(); it != src.end(); ++it) { for (ExPolygon& expoly: src) {
polygons.push_back(std::move(it->contour)); polygons.push_back(std::move(expoly.contour));
std::move(std::begin(it->holes), std::end(it->holes), std::back_inserter(polygons)); polygons.insert(polygons.end(),
it->holes.clear(); std::make_move_iterator(expoly.holes.begin()),
std::make_move_iterator(expoly.holes.end()));
} }
return polygons; return polygons;
} }
@ -285,6 +362,16 @@ inline ExPolygons to_expolygons(Polygons &&polys)
return ex_polys; return ex_polys;
} }
inline Points to_points(const ExPolygon &expoly)
{
Points out;
out.reserve(count_points(expoly));
append(out, expoly.contour.points);
for (const Polygon &hole : expoly.holes)
append(out, hole.points);
return out;
}
inline void polygons_append(Polygons &dst, const ExPolygon &src) inline void polygons_append(Polygons &dst, const ExPolygon &src)
{ {
dst.reserve(dst.size() + src.holes.size() + 1); dst.reserve(dst.size() + src.holes.size() + 1);
@ -305,17 +392,19 @@ inline void polygons_append(Polygons &dst, ExPolygon &&src)
{ {
dst.reserve(dst.size() + src.holes.size() + 1); dst.reserve(dst.size() + src.holes.size() + 1);
dst.push_back(std::move(src.contour)); dst.push_back(std::move(src.contour));
std::move(std::begin(src.holes), std::end(src.holes), std::back_inserter(dst)); dst.insert(dst.end(),
src.holes.clear(); std::make_move_iterator(src.holes.begin()),
std::make_move_iterator(src.holes.end()));
} }
inline void polygons_append(Polygons &dst, ExPolygons &&src) inline void polygons_append(Polygons &dst, ExPolygons &&src)
{ {
dst.reserve(dst.size() + number_polygons(src)); dst.reserve(dst.size() + number_polygons(src));
for (ExPolygons::iterator it = src.begin(); it != src.end(); ++ it) { for (ExPolygon& expoly: src) {
dst.push_back(std::move(it->contour)); dst.push_back(std::move(expoly.contour));
std::move(std::begin(it->holes), std::end(it->holes), std::back_inserter(dst)); dst.insert(dst.end(),
it->holes.clear(); std::make_move_iterator(expoly.holes.begin()),
std::make_move_iterator(expoly.holes.end()));
} }
} }
@ -329,21 +418,22 @@ inline void expolygons_append(ExPolygons &dst, ExPolygons &&src)
if (dst.empty()) { if (dst.empty()) {
dst = std::move(src); dst = std::move(src);
} else { } else {
std::move(std::begin(src), std::end(src), std::back_inserter(dst)); dst.insert(dst.end(),
src.clear(); std::make_move_iterator(src.begin()),
std::make_move_iterator(src.end()));
} }
} }
inline void expolygons_rotate(ExPolygons &expolys, double angle) inline void expolygons_rotate(ExPolygons &expolys, double angle)
{ {
for (ExPolygons::iterator p = expolys.begin(); p != expolys.end(); ++p) for (ExPolygon &expoly : expolys)
p->rotate(angle); expoly.rotate(angle);
} }
inline bool expolygons_contain(ExPolygons &expolys, const Point &pt) inline bool expolygons_contain(ExPolygons &expolys, const Point &pt, bool border_result = true)
{ {
for (ExPolygons::iterator p = expolys.begin(); p != expolys.end(); ++p) for (const ExPolygon &expoly : expolys)
if (p->contains(pt)) if (expoly.contains(pt, border_result))
return true; return true;
return false; return false;
} }
@ -357,6 +447,10 @@ inline ExPolygons expolygons_simplify(const ExPolygons &expolys, double toleranc
return out; return out;
} }
// Do expolygons match? If they match, they must have the same topology,
// however their contours may be rotated.
bool expolygons_match(const ExPolygon &l, const ExPolygon &r);
BoundingBox get_extents(const ExPolygon &expolygon); BoundingBox get_extents(const ExPolygon &expolygon);
BoundingBox get_extents(const ExPolygons &expolygons); BoundingBox get_extents(const ExPolygons &expolygons);
BoundingBox get_extents_rotated(const ExPolygon &poly, double angle); BoundingBox get_extents_rotated(const ExPolygon &poly, double angle);

View file

@ -1,6 +1,6 @@
#include "ExtrusionEntity.hpp" #include "ExtrusionEntity.hpp"
#include "ExtrusionEntityCollection.hpp" #include "ExtrusionEntityCollection.hpp"
#include "ExPolygonCollection.hpp" #include "ExPolygon.hpp"
#include "ClipperUtils.hpp" #include "ClipperUtils.hpp"
#include "Extruder.hpp" #include "Extruder.hpp"
#include "Flow.hpp" #include "Flow.hpp"
@ -12,14 +12,14 @@
namespace Slic3r { namespace Slic3r {
void ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const void ExtrusionPath::intersect_expolygons(const ExPolygons &collection, ExtrusionEntityCollection* retval) const
{ {
this->_inflate_collection(intersection_pl(Polylines{ polyline }, collection.expolygons), retval); this->_inflate_collection(intersection_pl(Polylines{ polyline }, collection), retval);
} }
void ExtrusionPath::subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const void ExtrusionPath::subtract_expolygons(const ExPolygons &collection, ExtrusionEntityCollection* retval) const
{ {
this->_inflate_collection(diff_pl(Polylines{ this->polyline }, collection.expolygons), retval); this->_inflate_collection(diff_pl(Polylines{ this->polyline }, collection), retval);
} }
void ExtrusionPath::clip_end(double distance) void ExtrusionPath::clip_end(double distance)

View file

@ -11,7 +11,8 @@
namespace Slic3r { namespace Slic3r {
class ExPolygonCollection; class ExPolygon;
using ExPolygons = std::vector<ExPolygon>;
class ExtrusionEntityCollection; class ExtrusionEntityCollection;
class Extruder; class Extruder;
@ -178,12 +179,12 @@ public:
size_t size() const { return this->polyline.size(); } size_t size() const { return this->polyline.size(); }
bool empty() const { return this->polyline.empty(); } bool empty() const { return this->polyline.empty(); }
bool is_closed() const { return ! this->empty() && this->polyline.points.front() == this->polyline.points.back(); } bool is_closed() const { return ! this->empty() && this->polyline.points.front() == this->polyline.points.back(); }
// Produce a list of extrusion paths into retval by clipping this path by ExPolygonCollection. // Produce a list of extrusion paths into retval by clipping this path by ExPolygons.
// Currently not used. // Currently not used.
void intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const; void intersect_expolygons(const ExPolygons &collection, ExtrusionEntityCollection* retval) const;
// Produce a list of extrusion paths into retval by removing parts of this path by ExPolygonCollection. // Produce a list of extrusion paths into retval by removing parts of this path by ExPolygons.
// Currently not used. // Currently not used.
void subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const; void subtract_expolygons(const ExPolygons &collection, ExtrusionEntityCollection* retval) const;
void clip_end(double distance); void clip_end(double distance);
void simplify(double tolerance); void simplify(double tolerance);
double length() const override; double length() const override;

View file

@ -415,7 +415,7 @@ public:
// bool sticks_removed = // bool sticks_removed =
remove_sticks(polygons_src); remove_sticks(polygons_src);
// if (sticks_removed) BOOST_LOG_TRIVIAL(error) << "Sticks removed!"; // if (sticks_removed) BOOST_LOG_TRIVIAL(error) << "Sticks removed!";
polygons_outer = aoffset1 == 0 ? polygons_src : offset(polygons_src, float(aoffset1), ClipperLib::jtMiter, miterLimit); polygons_outer = aoffset1 == 0 ? to_polygons(polygons_src) : offset(polygons_src, float(aoffset1), ClipperLib::jtMiter, miterLimit);
if (aoffset2 < 0) if (aoffset2 < 0)
polygons_inner = shrink(polygons_outer, float(aoffset1 - aoffset2), ClipperLib::jtMiter, miterLimit); polygons_inner = shrink(polygons_outer, float(aoffset1 - aoffset2), ClipperLib::jtMiter, miterLimit);
// Filter out contours with zero area or small area, contours with 2 points only. // Filter out contours with zero area or small area, contours with 2 points only.
@ -3165,7 +3165,7 @@ void FillMonotonicLineWGapFill::fill_surface_extrusion(const Surface* surface, c
for (ExPolygon& ex : gaps_ex_sorted) { for (ExPolygon& ex : gaps_ex_sorted) {
//BBS: Use DP simplify to avoid duplicated points and accelerate medial-axis calculation as well. //BBS: Use DP simplify to avoid duplicated points and accelerate medial-axis calculation as well.
ex.douglas_peucker(SCALED_RESOLUTION * 0.1); ex.douglas_peucker(SCALED_RESOLUTION * 0.1);
ex.medial_axis(max, min, &polylines); ex.medial_axis(min, max, &polylines);
} }
if (!polylines.empty() && !is_bridge(params.extrusion_role)) { if (!polylines.empty() && !is_bridge(params.extrusion_role)) {

View file

@ -4035,10 +4035,12 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role, LiftTyp
if (role == erSupportMaterial || role == erSupportTransition) { if (role == erSupportMaterial || role == erSupportTransition) {
const SupportLayer* support_layer = dynamic_cast<const SupportLayer*>(m_layer); const SupportLayer* support_layer = dynamic_cast<const SupportLayer*>(m_layer);
//FIXME support_layer->support_islands.contains should use some search structure! //FIXME support_layer->support_islands.contains should use some search structure!
if (support_layer != NULL && support_layer->support_islands.contains(travel)) if (support_layer != NULL)
// skip retraction if this is a travel move inside a support material island // skip retraction if this is a travel move inside a support material island
//FIXME not retracting over a long path may cause oozing, which in turn may result in missing material //FIXME not retracting over a long path may cause oozing, which in turn may result in missing material
// at the end of the extrusion path! // at the end of the extrusion path!
for (const ExPolygon& support_island : support_layer->support_islands)
if (support_island.contains(travel))
return false; return false;
//reduce the retractions in lightning infills for tree support //reduce the retractions in lightning infills for tree support
if (support_layer != NULL && support_layer->support_type==stInnerTree) if (support_layer != NULL && support_layer->support_type==stInnerTree)

View file

@ -1010,7 +1010,7 @@ static ExPolygons get_boundary(const Layer &layer)
ExPolygons boundary = union_ex(inner_offset(layer.lslices, 1.5 * perimeter_spacing)); ExPolygons boundary = union_ex(inner_offset(layer.lslices, 1.5 * perimeter_spacing));
if(support_layer) { if(support_layer) {
#ifdef INCLUDE_SUPPORTS_IN_BOUNDARY #ifdef INCLUDE_SUPPORTS_IN_BOUNDARY
append(boundary, inner_offset(support_layer->support_islands.expolygons, 1.5 * perimeter_spacing)); append(boundary, inner_offset(support_layer->support_islands, 1.5 * perimeter_spacing));
#endif #endif
auto *layer_below = layer.object()->get_first_layer_bellow_printz(layer.print_z, EPSILON); auto *layer_below = layer.object()->get_first_layer_bellow_printz(layer.print_z, EPSILON);
if (layer_below) if (layer_below)
@ -1063,7 +1063,7 @@ static Polygons get_boundary_external(const Layer &layer)
for (const ExPolygon &island : layer_below->lslices) for (const ExPolygon &island : layer_below->lslices)
append(holes_per_obj, island.holes); append(holes_per_obj, island.holes);
#ifdef INCLUDE_SUPPORTS_IN_BOUNDARY #ifdef INCLUDE_SUPPORTS_IN_BOUNDARY
append(supports_per_obj, support_layer->support_islands.expolygons); append(supports_per_obj, support_layer->support_islands);
#endif #endif
} }

View file

@ -1,6 +1,7 @@
#include "libslic3r.h" #include "libslic3r.h"
#include "ConvexHull.hpp" #include "ConvexHull.hpp"
#include "BoundingBox.hpp" #include "BoundingBox.hpp"
#include "../Geometry.hpp"
#include <boost/multiprecision/integer.hpp> #include <boost/multiprecision/integer.hpp>
@ -19,13 +20,13 @@ Polygon convex_hull(Points pts)
hull.points.resize(2 * n); hull.points.resize(2 * n);
// Build lower hull // Build lower hull
for (int i = 0; i < n; ++ i) { for (int i = 0; i < n; ++ i) {
while (k >= 2 && pts[i].ccw(hull[k-2], hull[k-1]) <= 0) while (k >= 2 && Geometry::orient(pts[i], hull[k-2], hull[k-1]) != Geometry::ORIENTATION_CCW)
-- k; -- k;
hull[k ++] = pts[i]; hull[k ++] = pts[i];
} }
// Build upper hull // Build upper hull
for (int i = n-2, t = k+1; i >= 0; i--) { for (int i = n-2, t = k+1; i >= 0; i--) {
while (k >= t && pts[i].ccw(hull[k-2], hull[k-1]) <= 0) while (k >= t && Geometry::orient(pts[i], hull[k-2], hull[k-1]) != Geometry::ORIENTATION_CCW)
-- k; -- k;
hull[k ++] = pts[i]; hull[k ++] = pts[i];
} }
@ -58,7 +59,7 @@ Pointf3s convex_hull(Pointf3s points)
Point k1 = Point::new_scale(hull[k - 1](0), hull[k - 1](1)); Point k1 = Point::new_scale(hull[k - 1](0), hull[k - 1](1));
Point k2 = Point::new_scale(hull[k - 2](0), hull[k - 2](1)); Point k2 = Point::new_scale(hull[k - 2](0), hull[k - 2](1));
if (p.ccw(k2, k1) <= 0) if (Geometry::orient(p, k2, k1) != Geometry::ORIENTATION_CCW)
--k; --k;
else else
break; break;
@ -76,7 +77,7 @@ Pointf3s convex_hull(Pointf3s points)
Point k1 = Point::new_scale(hull[k - 1](0), hull[k - 1](1)); Point k1 = Point::new_scale(hull[k - 1](0), hull[k - 1](1));
Point k2 = Point::new_scale(hull[k - 2](0), hull[k - 2](1)); Point k2 = Point::new_scale(hull[k - 2](0), hull[k - 2](1));
if (p.ccw(k2, k1) <= 0) if (Geometry::orient(p, k2, k1) != Geometry::ORIENTATION_CCW)
--k; --k;
else else
break; break;
@ -103,6 +104,29 @@ Polygon convex_hull(const Polygons &polygons)
return convex_hull(std::move(pp)); return convex_hull(std::move(pp));
} }
Polygon convex_hull(const ExPolygons &expolygons)
{
Points pp;
size_t sz = 0;
for (const auto &expoly : expolygons)
sz += expoly.contour.size();
pp.reserve(sz);
for (const auto &expoly : expolygons)
pp.insert(pp.end(), expoly.contour.points.begin(), expoly.contour.points.end());
return convex_hull(pp);
}
Polygon convex_hulll(const Polylines &polylines)
{
Points pp;
size_t sz = 0;
for (const auto &polyline : polylines)
sz += polyline.points.size();
pp.reserve(sz);
for (const auto &polyline : polylines)
pp.insert(pp.end(), polyline.points.begin(), polyline.points.end());
return convex_hull(pp);
}
namespace rotcalip { namespace rotcalip {
@ -374,7 +398,7 @@ bool inside_convex_polygon(const std::pair<std::vector<Vec2d>, std::vector<Vec2d
// At min x. // At min x.
assert(pt.x() == it_bottom->x()); assert(pt.x() == it_bottom->x());
assert(pt.x() == it_top->x()); assert(pt.x() == it_top->x());
assert(it_bottom->y() <= pt.y() <= it_top->y()); assert(it_bottom->y() <= pt.y() && pt.y() <= it_top->y());
return pt.y() >= it_bottom->y() && pt.y() <= it_top->y(); return pt.y() >= it_bottom->y() && pt.y() <= it_top->y();
} }

View file

@ -1,14 +1,22 @@
#ifndef slic3r_Geometry_ConvexHull_hpp_ #ifndef slic3r_Geometry_ConvexHull_hpp_
#define slic3r_Geometry_ConvexHull_hpp_ #define slic3r_Geometry_ConvexHull_hpp_
#include <vector>
#include "../Polygon.hpp" #include "../Polygon.hpp"
namespace Slic3r { namespace Slic3r {
class ExPolygon;
using ExPolygons = std::vector<ExPolygon>;
namespace Geometry { namespace Geometry {
Pointf3s convex_hull(Pointf3s points); Pointf3s convex_hull(Pointf3s points);
Polygon convex_hull(Points points); Polygon convex_hull(Points points);
Polygon convex_hull(const Polygons &polygons); Polygon convex_hull(const Polygons &polygons);
Polygon convex_hull(const ExPolygons &expolygons);
Polygon convex_hulll(const Polylines &polylines);
// Returns true if the intersection of the two convex polygons A and B // Returns true if the intersection of the two convex polygons A and B
// is not an empty set. // is not an empty set.

View file

@ -1,6 +1,7 @@
#include "MedialAxis.hpp" #include "MedialAxis.hpp"
#include "clipper.hpp" #include "clipper.hpp"
#include "VoronoiOffset.hpp"
#ifdef SLIC3R_DEBUG #ifdef SLIC3R_DEBUG
namespace boost { namespace polygon { namespace boost { namespace polygon {
@ -392,8 +393,7 @@ inline const typename VD::point_type retrieve_cell_point(const typename VD::cell
} }
template<typename VD, typename SEGMENTS> template<typename VD, typename SEGMENTS>
inline std::pair<typename VD::coord_type, typename VD::coord_type> inline std::pair<typename VD::coord_type, typename VD::coord_type> measure_edge_thickness(const VD &vd, const typename VD::edge_type& edge, const SEGMENTS &segments)
measure_edge_thickness(const VD &vd, const typename VD::edge_type& edge, const SEGMENTS &segments)
{ {
typedef typename VD::coord_type T; typedef typename VD::coord_type T;
const typename VD::point_type pa(edge.vertex0()->x(), edge.vertex0()->y()); const typename VD::point_type pa(edge.vertex0()->x(), edge.vertex0()->y());
@ -442,15 +442,21 @@ private:
const Lines &lines; const Lines &lines;
}; };
void MedialAxis::MedialAxis(double min_width, double max_width, const ExPolygon &expolygon) :
MedialAxis::build(ThickPolylines* polylines) m_expolygon(expolygon), m_lines(expolygon.lines()), m_min_width(min_width), m_max_width(max_width)
{}
void MedialAxis::build(ThickPolylines* polylines)
{ {
construct_voronoi(this->lines.begin(), this->lines.end(), &this->vd); construct_voronoi(m_lines.begin(), m_lines.end(), &m_vd);
Slic3r::Voronoi::annotate_inside_outside(m_vd, m_lines);
// static constexpr double threshold_alpha = M_PI / 12.; // 30 degrees
// std::vector<Vec2d> skeleton_edges = Slic3r::Voronoi::skeleton_edges_rough(vd, lines, threshold_alpha);
/* /*
// DEBUG: dump all Voronoi edges // DEBUG: dump all Voronoi edges
{ {
for (VD::const_edge_iterator edge = this->vd.edges().begin(); edge != this->vd.edges().end(); ++edge) { for (VD::const_edge_iterator edge = m_vd.edges().begin(); edge != m_vd.edges().end(); ++edge) {
if (edge->is_infinite()) continue; if (edge->is_infinite()) continue;
ThickPolyline polyline; ThickPolyline polyline;
@ -462,74 +468,57 @@ MedialAxis::build(ThickPolylines* polylines)
} }
*/ */
//typedef const VD::vertex_type vert_t;
typedef const VD::edge_type edge_t;
// collect valid edges (i.e. prune those not belonging to MAT) // collect valid edges (i.e. prune those not belonging to MAT)
// note: this keeps twins, so it inserts twice the number of the valid edges // note: this keeps twins, so it inserts twice the number of the valid edges
this->valid_edges.clear(); m_edge_data.assign(m_vd.edges().size() / 2, EdgeData{});
{ for (VD::const_edge_iterator edge = m_vd.edges().begin(); edge != m_vd.edges().end(); edge += 2)
std::set<const VD::edge_type*> seen_edges; if (edge->is_primary() && edge->is_finite() &&
for (VD::const_edge_iterator edge = this->vd.edges().begin(); edge != this->vd.edges().end(); ++edge) { (Voronoi::vertex_category(edge->vertex0()) == Voronoi::VertexCategory::Inside ||
// if we only process segments representing closed loops, none if the Voronoi::vertex_category(edge->vertex1()) == Voronoi::VertexCategory::Inside) &&
// infinite edges (if any) would be part of our MAT anyway this->validate_edge(&*edge)) {
if (edge->is_secondary() || edge->is_infinite()) continue; // Valid skeleton edge.
this->edge_data(*edge).first.active = true;
// don't re-validate twins
if (seen_edges.find(&*edge) != seen_edges.end()) continue; // TODO: is this needed?
seen_edges.insert(&*edge);
seen_edges.insert(edge->twin());
if (!this->validate_edge(&*edge)) continue;
this->valid_edges.insert(&*edge);
this->valid_edges.insert(edge->twin());
} }
}
this->edges = this->valid_edges;
// iterate through the valid edges to build polylines // iterate through the valid edges to build polylines
while (!this->edges.empty()) { ThickPolyline reverse_polyline;
const edge_t* edge = *this->edges.begin(); for (VD::const_edge_iterator seed_edge = m_vd.edges().begin(); seed_edge != m_vd.edges().end(); seed_edge += 2)
if (EdgeData &seed_edge_data = this->edge_data(*seed_edge).first; seed_edge_data.active) {
// Mark this edge as visited.
seed_edge_data.active = false;
// start a polyline // Start a polyline.
ThickPolyline polyline; ThickPolyline polyline;
polyline.points.push_back(Point( edge->vertex0()->x(), edge->vertex0()->y() )); polyline.points.emplace_back(seed_edge->vertex0()->x(), seed_edge->vertex0()->y());
polyline.points.push_back(Point( edge->vertex1()->x(), edge->vertex1()->y() )); polyline.points.emplace_back(seed_edge->vertex1()->x(), seed_edge->vertex1()->y());
polyline.width.push_back(this->thickness[edge].first); polyline.width.emplace_back(seed_edge_data.width_start);
polyline.width.push_back(this->thickness[edge].second); polyline.width.emplace_back(seed_edge_data.width_end);
// Grow the polyline in a forward direction.
this->process_edge_neighbors(&*seed_edge, &polyline);
assert(polyline.width.size() == polyline.points.size() * 2 - 2);
// remove this edge and its twin from the available edges // Grow the polyline in a backward direction.
(void)this->edges.erase(edge); reverse_polyline.clear();
(void)this->edges.erase(edge->twin()); this->process_edge_neighbors(seed_edge->twin(), &reverse_polyline);
polyline.points.insert(polyline.points.begin(), reverse_polyline.points.rbegin(), reverse_polyline.points.rend());
polyline.width.insert(polyline.width.begin(), reverse_polyline.width.rbegin(), reverse_polyline.width.rend());
polyline.endpoints.first = reverse_polyline.endpoints.second;
assert(polyline.width.size() == polyline.points.size() * 2 - 2);
// get next points // Prevent loop endpoints from being extended.
this->process_edge_neighbors(edge, &polyline);
// get previous points
{
ThickPolyline rpolyline;
this->process_edge_neighbors(edge->twin(), &rpolyline);
polyline.points.insert(polyline.points.begin(), rpolyline.points.rbegin(), rpolyline.points.rend());
polyline.width.insert(polyline.width.begin(), rpolyline.width.rbegin(), rpolyline.width.rend());
polyline.endpoints.first = rpolyline.endpoints.second;
}
assert(polyline.width.size() == polyline.points.size()*2 - 2);
// prevent loop endpoints from being extended
if (polyline.first_point() == polyline.last_point()) { if (polyline.first_point() == polyline.last_point()) {
polyline.endpoints.first = false; polyline.endpoints.first = false;
polyline.endpoints.second = false; polyline.endpoints.second = false;
} }
// append polyline to result // Append polyline to result.
polylines->push_back(polyline); polylines->emplace_back(std::move(polyline));
} }
#ifdef SLIC3R_DEBUG #ifdef SLIC3R_DEBUG
{ {
static int iRun = 0; static int iRun = 0;
dump_voronoi_to_svg(this->lines, this->vd, polylines, debug_out_path("MedialAxis-%d.svg", iRun ++).c_str()); dump_voronoi_to_svg(m_lines, m_vd, polylines, debug_out_path("MedialAxis-%d.svg", iRun ++).c_str());
printf("Thick lines: "); printf("Thick lines: ");
for (ThickPolylines::const_iterator it = polylines->begin(); it != polylines->end(); ++ it) { for (ThickPolylines::const_iterator it = polylines->begin(); it != polylines->end(); ++ it) {
ThickLines lines = it->thicklines(); ThickLines lines = it->thicklines();
@ -542,56 +531,68 @@ MedialAxis::build(ThickPolylines* polylines)
#endif /* SLIC3R_DEBUG */ #endif /* SLIC3R_DEBUG */
} }
void void MedialAxis::build(Polylines* polylines)
MedialAxis::build(Polylines* polylines)
{ {
ThickPolylines tp; ThickPolylines tp;
this->build(&tp); this->build(&tp);
polylines->insert(polylines->end(), tp.begin(), tp.end()); polylines->reserve(polylines->size() + tp.size());
for (auto &pl : tp)
polylines->emplace_back(pl.points);
} }
void void MedialAxis::process_edge_neighbors(const VD::edge_type *edge, ThickPolyline* polyline)
MedialAxis::process_edge_neighbors(const VD::edge_type* edge, ThickPolyline* polyline)
{ {
while (true) { for (;;) {
// Since rot_next() works on the edge starting point but we want // Since rot_next() works on the edge starting point but we want
// to find neighbors on the ending point, we just swap edge with // to find neighbors on the ending point, we just swap edge with
// its twin. // its twin.
const VD::edge_type* twin = edge->twin(); const VD::edge_type *twin = edge->twin();
// count neighbors for this edge // count neighbors for this edge
std::vector<const VD::edge_type*> neighbors; size_t num_neighbors = 0;
for (const VD::edge_type* neighbor = twin->rot_next(); neighbor != twin; const VD::edge_type *first_neighbor = nullptr;
neighbor = neighbor->rot_next()) { for (const VD::edge_type *neighbor = twin->rot_next(); neighbor != twin; neighbor = neighbor->rot_next())
if (this->valid_edges.count(neighbor) > 0) neighbors.push_back(neighbor); if (this->edge_data(*neighbor).first.active) {
if (num_neighbors == 0)
first_neighbor = neighbor;
++ num_neighbors;
} }
// if we have a single neighbor then we can continue recursively // if we have a single neighbor then we can continue recursively
if (neighbors.size() == 1) { if (num_neighbors == 1) {
const VD::edge_type* neighbor = neighbors.front(); if (std::pair<EdgeData&, bool> neighbor_data = this->edge_data(*first_neighbor);
neighbor_data.first.active) {
// break if this is a closed loop neighbor_data.first.active = false;
if (this->edges.count(neighbor) == 0) return; polyline->points.emplace_back(first_neighbor->vertex1()->x(), first_neighbor->vertex1()->y());
if (neighbor_data.second) {
Point new_point(neighbor->vertex1()->x(), neighbor->vertex1()->y()); polyline->width.push_back(neighbor_data.first.width_end);
polyline->points.push_back(new_point); polyline->width.push_back(neighbor_data.first.width_start);
polyline->width.push_back(this->thickness[neighbor].first); } else {
polyline->width.push_back(this->thickness[neighbor].second); polyline->width.push_back(neighbor_data.first.width_start);
(void)this->edges.erase(neighbor); polyline->width.push_back(neighbor_data.first.width_end);
(void)this->edges.erase(neighbor->twin()); }
edge = neighbor; edge = first_neighbor;
} else if (neighbors.size() == 0) { // Continue chaining.
continue;
}
} else if (num_neighbors == 0) {
polyline->endpoints.second = true; polyline->endpoints.second = true;
return;
} else { } else {
// T-shaped or star-shaped joint // T-shaped or star-shaped joint
return;
} }
// Stop chaining.
break;
} }
} }
bool MedialAxis::validate_edge(const VD::edge_type* edge) bool MedialAxis::validate_edge(const VD::edge_type* edge)
{ {
auto retrieve_segment = [this](const VD::cell_type* cell) -> const Line& { return m_lines[cell->source_index()]; };
auto retrieve_endpoint = [retrieve_segment](const VD::cell_type* cell) -> const Point& {
const Line &line = retrieve_segment(cell);
return cell->source_category() == boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT ? line.a : line.b;
};
// prevent overflows and detect almost-infinite edges // prevent overflows and detect almost-infinite edges
#ifndef CLIPPERLIB_INT32 #ifndef CLIPPERLIB_INT32
if (std::abs(edge->vertex0()->x()) > double(CLIPPER_MAX_COORD_UNSCALED) || if (std::abs(edge->vertex0()->x()) > double(CLIPPER_MAX_COORD_UNSCALED) ||
@ -602,32 +603,18 @@ bool MedialAxis::validate_edge(const VD::edge_type* edge)
#endif // CLIPPERLIB_INT32 #endif // CLIPPERLIB_INT32
// construct the line representing this edge of the Voronoi diagram // construct the line representing this edge of the Voronoi diagram
const Line line( const Line line({ edge->vertex0()->x(), edge->vertex0()->y() },
Point( edge->vertex0()->x(), edge->vertex0()->y() ), { edge->vertex1()->x(), edge->vertex1()->y() });
Point( edge->vertex1()->x(), edge->vertex1()->y() )
);
// discard edge if it lies outside the supplied shape
// this could maybe be optimized (checking inclusion of the endpoints
// might give false positives as they might belong to the contour itself)
if (this->expolygon != NULL) {
if (line.a == line.b) {
// in this case, contains(line) returns a false positive
if (!this->expolygon->contains(line.a)) return false;
} else {
if (!this->expolygon->contains(line)) return false;
}
}
// retrieve the original line segments which generated the edge we're checking // retrieve the original line segments which generated the edge we're checking
const VD::cell_type* cell_l = edge->cell(); const VD::cell_type* cell_l = edge->cell();
const VD::cell_type* cell_r = edge->twin()->cell(); const VD::cell_type* cell_r = edge->twin()->cell();
const Line &segment_l = this->retrieve_segment(cell_l); const Line &segment_l = retrieve_segment(cell_l);
const Line &segment_r = this->retrieve_segment(cell_r); const Line &segment_r = retrieve_segment(cell_r);
/* /*
SVG svg("edge.svg"); SVG svg("edge.svg");
svg.draw(*this->expolygon); svg.draw(m_expolygon);
svg.draw(line); svg.draw(line);
svg.draw(segment_l, "red"); svg.draw(segment_l, "red");
svg.draw(segment_r, "blue"); svg.draw(segment_r, "blue");
@ -651,16 +638,17 @@ bool MedialAxis::validate_edge(const VD::edge_type* edge)
coordf_t w0 = cell_r->contains_segment() coordf_t w0 = cell_r->contains_segment()
? segment_r.distance_to(line.a)*2 ? segment_r.distance_to(line.a)*2
: (this->retrieve_endpoint(cell_r) - line.a).cast<double>().norm()*2; : (retrieve_endpoint(cell_r) - line.a).cast<double>().norm()*2;
coordf_t w1 = cell_l->contains_segment() coordf_t w1 = cell_l->contains_segment()
? segment_l.distance_to(line.b)*2 ? segment_l.distance_to(line.b)*2
: (this->retrieve_endpoint(cell_l) - line.b).cast<double>().norm()*2; : (retrieve_endpoint(cell_l) - line.b).cast<double>().norm()*2;
if (cell_l->contains_segment() && cell_r->contains_segment()) { if (cell_l->contains_segment() && cell_r->contains_segment()) {
// calculate the relative angle between the two boundary segments // calculate the relative angle between the two boundary segments
double angle = fabs(segment_r.orientation() - segment_l.orientation()); double angle = fabs(segment_r.orientation() - segment_l.orientation());
if (angle > PI) angle = 2*PI - angle; if (angle > PI)
angle = 2. * PI - angle;
assert(angle >= 0 && angle <= PI); assert(angle >= 0 && angle <= PI);
// fabs(angle) ranges from 0 (collinear, same direction) to PI (collinear, opposite direction) // fabs(angle) ranges from 0 (collinear, same direction) to PI (collinear, opposite direction)
@ -669,44 +657,29 @@ bool MedialAxis::validate_edge(const VD::edge_type* edge)
// this filter ensures that we're dealing with a narrow/oriented area (longer than thick) // this filter ensures that we're dealing with a narrow/oriented area (longer than thick)
// we don't run it on edges not generated by two segments (thus generated by one segment // we don't run it on edges not generated by two segments (thus generated by one segment
// and the endpoint of another segment), since their orientation would not be meaningful // and the endpoint of another segment), since their orientation would not be meaningful
if (PI - angle > PI/8) { if (PI - angle > PI / 8.) {
// angle is not narrow enough // angle is not narrow enough
// only apply this filter to segments that are not too short otherwise their // only apply this filter to segments that are not too short otherwise their
// angle could possibly be not meaningful // angle could possibly be not meaningful
if (w0 < SCALED_EPSILON || w1 < SCALED_EPSILON || line.length() >= this->min_width) if (w0 < SCALED_EPSILON || w1 < SCALED_EPSILON || line.length() >= m_min_width)
return false; return false;
} }
} else { } else {
if (w0 < SCALED_EPSILON || w1 < SCALED_EPSILON) if (w0 < SCALED_EPSILON || w1 < SCALED_EPSILON)
return false; return false;
} }
//BBS
if (w0 < this->min_width || w1 < this->min_width)
return false;
//BBS
if (w0 > this->max_width || w1 > this->max_width)
return false;
this->thickness[edge] = std::make_pair(w0, w1);
this->thickness[edge->twin()] = std::make_pair(w1, w0);
if ((w0 >= m_min_width || w1 >= m_min_width) &&
(w0 <= m_max_width || w1 <= m_max_width)) {
std::pair<EdgeData&, bool> ed = this->edge_data(*edge);
if (ed.second)
std::swap(w0, w1);
ed.first.width_start = w0;
ed.first.width_end = w1;
return true; return true;
}
const Line& MedialAxis::retrieve_segment(const VD::cell_type* cell) const
{
return this->lines[cell->source_index()];
}
const Point& MedialAxis::retrieve_endpoint(const VD::cell_type* cell) const
{
const Line& line = this->retrieve_segment(cell);
if (cell->source_category() == boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT) {
return line.a;
} else {
return line.b;
} }
return false;
} }
} } // namespace Slicer::Geometry } } // namespace Slicer::Geometry

View file

@ -4,30 +4,43 @@
#include "Voronoi.hpp" #include "Voronoi.hpp"
#include "../ExPolygon.hpp" #include "../ExPolygon.hpp"
namespace Slic3r { namespace Geometry { namespace Slic3r::Geometry {
class MedialAxis { class MedialAxis {
public: public:
Lines lines; MedialAxis(double min_width, double max_width, const ExPolygon &expolygon);
const ExPolygon* expolygon;
double max_width;
double min_width;
MedialAxis(double _max_width, double _min_width, const ExPolygon* _expolygon = NULL)
: expolygon(_expolygon), max_width(_max_width), min_width(_min_width) {};
void build(ThickPolylines* polylines); void build(ThickPolylines* polylines);
void build(Polylines* polylines); void build(Polylines* polylines);
private: private:
// Input
const ExPolygon &m_expolygon;
Lines m_lines;
// for filtering of the skeleton edges
double m_min_width;
double m_max_width;
// Voronoi Diagram.
using VD = VoronoiDiagram; using VD = VoronoiDiagram;
VD vd; VD m_vd;
std::set<const VD::edge_type*> edges, valid_edges;
std::map<const VD::edge_type*, std::pair<coordf_t,coordf_t> > thickness; // Annotations of the VD skeleton edges.
struct EdgeData {
bool active { false };
double width_start { 0 };
double width_end { 0 };
};
// Returns a reference to EdgeData and a "reversed" boolean.
std::pair<EdgeData&, bool> edge_data(const VD::edge_type &edge) {
size_t edge_id = &edge - &m_vd.edges().front();
return { m_edge_data[edge_id / 2], (edge_id & 1) != 0 };
}
std::vector<EdgeData> m_edge_data;
void process_edge_neighbors(const VD::edge_type* edge, ThickPolyline* polyline); void process_edge_neighbors(const VD::edge_type* edge, ThickPolyline* polyline);
bool validate_edge(const VD::edge_type* edge); bool validate_edge(const VD::edge_type* edge);
const Line& retrieve_segment(const VD::cell_type* cell) const;
const Point& retrieve_endpoint(const VD::cell_type* cell) const;
}; };
} } // namespace Slicer::Geometry } // namespace Slicer::Geometry
#endif // slic3r_Geometry_MedialAxis_hpp_ #endif // slic3r_Geometry_MedialAxis_hpp_

View file

@ -5,9 +5,11 @@
#include "Flow.hpp" #include "Flow.hpp"
#include "SurfaceCollection.hpp" #include "SurfaceCollection.hpp"
#include "ExtrusionEntityCollection.hpp" #include "ExtrusionEntityCollection.hpp"
#include "ExPolygonCollection.hpp"
namespace Slic3r { namespace Slic3r {
class ExPolygon;
using ExPolygons = std::vector<ExPolygon>;
class Layer; class Layer;
using LayerPtrs = std::vector<Layer*>; using LayerPtrs = std::vector<Layer*>;
class LayerRegion; class LayerRegion;
@ -226,7 +228,7 @@ class SupportLayer : public Layer
public: public:
// Polygons covered by the supports: base, interface and contact areas. // Polygons covered by the supports: base, interface and contact areas.
// Used to suppress retraction if moving for a support extrusion over these support_islands. // Used to suppress retraction if moving for a support extrusion over these support_islands.
ExPolygonCollection support_islands; ExPolygons support_islands;
// Extrusion paths for the support base and for the support interface and contacts. // Extrusion paths for the support base and for the support interface and contacts.
ExtrusionEntityCollection support_fills; ExtrusionEntityCollection support_fills;
SupportInnerType support_type = stInnerNormal; SupportInnerType support_type = stInnerNormal;

View file

@ -29,7 +29,14 @@ bool Line::intersection_infinite(const Line &other, Point* point) const
if (std::fabs(denom) < EPSILON) if (std::fabs(denom) < EPSILON)
return false; return false;
double t1 = cross2(v12, v2) / denom; double t1 = cross2(v12, v2) / denom;
*point = (a1 + t1 * v1).cast<coord_t>(); Vec2d result = (a1 + t1 * v1);
if (result.x() > std::numeric_limits<coord_t>::max() || result.x() < std::numeric_limits<coord_t>::lowest() ||
result.y() > std::numeric_limits<coord_t>::max() || result.y() < std::numeric_limits<coord_t>::lowest()) {
// Intersection has at least one of the coordinates much bigger (or smaller) than coord_t maximum value (or minimum).
// So it can not be stored into the Point without integer overflows. That could mean that input lines are parallel or near parallel.
return false;
}
*point = (result).cast<coord_t>();
return true; return true;
} }
@ -84,28 +91,7 @@ bool Line::perpendicular_to(const Line& line) const
bool Line::intersection(const Line &l2, Point *intersection) const bool Line::intersection(const Line &l2, Point *intersection) const
{ {
const Line &l1 = *this; return line_alg::intersection(*this, l2, intersection);
const Vec2d v1 = (l1.b - l1.a).cast<double>();
const Vec2d v2 = (l2.b - l2.a).cast<double>();
double denom = cross2(v1, v2);
if (fabs(denom) < EPSILON)
#if 0
// Lines are collinear. Return true if they are coincident (overlappign).
return ! (fabs(nume_a) < EPSILON && fabs(nume_b) < EPSILON);
#else
return false;
#endif
const Vec2d v12 = (l1.a - l2.a).cast<double>();
double nume_a = cross2(v2, v12);
double nume_b = cross2(v1, v12);
double t1 = nume_a / denom;
double t2 = nume_b / denom;
if (t1 >= 0 && t1 <= 1.0f && t2 >= 0 && t2 <= 1.0f) {
// Get the intersection point.
(*intersection) = (l1.a.cast<double>() + t1 * v1).cast<coord_t>();
return true;
}
return false; // not intersecting
} }
bool Line::clip_with_bbox(const BoundingBox &bbox) bool Line::clip_with_bbox(const BoundingBox &bbox)

View file

@ -179,7 +179,6 @@ public:
Vector vector() const { return this->b - this->a; } Vector vector() const { return this->b - this->a; }
Vector normal() const { return Vector((this->b(1) - this->a(1)), -(this->b(0) - this->a(0))); } Vector normal() const { return Vector((this->b(1) - this->a(1)), -(this->b(0) - this->a(0))); }
bool intersection(const Line& line, Point* intersection) const; bool intersection(const Line& line, Point* intersection) const;
double ccw(const Point& point) const { return point.ccw(*this); }
// Clip a line with a bounding box. Returns false if the line is completely outside of the bounding box. // Clip a line with a bounding box. Returns false if the line is completely outside of the bounding box.
bool clip_with_bbox(const BoundingBox &bbox); bool clip_with_bbox(const BoundingBox &bbox);
// Extend the line from both sides by an offset. // Extend the line from both sides by an offset.
@ -238,6 +237,7 @@ public:
static const constexpr int Dim = 2; static const constexpr int Dim = 2;
using Scalar = Vec2d::Scalar; using Scalar = Vec2d::Scalar;
}; };
using Linesf = std::vector<Linef>;
class Linef3 class Linef3
{ {

View file

@ -722,7 +722,7 @@ void PerimeterGenerator::process_classic()
float(min_width / 2.)); float(min_width / 2.));
// the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop // the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop
for (ExPolygon &ex : expp) for (ExPolygon &ex : expp)
ex.medial_axis(ext_perimeter_width + ext_perimeter_spacing2, min_width, &thin_walls); ex.medial_axis(min_width, ext_perimeter_width + ext_perimeter_spacing2, &thin_walls);
} else { } else {
coord_t ext_perimeter_smaller_width = this->smaller_ext_perimeter_flow.scaled_width(); coord_t ext_perimeter_smaller_width = this->smaller_ext_perimeter_flow.scaled_width();
for (const ExPolygon& expolygon : last) { for (const ExPolygon& expolygon : last) {
@ -1004,7 +1004,7 @@ void PerimeterGenerator::process_classic()
for (ExPolygon& ex : gaps_ex) { for (ExPolygon& ex : gaps_ex) {
//BBS: Use DP simplify to avoid duplicated points and accelerate medial-axis calculation as well. //BBS: Use DP simplify to avoid duplicated points and accelerate medial-axis calculation as well.
ex.douglas_peucker(surface_simplify_resolution); ex.douglas_peucker(surface_simplify_resolution);
ex.medial_axis(max, min, &polylines); ex.medial_axis(min, max, &polylines);
} }
#ifdef GAPS_OF_PERIMETER_DEBUG_TO_SVG #ifdef GAPS_OF_PERIMETER_DEBUG_TO_SVG

View file

@ -6,6 +6,17 @@
namespace Slic3r { namespace Slic3r {
double Polygon::length() const
{
double l = 0;
if (this->points.size() > 1) {
l = (this->points.back() - this->points.front()).cast<double>().norm();
for (size_t i = 1; i < this->points.size(); ++ i)
l += (this->points[i] - this->points[i - 1]).cast<double>().norm();
}
return l;
}
Lines Polygon::lines() const Lines Polygon::lines() const
{ {
return to_lines(*this); return to_lines(*this);
@ -88,36 +99,11 @@ void Polygon::douglas_peucker(double tolerance)
this->points = std::move(p); this->points = std::move(p);
} }
// Does an unoriented polygon contain a point?
// Tested by counting intersections along a horizontal line.
bool Polygon::contains(const Point &point) const
{
// http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
bool result = false;
Points::const_iterator i = this->points.begin();
Points::const_iterator j = this->points.end() - 1;
for (; i != this->points.end(); j = i++) {
//FIXME this test is not numerically robust. Particularly, it does not handle horizontal segments at y == point(1) well.
// Does the ray with y == point(1) intersect this line segment?
#if 1
if ( (((*i)(1) > point(1)) != ((*j)(1) > point(1)))
&& ((double)point(0) < (double)((*j)(0) - (*i)(0)) * (double)(point(1) - (*i)(1)) / (double)((*j)(1) - (*i)(1)) + (double)(*i)(0)) )
result = !result;
#else
if (((*i)(1) > point(1)) != ((*j)(1) > point(1))) {
// Orientation predicated relative to i-th point.
double orient = (double)(point(0) - (*i)(0)) * (double)((*j)(1) - (*i)(1)) - (double)(point(1) - (*i)(1)) * (double)((*j)(0) - (*i)(0));
if (((*i)(1) > (*j)(1)) ? (orient > 0.) : (orient < 0.))
result = !result;
}
#endif
}
return result;
}
// this only works on CCW polygons as CW will be ripped out by Clipper's simplify_polygons()
Polygons Polygon::simplify(double tolerance) const Polygons Polygon::simplify(double tolerance) const
{ {
// Works on CCW polygons only, CW contour will be reoriented to CCW by Clipper's simplify_polygons()!
assert(this->is_counter_clockwise());
// repeat first point at the end in order to apply Douglas-Peucker // repeat first point at the end in order to apply Douglas-Peucker
// on the whole polygon // on the whole polygon
Points points = this->points; Points points = this->points;
@ -130,13 +116,6 @@ Polygons Polygon::simplify(double tolerance) const
return simplify_polygons(pp); return simplify_polygons(pp);
} }
void Polygon::simplify(double tolerance, Polygons &polygons) const
{
Polygons pp = this->simplify(tolerance);
polygons.reserve(polygons.size() + pp.size());
polygons.insert(polygons.end(), pp.begin(), pp.end());
}
// Only call this on convex polygons or it will return invalid results // Only call this on convex polygons or it will return invalid results
void Polygon::triangulate_convex(Polygons* polygons) const void Polygon::triangulate_convex(Polygons* polygons) const
{ {
@ -171,50 +150,114 @@ Point Polygon::centroid() const
return Point(Vec2d(c / (3. * area_sum))); return Point(Vec2d(c / (3. * area_sum)));
} }
// find all concave vertices (i.e. having an internal angle greater than the supplied angle) bool Polygon::intersection(const Line &line, Point *intersection) const
// (external = right side, thus we consider ccw orientation)
Points Polygon::concave_points(double angle) const
{ {
Points points; if (this->points.size() < 2)
angle = 2. * PI - angle + EPSILON; return false;
if (Line(this->points.front(), this->points.back()).intersection(line, intersection))
// check whether first point forms a concave angle return true;
if (this->points.front().ccw_angle(this->points.back(), *(this->points.begin()+1)) <= angle) for (size_t i = 1; i < this->points.size(); ++ i)
points.push_back(this->points.front()); if (Line(this->points[i - 1], this->points[i]).intersection(line, intersection))
return true;
// check whether points 1..(n-1) form concave angles return false;
for (Points::const_iterator p = this->points.begin()+1; p != this->points.end()-1; ++ p)
if (p->ccw_angle(*(p-1), *(p+1)) <= angle)
points.push_back(*p);
// check whether last point forms a concave angle
if (this->points.back().ccw_angle(*(this->points.end()-2), this->points.front()) <= angle)
points.push_back(this->points.back());
return points;
} }
// find all convex vertices (i.e. having an internal angle smaller than the supplied angle) bool Polygon::first_intersection(const Line& line, Point* intersection) const
// (external = right side, thus we consider ccw orientation)
Points Polygon::convex_points(double angle) const
{ {
Points points; if (this->points.size() < 2)
angle = 2*PI - angle - EPSILON; return false;
// check whether first point forms a convex angle bool found = false;
if (this->points.front().ccw_angle(this->points.back(), *(this->points.begin()+1)) >= angle) double dmin = 0.;
points.push_back(this->points.front()); Line l(this->points.back(), this->points.front());
for (size_t i = 0; i < this->points.size(); ++ i) {
l.b = this->points[i];
Point ip;
if (l.intersection(line, &ip)) {
if (! found) {
found = true;
dmin = (line.a - ip).cast<double>().squaredNorm();
*intersection = ip;
} else {
double d = (line.a - ip).cast<double>().squaredNorm();
if (d < dmin) {
dmin = d;
*intersection = ip;
}
}
}
l.a = l.b;
}
return found;
}
// check whether points 1..(n-1) form convex angles bool Polygon::intersections(const Line &line, Points *intersections) const
for (Points::const_iterator p = this->points.begin()+1; p != this->points.end()-1; ++p) { {
if (p->ccw_angle(*(p-1), *(p+1)) >= angle) points.push_back(*p); if (this->points.size() < 2)
return false;
size_t intersections_size = intersections->size();
Line l(this->points.back(), this->points.front());
for (size_t i = 0; i < this->points.size(); ++ i) {
l.b = this->points[i];
Point intersection;
if (l.intersection(line, &intersection))
intersections->emplace_back(std::move(intersection));
l.a = l.b;
}
return intersections->size() > intersections_size;
}
// Filter points from poly to the output with the help of FilterFn.
// filter function receives two vectors:
// v1: this_point - previous_point
// v2: next_point - this_point
// and returns true if the point is to be copied to the output.
template<typename FilterFn>
Points filter_points_by_vectors(const Points &poly, FilterFn filter)
{
// Last point is the first point visited.
Point p1 = poly.back();
// Previous vector to p1.
Vec2d v1 = (p1 - *(poly.end() - 2)).cast<double>();
Points out;
for (Point p2 : poly) {
// p2 is next point to the currently visited point p1.
Vec2d v2 = (p2 - p1).cast<double>();
if (filter(v1, v2))
out.emplace_back(p2);
v1 = v2;
p1 = p2;
} }
// check whether last point forms a convex angle return out;
if (this->points.back().ccw_angle(*(this->points.end()-2), this->points.front()) >= angle) }
points.push_back(this->points.back());
return points; template<typename ConvexConcaveFilterFn>
Points filter_convex_concave_points_by_angle_threshold(const Points &poly, double angle_threshold, ConvexConcaveFilterFn convex_concave_filter)
{
assert(angle_threshold >= 0.);
if (angle_threshold < EPSILON) {
double cos_angle = cos(angle_threshold);
return filter_points_by_vectors(poly, [convex_concave_filter, cos_angle](const Vec2d &v1, const Vec2d &v2){
return convex_concave_filter(v1, v2) && v1.normalized().dot(v2.normalized()) < cos_angle;
});
} else {
return filter_points_by_vectors(poly, [convex_concave_filter](const Vec2d &v1, const Vec2d &v2){
return convex_concave_filter(v1, v2);
});
}
}
Points Polygon::convex_points(double angle_threshold) const
{
return filter_convex_concave_points_by_angle_threshold(this->points, angle_threshold, [](const Vec2d &v1, const Vec2d &v2){ return cross2(v1, v2) > 0.; });
}
Points Polygon::concave_points(double angle_threshold) const
{
return filter_convex_concave_points_by_angle_threshold(this->points, angle_threshold, [](const Vec2d &v1, const Vec2d &v2){ return cross2(v1, v2) < 0.; });
} }
// Projection of a point onto the polygon. // Projection of a point onto the polygon.
@ -540,4 +583,65 @@ void remove_collinear(Polygons &polys)
remove_collinear(poly); remove_collinear(poly);
} }
Polygons polygons_simplify(const Polygons &source_polygons, double tolerance)
{
Polygons out;
out.reserve(source_polygons.size());
for (const Polygon &source_polygon : source_polygons) {
// Run Douglas / Peucker simplification algorithm on an open polyline (by repeating the first point at the end of the polyline),
Points simplified = MultiPoint::_douglas_peucker(to_polyline(source_polygon).points, tolerance);
// then remove the last (repeated) point.
simplified.pop_back();
// Simplify the decimated contour by ClipperLib.
bool ccw = ClipperLib::Area(simplified) > 0.;
for (Points &path : ClipperLib::SimplifyPolygons(ClipperUtils::SinglePathProvider(simplified), ClipperLib::pftNonZero)) {
if (! ccw)
// ClipperLib likely reoriented negative area contours to become positive. Reverse holes back to CW.
std::reverse(path.begin(), path.end());
out.emplace_back(std::move(path));
}
}
return out;
}
// Do polygons match? If they match, they must have the same topology,
// however their contours may be rotated.
bool polygons_match(const Polygon &l, const Polygon &r)
{
if (l.size() != r.size())
return false;
auto it_l = std::find(l.points.begin(), l.points.end(), r.points.front());
if (it_l == l.points.end())
return false;
auto it_r = r.points.begin();
for (; it_l != l.points.end(); ++ it_l, ++ it_r)
if (*it_l != *it_r)
return false;
it_l = l.points.begin();
for (; it_r != r.points.end(); ++ it_l, ++ it_r)
if (*it_l != *it_r)
return false;
return true;
}
bool contains(const Polygon &polygon, const Point &p, bool border_result)
{
if (const int poly_count_inside = ClipperLib::PointInPolygon(p, polygon.points);
poly_count_inside == -1)
return border_result;
else
return (poly_count_inside % 2) == 1;
}
bool contains(const Polygons &polygons, const Point &p, bool border_result)
{
int poly_count_inside = 0;
for (const Polygon &poly : polygons) {
const int is_inside_this_poly = ClipperLib::PointInPolygon(p, poly.points);
if (is_inside_this_poly == -1)
return border_result;
poly_count_inside += is_inside_this_poly;
}
return (poly_count_inside % 2) == 1;
}
} }

View file

@ -15,11 +15,14 @@ using Polygons = std::vector<Polygon>;
using PolygonPtrs = std::vector<Polygon*>; using PolygonPtrs = std::vector<Polygon*>;
using ConstPolygonPtrs = std::vector<const Polygon*>; using ConstPolygonPtrs = std::vector<const Polygon*>;
// Returns true if inside. Returns border_result if on boundary.
bool contains(const Polygon& polygon, const Point& p, bool border_result = true);
bool contains(const Polygons& polygons, const Point& p, bool border_result = true);
class Polygon : public MultiPoint class Polygon : public MultiPoint
{ {
public: public:
Polygon() = default; Polygon() = default;
virtual ~Polygon() = default;
explicit Polygon(const Points &points) : MultiPoint(points) {} explicit Polygon(const Points &points) : MultiPoint(points) {}
Polygon(std::initializer_list<Point> points) : MultiPoint(points) {} Polygon(std::initializer_list<Point> points) : MultiPoint(points) {}
Polygon(const Polygon &other) : MultiPoint(other.points) {} Polygon(const Polygon &other) : MultiPoint(other.points) {}
@ -38,9 +41,10 @@ public:
const Point& operator[](Points::size_type idx) const { return this->points[idx]; } const Point& operator[](Points::size_type idx) const { return this->points[idx]; }
// last point == first point for polygons // last point == first point for polygons
const Point& last_point() const override { return this->points.front(); } const Point& last_point() const { return this->points.front(); }
Lines lines() const override; double length() const;
Lines lines() const;
Polyline split_at_vertex(const Point &point) const; Polyline split_at_vertex(const Point &point) const;
// Split a closed polygon into an open polyline, with the split point duplicated at both ends. // Split a closed polygon into an open polyline, with the split point duplicated at both ends.
Polyline split_at_index(int index) const; Polyline split_at_index(int index) const;
@ -58,15 +62,26 @@ public:
void douglas_peucker(double tolerance); void douglas_peucker(double tolerance);
// Does an unoriented polygon contain a point? // Does an unoriented polygon contain a point?
// Tested by counting intersections along a horizontal line. bool contains(const Point &point) const { return Slic3r::contains(*this, point, true); }
bool contains(const Point &point) const; // Approximate on boundary test.
bool on_boundary(const Point &point, double eps) const
{ return (this->point_projection(point) - point).cast<double>().squaredNorm() < eps * eps; }
// Works on CCW polygons only, CW contour will be reoriented to CCW by Clipper's simplify_polygons()!
Polygons simplify(double tolerance) const; Polygons simplify(double tolerance) const;
void simplify(double tolerance, Polygons &polygons) const;
void densify(float min_length, std::vector<float>* lengths = nullptr); void densify(float min_length, std::vector<float>* lengths = nullptr);
void triangulate_convex(Polygons* polygons) const; void triangulate_convex(Polygons* polygons) const;
Point centroid() const; Point centroid() const;
Points concave_points(double angle = PI) const;
Points convex_points(double angle = PI) const; bool intersection(const Line& line, Point* intersection) const;
bool first_intersection(const Line& line, Point* intersection) const;
bool intersections(const Line &line, Points *intersections) const;
// Considering CCW orientation of this polygon, find all convex resp. concave points
// with the angle at the vertex larger than a threshold.
// Zero angle_threshold means to accept all convex resp. concave points.
Points convex_points(double angle_threshold = 0.) const;
Points concave_points(double angle_threshold = 0.) const;
// Projection of a point onto the polygon. // Projection of a point onto the polygon.
Point point_projection(const Point &point) const; Point point_projection(const Point &point) const;
std::vector<float> parameter_by_length() const; std::vector<float> parameter_by_length() const;
@ -136,14 +151,7 @@ inline void polygons_append(Polygons &dst, Polygons &&src)
} }
} }
inline Polygons polygons_simplify(const Polygons &polys, double tolerance) Polygons polygons_simplify(const Polygons &polys, double tolerance);
{
Polygons out;
out.reserve(polys.size());
for (const Polygon &p : polys)
polygons_append(out, p.simplify(tolerance));
return out;
}
inline void polygons_rotate(Polygons &polys, double angle) inline void polygons_rotate(Polygons &polys, double angle)
{ {
@ -164,13 +172,16 @@ inline Points to_points(const Polygon &poly)
return poly.points; return poly.points;
} }
inline size_t count_points(const Polygons &polys) {
size_t n_points = 0;
for (const auto &poly: polys) n_points += poly.points.size();
return n_points;
}
inline Points to_points(const Polygons &polys) inline Points to_points(const Polygons &polys)
{ {
size_t n_points = 0;
for (size_t i = 0; i < polys.size(); ++ i)
n_points += polys[i].points.size();
Points points; Points points;
points.reserve(n_points); points.reserve(count_points(polys));
for (const Polygon &poly : polys) for (const Polygon &poly : polys)
append(points, poly.points); append(points, poly.points);
return points; return points;
@ -190,11 +201,8 @@ inline Lines to_lines(const Polygon &poly)
inline Lines to_lines(const Polygons &polys) inline Lines to_lines(const Polygons &polys)
{ {
size_t n_lines = 0;
for (size_t i = 0; i < polys.size(); ++ i)
n_lines += polys[i].points.size();
Lines lines; Lines lines;
lines.reserve(n_lines); lines.reserve(count_points(polys));
for (size_t i = 0; i < polys.size(); ++ i) { for (size_t i = 0; i < polys.size(); ++ i) {
const Polygon &poly = polys[i]; const Polygon &poly = polys[i];
for (Points::const_iterator it = poly.points.begin(); it != poly.points.end()-1; ++it) for (Points::const_iterator it = poly.points.begin(); it != poly.points.end()-1; ++it)
@ -204,18 +212,22 @@ inline Lines to_lines(const Polygons &polys)
return lines; return lines;
} }
inline Polylines to_polylines(const Polygons &polys) inline Polyline to_polyline(const Polygon &polygon)
{ {
Polylines polylines; Polyline out;
polylines.assign(polys.size(), Polyline()); out.points.reserve(polygon.size() + 1);
size_t idx = 0; out.points.assign(polygon.points.begin(), polygon.points.end());
for (Polygons::const_iterator it = polys.begin(); it != polys.end(); ++ it) { out.points.push_back(polygon.points.front());
Polyline &pl = polylines[idx ++]; return out;
pl.points = it->points; }
pl.points.push_back(it->points.front());
} inline Polylines to_polylines(const Polygons &polygons)
assert(idx == polylines.size()); {
return polylines; Polylines out;
out.reserve(polygons.size());
for (const Polygon &polygon : polygons)
out.emplace_back(to_polyline(polygon));
return out;
} }
inline Polylines to_polylines(Polygons &&polys) inline Polylines to_polylines(Polygons &&polys)
@ -223,10 +235,10 @@ inline Polylines to_polylines(Polygons &&polys)
Polylines polylines; Polylines polylines;
polylines.assign(polys.size(), Polyline()); polylines.assign(polys.size(), Polyline());
size_t idx = 0; size_t idx = 0;
for (Polygons::const_iterator it = polys.begin(); it != polys.end(); ++ it) { for (auto it = polys.begin(); it != polys.end(); ++ it) {
Polyline &pl = polylines[idx ++]; Polyline &pl = polylines[idx ++];
pl.points = std::move(it->points); pl.points = std::move(it->points);
pl.points.push_back(it->points.front()); pl.points.push_back(pl.points.front());
} }
assert(idx == polylines.size()); assert(idx == polylines.size());
return polylines; return polylines;
@ -245,11 +257,14 @@ inline Polygons to_polygons(std::vector<Points> &&paths)
{ {
Polygons out; Polygons out;
out.reserve(paths.size()); out.reserve(paths.size());
for (const Points &path : paths) for (Points &path : paths)
out.emplace_back(std::move(path)); out.emplace_back(std::move(path));
return out; return out;
} }
// Do polygons match? If they match, they must have the same topology,
// however their contours may be rotated.
bool polygons_match(const Polygon &l, const Polygon &r);
} // Slic3r } // Slic3r
// start Boost // start Boost

View file

@ -2,7 +2,6 @@
#include "Polyline.hpp" #include "Polyline.hpp"
#include "Exception.hpp" #include "Exception.hpp"
#include "ExPolygon.hpp" #include "ExPolygon.hpp"
#include "ExPolygonCollection.hpp"
#include "Line.hpp" #include "Line.hpp"
#include "Polygon.hpp" #include "Polygon.hpp"
#include <iostream> #include <iostream>

View file

@ -2874,7 +2874,7 @@ void extract_support_layer(const json& support_layer_json, SupportLayer& support
ExPolygon polygon; ExPolygon polygon;
polygon = support_layer_json[JSON_SUPPORT_LAYER_ISLANDS][islands_index]; polygon = support_layer_json[JSON_SUPPORT_LAYER_ISLANDS][islands_index];
support_layer.support_islands.expolygons.push_back(std::move(polygon)); support_layer.support_islands.push_back(std::move(polygon));
} }
//support_fills //support_fills
@ -3009,7 +3009,7 @@ int Print::export_cached_data(const std::string& directory, bool with_space)
support_layer_json[JSON_SUPPORT_LAYER_TYPE] = support_layer->support_type; support_layer_json[JSON_SUPPORT_LAYER_TYPE] = support_layer->support_type;
//support_islands //support_islands
for (const ExPolygon& support_island : support_layer->support_islands.expolygons) { for (const ExPolygon& support_island : support_layer->support_islands) {
json support_island_json = support_island; json support_island_json = support_island;
support_islands_json.push_back(std::move(support_island_json)); support_islands_json.push_back(std::move(support_island_json));
} }

View file

@ -204,7 +204,7 @@ public:
void add(const ExPolygon &ep) void add(const ExPolygon &ep)
{ {
m_polys.emplace_back(ep); m_polys.emplace_back(ep);
m_index.insert(BoundingBox{ep}, unsigned(m_index.size())); m_index.insert(get_extents(ep), unsigned(m_index.size()));
} }
// Check an arbitrary polygon for intersection with the indexed polygons // Check an arbitrary polygon for intersection with the indexed polygons

View file

@ -4,6 +4,7 @@
#include <tbb/parallel_for.h> #include <tbb/parallel_for.h>
#include "SupportPointGenerator.hpp" #include "SupportPointGenerator.hpp"
#include "Geometry/ConvexHull.hpp"
#include "Concurrency.hpp" #include "Concurrency.hpp"
#include "Model.hpp" #include "Model.hpp"
#include "ExPolygon.hpp" #include "ExPolygon.hpp"
@ -11,7 +12,6 @@
#include "Point.hpp" #include "Point.hpp"
#include "ClipperUtils.hpp" #include "ClipperUtils.hpp"
#include "Tesselate.hpp" #include "Tesselate.hpp"
#include "ExPolygonCollection.hpp"
#include "MinAreaBoundingBox.hpp" #include "MinAreaBoundingBox.hpp"
#include "libslic3r.h" #include "libslic3r.h"
@ -550,7 +550,7 @@ void SupportPointGenerator::uniformly_cover(const ExPolygons& islands, Structure
// auto bb = get_extents(islands); // auto bb = get_extents(islands);
if (flags & icfIsNew) { if (flags & icfIsNew) {
auto chull = ExPolygonCollection{islands}.convex_hull(); auto chull = Geometry::convex_hull(islands);
auto rotbox = MinAreaBoundigBox{chull, MinAreaBoundigBox::pcConvex}; auto rotbox = MinAreaBoundigBox{chull, MinAreaBoundigBox::pcConvex};
Vec2d bbdim = {unscaled(rotbox.width()), unscaled(rotbox.height())}; Vec2d bbdim = {unscaled(rotbox.width()), unscaled(rotbox.height())};

View file

@ -88,10 +88,8 @@ void SVG::draw(const ExPolygon &expolygon, std::string fill, const float fill_op
this->fill = fill; this->fill = fill;
std::string d; std::string d;
Polygons pp = expolygon; for (const Polygon &p : to_polygons(expolygon))
for (Polygons::const_iterator p = pp.begin(); p != pp.end(); ++p) { d += this->get_path_d(p, true) + " ";
d += this->get_path_d(*p, true) + " ";
}
this->path(d, true, 0, fill_opacity); this->path(d, true, 0, fill_opacity);
} }
@ -392,7 +390,7 @@ void SVG::export_expolygons(const char *path, const std::vector<std::pair<Slic3r
for (const auto &exp_with_attr : expolygons_with_attributes) for (const auto &exp_with_attr : expolygons_with_attributes)
if (exp_with_attr.second.radius_points > 0) if (exp_with_attr.second.radius_points > 0)
for (const ExPolygon &expoly : exp_with_attr.first) for (const ExPolygon &expoly : exp_with_attr.first)
svg.draw((Points)expoly, exp_with_attr.second.color_points, exp_with_attr.second.radius_points); svg.draw(to_points(expoly), exp_with_attr.second.color_points, exp_with_attr.second.radius_points);
// Export legend. // Export legend.
// 1st row // 1st row

View file

@ -4670,7 +4670,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
std::stable_sort(layer_cache_item.overlapping.begin(), layer_cache_item.overlapping.end(), [](auto *l1, auto *l2) { return *l1 < *l2; }); std::stable_sort(layer_cache_item.overlapping.begin(), layer_cache_item.overlapping.end(), [](auto *l1, auto *l2) { return *l1 < *l2; });
} }
if (! polys.empty()) if (! polys.empty())
expolygons_append(support_layer.support_islands.expolygons, union_ex(polys)); expolygons_append(support_layer.support_islands, union_ex(polys));
} // for each support_layer_id } // for each support_layer_id
}); });

View file

@ -67,12 +67,9 @@ SurfacesPtr SurfaceCollection::filter_by_types(const SurfaceType *types, int nty
void SurfaceCollection::filter_by_type(SurfaceType type, Polygons* polygons) void SurfaceCollection::filter_by_type(SurfaceType type, Polygons* polygons)
{ {
for (Surfaces::iterator surface = this->surfaces.begin(); surface != this->surfaces.end(); ++surface) { for (const Surface &surface : this->surfaces)
if (surface->surface_type == type) { if (surface.surface_type == type)
Polygons pp = surface->expolygon; polygons_append(*polygons, to_polygons(surface.expolygon));
polygons->insert(polygons->end(), pp.begin(), pp.end());
}
}
} }
void SurfaceCollection::keep_type(const SurfaceType type) void SurfaceCollection::keep_type(const SurfaceType type)

View file

@ -2244,7 +2244,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
if (SQUARE_SUPPORT) { if (SQUARE_SUPPORT) {
// simplify support contours // simplify support contours
ExPolygons base_areas_simplified; ExPolygons base_areas_simplified;
for (auto &area : base_areas) { area.simplify(scale_(line_width / 2), &base_areas_simplified, SimplifyMethodDP); } for (auto &area : base_areas) { area.simplify(scale_(line_width / 2), &base_areas_simplified); }
base_areas = std::move(base_areas_simplified); base_areas = std::move(base_areas_simplified);
} }
//Subtract support floors. We can only compute floor_areas here instead of with roof_areas, //Subtract support floors. We can only compute floor_areas here instead of with roof_areas,
@ -2337,7 +2337,8 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
{ {
Polygon rev_hole = hole; Polygon rev_hole = hole;
rev_hole.make_counter_clockwise(); rev_hole.make_counter_clockwise();
ExPolygons ex_hole = to_expolygons(ExPolygon(rev_hole)); ExPolygons ex_hole;
ex_hole.emplace_back(std::move(ExPolygon(rev_hole)));
for (auto& other_area : base_areas) for (auto& other_area : base_areas)
//if (&other_area != &base_area) //if (&other_area != &base_area)
ex_hole = std::move(diff_ex(ex_hole, other_area)); ex_hole = std::move(diff_ex(ex_hole, other_area));

View file

@ -3977,7 +3977,7 @@ void PartPlateList::postprocess_arrange_polygon(arrangement::ArrangePolygon& arr
{ {
// outarea for large object // outarea for large object
arrange_polygon.bed_idx = m_plate_list.size(); arrange_polygon.bed_idx = m_plate_list.size();
BoundingBox apbox(arrange_polygon.poly); BoundingBox apbox = get_extents(arrange_polygon.poly);
auto apbox_size = apbox.size(); auto apbox_size = apbox.size();
//arrange_polygon.translation(X) = scaled<double>(0.5 * plate_stride_x()); //arrange_polygon.translation(X) = scaled<double>(0.5 * plate_stride_x());