FillAdaptive:

1) More accurate trimming of an anchor with another infill line
   or by another anchor line.
2) Trimming of very short infill lines, which are not anchored,
   by another infill lines.
This commit is contained in:
Vojtech Bubnik 2020-11-11 16:49:11 +01:00
parent 26836db629
commit 4d102ac8ca
3 changed files with 83 additions and 81 deletions

View file

@ -639,10 +639,8 @@ static inline Intersection* get_nearest_intersection(std::vector<std::pair<Inter
// translated in the direction of the intersection line (intersection.intersect_line). // translated in the direction of the intersection line (intersection.intersect_line).
static Line create_offset_line(Line offset_line, const Intersection &intersection, const double scaled_offset) static Line create_offset_line(Line offset_line, const Intersection &intersection, const double scaled_offset)
{ {
Vec2d dir = intersection.closest_line->vector().cast<double>().normalized();
// 50% overlap of the extrusion lines to achieve strong bonding. // 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((perp(intersection.closest_line->vector().cast<double>().normalized()) * (intersection.left ? scaled_offset : - scaled_offset)).cast<coord_t>());
offset_line.translate(offset_vector.x(), offset_vector.y());
// Extend the line by a small value to guarantee a collision with adjacent lines // 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) offset_line.extend(coord_t(scaled_offset * 1.16)); // / cos(PI/6)
return offset_line; 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 // 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. // Trim the hook start by the infill line it will connect to.
Point hook_start; Point hook_start;
@ -680,7 +681,9 @@ static void add_hook(const Intersection &intersection, const double scaled_offse
std::optional<Line> other_hook = intersection.other_hook(); std::optional<Line> other_hook = intersection.other_hook();
Vec2d hook_vector_norm = intersection.closest_line->vector().cast<double>().normalized(); Vec2d hook_vector_norm = intersection.closest_line->vector().cast<double>().normalized();
Vector hook_vector = (hook_length * hook_vector_norm).cast<coord_t>(); // 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<coord_t>();
Line hook_forward(hook_start, hook_start + hook_vector); 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(); }; 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; Point self_intersection_point;
bool self_intersection = other_hook && other_hook->intersection(hook_forward, &self_intersection_point); bool self_intersection = other_hook && other_hook->intersection(hook_forward, &self_intersection_point);
Point hook_end; // Find closest intersection of a line segment starting with pt pointing in dir
if (hook_intersections.empty() && ! self_intersection) { // with any of the hook_intersections, returns Euclidian distance.
// The hook is not limited by another infill line. Extrude it in its full length. // dir is normalized.
hook_end = hook_forward.b; auto max_hook_length = [hook_length, scaled_trim_distance, &lines_src](
} else { const Vec2d &pt, const Vec2d &dir,
const std::vector<std::pair<rtree_segment_t, size_t>> &hook_intersections,
// Find closest intersection of a line segment starting with pt pointing in dir bool self_intersection, const std::optional<Line> &self_intersection_line, const Point &self_intersection_point) {
// with any of the hook_intersections, returns Euclidian distance. // No hook is longer than hook_length, there shouldn't be any intersection closer than that.
// dir is normalized. auto max_length = double(hook_length);
auto max_hook_length = [hook_length, scaled_offset, &lines_src]( auto update_max_length = [&max_length](double d) {
const Vec2d &pt, const Vec2d &dir, if (d < max_length)
const std::vector<std::pair<rtree_segment_t, size_t>> &hook_intersections, max_length = d;
bool self_intersection, const std::optional<Line> &self_intersection_line, const Point &self_intersection_point) { };
// No hook is longer than hook_length, there shouldn't be any intersection closer than that. // Shift the trimming point away from the colliding thick line.
auto max_length = double(hook_length); auto shift_from_thick_line = [&dir, scaled_trim_distance](const Vec2d& dir2) {
auto update_max_length = [&max_length](double d) { return scaled_trim_distance * std::abs(cross2(dir, dir2.normalized()));
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<double>() + shift_from_thick_line(self_intersection_line.value().vector().cast<double>()) - pt).norm();
max_length = std::min(max_length, t);
}
return max_length;
}; };
// There is not enough space for the full hook length, try the opposite direction. for (const auto &hook_intersection : hook_intersections) {
Vec2d hook_startf = hook_start.cast<double>(); const rtree_segment_t &segment = hook_intersection.first;
double hook_forward_max_length = max_hook_length(hook_startf, hook_vector_norm, hook_intersections, self_intersection, other_hook, self_intersection_point); // 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<double>() - pt).dot(dir) - shift_from_thick_line(self_intersection_line.value().vector().cast<double>());
max_length = std::min(max_length, t);
}
return std::max(0., max_length);
};
Vec2d hook_startf = hook_start.cast<double>();
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(); hook_intersections.clear();
Line hook_backward(hook_start, hook_start - hook_vector); 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)); 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); self_intersection = other_hook && other_hook->intersection(hook_backward, &self_intersection_point);
hook_backward_max_length = max_hook_length(hook_startf, - hook_vector_norm, hook_intersections, self_intersection, other_hook, 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<coord_t>();
}
} }
// 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<coord_t>();
Points &pl = intersection.intersect_pl->points; Points &pl = intersection.intersect_pl->points;
if (intersection.front) { if (intersection.front) {
pl.front() = hook_start; pl.front() = hook_start;
@ -774,6 +767,9 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b
lines_src.reserve(lines.size()); 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 }; }); 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. // Keeping the vector of closest points outside the loop, so the vector does not need to be reallocated.
std::vector<std::pair<rtree_segment_t, size_t>> closest; std::vector<std::pair<rtree_segment_t, size_t>> closest;
// Pairs of lines touching at one end point. The pair is sorted to make the end point connection test symmetric. // 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); sort_remove_duplicates(lines_touching_at_endpoints);
const float scaled_offset = float(scale_(spacing) * 0.7); // 30% overlap
std::vector<Intersection> intersections; std::vector<Intersection> intersections;
{ {
// Minimum lenght of an infill line to anchor. Very short lines cannot be trimmed from both sides, // 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 // it does not help to anchor extremely short infill lines, it consumes too much plastic while not adding
// to the object rigidity. // 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_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_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; 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()) if (line.points.empty())
continue; continue;
const Point &front_point = line.points.front(); Point &front_point = line.points.front();
const Point &back_point = line.points.back(); Point &back_point = line.points.back();
// Find the nearest line from the start point of the line. // Find the nearest line from the start point of the line.
std::optional<size_t> tjoint_front, tjoint_back; std::optional<size_t> tjoint_front, tjoint_back;
@ -922,6 +917,15 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b
if (tjoint_back) if (tjoint_back)
// T-joint of line's back point with the 'closest' line. // 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); 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<double>().normalized()).cast<coord_t>();
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<double>().normalized()).cast<coord_t>();
} }
} }
} }
@ -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 may have alread been merged with another polyline, even with this one.
pl2 = &lines[update_merged_polyline_idx(pl2 - lines.data())]; pl2 = &lines[update_merged_polyline_idx(pl2 - lines.data())];
assert(pl1 <= pl2); assert(pl1 <= pl2);
// Avoid closing a loop. // Avoid closing a loop, ignore dropped infill lines.
if (pl1 != pl2) { if (pl1 != pl2 && ! pl1->points.empty() && ! pl2->points.empty()) {
// Merge the polylines. // Merge the polylines.
assert(pl1 < pl2); assert(pl1 < pl2);
assert(pl1->points.size() >= 2); 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 #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 }); 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 #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 #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 }); export_infill_lines_to_svg(boundary, lines, debug_out_path("FillAdaptive-add_hook0-pre-%d-%d.svg", iRun, iStep), { first_i.intersect_point });
++ iStep; ++ iStep;
@ -1138,6 +1142,7 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b
} else { } else {
// Both intersections are on different polylines // Both intersections are on different polylines
Line l(first_i_point, nearest_i_point); Line l(first_i_point, nearest_i_point);
l.translate((perp(first_i.closest_line->vector().cast<double>().normalized()) * (first_i.left ? scaled_trim_distance : - scaled_trim_distance)).cast<coord_t>());
Point pt_start, pt_end; 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_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); 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 #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 }); 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 #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 #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 }); 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 #endif // ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT

View file

@ -54,7 +54,8 @@ public:
Line(const Point& _a, const Point& _b) : a(_a), b(_b) {} Line(const Point& _a, const Point& _b) : a(_a), b(_b) {}
explicit operator Lines() const { Lines lines; lines.emplace_back(*this); return lines; } explicit operator Lines() const { Lines lines; lines.emplace_back(*this); return lines; }
void scale(double factor) { this->a *= factor; this->b *= factor; } 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 &center) { this->a.rotate(angle, center); this->b.rotate(angle, center); } void rotate(double angle, const Point &center) { this->a.rotate(angle, center); this->b.rotate(angle, center); }
void reverse() { std::swap(this->a, this->b); } void reverse() { std::swap(this->a, this->b); }
double length() const { return (b - a).cast<double>().norm(); } double length() const { return (b - a).cast<double>().norm(); }

