From f1c0c61895b665dd14792fb97117e4f3499bf0de Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 14 Nov 2022 19:01:17 +0100 Subject: [PATCH] Refactored Point / MultiPoint / Polyline / Polygon: 1) Removed virtual methods. There was not really need for them. 2) Some of the virtual methods were using conversion to Lines, which was unnecessary and expensive. 3) Removed some nearest element search methods from Point. --- .../Arachne/SkeletalTrapezoidation.cpp | 2 +- src/libslic3r/Arachne/utils/VoronoiUtils.cpp | 2 +- src/libslic3r/Arachne/utils/linearAlg2D.hpp | 6 +- src/libslic3r/ExPolygon.cpp | 27 ++-- src/libslic3r/ExPolygon.hpp | 10 +- src/libslic3r/Fill/FillConcentric.cpp | 4 +- src/libslic3r/GCode.cpp | 3 +- src/libslic3r/Line.hpp | 4 +- src/libslic3r/MultiPoint.cpp | 67 ---------- src/libslic3r/MultiPoint.hpp | 11 -- src/libslic3r/PerimeterGenerator.cpp | 4 +- src/libslic3r/Point.cpp | 115 ++++-------------- src/libslic3r/Point.hpp | 17 ++- src/libslic3r/Polygon.cpp | 75 +++++++++++- src/libslic3r/Polygon.hpp | 26 ++-- src/libslic3r/Polyline.cpp | 26 +++- src/libslic3r/Polyline.hpp | 13 +- xs/t/03_point.t | 20 +-- xs/xsp/Point.xsp | 5 +- 19 files changed, 173 insertions(+), 264 deletions(-) diff --git a/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp b/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp index 04dede546..04a1042d8 100644 --- a/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp +++ b/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp @@ -274,7 +274,7 @@ std::vector SkeletalTrapezoidation::discretize(const vd_t::edge_type& vd_ Point right_point = VoronoiUtils::getSourcePoint(*right_cell, segments); coord_t d = (right_point - left_point).cast().norm(); Point middle = (left_point + right_point) / 2; - Point x_axis_dir = Point(right_point - left_point).rotate_90_degree_ccw(); + Point x_axis_dir = perp(Point(right_point - left_point)); coord_t x_axis_length = x_axis_dir.cast().norm(); const auto projected_x = [x_axis_dir, x_axis_length, middle](Point from) //Project a point on the edge. diff --git a/src/libslic3r/Arachne/utils/VoronoiUtils.cpp b/src/libslic3r/Arachne/utils/VoronoiUtils.cpp index 82bd79523..069e1f5ad 100644 --- a/src/libslic3r/Arachne/utils/VoronoiUtils.cpp +++ b/src/libslic3r/Arachne/utils/VoronoiUtils.cpp @@ -165,7 +165,7 @@ std::vector VoronoiUtils::discretizeParabola(const Point& p, const Segmen Line(a, b).distance_to_infinite_squared(p, &pxx); const Point ppxx = pxx - p; const coord_t d = ppxx.cast().norm(); - const PointMatrix rot = PointMatrix(ppxx.rotate_90_degree_ccw()); + const PointMatrix rot = PointMatrix(perp(ppxx)); if (d == 0) { diff --git a/src/libslic3r/Arachne/utils/linearAlg2D.hpp b/src/libslic3r/Arachne/utils/linearAlg2D.hpp index 797bae0b9..304984b1c 100644 --- a/src/libslic3r/Arachne/utils/linearAlg2D.hpp +++ b/src/libslic3r/Arachne/utils/linearAlg2D.hpp @@ -38,15 +38,11 @@ inline static bool isInsideCorner(const Point &a, const Point &b, const Point &c return (p0.cast() * int64_t(len) / _len).cast(); }; - auto rotate_90_degree_ccw = [](const Vec2d &p) -> Vec2d { - return {-p.y(), p.x()}; - }; - constexpr coord_t normal_length = 10000; //Create a normal vector of reasonable length in order to reduce rounding error. const Point ba = normal(a - b, normal_length); const Point bc = normal(c - b, normal_length); const Vec2d bq = query_point.cast() - b.cast(); - const Vec2d perpendicular = rotate_90_degree_ccw(bq); //The query projects to this perpendicular to coordinate 0. + const Vec2d perpendicular = perp(bq); //The query projects to this perpendicular to coordinate 0. const double project_a_perpendicular = ba.cast().dot(perpendicular); //Project vertex A on the perpendicular line. const double project_c_perpendicular = bc.cast().dot(perpendicular); //Project vertex C on the perpendicular line. diff --git a/src/libslic3r/ExPolygon.cpp b/src/libslic3r/ExPolygon.cpp index 311194102..61c204044 100644 --- a/src/libslic3r/ExPolygon.cpp +++ b/src/libslic3r/ExPolygon.cpp @@ -98,30 +98,24 @@ 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 (! Slic3r::contains(contour, point, true)) + 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 (Slic3r::contains(hole, point, false)) + 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); -} - -bool ExPolygon::has_boundary_point(const Point &point) const -{ - if (this->contour.has_boundary_point(point)) + if (this->contour.on_boundary(point, eps)) return true; for (const Polygon &hole : this->holes) - if (hole.has_boundary_point(point)) + if (hole.on_boundary(point, eps)) return true; return false; } @@ -148,6 +142,9 @@ Point ExPolygon::point_projection(const Point &point) const 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)); @@ -164,7 +161,7 @@ bool ExPolygon::overlaps(const ExPolygon &other) const 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()); + return this->contains(other.contour.points.front()); } void ExPolygon::simplify_p(double tolerance, Polygons* polygons) const @@ -241,7 +238,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(); Vec2d p2 = polyline.points[1].cast(); // prevent the line from touching on the other side, otherwise intersection() might return that solution @@ -251,7 +248,7 @@ ExPolygon::medial_axis(double max_width, double min_width, ThickPolylines* polyl p1 -= (p2 - p1).normalized() * max_width; this->contour.intersection(Line(p1.cast(), p2.cast()), &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(); Vec2d p2 = polyline.points.back().cast(); // prevent the line from touching on the other side, otherwise intersection() might return that solution diff --git a/src/libslic3r/ExPolygon.hpp b/src/libslic3r/ExPolygon.hpp index c0f3d95a4..58f0d33e9 100644 --- a/src/libslic3r/ExPolygon.hpp +++ b/src/libslic3r/ExPolygon.hpp @@ -51,9 +51,9 @@ public: bool contains(const Line &line) const; bool contains(const Polyline &polyline) const; bool contains(const Polylines &polylines) const; - bool contains(const Point &point) const; - bool contains_b(const Point &point) const; - bool has_boundary_point(const Point &point) const; + bool contains(const Point &point, bool border_result = true) const; + // Approximate on boundary test. + bool on_boundary(const Point &point, double eps) const; // Projection of a point onto the polygon. Point point_projection(const Point &point) const; @@ -377,10 +377,10 @@ inline void expolygons_rotate(ExPolygons &expolys, double 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 (const ExPolygon &expoly : expolys) - if (expoly.contains(pt)) + if (expoly.contains(pt, border_result)) return true; return false; } diff --git a/src/libslic3r/Fill/FillConcentric.cpp b/src/libslic3r/Fill/FillConcentric.cpp index 69f530720..7b005ee35 100644 --- a/src/libslic3r/Fill/FillConcentric.cpp +++ b/src/libslic3r/Fill/FillConcentric.cpp @@ -40,7 +40,7 @@ void FillConcentric::_fill_surface_single( size_t iPathFirst = polylines_out.size(); Point last_pos(0, 0); for (const Polygon &loop : loops) { - polylines_out.emplace_back(loop.split_at_index(last_pos.nearest_point_index(loop.points))); + polylines_out.emplace_back(loop.split_at_index(nearest_point_index(loop.points, last_pos))); last_pos = polylines_out.back().last_point(); } @@ -100,7 +100,7 @@ void FillConcentric::_fill_surface_single(const FillParams ¶ms, if (extrusion->is_closed && thick_polyline.points.front() == thick_polyline.points.back() && thick_polyline.width.front() == thick_polyline.width.back()) { thick_polyline.points.pop_back(); assert(thick_polyline.points.size() * 2 == thick_polyline.width.size()); - int nearest_idx = last_pos.nearest_point_index(thick_polyline.points); + int nearest_idx = nearest_point_index(thick_polyline.points, last_pos); std::rotate(thick_polyline.points.begin(), thick_polyline.points.begin() + nearest_idx, thick_polyline.points.end()); std::rotate(thick_polyline.width.begin(), thick_polyline.width.begin() + 2 * nearest_idx, thick_polyline.width.end()); thick_polyline.points.emplace_back(thick_polyline.points.front()); diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index f42ae4f83..12342ddca 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -116,8 +116,7 @@ namespace Slic3r { Point pos = Point::new_scale(writer_pos(0), writer_pos(1)); // find standby point - Point standby_point; - pos.nearest_point(this->standby_points, &standby_point); + Point standby_point = nearest_point(this->standby_points, pos).first; /* We don't call gcodegen.travel_to() because we don't need retraction (it was already triggered by the caller) nor avoid_crossing_perimeters and also because the coordinates diff --git a/src/libslic3r/Line.hpp b/src/libslic3r/Line.hpp index f9a8977d5..f7a14c251 100644 --- a/src/libslic3r/Line.hpp +++ b/src/libslic3r/Line.hpp @@ -54,11 +54,11 @@ double distance_to_squared(const L &line, const Vec, Scalar> &point, V // We find projection of this point onto the line. // It falls where t = [(this-a) . (b-a)] / |b-a|^2 const double t = va.dot(v) / l2; - if (t < 0.0) { + if (t <= 0.0) { // beyond the 'a' end of the segment *nearest_point = get_a(line); return va.squaredNorm(); - } else if (t > 1.0) { + } else if (t >= 1.0) { // beyond the 'b' end of the segment *nearest_point = get_b(line); return (point - get_b(line)).template cast().squaredNorm(); diff --git a/src/libslic3r/MultiPoint.cpp b/src/libslic3r/MultiPoint.cpp index 5ed9eb23c..f18720bd6 100644 --- a/src/libslic3r/MultiPoint.cpp +++ b/src/libslic3r/MultiPoint.cpp @@ -45,16 +45,6 @@ void MultiPoint::rotate(double angle, const Point ¢er) } } -double MultiPoint::length() const -{ - const Lines& lines = this->lines(); - double len = 0; - for (auto it = lines.cbegin(); it != lines.cend(); ++it) { - len += it->length(); - } - return len; -} - int MultiPoint::find_point(const Point &point) const { for (const Point &pt : this->points) @@ -81,12 +71,6 @@ int MultiPoint::find_point(const Point &point, double scaled_epsilon) const return dist2_min < eps2 ? idx_min : -1; } -bool MultiPoint::has_boundary_point(const Point &point) const -{ - double dist = (point.projection_onto(*this) - point).cast().norm(); - return dist < SCALED_EPSILON; -} - BoundingBox MultiPoint::bounding_box() const { return BoundingBox(this->points); @@ -119,49 +103,6 @@ bool MultiPoint::remove_duplicate_points() return false; } -bool MultiPoint::intersection(const Line& line, Point* intersection) const -{ - Lines lines = this->lines(); - for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it) { - if (it->intersection(line, intersection)) return true; - } - return false; -} - -bool MultiPoint::first_intersection(const Line& line, Point* intersection) const -{ - bool found = false; - double dmin = 0.; - for (const Line &l : this->lines()) { - Point ip; - if (l.intersection(line, &ip)) { - if (! found) { - found = true; - dmin = (line.a - ip).cast().norm(); - *intersection = ip; - } else { - double d = (line.a - ip).cast().norm(); - if (d < dmin) { - dmin = d; - *intersection = ip; - } - } - } - } - return found; -} - -bool MultiPoint::intersections(const Line &line, Points *intersections) const -{ - size_t intersections_size = intersections->size(); - for (const Line &polygon_line : this->lines()) { - Point intersection; - if (polygon_line.intersection(line, &intersection)) - intersections->emplace_back(std::move(intersection)); - } - return intersections->size() > intersections_size; -} - std::vector MultiPoint::_douglas_peucker(const std::vector& pts, const double tolerance) { std::vector result_pts; @@ -363,14 +304,6 @@ void MultiPoint3::translate(const Point& vector) this->translate(vector(0), vector(1)); } -double MultiPoint3::length() const -{ - double len = 0.0; - for (const Line3& line : this->lines()) - len += line.length(); - return len; -} - BoundingBox3 MultiPoint3::bounding_box() const { return BoundingBox3(points); diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp index bc9cf761d..778b30c57 100644 --- a/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -18,7 +18,6 @@ public: Points points; MultiPoint() = default; - virtual ~MultiPoint() = default; MultiPoint(const MultiPoint &other) : points(other.points) {} MultiPoint(MultiPoint &&other) : points(std::move(other.points)) {} MultiPoint(std::initializer_list list) : points(list) {} @@ -37,11 +36,8 @@ public: const Point& front() const { return this->points.front(); } const Point& back() const { return this->points.back(); } const Point& first_point() const { return this->front(); } - virtual const Point& last_point() const = 0; - virtual Lines lines() const = 0; size_t size() const { return points.size(); } bool empty() const { return points.empty(); } - double length() const; bool is_valid() const { return this->points.size() >= 2; } // Return index of a polygon point exactly equal to point. @@ -50,7 +46,6 @@ public: // Return index of the closest point to point closer than scaled_epsilon. // Return -1 if no such point exists. int find_point(const Point &point, const double scaled_epsilon) const; - bool has_boundary_point(const Point &point) const; int closest_point_index(const Point &point) const { int idx = -1; if (! this->points.empty()) { @@ -86,10 +81,6 @@ public: } } - 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; - static Points _douglas_peucker(const Points &points, const double tolerance); static Points visivalingam(const Points& pts, const double& tolerance); @@ -110,8 +101,6 @@ public: void translate(double x, double y); void translate(const Point& vector); - virtual Lines3 lines() const = 0; - double length() const; bool is_valid() const { return this->points.size() >= 2; } BoundingBox3 bounding_box() const; diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 92759238e..e864a37ad 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -405,8 +405,8 @@ static ClipperLib_Z::Paths clip_extrusion(const ClipperLib_Z::Path &subject, con Point prev(subject.front().x(), subject.front().y()); for (auto it = std::next(subject.begin()); it != subject.end(); ++it) { Point curr(it->x(), it->y()); - Point projected_pt = pt.projection_onto(Line(prev, curr)); - if (double dist_sqr = (projected_pt - pt).cast().squaredNorm(); dist_sqr < dist_sqr_min) { + Point projected_pt; + if (double dist_sqr = line_alg::distance_to_squared(Line(prev, curr), pt, &projected_pt); dist_sqr < dist_sqr_min) { dist_sqr_min = dist_sqr; projected_pt_min = projected_pt; it_min = std::prev(it); diff --git a/src/libslic3r/Point.cpp b/src/libslic3r/Point.cpp index 9f56f96d6..a1145a952 100644 --- a/src/libslic3r/Point.cpp +++ b/src/libslic3r/Point.cpp @@ -57,98 +57,6 @@ void Point::rotate(double angle, const Point ¢er) (*this)(1) = (coord_t)round( (double)center(1) + c * dy + s * dx ); } -int Point::nearest_point_index(const Points &points) const -{ - PointConstPtrs p; - p.reserve(points.size()); - for (Points::const_iterator it = points.begin(); it != points.end(); ++it) - p.push_back(&*it); - return this->nearest_point_index(p); -} - -int Point::nearest_point_index(const PointConstPtrs &points) const -{ - int idx = -1; - double distance = -1; // double because long is limited to 2147483647 on some platforms and it's not enough - - for (PointConstPtrs::const_iterator it = points.begin(); it != points.end(); ++it) { - /* If the X distance of the candidate is > than the total distance of the - best previous candidate, we know we don't want it */ - double d = sqr((*this)(0) - (*it)->x()); - if (distance != -1 && d > distance) continue; - - /* If the Y distance of the candidate is > than the total distance of the - best previous candidate, we know we don't want it */ - d += sqr((*this)(1) - (*it)->y()); - if (distance != -1 && d > distance) continue; - - idx = it - points.begin(); - distance = d; - - if (distance < EPSILON) break; - } - - return idx; -} - -int Point::nearest_point_index(const PointPtrs &points) const -{ - PointConstPtrs p; - p.reserve(points.size()); - for (PointPtrs::const_iterator it = points.begin(); it != points.end(); ++it) - p.push_back(*it); - return this->nearest_point_index(p); -} - -bool Point::nearest_point(const Points &points, Point* point) const -{ - int idx = this->nearest_point_index(points); - if (idx == -1) return false; - *point = points.at(idx); - return true; -} - -Point Point::projection_onto(const MultiPoint &poly) const -{ - Point running_projection = poly.first_point(); - double running_min = (running_projection - *this).cast().norm(); - - Lines lines = poly.lines(); - for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) { - Point point_temp = this->projection_onto(*line); - if ((point_temp - *this).cast().norm() < running_min) { - running_projection = point_temp; - running_min = (running_projection - *this).cast().norm(); - } - } - return running_projection; -} - -Point Point::projection_onto(const Line &line) const -{ - if (line.a == line.b) return line.a; - - /* - (Ported from VisiLibity by Karl J. Obermeyer) - The projection of point_temp onto the line determined by - line_segment_temp can be represented as an affine combination - expressed in the form projection of - Point = theta*line_segment_temp.first + (1.0-theta)*line_segment_temp.second. - If theta is outside the interval [0,1], then one of the Line_Segment's endpoints - must be closest to calling Point. - */ - double lx = (double)(line.b(0) - line.a(0)); - double ly = (double)(line.b(1) - line.a(1)); - double theta = ( (double)(line.b(0) - (*this)(0))*lx + (double)(line.b(1)- (*this)(1))*ly ) - / ( sqr(lx) + sqr(ly) ); - - if (0.0 <= theta && theta <= 1.0) - return (theta * line.a.cast() + (1.0-theta) * line.b.cast()).cast(); - - // Else pick closest endpoint. - return ((line.a - *this).cast().squaredNorm() < (line.b - *this).cast().squaredNorm()) ? line.a : line.b; -} - bool has_duplicate_points(std::vector &&pts) { std::sort(pts.begin(), pts.end()); @@ -179,6 +87,29 @@ BoundingBoxf get_extents(const std::vector &pts) return bbox; } +int nearest_point_index(const Points &points, const Point &pt) +{ + int64_t distance = std::numeric_limits::max(); + int idx = -1; + + for (const Point &pt2 : points) { + // If the X distance of the candidate is > than the total distance of the + // best previous candidate, we know we don't want it. + int64_t d = sqr(pt2.x() - pt.x()); + if (d < distance) { + // If the Y distance of the candidate is > than the total distance of the + // best previous candidate, we know we don't want it. + d += sqr(pt2.y() - pt.y()); + if (d < distance) { + idx = &pt2 - points.data(); + distance = d; + } + } + } + + return idx; +} + std::ostream& operator<<(std::ostream &stm, const Vec2d &pointf) { return stm << pointf(0) << "," << pointf(1); diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index 949ddbad1..3d0bae2c3 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -17,8 +17,6 @@ namespace Slic3r { class BoundingBox; class BoundingBoxf; -class Line; -class MultiPoint; class Point; using Vector = Point; @@ -183,13 +181,6 @@ public: Point rotated(double angle) const { Point res(*this); res.rotate(angle); return res; } Point rotated(double cos_a, double sin_a) const { Point res(*this); res.rotate(cos_a, sin_a); return res; } Point rotated(double angle, const Point ¢er) const { Point res(*this); res.rotate(angle, center); return res; } - Point rotate_90_degree_ccw() const { return Point(-this->y(), this->x()); } - int nearest_point_index(const Points &points) const; - int nearest_point_index(const PointConstPtrs &points) const; - int nearest_point_index(const PointPtrs &points) const; - bool nearest_point(const Points &points, Point* point) const; - Point projection_onto(const MultiPoint &poly) const; - Point projection_onto(const Line &line) const; }; inline bool operator<(const Point &l, const Point &r) @@ -242,6 +233,14 @@ BoundingBox get_extents(const Points &pts); BoundingBox get_extents(const std::vector &pts); BoundingBoxf get_extents(const std::vector &pts); +int nearest_point_index(const Points &points, const Point &pt); + +inline std::pair nearest_point(const Points &points, const Point &pt) +{ + int idx = nearest_point_index(points, pt); + return idx == -1 ? std::make_pair(Point(), false) : std::make_pair(points[idx], true); +} + // Test for duplicate points in a vector of points. // The points are copied, sorted and checked for duplicates globally. bool has_duplicate_points(std::vector &&pts); diff --git a/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp index 26b326b1c..717e25aed 100644 --- a/src/libslic3r/Polygon.cpp +++ b/src/libslic3r/Polygon.cpp @@ -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().norm(); + for (size_t i = 1; i < this->points.size(); ++ i) + l += (this->points[i] - this->points[i - 1]).cast().norm(); + } + return l; +} + Lines Polygon::lines() const { return to_lines(*this); @@ -88,12 +99,6 @@ void Polygon::douglas_peucker(double tolerance) this->points = std::move(p); } -// Does an unoriented polygon contain a point? -bool Polygon::contains(const Point &p) const -{ - return Slic3r::contains(*this, p, true); -} - // this only works on CCW polygons as CW will be ripped out by Clipper's simplify_polygons() Polygons Polygon::simplify(double tolerance) const { @@ -150,6 +155,64 @@ Point Polygon::centroid() const return Point(Vec2d(c / (3. * area_sum))); } +bool Polygon::intersection(const Line &line, Point *intersection) const +{ + 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; +} + +bool Polygon::first_intersection(const Line& line, Point* intersection) const +{ + 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().squaredNorm(); + *intersection = ip; + } else { + double d = (line.a - ip).cast().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 diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp index ccfe213e8..8a4a2ba0a 100644 --- a/src/libslic3r/Polygon.hpp +++ b/src/libslic3r/Polygon.hpp @@ -15,11 +15,14 @@ using Polygons = std::vector; using PolygonPtrs = std::vector; using ConstPolygonPtrs = std::vector; +// 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 { public: Polygon() = default; - ~Polygon() override = default; explicit Polygon(const Points &points) : MultiPoint(points) {} Polygon(std::initializer_list points) : MultiPoint(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]; } // 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; // Split a closed polygon into an open polyline, with the split point duplicated at both ends. Polyline split_at_index(int index) const; @@ -58,13 +62,21 @@ public: void douglas_peucker(double tolerance); // Does an unoriented polygon contain a point? - // Tested by counting intersections along a horizontal line. - bool contains(const Point &point) const; + bool contains(const Point &point) const { return Slic3r::contains(*this, point, true); } + // Approximate on boundary test. + bool on_boundary(const Point &point, double eps) const + { return (this->point_projection(point) - point).cast().squaredNorm() < eps * eps; } + Polygons simplify(double tolerance) const; void simplify(double tolerance, Polygons &polygons) const; void densify(float min_length, std::vector* lengths = nullptr); void triangulate_convex(Polygons* polygons) const; Point centroid() 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. @@ -254,10 +266,6 @@ inline Polygons to_polygons(std::vector &&paths) // however their contours may be rotated. bool polygons_match(const Polygon &l, const Polygon &r); -// 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); - Polygon make_circle(double radius, double error); Polygon make_circle_num_segments(double radius, size_t num_segments); diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp index 42d89c882..e2dcabfe1 100644 --- a/src/libslic3r/Polyline.cpp +++ b/src/libslic3r/Polyline.cpp @@ -19,6 +19,14 @@ const Point& Polyline::leftmost_point() const return *p; } +double Polyline::length() const +{ + double l = 0; + for (size_t i = 1; i < this->points.size(); ++ i) + l += (this->points[i] - this->points[i - 1]).cast().norm(); + return l; +} + Lines Polyline::lines() const { Lines lines; @@ -146,9 +154,8 @@ void Polyline::split_at(const Point &point, Polyline* p1, Polyline* p2) const auto min_point_it = this->points.cbegin(); Point prev = this->points.front(); for (auto it = this->points.cbegin() + 1; it != this->points.cend(); ++ it) { - Point proj = point.projection_onto(Line(prev, *it)); - auto d2 = (proj - point).cast().squaredNorm(); - if (d2 < min_dist2) { + Point proj; + if (double d2 = line_alg::distance_to_squared(Line(prev, *it), point, &proj); d2 < min_dist2) { min_dist2 = d2; min_point_it = it; } @@ -235,9 +242,8 @@ std::pair foot_pt(const Points &polyline, const Point &pt) auto it = polyline.begin(); auto it_proj = polyline.begin(); for (++ it; it != polyline.end(); ++ it) { - Point foot_pt = pt.projection_onto(Line(prev, *it)); - double d2 = (foot_pt - pt).cast().squaredNorm(); - if (d2 < d2_min) { + Point foot_pt; + if (double d2 = line_alg::distance_to_squared(Line(prev, *it), pt, &foot_pt); d2 < d2_min) { d2_min = d2; foot_pt_min = foot_pt; it_proj = it; @@ -286,6 +292,14 @@ void ThickPolyline::clip_end(double distance) assert(this->width.size() == (this->points.size() - 1) * 2); } +double Polyline3::length() const +{ + double l = 0; + for (size_t i = 1; i < this->points.size(); ++ i) + l += (this->points[i] - this->points[i - 1]).cast().norm(); + return l; +} + Lines3 Polyline3::lines() const { Lines3 lines; diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index 547664d4f..bee5e51ba 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -17,7 +17,6 @@ typedef std::vector ThickPolylines; class Polyline : public MultiPoint { public: Polyline() = default; - ~Polyline() override = default; Polyline(const Polyline &other) : MultiPoint(other.points) {} Polyline(Polyline &&other) : MultiPoint(std::move(other.points)) {} Polyline(std::initializer_list list) : MultiPoint(list) {} @@ -64,11 +63,12 @@ public: Point& operator[](Points::size_type idx) { return this->points[idx]; } const Point& operator[](Points::size_type idx) const { return this->points[idx]; } - const Point& last_point() const override { return this->points.back(); } + double length() const; + const Point& last_point() const { return this->points.back(); } const Point& leftmost_point() const; - Lines lines() const override; + Lines lines() const; - virtual void clip_end(double distance); + void clip_end(double distance); void clip_start(double distance); void extend_end(double distance); void extend_start(double distance); @@ -170,7 +170,7 @@ public: std::swap(this->endpoints.first, this->endpoints.second); } - void clip_end(double distance) override; + void clip_end(double distance); std::vector width; std::pair endpoints; @@ -191,7 +191,8 @@ inline ThickPolylines to_thick_polylines(Polylines &&polylines, const coordf_t w class Polyline3 : public MultiPoint3 { public: - virtual Lines3 lines() const; + double length() const; + Lines3 lines() const; }; typedef std::vector Polylines3; diff --git a/xs/t/03_point.t b/xs/t/03_point.t index f888349b3..389e120c7 100644 --- a/xs/t/03_point.t +++ b/xs/t/03_point.t @@ -4,7 +4,7 @@ use strict; use warnings; use Slic3r::XS; -use Test::More tests => 21; +use Test::More tests => 16; my $point = Slic3r::Point->new(10, 15); @@ -59,22 +59,4 @@ is_deeply [ @$point2 ], [30, 15], 'translate'; ok $p0->ccw($p1, $p2) < 0, 'ccw() does not overflow'; } -{ - my $point = Slic3r::Point->new(15,15); - my $line = Slic3r::Line->new([10,10], [20,10]); - is_deeply $point->projection_onto_line($line)->pp, [15,10], 'project_onto_line'; - - $point = Slic3r::Point->new(0, 15); - is_deeply $point->projection_onto_line($line)->pp, [10,10], 'project_onto_line'; - - $point = Slic3r::Point->new(25, 15); - is_deeply $point->projection_onto_line($line)->pp, [20,10], 'project_onto_line'; - - $point = Slic3r::Point->new(10,10); - is_deeply $point->projection_onto_line($line)->pp, [10,10], 'project_onto_line'; - - $point = Slic3r::Point->new(12, 10); - is_deeply $point->projection_onto_line($line)->pp, [12,10], 'project_onto_line'; -} - __END__ diff --git a/xs/xsp/Point.xsp b/xs/xsp/Point.xsp index 2d70e0203..0d44ea364 100644 --- a/xs/xsp/Point.xsp +++ b/xs/xsp/Point.xsp @@ -29,9 +29,8 @@ %code{% (*THIS)(0) = val; %}; void set_y(int val) %code{% (*THIS)(1) = val; %}; - int nearest_point_index(Points points); Clone nearest_point(Points points) - %code{% Point p; THIS->nearest_point(points, &p); RETVAL = p; %}; + %code{% RETVAL = nearest_point(points, *THIS).first; %}; double distance_to(Point* point) %code{% RETVAL = (*point - *THIS).cast().norm(); %}; double distance_to_line(Line* line) @@ -40,8 +39,6 @@ %code{% RETVAL = line->perp_distance_to(*THIS); %}; double ccw(Point* p1, Point* p2) %code{% RETVAL = cross2((*p1 - *THIS).cast(), (*p2 - *p1).cast()); %}; - Point* projection_onto_line(Line* line) - %code{% RETVAL = new Point(THIS->projection_onto(*line)); %}; Point* negative() %code{% RETVAL = new Point(- *THIS); %}; std::string serialize() %code{% char buf[2048]; sprintf(buf, "%ld,%ld", (*THIS)(0), (*THIS)(1)); RETVAL = buf; %};