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)
{
#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;
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<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)
{
if (this->defined) {
min(0) = _align_to_grid(min(0), cell_size);
min(1) = _align_to_grid(min(1), cell_size);
min(0) = Slic3r::align_to_grid(min(0), cell_size);
min(1) = Slic3r::align_to_grid(min(1), cell_size);
}
}

View File

@ -203,6 +203,8 @@ public:
void reverse() override;
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(); }
size_t size() const { return this->paths.size(); }
bool empty() const { return this->paths.empty(); }
double length() const override;
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.

View File

@ -147,7 +147,7 @@ void Fill3DHoneycomb::_fill_surface_single(
// align bounding box to a multiple of our honeycomb grid module
// (a module is 2*$distance since one $distance half-module is
// 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
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 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,
// 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))); }
static coord_t _adjust_solid_spacing(const coord_t width, const coord_t distance);
};
} // namespace Slic3r

View File

@ -166,7 +166,7 @@ void FillGyroid::_fill_surface_single(
coord_t distance = coord_t(scale_(this->spacing) / density_adjusted);
// 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
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
// $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.
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);

View File

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

View File

@ -97,6 +97,17 @@ protected:
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

View File

@ -299,20 +299,23 @@ bool liang_barsky_line_clipping(
// Ugly named variant, that accepts the squared line
// 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>
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 y0 = - b * c / lv2;
T d = r2 - c * c / lv2;
if (d < T(0))
T x0 = - a * c;
T y0 = - b * c;
T d2 = r2 * lv2 - c * c;
if (d2 < T(0))
return 0;
T mult = sqrt(d / lv2);
out.first.x() = x0 + b * mult;
out.first.y() = y0 - a * mult;
out.second.x() = x0 - b * mult;
out.second.y() = y0 + a * mult;
return mult == T(0) ? 1 : 2;
T d = sqrt(d2);
out.first.x() = (x0 + b * d) / lv2;
out.first.y() = (y0 - a * d) / lv2;
out.second.x() = (x0 - b * d) / lv2;
out.second.y() = (y0 + a * d) / lv2;
return d == T(0) ? 1 : 2;
}
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)

View File

@ -64,6 +64,7 @@ public:
bool has_duplicate_points() const;
// Remove exact duplicates, return true if any duplicate has been removed.
bool remove_duplicate_points();
void clear() { this->points.clear(); }
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::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;
}
// 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
// start Boost

View File

@ -180,7 +180,6 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
} else if (
opt_key == "complete_objects"
|| opt_key == "filament_type"
|| opt_key == "filament_soluble"
|| opt_key == "first_layer_temperature"
|| opt_key == "filament_loading_speed"
|| opt_key == "filament_loading_speed_start"
@ -213,6 +212,12 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|| opt_key == "z_offset") {
steps.emplace_back(psWipeTower);
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 (
opt_key == "first_layer_extrusion_width"
|| opt_key == "min_layer_height"

View File

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

View File

@ -51,7 +51,7 @@ enum class FuzzySkinType {
enum InfillPattern : int {
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 {

View File

@ -1607,7 +1607,7 @@ static inline std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupport
height = layer.lower_layer->height;
bottom_z = (layer_id == 1) ? slicing_params.object_print_z_min : layer.lower_layer->lower_layer->print_z;
} 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;
height = 0.;
// 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());
// Fill in the rest.
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());
}
}
@ -3174,8 +3174,13 @@ static inline void fill_expolygons_with_sheath_generate_paths(
// Support layers, partially processed.
struct MyLayerExtruded
{
MyLayerExtruded() : layer(nullptr), m_polygons_to_extrude(nullptr) {}
~MyLayerExtruded() { delete m_polygons_to_extrude; m_polygons_to_extrude = nullptr; }
MyLayerExtruded& operator=(MyLayerExtruded &&rhs) {
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 {
return layer == nullptr || layer->polygons.empty();
@ -3183,7 +3188,7 @@ struct MyLayerExtruded
void set_polygons_to_extrude(Polygons &&polygons) {
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
*m_polygons_to_extrude = std::move(polygons);
}
@ -3204,12 +3209,11 @@ struct MyLayerExtruded
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).
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));
*m_polygons_to_extrude = union_(*m_polygons_to_extrude, true);
delete other.m_polygons_to_extrude;
other.m_polygons_to_extrude = nullptr;
other.m_polygons_to_extrude.reset();
} else if (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).
@ -3232,12 +3236,14 @@ struct MyLayerExtruded
}
// 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.
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.
// 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;
@ -3763,7 +3769,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
// Prepare fillers.
SupportMaterialPattern support_pattern = m_object_config->support_material_pattern;
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;
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::vector<LayerCache> layer_caches(support_layers.size(), LayerCache());
std::vector<LayerCache> layer_caches(support_layers.size());
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
}
/*