mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-17 11:47:54 -06:00
ENH: fix for STUDIO-881
Thanks prusa Signed-off-by: salt.wei <salt.wei@bambulab.com> Change-Id: I2e1c1088d29dd5401016ca41d3ed6dec87e0acd1
This commit is contained in:
parent
b4ffa91cb4
commit
61b271f379
31 changed files with 703 additions and 471 deletions
|
@ -6,6 +6,17 @@
|
|||
|
||||
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
|
||||
{
|
||||
return to_lines(*this);
|
||||
|
@ -88,36 +99,11 @@ void Polygon::douglas_peucker(double tolerance)
|
|||
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
|
||||
{
|
||||
// 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
|
||||
// on the whole polygon
|
||||
Points points = this->points;
|
||||
|
@ -130,13 +116,6 @@ Polygons Polygon::simplify(double tolerance) const
|
|||
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
|
||||
void Polygon::triangulate_convex(Polygons* polygons) const
|
||||
{
|
||||
|
@ -171,50 +150,114 @@ Point Polygon::centroid() const
|
|||
return Point(Vec2d(c / (3. * area_sum)));
|
||||
}
|
||||
|
||||
// find all concave vertices (i.e. having an internal angle greater than the supplied angle)
|
||||
// (external = right side, thus we consider ccw orientation)
|
||||
Points Polygon::concave_points(double angle) const
|
||||
bool Polygon::intersection(const Line &line, Point *intersection) const
|
||||
{
|
||||
Points points;
|
||||
angle = 2. * PI - angle + EPSILON;
|
||||
|
||||
// check whether first point forms a concave angle
|
||||
if (this->points.front().ccw_angle(this->points.back(), *(this->points.begin()+1)) <= angle)
|
||||
points.push_back(this->points.front());
|
||||
|
||||
// check whether points 1..(n-1) form concave angles
|
||||
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;
|
||||
if (this->points.size() < 2)
|
||||
return false;
|
||||
if (Line(this->points.front(), this->points.back()).intersection(line, intersection))
|
||||
return true;
|
||||
for (size_t i = 1; i < this->points.size(); ++ i)
|
||||
if (Line(this->points[i - 1], this->points[i]).intersection(line, intersection))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// find all convex vertices (i.e. having an internal angle smaller than the supplied angle)
|
||||
// (external = right side, thus we consider ccw orientation)
|
||||
Points Polygon::convex_points(double angle) const
|
||||
bool Polygon::first_intersection(const Line& line, Point* intersection) const
|
||||
{
|
||||
Points points;
|
||||
angle = 2*PI - angle - EPSILON;
|
||||
|
||||
// check whether first point forms a convex angle
|
||||
if (this->points.front().ccw_angle(this->points.back(), *(this->points.begin()+1)) >= angle)
|
||||
points.push_back(this->points.front());
|
||||
|
||||
// check whether points 1..(n-1) form convex angles
|
||||
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;
|
||||
|
||||
bool found = false;
|
||||
double dmin = 0.;
|
||||
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;
|
||||
}
|
||||
|
||||
bool Polygon::intersections(const Line &line, Points *intersections) const
|
||||
{
|
||||
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
|
||||
if (this->points.back().ccw_angle(*(this->points.end()-2), this->points.front()) >= angle)
|
||||
points.push_back(this->points.back());
|
||||
|
||||
return points;
|
||||
return out;
|
||||
}
|
||||
|
||||
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.
|
||||
|
@ -540,4 +583,65 @@ void remove_collinear(Polygons &polys)
|
|||
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;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue