From 5f86d11c74dbdb0a4aa955b358493f69bf72b5c1 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik <bubnikv@gmail.com> Date: Fri, 5 Feb 2021 11:20:04 +0100 Subject: [PATCH] EdgeGrid refactoring to support both open and closed contours. Squashed commit of the following: commit 4e13a8fe19abcc9aae39a9bc4c7953a743196504 Merge: 6ae766409 6f89da1f3 Author: Vojtech Bubnik <bubnikv@gmail.com> Date: Fri Feb 5 11:19:35 2021 +0100 Merge remote-tracking branch 'remotes/origin/master' into vb_edgegrid_open_lines commit 6ae76640942269993c942861f0444088843e3fa1 Author: Vojtech Bubnik <bubnikv@gmail.com> Date: Fri Feb 5 11:14:48 2021 +0100 EdgeGrid enhancement to accept both the open and closed lines. commit 36a5efcd558bd5fd5f46b5f561387a2c73221553 Author: Vojtech Bubnik <bubnikv@gmail.com> Date: Fri Feb 5 10:52:14 2021 +0100 EdgeGrid improvements: Documentation, one bug fix after recent refactoring. commit 6f89da1f394561c7338676a1c8e8e72faeb85aad Author: tamasmeszaros <meszaros.q@gmail.com> Date: Thu Feb 4 20:31:50 2021 +0100 Disable libicu for boost-regex Should have been disabled from the beginning commit ffc77b1a72a0be9b5622fd33defeebb24bf07b34 Author: Vojtech Bubnik <bubnikv@gmail.com> Date: Thu Feb 4 18:40:33 2021 +0100 EdgeGrid: Annotated those methods that do not work with open contours. commit 8039a645b4bf0c46c99b90a9c34e7189d7442f86 Author: Vojtech Bubnik <bubnikv@gmail.com> Date: Thu Feb 4 18:28:21 2021 +0100 Refactoring of EdgeGrid structure to support both closed and open lines. --- src/libslic3r/Brim.cpp | 11 +- src/libslic3r/EdgeGrid.cpp | 245 +++++++++--------- src/libslic3r/EdgeGrid.hpp | 95 ++++++- src/libslic3r/ElephantFootCompensation.cpp | 60 +++-- src/libslic3r/Fill/FillBase.cpp | 2 +- .../GCode/AvoidCrossingPerimeters.cpp | 46 ++-- 6 files changed, 272 insertions(+), 187 deletions(-) diff --git a/src/libslic3r/Brim.cpp b/src/libslic3r/Brim.cpp index 9f18405b7..0dc528819 100644 --- a/src/libslic3r/Brim.cpp +++ b/src/libslic3r/Brim.cpp @@ -230,14 +230,11 @@ static Polylines connect_brim_lines(Polylines &&polylines, const Polygons &brim_ if (polylines.empty()) return Polylines(); - std::vector<Points> polylines_points(polylines.size() + brim_area.size()); - for (const Polyline &poly : polylines) - polylines_points[&poly - &polylines.front()] = poly.points; - for (const Polygon &poly : brim_area) - polylines_points.emplace_back(poly.points); + BoundingBox bbox = get_extents(polylines); + bbox.merge(get_extents(brim_area)); - EdgeGrid::Grid grid(get_extents(polylines).inflated(SCALED_EPSILON)); - grid.create(polylines_points, coord_t(scale_(10.))); + EdgeGrid::Grid grid(bbox.inflated(SCALED_EPSILON)); + grid.create(brim_area, polylines, coord_t(scale_(10.))); struct Visitor { diff --git a/src/libslic3r/EdgeGrid.cpp b/src/libslic3r/EdgeGrid.cpp index e5e8718c3..5e541ab69 100644 --- a/src/libslic3r/EdgeGrid.cpp +++ b/src/libslic3r/EdgeGrid.cpp @@ -34,76 +34,82 @@ EdgeGrid::Grid::~Grid() void EdgeGrid::Grid::create(const Polygons &polygons, coord_t resolution) { - // Count the contours. - size_t ncontours = 0; - for (size_t j = 0; j < polygons.size(); ++ j) - if (! polygons[j].points.empty()) - ++ ncontours; - // Collect the contours. - m_contours.assign(ncontours, nullptr); - ncontours = 0; - for (size_t j = 0; j < polygons.size(); ++ j) - if (! polygons[j].points.empty()) - m_contours[ncontours ++] = &polygons[j].points; + m_contours.reserve(std::count_if(polygons.begin(), polygons.end(), [](const Polygon &p) { return ! p.empty(); })); + for (const Polygon &polygon : polygons) + if (! polygon.empty()) + m_contours.emplace_back(polygon.points, false); create_from_m_contours(resolution); } void EdgeGrid::Grid::create(const std::vector<const Polygon*> &polygons, coord_t resolution) { - // Count the contours. - size_t ncontours = 0; - for (size_t j = 0; j < polygons.size(); ++ j) - if (! polygons[j]->points.empty()) - ++ ncontours; - // Collect the contours. - m_contours.assign(ncontours, nullptr); - ncontours = 0; - for (size_t j = 0; j < polygons.size(); ++ j) - if (! polygons[j]->points.empty()) - m_contours[ncontours ++] = &polygons[j]->points; + m_contours.reserve(std::count_if(polygons.begin(), polygons.end(), [](const Polygon *p) { return ! p->empty(); })); + for (const Polygon *polygon : polygons) + if (! polygon->empty()) + m_contours.emplace_back(polygon->points, false); create_from_m_contours(resolution); } -void EdgeGrid::Grid::create(const std::vector<Points> &polygons, coord_t resolution) +void EdgeGrid::Grid::create(const std::vector<Points> &polygons, coord_t resolution, bool open_polylines) { - // Count the contours. - size_t ncontours = 0; - for (size_t j = 0; j < polygons.size(); ++ j) - if (! polygons[j].empty()) - ++ ncontours; - // Collect the contours. - m_contours.assign(ncontours, nullptr); - ncontours = 0; - for (size_t j = 0; j < polygons.size(); ++ j) - if (! polygons[j].empty()) - m_contours[ncontours ++] = &polygons[j]; + m_contours.reserve(std::count_if(polygons.begin(), polygons.end(), [](const Points &p) { return p.size() > 1; })); + for (const Points &points : polygons) + if (points.size() > 1) { + const Point *begin = points.data(); + const Point *end = points.data() + points.size(); + bool open = open_polylines; + if (open_polylines) { + if (*begin == end[-1]) { + open = false; + -- end; + } + } else + assert(*begin != end[-1]); + m_contours.emplace_back(begin, end, open); + } + + create_from_m_contours(resolution); +} + +void EdgeGrid::Grid::create(const Polygons &polygons, const Polylines &polylines, coord_t resolution) +{ + // Collect the contours. + m_contours.reserve( + std::count_if(polygons.begin(), polygons.end(), [](const Polygon &p) { return p.size() > 1; }) + + std::count_if(polylines.begin(), polylines.end(), [](const Polyline &p) { return p.size() > 1; })); + + for (const Polyline &polyline : polylines) + if (polyline.size() > 1) { + const Point *begin = polyline.points.data(); + const Point *end = polyline.points.data() + polyline.size(); + bool open = true; + if (*begin == end[-1]) { + open = false; + -- end; + } + m_contours.emplace_back(begin, end, open); + } + + for (const Polygon &polygon : polygons) + if (polygon.size() > 1) + m_contours.emplace_back(polygon.points, false); create_from_m_contours(resolution); } void EdgeGrid::Grid::create(const ExPolygon &expoly, coord_t resolution) { - // Count the contours. - size_t ncontours = 0; - if (! expoly.contour.points.empty()) - ++ ncontours; - for (size_t j = 0; j < expoly.holes.size(); ++ j) - if (! expoly.holes[j].points.empty()) - ++ ncontours; - - // Collect the contours. - m_contours.assign(ncontours, nullptr); - ncontours = 0; - if (! expoly.contour.points.empty()) - m_contours[ncontours++] = &expoly.contour.points; - for (size_t j = 0; j < expoly.holes.size(); ++ j) - if (! expoly.holes[j].points.empty()) - m_contours[ncontours++] = &expoly.holes[j].points; + m_contours.reserve((expoly.contour.empty() ? 0 : 1) + std::count_if(expoly.holes.begin(), expoly.holes.end(), [](const Polygon &p) { return ! p.empty(); })); + if (! expoly.contour.empty()) + m_contours.emplace_back(expoly.contour.points, false); + for (const Polygon &hole : expoly.holes) + if (! hole.empty()) + m_contours.emplace_back(hole.points, false); create_from_m_contours(resolution); } @@ -112,25 +118,20 @@ void EdgeGrid::Grid::create(const ExPolygons &expolygons, coord_t resolution) { // Count the contours. size_t ncontours = 0; - for (size_t i = 0; i < expolygons.size(); ++ i) { - const ExPolygon &expoly = expolygons[i]; - if (! expoly.contour.points.empty()) + for (const ExPolygon &expoly : expolygons) { + if (! expoly.contour.empty()) ++ ncontours; - for (size_t j = 0; j < expoly.holes.size(); ++ j) - if (! expoly.holes[j].points.empty()) - ++ ncontours; + ncontours += std::count_if(expoly.holes.begin(), expoly.holes.end(), [](const Polygon &p) { return ! p.empty(); }); } // Collect the contours. - m_contours.assign(ncontours, nullptr); - ncontours = 0; - for (size_t i = 0; i < expolygons.size(); ++ i) { - const ExPolygon &expoly = expolygons[i]; - if (! expoly.contour.points.empty()) - m_contours[ncontours++] = &expoly.contour.points; - for (size_t j = 0; j < expoly.holes.size(); ++ j) - if (! expoly.holes[j].points.empty()) - m_contours[ncontours++] = &expoly.holes[j].points; + m_contours.reserve(ncontours); + for (const ExPolygon &expoly : expolygons) { + if (! expoly.contour.empty()) + m_contours.emplace_back(expoly.contour.points, false); + for (const Polygon &hole : expoly.holes) + if (! hole.empty()) + m_contours.emplace_back(hole.points, false); } create_from_m_contours(resolution); @@ -146,11 +147,13 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution) { assert(resolution > 0); // 1) Measure the bounding box. - for (size_t i = 0; i < m_contours.size(); ++ i) { - const Slic3r::Points &pts = *m_contours[i]; - for (size_t j = 0; j < pts.size(); ++ j) - m_bbox.merge(pts[j]); + for (const Contour &contour : m_contours) { + assert(contour.num_segments() > 0); + assert(*contour.begin() != contour.end()[-1]); + for (const Slic3r::Point &pt : contour) + m_bbox.merge(pt); } + coord_t eps = 16; m_bbox.min(0) -= eps; m_bbox.min(1) -= eps; @@ -165,11 +168,11 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution) // 3) First round of contour rasterization, count the edges per grid cell. for (size_t i = 0; i < m_contours.size(); ++ i) { - const Slic3r::Points &pts = *m_contours[i]; - for (size_t j = 0; j < pts.size(); ++ j) { + const Contour &contour = m_contours[i]; + for (size_t j = 0; j < contour.num_segments(); ++ j) { // End points of the line segment. - Slic3r::Point p1(pts[j]); - Slic3r::Point p2 = pts[(j + 1 == pts.size()) ? 0 : j + 1]; + Slic3r::Point p1(contour.segment_start(j)); + Slic3r::Point p2(contour.segment_end(j)); p1(0) -= m_bbox.min(0); p1(1) -= m_bbox.min(1); p2(0) -= m_bbox.min(0); @@ -328,9 +331,9 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution) assert(visitor.i == 0); for (; visitor.i < m_contours.size(); ++ visitor.i) { - const Slic3r::Points &pts = *m_contours[visitor.i]; - for (visitor.j = 0; visitor.j < pts.size(); ++ visitor.j) - this->visit_cells_intersecting_line(pts[visitor.j], pts[(visitor.j + 1 == pts.size()) ? 0 : visitor.j + 1], visitor); + const Contour &contour = m_contours[visitor.i]; + for (visitor.j = 0; visitor.j < contour.num_segments(); ++ visitor.j) + this->visit_cells_intersecting_line(contour.segment_start(visitor.j), contour.segment_end(visitor.j), visitor); } } @@ -696,11 +699,12 @@ void EdgeGrid::Grid::calculate_sdf() const Cell &cell = m_cells[r * m_cols + c]; // For each segment in the cell: for (size_t i = cell.begin; i != cell.end; ++ i) { - const Slic3r::Points &pts = *m_contours[m_cell_data[i].first]; + const Contour &contour = m_contours[m_cell_data[i].first]; + assert(contour.closed()); size_t ipt = m_cell_data[i].second; // End points of the line segment. - const Slic3r::Point &p1 = pts[ipt]; - const Slic3r::Point &p2 = pts[(ipt + 1 == pts.size()) ? 0 : ipt + 1]; + const Slic3r::Point &p1 = contour.segment_start(ipt); + const Slic3r::Point &p2 = contour.segment_end(ipt); // Segment vector const Slic3r::Point v_seg = p2 - p1; // l2 of v_seg @@ -724,7 +728,7 @@ void EdgeGrid::Grid::calculate_sdf() double dabs = sqrt(int64_t(v_pt(0)) * int64_t(v_pt(0)) + int64_t(v_pt(1)) * int64_t(v_pt(1))); if (dabs < d_min) { // Previous point. - const Slic3r::Point &p0 = pts[(ipt == 0) ? (pts.size() - 1) : ipt - 1]; + const Slic3r::Point &p0 = contour.segment_prev(ipt); Slic3r::Point v_seg_prev = p1 - p0; int64_t t2_pt = int64_t(v_seg_prev(0)) * int64_t(v_pt(0)) + int64_t(v_seg_prev(1)) * int64_t(v_pt(1)); if (t2_pt > 0) { @@ -1044,7 +1048,7 @@ float EdgeGrid::Grid::signed_distance_bilinear(const Point &pt) const return f; } -EdgeGrid::Grid::ClosestPointResult EdgeGrid::Grid::closest_point(const Point &pt, coord_t search_radius) const +EdgeGrid::Grid::ClosestPointResult EdgeGrid::Grid::closest_point_signed_distance(const Point &pt, coord_t search_radius) const { BoundingBox bbox; bbox.min = bbox.max = Point(pt(0) - m_bbox.min(0), pt(1) - m_bbox.min(1)); @@ -1083,12 +1087,13 @@ EdgeGrid::Grid::ClosestPointResult EdgeGrid::Grid::closest_point(const Point &pt for (int c = bbox.min(0); c <= bbox.max(0); ++ c) { const Cell &cell = m_cells[r * m_cols + c]; for (size_t i = cell.begin; i < cell.end; ++ i) { - const size_t contour_idx = m_cell_data[i].first; - const Slic3r::Points &pts = *m_contours[contour_idx]; + const size_t contour_idx = m_cell_data[i].first; + const Contour &contour = m_contours[contour_idx]; + assert(contour.closed()); size_t ipt = m_cell_data[i].second; // End points of the line segment. - const Slic3r::Point &p1 = pts[ipt]; - const Slic3r::Point &p2 = pts[(ipt + 1 == pts.size()) ? 0 : ipt + 1]; + const Slic3r::Point &p1 = contour.segment_start(ipt); + const Slic3r::Point &p2 = contour.segment_end(ipt); const Slic3r::Point v_seg = p2 - p1; const Slic3r::Point v_pt = pt - p1; // dot(p2-p1, pt-p1) @@ -1100,7 +1105,7 @@ EdgeGrid::Grid::ClosestPointResult EdgeGrid::Grid::closest_point(const Point &pt double dabs = sqrt(int64_t(v_pt(0)) * int64_t(v_pt(0)) + int64_t(v_pt(1)) * int64_t(v_pt(1))); if (dabs < d_min) { // Previous point. - const Slic3r::Point &p0 = pts[(ipt == 0) ? (pts.size() - 1) : ipt - 1]; + const Slic3r::Point &p0 = contour.segment_prev(ipt); Slic3r::Point v_seg_prev = p1 - p0; int64_t t2_pt = int64_t(v_seg_prev(0)) * int64_t(v_pt(0)) + int64_t(v_seg_prev(1)) * int64_t(v_pt(1)); if (t2_pt > 0) { @@ -1156,9 +1161,9 @@ EdgeGrid::Grid::ClosestPointResult EdgeGrid::Grid::closest_point(const Point &pt assert(result.t >= 0. && result.t <= 1.); #ifndef NDEBUG { - const Slic3r::Points &pts = *m_contours[result.contour_idx]; - const Slic3r::Point &p1 = pts[result.start_point_idx]; - const Slic3r::Point &p2 = pts[(result.start_point_idx + 1 == pts.size()) ? 0 : result.start_point_idx + 1]; + const Contour &contour = m_contours[result.contour_idx]; + const Slic3r::Point &p1 = contour.segment_start(result.start_point_idx); + const Slic3r::Point &p2 = contour.segment_end(result.start_point_idx); Vec2d vfoot; if (result.t == 0) vfoot = p1.cast<double>() - pt.cast<double>(); @@ -1212,11 +1217,12 @@ bool EdgeGrid::Grid::signed_distance_edges(const Point &pt, coord_t search_radiu for (int c = bbox.min(0); c <= bbox.max(0); ++ c) { const Cell &cell = m_cells[r * m_cols + c]; for (size_t i = cell.begin; i < cell.end; ++ i) { - const Slic3r::Points &pts = *m_contours[m_cell_data[i].first]; + const Contour &contour = m_contours[m_cell_data[i].first]; + assert(contour.closed()); size_t ipt = m_cell_data[i].second; // End points of the line segment. - const Slic3r::Point &p1 = pts[ipt]; - const Slic3r::Point &p2 = pts[(ipt + 1 == pts.size()) ? 0 : ipt + 1]; + const Slic3r::Point &p1 = contour.segment_start(ipt); + const Slic3r::Point &p2 = contour.segment_end(ipt); Slic3r::Point v_seg = p2 - p1; Slic3r::Point v_pt = pt - p1; // dot(p2-p1, pt-p1) @@ -1228,7 +1234,7 @@ bool EdgeGrid::Grid::signed_distance_edges(const Point &pt, coord_t search_radiu double dabs = sqrt(int64_t(v_pt(0)) * int64_t(v_pt(0)) + int64_t(v_pt(1)) * int64_t(v_pt(1))); if (dabs < d_min) { // Previous point. - const Slic3r::Point &p0 = pts[(ipt == 0) ? (pts.size() - 1) : ipt - 1]; + const Slic3r::Point &p0 = contour.segment_prev(ipt); Slic3r::Point v_seg_prev = p1 - p0; int64_t t2_pt = int64_t(v_seg_prev(0)) * int64_t(v_pt(0)) + int64_t(v_seg_prev(1)) * int64_t(v_pt(1)); if (t2_pt > 0) { @@ -1418,26 +1424,26 @@ std::vector<std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge>> const Cell &cell = m_cells[r * m_cols + c]; // For each pair of segments in the cell: for (size_t i = cell.begin; i != cell.end; ++ i) { - const Slic3r::Points &ipts = *m_contours[m_cell_data[i].first]; + const Contour &icontour = m_contours[m_cell_data[i].first]; size_t ipt = m_cell_data[i].second; // End points of the line segment and their vector. - const Slic3r::Point &ip1 = ipts[ipt]; - const Slic3r::Point &ip2 = ipts[(ipt + 1 == ipts.size()) ? 0 : ipt + 1]; + const Slic3r::Point &ip1 = icontour.segment_start(ipt); + const Slic3r::Point &ip2 = icontour.segment_end(ipt); for (size_t j = i + 1; j != cell.end; ++ j) { - const Slic3r::Points &jpts = *m_contours[m_cell_data[j].first]; - size_t jpt = m_cell_data[j].second; + const Contour &jcontour = m_contours[m_cell_data[j].first]; + size_t jpt = m_cell_data[j].second; // End points of the line segment and their vector. - const Slic3r::Point &jp1 = jpts[jpt]; - const Slic3r::Point &jp2 = jpts[(jpt + 1 == jpts.size()) ? 0 : jpt + 1]; - if (&ipts == &jpts && (&ip1 == &jp2 || &jp1 == &ip2)) + const Slic3r::Point &jp1 = jcontour.segment_start(jpt); + const Slic3r::Point &jp2 = jcontour.segment_end(jpt); + if (&icontour == &jcontour && (&ip1 == &jp2 || &jp1 == &ip2)) // Segments of the same contour share a common vertex. continue; if (Geometry::segments_intersect(ip1, ip2, jp1, jp2)) { // The two segments intersect. Add them to the output. - int jfirst = (&jpts < &ipts) || (&jpts == &ipts && jpt < ipt); + int jfirst = (&jcontour < &icontour) || (&jcontour == &icontour && jpt < ipt); out.emplace_back(jfirst ? - std::make_pair(std::make_pair(&ipts, ipt), std::make_pair(&jpts, jpt)) : - std::make_pair(std::make_pair(&ipts, ipt), std::make_pair(&jpts, jpt))); + std::make_pair(std::make_pair(&icontour, ipt), std::make_pair(&jcontour, jpt)) : + std::make_pair(std::make_pair(&icontour, ipt), std::make_pair(&jcontour, jpt))); } } } @@ -1455,18 +1461,18 @@ bool EdgeGrid::Grid::has_intersecting_edges() const const Cell &cell = m_cells[r * m_cols + c]; // For each pair of segments in the cell: for (size_t i = cell.begin; i != cell.end; ++ i) { - const Slic3r::Points &ipts = *m_contours[m_cell_data[i].first]; + const Contour &icontour = m_contours[m_cell_data[i].first]; size_t ipt = m_cell_data[i].second; // End points of the line segment and their vector. - const Slic3r::Point &ip1 = ipts[ipt]; - const Slic3r::Point &ip2 = ipts[(ipt + 1 == ipts.size()) ? 0 : ipt + 1]; + const Slic3r::Point &ip1 = icontour.segment_start(ipt); + const Slic3r::Point &ip2 = icontour.segment_end(ipt); for (size_t j = i + 1; j != cell.end; ++ j) { - const Slic3r::Points &jpts = *m_contours[m_cell_data[j].first]; + const Contour &jcontour = m_contours[m_cell_data[j].first]; size_t jpt = m_cell_data[j].second; // End points of the line segment and their vector. - const Slic3r::Point &jp1 = jpts[jpt]; - const Slic3r::Point &jp2 = jpts[(jpt + 1 == jpts.size()) ? 0 : jpt + 1]; - if (! (&ipts == &jpts && (&ip1 == &jp2 || &jp1 == &ip2)) && + const Slic3r::Point &jp1 = jcontour.segment_start(jpt); + const Slic3r::Point &jp2 = jcontour.segment_end(jpt); + if (! (&icontour == &jcontour && (&ip1 == &jp2 || &jp1 == &ip2)) && Geometry::segments_intersect(ip1, ip2, jp1, jp2)) return true; } @@ -1601,22 +1607,27 @@ void export_intersections_to_svg(const std::string &filename, const Polygons &po SVG svg(filename.c_str(), bbox); svg.draw(union_ex(polygons), "gray", 0.25f); svg.draw_outline(polygons, "black"); - std::set<const Points*> intersecting_contours; + std::set<const EdgeGrid::Contour*> intersecting_contours; for (const std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge> &ie : intersections) { intersecting_contours.insert(ie.first.first); intersecting_contours.insert(ie.second.first); } // Highlight the contours with intersections. coord_t line_width = coord_t(scale_(0.01)); - for (const Points *ic : intersecting_contours) { - svg.draw_outline(Polygon(*ic), "green"); - svg.draw_outline(Polygon(*ic), "black", line_width); + for (const EdgeGrid::Contour *ic : intersecting_contours) { + if (ic->open()) + svg.draw(Polyline(Points(ic->begin(), ic->end())), "green"); + else { + Polygon polygon(Points(ic->begin(), ic->end())); + svg.draw_outline(polygon, "green"); + svg.draw_outline(polygon, "black", line_width); + } } // Paint the intersections. for (const std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge> &intersecting_edges : intersections) { auto edge = [](const EdgeGrid::Grid::ContourEdge &e) { - return Line(e.first->at(e.second), - e.first->at((e.second + 1 == e.first->size()) ? 0 : e.second + 1)); + return Line(e.first->segment_start(e.second), + e.first->segment_end(e.second)); }; svg.draw(edge(intersecting_edges.first), "red", line_width); svg.draw(edge(intersecting_edges.second), "red", line_width); diff --git a/src/libslic3r/EdgeGrid.hpp b/src/libslic3r/EdgeGrid.hpp index ef09e28dd..42d8d92c2 100644 --- a/src/libslic3r/EdgeGrid.hpp +++ b/src/libslic3r/EdgeGrid.hpp @@ -12,6 +12,63 @@ namespace Slic3r { namespace EdgeGrid { + +class Contour { +public: + Contour() = default; + Contour(const Slic3r::Point *begin, const Slic3r::Point *end, bool open) : m_begin(begin), m_end(end), m_open(open) {} + Contour(const Slic3r::Point *data, size_t size, bool open) : Contour(data, data + size, open) {} + Contour(const std::vector<Slic3r::Point> &pts, bool open) : Contour(pts.data(), pts.size(), open) {} + + const Slic3r::Point *begin() const { return m_begin; } + const Slic3r::Point *end() const { return m_end; } + bool open() const { return m_open; } + bool closed() const { return ! m_open; } + + // Start point of a segment idx. + const Slic3r::Point& segment_start(size_t idx) const { + assert(idx < this->num_segments()); + return m_begin[idx]; + } + + // End point of a segment idx. + const Slic3r::Point& segment_end(size_t idx) const { + assert(idx < this->num_segments()); + const Slic3r::Point *ptr = m_begin + idx + 1; + return ptr == m_end ? *m_begin : *ptr; + } + + // Start point of a segment preceding idx. + const Slic3r::Point& segment_prev(size_t idx) const { + assert(idx < this->num_segments()); + assert(idx > 0 || ! m_open); + return idx == 0 ? m_end[-1] : m_begin[idx - 1]; + } + + // Index of a segment preceding idx. + const size_t segment_idx_prev(size_t idx) const { + assert(idx < this->num_segments()); + assert(idx > 0 || ! m_open); + return (idx == 0 ? this->size() : idx) - 1; + } + + // Index of a segment preceding idx. + const size_t segment_idx_next(size_t idx) const { + assert(idx < this->num_segments()); + ++ idx; + return m_begin + idx == m_end ? 0 : idx; + } + + size_t num_segments() const { return this->size() - (m_open ? 1 : 0); } + +private: + size_t size() const { return m_end - m_begin; } + + const Slic3r::Point *m_begin { nullptr }; + const Slic3r::Point *m_end { nullptr }; + bool m_open { false }; +}; + class Grid { public: @@ -21,14 +78,24 @@ public: void set_bbox(const BoundingBox &bbox) { m_bbox = bbox; } + // Fill in the grid with open polylines or closed contours. + // If open flag is indicated, then polylines_or_polygons are considered to be open by default. + // Only if the first point of a polyline is equal to the last point of a polyline, + // then the polyline is considered to be closed and the last repeated point is removed when + // inserted into the EdgeGrid. + // Most of the Grid functions expect all the contours to be closed, you have been warned! + void create(const std::vector<Points> &polylines_or_polygons, coord_t resolution, bool open); + void create(const Polygons &polygons, const Polylines &polylines, coord_t resolution); + + // Fill in the grid with closed contours. void create(const Polygons &polygons, coord_t resolution); void create(const std::vector<const Polygon*> &polygons, coord_t resolution); - void create(const std::vector<Points> &polygons, coord_t resolution); + 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 ExPolygons &expolygons, coord_t resolution); void create(const ExPolygonCollection &expolygons, coord_t resolution); - const std::vector<const Slic3r::Points*>& contours() const { return m_contours; } + const std::vector<Contour>& contours() const { return m_contours; } #if 0 // Test, whether the edges inside the grid intersect with the polygons provided. @@ -45,12 +112,14 @@ public: // Fill in a rough m_signed_distance_field from the edge grid. // The rough SDF is used by signed_distance() for distances outside of the search_radius. + // Only call this function for closed contours! void calculate_sdf(); // Return an estimate of the signed distance based on m_signed_distance_field grid. float signed_distance_bilinear(const Point &pt) const; // Calculate a signed distance to the contours in search_radius from the point. + // Only call this function for closed contours! struct ClosestPointResult { size_t contour_idx = size_t(-1); size_t start_point_idx = size_t(-1); @@ -61,12 +130,14 @@ public: bool valid() const { return contour_idx != size_t(-1); } }; - ClosestPointResult closest_point(const Point &pt, coord_t search_radius) const; + ClosestPointResult closest_point_signed_distance(const Point &pt, coord_t search_radius) const; + // Only call this function for closed contours! bool signed_distance_edges(const Point &pt, coord_t search_radius, coordf_t &result_min_dist, bool *pon_segment = nullptr) const; // Calculate a signed distance to the contours in search_radius from the point. If no edge is found in search_radius, // return an interpolated value from m_signed_distance_field, if it exists. + // Only call this function for closed contours! bool signed_distance(const Point &pt, coord_t search_radius, coordf_t &result_min_dist) const; const BoundingBox& bbox() const { return m_bbox; } @@ -77,8 +148,8 @@ public: // For supports: Contours enclosing the rasterized edges. Polygons contours_simplified(coord_t offset, bool fill_holes) const; - typedef std::pair<const Slic3r::Points*, size_t> ContourPoint; - typedef std::pair<const Slic3r::Points*, size_t> ContourEdge; + typedef std::pair<const Contour*, size_t> ContourPoint; + typedef std::pair<const Contour*, size_t> ContourEdge; std::vector<std::pair<ContourEdge, ContourEdge>> intersecting_edges() const; bool has_intersecting_edges() const; @@ -256,16 +327,16 @@ public: std::pair<const Slic3r::Point&, const Slic3r::Point&> segment(const std::pair<size_t, size_t> &contour_and_segment_idx) const { - const Slic3r::Points &ipts = *m_contours[contour_and_segment_idx.first]; - size_t ipt = contour_and_segment_idx.second; - return std::pair<const Slic3r::Point&, const Slic3r::Point&>(ipts[ipt], ipts[ipt + 1 == ipts.size() ? 0 : ipt + 1]); + const Contour &contour = m_contours[contour_and_segment_idx.first]; + size_t iseg = contour_and_segment_idx.second; + return std::pair<const Slic3r::Point&, const Slic3r::Point&>(contour.segment_start(iseg), contour.segment_end(iseg)); } Line line(const std::pair<size_t, size_t> &contour_and_segment_idx) const { - const Slic3r::Points &ipts = *m_contours[contour_and_segment_idx.first]; - size_t ipt = contour_and_segment_idx.second; - return Line(ipts[ipt], ipts[ipt + 1 == ipts.size() ? 0 : ipt + 1]); + const Contour &contour = m_contours[contour_and_segment_idx.first]; + size_t iseg = contour_and_segment_idx.second; + return Line(contour.segment_start(iseg), contour.segment_end(iseg)); } protected: @@ -302,7 +373,7 @@ protected: // Referencing the source contours. // This format allows one to work with any Slic3r fixed point contour format // (Polygon, ExPolygon, ExPolygonCollection etc). - std::vector<const Slic3r::Points*> m_contours; + std::vector<Contour> m_contours; // Referencing a contour and a line segment of m_contours. std::vector<std::pair<size_t, size_t> > m_cell_data; diff --git a/src/libslic3r/ElephantFootCompensation.cpp b/src/libslic3r/ElephantFootCompensation.cpp index 1e50ade5a..9610582f7 100644 --- a/src/libslic3r/ElephantFootCompensation.cpp +++ b/src/libslic3r/ElephantFootCompensation.cpp @@ -104,7 +104,7 @@ std::vector<float> contour_distance(const EdgeGrid::Grid &grid, const size_t idx double param_hi; double param_end = resampled_point_parameters.back().curve_parameter; { - const Slic3r::Points &ipts = *grid.contours()[it_contour_and_segment->first]; + const EdgeGrid::Contour &contour = grid.contours()[it_contour_and_segment->first]; size_t ipt = it_contour_and_segment->second; ResampledPoint key(ipt, false, 0.); auto lower = [](const ResampledPoint& l, const ResampledPoint r) { return l.idx_src < r.idx_src || (l.idx_src == r.idx_src && int(l.interpolated) > int(r.interpolated)); }; @@ -112,7 +112,7 @@ std::vector<float> contour_distance(const EdgeGrid::Grid &grid, const size_t idx assert(it != resampled_point_parameters.end() && it->idx_src == ipt && ! it->interpolated); double t2 = cross2(dir, vptpt2) / denom; assert(t2 > - EPSILON && t2 < 1. + EPSILON); - if (++ ipt == ipts.size()) + if (contour.begin() + (++ ipt) == contour.end()) param_hi = t2 * dir2.norm(); else param_hi = it->curve_parameter + t2 * dir2.norm(); @@ -251,7 +251,7 @@ std::vector<float> contour_distance2(const EdgeGrid::Grid &grid, const size_t id #endif struct Visitor { Visitor(const EdgeGrid::Grid &grid, const size_t idx_contour, const std::vector<ResampledPoint> &resampled_point_parameters, double dist_same_contour_accept, double dist_same_contour_reject) : - grid(grid), idx_contour(idx_contour), contour(*grid.contours()[idx_contour]), resampled_point_parameters(resampled_point_parameters), dist_same_contour_accept(dist_same_contour_accept), dist_same_contour_reject(dist_same_contour_reject) {} + grid(grid), idx_contour(idx_contour), contour(grid.contours()[idx_contour]), resampled_point_parameters(resampled_point_parameters), dist_same_contour_accept(dist_same_contour_accept), dist_same_contour_reject(dist_same_contour_reject) {} void init(const Points &contour, const Point &apoint) { this->idx_point = &apoint - contour.data(); @@ -283,15 +283,15 @@ std::vector<float> contour_distance2(const EdgeGrid::Grid &grid, const size_t id double param_lo = resampled_point_parameters[this->idx_point].curve_parameter; double param_hi; double param_end = resampled_point_parameters.back().curve_parameter; - const Slic3r::Points &ipts = *grid.contours()[it_contour_and_segment->first]; - const size_t ipt = it_contour_and_segment->second; + const EdgeGrid::Contour &contour = grid.contours()[it_contour_and_segment->first]; + const size_t ipt = it_contour_and_segment->second; { ResampledPoint key(ipt, false, 0.); auto lower = [](const ResampledPoint& l, const ResampledPoint r) { return l.idx_src < r.idx_src || (l.idx_src == r.idx_src && int(l.interpolated) > int(r.interpolated)); }; auto it = std::lower_bound(resampled_point_parameters.begin(), resampled_point_parameters.end(), key, lower); assert(it != resampled_point_parameters.end() && it->idx_src == ipt && ! it->interpolated); param_hi = t * sqrt(l2); - if (ipt + 1 < ipts.size()) + if (contour.begin() + ipt + 1 < contour.end()) param_hi += it->curve_parameter; } if (param_lo > param_hi) @@ -307,9 +307,9 @@ std::vector<float> contour_distance2(const EdgeGrid::Grid &grid, const size_t id // Bulge is estimated by 0.6 of the circle circumference drawn around the bisector. // Test whether the contour is convex or concave. bool inside = - (t == 0.) ? this->inside_corner(ipts, ipt, this->point) : - (t == 1.) ? this->inside_corner(ipts, ipt + 1 == ipts.size() ? 0 : ipt + 1, this->point) : - this->left_of_segment(ipts, ipt, this->point); + (t == 0.) ? this->inside_corner(contour, ipt, this->point) : + (t == 1.) ? this->inside_corner(contour, contour.segment_idx_next(ipt), this->point) : + this->left_of_segment(contour, ipt, this->point); accept = inside && dist_along_contour > 0.6 * M_PI * dist; } } @@ -329,7 +329,7 @@ std::vector<float> contour_distance2(const EdgeGrid::Grid &grid, const size_t id const EdgeGrid::Grid &grid; const size_t idx_contour; - const Points &contour; + const EdgeGrid::Contour &contour; const std::vector<ResampledPoint> &resampled_point_parameters; const double dist_same_contour_accept; const double dist_same_contour_reject; @@ -358,24 +358,28 @@ std::vector<float> contour_distance2(const EdgeGrid::Grid &grid, const size_t id return Vec2d(- v.y(), v.x()); } - static bool inside_corner(const Slic3r::Points &contour, size_t i, const Point &pt_oposite) { - const Vec2d pt = pt_oposite.cast<double>(); - size_t iprev = prev_idx_modulo(i, contour); - size_t inext = next_idx_modulo(i, contour); - Vec2d v1 = (contour[i] - contour[iprev]).cast<double>(); - Vec2d v2 = (contour[inext] - contour[i]).cast<double>(); - bool left_of_v1 = cross2(v1, pt - contour[iprev].cast<double>()) > 0.; - bool left_of_v2 = cross2(v2, pt - contour[i ].cast<double>()) > 0.; - return cross2(v1, v2) > 0 ? - left_of_v1 && left_of_v2 : // convex corner - left_of_v1 || left_of_v2; // concave corner - } - static bool left_of_segment(const Slic3r::Points &contour, size_t i, const Point &pt_oposite) { - const Vec2d pt = pt_oposite.cast<double>(); - size_t inext = next_idx_modulo(i, contour); - Vec2d v = (contour[inext] - contour[i]).cast<double>(); - return cross2(v, pt - contour[i].cast<double>()) > 0.; - } + static bool inside_corner(const EdgeGrid::Contour &contour, size_t i, const Point &pt_oposite) + { + const Vec2d pt = pt_oposite.cast<double>(); + const Point &pt_prev = contour.segment_prev(i); + const Point &pt_this = contour.segment_start(i); + const Point &pt_next = contour.segment_end(i); + Vec2d v1 = (pt_this - pt_prev).cast<double>(); + Vec2d v2 = (pt_next - pt_this).cast<double>(); + bool left_of_v1 = cross2(v1, pt - pt_prev.cast<double>()) > 0.; + bool left_of_v2 = cross2(v2, pt - pt_this.cast<double>()) > 0.; + return cross2(v1, v2) > 0 ? left_of_v1 && left_of_v2 : // convex corner + left_of_v1 || left_of_v2; // concave corner + } + + static bool left_of_segment(const EdgeGrid::Contour &contour, size_t i, const Point &pt_oposite) + { + const Vec2d pt = pt_oposite.cast<double>(); + const Point &pt_this = contour.segment_start(i); + const Point &pt_next = contour.segment_end(i); + Vec2d v = (pt_next - pt_this).cast<double>(); + return cross2(v, pt - pt_this.cast<double>()) > 0.; + } } visitor(grid, idx_contour, resampled_point_parameters, 0.5 * compensation * M_PI, search_radius); out.reserve(contour.size()); diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index af53907c7..6ebc6023c 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -1129,7 +1129,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po intersection_points.reserve(infill_ordered.size() * 2); for (const Polyline &pl : infill_ordered) for (const Point *pt : { &pl.points.front(), &pl.points.back() }) { - EdgeGrid::Grid::ClosestPointResult cp = grid.closest_point(*pt, coord_t(SCALED_EPSILON)); + EdgeGrid::Grid::ClosestPointResult cp = grid.closest_point_signed_distance(*pt, coord_t(SCALED_EPSILON)); if (cp.valid()) { // The infill end point shall lie on the contour. assert(cp.distance <= 3.); diff --git a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp index f1d895372..853dc722b 100644 --- a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp +++ b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp @@ -596,7 +596,7 @@ static std::vector<float> contour_distance(const EdgeGrid::Grid &grid, { struct Visitor { Visitor(const EdgeGrid::Grid &grid, const size_t contour_idx, const std::vector<float> &polygon_distances, double dist_same_contour_accept, double dist_same_contour_reject) : - grid(grid), idx_contour(contour_idx), contour(*grid.contours()[contour_idx]), boundary_parameters(polygon_distances), dist_same_contour_accept(dist_same_contour_accept), dist_same_contour_reject(dist_same_contour_reject) {} + grid(grid), idx_contour(contour_idx), contour(grid.contours()[contour_idx]), boundary_parameters(polygon_distances), dist_same_contour_accept(dist_same_contour_accept), dist_same_contour_reject(dist_same_contour_reject) {} void init(const Points &contour, const Point &apoint) { @@ -630,12 +630,12 @@ static std::vector<float> contour_distance(const EdgeGrid::Grid &grid, // Complex case: The closest segment originates from the same contour as the starting point. // Reject the closest point if its distance along the contour is reasonable compared to the current contour bisector // (this->pt, foot). - const Slic3r::Points &ipts = *grid.contours()[it_contour_and_segment->first]; - double param_lo = boundary_parameters[this->idx_point]; - double param_hi = t * sqrt(l2); - double param_end = boundary_parameters.back(); + const EdgeGrid::Contour &contour = grid.contours()[it_contour_and_segment->first]; + double param_lo = boundary_parameters[this->idx_point]; + double param_hi = t * sqrt(l2); + double param_end = boundary_parameters.back(); const size_t ipt = it_contour_and_segment->second; - if (ipt + 1 < ipts.size()) + if (contour.begin() + ipt + 1 < contour.end()) param_hi += boundary_parameters[ipt > 0 ? ipt - 1 : 0]; if (param_lo > param_hi) std::swap(param_lo, param_hi); @@ -649,9 +649,9 @@ static std::vector<float> contour_distance(const EdgeGrid::Grid &grid, // longer than the bisector. That is, the path shall not bulge away from the bisector too much. // Bulge is estimated by 0.6 of the circle circumference drawn around the bisector. // Test whether the contour is convex or concave. - bool inside = (t == 0.) ? this->inside_corner(ipts, ipt, this->point) : - (t == 1.) ? this->inside_corner(ipts, ipt + 1 == ipts.size() ? 0 : ipt + 1, this->point) : - this->left_of_segment(ipts, ipt, this->point); + bool inside = (t == 0.) ? this->inside_corner(contour, ipt, this->point) : + (t == 1.) ? this->inside_corner(contour, contour.segment_idx_next(ipt), this->point) : + this->left_of_segment(contour, ipt, this->point); accept = inside && dist_along_contour > 0.6 * M_PI * dist; } } @@ -668,7 +668,7 @@ static std::vector<float> contour_distance(const EdgeGrid::Grid &grid, const EdgeGrid::Grid &grid; const size_t idx_contour; - const Points &contour; + const EdgeGrid::Contour &contour; const std::vector<float> &boundary_parameters; const double dist_same_contour_accept; @@ -691,25 +691,27 @@ static std::vector<float> contour_distance(const EdgeGrid::Grid &grid, return Vec2d(-v1.y() - v2.y(), v1.x() + v2.x()); } - static bool inside_corner(const Slic3r::Points &contour, size_t i, const Point &pt_oposite) + static bool inside_corner(const EdgeGrid::Contour &contour, size_t i, const Point &pt_oposite) { const Vec2d pt = pt_oposite.cast<double>(); - size_t iprev = prev_idx_modulo(i, contour); - size_t inext = next_idx_modulo(i, contour); - Vec2d v1 = (contour[i] - contour[iprev]).cast<double>(); - Vec2d v2 = (contour[inext] - contour[i]).cast<double>(); - bool left_of_v1 = cross2(v1, pt - contour[iprev].cast<double>()) > 0.; - bool left_of_v2 = cross2(v2, pt - contour[i].cast<double>()) > 0.; + const Point &pt_prev = contour.segment_prev(i); + const Point &pt_this = contour.segment_start(i); + const Point &pt_next = contour.segment_end(i); + Vec2d v1 = (pt_this - pt_prev).cast<double>(); + Vec2d v2 = (pt_next - pt_this).cast<double>(); + bool left_of_v1 = cross2(v1, pt - pt_prev.cast<double>()) > 0.; + bool left_of_v2 = cross2(v2, pt - pt_this.cast<double>()) > 0.; return cross2(v1, v2) > 0 ? left_of_v1 && left_of_v2 : // convex corner left_of_v1 || left_of_v2; // concave corner } - static bool left_of_segment(const Slic3r::Points &contour, size_t i, const Point &pt_oposite) + static bool left_of_segment(const EdgeGrid::Contour &contour, size_t i, const Point &pt_oposite) { - const Vec2d pt = pt_oposite.cast<double>(); - size_t inext = next_idx_modulo(i, contour); - Vec2d v = (contour[inext] - contour[i]).cast<double>(); - return cross2(v, pt - contour[i].cast<double>()) > 0.; + const Vec2d pt = pt_oposite.cast<double>(); + const Point &pt_this = contour.segment_start(i); + const Point &pt_next = contour.segment_end(i); + Vec2d v = (pt_next - pt_this).cast<double>(); + return cross2(v, pt - pt_this.cast<double>()) > 0.; } } visitor(grid, contour_idx, poly_distances, 0.5 * compensation * M_PI, search_radius);