1
0
mirror of https://github.com/FULU-Foundation/OrcaSlicer-bambulab.git synced 2026-07-29 10:55:50 +00:00

New FDM support sparse infill zig-zag algorithm.

Fixed some old support and infill issues.

Fixes support problem #4295
Fixes Parts of interface layer extends beyond supports and cannot be printed
Fixes support missing under horizontal overhang #6058
Fixes Slicer double-traces small sections of Rectilinear Supports, causes
Fixes plastic buildup and nozzle crashes #4951
Fixes Add "Angle Interface layers" #2969
This commit is contained in:
Vojtech Bubnik
2021-04-08 15:29:40 +02:00
parent 0db55a0699
commit 8fd731f7a0
18 changed files with 1473 additions and 285 deletions
+184 -96
View File
@@ -798,33 +798,44 @@ static std::vector<SegmentedIntersectionLine> slice_region_by_vertical_lines(con
assert(l <= this_x);
assert(r >= this_x);
// Calculate the intersection position in y axis. x is known.
if (p1(0) == this_x) {
if (p2(0) == this_x) {
if (p1.x() == this_x) {
if (p2.x() == this_x) {
// Ignore strictly vertical segments.
continue;
}
is.pos_p = p1(1);
const Point &p0 = prev_value_modulo(iPrev, contour);
if (int64_t(p0.x() - p1.x()) * int64_t(p2.x() - p1.x()) > 0) {
// Ignore points of a contour touching the infill line from one side.
continue;
}
is.pos_p = p1.y();
is.pos_q = 1;
} else if (p2(0) == this_x) {
is.pos_p = p2(1);
} else if (p2.x() == this_x) {
const Point &p3 = next_value_modulo(iSegment, contour);
if (int64_t(p3.x() - p2.x()) * int64_t(p1.x() - p2.x()) > 0) {
// Ignore points of a contour touching the infill line from one side.
continue;
}
is.pos_p = p2.y();
is.pos_q = 1;
} else {
// First calculate the intersection parameter 't' as a rational number with non negative denominator.
if (p2(0) > p1(0)) {
is.pos_p = this_x - p1(0);
is.pos_q = p2(0) - p1(0);
if (p2.x() > p1.x()) {
is.pos_p = this_x - p1.x();
is.pos_q = p2.x() - p1.x();
} else {
is.pos_p = p1(0) - this_x;
is.pos_q = p1(0) - p2(0);
is.pos_p = p1.x() - this_x;
is.pos_q = p1.x() - p2.x();
}
assert(is.pos_p >= 0 && is.pos_p <= is.pos_q);
assert(is.pos_q > 1);
assert(is.pos_p > 0 && is.pos_p < is.pos_q);
// Make an intersection point from the 't'.
is.pos_p *= int64_t(p2(1) - p1(1));
is.pos_p += p1(1) * int64_t(is.pos_q);
is.pos_p *= int64_t(p2.y() - p1.y());
is.pos_p += p1.y() * int64_t(is.pos_q);
}
// +-1 to take rounding into account.
assert(is.pos() + 1 >= std::min(p1(1), p2(1)));
assert(is.pos() <= std::max(p1(1), p2(1)) + 1);
assert(is.pos() + 1 >= std::min(p1.y(), p2.y()));
assert(is.pos() <= std::max(p1.y(), p2.y()) + 1);
segs[i].intersections.push_back(is);
}
}
@@ -844,55 +855,46 @@ static std::vector<SegmentedIntersectionLine> slice_region_by_vertical_lines(con
size_t j = 0;
for (size_t i = 0; i < sil.intersections.size(); ++ i) {
// What is the orientation of the segment at the intersection point?
size_t iContour = sil.intersections[i].iContour;
const Points &contour = poly_with_offset.contour(iContour).points;
size_t iSegment = sil.intersections[i].iSegment;
size_t iPrev = ((iSegment == 0) ? contour.size() : iSegment) - 1;
coord_t dir = contour[iSegment](0) - contour[iPrev](0);
bool low = dir > 0;
sil.intersections[i].type = poly_with_offset.is_contour_outer(iContour) ?
SegmentIntersection &is = sil.intersections[i];
const size_t iContour = is.iContour;
const Points &contour = poly_with_offset.contour(iContour).points;
const size_t iSegment = is.iSegment;
const size_t iPrev = prev_idx_modulo(iSegment, contour);
const coord_t dir = contour[iSegment].x() - contour[iPrev].x();
const bool low = dir > 0;
is.type = poly_with_offset.is_contour_outer(iContour) ?
(low ? SegmentIntersection::OUTER_LOW : SegmentIntersection::OUTER_HIGH) :
(low ? SegmentIntersection::INNER_LOW : SegmentIntersection::INNER_HIGH);
if (j > 0 && sil.intersections[i].iContour == sil.intersections[j-1].iContour) {
// Two successive intersection points on a vertical line with the same contour. This may be a special case.
if (sil.intersections[i].pos() == sil.intersections[j-1].pos()) {
// Two successive segments meet exactly at the vertical line.
#ifdef SLIC3R_DEBUG
// Verify that the segments of sil.intersections[i] and sil.intersections[j-1] are adjoint.
size_t iSegment2 = sil.intersections[j-1].iSegment;
size_t iPrev2 = ((iSegment2 == 0) ? contour.size() : iSegment2) - 1;
assert(iSegment == iPrev2 || iSegment2 == iPrev);
#endif /* SLIC3R_DEBUG */
if (sil.intersections[i].type == sil.intersections[j-1].type) {
bool take_next = true;
if (j > 0) {
SegmentIntersection &is2 = sil.intersections[j - 1];
if (iContour == is2.iContour && is.pos_q == 1 && is2.pos_q == 1) {
// Two successive intersection points on a vertical line with the same contour, both points are end points of their respective contour segments.
if (is.pos_p == is2.pos_p) {
// Two successive segments meet exactly at the vertical line.
// Verify that the segments of sil.intersections[i] and sil.intersections[j-1] are adjoint.
assert(iSegment == prev_idx_modulo(is2.iSegment, contour) || is2.iSegment == iPrev);
assert(is.type == is2.type);
// Two successive segments of the same direction (both to the right or both to the left)
// meet exactly at the vertical line.
// Remove the second intersection point.
} else {
// This is a loop returning to the same point.
// It may as well be a vertex of a loop touching this vertical line.
// Remove both the lines.
-- j;
take_next = false;
} else if (is.type == is2.type) {
// Two non successive segments of the same direction (both to the right or both to the left)
// meet exactly at the vertical line. That means there is a Z shaped path, where the center segment
// of the Z shaped path is aligned with this vertical line.
// Remove one of the intersection points while maximizing the vertical segment length.
if (low) {
// Remove the second intersection point, keep the first intersection point.
} else {
// Remove the first intersection point, keep the second intersection point.
sil.intersections[j-1] = sil.intersections[i];
}
take_next = false;
}
} else if (sil.intersections[i].type == sil.intersections[j-1].type) {
// Two non successive segments of the same direction (both to the right or both to the left)
// meet exactly at the vertical line. That means there is a Z shaped path, where the center segment
// of the Z shaped path is aligned with this vertical line.
// Remove one of the intersection points while maximizing the vertical segment length.
if (low) {
// Remove the second intersection point, keep the first intersection point.
} else {
// Remove the first intersection point, keep the second intersection point.
sil.intersections[j-1] = sil.intersections[i];
}
} else {
// Vertical line intersects a contour segment at a general position (not at one of its end points).
// or the contour just touches this vertical line with a vertical segment or a sequence of vertical segments.
// Keep both intersection points.
if (j < i)
sil.intersections[j] = sil.intersections[i];
++ j;
}
} else {
}
if (take_next) {
// Vertical line intersects a contour segment at a general position (not at one of its end points).
if (j < i)
sil.intersections[j] = sil.intersections[i];
@@ -905,7 +907,13 @@ static std::vector<SegmentedIntersectionLine> slice_region_by_vertical_lines(con
}
// Verify the segments. If something is wrong, give up.
#define ASSERT_THROW(CONDITION) do { assert(CONDITION); if (! (CONDITION)) throw InfillFailedException(); } while (0)
#ifdef INFILL_DEBUG_OUTPUT
#define INFILL_DEBUG_ASSERT(CONDITION)
try {
#else // INFILL_DEBUG_OUTPUT
#define INFILL_DEBUG_ASSERT(CONDITION) assert(CONDITION)
#endif // INFILL_DEBUG_OUTPUT
#define ASSERT_THROW(CONDITION) do { INFILL_DEBUG_ASSERT(CONDITION); if (! (CONDITION)) throw InfillFailedException(); } while (0)
for (size_t i_seg = 0; i_seg < segs.size(); ++ i_seg) {
SegmentedIntersectionLine &sil = segs[i_seg];
// The intersection points have to be even.
@@ -925,6 +933,56 @@ static std::vector<SegmentedIntersectionLine> slice_region_by_vertical_lines(con
}
}
#undef ASSERT_THROW
#undef INFILL_DEBUG_ASSERT
#ifdef INFILL_DEBUG_OUTPUT
} catch (const InfillFailedException & /* ex */) {
// Export the buggy result into an SVG file.
static int iRun = 0;
BoundingBox bbox = get_extents(poly_with_offset.polygons_src);
bbox.offset(scale_(3.));
::Slic3r::SVG svg(debug_out_path("slice_region_by_vertical_lines-failed-%d.svg", iRun ++), bbox);
svg.draw(poly_with_offset.polygons_src);
svg.draw_outline(poly_with_offset.polygons_src, "green");
svg.draw_outline(poly_with_offset.polygons_outer, "green");
svg.draw_outline(poly_with_offset.polygons_inner, "green");
for (size_t i_seg = 0; i_seg < segs.size(); ++i_seg) {
SegmentedIntersectionLine &sil = segs[i_seg];
for (size_t i = 0; i < sil.intersections.size();) {
// An intersection segment crossing the bigger contour may cross the inner offsetted contour even number of times.
if (sil.intersections[i].type != SegmentIntersection::OUTER_LOW) {
svg.draw(Point(sil.pos, sil.intersections[i].pos()), "red");
break;
}
size_t j = i + 1;
if (j == sil.intersections.size()) {
svg.draw(Point(sil.pos, sil.intersections[i].pos()), "magenta");
break;
}
if (! (sil.intersections[j].type == SegmentIntersection::INNER_LOW || sil.intersections[j].type == SegmentIntersection::OUTER_HIGH)) {
svg.draw(Point(sil.pos, sil.intersections[j].pos()), "blue");
break;
}
for (; j < sil.intersections.size() && sil.intersections[j].is_inner(); ++j);
if (j == sil.intersections.size()) {
svg.draw(Point(sil.pos, sil.intersections[j - 1].pos()), "magenta");
break;
}
if ((j & 1) != 1 || sil.intersections[j].type != SegmentIntersection::OUTER_HIGH) {
svg.draw(Point(sil.pos, sil.intersections[j].pos()), "red");
break;
}
if (! (i + 1 == j || sil.intersections[j - 1].type == SegmentIntersection::INNER_HIGH)) {
svg.draw(Point(sil.pos, sil.intersections[j].pos()), "red");
break;
}
svg.draw(Line(Point(sil.pos, sil.intersections[i].pos()), Point(sil.pos, sil.intersections[j].pos())), "black");
i = j + 1;
}
}
assert(false);
throw;
}
#endif //INFILL_DEBUG_OUTPUT
return segs;
}
@@ -2714,10 +2772,10 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa
// extend bounding box so that our pattern will be aligned with other layers
// Transform the reference point to the rotated coordinate system.
Point refpt = rotate_vector.second.rotated(- rotate_vector.first);
// _align_to_grid will not work correctly with positive pattern_shift.
// align_to_grid will not work correctly with positive pattern_shift.
coord_t pattern_shift_scaled = coord_t(scale_(pattern_shift)) % line_spacing;
refpt.x() -= (pattern_shift_scaled >= 0) ? pattern_shift_scaled : (line_spacing + pattern_shift_scaled);
bounding_box.merge(_align_to_grid(
bounding_box.merge(align_to_grid(
bounding_box.min,
Point(line_spacing, line_spacing),
refpt));
@@ -2825,6 +2883,45 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa
return true;
}
void make_fill_lines(const ExPolygonWithOffset &poly_with_offset, Point refpt, double angle, coord_t x_margin, coord_t line_spacing, coord_t pattern_shift, Polylines &fill_lines)
{
BoundingBox bounding_box = poly_with_offset.bounding_box_src();
// Don't produce infill lines, which fully overlap with the infill perimeter.
coord_t x_min = bounding_box.min.x() + x_margin;
coord_t x_max = bounding_box.max.x() - x_margin;
// extend bounding box so that our pattern will be aligned with other layers
// align_to_grid will not work correctly with positive pattern_shift.
coord_t pattern_shift_scaled = pattern_shift % line_spacing;
refpt.x() -= (pattern_shift_scaled >= 0) ? pattern_shift_scaled : (line_spacing + pattern_shift_scaled);
bounding_box.merge(Slic3r::align_to_grid(bounding_box.min, Point(line_spacing, line_spacing), refpt));
// Intersect a set of euqally spaced vertical lines wiht expolygon.
// n_vlines = ceil(bbox_width / line_spacing)
const size_t n_vlines = (bounding_box.max.x() - bounding_box.min.x() + line_spacing - 1) / line_spacing;
const double cos_a = cos(angle);
const double sin_a = sin(angle);
for (const SegmentedIntersectionLine &vline : slice_region_by_vertical_lines(poly_with_offset, n_vlines, bounding_box.min.x(), line_spacing))
if (vline.pos >= x_min) {
if (vline.pos > x_max)
break;
for (auto it = vline.intersections.begin(); it != vline.intersections.end();) {
auto it_low = it ++;
assert(it_low->type == SegmentIntersection::OUTER_LOW);
if (it_low->type != SegmentIntersection::OUTER_LOW)
continue;
auto it_high = it;
assert(it_high->type == SegmentIntersection::OUTER_HIGH);
if (it_high->type == SegmentIntersection::OUTER_HIGH) {
if (angle == 0.)
fill_lines.emplace_back(Point(vline.pos, it_low->pos()), Point(vline.pos, it_high->pos()));
else
fill_lines.emplace_back(Point(vline.pos, it_low->pos()).rotated(cos_a, sin_a), Point(vline.pos, it_high->pos()).rotated(cos_a, sin_a));
++ it;
}
}
}
}
bool FillRectilinear::fill_surface_by_multilines(const Surface *surface, FillParams params, const std::initializer_list<SweepParams> &sweep_params, Polylines &polylines_out)
{
assert(sweep_params.size() > 1);
@@ -2843,42 +2940,8 @@ bool FillRectilinear::fill_surface_by_multilines(const Surface *surface, FillPar
std::pair<float, Point> rotate_vector = this->_infill_direction(surface);
for (const SweepParams &sweep : sweep_params) {
// Rotate polygons so that we can work with vertical lines here
double angle = rotate_vector.first + sweep.angle_base;
ExPolygonWithOffset poly_with_offset(poly_with_offset_base, - angle);
BoundingBox bounding_box = poly_with_offset.bounding_box_src();
// Don't produce infill lines, which fully overlap with the infill perimeter.
coord_t x_min = bounding_box.min.x() + line_width + coord_t(SCALED_EPSILON);
coord_t x_max = bounding_box.max.x() - line_width - coord_t(SCALED_EPSILON);
// extend bounding box so that our pattern will be aligned with other layers
// Transform the reference point to the rotated coordinate system.
Point refpt = rotate_vector.second.rotated(- angle);
// _align_to_grid will not work correctly with positive pattern_shift.
coord_t pattern_shift_scaled = coord_t(scale_(sweep.pattern_shift)) % line_spacing;
refpt.x() -= (pattern_shift_scaled >= 0) ? pattern_shift_scaled : (line_spacing + pattern_shift_scaled);
bounding_box.merge(_align_to_grid(bounding_box.min, Point(line_spacing, line_spacing), refpt));
// Intersect a set of euqally spaced vertical lines wiht expolygon.
// n_vlines = ceil(bbox_width / line_spacing)
const size_t n_vlines = (bounding_box.max.x() - bounding_box.min.x() + line_spacing - 1) / line_spacing;
const double cos_a = cos(angle);
const double sin_a = sin(angle);
for (const SegmentedIntersectionLine &vline : slice_region_by_vertical_lines(poly_with_offset, n_vlines, bounding_box.min.x(), line_spacing))
if (vline.pos > x_min) {
if (vline.pos >= x_max)
break;
for (auto it = vline.intersections.begin(); it != vline.intersections.end();) {
auto it_low = it ++;
assert(it_low->type == SegmentIntersection::OUTER_LOW);
if (it_low->type != SegmentIntersection::OUTER_LOW)
continue;
auto it_high = it;
assert(it_high->type == SegmentIntersection::OUTER_HIGH);
if (it_high->type == SegmentIntersection::OUTER_HIGH) {
fill_lines.emplace_back(Point(vline.pos, it_low->pos()).rotated(cos_a, sin_a), Point(vline.pos, it_high->pos()).rotated(cos_a, sin_a));
++ it;
}
}
}
float angle = rotate_vector.first + sweep.angle_base;
make_fill_lines(ExPolygonWithOffset(poly_with_offset_base, - angle), rotate_vector.second.rotated(-angle), angle, line_width + coord_t(SCALED_EPSILON), line_spacing, coord_t(scale_(sweep.pattern_shift)), fill_lines);
}
if (params.dont_connect() || fill_lines.size() <= 1) {
@@ -2954,4 +3017,29 @@ Polylines FillCubic::fill_surface(const Surface *surface, const FillParams &para
return polylines_out;
}
Polylines FillSupportBase::fill_surface(const Surface *surface, const FillParams &params)
{
assert(! params.full_infill());
Polylines polylines_out;
std::pair<float, Point> rotate_vector = this->_infill_direction(surface);
ExPolygonWithOffset poly_with_offset(surface->expolygon, - rotate_vector.first, float(scale_(this->overlap - 0.5 * this->spacing)));
if (poly_with_offset.n_contours > 0) {
Polylines fill_lines;
coord_t line_spacing = coord_t(scale_(this->spacing) / params.density);
// Create infill lines, keep them vertical.
make_fill_lines(poly_with_offset, rotate_vector.second.rotated(- rotate_vector.first), 0, 0, line_spacing, 0, fill_lines);
// Both the poly_with_offset and polylines_out are rotated, so the infill lines are strictly vertical.
connect_base_support(std::move(fill_lines), poly_with_offset.polygons_outer, poly_with_offset.bounding_box_outer(), polylines_out, this->spacing, params);
// Rotate back by rotate_vector.first
const double cos_a = cos(rotate_vector.first);
const double sin_a = sin(rotate_vector.first);
for (Polyline &pl : polylines_out)
for (Point &pt : pl.points)
pt.rotate(cos_a, sin_a);
}
return polylines_out;
}
} // namespace Slic3r