diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 1a9a153b94..85e11eded3 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -127,6 +127,8 @@ add_library(libslic3r STATIC Point.hpp Polygon.cpp Polygon.hpp + PolygonTrimmer.cpp + PolygonTrimmer.hpp Polyline.cpp Polyline.hpp PolylineCollection.cpp diff --git a/src/libslic3r/EdgeGrid.cpp b/src/libslic3r/EdgeGrid.cpp index 9d02ef09b7..f40d499de1 100644 --- a/src/libslic3r/EdgeGrid.cpp +++ b/src/libslic3r/EdgeGrid.cpp @@ -11,6 +11,7 @@ #include "libslic3r.h" #include "ClipperUtils.hpp" #include "EdgeGrid.hpp" +#include "Geometry.hpp" #include "SVG.hpp" #if 0 @@ -275,134 +276,24 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution) // 6) Finally fill in m_cell_data by rasterizing the lines once again. for (size_t i = 0; i < m_cells.size(); ++i) m_cells[i].end = m_cells[i].begin; - 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) { - // End points of the line segment. - Slic3r::Point p1(pts[j]); - Slic3r::Point p2 = pts[(j + 1 == pts.size()) ? 0 : j + 1]; - p1(0) -= m_bbox.min(0); - p1(1) -= m_bbox.min(1); - p2(0) -= m_bbox.min(0); - p2(1) -= m_bbox.min(1); - // Get the cells of the end points. - coord_t ix = p1(0) / m_resolution; - coord_t iy = p1(1) / m_resolution; - coord_t ixb = p2(0) / m_resolution; - coord_t iyb = p2(1) / m_resolution; - assert(ix >= 0 && size_t(ix) < m_cols); - assert(iy >= 0 && size_t(iy) < m_rows); - assert(ixb >= 0 && size_t(ixb) < m_cols); - assert(iyb >= 0 && size_t(iyb) < m_rows); - // Account for the end points. - m_cell_data[m_cells[iy*m_cols + ix].end++] = std::pair(i, j); - if (ix == ixb && iy == iyb) - // Both ends fall into the same cell. - continue; - // Raster the centeral part of the line. - coord_t dx = std::abs(p2(0) - p1(0)); - coord_t dy = std::abs(p2(1) - p1(1)); - if (p1(0) < p2(0)) { - int64_t ex = int64_t((ix + 1)*m_resolution - p1(0)) * int64_t(dy); - if (p1(1) < p2(1)) { - // x positive, y positive - int64_t ey = int64_t((iy + 1)*m_resolution - p1(1)) * int64_t(dx); - do { - assert(ix <= ixb && iy <= iyb); - if (ex < ey) { - ey -= ex; - ex = int64_t(dy) * m_resolution; - ix += 1; - } - else if (ex == ey) { - ex = int64_t(dy) * m_resolution; - ey = int64_t(dx) * m_resolution; - ix += 1; - iy += 1; - } - else { - assert(ex > ey); - ex -= ey; - ey = int64_t(dx) * m_resolution; - iy += 1; - } - m_cell_data[m_cells[iy*m_cols + ix].end++] = std::pair(i, j); - } while (ix != ixb || iy != iyb); - } - else { - // x positive, y non positive - int64_t ey = int64_t(p1(1) - iy*m_resolution) * int64_t(dx); - do { - assert(ix <= ixb && iy >= iyb); - if (ex <= ey) { - ey -= ex; - ex = int64_t(dy) * m_resolution; - ix += 1; - } - else { - ex -= ey; - ey = int64_t(dx) * m_resolution; - iy -= 1; - } - m_cell_data[m_cells[iy*m_cols + ix].end++] = std::pair(i, j); - } while (ix != ixb || iy != iyb); - } - } - else { - int64_t ex = int64_t(p1(0) - ix*m_resolution) * int64_t(dy); - if (p1(1) < p2(1)) { - // x non positive, y positive - int64_t ey = int64_t((iy + 1)*m_resolution - p1(1)) * int64_t(dx); - do { - assert(ix >= ixb && iy <= iyb); - if (ex < ey) { - ey -= ex; - ex = int64_t(dy) * m_resolution; - ix -= 1; - } - else { - assert(ex >= ey); - ex -= ey; - ey = int64_t(dx) * m_resolution; - iy += 1; - } - m_cell_data[m_cells[iy*m_cols + ix].end++] = std::pair(i, j); - } while (ix != ixb || iy != iyb); - } - else { - // x non positive, y non positive - int64_t ey = int64_t(p1(1) - iy*m_resolution) * int64_t(dx); - do { - assert(ix >= ixb && iy >= iyb); - if (ex < ey) { - ey -= ex; - ex = int64_t(dy) * m_resolution; - ix -= 1; - } - else if (ex == ey) { - // The lower edge of a grid cell belongs to the cell. - // Handle the case where the ray may cross the lower left corner of a cell in a general case, - // or a left or lower edge in a degenerate case (horizontal or vertical line). - if (dx > 0) { - ex = int64_t(dy) * m_resolution; - ix -= 1; - } - if (dy > 0) { - ey = int64_t(dx) * m_resolution; - iy -= 1; - } - } - else { - assert(ex > ey); - ex -= ey; - ey = int64_t(dx) * m_resolution; - iy -= 1; - } - m_cell_data[m_cells[iy*m_cols + ix].end++] = std::pair(i, j); - } while (ix != ixb || iy != iyb); - } - } - } + + struct Visitor { + Visitor(std::vector> &cell_data, std::vector &cells, size_t cols) : + cell_data(cell_data), cells(cells), cols(cols), i(0), j(0) {} + + void operator()(coord_t iy, coord_t ix) { cell_data[cells[iy*cols + ix].end++] = std::pair(i, j); } + + std::vector> &cell_data; + std::vector &cells; + size_t cols; + size_t i; + size_t j; + } visitor(m_cell_data, m_cells, m_cols); + + for (; visitor.i < m_contours.size(); ++ visitor.i) { + const Slic3r::Points &pts = *m_contours[visitor.i]; + for (; 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); } } @@ -1360,28 +1251,6 @@ Polygons EdgeGrid::Grid::contours_simplified(coord_t offset, bool fill_holes) co return out; } -inline int segments_could_intersect( - const Slic3r::Point &ip1, const Slic3r::Point &ip2, - const Slic3r::Point &jp1, const Slic3r::Point &jp2) -{ - Vec2i64 iv = (ip2 - ip1).cast(); - Vec2i64 vij1 = (jp1 - ip1).cast(); - Vec2i64 vij2 = (jp2 - ip1).cast(); - int64_t tij1 = cross2(iv, vij1); - int64_t tij2 = cross2(iv, vij2); - int sij1 = (tij1 > 0) ? 1 : ((tij1 < 0) ? -1 : 0); // signum - int sij2 = (tij2 > 0) ? 1 : ((tij2 < 0) ? -1 : 0); - return sij1 * sij2; -} - -inline bool segments_intersect( - const Slic3r::Point &ip1, const Slic3r::Point &ip2, - const Slic3r::Point &jp1, const Slic3r::Point &jp2) -{ - return segments_could_intersect(ip1, ip2, jp1, jp2) <= 0 && - segments_could_intersect(jp1, jp2, ip1, ip2) <= 0; -} - std::vector> EdgeGrid::Grid::intersecting_edges() const { std::vector> out; @@ -1405,7 +1274,7 @@ std::vector> if (&ipts == &jpts && (&ip1 == &jp2 || &jp1 == &ip2)) // Segments of the same contour share a common vertex. continue; - if (segments_intersect(ip1, ip2, jp1, jp2)) { + 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); out.emplace_back(jfirst ? @@ -1440,7 +1309,7 @@ bool EdgeGrid::Grid::has_intersecting_edges() const 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)) && - segments_intersect(ip1, ip2, jp1, jp2)) + Geometry::segments_intersect(ip1, ip2, jp1, jp2)) return true; } } diff --git a/src/libslic3r/EdgeGrid.hpp b/src/libslic3r/EdgeGrid.hpp index 7faafdb3e1..cad20e07bb 100644 --- a/src/libslic3r/EdgeGrid.hpp +++ b/src/libslic3r/EdgeGrid.hpp @@ -65,6 +65,145 @@ public: std::vector> intersecting_edges() const; bool has_intersecting_edges() const; + template void visit_cells_intersecting_line(Slic3r::Point p1, Slic3r::Point p2, FUNCTION func) const + { + // End points of the line segment. + p1(0) -= m_bbox.min(0); + p1(1) -= m_bbox.min(1); + p2(0) -= m_bbox.min(0); + p2(1) -= m_bbox.min(1); + // Get the cells of the end points. + coord_t ix = p1(0) / m_resolution; + coord_t iy = p1(1) / m_resolution; + coord_t ixb = p2(0) / m_resolution; + coord_t iyb = p2(1) / m_resolution; + assert(ix >= 0 && size_t(ix) < m_cols); + assert(iy >= 0 && size_t(iy) < m_rows); + assert(ixb >= 0 && size_t(ixb) < m_cols); + assert(iyb >= 0 && size_t(iyb) < m_rows); + // Account for the end points. + func(iy, ix); + if (ix == ixb && iy == iyb) + // Both ends fall into the same cell. + return; + // Raster the centeral part of the line. + coord_t dx = std::abs(p2(0) - p1(0)); + coord_t dy = std::abs(p2(1) - p1(1)); + if (p1(0) < p2(0)) { + int64_t ex = int64_t((ix + 1)*m_resolution - p1(0)) * int64_t(dy); + if (p1(1) < p2(1)) { + // x positive, y positive + int64_t ey = int64_t((iy + 1)*m_resolution - p1(1)) * int64_t(dx); + do { + assert(ix <= ixb && iy <= iyb); + if (ex < ey) { + ey -= ex; + ex = int64_t(dy) * m_resolution; + ix += 1; + } + else if (ex == ey) { + ex = int64_t(dy) * m_resolution; + ey = int64_t(dx) * m_resolution; + ix += 1; + iy += 1; + } + else { + assert(ex > ey); + ex -= ey; + ey = int64_t(dx) * m_resolution; + iy += 1; + } + func(iy, ix); + } while (ix != ixb || iy != iyb); + } + else { + // x positive, y non positive + int64_t ey = int64_t(p1(1) - iy*m_resolution) * int64_t(dx); + do { + assert(ix <= ixb && iy >= iyb); + if (ex <= ey) { + ey -= ex; + ex = int64_t(dy) * m_resolution; + ix += 1; + } + else { + ex -= ey; + ey = int64_t(dx) * m_resolution; + iy -= 1; + } + func(iy, ix); + } while (ix != ixb || iy != iyb); + } + } + else { + int64_t ex = int64_t(p1(0) - ix*m_resolution) * int64_t(dy); + if (p1(1) < p2(1)) { + // x non positive, y positive + int64_t ey = int64_t((iy + 1)*m_resolution - p1(1)) * int64_t(dx); + do { + assert(ix >= ixb && iy <= iyb); + if (ex < ey) { + ey -= ex; + ex = int64_t(dy) * m_resolution; + ix -= 1; + } + else { + assert(ex >= ey); + ex -= ey; + ey = int64_t(dx) * m_resolution; + iy += 1; + } + func(iy, ix); + } while (ix != ixb || iy != iyb); + } + else { + // x non positive, y non positive + int64_t ey = int64_t(p1(1) - iy*m_resolution) * int64_t(dx); + do { + assert(ix >= ixb && iy >= iyb); + if (ex < ey) { + ey -= ex; + ex = int64_t(dy) * m_resolution; + ix -= 1; + } + else if (ex == ey) { + // The lower edge of a grid cell belongs to the cell. + // Handle the case where the ray may cross the lower left corner of a cell in a general case, + // or a left or lower edge in a degenerate case (horizontal or vertical line). + if (dx > 0) { + ex = int64_t(dy) * m_resolution; + ix -= 1; + } + if (dy > 0) { + ey = int64_t(dx) * m_resolution; + iy -= 1; + } + } + else { + assert(ex > ey); + ex -= ey; + ey = int64_t(dx) * m_resolution; + iy -= 1; + } + func(iy, ix); + } while (ix != ixb || iy != iyb); + } + } + } + + std::pair>::const_iterator, std::vector>::const_iterator> cell_data_range(coord_t row, coord_t col) const + { + const EdgeGrid::Grid::Cell &cell = m_cells[row * m_cols + col]; + return std::make_pair(m_cell_data.begin() + cell.begin, m_cell_data.begin() + cell.end); + } + + std::pair segment(const std::pair &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(ipts[ipt], ipts[(ipt + 1 == ipts.size()) ? 0 : ipt + 1]); + } + protected: struct Cell { Cell() : begin(0), end(0) {} diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index dca1872d82..eec2673227 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -111,6 +111,29 @@ inline bool segment_segment_intersection(const Vec2d &p1, const Vec2d &v1, const return true; } + +inline int segments_could_intersect( + const Slic3r::Point &ip1, const Slic3r::Point &ip2, + const Slic3r::Point &jp1, const Slic3r::Point &jp2) +{ + Vec2i64 iv = (ip2 - ip1).cast(); + Vec2i64 vij1 = (jp1 - ip1).cast(); + Vec2i64 vij2 = (jp2 - ip1).cast(); + int64_t tij1 = cross2(iv, vij1); + int64_t tij2 = cross2(iv, vij2); + int sij1 = (tij1 > 0) ? 1 : ((tij1 < 0) ? -1 : 0); // signum + int sij2 = (tij2 > 0) ? 1 : ((tij2 < 0) ? -1 : 0); + return sij1 * sij2; +} + +inline bool segments_intersect( + const Slic3r::Point &ip1, const Slic3r::Point &ip2, + const Slic3r::Point &jp1, const Slic3r::Point &jp2) +{ + return segments_could_intersect(ip1, ip2, jp1, jp2) <= 0 && + segments_could_intersect(jp1, jp2, ip1, ip2) <= 0; +} + Pointf3s convex_hull(Pointf3s points); Polygon convex_hull(Points points); Polygon convex_hull(const Polygons &polygons); diff --git a/src/libslic3r/PolygonTrimmer.cpp b/src/libslic3r/PolygonTrimmer.cpp new file mode 100644 index 0000000000..3e3c9b4982 --- /dev/null +++ b/src/libslic3r/PolygonTrimmer.cpp @@ -0,0 +1,56 @@ +#include "PolygonTrimmer.hpp" +#include "EdgeGrid.hpp" +#include "Geometry.hpp" + +namespace Slic3r { + +TrimmedLoop trim_loop(const Polygon &loop, const EdgeGrid::Grid &grid) +{ + assert(! loop.empty()); + assert(loop.size() >= 2); + + TrimmedLoop out; + + if (loop.size() >= 2) { + size_t cnt = loop.points.size(); + + struct Visitor { + Visitor(const EdgeGrid::Grid &grid, const Slic3r::Point *pt_prev, const Slic3r::Point *pt_this) : grid(grid), pt_prev(pt_prev), pt_this(pt_this) {} + + void operator()(coord_t iy, coord_t ix) { + // Called with a row and colum of the grid cell, which is intersected by a line. + auto cell_data_range = grid.cell_data_range(iy, ix); + for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second; ++ it_contour_and_segment) { + // End points of the line segment and their vector. + auto segment = grid.segment(*it_contour_and_segment); + if (Geometry::segments_intersect(segment.first, segment.second, *pt_prev, *pt_this)) { + // The two segments intersect. Add them to the output. + } + } + } + + const EdgeGrid::Grid &grid; + const Slic3r::Point *pt_this; + const Slic3r::Point *pt_prev; + } visitor(grid, &loop.points.back(), nullptr); + + for (const Point &pt_this : loop.points) { + visitor.pt_this = &pt_this; + grid.visit_cells_intersecting_line(*visitor.pt_prev, pt_this, visitor); + visitor.pt_prev = &pt_this; + } + } + + return out; +} + +std::vector trim_loops(const Polygons &loops, const EdgeGrid::Grid &grid) +{ + std::vector out; + out.reserve(loops.size()); + for (const Polygon &loop : loops) + out.emplace_back(trim_loop(loop, grid)); + return out; +} + +} diff --git a/src/libslic3r/PolygonTrimmer.hpp b/src/libslic3r/PolygonTrimmer.hpp new file mode 100644 index 0000000000..b2a780be91 --- /dev/null +++ b/src/libslic3r/PolygonTrimmer.hpp @@ -0,0 +1,31 @@ +#ifndef slic3r_PolygonTrimmer_hpp_ +#define slic3r_PolygonTrimmer_hpp_ + +#include "libslic3r.h" +#include +#include +#include "Line.hpp" +#include "MultiPoint.hpp" +#include "Polyline.hpp" + +namespace Slic3r { + +namespace EdgeGrid { + class Grid; +} + +struct TrimmedLoop +{ + std::vector points; + // Number of points per segment. Empty if the loop is + std::vector segments; + + bool is_trimmed() const { return ! segments.empty(); } +}; + +TrimmedLoop trim_loop(const Polygon &loop, const EdgeGrid::Grid &grid); +std::vector trim_loops(const Polygons &loops, const EdgeGrid::Grid &grid); + +} // namespace Slic3r + +#endif /* slic3r_PolygonTrimmer_hpp_ */