mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-05-19 11:23:42 +00:00
Allow specifying rotation patterns for Sparse and Solid infill (#9924)
* 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. Change-Id: I798affa99f4b5c3bd67f47643e67530fb7c3e0cb (cherry picked from commit 2808d04d5deef6f99f9618648e46f11de03efc98) * Add Cross zag and locked-zag for shoes Ported from BambuStudio * wip * sparse infill roratation template * solid_infill_rotate_template * remove rotate_solid_infill_direction * hide sparse infill rotation template for non applicable infill pattern * hide solid_infill_rotate_template for non supported solid infill patterns * update icon * support empty string for ConfigOptionFloats deserialize * fix build errors --------- Co-authored-by: Lukáš Hejl <hejl.lukas@gmail.com>
This commit is contained in:
@@ -987,7 +987,6 @@ static std::vector<SegmentedIntersectionLine> slice_region_by_vertical_lines(con
|
||||
throw;
|
||||
}
|
||||
#endif //INFILL_DEBUG_OUTPUT
|
||||
|
||||
return segs;
|
||||
}
|
||||
|
||||
@@ -1352,8 +1351,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)));
|
||||
}
|
||||
|
||||
static void traverse_graph_generate_polylines(
|
||||
const ExPolygonWithOffset& poly_with_offset, const FillParams& params, const coord_t link_max_length, std::vector<SegmentedIntersectionLine>& segs, Polylines& polylines_out)
|
||||
static void traverse_graph_generate_polylines(const ExPolygonWithOffset &poly_with_offset,
|
||||
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.
|
||||
// Mark an outer only chord as consumed, if the distance is low.
|
||||
@@ -1387,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<coordf_t>().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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1487,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;
|
||||
@@ -2737,6 +2737,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)
|
||||
{
|
||||
// At the end, only the new polylines will be rotated back.
|
||||
@@ -2749,6 +2760,8 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa
|
||||
|
||||
// Rotate polygons so that we can work with vertical lines here
|
||||
std::pair<float, Point> rotate_vector = this->_infill_direction(surface);
|
||||
if (params.locked_zag)
|
||||
rotate_vector.first += float(M_PI/2.);
|
||||
rotate_vector.first += angleBase;
|
||||
|
||||
assert(params.density > 0.0001f && params.density <= 1.f);
|
||||
@@ -2766,11 +2779,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<double>(line_spacing);
|
||||
} else {
|
||||
// extend bounding box so that our pattern will be aligned with other layers
|
||||
@@ -2792,6 +2808,13 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa
|
||||
if (params.full_infill())
|
||||
x0 += (line_spacing + coord_t(SCALED_EPSILON)) / 2;
|
||||
|
||||
int gap_line = params.horiz_move / line_spacing;
|
||||
if (gap_line % 2 == 0) {
|
||||
x0 += params.horiz_move - gap_line * line_spacing;
|
||||
} else {
|
||||
x0 += params.horiz_move - (gap_line - 1) * line_spacing;
|
||||
n_vlines += 1;
|
||||
}
|
||||
#ifdef SLIC3R_DEBUG
|
||||
static int iRun = 0;
|
||||
BoundingBox bbox_svg = poly_with_offset.bounding_box_outer();
|
||||
@@ -2803,7 +2826,6 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa
|
||||
}
|
||||
iRun ++;
|
||||
#endif /* SLIC3R_DEBUG */
|
||||
|
||||
std::vector<SegmentedIntersectionLine> segs = slice_region_by_vertical_lines(poly_with_offset, n_vlines, x0, line_spacing);
|
||||
// Connect by horizontal / vertical links, classify the links based on link_max_length as too long.
|
||||
connect_segment_intersections_by_contours(poly_with_offset, segs, params, link_max_length);
|
||||
@@ -2848,8 +2870,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);
|
||||
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
|
||||
{
|
||||
@@ -2876,6 +2899,11 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa
|
||||
//FIXME rather simplify the paths to avoid very short edges?
|
||||
//assert(! it->has_duplicate_points());
|
||||
it->remove_duplicate_points();
|
||||
|
||||
//get origin direction infill
|
||||
if (params.symmetric_infill_y_axis) {
|
||||
it->symmetric_y(params.symmetric_y_axis);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SLIC3R_DEBUG
|
||||
@@ -2884,6 +2912,8 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa
|
||||
assert(! polyline.has_duplicate_points());
|
||||
#endif /* SLIC3R_DEBUG */
|
||||
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2963,7 +2993,8 @@ bool FillRectilinear::fill_surface_by_multilines(const Surface *surface, FillPar
|
||||
Polylines FillRectilinear::fill_surface(const Surface *surface, const FillParams ¶ms)
|
||||
{
|
||||
Polylines polylines_out;
|
||||
if (params.full_infill()) {
|
||||
// Orca Todo: fow now don't use fill_surface_by_multilines for zipzag infill
|
||||
if (params.full_infill() || params.pattern == ipCrossZag || params.pattern == ipZigZag || params.pattern == ipLockedZag) {
|
||||
if (!fill_surface_by_lines(surface, params, 0.f, 0.f, polylines_out))
|
||||
BOOST_LOG_TRIVIAL(error) << "FillRectilinear::fill_surface() fill_surface_by_lines() failed to fill a region.";
|
||||
} else {
|
||||
@@ -3409,5 +3440,119 @@ void FillMonotonicLineWGapFill::fill_surface_by_lines(const Surface* surface, co
|
||||
}
|
||||
}*/
|
||||
|
||||
void FillLockedZag::fill_surface_locked_zag (const Surface * surface,
|
||||
const FillParams & params,
|
||||
std::vector<std::pair<Polylines, Flow>> &multi_width_polyline)
|
||||
{
|
||||
// merge different part exps
|
||||
// diff skin flow
|
||||
Polylines skin_lines;
|
||||
Polylines skeloton_lines;
|
||||
double offset_threshold = params.skin_infill_depth;
|
||||
double overlap_threshold = params.infill_lock_depth;
|
||||
Surface cross_surface = *surface;
|
||||
Surface zig_surface = *surface;
|
||||
// inner exps
|
||||
// inner union exps
|
||||
ExPolygons zig_expas = offset_ex({surface->expolygon}, -offset_threshold);
|
||||
ExPolygons cross_expas = diff_ex(surface->expolygon, zig_expas);
|
||||
|
||||
bool zig_get = false;
|
||||
FillParams zig_params = params;
|
||||
zig_params.horiz_move = 0;
|
||||
// generate skeleton for diff density
|
||||
auto generate_for_different_flow = [&multi_width_polyline](const std::map<Flow, ExPolygons> &flow_params, const Polylines &polylines) {
|
||||
auto it = flow_params.begin();
|
||||
while (it != flow_params.end()) {
|
||||
ExPolygons region_exp = union_safety_offset_ex(it->second);
|
||||
|
||||
Polylines polys = intersection_pl(polylines, region_exp);
|
||||
multi_width_polyline.emplace_back(polys, it->first);
|
||||
it++;
|
||||
}
|
||||
};
|
||||
|
||||
auto it = this->lock_param.skeleton_density_params.begin();
|
||||
while (it != this->lock_param.skeleton_density_params.end()) {
|
||||
ExPolygons region_exp = union_safety_offset_ex(it->second);
|
||||
ExPolygons exps = intersection_ex(region_exp, zig_expas);
|
||||
zig_params.density = it->first;
|
||||
exps = intersection_ex(offset_ex(exps, overlap_threshold), surface->expolygon);
|
||||
for (ExPolygon &exp : exps) {
|
||||
zig_surface.expolygon = exp;
|
||||
|
||||
Polylines zig_polylines_out = this->fill_surface(&zig_surface, zig_params);
|
||||
skeloton_lines.insert(skeloton_lines.end(), zig_polylines_out.begin(), zig_polylines_out.end());
|
||||
}
|
||||
it++;
|
||||
}
|
||||
|
||||
// set skeleton flow
|
||||
generate_for_different_flow(this->lock_param.skeleton_flow_params, skeloton_lines);
|
||||
|
||||
// skin exps
|
||||
bool cross_get = false;
|
||||
FillParams cross_params = params;
|
||||
cross_params.locked_zag = false;
|
||||
auto skin_density = this->lock_param.skin_density_params.begin();
|
||||
while (skin_density != this->lock_param.skin_density_params.end()) {
|
||||
ExPolygons region_exp = union_safety_offset_ex(skin_density->second);
|
||||
ExPolygons exps = intersection_ex(region_exp, cross_expas);
|
||||
cross_params.density = skin_density->first;
|
||||
for (ExPolygon &exp : exps) {
|
||||
cross_surface.expolygon = exp;
|
||||
Polylines cross_polylines_out = this->fill_surface(&cross_surface, cross_params);
|
||||
skin_lines.insert(skin_lines.end(), cross_polylines_out.begin(), cross_polylines_out.end());
|
||||
}
|
||||
skin_density++;
|
||||
}
|
||||
|
||||
generate_for_different_flow(this->lock_param.skin_flow_params, skin_lines);
|
||||
}
|
||||
|
||||
void FillLockedZag::fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out)
|
||||
{
|
||||
Polylines polylines;
|
||||
ThickPolylines thick_polylines;
|
||||
std::vector<std::pair<Polylines, Flow>> multi_width_polyline;
|
||||
try {
|
||||
this->fill_surface_locked_zag(surface, params, multi_width_polyline);
|
||||
}
|
||||
catch (InfillFailedException&) {}
|
||||
|
||||
if (!thick_polylines.empty() || !multi_width_polyline.empty()) {
|
||||
// Save into layer.
|
||||
ExtrusionEntityCollection* eec = nullptr;
|
||||
out.push_back(eec = new ExtrusionEntityCollection());
|
||||
// Only concentric fills are not sorted.
|
||||
eec->no_sort = this->no_sort();
|
||||
size_t idx = eec->entities.size();
|
||||
{
|
||||
for (std::pair<Polylines, Flow> &poly_with_flow: multi_width_polyline) {
|
||||
// calculate actual flow from spacing (which might have been adjusted by the infill
|
||||
// pattern generator)
|
||||
double flow_mm3_per_mm = poly_with_flow.second.mm3_per_mm();
|
||||
double flow_width = poly_with_flow.second.width();
|
||||
if (params.using_internal_flow) {
|
||||
// if we used the internal flow we're not doing a solid infill
|
||||
// so we can safely ignore the slight variation that might have
|
||||
// been applied to f->spacing
|
||||
} else {
|
||||
Flow new_flow = poly_with_flow.second.with_spacing(this->spacing);
|
||||
flow_mm3_per_mm = new_flow.mm3_per_mm();
|
||||
flow_width = new_flow.width();
|
||||
}
|
||||
extrusion_entities_append_paths(
|
||||
eec->entities, std::move(poly_with_flow.first),
|
||||
params.extrusion_role,
|
||||
flow_mm3_per_mm, float(flow_width), poly_with_flow.second.height());
|
||||
}
|
||||
}
|
||||
if (!params.can_reverse) {
|
||||
for (size_t i = idx; i < eec->entities.size(); i++)
|
||||
eec->entities[i]->set_reverse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
Reference in New Issue
Block a user