diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index 94b9a2b74f..c96e4e0238 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -639,10 +639,8 @@ static inline Intersection* get_nearest_intersection(std::vectorvector().cast().normalized(); // 50% overlap of the extrusion lines to achieve strong bonding. - Vec2d offset_vector = Vec2d(- dir.y(), dir.x()) * (intersection.left ? scaled_offset : - scaled_offset); - offset_line.translate(offset_vector.x(), offset_vector.y()); + offset_line.translate((perp(intersection.closest_line->vector().cast().normalized()) * (intersection.left ? scaled_offset : - scaled_offset)).cast()); // Extend the line by a small value to guarantee a collision with adjacent lines offset_line.extend(coord_t(scaled_offset * 1.16)); // / cos(PI/6) return offset_line; @@ -668,7 +666,10 @@ static inline rtree_segment_t mk_rtree_seg(const Line &l) { } // Create a hook based on hook_line and append it to the begin or end of the polyline in the intersection -static void add_hook(const Intersection &intersection, const double scaled_offset, const int hook_length, const rtree_t &rtree, const Lines &lines_src) +static void add_hook( + const Intersection &intersection, const double scaled_offset, + const int hook_length, double scaled_trim_distance, + const rtree_t &rtree, const Lines &lines_src) { // Trim the hook start by the infill line it will connect to. Point hook_start; @@ -680,7 +681,9 @@ static void add_hook(const Intersection &intersection, const double scaled_offse std::optional other_hook = intersection.other_hook(); Vec2d hook_vector_norm = intersection.closest_line->vector().cast().normalized(); - Vector hook_vector = (hook_length * hook_vector_norm).cast(); + // hook_vector is extended by the thickness of the infill line, so that a collision is found against + // the infill centerline to be later trimmed by the thickened line. + Vector hook_vector = ((hook_length + 1.16 * scaled_trim_distance) * hook_vector_norm).cast(); Line hook_forward(hook_start, hook_start + hook_vector); auto filter_itself = [&intersection, &lines_src](const auto &item) { return item.second != intersection.intersect_line - lines_src.data(); }; @@ -690,71 +693,61 @@ static void add_hook(const Intersection &intersection, const double scaled_offse Point self_intersection_point; bool self_intersection = other_hook && other_hook->intersection(hook_forward, &self_intersection_point); - Point hook_end; - if (hook_intersections.empty() && ! self_intersection) { - // The hook is not limited by another infill line. Extrude it in its full length. - hook_end = hook_forward.b; - } else { - - // Find closest intersection of a line segment starting with pt pointing in dir - // with any of the hook_intersections, returns Euclidian distance. - // dir is normalized. - auto max_hook_length = [hook_length, scaled_offset, &lines_src]( - const Vec2d &pt, const Vec2d &dir, - const std::vector> &hook_intersections, - bool self_intersection, const std::optional &self_intersection_line, const Point &self_intersection_point) { - // No hook is longer than hook_length, there shouldn't be any intersection closer than that. - auto max_length = double(hook_length); - auto update_max_length = [&max_length](double d) { - if (d < max_length) - max_length = std::max(0., d); - }; - // Shift the trimming point away from the colliding thick line. - auto shift_from_thick_line = [&dir, scaled_offset](const Vec2d& dir2) -> Vec2d { - Vec2d perp = Vec2d(-dir2.y(), dir2.x()).normalized(); - double shift = perp.dot(dir) > 0. ? -scaled_offset : scaled_offset; - return perp * (0.5 * shift); - }; - - for (const auto &hook_intersection : hook_intersections) { - const rtree_segment_t &segment = hook_intersection.first; - // Segment start and end points, segment vector. - Vec2d pt2(bg::get<0, 0>(segment), bg::get<0, 1>(segment)); - Vec2d dir2 = Vec2d(bg::get<1, 0>(segment), bg::get<1, 1>(segment)) - pt2; - // Find intersection of (pt, dir) with (pt2, dir2), where dir is normalized. - double denom = cross2(dir, dir2); - assert(std::abs(denom) > EPSILON); - if (hook_intersection.second < lines_src.size()) - // Trimming by another infill line. Reduce overlap. - pt2 += shift_from_thick_line(dir2); - update_max_length(cross2(pt2 - pt, dir2) / denom); - } - if (self_intersection) { - double t = (self_intersection_point.cast() + shift_from_thick_line(self_intersection_line.value().vector().cast()) - pt).norm(); - max_length = std::min(max_length, t); - } - return max_length; + // Find closest intersection of a line segment starting with pt pointing in dir + // with any of the hook_intersections, returns Euclidian distance. + // dir is normalized. + auto max_hook_length = [hook_length, scaled_trim_distance, &lines_src]( + const Vec2d &pt, const Vec2d &dir, + const std::vector> &hook_intersections, + bool self_intersection, const std::optional &self_intersection_line, const Point &self_intersection_point) { + // No hook is longer than hook_length, there shouldn't be any intersection closer than that. + auto max_length = double(hook_length); + auto update_max_length = [&max_length](double d) { + if (d < max_length) + max_length = d; + }; + // Shift the trimming point away from the colliding thick line. + auto shift_from_thick_line = [&dir, scaled_trim_distance](const Vec2d& dir2) { + return scaled_trim_distance * std::abs(cross2(dir, dir2.normalized())); }; - // There is not enough space for the full hook length, try the opposite direction. - Vec2d hook_startf = hook_start.cast(); - double hook_forward_max_length = max_hook_length(hook_startf, hook_vector_norm, hook_intersections, self_intersection, other_hook, self_intersection_point); + for (const auto &hook_intersection : hook_intersections) { + const rtree_segment_t &segment = hook_intersection.first; + // Segment start and end points, segment vector. + Vec2d pt2(bg::get<0, 0>(segment), bg::get<0, 1>(segment)); + Vec2d dir2 = Vec2d(bg::get<1, 0>(segment), bg::get<1, 1>(segment)) - pt2; + // Find intersection of (pt, dir) with (pt2, dir2), where dir is normalized. + double denom = cross2(dir, dir2); + assert(std::abs(denom) > EPSILON); + double t = cross2(pt2 - pt, dir2) / denom; + if (hook_intersection.second < lines_src.size()) + // Trimming by another infill line. Reduce overlap. + t -= shift_from_thick_line(dir2); + update_max_length(t); + } + if (self_intersection) { + double t = (self_intersection_point.cast() - pt).dot(dir) - shift_from_thick_line(self_intersection_line.value().vector().cast()); + max_length = std::min(max_length, t); + } + return std::max(0., max_length); + }; + + Vec2d hook_startf = hook_start.cast(); + double hook_forward_max_length = max_hook_length(hook_startf, hook_vector_norm, hook_intersections, self_intersection, other_hook, self_intersection_point); + double hook_backward_max_length = 0.; + if (hook_forward_max_length < hook_length - SCALED_EPSILON) { + // Try the other side. hook_intersections.clear(); Line hook_backward(hook_start, hook_start - hook_vector); rtree.query(bgi::intersects(mk_rtree_seg(hook_backward)) && bgi::satisfies(filter_itself), std::back_inserter(hook_intersections)); self_intersection = other_hook && other_hook->intersection(hook_backward, &self_intersection_point); - - if (hook_intersections.empty() && ! self_intersection) { - // The hook in the other direction is not limited by another infill line. Extrude it in its full length. - hook_end = hook_backward.b; - } else { - // There is not enough space for the full hook in both directions, take the longer one. - double hook_backward_max_length = max_hook_length(hook_startf, - hook_vector_norm, hook_intersections, self_intersection, other_hook, self_intersection_point); - Vec2d hook_dir = (hook_forward_max_length > hook_backward_max_length ? hook_forward_max_length : - hook_backward_max_length) * hook_vector_norm; - hook_end = hook_start + hook_dir.cast(); - } + hook_backward_max_length = max_hook_length(hook_startf, - hook_vector_norm, hook_intersections, self_intersection, other_hook, self_intersection_point); } + // Take the longer hook. + Vec2d hook_dir = (hook_forward_max_length > hook_backward_max_length ? hook_forward_max_length : - hook_backward_max_length) * hook_vector_norm; + Point hook_end = hook_start + hook_dir.cast(); + Points &pl = intersection.intersect_pl->points; if (intersection.front) { pl.front() = hook_start; @@ -774,6 +767,9 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b lines_src.reserve(lines.size()); std::transform(lines.begin(), lines.end(), std::back_inserter(lines_src), [](const Line& l) { return Polyline{ l.a, l.b }; }); + const float scaled_offset = float(scale_(spacing) * 0.7); // 30% overlap + const float scaled_trim_distance = float(scale_(spacing) * 0.5 * 0.75); // 25% overlap + // Keeping the vector of closest points outside the loop, so the vector does not need to be reallocated. std::vector> closest; // Pairs of lines touching at one end point. The pair is sorted to make the end point connection test symmetric. @@ -836,13 +832,12 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b sort_remove_duplicates(lines_touching_at_endpoints); - const float scaled_offset = float(scale_(spacing) * 0.7); // 30% overlap - std::vector intersections; { // Minimum lenght of an infill line to anchor. Very short lines cannot be trimmed from both sides, // it does not help to anchor extremely short infill lines, it consumes too much plastic while not adding // to the object rigidity. + assert(scaled_offset > scaled_trim_distance); const double line_len_threshold_drop_both_sides = scaled_offset * (2. / cos(PI / 6.) + 0.5) + SCALED_EPSILON; const double line_len_threshold_anchor_both_sides = line_len_threshold_drop_both_sides + scaled_offset; const double line_len_threshold_drop_single_side = scaled_offset * (1. / cos(PI / 6.) + 1.5) + SCALED_EPSILON; @@ -852,8 +847,8 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b if (line.points.empty()) continue; - const Point &front_point = line.points.front(); - const Point &back_point = line.points.back(); + Point &front_point = line.points.front(); + Point &back_point = line.points.back(); // Find the nearest line from the start point of the line. std::optional tjoint_front, tjoint_back; @@ -922,6 +917,15 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b if (tjoint_back) // T-joint of line's back point with the 'closest' line. intersections.emplace_back(lines_src[tjoint_back.value()], lines_src[line_idx], &line, back_point, false); + } else { + if (tjoint_front) + // T joint at the front at a 60 degree angle, the line is very short. + // Trim the front side. + front_point += ((scaled_trim_distance * 1.155) * (back_point - front_point).cast().normalized()).cast(); + if (tjoint_back) + // T joint at the front at a 60 degree angle, the line is very short. + // Trim the front side. + back_point += ((scaled_trim_distance * 1.155) * (front_point - back_point).cast().normalized()).cast(); } } } @@ -1008,8 +1012,8 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b // pl2 may have alread been merged with another polyline, even with this one. pl2 = &lines[update_merged_polyline_idx(pl2 - lines.data())]; assert(pl1 <= pl2); - // Avoid closing a loop. - if (pl1 != pl2) { + // Avoid closing a loop, ignore dropped infill lines. + if (pl1 != pl2 && ! pl1->points.empty() && ! pl2->points.empty()) { // Merge the polylines. assert(pl1 < pl2); assert(pl1->points.size() >= 2); @@ -1061,7 +1065,7 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b #ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT export_infill_lines_to_svg(boundary, lines, debug_out_path("FillAdaptive-add_hook0-pre-%d-%d.svg", iRun, iStep), { first_i.intersect_point }); #endif // ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT - add_hook(first_i, scaled_offset, hook_length, rtree, lines_src); + add_hook(first_i, scaled_offset, hook_length, scaled_trim_distance, rtree, lines_src); #ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT export_infill_lines_to_svg(boundary, lines, debug_out_path("FillAdaptive-add_hook0-pre-%d-%d.svg", iRun, iStep), { first_i.intersect_point }); ++ iStep; @@ -1138,6 +1142,7 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b } else { // Both intersections are on different polylines Line l(first_i_point, nearest_i_point); + l.translate((perp(first_i.closest_line->vector().cast().normalized()) * (first_i.left ? scaled_trim_distance : - scaled_trim_distance)).cast()); Point pt_start, pt_end; bool trim_start = first_i .intersect_pl->points.size() == 3 && first_i .other_hook_intersects(l, pt_start); bool trim_end = nearest_i.intersect_pl->points.size() == 3 && nearest_i.other_hook_intersects(l, pt_end); @@ -1175,7 +1180,7 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b #ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT export_infill_lines_to_svg(boundary, lines, debug_out_path("FillAdaptive-add_hook-pre-%d-%d.svg", iRun, iStep), { first_i.intersect_point }); #endif // ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT - add_hook(first_i, scaled_offset, hook_length, rtree, lines_src); + add_hook(first_i, scaled_offset, hook_length, scaled_trim_distance, rtree, lines_src); #ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT export_infill_lines_to_svg(boundary, lines, debug_out_path("FillAdaptive-add_hook-post-%d-%d.svg", iRun, iStep), { first_i.intersect_point }); #endif // ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT diff --git a/src/libslic3r/Line.hpp b/src/libslic3r/Line.hpp index 46f1a0442f..72532b4e33 100644 --- a/src/libslic3r/Line.hpp +++ b/src/libslic3r/Line.hpp @@ -54,7 +54,8 @@ public: Line(const Point& _a, const Point& _b) : a(_a), b(_b) {} explicit operator Lines() const { Lines lines; lines.emplace_back(*this); return lines; } void scale(double factor) { this->a *= factor; this->b *= factor; } - void translate(double x, double y) { Vector v(x, y); this->a += v; this->b += v; } + void translate(const Point &v) { this->a += v; this->b += v; } + void translate(double x, double y) { this->translate(Point(x, y)); } void rotate(double angle, const Point ¢er) { this->a.rotate(angle, center); this->b.rotate(angle, center); } void reverse() { std::swap(this->a, this->b); } double length() const { return (b - a).cast().norm(); } diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index dac4d5877e..3b0c543242 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -61,18 +61,14 @@ inline int64_t cross2(const Vec2i64 &v1, const Vec2i64 &v2) { return v1(0) * v2( inline float cross2(const Vec2f &v1, const Vec2f &v2) { return v1(0) * v2(1) - v1(1) * v2(0); } inline double cross2(const Vec2d &v1, const Vec2d &v2) { return v1(0) * v2(1) - v1(1) * v2(0); } -template Eigen::Matrix -to_2d(const Eigen::Matrix &ptN) { return {ptN(0), ptN(1)}; } +template +inline Eigen::Matrix perp(const Eigen::MatrixBase> &v) { return Eigen::Matrix(- v.y(), v.x()); } -//inline Vec2i32 to_2d(const Vec3i32 &pt3) { return Vec2i32(pt3(0), pt3(1)); } -//inline Vec2i64 to_2d(const Vec3i64 &pt3) { return Vec2i64(pt3(0), pt3(1)); } -//inline Vec2f to_2d(const Vec3f &pt3) { return Vec2f (pt3(0), pt3(1)); } -//inline Vec2d to_2d(const Vec3d &pt3) { return Vec2d (pt3(0), pt3(1)); } +template +Eigen::Matrix to_2d(const Eigen::MatrixBase> &ptN) { return { ptN(0), ptN(1) }; } -inline Vec3d to_3d(const Vec2d &v, double z) { return Vec3d(v(0), v(1), z); } -inline Vec3f to_3d(const Vec2f &v, float z) { return Vec3f(v(0), v(1), z); } -inline Vec3i64 to_3d(const Vec2i64 &v, float z) { return Vec3i64(int64_t(v(0)), int64_t(v(1)), int64_t(z)); } -inline Vec3crd to_3d(const Vec3crd &p, coord_t z) { return Vec3crd(p(0), p(1), z); } +template +Eigen::Matrix to_3d(const Eigen::MatrixBase> & pt, const T z) { return { pt(0), pt(1), z }; } inline Vec2d unscale(coord_t x, coord_t y) { return Vec2d(unscale(x), unscale(y)); } inline Vec2d unscale(const Vec2crd &pt) { return Vec2d(unscale(pt(0)), unscale(pt(1))); }