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

View file

@ -217,6 +217,11 @@ int APIENTRY wWinMain(HINSTANCE /* hInstance */, HINSTANCE /* hPrevInstance */,
int wmain(int argc, wchar_t **argv) int wmain(int argc, wchar_t **argv)
{ {
#endif #endif
// Allow the asserts to open message box, such message box allows to ignore the assert and continue with the application.
// Without this call, the seemingly same message box is being opened by the abort() function, but that is too late and
// the application will be killed even if "Ignore" button is pressed.
_set_error_mode(_OUT_TO_MSGBOX);
std::vector<wchar_t*> argv_extended; std::vector<wchar_t*> argv_extended;
argv_extended.emplace_back(argv[0]); argv_extended.emplace_back(argv[0]);

View file

@ -225,24 +225,11 @@ BoundingBox3Base<PointClass>::max_size() const
template coordf_t BoundingBox3Base<Vec3f>::max_size() const; template coordf_t BoundingBox3Base<Vec3f>::max_size() const;
template coordf_t BoundingBox3Base<Vec3d>::max_size() const; template coordf_t BoundingBox3Base<Vec3d>::max_size() const;
// Align a coordinate to a grid. The coordinate may be negative,
// the aligned value will never be bigger than the original one.
static inline coord_t _align_to_grid(const coord_t coord, const coord_t spacing) {
// Current C++ standard defines the result of integer division to be rounded to zero,
// for both positive and negative numbers. Here we want to round down for negative
// numbers as well.
coord_t aligned = (coord < 0) ?
((coord - spacing + 1) / spacing) * spacing :
(coord / spacing) * spacing;
assert(aligned <= coord);
return aligned;
}
void BoundingBox::align_to_grid(const coord_t cell_size) void BoundingBox::align_to_grid(const coord_t cell_size)
{ {
if (this->defined) { if (this->defined) {
min(0) = _align_to_grid(min(0), cell_size); min(0) = Slic3r::align_to_grid(min(0), cell_size);
min(1) = _align_to_grid(min(1), cell_size); min(1) = Slic3r::align_to_grid(min(1), cell_size);
} }
} }

View file

@ -203,6 +203,8 @@ public:
void reverse() override; void reverse() override;
const Point& first_point() const override { return this->paths.front().polyline.points.front(); } const Point& first_point() const override { return this->paths.front().polyline.points.front(); }
const Point& last_point() const override { return this->paths.back().polyline.points.back(); } const Point& last_point() const override { return this->paths.back().polyline.points.back(); }
size_t size() const { return this->paths.size(); }
bool empty() const { return this->paths.empty(); }
double length() const override; double length() const override;
ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); } ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); }
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.

View file

