Ironing and Monotonous infill - first working implementation.

This commit is contained in:
bubnikv 2020-04-28 17:19:11 +02:00
parent 033548a568
commit ec81de7553
4 changed files with 342 additions and 352 deletions

View file

@ -532,7 +532,8 @@ void Layer::make_ironing()
fill.z = this->print_z;
fill.overlap = 0;
fill_params.density = 1.;
fill_params.dont_connect = true;
// fill_params.dont_connect = true;
fill_params.dont_connect = false;
fill_params.monotonous = true;
for (size_t i = 0; i < by_extruder.size(); ++ i) {

View file

@ -160,19 +160,7 @@ struct SegmentIntersection
enum class LinkQuality : uint8_t {
Invalid,
Valid,
// Valid link, to be followed when extruding.
// Link inside a monotonous region.
ValidMonotonous,
// Valid link, to be possibly followed when extruding.
// Link between two monotonous regions.
ValidNonMonotonous,
// Link from T to end of another contour.
FromT,
// Link from end of one contour to T.
ToT,
// Link from one T to another T, making a letter H.
H,
// Vertical segment
// Valid link, but too long to be followed.
TooLong,
};
@ -231,6 +219,10 @@ struct SegmentIntersection
int left_horizontal() const { return this->has_left_horizontal() ? this->prev_on_contour : -1; }
int right_horizontal() const { return this->has_right_horizontal() ? this->next_on_contour : -1; }
int horizontal(Side side) const { return side == Side::Left ? this->left_horizontal() : this->right_horizontal(); }
LinkQuality horizontal_quality(Side side) const {
assert(this->has_horizontal(side));
return side == Side::Left ? this->prev_on_contour_quality : this->next_on_contour_quality;
}
int left_vertical_up() const { return this->has_left_vertical_up() ? this->prev_on_contour : -1; }
int left_vertical_down() const { return this->has_left_vertical_down() ? this->prev_on_contour : -1; }
@ -251,7 +243,6 @@ struct SegmentIntersection
return this->has_left_vertical_up() ? this->left_vertical_up() : this->right_vertical_up();
}
LinkQuality vertical_up_quality() const {
assert(this->has_left_vertical_up() != this->has_right_vertical_up());
return this->has_left_vertical_up() ? this->prev_on_contour_quality : this->next_on_contour_quality;
}
// Returns -1 if there is no link down.
@ -260,10 +251,10 @@ struct SegmentIntersection
return this->has_left_vertical_down() ? this->left_vertical_down() : this->right_vertical_down();
}
LinkQuality vertical_down_quality() const {
assert(this->has_left_vertical_down() != this->has_right_vertical_down());
return this->has_left_vertical_down() ? this->prev_on_contour_quality : this->next_on_contour_quality;
}
int vertical_outside() const { return this->is_low() ? this->vertical_down() : this->vertical_up(); }
LinkQuality vertical_outside_quality() const { return this->is_low() ? this->vertical_down_quality() : this->vertical_up_quality(); }
// Compare two y intersection points given by rational numbers.
// Note that the rational number is given as pos_p/pos_q, where pos_p is int64 and pos_q is uint32.
@ -463,25 +454,8 @@ static inline int distance_of_segmens(const Polygon &poly, size_t seg1, size_t s
return d;
}
enum IntersectionTypeOtherVLine {
// There is no connection point on the other vertical line.
INTERSECTION_TYPE_OTHER_VLINE_UNDEFINED = -1,
// Connection point on the other vertical segment was found
// and it could be followed.
INTERSECTION_TYPE_OTHER_VLINE_OK = 0,
// The connection segment connects to a middle of a vertical segment.
// Cannot follow.
INTERSECTION_TYPE_OTHER_VLINE_INNER,
// Cannot extend the contor to this intersection point as either the connection segment
// or the succeeding vertical segment were already consumed.
INTERSECTION_TYPE_OTHER_VLINE_CONSUMED,
// Not the first intersection along the contor. This intersection point
// has been preceded by an intersection point along the vertical line.
INTERSECTION_TYPE_OTHER_VLINE_NOT_FIRST,
};
// Find an intersection on a previous line, but return -1, if the connecting segment of a perimeter was already extruded.
static inline IntersectionTypeOtherVLine intersection_type_on_prev_next_vertical_line(
static inline bool intersection_on_prev_next_vertical_line_valid(
const std::vector<SegmentedIntersectionLine> &segs,
size_t iVerticalLine,
size_t iIntersection,
@ -492,10 +466,10 @@ static inline IntersectionTypeOtherVLine intersection_type_on_prev_next_vertical
if (it_this.has_vertical(side))
// Not the first intersection along the contor. This intersection point
// has been preceded by an intersection point along the vertical line.
return INTERSECTION_TYPE_OTHER_VLINE_NOT_FIRST;
return false;
int iIntersectionOther = it_this.horizontal(side);
if (iIntersectionOther == -1)
return INTERSECTION_TYPE_OTHER_VLINE_UNDEFINED;
return false;
assert(side == SegmentIntersection::Side::Right ? (iVerticalLine + 1 < segs.size()) : (iVerticalLine > 0));
const SegmentedIntersectionLine &vline_other = segs[side == SegmentIntersection::Side::Right ? (iVerticalLine + 1) : (iVerticalLine - 1)];
const SegmentIntersection &it_other = vline_other.intersections[iIntersectionOther];
@ -507,52 +481,50 @@ static inline IntersectionTypeOtherVLine intersection_type_on_prev_next_vertical
if (it_other2.is_inner())
// Cannot follow a perimeter segment into the middle of another vertical segment.
// Only perimeter segments connecting to the end of a vertical segment are followed.
return INTERSECTION_TYPE_OTHER_VLINE_INNER;
return false;
assert(it_other.is_low() == it_other2.is_low());
if (it_this.horizontal_quality(side) != SegmentIntersection::LinkQuality::Valid)
return false;
if (side == SegmentIntersection::Side::Right ? it_this.consumed_perimeter_right : it_other.consumed_perimeter_right)
// This perimeter segment was already consumed.
return INTERSECTION_TYPE_OTHER_VLINE_CONSUMED;
return false;
if (it_other.is_low() ? it_other.consumed_vertical_up : vline_other.intersections[iIntersectionOther - 1].consumed_vertical_up)
// This vertical segment was already consumed.
return INTERSECTION_TYPE_OTHER_VLINE_CONSUMED;
return INTERSECTION_TYPE_OTHER_VLINE_OK;
return false;
#if 0
if (it_other.vertical_outside() != -1 && it_other.vertical_outside_quality() == SegmentIntersection::LinkQuality::Valid)
// Landed inside a vertical run. Stop here.
return false;
#endif
return true;
}
static inline IntersectionTypeOtherVLine intersection_type_on_prev_vertical_line(
static inline bool intersection_on_prev_vertical_line_valid(
const std::vector<SegmentedIntersectionLine> &segs,
size_t iVerticalLine,
size_t iIntersection)
{
return intersection_type_on_prev_next_vertical_line(segs, iVerticalLine, iIntersection, SegmentIntersection::Side::Left);
return intersection_on_prev_next_vertical_line_valid(segs, iVerticalLine, iIntersection, SegmentIntersection::Side::Left);
}
static inline IntersectionTypeOtherVLine intersection_type_on_next_vertical_line(
static inline bool intersection_on_next_vertical_line_valid(
const std::vector<SegmentedIntersectionLine> &segs,
size_t iVerticalLine,
size_t iIntersection)
{
return intersection_type_on_prev_next_vertical_line(segs, iVerticalLine, iIntersection, SegmentIntersection::Side::Right);
return intersection_on_prev_next_vertical_line_valid(segs, iVerticalLine, iIntersection, SegmentIntersection::Side::Right);
}
// Measure an Euclidian length of a perimeter segment when going from iIntersection to iIntersection2.
static inline coordf_t measure_perimeter_prev_next_segment_length(
static inline coordf_t measure_perimeter_horizontal_segment_length(
const ExPolygonWithOffset &poly_with_offset,
const std::vector<SegmentedIntersectionLine> &segs,
size_t iVerticalLine,
size_t iIntersection,
size_t iIntersection2,
bool dir_is_next)
size_t iIntersection2)
{
size_t iVerticalLineOther = iVerticalLine;
if (dir_is_next) {
if (++ iVerticalLineOther == segs.size())
// No successive vertical line.
return coordf_t(-1);
} else if (iVerticalLineOther -- == 0) {
// No preceding vertical line.
return coordf_t(-1);
}
size_t iVerticalLineOther = iVerticalLine + 1;
assert(iVerticalLineOther < segs.size());
const SegmentedIntersectionLine &vline = segs[iVerticalLine];
const SegmentIntersection &it = vline.intersections[iIntersection];
const SegmentedIntersectionLine &vline2 = segs[iVerticalLineOther];
@ -562,36 +534,14 @@ static inline coordf_t measure_perimeter_prev_next_segment_length(
// const bool ccw = poly_with_offset.is_contour_ccw(vline.iContour);
assert(it.type == it2.type);
assert(it.iContour == it2.iContour);
assert(it.is_inner());
const bool forward = it.is_low() == dir_is_next;
Point p1(vline.pos, it.pos());
Point p2(vline2.pos, it2.pos());
return forward ?
return it.is_low() ?
segment_length(poly, it .iSegment, p1, it2.iSegment, p2) :
segment_length(poly, it2.iSegment, p2, it .iSegment, p1);
}
static inline coordf_t measure_perimeter_prev_segment_length(
const ExPolygonWithOffset &poly_with_offset,
const std::vector<SegmentedIntersectionLine> &segs,
size_t iVerticalLine,
size_t iIntersection,
size_t iIntersection2)
{
return measure_perimeter_prev_next_segment_length(poly_with_offset, segs, iVerticalLine, iIntersection, iIntersection2, false);
}
static inline coordf_t measure_perimeter_next_segment_length(
const ExPolygonWithOffset &poly_with_offset,
const std::vector<SegmentedIntersectionLine> &segs,
size_t iVerticalLine,
size_t iIntersection,
size_t iIntersection2)
{
return measure_perimeter_prev_next_segment_length(poly_with_offset, segs, iVerticalLine, iIntersection, iIntersection2, true);
}
// Append the points of a perimeter segment when going from iIntersection to iIntersection2.
// The first point (the point of iIntersection) will not be inserted,
// the last point will be inserted.
@ -646,8 +596,7 @@ static inline coordf_t measure_perimeter_segment_on_vertical_line_length(
const SegmentIntersection &itsct = il.intersections[iIntersection];
const SegmentIntersection &itsct2 = il.intersections[iIntersection2];
const Polygon &poly = poly_with_offset.contour(itsct.iContour);
assert(itsct.is_inner());
assert(itsct2.is_inner());
assert(itsct.is_inner() == itsct2.is_inner());
assert(itsct.type != itsct2.type);
assert(itsct.iContour == itsct2.iContour);
Point p1(il.pos, itsct.pos());
@ -943,7 +892,9 @@ static std::vector<SegmentedIntersectionLine> slice_region_by_vertical_lines(con
// Connect each contour / vertical line intersection point with another two contour / vertical line intersection points.
// (fill in SegmentIntersection::{prev_on_contour, prev_on_contour_vertical, next_on_contour, next_on_contour_vertical}.
// These contour points are either on the same vertical line, or on the vertical line left / right to the current one.
static void connect_segment_intersections_by_contours(const ExPolygonWithOffset &poly_with_offset, std::vector<SegmentedIntersectionLine> &segs)
static void connect_segment_intersections_by_contours(
const ExPolygonWithOffset &poly_with_offset, std::vector<SegmentedIntersectionLine> &segs,
const FillParams &params, const coord_t link_max_length)
{
for (size_t i_vline = 0; i_vline < segs.size(); ++ i_vline) {
SegmentedIntersectionLine &il = segs[i_vline];
@ -1026,13 +977,88 @@ static void connect_segment_intersections_by_contours(const ExPolygonWithOffset
itsct.next_on_contour_type = same_next ?
(inext < i_intersection ? SegmentIntersection::LinkType::Down : SegmentIntersection::LinkType::Up) :
SegmentIntersection::LinkType::Horizontal;
if (same_prev) {
// Only follow a vertical perimeter segment if it skips just the outer intersections.
SegmentIntersection *it = &itsct;
SegmentIntersection *end = il.intersections.data() + iprev;
assert(it != end);
if (it > end)
std::swap(it, end);
for (++ it; it != end; ++ it)
if (it->is_inner()) {
itsct.prev_on_contour_quality = SegmentIntersection::LinkQuality::Invalid;
break;
}
}
if (same_next) {
// Only follow a vertical perimeter segment if it skips just the outer intersections.
SegmentIntersection *it = &itsct;
SegmentIntersection *end = il.intersections.data() + inext;
assert(it != end);
if (it > end)
std::swap(it, end);
for (++ it; it != end; ++ it)
if (it->is_inner()) {
itsct.next_on_contour_quality = SegmentIntersection::LinkQuality::Invalid;
break;
}
}
// If both iprev and inext are on this vline, then there must not be any intersection with the previous or next contour and we will
// not trace this contour when generating infill.
if (same_prev && same_next) {
assert(iprev != i_intersection);
assert(inext != i_intersection);
if ((iprev > i_intersection) == (inext > i_intersection)) {
// Both closest intersections of this contour are on the same vertical line and at the same side of this point.
// Ignore them when tracing the infill.
itsct.prev_on_contour_quality = SegmentIntersection::LinkQuality::Invalid;
itsct.next_on_contour_quality = SegmentIntersection::LinkQuality::Invalid;
}
}
if (params.dont_connect) {
if (itsct.prev_on_contour_quality == SegmentIntersection::LinkQuality::Valid)
itsct.prev_on_contour_quality = SegmentIntersection::LinkQuality::TooLong;
if (itsct.next_on_contour_quality == SegmentIntersection::LinkQuality::Valid)
itsct.next_on_contour_quality = SegmentIntersection::LinkQuality::TooLong;
} else if (link_max_length > 0) {
// Measure length of the links.
if (itsct.prev_on_contour_quality == SegmentIntersection::LinkQuality::Valid &&
(same_prev ?
measure_perimeter_segment_on_vertical_line_length(poly_with_offset, segs, i_vline, iprev, i_intersection, forward) :
measure_perimeter_horizontal_segment_length(poly_with_offset, segs, i_vline - 1, iprev, i_intersection)) > link_max_length)
itsct.prev_on_contour_quality = SegmentIntersection::LinkQuality::TooLong;
if (itsct.next_on_contour_quality == SegmentIntersection::LinkQuality::Valid &&
(same_next ?
measure_perimeter_segment_on_vertical_line_length(poly_with_offset, segs, i_vline, i_intersection, inext, forward) :
measure_perimeter_horizontal_segment_length(poly_with_offset, segs, i_vline, i_intersection, inext)) > link_max_length)
itsct.next_on_contour_quality = SegmentIntersection::LinkQuality::TooLong;
}
}
// Make the LinkQuality::Invalid symmetric on vertical connections.
for (int i_intersection = 0; i_intersection < il.intersections.size(); ++ i_intersection) {
SegmentIntersection &it = il.intersections[i_intersection];
if (it.has_left_vertical() && it.prev_on_contour_quality == SegmentIntersection::LinkQuality::Invalid) {
SegmentIntersection &it2 = il.intersections[it.left_vertical()];
assert(it2.left_vertical() == i_intersection);
it2.prev_on_contour_quality = SegmentIntersection::LinkQuality::Invalid;
}
if (it.has_right_vertical() && it.next_on_contour_quality == SegmentIntersection::LinkQuality::Invalid) {
SegmentIntersection &it2 = il.intersections[it.right_vertical()];
assert(it2.right_vertical() == i_intersection);
it2.next_on_contour_quality = SegmentIntersection::LinkQuality::Invalid;
}
}
}
#ifndef NDEBUG
// Validate the connectivity.
for (size_t i_vline = 0; i_vline + 1 < segs.size(); ++ i_vline) {
const SegmentedIntersectionLine &il_left = segs[i_vline];
const SegmentedIntersectionLine &il_left = segs[i_vline];
const SegmentedIntersectionLine &il_right = segs[i_vline + 1];
for (const SegmentIntersection &it : il_left.intersections) {
if (it.has_right_horizontal()) {
@ -1055,6 +1081,28 @@ static void connect_segment_intersections_by_contours(const ExPolygonWithOffset
}
}
}
for (size_t i_vline = 0; i_vline < segs.size(); ++ i_vline) {
const SegmentedIntersectionLine &il = segs[i_vline];
for (const SegmentIntersection &it : il.intersections) {
auto i_it = int(&it - il.intersections.data());
if (it.has_left_vertical_up()) {
assert(il.intersections[it.left_vertical_up()].left_vertical_down() == i_it);
assert(il.intersections[it.left_vertical_up()].prev_on_contour_quality == it.prev_on_contour_quality);
}
if (it.has_left_vertical_down()) {
assert(il.intersections[it.left_vertical_down()].left_vertical_up() == i_it);
assert(il.intersections[it.left_vertical_down()].prev_on_contour_quality == it.prev_on_contour_quality);
}
if (it.has_right_vertical_up()) {
assert(il.intersections[it.right_vertical_up()].right_vertical_down() == i_it);
assert(il.intersections[it.right_vertical_up()].next_on_contour_quality == it.next_on_contour_quality);
}
if (it.has_right_vertical_down()) {
assert(il.intersections[it.right_vertical_down()].right_vertical_up() == i_it);
assert(il.intersections[it.right_vertical_down()].next_on_contour_quality == it.next_on_contour_quality);
}
}
}
#endif /* NDEBUG */
}
@ -1104,161 +1152,6 @@ static SegmentIntersection& end_of_vertical_run(SegmentedIntersectionLine &il, S
return const_cast<SegmentIntersection&>(end_of_vertical_run(std::as_const(il), std::as_const(start)));
}
static void classify_vertical_runs(
const ExPolygonWithOffset &poly_with_offset, const FillParams &params, const coord_t link_max_length,
std::vector<SegmentedIntersectionLine> &segs, size_t i_vline)
{
SegmentedIntersectionLine &vline = segs[i_vline];
for (size_t i_intersection = 0; i_intersection + 1 < vline.intersections.size(); ++ i_intersection) {
if (vline.intersections[i_intersection].type == SegmentIntersection::OUTER_LOW) {
if (vline.intersections[++ i_intersection].type == SegmentIntersection::INNER_LOW) {
for (;;) {
SegmentIntersection &start = vline.intersections[i_intersection];
SegmentIntersection &end = end_of_vertical_run_raw(start);
SegmentIntersection::LinkQuality link_quality = SegmentIntersection::LinkQuality::Valid;
// End of a contour starting at end and ending above end at the same vertical line.
int inext = end.vertical_outside();
if (inext == -1) {
i_intersection = &end - vline.intersections.data() + 1;
break;
}
SegmentIntersection &start2 = vline.intersections[inext];
if (params.dont_connect)
link_quality = SegmentIntersection::LinkQuality::TooLong;
else {
for (SegmentIntersection *it = &end + 1; it != &start2; ++ it)
if (it->is_inner()) {
link_quality = SegmentIntersection::LinkQuality::Invalid;
break;
}
if (link_quality == SegmentIntersection::LinkQuality::Valid && link_max_length > 0) {
// Measure length of the link.
coordf_t link_length = measure_perimeter_segment_on_vertical_line_length(
poly_with_offset, segs, i_vline, i_intersection, inext, end.has_right_vertical_outside());
if (link_length > link_max_length)
link_quality = SegmentIntersection::LinkQuality::TooLong;
}
}
(end.has_left_vertical_up() ? end.prev_on_contour_quality : end.next_on_contour_quality) = link_quality;
(start2.has_left_vertical_down() ? start2.prev_on_contour_quality : start2.next_on_contour_quality) = link_quality;
if (link_quality != SegmentIntersection::LinkQuality::Valid) {
i_intersection = &end - vline.intersections.data() + 1;
break;
}
i_intersection = &start2 - vline.intersections.data();
}
} else
++ i_intersection;
} else
++ i_intersection;
}
}
static void classify_horizontal_links(
const ExPolygonWithOffset &poly_with_offset, const FillParams &params, const coord_t link_max_length,
std::vector<SegmentedIntersectionLine> &segs, size_t i_vline)
{
SegmentedIntersectionLine &vline_left = segs[i_vline];
SegmentedIntersectionLine &vline_right = segs[i_vline + 1];
// Traverse both left and right together.
size_t i_intersection_left = 0;
size_t i_intersection_right = 0;
while (i_intersection_left + 1 < vline_left.intersections.size() && i_intersection_right + 1 < vline_right.intersections.size()) {
if (i_intersection_left < vline_left.intersections.size() && vline_left.intersections[i_intersection_left].type != SegmentIntersection::INNER_LOW) {
++ i_intersection_left;
continue;
}
if (i_intersection_right < vline_right.intersections.size() && vline_right.intersections[i_intersection_right].type != SegmentIntersection::INNER_LOW) {
++ i_intersection_right;
continue;
}
if (i_intersection_left + 1 >= vline_left.intersections.size()) {
// Trace right only.
} else if (i_intersection_right + 1 >= vline_right.intersections.size()) {
// Trace left only.
} else {
// Trace both.
SegmentIntersection &start_left = vline_left.intersections[i_intersection_left];
SegmentIntersection &end_left = end_of_vertical_run(vline_left, start_left);
SegmentIntersection &start_right = vline_right.intersections[i_intersection_right];
SegmentIntersection &end_right = end_of_vertical_run(vline_right, start_right);
// Do these runs overlap?
int end_right_horizontal = end_left.right_horizontal();
int end_left_horizontal = end_right.left_horizontal();
if (end_right_horizontal != -1) {
if (end_right_horizontal < &start_right - vline_right.intersections.data()) {
// Left precedes the right segment.
}
} else if (end_left_horizontal != -1) {
if (end_left_horizontal < &start_left - vline_left.intersections.data()) {
// Right precedes the left segment.
}
}
}
}
#if 0
for (size_t i_intersection = 0; i_intersection + 1 < seg.intersections.size(); ++ i_intersection) {
if (segs.intersections[i_intersection].type == SegmentIntersection::OUTER_LOW) {
if (segs.intersections[++ i_intersection].type == SegmentIntersection::INNER_LOW) {
for (;;) {
SegmentIntersection &start = segs.intersections[i_intersection];
SegmentIntersection &end = end_of_vertical_run_raw(start);
SegmentIntersection::LinkQuality link_quality = SegmentIntersection::LinkQuality::Valid;
// End of a contour starting at end and ending above end at the same vertical line.
int inext = end.vertical_outside();
if (inext == -1) {
i_intersection = &end - segs.intersections.data() + 1;
break;
}
SegmentIntersection &start2 = segs.intersections[inext];
if (params.dont_connect)
link_quality = SegmentIntersection::LinkQuality::TooLong;
else {
for (SegmentIntersection *it = &end + 1; it != &start2; ++ it)
if (it->is_inner()) {
link_quality = SegmentIntersection::LinkQuality::Invalid;
break;
}
if (link_quality == SegmentIntersection::LinkQuality::Valid && link_max_length > 0) {
// Measure length of the link.
coordf_t link_length = measure_perimeter_segment_on_vertical_line_length(
poly_with_offset, segs, i_vline, i_intersection, inext, intrsctn->has_right_vertical_outside());
if (link_length > link_max_length)
link_quality = SegmentIntersection::LinkQuality::TooLong;
}
}
(end.has_left_vertical_up() ? end.prev_on_contour_quality : end.next_on_contour_quality) = link_quality;
(start2.has_left_vertical_down() ? start2.prev_on_contour_quality : start2.next_on_contour_quality) = link_quality;
if (link_quality != SegmentIntersection::LinkQuality::Valid) {
i_intersection = &end - segs.intersections.data() + 1;
break;
}
i_intersection = &start2 - segs.intersections.data();
}
} else
++ i_intersection;
} else
++ i_intersection;
}
#endif
}
static void disconnect_invalid_contour_links(
const ExPolygonWithOffset& poly_with_offset, const FillParams& params, const coord_t link_max_length, std::vector<SegmentedIntersectionLine>& segs)
{
// Make the links symmetric!
// Validate vertical runs including vertical contour links.
for (size_t i_vline = 0; i_vline < segs.size(); ++ i_vline) {
classify_vertical_runs(poly_with_offset, params, link_max_length, segs, i_vline);
if (i_vline > 0)
classify_horizontal_links(poly_with_offset, params, link_max_length, segs, i_vline - 1);
}
}
static void traverse_graph_generate_polylines(
const ExPolygonWithOffset& poly_with_offset, const FillParams& params, const coord_t link_max_length, std::vector<SegmentedIntersectionLine>& segs, Polylines& polylines_out)
{
@ -1392,44 +1285,30 @@ static void traverse_graph_generate_polylines(
if (try_connect) {
// Decide, whether to finish the segment, or whether to follow the perimeter.
// 1) Find possible connection points on the previous / next vertical line.
IntersectionTypeOtherVLine intrsection_type_prev = intersection_type_on_prev_vertical_line(segs, i_vline, i_intersection);
IntersectionTypeOtherVLine intrsctn_type_next = intersection_type_on_next_vertical_line(segs, i_vline, i_intersection);
int i_prev = it->left_horizontal();
int i_next = it->right_horizontal();
bool intersection_prev_valid = intersection_on_prev_vertical_line_valid(segs, i_vline, i_intersection);
bool intersection_next_valid = intersection_on_next_vertical_line_valid(segs, i_vline, i_intersection);
bool intersection_horizontal_valid = intersection_prev_valid || intersection_next_valid;
// Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed.
if (i_prev != -1)
segs[i_vline - 1].intersections[i_prev].consumed_perimeter_right = true;
if (i_next != -1)
it->consumed_perimeter_right = true;
// Try to connect to a previous or next vertical line, making a zig-zag pattern.
if (intrsection_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK || intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) {
if (intersection_horizontal_valid) {
// A horizontal connection along the perimeter line exists.
int i_prev = it->left_horizontal();
int i_next = it->right_horizontal();
coordf_t dist_prev = (intrsection_type_prev != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits<coord_t>::max() :
measure_perimeter_prev_segment_length(poly_with_offset, segs, i_vline, i_intersection, i_prev);
coordf_t dist_next = (intrsctn_type_next != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits<coord_t>::max() :
measure_perimeter_next_segment_length(poly_with_offset, segs, i_vline, i_intersection, i_next);
// Take the shorter path.
//FIXME this may not be always the best strategy to take the shortest connection line now.
bool take_next = (intrsection_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK && intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) ?
(dist_next < dist_prev) :
intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK;
assert(it->is_inner());
bool skip = params.dont_connect || (link_max_length > 0 && (take_next ? dist_next : dist_prev) > link_max_length);
if (skip) {
#if 1
// Just skip the connecting contour and start a new path.
goto dont_connect;
#else
polyline_current->points.emplace_back(vline.pos, it->pos());
polylines_out.emplace_back();
polyline_current = &polylines_out.back();
const SegmentedIntersectionLine& il2 = segs[take_next ? (i_vline + 1) : (i_vline - 1)];
polyline_current->points.emplace_back(il2.pos, il2.intersections[take_next ? i_next : i_prev].pos());
#endif
} else {
polyline_current->points.emplace_back(vline.pos, it->pos());
emit_perimeter_prev_next_segment(poly_with_offset, segs, i_vline, it->iContour, i_intersection, take_next ? i_next : i_prev, *polyline_current, take_next);
}
// Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed.
if (i_prev != -1)
segs[i_vline - 1].intersections[i_prev].consumed_perimeter_right = true;
if (i_next != -1)
it->consumed_perimeter_right = true;
assert(it->is_inner());
bool take_next = intersection_next_valid;
if (intersection_prev_valid && intersection_next_valid) {
// Take the shorter segment. This greedy heuristics may not be the best.
coordf_t dist_prev = measure_perimeter_horizontal_segment_length(poly_with_offset, segs, i_vline - 1, i_prev, i_intersection);
coordf_t dist_next = measure_perimeter_horizontal_segment_length(poly_with_offset, segs, i_vline, i_intersection, i_next);
take_next = dist_next < dist_prev;
}
polyline_current->points.emplace_back(vline.pos, it->pos());
emit_perimeter_prev_next_segment(poly_with_offset, segs, i_vline, it->iContour, i_intersection, take_next ? i_next : i_prev, *polyline_current, take_next);
//FIXME consume the left / right connecting segments at the other end of this line? Currently it is not critical because a perimeter segment is not followed if the vertical segment at the other side has already been consumed.
// Advance to the neighbor line.
if (take_next) {
@ -1443,64 +1322,43 @@ static void traverse_graph_generate_polylines(
continue;
}
// 5) Try to connect to a previous or next point on the same vertical line.
if (int inext = it->vertical_outside(); inext != -1) {
bool valid = true;
// Verify, that there is no intersection with the inner contour up to the end of the contour segment.
// Verify, that the successive segment has not been consumed yet.
if (going_up) {
if (vline.intersections[inext].consumed_vertical_up)
valid = false;
else {
for (int i = i_intersection + 1; i < inext && valid; ++ i)
if (vline.intersections[i].is_inner())
valid = false;
}
} else {
if (vline.intersections[inext - 1].consumed_vertical_up)
valid = false;
else {
for (int i = inext + 1; i < i_intersection && valid; ++ i)
if (vline.intersections[i].is_inner())
valid = false;
}
}
if (valid) {
const Polygon &poly = poly_with_offset.contour(it->iContour);
assert(it->iContour == vline.intersections[inext].iContour);
// Skip this perimeter line?
bool skip = params.dont_connect;
bool dir_forward = it->has_right_vertical_outside();
if (! skip && link_max_length > 0) {
coordf_t link_length = measure_perimeter_segment_on_vertical_line_length(
poly_with_offset, segs, i_vline, i_intersection, inext, dir_forward);
skip = link_length > link_max_length;
}
polyline_current->points.emplace_back(vline.pos, it->pos());
if (skip) {
// Just skip the connecting contour and start a new path.
polylines_out.emplace_back();
polyline_current = &polylines_out.back();
polyline_current->points.emplace_back(vline.pos, vline.intersections[inext].pos());
} else {
// Consume the connecting contour and the next segment.
emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, it->iContour, i_intersection, inext, *polyline_current, dir_forward);
}
// Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed.
// If there are any outer intersection points skipped (bypassed) by the contour,
// mark them as processed.
if (going_up)
for (int i = i_intersection; i < inext; ++ i)
vline.intersections[i].consumed_vertical_up = true;
else
for (int i = inext; i < i_intersection; ++ i)
vline.intersections[i].consumed_vertical_up = true;
// seg.intersections[going_up ? i_intersection : i_intersection - 1].consumed_vertical_up = true;
it->consumed_perimeter_right = true;
(going_up ? ++ it : -- it)->consumed_perimeter_right = true;
i_intersection = inext;
continue;
// Try to connect to a previous or next point on the same vertical line.
int i_vertical = it->vertical_outside();
auto vertical_link_quality = (i_vertical == -1 || vline.intersections[i_vertical + (going_up ? 0 : -1)].consumed_vertical_up) ?
SegmentIntersection::LinkQuality::Invalid : it->vertical_outside_quality();
#if 0
if (vertical_link_quality == SegmentIntersection::LinkQuality::Valid ||
// Follow the link if there is no horizontal link available.
(! intersection_horizontal_valid && vertical_link_quality != SegmentIntersection::LinkQuality::Invalid)) {
#else
if (vertical_link_quality != SegmentIntersection::LinkQuality::Invalid) {
#endif
assert(it->iContour == vline.intersections[i_vertical].iContour);
polyline_current->points.emplace_back(vline.pos, it->pos());
if (vertical_link_quality == SegmentIntersection::LinkQuality::Valid)
// Consume the connecting contour and the next segment.
emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, it->iContour, i_intersection, i_vertical,
*polyline_current, going_up ? it->has_left_vertical_up() : it->has_right_vertical_down());
else {
// Just skip the connecting contour and start a new path.
polylines_out.emplace_back();
polyline_current = &polylines_out.back();
polyline_current->points.emplace_back(vline.pos, vline.intersections[i_vertical].pos());
}
// Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed.
// If there are any outer intersection points skipped (bypassed) by the contour,
// mark them as processed.
if (going_up)
for (int i = i_intersection; i < i_vertical; ++i)
vline.intersections[i].consumed_vertical_up = true;
else
for (int i = i_vertical; i < i_intersection; ++i)
vline.intersections[i].consumed_vertical_up = true;
// seg.intersections[going_up ? i_intersection : i_intersection - 1].consumed_vertical_up = true;
it->consumed_perimeter_right = true;
(going_up ? ++it : --it)->consumed_perimeter_right = true;
i_intersection = i_vertical;
continue;
}
dont_connect:
@ -1553,10 +1411,16 @@ struct MonotonousRegion
int left_intersection_point(bool region_flipped) const { return region_flipped ? left.high : left.low; }
int right_intersection_point(bool region_flipped) const { return (region_flipped == flips) ? right.low : right.high; }
#if NDEBUG
// Left regions are used to track whether all regions left to this one have already been printed.
boost::container::small_vector<MonotonousRegion*, 4> left_neighbors;
// Right regions are held to pick a next region to be extruded using the "Ant colony" heuristics.
boost::container::small_vector<MonotonousRegion*, 4> right_neighbors;
#else
// For debugging, use the normal vector as it is better supported by debug visualizers.
std::vector<MonotonousRegion*> left_neighbors;
std::vector<MonotonousRegion*> right_neighbors;
#endif
};
struct AntPath
@ -1607,7 +1471,7 @@ public:
int i_right = vline_from.intersections[i_from].right_horizontal();
if (i_right == i_to && vline_from.intersections[i_from].next_on_contour_quality == SegmentIntersection::LinkQuality::Valid) {
// Measure length along the contour.
path.length = unscale<float>(measure_perimeter_next_segment_length(m_poly_with_offset, m_segs, region_from.right.vline, i_from, i_to));
path.length = unscale<float>(measure_perimeter_horizontal_segment_length(m_poly_with_offset, m_segs, region_from.right.vline, i_from, i_to));
}
}
if (path.length == -1.) {
@ -1785,6 +1649,24 @@ static std::vector<MonotonousRegion> generate_montonous_regions(std::vector<Segm
{
std::vector<MonotonousRegion> monotonous_regions;
#ifndef NDEBUG
#define SLIC3R_DEBUG_MONOTONOUS_REGIONS
#endif
#ifdef SLIC3R_DEBUG_MONOTONOUS_REGIONS
std::vector<std::vector<std::pair<int, int>>> consumed(segs.size());
auto test_overlap = [&consumed](int segment, int low, int high) {
for (const std::pair<int, int>& interval : consumed[segment])
if ((low >= interval.first && low <= interval.second) ||
(interval.first >= low && interval.first <= high))
return true;
consumed[segment].emplace_back(low, high);
return false;
};
#else
auto test_overlap = [](int, int, int) { return false; };
#endif
for (int i_vline_seed = 0; i_vline_seed < segs.size(); ++ i_vline_seed) {
SegmentedIntersectionLine &vline_seed = segs[i_vline_seed];
for (int i_intersection_seed = 1; i_intersection_seed + 1 < vline_seed.intersections.size(); ) {
@ -1805,6 +1687,7 @@ static std::vector<MonotonousRegion> generate_montonous_regions(std::vector<Segm
region.left.low = int(left.first - vline_seed.intersections.data());
region.left.high = int(left.second - vline_seed.intersections.data());
region.right = region.left;
assert(! test_overlap(region.left.vline, region.left.low, region.left.high));
start->consumed_vertical_up = true;
int num_lines = 1;
while (++ i_vline < segs.size()) {
@ -1826,7 +1709,8 @@ static std::vector<MonotonousRegion> generate_montonous_regions(std::vector<Segm
region.right.low = int(right.first - vline_right.intersections.data());
region.right.high = int(right.second - vline_right.intersections.data());
right.first->consumed_vertical_up = true;
++ num_lines;
assert(! test_overlap(region.right.vline, region.right.low, region.right.high));
++ num_lines;
left = right;
}
// Even number of lines makes the infill zig-zag to exit on the other side of the region than where it starts.
@ -1898,6 +1782,40 @@ static void connect_monotonous_regions(std::vector<MonotonousRegion> &regions, s
}
}
}
// Sometimes a segment may indicate that it connects to a segment on the other side while the other does not.
// This may be a valid case if one side contains runs of OUTER_LOW, INNER_LOW, {INNER_HIGH, INNER_LOW}*, INNER_HIGH, OUTER_HIGH,
// where the part in the middle does not connect to the other side, but it will be extruded through.
for (MonotonousRegion &region : regions) {
std::sort(region.left_neighbors.begin(), region.left_neighbors.end());
std::sort(region.right_neighbors.begin(), region.right_neighbors.end());
}
for (MonotonousRegion &region : regions) {
for (MonotonousRegion *neighbor : region.left_neighbors) {
auto it = std::lower_bound(neighbor->right_neighbors.begin(), neighbor->right_neighbors.end(), &region);
if (it == neighbor->right_neighbors.end() || *it != &region)
neighbor->right_neighbors.insert(it, &region);
}
for (MonotonousRegion *neighbor : region.right_neighbors) {
auto it = std::lower_bound(neighbor->left_neighbors.begin(), neighbor->left_neighbors.end(), &region);
if (it == neighbor->left_neighbors.end() || *it != &region)
neighbor->left_neighbors.insert(it, &region);
}
}
#ifndef NDEBUG
// Verify symmetry of the left_neighbors / right_neighbors.
for (MonotonousRegion &region : regions) {
for (MonotonousRegion *neighbor : region.left_neighbors) {
assert(std::count(region.left_neighbors.begin(), region.left_neighbors.end(), neighbor) == 1);
assert(std::find(neighbor->right_neighbors.begin(), neighbor->right_neighbors.end(), &region) != neighbor->right_neighbors.end());
}
for (MonotonousRegion *neighbor : region.right_neighbors) {
assert(std::count(region.right_neighbors.begin(), region.right_neighbors.end(), neighbor) == 1);
assert(std::find(neighbor->left_neighbors.begin(), neighbor->left_neighbors.end(), &region) != neighbor->left_neighbors.end());
}
}
#endif /* NDEBUG */
}
// Raad Salman: Algorithms for the Precedence Constrained Generalized Travelling Salesperson Problem
@ -1936,8 +1854,8 @@ inline void print_ant(const std::string& fmt, TArgs&&... args) {
static std::vector<MonotonousRegionLink> chain_monotonous_regions(
std::vector<MonotonousRegion> &regions, const ExPolygonWithOffset &poly_with_offset, const std::vector<SegmentedIntersectionLine> &segs, std::mt19937_64 &rng)
{
// Number of left neighbors (regions that this region depends on, this region cannot be printed before the regions left of it are printed).
std::vector<int32_t> left_neighbors_unprocessed(regions.size(), 0);
// Number of left neighbors (regions that this region depends on, this region cannot be printed before the regions left of it are printed) + self.
std::vector<int32_t> left_neighbors_unprocessed(regions.size(), 1);
// Queue of regions, which have their left neighbors already printed.
std::vector<MonotonousRegion*> queue;
queue.reserve(regions.size());
@ -1945,7 +1863,7 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
if (region.left_neighbors.empty())
queue.emplace_back(&region);
else
left_neighbors_unprocessed[&region - regions.data()] = int(region.left_neighbors.size());
left_neighbors_unprocessed[&region - regions.data()] += int(region.left_neighbors.size());
// Make copy of structures that need to be initialized at each ant iteration.
auto left_neighbors_unprocessed_initial = left_neighbors_unprocessed;
auto queue_initial = queue;
@ -1964,6 +1882,64 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
};
std::vector<NextCandidate> next_candidates;
auto validate_unprocessed =
#ifdef NDEBUG
[]() { return true; };
#else
[&regions, &left_neighbors_unprocessed, &path, &queue]() {
std::vector<unsigned char> regions_processed(regions.size(), false);
std::vector<unsigned char> regions_in_queue(regions.size(), false);
for (const MonotonousRegion *region : queue) {
// This region is not processed yet, his predecessors are processed.
assert(left_neighbors_unprocessed[region - regions.data()] == 1);
regions_in_queue[region - regions.data()] = true;
}
for (const MonotonousRegionLink &link : path) {
assert(left_neighbors_unprocessed[link.region - regions.data()] == 0);
regions_processed[link.region - regions.data()] = true;
}
for (size_t i = 0; i < regions_processed.size(); ++ i) {
assert(! regions_processed[i] || ! regions_in_queue[i]);
const MonotonousRegion &region = regions[i];
if (regions_processed[i] || regions_in_queue[i]) {
assert(left_neighbors_unprocessed[i] == (regions_in_queue[i] ? 1 : 0));
// All left neighbors should be processed already.
for (const MonotonousRegion *left : region.left_neighbors) {
assert(regions_processed[left - regions.data()]);
assert(left_neighbors_unprocessed[left - regions.data()] == 0);
}
} else {
// Some left neihgbor should not be processed yet.
assert(left_neighbors_unprocessed[i] > 1);
size_t num_predecessors_unprocessed = 0;
bool has_left_last_on_path = false;
for (const MonotonousRegion* left : region.left_neighbors) {
size_t iprev = left - regions.data();
if (regions_processed[iprev]) {
assert(left_neighbors_unprocessed[iprev] == 0);
if (left == path.back().region) {
// This region should actually be on queue, but to optimize the queue management
// this item will be processed in the next round by traversing path.back().region->right_neighbors before processing the queue.
assert(! has_left_last_on_path);
has_left_last_on_path = true;
++ num_predecessors_unprocessed;
}
} else {
if (regions_in_queue[iprev])
assert(left_neighbors_unprocessed[iprev] == 1);
else
assert(left_neighbors_unprocessed[iprev] > 1);
++ num_predecessors_unprocessed;
}
}
assert(num_predecessors_unprocessed > 0);
assert(left_neighbors_unprocessed[i] == num_predecessors_unprocessed + 1);
}
}
return true;
};
#endif /* NDEBUG */
// How many times to repeat the ant simulation.
constexpr int num_rounds = 10;
// With how many ants each of the run will be performed?
@ -1999,13 +1975,16 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
path.clear();
queue = queue_initial;
left_neighbors_unprocessed = left_neighbors_unprocessed_initial;
assert(validate_unprocessed());
// Pick randomly the first from the queue at random orientation.
int first_idx = std::uniform_int_distribution<>(0, int(queue.size()) - 1)(rng);
path.emplace_back(MonotonousRegionLink{ queue[first_idx], rng() > rng.max() / 2 });
*(queue.begin() + first_idx) = std::move(queue.back());
queue.pop_back();
-- left_neighbors_unprocessed[path.back().region - regions.data()];
assert(left_neighbors_unprocessed[path.back().region - regions.data()] == 0);
print_ant("\tRegion (%1%:%2%,%3%) (%4%:%5%,%6%)",
assert(validate_unprocessed());
print_ant("\tRegion (%1%:%2%,%3%) (%4%:%5%,%6%)",
path.back().region->left.vline,
path.back().flipped ? path.back().region->left.high : path.back().region->left.low,
path.back().flipped ? path.back().region->left.low : path.back().region->left.high,
@ -2014,7 +1993,7 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
path.back().flipped == path.back().region->flips ? path.back().region->right.low : path.back().region->right.high);
while (! queue.empty() || ! path.back().region->right_neighbors.empty()) {
// Chain.
// Chain.
MonotonousRegion &region = *path.back().region;
bool dir = path.back().flipped;
// Sort by distance to pt.
@ -2022,8 +2001,8 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
next_candidates.reserve(region.right_neighbors.size() * 2);
for (MonotonousRegion *next : region.right_neighbors) {
int &unprocessed = left_neighbors_unprocessed[next - regions.data()];
assert(unprocessed > 0);
if (-- unprocessed == 0) {
assert(unprocessed > 1);
if (-- unprocessed == 1) {
// Dependencies of the successive blocks are satisfied.
AntPath &path1 = path_matrix(region, dir, *next, false);
AntPath &path1_flipped = path_matrix(region, ! dir, *next, true);
@ -2038,6 +2017,7 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
if (num_direct_neighbors == 0) {
// Add the queue candidates.
for (MonotonousRegion *next : queue) {
assert(left_neighbors_unprocessed[next - regions.data()] == 1);
AntPath &path1 = path_matrix(region, dir, *next, false);
AntPath &path1_flipped = path_matrix(region, ! dir, *next, true);
AntPath &path2 = path_matrix(region, dir, *next, true);
@ -2068,13 +2048,13 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
print_ant("\tTaking path at probability threshold %1% of %2%", probability_threshold, total_probability);
}
// Move the other right neighbors with satisified constraints to the queue.
bool direct_neighbor_taken = take_path - next_candidates.begin() < num_direct_neighbors;
for (std::vector<NextCandidate>::iterator it_next_candidate = next_candidates.begin(); it_next_candidate != next_candidates.begin() + num_direct_neighbors; ++ it_next_candidate)
if ((queue.empty() || it_next_candidate->region != queue.back()) && it_next_candidate->region != take_path->region)
queue.emplace_back(it_next_candidate->region);
if (take_path - next_candidates.begin() >= num_direct_neighbors) {
// Remove the selected path from the queue.
auto it = std::find(queue.begin(), queue.end(), take_path->region);
assert(it != queue.end());
*it = queue.back();
queue.pop_back();
}
@ -2084,6 +2064,8 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
path.back().next = take_path->link;
path.back().next_flipped = take_path->link_flipped;
path.emplace_back(MonotonousRegionLink{ next_region, next_dir });
assert(left_neighbors_unprocessed[next_region - regions.data()] == 1);
left_neighbors_unprocessed[next_region - regions.data()] = 0;
print_ant("\tRegion (%1%:%2%,%3%) (%4%:%5%,%6%) length to prev %7%",
next_region->left.vline,
next_dir ? next_region->left.high : next_region->left.low,
@ -2103,7 +2085,8 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
// Update pheromones along this link.
take_path->link->pheromone = (1.f - pheromone_evaporation) * take_path->link->pheromone + pheromone_evaporation * pheromone_initial_deposit;
}
assert(validate_unprocessed());
}
// Perform 3-opt local optimization of the path.
monotonous_3_opt(path, segs);
@ -2206,10 +2189,11 @@ static void polylines_from_paths(const std::vector<MonotonousRegionLink> &path,
do {
++ it;
iright = std::max(iright, it->right_horizontal());
} while (it->type != SegmentIntersection::INNER_HIGH);
assert(it->is_inner());
} while (it->type != SegmentIntersection::INNER_HIGH || (it + 1)->type != SegmentIntersection::OUTER_HIGH);
polyline->points.emplace_back(vline.pos, it->pos());
int inext = it->vertical_up();
if (inext == -1)
if (inext == -1 || it->vertical_up_quality() != SegmentIntersection::LinkQuality::Valid)
break;
const Polygon &poly = poly_with_offset.contour(it->iContour);
assert(it->iContour == vline.intersections[inext].iContour);
@ -2218,17 +2202,18 @@ static void polylines_from_paths(const std::vector<MonotonousRegionLink> &path,
}
} else {
// Going down.
assert(it->is_high());
assert(i_intersection > 0);
assert(it->is_high());
assert(i_intersection > 0);
for (;;) {
do {
-- it;
if (int iright_new = it->right_horizontal(); iright_new != -1)
iright = iright_new;
} while (it->type != SegmentIntersection::INNER_LOW);
assert(it->is_inner());
} while (it->type != SegmentIntersection::INNER_LOW || (it - 1)->type != SegmentIntersection::OUTER_LOW);
polyline->points.emplace_back(vline.pos, it->pos());
int inext = it->vertical_down();
if (inext == -1)
if (inext == -1 || it->vertical_down_quality() != SegmentIntersection::LinkQuality::Valid)
break;
const Polygon &poly = poly_with_offset.contour(it->iContour);
assert(it->iContour == vline.intersections[inext].iContour);
@ -2347,7 +2332,8 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
#endif /* SLIC3R_DEBUG */
std::vector<SegmentedIntersectionLine> segs = slice_region_by_vertical_lines(poly_with_offset, n_vlines, x0, line_spacing);
connect_segment_intersections_by_contours(poly_with_offset, segs);
// Connect by horizontal / vertical links, classify the links based on link_max_length as too long.
connect_segment_intersections_by_contours(poly_with_offset, segs, params, link_max_length);
#ifdef SLIC3R_DEBUG
// Paint the segments and finalize the SVG file.

View file

@ -1123,7 +1123,7 @@ void PrintConfigDef::init_fff_params()
def->sidetext = L("mm/s");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(60));
def->set_default_value(new ConfigOptionFloat(15));
def = this->add("layer_gcode", coString);
def->label = L("After layer change G-code");

View file

@ -43,6 +43,7 @@ std::vector<std::pair<size_t, bool>> chain_segments_closest_point(std::vector<En
assert(next_idx < end_points.size());
EndPointType &end_point = end_points[next_idx];
end_point.chain_id = 1;
assert((next_idx & 1) == 0 || could_reverse_func(next_idx >> 1));
out.emplace_back(next_idx / 2, (next_idx & 1) != 0);
this_idx = next_idx ^ 1;
}
@ -165,7 +166,9 @@ std::vector<std::pair<size_t, bool>> chain_segments_greedy_constrained_reversals
EndPoint *first_point = nullptr;
size_t first_point_idx = std::numeric_limits<size_t>::max();
if (start_near != nullptr) {
size_t idx = find_closest_point(kdtree, start_near->template cast<double>());
size_t idx = find_closest_point(kdtree, start_near->template cast<double>(),
// Don't start with a reverse segment, if flipping of the segment is not allowed.
[&could_reverse_func](size_t idx) { return (idx & 1) == 0 || could_reverse_func(idx >> 1); });
assert(idx < end_points.size());
first_point = &end_points[idx];
first_point->distance_out = 0.;