mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-07-30 04:42:00 +08:00
SPE-2405: Add Zig Zag infill that is rectilinear infill but with a consistent pattern between layers.
This Zig Zag infill is inspired by the Zig Zag infill in Cura.
This commit is contained in:
parent
4414f24bc7
commit
ceb13b1faa
@ -59,6 +59,13 @@ BoundingBox BoundingBox::rotated(double angle, const Point ¢er) const
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BoundingBox BoundingBox::scaled(double factor) const
|
||||||
|
{
|
||||||
|
BoundingBox out(*this);
|
||||||
|
out.scale(factor);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
template <class PointType, typename APointsType> void
|
template <class PointType, typename APointsType> void
|
||||||
BoundingBoxBase<PointType, APointsType>::scale(double factor)
|
BoundingBoxBase<PointType, APointsType>::scale(double factor)
|
||||||
{
|
{
|
||||||
|
@ -220,7 +220,9 @@ public:
|
|||||||
BoundingBox(const BoundingBoxBase<Vec2crd> &bb): BoundingBox(bb.min, bb.max) {}
|
BoundingBox(const BoundingBoxBase<Vec2crd> &bb): BoundingBox(bb.min, bb.max) {}
|
||||||
BoundingBox(const Points &points) : BoundingBoxBase<Point, Points>(points) {}
|
BoundingBox(const Points &points) : BoundingBoxBase<Point, Points>(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);
|
friend BoundingBox get_extents_rotated(const Points &points, double angle);
|
||||||
};
|
};
|
||||||
|
@ -679,7 +679,8 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc
|
|||||||
case ipGyroid:
|
case ipGyroid:
|
||||||
case ipHilbertCurve:
|
case ipHilbertCurve:
|
||||||
case ipArchimedeanChords:
|
case ipArchimedeanChords:
|
||||||
case ipOctagramSpiral: break;
|
case ipOctagramSpiral:
|
||||||
|
case ipZigZag: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the filler object.
|
// Create the filler object.
|
||||||
|
@ -67,6 +67,7 @@ Fill* Fill::new_from_type(const InfillPattern type)
|
|||||||
case ipSupportBase: return new FillSupportBase();
|
case ipSupportBase: return new FillSupportBase();
|
||||||
case ipLightning: return new FillLightning::Filler();
|
case ipLightning: return new FillLightning::Filler();
|
||||||
case ipEnsuring: return new FillEnsuring();
|
case ipEnsuring: return new FillEnsuring();
|
||||||
|
case ipZigZag: return new FillZigZag();
|
||||||
default: throw Slic3r::InvalidArgument("unknown type");
|
default: throw Slic3r::InvalidArgument("unknown type");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,6 +138,9 @@ public:
|
|||||||
|
|
||||||
virtual bool is_self_crossing() = 0;
|
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.
|
// Perform the fill.
|
||||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||||
virtual ThickPolylines fill_surface_arachne(const Surface *surface, const FillParams ¶ms);
|
virtual ThickPolylines fill_surface_arachne(const Surface *surface, const FillParams ¶ms);
|
||||||
|
@ -1363,8 +1363,11 @@ static SegmentIntersection& end_of_vertical_run(SegmentedIntersectionLine &il, S
|
|||||||
return const_cast<SegmentIntersection&>(end_of_vertical_run(std::as_const(il), std::as_const(start)));
|
return const_cast<SegmentIntersection&>(end_of_vertical_run(std::as_const(il), std::as_const(start)));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void traverse_graph_generate_polylines(
|
static void traverse_graph_generate_polylines(const ExPolygonWithOffset &poly_with_offset,
|
||||||
const ExPolygonWithOffset& poly_with_offset, const FillParams& params, const coord_t link_max_length, std::vector<SegmentedIntersectionLine>& segs, Polylines& polylines_out)
|
const FillParams ¶ms,
|
||||||
|
std::vector<SegmentedIntersectionLine> &segs,
|
||||||
|
const bool consistent_pattern,
|
||||||
|
Polylines &polylines_out)
|
||||||
{
|
{
|
||||||
// For each outer only chords, measure their maximum distance to the bow of the outer contour.
|
// 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.
|
// Mark an outer only chord as consumed, if the distance is low.
|
||||||
@ -1398,34 +1401,28 @@ static void traverse_graph_generate_polylines(
|
|||||||
pointLast = polylines_out.back().points.back();
|
pointLast = polylines_out.back().points.back();
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (i_intersection == -1) {
|
if (i_intersection == -1) {
|
||||||
// The path has been interrupted. Find a next starting point, closest to the previous extruder position.
|
// The path has been interrupted. Find a next starting point.
|
||||||
coordf_t dist2min = std::numeric_limits<coordf_t>().max();
|
for (int i_vline2 = 0; i_vline2 < int(segs.size()); ++i_vline2) {
|
||||||
for (int i_vline2 = 0; i_vline2 < int(segs.size()); ++ i_vline2) {
|
|
||||||
const SegmentedIntersectionLine &vline = segs[i_vline2];
|
const SegmentedIntersectionLine &vline = segs[i_vline2];
|
||||||
if (! vline.intersections.empty()) {
|
if (!vline.intersections.empty()) {
|
||||||
assert(vline.intersections.size() > 1);
|
assert(vline.intersections.size() > 1);
|
||||||
// Even number of intersections with the loops.
|
// Even number of intersections with the loops.
|
||||||
assert((vline.intersections.size() & 1) == 0);
|
assert((vline.intersections.size() & 1) == 0);
|
||||||
assert(vline.intersections.front().type == SegmentIntersection::OUTER_LOW);
|
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()) {
|
if (intrsctn.is_outer()) {
|
||||||
assert(intrsctn.is_low() || i > 0);
|
assert(intrsctn.is_low() || intrsctn_idx > 0);
|
||||||
bool consumed = intrsctn.is_low() ?
|
const bool consumed = intrsctn.is_low() ? intrsctn.consumed_vertical_up : vline.intersections[intrsctn_idx - 1].consumed_vertical_up;
|
||||||
intrsctn.consumed_vertical_up :
|
if (!consumed) {
|
||||||
vline.intersections[i - 1].consumed_vertical_up;
|
i_vline = i_vline2;
|
||||||
if (! consumed) {
|
i_intersection = intrsctn_idx;
|
||||||
coordf_t dist2 = sqr(coordf_t(pointLast(0) - vline.pos)) + sqr(coordf_t(pointLast(1) - intrsctn.pos()));
|
goto found;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1498,9 +1495,13 @@ static void traverse_graph_generate_polylines(
|
|||||||
// 1) Find possible connection points on the previous / next vertical line.
|
// 1) Find possible connection points on the previous / next vertical line.
|
||||||
int i_prev = it->left_horizontal();
|
int i_prev = it->left_horizontal();
|
||||||
int i_next = it->right_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_next_valid = intersection_on_next_vertical_line_valid(segs, i_vline, i_intersection);
|
||||||
bool intersection_horizontal_valid = intersection_prev_valid || intersection_next_valid;
|
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.
|
// 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)
|
if (i_prev != -1)
|
||||||
segs[i_vline - 1].intersections[i_prev].consumed_perimeter_right = true;
|
segs[i_vline - 1].intersections[i_prev].consumed_perimeter_right = true;
|
||||||
@ -2748,6 +2749,17 @@ static void polylines_from_paths(const std::vector<MonotonicRegionLink> &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)
|
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.
|
// At the end, only the new polylines will be rotated back.
|
||||||
@ -2777,11 +2789,14 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa
|
|||||||
return true;
|
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
|
// define flow spacing according to requested density
|
||||||
if (params.full_infill() && !params.dont_adjust) {
|
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<double>(line_spacing);
|
this->spacing = unscale<double>(line_spacing);
|
||||||
} else {
|
} else {
|
||||||
// extend bounding box so that our pattern will be aligned with other layers
|
// extend bounding box so that our pattern will be aligned with other layers
|
||||||
@ -2859,8 +2874,9 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa
|
|||||||
std::vector<MonotonicRegionLink> path = chain_monotonic_regions(regions, poly_with_offset, segs, rng);
|
std::vector<MonotonicRegionLink> path = chain_monotonic_regions(regions, poly_with_offset, segs, rng);
|
||||||
polylines_from_paths(path, poly_with_offset, segs, polylines_out);
|
polylines_from_paths(path, poly_with_offset, segs, polylines_out);
|
||||||
}
|
}
|
||||||
} else
|
} else {
|
||||||
traverse_graph_generate_polylines(poly_with_offset, params, this->link_max_length, segs, polylines_out);
|
traverse_graph_generate_polylines(poly_with_offset, params, segs, this->has_consistent_pattern(), polylines_out);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef SLIC3R_DEBUG
|
#ifdef SLIC3R_DEBUG
|
||||||
{
|
{
|
||||||
|
@ -48,6 +48,9 @@ protected:
|
|||||||
float pattern_shift;
|
float pattern_shift;
|
||||||
};
|
};
|
||||||
bool fill_surface_by_multilines(const Surface *surface, FillParams params, const std::initializer_list<SweepParams> &sweep_params, Polylines &polylines_out);
|
bool fill_surface_by_multilines(const Surface *surface, FillParams params, const std::initializer_list<SweepParams> &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
|
class FillAlignedRectilinear : public FillRectilinear
|
||||||
@ -143,6 +146,15 @@ protected:
|
|||||||
float _layer_angle(size_t idx) const override { return 0.f; }
|
float _layer_angle(size_t idx) const override { return 0.f; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 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 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);
|
Points sample_grid_pattern(const Polygons &polygons, coord_t spacing, const BoundingBox &global_bounding_box);
|
||||||
|
@ -146,7 +146,8 @@ static const t_config_enum_values s_keys_map_InfillPattern {
|
|||||||
{ "octagramspiral", ipOctagramSpiral },
|
{ "octagramspiral", ipOctagramSpiral },
|
||||||
{ "adaptivecubic", ipAdaptiveCubic },
|
{ "adaptivecubic", ipAdaptiveCubic },
|
||||||
{ "supportcubic", ipSupportCubic },
|
{ "supportcubic", ipSupportCubic },
|
||||||
{ "lightning", ipLightning }
|
{ "lightning", ipLightning },
|
||||||
|
{ "zigzag", ipZigZag }
|
||||||
};
|
};
|
||||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(InfillPattern)
|
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(InfillPattern)
|
||||||
|
|
||||||
@ -1482,7 +1483,8 @@ void PrintConfigDef::init_fff_params()
|
|||||||
{ "octagramspiral", L("Octagram Spiral")},
|
{ "octagramspiral", L("Octagram Spiral")},
|
||||||
{ "adaptivecubic", L("Adaptive Cubic")},
|
{ "adaptivecubic", L("Adaptive Cubic")},
|
||||||
{ "supportcubic", L("Support Cubic")},
|
{ "supportcubic", L("Support Cubic")},
|
||||||
{ "lightning", L("Lightning")}
|
{ "lightning", L("Lightning")},
|
||||||
|
{ "zigzag", L("Zig Zag")}
|
||||||
});
|
});
|
||||||
def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipStars));
|
def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipStars));
|
||||||
|
|
||||||
|
@ -106,6 +106,7 @@ enum InfillPattern : int {
|
|||||||
ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipAdaptiveCubic, ipSupportCubic, ipSupportBase,
|
ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipAdaptiveCubic, ipSupportCubic, ipSupportBase,
|
||||||
ipLightning,
|
ipLightning,
|
||||||
ipEnsuring,
|
ipEnsuring,
|
||||||
|
ipZigZag,
|
||||||
ipCount,
|
ipCount,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user