@ -147,7 +147,7 @@ void Fill3DHoneycomb::_fill_surface_single(
// align bounding box to a multiple of our honeycomb grid module // align bounding box to a multiple of our honeycomb grid module
// (a module is 2*$distance since one $distance half-module is // (a module is 2*$distance since one $distance half-module is
// growing while the other $distance half-module is shrinking) // growing while the other $distance half-module is shrinking)
bb.merge(_align_to_grid(bb.min, Point(2*distance, 2*distance))); bb.merge(align_to_grid(bb.min, Point(2*distance, 2*distance)));
// generate pattern // generate pattern
Polylines polylines = makeGrid( Polylines polylines = makeGrid(

File diff suppressed because it is too large Load diff

View file

@ -133,26 +133,10 @@ public:
static void connect_infill(Polylines &&infill_ordered, const Polygons &boundary, const BoundingBox& bbox, Polylines &polylines_out, const double spacing, const FillParams &params); static void connect_infill(Polylines &&infill_ordered, const Polygons &boundary, const BoundingBox& bbox, Polylines &polylines_out, const double spacing, const FillParams &params);
static void connect_infill(Polylines &&infill_ordered, const std::vector<const Polygon*> &boundary, const BoundingBox &bbox, Polylines &polylines_out, double spacing, const FillParams &params); static void connect_infill(Polylines &&infill_ordered, const std::vector<const Polygon*> &boundary, const BoundingBox &bbox, Polylines &polylines_out, double spacing, const FillParams &params);
static coord_t _adjust_solid_spacing(const coord_t width, const coord_t distance); static void connect_base_support(Polylines &&infill_ordered, const std::vector<const Polygon*> &boundary_src, const BoundingBox &bbox, Polylines &polylines_out, const double spacing, const FillParams &params);
static void connect_base_support(Polylines &&infill_ordered, const Polygons &boundary_src, const BoundingBox &bbox, Polylines &polylines_out, const double spacing, const FillParams &params);
// Align a coordinate to a grid. The coordinate may be negative, static coord_t _adjust_solid_spacing(const coord_t width, const coord_t distance);
// the aligned value will never be bigger than the original one.
static coord_t _align_to_grid(const coord_t coord, const coord_t spacing) {
// Current C++ standard defines the result of integer division to be rounded to zero,
// for both positive and negative numbers. Here we want to round down for negative
// numbers as well.
coord_t aligned = (coord < 0) ?
((coord - spacing + 1) / spacing) * spacing :
(coord / spacing) * spacing;
assert(aligned <= coord);
return aligned;
}
static Point _align_to_grid(Point coord, Point spacing)
{ return Point(_align_to_grid(coord(0), spacing(0)), _align_to_grid(coord(1), spacing(1))); }
static coord_t _align_to_grid(coord_t coord, coord_t spacing, coord_t base)
{ return base + _align_to_grid(coord - base, spacing); }
static Point _align_to_grid(Point coord, Point spacing, Point base)
{ return Point(_align_to_grid(coord(0), spacing(0), base(0)), _align_to_grid(coord(1), spacing(1), base(1))); }
}; };
} // namespace Slic3r } // namespace Slic3r

View file

