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:
parent
0db55a0699
commit
8fd731f7a0
@ -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]);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
@ -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 ¶ms);
|
||||
static void connect_infill(Polylines &&infill_ordered, const std::vector<const Polygon*> &boundary, const BoundingBox &bbox, Polylines &polylines_out, double spacing, const FillParams ¶ms);
|
||||
|
||||
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 ¶ms);
|
||||
static void connect_base_support(Polylines &&infill_ordered, const Polygons &boundary_src, const BoundingBox &bbox, Polylines &polylines_out, const double spacing, const FillParams ¶ms);
|
||||
|
||||
// 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
|
||||
|
@ -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(
|
||||
|
@ -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);
|
||||
|
@ -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)));
|
||||
|
@ -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 ¶
|
||||
return polylines_out;
|
||||
}
|
||||
|
||||
Polylines FillSupportBase::fill_surface(const Surface *surface, const FillParams ¶ms)
|
||||
{
|
||||
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
|
||||
|
||||
|
@ -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 ¶ms) 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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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); }
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user