From 86fbb9a095e30ce0f6fc998cf1b812e716ac7871 Mon Sep 17 00:00:00 2001 From: supermerill Date: Wed, 23 Jan 2019 10:08:42 +0100 Subject: [PATCH 01/12] gyroid & 3Dhoneycomb: now 'connected lines' follow the perimeters --- src/libslic3r/Fill/Fill3DHoneycomb.cpp | 59 +++-- src/libslic3r/Fill/FillBase.cpp | 287 +++++++++++++++++++++++++ src/libslic3r/Fill/FillBase.hpp | 2 + src/libslic3r/Fill/FillGyroid.cpp | 99 +++++---- src/libslic3r/Point.hpp | 7 + 5 files changed, 377 insertions(+), 77 deletions(-) diff --git a/src/libslic3r/Fill/Fill3DHoneycomb.cpp b/src/libslic3r/Fill/Fill3DHoneycomb.cpp index 6a37e4369f..7da18d9e31 100644 --- a/src/libslic3r/Fill/Fill3DHoneycomb.cpp +++ b/src/libslic3r/Fill/Fill3DHoneycomb.cpp @@ -161,43 +161,38 @@ void Fill3DHoneycomb::_fill_surface_single( for (Polylines::iterator it = polylines.begin(); it != polylines.end(); ++ it) it->translate(bb.min(0), bb.min(1)); - // clip pattern to boundaries - polylines = intersection_pl(polylines, (Polygons)expolygon); - - // connect lines - if (! params.dont_connect && ! polylines.empty()) { // prevent calling leftmost_point() on empty collections - ExPolygon expolygon_off; - { - ExPolygons expolygons_off = offset_ex(expolygon, SCALED_EPSILON); - if (! expolygons_off.empty()) { - // When expanding a polygon, the number of islands could only shrink. Therefore the offset_ex shall generate exactly one expanded island for one input island. - assert(expolygons_off.size() == 1); - std::swap(expolygon_off, expolygons_off.front()); + // clip pattern to boundaries, keeping the polyline order & ordering the fragment to be able to join them easily + Polylines polylines_chained; + for (size_t idx_polyline = 0; idx_polyline < polylines.size(); ++idx_polyline) { + Polyline &poly_to_cut = polylines[idx_polyline]; + Polylines polylines_to_sort = intersection_pl(Polylines() = { poly_to_cut }, (Polygons)expolygon); + for (Polyline &polyline : polylines_to_sort) { + //TODO: replace by closest_index_point() + if (poly_to_cut.points.front().distance_to_square(polyline.points.front()) > poly_to_cut.points.front().distance_to_square(polyline.points.back())) { + polyline.reverse(); } } - Polylines chained = PolylineCollection::chained_path_from( - std::move(polylines), - PolylineCollection::leftmost_point(polylines), false); // reverse allowed - bool first = true; - for (Polylines::iterator it_polyline = chained.begin(); it_polyline != chained.end(); ++ it_polyline) { - if (! first) { - // Try to connect the lines. - Points &pts_end = polylines_out.back().points; - const Point &first_point = it_polyline->points.front(); - const Point &last_point = pts_end.back(); - // TODO: we should also check that both points are on a fill_boundary to avoid - // connecting paths on the boundaries of internal regions - if ((last_point - first_point).cast().norm() <= 1.5 * distance && - expolygon_off.contains(Line(last_point, first_point))) { - // Append the polyline. - pts_end.insert(pts_end.end(), it_polyline->points.begin(), it_polyline->points.end()); - continue; + if (polylines_to_sort.size() > 1) { + Point nearest = poly_to_cut.points.front(); + //Bubble sort + for (size_t idx_sort = polylines_to_sort.size() - 1; idx_sort > 0; idx_sort--) { + for (size_t idx_bubble = 0; idx_bubble < idx_sort; idx_bubble++) { + if (polylines_to_sort[idx_bubble + 1].points.front().distance_to_square(nearest) < polylines_to_sort[idx_bubble].points.front().distance_to_square(nearest)) { + iter_swap(polylines_to_sort.begin() + idx_bubble, polylines_to_sort.begin() + idx_bubble + 1); + } } } - // The lines cannot be connected. - polylines_out.emplace_back(std::move(*it_polyline)); - first = false; } + polylines_chained.insert(polylines_chained.end(), polylines_to_sort.begin(), polylines_to_sort.end()); + } + // connect lines if needed + if (!polylines_chained.empty()) { + if (params.dont_connect) { + polylines_out.insert(polylines_out.end(), polylines_chained.begin(), polylines_chained.end()); + } else { + this->connect_infill(polylines_chained, expolygon, polylines_out); + } + } } diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index 7a99e84f71..df32ff1790 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -130,4 +130,291 @@ std::pair Fill::_infill_direction(const Surface *surface) const return std::pair(out_angle, out_shift); } + + + +/// cut poly between poly.point[idx_1] & poly.point[idx_1+1] +/// add p1+-width to one part and p2+-width to the other one. +/// add the "new" polyline to polylines (to part cut from poly) +/// p1 & p2 have to be between poly.point[idx_1] & poly.point[idx_1+1] +/// if idx_1 is ==0 or == size-1, then we don't need to create a new polyline. +void cut_polyline(Polyline &poly, Polylines &polylines, size_t idx_1, Point p1, Point p2) { + //reorder points + if (p1.distance_to_square(poly.points[idx_1]) > p2.distance_to_square(poly.points[idx_1])) { + Point temp = p2; + p2 = p1; + p1 = temp; + } + if (idx_1 == 0) { + poly.points.insert(poly.points.begin(), p2); + } else if (idx_1 == poly.points.size() - 1) { + poly.points.push_back(p1); + } else { + // create new polyline + Polyline new_poly; + //put points in new_poly + new_poly.points.push_back(p2); + new_poly.points.insert(new_poly.points.end(), poly.points.begin() + idx_1 + 1, poly.points.end()); + //erase&put points in poly + poly.points.erase(poly.points.begin() + idx_1 + 1, poly.points.end()); + poly.points.push_back(p1); + polylines.emplace_back(new_poly); + } +} + +/// the poly is like a polygon but with first_point != last_point (already removed) +void cut_polygon(Polyline &poly, size_t idx_1, Point p1, Point p2) { + //reorder points + if (p1.distance_to_square(poly.points[idx_1]) > p2.distance_to_square(poly.points[idx_1])) { + Point temp = p2; + p2 = p1; + p1 = temp; + } + //check if we need to rotate before cutting + if (idx_1 != poly.size() - 1) { + //put points in new_poly + poly.points.insert(poly.points.end(), poly.points.begin(), poly.points.begin() + idx_1 + 1); + poly.points.erase(poly.points.begin(), poly.points.begin() + idx_1 + 1); + } + //put points in poly + poly.points.push_back(p1); + poly.points.insert(poly.points.begin(), p2); +} + +/// check if the polyline from pts_to_check may be at 'width' distance of a point in polylines_blocker +/// it use equally_spaced_points with width/2 precision, so don't worry with pts_to_check number of points. +/// it use the given polylines_blocker points, be sure to put enough of them to be reliable. +/// complexity : N(pts_to_check.equally_spaced_points(width / 2)) x N(polylines_blocker.points) +bool collision(const Points &pts_to_check, const Polylines &polylines_blocker, const coordf_t width) { + //check if it's not too close to a polyline + coordf_t min_dist = width * width * 0.9 - SCALED_EPSILON; + Polyline better_polylines(pts_to_check); + Points better_pts = better_polylines.equally_spaced_points(width / 2); + for (const Point &p : better_pts) { + for (const Polyline &poly2 : polylines_blocker) { + for (const Point &p2 : poly2.points) { + if (p.distance_to_square(p2) < min_dist) { + return true; + } + } + } + } + return false; +} + +/// Try to find a path inside polylines that allow to go from p1 to p2. +/// width if the width of the extrusion +/// polylines_blockers are the array of polylines to check if the path isn't blocked by something. +/// complexity: N(polylines.points) + a collision check after that if we finded a path: N(2(p2-p1)/width) x N(polylines_blocker.points) +Points getFrontier(Polylines &polylines, const Point& p1, const Point& p2, const coord_t width, const Polylines &polylines_blockers) { + for (size_t idx_poly = 0; idx_poly < polylines.size(); ++idx_poly) { + Polyline &poly = polylines[idx_poly]; + if (poly.size() <= 1) continue; + + //loop? + if (poly.first_point() == poly.last_point()) { + //polygon : try to find a line for p1 & p2. + size_t idx_11, idx_12, idx_21, idx_22; + idx_11 = poly.closest_point_index(p1); + idx_12 = idx_11; + if (Line(poly.points[idx_11], poly.points[(idx_11 + 1) % (poly.points.size() - 1)]).distance_to(p1) < SCALED_EPSILON) { + idx_12 = (idx_11 + 1) % (poly.points.size() - 1); + } else if (Line(poly.points[(idx_11 > 0) ? (idx_11 - 1) : (poly.points.size() - 2)], poly.points[idx_11]).distance_to(p1) < SCALED_EPSILON) { + idx_11 = (idx_11 > 0) ? (idx_11 - 1) : (poly.points.size() - 2); + } else { + continue; + } + idx_21 = poly.closest_point_index(p2); + idx_22 = idx_21; + if (Line(poly.points[idx_21], poly.points[(idx_21 + 1) % (poly.points.size() - 1)]).distance_to(p2) < SCALED_EPSILON) { + idx_22 = (idx_21 + 1) % (poly.points.size() - 1); + } else if (Line(poly.points[(idx_21 > 0) ? (idx_21 - 1) : (poly.points.size() - 2)], poly.points[idx_21]).distance_to(p2) < SCALED_EPSILON) { + idx_21 = (idx_21 > 0) ? (idx_21 - 1) : (poly.points.size() - 2); + } else { + continue; + } + + + //edge case: on the same line + if (idx_11 == idx_21 && idx_12 == idx_22) { + if (collision(Points() = { p1, p2 }, polylines_blockers, width)) return Points(); + //break loop + poly.points.erase(poly.points.end() - 1); + cut_polygon(poly, idx_11, p1, p2); + return Points() = { Line(p1, p2).midpoint() }; + } + + //compute distance & array for the ++ path + Points ret_1_to_2; + double dist_1_to_2 = p1.distance_to(poly.points[idx_12]); + ret_1_to_2.push_back(poly.points[idx_12]); + size_t max = idx_12 <= idx_21 ? idx_21 : poly.points.size() - 2; + for (size_t i = idx_12 + 1; i < max; i++) { + dist_1_to_2 += poly.points[i - 1].distance_to(poly.points[i]); + ret_1_to_2.push_back(poly.points[i]); + } + if (idx_12 > idx_21) { + dist_1_to_2 += poly.points.back().distance_to(poly.points.front()); + ret_1_to_2.push_back(poly.points[0]); + for (size_t i = 1; i <= idx_21; i++) { + dist_1_to_2 += poly.points[i - 1].distance_to(poly.points[i]); + ret_1_to_2.push_back(poly.points[i]); + } + } + dist_1_to_2 += p2.distance_to(poly.points[idx_21]); + + //compute distance & array for the -- path + Points ret_2_to_1; + double dist_2_to_1 = p1.distance_to(poly.points[idx_11]); + ret_2_to_1.push_back(poly.points[idx_11]); + size_t min = idx_22 <= idx_11 ? idx_22 : 0; + for (size_t i = idx_11; i > min; i--) { + dist_2_to_1 += poly.points[i - 1].distance_to(poly.points[i]); + ret_2_to_1.push_back(poly.points[i - 1]); + } + if (idx_22 > idx_11) { + dist_2_to_1 += poly.points.back().distance_to(poly.points.front()); + ret_2_to_1.push_back(poly.points[poly.points.size() - 1]); + for (size_t i = poly.points.size() - 2; i > idx_22; i--) { + dist_2_to_1 += poly.points[i - 1].distance_to(poly.points[i]); + ret_2_to_1.push_back(poly.points[i - 1]); + } + } + dist_2_to_1 += p2.distance_to(poly.points[idx_22]); + + //choose between the two direction (keep the short one) + if (dist_1_to_2 < dist_2_to_1) { + if (collision(ret_1_to_2, polylines_blockers, width)) return Points(); + //break loop + poly.points.erase(poly.points.end() - 1); + //remove points + if (idx_12 <= idx_21) { + poly.points.erase(poly.points.begin() + idx_12, poly.points.begin() + idx_21 + 1); + cut_polygon(poly, idx_11, p1, p2); + } else { + poly.points.erase(poly.points.begin() + idx_12, poly.points.end()); + poly.points.erase(poly.points.begin(), poly.points.begin() + idx_21); + cut_polygon(poly, poly.points.size() - 1, p1, p2); + } + return ret_1_to_2; + } else { + if (collision(ret_2_to_1, polylines_blockers, width)) return Points(); + //break loop + poly.points.erase(poly.points.end() - 1); + //remove points + if (idx_22 <= idx_11) { + poly.points.erase(poly.points.begin() + idx_22, poly.points.begin() + idx_11 + 1); + cut_polygon(poly, idx_21, p1, p2); + } else { + poly.points.erase(poly.points.begin() + idx_22, poly.points.end()); + poly.points.erase(poly.points.begin(), poly.points.begin() + idx_11); + cut_polygon(poly, poly.points.size() - 1, p1, p2); + } + return ret_2_to_1; + } + } else { + //polyline : try to find a line for p1 & p2. + size_t idx_1, idx_2; + idx_1 = poly.closest_point_index(p1); + if (idx_1 < poly.points.size() - 1 && Line(poly.points[idx_1], poly.points[idx_1 + 1]).distance_to(p1) < SCALED_EPSILON) { + } else if (idx_1 > 0 && Line(poly.points[idx_1 - 1], poly.points[idx_1]).distance_to(p1) < SCALED_EPSILON) { + idx_1 = idx_1 - 1; + } else { + continue; + } + idx_2 = poly.closest_point_index(p2); + if (idx_2 < poly.points.size() - 1 && Line(poly.points[idx_2], poly.points[idx_2 + 1]).distance_to(p2) < SCALED_EPSILON) { + } else if (idx_2 > 0 && Line(poly.points[idx_2 - 1], poly.points[idx_2]).distance_to(p2) < SCALED_EPSILON) { + idx_2 = idx_2 - 1; + } else { + continue; + } + + //edge case: on the same line + if (idx_1 == idx_2) { + if (collision(Points() = { p1, p2 }, polylines_blockers, width)) return Points(); + cut_polyline(poly, polylines, idx_1, p1, p2); + return Points() = { Line(p1, p2).midpoint() }; + } + + //create ret array + size_t first_idx = idx_1; + size_t last_idx = idx_2 + 1; + if (idx_1 > idx_2) { + first_idx = idx_2; + last_idx = idx_1 + 1; + } + Points p_ret; + p_ret.insert(p_ret.end(), poly.points.begin() + first_idx + 1, poly.points.begin() + last_idx); + if (collision(p_ret, polylines_blockers, width)) return Points(); + //cut polyline + poly.points.erase(poly.points.begin() + first_idx + 1, poly.points.begin() + last_idx); + cut_polyline(poly, polylines, first_idx, p1, p2); + //order the returned array to be p1->p2 + if (idx_1 > idx_2) { + std::reverse(p_ret.begin(), p_ret.end()); + } + return p_ret; + } + + } + + return Points(); +} + +/// Connect the infill_ordered polylines, in this order, from the back point to the next front point. +/// It uses only the boundary polygons to do so, and can't pass two times at the same place. +/// It avoid passing over the infill_ordered's polylines (preventing local over-extrusion). +/// return the connected polylines in polylines_out. Can output polygons (stored as polylines with first_point = last_point). +/// complexity: worst: N(infill_ordered.points) x N(boundary.points) +/// typical: N(infill_ordered) x ( N(boundary.points) + N(infill_ordered.points) ) +void Fill::connect_infill(const Polylines &infill_ordered, const ExPolygon &boundary, Polylines &polylines_out) { + + //TODO: fallback to the quick & dirty old algorithm when n(points) is too high. + Polylines polylines_frontier = to_polylines(((Polygons)boundary)); + + Polylines polylines_blocker; + coord_t clip_size = scale_(this->spacing) * 2; + for (const Polyline &polyline : infill_ordered) { + if (polyline.length() > 1.8 * clip_size) { + polylines_blocker.push_back(polyline); + polylines_blocker.back().clip_end(clip_size); + polylines_blocker.back().clip_start(clip_size); + } + } + + + Polylines polylines_connected; + bool first = true; + for (const Polyline &polyline : infill_ordered) { + if (!first) { + // Try to connect the lines. + Points &pts_end = polylines_connected.back().points; + const Point &first_point = polyline.points.front(); + const Point &last_point = pts_end.back(); + + Points pts = getFrontier(polylines_frontier, last_point, first_point, scale_(this->spacing), polylines_blocker); + if (!pts.empty()) { + pts_end.insert(pts_end.end(), pts.begin(), pts.end()); + pts_end.insert(pts_end.end(), polyline.points.begin(), polyline.points.end()); + continue; + } + } + // The lines cannot be connected. + polylines_connected.emplace_back(std::move(polyline)); + + first = false; + } + + //try to create some loops if possible + for (Polyline &polyline : polylines_connected) { + Points pts = getFrontier(polylines_frontier, polyline.last_point(), polyline.first_point(), scale_(this->spacing), polylines_blocker); + if (!pts.empty()) { + polyline.points.insert(polyline.points.end(), pts.begin(), pts.end()); + polyline.points.insert(polyline.points.begin(), polyline.points.back()); + } + polylines_out.emplace_back(polyline); + } +} + } // namespace Slic3r diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp index 8bf6c36898..b7f6b5b10b 100644 --- a/src/libslic3r/Fill/FillBase.hpp +++ b/src/libslic3r/Fill/FillBase.hpp @@ -109,6 +109,8 @@ protected: virtual std::pair _infill_direction(const Surface *surface) const; + void connect_infill(const Polylines &infill_ordered, const ExPolygon &boundary, Polylines &polylines_out); + public: static coord_t _adjust_solid_spacing(const coord_t width, const coord_t distance); diff --git a/src/libslic3r/Fill/FillGyroid.cpp b/src/libslic3r/Fill/FillGyroid.cpp index 04319bb268..9972e210c8 100644 --- a/src/libslic3r/Fill/FillGyroid.cpp +++ b/src/libslic3r/Fill/FillGyroid.cpp @@ -109,16 +109,20 @@ static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double std::swap(width,height); } - std::vector one_period = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip); // creates one period of the waves, so it doesn't have to be recalculated all the time + std::vector one_period_odd = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip); // creates one period of the waves, so it doesn't have to be recalculated all the time + flip = !flip; // even polylines are a bit shifted + std::vector one_period_even = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip); Polylines result; - for (double y0 = lower_bound; y0 < upper_bound+EPSILON; y0 += 2*M_PI) // creates odd polylines - result.emplace_back(make_wave(one_period, width, height, y0, scaleFactor, z_cos, z_sin, vertical)); - - flip = !flip; // even polylines are a bit shifted - one_period = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip); // updates the one period sample - for (double y0 = lower_bound + M_PI; y0 < upper_bound+EPSILON; y0 += 2*M_PI) // creates even polylines - result.emplace_back(make_wave(one_period, width, height, y0, scaleFactor, z_cos, z_sin, vertical)); + for (double y0 = lower_bound; y0 < upper_bound + EPSILON; y0 += M_PI) { + // creates odd polylines + result.emplace_back(make_wave(one_period_odd, width, height, y0, scaleFactor, z_cos, z_sin, vertical)); + // creates even polylines + y0 += M_PI; + if (y0 < upper_bound + EPSILON) { + result.emplace_back(make_wave(one_period_even, width, height, y0, scaleFactor, z_cos, z_sin, vertical)); + } + } return result; } @@ -141,7 +145,7 @@ void FillGyroid::_fill_surface_single( bb.merge(_align_to_grid(bb.min, Point(2.*M_PI*distance, 2.*M_PI*distance))); // generate pattern - Polylines polylines = make_gyroid_waves( + Polylines polylines_square = make_gyroid_waves( scale_(this->z), density_adjusted, this->spacing, @@ -149,46 +153,51 @@ void FillGyroid::_fill_surface_single( ceil(bb.size()(1) / distance) + 1.); // move pattern in place - for (Polyline &polyline : polylines) + for (Polyline &polyline : polylines_square) polyline.translate(bb.min(0), bb.min(1)); - // clip pattern to boundaries - polylines = intersection_pl(polylines, (Polygons)expolygon); - - // connect lines - if (! params.dont_connect && ! polylines.empty()) { // prevent calling leftmost_point() on empty collections - ExPolygon expolygon_off; - { - ExPolygons expolygons_off = offset_ex(expolygon, (float)SCALED_EPSILON); - if (! expolygons_off.empty()) { - // When expanding a polygon, the number of islands could only shrink. Therefore the offset_ex shall generate exactly one expanded island for one input island. - assert(expolygons_off.size() == 1); - std::swap(expolygon_off, expolygons_off.front()); - } - } - Polylines chained = PolylineCollection::chained_path_from( - std::move(polylines), - PolylineCollection::leftmost_point(polylines), false); // reverse allowed - bool first = true; - for (Polyline &polyline : chained) { - if (! first) { - // Try to connect the lines. - Points &pts_end = polylines_out.back().points; - const Point &first_point = polyline.points.front(); - const Point &last_point = pts_end.back(); - // TODO: we should also check that both points are on a fill_boundary to avoid - // connecting paths on the boundaries of internal regions - // TODO: avoid crossing current infill path - if ((last_point - first_point).cast().norm() <= 5 * distance && - expolygon_off.contains(Line(last_point, first_point))) { - // Append the polyline. - pts_end.insert(pts_end.end(), polyline.points.begin(), polyline.points.end()); - continue; + // clip pattern to boundaries, keeping the polyline order & ordering the fragment to be able to join them easily + //Polylines polylines = intersection_pl(polylines_square, (Polygons)expolygon); + Polylines polylines_chained; + for (size_t idx_polyline = 0; idx_polyline < polylines_square.size(); ++idx_polyline) { + Polyline &poly_to_cut = polylines_square[idx_polyline]; + Polylines polylines_to_sort = intersection_pl(Polylines() = { poly_to_cut }, (Polygons)expolygon); + for (Polyline &polyline : polylines_to_sort) { + //TODO: replace by closest_index_point() + if (idx_polyline % 2 == 0) { + if (poly_to_cut.points.front().distance_to_square(polyline.points.front()) > poly_to_cut.points.front().distance_to_square(polyline.points.back())) { + polyline.reverse(); + } + } else { + if (poly_to_cut.points.back().distance_to_square(polyline.points.front()) > poly_to_cut.points.back().distance_to_square(polyline.points.back())) { + polyline.reverse(); } } - // The lines cannot be connected. - polylines_out.emplace_back(std::move(polyline)); - first = false; + } + if (polylines_to_sort.size() > 1) { + Point nearest = poly_to_cut.points.front(); + if (idx_polyline % 2 != 0) { + nearest = poly_to_cut.points.back(); + } + //Bubble sort + for (size_t idx_sort = polylines_to_sort.size() - 1; idx_sort > 0; idx_sort--) { + for (size_t idx_bubble = 0; idx_bubble < idx_sort; idx_bubble++) { + if (polylines_to_sort[idx_bubble + 1].points.front().distance_to_square(nearest) < polylines_to_sort[idx_bubble].points.front().distance_to_square(nearest)) { + iter_swap(polylines_to_sort.begin() + idx_bubble, polylines_to_sort.begin() + idx_bubble + 1); + } + } + } + } + polylines_chained.insert(polylines_chained.end(), polylines_to_sort.begin(), polylines_to_sort.end()); + } + + if (!polylines_chained.empty()) { + + // connect lines + if (params.dont_connect) { + polylines_out.insert(polylines_out.end(), polylines_chained.begin(), polylines_chained.end()); + } else { + this->connect_infill(polylines_chained, expolygon, polylines_out); } } } diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index 994f45e59d..2909147711 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -124,6 +124,13 @@ public: double ccw_angle(const Point &p1, const Point &p2) const; Point projection_onto(const MultiPoint &poly) const; Point projection_onto(const Line &line) const; + + double distance_to(const Point &point) const { return (point - *this).cast().norm(); } + double distance_to_square(const Point &point) const { + double dx = (point.x() - this->x()); + double dy = (point.y() - this->y()); + return dx*dx + dy*dy; + } }; namespace int128 { From 19df45c39deebbb9a9a1c4ebaad7b9c0fcb2b231 Mon Sep 17 00:00:00 2001 From: supermerill Date: Tue, 12 Feb 2019 00:13:44 +0100 Subject: [PATCH 02/12] bugfix gyroid & 3Dhoneycomb "connected lines" --- src/libslic3r/Fill/FillBase.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index df32ff1790..d7e17698e4 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -290,7 +290,9 @@ Points getFrontier(Polylines &polylines, const Point& p1, const Point& p2, const //remove points if (idx_12 <= idx_21) { poly.points.erase(poly.points.begin() + idx_12, poly.points.begin() + idx_21 + 1); - cut_polygon(poly, idx_11, p1, p2); + if (idx_12 != 0) { + cut_polygon(poly, idx_11, p1, p2); + } //else : already cut at the good place } else { poly.points.erase(poly.points.begin() + idx_12, poly.points.end()); poly.points.erase(poly.points.begin(), poly.points.begin() + idx_21); @@ -304,7 +306,9 @@ Points getFrontier(Polylines &polylines, const Point& p1, const Point& p2, const //remove points if (idx_22 <= idx_11) { poly.points.erase(poly.points.begin() + idx_22, poly.points.begin() + idx_11 + 1); - cut_polygon(poly, idx_21, p1, p2); + if (idx_22 != 0) { + cut_polygon(poly, idx_21, p1, p2); + } //else : already cut at the good place } else { poly.points.erase(poly.points.begin() + idx_22, poly.points.end()); poly.points.erase(poly.points.begin(), poly.points.begin() + idx_11); From b6936a46e3062c16d5aba0c3be526afb48ff7140 Mon Sep 17 00:00:00 2001 From: supermerill Date: Fri, 8 Mar 2019 18:29:51 +0100 Subject: [PATCH 03/12] bugfix "connected lines" for gyroid & 3Dhoney --- src/libslic3r/Fill/Fill3DHoneycomb.cpp | 2 +- src/libslic3r/Fill/FillBase.cpp | 120 ++++++++++++++++++++----- src/libslic3r/Fill/FillBase.hpp | 2 +- src/libslic3r/Fill/FillGyroid.cpp | 12 ++- 4 files changed, 111 insertions(+), 25 deletions(-) diff --git a/src/libslic3r/Fill/Fill3DHoneycomb.cpp b/src/libslic3r/Fill/Fill3DHoneycomb.cpp index 7da18d9e31..bb49583204 100644 --- a/src/libslic3r/Fill/Fill3DHoneycomb.cpp +++ b/src/libslic3r/Fill/Fill3DHoneycomb.cpp @@ -190,7 +190,7 @@ void Fill3DHoneycomb::_fill_surface_single( if (params.dont_connect) { polylines_out.insert(polylines_out.end(), polylines_chained.begin(), polylines_chained.end()); } else { - this->connect_infill(polylines_chained, expolygon, polylines_out); + this->connect_infill(polylines_chained, expolygon, polylines_out, params); } } diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index d7e17698e4..f9259eed93 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -145,10 +145,9 @@ void cut_polyline(Polyline &poly, Polylines &polylines, size_t idx_1, Point p1, p2 = p1; p1 = temp; } - if (idx_1 == 0) { - poly.points.insert(poly.points.begin(), p2); - } else if (idx_1 == poly.points.size() - 1) { - poly.points.push_back(p1); + if (idx_1 == poly.points.size() - 1) { + //shouldn't be possible. + poly.points.erase(poly.points.end() - 1); } else { // create new polyline Polyline new_poly; @@ -158,7 +157,11 @@ void cut_polyline(Polyline &poly, Polylines &polylines, size_t idx_1, Point p1, //erase&put points in poly poly.points.erase(poly.points.begin() + idx_1 + 1, poly.points.end()); poly.points.push_back(p1); - polylines.emplace_back(new_poly); + //safe test + if (poly.length() == 0) + poly.points = new_poly.points; + else + polylines.emplace_back(new_poly); } } @@ -187,13 +190,13 @@ void cut_polygon(Polyline &poly, size_t idx_1, Point p1, Point p2) { /// complexity : N(pts_to_check.equally_spaced_points(width / 2)) x N(polylines_blocker.points) bool collision(const Points &pts_to_check, const Polylines &polylines_blocker, const coordf_t width) { //check if it's not too close to a polyline - coordf_t min_dist = width * width * 0.9 - SCALED_EPSILON; + coordf_t min_dist_square = width * width * 0.9 - SCALED_EPSILON; Polyline better_polylines(pts_to_check); Points better_pts = better_polylines.equally_spaced_points(width / 2); for (const Point &p : better_pts) { for (const Polyline &poly2 : polylines_blocker) { for (const Point &p2 : poly2.points) { - if (p.distance_to_square(p2) < min_dist) { + if (p.distance_to_square(p2) < min_dist_square) { return true; } } @@ -206,7 +209,7 @@ bool collision(const Points &pts_to_check, const Polylines &polylines_blocker, c /// width if the width of the extrusion /// polylines_blockers are the array of polylines to check if the path isn't blocked by something. /// complexity: N(polylines.points) + a collision check after that if we finded a path: N(2(p2-p1)/width) x N(polylines_blocker.points) -Points getFrontier(Polylines &polylines, const Point& p1, const Point& p2, const coord_t width, const Polylines &polylines_blockers) { +Points getFrontier(Polylines &polylines, const Point& p1, const Point& p2, const coord_t width, const Polylines &polylines_blockers, coord_t max_size = -1) { for (size_t idx_poly = 0; idx_poly < polylines.size(); ++idx_poly) { Polyline &poly = polylines[idx_poly]; if (poly.size() <= 1) continue; @@ -248,7 +251,7 @@ Points getFrontier(Polylines &polylines, const Point& p1, const Point& p2, const Points ret_1_to_2; double dist_1_to_2 = p1.distance_to(poly.points[idx_12]); ret_1_to_2.push_back(poly.points[idx_12]); - size_t max = idx_12 <= idx_21 ? idx_21 : poly.points.size() - 2; + size_t max = idx_12 <= idx_21 ? idx_21+1 : poly.points.size(); for (size_t i = idx_12 + 1; i < max; i++) { dist_1_to_2 += poly.points[i - 1].distance_to(poly.points[i]); ret_1_to_2.push_back(poly.points[i]); @@ -275,13 +278,17 @@ Points getFrontier(Polylines &polylines, const Point& p1, const Point& p2, const if (idx_22 > idx_11) { dist_2_to_1 += poly.points.back().distance_to(poly.points.front()); ret_2_to_1.push_back(poly.points[poly.points.size() - 1]); - for (size_t i = poly.points.size() - 2; i > idx_22; i--) { + for (size_t i = poly.points.size() - 1; i > idx_22; i--) { dist_2_to_1 += poly.points[i - 1].distance_to(poly.points[i]); ret_2_to_1.push_back(poly.points[i - 1]); } } dist_2_to_1 += p2.distance_to(poly.points[idx_22]); + if (max_size < dist_2_to_1 && max_size < dist_1_to_2) { + return Points(); + } + //choose between the two direction (keep the short one) if (dist_1_to_2 < dist_2_to_1) { if (collision(ret_1_to_2, polylines_blockers, width)) return Points(); @@ -350,6 +357,14 @@ Points getFrontier(Polylines &polylines, const Point& p1, const Point& p2, const } Points p_ret; p_ret.insert(p_ret.end(), poly.points.begin() + first_idx + 1, poly.points.begin() + last_idx); + + coordf_t length = 0; + for (size_t i = 1; i < p_ret.size(); i++) length += p_ret[i - 1].distance_to(p_ret[i]); + + if (max_size < length) { + return Points(); + } + if (collision(p_ret, polylines_blockers, width)) return Points(); //cut polyline poly.points.erase(poly.points.begin() + first_idx + 1, poly.points.begin() + last_idx); @@ -372,7 +387,7 @@ Points getFrontier(Polylines &polylines, const Point& p1, const Point& p2, const /// return the connected polylines in polylines_out. Can output polygons (stored as polylines with first_point = last_point). /// complexity: worst: N(infill_ordered.points) x N(boundary.points) /// typical: N(infill_ordered) x ( N(boundary.points) + N(infill_ordered.points) ) -void Fill::connect_infill(const Polylines &infill_ordered, const ExPolygon &boundary, Polylines &polylines_out) { +void Fill::connect_infill(const Polylines &infill_ordered, const ExPolygon &boundary, Polylines &polylines_out, const FillParams ¶ms) { //TODO: fallback to the quick & dirty old algorithm when n(points) is too high. Polylines polylines_frontier = to_polylines(((Polygons)boundary)); @@ -380,26 +395,54 @@ void Fill::connect_infill(const Polylines &infill_ordered, const ExPolygon &boun Polylines polylines_blocker; coord_t clip_size = scale_(this->spacing) * 2; for (const Polyline &polyline : infill_ordered) { - if (polyline.length() > 1.8 * clip_size) { + if (polyline.length() > 2.01 * clip_size) { polylines_blocker.push_back(polyline); polylines_blocker.back().clip_end(clip_size); polylines_blocker.back().clip_start(clip_size); } } + //length between two lines + coordf_t ideal_length = (1 / params.density) * this->spacing; - Polylines polylines_connected; + Polylines polylines_connected_first; bool first = true; for (const Polyline &polyline : infill_ordered) { if (!first) { // Try to connect the lines. - Points &pts_end = polylines_connected.back().points; - const Point &first_point = polyline.points.front(); + Points &pts_end = polylines_connected_first.back().points; const Point &last_point = pts_end.back(); + const Point &first_point = polyline.points.front(); + if (last_point.distance_to(first_point) < scale_(this->spacing) * 10) { + Points pts_frontier = getFrontier(polylines_frontier, last_point, first_point, scale_(this->spacing), polylines_blocker, (coord_t)scale_(ideal_length) * 2); + if (!pts_frontier.empty()) { + // The lines can be connected. + pts_end.insert(pts_end.end(), pts_frontier.begin(), pts_frontier.end()); + pts_end.insert(pts_end.end(), polyline.points.begin(), polyline.points.end()); + continue; + } + } + } + // The lines cannot be connected. + polylines_connected_first.emplace_back(std::move(polyline)); - Points pts = getFrontier(polylines_frontier, last_point, first_point, scale_(this->spacing), polylines_blocker); - if (!pts.empty()) { - pts_end.insert(pts_end.end(), pts.begin(), pts.end()); + first = false; + } + + Polylines polylines_connected; + first = true; + for (const Polyline &polyline : polylines_connected_first) { + if (!first) { + // Try to connect the lines. + Points &pts_end = polylines_connected.back().points; + const Point &last_point = pts_end.back(); + const Point &first_point = polyline.points.front(); + + Polylines before = polylines_frontier; + Points pts_frontier = getFrontier(polylines_frontier, last_point, first_point, scale_(this->spacing), polylines_blocker); + if (!pts_frontier.empty()) { + // The lines can be connected. + pts_end.insert(pts_end.end(), pts_frontier.begin(), pts_frontier.end()); pts_end.insert(pts_end.end(), polyline.points.begin(), polyline.points.end()); continue; } @@ -410,11 +453,46 @@ void Fill::connect_infill(const Polylines &infill_ordered, const ExPolygon &boun first = false; } + //try to link to nearest point if possible + for (int idx1 = 0; idx1 < polylines_connected.size(); idx1++) { + size_t min_idx = 0; + coordf_t min_length = 0; + bool switch_id1 = false; + bool switch_id2 = false; + for (int idx2 = idx1 + 1; idx2 < polylines_connected.size(); idx2++) { + double last_first = polylines_connected[idx1].last_point().distance_to_square(polylines_connected[idx2].first_point()); + double first_first = polylines_connected[idx1].first_point().distance_to_square(polylines_connected[idx2].first_point()); + double first_last = polylines_connected[idx1].first_point().distance_to_square(polylines_connected[idx2].last_point()); + double last_last = polylines_connected[idx1].last_point().distance_to_square(polylines_connected[idx2].last_point()); + double min = std::min(std::min(last_first, last_last), std::min(first_first, first_last)); + if (min < min_length || min_length == 0) { + min_idx = idx2; + switch_id1 = (std::min(last_first, last_last) > std::min(first_first, first_last)); + switch_id2 = (std::min(last_first, first_first) > std::min(last_last, first_last)); + min_length = min; + } + } + if (min_idx > idx1 && min_idx < polylines_connected.size()){ + Points pts_frontier = getFrontier(polylines_frontier, + switch_id1 ? polylines_connected[idx1].first_point() : polylines_connected[idx1].last_point(), + switch_id2 ? polylines_connected[min_idx].last_point() : polylines_connected[min_idx].first_point(), + scale_(this->spacing), polylines_blocker); + if (!pts_frontier.empty()) { + if (switch_id1) polylines_connected[idx1].reverse(); + if (switch_id2) polylines_connected[min_idx].reverse(); + Points &pts_end = polylines_connected[idx1].points; + pts_end.insert(pts_end.end(), pts_frontier.begin(), pts_frontier.end()); + pts_end.insert(pts_end.end(), polylines_connected[min_idx].points.begin(), polylines_connected[min_idx].points.end()); + polylines_connected.erase(polylines_connected.begin() + min_idx); + } + } + } + //try to create some loops if possible for (Polyline &polyline : polylines_connected) { - Points pts = getFrontier(polylines_frontier, polyline.last_point(), polyline.first_point(), scale_(this->spacing), polylines_blocker); - if (!pts.empty()) { - polyline.points.insert(polyline.points.end(), pts.begin(), pts.end()); + Points pts_frontier = getFrontier(polylines_frontier, polyline.last_point(), polyline.first_point(), scale_(this->spacing), polylines_blocker); + if (!pts_frontier.empty()) { + polyline.points.insert(polyline.points.end(), pts_frontier.begin(), pts_frontier.end()); polyline.points.insert(polyline.points.begin(), polyline.points.back()); } polylines_out.emplace_back(polyline); diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp index b7f6b5b10b..98717c41fe 100644 --- a/src/libslic3r/Fill/FillBase.hpp +++ b/src/libslic3r/Fill/FillBase.hpp @@ -109,7 +109,7 @@ protected: virtual std::pair _infill_direction(const Surface *surface) const; - void connect_infill(const Polylines &infill_ordered, const ExPolygon &boundary, Polylines &polylines_out); + void connect_infill(const Polylines &infill_ordered, const ExPolygon &boundary, Polylines &polylines_out, const FillParams ¶ms); public: static coord_t _adjust_solid_spacing(const coord_t width, const coord_t distance); diff --git a/src/libslic3r/Fill/FillGyroid.cpp b/src/libslic3r/Fill/FillGyroid.cpp index 9972e210c8..b8fe0ed727 100644 --- a/src/libslic3r/Fill/FillGyroid.cpp +++ b/src/libslic3r/Fill/FillGyroid.cpp @@ -191,13 +191,21 @@ void FillGyroid::_fill_surface_single( polylines_chained.insert(polylines_chained.end(), polylines_to_sort.begin(), polylines_to_sort.end()); } + size_t polylines_out_first_idx = polylines_out.size(); if (!polylines_chained.empty()) { - // connect lines if (params.dont_connect) { polylines_out.insert(polylines_out.end(), polylines_chained.begin(), polylines_chained.end()); } else { - this->connect_infill(polylines_chained, expolygon, polylines_out); + this->connect_infill(polylines_chained, expolygon, polylines_out, params); + } + } + + //remove too small bits (larger than longer); + for (size_t idx = polylines_out_first_idx; idx < polylines_out.size(); idx++) { + if (polylines_out[idx].length() < scale_(this->spacing * 3)) { + polylines_out.erase(polylines_out.begin() + idx); + idx--; } } } From b9901f1730cca7040bc1d863afc5d0a8963d75be Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Thu, 20 Jun 2019 00:27:22 +0200 Subject: [PATCH 04/12] Parametric tolerance for Gyroid infill --- src/libslic3r/Fill/FillGyroid.cpp | 57 ++++++++++++++++++------------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/src/libslic3r/Fill/FillGyroid.cpp b/src/libslic3r/Fill/FillGyroid.cpp index b8fe0ed727..7c6674556a 100644 --- a/src/libslic3r/Fill/FillGyroid.cpp +++ b/src/libslic3r/Fill/FillGyroid.cpp @@ -55,43 +55,52 @@ static inline Polyline make_wave( return polyline; } -static std::vector make_one_period(double width, double scaleFactor, double z_cos, double z_sin, bool vertical, bool flip) +static std::vector make_one_period(double width, double scaleFactor, double z_cos, double z_sin, bool vertical, bool flip, double tolerance) { std::vector points; - double dx = M_PI_4; // very coarse spacing to begin with + double dx = M_PI_2; // exact coordinates on main inflexion lobes double limit = std::min(2*M_PI, width); for (double x = 0.; x < limit + EPSILON; x += dx) { // so the last point is there too x = std::min(x, limit); - points.emplace_back(Vec2d(x,f(x, z_sin,z_cos, vertical, flip))); + points.emplace_back(Vec2d(x, f(x, z_sin, z_cos, vertical, flip))); } - // now we will check all internal points and in case some are too far from the line connecting its neighbours, - // we will add one more point on each side: - const double tolerance = .1; - for (unsigned int i=1;i(scaleFactor) * std::abs(cross2(rp, lp) - cross2(rp - lp, tp)) / lrv.norm(); - if (dist_mm > tolerance) { // if the difference from straight line is more than this - double x = 0.5f * (points[i-1](0) + points[i](0)); - points.emplace_back(Vec2d(x, f(x, z_sin, z_cos, vertical, flip))); - x = 0.5f * (points[i+1](0) + points[i](0)); - points.emplace_back(Vec2d(x, f(x, z_sin, z_cos, vertical, flip))); - // we added the points to the end, but need them all in order - std::sort(points.begin(), points.end(), [](const Vec2d &lhs, const Vec2d &rhs){ return lhs < rhs; }); - // decrement i so we also check the first newly added point - --i; + // piecewise increase in resolution up to requested tolerance + for(;;) + { + size_t size = points.size(); + for (unsigned int i = 1;i < size; ++i) { + auto& lp = points[i-1]; // left point + auto& rp = points[i]; // right point + double x = lp(0) + (rp(0) - lp(0)) / 2; + double y = f(x, z_sin, z_cos, vertical, flip); + Vec2d ip = {x, y}; + if (std::abs(cross2(Vec2d(ip - lp), Vec2d(ip - rp))) > sqr(tolerance)) { + points.emplace_back(std::move(ip)); + } + } + + if (size == points.size()) + break; + else + { + // insert new points in order + std::sort(points.begin(), points.end(), + [](const Vec2d &lhs, const Vec2d &rhs) { return lhs(0) < rhs(0); }); } } + return points; } static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double line_spacing, double width, double height) { const double scaleFactor = scale_(line_spacing) / density_adjusted; + + // tolerance (in scaled units) + // TODO: should consider layer thickness + const double tolerance = line_spacing / 2 / unscale(scaleFactor); + //scale factor for 5% : 8 712 388 // 1z = 10^-6 mm ? const double z = gridZ / scaleFactor; @@ -109,9 +118,9 @@ static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double std::swap(width,height); } - std::vector one_period_odd = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip); // creates one period of the waves, so it doesn't have to be recalculated all the time + std::vector one_period_odd = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip, tolerance); // creates one period of the waves, so it doesn't have to be recalculated all the time flip = !flip; // even polylines are a bit shifted - std::vector one_period_even = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip); + std::vector one_period_even = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip, tolerance); Polylines result; for (double y0 = lower_bound; y0 < upper_bound + EPSILON; y0 += M_PI) { From 5932881291be0ef99abc72677fdf2507e9fd0fce Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sat, 22 Jun 2019 15:59:54 +0200 Subject: [PATCH 05/12] Reduce reallocations and memory usage in gyroid --- src/libslic3r/Fill/FillGyroid.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libslic3r/Fill/FillGyroid.cpp b/src/libslic3r/Fill/FillGyroid.cpp index 7c6674556a..ecc1d54a0b 100644 --- a/src/libslic3r/Fill/FillGyroid.cpp +++ b/src/libslic3r/Fill/FillGyroid.cpp @@ -35,6 +35,7 @@ static inline Polyline make_wave( { std::vector points = one_period; double period = points.back()(0); + points.reserve(one_period.size() * floor(width / period)); points.pop_back(); int n = points.size(); do { @@ -44,6 +45,7 @@ static inline Polyline make_wave( // and construct the final polyline to return: Polyline polyline; + polyline.points.reserve(points.size()); for (auto& point : points) { point(1) += offset; point(1) = clamp(0., height, double(point(1))); @@ -60,6 +62,7 @@ static std::vector make_one_period(double width, double scaleFactor, doub std::vector points; double dx = M_PI_2; // exact coordinates on main inflexion lobes double limit = std::min(2*M_PI, width); + points.reserve(ceil(limit / tolerance / 3)); for (double x = 0.; x < limit + EPSILON; x += dx) { // so the last point is there too x = std::min(x, limit); points.emplace_back(Vec2d(x, f(x, z_sin, z_cos, vertical, flip))); From e7616efc89462a278017ef3b1f2620f999a3e4e5 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sat, 22 Jun 2019 18:56:44 +0200 Subject: [PATCH 06/12] Handle truncated gyroid patterns correctly When generating patterns which are less than a full wave, always generate the last point correctly. When extending a full wave to a line, fixup the last point to the real gyroid position instead of shifting the point. --- src/libslic3r/Fill/FillGyroid.cpp | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/Fill/FillGyroid.cpp b/src/libslic3r/Fill/FillGyroid.cpp index ecc1d54a0b..c0b3bd907c 100644 --- a/src/libslic3r/Fill/FillGyroid.cpp +++ b/src/libslic3r/Fill/FillGyroid.cpp @@ -31,17 +31,22 @@ static inline double f(double x, double z_sin, double z_cos, bool vertical, bool static inline Polyline make_wave( const std::vector& one_period, double width, double height, double offset, double scaleFactor, - double z_cos, double z_sin, bool vertical) + double z_cos, double z_sin, bool vertical, bool flip) { std::vector points = one_period; double period = points.back()(0); - points.reserve(one_period.size() * floor(width / period)); - points.pop_back(); - int n = points.size(); - do { - points.emplace_back(Vec2d(points[points.size()-n](0) + period, points[points.size()-n](1))); - } while (points.back()(0) < width); - points.back()(0) = width; + if (width != period) // do not extend if already truncated + { + points.reserve(one_period.size() * floor(width / period)); + points.pop_back(); + + int n = points.size(); + do { + points.emplace_back(Vec2d(points[points.size()-n](0) + period, points[points.size()-n](1))); + } while (points.back()(0) < width - EPSILON); + + points.emplace_back(Vec2d(width, f(width, z_sin, z_cos, vertical, flip))); + } // and construct the final polyline to return: Polyline polyline; @@ -63,10 +68,11 @@ static std::vector make_one_period(double width, double scaleFactor, doub double dx = M_PI_2; // exact coordinates on main inflexion lobes double limit = std::min(2*M_PI, width); points.reserve(ceil(limit / tolerance / 3)); - for (double x = 0.; x < limit + EPSILON; x += dx) { // so the last point is there too - x = std::min(x, limit); + + for (double x = 0.; x < limit - EPSILON; x += dx) { points.emplace_back(Vec2d(x, f(x, z_sin, z_cos, vertical, flip))); } + points.emplace_back(Vec2d(limit, f(limit, z_sin, z_cos, vertical, flip))); // piecewise increase in resolution up to requested tolerance for(;;) @@ -128,11 +134,11 @@ static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double for (double y0 = lower_bound; y0 < upper_bound + EPSILON; y0 += M_PI) { // creates odd polylines - result.emplace_back(make_wave(one_period_odd, width, height, y0, scaleFactor, z_cos, z_sin, vertical)); + result.emplace_back(make_wave(one_period_odd, width, height, y0, scaleFactor, z_cos, z_sin, vertical, flip)); // creates even polylines y0 += M_PI; if (y0 < upper_bound + EPSILON) { - result.emplace_back(make_wave(one_period_even, width, height, y0, scaleFactor, z_cos, z_sin, vertical)); + result.emplace_back(make_wave(one_period_even, width, height, y0, scaleFactor, z_cos, z_sin, vertical, flip)); } } From 1a846421530328c1190e1180929fdf5ae1496328 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Tue, 16 Jul 2019 01:15:00 +0200 Subject: [PATCH 07/12] Allow gyroid pattern rotation over Z --- src/libslic3r/Fill/FillGyroid.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/Fill/FillGyroid.cpp b/src/libslic3r/Fill/FillGyroid.cpp index c0b3bd907c..1e1e35b9e7 100644 --- a/src/libslic3r/Fill/FillGyroid.cpp +++ b/src/libslic3r/Fill/FillGyroid.cpp @@ -152,7 +152,8 @@ void FillGyroid::_fill_surface_single( ExPolygon &expolygon, Polylines &polylines_out) { - // no rotation is supported for this infill pattern (yet) + expolygon.rotate(-this->angle); + BoundingBox bb = expolygon.contour.bounding_box(); // Density adjusted to have a good %of weight. double density_adjusted = std::max(0., params.density * 2.44); @@ -160,7 +161,7 @@ void FillGyroid::_fill_surface_single( coord_t distance = coord_t(scale_(this->spacing) / density_adjusted); // align bounding box to a multiple of our grid module - bb.merge(_align_to_grid(bb.min, Point(2.*M_PI*distance, 2.*M_PI*distance))); + bb.merge(_align_to_grid(bb.min, Point(2*M_PI*distance, 2*M_PI*distance))); // generate pattern Polylines polylines_square = make_gyroid_waves( @@ -169,16 +170,15 @@ void FillGyroid::_fill_surface_single( this->spacing, ceil(bb.size()(0) / distance) + 1., ceil(bb.size()(1) / distance) + 1.); - - // move pattern in place - for (Polyline &polyline : polylines_square) - polyline.translate(bb.min(0), bb.min(1)); // clip pattern to boundaries, keeping the polyline order & ordering the fragment to be able to join them easily - //Polylines polylines = intersection_pl(polylines_square, (Polygons)expolygon); Polylines polylines_chained; for (size_t idx_polyline = 0; idx_polyline < polylines_square.size(); ++idx_polyline) { + // shift the polyline to the grid origin Polyline &poly_to_cut = polylines_square[idx_polyline]; + poly_to_cut.translate(bb.min); + + // intersect Polylines polylines_to_sort = intersection_pl(Polylines() = { poly_to_cut }, (Polygons)expolygon); for (Polyline &polyline : polylines_to_sort) { //TODO: replace by closest_index_point() @@ -226,6 +226,12 @@ void FillGyroid::_fill_surface_single( idx--; } } + + // new paths must be rotated back + for (Polylines::iterator it = polylines_out.begin() + polylines_out_first_idx; + it != polylines_out.end(); ++it) { + it->rotate(this->angle); + } } } // namespace Slic3r From f8490fb4e0265a7c3ecd5292d1ae9716755fc56b Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Fri, 19 Jul 2019 12:59:57 +0200 Subject: [PATCH 08/12] Limit upper tolerance in Gyroid Do not reduce resolution more than necessary when using larger nozzles and/or higher layer heights. --- src/libslic3r/Fill/FillGyroid.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Fill/FillGyroid.cpp b/src/libslic3r/Fill/FillGyroid.cpp index 1e1e35b9e7..651abffd9c 100644 --- a/src/libslic3r/Fill/FillGyroid.cpp +++ b/src/libslic3r/Fill/FillGyroid.cpp @@ -106,9 +106,9 @@ static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double { const double scaleFactor = scale_(line_spacing) / density_adjusted; - // tolerance (in scaled units) - // TODO: should consider layer thickness - const double tolerance = line_spacing / 2 / unscale(scaleFactor); + // tolerance (in scaled units) - note: clamp the maximum tolerance + // as there's no benefit to reduce the definition with large nozzles + const double tolerance = std::min(line_spacing, 0.4) / 2 / unscale(scaleFactor); //scale factor for 5% : 8 712 388 // 1z = 10^-6 mm ? From da6c285f1f1bf8e1bdec8ec2998e3962842d6cc8 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Wed, 7 Aug 2019 21:45:23 +0200 Subject: [PATCH 09/12] Maximize gyroid printing speed angle Counter-rotate the default angle by 45' so that gyroid is kept at it's maximum printing speed by default. --- src/libslic3r/Fill/FillGyroid.cpp | 12 ++++++++---- src/libslic3r/Fill/FillGyroid.hpp | 5 +++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/Fill/FillGyroid.cpp b/src/libslic3r/Fill/FillGyroid.cpp index 651abffd9c..b7f0f879b9 100644 --- a/src/libslic3r/Fill/FillGyroid.cpp +++ b/src/libslic3r/Fill/FillGyroid.cpp @@ -152,7 +152,9 @@ void FillGyroid::_fill_surface_single( ExPolygon &expolygon, Polylines &polylines_out) { - expolygon.rotate(-this->angle); + float infill_angle = this->angle + (CorrectionAngle * 2*M_PI) / 360.; + if(abs(infill_angle) >= EPSILON) + expolygon.rotate(-infill_angle); BoundingBox bb = expolygon.contour.bounding_box(); // Density adjusted to have a good %of weight. @@ -228,9 +230,11 @@ void FillGyroid::_fill_surface_single( } // new paths must be rotated back - for (Polylines::iterator it = polylines_out.begin() + polylines_out_first_idx; - it != polylines_out.end(); ++it) { - it->rotate(this->angle); + if(abs(infill_angle) >= EPSILON) { + for (Polylines::iterator it = polylines_out.begin() + polylines_out_first_idx; + it != polylines_out.end(); ++it) { + it->rotate(infill_angle); + } } } diff --git a/src/libslic3r/Fill/FillGyroid.hpp b/src/libslic3r/Fill/FillGyroid.hpp index 9c3cef940b..171a6b0345 100644 --- a/src/libslic3r/Fill/FillGyroid.hpp +++ b/src/libslic3r/Fill/FillGyroid.hpp @@ -16,6 +16,11 @@ public: // require bridge flow since most of this pattern hangs in air virtual bool use_bridge_flow() const { return false; } + // Correction applied to regular infill angle to maximize printing + // speed in default configuration (degrees) + static constexpr float CorrectionAngle = -45.; + + protected: virtual void _fill_surface_single( const FillParams ¶ms, From 90c85b7c8ab3397271761fa64a68b0bcce31da0b Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Wed, 7 Aug 2019 21:58:45 +0200 Subject: [PATCH 10/12] Move gyroid constants to the class declaration --- src/libslic3r/Fill/FillGyroid.cpp | 8 ++++---- src/libslic3r/Fill/FillGyroid.hpp | 6 ++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/Fill/FillGyroid.cpp b/src/libslic3r/Fill/FillGyroid.cpp index b7f0f879b9..ae96f1f90b 100644 --- a/src/libslic3r/Fill/FillGyroid.cpp +++ b/src/libslic3r/Fill/FillGyroid.cpp @@ -106,9 +106,9 @@ static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double { const double scaleFactor = scale_(line_spacing) / density_adjusted; - // tolerance (in scaled units) - note: clamp the maximum tolerance - // as there's no benefit to reduce the definition with large nozzles - const double tolerance = std::min(line_spacing, 0.4) / 2 / unscale(scaleFactor); + // tolerance in scaled units. clamp the maximum tolerance as there's + // no processing-speed benefit to do so beyond a certain point + const double tolerance = std::min(line_spacing, FillGyroid::PatternTolerance) / 2 / unscale(scaleFactor); //scale factor for 5% : 8 712 388 // 1z = 10^-6 mm ? @@ -158,7 +158,7 @@ void FillGyroid::_fill_surface_single( BoundingBox bb = expolygon.contour.bounding_box(); // Density adjusted to have a good %of weight. - double density_adjusted = std::max(0., params.density * 2.44); + double density_adjusted = std::max(0., params.density * DensityAdjust); // Distance between the gyroid waves in scaled coordinates. coord_t distance = coord_t(scale_(this->spacing) / density_adjusted); diff --git a/src/libslic3r/Fill/FillGyroid.hpp b/src/libslic3r/Fill/FillGyroid.hpp index 171a6b0345..261c0039e1 100644 --- a/src/libslic3r/Fill/FillGyroid.hpp +++ b/src/libslic3r/Fill/FillGyroid.hpp @@ -20,6 +20,12 @@ public: // speed in default configuration (degrees) static constexpr float CorrectionAngle = -45.; + // Density adjustment to have a good %of weight. + static constexpr double DensityAdjust = 2.44; + + // Gyroid upper resolution tolerance (mm^-2) + static constexpr double PatternTolerance = 0.4; + protected: virtual void _fill_surface_single( From 753b34a0d3cae8acd6fb6b65abad7631c9a4f5ec Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Thu, 8 Aug 2019 16:53:26 +0200 Subject: [PATCH 11/12] Make Gyroid::PatternTolerance match the description Move the division out of the switch in order to make the tolerance match the expected unit. --- src/libslic3r/Fill/FillGyroid.cpp | 6 +++--- src/libslic3r/Fill/FillGyroid.hpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/Fill/FillGyroid.cpp b/src/libslic3r/Fill/FillGyroid.cpp index ae96f1f90b..341815e9bb 100644 --- a/src/libslic3r/Fill/FillGyroid.cpp +++ b/src/libslic3r/Fill/FillGyroid.cpp @@ -108,10 +108,10 @@ static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double // tolerance in scaled units. clamp the maximum tolerance as there's // no processing-speed benefit to do so beyond a certain point - const double tolerance = std::min(line_spacing, FillGyroid::PatternTolerance) / 2 / unscale(scaleFactor); + const double tolerance = std::min(line_spacing / 2, FillGyroid::PatternTolerance) / unscale(scaleFactor); - //scale factor for 5% : 8 712 388 - // 1z = 10^-6 mm ? + //scale factor for 5% : 8 712 388 + // 1z = 10^-6 mm ? const double z = gridZ / scaleFactor; const double z_sin = sin(z); const double z_cos = cos(z); diff --git a/src/libslic3r/Fill/FillGyroid.hpp b/src/libslic3r/Fill/FillGyroid.hpp index 261c0039e1..37babb25e3 100644 --- a/src/libslic3r/Fill/FillGyroid.hpp +++ b/src/libslic3r/Fill/FillGyroid.hpp @@ -24,7 +24,7 @@ public: static constexpr double DensityAdjust = 2.44; // Gyroid upper resolution tolerance (mm^-2) - static constexpr double PatternTolerance = 0.4; + static constexpr double PatternTolerance = 0.2; protected: From 189d7be93bfd67b4ce1691b19b174b87c39a1857 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Thu, 8 Aug 2019 17:04:15 +0200 Subject: [PATCH 12/12] Fix two warnings --- src/libslic3r/Fill/FillBase.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index f9259eed93..c4ad93be5a 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -454,12 +454,12 @@ void Fill::connect_infill(const Polylines &infill_ordered, const ExPolygon &boun } //try to link to nearest point if possible - for (int idx1 = 0; idx1 < polylines_connected.size(); idx1++) { + for (size_t idx1 = 0; idx1 < polylines_connected.size(); idx1++) { size_t min_idx = 0; coordf_t min_length = 0; bool switch_id1 = false; bool switch_id2 = false; - for (int idx2 = idx1 + 1; idx2 < polylines_connected.size(); idx2++) { + for (size_t idx2 = idx1 + 1; idx2 < polylines_connected.size(); idx2++) { double last_first = polylines_connected[idx1].last_point().distance_to_square(polylines_connected[idx2].first_point()); double first_first = polylines_connected[idx1].first_point().distance_to_square(polylines_connected[idx2].first_point()); double first_last = polylines_connected[idx1].first_point().distance_to_square(polylines_connected[idx2].last_point());