@ -166,7 +166,7 @@ void FillGyroid::_fill_surface_single(
coord_t distance = coord_t(scale_(this->spacing) / density_adjusted); coord_t distance = coord_t(scale_(this->spacing) / density_adjusted);
// align bounding box to a multiple of our grid module // align bounding box to a multiple of our grid module
bb.merge(_align_to_grid(bb.min, Point(2*M_PI*distance, 2*M_PI*distance))); bb.merge(align_to_grid(bb.min, Point(2*M_PI*distance, 2*M_PI*distance)));
// generate pattern // generate pattern
Polylines polylines = make_gyroid_waves( Polylines polylines = make_gyroid_waves(

View file

@ -47,7 +47,7 @@ void FillHoneycomb::_fill_surface_single(
// extend bounding box so that our pattern will be aligned with other layers // extend bounding box so that our pattern will be aligned with other layers
// $bounding_box->[X1] and [Y1] represent the displacement between new bounding box offset and old one // $bounding_box->[X1] and [Y1] represent the displacement between new bounding box offset and old one
// The infill is not aligned to the object bounding box, but to a world coordinate system. Supposedly good enough. // The infill is not aligned to the object bounding box, but to a world coordinate system. Supposedly good enough.
bounding_box.merge(_align_to_grid(bounding_box.min, Point(m.hex_width, m.pattern_height))); bounding_box.merge(align_to_grid(bounding_box.min, Point(m.hex_width, m.pattern_height)));
} }
coord_t x = bounding_box.min(0); coord_t x = bounding_box.min(0);

View file

@ -31,7 +31,7 @@ void FillLine::_fill_surface_single(
} else { } else {
// extend bounding box so that our pattern will be aligned with other layers // extend bounding box so that our pattern will be aligned with other layers
// Transform the reference point to the rotated coordinate system. // Transform the reference point to the rotated coordinate system.
bounding_box.merge(_align_to_grid( bounding_box.merge(align_to_grid(
bounding_box.min, bounding_box.min,
Point(this->_line_spacing, this->_line_spacing), Point(this->_line_spacing, this->_line_spacing),
direction.second.rotated(- direction.first))); direction.second.rotated(- direction.first)));

View file

@ -798,33 +798,44 @@ static std::vector<SegmentedIntersectionLine> slice_region_by_vertical_lines(con
assert(l <= this_x); assert(l <= this_x);
assert(r >= this_x); assert(r >= this_x);
// Calculate the intersection position in y axis. x is known. // Calculate the intersection position in y axis. x is known.
if (p1(0) == this_x) { if (p1.x() == this_x) {
if (p2(0) == this_x) { if (p2.x() == this_x) {
// Ignore strictly vertical segments. // Ignore strictly vertical segments.
continue; 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; is.pos_q = 1;
} else if (p2(0) == this_x) { } else if (p2.x() == this_x) {
is.pos_p = p2(1); 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; is.pos_q = 1;
} else { } else {
// First calculate the intersection parameter 't' as a rational number with non negative denominator. // First calculate the intersection parameter 't' as a rational number with non negative denominator.
if (p2(0) > p1(0)) { if (p2.x() > p1.x()) {
is.pos_p = this_x - p1(0); is.pos_p = this_x - p1.x();
is.pos_q = p2(0) - p1(0); is.pos_q = p2.x() - p1.x();
} else { } else {
is.pos_p = p1(0) - this_x; is.pos_p = p1.x() - this_x;
is.pos_q = p1(0) - p2(0); 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'. // Make an intersection point from the 't'.
is.pos_p *= int64_t(p2(1) - p1(1)); is.pos_p *= int64_t(p2.y() - p1.y());
is.pos_p += p1(1) * int64_t(is.pos_q); is.pos_p += p1.y() * int64_t(is.pos_q);
} }
// +-1 to take rounding into account. // +-1 to take rounding into account.
assert(is.pos() + 1 >= std::min(p1(1), p2(1))); assert(is.pos() + 1 >= std::min(p1.y(), p2.y()));
assert(is.pos() <= std::max(p1(1), p2(1)) + 1); assert(is.pos() <= std::max(p1.y(), p2.y()) + 1);
segs[i].intersections.push_back(is); segs[i].intersections.push_back(is);
} }
} }
@ -844,55 +855,46 @@ static std::vector<SegmentedIntersectionLine> slice_region_by_vertical_lines(con
size_t j = 0; size_t j = 0;
for (size_t i = 0; i < sil.intersections.size(); ++ i) { for (size_t i = 0; i < sil.intersections.size(); ++ i) {
// What is the orientation of the segment at the intersection point? // What is the orientation of the segment at the intersection point?
size_t iContour = sil.intersections[i].iContour; SegmentIntersection &is = sil.intersections[i];
const Points &contour = poly_with_offset.contour(iContour).points; const size_t iContour = is.iContour;
size_t iSegment = sil.intersections[i].iSegment; const Points &contour = poly_with_offset.contour(iContour).points;
size_t iPrev = ((iSegment == 0) ? contour.size() : iSegment) - 1; const size_t iSegment = is.iSegment;
coord_t dir = contour[iSegment](0) - contour[iPrev](0); const size_t iPrev = prev_idx_modulo(iSegment, contour);
bool low = dir > 0; const coord_t dir = contour[iSegment].x() - contour[iPrev].x();
sil.intersections[i].type = poly_with_offset.is_contour_outer(iContour) ? const bool low = dir > 0;
is.type = poly_with_offset.is_contour_outer(iContour) ?
(low ? SegmentIntersection::OUTER_LOW : SegmentIntersection::OUTER_HIGH) : (low ? SegmentIntersection::OUTER_LOW : SegmentIntersection::OUTER_HIGH) :
(low ? SegmentIntersection::INNER_LOW : SegmentIntersection::INNER_HIGH); (low ? SegmentIntersection::INNER_LOW : SegmentIntersection::INNER_HIGH);
if (j > 0 && sil.intersections[i].iContour == sil.intersections[j-1].iContour) { bool take_next = true;
// Two successive intersection points on a vertical line with the same contour. This may be a special case. if (j > 0) {
if (sil.intersections[i].pos() == sil.intersections[j-1].pos()) { SegmentIntersection &is2 = sil.intersections[j - 1];
// Two successive segments meet exactly at the vertical line. if (iContour == is2.iContour && is.pos_q == 1 && is2.pos_q == 1) {
#ifdef SLIC3R_DEBUG // Two successive intersection points on a vertical line with the same contour, both points are end points of their respective contour segments.
// Verify that the segments of sil.intersections[i] and sil.intersections[j-1] are adjoint. if (is.pos_p == is2.pos_p) {
size_t iSegment2 = sil.intersections[j-1].iSegment; // Two successive segments meet exactly at the vertical line.
size_t iPrev2 = ((iSegment2 == 0) ? contour.size() : iSegment2) - 1; // Verify that the segments of sil.intersections[i] and sil.intersections[j-1] are adjoint.
assert(iSegment == iPrev2 || iSegment2 == iPrev); assert(iSegment == prev_idx_modulo(is2.iSegment, contour) || is2.iSegment == iPrev);
#endif /* SLIC3R_DEBUG */ assert(is.type == is2.type);
if (sil.intersections[i].type == sil.intersections[j-1].type) {
// Two successive segments of the same direction (both to the right or both to the left) // Two successive segments of the same direction (both to the right or both to the left)
// meet exactly at the vertical line. // meet exactly at the vertical line.
// Remove the second intersection point. // Remove the second intersection point.
} else { take_next = false;
// This is a loop returning to the same point. } else if (is.type == is2.type) {
// It may as well be a vertex of a loop touching this vertical line. // Two non successive segments of the same direction (both to the right or both to the left)
// Remove both the lines. // meet exactly at the vertical line. That means there is a Z shaped path, where the center segment
-- j; // 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). // Vertical line intersects a contour segment at a general position (not at one of its end points).
if (j < i) if (j < i)
sil.intersections[j] = sil.intersections[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. // 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) { for (size_t i_seg = 0; i_seg < segs.size(); ++ i_seg) {
SegmentedIntersectionLine &sil = segs[i_seg]; SegmentedIntersectionLine &sil = segs[i_seg];
// The intersection points have to be even. // 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 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; 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 // extend bounding box so that our pattern will be aligned with other layers
// Transform the reference point to the rotated coordinate system. // Transform the reference point to the rotated coordinate system.
Point refpt = rotate_vector.second.rotated(- rotate_vector.first); 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; 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); 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, bounding_box.min,
Point(line_spacing, line_spacing), Point(line_spacing, line_spacing),
refpt)); refpt));
@ -2825,6 +2883,45 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa
return true; 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) 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); 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); std::pair<float, Point> rotate_vector = this->_infill_direction(surface);
for (const SweepParams &sweep : sweep_params) { for (const SweepParams &sweep : sweep_params) {
// Rotate polygons so that we can work with vertical lines here // Rotate polygons so that we can work with vertical lines here
double angle = rotate_vector.first + sweep.angle_base; float angle = rotate_vector.first + sweep.angle_base;
ExPolygonWithOffset poly_with_offset(poly_with_offset_base, - angle); 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);
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;
}
}
}
} }
if (params.dont_connect() || fill_lines.size() <= 1) { 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; 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 } // namespace Slic3r

