diff --git a/resources/images/param_zigzag.svg b/resources/images/param_zigzag.svg
new file mode 100644
index 000000000..4294e7af7
--- /dev/null
+++ b/resources/images/param_zigzag.svg
@@ -0,0 +1,158 @@
+
+
diff --git a/src/libslic3r/BoundingBox.cpp b/src/libslic3r/BoundingBox.cpp
index 15e9145e8..16ea80b2a 100644
--- a/src/libslic3r/BoundingBox.cpp
+++ b/src/libslic3r/BoundingBox.cpp
@@ -53,6 +53,13 @@ BoundingBox BoundingBox::rotated(double angle, const Point ¢er) const
return out;
}
+BoundingBox BoundingBox::scaled(double factor) const
+{
+ BoundingBox out(*this);
+ out.scale(factor);
+ return out;
+}
+
template void
BoundingBoxBase::scale(double factor)
{
diff --git a/src/libslic3r/BoundingBox.hpp b/src/libslic3r/BoundingBox.hpp
index 22767622f..fa0045a08 100644
--- a/src/libslic3r/BoundingBox.hpp
+++ b/src/libslic3r/BoundingBox.hpp
@@ -211,7 +211,9 @@ public:
BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase(pmin, pmax) {}
BoundingBox(const Points &points) : BoundingBoxBase(points) {}
- BoundingBox inflated(coordf_t delta) const throw() { BoundingBox out(*this); out.offset(delta); return out; }
+ BoundingBox inflated(coordf_t delta) const noexcept { BoundingBox out(*this); out.offset(delta); return out; }
+
+ BoundingBox scaled(double factor) const;
friend BoundingBox get_extents_rotated(const Points &points, double angle);
};
diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp
index 2532c4896..2a2dad1af 100644
--- a/src/libslic3r/Fill/Fill.cpp
+++ b/src/libslic3r/Fill/Fill.cpp
@@ -543,23 +543,24 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc
//case ipEnsuring: continue; break;
case ipLightning:
case ipAdaptiveCubic:
- case ipSupportCubic:
- case ipRectilinear:
- case ipMonotonic:
- case ipAlignedRectilinear:
- case ipGrid:
- case ipTriangles:
- case ipStars:
- case ipCubic:
- case ipLine:
- case ipConcentric:
- case ipHoneycomb:
- case ip3DHoneycomb:
- case ipGyroid:
- case ipHilbertCurve:
- case ipArchimedeanChords:
- case ipOctagramSpiral: break;
- }
+ case ipSupportCubic:
+ case ipRectilinear:
+ case ipMonotonic:
+ case ipAlignedRectilinear:
+ case ipGrid:
+ case ipTriangles:
+ case ipStars:
+ case ipCubic:
+ case ipLine:
+ case ipConcentric:
+ case ipHoneycomb:
+ case ip3DHoneycomb:
+ case ipGyroid:
+ case ipHilbertCurve:
+ case ipArchimedeanChords:
+ case ipOctagramSpiral:
+ case ipZigZag: break;
+ }
// Create the filler object.
std::unique_ptr f = std::unique_ptr(Fill::new_from_type(surface_fill.params.pattern));
diff --git a/src/libslic3r/Fill/Fill3DHoneycomb.hpp b/src/libslic3r/Fill/Fill3DHoneycomb.hpp
index f7a27cf4f..ccb37a36a 100644
--- a/src/libslic3r/Fill/Fill3DHoneycomb.hpp
+++ b/src/libslic3r/Fill/Fill3DHoneycomb.hpp
@@ -15,6 +15,10 @@ public:
Fill* clone() const override { return new Fill3DHoneycomb(*this); };
~Fill3DHoneycomb() override {}
+ // require bridge flow since most of this pattern hangs in air
+ bool use_bridge_flow() const override { return true; }
+ bool is_self_crossing() override { return false; }
+
protected:
void _fill_surface_single(
const FillParams ¶ms,
diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp
index 0578cc3e1..0132ffaa6 100644
--- a/src/libslic3r/Fill/FillAdaptive.hpp
+++ b/src/libslic3r/Fill/FillAdaptive.hpp
@@ -71,6 +71,7 @@ protected:
// may not be optimal as the internal infill lines may get extruded before the long infill
// lines to which the short infill lines are supposed to anchor.
bool no_sort() const override { return false; }
+ bool is_self_crossing() override { return true; }
};
} // namespace FillAdaptive
diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp
index 27b73a31c..f37a857de 100644
--- a/src/libslic3r/Fill/FillBase.cpp
+++ b/src/libslic3r/Fill/FillBase.cpp
@@ -56,6 +56,7 @@ Fill* Fill::new_from_type(const InfillPattern type)
case ipConcentricInternal: return new FillConcentricInternal();
// BBS: for bottom and top surface only
case ipMonotonicLine: return new FillMonotonicLineWGapFill();
+ case ipZigZag: return new FillZigZag();
default: throw Slic3r::InvalidArgument("unknown type");
}
}
@@ -167,7 +168,7 @@ void Fill::fill_surface_extrusion(const Surface* surface, const FillParams& para
// Calculate a new spacing to fill width with possibly integer number of lines,
// the first and last line being centered at the interval ends.
-// This function possibly increases the spacing, never decreases,
+// This function possibly increases the spacing, never decreases,
// and for a narrow width the increase in spacing may become severe,
// therefore the adjustment is limited to 20% increase.
coord_t Fill::_adjust_solid_spacing(const coord_t width, const coord_t distance)
@@ -176,8 +177,8 @@ coord_t Fill::_adjust_solid_spacing(const coord_t width, const coord_t distance)
assert(distance > 0);
// floor(width / distance)
const auto number_of_intervals = coord_t((width - EPSILON) / distance);
- coord_t distance_new = (number_of_intervals == 0) ?
- distance :
+ coord_t distance_new = (number_of_intervals == 0) ?
+ distance :
coord_t((width - EPSILON) / number_of_intervals);
const coordf_t factor = coordf_t(distance_new) / coordf_t(distance);
assert(factor > 1. - 1e-5);
@@ -203,8 +204,8 @@ std::pair Fill::_infill_direction(const Surface *surface) const
// Bounding box is the bounding box of a perl object Slic3r::Print::Object (c++ object Slic3r::PrintObject)
// The bounding box is only undefined in unit tests.
- Point out_shift = empty(this->bounding_box) ?
- surface->expolygon.contour.bounding_box().center() :
+ Point out_shift = empty(this->bounding_box) ?
+ surface->expolygon.contour.bounding_box().center() :
this->bounding_box.center();
#if 0
@@ -278,10 +279,10 @@ struct ContourIntersectionPoint {
bool could_take_next() const throw() { return ! this->consumed && this->contour_not_taken_length_next > SCALED_EPSILON; }
// Could extrude a complete segment from this to this->prev_on_contour.
- bool could_connect_prev() const throw()
+ bool could_connect_prev() const throw()
{ return ! this->consumed && this->prev_on_contour != this && ! this->prev_on_contour->consumed && ! this->prev_trimmed && ! this->prev_on_contour->next_trimmed; }
// Could extrude a complete segment from this to this->next_on_contour.
- bool could_connect_next() const throw()
+ bool could_connect_next() const throw()
{ return ! this->consumed && this->next_on_contour != this && ! this->next_on_contour->consumed && ! this->next_trimmed && ! this->next_on_contour->prev_trimmed; }
};
@@ -490,7 +491,7 @@ static void take(Polyline &pl1, const Polyline &pl2, const Points &contour, Cont
}
static void take_limited(
- Polyline &pl1, const Points &contour, const std::vector ¶ms,
+ Polyline &pl1, const Points &contour, const std::vector ¶ms,
ContourIntersectionPoint *cp_start, ContourIntersectionPoint *cp_end, bool clockwise, double take_max_length, double line_half_width)
{
#ifndef NDEBUG
@@ -654,8 +655,8 @@ static inline SegmentPoint clip_end_segment_and_point(const Points &polyline, do
// Calculate intersection of a line with a thick segment.
// Returns Eucledian parameters of the line / thick segment overlap.
static inline bool line_rounded_thick_segment_collision(
- const Vec2d &line_a, const Vec2d &line_b,
- const Vec2d &segment_a, const Vec2d &segment_b, const double offset,
+ const Vec2d &line_a, const Vec2d &line_b,
+ const Vec2d &segment_a, const Vec2d &segment_b, const double offset,
std::pair &out_interval)
{
const Vec2d line_v0 = line_b - line_a;
@@ -718,8 +719,8 @@ static inline bool line_rounded_thick_segment_collision(
std::pair interval;
if (Geometry::liang_barsky_line_clipping_interval(
Vec2d(line_p0.dot(dir_x), line_p0.dot(dir_y)),
- Vec2d(line_v0.dot(dir_x), line_v0.dot(dir_y)),
- BoundingBoxf(Vec2d(0., - offset), Vec2d(segment_l, offset)),
+ Vec2d(line_v0.dot(dir_x), line_v0.dot(dir_y)),
+ BoundingBoxf(Vec2d(0., - offset), Vec2d(segment_l, offset)),
interval))
extend_interval(interval.first, interval.second);
} else
@@ -1079,7 +1080,7 @@ void mark_boundary_segments_touching_infill(
// Clip the infill polyline by the Eucledian distance along the polyline.
SegmentPoint start_point = clip_start_segment_and_point(polyline.points, clip_distance);
SegmentPoint end_point = clip_end_segment_and_point(polyline.points, clip_distance);
- if (start_point.valid() && end_point.valid() &&
+ if (start_point.valid() && end_point.valid() &&
(start_point.idx_segment < end_point.idx_segment || (start_point.idx_segment == end_point.idx_segment && start_point.t < end_point.t))) {
// The clipped polyline is non-empty.
#ifdef INFILL_DEBUG_OUTPUT
@@ -1219,21 +1220,21 @@ struct BoundaryInfillGraph
};
static Direction dir(const Point &p1, const Point &p2) {
- return p1.x() == p2.x() ?
+ return p1.x() == p2.x() ?
(p1.y() < p2.y() ? Up : Down) :
(p1.x() < p2.x() ? Right : Left);
}
const Direction dir_prev(const ContourIntersectionPoint &cp) const {
assert(cp.prev_on_contour);
- return cp.could_take_prev() ?
+ return cp.could_take_prev() ?
dir(this->point(cp), this->point(*cp.prev_on_contour)) :
Taken;
}
const Direction dir_next(const ContourIntersectionPoint &cp) const {
assert(cp.next_on_contour);
- return cp.could_take_next() ?
+ return cp.could_take_next() ?
dir(this->point(cp), this->point(*cp.next_on_contour)) :
Taken;
}
@@ -1291,7 +1292,7 @@ static inline void mark_boundary_segments_overlapping_infill(
assert(interval.first == 0.);
double len_out = closed_contour_distance_ccw(contour_params[cp.point_idx], contour_params[i], contour_params.back()) + interval.second;
if (len_out < cp.contour_not_taken_length_next) {
- // Leaving the infill line region before exiting cp.contour_not_taken_length_next,
+ // Leaving the infill line region before exiting cp.contour_not_taken_length_next,
// thus at least some of the contour is outside and we will extrude this segment.
inside = false;
break;
@@ -1323,7 +1324,7 @@ static inline void mark_boundary_segments_overlapping_infill(
assert(interval.first == 0.);
double len_out = closed_contour_distance_cw(contour_params[cp.point_idx], contour_params[i], contour_params.back()) + interval.second;
if (len_out < cp.contour_not_taken_length_prev) {
- // Leaving the infill line region before exiting cp.contour_not_taken_length_next,
+ // Leaving the infill line region before exiting cp.contour_not_taken_length_next,
// thus at least some of the contour is outside and we will extrude this segment.
inside = false;
break;
@@ -1420,7 +1421,7 @@ BoundaryInfillGraph create_boundary_infill_graph(const Polylines &infill_ordered
ContourIntersectionPoint *pthis = &out.map_infill_end_point_to_boundary[it->second];
if (pprev) {
pprev->next_on_contour = pthis;
- pthis->prev_on_contour = pprev;
+ pthis->prev_on_contour = pprev;
} else
pfirst = pthis;
contour_intersection_points.emplace_back(pthis);
@@ -1445,7 +1446,7 @@ BoundaryInfillGraph create_boundary_infill_graph(const Polylines &infill_ordered
ip->param = contour_params[ip->point_idx];
// and measure distance to the previous and next intersection point.
const double contour_length = contour_params.back();
- for (ContourIntersectionPoint *ip : contour_intersection_points)
+ for (ContourIntersectionPoint *ip : contour_intersection_points)
if (ip->next_on_contour == ip) {
assert(ip->prev_on_contour == ip);
ip->contour_not_taken_length_prev = ip->contour_not_taken_length_next = contour_length;
@@ -1839,14 +1840,14 @@ static inline void base_support_extend_infill_lines(Polylines &infill, BoundaryI
// The contour is supposed to enter the "forbidden" zone outside of the (left, right) band at tbegin and also at tend.
static inline void emit_loops_in_band(
// Vertical band, which will trim the contour between tbegin and tend.
- coord_t left,
+ coord_t left,
coord_t right,
// Contour and its parametrization.
const Points &contour,
const std::vector &contour_params,
// Span of the parameters of an arch to trim with the vertical band.
double tbegin,
- double tend,
+ double tend,
// Minimum arch length to put into polylines_out. Shorter arches are not necessary to support a dense support infill.
double min_length,
Polylines &polylines_out)
@@ -1902,13 +1903,13 @@ static inline void emit_loops_in_band(
};
enum InOutBand {
- Entering,
+ Entering,
Leaving,
};
class State {
public:
- State(coord_t left, coord_t right, double min_length, Polylines &polylines_out) :
+ State(coord_t left, coord_t right, double min_length, Polylines &polylines_out) :
m_left(left), m_right(right), m_min_length(min_length), m_polylines_out(polylines_out) {}
void add_inner_point(const Point* p)
@@ -2209,7 +2210,7 @@ void Fill::connect_base_support(Polylines &&infill_ordered, const std::vector SCALED_EPSILON &&
+ if (cp.contour_not_taken_length_prev > SCALED_EPSILON &&
(cost_prev.self_loop ?
cost_prev.cost > cap_cost :
cost_prev.cost > cost_veryhigh)) {
@@ -2555,7 +2556,7 @@ void Fill::connect_base_support(Polylines &&infill_ordered, const std::vector SCALED_EPSILON &&
+ if (cp.contour_not_taken_length_next > SCALED_EPSILON &&
(cost_next.self_loop ?
cost_next.cost > cap_cost :
cost_next.cost > cost_veryhigh)) {
diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp
index 5672d55e4..4fbe5c367 100644
--- a/src/libslic3r/Fill/FillBase.hpp
+++ b/src/libslic3r/Fill/FillBase.hpp
@@ -123,6 +123,11 @@ public:
// Do not sort the fill lines to optimize the print head path?
virtual bool no_sort() const { return false; }
+ virtual bool is_self_crossing() = 0;
+
+ // Return true if infill has a consistent pattern between layers.
+ virtual bool has_consistent_pattern() const { return false; }
+
// Perform the fill.
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
virtual ThickPolylines fill_surface_arachne(const Surface* surface, const FillParams& params);
@@ -148,11 +153,11 @@ protected:
// The expolygon may be modified by the method to avoid a copy.
virtual void _fill_surface_single(
- const FillParams & /* params */,
+ const FillParams & /* params */,
unsigned int /* thickness_layers */,
- const std::pair & /* direction */,
+ const std::pair & /* direction */,
ExPolygon /* expolygon */,
- Polylines & /* polylines_out */) {};
+ Polylines & /* polylines_out */) {}
// Used for concentric infill to generate ThickPolylines using Arachne.
virtual void _fill_surface_single(const FillParams& params,
diff --git a/src/libslic3r/Fill/FillConcentric.hpp b/src/libslic3r/Fill/FillConcentric.hpp
index 2e63dcdc8..cdbd15204 100644
--- a/src/libslic3r/Fill/FillConcentric.hpp
+++ b/src/libslic3r/Fill/FillConcentric.hpp
@@ -9,6 +9,7 @@ class FillConcentric : public Fill
{
public:
~FillConcentric() override = default;
+ bool is_self_crossing() override { return false; }
protected:
Fill* clone() const override { return new FillConcentric(*this); };
diff --git a/src/libslic3r/Fill/FillConcentricInternal.hpp b/src/libslic3r/Fill/FillConcentricInternal.hpp
index 9c8442e25..5d38b6608 100644
--- a/src/libslic3r/Fill/FillConcentricInternal.hpp
+++ b/src/libslic3r/Fill/FillConcentricInternal.hpp
@@ -10,6 +10,7 @@ class FillConcentricInternal : public Fill
public:
~FillConcentricInternal() override = default;
void fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) override;
+ bool is_self_crossing() override { return false; }
protected:
Fill* clone() const override { return new FillConcentricInternal(*this); };
diff --git a/src/libslic3r/Fill/FillCrossHatch.hpp b/src/libslic3r/Fill/FillCrossHatch.hpp
index 859d5bd8b..34c46116e 100644
--- a/src/libslic3r/Fill/FillCrossHatch.hpp
+++ b/src/libslic3r/Fill/FillCrossHatch.hpp
@@ -14,6 +14,7 @@ class FillCrossHatch : public Fill
public:
Fill *clone() const override { return new FillCrossHatch(*this); };
~FillCrossHatch() override {}
+ bool is_self_crossing() override { return false; }
protected:
void _fill_surface_single(
diff --git a/src/libslic3r/Fill/FillGyroid.hpp b/src/libslic3r/Fill/FillGyroid.hpp
index ac66dfca9..751a7c257 100644
--- a/src/libslic3r/Fill/FillGyroid.hpp
+++ b/src/libslic3r/Fill/FillGyroid.hpp
@@ -15,6 +15,7 @@ public:
// require bridge flow since most of this pattern hangs in air
bool use_bridge_flow() const override { return false; }
+ bool is_self_crossing() override { return false; }
// Correction applied to regular infill angle to maximize printing
// speed in default configuration (degrees)
diff --git a/src/libslic3r/Fill/FillHoneycomb.hpp b/src/libslic3r/Fill/FillHoneycomb.hpp
index 707e976fd..55be42385 100644
--- a/src/libslic3r/Fill/FillHoneycomb.hpp
+++ b/src/libslic3r/Fill/FillHoneycomb.hpp
@@ -13,6 +13,7 @@ class FillHoneycomb : public Fill
{
public:
~FillHoneycomb() override {}
+ bool is_self_crossing() override { return false; }
protected:
Fill* clone() const override { return new FillHoneycomb(*this); };
diff --git a/src/libslic3r/Fill/FillLightning.hpp b/src/libslic3r/Fill/FillLightning.hpp
index 6e672783a..74aa159b0 100644
--- a/src/libslic3r/Fill/FillLightning.hpp
+++ b/src/libslic3r/Fill/FillLightning.hpp
@@ -20,6 +20,7 @@ class Filler : public Slic3r::Fill
{
public:
~Filler() override = default;
+ bool is_self_crossing() override { return false; }
Generator *generator { nullptr };
protected:
diff --git a/src/libslic3r/Fill/FillLine.hpp b/src/libslic3r/Fill/FillLine.hpp
index 9bf2b97e0..4ad9ca49a 100644
--- a/src/libslic3r/Fill/FillLine.hpp
+++ b/src/libslic3r/Fill/FillLine.hpp
@@ -14,6 +14,7 @@ class FillLine : public Fill
public:
Fill* clone() const override { return new FillLine(*this); };
~FillLine() override = default;
+ bool is_self_crossing() override { return false; }
protected:
void _fill_surface_single(
diff --git a/src/libslic3r/Fill/FillPlanePath.hpp b/src/libslic3r/Fill/FillPlanePath.hpp
index f05f3209a..1371e8506 100644
--- a/src/libslic3r/Fill/FillPlanePath.hpp
+++ b/src/libslic3r/Fill/FillPlanePath.hpp
@@ -37,6 +37,7 @@ class FillPlanePath : public Fill
{
public:
~FillPlanePath() override = default;
+ bool is_self_crossing() override { return false; }
protected:
void _fill_surface_single(
diff --git a/src/libslic3r/Fill/FillRectilinear.cpp b/src/libslic3r/Fill/FillRectilinear.cpp
index 5d5eb0add..6640501a1 100644
--- a/src/libslic3r/Fill/FillRectilinear.cpp
+++ b/src/libslic3r/Fill/FillRectilinear.cpp
@@ -1351,8 +1351,11 @@ static SegmentIntersection& end_of_vertical_run(SegmentedIntersectionLine &il, S
return const_cast(end_of_vertical_run(std::as_const(il), std::as_const(start)));
}
-static void traverse_graph_generate_polylines(
- const ExPolygonWithOffset& poly_with_offset, const FillParams& params, const coord_t link_max_length, std::vector& segs, Polylines& polylines_out)
+static void traverse_graph_generate_polylines(const ExPolygonWithOffset &poly_with_offset,
+ const FillParams ¶ms,
+ std::vector &segs,
+ const bool consistent_pattern,
+ Polylines &polylines_out)
{
// For each outer only chords, measure their maximum distance to the bow of the outer contour.
// Mark an outer only chord as consumed, if the distance is low.
@@ -1386,34 +1389,28 @@ static void traverse_graph_generate_polylines(
pointLast = polylines_out.back().points.back();
for (;;) {
if (i_intersection == -1) {
- // The path has been interrupted. Find a next starting point, closest to the previous extruder position.
- coordf_t dist2min = std::numeric_limits().max();
- for (int i_vline2 = 0; i_vline2 < int(segs.size()); ++ i_vline2) {
+ // The path has been interrupted. Find a next starting point.
+ for (int i_vline2 = 0; i_vline2 < int(segs.size()); ++i_vline2) {
const SegmentedIntersectionLine &vline = segs[i_vline2];
- if (! vline.intersections.empty()) {
+ if (!vline.intersections.empty()) {
assert(vline.intersections.size() > 1);
// Even number of intersections with the loops.
assert((vline.intersections.size() & 1) == 0);
assert(vline.intersections.front().type == SegmentIntersection::OUTER_LOW);
- for (int i = 0; i < int(vline.intersections.size()); ++ i) {
- const SegmentIntersection& intrsctn = vline.intersections[i];
+
+ // For infill that needs to be consistent between layers (like Zig Zag),
+ // we are switching between forward and backward passes based on the line index.
+ const bool forward_pass = !consistent_pattern || (i_vline2 % 2 == 0);
+ for (int i = 0; i < int(vline.intersections.size()); ++i) {
+ const int intrsctn_idx = forward_pass ? i : int(vline.intersections.size()) - i - 1;
+ const SegmentIntersection &intrsctn = vline.intersections[intrsctn_idx];
if (intrsctn.is_outer()) {
- assert(intrsctn.is_low() || i > 0);
- bool consumed = intrsctn.is_low() ?
- intrsctn.consumed_vertical_up :
- vline.intersections[i - 1].consumed_vertical_up;
- if (! consumed) {
- coordf_t dist2 = sqr(coordf_t(pointLast(0) - vline.pos)) + sqr(coordf_t(pointLast(1) - intrsctn.pos()));
- if (dist2 < dist2min) {
- dist2min = dist2;
- i_vline = i_vline2;
- i_intersection = i;
- //FIXME We are taking the first left point always. Verify, that the caller chains the paths
- // by a shortest distance, while reversing the paths if needed.
- //if (polylines_out.empty())
- // Initial state, take the first line, which is the first from the left.
- goto found;
- }
+ assert(intrsctn.is_low() || intrsctn_idx > 0);
+ const bool consumed = intrsctn.is_low() ? intrsctn.consumed_vertical_up : vline.intersections[intrsctn_idx - 1].consumed_vertical_up;
+ if (!consumed) {
+ i_vline = i_vline2;
+ i_intersection = intrsctn_idx;
+ goto found;
}
}
}
@@ -1486,9 +1483,13 @@ static void traverse_graph_generate_polylines(
// 1) Find possible connection points on the previous / next vertical line.
int i_prev = it->left_horizontal();
int i_next = it->right_horizontal();
- bool intersection_prev_valid = intersection_on_prev_vertical_line_valid(segs, i_vline, i_intersection);
+
+ // To ensure pattern consistency between layers for Zig Zag infill, we always
+ // try to connect to the next vertical line and never to the previous vertical line.
+ bool intersection_prev_valid = intersection_on_prev_vertical_line_valid(segs, i_vline, i_intersection) && !consistent_pattern;
bool intersection_next_valid = intersection_on_next_vertical_line_valid(segs, i_vline, i_intersection);
bool intersection_horizontal_valid = intersection_prev_valid || intersection_next_valid;
+
// Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed.
if (i_prev != -1)
segs[i_vline - 1].intersections[i_prev].consumed_perimeter_right = true;
@@ -2736,6 +2737,17 @@ static void polylines_from_paths(const std::vector &path, c
}
}
+// The extended bounding box of the whole object that covers any rotation of every layer.
+BoundingBox FillRectilinear::extended_object_bounding_box() const {
+ BoundingBox out = this->bounding_box;
+ out.merge(Point(out.min.y(), out.min.x()));
+ out.merge(Point(out.max.y(), out.max.x()));
+
+ // The bounding box is scaled by sqrt(2.) to ensure that the bounding box
+ // covers any possible rotations.
+ return out.scaled(sqrt(2.));
+}
+
bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, float angleBase, float pattern_shift, Polylines &polylines_out)
{
// At the end, only the new polylines will be rotated back.
@@ -2765,11 +2777,14 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa
return true;
}
- BoundingBox bounding_box = poly_with_offset.bounding_box_src();
+ // For infill that needs to be consistent between layers (like Zig Zag),
+ // we use bounding box of whole object to match vertical lines between layers.
+ BoundingBox bounding_box_src = poly_with_offset.bounding_box_src();
+ BoundingBox bounding_box = this->has_consistent_pattern() ? this->extended_object_bounding_box() : bounding_box_src;
// define flow spacing according to requested density
if (params.full_infill() && !params.dont_adjust) {
- line_spacing = this->_adjust_solid_spacing(bounding_box.size()(0), line_spacing);
+ line_spacing = this->_adjust_solid_spacing(bounding_box_src.size().x(), line_spacing);
this->spacing = unscale(line_spacing);
} else {
// extend bounding box so that our pattern will be aligned with other layers
@@ -2847,8 +2862,9 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa
std::vector path = chain_monotonic_regions(regions, poly_with_offset, segs, rng);
polylines_from_paths(path, poly_with_offset, segs, polylines_out);
}
- } else
- traverse_graph_generate_polylines(poly_with_offset, params, this->link_max_length, segs, polylines_out);
+ } else {
+ traverse_graph_generate_polylines(poly_with_offset, params, segs, this->has_consistent_pattern(), polylines_out);
+ }
#ifdef SLIC3R_DEBUG
{
diff --git a/src/libslic3r/Fill/FillRectilinear.hpp b/src/libslic3r/Fill/FillRectilinear.hpp
index cb9d74845..1655d3119 100644
--- a/src/libslic3r/Fill/FillRectilinear.hpp
+++ b/src/libslic3r/Fill/FillRectilinear.hpp
@@ -15,6 +15,7 @@ public:
Fill* clone() const override { return new FillRectilinear(*this); }
~FillRectilinear() override = default;
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
+ bool is_self_crossing() override { return false; }
protected:
// Fill by single directional lines, interconnect the lines along perimeters.
@@ -27,6 +28,9 @@ protected:
float pattern_shift;
};
bool fill_surface_by_multilines(const Surface *surface, FillParams params, const std::initializer_list &sweep_params, Polylines &polylines_out);
+
+ // The extended bounding box of the whole object that covers any rotation of every layer.
+ BoundingBox extended_object_bounding_box() const;
};
class FillAlignedRectilinear : public FillRectilinear
@@ -64,6 +68,7 @@ public:
Fill* clone() const override { return new FillGrid(*this); }
~FillGrid() override = default;
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
+ bool is_self_crossing() override { return true; }
protected:
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
@@ -76,6 +81,7 @@ public:
Fill* clone() const override { return new FillTriangles(*this); }
~FillTriangles() override = default;
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
+ bool is_self_crossing() override { return true; }
protected:
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
@@ -88,6 +94,7 @@ public:
Fill* clone() const override { return new FillStars(*this); }
~FillStars() override = default;
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
+ bool is_self_crossing() override { return true; }
protected:
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
@@ -100,6 +107,7 @@ public:
Fill* clone() const override { return new FillCubic(*this); }
~FillCubic() override = default;
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
+ bool is_self_crossing() override { return true; }
protected:
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
@@ -123,6 +131,7 @@ class FillMonotonicLineWGapFill : public Fill
public:
~FillMonotonicLineWGapFill() override = default;
void fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) override;
+ bool is_self_crossing() override { return false; }
protected:
Fill* clone() const override { return new FillMonotonicLineWGapFill(*this); };
@@ -132,9 +141,18 @@ private:
void fill_surface_by_lines(const Surface* surface, const FillParams& params, Polylines& polylines_out);
};
-Points sample_grid_pattern(const ExPolygon& expolygon, coord_t spacing, const BoundingBox& global_bounding_box);
-Points sample_grid_pattern(const ExPolygons& expolygons, coord_t spacing, const BoundingBox& global_bounding_box);
-Points sample_grid_pattern(const Polygons& polygons, coord_t spacing, const BoundingBox& global_bounding_box);
+class FillZigZag : public FillRectilinear
+{
+public:
+ Fill* clone() const override { return new FillZigZag(*this); }
+ ~FillZigZag() override = default;
+
+ bool has_consistent_pattern() const override { return true; }
+};
+
+Points sample_grid_pattern(const ExPolygon &expolygon, coord_t spacing, const BoundingBox &global_bounding_box);
+Points sample_grid_pattern(const ExPolygons &expolygons, coord_t spacing, const BoundingBox &global_bounding_box);
+Points sample_grid_pattern(const Polygons &polygons, coord_t spacing, const BoundingBox &global_bounding_box);
} // namespace Slic3r
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index ffaf975e2..2e17d5a23 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -148,7 +148,8 @@ static t_config_enum_values s_keys_map_InfillPattern {
{ "octagramspiral", ipOctagramSpiral },
{ "supportcubic", ipSupportCubic },
{ "lightning", ipLightning },
- { "crosshatch", ipCrossHatch}
+ { "crosshatch", ipCrossHatch},
+ { "zigzag", ipZigZag }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(InfillPattern)
@@ -1963,6 +1964,7 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("supportcubic");
def->enum_values.push_back("lightning");
def->enum_values.push_back("crosshatch");
+ def->enum_values.push_back("zigzag");
def->enum_labels.push_back(L("Concentric"));
def->enum_labels.push_back(L("Rectilinear"));
def->enum_labels.push_back(L("Grid"));
@@ -1981,6 +1983,7 @@ void PrintConfigDef::init_fff_params()
def->enum_labels.push_back(L("Support Cubic"));
def->enum_labels.push_back(L("Lightning"));
def->enum_labels.push_back(L("Cross Hatch"));
+ def->enum_labels.push_back(L("Zig Zag"));
def->set_default_value(new ConfigOptionEnum(ipCubic));
def = this->add("top_surface_acceleration", coFloats);
diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index ced3901d6..ee807bd2a 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -54,7 +54,7 @@ enum AuthorizationType {
enum InfillPattern : int {
ipConcentric, ipRectilinear, ipGrid, ipLine, ipCubic, ipTriangles, ipStars, ipGyroid, ipHoneycomb, ipAdaptiveCubic, ipMonotonic, ipMonotonicLine, ipAlignedRectilinear, ip3DHoneycomb,
ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipSupportCubic, ipSupportBase, ipConcentricInternal,
- ipLightning, ipCrossHatch,
+ ipLightning, ipCrossHatch, ipZigZag,
ipCount,
};