Fix of [2.3.0-alpha4] Crash - several models cause crash when slicing #5208

Fixed some issues in internal anchors of the Adaptive Cubic infill.
The ugly and dangerous implicit casting operators in Line, MultiPoint,
Polyline and Polygon were made explicit.
This commit is contained in:
Vojtech Bubnik 2020-11-24 16:00:46 +01:00
parent 10c41290fd
commit 62bdc192d8
25 changed files with 237 additions and 110 deletions

View file

@ -37,7 +37,8 @@ struct SurfaceFillParams
bool dont_adjust = false;
// Length of the infill anchor along the perimeter line.
// 1000mm is roughly the maximum length line that fits into a 32bit coord_t.
float anchor_length = 1000.f;
float anchor_length = 1000.f;
float anchor_length_max = 1000.f;
// width, height of extrusion, nozzle diameter, is bridge
// For the output, for fill generator.
@ -68,6 +69,7 @@ struct SurfaceFillParams
RETURN_COMPARE_NON_EQUAL(density);
RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_adjust);
RETURN_COMPARE_NON_EQUAL(anchor_length);
RETURN_COMPARE_NON_EQUAL(anchor_length_max);
RETURN_COMPARE_NON_EQUAL(flow.width);
RETURN_COMPARE_NON_EQUAL(flow.height);
RETURN_COMPARE_NON_EQUAL(flow.nozzle_diameter);
@ -85,7 +87,8 @@ struct SurfaceFillParams
this->angle == rhs.angle &&
this->density == rhs.density &&
this->dont_adjust == rhs.dont_adjust &&
this->anchor_length == rhs.anchor_length &&
this->anchor_length == rhs.anchor_length &&
this->anchor_length_max == rhs.anchor_length_max &&
this->flow == rhs.flow &&
this->extrusion_role == rhs.extrusion_role;
}
@ -171,8 +174,12 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
// Anchor a sparse infill to inner perimeters with the following anchor length:
params.anchor_length = float(region_config.infill_anchor);
if (region_config.infill_anchor.percent)
params.anchor_length *= 0.01 * params.spacing;
params.anchor_length = float(params.anchor_length * 0.01 * params.spacing);
params.anchor_length_max = float(region_config.infill_anchor_max);
if (region_config.infill_anchor_max.percent)
params.anchor_length_max = float(params.anchor_length_max * 0.01 * params.spacing);
}
params.anchor_length = std::min(params.anchor_length, params.anchor_length_max);
auto it_params = set_surface_params.find(params);
if (it_params == set_surface_params.end())
@ -376,9 +383,10 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
// apply half spacing using this flow's own spacing and generate infill
FillParams params;
params.density = float(0.01 * surface_fill.params.density);
params.dont_adjust = surface_fill.params.dont_adjust; // false
params.anchor_length = surface_fill.params.anchor_length;
params.density = float(0.01 * surface_fill.params.density);
params.dont_adjust = surface_fill.params.dont_adjust; // false
params.anchor_length = surface_fill.params.anchor_length;
params.anchor_length_max = surface_fill.params.anchor_length_max;
for (ExPolygon &expoly : surface_fill.expolygons) {
// Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon.

View file

@ -667,9 +667,26 @@ 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, double scaled_trim_distance,
const coordf_t hook_length, double scaled_trim_distance,
const rtree_t &rtree, const Lines &lines_src)
{
if (hook_length < SCALED_EPSILON)
// Ignore open hooks.
return;
#ifndef NDEBUG
{
const Vec2d v = (intersection.closest_line->b - intersection.closest_line->a).cast<double>();
const Vec2d va = (intersection.intersect_point - intersection.closest_line->a).cast<double>();
const double l2 = v.squaredNorm(); // avoid a sqrt
assert(l2 > 0.);
const double t = va.dot(v) / l2;
assert(t > 0. && t < 1.);
const double d = (t * v - va).norm();
assert(d < 1000.);
}
#endif // NDEBUG
// Trim the hook start by the infill line it will connect to.
Point hook_start;
bool intersection_found = intersection.intersect_line->intersection(
@ -700,7 +717,7 @@ static void add_hook(
const std::vector<std::pair<rtree_segment_t, size_t>> &hook_intersections,
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.
auto max_length = double(hook_length);
auto max_length = hook_length;
auto update_max_length = [&max_length](double d) {
if (d < max_length)
max_length = d;
@ -757,15 +774,32 @@ static void add_hook(
}
}
static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &boundary, const double spacing, const int hook_length)
#ifndef NDEBUG
bool validate_intersection_t_joint(const Intersection &intersection)
{
const Vec2d v = (intersection.closest_line->b - intersection.closest_line->a).cast<double>();
const Vec2d va = (intersection.intersect_point - intersection.closest_line->a).cast<double>();
const double l2 = v.squaredNorm(); // avoid a sqrt
assert(l2 > 0.);
const double t = va.dot(v);
assert(t > SCALED_EPSILON && t < l2 - SCALED_EPSILON);
const double d = ((t / l2) * v - va).norm();
assert(d < 1000.);
return true;
}
bool validate_intersections(const std::vector<Intersection> &intersections)
{
for (const Intersection& intersection : intersections)
assert(validate_intersection_t_joint(intersection));
return true;
}
#endif // NDEBUG
static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &boundary, const double spacing, const coordf_t hook_length, const coordf_t hook_length_max)
{
rtree_t rtree;
size_t poly_idx = 0;
Lines lines_src;
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 }; });
// 19% overlap, slightly lower than the allowed overlap in Fill::connect_infill()
const float scaled_offset = float(scale_(spacing) * 0.81);
// 25% overlap
@ -814,16 +848,19 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b
}
return std::make_pair(static_cast<Polyline*>(nullptr), false);
};
auto collinear_front = collinear_segment(poly.points.front(), poly.points.back(), &poly);
auto collinear_front = collinear_segment(poly.points.front(), poly.points.back(), &poly);
auto collinear_back = collinear_segment(poly.points.back(), poly.points.front(), &poly);
assert(! collinear_front.first || ! collinear_back.first || collinear_front.first != collinear_back.first);
if (collinear_front.first) {
Polyline &other = *collinear_front.first;
assert(&other != &poly);
poly.points.front() = collinear_front.second ? other.points.back() : other.points.front();
other.points.clear();
}
auto collinear_back = collinear_segment(poly.points.back(), poly.points.front(), &poly);
if (collinear_back.first) {
Polyline &other = *collinear_front.first;
poly.points.back() = collinear_front.second ? other.points.back() : other.points.front();
Polyline &other = *collinear_back.first;
assert(&other != &poly);
poly.points.back() = collinear_back.second ? other.points.back() : other.points.front();
other.points.clear();
}
}
@ -831,6 +868,12 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b
}
}
// Convert input polylines to lines_src after the colinear segments were merged.
Lines lines_src;
lines_src.reserve(lines.size());
std::transform(lines.begin(), lines.end(), std::back_inserter(lines_src), [](const Polyline &pl) {
return pl.empty() ? Line(Point(0, 0), Point(0, 0)) : Line(pl.points.front(), pl.points.back()); });
sort_remove_duplicates(lines_touching_at_endpoints);
std::vector<Intersection> intersections;
@ -854,23 +897,38 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b
// Find the nearest line from the start point of the line.
std::optional<size_t> tjoint_front, tjoint_back;
{
auto has_tjoint = [&closest, line_idx, &rtree, &lines](const Point &pt) {
auto filter_itself = [line_idx](const auto &item) { return item.second != line_idx; };
auto has_tjoint = [&closest, line_idx, &rtree, &lines, &lines_src](const Point &pt) {
auto filter_t_joint = [line_idx, &lines_src, pt](const auto &item) {
if (item.second != line_idx) {
// Verify that the point projects onto the line.
const Line &line = lines_src[item.second];
const Vec2d v = (line.b - line.a).cast<double>();
const Vec2d va = (pt - line.a).cast<double>();
const double l2 = v.squaredNorm(); // avoid a sqrt
if (l2 > 0.) {
const double t = va.dot(v);
return t > SCALED_EPSILON && t < l2 - SCALED_EPSILON;
}
}
return false;
};
closest.clear();
rtree.query(bgi::nearest(mk_rtree_point(pt), 1) && bgi::satisfies(filter_itself), std::back_inserter(closest));
const Polyline &pl = lines[closest.front().second];
rtree.query(bgi::nearest(mk_rtree_point(pt), 1) && bgi::satisfies(filter_t_joint), std::back_inserter(closest));
std::optional<size_t> out;
if (pl.points.empty()) {
// The closest infill line was already dropped as it was too short.
// Such an infill line should not make a T-joint anyways.
#if 0 // #ifndef NDEBUG
const auto &seg = closest.front().first;
struct Linef { Vec2d a; Vec2d b; };
Linef l { { bg::get<0, 0>(seg), bg::get<0, 1>(seg) }, { bg::get<1, 0>(seg), bg::get<1, 1>(seg) } };
assert(line_alg::distance_to_squared(l, Vec2d(pt.cast<double>())) > 1000 * 1000);
#endif // NDEBUG
} else if (((Line)pl).distance_to_squared(pt) <= 1000 * 1000)
out = closest.front().second;
if (! closest.empty()) {
const Polyline &pl = lines[closest.front().second];
if (pl.points.empty()) {
// The closest infill line was already dropped as it was too short.
// Such an infill line should not make a T-joint anyways.
#if 0 // #ifndef NDEBUG
const auto &seg = closest.front().first;
struct Linef { Vec2d a; Vec2d b; };
Linef l { { bg::get<0, 0>(seg), bg::get<0, 1>(seg) }, { bg::get<1, 0>(seg), bg::get<1, 1>(seg) } };
assert(line_alg::distance_to_squared(l, Vec2d(pt.cast<double>())) > 1000 * 1000);
#endif // NDEBUG
} else if (((Line)pl).distance_to_squared(pt) <= 1000 * 1000)
out = closest.front().second;
}
return out;
};
// Refuse to create a T-joint if the infill lines touch at their ends.
@ -912,12 +970,16 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b
// A shorter line than spacing could produce a degenerate polyline.
line.points.clear();
} else if (anchor) {
if (tjoint_front)
if (tjoint_front) {
// T-joint of line's front point with the 'closest' line.
intersections.emplace_back(lines_src[*tjoint_front], lines_src[line_idx], &line, front_point, true);
if (tjoint_back)
assert(validate_intersection_t_joint(intersections.back()));
}
if (tjoint_back) {
// T-joint of line's back point with the 'closest' line.
intersections.emplace_back(lines_src[*tjoint_back], lines_src[line_idx], &line, back_point, false);
assert(validate_intersection_t_joint(intersections.back()));
}
} else {
if (tjoint_front)
// T joint at the front at a 60 degree angle, the line is very short.
@ -940,6 +1002,7 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b
++ it;
}
}
assert(validate_intersections(intersections));
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
static int iRun = 0;
@ -1106,7 +1169,7 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b
}
Points &first_points = first_i.intersect_pl->points;
Points &second_points = nearest_i.intersect_pl->points;
could_connect &= (nearest_i_point - first_i_point).cast<double>().squaredNorm() <= Slic3r::sqr(3. * hook_length);
could_connect &= (nearest_i_point - first_i_point).cast<double>().squaredNorm() <= Slic3r::sqr(hook_length_max);
if (could_connect) {
// Both intersections are so close that their polylines can be connected.
// Verify that no other infill line intersects this anchor line.
@ -1219,7 +1282,7 @@ bool has_no_collinear_lines(const Polylines &polylines)
const Point* operator()(const LineEnd &pt) const { return &pt.point(); }
};
typedef ClosestPointInRadiusLookup<LineEnd, LineEndAccessor> ClosestPointLookupType;
ClosestPointLookupType closest_end_point_lookup(1001. * sqrt(2.));
ClosestPointLookupType closest_end_point_lookup(coord_t(1001. * sqrt(2.)));
for (const Polyline& pl : polylines) {
// assert(pl.points.size() == 2);
auto line_start = LineEnd(&pl, true);
@ -1321,9 +1384,10 @@ void Filler::_fill_surface_single(
}
#endif /* ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT */
const auto hook_length = coord_t(std::min(scale_(this->spacing * 5), scale_(params.anchor_length)));
const auto hook_length = coordf_t(std::min<float>(std::numeric_limits<coord_t>::max(), scale_(params.anchor_length)));
const auto hook_length_max = coordf_t(std::min<float>(std::numeric_limits<coord_t>::max(), scale_(params.anchor_length_max)));
Polylines all_polylines_with_hooks = all_polylines.size() > 1 ? connect_lines_using_hooks(std::move(all_polylines), expolygon, this->spacing, hook_length) : std::move(all_polylines);
Polylines all_polylines_with_hooks = all_polylines.size() > 1 ? connect_lines_using_hooks(std::move(all_polylines), expolygon, this->spacing, hook_length, hook_length_max) : std::move(all_polylines);
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
{

View file

@ -200,10 +200,10 @@ struct ContourIntersectionPoint {
// Could extrude a complete segment from this to this->prev_on_contour.
bool could_connect_prev() const throw()
{ return ! this->consumed && this->prev_on_contour && ! this->prev_on_contour->consumed && ! this->prev_trimmed && ! this->prev_on_contour->next_trimmed; }
{ return ! this->consumed && this->prev_on_contour != this && ! this->prev_on_contour->consumed && ! this->prev_trimmed && ! this->prev_on_contour->next_trimmed; }
// Could extrude a complete segment from this to this->next_on_contour.
bool could_connect_next() const throw()
{ return ! this->consumed && this->next_on_contour && ! this->next_on_contour->consumed && ! this->next_trimmed && ! this->next_on_contour->prev_trimmed; }
{ return ! this->consumed && this->next_on_contour != this && ! this->next_on_contour->consumed && ! this->next_trimmed && ! this->next_on_contour->prev_trimmed; }
};
// Distance from param1 to param2 when going counter-clockwise.
@ -390,7 +390,12 @@ static void take(Polyline &pl1, const Polyline &pl2, const Points &contour, size
static void take(Polyline &pl1, const Polyline &pl2, const Points &contour, ContourIntersectionPoint *cp_start, ContourIntersectionPoint *cp_end, bool clockwise)
{
assert(cp_start->prev_on_contour != nullptr);
assert(cp_start->next_on_contour != nullptr);
assert(cp_end ->prev_on_contour != nullptr);
assert(cp_end ->next_on_contour != nullptr);
assert(cp_start != cp_end);
take(pl1, pl2, contour, cp_start->point_idx, cp_end->point_idx, clockwise);
// Mark the contour segments in between cp_start and cp_end as consumed.
@ -410,7 +415,12 @@ static void take_limited(
ContourIntersectionPoint *cp_start, ContourIntersectionPoint *cp_end, bool clockwise, float take_max_length, float line_half_width)
{
#ifndef NDEBUG
assert(cp_start != cp_end);
// This is a valid case, where a single infill line connect to two different contours (outer contour + hole or two holes).
// assert(cp_start != cp_end);
assert(cp_start->prev_on_contour != nullptr);
assert(cp_start->next_on_contour != nullptr);
assert(cp_end ->prev_on_contour != nullptr);
assert(cp_end ->next_on_contour != nullptr);
assert(pl1.size() >= 2);
assert(contour.size() + 1 == params.size());
#endif /* NDEBUG */
@ -438,8 +448,18 @@ static void take_limited(
float length = params.back();
float length_to_go = take_max_length;
cp_start->consumed = true;
if (clockwise) {
if (cp_start == cp_end) {
length_to_go = std::max(0.f, std::min(length_to_go, length - line_half_width));
length_to_go = std::min(length_to_go, clockwise ? cp_start->contour_not_taken_length_prev : cp_start->contour_not_taken_length_next);
cp_start->consume_prev();
cp_start->consume_next();
if (length_to_go > SCALED_EPSILON)
clockwise ?
take_cw_limited (pl1, contour, params, cp_start->point_idx, cp_start->point_idx, length_to_go) :
take_ccw_limited(pl1, contour, params, cp_start->point_idx, cp_start->point_idx, length_to_go);
} else if (clockwise) {
// Going clockwise from cp_start to cp_end.
assert(cp_start != cp_end);
for (ContourIntersectionPoint *cp = cp_start; cp != cp_end; cp = cp->prev_on_contour) {
// Length of the segment from cp to cp->prev_on_contour.
float l = closed_contour_distance_cw(cp->param, cp->prev_on_contour->param, length);
@ -461,6 +481,7 @@ static void take_limited(
}
}
} else {
assert(cp_start != cp_end);
for (ContourIntersectionPoint *cp = cp_start; cp != cp_end; cp = cp->next_on_contour) {
float l = closed_contour_distance_ccw(cp->param, cp->next_on_contour->param, length);
length_to_go = std::min(length_to_go, cp->contour_not_taken_length_next);
@ -869,6 +890,10 @@ void mark_boundary_segments_touching_infill(
for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second; ++ it_contour_and_segment) {
// End points of the line segment and their vector.
auto segment = this->grid.segment(*it_contour_and_segment);
std::vector<ContourIntersectionPoint*> &intersections = boundary_intersections[it_contour_and_segment->first];
if (intersections.empty())
// There is no infil line touching this contour, thus effort will be saved to calculate overlap with other infill lines.
continue;
const Vec2d seg_pt1 = segment.first.cast<double>();
const Vec2d seg_pt2 = segment.second.cast<double>();
std::pair<double, double> interval;
@ -892,20 +917,23 @@ void mark_boundary_segments_touching_infill(
const float param_overlap1 = param_seg_pt1 + interval.first;
const float param_overlap2 = param_seg_pt1 + interval.second;
// 2) Find the ContourIntersectionPoints before param_overlap1 and after param_overlap2.
std::vector<ContourIntersectionPoint*> &intersections = boundary_intersections[it_contour_and_segment->first];
// Find the span of ContourIntersectionPoints, that is trimmed by the interval (param_overlap1, param_overlap2).
ContourIntersectionPoint *ip_low, *ip_high;
{
if (intersections.size() == 1) {
// Only a single infill line touches this contour.
ip_low = ip_high = intersections.front();
} else {
assert(intersections.size() > 1);
auto it_low = Slic3r::lower_bound_by_predicate(intersections.begin(), intersections.end(), [param_overlap1](const ContourIntersectionPoint *l) { return l->param < param_overlap1; });
auto it_high = Slic3r::lower_bound_by_predicate(intersections.begin(), intersections.end(), [param_overlap2](const ContourIntersectionPoint *l) { return l->param < param_overlap2; });
ip_low = it_low == intersections.end() ? intersections.front() : *it_low;
ip_high = it_high == intersections.end() ? intersections.front() : *it_high;
if (ip_low->param != param_overlap1)
ip_low = ip_low->prev_on_contour;
assert(ip_low != ip_high);
// Verify that the interval (param_overlap1, param_overlap2) is inside the interval (ip_low->param, ip_high->param).
assert(cyclic_interval_inside_interval(ip_low->param, ip_high->param, param_overlap1, param_overlap2, contour_length));
}
assert(ip_low != ip_high);
// Verify that the interval (param_overlap1, param_overlap2) is inside the interval (ip_low->param, ip_high->param).
assert(cyclic_interval_inside_interval(ip_low->param, ip_high->param, param_overlap1, param_overlap2, contour_length));
assert(validate_boundary_intersections(boundary_intersections));
// Mark all ContourIntersectionPoints between ip_low and ip_high as consumed.
if (ip_low->next_on_contour != ip_high)
@ -1068,8 +1096,11 @@ void Fill::connect_infill(Polylines &&infill_ordered, const Polygons &boundary_s
void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Polygon*> &boundary_src, const BoundingBox &bbox, Polylines &polylines_out, const double spacing, const FillParams &params)
{
assert(! infill_ordered.empty());
assert(params.anchor_length >= 0.01f);
const auto anchor_length = float(scale_(params.anchor_length));
assert(params.anchor_length >= 0.f);
assert(params.anchor_length_max >= 0.01f);
assert(params.anchor_length_max >= params.anchor_length);
const auto anchor_length = float(scale_(params.anchor_length));
const auto anchor_length_max = float(scale_(params.anchor_length_max));
#if 0
append(polylines_out, infill_ordered);
@ -1097,7 +1128,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
EdgeGrid::Grid::ClosestPointResult cp = grid.closest_point(*pt, SCALED_EPSILON);
if (cp.valid()) {
// The infill end point shall lie on the contour.
assert(cp.distance < 2.);
assert(cp.distance <= 3.);
intersection_points.emplace_back(cp, (&pl - infill_ordered.data()) * 2 + (pt == &pl.points.front() ? 0 : 1));
}
}
@ -1154,7 +1185,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
//add new point here
contour_dst.emplace_back(pt);
}
if (pprev != pfirst) {
if (pfirst) {
pprev->next_on_contour = pfirst;
pfirst->prev_on_contour = pprev;
}
@ -1170,10 +1201,15 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
ip->param = contour_params[ip->point_idx];
// and measure distance to the previous and next intersection point.
const float contour_length = contour_params.back();
for (ContourIntersectionPoint *ip : contour_intersection_points) {
ip->contour_not_taken_length_prev = closed_contour_distance_ccw(ip->prev_on_contour->param, ip->param, contour_length);
ip->contour_not_taken_length_next = closed_contour_distance_ccw(ip->param, ip->next_on_contour->param, contour_length);
}
for (ContourIntersectionPoint *ip : contour_intersection_points)
if (ip->next_on_contour == ip) {
assert(ip->prev_on_contour == ip);
ip->contour_not_taken_length_prev = ip->contour_not_taken_length_next = contour_length;
} else {
assert(ip->prev_on_contour != ip);
ip->contour_not_taken_length_prev = closed_contour_distance_ccw(ip->prev_on_contour->param, ip->param, contour_length);
ip->contour_not_taken_length_next = closed_contour_distance_ccw(ip->param, ip->next_on_contour->param, contour_length);
}
}
assert(boundary.size() == boundary_src.size());
@ -1277,7 +1313,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
idx_first = get_and_update_merged_with(idx_first);
assert(idx_first < idx_second);
assert(idx_second == merged_with[idx_second]);
if (could_connect && length < anchor_length * 2.5) {
if (could_connect && length < anchor_length_max) {
// Take the complete contour.
// Connect the two polygons using the boundary contour.
take(infill_ordered[idx_first], infill_ordered[idx_second], boundary[cp1->contour_idx], cp1, cp2, connection_cost.reversed);
@ -1299,10 +1335,11 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
std::vector<Arc> arches;
arches.reserve(map_infill_end_point_to_boundary.size());
for (ContourIntersectionPoint &cp : map_infill_end_point_to_boundary)
if (! cp.contour_idx != boundary_idx_unconnected && cp.next_on_contour != &cp && cp.could_connect_next())
if (cp.contour_idx != boundary_idx_unconnected && cp.next_on_contour != &cp && cp.could_connect_next())
arches.push_back({ &cp, path_length_along_contour_ccw(&cp, cp.next_on_contour, boundary_params[cp.contour_idx].back()) });
std::sort(arches.begin(), arches.end(), [](const auto &l, const auto &r) { return l.arc_length < r.arc_length; });
//FIXME improve the Traveling Salesman problem with 2-opt and 3-opt local optimization.
for (Arc &arc : arches)
if (! arc.intersection->consumed && ! arc.intersection->next_on_contour->consumed) {
// Indices of the polylines to be connected by a perimeter segment.
@ -1315,7 +1352,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
if (polyline_idx1 != polyline_idx2) {
Polyline &polyline1 = infill_ordered[polyline_idx1];
Polyline &polyline2 = infill_ordered[polyline_idx2];
if (arc.arc_length < anchor_length * 2.5) {
if (arc.arc_length < anchor_length_max) {
// Not closing a loop, connecting the lines.
assert(contour[cp1->point_idx] == polyline1.points.front() || contour[cp1->point_idx] == polyline1.points.back());
if (contour[cp1->point_idx] == polyline1.points.front())
@ -1333,7 +1370,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
polyline2.points.clear();
merged_with[polyline_idx2] = merged_with[polyline_idx1];
}
} else {
} else if (anchor_length > SCALED_EPSILON) {
// Move along the perimeter, but don't take the whole arc.
take_limited(polyline1, contour, contour_params, cp1, cp2, false, anchor_length, line_half_width);
take_limited(polyline2, contour, contour_params, cp2, cp1, true, anchor_length, line_half_width);
@ -1360,7 +1397,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
assert(contour[contour_point.point_idx] == polyline.points.front() || contour[contour_point.point_idx] == polyline.points.back());
bool connected = false;
for (float l : { std::min(lprev, lnext), std::max(lprev, lnext) }) {
if (l == std::numeric_limits<float>::max() || l > anchor_length * 2.5)
if (l == std::numeric_limits<float>::max() || l > anchor_length_max)
break;
// Take the complete contour.
bool reversed = l == lprev;
@ -1392,7 +1429,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
connected = true;
break;
}
if (! connected) {
if (! connected && anchor_length > SCALED_EPSILON) {
// Which to take? One could optimize for:
// 1) Shortest path
// 2) Hook length

View file

@ -34,14 +34,15 @@ struct FillParams
{
bool full_infill() const { return density > 0.9999f; }
// Don't connect the fill lines around the inner perimeter.
bool dont_connect() const { return anchor_length < 0.05f; }
bool dont_connect() const { return anchor_length_max < 0.05f; }
// Fill density, fraction in <0, 1>
float density { 0.f };
// Length of an infill anchor along the perimeter.
// 1000mm is roughly the maximum length line that fits into a 32bit coord_t.
float anchor_length { 1000.f };
float anchor_length { 1000.f };
float anchor_length_max { 1000.f };
// Don't adjust spacing to fill the space evenly.
bool dont_adjust { true };

View file

@ -39,7 +39,7 @@ void FillConcentric::_fill_surface_single(
size_t iPathFirst = polylines_out.size();
Point last_pos(0, 0);
for (const Polygon &loop : loops) {
polylines_out.push_back(loop.split_at_index(last_pos.nearest_point_index(loop)));
polylines_out.push_back(loop.split_at_index(last_pos.nearest_point_index(loop.points)));
last_pos = polylines_out.back().last_point();
}