View file

@ -97,6 +97,17 @@ protected:
float _layer_angle(size_t idx) const override { return 0.f; } float _layer_angle(size_t idx) const override { return 0.f; }
}; };
class FillSupportBase : public FillRectilinear
{
public:
Fill* clone() const override { return new FillSupportBase(*this); }
~FillSupportBase() override = default;
Polylines fill_surface(const Surface *surface, const FillParams &params) override;
protected:
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
float _layer_angle(size_t idx) const override { return 0.f; }
};
} // namespace Slic3r } // namespace Slic3r

View file

@ -299,20 +299,23 @@ bool liang_barsky_line_clipping(
// Ugly named variant, that accepts the squared line // Ugly named variant, that accepts the squared line
// Don't call me with a nearly zero length vector! // Don't call me with a nearly zero length vector!
// sympy:
// factor(solve([a * x + b * y + c, x**2 + y**2 - r**2], [x, y])[0])
// factor(solve([a * x + b * y + c, x**2 + y**2 - r**2], [x, y])[1])
template<typename T> template<typename T>
int ray_circle_intersections_r2_lv2_c(T r2, T a, T b, T lv2, T c, std::pair<Eigen::Matrix<T, 2, 1, Eigen::DontAlign>, Eigen::Matrix<T, 2, 1, Eigen::DontAlign>> &out) int ray_circle_intersections_r2_lv2_c(T r2, T a, T b, T lv2, T c, std::pair<Eigen::Matrix<T, 2, 1, Eigen::DontAlign>, Eigen::Matrix<T, 2, 1, Eigen::DontAlign>> &out)
{ {
T x0 = - a * c / lv2; T x0 = - a * c;
T y0 = - b * c / lv2; T y0 = - b * c;
T d = r2 - c * c / lv2; T d2 = r2 * lv2 - c * c;
if (d < T(0)) if (d2 < T(0))
return 0; return 0;
T mult = sqrt(d / lv2); T d = sqrt(d2);
out.first.x() = x0 + b * mult; out.first.x() = (x0 + b * d) / lv2;
out.first.y() = y0 - a * mult; out.first.y() = (y0 - a * d) / lv2;
out.second.x() = x0 - b * mult; out.second.x() = (x0 - b * d) / lv2;
out.second.y() = y0 + a * mult; out.second.y() = (y0 + a * d) / lv2;
return mult == T(0) ? 1 : 2; return d == T(0) ? 1 : 2;
} }
template<typename T> template<typename T>
int ray_circle_intersections(T r, T a, T b, T c, std::pair<Eigen::Matrix<T, 2, 1, Eigen::DontAlign>, Eigen::Matrix<T, 2, 1, Eigen::DontAlign>> &out) int ray_circle_intersections(T r, T a, T b, T c, std::pair<Eigen::Matrix<T, 2, 1, Eigen::DontAlign>, Eigen::Matrix<T, 2, 1, Eigen::DontAlign>> &out)

View file

@ -64,6 +64,7 @@ public:
bool has_duplicate_points() const; bool has_duplicate_points() const;
// Remove exact duplicates, return true if any duplicate has been removed. // Remove exact duplicates, return true if any duplicate has been removed.
bool remove_duplicate_points(); bool remove_duplicate_points();
void clear() { this->points.clear(); }
void append(const Point &point) { this->points.push_back(point); } void append(const Point &point) { this->points.push_back(point); }
void append(const Points &src) { this->append(src.begin(), src.end()); } void append(const Points &src) { this->append(src.begin(), src.end()); }
void append(const Points::const_iterator &begin, const Points::const_iterator &end) { this->points.insert(this->points.end(), begin, end); } void append(const Points::const_iterator &begin, const Points::const_iterator &end) { this->points.insert(this->points.end(), begin, end); }

View file

@ -413,6 +413,25 @@ unscaled(const Eigen::Matrix<Tin, N, EigenArgs...> &v) noexcept
return v.template cast<Tout>() * SCALING_FACTOR; return v.template cast<Tout>() * SCALING_FACTOR;
} }
// Align a coordinate to a grid. The coordinate may be negative,
// the aligned value will never be bigger than the original one.
inline coord_t align_to_grid(const coord_t coord, const coord_t spacing) {
// Current C++ standard defines the result of integer division to be rounded to zero,
// for both positive and negative numbers. Here we want to round down for negative
// numbers as well.
coord_t aligned = (coord < 0) ?
((coord - spacing + 1) / spacing) * spacing :
(coord / spacing) * spacing;
assert(aligned <= coord);
return aligned;
}
inline Point align_to_grid(Point coord, Point spacing)
{ return Point(align_to_grid(coord.x(), spacing.x()), align_to_grid(coord.y(), spacing.y())); }
inline coord_t align_to_grid(coord_t coord, coord_t spacing, coord_t base)
{ return base + align_to_grid(coord - base, spacing); }
inline Point align_to_grid(Point coord, Point spacing, Point base)
{ return Point(align_to_grid(coord.x(), spacing.x(), base.x()), align_to_grid(coord.y(), spacing.y(), base.y())); }
} // namespace Slic3r } // namespace Slic3r
// start Boost // start Boost