View file

@ -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 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); } inline double cross2(const Vec2d &v1, const Vec2d &v2) { return v1(0) * v2(1) - v1(1) * v2(0); }
template<class T, int N> Eigen::Matrix<T, 2, 1, Eigen::DontAlign> template<typename T, int Options>
to_2d(const Eigen::Matrix<T, N, 1, Eigen::DontAlign> &ptN) { return {ptN(0), ptN(1)}; } inline Eigen::Matrix<T, 2, 1, Eigen::DontAlign> perp(const Eigen::MatrixBase<Eigen::Matrix<T, 2, 1, Options>> &v) { return Eigen::Matrix<T, 2, 1, Eigen::DontAlign>(- v.y(), v.x()); }
//inline Vec2i32 to_2d(const Vec3i32 &pt3) { return Vec2i32(pt3(0), pt3(1)); } template<class T, int N, int Options>
//inline Vec2i64 to_2d(const Vec3i64 &pt3) { return Vec2i64(pt3(0), pt3(1)); } Eigen::Matrix<T, 2, 1, Eigen::DontAlign> to_2d(const Eigen::MatrixBase<Eigen::Matrix<T, N, 1, Options>> &ptN) { return { ptN(0), ptN(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)); }
inline Vec3d to_3d(const Vec2d &v, double z) { return Vec3d(v(0), v(1), z); } template<class T, int Options>
inline Vec3f to_3d(const Vec2f &v, float z) { return Vec3f(v(0), v(1), z); } Eigen::Matrix<T, 3, 1, Eigen::DontAlign> to_3d(const Eigen::MatrixBase<Eigen::Matrix<T, 2, 1, Options>> & pt, const T z) { return { pt(0), pt(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); }
inline Vec2d unscale(coord_t x, coord_t y) { return Vec2d(unscale<double>(x), unscale<double>(y)); } inline Vec2d unscale(coord_t x, coord_t y) { return Vec2d(unscale<double>(x), unscale<double>(y)); }
inline Vec2d unscale(const Vec2crd &pt) { return Vec2d(unscale<double>(pt(0)), unscale<double>(pt(1))); } inline Vec2d unscale(const Vec2crd &pt) { return Vec2d(unscale<double>(pt(0)), unscale<double>(pt(1))); }