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

@ -12,27 +12,6 @@
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)
{
contour.scale(factor);
@ -40,6 +19,13 @@ void ExPolygon::scale(double 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)
{
contour.translate(p);
@ -118,34 +104,53 @@ bool ExPolygon::contains(const Polylines &polylines) const
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;
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 true;
}
// inclusive version of contains() that also checks whether point is on boundaries
bool ExPolygon::contains_b(const Point &point) const
bool ExPolygon::on_boundary(const Point &point, double eps) 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
ExPolygon::has_boundary_point(const Point &point) const
// Projection of a point onto the polygon.
Point ExPolygon::point_projection(const Point &point) const
{
if (this->contour.has_boundary_point(point)) return true;
for (Polygons::const_iterator h = this->holes.begin(); h != this->holes.end(); ++h) {
if (h->has_boundary_point(point)) return true;
if (this->holes.empty()) {
return this->contour.point_projection(point);
} 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
{
if (this->empty() || other.empty())
return false;
#if 0
BoundingBox bbox = get_extents(other);
bbox.merge(get_extents(*this));
@ -155,61 +160,62 @@ bool ExPolygon::overlaps(const ExPolygon &other) const
svg.draw_outline(*this);
svg.draw_outline(other, "blue");
#endif
Polylines pl_out = intersection_pl((Polylines)other, *this);
Polylines pl_out = intersection_pl(to_polylines(other), *this);
#if 0
svg.draw(pl_out, "red");
#endif
if (! pl_out.empty())
return true;
//FIXME ExPolygon::overlaps() shall be commutative, it is not!
return ! other.contour.points.empty() && this->contains_b(other.contour.points.front());
// See unit test SCENARIO("Clipper diff with polyline", "[Clipper]")
// for in which case the intersection_pl produces any intersection.
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 ExPolygon::simplify_p(double tolerance, SimplifyMethod method) const
Polygons ExPolygon::simplify_p(double tolerance) const
{
Polygons pp;
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
{
Polygon p = this->contour;
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();
pp.emplace_back(std::move(p));
}
// holes
for (Polygon p : this->holes) {
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();
pp.emplace_back(std::move(p));
}
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
ExPolygon::medial_axis(double max_width, double min_width, ThickPolylines* polylines) const
void ExPolygon::medial_axis(double min_width, double max_width, ThickPolylines* polylines) const
{
// init helper object
Slic3r::Geometry::MedialAxis ma(max_width, min_width, this);
ma.lines = this->lines();
Slic3r::Geometry::MedialAxis ma(min_width, max_width, *this);
// compute the Voronoi diagram and extract medial axis polylines
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 */
Point new_front = polyline.points.front();
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 p2 = polyline.points[1].cast<double>();
// 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;
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 p2 = polyline.points.back().cast<double>();
// prevent the line from touching on the other side, otherwise intersection() might return that solution
@ -312,16 +318,17 @@ ExPolygon::medial_axis(double max_width, double min_width, ThickPolylines* polyl
}
}
}
polylines->insert(polylines->end(), pp.begin(), pp.end());
}
void
ExPolygon::medial_axis(double max_width, double min_width, Polylines* polylines) const
void ExPolygon::medial_axis(double min_width, double max_width, Polylines* polylines) const
{
ThickPolylines tp;
this->medial_axis(max_width, min_width, &tp);
polylines->insert(polylines->end(), tp.begin(), tp.end());
this->medial_axis(min_width, max_width, &tp);
polylines->reserve(polylines->size() + tp.size());
for (auto &pl : tp)
polylines->emplace_back(pl.points);
}
Lines ExPolygon::lines() const
@ -334,6 +341,18 @@ Lines ExPolygon::lines() const
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)
{
return get_extents(expolygon.contour);