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:
SoftFever
2025-06-22 23:10:35 +08:00
committed by GitHub
parent fa70582ed1
commit 88fb8187d9
39 changed files with 1551 additions and 714 deletions

View File

@@ -34,7 +34,6 @@ struct SurfaceFillParams
coordf_t overlap = 0.;
// Angle as provided by the region config, in radians.
float angle = 0.f;
bool rotate_angle = true;
// Is bridging used for this fill? Bridging parameters may be used even if this->flow.bridge() is not set.
bool bridge;
// Non-negative for a bridge.
@@ -68,6 +67,9 @@ struct SurfaceFillParams
// Params for lattice infill angles
float lattice_angle_1 = 0.f;
float lattice_angle_2 = 0.f;
float infill_lock_depth = 0;
float skin_infill_depth = 0;
bool symmetric_infill_y_axis = false;
// Params for 2D honeycomb
float infill_overhang_angle = 60.f;
@@ -85,7 +87,6 @@ struct SurfaceFillParams
RETURN_COMPARE_NON_EQUAL(spacing);
RETURN_COMPARE_NON_EQUAL(overlap);
RETURN_COMPARE_NON_EQUAL(angle);
RETURN_COMPARE_NON_EQUAL(rotate_angle);
RETURN_COMPARE_NON_EQUAL(density);
// RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_adjust);
RETURN_COMPARE_NON_EQUAL(anchor_length);
@@ -100,33 +101,36 @@ struct SurfaceFillParams
RETURN_COMPARE_NON_EQUAL(solid_infill_speed);
RETURN_COMPARE_NON_EQUAL(lattice_angle_1);
RETURN_COMPARE_NON_EQUAL(lattice_angle_2);
RETURN_COMPARE_NON_EQUAL(infill_overhang_angle);
RETURN_COMPARE_NON_EQUAL(symmetric_infill_y_axis);
RETURN_COMPARE_NON_EQUAL(infill_lock_depth);
RETURN_COMPARE_NON_EQUAL(skin_infill_depth); RETURN_COMPARE_NON_EQUAL(infill_overhang_angle);
return false;
}
bool operator==(const SurfaceFillParams &rhs) const {
return this->extruder == rhs.extruder &&
this->pattern == rhs.pattern &&
this->spacing == rhs.spacing &&
this->overlap == rhs.overlap &&
this->angle == rhs.angle &&
this->rotate_angle == rhs.rotate_angle &&
this->bridge == rhs.bridge &&
this->bridge_angle == rhs.bridge_angle &&
this->density == rhs.density &&
// this->dont_adjust == rhs.dont_adjust &&
this->anchor_length == rhs.anchor_length &&
this->anchor_length_max == rhs.anchor_length_max &&
this->flow == rhs.flow &&
this->extrusion_role == rhs.extrusion_role &&
this->sparse_infill_speed == rhs.sparse_infill_speed &&
this->top_surface_speed == rhs.top_surface_speed &&
this->solid_infill_speed == rhs.solid_infill_speed &&
this->lattice_angle_1 == rhs.lattice_angle_1 &&
this->lattice_angle_2 == rhs.lattice_angle_2 &&
bool operator==(const SurfaceFillParams &rhs) const {
return this->extruder == rhs.extruder &&
this->pattern == rhs.pattern &&
this->spacing == rhs.spacing &&
this->overlap == rhs.overlap &&
this->angle == rhs.angle &&
this->bridge == rhs.bridge &&
this->bridge_angle == rhs.bridge_angle &&
this->density == rhs.density &&
// this->dont_adjust == rhs.dont_adjust &&
this->anchor_length == rhs.anchor_length &&
this->anchor_length_max == rhs.anchor_length_max &&
this->flow == rhs.flow &&
this->extrusion_role == rhs.extrusion_role &&
this->sparse_infill_speed == rhs.sparse_infill_speed &&
this->top_surface_speed == rhs.top_surface_speed &&
this->solid_infill_speed == rhs.solid_infill_speed &&
this->lattice_angle_1 == rhs.lattice_angle_1 &&
this->lattice_angle_2 == rhs.lattice_angle_2 &&
this->infill_lock_depth == rhs.infill_lock_depth &&
this->skin_infill_depth == rhs.skin_infill_depth &&
this->infill_overhang_angle == rhs.infill_overhang_angle;
}
}
};
struct SurfaceFill {
@@ -602,16 +606,34 @@ void split_solid_surface(size_t layer_id, const SurfaceFill &fill, ExPolygons &n
#endif
}
std::vector<SurfaceFill> group_fills(const Layer &layer)
std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_param)
{
std::vector<SurfaceFill> surface_fills;
// Fill in a map of a region & surface to SurfaceFillParams.
std::set<SurfaceFillParams> set_surface_params;
std::vector<std::vector<const SurfaceFillParams*>> region_to_surface_params(layer.regions().size(), std::vector<const SurfaceFillParams*>());
SurfaceFillParams params;
bool has_internal_voids = false;
const PrintObjectConfig& object_config = layer.object()->config();
auto append_flow_param = [](std::map<Flow, ExPolygons> &flow_params, Flow flow, const ExPolygon &exp) {
auto it = flow_params.find(flow);
if (it == flow_params.end())
flow_params.insert({flow, {exp}});
else
it->second.push_back(exp);
it++;
};
auto append_density_param = [](std::map<float, ExPolygons> &density_params, float density, const ExPolygon &exp) {
auto it = density_params.find(density);
if (it == density_params.end())
density_params.insert({density, {exp}});
else
it->second.push_back(exp);
it++;
};
for (size_t region_id = 0; region_id < layer.regions().size(); ++ region_id) {
const LayerRegion &layerm = *layer.regions()[region_id];
region_to_surface_params[region_id].assign(layerm.fill_surfaces.size(), nullptr);
@@ -628,8 +650,19 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
params.lattice_angle_1 = region_config.lattice_angle_1;
params.lattice_angle_2 = region_config.lattice_angle_2;
params.infill_overhang_angle = region_config.infill_overhang_angle;
if (params.pattern == ipLockedZag) {
params.infill_lock_depth = scale_(region_config.infill_lock_depth);
params.skin_infill_depth = scale_(region_config.skin_infill_depth);
}
if (params.pattern == ipCrossZag || params.pattern == ipLockedZag) {
params.symmetric_infill_y_axis = region_config.symmetric_infill_y_axis;
} else if (params.pattern == ipZigZag) {
if (surface.is_solid()) {
params.symmetric_infill_y_axis = region_config.symmetric_infill_y_axis;
}
if (surface.is_solid()) {
params.density = 100.f;
//FIXME for non-thick bridges, shall we allow a bottom surface pattern?
if (surface.is_solid_infill())
@@ -667,10 +700,8 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
params.bridge_angle = float(surface.bridge_angle);
if (params.extrusion_role == erInternalInfill) {
params.angle = float(Geometry::deg2rad(region_config.infill_direction.value));
params.rotate_angle = (params.pattern == ipRectilinear || params.pattern == ipLine);
} else {
params.angle = float(Geometry::deg2rad(region_config.solid_infill_direction.value));
params.rotate_angle = region_config.rotate_solid_infill_direction;
}
// Calculate the actual flow we'll be using for this infill.
@@ -709,7 +740,28 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
params.anchor_length = std::min(params.anchor_length, params.anchor_length_max);
}
auto it_params = set_surface_params.find(params);
//get locked region param
if (params.pattern == ipLockedZag){
const PrintObject *object = layerm.layer()->object();
auto nozzle_diameter = float(object->print()->config().nozzle_diameter.get_at(layerm.region().extruder(extrusion_role) - 1));
Flow skin_flow = params.bridge ? params.flow : Flow::new_from_config_width(extrusion_role, region_config.skin_infill_line_width, nozzle_diameter, float((surface.thickness == -1) ? layer.height : surface.thickness));
//add skin flow
append_flow_param(lock_param.skin_flow_params, skin_flow, surface.expolygon);
Flow skeleton_flow = params.bridge ? params.flow : Flow::new_from_config_width(extrusion_role, region_config.skeleton_infill_line_width, nozzle_diameter, float((surface.thickness == -1) ? layer.height : surface.thickness)) ;
// add skeleton flow
append_flow_param(lock_param.skeleton_flow_params, skeleton_flow, surface.expolygon);
// add skin density
append_density_param(lock_param.skin_density_params, float(0.01 * region_config.skin_infill_density), surface.expolygon);
// add skin density
append_density_param(lock_param.skeleton_density_params, float(0.01 * region_config.skeleton_infill_density), surface.expolygon);
}
auto it_params = set_surface_params.find(params);
if (it_params == set_surface_params.end())
it_params = set_surface_params.insert(it_params, params);
region_to_surface_params[region_id][&surface - &layerm.fill_surfaces.surfaces.front()] = &(*it_params);
@@ -829,7 +881,6 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
params.density = 100.f;
params.extrusion_role = erSolidInfill;
params.angle = float(Geometry::deg2rad(layerm.region().config().solid_infill_direction.value));
params.rotate_angle = layerm.region().config().rotate_solid_infill_direction;
// calculate the actual flow we'll be using for this infill
params.flow = layerm.flow(frSolidInfill);
params.spacing = params.flow.spacing();
@@ -914,8 +965,8 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
// this->export_region_fill_surfaces_to_svg_debug("10_fill-initial");
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
std::vector<SurfaceFill> surface_fills = group_fills(*this);
LockRegionParam lock_param;
std::vector<SurfaceFill> surface_fills = group_fills(*this, lock_param);
const Slic3r::BoundingBox bbox = this->object()->bounding_box();
const auto resolution = this->object()->print()->config().resolution.value;
@@ -933,14 +984,22 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
f->layer_id = this->id();
f->z = this->print_z;
f->angle = surface_fill.params.angle;
f->rotate_angle = surface_fill.params.rotate_angle;
f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree;
f->print_config = &this->object()->print()->config();
f->print_object_config = &this->object()->config();
if (surface_fill.params.pattern == ipLightning)
f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree;
if (surface_fill.params.pattern == ipConcentricInternal) {
FillConcentricInternal *fill_concentric = dynamic_cast<FillConcentricInternal *>(f.get());
assert(fill_concentric != nullptr);
fill_concentric->print_config = &this->object()->print()->config();
fill_concentric->print_object_config = &this->object()->config();
} else if (surface_fill.params.pattern == ipConcentric) {
FillConcentric *fill_concentric = dynamic_cast<FillConcentric *>(f.get());
assert(fill_concentric != nullptr);
fill_concentric->print_config = &this->object()->print()->config();
fill_concentric->print_object_config = &this->object()->config();
} else if (surface_fill.params.pattern == ipLightning)
dynamic_cast<FillLightning::Filler*>(f.get())->generator = lightning_generator;
// calculate flow spacing for infill pattern generation
bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.bridge;
double link_max_length = 0.;
@@ -979,11 +1038,41 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
params.extrusion_role = surface_fill.params.extrusion_role;
params.using_internal_flow = using_internal_flow;
params.no_extrusion_overlap = surface_fill.params.overlap;
params.config = &layerm->region().config();
auto &region_config = layerm->region().config();
ConfigOptionFloats rotate_angles;
rotate_angles.deserialize( surface_fill.params.extrusion_role == erInternalInfill ? region_config.sparse_infill_rotate_template.value : region_config.solid_infill_rotate_template.value);
auto rotate_angle_idx = f->layer_id % rotate_angles.size();
f->rotate_angle = Geometry::deg2rad(rotate_angles.values[rotate_angle_idx]);
params.config = &region_config;
params.pattern = surface_fill.params.pattern;
if( surface_fill.params.pattern == ipLockedZag ) {
params.locked_zag = true;
params.infill_lock_depth = surface_fill.params.infill_lock_depth;
params.skin_infill_depth = surface_fill.params.skin_infill_depth;
f->set_lock_region_param(lock_param);
}
if (surface_fill.params.pattern == ipCrossZag || surface_fill.params.pattern == ipLockedZag) {
if (f->layer_id % 2 == 0) {
params.horiz_move -= scale_(region_config.infill_shift_step) * (f->layer_id / 2);
} else {
params.horiz_move += scale_(region_config.infill_shift_step) * (f->layer_id / 2);
}
params.symmetric_infill_y_axis = surface_fill.params.symmetric_infill_y_axis;
}
if (surface_fill.params.pattern == ipGrid)
params.can_reverse = false;
for (ExPolygon& expoly : surface_fill.expolygons) {
f->no_overlap_expolygons = intersection_ex(surface_fill.no_overlap_expolygons, ExPolygons() = {expoly}, ApplySafetyOffset::Yes);
f->no_overlap_expolygons = intersection_ex(surface_fill.no_overlap_expolygons, ExPolygons() = {expoly}, ApplySafetyOffset::Yes);
if (params.symmetric_infill_y_axis) {
params.symmetric_y_axis = f->extended_object_bounding_box().center().x();
expoly.symmetric_y(params.symmetric_y_axis);
}
// Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon.
f->spacing = surface_fill.params.spacing;
surface_fill.surface.expolygon = std::move(expoly);
@@ -1023,9 +1112,10 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree, FillLightning::Generator* lightning_generator) const
{
std::vector<SurfaceFill> surface_fills = group_fills(*this);
const Slic3r::BoundingBox bbox = this->object()->bounding_box();
const auto resolution = this->object()->print()->config().resolution.value;
LockRegionParam skin_inner_param;
std::vector<SurfaceFill> surface_fills = group_fills(*this, skin_inner_param);
const Slic3r::BoundingBox bbox = this->object()->bounding_box();
const auto resolution = this->object()->print()->config().resolution.value;
Polylines sparse_infill_polylines{};
@@ -1059,7 +1149,10 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc
case ipTpmsD:
case ipHilbertCurve:
case ipArchimedeanChords:
case ipOctagramSpiral: break;
case ipOctagramSpiral:
case ipZigZag:
case ipCrossZag:
case ipLockedZag: break;
}
// Create the filler object.

View File

@@ -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 &params,

View File

@@ -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

View File

@@ -67,6 +67,9 @@ Fill* Fill::new_from_type(const InfillPattern type)
// BBS: for bottom and top surface only
// Orca: Replace BBS implementation with Prusa implementation
case ipMonotonicLine: return new FillMonotonicLines();
case ipZigZag: return new FillZigZag();
case ipCrossZag: return new FillCrossZag();
case ipLockedZag: return new FillLockedZag();
default: throw Slic3r::InvalidArgument("unknown type");
}
}
@@ -243,7 +246,7 @@ void Fill::_create_gap_fill(const Surface* surface, const FillParams& params, Ex
// 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)
@@ -252,8 +255,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);
@@ -279,8 +282,8 @@ std::pair<float, Point> 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
@@ -354,10 +357,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; }
};
@@ -566,7 +569,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<double> &params,
Polyline &pl1, const Points &contour, const std::vector<double> &params,
ContourIntersectionPoint *cp_start, ContourIntersectionPoint *cp_end, bool clockwise, double take_max_length, double line_half_width)
{
#ifndef NDEBUG
@@ -730,8 +733,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<double, double> &out_interval)
{
const Vec2d line_v0 = line_b - line_a;
@@ -794,8 +797,8 @@ static inline bool line_rounded_thick_segment_collision(
std::pair<double, double> 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
@@ -1155,7 +1158,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
@@ -1295,21 +1298,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;
}
@@ -1367,7 +1370,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;
@@ -1399,7 +1402,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;
@@ -1496,7 +1499,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);
@@ -1521,7 +1524,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;
@@ -1556,6 +1559,18 @@ BoundaryInfillGraph create_boundary_infill_graph(const Polylines &infill_ordered
return out;
}
// The extended bounding box of the whole object that covers any rotation of every layer.
BoundingBox Fill::extended_object_bounding_box() const
{
BoundingBox out = 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.));
}
void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Polygon*> &boundary_src, const BoundingBox &bbox, Polylines &polylines_out, const double spacing, const FillParams &params)
{
assert(! infill_ordered.empty());
@@ -1927,14 +1942,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<double> &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)
@@ -1990,13 +2005,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)
@@ -2297,7 +2312,7 @@ void Fill::connect_base_support(Polylines &&infill_ordered, const std::vector<co
#endif // INFILL_DEBUG_OUTPUT
base_support_extend_infill_lines(infill_ordered, graph, spacing, params);
#ifdef INFILL_DEBUG_OUTPUT
export_partial_infill_to_svg(debug_out_path("connect_base_support-extended-%03d.svg", iRun), graph, infill_ordered, polylines_out);
#endif // INFILL_DEBUG_OUTPUT
@@ -2332,7 +2347,7 @@ void Fill::connect_base_support(Polylines &&infill_ordered, const std::vector<co
};
// Connect infill lines at cp and cpo_next_on_contour.
// If the complete arch cannot be taken, then
// If the complete arch cannot be taken, then
// if (take_first)
// take the infill line at cp and an arc from cp towards cp.next_on_contour.
// else
@@ -2626,7 +2641,7 @@ void Fill::connect_base_support(Polylines &&infill_ordered, const std::vector<co
for (ContourIntersectionPoint &cp : graph.map_infill_end_point_to_boundary) {
const SupportArcCost &cost_prev = arches[(&cp - graph.map_infill_end_point_to_boundary.data()) * 2];
const SupportArcCost &cost_next = *(&cost_prev + 1);
if (cp.contour_not_taken_length_prev > 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)) {
@@ -2643,7 +2658,7 @@ void Fill::connect_base_support(Polylines &&infill_ordered, const std::vector<co
polylines_out.emplace_back(std::move(pl));
}
}
if (cp.contour_not_taken_length_next > 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)) {

View File

@@ -36,6 +36,15 @@ public:
InfillFailedException() : Slic3r::RuntimeError("Infill failed") {}
};
struct LockRegionParam
{
LockRegionParam() {}
std::map<float, ExPolygons> skin_density_params;
std::map<float, ExPolygons> skeleton_density_params;
std::map<Flow, ExPolygons> skin_flow_params;
std::map<Flow, ExPolygons> skeleton_flow_params;
};
struct FillParams
{
bool full_infill() const { return density > 0.9999f; }
@@ -72,6 +81,7 @@ struct FillParams
// For 2D lattice
coordf_t lattice_angle_1 { 0.f };
coordf_t lattice_angle_2 { 0.f };
InfillPattern pattern{ ipRectilinear };
// For 2D Honeycomb
float infill_overhang_angle { 60 };
@@ -85,6 +95,13 @@ struct FillParams
const PrintRegionConfig* config{ nullptr };
bool dont_sort{ false }; // do not sort the lines, just simply connect them
bool can_reverse{true};
float horiz_move{0.0}; //move infill to get cross zag pattern
bool symmetric_infill_y_axis{false};
coord_t symmetric_y_axis{0};
bool locked_zag{false};
float infill_lock_depth{0.0};
float skin_infill_depth{0.0};
};
static_assert(IsTriviallyCopyable<FillParams>::value, "FillParams class is not POD (and it should be - see constructor).");
@@ -102,7 +119,7 @@ public:
// in radians, ccw, 0 = East
float angle;
// Orca: enable angle shifting for layer change
bool rotate_angle{ true };
float rotate_angle{ M_PI/180.0 };
// In scaled coordinates. Maximum lenght of a perimeter segment connecting two infill lines.
// Used by the FillRectilinear2, FillGrid2, FillTriangles, FillStars and FillCubic.
// If left to zero, the links will not be limited.
@@ -135,20 +152,25 @@ public:
static bool use_bridge_flow(const InfillPattern type);
void set_bounding_box(const Slic3r::BoundingBox &bbox) { bounding_box = bbox; }
BoundingBox extended_object_bounding_box() const;
// Use bridge flow for the fill?
virtual bool use_bridge_flow() const { return false; }
// 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 &params);
virtual ThickPolylines fill_surface_arachne(const Surface* surface, const FillParams& params);
virtual void set_lock_region_param(const LockRegionParam &lock_param){};
// BBS: this method is used to fill the ExtrusionEntityCollection.
// It call fill_surface by default
virtual void fill_surface_extrusion(const Surface* surface, const FillParams& params, ExtrusionEntitiesPtr& out);
virtual void fill_surface_extrusion(const Surface *surface, const FillParams &params, ExtrusionEntitiesPtr &out);
protected:
Fill() :
@@ -159,7 +181,7 @@ protected:
overlap(0.),
// Initial angle is undefined.
angle(FLT_MAX),
rotate_angle(true),
rotate_angle(M_PI/180.0),
link_max_length(0),
loop_clipping(0),
// The initial bounding box is empty, therefore undefined.
@@ -168,11 +190,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<float, Point> & /* direction */,
const std::pair<float, Point> & /* 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,
@@ -181,7 +203,7 @@ protected:
ExPolygon expolygon,
ThickPolylines& thick_polylines_out) {}
virtual float _layer_angle(size_t idx) const { return (rotate_angle && (idx & 1)) ? float(M_PI/2.) : 0; }
virtual float _layer_angle(size_t idx) const { return rotate_angle; }
virtual std::pair<float, Point> _infill_direction(const Surface *surface) const;

View File

@@ -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); };

View File

@@ -10,6 +10,7 @@ class FillConcentricInternal : public Fill
public:
~FillConcentricInternal() override = default;
void fill_surface_extrusion(const Surface *surface, const FillParams &params, ExtrusionEntitiesPtr &out) override;
bool is_self_crossing() override { return false; }
protected:
Fill* clone() const override { return new FillConcentricInternal(*this); };

View File

@@ -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(

View File

@@ -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)

View File

@@ -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); };

View File

@@ -20,6 +20,7 @@ class Filler : public Slic3r::Fill
{
public:
~Filler() override = default;
bool is_self_crossing() override { return false; }
Generator *generator { nullptr };
protected:

View File

@@ -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(

View File

@@ -37,6 +37,7 @@ class FillPlanePath : public Fill
{
public:
~FillPlanePath() override = default;
bool is_self_crossing() override { return false; }
protected:
void _fill_surface_single(

View File

@@ -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 &params,
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 &params, 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 &params)
{
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 &params, 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

View File

@@ -16,6 +16,7 @@ public:
Fill* clone() const override { return new FillRectilinear(*this); }
~FillRectilinear() override = default;
Polylines fill_surface(const Surface *surface, const FillParams &params) override;
bool is_self_crossing() override { return false; }
protected:
// Fill by single directional lines, interconnect the lines along perimeters.
@@ -28,6 +29,9 @@ protected:
float pattern_shift;
};
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
@@ -65,6 +69,7 @@ public:
Fill* clone() const override { return new FillGrid(*this); }
~FillGrid() override = default;
Polylines fill_surface(const Surface *surface, const FillParams &params) 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.
@@ -89,6 +94,7 @@ public:
Fill* clone() const override { return new FillTriangles(*this); }
~FillTriangles() override = default;
Polylines fill_surface(const Surface *surface, const FillParams &params) 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.
@@ -101,6 +107,7 @@ public:
Fill* clone() const override { return new FillStars(*this); }
~FillStars() override = default;
Polylines fill_surface(const Surface *surface, const FillParams &params) 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.
@@ -113,6 +120,7 @@ public:
Fill* clone() const override { return new FillCubic(*this); }
~FillCubic() override = default;
Polylines fill_surface(const Surface *surface, const FillParams &params) 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.
@@ -170,6 +178,7 @@ public:
public:
~FillMonotonicLineWGapFill() override = default;
void fill_surface_extrusion(const Surface *surface, const FillParams &params, ExtrusionEntitiesPtr &out) override;
bool is_self_crossing() override { return false; }
protected:
Fill* clone() const override { return new FillMonotonicLineWGapFill(*this); };
@@ -179,9 +188,43 @@ 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; }
};
class FillCrossZag : public FillRectilinear
{
public:
Fill *clone() const override { return new FillCrossZag(*this); }
~FillCrossZag() override = default;
bool has_consistent_pattern() const override { return true; }
};
class FillLockedZag : public FillRectilinear
{
public:
Fill *clone() const override { return new FillLockedZag(*this); }
~FillLockedZag() override = default;
LockRegionParam lock_param;
void fill_surface_extrusion(const Surface *surface, const FillParams &params, ExtrusionEntitiesPtr &out) override;
bool has_consistent_pattern() const override { return true; }
void set_lock_region_param(const LockRegionParam &lock_param) override { this->lock_param = lock_param;};
void fill_surface_locked_zag(const Surface * surface,
const FillParams & params,
std::vector<std::pair<Polylines, Flow>> &multi_width_polyline);
};
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

View File

@@ -25,20 +25,20 @@ public:
// speed in default configuration (degrees)
static constexpr float CorrectionAngle = -45.;
void _fill_surface_single(const FillParams& params,
unsigned int thickness_layers,
const std::pair<float, Point>& direction,
ExPolygon expolygon,
Polylines& polylines_out) override;
bool is_self_crossing() override { return false; }
// Density adjustment to have a good %of weight.
static constexpr double DensityAdjust = 2.1;
// Gyroid upper resolution tolerance (mm^-2)
static constexpr double PatternTolerance = 0.1;
protected:
void _fill_surface_single(
const FillParams &params,
unsigned int thickness_layers,
const std::pair<float, Point> &direction,
ExPolygon expolygon,
Polylines &polylines_out) override;
};
} // namespace Slic3r