View file

@ -180,7 +180,6 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
} else if ( } else if (
opt_key == "complete_objects" opt_key == "complete_objects"
|| opt_key == "filament_type" || opt_key == "filament_type"
|| opt_key == "filament_soluble"
|| opt_key == "first_layer_temperature" || opt_key == "first_layer_temperature"
|| opt_key == "filament_loading_speed" || opt_key == "filament_loading_speed"
|| opt_key == "filament_loading_speed_start" || opt_key == "filament_loading_speed_start"
@ -213,6 +212,12 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|| opt_key == "z_offset") { || opt_key == "z_offset") {
steps.emplace_back(psWipeTower); steps.emplace_back(psWipeTower);
steps.emplace_back(psSkirt); steps.emplace_back(psSkirt);
} else if (opt_key == "filament_soluble") {
steps.emplace_back(psWipeTower);
// Soluble support interface / non-soluble base interface produces non-soluble interface layers below soluble interface layers.
// Thus switching between soluble / non-soluble interface layer material may require recalculation of supports.
//FIXME Killing supports on any change of "filament_soluble" is rough. We should check for each object whether that is necessary.
osteps.emplace_back(posSupportMaterial);
} else if ( } else if (
opt_key == "first_layer_extrusion_width" opt_key == "first_layer_extrusion_width"
|| opt_key == "min_layer_height" || opt_key == "min_layer_height"

View file

@ -1814,8 +1814,8 @@ void PrintConfigDef::init_fff_params()
def->category = L("Support material"); def->category = L("Support material");
def->tooltip = L("Density of the first raft or support layer."); def->tooltip = L("Density of the first raft or support layer.");
def->sidetext = L("%"); def->sidetext = L("%");
def->min = 0; def->min = 10;
def->max = 150; def->max = 100;
def->mode = comExpert; def->mode = comExpert;
def->set_default_value(new ConfigOptionPercent(90)); def->set_default_value(new ConfigOptionPercent(90));

View file

@ -51,7 +51,7 @@ enum class FuzzySkinType {
enum InfillPattern : int { enum InfillPattern : int {
ipRectilinear, ipMonotonic, ipAlignedRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb, ipRectilinear, ipMonotonic, ipAlignedRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb,
ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipAdaptiveCubic, ipSupportCubic, ipCount, ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipAdaptiveCubic, ipSupportCubic, ipSupportBase, ipCount,
}; };
enum class IroningType { enum class IroningType {

View file

@ -1607,7 +1607,7 @@ static inline std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupport
height = layer.lower_layer->height; height = layer.lower_layer->height;
bottom_z = (layer_id == 1) ? slicing_params.object_print_z_min : layer.lower_layer->lower_layer->print_z; bottom_z = (layer_id == 1) ? slicing_params.object_print_z_min : layer.lower_layer->lower_layer->print_z;
} else { } else {
print_z = layer.bottom_z() - slicing_params.gap_object_support; print_z = layer.bottom_z() - slicing_params.gap_support_object;
bottom_z = print_z; bottom_z = print_z;
height = 0.; height = 0.;
// Ignore this contact area if it's too low. // Ignore this contact area if it's too low.
@ -3166,7 +3166,7 @@ static inline void fill_expolygons_with_sheath_generate_paths(
extrusion_entities_append_paths(out, polylines, erSupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height()); extrusion_entities_append_paths(out, polylines, erSupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height());
// Fill in the rest. // Fill in the rest.
fill_expolygons_generate_paths(out, offset_ex(expoly, float(-0.4 * spacing)), filler, fill_params, density, role, flow); fill_expolygons_generate_paths(out, offset_ex(expoly, float(-0.4 * spacing)), filler, fill_params, density, role, flow);
if (no_sort) if (no_sort && ! eec->empty())
dst.emplace_back(eec.release()); dst.emplace_back(eec.release());
} }
} }
@ -3174,8 +3174,13 @@ static inline void fill_expolygons_with_sheath_generate_paths(
// Support layers, partially processed. // Support layers, partially processed.
struct MyLayerExtruded struct MyLayerExtruded
{ {
MyLayerExtruded() : layer(nullptr), m_polygons_to_extrude(nullptr) {} MyLayerExtruded& operator=(MyLayerExtruded &&rhs) {
~MyLayerExtruded() { delete m_polygons_to_extrude; m_polygons_to_extrude = nullptr; } this->layer = rhs.layer;
this->extrusions = std::move(rhs.extrusions);
this->m_polygons_to_extrude = std::move(m_polygons_to_extrude);
rhs.layer = nullptr;
return *this;
}
bool empty() const { bool empty() const {
return layer == nullptr || layer->polygons.empty(); return layer == nullptr || layer->polygons.empty();
@ -3183,7 +3188,7 @@ struct MyLayerExtruded
void set_polygons_to_extrude(Polygons &&polygons) { void set_polygons_to_extrude(Polygons &&polygons) {
if (m_polygons_to_extrude == nullptr) if (m_polygons_to_extrude == nullptr)
m_polygons_to_extrude = new Polygons(std::move(polygons)); m_polygons_to_extrude = std::make_unique<Polygons>(std::move(polygons));
else else
*m_polygons_to_extrude = std::move(polygons); *m_polygons_to_extrude = std::move(polygons);
} }
@ -3204,12 +3209,11 @@ struct MyLayerExtruded
if (m_polygons_to_extrude == nullptr) { if (m_polygons_to_extrude == nullptr) {
// This layer has no extrusions generated yet, if it has no m_polygons_to_extrude (its area to extrude was not reduced yet). // This layer has no extrusions generated yet, if it has no m_polygons_to_extrude (its area to extrude was not reduced yet).
assert(this->extrusions.empty()); assert(this->extrusions.empty());
m_polygons_to_extrude = new Polygons(this->layer->polygons); m_polygons_to_extrude = std::make_unique<Polygons>(this->layer->polygons);
} }
Slic3r::polygons_append(*m_polygons_to_extrude, std::move(*other.m_polygons_to_extrude)); Slic3r::polygons_append(*m_polygons_to_extrude, std::move(*other.m_polygons_to_extrude));
*m_polygons_to_extrude = union_(*m_polygons_to_extrude, true); *m_polygons_to_extrude = union_(*m_polygons_to_extrude, true);
delete other.m_polygons_to_extrude; other.m_polygons_to_extrude.reset();
other.m_polygons_to_extrude = nullptr;
} else if (m_polygons_to_extrude != nullptr) { } else if (m_polygons_to_extrude != nullptr) {
assert(other.m_polygons_to_extrude == nullptr); assert(other.m_polygons_to_extrude == nullptr);
// The other layer has no extrusions generated yet, if it has no m_polygons_to_extrude (its area to extrude was not reduced yet). // The other layer has no extrusions generated yet, if it has no m_polygons_to_extrude (its area to extrude was not reduced yet).
@ -3232,12 +3236,14 @@ struct MyLayerExtruded
} }
// The source layer. It carries the height and extrusion type (bridging / non bridging, extrusion height). // The source layer. It carries the height and extrusion type (bridging / non bridging, extrusion height).
PrintObjectSupportMaterial::MyLayer *layer; PrintObjectSupportMaterial::MyLayer *layer { nullptr };
// Collect extrusions. They will be exported sorted by the bottom height. // Collect extrusions. They will be exported sorted by the bottom height.
ExtrusionEntitiesPtr extrusions; ExtrusionEntitiesPtr extrusions;
private:
// In case the extrusions are non-empty, m_polygons_to_extrude may contain the rest areas yet to be filled by additional support. // In case the extrusions are non-empty, m_polygons_to_extrude may contain the rest areas yet to be filled by additional support.
// This is useful mainly for the loop interfaces, which are generated before the zig-zag infills. // This is useful mainly for the loop interfaces, which are generated before the zig-zag infills.
Polygons *m_polygons_to_extrude; std::unique_ptr<Polygons> m_polygons_to_extrude;
}; };
typedef std::vector<MyLayerExtruded*> MyLayerExtrudedPtrs; typedef std::vector<MyLayerExtruded*> MyLayerExtrudedPtrs;
@ -3763,7 +3769,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
// Prepare fillers. // Prepare fillers.
SupportMaterialPattern support_pattern = m_object_config->support_material_pattern; SupportMaterialPattern support_pattern = m_object_config->support_material_pattern;
bool with_sheath = m_object_config->support_material_with_sheath; bool with_sheath = m_object_config->support_material_with_sheath;
InfillPattern infill_pattern = (support_pattern == smpHoneycomb ? ipHoneycomb : ipRectilinear); InfillPattern infill_pattern = (support_pattern == smpHoneycomb ? ipHoneycomb : ipSupportBase);
std::vector<float> angles; std::vector<float> angles;
angles.push_back(base_angle); angles.push_back(base_angle);
@ -3900,7 +3906,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
std::stable_sort(this->nonempty.begin(), this->nonempty.end(), [](const LayerCacheItem &lc1, const LayerCacheItem &lc2) { return lc1.layer_extruded->layer->height > lc2.layer_extruded->layer->height; }); std::stable_sort(this->nonempty.begin(), this->nonempty.end(), [](const LayerCacheItem &lc1, const LayerCacheItem &lc2) { return lc1.layer_extruded->layer->height > lc2.layer_extruded->layer->height; });
} }
}; };
std::vector<LayerCache> layer_caches(support_layers.size(), LayerCache()); std::vector<LayerCache> layer_caches(support_layers.size());
const auto fill_type_interface = const auto fill_type_interface =
@ -4152,6 +4158,27 @@ void PrintObjectSupportMaterial::generate_toolpaths(
} }
} }
}); });
#ifndef NDEBUG
struct Test {
static bool verify_nonempty(const ExtrusionEntityCollection *collection) {
for (const ExtrusionEntity *ee : collection->entities) {
if (const ExtrusionPath *path = dynamic_cast<const ExtrusionPath*>(ee))
assert(! path->empty());
else if (const ExtrusionMultiPath *multipath = dynamic_cast<const ExtrusionMultiPath*>(ee))
assert(! multipath->empty());
else if (const ExtrusionEntityCollection *eecol = dynamic_cast<const ExtrusionEntityCollection*>(ee)) {
assert(! eecol->empty());
return verify_nonempty(eecol);
} else
assert(false);
}
return true;
}
};
for (const SupportLayer *support_layer : support_layers)
assert(Test::verify_nonempty(&support_layer->support_fills));
#endif // NDEBUG
} }
/* /*