OrcaSlicer latest
OrcaSlicer
Loading...
Searching...
No Matches
Slic3r::PrintObject Class Reference

#include </home/runner/work/OrcaSlicer/OrcaSlicer/src/libslic3r/Print.hpp>

Inheritance diagram for Slic3r::PrintObject:
Collaboration diagram for Slic3r::PrintObject:

Classes

struct  BeltDroppedFill

Public Types

typedef PrintState< PrintObjectStep, COUNT > PrintObjectState
using Timestamp = uint64_t

Public Member Functions

const Vec3crd & size () const
const PrintObjectConfig & config () const
void configBrimWidth (double m)
ConstLayerPtrsAdaptor layers () const
ConstSupportLayerPtrsAdaptor support_layers () const
const Transform3d & trafo () const
Transform3d trafo_centered () const
Transform3d trafo_sliced () const
const PrintInstances & instances () const
const std::vector< BoundingBox > & separated_body_bboxes () const
PrintInstances & instances ()
LayerPtrs & layers ()
SupportLayerPtrs & support_layers ()
BoundingBox bounding_box () const
coord_t height () const
double max_z () const
const Point & center_offset () const
const AdaptiveInfillField * adaptive_infill_field (AdaptiveInfillMode mode) const
void generate_support_preview ()
const std::vector< VolumeSlices > & firstLayerObjSlice () const
std::vector< VolumeSlices > & firstLayerObjSliceMod ()
const std::vector< groupedVolumeSlices > & firstLayerObjGroups () const
std::vector< groupedVolumeSlices > & firstLayerObjGroupsMod ()
bool has_brim () const
bool has_belt_brim () const
unsigned int belt_brim_filament () const
const std::vector< ExtrusionEntityCollection > & belt_brim_by_layer () const
const std::vector< BeltBrimBand > & belt_brim_prologue () const
void clear_belt_brim ()
void set_belt_brim (std::vector< ExtrusionEntityCollection > &&by_layer, std::vector< ExPolygons > &&areas, std::vector< BeltBrimBand > &&prologue)
const ExtrusionEntityCollection & object_skirt () const
size_t total_layer_count () const
size_t layer_count () const
void clear_layers ()
const Layer * get_layer (int idx) const
Layer * get_layer (int idx)
const Layer * get_layer_at_printz (coordf_t print_z) const
Layer * get_layer_at_printz (coordf_t print_z)
const Layer * get_layer_at_printz (coordf_t print_z, coordf_t epsilon) const
Layer * get_layer_at_printz (coordf_t print_z, coordf_t epsilon)
int get_layer_idx_get_printz (coordf_t print_z, coordf_t epsilon)
const Layer * get_layer_at_bottomz (coordf_t bottom_z, coordf_t epsilon) const
Layer * get_layer_at_bottomz (coordf_t bottom_z, coordf_t epsilon)
const Layer * get_first_layer_bellow_printz (coordf_t print_z, coordf_t epsilon) const
Layer * add_layer (int id, coordf_t height, coordf_t print_z, coordf_t slice_z)
SupportLayer * add_tree_support_layer (int id, coordf_t height, coordf_t print_z, coordf_t slice_z)
std::shared_ptr< TreeSupportData > alloc_tree_support_preview_cache ()
void clear_tree_support_preview_cache ()
size_t support_layer_count () const
void clear_support_layers ()
SupportLayer * get_support_layer (int idx)
const SupportLayer * get_support_layer_at_printz (coordf_t print_z, coordf_t epsilon) const
SupportLayer * get_support_layer_at_printz (coordf_t print_z, coordf_t epsilon)
SupportLayer * add_support_layer (int id, int interface_id, coordf_t height, coordf_t print_z)
SupportLayerPtrs::iterator insert_support_layer (SupportLayerPtrs::iterator pos, size_t id, size_t interface_id, coordf_t height, coordf_t print_z, coordf_t slice_z)
const SlicingParameters & slicing_parameters () const
size_t num_printing_regions () const throw ()
const PrintRegion & printing_region (size_t idx) const throw ()
std::vector< std::reference_wrapper< const PrintRegion > > all_regions () const
const PrintObjectRegions * shared_regions () const throw ()
bool has_support () const
bool has_raft () const
bool has_support_material () const
bool is_mm_painted () const
bool is_fuzzy_skin_painted () const
std::vector< unsigned int > object_extruders () const
std::vector< unsigned int > printing_extruders () const
void slice ()
std::vector< Polygons > slice_support_volumes (const ModelVolumeType model_volume_type) const
std::vector< Polygons > slice_support_blockers () const
std::vector< Polygons > slice_support_enforcers () const
std::vector< ExPolygons > slice_single_volume_regions (const ModelVolume *volume) const
void project_and_append_custom_facets (bool seam, EnforcerBlockerType type, std::vector< Polygons > &expolys, std::vector< std::pair< Vec3f, Vec3f > > *vertical_points=nullptr) const
BoundingBox get_first_layer_bbox (float &area, float &layer_height, std::string &name)
void get_certain_layers (float start, float end, std::vector< LayerPtrs > &out, std::vector< BoundingBox > &boundingbox_objects)
Points get_instances_shift_without_plate_offset () const
PrintObject * get_shared_object () const
void set_shared_object (PrintObject *object)
void clear_shared_object ()
void copy_layers_from_shared_object ()
void copy_layers_overhang_from_shared_object ()
size_t get_id () const
void set_id (size_t id)
double belt_global_z_offset () const
Print * print ()
const Print * print () const
bool is_step_done (PrintObjectStep step) const
PrintStateBase::StateWithTimeStamp step_state_with_timestamp (PrintObjectStep step) const
PrintStateBase::StateWithWarnings step_state_with_warnings (PrintObjectStep step) const
const ModelObject * model_object () const
ModelObject * model_object ()
ObjectID id () const
virtual Timestamp timestamp () const

Static Public Member Functions

template<typename PolysType>
static void remove_bridges_from_contacts (const Layer *lower_layer, const Layer *current_layer, float extrusion_width, PolysType *overhang_regions, float max_bridge_length=scale_(10), bool break_bridge=false)
static bool update_layer_height_profile (const ModelObject &model_object, const SlicingParameters &slicing_parameters, std::vector< coordf_t > &layer_height_profile)
static SlicingParameters slicing_parameters (const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z, const Vec3d &object_shrinkage_compensation, std::vector< int > variant_index=std::vector< int >())
static bool has_support (const PrintObjectConfig &config)
static bool has_raft (const PrintObjectConfig &config)
static bool has_support_material (const PrintObjectConfig &config)

Public Attributes

BoundingBox firstLayerObjectBrimBoundingBox
std::vector< int > object_first_layer_wall_extruders

Static Public Attributes

static bool clip_multipart_objects = true
static bool infill_only_where_needed = false

Protected Member Functions

bool set_started (PrintObjectStep step)
PrintStateBase::TimeStamp set_done (PrintObjectStep step)
bool invalidate_steps (StepTypeIterator step_begin, StepTypeIterator step_end)
bool invalidate_steps (std::initializer_list< PrintObjectStep > il)
bool invalidate_all_steps_without_cancel ()
bool is_step_started_unguarded (PrintObjectStep step) const
bool is_step_done_unguarded (PrintObjectStep step) const
void active_step_add_warning (PrintStateBase::WarningLevel warning_level, const std::string &message, PrintStateBase::SlicingNotificationType message_id=PrintStateBase::SlicingDefaultNotification)
void throw_if_canceled ()
void status_update_warnings (PrintBase *print, int step, PrintStateBase::WarningLevel warning_level, const std::string &message, PrintStateBase::SlicingNotificationType message_id=PrintStateBase::SlicingDefaultNotification)
void emptylayer_update_msg (PrintBase *print, int type, const std::string &message, bool overwrite)
void set_new_unique_id ()
void set_invalid_id ()
void copy_id (const ObjectBase &rhs)
virtual void assign_new_unique_ids_recursive ()

Static Protected Member Functions

static std::mutex & state_mutex (PrintBase *print)
static std::function< void()> cancel_callback (PrintBase *print)

Protected Attributes

friend PrintType
Print * m_print
ModelObject * m_model_object

Private Types

typedef PrintObjectBaseWithState< Print, PrintObjectStep, posCount > Inherited

Private Member Functions

 PrintObject (Print *print, ModelObject *model_object, const Transform3d &trafo, PrintInstances &&instances)
 ~PrintObject ()
void config_apply (const ConfigBase &other, bool ignore_nonexistent=false)
void config_apply_only (const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent=false)
PrintBase::ApplyStatus set_instances (PrintInstances &&instances)
bool invalidate_step (PrintObjectStep step)
bool invalidate_all_steps ()
bool invalidate_state_by_config_options (const ConfigOptionResolver &old_config, const ConfigOptionResolver &new_config, const std::vector< t_config_option_key > &opt_keys)
void update_slicing_parameters ()
void make_perimeters ()
void prepare_infill ()
void infill ()
void ironing ()
bool need_z_contouring () const
void contour_z ()
void generate_support_material (bool with_belt_brim=true)
void generate_belt_brim ()
void estimate_curled_extrusions ()
void simplify_extrusion_path ()
std::vector< std::set< int > > detect_extruder_geometric_unprintables () const
 Determines the unprintable filaments for each extruder based on its printable area.
void slice_volumes ()
void belt_shift_layer_grid (double delta)
size_t belt_truncate_layers_above (coordf_t z)
void belt_restore_truncated_layers ()
size_t belt_drop_unclaimed_fills (Layer *layer, const std::function< bool(const ExtrusionEntity *)> &claimed)
void belt_restore_dropped_fills ()
void belt_undo_purge_plan ()
ExPolygons _shrink_contour_holes (double contour_delta, double hole_delta, const ExPolygons &polys) const
void detect_overhangs_for_lift ()
void clear_overhangs_for_lift ()
void _transform_hole_to_polyholes ()
void detect_surfaces_type ()
void process_external_surfaces ()
void discover_vertical_shells ()
void bridge_over_infill ()
void clip_fill_surfaces ()
void discover_horizontal_shells ()
void combine_infill ()
void _generate_support_material ()
FillAdaptive::RegionOctrees prepare_adaptive_infill_data (const std::vector< std::pair< const Surface *, const Layer * > > &surfaces_w_layer) const
FillLightning::GeneratorPtr prepare_lightning_infill_data ()
AdaptiveInfillFields prepare_adaptive_infill_fields () const
SupportNecessaryType is_support_necessary ()
void apply_conical_overhang ()
template<class Archive>
void serialize (Archive &ar)

Static Private Member Functions

static PrintObjectConfig object_config_from_model_object (const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders, std::vector< int > &variant_index)
static ObjectID generate_new_id ()
template<class Archive>
static void load_and_construct (Archive &ar, cereal::construct< ObjectBase > &construct)

Private Attributes

Vec3crd m_size
double m_max_z
PrintObjectConfig m_config
Transform3d m_trafo = Transform3d::Identity()
std::vector< PrintInstance > m_instances
Point m_center_offset
PrintObjectRegions * m_shared_regions { nullptr }
SlicingParameters m_slicing_params
LayerPtrs m_layers
LayerPtrs m_belt_truncated_layers
std::vector< BeltDroppedFill > m_belt_dropped_fills
SupportLayerPtrs m_support_layers
std::vector< ExtrusionEntityCollection > m_belt_brim_by_layer
std::vector< ExPolygons > m_belt_brim_areas_by_layer
std::vector< BeltBrimBand > m_belt_brim_prologue
std::shared_ptr< TreeSupportData > m_tree_support_preview_cache
bool m_typed_slices = false
FillAdaptive::RegionOctrees m_adaptive_fill_octrees
std::vector< BoundingBox > m_separated_body_bboxes
FillLightning::GeneratorPtr m_lightning_generator
AdaptiveInfillFields m_adaptive_infill_fields
std::vector< VolumeSlices > firstLayerObjSliceByVolume
std::vector< groupedVolumeSlices > firstLayerObjSliceByGroups
ExtrusionEntityCollection m_skirt
PrintObject * m_shared_object { nullptr }
double m_belt_global_z_offset { 0.0 }
bool m_belt_brim_pending { false }
double m_belt_min_z { 0.0 }
double m_belt_floor_z_shift_cached { 0.0 }
bool m_belt_floor_z_shift_cache_valid { false }
size_t m_id {0}
PrintState< PrintObjectStep, COUNT > m_state

Static Private Attributes

static size_t s_last_id = 0

Friends

class Print

Detailed Description


Class Documentation

◆ Slic3r::PrintObject::BeltDroppedFill

struct Slic3r::PrintObject::BeltDroppedFill
Collaboration diagram for Slic3r::PrintObject::BeltDroppedFill:
Class Members
ExtrusionEntity * entity { nullptr }
size_t index { 0 }
Layer * layer { nullptr }
size_t region_idx { 0 }

Member Typedef Documentation

◆ Inherited

◆ PrintObjectState

typedef PrintState<PrintObjectStep, COUNT> Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, COUNT >::PrintObjectState
inherited

◆ Timestamp

using Slic3r::ObjectBase::Timestamp = uint64_t
inherited

Constructor & Destructor Documentation

◆ PrintObject()

Slic3r::PrintObject::PrintObject ( Print * print,
ModelObject * model_object,
const Transform3d & trafo,
PrintInstances && instances )
private
134 :
136 m_trafo(trafo),
137 // BBS
139{
140 // Compute centering offet to be applied to our meshes so that we work with smaller coordinates
141 // requiring less bits to represent Clipper coordinates.
142
143 // Snug bounding box of a rotated and scaled object by the 1st instantion, without the instance translation applied.
144 // All the instances share the transformation matrix with the exception of translation in XY and rotation by Z,
145 // therefore a bounding box from 1st instance of a ModelObject is good enough for calculating the object center,
146 // snug height and an approximate bounding box in XY.
147 BoundingBoxf3 bbox = model_object->raw_bounding_box();
148 Vec3d bbox_center = bbox.center();
149
150 // We may need to rotate the bbox / bbox_center from the original instance to the current instance.
151 double z_diff = Geometry::rotation_diff_z(model_object->instances.front()->get_rotation(), instances.front().model_instance->get_rotation());
152 if (std::abs(z_diff) > EPSILON) {
153 auto z_rot = Eigen::AngleAxisd(z_diff, Vec3d::UnitZ());
154 bbox = bbox.transformed(Transform3d(z_rot));
155 bbox_center = (z_rot * bbox_center).eval();
156 }
157
158 // Center of the transformed mesh (without translation).
159 m_center_offset = Point::new_scale(bbox_center.x(), bbox_center.y());
160 // Size of the transformed mesh. This bounding may not be snug in XY plane, but it is snug in Z.
161 m_size = (bbox.size() * (1. / SCALING_FACTOR)).cast<coord_t>();
162 m_max_z = scaled(model_object->instance_bounding_box(0).max(2));
163
164 this->set_instances(std::move(instances));
165}
#define EPSILON
Definition ColorUtils.cpp:64
static Point new_scale(coordf_t x, coordf_t y)
Definition Point.hpp:214
PrintObjectBaseWithState(Print *print, ModelObject *model_object)
Definition PrintBase.hpp:646
const ModelObject * model_object() const
Definition PrintBase.hpp:346
const PrintInstances & instances() const
Definition Print.hpp:383
std::shared_ptr< TreeSupportData > m_tree_support_preview_cache
Definition Print.hpp:701
Vec3crd m_size
Definition Print.hpp:665
const Transform3d & trafo() const
Definition Print.hpp:376
double m_max_z
Definition Print.hpp:666
Point m_center_offset
Definition Print.hpp:674
PrintBase::ApplyStatus set_instances(PrintInstances &&instances)
Definition PrintObject.cpp:175
Transform3d m_trafo
Definition Print.hpp:669
double SCALING_FACTOR
Definition libslic3r.cpp:3
int64_t coord_t
Definition libslic3r.h:48
double rotation_diff_z(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to)
Definition Geometry.cpp:765
BoundingBox scaled(const BoundingBoxf &bb)
Definition BoundingBox.hpp:284
Eigen::Matrix< double, 3, 1, Eigen::DontAlign > Vec3d
Definition Point.hpp:68
Eigen::Transform< double, 3, Eigen::Affine, Eigen::DontAlign > Transform3d
Definition Point.hpp:96

References Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::PrintObjectBaseWithState(), Slic3r::BoundingBox3Base< PointType >::center(), EPSILON, instances(), m_center_offset, m_max_z, m_size, m_trafo, m_tree_support_preview_cache, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::model_object(), Slic3r::Point::new_scale(), Print, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::print(), Slic3r::Geometry::rotation_diff_z(), Slic3r::scaled(), SCALING_FACTOR, set_instances(), Slic3r::BoundingBox3Base< PointType >::size(), trafo(), and Slic3r::BoundingBoxf3::transformed().

Referenced by bridge_over_infill(), get_shared_object(), and set_shared_object().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ~PrintObject()

Slic3r::PrintObject::~PrintObject ( )
private
168{
169 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, m_shared_object %2%")%this%m_shared_object;
170 if (m_shared_regions && -- m_shared_regions->m_ref_cnt == 0) delete m_shared_regions;
171 clear_layers();
173}
void clear_layers()
Definition PrintObject.cpp:1306
void clear_support_layers()
Definition PrintObject.cpp:1343
PrintObject * m_shared_object
Definition Print.hpp:718
PrintObjectRegions * m_shared_regions
Definition Print.hpp:678

References clear_layers(), clear_support_layers(), m_shared_object, and m_shared_regions.

Here is the call graph for this function:

Member Function Documentation

◆ _generate_support_material()

void Slic3r::PrintObject::_generate_support_material ( )
private
5086{
5087 if (is_tree(m_config.support_type.value)) {
5088 TreeSupport tree_support(*this, m_slicing_params);
5089 tree_support.throw_on_cancel = [this]() { this->throw_if_canceled(); };
5090 tree_support.generate();
5091 }
5092 else {
5093 // The normal generator anchors its layer grid at the slicing frame origin
5094 // (SlicingParameters: first layer at first_print_layer_height, raft at
5095 // z = 0), so it has to see the object layers in that frame. On a belt the
5096 // object layers carry the global Z offset (PrintObject::slice()), which is
5097 // negative for the leading half of the belt: a top contact below z = 0
5098 // then turns the intermediate-layer count negative and the generator
5099 // allocates layers until memory runs out. Lift the offset off the object
5100 // layers and the belt floor for the duration of the run and put it back
5101 // on everything, including the new support layers, afterwards (organic
5102 // tree support is shifted the same way below).
5103 const double global_z = m_belt_global_z_offset;
5104 const bool unshift = std::abs(global_z) > EPSILON;
5105 auto shift_object_frame = [this, global_z](double sign) {
5106 for (Layer *layer : m_layers)
5107 layer->print_z += sign * global_z;
5108 m_slicing_params.belt_floor_z_shift += sign * global_z;
5109 };
5110 if (unshift)
5111 shift_object_frame(-1.);
5112 try {
5113 PrintObjectSupportMaterial support_material(this, m_slicing_params);
5114 support_material.generate(*this);
5115 } catch (...) {
5116 if (unshift)
5117 shift_object_frame(1.);
5118 throw;
5119 }
5120 if (unshift) {
5121 shift_object_frame(1.);
5122 for (SupportLayer *sl : m_support_layers)
5123 sl->print_z += global_z;
5124 }
5125 }
5126 // Global Z offset for support layers:
5127 // - Normal support: generated in the object frame above and shifted afterwards.
5128 // - Non-organic tree support (slim/strong/hybrid): plan_layer_heights() reads
5129 // from globally-offset object layers, so support layers already have it.
5130 // - Organic tree support: generate_tree_support_3D() computes its own Z values
5131 // independently and does NOT inherit the offset — apply it here.
5132 // Belt floor polygon clipping for non-organic tree support is done inside
5133 // draw_circles() before area_groups and toolpaths are built.
5134 if (is_tree(m_config.support_type.value) && std::abs(m_belt_global_z_offset) > EPSILON) {
5135 // Resolve effective support style (same logic as SupportParameters).
5136 auto style = m_config.support_style.value;
5137 if (style == smsDefault)
5139 if (style == smsTreeOrganic) {
5140 for (SupportLayer *sl : m_support_layers)
5141 sl->print_z += m_belt_global_z_offset;
5142 }
5143 }
5144
5145}
#define style
Definition WipeTowerDialog.cpp:101
double m_belt_global_z_offset
Definition Print.hpp:721
SlicingParameters m_slicing_params
Definition Print.hpp:680
SupportLayerPtrs m_support_layers
Definition Print.hpp:693
PrintObjectConfig m_config
Definition Print.hpp:667
LayerPtrs m_layers
Definition Print.hpp:681
bool is_tree(SupportType stype)
Definition PrintConfig.hpp:381
@ smsDefault
Definition PrintConfig.hpp:363
@ smsTreeOrganic
Definition PrintConfig.hpp:363

References _generate_support_material(), EPSILON, Slic3r::PrintObjectSupportMaterial::generate(), Slic3r::TreeSupport::generate(), Slic3r::is_tree(), m_belt_global_z_offset, m_config, m_layers, m_slicing_params, m_support_layers, Slic3r::smsDefault, Slic3r::smsTreeOrganic, style, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::throw_if_canceled(), and Slic3r::TreeSupport::throw_on_cancel.

Referenced by _generate_support_material(), and generate_support_material().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _shrink_contour_holes()

ExPolygons Slic3r::PrintObject::_shrink_contour_holes ( double contour_delta,
double hole_delta,
const ExPolygons & polys ) const
private
1703{
1704 ExPolygons new_ex_polys;
1705 for (const ExPolygon& ex_poly : polys) {
1706 Polygons contours;
1708 //BBS: modify hole
1709 for (const Polygon& hole : ex_poly.holes) {
1710 if (hole_delta != 0) {
1711 for (Polygon& newHole : offset(hole, -hole_delta)) {
1712 newHole.make_counter_clockwise();
1713 holes.emplace_back(std::move(newHole));
1714 }
1715 } else {
1716 holes.push_back(hole);
1717 holes.back().make_counter_clockwise();
1718 }
1719 }
1720 //BBS: modify contour
1721 if (contour_delta != 0) {
1722 Polygons new_contours = offset(ex_poly.contour, contour_delta);
1723 if (new_contours.size() == 0)
1724 continue;
1725 contours.insert(contours.end(), std::make_move_iterator(new_contours.begin()), std::make_move_iterator(new_contours.end()));
1726 } else {
1727 contours.push_back(ex_poly.contour);
1728 }
1729 ExPolygons temp = diff_ex(union_(contours), union_(holes));
1730 new_ex_polys.insert(new_ex_polys.end(), std::make_move_iterator(temp.begin()), std::make_move_iterator(temp.end()));
1731 }
1732 return union_ex(new_ex_polys);
1733}
Slic3r::Polygons union_(const Slic3r::Polygons &subject)
Definition ClipperUtils.cpp:801
Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:831
std::vector< ExPolygon > ExPolygons
Definition ExPolygon.hpp:20
std::vector< Polygon, PointsAllocator< Polygon > > Polygons
Definition Polygon.hpp:22
Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, JoinType joinType, double miterLimit)
Definition ClipperUtils.cpp:582
Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, PolyFillType fill_type)
Definition ClipperUtils.cpp:1011
const Polygons & holes(const ExPolygon &p)
Definition AGGRaster.hpp:31

References _shrink_contour_holes(), Slic3r::diff_ex(), Slic3r::holes(), Slic3r::offset(), Slic3r::union_(), and Slic3r::union_ex().

Referenced by _shrink_contour_holes().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _transform_hole_to_polyholes()

void Slic3r::PrintObject::_transform_hole_to_polyholes ( )
private
239{
240 // get all circular holes for each layer
241 // the id is center-diameter-extruderid
242 //the tuple is Point center; float diameter_max; int extruder_id; coord_t max_variation; bool twist; int max_edges;
243 std::vector<std::vector<std::pair<std::tuple<Point, float, int, coord_t, bool, int>, Polygon*>>> layerid2center;
244 for (size_t i = 0; i < this->m_layers.size(); i++) layerid2center.emplace_back();
245 tbb::parallel_for(
246 tbb::blocked_range<size_t>(0, m_layers.size()),
247 [this, &layerid2center](const tbb::blocked_range<size_t>& range) {
248 for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
249 m_print->throw_if_canceled();
250 Layer* layer = m_layers[layer_idx];
251 for (size_t region_idx = 0; region_idx < layer->m_regions.size(); ++region_idx)
252 {
253 if (layer->m_regions[region_idx]->region().config().hole_to_polyhole) {
254 for (Surface& surf : layer->m_regions[region_idx]->slices.surfaces) {
255 for (Polygon& hole : surf.expolygon.holes) {
256 //test if convex (as it's clockwise bc it's a hole, we have to do the opposite)
257 if (hole.convex_points(PI).empty() && hole.points.size() > 8) {
258 // Computing circle center
259 Point center = hole.centroid();
260 double diameter_min = std::numeric_limits<float>::max(), diameter_max = 0;
261 double diameter_sum = 0;
262 for (int i = 0; i < hole.points.size(); ++i) {
263 double dist = hole.points[i].distance_to(center);
264 diameter_min = std::min(diameter_min, dist);
265 diameter_max = std::max(diameter_max, dist);
266 diameter_sum += dist;
267 }
268 //also use center of lines to check it's not a rectangle
269 double diameter_line_min = std::numeric_limits<float>::max(), diameter_line_max = 0;
270 Lines hole_lines = hole.lines();
271 for (Line l : hole_lines) {
272 Point midline = (l.a + l.b) / 2;
273 double dist = center.distance_to(midline);
274 diameter_line_min = std::min(diameter_line_min, dist);
275 diameter_line_max = std::max(diameter_line_max, dist);
276 }
277
278
279 // SCALED_EPSILON was a bit too harsh. Now using a config, as some may want some harsh setting and some don't.
280 coord_t max_variation = std::max(SCALED_EPSILON, scale_(this->m_layers[layer_idx]->m_regions[region_idx]->region().config().hole_to_polyhole_threshold.get_abs_value(unscaled(diameter_sum / hole.points.size()))));
281 bool twist = this->m_layers[layer_idx]->m_regions[region_idx]->region().config().hole_to_polyhole_twisted.value;
282 int max_edges = this->m_layers[layer_idx]->m_regions[region_idx]->region().config().hole_to_polyhole_max_edges.value;
283 if (diameter_max - diameter_min < max_variation * 2 && diameter_line_max - diameter_line_min < max_variation * 2) {
284 layerid2center[layer_idx].emplace_back(
285 std::tuple<Point, float, int, coord_t, bool, int>{center, diameter_max, layer->m_regions[region_idx]->region().config().outer_wall_filament_id.value, max_variation, twist, max_edges}, & hole);
286 }
287 }
288 }
289 }
290 }
291 }
292 // for layer->slices, it will be also replaced later.
293 }
294 });
295 //sort holes per center-diameter
296 std::map<std::tuple<Point, float, int, coord_t, bool, int>, std::vector<std::pair<Polygon*, int>>> id2layerz2hole;
297
298 //search & find hole that span at least X layers
299 const size_t min_nb_layers = 2;
300 for (size_t layer_idx = 0; layer_idx < this->m_layers.size(); ++layer_idx) {
301 for (size_t hole_idx = 0; hole_idx < layerid2center[layer_idx].size(); ++hole_idx) {
302 //get all other same polygons
303 std::tuple<Point, float, int, coord_t, bool, int>& id = layerid2center[layer_idx][hole_idx].first;
304 float max_z = layers()[layer_idx]->print_z;
305 std::vector<std::pair<Polygon*, int>> holes;
306 holes.emplace_back(layerid2center[layer_idx][hole_idx].second, layer_idx);
307 for (size_t search_layer_idx = layer_idx + 1; search_layer_idx < this->m_layers.size(); ++search_layer_idx) {
308 if (layers()[search_layer_idx]->print_z - layers()[search_layer_idx]->height - max_z > EPSILON) break;
309 //search an other polygon with same id
310 for (size_t search_hole_idx = 0; search_hole_idx < layerid2center[search_layer_idx].size(); ++search_hole_idx) {
311 std::tuple<Point, float, int, coord_t, bool, int>& search_id = layerid2center[search_layer_idx][search_hole_idx].first;
312 if (std::get<2>(id) == std::get<2>(search_id)
313 && std::get<0>(id).distance_to(std::get<0>(search_id)) < std::get<3>(id)
314 && std::abs(std::get<1>(id) - std::get<1>(search_id)) < std::get<3>(id)
315 ) {
316 max_z = layers()[search_layer_idx]->print_z;
317 holes.emplace_back(layerid2center[search_layer_idx][search_hole_idx].second, search_layer_idx);
318 layerid2center[search_layer_idx].erase(layerid2center[search_layer_idx].begin() + search_hole_idx);
319 search_hole_idx--;
320 break;
321 }
322 }
323 }
324 //check if strait hole or first layer hole (cause of first layer compensation)
325 if (holes.size() >= min_nb_layers || (holes.size() == 1 && holes[0].second == 0)) {
326 id2layerz2hole.emplace(std::move(id), std::move(holes));
327 }
328 }
329 }
330 //create a polyhole per id and replace holes points by it.
331 for (auto entry : id2layerz2hole) {
332 Polygons polyholes = create_polyholes(std::get<0>(entry.first), std::get<1>(entry.first), scale_(print()->config().nozzle_diameter.get_at(std::get<2>(entry.first) - 1)), std::get<4>(entry.first), std::get<5>(entry.first));
333 for (auto& poly_to_replace : entry.second) {
334 Polygon polyhole = polyholes[poly_to_replace.second % polyholes.size()];
335 //search the clone in layers->slices
336 for (ExPolygon& explo_slice : m_layers[poly_to_replace.second]->lslices) {
337 for (Polygon& poly_slice : explo_slice.holes) {
338 if (poly_slice.points == poly_to_replace.first->points) {
339 poly_slice.points = polyhole.points;
340 }
341 }
342 }
343 // copy
344 poly_to_replace.first->points = polyhole.points;
345 }
346 }
347}
Points points
Definition MultiPoint.hpp:22
coord_t height() const
Definition Print.hpp:406
ConstLayerPtrsAdaptor layers() const
Definition Print.hpp:374
const PrintObjectConfig & config() const
Definition Print.hpp:372
double max_z() const
Definition Print.hpp:407
#define scale_(val)
Definition libslic3r.h:106
T * begin(Slic3r::Mat< N, M, T > &mat)
Definition Point.hpp:838
double distance_to(const Point &p1, const Point &p2)
Definition Arrange.cpp:1188
zaa_minimize_perimeter_height nozzle_diameter((ConfigOptionBool, reduce_infill_retraction))((ConfigOptionBool
Polygons create_polyholes(const Point center, const coord_t radius, const coord_t nozzle_diameter, bool multiple, int max_edges)
Definition PrintObject.cpp:209
auto range(Cont &&cont)
Definition libslic3r.h:405
Slic3r::Polygon Polygon
Definition Emboss.cpp:101

References m_layers, and Slic3r::range().

Referenced by slice().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ active_step_add_warning()

void Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, COUNT >::active_step_add_warning ( PrintStateBase::WarningLevel warning_level,
const std::string & message,
PrintStateBase::SlicingNotificationType message_id = PrintStateBase::SlicingDefaultNotification )
inlineprotectedinherited
675 {
677 if (active_step.second)
679 }
Definition PrintBase.hpp:635
void active_step_add_warning(PrintStateBase::WarningLevel warning_level, const std::string &message, PrintStateBase::SlicingNotificationType message_id=PrintStateBase::SlicingDefaultNotification)
Definition PrintBase.hpp:674
void status_update_warnings(PrintBase *print, int step, PrintStateBase::WarningLevel warning_level, const std::string &message, PrintStateBase::SlicingNotificationType message_id=PrintStateBase::SlicingDefaultNotification)
Definition PrintBase.cpp:165

References m_print, m_state, Slic3r::PrintStateBase::SlicingDefaultNotification, Slic3r::PrintObjectBase::state_mutex(), and Slic3r::PrintObjectBase::status_update_warnings().

Referenced by Slic3r::PrintObject::generate_support_material(), and Slic3r::PrintObject::slice_volumes().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ adaptive_infill_field()

const AdaptiveInfillField * Slic3r::PrintObject::adaptive_infill_field ( AdaptiveInfillMode mode) const
inline
410{ return m_adaptive_infill_fields[size_t(mode)].get(); }
AdaptiveInfillFields m_adaptive_infill_fields
Definition Print.hpp:710

References m_adaptive_infill_fields.

◆ add_layer()

Layer * Slic3r::PrintObject::add_layer ( int id,
coordf_t height,
coordf_t print_z,
coordf_t slice_z )
1326{
1327 m_layers.emplace_back(new Layer(id, this, height, print_z, slice_z));
1328 return m_layers.back();
1329}

References add_layer(), height(), and m_layers.

Referenced by add_layer().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ add_support_layer()

SupportLayer * Slic3r::PrintObject::add_support_layer ( int id,
int interface_id,
coordf_t height,
coordf_t print_z )
1444{
1445 m_support_layers.emplace_back(new SupportLayer(id, interface_id, this, height, print_z, -1));
1446 return m_support_layers.back();
1447}

References add_support_layer(), height(), and m_support_layers.

Referenced by add_support_layer().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ add_tree_support_layer()

SupportLayer * Slic3r::PrintObject::add_tree_support_layer ( int id,
coordf_t height,
coordf_t print_z,
coordf_t slice_z )
1437{
1438 m_support_layers.emplace_back(new SupportLayer(id, 0, this, height, print_z, slice_z));
1439 m_support_layers.back()->support_type = stInnerTree;
1440 return m_support_layers.back();
1441}
@ stInnerTree
Definition Layer.hpp:299

References add_tree_support_layer(), height(), m_support_layers, and Slic3r::stInnerTree.

Referenced by add_tree_support_layer().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ all_regions()

std::vector< std::reference_wrapper< const PrintRegion > > Slic3r::PrintObject::all_regions ( ) const
198{
199 std::vector<std::reference_wrapper<const PrintRegion>> out;
201 return out;
202
203 out.reserve(m_shared_regions->all_regions.size());
204 for (const std::unique_ptr<Slic3r::PrintRegion> &region : m_shared_regions->all_regions)
205 out.emplace_back(*region.get());
206 return out;
207}

References m_shared_regions.

Referenced by object_extruders().

Here is the caller graph for this function:

◆ alloc_tree_support_preview_cache()

std::shared_ptr< TreeSupportData > Slic3r::PrintObject::alloc_tree_support_preview_cache ( )
1427{
1429 const coordf_t xy_distance = m_config.support_object_xy_distance.value;
1430 m_tree_support_preview_cache = std::make_shared<TreeSupportData>(*this, xy_distance, g_config_tree_support_collision_resolution);
1431 }
1432
1434}
static constexpr float g_config_tree_support_collision_resolution
Definition libslic3r.h:118
double coordf_t
Definition GUI_ObjectList.hpp:46

References alloc_tree_support_preview_cache(), g_config_tree_support_collision_resolution, m_config, and m_tree_support_preview_cache.

Referenced by alloc_tree_support_preview_cache(), and Slic3r::TreeSupport::move_bounds_to_contact_nodes().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ apply_conical_overhang()

void Slic3r::PrintObject::apply_conical_overhang ( )
private
1584 {
1585 BOOST_LOG_TRIVIAL(info) << "Make overhang printable...";
1586
1587 if (m_layers.empty()) {
1588 return;
1589 }
1590
1591 const double conical_overhang_angle = this->config().make_overhang_printable_angle;
1592 if (conical_overhang_angle == 90.0) {
1593 return;
1594 }
1595 const double angle_radians = conical_overhang_angle * M_PI / 180.;
1596 const double max_hole_area = this->config().make_overhang_printable_hole_size; // in MM^2
1597 const double tan_angle = tan(angle_radians); // the XY-component of the angle
1598 BOOST_LOG_TRIVIAL(info) << "angle " << angle_radians << " maxHoleArea " << max_hole_area << " tan_angle "
1599 << tan_angle;
1600 const coordf_t layer_thickness = m_config.layer_height.value;
1601 const coordf_t max_dist_from_lower_layer = tan_angle * layer_thickness; // max dist which can be bridged, in MM
1602 BOOST_LOG_TRIVIAL(info) << "layer_thickness " << layer_thickness << " max_dist_from_lower_layer "
1603 << max_dist_from_lower_layer;
1604
1605 // Pre-scale config
1606 const coordf_t scaled_max_dist_from_lower_layer = -float(scale_(max_dist_from_lower_layer));
1607 const coordf_t scaled_max_hole_area = float(scale_(scale_(max_hole_area)));
1608
1609
1610 for (auto i = m_layers.rbegin() + 1; i != m_layers.rend(); ++i) {
1611 m_print->throw_if_canceled();
1612 Layer *layer = *i;
1613 Layer *upper_layer = layer->upper_layer;
1614
1615 if (upper_layer->empty()) {
1616 continue;
1617 }
1618
1619 // Skip if entire layer has this disabled
1620 if (std::all_of(layer->m_regions.begin(), layer->m_regions.end(),
1621 [](const LayerRegion *r) { return r->slices.empty() || !r->region().config().make_overhang_printable; })) {
1622 continue;
1623 }
1624
1625 //layer->export_region_slices_to_svg_debug("layer_before_conical_overhang");
1626 //upper_layer->export_region_slices_to_svg_debug("upper_layer_before_conical_overhang");
1627
1628
1629 // Merge the upper layer because we want to offset the entire layer uniformly, otherwise
1630 // the model could break at the region boundary.
1631 auto upper_poly = upper_layer->merged(float(SCALED_EPSILON));
1632 upper_poly = union_ex(upper_poly);
1633
1634 // Merge layer for the same reason
1635 auto current_poly = layer->merged(float(SCALED_EPSILON));
1636 current_poly = union_ex(current_poly);
1637
1638 // Avoid closing up of recessed holes in the base of a model.
1639 // Detects when a hole is completely covered by the layer above and removes the hole from the layer above before
1640 // adding it in.
1641 // This should have no effect any time a hole in a layer interacts with any polygon in the layer above
1642 if (scaled_max_hole_area > 0.0) {
1643
1644 // Now go through all the holes in the current layer and check if they intersect anything in the layer above
1645 // If not, then they're the top of a hole and should be cut from the layer above before the union
1646 for (auto layer_polygon : current_poly) {
1647 for (auto hole : layer_polygon.holes) {
1648 if (std::abs(hole.area()) < scaled_max_hole_area) {
1649 ExPolygon hole_poly(hole);
1650 auto hole_with_above = intersection_ex(upper_poly, hole_poly);
1651 if (!hole_with_above.empty()) {
1652 // The hole had some intersection with the above layer, check if it's a complete overlap
1653 auto hole_difference = xor_ex(hole_with_above, hole_poly);
1654 if (hole_difference.empty()) {
1655 // The layer above completely cover it, remove it from the layer above
1656 upper_poly = diff_ex(upper_poly, hole_poly);
1657 }
1658 }
1659 }
1660 }
1661 }
1662 }
1663
1664 // Now offset the upper layer to be added into current layer
1665 upper_poly = offset_ex(upper_poly, scaled_max_dist_from_lower_layer);
1666
1667 for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) {
1668 // export_to_svg(debug_out_path("Surface-obj-%d-layer-%d-region-%d.svg", id().id, layer->id(), region_id).c_str(),
1669 // layer->m_regions[region_id]->slices.surfaces);
1670
1671 // Disable on given region
1672 if (!upper_layer->m_regions[region_id]->region().config().make_overhang_printable) {
1673 continue;
1674 }
1675
1676 // Calculate the scaled upper poly that belongs to current region
1677 auto p = union_ex(intersection_ex(upper_layer->m_regions[region_id]->slices.surfaces, upper_poly));
1678
1679 // Remove all islands that have already been fully covered by current layer
1680 p.erase(std::remove_if(p.begin(), p.end(), [&current_poly](const ExPolygon& ex) {
1681 return diff_ex(ex, current_poly).empty();
1682 }), p.end());
1683
1684 // And now union it with current region
1685 ExPolygons layer_polygons = to_expolygons(layer->m_regions[region_id]->slices.surfaces);
1686 layer->m_regions[region_id]->slices.set(union_ex(layer_polygons, p), stInternal);
1687
1688 // Then remove it from all other regions, to avoid overlapping regions
1689 for (size_t other_region = 0; other_region < this->num_printing_regions(); ++other_region) {
1690 if (other_region == region_id) {
1691 continue;
1692 }
1693 ExPolygons s = to_expolygons(layer->m_regions[other_region]->slices.surfaces);
1694 layer->m_regions[other_region]->slices.set(diff_ex(s, p, ApplySafetyOffset::Yes), stInternal);
1695 }
1696 }
1697 //layer->export_region_slices_to_svg_debug("layer_after_conical_overhang");
1698 }
1699}
#define M_PI
Definition ExtrusionSimulator.cpp:31
size_t num_printing_regions() const
Definition Print.hpp:500
#define SCALED_EPSILON
Definition libslic3r.h:109
ExPolygons to_expolygons(const Polygons &polys)
Definition ExPolygon.hpp:373
Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, JoinType joinType, double miterLimit)
Definition ClipperUtils.cpp:605
Slic3r::ExPolygons xor_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:1029
Slic3r::ExPolygons intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:878
@ Yes
Definition ClipperUtils.hpp:51
@ stInternal
Definition Surface.hpp:26

References apply_conical_overhang(), config(), Slic3r::diff_ex(), Slic3r::Layer::empty(), Slic3r::intersection_ex(), m_config, m_layers, M_PI, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::m_print, Slic3r::Layer::m_regions, Slic3r::Layer::merged(), num_printing_regions(), Slic3r::offset_ex(), scale_, SCALED_EPSILON, Slic3r::stInternal, Slic3r::to_expolygons(), Slic3r::union_ex(), Slic3r::Layer::upper_layer, Slic3r::xor_ex(), and Slic3r::Yes.

Referenced by apply_conical_overhang(), and slice_volumes().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ assign_new_unique_ids_recursive()

virtual void Slic3r::ObjectBase::assign_new_unique_ids_recursive ( )
inlineprotectedvirtualinherited

Reimplemented in Slic3r::Model, Slic3r::ModelObject, and Slic3r::ModelVolume.

95{ this->set_new_unique_id(); }
void set_new_unique_id()
Definition ObjectID.hpp:89

References set_new_unique_id().

Here is the call graph for this function:

◆ belt_brim_by_layer()

const std::vector< ExtrusionEntityCollection > & Slic3r::PrintObject::belt_brim_by_layer ( ) const
inline
438{ return m_belt_brim_by_layer; }
std::vector< ExtrusionEntityCollection > m_belt_brim_by_layer
Definition Print.hpp:697

References m_belt_brim_by_layer.

◆ belt_brim_filament()

unsigned int Slic3r::PrintObject::belt_brim_filament ( ) const
1396{
1397 // 1-based, matching PrintRegion::outer_wall_filament_id and the raw values pushed
1398 // into LayerTools::extruders in ToolOrdering::collect_extruders (the whole list is
1399 // reindexed to 0-based later). Lowest positive outer-wall filament over the
1400 // printing regions; 1 when none is explicitly set.
1401 unsigned int brim_filament = 0;
1402 for (size_t i = 0; i < this->num_printing_regions(); ++ i) {
1403 const unsigned int f = this->printing_region(i).config().outer_wall_filament_id.value;
1404 if (f > 0 && (brim_filament == 0 || f < brim_filament))
1405 brim_filament = f;
1406 }
1407 return brim_filament == 0 ? 1u : brim_filament;
1408}
const PrintRegion & printing_region(size_t idx) const
Definition Print.hpp:501
const PrintRegionConfig & config() const
Definition Print.hpp:154
static double f(double x, double z_sin, double z_cos, bool vertical, bool flip)
Definition FillGyroid.cpp:124

References belt_brim_filament(), Slic3r::f(), num_printing_regions(), and printing_region().

Referenced by belt_brim_filament().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ belt_brim_prologue()

const std::vector< BeltBrimBand > & Slic3r::PrintObject::belt_brim_prologue ( ) const
inline
439{ return m_belt_brim_prologue; }
std::vector< BeltBrimBand > m_belt_brim_prologue
Definition Print.hpp:699

References m_belt_brim_prologue.

◆ belt_drop_unclaimed_fills()

size_t Slic3r::PrintObject::belt_drop_unclaimed_fills ( Layer * layer,
const std::function< bool(const ExtrusionEntity *)> & claimed )
private
377{
378 if (layer == nullptr)
379 return 0;
380 size_t dropped = 0;
381 for (size_t ri = 0; ri < layer->regions().size(); ++ri) {
382 LayerRegion *lr = layer->get_region(ri);
383 auto &ents = lr->fills.entities;
385 keep.reserve(ents.size());
386 for (size_t i = 0; i < ents.size(); ++i) {
387 if (claimed(ents[i])) {
388 keep.emplace_back(ents[i]);
389 } else {
390 // Stash with its original index so the restore is exact.
391 m_belt_dropped_fills.push_back(BeltDroppedFill{ layer, ri, i, ents[i] });
392 ++dropped;
393 }
394 }
395 ents = std::move(keep);
396 }
397 return dropped;
398}
std::vector< BeltDroppedFill > m_belt_dropped_fills
Definition Print.hpp:692
const Vec3crd & size() const
Definition Print.hpp:371
Definition Print.hpp:686
std::vector< ExtrusionEntity * > ExtrusionEntitiesPtr
Definition ExtrusionEntity.hpp:172

References Slic3r::ExtrusionEntityCollection::entities, Slic3r::LayerRegion::fills, Slic3r::Layer::get_region(), m_belt_dropped_fills, Slic3r::Layer::regions(), and size().

Referenced by Slic3r::Print::_plan_belt_purge().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ belt_global_z_offset()

double Slic3r::PrintObject::belt_global_z_offset ( ) const
inline
735{ return m_belt_global_z_offset; }

References m_belt_global_z_offset.

Referenced by Slic3r::TreeSupport3D::TreeModelVolumes::TreeModelVolumes().

Here is the caller graph for this function:

◆ belt_restore_dropped_fills()

void Slic3r::PrintObject::belt_restore_dropped_fills ( )
private
401{
402 if (m_belt_dropped_fills.empty())
403 return;
404 // Ascending index per (layer, region): inserting in that order lands every
405 // entity back at its original position, because each insertion shifts only
406 // the entries after it, which are themselves still to be inserted.
407 std::stable_sort(m_belt_dropped_fills.begin(), m_belt_dropped_fills.end(),
408 [](const BeltDroppedFill &a, const BeltDroppedFill &b) {
409 if (a.layer != b.layer) return a.layer < b.layer;
410 if (a.region_idx != b.region_idx) return a.region_idx < b.region_idx;
411 return a.index < b.index;
412 });
413 for (const BeltDroppedFill &d : m_belt_dropped_fills) {
414 auto &ents = d.layer->get_region(d.region_idx)->fills.entities;
415 ents.insert(ents.begin() + std::min(d.index, ents.size()), d.entity);
416 }
417 m_belt_dropped_fills.clear();
418}

References m_belt_dropped_fills.

Referenced by belt_undo_purge_plan().

Here is the caller graph for this function:

◆ belt_restore_truncated_layers()

void Slic3r::PrintObject::belt_restore_truncated_layers ( )
private
421{
422 if (m_belt_truncated_layers.empty())
423 return;
424
427 for (size_t i = 0; i < m_layers.size(); ++i) {
428 m_layers[i]->lower_layer = i == 0 ? nullptr : m_layers[i - 1];
429 m_layers[i]->upper_layer = i + 1 < m_layers.size() ? m_layers[i + 1] : nullptr;
430 }
431}
LayerPtrs m_belt_truncated_layers
Definition Print.hpp:682

References m_belt_truncated_layers, and m_layers.

Referenced by belt_undo_purge_plan().

Here is the caller graph for this function:

◆ belt_shift_layer_grid()

void Slic3r::PrintObject::belt_shift_layer_grid ( double delta)
private
325{
326 if (std::abs(delta) < EPSILON)
327 return;
328 for (Layer *layer : m_layers)
329 layer->print_z += delta;
330 for (SupportLayer *layer : m_support_layers)
331 layer->print_z += delta;
332 // The brim's apron bands below the first layer carry their own print_z.
333 for (BeltBrimBand &band : m_belt_brim_prologue)
334 band.print_z += delta;
335 m_slicing_params.belt_floor_z_shift += delta;
336 // The grid stays shifted across a support-only or brim-only change (posSlice does
337 // not rerun), so everything slice() derived from it has to follow: the cached floor
338 // that update_slicing_parameters() restores, and the global offset the organic
339 // support layers and the adaptive infill octree are placed with. Left alone, the
340 // next alignment finds a delta of 0 and the floor and the supports sit up to half
341 // a layer off the grid, unlike a fresh slice.
344 m_belt_global_z_offset += delta;
345}
bool m_belt_floor_z_shift_cache_valid
Definition Print.hpp:733
double m_belt_floor_z_shift_cached
Definition Print.hpp:732

References EPSILON, m_belt_brim_prologue, m_belt_floor_z_shift_cache_valid, m_belt_floor_z_shift_cached, m_belt_global_z_offset, m_layers, m_slicing_params, and m_support_layers.

◆ belt_truncate_layers_above()

size_t Slic3r::PrintObject::belt_truncate_layers_above ( coordf_t z)
private
354{
355 // A repeated plan always starts from the restored full layer set.
356 assert(m_belt_truncated_layers.empty());
357 size_t keep = m_layers.size();
358 while (keep > 0 && m_layers[keep - 1]->print_z > z + EPSILON)
359 --keep;
360 if (keep >= m_layers.size())
361 return 0;
362 const size_t removed = m_layers.size() - keep;
363 m_belt_truncated_layers.assign(m_layers.begin() + keep, m_layers.end());
364 m_layers.resize(keep);
365 if (!m_layers.empty())
366 m_layers.back()->upper_layer = nullptr;
367 return removed;
368}

References EPSILON, m_belt_truncated_layers, and m_layers.

◆ belt_undo_purge_plan()

void Slic3r::PrintObject::belt_undo_purge_plan ( )
inlineprivate
void belt_restore_dropped_fills()
Definition BeltPurge.cpp:400
void belt_restore_truncated_layers()
Definition BeltPurge.cpp:420

References belt_restore_dropped_fills(), and belt_restore_truncated_layers().

Here is the call graph for this function:

◆ bounding_box()

BoundingBox Slic3r::PrintObject::bounding_box ( ) const
inline
403{ return BoundingBox(Point(- m_size.x() / 2, - m_size.y() / 2), Point(m_size.x() / 2, m_size.y() / 2)); }
Kernel::Point_3 Point
Definition MeshRepair.cpp:81

References m_size.

Referenced by discover_vertical_shells(), and Slic3r::SeamPlacer::init().

Here is the caller graph for this function:

◆ bridge_over_infill()

void Slic3r::PrintObject::bridge_over_infill ( )
private
3089{
3090 BOOST_LOG_TRIVIAL(info) << "Bridge over infill - Start" << log_memory_info();
3091 struct CandidateSurface
3092 {
3093 CandidateSurface(const Surface *original_surface,
3094 int layer_index,
3095 Polygons new_polys,
3096 const LayerRegion *region,
3097 double bridge_angle)
3098 : original_surface(original_surface)
3099 , layer_index(layer_index)
3100 , new_polys(new_polys)
3101 , region(region)
3102 , bridge_angle(bridge_angle)
3103 {}
3104 const Surface *original_surface;
3105 int layer_index;
3106 Polygons new_polys;
3107 const LayerRegion *region;
3108 double bridge_angle;
3109 };
3110
3111 std::map<size_t, std::vector<CandidateSurface>> surfaces_by_layer;
3112 // Orca:
3113 // Detect use of lightning infill. Moved earlier in the function to pass to the gather and filter surfaces threads.
3114 bool has_lightning_infill = false;
3115 for (size_t i = 0; i < this->num_printing_regions(); i++) {
3116 if (this->printing_region(i).config().sparse_infill_pattern == ipLightning) {
3117 has_lightning_infill = true;
3118 break;
3119 }
3120 }
3121
3122 // SECTION to gather and filter surfaces for expanding, and then cluster them by layer
3123 {
3124 tbb::concurrent_vector<CandidateSurface> candidate_surfaces;
3125 tbb::parallel_for(tbb::blocked_range<size_t>(0, this->layers().size()), [po = static_cast<const PrintObject *>(this), &candidate_surfaces](tbb::blocked_range<size_t> r) {
3126 PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
3127 for (size_t lidx = r.begin(); lidx < r.end(); lidx++) {
3128 const Layer *layer = po->get_layer(lidx);
3129 if (layer->lower_layer == nullptr) {
3130 continue;
3131 }
3132 double spacing = layer->regions().front()->flow(frSolidInfill).scaled_spacing();
3133 // unsupported area will serve as a filter for polygons worth bridging.
3134 Polygons unsupported_area;
3135 Polygons lower_layer_solids;
3136 // Whether every fill surface of the lower layer prints at full density, which the early-out below needs. Kept
3137 // apart from the unchanged rule for what counts as a solid to expand.
3138 bool dense_below = true;
3139 for (const LayerRegion *region : layer->lower_layer->regions()) {
3140 Polygons fill_polys = to_polygons(region->fill_expolygons);
3141 // initially consider the whole layer unsupported, but also gather solid layers to later cut off supported parts
3142 unsupported_area.insert(unsupported_area.end(), fill_polys.begin(), fill_polys.end());
3143 for (const Surface &surface : region->fill_surfaces) {
3144 if (surface.surface_type != stInternal || region->region().config().sparse_infill_density.value == 100) {
3145 Polygons p = to_polygons(surface.expolygon);
3146 lower_layer_solids.insert(lower_layer_solids.end(), p.begin(), p.end());
3147 }
3148 dense_below = dense_below && prints_dense(surface, region->region().config(), po->config(), layer->lower_layer->id());
3149 }
3150 }
3151 unsupported_area = closing(unsupported_area, float(SCALED_EPSILON));
3152
3153 // Orca:
3154 // Don't filter small internal unsupported areas if the user has requested so.
3155 double expansion_multiplier = 3;
3156 if(po->config().dont_filter_internal_bridges.value !=ibfDisabled){
3157 expansion_multiplier = 1;
3158 }
3159 // By expanding the lower layer solids, we avoid making bridges from the tiny internal overhangs that are (very likely) supported by previous layer solids
3160 // NOTE that we cannot filter out polygons worth bridging by their area, because sometimes there is a very small internal island that will grow into large hole
3161 lower_layer_solids = shrink(lower_layer_solids, 1 * spacing); // first remove thin regions that will not support anything
3162 // By shrinking the unsupported area, we avoid making bridges from narrow ensuring region along perimeters.
3163 unsupported_area = shrink(unsupported_area, expansion_multiplier * spacing);
3164 // Then expand the solids back (opening), and further for parts supported by internal solids. Over a lower layer
3165 // printed at full density the expansion nearly always covers everything, which diff_expanded() proves at a
3166 // fraction of the cost. Where Clipper's own offset decimation folds a sliver of solid thinner than about 0.01
3167 // of the reach, about 16 µm here, its result may differ from the full expansion's; that is accepted.
3168 const float reach = float((1 + expansion_multiplier) * spacing);
3169 unsupported_area = dense_below ? diff_expanded(unsupported_area, lower_layer_solids, reach) :
3170 diff(unsupported_area, expand(lower_layer_solids, reach));
3171
3172 for (const LayerRegion *region : layer->regions()) {
3173 SurfacesPtr region_internal_solids = region->fill_surfaces.filter_by_type(stInternalSolid);
3174 for (const Surface *s : region_internal_solids) {
3175 Polygons unsupported = intersection(to_polygons(s->expolygon), unsupported_area);
3176
3177 // Orca: If the user has selected to always support internal overhanging regions, no matter how small
3178 // skip the filtering
3179 if (po->config().dont_filter_internal_bridges.value == ibfNofilter){
3180 // expand the unsupported area by 4x spacing to trigger internal bridging
3181 unsupported = expand(unsupported, 4 * spacing);
3182 candidate_surfaces.push_back(CandidateSurface(s, lidx, unsupported, region, 0));
3183 }else{
3184 // The following flag marks those surfaces, which overlap with unuspported area, but at least part of them is supported.
3185 // These regions can be filtered by area, because they for sure are touching solids on lower layers, and it does not make sense to bridge their tiny overhangs
3186 bool partially_supported = area(unsupported) < area(to_polygons(s->expolygon)) - EPSILON;
3187 if (!unsupported.empty() && (!partially_supported || area(unsupported) > 3 * 3 * spacing * spacing)) {
3188 Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 4 * spacing));
3189 // after we extracted the part worth briding, we go over the leftovers and merge the tiny ones back, to not brake the surface too much
3190 for (const Polygon& p : diff(to_polygons(s->expolygon), expand(worth_bridging, spacing))) {
3191 double area = p.area();
3192 if (area < spacing * scale_(12.0) && area > spacing * spacing) {
3193 worth_bridging.push_back(p);
3194 }
3195 }
3196 worth_bridging = intersection(closing(worth_bridging, float(SCALED_EPSILON)), s->expolygon);
3197 candidate_surfaces.push_back(CandidateSurface(s, lidx, worth_bridging, region, 0));
3198
3199#ifdef DEBUG_BRIDGE_OVER_INFILL
3200 debug_draw(std::to_string(lidx) + "_candidate_surface_" + std::to_string(area(s->expolygon)),
3201 to_lines(region->layer()->lslices), to_lines(s->expolygon), to_lines(worth_bridging),
3202 to_lines(unsupported_area));
3203#endif
3204#ifdef DEBUG_BRIDGE_OVER_INFILL
3205 debug_draw(std::to_string(lidx) + "_candidate_processing_" + std::to_string(area(unsupported)),
3206 to_lines(unsupported), to_lines(intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing))),
3207 to_lines(diff(to_polygons(s->expolygon), expand(worth_bridging, spacing))),
3208 to_lines(unsupported_area));
3209#endif
3210 }
3211 }
3212 }
3213 }
3214 }
3215 });
3216
3217 for (const CandidateSurface &c : candidate_surfaces) {
3218 surfaces_by_layer[c.layer_index].push_back(c);
3219 }
3220 }
3221
3222 // LIGHTNING INFILL SECTION - If lightning infill is used somewhere, we check the areas that are going to be bridges, and those that rely on the
3223 // lightning infill under them get expanded. This somewhat helps to ensure that most of the extrusions are anchored to the lightning infill at the ends.
3224 // It requires modifying this instance of print object in a specific way, so that we do not invalidate the pointers in our surfaces_by_layer structure.
3225 if (has_lightning_infill) {
3226 // Prepare backup data for the Layer Region infills. Before modfiyng the layer region, we backup its fill surfaces by moving! them into this map.
3227 // then a copy is created, modifiyed and passed to lightning infill generator. After generator is created, we restore the original state of the fills
3228 // again by moving the data from this map back to the layer regions. This ensures that pointers to surfaces stay valid.
3229 std::map<size_t, std::map<const LayerRegion *, SurfaceCollection>> backup_surfaces;
3230 for (size_t lidx = 0; lidx < this->layer_count(); lidx++) {
3231 backup_surfaces[lidx] = {};
3232 }
3233
3234 // ORCA: Two-phase split (collect-then-apply) to eliminate a data race in
3235 // the original single-phase parallel_for, where iteration `lidx` read
3236 // m_layers[lidx-1]->regions()->fill_surfaces (its lower_layer) to compute
3237 // `lightning_fill`, while iteration `lidx-1`, on an adjacent TBB block,
3238 // was concurrently std::move-ing / emplace_back-ing into that same
3239 // SurfaceCollection.
3240 //
3241 // Semantic choice — read ORIGINAL surfaces in Phase 1:
3242 // The lower_layer that iteration `lidx` looks at is the *current* layer
3243 // for iteration `lidx-1`, which Phase 2 will modify. We therefore have to
3244 // pick whether Phase 1 sees that layer's pre-modification or
3245 // post-modification state. We deliberately use the original (pre-modification)
3246 // state, for two reasons:
3247 // 1. The gate is asking "does the layer below use lightning sparse
3248 // infill?" — that's a property of the layer's configuration plus its
3249 // original sparse-infill classification. Phase 2's edits only carve
3250 // a small overhang-aligned slice of sparse into solid; they do not
3251 // change whether the layer is using lightning. The realistic gate
3252 // answer is the same either way.
3253 // 2. Each layer's solid expansion is meant to give its OWN lower_layer
3254 // something to anchor lightning lines onto. Cascading the gate
3255 // across layers ("skip mine because the layer below already did
3256 // some") would invert that intent and force a serial Phase 2.
3257 // The original racy code didn't actually implement either choice
3258 // consistently — it returned whichever bytes happened to be in the
3259 // vector when the thread arrived. This split makes the behaviour
3260 // defined, deterministic across runs and platforms, and equivalent to
3261 // a clean sequential implementation that gathered all gates first and
3262 // then applied modifications.
3263 //
3264 // Phase 1: read-only — for each layer, determine whether its lower_layer
3265 // has any stInternal area inside a lightning-infill region. That's the
3266 // sole purpose of `lightning_fill` in the original code: a gate. Capture
3267 // it once into a per-layer bool, derived from the original (unmodified)
3268 // surfaces, so the gate is platform-independent and order-independent.
3269 std::vector<char> needs_lightning_expansion(this->layers().size(), 0);
3270 tbb::parallel_for(tbb::blocked_range<size_t>(0, this->layers().size()), [po = this, &surfaces_by_layer,
3271 &needs_lightning_expansion](tbb::blocked_range<size_t> r) {
3272 PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
3273 for (size_t lidx = r.begin(); lidx < r.end(); lidx++) {
3274 if (surfaces_by_layer.find(lidx) == surfaces_by_layer.end())
3275 continue;
3276 const Layer *layer = po->get_layer(lidx);
3277 const Layer *lower_layer = layer->lower_layer;
3278 if (lower_layer == nullptr)
3279 continue;
3280 for (const LayerRegion *region : lower_layer->regions()) {
3281 if (region->region().config().sparse_infill_pattern == ipLightning
3282 && ! region->fill_surfaces.filter_by_type(stInternal).empty()) {
3283 needs_lightning_expansion[lidx] = 1;
3284 break;
3285 }
3286 }
3287 }
3288 });
3289
3290 // Phase 2: write-only — each iteration mutates only m_layers[lidx]'s
3291 // fill_surfaces and never reads any other layer's surfaces. Different
3292 // iterations write to disjoint LayerRegion::fill_surfaces vectors, so
3293 // there is no aliasing between worker threads.
3294 tbb::parallel_for(tbb::blocked_range<size_t>(0, this->layers().size()), [po = this, &backup_surfaces,
3295 &surfaces_by_layer,
3296 &needs_lightning_expansion](tbb::blocked_range<size_t> r) {
3297 PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
3298 for (size_t lidx = r.begin(); lidx < r.end(); lidx++) {
3299 if (! needs_lightning_expansion[lidx])
3300 continue;
3301
3302 Layer *layer = po->get_layer(lidx);
3303
3304 for (LayerRegion *region : layer->regions()) {
3305 backup_surfaces[lidx][region] = std::move(
3306 region->fill_surfaces); // Make backup copy by move!! so that pointers in candidate surfaces stay valid
3307 // Copy the surfaces back, this will make copy, but we will later discard it anyway
3308 region->fill_surfaces = backup_surfaces[lidx][region];
3309 }
3310
3311 for (LayerRegion *region : layer->regions()) {
3312 ExPolygons sparse_infill = to_expolygons(region->fill_surfaces.filter_by_type(stInternal));
3313 ExPolygons solid_infill = to_expolygons(region->fill_surfaces.filter_by_type(stInternalSolid));
3314
3315 if (sparse_infill.empty()) {
3316 break;
3317 }
3318 for (const auto &surface : surfaces_by_layer[lidx]) {
3319 if (surface.region != region)
3320 continue;
3321 ExPolygons expansion = intersection_ex(sparse_infill, expand(surface.new_polys, scaled<float>(3.0)));
3322 solid_infill.insert(solid_infill.end(), expansion.begin(), expansion.end());
3323 }
3324
3325 solid_infill = union_safety_offset_ex(solid_infill);
3326 sparse_infill = diff_ex(sparse_infill, solid_infill);
3327
3328 region->fill_surfaces.remove_types({stInternalSolid, stInternal});
3329 for (const ExPolygon &ep : solid_infill) {
3330 region->fill_surfaces.surfaces.emplace_back(stInternalSolid, ep);
3331 }
3332 for (const ExPolygon &ep : sparse_infill) {
3333 region->fill_surfaces.surfaces.emplace_back(stInternal, ep);
3334 }
3335 }
3336 }
3337 });
3338
3339 // Use the modified surfaces to generate expanded lightning anchors
3341
3342 // And now restore carefully the original surfaces, again using move to avoid reallocation and preserving the validity of the
3343 // pointers in surface candidates
3344 for (size_t lidx = 0; lidx < this->layer_count(); lidx++) {
3345 Layer *layer = this->get_layer(lidx);
3346 for (LayerRegion *region : layer->regions()) {
3347 if (backup_surfaces[lidx].find(region) != backup_surfaces[lidx].end()) {
3348 region->fill_surfaces = std::move(backup_surfaces[lidx][region]);
3349 }
3350 }
3351 }
3352 }
3353
3354 std::map<size_t, Polylines> infill_lines;
3355 // SECTION to generate infill polylines
3356 {
3357 std::vector<std::pair<const Surface *, const Layer *>> surfaces_w_layer;
3358 for (const auto &pair : surfaces_by_layer) {
3359 for (const CandidateSurface &c : pair.second) {
3360 surfaces_w_layer.emplace_back(c.original_surface, c.region->m_layer);
3361 }
3362 }
3363
3364 this->m_adaptive_fill_octrees = this->prepare_adaptive_infill_data(surfaces_w_layer);
3366
3367 std::vector<size_t> layers_to_generate_infill;
3368 for (const auto &pair : surfaces_by_layer) {
3369 assert(pair.first > 0);
3370 infill_lines[pair.first - 1] = {};
3371 layers_to_generate_infill.push_back(pair.first - 1);
3372 }
3373
3374 tbb::parallel_for(tbb::blocked_range<size_t>(0, layers_to_generate_infill.size()), [po = static_cast<const PrintObject *>(this),
3375 &layers_to_generate_infill,
3376 &infill_lines](tbb::blocked_range<size_t> r) {
3377 PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
3378 for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) {
3379 size_t lidx = layers_to_generate_infill[job_idx];
3380 infill_lines.at(
3381 lidx) = po->get_layer(lidx)->generate_sparse_infill_polylines_for_anchoring(&po->m_adaptive_fill_octrees,
3382 po->m_lightning_generator.get());
3383 }
3384 });
3385#ifdef DEBUG_BRIDGE_OVER_INFILL
3386 for (const auto &il : infill_lines) {
3387 debug_draw(std::to_string(il.first) + "_infill_lines", to_lines(get_layer(il.first)->lslices), to_lines(il.second), {}, {});
3388 }
3389#endif
3390 }
3391
3392 // cluster layers by depth needed for thick bridges. Each cluster is to be processed by single thread sequentially, so that bridges cannot appear one on another
3393 std::vector<std::vector<size_t>> clustered_layers_for_threads;
3394 float target_flow_height_factor = 0.9f;
3395 {
3396 std::vector<size_t> layers_with_candidates;
3397 std::map<size_t, Polygons> layer_area_covered_by_candidates;
3398 for (const auto& pair : surfaces_by_layer) {
3399 layers_with_candidates.push_back(pair.first);
3400 layer_area_covered_by_candidates[pair.first] = {};
3401 }
3402
3403 // prepare inflated filter for each candidate on each layer. layers will be put into single thread cluster if they are close to each other (z-axis-wise)
3404 // and if the inflated AABB polygons overlap somewhere
3405 tbb::parallel_for(tbb::blocked_range<size_t>(0, layers_with_candidates.size()), [&layers_with_candidates, &surfaces_by_layer,
3406 &layer_area_covered_by_candidates](
3407 tbb::blocked_range<size_t> r) {
3408 PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
3409 for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) {
3410 size_t lidx = layers_with_candidates[job_idx];
3411 for (const auto &candidate : surfaces_by_layer.at(lidx)) {
3412 Polygon candiate_inflated_aabb = get_extents(candidate.new_polys).inflated(scale_(7)).polygon();
3413 layer_area_covered_by_candidates.at(lidx) = union_(layer_area_covered_by_candidates.at(lidx),
3414 Polygons{candiate_inflated_aabb});
3415 }
3416 }
3417 });
3418
3419 // note: surfaces_by_layer is ordered map
3420 for (auto pair : surfaces_by_layer) {
3421 if (clustered_layers_for_threads.empty() ||
3422 this->get_layer(clustered_layers_for_threads.back().back())->print_z <
3423 this->get_layer(pair.first)->print_z -
3424 this->get_layer(pair.first)->regions()[0]->bridging_flow(frSolidInfill, true).height() * target_flow_height_factor -
3425 EPSILON ||
3426 intersection(layer_area_covered_by_candidates[clustered_layers_for_threads.back().back()],
3427 layer_area_covered_by_candidates[pair.first])
3428 .empty()) {
3429 clustered_layers_for_threads.push_back({pair.first});
3430 } else {
3431 clustered_layers_for_threads.back().push_back(pair.first);
3432 }
3433 }
3434
3435#ifdef DEBUG_BRIDGE_OVER_INFILL
3436 std::cout << "BRIDGE OVER INFILL CLUSTERED LAYERS FOR SINGLE THREAD" << std::endl;
3437 for (auto cluster : clustered_layers_for_threads) {
3438 std::cout << "CLUSTER: ";
3439 for (auto l : cluster) {
3440 std::cout << l << " ";
3441 }
3442 std::cout << std::endl;
3443 }
3444#endif
3445 }
3446
3447 // LAMBDA to gather areas with sparse infill deep enough that we can fit thick bridges there.
3448 auto gather_areas_w_depth = [target_flow_height_factor](const PrintObject *po, int lidx, float target_flow_height) {
3449 // Gather layers sparse infill areas, to depth defined by used bridge flow
3450 ExPolygons layers_sparse_infill{};
3451 ExPolygons not_sparse_infill{};
3452 double bottom_z = po->get_layer(lidx)->print_z - target_flow_height * target_flow_height_factor - EPSILON;
3453 for (int i = int(lidx) - 1; i >= 0; --i) {
3454 // Stop iterating if layer is lower than bottom_z and at least one iteration was made
3455 const Layer *layer = po->get_layer(i);
3456 if (layer->print_z < bottom_z && i < int(lidx) - 1)
3457 break;
3458
3459 for (const LayerRegion *region : layer->regions()) {
3460 bool has_low_density = region->region().config().sparse_infill_density.value < 100;
3461 for (const Surface &surface : region->fill_surfaces) {
3462 if ((surface.surface_type == stInternal && has_low_density) || surface.surface_type == stInternalVoid ) {
3463 layers_sparse_infill.push_back(surface.expolygon);
3464 } else {
3465 not_sparse_infill.push_back(surface.expolygon);
3466 }
3467 }
3468 }
3469 }
3470 layers_sparse_infill = union_ex(layers_sparse_infill);
3471 layers_sparse_infill = closing_ex(layers_sparse_infill, float(SCALED_EPSILON));
3472 not_sparse_infill = union_ex(not_sparse_infill);
3473 not_sparse_infill = closing_ex(not_sparse_infill, float(SCALED_EPSILON));
3474 return diff(layers_sparse_infill, not_sparse_infill);
3475 };
3476
3477 // Orca: Derive the fallback bridge direction from the supplied anchor geometry.
3478 // Pattern-specific angle selection belongs at the call site, where the supporting
3479 // layer and region are known; this helper must not override it with a base config angle.
3480 auto determine_bridging_angle = [](const Polygons &bridged_area, const Lines &anchors) {
3481 AABBTreeLines::LinesDistancer<Line> lines_tree(anchors);
3482
3483 std::map<double, int> counted_directions;
3484 for (const Polygon &p : bridged_area) {
3485 double acc_distance = 0;
3486 for (int point_idx = 0; point_idx < int(p.points.size()) - 1; ++point_idx) {
3487 Vec2d start = p.points[point_idx].cast<double>();
3488 Vec2d next = p.points[point_idx + 1].cast<double>();
3489 Vec2d v = next - start; // vector from next to current
3490 double dist_to_next = v.norm();
3491 acc_distance += dist_to_next;
3492 if (acc_distance > scaled(2.0)) {
3493 acc_distance = 0.0;
3494 v.normalize();
3495 int lines_count = int(std::ceil(dist_to_next / scaled(2.0)));
3496 float step_size = dist_to_next / lines_count;
3497 for (int i = 0; i < lines_count; ++i) {
3498 Point a = (start + v * (i * step_size)).cast<coord_t>();
3499 auto [distance, index, p] = lines_tree.distance_from_lines_extra<false>(a);
3500 double angle = lines_tree.get_line(index).orientation();
3501 if (angle > PI) {
3502 angle -= PI;
3503 }
3504 angle += PI * 0.5;
3505 counted_directions[angle]++;
3506 }
3507 }
3508 }
3509 }
3510
3511 std::pair<double, int> best_dir{0, 0};
3512 // sliding window accumulation
3513 for (const auto &dir : counted_directions) {
3514 int score_acc = 0;
3515 double dir_acc = 0;
3516 double window_start_angle = dir.first - PI * 0.1;
3517 double window_end_angle = dir.first + PI * 0.1;
3518 for (auto dirs_window = counted_directions.lower_bound(window_start_angle);
3519 dirs_window != counted_directions.upper_bound(window_end_angle); dirs_window++) {
3520 dir_acc += dirs_window->first * dirs_window->second;
3521 score_acc += dirs_window->second;
3522 }
3523 // current span of directions is 0.5 PI to 1.5 PI (due to the aproach.). Edge values should also account for the
3524 // opposite direction.
3525 if (window_start_angle < 0.5 * PI) {
3526 for (auto dirs_window = counted_directions.lower_bound(1.5 * PI - (0.5 * PI - window_start_angle));
3527 dirs_window != counted_directions.end(); dirs_window++) {
3528 dir_acc += (dirs_window->first - PI) * dirs_window->second;
3529 score_acc += dirs_window->second;
3530 }
3531 }
3532 if (window_start_angle > 1.5 * PI) {
3533 for (auto dirs_window = counted_directions.begin();
3534 dirs_window != counted_directions.upper_bound(window_start_angle - 1.5 * PI); dirs_window++) {
3535 dir_acc += (dirs_window->first + PI) * dirs_window->second;
3536 score_acc += dirs_window->second;
3537 }
3538 }
3539
3540 if (score_acc > best_dir.second) {
3541 best_dir = {dir_acc / score_acc, score_acc};
3542 }
3543 }
3544 double bridging_angle = best_dir.first;
3545 if (bridging_angle == 0) {
3546 bridging_angle = 0.001;
3547 }
3548
3549 return bridging_angle;
3550 };
3551
3552 // Orca: Extend scan sections to the nearest anchors and reconstruct the bridge area.
3553 // scan_spacing controls boundary sampling independently of the extrusion spacing;
3554 // anchoring overlap and smoothing thresholds still use the physical bridging flow.
3555 auto construct_anchored_polygon = [](Polygons bridged_area, Lines anchors, const Flow &bridging_flow, double bridging_angle,
3556 coord_t scan_spacing, bool restore_anchors = false) {
3557 auto lines_rotate = [](Lines &lines, double cos_angle, double sin_angle) {
3558 for (Line &l : lines) {
3559 double ax = double(l.a.x());
3560 double ay = double(l.a.y());
3561 l.a.x() = coord_t(round(cos_angle * ax - sin_angle * ay));
3562 l.a.y() = coord_t(round(cos_angle * ay + sin_angle * ax));
3563 double bx = double(l.b.x());
3564 double by = double(l.b.y());
3565 l.b.x() = coord_t(round(cos_angle * bx - sin_angle * by));
3566 l.b.y() = coord_t(round(cos_angle * by + sin_angle * bx));
3567 }
3568 };
3569
3570 auto segments_overlap = [](coord_t alow, coord_t ahigh, coord_t blow, coord_t bhigh) {
3571 return (alow >= blow && alow <= bhigh) || (ahigh >= blow && ahigh <= bhigh) || (blow >= alow && blow <= ahigh) ||
3572 (bhigh >= alow && bhigh <= ahigh);
3573 };
3574
3575 Polygons expanded_bridged_area{};
3576 double aligning_angle = -bridging_angle + PI * 0.5;
3577 {
3578 polygons_rotate(bridged_area, aligning_angle);
3579 lines_rotate(anchors, cos(aligning_angle), sin(aligning_angle));
3580 BoundingBox bb_x = get_extents(bridged_area);
3581 BoundingBox bb_y = get_extents(anchors);
3582
3583 const size_t n_vlines = (bb_x.max.x() - bb_x.min.x() + scan_spacing - 1) / scan_spacing;
3584 std::vector<Line> vertical_lines(n_vlines);
3585 for (size_t i = 0; i < n_vlines; i++) {
3586 // Orca: Sample the center of each reconstructed strip. Its edges lie
3587 // half a scan step away, even when the sampling is finer than extrusion.
3588 coord_t x = bb_x.min.x() + (i + 0.5) * scan_spacing;
3589 coord_t y_min = bb_y.min.y() - bridging_flow.scaled_spacing();
3590 coord_t y_max = bb_y.max.y() + bridging_flow.scaled_spacing();
3591 vertical_lines[i].a = Point{x, y_min};
3592 vertical_lines[i].b = Point{x, y_max};
3593 }
3594
3595 // The vertical lines only span the bridged area's x range, so anchors entirely outside it can never be
3596 // hit. Leaving them out gives the same intersections without building a tree over the whole layer's
3597 // boundary for every bridge.
3598 const coord_t scan_x_min = bb_x.min.x();
3599 const coord_t scan_x_max = bb_x.min.x() + coord_t(n_vlines) * scan_spacing;
3600 anchors.erase(std::remove_if(anchors.begin(), anchors.end(),
3601 [scan_x_min, scan_x_max](const Line &l) {
3602 return std::max(l.a.x(), l.b.x()) < scan_x_min || std::min(l.a.x(), l.b.x()) > scan_x_max;
3603 }),
3604 anchors.end());
3605 auto anchors_and_walls_tree = AABBTreeLines::LinesDistancer<Line>{std::move(anchors)};
3606 auto bridged_area_tree = AABBTreeLines::LinesDistancer<Line>{to_lines(bridged_area)};
3607
3608 std::vector<std::vector<Line>> polygon_sections(n_vlines);
3609 for (size_t i = 0; i < n_vlines; i++) {
3610 auto area_intersections = bridged_area_tree.intersections_with_line<true>(vertical_lines[i]);
3611 for (int intersection_idx = 0; intersection_idx < int(area_intersections.size()) - 1; intersection_idx++) {
3612 if (bridged_area_tree.outside(
3613 (area_intersections[intersection_idx].first + area_intersections[intersection_idx + 1].first) / 2) < 0) {
3614 polygon_sections[i].emplace_back(area_intersections[intersection_idx].first,
3615 area_intersections[intersection_idx + 1].first);
3616 }
3617 }
3618 auto anchors_intersections = anchors_and_walls_tree.intersections_with_line<true>(vertical_lines[i]);
3619
3620 for (Line &section : polygon_sections[i]) {
3621 // Orca: A repaired boundary may already overlap its anchor by one flow width.
3622 // Include that overlap in the search so restoring rounded corners does not
3623 // extend every already anchored section into the next sparse infill cell.
3624 const coord_t overlap = restore_anchors ? bridging_flow.scaled_width() + SCALED_EPSILON : 0;
3625 auto maybe_below_anchor = std::upper_bound(anchors_intersections.rbegin(), anchors_intersections.rend(), section.a + Point{0, overlap},
3626 [](const Point &a, const std::pair<Point, size_t> &b) {
3627 return a.y() > b.first.y();
3628 });
3629 if (maybe_below_anchor != anchors_intersections.rend()) {
3630 section.a = maybe_below_anchor->first;
3631 section.a.y() -= bridging_flow.scaled_width() * (0.5 + 0.5);
3632 }
3633
3634 auto maybe_upper_anchor = std::upper_bound(anchors_intersections.begin(), anchors_intersections.end(), section.b - Point{0, overlap},
3635 [](const Point &a, const std::pair<Point, size_t> &b) {
3636 return a.y() < b.first.y();
3637 });
3638 if (maybe_upper_anchor != anchors_intersections.end()) {
3639 section.b = maybe_upper_anchor->first;
3640 section.b.y() += bridging_flow.scaled_width() * (0.5 + 0.5);
3641 }
3642 }
3643
3644 for (int section_idx = 0; section_idx < int(polygon_sections[i].size()) - 1; section_idx++) {
3645 Line &section_a = polygon_sections[i][section_idx];
3646 Line &section_b = polygon_sections[i][section_idx + 1];
3647 if (segments_overlap(section_a.a.y(), section_a.b.y(), section_b.a.y(), section_b.b.y())) {
3648 section_b.a = section_a.a.y() < section_b.a.y() ? section_a.a : section_b.a;
3649 section_b.b = section_a.b.y() < section_b.b.y() ? section_b.b : section_a.b;
3650 section_a.a = section_a.b;
3651 }
3652 }
3653
3654 polygon_sections[i].erase(std::remove_if(polygon_sections[i].begin(), polygon_sections[i].end(),
3655 [](const Line &s) { return s.a == s.b; }),
3656 polygon_sections[i].end());
3657 std::sort(polygon_sections[i].begin(), polygon_sections[i].end(),
3658 [](const Line &a, const Line &b) {
3659 if (a == b) return false; // Ensure irreflexivity
3660 return a.a.y() < b.b.y();
3661 });
3662 }
3663
3664 // Orca: Reconstruct the polygon from scan sections. At discontinuities and
3665 // strip starts/ends, use half the scan step for the X offsets; using half an
3666 // extrusion spacing would overlap the finer strips and distort curved anchors.
3667 struct TracedPoly
3668 {
3669 Points lows;
3670 Points highs;
3671 };
3672
3673 std::vector<TracedPoly> current_traced_polys;
3674 for (const auto &polygon_slice : polygon_sections) {
3675 std::unordered_set<const Line *> used_segments;
3676 for (TracedPoly &traced_poly : current_traced_polys) {
3677 auto candidates_begin = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), traced_poly.lows.back(),
3678 [](const Point &low, const Line &seg) { return seg.b.y() > low.y(); });
3679 auto candidates_end = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), traced_poly.highs.back(),
3680 [](const Point &high, const Line &seg) { return seg.a.y() > high.y(); });
3681
3682 bool segment_added = false;
3683 for (auto candidate = candidates_begin; candidate != candidates_end && !segment_added; candidate++) {
3684 if (used_segments.find(&(*candidate)) != used_segments.end()) {
3685 continue;
3686 }
3687
3688 if ((traced_poly.lows.back() - candidate->a).cast<double>().squaredNorm() <
3689 36.0 * double(bridging_flow.scaled_spacing()) * bridging_flow.scaled_spacing()) {
3690 traced_poly.lows.push_back(candidate->a);
3691 } else {
3692 traced_poly.lows.push_back(traced_poly.lows.back() + Point{scan_spacing / 2, 0});
3693 traced_poly.lows.push_back(candidate->a - Point{scan_spacing / 2, 0});
3694 traced_poly.lows.push_back(candidate->a);
3695 }
3696
3697 if ((traced_poly.highs.back() - candidate->b).cast<double>().squaredNorm() <
3698 36.0 * double(bridging_flow.scaled_spacing()) * bridging_flow.scaled_spacing()) {
3699 traced_poly.highs.push_back(candidate->b);
3700 } else {
3701 traced_poly.highs.push_back(traced_poly.highs.back() + Point{scan_spacing / 2, 0});
3702 traced_poly.highs.push_back(candidate->b - Point{scan_spacing / 2, 0});
3703 traced_poly.highs.push_back(candidate->b);
3704 }
3705 segment_added = true;
3706 used_segments.insert(&(*candidate));
3707 }
3708
3709 if (!segment_added) {
3710 // Orca: No section continues this strip; close at its right edge.
3711 traced_poly.lows.push_back(traced_poly.lows.back() + Point{scan_spacing / 2, 0});
3712 traced_poly.highs.push_back(traced_poly.highs.back() + Point{scan_spacing / 2, 0});
3713 Polygon &new_poly = expanded_bridged_area.emplace_back(std::move(traced_poly.lows));
3714 new_poly.points.insert(new_poly.points.end(), traced_poly.highs.rbegin(), traced_poly.highs.rend());
3715 traced_poly.lows.clear();
3716 traced_poly.highs.clear();
3717 }
3718 }
3719
3720 current_traced_polys.erase(std::remove_if(current_traced_polys.begin(), current_traced_polys.end(),
3721 [](const TracedPoly &tp) { return tp.lows.empty(); }),
3722 current_traced_polys.end());
3723
3724 for (const auto &segment : polygon_slice) {
3725 if (used_segments.find(&segment) == used_segments.end()) {
3726 TracedPoly &new_tp = current_traced_polys.emplace_back();
3727 new_tp.lows.push_back(segment.a - Point{scan_spacing / 2, 0});
3728 new_tp.lows.push_back(segment.a);
3729 new_tp.highs.push_back(segment.b - Point{scan_spacing / 2, 0});
3730 new_tp.highs.push_back(segment.b);
3731 }
3732 }
3733 }
3734
3735 // add not closed polys
3736 for (TracedPoly &traced_poly : current_traced_polys) {
3737 Polygon &new_poly = expanded_bridged_area.emplace_back(std::move(traced_poly.lows));
3738 new_poly.points.insert(new_poly.points.end(), traced_poly.highs.rbegin(), traced_poly.highs.rend());
3739 }
3740 expanded_bridged_area = union_safety_offset(expanded_bridged_area);
3741 }
3742
3743 polygons_rotate(expanded_bridged_area, -aligning_angle);
3744 return expanded_bridged_area;
3745 };
3746
3747 tbb::parallel_for(tbb::blocked_range<size_t>(0, clustered_layers_for_threads.size()), [po = static_cast<const PrintObject *>(this),
3748 target_flow_height_factor, &surfaces_by_layer,
3749 &clustered_layers_for_threads,
3750 gather_areas_w_depth, &infill_lines,
3751 determine_bridging_angle,
3752 construct_anchored_polygon](
3753 tbb::blocked_range<size_t> r) {
3754 PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
3755 for (size_t cluster_idx = r.begin(); cluster_idx < r.end(); cluster_idx++) {
3756 for (size_t job_idx = 0; job_idx < clustered_layers_for_threads[cluster_idx].size(); job_idx++) {
3757 size_t lidx = clustered_layers_for_threads[cluster_idx][job_idx];
3758 const Layer *layer = po->get_layer(lidx);
3759 // this thread has exclusive access to all surfaces in layers enumerated in
3760 // clustered_layers_for_threads[cluster_idx]
3761
3762 // Presort the candidate polygons. This will help choose the same angle for neighbournig surfaces, that
3763 // would otherwise compete over anchoring sparse infill lines, leaving one area unachored
3764 std::sort(surfaces_by_layer[lidx].begin(), surfaces_by_layer[lidx].end(),
3765 [](const CandidateSurface &left, const CandidateSurface &right) {
3766 auto a = get_extents(left.new_polys);
3767 auto b = get_extents(right.new_polys);
3768
3769 if (a.min.x() == b.min.x()) {
3770 return a.min.y() < b.min.y();
3771 };
3772 return a.min.x() < b.min.x();
3773 });
3774 if (surfaces_by_layer[lidx].size() > 2) {
3775 Vec2d origin = get_extents(surfaces_by_layer[lidx].front().new_polys).max.cast<double>();
3776 std::stable_sort(surfaces_by_layer[lidx].begin() + 1, surfaces_by_layer[lidx].end(),
3777 [origin](const CandidateSurface &left, const CandidateSurface &right) {
3778 auto a = get_extents(left.new_polys);
3779 auto b = get_extents(right.new_polys);
3780
3781 return (origin - a.min.cast<double>()).squaredNorm() <
3782 (origin - b.min.cast<double>()).squaredNorm();
3783 });
3784 }
3785
3786 // Gather deep infill areas, where thick bridges fit
3787 coordf_t spacing = surfaces_by_layer[lidx].front().region->bridging_flow(frSolidInfill, true).scaled_spacing();
3788 coordf_t target_flow_height = surfaces_by_layer[lidx].front().region->bridging_flow(frSolidInfill, true).height() *
3789 target_flow_height_factor;
3790 Polygons deep_infill_area = gather_areas_w_depth(po, lidx, target_flow_height);
3791
3792 {
3793 // Now also remove area that has been already filled on lower layers by bridging expansion - For this
3794 // reason we did the clustering of layers per thread.
3795 Polygons filled_polyons_on_lower_layers;
3796 double bottom_z = layer->print_z - target_flow_height - EPSILON;
3797 if (job_idx > 0) {
3798 for (int lower_job_idx = job_idx - 1; lower_job_idx >= 0; lower_job_idx--) {
3799 size_t lower_layer_idx = clustered_layers_for_threads[cluster_idx][lower_job_idx];
3800 const Layer *lower_layer = po->get_layer(lower_layer_idx);
3801 if (lower_layer->print_z >= bottom_z) {
3802 for (const auto &c : surfaces_by_layer[lower_layer_idx]) {
3803 filled_polyons_on_lower_layers.insert(filled_polyons_on_lower_layers.end(), c.new_polys.begin(),
3804 c.new_polys.end());
3805 }
3806 } else {
3807 break;
3808 }
3809 }
3810 }
3811 deep_infill_area = diff(deep_infill_area, filled_polyons_on_lower_layers);
3812 }
3813
3814 deep_infill_area = expand(deep_infill_area, spacing * 1.5);
3815
3816 // Now gather expansion polygons - internal infill on current layer, from which we can cut off anchors
3817 Polygons lightning_area;
3818 Polygons expansion_area;
3819 Polygons total_fill_area;
3820 Polygons total_top_area;
3821 for (const LayerRegion *region : layer->regions()) {
3822 Polygons top_polys = to_polygons(region->fill_surfaces.filter_by_types({stTop}));
3823 total_top_area.insert(total_top_area.end(), top_polys.begin(), top_polys.end());
3824 Polygons internal_polys = to_polygons(region->fill_surfaces.filter_by_types({stInternal, stInternalSolid}));
3825 expansion_area.insert(expansion_area.end(), internal_polys.begin(), internal_polys.end());
3826 Polygons fill_polys = to_polygons(region->fill_expolygons);
3827 total_fill_area.insert(total_fill_area.end(), fill_polys.begin(), fill_polys.end());
3828 if (region->region().config().sparse_infill_pattern == ipLightning) {
3829 Polygons l = to_polygons(region->fill_surfaces.filter_by_type(stInternal));
3830 lightning_area.insert(lightning_area.end(), l.begin(), l.end());
3831 }
3832 }
3833 total_fill_area = closing(total_fill_area, float(SCALED_EPSILON));
3834 expansion_area = closing(expansion_area, float(SCALED_EPSILON));
3835 expansion_area = intersection(expansion_area, deep_infill_area);
3836 // Orca: Preserve the real lower-layer anchors for every candidate in this
3837 // layer. Replacing this shared set for one pattern also changes later regions,
3838 // and synthetic straight lines can claim support where no infill is printed.
3839 const Polylines anchors = intersection_pl(infill_lines[lidx - 1], shrink(expansion_area, spacing));
3840 Polygons internal_unsupported_area = shrink(deep_infill_area, spacing * 4.5);
3841
3842#ifdef DEBUG_BRIDGE_OVER_INFILL
3843 debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + "_" + "_total_area",
3844 to_lines(total_fill_area), to_lines(expansion_area), to_lines(deep_infill_area), to_lines(anchors));
3845#endif
3846
3847 std::vector<CandidateSurface> expanded_surfaces;
3848 expanded_surfaces.reserve(surfaces_by_layer[lidx].size());
3849 // The expanded fill boundary depends only on the bridging flow, and total_fill_area is not
3850 // modified below, so build it once per spacing rather than once per candidate. A layer split
3851 // into many candidates (e.g. by colour painting) otherwise repeats a layer-wide offset for each.
3852 std::map<coord_t, Polylines> boundary_by_spacing;
3853 // expansion_area is a clean, non-overlapping set, so cutting a bridge out of it only changes the
3854 // polygons near that bridge. The rest are passed through untouched instead of being fed to Clipper
3855 // with the whole layer again for every candidate.
3856 // Not `near`/`far`: the Windows headers still define those as macros, and they expand to
3857 // nothing, which turns the declaration below into an empty one.
3858 const auto split_near = [](const Polygons &polys, const BoundingBox &bbox, Polygons &rest) {
3859 Polygons nearby;
3860 for (const Polygon &p : polys)
3861 (get_extents(p).overlap(bbox) ? nearby : rest).emplace_back(p);
3862 return nearby;
3863 };
3864 for (const CandidateSurface &candidate : surfaces_by_layer[lidx]) {
3865 const auto &region_config = candidate.region->region().config();
3866 const bool turning_pattern = region_config.sparse_infill_pattern == ipHilbertCurve ||
3867 region_config.sparse_infill_pattern == ipOctagramSpiral;
3868 const Flow &flow = candidate.region->bridging_flow(frSolidInfill, true);
3869 const Polygons expanded_polys = expand(candidate.new_polys, flow.scaled_spacing());
3870 // deep_infill_area and internal_unsupported_area cover the whole layer; only their part under
3871 // this candidate can change the results, so they are clipped to its box first.
3872 ExPolygons bridge_components;
3873 if (!expanded_polys.empty())
3874 bridge_components = intersection_ex(expanded_polys,
3875 ClipperUtils::clip_clipper_polygons_with_subject_bbox(
3876 deep_infill_area, get_extents(expanded_polys).inflated(SCALED_EPSILON)));
3877 // Orca: Filter whole bridge areas so their holes remain holes.
3878 bridge_components.erase(std::remove_if(bridge_components.begin(), bridge_components.end(),
3879 [&internal_unsupported_area](const ExPolygon &component) {
3880 return intersection_ex(component, ClipperUtils::clip_clipper_polygons_with_subject_bbox(
3881 internal_unsupported_area,
3882 get_extents(component).inflated(SCALED_EPSILON)))
3883 .empty();
3884 }),
3885 bridge_components.end());
3886 Polygons area_to_be_bridge = to_polygons(std::move(bridge_components));
3887
3888 if (area_to_be_bridge.empty())
3889 continue;
3890
3891 // Not split like the cut of expansion_area below: the whole limiting area is grown by 30% of the spacing,
3892 // which merges neighbouring polygons, and any of its boundary can anchor the bridge.
3893 Polygons limiting_area = union_(area_to_be_bridge, expansion_area);
3894
3895 auto boundary_it = boundary_by_spacing.find(flow.scaled_spacing());
3896 if (boundary_it == boundary_by_spacing.end())
3897 boundary_it = boundary_by_spacing
3898 .emplace(flow.scaled_spacing(), to_polylines(expand(total_fill_area, 1.3f * flow.scaled_spacing())))
3899 .first;
3900 Polylines boundary_plines = boundary_it->second;
3901 {
3902 Polylines limiting_plines = to_polylines(expand(limiting_area, 0.3f * flow.scaled_spacing()));
3903 boundary_plines.insert(boundary_plines.end(), limiting_plines.begin(), limiting_plines.end());
3904 }
3905
3906#ifdef DEBUG_BRIDGE_OVER_INFILL
3907 int r = rand();
3908 debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + "_" +
3909 "_anchors_" + std::to_string(r),
3910 to_lines(area_to_be_bridge), to_lines(boundary_plines), to_lines(anchors), to_lines(expansion_area));
3911#endif
3912
3913 double bridging_angle = -1.;
3914 if (!anchors.empty() && turning_pattern) {
3915 // Orca: Keep adjacent bridges over Hilbert/Octagram aligned despite
3916 // their many local turning directions. Use the lower layer's rotation,
3917 // since that is the infill supporting the bridge, not the current layer's.
3918 for (const LayerRegion *lower_region : layer->lower_layer->regions()) {
3919 // Orca: Apply the configured direction only if the same region has
3920 // sparse infill below this bridge. A height modifier may put another
3921 // pattern underneath, requiring the geometry-based fallback below.
3922 if (&lower_region->region() != &candidate.region->region() ||
3923 intersection(area_to_be_bridge, to_polygons(lower_region->fill_surfaces.filter_by_type(stInternal))).empty())
3924 continue;
3925 bridging_angle = calculate_infill_rotation_angle(po, layer->lower_layer->id(), region_config.infill_direction.value,
3926 region_config.sparse_infill_rotate_template.value) + 0.5 * PI;
3927 // Orca: Apply model alignment as infill generation does, then normalize
3928 // the undirected bridge angle to [0, PI), including negative rotations.
3929 if (region_config.align_infill_direction_to_model) {
3930 const auto &m = po->trafo().matrix();
3931 bridging_angle += std::atan2(double(m(1, 0)), double(m(0, 0)));
3932 }
3933 bridging_angle = std::fmod(bridging_angle, PI);
3934 if (bridging_angle < 0.)
3935 bridging_angle += PI;
3936 break;
3937 }
3938 }
3939 // Orca: A different region below (e.g. a height modifier) needs the actual anchor
3940 // directions. When there are no sparse anchors, use the expansion boundaries.
3941 if (bridging_angle < 0.)
3942 bridging_angle = determine_bridging_angle(area_to_be_bridge, to_lines(anchors.empty() ? boundary_plines : anchors));
3943
3944 // Orca: Preserve the user's absolute or relative internal bridge angle
3945 // override after automatic direction selection.
3946 if (candidate.region->region().config().internal_bridge_angle.value > 0) {
3947 const double custom_angle_rad = Geometry::deg2rad(region_config.internal_bridge_angle.value);
3948 if (region_config.relative_bridge_angle.value)
3949 bridging_angle += custom_angle_rad;
3950 else {
3951 bridging_angle = custom_angle_rad;
3952 if (region_config.align_infill_direction_to_model) {
3953 auto m = po->trafo().matrix();
3954 bridging_angle += std::atan2((double)m(1, 0), (double)m(0, 0));
3955 }
3956 }
3957 }
3958
3959 // Orca: Changing the bridge direction must not change its physical supports.
3960 // Extend to actual sparse infill or the existing boundary anchors, never to
3961 // a synthetic grid that merely has the same nominal angle and spacing.
3962 boundary_plines.insert(boundary_plines.end(), anchors.begin(), anchors.end());
3963 if (!lightning_area.empty() && !intersection(area_to_be_bridge, lightning_area).empty()) {
3964 boundary_plines = intersection_pl(boundary_plines, expand(area_to_be_bridge, scale_(10)));
3965 }
3966 // Orca: Use four samples per extrusion spacing for Hilbert/Octagram so the
3967 // reconstructed boundary follows rounded anchors instead of cutting corners.
3968 // Keep the original step for other patterns and at least one coordinate unit
3969 // after integer division. This changes boundary accuracy, not infill density.
3970 const coord_t scan_spacing = std::max(coord_t(1), flow.scaled_spacing() / (turning_pattern ? 4 : 1));
3971 Polygons bridging_area = construct_anchored_polygon(area_to_be_bridge, to_lines(boundary_plines), flow, bridging_angle, scan_spacing);
3972
3973 // Check collision with other expanded surfaces
3974 {
3975 bool reconstruct = false;
3976 Polygons tmp_expanded_area = expand(bridging_area, 3.0 * flow.scaled_spacing());
3977 const BoundingBox tmp_expanded_bbox = get_extents(tmp_expanded_area);
3978 for (const CandidateSurface &s : expanded_surfaces) {
3979 // Surfaces whose boxes miss each other cannot intersect, which is most pairs on a busy layer.
3980 if (get_extents(s.new_polys).overlap(tmp_expanded_bbox) &&
3981 !intersection(s.new_polys, tmp_expanded_area).empty()) {
3982 bridging_angle = s.bridge_angle;
3983 reconstruct = true;
3984 break;
3985 }
3986 }
3987 if (reconstruct) {
3988 // Orca: Retain the same sampling accuracy when matching a nearby
3989 // bridge's direction; rebuilding must not lose the curved supports.
3990 bridging_area = construct_anchored_polygon(area_to_be_bridge, to_lines(boundary_plines), flow, bridging_angle, scan_spacing);
3991 }
3992 }
3993
3994 // Orca: Keep fine details for better anchoring
3995 // bridging_area = opening(bridging_area, flow.scaled_spacing());
3996 bridging_area = opening(bridging_area, flow.scaled_spacing() * 0.75f);
3997 bridging_area = closing(bridging_area, flow.scaled_spacing());
3998 // Orca: Opening/closing can pull rounded bridge ends away from their real
3999 // supports. Restore those contacts after smoothing, preserving the cleaned
4000 // area and the selected angle; do not smooth the restored contacts again.
4001 if (turning_pattern && !bridging_area.empty()) {
4002 bridging_area = union_(bridging_area, construct_anchored_polygon(bridging_area, to_lines(boundary_plines), flow,
4003 bridging_angle, scan_spacing, true));
4004 }
4005 // Each of these meets one bridge with the whole layer, so the layer side is first cut down to the
4006 // bridge's box (and expansion_area split as above); the result is the same.
4007 if (!bridging_area.empty()) {
4008 const BoundingBox bridging_bbox = get_extents(bridging_area).inflated(SCALED_EPSILON);
4009 bridging_area = intersection(bridging_area, ClipperUtils::clip_clipper_polygons_with_subject_bbox(limiting_area, bridging_bbox));
4010 bridging_area = intersection(bridging_area, ClipperUtils::clip_clipper_polygons_with_subject_bbox(total_fill_area, bridging_bbox));
4011 bridging_area = diff(bridging_area, ClipperUtils::clip_clipper_polygons_with_subject_bbox(total_top_area, bridging_bbox));
4012 }
4013 if (!bridging_area.empty()) {
4014 Polygons kept;
4015 const Polygons cut = split_near(expansion_area, get_extents(bridging_area).inflated(SCALED_EPSILON), kept);
4016 append(kept, diff(cut, bridging_area));
4017 expansion_area = std::move(kept);
4018 }
4019
4020#ifdef DEBUG_BRIDGE_OVER_INFILL
4021 debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + "_" + "_expanded_bridging" + std::to_string(r),
4022 to_lines(layer->lslices), to_lines(boundary_plines), to_lines(candidate.new_polys), to_lines(bridging_area));
4023#endif
4024
4025 expanded_surfaces.push_back(CandidateSurface(candidate.original_surface, candidate.layer_index, bridging_area,
4026 candidate.region, bridging_angle));
4027 }
4028 surfaces_by_layer[lidx].swap(expanded_surfaces);
4029 expanded_surfaces.clear();
4030 }
4031 }
4032 });
4033
4034 BOOST_LOG_TRIVIAL(info) << "Bridge over infill - Directions and expanded surfaces computed" << log_memory_info();
4035
4036 tbb::parallel_for(tbb::blocked_range<size_t>(0, this->layers().size()), [po = this, &surfaces_by_layer](tbb::blocked_range<size_t> r) {
4037 PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
4038 for (size_t lidx = r.begin(); lidx < r.end(); lidx++) {
4039 if (surfaces_by_layer.find(lidx) == surfaces_by_layer.end() && surfaces_by_layer.find(lidx + 1) == surfaces_by_layer.end())
4040 continue;
4041 Layer *layer = po->get_layer(lidx);
4042
4043 Polygons cut_from_infill{};
4044 if (surfaces_by_layer.find(lidx) != surfaces_by_layer.end()) {
4045 for (const auto &surface : surfaces_by_layer.at(lidx)) {
4046 cut_from_infill.insert(cut_from_infill.end(), surface.new_polys.begin(), surface.new_polys.end());
4047 }
4048 }
4049
4050 Polygons additional_ensuring_areas{};
4051 if (surfaces_by_layer.find(lidx + 1) != surfaces_by_layer.end()) {
4052 for (const auto &surface : surfaces_by_layer.at(lidx + 1)) {
4053 auto additional_area = diff(surface.new_polys,
4054 shrink(surface.new_polys, surface.region->flow(frSolidInfill).scaled_spacing()));
4055 additional_ensuring_areas.insert(additional_ensuring_areas.end(), additional_area.begin(), additional_area.end());
4056 }
4057 }
4058
4059 for (LayerRegion *region : layer->regions()) {
4060 Surfaces new_surfaces;
4061
4062 Polygons near_perimeters = to_polygons(union_safety_offset_ex(to_polygons(region->fill_surfaces.surfaces)));
4063 near_perimeters = diff(near_perimeters, shrink(near_perimeters, region->flow(frSolidInfill).scaled_spacing()));
4064 ExPolygons additional_ensuring = intersection_ex(additional_ensuring_areas, near_perimeters);
4065
4066 SurfacesPtr internal_infills = region->fill_surfaces.filter_by_type(stInternal);
4067 ExPolygons new_internal_infills = diff_ex(internal_infills, cut_from_infill);
4068 new_internal_infills = diff_ex(new_internal_infills, additional_ensuring);
4069 for (const ExPolygon &ep : new_internal_infills) {
4070 new_surfaces.emplace_back(stInternal, ep);
4071 }
4072
4073 SurfacesPtr internal_solids = region->fill_surfaces.filter_by_type(stInternalSolid);
4074 if (surfaces_by_layer.find(lidx) != surfaces_by_layer.end()) {
4075 for (const CandidateSurface &cs : surfaces_by_layer.at(lidx)) {
4076 for (const Surface *surface : internal_solids) {
4077 if (cs.original_surface == surface) {
4078 Surface tmp{*surface, {}};
4079 tmp.surface_type = stInternalBridge;
4080 tmp.bridge_angle = cs.bridge_angle;
4081 for (const ExPolygon &ep : union_ex(cs.new_polys)) {
4082 new_surfaces.emplace_back(tmp, ep);
4083 }
4084 break;
4085 }
4086 }
4087 }
4088 }
4089 ExPolygons new_internal_solids = to_expolygons(internal_solids);
4090 new_internal_solids.insert(new_internal_solids.end(), additional_ensuring.begin(), additional_ensuring.end());
4091 new_internal_solids = diff_ex(new_internal_solids, cut_from_infill);
4092 new_internal_solids = union_safety_offset_ex(new_internal_solids);
4093 for (const ExPolygon &ep : new_internal_solids) {
4094 new_surfaces.emplace_back(stInternalSolid, ep);
4095 }
4096
4097#ifdef DEBUG_BRIDGE_OVER_INFILL
4098 debug_draw("Aensuring_" + std::to_string(reinterpret_cast<uint64_t>(&region)), to_polylines(additional_ensuring),
4099 to_polylines(near_perimeters), to_polylines(to_polygons(internal_infills)),
4100 to_polylines(to_polygons(internal_solids)));
4101 debug_draw("Aensuring_" + std::to_string(reinterpret_cast<uint64_t>(&region)) + "_new", to_polylines(additional_ensuring),
4102 to_polylines(near_perimeters), to_polylines(to_polygons(new_internal_infills)),
4103 to_polylines(to_polygons(new_internal_solids)));
4104#endif
4105
4106 region->fill_surfaces.remove_types({stInternalSolid, stInternal});
4107 region->fill_surfaces.append(new_surfaces);
4108 }
4109 }
4110 });
4111
4112 // ======================================================================================================================================
4113 // === ORCA: Create a second internal bridge layer above the first bridge layer. ========================================================
4114 // ======================================================================================================================================
4115 if ( this->m_config.enable_extra_bridge_layer == eblApplyToAll || this->m_config.enable_extra_bridge_layer == eblInternalBridgeOnly) {
4116 // ORCA: Two-phase to eliminate the same data race as the external-bridge
4117 // pass in detect_surfaces_type().
4118 //
4119 // Phase 1: read-only — for each layer, collect its stInternalBridge polygons and
4120 // the matching bridge angle into a per-layer cache.
4121 struct LayerBridgeCache {
4122 ExPolygons polys;
4123 double angle = 0.0;
4124 float offset_distance = 0.0f;
4125 bool has_bridge = false;
4126 };
4127 // Guard against size_t underflow when the object has 0 or 1 layers — there is
4128 // no "layer above" to receive an extra bridge, so the whole pass is a no-op.
4129 const size_t last = (this->layers().size() < 2) ? 0 : this->layers().size() - 1;
4130 std::vector<LayerBridgeCache> caches(this->layers().size());
4131
4132 tbb::parallel_for( tbb::blocked_range<size_t>(0, last), [this, &caches](const tbb::blocked_range<size_t>& r) {
4133 for (size_t lidx = r.begin(); lidx < r.end(); ++lidx) {
4134 Layer* layer = this->get_layer(lidx);
4135 LayerBridgeCache &cache = caches[lidx];
4136
4137 if (!layer->regions().empty())
4138
4139 cache.offset_distance = layer->regions().front()->flow(frSolidInfill).scaled_width();
4140 for (LayerRegion *region : layer->regions()) {
4141 for (const Surface &surf : region->fill_surfaces.surfaces) {
4142 if (surf.surface_type == stInternalBridge) {
4143 cache.polys.push_back(surf.expolygon);
4144 cache.angle = surf.bridge_angle; // last bridge angle on this layer wins, matching prior behaviour
4145 cache.has_bridge = true;
4146 }
4147 }
4148 }
4149
4150 if (!cache.has_bridge || cache.polys.empty()) {
4151 cache.has_bridge = false;
4152 continue;
4153 }
4154
4155 // Shrink-expand to remove trivial bridging areas
4156 cache.polys = offset_ex(shrink_ex(cache.polys, cache.offset_distance), cache.offset_distance);
4157 if (cache.polys.empty())
4158 cache.has_bridge = false;
4159 }
4160 });
4161
4162 // Phase 2: write — each iteration mutates only m_layers[lidx+1]->fill_surfaces and
4163 // pulls its bridge polygons from the precomputed cache. Different iterations never
4164 // touch the same fill_surfaces vector, so there is no aliasing between workers.
4165 tbb::parallel_for( tbb::blocked_range<size_t>(0, last), [this, &caches](const tbb::blocked_range<size_t>& r) {
4166 for (size_t lidx = r.begin(); lidx < r.end(); ++lidx)
4167 {
4168 const LayerBridgeCache &cache = caches[lidx];
4169
4170 // If no bridging in this layer, continue with the next
4171 if (!cache.has_bridge || cache.polys.empty())
4172 continue;
4173
4174 // (C) If there is a next layer, identify overlapping stInternal & stInternalSolid areas and convert the overlap to stSecondInternalBridge
4175 if (lidx + 1 < this->layers().size()) {
4176 Layer* next_layer = this->get_layer(lidx + 1);
4177
4178 // second bridging angle is 90 degrees offset
4179
4180 double bridging_angle_second = cache.angle + M_PI / 2.0;
4181 // Union the bridging polygons
4182 ExPolygons bridging_union = union_safety_offset_ex(cache.polys);
4183 const float offset_distance = cache.offset_distance;
4184
4185 for (LayerRegion *next_region : next_layer->regions()) {
4186 Surfaces next_new_surfaces;
4187 Surfaces keep_surfaces;
4188
4189 // 1) Do not modify (keep) anything that isn't stInternal or stInternalSolid
4190 for (const Surface &s : next_region->fill_surfaces.surfaces) {
4191 if ( (s.surface_type != stInternal) && (s.surface_type != stInternalSolid)) {
4192 keep_surfaces.push_back(s);
4193 }
4194 }
4195
4196 // 2) For stInternal & stInternalSolid surfaces, check if they overlap bridging_union
4197 // 2a) Gather the next internal stInternalSolid surfaces first
4198 SurfacesPtr next_internals = next_region->fill_surfaces.filter_by_types({ stInternal, stInternalSolid });
4199
4200 // 2b) For every collected next stInternalSolid surface
4201 for (const Surface *s : next_internals) {
4202 // Intersect it with the current layer bridging polygons
4203 ExPolygons overlap = intersection_ex( s->expolygon, bridging_union, ApplySafetyOffset::Yes );
4204
4205 // Shrink + expand to remove trivial polygons
4206 overlap = offset_ex(shrink_ex(overlap, offset_distance), offset_distance);
4207
4208 // Overlapping portion found -> this will become the second internal bridge
4209 if (!overlap.empty()) {
4210 // Create second bridge surface
4211 Surface tmp{*s, {}};
4212 tmp.surface_type = stSecondInternalBridge;
4213 tmp.bridge_angle = bridging_angle_second;
4214
4215 // Insert bridging polygons
4216 for (const ExPolygon &ep : overlap) {
4217 next_new_surfaces.emplace_back(tmp, ep);
4218 }
4219
4220 // Calculate leftover polygons = s->expolygon - bridging_union
4221 ExPolygons leftover = diff_ex(s->expolygon, bridging_union, ApplySafetyOffset::Yes);
4222 // Shrink + expand to remove trivial polygons
4223 leftover = offset_ex(shrink_ex(leftover, offset_distance), offset_distance);
4224
4225 // Leftover polygons exist. Add them to the new surface maintaining their original attributes
4226 if (!leftover.empty()) {
4227 ExPolygons unified_leftover = union_safety_offset_ex(leftover);
4228 for (const ExPolygon &ep : unified_leftover) {
4229 // keep same type / angle as original
4230 Surface leftover_surf{*s, {}};
4231 leftover_surf.surface_type = s->surface_type;
4232 leftover_surf.bridge_angle = s->bridge_angle;
4233 next_new_surfaces.emplace_back(leftover_surf, ep);
4234 }
4235 }
4236 }
4237 else { // No overlapping portion found
4238 // keep the surface intact
4239 keep_surfaces.push_back(*s);
4240 }
4241 }
4242
4243 // 3) Rebuild next_region surfaces
4244 next_region->fill_surfaces.surfaces.clear();
4245 next_region->fill_surfaces.append(keep_surfaces);
4246 next_region->fill_surfaces.append(next_new_surfaces);
4247 } // end for next_layer->regions
4248 } // end if next layer
4249 }
4250 }); // end parallel_for
4251
4252 // =================================================================================================================
4253 // === ORCA: Interim workaround - for now the new stSecondInternalBridge surfaces are re-classified ===============
4254 // === back to an internal bridge. As a starting point, this improves bridging reliability as it extrudes ==========
4255 // === two external bridge layers. However, TODO: Implement a new surface type throughout the codebase =============
4256 // =================================================================================================================
4257 for (size_t lidx = 0; lidx < this->layers().size(); ++lidx) {
4258 Layer* layer = this->get_layer(lidx);
4259 for (LayerRegion* region : layer->regions()) {
4260 for (Surface &surf : region->fill_surfaces.surfaces) {
4261 if (surf.surface_type == stSecondInternalBridge) {
4262 surf.surface_type = stInternalBridge;
4263 }
4264 }
4265 }
4266 }
4267 }
4268 // ===========================================================================================
4269 // === ORCA: End of second bridging pass =====================================================
4270 // ===========================================================================================
4271
4272 BOOST_LOG_TRIVIAL(info) << "Bridge over infill - End" << log_memory_info();
4273
4274} // void PrintObject::bridge_over_infill()
#define PRINT_OBJECT_TIME_LIMIT_MILLIS(limit)
Definition PrintObject.cpp:113
size_t size() const
Definition Print.hpp:210
ExPolygons lslices
Definition Layer.hpp:170
FillAdaptive::RegionOctrees m_adaptive_fill_octrees
Definition Print.hpp:707
PrintObject(Print *print, ModelObject *model_object, const Transform3d &trafo, PrintInstances &&instances)
Definition PrintObject.cpp:134
FillLightning::GeneratorPtr m_lightning_generator
Definition Print.hpp:709
AdaptiveInfillFields prepare_adaptive_infill_fields() const
Definition PrintObject.cpp:1274
FillAdaptive::RegionOctrees prepare_adaptive_infill_data(const std::vector< std::pair< const Surface *, const Layer * > > &surfaces_w_layer) const
Definition PrintObject.cpp:1169
size_t layer_count() const
Definition Print.hpp:454
const Layer * get_layer(int idx) const
Definition Print.hpp:456
FillLightning::GeneratorPtr prepare_lightning_infill_data()
Definition PrintObject.cpp:1262
static constexpr double PI
Definition libslic3r.h:81
T * end(Slic3r::Mat< N, M, T > &mat)
Definition Point.hpp:841
void segment(CGALMesh &src, std::vector< CGALMesh > &dst, double smoothing_alpha=0.5, int segment_number=5)
Definition MeshBoolean.cpp:480
float distance(SpiralVase::SpiralPoint a, SpiralVase::SpiralPoint b)
Definition SpiralVase.cpp:19
@ ipLightning
Definition PrintConfig.hpp:131
std::vector< Line > Lines
Definition Line.hpp:20
std::vector< const Surface * > SurfacesPtr
Definition Surface.hpp:124
Polylines to_polylines(const ExPolygon &src)
Definition ExPolygon.hpp:235
Slic3r::Polygons expand(const Slic3r::Polygon &polygon, const float delta, JoinType joinType=DefaultJoinType, double miterLimit=DefaultMiterLimit)
Definition ClipperUtils.hpp:397
@ frSolidInfill
Definition Flow.hpp:22
BoundingBox get_extents(const ExPolygonsWithIds &e)
Definition Emboss.cpp:1374
Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:769
Lines to_lines(const ExPolygon &src)
Definition ExPolygon.hpp:124
@ eblInternalBridgeOnly
Definition PrintConfig.hpp:420
@ eblApplyToAll
Definition PrintConfig.hpp:420
@ ibfDisabled
Definition PrintConfig.hpp:415
double angle(const Eigen::MatrixBase< Derived > &v1, const Eigen::MatrixBase< Derived2 > &v2)
Definition Point.hpp:138
std::string log_memory_info(bool ignore_loglevel)
Definition utils.cpp:1749
Slic3r::Polygons closing(const Slic3r::Polygons &polygons, const float delta1, const float delta2, JoinType joinType, double miterLimit)
Definition ClipperUtils.cpp:670
Polygons to_polygons(const ExPolygon &src)
Definition ExPolygon.hpp:307
std::vector< Surface > Surfaces
Definition Surface.hpp:123
Slic3r::ExPolygons union_safety_offset_ex(const Slic3r::Polygons &polygons)
Definition ClipperUtils.hpp:388
static bool prints_dense(const Surface &surface, const PrintRegionConfig &region_config, const PrintObjectConfig &object_config, size_t layer_id)
Definition PrintObject.cpp:3063
Slic3r::Polygons shrink(const Slic3r::Polygons &polygons, const float delta, JoinType joinType=DefaultJoinType, double miterLimit=DefaultMiterLimit)
Definition ClipperUtils.hpp:406
Slic3r::Polygons diff_expanded(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, float delta)
Definition ClipperUtils.cpp:751
std::vector< Point, PointsAllocator< Point > > Points
Definition Point.hpp:73
Slic3r::Polygons union_safety_offset(const Slic3r::Polygons &polygons)
Definition ClipperUtils.hpp:386
@ stInternalSolid
Definition Surface.hpp:28
@ stSecondInternalBridge
Definition Surface.hpp:32
@ stInternalBridge
Definition Surface.hpp:30
@ stInternalVoid
Definition Surface.hpp:35
Eigen::Matrix< double, 2, 1, Eigen::DontAlign > Vec2d
Definition Point.hpp:67
void polygons_rotate(Polygons &polys, double angle)
Definition Polygon.hpp:169
Slic3r::ExPolygons shrink_ex(const Slic3r::Polygons &polygons, const float delta, JoinType joinType=DefaultJoinType, double miterLimit=DefaultMiterLimit)
Definition ClipperUtils.hpp:408
auto cast(const BoundingBoxBase< Tin > &b)
Definition BoundingBox.hpp:299
Slic3r::Polygons diff(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:731
Slic3r::ExPolygons closing_ex(const Slic3r::Polygons &polygons, const float delta1, const float delta2, JoinType joinType, double miterLimit)
Definition ClipperUtils.cpp:678
RaycastManager::TrItems::iterator find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key)
Definition RaycastManager.cpp:298

References PrintObject(), Slic3r::area(), bridge_over_infill(), Slic3r::closing(), config(), Slic3r::diff(), Slic3r::diff_ex(), Slic3r::diff_expanded(), EPSILON, Slic3r::expand(), Slic3r::Surface::expolygon, Slic3r::frSolidInfill, get_layer(), Slic3r::ibfDisabled, Slic3r::ibfNofilter, Slic3r::Layer::id(), Slic3r::intersection(), Slic3r::intersection_ex(), Slic3r::ipLightning, layer_count(), layers(), Slic3r::log_memory_info(), Slic3r::Layer::lower_layer, m_adaptive_fill_octrees, m_adaptive_infill_fields, m_lightning_generator, num_printing_regions(), prepare_adaptive_infill_data(), prepare_adaptive_infill_fields(), prepare_lightning_infill_data(), PRINT_OBJECT_TIME_LIMIT_MILLIS, printing_region(), Slic3r::prints_dense(), Slic3r::Layer::regions(), scale_, Slic3r::scaled(), SCALED_EPSILON, Slic3r::shrink(), size(), Slic3r::stInternal, Slic3r::stInternalSolid, Slic3r::Surface::surface_type, Slic3r::to_expolygons(), Slic3r::to_lines(), Slic3r::to_polygons(), and Slic3r::union_safety_offset_ex().

Referenced by bridge_over_infill(), and prepare_infill().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ cancel_callback()

std::function< void()> Slic3r::PrintObjectBase::cancel_callback ( PrintBase * print)
staticprotectedinherited
161{
162 return print->cancel_callback();
163}

References Slic3r::PrintBase::cancel_callback().

Referenced by Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::invalidate_all_steps(), Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::invalidate_step(), Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::invalidate_steps(), and Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::invalidate_steps().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ center_offset()

const Point & Slic3r::PrintObject::center_offset ( ) const
inline
409{ return m_center_offset; }

References m_center_offset.

Referenced by contour_z().

Here is the caller graph for this function:

◆ clear_belt_brim()

void Slic3r::PrintObject::clear_belt_brim ( )
1411{
1412 m_belt_brim_by_layer.clear();
1414 m_belt_brim_prologue.clear();
1415}
std::vector< ExPolygons > m_belt_brim_areas_by_layer
Definition Print.hpp:698

References clear_belt_brim(), m_belt_brim_areas_by_layer, m_belt_brim_by_layer, and m_belt_brim_prologue.

Referenced by clear_belt_brim(), and clear_support_layers().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ clear_layers()

void Slic3r::PrintObject::clear_layers ( )
1307{
1308 if (!m_shared_object) {
1309 for (Layer *l : m_layers)
1310 delete l;
1311 m_layers.clear();
1312 for (Layer *l : m_belt_truncated_layers)
1313 delete l;
1315 // Fills dropped for plastic saving are owned by the stash while they sit
1316 // outside their layer's collection, so they are freed here too. Order
1317 // matters only in that these point at layers deleted just above, and we
1318 // never dereference the layer -- just the entity.
1319 for (const BeltDroppedFill &d : m_belt_dropped_fills)
1320 delete d.entity;
1321 m_belt_dropped_fills.clear();
1322 }
1323}

References clear_layers(), m_belt_dropped_fills, m_belt_truncated_layers, m_layers, and m_shared_object.

Referenced by ~PrintObject(), clear_layers(), and slice().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ clear_overhangs_for_lift()

void Slic3r::PrintObject::clear_overhangs_for_lift ( )
private
921{
922 if (!m_shared_object) {
923 for (Layer* l : m_layers)
924 l->loverhangs.clear();
925 }
926}

References clear_overhangs_for_lift(), m_layers, and m_shared_object.

Referenced by clear_overhangs_for_lift(), and detect_overhangs_for_lift().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ clear_shared_object()

void Slic3r::PrintObject::clear_shared_object ( )
3138{
3139 if (m_shared_object) {
3140 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, clear previous shared object data %2%")%this %m_shared_object;
3141 m_layers.clear();
3142 m_support_layers.clear();
3143
3144 m_shared_object = nullptr;
3145
3147 }
3148}
bool invalidate_all_steps_without_cancel()
Definition PrintBase.hpp:666

References Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::invalidate_all_steps_without_cancel(), m_layers, m_shared_object, and m_support_layers.

Here is the call graph for this function:

◆ clear_support_layers()

void Slic3r::PrintObject::clear_support_layers ( )
1344{
1345 if (!m_shared_object) {
1346 for (SupportLayer* l : m_support_layers)
1347 delete l;
1348 m_support_layers.clear();
1349 for (auto l : m_layers) {
1350 l->sharp_tails.clear();
1351 l->sharp_tails_height.clear();
1352 l->cantilevers.clear();
1353 }
1354 }
1355 // Belt brim is owned by the same step, so it must die with it or an
1356 // invalidate-without-rerun would leave stale bands (and stale prologue Zs)
1357 // behind. Unconditional: unlike support layers it is never shared.
1358 this->clear_belt_brim();
1359}
void clear_belt_brim()
Definition PrintObject.cpp:1410

References clear_belt_brim(), clear_support_layers(), m_layers, m_shared_object, and m_support_layers.

Referenced by ~PrintObject(), clear_support_layers(), generate_support_material(), and is_support_necessary().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ clear_tree_support_preview_cache()

void Slic3r::PrintObject::clear_tree_support_preview_cache ( )
inline

References m_tree_support_preview_cache.

Referenced by generate_support_material().

Here is the caller graph for this function:

◆ clip_fill_surfaces()

void Slic3r::PrintObject::clip_fill_surfaces ( )
private
4666{
4668 return;
4669 bool has_infill = false;
4670 for (size_t i = 0; i < this->num_printing_regions(); ++ i)
4671 if (this->printing_region(i).config().sparse_infill_density > 0) {
4672 has_infill = true;
4673 break;
4674 }
4675 if (! has_infill)
4676 return;
4677
4678 // We only want infill under ceilings; this is almost like an
4679 // internal support material.
4680 // Proceed top-down, skipping the bottom layer.
4681 Polygons upper_internal;
4682 for (int layer_id = int(m_layers.size()) - 1; layer_id > 0; -- layer_id) {
4683 Layer *layer = m_layers[layer_id];
4684 Layer *lower_layer = m_layers[layer_id - 1];
4685 // Detect things that we need to support.
4686 // Cummulative fill surfaces.
4687 Polygons fill_surfaces;
4688 // Solid surfaces to be supported.
4689 Polygons overhangs;
4690 for (const LayerRegion *layerm : layer->m_regions)
4691 for (const Surface &surface : layerm->fill_surfaces.surfaces) {
4692 Polygons polygons = to_polygons(surface.expolygon);
4693 if (surface.is_solid())
4694 polygons_append(overhangs, polygons);
4695 polygons_append(fill_surfaces, std::move(polygons));
4696 }
4697 Polygons lower_layer_fill_surfaces;
4698 Polygons lower_layer_internal_surfaces;
4699 for (const LayerRegion *layerm : lower_layer->m_regions)
4700 for (const Surface &surface : layerm->fill_surfaces.surfaces) {
4701 Polygons polygons = to_polygons(surface.expolygon);
4702 if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid)
4703 polygons_append(lower_layer_internal_surfaces, polygons);
4704 polygons_append(lower_layer_fill_surfaces, std::move(polygons));
4705 }
4706 // We also need to support perimeters when there's at least one full unsupported loop
4707 {
4708 // Get perimeters area as the difference between slices and fill_surfaces
4709 // Only consider the area that is not supported by lower perimeters
4710 Polygons perimeters = intersection(diff(layer->lslices, fill_surfaces), lower_layer_fill_surfaces);
4711 // Only consider perimeter areas that are at least one extrusion width thick.
4712 //FIXME Offset2 eats out from both sides, while the perimeters are create outside in.
4713 //Should the pw not be half of the current value?
4714 float pw = FLT_MAX;
4715 for (const LayerRegion *layerm : layer->m_regions)
4716 pw = std::min(pw, (float)layerm->flow(frPerimeter).scaled_width());
4717 // Append such thick perimeters to the areas that need support
4718 polygons_append(overhangs, opening(perimeters, pw));
4719 }
4720 // Merge the new overhangs, find new internal infill.
4721 polygons_append(upper_internal, std::move(overhangs));
4722 const auto closing_radius = scaled<float>(2.f);
4723 upper_internal = intersection(
4724 // Regularize the overhang regions, so that the infill areas will not become excessively jagged.
4726 closing(upper_internal, closing_radius, jtSquare, 0.),
4727 scaled<coord_t>(0.1)),
4728 lower_layer_internal_surfaces);
4729 // Apply new internal infill to regions.
4730 for (LayerRegion *layerm : lower_layer->m_regions) {
4731 if (layerm->region().config().sparse_infill_density.value == 0)
4732 continue;
4733 Polygons internal;
4734 for (Surface &surface : layerm->fill_surfaces.surfaces)
4735 if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid)
4736 polygons_append(internal, std::move(surface.expolygon));
4737 layerm->fill_surfaces.remove_types({ stInternal, stInternalVoid });
4738 layerm->fill_surfaces.append(intersection_ex(internal, upper_internal, ApplySafetyOffset::Yes), stInternal);
4739 layerm->fill_surfaces.append(diff_ex (internal, upper_internal, ApplySafetyOffset::Yes), stInternalVoid);
4740 // If there are voids it means that our internal infill is not adjacent to
4741 // perimeters. In this case it would be nice to add a loop around infill to
4742 // make it more robust and nicer. TODO.
4743#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
4744 layerm->export_region_fill_surfaces_to_svg_debug("6_clip_fill_surfaces");
4745#endif
4746 }
4747 m_print->throw_if_canceled();
4748 }
4749}
static bool infill_only_where_needed
Definition Print.hpp:750
@ jtSquare
Definition ClipperUtils.hpp:21
void smooth_outward(MutablePolygon &polygon, coord_t clip_dist_scaled)
Definition MutablePolygon.cpp:262
@ frPerimeter
Definition Flow.hpp:20
sparse_infill_density((ConfigOptionEnum< InfillPattern >, sparse_infill_pattern))((ConfigOptionPercent
Slic3r::Polygons opening(const Slic3r::Polygons &polygons, const float delta1, const float delta2, JoinType joinType, double miterLimit)
Definition ClipperUtils.cpp:696
void polygons_append(Polygons &dst, const ExPolygon &src)
Definition ExPolygon.hpp:411

References Slic3r::SurfaceCollection::append(), clip_fill_surfaces(), Slic3r::closing(), config(), Slic3r::PrintRegion::config(), Slic3r::diff(), Slic3r::diff_ex(), Slic3r::Surface::expolygon, Slic3r::LayerRegion::export_region_fill_surfaces_to_svg_debug(), Slic3r::LayerRegion::fill_surfaces, Slic3r::LayerRegion::flow(), Slic3r::frPerimeter, infill_only_where_needed, Slic3r::intersection(), Slic3r::intersection_ex(), Slic3r::Surface::is_solid(), Slic3r::jtSquare, Slic3r::Layer::lslices, m_layers, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::m_print, Slic3r::Layer::m_regions, num_printing_regions(), Slic3r::opening(), Slic3r::polygons_append(), printing_region(), Slic3r::LayerRegion::region(), Slic3r::SurfaceCollection::remove_types(), Slic3r::scaled(), Slic3r::Flow::scaled_width(), Slic3r::smooth_outward(), Slic3r::sparse_infill_density(), Slic3r::stInternal, Slic3r::stInternalVoid, Slic3r::Surface::surface_type, Slic3r::SurfaceCollection::surfaces, Slic3r::to_polygons(), and Slic3r::Yes.

Referenced by clip_fill_surfaces(), and prepare_infill().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ combine_infill()

void Slic3r::PrintObject::combine_infill ( )
private
4963{
4964 // Work on each region separately.
4965 for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
4966 const PrintRegion &region = this->printing_region(region_id);
4967 //BBS
4968 const bool enable_combine_infill = region.config().infill_combination.value;
4969 if (enable_combine_infill == false || region.config().sparse_infill_density == 0.)
4970 continue;
4971
4972 // Support internal solid infill when sparse_infill_density is 100%
4973 const bool use_solid_infill = fabs(region.config().sparse_infill_density.value - 100.) < EPSILON;
4974 const SurfaceType surface_type = use_solid_infill ? stInternalSolid : stInternal;
4975 const InfillPattern infill_pattern = use_solid_infill ? region.config().internal_solid_infill_pattern :
4976 region.config().sparse_infill_pattern;
4977
4978 // Limit the number of combined layers to the maximum height allowed by this regions' nozzle.
4979 //FIXME limit the layer height to max_layer_height
4980 double nozzle_diameter = std::min(
4981 nozzle_diameter_for_filament(this->print()->config(), region.config().sparse_infill_filament_id.value, this->print()->is_BBL_printer()),
4982 nozzle_diameter_for_filament(this->print()->config(), region.config().internal_solid_filament_id.value, this->print()->is_BBL_printer()));
4983
4984 //Orca: Limit combination of infill to up to infill_combination_max_layer_height
4985 const double infill_combination_max_layer_height = region.config().infill_combination_max_layer_height.get_abs_value(nozzle_diameter);
4987
4988 // define the combinations
4989 std::vector<size_t> combine(m_layers.size(), 0);
4990 {
4991 double current_height = 0.;
4992 size_t num_layers = 0;
4993 for (size_t layer_idx = 0; layer_idx < m_layers.size(); ++ layer_idx) {
4994 m_print->throw_if_canceled();
4995 const Layer *layer = m_layers[layer_idx];
4996 if (layer->id() == 0)
4997 // Skip first print layer (which may not be first layer in array because of raft).
4998 continue;
4999 // Check whether the combination of this layer with the lower layers' buffer
5000 // would exceed max layer height or max combined layer count.
5001 // BBS: automatically calculate how many layers should be combined
5002 if (current_height + layer->height >= nozzle_diameter + EPSILON) {
5003 // Append combination to lower layer.
5004 combine[layer_idx - 1] = num_layers;
5005 current_height = 0.;
5006 num_layers = 0;
5007 }
5008 current_height += layer->height;
5009 ++ num_layers;
5010 }
5011
5012 // Append lower layers (if any) to uppermost layer.
5013 combine[m_layers.size() - 1] = num_layers;
5014 }
5015
5016 // loop through layers to which we have assigned layers to combine
5017 for (size_t layer_idx = 0; layer_idx < m_layers.size(); ++ layer_idx) {
5018 m_print->throw_if_canceled();
5019 size_t num_layers = combine[layer_idx];
5020 if (num_layers <= 1)
5021 continue;
5022 // Get all the LayerRegion objects to be combined.
5023 std::vector<LayerRegion*> layerms;
5024 layerms.reserve(num_layers);
5025 for (size_t i = layer_idx + 1 - num_layers; i <= layer_idx; ++ i)
5026 layerms.emplace_back(m_layers[i]->regions()[region_id]);
5027 // We need to perform a multi-layer intersection, so let's split it in pairs.
5028 // Initialize the intersection with the candidates of the lowest layer.
5029 ExPolygons intersection = to_expolygons(layerms.front()->fill_surfaces.filter_by_type(surface_type));
5030 // Start looping from the second layer and intersect the current intersection with it.
5031 for (size_t i = 1; i < layerms.size(); ++ i)
5032 intersection = intersection_ex(layerms[i]->fill_surfaces.filter_by_type(surface_type), intersection);
5033 double area_threshold = layerms.front()->infill_area_threshold();
5034 if (! intersection.empty() && area_threshold > 0.)
5035 intersection.erase(std::remove_if(intersection.begin(), intersection.end(),
5036 [area_threshold](const ExPolygon &expoly) { return expoly.area() <= area_threshold; }),
5037 intersection.end());
5038 if (intersection.empty())
5039 continue;
5040// Slic3r::debugf " combining %d %s regions from layers %d-%d\n",
5041// scalar(@$intersection),
5042// ($type == stInternal ? 'internal' : 'internal-solid'),
5043// $layer_idx-($every-1), $layer_idx;
5044 // intersection now contains the regions that can be combined across the full amount of layers,
5045 // so let's remove those areas from all layers.
5046 Polygons intersection_with_clearance;
5047 intersection_with_clearance.reserve(intersection.size());
5048 float clearance_offset =
5049 0.5f * layerms.back()->flow(frPerimeter).scaled_width() +
5050 // Because fill areas for rectilinear and honeycomb are grown
5051 // later to overlap perimeters, we need to counteract that too.
5052 ((infill_pattern == ipRectilinear ||
5053 infill_pattern == ipMonotonic ||
5054 infill_pattern == ipGrid ||
5055 infill_pattern == ipLateralLattice ||
5056 infill_pattern == ipLine ||
5057 infill_pattern == ipHoneycomb ||
5058 infill_pattern == ipLateralHoneycomb) ? 1.5f : 0.5f) *
5059 layerms.back()->flow(frSolidInfill).scaled_width();
5060 for (ExPolygon &expoly : intersection)
5061 polygons_append(intersection_with_clearance, offset(expoly, clearance_offset));
5062 for (LayerRegion *layerm : layerms) {
5063 Polygons internal = to_polygons(std::move(layerm->fill_surfaces.filter_by_type(surface_type)));
5064 layerm->fill_surfaces.remove_type(surface_type);
5065 layerm->fill_surfaces.append(diff_ex(internal, intersection_with_clearance), surface_type);
5066 if (layerm == layerms.back()) {
5067 // Apply surfaces back with adjusted depth to the uppermost layer.
5068 Surface templ(surface_type, ExPolygon());
5069 templ.thickness = 0.;
5070 for (LayerRegion *layerm2 : layerms)
5071 templ.thickness += layerm2->layer()->height;
5072 templ.thickness_layers = (unsigned short)layerms.size();
5073 layerm->fill_surfaces.append(intersection, templ);
5074 } else {
5075 // Save void surfaces.
5076 layerm->fill_surfaces.append(
5077 intersection_ex(internal, intersection_with_clearance),
5079 }
5080 }
5081 }
5082 }
5083}
const PrintConfig & config() const
Definition Print.hpp:1131
InfillPattern
Definition PrintConfig.hpp:126
@ ipGrid
Definition PrintConfig.hpp:129
@ ipLateralLattice
Definition PrintConfig.hpp:132
@ ipMonotonic
Definition PrintConfig.hpp:127
@ ipLine
Definition PrintConfig.hpp:129
@ ipHoneycomb
Definition PrintConfig.hpp:132
@ ipRectilinear
Definition PrintConfig.hpp:128
@ ipLateralHoneycomb
Definition PrintConfig.hpp:132
infill_combination_max_layer_height((ConfigOptionInt, fill_multiline))((ConfigOptionEnum< AdaptiveInfillMode >
double nozzle_diameter_for_filament(const PrintConfig &config, int filament_id, bool is_bbl_printer)
Definition PrintConfig.cpp:148
SurfaceType
Definition Surface.hpp:16

References combine_infill(), config(), Slic3r::PrintRegion::config(), Slic3r::diff_ex(), EPSILON, Slic3r::frPerimeter, Slic3r::frSolidInfill, Slic3r::Layer::height, Slic3r::Layer::id(), Slic3r::infill_combination_max_layer_height(), Slic3r::intersection(), Slic3r::intersection_ex(), Slic3r::ipGrid, Slic3r::ipHoneycomb, Slic3r::ipLateralHoneycomb, Slic3r::ipLateralLattice, Slic3r::ipLine, Slic3r::ipMonotonic, Slic3r::ipRectilinear, m_layers, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::m_print, Slic3r::nozzle_diameter(), Slic3r::nozzle_diameter_for_filament(), num_printing_regions(), Slic3r::offset(), Slic3r::polygons_append(), Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::print(), printing_region(), Slic3r::stInternal, Slic3r::stInternalSolid, Slic3r::stInternalVoid, Slic3r::Surface::thickness, Slic3r::Surface::thickness_layers, Slic3r::to_expolygons(), and Slic3r::to_polygons().

Referenced by combine_infill(), and prepare_infill().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ config()

const PrintObjectConfig & Slic3r::PrintObject::config ( ) const
inline
372{ return m_config; }

References m_config, and Slic3r::PrintObjectConfig.

Referenced by Slic3r::FillLightning::Generator::Generator(), Slic3r::FillLightning::Generator::Generator(), Slic3r::TreeSupport3D::TreeSupportMeshGroupSettings::TreeSupportMeshGroupSettings(), Slic3r::Print::_align_belt_purge_layers(), Slic3r::GCode::_do_export(), apply_conical_overhang(), bridge_over_infill(), clip_fill_surfaces(), combine_infill(), libvgcode::convert_object_to_vertices(), detect_overhangs_for_lift(), detect_surfaces_type(), discover_vertical_shells(), estimate_curled_extrusions(), Slic3r::TreeSupport3D::finalize_raft_contact(), Slic3r::fuzzy_skin_segmentation_by_painting(), Slic3r::TreeSupport3D::generate_raft_contact(), Slic3r::get_imex_active_tools(), Slic3r::TimelapsePosPicker::get_raft_height(), Slic3r::group_fills(), Slic3r::TreeSupport3D::group_meshes(), has_brim(), has_raft(), has_support(), has_support_material(), Slic3r::SeamPlacer::init(), invalidate_state_by_config_options(), Slic3r::LayerRegion::make_perimeters(), Slic3r::multi_material_segmentation_by_painting(), need_z_contouring(), object_config_from_model_object(), Slic3r::outer_inner_brim_area(), Slic3r::SeamPlacer::place_seam(), prepare_adaptive_infill_data(), prepare_adaptive_infill_fields(), prepare_lightning_infill_data(), printing_extruders(), process_external_surfaces(), remove_bridges_from_contacts(), Slic3r::SupportMaterialInternal::remove_bridges_from_contacts(), Slic3r::segmentation_top_and_bottom_layers(), Slic3r::support_material_1st_layer_flow(), Slic3r::support_material_flow(), Slic3r::support_material_interface_flow(), Slic3r::support_transition_flow(), trafo_sliced(), and Slic3r::Print::validate().

Here is the caller graph for this function:

◆ config_apply()

void Slic3r::PrintObject::config_apply ( const ConfigBase & other,
bool ignore_nonexistent = false )
inlineprivate
561{ m_config.apply(other, ignore_nonexistent); }

References m_config.

◆ config_apply_only()

void Slic3r::PrintObject::config_apply_only ( const ConfigBase & other,
const t_config_option_keys & keys,
bool ignore_nonexistent = false )
inlineprivate
562{ m_config.apply_only(other, keys, ignore_nonexistent); }

References m_config.

◆ configBrimWidth()

void Slic3r::PrintObject::configBrimWidth ( double m)
inline
373{m_config.brim_width.value = m; }

References m_config.

◆ contour_z()

void Slic3r::PrintObject::contour_z ( )
private
872{
873 if (!this->set_started(posContouring)) {
874 return;
875 }
876
877 m_print->set_status(40, L("Z contouring"));
878 BOOST_LOG_TRIVIAL(debug) << "Contouring in parallel - start";
879
880 TriangleMesh mesh = this->m_model_object->raw_mesh();
881 // A PrintObject groups the instances that share their whole transformation but the XY shift: an
882 // instance rotated, scaled or mirrored differently is a PrintObject of its own. So the first
883 // instance stands for all of them.
884 const ModelInstance *inst = m_instances.front().model_instance;
886 Geometry::Transformation trans = inst->get_transformation();
887
888 double z = this->m_model_object->min_z();
889 trans.set_offset(Vec3d(-unscale<double>(center_offset.x()), -unscale<double>(center_offset.y()), 0));
890 mesh.transform(trans.get_matrix());
891
892 sla::IndexedMesh imesh(mesh);
893 imesh.ground_level_offset(-z);
894
895 std::mutex mtx;
896 size_t completed = 0;
897 tbb::parallel_for(
898 // Contouring starting with layer second layer to avoid build plate collision
899 tbb::blocked_range<size_t>(1, m_layers.size()),
900 [&, this](const tbb::blocked_range<size_t>& range) {
901 for (size_t layer_idx = range.begin(); layer_idx < range.end(); layer_idx++) {
902 m_print->throw_if_canceled();
903 m_layers[layer_idx]->make_contour_z(imesh);
904
905 std::scoped_lock lock(mtx);
906 completed++;
907 // TRN Slicing status: layers done, total layers, percentage done.
908 std::string msg = (boost::format(L("Z contoured layer %d/%d (%d%%)")) % (completed) % m_layers.size() % int(double(completed) / m_layers.size() * 100)).str();
909 m_print->set_status(40, msg);
910 }
911 }
912 );
913 m_print->throw_if_canceled();
914 BOOST_LOG_TRIVIAL(debug) << "Contouring in parallel - end";
915
916 this->set_done(posContouring);
917}
#define L(s)
Definition ExtrusionEntity.cpp:25
PrintStateBase::TimeStamp set_done(PrintObjectStep step)
Definition PrintBase.hpp:650
bool set_started(PrintObjectStep step)
Definition PrintBase.hpp:648
ModelObject * m_model_object
Definition PrintBase.hpp:362
const Point & center_offset() const
Definition Print.hpp:409
std::vector< PrintInstance > m_instances
Definition Print.hpp:671
T unscale(Q v)
Definition libslic3r.h:138
@ posContouring
Definition Print.hpp:125

References center_offset(), contour_z(), Slic3r::Geometry::Transformation::get_matrix(), Slic3r::ModelInstance::get_transformation(), Slic3r::sla::IndexedMesh::ground_level_offset(), L, m_instances, m_layers, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::m_model_object, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::m_print, Slic3r::posContouring, Slic3r::range(), Slic3r::Geometry::Transformation::set_offset(), Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::set_started(), Slic3r::TriangleMesh::transform(), and Slic3r::unscale().

Referenced by contour_z(), and Slic3r::Print::process().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ copy_id()

void Slic3r::ObjectBase::copy_id ( const ObjectBase & rhs)
inlineprotectedinherited
92{ m_id = rhs.id(); }
ObjectID m_id
Definition ObjectID.hpp:98

References ObjectBase(), id(), and m_id.

Referenced by Slic3r::CutObjectBase::copy().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ copy_layers_from_shared_object()

void Slic3r::PrintObject::copy_layers_from_shared_object ( )
3151{
3152 if (m_shared_object) {
3153 // Orca: Print::process() calls this once per slice for each sharing object, after clear_shared_object() has
3154 // emptied any earlier alias, so the vectors hold only layers this object sliced itself before it started sharing
3155 // (or nothing). Free them here, as clear_support_layers() / clear_layers() would; those skip deletion while
3156 // m_shared_object is set. The belt-only data they also free is always empty here, because belt printers never
3157 // share layers.
3158 for (SupportLayer *layer : m_support_layers)
3159 delete layer;
3160 for (Layer *layer : m_layers)
3161 delete layer;
3162 m_layers.clear();
3163 m_support_layers.clear();
3164
3167
3168 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, copied layers from object %2%")%this%m_shared_object;
3169 m_layers = m_shared_object->layers();
3170 m_support_layers = m_shared_object->support_layers();
3171
3172 firstLayerObjSliceByVolume = m_shared_object->firstLayerObjSlice();
3173 firstLayerObjSliceByGroups = m_shared_object->firstLayerObjGroups();
3174 }
3175}
std::vector< VolumeSlices > firstLayerObjSliceByVolume
Definition Print.hpp:712
std::vector< groupedVolumeSlices > firstLayerObjSliceByGroups
Definition Print.hpp:713

References firstLayerObjSliceByGroups, firstLayerObjSliceByVolume, m_layers, m_shared_object, and m_support_layers.

◆ copy_layers_overhang_from_shared_object()

void Slic3r::PrintObject::copy_layers_overhang_from_shared_object ( )
3178{
3179 if (m_shared_object) {
3180 for (size_t index = 0; index < m_layers.size() && index < m_shared_object->m_layers.size(); index++)
3181 {
3182 Layer* layer_src = m_layers[index];
3183 layer_src->loverhangs = m_shared_object->m_layers[index]->loverhangs;
3184 layer_src->loverhangs_bbox = m_shared_object->m_layers[index]->loverhangs_bbox;
3185 }
3186 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, copied layer overhang from object %2%")%this%m_shared_object;
3187 }
3188}

References Slic3r::Layer::loverhangs, Slic3r::Layer::loverhangs_bbox, m_layers, and m_shared_object.

◆ detect_extruder_geometric_unprintables()

std::vector< std::set< int > > Slic3r::PrintObject::detect_extruder_geometric_unprintables ( ) const
private

Determines the unprintable filaments for each extruder based on its printable area.

The returned array will always have the same size as the number of extruders. If extruder num is 1, just return an empty vector. If an extruder has no unprintable filaments, an empty set will also be returned

Returns
A vector of sets representing unprintable filaments for each extruder
350{
351 int extruder_size = m_print->config().nozzle_diameter.size();
352 if(extruder_size == 1)
353 return std::vector<std::set<int>>(1, std::set<int>());
354
355 std::vector<std::set<int>> geometric_unprintables(extruder_size); // the container to return
356
357 std::vector<std::vector<BedExcludeRegion>> exclusion_regions = get_bed_excluded_regions_by_extruder(m_print->config());
358 exclusion_regions.resize(extruder_size);
359 const Point plate_origin(scale_(m_print->get_plate_origin().x()), scale_(m_print->get_plate_origin().y()));
360 for (std::vector<BedExcludeRegion> &regions : exclusion_regions)
361 for (BedExcludeRegion &region : regions)
362 region.polygon.translate(plate_origin);
363
364 const std::vector<unsigned int> used_filaments = printing_extruders();
365 for (size_t extruder_id = 0; extruder_id < exclusion_regions.size(); ++extruder_id) {
366 const bool collision = std::any_of(m_instances.begin(), m_instances.end(), [&](const PrintInstance &instance) {
367 return instance.model_instance != nullptr &&
368 instance.model_instance->intersects_bed_exclude_regions(exclusion_regions[extruder_id]);
369 });
370 if (collision)
371 geometric_unprintables[extruder_id].insert(used_filaments.begin(), used_filaments.end());
372 }
373
374 std::vector<double> printable_height_per_extruder = m_print->config().extruder_printable_height.values;
375 assert(printable_height_per_extruder.size() == extruder_size);
376
377 // check unprintable filaments caused by printable height limit
378 for (size_t extruder_id = 0; extruder_id < printable_height_per_extruder.size(); ++extruder_id) {
379 double printable_height = printable_height_per_extruder[extruder_id];
380 for (size_t layer_idx = 0; layer_idx < m_layers.size(); ++layer_idx) {
381 auto layer = m_layers[layer_idx];
382 if (layer->print_z <= printable_height)
383 continue;
384 for (auto layerm : layer->regions()) {
385 auto region = layerm->region();
386 int outer_wall_filament_id = region.config().outer_wall_filament_id;
387 int inner_wall_filament_id = region.config().inner_wall_filament_id;
388 int internal_solid_filament_id = region.config().internal_solid_filament_id;
389 int top_surface_filament_id = region.config().top_surface_filament_id;
390 int bottom_surface_filament_id = region.config().bottom_surface_filament_id;
391 int sparse_infill_filament_id = region.config().sparse_infill_filament_id;
392
393 if (!layerm->fills.entities.empty()) {
395 geometric_unprintables[extruder_id].insert(internal_solid_filament_id - 1);
396 if (top_surface_filament_id > 0)
397 geometric_unprintables[extruder_id].insert(top_surface_filament_id - 1);
399 geometric_unprintables[extruder_id].insert(bottom_surface_filament_id - 1);
401 geometric_unprintables[extruder_id].insert(sparse_infill_filament_id - 1);
402 }
403 if (!layerm->perimeters.entities.empty()) {
404 if (outer_wall_filament_id > 0)
405 geometric_unprintables[extruder_id].insert(outer_wall_filament_id - 1);
407 geometric_unprintables[extruder_id].insert(inner_wall_filament_id - 1);
408 }
409 }
410 }
411 }
412
413 std::vector<tbb::concurrent_unordered_set<int>> tbb_geometric_unprintables(extruder_size); // the container used in tbb
414
415 std::vector<Polygons> unprintable_area_in_obj_coord = m_print->get_extruder_unprintable_polygons();
416 std::vector<BoundingBox> unprintable_area_bbox;
417
418 // transform the unprintable areas to obj coord is cheaper than thransform obj into world coord
419 for (auto& polys : unprintable_area_in_obj_coord) {
420 for (auto& poly : polys) {
421 poly.translate(-m_instances.front().shift_without_plate_offset());
422 }
423 unprintable_area_bbox.emplace_back(get_extents(polys));
424 }
425
426 // check unprintbale filaments caused by printable area limit
427 tbb::parallel_for(tbb::blocked_range<int>(0, m_layers.size()),
428 [this, &tbb_geometric_unprintables, &unprintable_area_in_obj_coord, &unprintable_area_bbox](const tbb::blocked_range<int>& range) {
429 for (int j = range.begin(); j < range.end(); ++j) {
430 auto layer = m_layers[j];
431 for (auto layerm : layer->regions()) {
432 const auto& region = layerm->region();
433 int outer_wall_filament_id = region.config().outer_wall_filament_id;
434 int inner_wall_filament_id = region.config().inner_wall_filament_id;
435 int internal_solid_filament_id = region.config().internal_solid_filament_id;
436 int top_surface_filament_id = region.config().top_surface_filament_id;
437 int bottom_surface_filament_id = region.config().bottom_surface_filament_id;
438 int sparse_infill_filament_id = region.config().sparse_infill_filament_id;
439 std::optional<ExPolygons> fill_expolys;
440 BoundingBox fill_bbox;
441 std::optional<ExPolygons> wall_expolys;
442 BoundingBox wall_bbox;
443
444 for (size_t idx = 0; idx < unprintable_area_in_obj_coord.size(); ++idx) {
445 bool do_infill_filament_detect = (internal_solid_filament_id > 0 && tbb_geometric_unprintables[idx].count(internal_solid_filament_id - 1) == 0) ||
446 (top_surface_filament_id > 0 && tbb_geometric_unprintables[idx].count(top_surface_filament_id - 1) == 0) ||
447 (bottom_surface_filament_id > 0 && tbb_geometric_unprintables[idx].count(bottom_surface_filament_id - 1) == 0) ||
448 (sparse_infill_filament_id > 0 && tbb_geometric_unprintables[idx].count(sparse_infill_filament_id-1) == 0);
449
450 bool infill_unprintable = !layerm->fills.entities.empty() &&
451 ((internal_solid_filament_id > 0 && tbb_geometric_unprintables[idx].count(internal_solid_filament_id - 1) > 0) ||
452 (top_surface_filament_id > 0 && tbb_geometric_unprintables[idx].count(top_surface_filament_id - 1) > 0) ||
453 (bottom_surface_filament_id > 0 && tbb_geometric_unprintables[idx].count(bottom_surface_filament_id - 1) > 0) ||
454 (sparse_infill_filament_id > 0 && tbb_geometric_unprintables[idx].count(sparse_infill_filament_id - 1) > 0));
455
456 if (!layerm->fills.entities.empty() && do_infill_filament_detect) {
457 if (!fill_expolys) {
458 fill_expolys = layerm->fill_expolygons;
459 fill_bbox = get_extents(*fill_expolys);
460 }
461 if (fill_bbox.overlap(unprintable_area_bbox[idx]) &&
462 !intersection(*fill_expolys, unprintable_area_in_obj_coord[idx]).empty()) {
463 if (internal_solid_filament_id > 0)
464 tbb_geometric_unprintables[idx].insert(internal_solid_filament_id - 1);
465 if (top_surface_filament_id > 0)
466 tbb_geometric_unprintables[idx].insert(top_surface_filament_id - 1);
467 if (bottom_surface_filament_id > 0)
468 tbb_geometric_unprintables[idx].insert(bottom_surface_filament_id - 1);
469 if (sparse_infill_filament_id > 0)
470 tbb_geometric_unprintables[idx].insert(sparse_infill_filament_id - 1);
471 infill_unprintable = true;
472 }
473 }
474
475 bool do_outer_wall_filament_detect = outer_wall_filament_id > 0 && tbb_geometric_unprintables[idx].count(outer_wall_filament_id - 1) == 0;
476 bool do_inner_wall_filament_detect = inner_wall_filament_id > 0 && tbb_geometric_unprintables[idx].count(inner_wall_filament_id - 1) == 0;
477 if (!layerm->perimeters.entities.empty() && (do_outer_wall_filament_detect || do_inner_wall_filament_detect)) {
478 // if infill is unprintable, no need to check wall since wall contour surrounds infill contour
479 if (infill_unprintable) {
480 if (outer_wall_filament_id > 0)
481 tbb_geometric_unprintables[idx].insert(outer_wall_filament_id - 1);
482 if (inner_wall_filament_id > 0)
483 tbb_geometric_unprintables[idx].insert(inner_wall_filament_id - 1);
484 continue;
485 }
486
487 if (!wall_expolys) {
488 if (!fill_expolys) {
489 fill_expolys = layerm->fill_expolygons;
490 fill_bbox = get_extents(*fill_expolys);
491 }
492 wall_expolys = diff_ex(layerm->raw_slices, *fill_expolys);
493 wall_bbox = get_extents(*wall_expolys);
494 }
495
496 if (wall_bbox.overlap(unprintable_area_bbox[idx]) &&
497 !intersection(*wall_expolys, unprintable_area_in_obj_coord[idx]).empty()) {
498 if (outer_wall_filament_id > 0)
499 tbb_geometric_unprintables[idx].insert(outer_wall_filament_id - 1);
500 if (inner_wall_filament_id > 0)
501 tbb_geometric_unprintables[idx].insert(inner_wall_filament_id - 1);
502 }
503 }
504 }
505 }
506 }
507 });
508
509 // add the elems in tbb container to final contianer
510 for (size_t idx = 0; idx < extruder_size; ++idx) {
511 geometric_unprintables[idx].insert(tbb_geometric_unprintables[idx].begin(), tbb_geometric_unprintables[idx].end());
512 }
513
514 return geometric_unprintables;
515}
std::vector< unsigned int > printing_extruders() const
Definition PrintObject.cpp:4583
void poly(std::vector< std::pair< Vec2d, Vec2d > > &out, std::initializer_list< Vec2d > p)
Definition DesignSketchTool.cpp:7751
bottom_surface_filament_id((ConfigOptionFloatOrPercent, internal_solid_infill_line_width))((ConfigOptionFloatsNullable
sparse_infill_filament_id((ConfigOptionFloatOrPercent, sparse_infill_line_width))((ConfigOptionPercent
zaa_minimize_perimeter_height printable_height((ConfigOptionFloatsNullable, extruder_printable_height))((ConfigOptionPoint
std::vector< std::vector< BedExcludeRegion > > get_bed_excluded_regions_by_extruder(const DynamicPrintConfig &cfg)
Definition PrintConfig.cpp:14211
inner_wall_filament_id((ConfigOptionFloatOrPercent, inner_wall_line_width))((ConfigOptionFloatsNullable
internal_solid_filament_id((ConfigOptionInt, top_surface_filament_id))((ConfigOptionInt

References Slic3r::bottom_surface_filament_id(), detect_extruder_geometric_unprintables(), Slic3r::get_bed_excluded_regions_by_extruder(), Slic3r::get_extents(), Slic3r::inner_wall_filament_id(), Slic3r::internal_solid_filament_id(), m_instances, m_layers, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::m_print, Slic3r::printable_height(), printing_extruders(), Slic3r::range(), scale_, and Slic3r::sparse_infill_filament_id().

Referenced by detect_extruder_geometric_unprintables().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ detect_overhangs_for_lift()

void Slic3r::PrintObject::detect_overhangs_for_lift ( )
private
931{
933 const double nozzle_diameter = m_print->config().nozzle_diameter.get_at(0);
934 const coordf_t line_width = this->config().get_abs_value("line_width", nozzle_diameter);
935
936 const float min_overlap = line_width * g_min_overhang_percent_for_lift;
937 size_t num_layers = this->layer_count();
938 size_t num_raft_layers = m_slicing_params.raft_layers();
939
940 m_print->set_status(71, L("Detect overhangs for auto-lift"));
941
943
944 tbb::spin_mutex layer_storage_mutex;
945 tbb::parallel_for(tbb::blocked_range<size_t>(num_raft_layers + 1, num_layers),
946 [this, min_overlap, line_width](const tbb::blocked_range<size_t>& range)
947 {
948 for (size_t layer_id = range.begin(); layer_id < range.end(); ++layer_id) {
949 Layer& layer = *m_layers[layer_id];
950 Layer& lower_layer = *layer.lower_layer;
951
952 ExPolygons overhangs = diff_ex(layer.lslices, offset_ex(lower_layer.lslices, scale_(min_overlap)));
953 layer.loverhangs = offset2_ex(overhangs, -0.1f * scale_(line_width), 0.1f * scale_(line_width));
954 layer.loverhangs_bbox = get_extents(layer.loverhangs);
955 }
956 });
957
959 }
960}
void clear_overhangs_for_lift()
Definition PrintObject.cpp:920
static const float g_min_overhang_percent_for_lift
Definition PrintObject.cpp:928
@ posDetectOverhangsForLift
Definition Print.hpp:127
ExPolygons offset2_ex(const ExPolygons &expolygons, const float delta1, const float delta2, JoinType joinType, double miterLimit)
Definition ClipperUtils.cpp:652

References clear_overhangs_for_lift(), config(), detect_overhangs_for_lift(), Slic3r::diff_ex(), Slic3r::g_min_overhang_percent_for_lift, Slic3r::get_extents(), L, layer_count(), Slic3r::Layer::loverhangs, Slic3r::Layer::loverhangs_bbox, Slic3r::Layer::lower_layer, Slic3r::Layer::lslices, m_layers, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::m_print, m_slicing_params, Slic3r::nozzle_diameter(), Slic3r::offset2_ex(), Slic3r::offset_ex(), Slic3r::posDetectOverhangsForLift, Slic3r::range(), scale_, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::set_done(), and Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::set_started().

Referenced by detect_overhangs_for_lift().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ detect_surfaces_type()

void Slic3r::PrintObject::detect_surfaces_type ( )
private
1942{
1943 BOOST_LOG_TRIVIAL(info) << "Detecting solid surfaces..." << log_memory_info();
1944
1945 // Interface shells: the intersecting parts are treated as self standing objects supporting each other.
1946 // Each of the objects will have a full number of top / bottom layers, even if these top / bottom layers
1947 // are completely hidden inside a collective body of intersecting parts.
1948 // This is useful if one of the parts is to be dissolved, or if it is transparent and the internal shells
1949 // should be visible.
1950 bool spiral_mode = this->print()->config().spiral_mode.value;
1951 bool interface_shells = ! spiral_mode && m_config.interface_shells.value;
1952 size_t num_layers = spiral_mode ? std::min(size_t(this->printing_region(0).config().bottom_shell_layers), m_layers.size()) : m_layers.size();
1953
1954 // The regions of a layer do not see each other here, and a layer cut through a fine relief takes far longer than the
1955 // others, so the regions run next to each other instead of one after another, each still over all layers.
1956 tbb::parallel_for(size_t(0), this->num_printing_regions(), [&](size_t region_id) {
1957 BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << region_id << " in parallel - start";
1958#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
1959 for (Layer *layer : m_layers)
1960 layer->m_regions[region_id]->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-initial");
1961#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
1962
1963 // If interface shells are allowed, the region->surfaces cannot be overwritten as they may be used by other threads.
1964 // Cache the result of the following parallel_loop.
1965 std::vector<Surfaces> surfaces_new;
1966 if (interface_shells)
1967 surfaces_new.assign(num_layers, Surfaces());
1968
1969 tbb::parallel_for(
1970 tbb::blocked_range<size_t>(0,
1971 spiral_mode ?
1972 // In spiral vase mode, reserve the last layer for the top surface if more than 1 layer is planned for the vase bottom.
1973 ((num_layers > 1) ? num_layers - 1 : num_layers) :
1974 // In non-spiral vase mode, go over all layers.
1975 m_layers.size()),
1976 [this, region_id, interface_shells, &surfaces_new](const tbb::blocked_range<size_t>& range) {
1977 // If we have soluble support material, don't bridge. The overhang will be squished against a soluble layer separating
1978 // the support from the print.
1979 // BBS: the above logic only applys for normal(auto) support. Complete logic:
1980 // 1. has support, top z distance=0 (soluble material), auto support
1981 // 2. for normal(auto), bridge_no_support is off
1982 // 3. for tree(auto), interface top layers=0, max bridge length=0, support_critical_regions_only=false (only in this way the bridge is fully supported)
1983 bool bottom_is_fully_supported = this->has_support() && m_config.support_top_z_distance.value == 0 && is_auto(m_config.support_type.value);
1984 if (m_config.support_type.value == stNormalAuto)
1985 bottom_is_fully_supported &= !m_config.bridge_no_support.value;
1986 else if (m_config.support_type.value == stTreeAuto) {
1987 bottom_is_fully_supported &= (m_config.support_interface_top_layers.value > 0 && m_config.max_bridge_length.value == 0 && m_config.support_critical_regions_only.value==false);
1988 }
1989 SurfaceType surface_type_bottom_other = bottom_is_fully_supported ? stBottom : stBottomBridge;
1990 for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
1991 m_print->throw_if_canceled();
1992 // BOOST_LOG_TRIVIAL(trace) << "Detecting solid surfaces for region " << region_id << " and layer " << layer->print_z;
1993 Layer *layer = m_layers[idx_layer];
1994 LayerRegion *layerm = layer->m_regions[region_id];
1995 // comparison happens against the *full* slices (considering all regions)
1996 // unless internal shells are requested
1997 Layer *upper_layer = (idx_layer + 1 < this->layer_count()) ? m_layers[idx_layer + 1] : nullptr;
1998 Layer *lower_layer = (idx_layer > 0) ? m_layers[idx_layer - 1] : nullptr;
1999 // collapse very narrow parts (using the safety offset in the diff is not enough)
2000 const float offset = layerm->flow(frExternalPerimeter).scaled_width() / 10.f;
2001
2002 ExPolygons layerm_slices_surfaces = to_expolygons(layerm->slices.surfaces);
2003 // no_perimeter_full_bridge allow to put bridges where there are nothing, hence adding area to slice, that's why we need to start from the result of PerimeterGenerator.
2004 if (layerm->region().config().counterbore_hole_bridging.value == chbFilled) {
2005 layerm_slices_surfaces = union_ex(layerm_slices_surfaces, to_expolygons(layerm->fill_surfaces.surfaces));
2006 }
2007
2008 // find top surfaces (difference between current surfaces
2009 // of current layer and upper one)
2010 Surfaces top;
2011 if (upper_layer) {
2012 ExPolygons upper_slices = interface_shells ?
2013 diff_ex(layerm_slices_surfaces, upper_layer->m_regions[region_id]->slices.surfaces, ApplySafetyOffset::Yes) :
2014 diff_ex_by_piece(layerm_slices_surfaces, to_polygons(upper_layer->lslices), ApplySafetyOffset::Yes);
2015 surfaces_append(top, opening_ex(upper_slices, offset), stTop);
2016 } else {
2017 // if no upper layer, all surfaces of this one are solid
2018 // we clone surfaces because we're going to clear the slices collection
2019 top = layerm->slices.surfaces;
2020 for (Surface &surface : top)
2021 surface.surface_type = stTop;
2022 }
2023
2024 // Find bottom surfaces (difference between current surfaces of current layer and lower one).
2025 Surfaces bottom;
2026 if (lower_layer) {
2027#if 0
2028 //FIXME Why is this branch failing t\multi.t ?
2029 Polygons lower_slices = interface_shells ?
2030 to_polygons(lower_layer->get_region(region_id)->slices.surfaces) :
2031 to_polygons(lower_layer->slices);
2032 surfaces_append(bottom,
2033 opening_ex(diff(layerm_slices_surfaces, lower_slices, true), offset),
2034 surface_type_bottom_other);
2035#else
2036 // Any surface lying on the void is a true bottom bridge (an overhang)
2038 bottom,
2039 opening_ex(
2040 diff_ex_by_piece(layerm_slices_surfaces, to_polygons(lower_layer->lslices), ApplySafetyOffset::Yes),
2041 offset),
2042 surface_type_bottom_other);
2043 // if user requested internal shells, we need to identify surfaces
2044 // lying on other slices not belonging to this region
2045 if (interface_shells) {
2046 // non-bridging bottom surfaces: any part of this layer lying
2047 // on something else, excluding those lying on our own region
2049 bottom,
2050 opening_ex(
2051 diff_ex(
2052 intersection(layerm_slices_surfaces, lower_layer->lslices), // supported
2053 lower_layer->m_regions[region_id]->slices.surfaces,
2055 offset),
2056 stBottom);
2057 }
2058#endif
2059 } else {
2060 // if no lower layer, all surfaces of this one are solid
2061 // we clone surfaces because we're going to clear the slices collection
2062 bottom = layerm->slices.surfaces;
2063 for (Surface &surface : bottom)
2064 surface.surface_type = stBottom;
2065 }
2066
2067 // now, if the object contained a thin membrane, we could have overlapping bottom
2068 // and top surfaces; let's do an intersection to discover them and consider them
2069 // as bottom surfaces (to allow for bridge detection)
2070 if (! top.empty() && ! bottom.empty()) {
2071 const auto cracks = intersection_ex_by_piece(to_expolygons(top), to_polygons(bottom));
2072 if (!cracks.empty()) {
2073 if (lower_layer) { // Only detect small cracks for non-first layer, because first layer should always be bottom
2074 const float small_crack_threshold = -layerm->flow(frExternalPerimeter).scaled_width() * 1.5;
2075
2076 // Only the bottom surfaces near a crack can take part: one that contains it must contain its box,
2077 // and one whose box misses the grown crack is left unchanged by removing it. A layer cut through
2078 // a fine relief has thousands of both, which made this loop quadratic.
2079 for (const auto& crack : cracks) {
2080 if (offset_ex(crack, small_crack_threshold).empty()) {
2081 // For small cracks, if it's part of a large bottom surface, then it should be added to bottom as well
2082 const BoundingBox crack_bbox = get_extents(crack);
2083 if (std::any_of(bottom.begin(), bottom.end(), [&crack, &crack_bbox, small_crack_threshold](const Surface& s) {
2084 const auto& se = s.expolygon;
2085 return get_extents(se).inflated(SCALED_EPSILON).contains(crack_bbox)
2086 && diff_ex(crack, se, ApplySafetyOffset::Yes).empty()
2087 && se.area() > crack.area() * 2
2088 && !offset_ex(diff_ex(se, crack), small_crack_threshold).empty();
2089 })) continue;
2090
2091 // Crack too small, leave it as part of the top surface, remove it from bottom surfaces
2092 const ExPolygons grown_crack = offset_ex(crack, -small_crack_threshold);
2093 const BoundingBox grown_bbox = get_extents(grown_crack);
2094 Surfaces bot_tmp;
2095 for (auto& b : bottom) {
2096 if (get_extents(b.expolygon).overlap(grown_bbox))
2097 surfaces_append(bot_tmp, diff_ex(b.expolygon, grown_crack), b.surface_type);
2098 else
2099 bot_tmp.emplace_back(std::move(b));
2100 }
2101 bottom = std::move(bot_tmp);
2102 }
2103 }
2104 }
2105
2106 ExPolygons top_expolygons = to_expolygons(std::move(top));
2107 top.clear();
2108 surfaces_append(top, diff_ex_by_piece(top_expolygons, to_polygons(bottom)), stTop);
2109 }
2110 }
2111
2112 // ORCA: Grow the top surfaces by top_surface_expansion, so the top solid infill also covers the
2113 // material left by features rising from the middle of a top surface (filling the holes and
2114 // joining the tops, so the features rest on solid infill). Each connected island is grown and
2115 // clipped separately: growing one island's top across a gap into another - which may have no top
2116 // surface at all, leaving a partially filled layer - is never allowed. The original top is
2117 // unioned back in and bottom surfaces are never claimed, so this can only add area.
2118 const PrintRegionConfig &region_config = layerm->region().config();
2119 const double top_expansion = region_config.top_surface_expansion.value;
2120 // Nothing to expand without a top fill: a 0% top surface density leaves the top layer with
2121 // walls only, and zero top shell layers retypes it as internal in prepare_fill_surfaces().
2122 if (top_expansion > 0. && region_config.top_shell_layers.value > 0 &&
2123 region_config.top_surface_density.value > 0. && ! top.empty()) {
2124 const double d = scale_(top_expansion);
2125 const ExPolygons T = union_ex(to_expolygons(top));
2126 // Walls are laid out on spacing, not width; and only_one_wall_top leaves a single wall over
2127 // a top surface, which is exactly the situation handled here.
2128 const int wall_loops = region_config.only_one_wall_top.value ? std::min(region_config.wall_loops.value, 1)
2129 : region_config.wall_loops.value;
2130 const double wall_band = wall_loops <= 0 ? 0. :
2131 double(layerm->flow(frExternalPerimeter).scaled_width()) +
2132 double(layerm->flow(frPerimeter).scaled_spacing()) * double(wall_loops - 1);
2133 const double margin = scale_(region_config.top_surface_expansion_margin.value);
2134 // minimum real top to act on: ignore anything thinner than ~2 top-infill lines
2135 const float min_top = float(layerm->flow(frTopSolidInfill).scaled_width());
2136 const auto direction = region_config.top_surface_expansion_direction.value;
2137
2138 ExPolygons grown;
2139 for (const ExPolygon &island : union_ex(layerm_slices_surfaces)) {
2140 // The top infill only exists inside the perimeters, so seed and measure from the infill
2141 // region (the island minus the wall band), not the raw slice: a section whose exposed top
2142 // is just the walls themselves is then skipped instead of being flooded inward. Clip the
2143 // layer's tops to the island first, to keep the boolean ops proportional to the island.
2144 const ExPolygons infill_region = wall_band > 0. ? offset_ex(island, -float(wall_band)) : ExPolygons{ island };
2145 const ExPolygons island_top = intersection_ex(
2146 ClipperUtils::clip_clipper_polygons_with_subject_bbox(T, get_extents(island).inflated(SCALED_EPSILON)),
2147 infill_region);
2148 if (opening_ex(island_top, min_top).empty())
2149 continue; // no real top infill in this section - never expand into it
2150
2151 // Grow, then keep only what the configured direction allows, using the top's own filled
2152 // outline (same outer edge, holes closed) to tell the two apart.
2153 ExPolygons expanded = offset_ex(island_top, float(d), jtMiter, 2.);
2154 if (direction != TopSurfaceExpansionDirection::InwardAndOutward) {
2155 ExPolygons outline;
2156 outline.reserve(island_top.size());
2157 for (const ExPolygon &ex : island_top)
2158 outline.emplace_back(ex.contour);
2159 outline = union_ex(outline);
2160 expanded = direction == TopSurfaceExpansionDirection::Inward ?
2161 intersection_ex(expanded, outline) : // only growth into the holes
2162 diff_ex(expanded, diff_ex(outline, island_top)); // only growth past the outer edge
2163 }
2164 // hold the expansion clear of the walls by the configured margin
2165 const ExPolygons allowed = margin > 0. ? offset_ex(infill_region, -float(margin)) : infill_region;
2166 append(grown, intersection_ex(expanded, allowed));
2167 }
2168
2169 ExPolygons new_top = diff_ex(union_ex(T, grown), to_expolygons(bottom));
2170 top.clear();
2171 surfaces_append(top, std::move(new_top), stTop);
2172 }
2173
2174 #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2175 {
2176 static int iRun = 0;
2177 std::vector<std::pair<Slic3r::ExPolygons, SVG::ExPolygonAttributes>> expolygons_with_attributes;
2178 expolygons_with_attributes.emplace_back(std::make_pair(union_ex(top), SVG::ExPolygonAttributes("green")));
2179 expolygons_with_attributes.emplace_back(std::make_pair(union_ex(bottom), SVG::ExPolygonAttributes("brown")));
2180 expolygons_with_attributes.emplace_back(std::make_pair(to_expolygons(layerm->slices.surfaces), SVG::ExPolygonAttributes("black")));
2181 SVG::export_expolygons(debug_out_path("1_detect_surfaces_type_%d_region%d-layer_%f.svg", iRun ++, region_id, layer->print_z).c_str(), expolygons_with_attributes);
2182 }
2183 #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2184
2185 // save surfaces to layer
2186 Surfaces &surfaces_out = interface_shells ? surfaces_new[idx_layer] : layerm->slices.surfaces;
2187 Surfaces surfaces_backup;
2188 if (! interface_shells) {
2189 surfaces_backup = std::move(surfaces_out);
2190 surfaces_out.clear();
2191 }
2192 //const Surfaces &surfaces_prev = interface_shells ? layerm->slices.surfaces : surfaces_backup;
2193 const ExPolygons& surfaces_prev_expolys = interface_shells ? layerm_slices_surfaces : to_expolygons(surfaces_backup);
2194
2195 // find internal surfaces (difference between top/bottom surfaces and others)
2196 {
2197 Polygons topbottom = to_polygons(top);
2198 polygons_append(topbottom, to_polygons(bottom));
2199 surfaces_append(surfaces_out, diff_ex_by_piece(surfaces_prev_expolys, topbottom), stInternal);
2200 }
2201
2202 surfaces_append(surfaces_out, std::move(top));
2203 surfaces_append(surfaces_out, std::move(bottom));
2204
2205 // Slic3r::debugf " layer %d has %d bottom, %d top and %d internal surfaces\n",
2206 // $layerm->layer->id, scalar(@bottom), scalar(@top), scalar(@internal) if $Slic3r::debug;
2207
2208 #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2209 layerm->export_region_slices_to_svg_debug("detect_surfaces_type-final");
2210 #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2211 }
2212 }
2213 ); // for each layer of a region
2214 m_print->throw_if_canceled();
2215
2216 if (interface_shells) {
2217 // Move surfaces_new to layerm->slices.surfaces
2218 for (size_t idx_layer = 0; idx_layer < num_layers; ++ idx_layer)
2219 m_layers[idx_layer]->m_regions[region_id]->slices.surfaces = std::move(surfaces_new[idx_layer]);
2220 }
2221
2222 if (spiral_mode) {
2223 if (num_layers > 1)
2224 // Turn the last bottom layer infill to a top infill, so it will be extruded with a proper pattern.
2225 m_layers[num_layers - 1]->m_regions[region_id]->slices.set_type(stTop);
2226 for (size_t i = num_layers; i < m_layers.size(); ++ i)
2227 m_layers[i]->m_regions[region_id]->slices.set_type(stInternal);
2228 }
2229
2230 // ==================================================================================================
2231 // === ORCA: Create a SECOND bridge layer above the first bridge layer. =============================
2232 // === ORCA: Surface is flagged as a new surface type called stInternalAfterExternalBridge ==================
2233 // === Algorithm only considers stInternal surfaces for re-classification, leaving stTop unaffected =
2234 // ==================================================================================================
2235 // Only iterate to the second-to-last layer, since we look at layer i+1.
2236 if( (this->config().enable_extra_bridge_layer.value == eblApplyToAll) || (this->config().enable_extra_bridge_layer.value == eblExternalBridgeOnly)){
2237 const size_t last = (m_layers.empty() ? 0 : m_layers.size() - 1);
2238
2239 // ORCA: Two-phase split (collect-then-apply) to eliminate a data race in the
2240 // original single-phase parallel_for, where iteration `i` rewrote
2241 // m_layers[i+1]->slices.surfaces via std::move while iteration `i+1` (running
2242 // on an adjacent TBB block on another worker thread) was iterating that same
2243 // Surfaces vector as its bot_surfs. Splitting into a read-only collect pass
2244 // followed by a write-only apply pass removes the cross-iteration aliasing.
2245 //
2246 // Phase 1: read-only pass — collect each layer's stBottomBridge polygons into a
2247 // per-layer cache. No surfaces are mutated, so concurrent reads are safe.
2248 std::vector<Polygons> bridge_polys_per_layer(last);
2249 tbb::parallel_for(tbb::blocked_range<size_t>(0, last), [this, region_id, &bridge_polys_per_layer](const tbb::blocked_range<size_t> &range) {
2250 for (size_t i = range.begin(); i < range.end(); ++i) {
2251 m_print->throw_if_canceled();
2252 const Surfaces &bot_surfs = m_layers[i]->m_regions[region_id]->slices.surfaces;
2253 for (const Surface &sbot : bot_surfs) {
2254 if (sbot.surface_type == stBottomBridge) {
2255 polygons_append(bridge_polys_per_layer[i], to_polygons(sbot));
2256 }
2257 }
2258 }
2259 });
2260
2261 // Phase 2: write pass — each iteration mutates only m_layers[i+1]->slices.surfaces
2262 // and reads its bridge polygons from the precomputed cache. Different iterations
2263 // never share a write target, so there is no aliasing between worker threads.
2264 tbb::parallel_for( tbb::blocked_range<size_t>(0, last), [this, region_id, &bridge_polys_per_layer](const tbb::blocked_range<size_t> &range) {
2265 for (size_t i = range.begin(); i < range.end(); ++i) {
2266 m_print->throw_if_canceled();
2267
2268 // Step 1 + 2: pull the precomputed bridge polygons for the current source layer.
2269 const Polygons &polygons_bridge = bridge_polys_per_layer[i];
2270
2271 // Step 3: Early termination of loop if no meaningfull bridge found
2272 // No bridge polygons found, continue to the next layer
2273 if (polygons_bridge.empty())
2274 continue;
2275
2276 // Step 4: Bottom bridge polygons found - scan and create layer+1 bridge polygon
2277 Surfaces &top_surfs = m_layers[i + 1]->m_regions[region_id]->slices.surfaces;
2278 Surfaces new_surfaces;
2279 new_surfaces.reserve(top_surfs.size());
2280
2281 //filtering parameters here. Filter bridges that are less than 2x external walls and 2xN internal perimeters wide.
2282 LayerRegion *layerm = m_layers[i]->m_regions[region_id];
2283 int number_of_internal_walls = std::max(0, layerm->m_region->config().wall_loops - 1); // number of internal walls, clamped to a minimum of 0 as a safety precaution
2284 float offset_distance = layerm->flow(frExternalPerimeter).scaled_width() // shrink down by external perimeter width (effectively filtering out 2x external perimeters wide bridges)
2285 + ((layerm->flow(frPerimeter).scaled_width()) * number_of_internal_walls); // shrink down by number of external walls * width of them, effectively filtering out 2x internal perimeter wide bridges
2286 // The reason for doing the above filtering is that in pure bridges, the walls are always printed separately as overhang walls. Here we care about the bridge infill which is distinct and is the remainder
2287 // of the bridge area minus the perimeter width on both sides of the bridge itself.
2288 // This would also skip generation of very short dual bridge layers (that are shorter than N perimeters), but these are unecessary as the bridge distance is
2289 // We could reduce this slightly to account for innacurcies in the clipping operation.
2290 // TODO: Monitor GitHub issues to check whether second bridge layers are ommited where they should be generated. If yes, reduce the filtering distance
2291
2292 // ORCA: Same-layer-top guard.
2293 //
2294 // Collect every stTop polygon present at layer i+1 (this region) and
2295 // expand it by the same `offset_distance` used by the bridge filter
2296 // above. Note that `offset_distance` here is the full wall-band
2297 // distance for the region (external wall width + all configured
2298 // internal wall widths, i.e. external + (wall_loops - 1) × internal),
2299 // not a single perimeter width. Any candidate second-bridge area that
2300 // falls under this expanded mask will be subtracted out below.
2301 //
2302 // Why this exists: detect_surfaces_type() classifies a layer's "top"
2303 // surfaces as the geometry that is not covered by the layer above. Those
2304 // stTop regions often have small stInternal islands embedded in them.
2305 // The pre-existing wall-band filter (shrink_ex/offset_ex by
2306 // offset_distance) is supposed to throw those tiny islands away, but
2307 // its result is sensitive to Clipper's floating-point order of
2308 // operations: on macOS ARM the filter eats them, on Windows/Intel it
2309 // doesn't. Visible bridges then show up scattered across the printed
2310 // top surface.
2311 //
2312 // Expanding stTop by offset_distance and subtracting it from the
2313 // overlap makes the decision platform-independent: an island fully
2314 // surrounded by stTop disappears regardless of which Clipper happens
2315 // to be doing the math, while large stInternal regions away from the
2316 // top survive intact (the expansion only nibbles the wall-band depth
2317 // inward).
2318 //
2319 // Keep ExPolygons throughout so that any holes inside an stTop surface
2320 // are offset with the correct sign (positive offset shrinks holes /
2321 // grows the solid region). Using Polygons + expand() would treat the
2322 // contour and each hole as independent polygons and could distort the
2323 // mask.
2324 ExPolygons same_layer_top_expanded;
2325 {
2326 ExPolygons same_layer_top;
2327 for (const Surface &s : top_surfs) {
2328 if (s.surface_type == stTop)
2329 same_layer_top.push_back(s.expolygon);
2330 }
2331 if (! same_layer_top.empty())
2332 same_layer_top_expanded = offset_ex(same_layer_top, offset_distance);
2333 }
2334
2335 // For each surface in the layer above
2336 for (Surface &s_up : top_surfs) {
2337 // Only reclassify stInternal polygons (i.e. what will become later solid and sparse infill)
2338 // Leave the rest unaffected
2339 if (s_up.surface_type != stInternal) {
2340 new_surfaces.push_back(std::move(s_up)); // do not modify them
2341 continue; // continue to the next surface
2342 }
2343 // Identify stInternal polygons that overlap with the bridging polygons on the layer underneath.
2344 Polygons p_up = to_polygons(s_up);
2345 ExPolygons overlap = intersection_ex(p_up, polygons_bridge , ApplySafetyOffset::Yes);
2346 // Filter out the resulting candidate bridges based on size. First perform a shrink operation...
2347 // ...followed by an expand operation to bring them back to the original size (positive offset)
2348 overlap = offset_ex(shrink_ex(overlap, offset_distance), offset_distance);
2349
2350 // ORCA: subtract the expanded same-layer stTop mask (see comment above
2351 // the mask construction). Drops stInternal islands fully surrounded by
2352 // stTop at i+1 without affecting bridges that lie away from the top.
2353 if (! same_layer_top_expanded.empty() && ! overlap.empty())
2354 overlap = diff_ex(overlap, same_layer_top_expanded, ApplySafetyOffset::Yes);
2355
2356 // Now subtract the filtered new bridge layer from the remaining internal surfaces to create the new internal surface
2357 ExPolygons remainder = diff_ex(p_up, overlap, ApplySafetyOffset::Yes);
2358
2359 // Remainder stays as stInternal
2360 ExPolygons unified_remainder = union_safety_offset_ex(remainder);
2361 for (auto &ex_remainder : unified_remainder) {
2362 Surface s(stInternal, ex_remainder);
2363 new_surfaces.push_back(std::move(s));
2364 }
2365 // Overlap portion becomes the new polygon type - stInternalAfterExternalBridge
2366 ExPolygons unified_overlap = union_safety_offset_ex(overlap);
2367 for (auto &ex_overlap : unified_overlap) {
2368 Surface s(stInternalAfterExternalBridge, ex_overlap);
2369 new_surfaces.push_back(std::move(s));
2370 }
2371 }
2372 top_surfs = std::move(new_surfaces);
2373 }
2374 }
2375 );
2376 }
2377 // ==============================================================================================================
2378 // === ORCA: End of second external bridge layer changes =======================================================
2379 // ==============================================================================================================
2380
2381 }); // for each this->print->region_count
2382
2383 // ==============================================================================================================
2384 // === ORCA: Interim workaround - for now the new stInternalAfterExternalBridge surfaace is re-classified ==============
2385 // === back to a bottom bridge. As a starting point, this improves bridging reliability as it extrudes ==========
2386 // === two external bridge layers. However, TODO: Implement a new surface type throughout the codebase ==========
2387 // ==============================================================================================================
2388 // Once all the regions have their second bridge layer, and before their slices are trimmed into fill surfaces below.
2389 if ((this->config().enable_extra_bridge_layer.value == eblApplyToAll) || (this->config().enable_extra_bridge_layer.value == eblExternalBridgeOnly)) {
2390 tbb::parallel_for(tbb::blocked_range<size_t>(0, m_layers.size()), [this](const tbb::blocked_range<size_t> &range) {
2391 for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer)
2392 for (LayerRegion *layerm : m_layers[idx_layer]->regions())
2393 for (Surface &s : layerm->slices.surfaces)
2394 if (s.surface_type == stInternalAfterExternalBridge)
2395 s.surface_type = stBottomBridge;
2396 });
2397 m_print->throw_if_canceled();
2398 }
2399
2400 tbb::parallel_for(size_t(0), this->num_printing_regions(), [&](size_t region_id) {
2401 BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << region_id << " - clipping in parallel - start";
2402 // Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces.
2403 tbb::parallel_for(
2404 tbb::blocked_range<size_t>(0, m_layers.size()),
2405 [this, region_id](const tbb::blocked_range<size_t>& range) {
2406 for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
2407 m_print->throw_if_canceled();
2408 LayerRegion *layerm = m_layers[idx_layer]->m_regions[region_id];
2409 layerm->slices_to_fill_surfaces_clipped();
2410#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2411 layerm->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-final");
2412#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2413 } // for each layer of a region
2414 });
2415 m_print->throw_if_canceled();
2416 BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << region_id << " - clipping in parallel - end";
2417 });
2418
2419 // Mark the object to have the region slices classified (typed, which also means they are split based on whether they are supported, bridging, top layers etc.)
2420 m_typed_slices = true;
2421}
bool m_typed_slices
Definition Print.hpp:705
static void export_expolygons(const char *path, const BoundingBox &bbox, const Slic3r::ExPolygons &expolygons, std::string stroke_outer="black", std::string stroke_holes="blue", coordf_t stroke_width=0)
Definition SVG.cpp:381
Slic3r::ExPolygons opening_ex(const Slic3r::ExPolygons &polygons, const float delta, JoinType joinType=DefaultJoinType, double miterLimit=DefaultMiterLimit)
Definition ClipperUtils.hpp:447
std::string debug_out_path(const char *name,...)
Definition utils.cpp:382
zaa_minimize_perimeter_height PrintRegionConfig
Definition PrintConfig.hpp:2183
@ frExternalPerimeter
Definition Flow.hpp:19
@ eblExternalBridgeOnly
Definition PrintConfig.hpp:420
bottom_shell_layers((ConfigOptionFloat, bottom_shell_thickness))((ConfigOptionInt
void surfaces_append(Surfaces &dst, const ExPolygons &src, SurfaceType surfaceType)
Definition Surface.hpp:254
Slic3r::ExPolygons diff_ex_by_piece(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:1006
ApplySafetyOffset
Definition ClipperUtils.hpp:49
@ stTop
Definition Surface.hpp:18
@ stBottomBridge
Definition Surface.hpp:22
@ stBottom
Definition Surface.hpp:20
@ stInternalAfterExternalBridge
Definition Surface.hpp:24

References Slic3r::bottom_shell_layers(), config(), detect_surfaces_type(), Slic3r::diff(), Slic3r::diff_ex(), Slic3r::diff_ex_by_piece(), Slic3r::intersection(), Slic3r::log_memory_info(), m_config, m_layers, num_printing_regions(), Slic3r::offset(), Slic3r::opening_ex(), Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::print(), printing_region(), Slic3r::PrintRegionConfig, Slic3r::range(), Slic3r::stBottom, Slic3r::stBottomBridge, Slic3r::stTop, Slic3r::surfaces_append(), Slic3r::to_expolygons(), Slic3r::to_polygons(), Slic3r::union_ex(), and Slic3r::Yes.

Referenced by detect_surfaces_type(), and prepare_infill().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ discover_horizontal_shells()

void Slic3r::PrintObject::discover_horizontal_shells ( )
private
4752{
4753 BOOST_LOG_TRIVIAL(trace) << "discover_horizontal_shells()";
4754
4755 for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
4756 for (size_t i = 0; i < m_layers.size(); ++ i) {
4757 m_print->throw_if_canceled();
4758 Layer *layer = m_layers[i];
4759 LayerRegion *layerm = layer->regions()[region_id];
4760 const PrintRegionConfig &region_config = layerm->region().config();
4761
4762 if (!region_config.extra_solid_infills.value.empty() &&
4763 check_layer_id_pattern(region_config.extra_solid_infills.value, i)) {
4764 // Insert a solid internal layer. Mark stInternal surfaces as stInternalSolid.
4765 for (Surface& surface : layerm->fill_surfaces.surfaces)
4766 if (surface.surface_type == stInternal)
4767 surface.surface_type = stInternalSolid;
4768 }
4769
4770 // If ensure_vertical_shell_thickness, then the rest has already been performed by discover_vertical_shells().
4771 if (region_config.ensure_vertical_shell_thickness.value == evstAll)
4772 continue;
4773
4774 coordf_t print_z = layer->print_z;
4775 coordf_t bottom_z = layer->bottom_z();
4776 for (size_t idx_surface_type = 0; idx_surface_type < 3; ++ idx_surface_type) {
4777 m_print->throw_if_canceled();
4778 SurfaceType type = (idx_surface_type == 0) ? stTop : (idx_surface_type == 1) ? stBottom : stBottomBridge;
4779 int num_solid_layers = (type == stTop) ? region_config.top_shell_layers.value : region_config.bottom_shell_layers.value;
4780 if (num_solid_layers == 0)
4781 continue;
4782 // Find slices of current type for current layer.
4783 // Use slices instead of fill_surfaces, because they also include the perimeter area,
4784 // which needs to be propagated in shells; we need to grow slices like we did for
4785 // fill_surfaces though. Using both ungrown slices and grown fill_surfaces will
4786 // not work in some situations, as there won't be any grown region in the perimeter
4787 // area (this was seen in a model where the top layer had one extra perimeter, thus
4788 // its fill_surfaces were thinner than the lower layer's infill), however it's the best
4789 // solution so far. Growing the external slices by EXTERNAL_INFILL_MARGIN will put
4790 // too much solid infill inside nearly-vertical slopes.
4791
4792 // Surfaces including the area of perimeters. Everything, that is visible from the top / bottom
4793 // (not covered by a layer above / below).
4794 // This does not contain the areas covered by perimeters!
4795 Polygons solid;
4796 for (const Surface &surface : layerm->slices.surfaces)
4797 if (surface.surface_type == type)
4798 polygons_append(solid, to_polygons(surface.expolygon));
4799 // Infill areas (slices without the perimeters).
4800 for (const Surface &surface : layerm->fill_surfaces.surfaces)
4801 if (surface.surface_type == type)
4802 polygons_append(solid, to_polygons(surface.expolygon));
4803 if (solid.empty())
4804 continue;
4805// Slic3r::debugf "Layer %d has %s surfaces\n", $i, ($type == stTop) ? 'top' : 'bottom';
4806
4807 // Scatter top / bottom regions to other layers. Scattering process is inherently serial, it is difficult to parallelize without locking.
4808 for (int n = (type == stTop) ? int(i) - 1 : int(i) + 1;
4809 (type == stTop) ?
4810 (n >= 0 && (int(i) - n < num_solid_layers ||
4811 print_z - m_layers[n]->print_z < region_config.top_shell_thickness.value - EPSILON)) :
4812 (n < int(m_layers.size()) && (n - int(i) < num_solid_layers ||
4813 m_layers[n]->bottom_z() - bottom_z < region_config.bottom_shell_thickness.value - EPSILON));
4814 (type == stTop) ? -- n : ++ n)
4815 {
4816// Slic3r::debugf " looking for neighbors on layer %d...\n", $n;
4817 // Reference to the lower layer of a TOP surface, or an upper layer of a BOTTOM surface.
4818 LayerRegion *neighbor_layerm = m_layers[n]->regions()[region_id];
4819
4820 // find intersection between neighbor and current layer's surfaces
4821 // intersections have contours and holes
4822 // we update $solid so that we limit the next neighbor layer to the areas that were
4823 // found on this one - in other words, solid shells on one layer (for a given external surface)
4824 // are always a subset of the shells found on the previous shell layer
4825 // this approach allows for DWIM in hollow sloping vases, where we want bottom
4826 // shells to be generated in the base but not in the walls (where there are many
4827 // narrow bottom surfaces): reassigning $solid will consider the 'shadow' of the
4828 // upper perimeter as an obstacle and shell will not be propagated to more upper layers
4829 //FIXME How does it work for stInternalBRIDGE? This is set for sparse infill. Likely this does not work.
4830 Polygons new_internal_solid;
4831 {
4832 Polygons internal;
4833 for (const Surface &surface : neighbor_layerm->fill_surfaces.surfaces)
4834 if (surface.surface_type == stInternal || surface.surface_type == stInternalSolid)
4835 polygons_append(internal, to_polygons(surface.expolygon));
4836 new_internal_solid = intersection(solid, internal, ApplySafetyOffset::Yes);
4837 }
4838 if (new_internal_solid.empty()) {
4839 // No internal solid needed on this layer. In order to decide whether to continue
4840 // searching on the next neighbor (thus enforcing the configured number of solid
4841 // layers, use different strategies according to configured infill density:
4842
4843 // Orca: Also use the same strategy if the user has selected to further reduce
4844 // the amount of solid infill on walls.
4845 if (region_config.sparse_infill_density.value == 0 || region_config.ensure_vertical_shell_thickness.value == evstCriticalOnly || region_config.ensure_vertical_shell_thickness.value == evstNone) {
4846 // If user expects the object to be void (for example a hollow sloping vase),
4847 // don't continue the search. In this case, we only generate the external solid
4848 // shell if the object would otherwise show a hole (gap between perimeters of
4849 // the two layers), and internal solid shells are a subset of the shells found
4850 // on each previous layer.
4851 goto EXTERNAL;
4852 } else {
4853 // If we have internal infill, we can generate internal solid shells freely.
4854 continue;
4855 }
4856 }
4857
4858 float factor = 0.0f;
4859 if (region_config.sparse_infill_density.value == 0)
4860 factor = 1.0f;
4861 else if (region_config.ensure_vertical_shell_thickness.value == evstNone)
4862 factor = 0.5f;
4863 else if (region_config.ensure_vertical_shell_thickness.value == evstCriticalOnly)
4864 factor = 0.2f;
4865 if (factor > 0.0f) {
4866 // if we're printing a hollow object we discard any solid shell thinner
4867 // than a perimeter width, since it's probably just crossing a sloping wall
4868 // and it's not wanted in a hollow print even if it would make sense when
4869 // obeying the solid shell count option strictly (DWIM!)
4870
4871 // Orca: Also use the same strategy if the user has selected to reduce
4872 // the amount of solid infill on walls. However reduce the margin to 20% overhang
4873 // as we want to generate infill on sloped vertical surfaces but still keep a small amount of
4874 // filtering. This is an arbitrary value to make this option safe
4875 // by ensuring that top surfaces, especially slanted ones dont go **completely** unsupported
4876 // especially when using single perimeter top layers.
4877 float margin = float(neighbor_layerm->flow(frExternalPerimeter).scaled_width()) * factor;
4878 Polygons too_narrow = diff(new_internal_solid,
4879 opening(new_internal_solid, margin, margin + ClipperSafetyOffset, jtMiter, 5));
4880 // Trim the regularized region by the original region.
4881 if (!too_narrow.empty())
4882 new_internal_solid = solid = diff(new_internal_solid, too_narrow);
4883 }
4884
4885 // make sure the new internal solid is wide enough, as it might get collapsed
4886 // when spacing is added in Fill.pm
4887 {
4888 //FIXME Vojtech: Disable this and you will be sorry.
4889 float margin = (region_config.ensure_vertical_shell_thickness.value != evstNone ? 3.f : 1.0f) * layerm->flow(frSolidInfill).scaled_width(); // require at least this size
4890 // we use a higher miterLimit here to handle areas with acute angles
4891 // in those cases, the default miterLimit would cut the corner and we'd
4892 // get a triangle in $too_narrow; if we grow it below then the shell
4893 // would have a different shape from the external surface and we'd still
4894 // have the same angle, so the next shell would be grown even more and so on.
4895 Polygons too_narrow = diff(
4896 new_internal_solid,
4897 opening(new_internal_solid, margin, margin + ClipperSafetyOffset, jtMiter, 5));
4898 if (! too_narrow.empty()) {
4899 // grow the collapsing parts and add the extra area to the neighbor layer
4900 // as well as to our original surfaces so that we support this
4901 // additional area in the next shell too
4902 // make sure our grown surfaces don't exceed the fill area
4903 Polygons internal;
4904 for (const Surface &surface : neighbor_layerm->fill_surfaces.surfaces)
4905 if (surface.is_internal() && !surface.is_bridge())
4906 polygons_append(internal, to_polygons(surface.expolygon));
4907 polygons_append(new_internal_solid,
4909 expand(too_narrow, +margin),
4910 // Discard bridges as they are grown for anchoring and we can't
4911 // remove such anchors. (This may happen when a bridge is being
4912 // anchored onto a wall where little space remains after the bridge
4913 // is grown, and that little space is an internal solid shell so
4914 // it triggers this too_narrow logic.)
4915 internal));
4916 // solid = new_internal_solid;
4917 }
4918 }
4919
4920 // internal-solid are the union of the existing internal-solid surfaces
4921 // and new ones
4922 SurfaceCollection backup = std::move(neighbor_layerm->fill_surfaces);
4923 polygons_append(new_internal_solid, to_polygons(backup.filter_by_type(stInternalSolid)));
4924 ExPolygons internal_solid = union_ex(new_internal_solid);
4925 // assign new internal-solid surfaces to layer
4926 neighbor_layerm->fill_surfaces.set(internal_solid, stInternalSolid);
4927 // subtract intersections from layer surfaces to get resulting internal surfaces
4928 Polygons polygons_internal = to_polygons(std::move(internal_solid));
4929 ExPolygons internal = diff_ex(backup.filter_by_type(stInternal), polygons_internal, ApplySafetyOffset::Yes);
4930 // assign resulting internal surfaces to layer
4931 neighbor_layerm->fill_surfaces.append(internal, stInternal);
4932 polygons_append(polygons_internal, to_polygons(std::move(internal)));
4933 // assign top and bottom surfaces to layer
4934 backup.keep_types({ stTop, stBottom, stBottomBridge });
4935 std::vector<SurfacesPtr> top_bottom_groups;
4936 backup.group(&top_bottom_groups);
4937 for (SurfacesPtr &group : top_bottom_groups)
4938 neighbor_layerm->fill_surfaces.append(
4939 diff_ex(group, polygons_internal),
4940 // Use an existing surface as a template, it carries the bridge angle etc.
4941 *group.front());
4942 }
4943 EXTERNAL:;
4944 } // foreach type (stTop, stBottom, stBottomBridge)
4945 } // for each layer
4946 } // for each region
4947
4948#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
4949 for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) {
4950 for (const Layer *layer : m_layers) {
4951 const LayerRegion *layerm = layer->m_regions[region_id];
4952 layerm->export_region_slices_to_svg_debug("5_discover_horizontal_shells");
4953 layerm->export_region_fill_surfaces_to_svg_debug("5_discover_horizontal_shells");
4954 } // for each layer
4955 } // for each region
4956#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
4957} // void PrintObject::discover_horizontal_shells()
void trace(unsigned int level, const char *message)
Definition utils.cpp:247
@ jtMiter
Definition ClipperUtils.hpp:21
bool check_layer_id_pattern(const std::string &pattern, int layer_id)
Definition utils.cpp:2177
@ evstAll
Definition PrintConfig.hpp:410
@ evstCriticalOnly
Definition PrintConfig.hpp:408
@ evstNone
Definition PrintConfig.hpp:407
static constexpr const float ClipperSafetyOffset
Definition ClipperUtils.hpp:30

References Slic3r::SurfaceCollection::append(), Slic3r::Layer::bottom_z(), Slic3r::check_layer_id_pattern(), Slic3r::ClipperSafetyOffset, Slic3r::PrintRegion::config(), Slic3r::diff(), Slic3r::diff_ex(), discover_horizontal_shells(), EPSILON, Slic3r::evstAll, Slic3r::evstCriticalOnly, Slic3r::evstNone, Slic3r::expand(), Slic3r::Surface::expolygon, Slic3r::LayerRegion::export_region_fill_surfaces_to_svg_debug(), Slic3r::LayerRegion::export_region_slices_to_svg_debug(), Slic3r::LayerRegion::fill_surfaces, Slic3r::SurfaceCollection::filter_by_type(), Slic3r::LayerRegion::flow(), Slic3r::frExternalPerimeter, Slic3r::frSolidInfill, Slic3r::SurfaceCollection::group(), Slic3r::intersection(), Slic3r::Surface::is_bridge(), Slic3r::Surface::is_internal(), Slic3r::jtMiter, Slic3r::SurfaceCollection::keep_types(), m_layers, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::m_print, num_printing_regions(), Slic3r::opening(), Slic3r::polygons_append(), Slic3r::Layer::print_z, Slic3r::PrintRegionConfig, Slic3r::LayerRegion::region(), Slic3r::Layer::regions(), Slic3r::Flow::scaled_width(), Slic3r::SurfaceCollection::set(), Slic3r::LayerRegion::slices, Slic3r::stBottom, Slic3r::stBottomBridge, Slic3r::stInternal, Slic3r::stInternalSolid, Slic3r::stTop, Slic3r::Surface::surface_type, Slic3r::SurfaceCollection::surfaces, Slic3r::to_polygons(), Slic3r::trace(), Slic3r::union_ex(), and Slic3r::Yes.

Referenced by discover_horizontal_shells(), and prepare_infill().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ discover_vertical_shells()

void Slic3r::PrintObject::discover_vertical_shells ( )
private
2508{
2509 PROFILE_FUNC();
2510
2511 BOOST_LOG_TRIVIAL(info) << "Discovering vertical shells..." << log_memory_info();
2512
2513 struct DiscoverVerticalShellsCacheEntry
2514 {
2515 // Collected polygons, offsetted
2516 Polygons top_surfaces;
2517 Polygons bottom_surfaces;
2519 };
2520 bool spiral_mode = this->print()->config().spiral_mode.value;
2521 size_t num_layers = spiral_mode ? std::min(size_t(this->printing_region(0).config().bottom_shell_layers), m_layers.size()) : m_layers.size();
2522 std::vector<DiscoverVerticalShellsCacheEntry> cache_top_botom_regions(num_layers, DiscoverVerticalShellsCacheEntry());
2523 bool top_bottom_surfaces_all_regions = this->num_printing_regions() > 1 && ! m_config.interface_shells.value;
2524// static constexpr const float top_bottom_expansion_coeff = 1.05f;
2525 // Just a tiny fraction of an infill extrusion width to merge neighbor regions reliably.
2526 static constexpr const float top_bottom_expansion_coeff = 0.05f;
2527 if (top_bottom_surfaces_all_regions) {
2528 // This is a multi-material print and interface_shells are disabled, meaning that the vertical shell thickness
2529 // is calculated over all materials.
2530 // Is the "ensure vertical wall thickness" applicable to any region?
2531 bool has_extra_layers = false;
2532 for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) {
2533 const PrintRegionConfig &config = this->printing_region(region_id).config();
2534 if (config.ensure_vertical_shell_thickness.value == evstAll) {
2535 has_extra_layers = true;
2536 break;
2537 }
2538 }
2539 if (! has_extra_layers)
2540 // The "ensure vertical wall thickness" feature is not applicable to any of the regions. Quit.
2541 return;
2542 BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - start : cache top / bottom";
2543 // One layer per task: on a layer cut through a fine relief the unions below take far longer than elsewhere, and a
2544 // few such layers next to each other must not end up in one task.
2545 tbb::parallel_for(
2546 tbb::blocked_range<size_t>(0, num_layers, 1),
2547 [this, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) {
2548 const std::initializer_list<SurfaceType> surfaces_bottom { stBottom, stBottomBridge };
2549 const size_t num_regions = this->num_printing_regions();
2550 for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
2551 m_print->throw_if_canceled();
2552 const Layer &layer = *m_layers[idx_layer];
2553 DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[idx_layer];
2554 const auto top_bottom_expansion = [&layer](size_t region_id) {
2555 return float(layer.m_regions[region_id]->flow(frSolidInfill).scaled_spacing()) * top_bottom_expansion_coeff;
2556 };
2557#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2558 static size_t debug_idx = 0;
2559 ++ debug_idx;
2560#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2561 // The top surfaces, the bottom surfaces and the holes are independent of each other.
2562 tbb::parallel_invoke(
2563 [&]() {
2564 for (size_t region_id = 0; region_id < num_regions; ++ region_id)
2565 append(cache.top_surfaces, offset(layer.m_regions[region_id]->slices.filter_by_type(stTop), top_bottom_expansion(region_id)));
2566// append(cache.top_surfaces, offset(layerm.fill_surfaces.filter_by_type(stTop), top_bottom_expansion));
2567 // Save some computing time by reducing the number of polygons.
2568 cache.top_surfaces = union_(cache.top_surfaces);
2569 },
2570 [&]() {
2571 for (size_t region_id = 0; region_id < num_regions; ++ region_id)
2572 append(cache.bottom_surfaces, offset(layer.m_regions[region_id]->slices.filter_by_types(surfaces_bottom), top_bottom_expansion(region_id)));
2573// append(cache.bottom_surfaces, offset(layerm.fill_surfaces.filter_by_types(surfaces_bottom), top_bottom_expansion));
2574 cache.bottom_surfaces = union_(cache.bottom_surfaces);
2575 },
2576 [&]() {
2577 // Simulate single set of perimeters over all merged regions.
2578 float perimeter_offset = 0.f;
2579 float perimeter_min_spacing = FLT_MAX;
2580 for (size_t region_id = 0; region_id < num_regions; ++ region_id) {
2581 const LayerRegion &layerm = *layer.m_regions[region_id];
2582 // Calculate the maximum perimeter offset as if the slice was extruded with a single extruder only.
2583 // First find the maxium number of perimeters per region slice.
2584 unsigned int perimeters = 0;
2585 for (const Surface &s : layerm.slices.surfaces)
2586 perimeters = std::max<unsigned int>(perimeters, s.extra_perimeters);
2587 perimeters += layerm.region().config().wall_loops.value;
2588 // Then calculate the infill offset.
2589 if (perimeters > 0) {
2590 Flow extflow = layerm.flow(frExternalPerimeter);
2591 Flow flow = layerm.flow(frPerimeter);
2592 perimeter_offset = std::max(perimeter_offset,
2593 0.5f * float(extflow.scaled_width() + extflow.scaled_spacing()) + (float(perimeters) - 1.f) * flow.scaled_spacing());
2594 perimeter_min_spacing = std::min(perimeter_min_spacing, float(std::min(extflow.scaled_spacing(), flow.scaled_spacing())));
2595 }
2596 polygons_append(cache.holes, to_polygons(layerm.fill_expolygons));
2597 }
2598 // For a multi-material print, simulate perimeter / infill split as if only a single extruder has been used for the whole print.
2599 if (perimeter_offset > 0.) {
2600 // The layer.lslices are forced to merge by expanding them first.
2601 polygons_append(cache.holes, offset2(layer.lslices, 0.3f * perimeter_min_spacing, - perimeter_offset - 0.3f * perimeter_min_spacing));
2602#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2603 {
2604 Slic3r::SVG svg(debug_out_path("discover_vertical_shells-extra-holes-%d.svg", debug_idx), get_extents(layer.lslices));
2605 svg.draw(layer.lslices, "blue");
2606 svg.draw(union_ex(cache.holes), "red");
2607 svg.draw_outline(union_ex(cache.holes), "black", "blue", scale_(0.05));
2608 svg.Close();
2609 }
2610#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2611 }
2612 cache.holes = union_(cache.holes);
2613 });
2614 }
2615 });
2616 m_print->throw_if_canceled();
2617 BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - end : cache top / bottom";
2618 }
2619
2620 // With one top/bottom cache for all regions, the shell and hole accumulation in the loop below depends on nothing
2621 // region-specific but the shell settings and the external perimeter spacing, so a region sharing them with an earlier
2622 // one reuses its result instead of repeating it: that accumulation is a union over several layers of top/bottom
2623 // surfaces, and a multi-material print has a region per filament.
2624 using AccumulationKey = std::array<double, 5>;
2625 struct ShellAccumulation
2626 {
2627 AccumulationKey key;
2628 Polygons shell;
2630 };
2631 const auto accumulation_key = [](const PrintRegionConfig &region_config, const LayerRegion *layerm) {
2632 return AccumulationKey{ double(region_config.top_shell_layers.value), region_config.top_shell_thickness.value,
2633 double(region_config.bottom_shell_layers.value), region_config.bottom_shell_thickness.value,
2634 double(layerm->flow(frExternalPerimeter).scaled_spacing()) };
2635 };
2636 const auto accumulate_shell = [this, &cache_top_botom_regions](size_t idx_layer, const PrintRegionConfig &region_config,
2637 const LayerRegion *layerm, Polygons &shell, Polygons &holes) {
2638 const Layer *layer = m_layers[idx_layer];
2639 polygons_append(holes, cache_top_botom_regions[idx_layer].holes);
2640 auto combine_holes = [&holes](const Polygons &holes2) {
2641 if (holes.empty() || holes2.empty())
2642 holes.clear();
2643 else
2644 holes = intersection(holes, holes2);
2645 };
2646 auto combine_shells = [&shell](const Polygons &shells2) {
2647 if (shell.empty())
2648 shell = std::move(shells2);
2649 else if (! shells2.empty()) {
2650 polygons_append(shell, shells2);
2651 // Running the union_ using the Clipper library piece by piece is cheaper
2652 // than running the union_ all at once.
2653 shell = union_(shell);
2654 }
2655 };
2656 static constexpr const bool one_more_layer_below_top_bottom_surfaces = false;
2657 if (int n_top_layers = region_config.top_shell_layers.value; n_top_layers > 0) {
2658 // Gather top regions projected to this layer.
2659 coordf_t print_z = layer->print_z;
2660 int i = int(idx_layer) + 1;
2661 int itop = int(idx_layer) + n_top_layers;
2662 bool at_least_one_top_projected = false;
2663 for (; i < int(cache_top_botom_regions.size()) &&
2664 (i < itop || m_layers[i]->print_z - print_z < region_config.top_shell_thickness - EPSILON);
2665 ++ i) {
2666 at_least_one_top_projected = true;
2667 const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i];
2668 combine_holes(cache.holes);
2669 combine_shells(cache.top_surfaces);
2670 }
2671 if (!at_least_one_top_projected && i < int(cache_top_botom_regions.size())) {
2672 // Lets consider this a special case - with only 1 top solid and minimal shell thickness settings, the
2673 // boundaries of solid layers are not anchored over/under perimeters, so lets fix it by adding at least one
2674 // perimeter width of area
2675 Polygons anchor_area = intersection(expand(cache_top_botom_regions[idx_layer].top_surfaces,
2676 layerm->flow(frExternalPerimeter).scaled_spacing()),
2677 to_polygons(m_layers[i]->lslices));
2678 combine_shells(anchor_area);
2679 }
2680
2681 if (one_more_layer_below_top_bottom_surfaces)
2682 if (i < int(cache_top_botom_regions.size()) &&
2683 (i <= itop || m_layers[i]->bottom_z() - print_z < region_config.top_shell_thickness - EPSILON))
2684 combine_holes(cache_top_botom_regions[i].holes);
2685 }
2686 if (int n_bottom_layers = region_config.bottom_shell_layers.value; n_bottom_layers > 0) {
2687 // Gather bottom regions projected to this layer.
2688 coordf_t bottom_z = layer->bottom_z();
2689 int i = int(idx_layer) - 1;
2690 int ibottom = int(idx_layer) - n_bottom_layers;
2691 bool at_least_one_bottom_projected = false;
2692 for (; i >= 0 &&
2693 (i > ibottom || bottom_z - m_layers[i]->bottom_z() < region_config.bottom_shell_thickness - EPSILON);
2694 -- i) {
2695 at_least_one_bottom_projected = true;
2696 const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i];
2697 combine_holes(cache.holes);
2698 combine_shells(cache.bottom_surfaces);
2699 }
2700
2701 if (!at_least_one_bottom_projected && i >= 0) {
2702 Polygons anchor_area = intersection(expand(cache_top_botom_regions[idx_layer].bottom_surfaces,
2703 layerm->flow(frExternalPerimeter).scaled_spacing()),
2704 to_polygons(m_layers[i]->lslices));
2705 combine_shells(anchor_area);
2706 }
2707
2708 if (one_more_layer_below_top_bottom_surfaces)
2709 if (i >= 0 &&
2710 (i > ibottom || bottom_z - m_layers[i]->print_z < region_config.bottom_shell_thickness - EPSILON))
2711 combine_holes(cache_top_botom_regions[i].holes);
2712 }
2713 };
2714 std::vector<std::vector<ShellAccumulation>> shell_accumulations(top_bottom_surfaces_all_regions ? num_layers : 0);
2715 if (! shell_accumulations.empty()) {
2716 // Every (layer, key) pair is accumulated once, before the regions, so that nothing in the loop below is shared
2717 // between them and they can run next to each other.
2718 std::vector<std::array<size_t, 3>> todo; // layer, its slot, a region holding the key
2719 for (size_t idx_layer = 0; idx_layer < num_layers; ++ idx_layer) {
2720 std::vector<ShellAccumulation> &accumulations = shell_accumulations[idx_layer];
2721 for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
2722 if (this->printing_region(region_id).config().ensure_vertical_shell_thickness.value != evstAll)
2723 continue;
2724 const LayerRegion *layerm = m_layers[idx_layer]->m_regions[region_id];
2725 const AccumulationKey key = accumulation_key(layerm->region().config(), layerm);
2726 if (std::none_of(accumulations.begin(), accumulations.end(), [&key](const ShellAccumulation &a) { return a.key == key; })) {
2727 todo.push_back({ idx_layer, accumulations.size(), region_id });
2728 accumulations.push_back({ key, {}, {} });
2729 }
2730 }
2731 }
2732 tbb::parallel_for(size_t(0), todo.size(), [this, &todo, &shell_accumulations, &accumulate_shell](size_t i) {
2733 m_print->throw_if_canceled();
2734 const LayerRegion *layerm = m_layers[todo[i][0]]->m_regions[todo[i][2]];
2735 ShellAccumulation &out = shell_accumulations[todo[i][0]][todo[i][1]];
2736 accumulate_shell(todo[i][0], layerm->region().config(), layerm, out.shell, out.holes);
2737 });
2738 m_print->throw_if_canceled();
2739 }
2740
2741 const auto process_region = [&](size_t region_id) {
2742 const PrintRegion &region = this->printing_region(region_id);
2743 if (region.config().ensure_vertical_shell_thickness.value != evstAll )
2744 // This region will be handled by discover_horizontal_shells().
2745 return;
2746
2747 //FIXME Improve the heuristics for a grain size.
2748 size_t grain_size = std::max(num_layers / 16, size_t(1));
2749
2750 if (! top_bottom_surfaces_all_regions) {
2751 // This is either a single material print, or a multi-material print and interface_shells are enabled, meaning that the vertical shell thickness
2752 // is calculated over a single material.
2753 BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << region_id << " in parallel - start : cache top / bottom";
2754 tbb::parallel_for(
2755 tbb::blocked_range<size_t>(0, num_layers, grain_size),
2756 [this, region_id, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) {
2757 const std::initializer_list<SurfaceType> surfaces_bottom { stBottom, stBottomBridge };
2758 for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
2759 m_print->throw_if_canceled();
2760 Layer &layer = *m_layers[idx_layer];
2761 LayerRegion &layerm = *layer.m_regions[region_id];
2762 float top_bottom_expansion = float(layerm.flow(frSolidInfill).scaled_spacing()) * top_bottom_expansion_coeff;
2763 // Top surfaces.
2764 auto &cache = cache_top_botom_regions[idx_layer];
2765 cache.top_surfaces = offset(layerm.slices.filter_by_type(stTop), top_bottom_expansion);
2766// append(cache.top_surfaces, offset(layerm.fill_surfaces.filter_by_type(stTop), top_bottom_expansion));
2767 // Bottom surfaces.
2768 cache.bottom_surfaces = offset(layerm.slices.filter_by_types(surfaces_bottom), top_bottom_expansion);
2769// append(cache.bottom_surfaces, offset(layerm.fill_surfaces.filter_by_types(surfaces_bottom), top_bottom_expansion));
2770 // Holes over all regions. Only collect them once, they are valid for all region_id iterations.
2771 if (cache.holes.empty()) {
2772 for (size_t region_id = 0; region_id < layer.regions().size(); ++ region_id)
2773 polygons_append(cache.holes, to_polygons(layer.regions()[region_id]->fill_expolygons));
2774 }
2775 }
2776 });
2777 m_print->throw_if_canceled();
2778 BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << region_id << " in parallel - end : cache top / bottom";
2779 }
2780
2781 BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << region_id << " in parallel - start : ensure vertical wall thickness";
2782 grain_size = 1;
2783 tbb::parallel_for(
2784 tbb::blocked_range<size_t>(0, num_layers, grain_size),
2785 [this, region_id, &shell_accumulations, &accumulation_key, &accumulate_shell]
2786 (const tbb::blocked_range<size_t>& range) {
2787 // printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end());
2788 for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
2789 m_print->throw_if_canceled();
2790#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2791 static size_t debug_idx = 0;
2792 ++ debug_idx;
2793#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2794
2795 Layer *layer = m_layers[idx_layer];
2796 LayerRegion *layerm = layer->m_regions[region_id];
2797 const PrintRegionConfig &region_config = layerm->region().config();
2798
2799#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2800 layerm->export_region_slices_to_svg_debug("3_discover_vertical_shells-initial");
2801 layerm->export_region_fill_surfaces_to_svg_debug("3_discover_vertical_shells-initial");
2802#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2803
2804 Flow solid_infill_flow = layerm->flow(frSolidInfill);
2805 coord_t infill_line_spacing = solid_infill_flow.scaled_spacing();
2806 // Find a union of perimeters below / above this surface to guarantee a minimum shell thickness.
2807 Polygons shell;
2809#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2810 ExPolygons shell_ex;
2811#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2812 float min_perimeter_infill_spacing = float(infill_line_spacing) * 1.05f;
2813#if 0
2814// #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2815 {
2816 Slic3r::SVG svg_cummulative(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d.svg", debug_idx), this->bounding_box());
2817 for (int n = (int)idx_layer - n_extra_bottom_layers; n <= (int)idx_layer + n_extra_top_layers; ++ n) {
2818 if (n < 0 || n >= (int)m_layers.size())
2819 continue;
2820 ExPolygons &expolys = m_layers[n]->perimeter_expolygons;
2821 for (size_t i = 0; i < expolys.size(); ++ i) {
2822 Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d-layer%d-expoly%d.svg", debug_idx, n, i), get_extents(expolys[i]));
2823 svg.draw(expolys[i]);
2824 svg.draw_outline(expolys[i].contour, "black", scale_(0.05));
2825 svg.draw_outline(expolys[i].holes, "blue", scale_(0.05));
2826 svg.Close();
2827
2828 svg_cummulative.draw(expolys[i]);
2829 svg_cummulative.draw_outline(expolys[i].contour, "black", scale_(0.05));
2830 svg_cummulative.draw_outline(expolys[i].holes, "blue", scale_(0.05));
2831 }
2832 }
2833 }
2834#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2835 const AccumulationKey key = accumulation_key(region_config, layerm);
2836 const ShellAccumulation *reused = shell_accumulations.empty() ? nullptr :
2837 [&]() -> const ShellAccumulation * {
2838 for (const ShellAccumulation &a : shell_accumulations[idx_layer])
2839 if (a.key == key)
2840 return &a;
2841 return nullptr;
2842 }();
2843 if (reused != nullptr) {
2844 shell = reused->shell;
2845 holes = reused->holes;
2846 } else
2847 accumulate_shell(idx_layer, region_config, layerm, shell, holes);
2848#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2849 {
2850 Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", debug_idx), get_extents(shell));
2851 svg.draw(shell);
2852 svg.draw_outline(shell, "black", scale_(0.05));
2853 svg.Close();
2854 }
2855#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2856#if 0
2857// shell = union_(shell, true);
2858 shell = union_(shell, false);
2859#endif
2860#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2861 shell_ex = union_safety_offset_ex(shell);
2862#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2863
2864 //if (shell.empty())
2865 // continue;
2866
2867#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2868 {
2869 Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-after-union-%d.svg", debug_idx), get_extents(shell));
2870 svg.draw(shell_ex);
2871 svg.draw_outline(shell_ex, "black", "blue", scale_(0.05));
2872 svg.Close();
2873 }
2874#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2875
2876#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2877 {
2878 Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internal-wshell-%d.svg", debug_idx), get_extents(shell));
2879 svg.draw(layerm->fill_surfaces.filter_by_type(stInternal), "yellow", 0.5);
2880 svg.draw_outline(layerm->fill_surfaces.filter_by_type(stInternal), "black", "blue", scale_(0.05));
2881 svg.draw(shell_ex, "blue", 0.5);
2882 svg.draw_outline(shell_ex, "black", "blue", scale_(0.05));
2883 svg.Close();
2884 }
2885 {
2886 Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", debug_idx), get_extents(shell));
2887 svg.draw(layerm->fill_surfaces.filter_by_type(stInternalVoid), "yellow", 0.5);
2888 svg.draw_outline(layerm->fill_surfaces.filter_by_type(stInternalVoid), "black", "blue", scale_(0.05));
2889 svg.draw(shell_ex, "blue", 0.5);
2890 svg.draw_outline(shell_ex, "black", "blue", scale_(0.05));
2891 svg.Close();
2892 }
2893 {
2894 Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", debug_idx), get_extents(shell));
2895 svg.draw(layerm->fill_surfaces.filter_by_type(stInternalVoid), "yellow", 0.5);
2896 svg.draw_outline(layerm->fill_surfaces.filter_by_type(stInternalVoid), "black", "blue", scale_(0.05));
2897 svg.draw(shell_ex, "blue", 0.5);
2898 svg.draw_outline(shell_ex, "black", "blue", scale_(0.05));
2899 svg.Close();
2900 }
2901#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2902
2903 // Trim the shells region by the internal & internal void surfaces.
2904 const Polygons polygonsInternal = to_polygons(layerm->fill_surfaces.filter_by_types({ stInternal, stInternalVoid, stInternalSolid }));
2905 shell = intersection(shell, polygonsInternal, ApplySafetyOffset::Yes);
2906 polygons_append(shell, diff(polygonsInternal, holes));
2907 if (shell.empty())
2908 continue;
2909
2910 // Append the internal solids, so they will be merged with the new ones.
2911 polygons_append(shell, to_polygons(layerm->fill_surfaces.filter_by_type(stInternalSolid)));
2912
2913 // These regions will be filled by a rectilinear full infill. Currently this type of infill
2914 // only fills regions, which fit at least a single line. To avoid gaps in the sparse infill,
2915 // make sure that this region does not contain parts narrower than the infill spacing width.
2916#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2917 Polygons shell_before = shell;
2918#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
2919 ExPolygons regularized_shell;
2920 {
2921 // Open to remove (filter out) regions narrower than a bit less than an infill extrusion line width.
2922 // Such narrow regions are difficult to fill in with a gap fill algorithm (or Arachne), however they are most likely
2923 // not needed for print stability / quality.
2924 const float narrow_ensure_vertical_wall_thickness_region_radius = 0.5f * 0.65f * min_perimeter_infill_spacing;
2925 // Then close gaps narrower than 1.2 * line width, such gaps are difficult to fill in with sparse infill,
2926 // thus they will be merged into the solid infill.
2927 const float narrow_sparse_infill_region_radius = 0.5f * 1.2f * min_perimeter_infill_spacing;
2928 // Finally expand the infill a bit to remove tiny gaps between solid infill and the other regions.
2929 const float tiny_overlap_radius = 0.2f * min_perimeter_infill_spacing;
2930 regularized_shell = shrink_ex(offset2_ex(union_ex(shell),
2931 // Open to remove (filter out) regions narrower than an infill extrusion line width.
2932 -narrow_ensure_vertical_wall_thickness_region_radius,
2933 // Then close gaps narrower than 1.2 * line width, such gaps are difficult to fill in with sparse infill.
2934 narrow_ensure_vertical_wall_thickness_region_radius + narrow_sparse_infill_region_radius, jtSquare),
2935 // Finally expand the infill a bit to remove tiny gaps between solid infill and the other regions.
2936 narrow_sparse_infill_region_radius - tiny_overlap_radius, jtSquare);
2937
2938 Polygons object_volume;
2939 Polygons internal_volume;
2940 {
2941 if (idx_layer > 0 && idx_layer + 1 < m_layers.size())
2942 object_volume = to_polygons(intersection_ex_by_piece(m_layers[idx_layer - 1]->lslices, to_polygons(m_layers[idx_layer + 1]->lslices)));
2943 internal_volume = closing(polygonsInternal, SCALED_EPSILON);
2944 }
2945
2946 // The regularization operation may cause scattered tiny drops on the smooth parts of the model, filter them out
2947 // If the region checks both following conditions, it is removed:
2948 // 1. the area is very small,
2949 // OR the area is quite small and it is fully wrapped in model (not visible)
2950 // the in-model condition is there due to small sloping surfaces, e.g. top of the hull of the benchy
2951 // 2. the area does not fully cover an internal polygon
2952 // This is there mainly for a very thin parts, where the solid layers would be missing if the part area is quite small
2953 // Both tests below compare a small piece against the whole layer. Done literally, that is
2954 // quadratic in the number of pieces, which is what a layer split up by colour painting has,
2955 // so each is restricted to the part of the layer near the piece with an identical result:
2956 // object_volume is clipped to the piece's box, and only the internal polygons whose box meets
2957 // the expanded piece take part in the count, since the others pass through the difference
2958 // unchanged and add the same number to both sides of it.
2959 std::vector<BoundingBox> internal_bboxes;
2960 internal_bboxes.reserve(internal_volume.size());
2961 for (const Polygon &poly : internal_volume)
2962 internal_bboxes.emplace_back(get_extents(poly));
2963 regularized_shell.erase(std::remove_if(regularized_shell.begin(), regularized_shell.end(),
2964 [&internal_volume, &internal_bboxes, &min_perimeter_infill_spacing,
2965 &object_volume](const ExPolygon &p) {
2966 const bool small = p.area() < min_perimeter_infill_spacing * scaled(1.5) ||
2967 (p.area() < min_perimeter_infill_spacing * scaled(8.0) &&
2968 diff(to_polygons(p),
2969 ClipperUtils::clip_clipper_polygons_with_subject_bbox(
2970 object_volume, get_extents(p).inflated(SCALED_EPSILON)))
2971 .empty());
2972 if (!small)
2973 return false;
2974 const Polygons expanded = expand(to_polygons(p), min_perimeter_infill_spacing);
2975 const BoundingBox bbox = get_extents(expanded);
2976 Polygons nearby;
2977 for (size_t i = 0; i < internal_volume.size(); ++i)
2978 if (internal_bboxes[i].overlap(bbox))
2979 nearby.emplace_back(internal_volume[i]);
2980 return diff(nearby, expanded).size() >= nearby.size();
2981 }),
2982 regularized_shell.end());
2983 }
2984 if (regularized_shell.empty())
2985 continue;
2986
2987 ExPolygons new_internal_solid = intersection_ex(polygonsInternal, regularized_shell);
2988#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
2989 {
2990 Slic3r::SVG svg(debug_out_path("discover_vertical_shells-regularized-%d.svg", debug_idx), get_extents(shell_before));
2991 // Source shell.
2992 svg.draw(union_safety_offset_ex(shell_before));
2993 // Shell trimmed to the internal surfaces.
2994 svg.draw_outline(union_safety_offset_ex(shell), "black", "blue", scale_(0.05));
2995 // Regularized infill region.
2996 svg.draw_outline(new_internal_solid, "red", "magenta", scale_(0.05));
2997 svg.Close();
2998 }
2999#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
3000
3001 // Trim the internal & internalvoid by the shell.
3002 const Polygons regularized_shell_polygons = to_polygons(regularized_shell);
3003 Slic3r::ExPolygons new_internal = diff_ex_by_piece(to_expolygons(layerm->fill_surfaces.filter_by_type(stInternal)), regularized_shell_polygons);
3004 Slic3r::ExPolygons new_internal_void = diff_ex_by_piece(to_expolygons(layerm->fill_surfaces.filter_by_type(stInternalVoid)), regularized_shell_polygons);
3005
3006#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
3007 {
3008 SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal-%d.svg", debug_idx), get_extents(shell), new_internal, "black", "blue", scale_(0.05));
3009 SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal_void-%d.svg", debug_idx), get_extents(shell), new_internal_void, "black", "blue", scale_(0.05));
3010 SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal_solid-%d.svg", debug_idx), get_extents(shell), new_internal_solid, "black", "blue", scale_(0.05));
3011 }
3012#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
3013
3014 // Assign resulting internal surfaces to layer.
3015 layerm->fill_surfaces.keep_types({ stTop, stBottom, stBottomBridge });
3016 layerm->fill_surfaces.append(new_internal, stInternal);
3017 layerm->fill_surfaces.append(new_internal_void, stInternalVoid);
3018 layerm->fill_surfaces.append(new_internal_solid, stInternalSolid);
3019 } // for each layer
3020 });
3021 m_print->throw_if_canceled();
3022 BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << region_id << " in parallel - end";
3023
3024#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
3025 for (size_t idx_layer = 0; idx_layer < m_layers.size(); ++idx_layer) {
3026 LayerRegion *layerm = m_layers[idx_layer]->get_region(region_id);
3027 layerm->export_region_slices_to_svg_debug("3_discover_vertical_shells-final");
3028 layerm->export_region_fill_surfaces_to_svg_debug("3_discover_vertical_shells-final");
3029 }
3030#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
3031 }; // for each region
3032 if (top_bottom_surfaces_all_regions)
3033 // Nothing is shared between the regions, and a layer cut through a fine relief takes far longer than the others,
3034 // so they run next to each other instead of one after another.
3035 tbb::parallel_for(size_t(0), this->num_printing_regions(), process_region);
3036 else
3037 // Here every region fills the one top/bottom cache with its own surfaces first.
3038 for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id)
3039 process_region(region_id);
3040} // void PrintObject::discover_vertical_shells()
BoundingBox bounding_box() const
Definition Print.hpp:403
Polygons offset2(const ExPolygons &expolygons, const float delta1, const float delta2, JoinType joinType, double miterLimit)
Definition ClipperUtils.cpp:645
void append(std::vector< T, Alloc > &dest, const std::vector< T, Alloc2 > &src)
Definition libslic3r.h:157
Slic3r::ExPolygons intersection_ex_by_piece(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:1008
const Polygon & contour(const ExPolygon &p)
Definition AGGRaster.hpp:30

References Slic3r::append(), Slic3r::SurfaceCollection::append(), Slic3r::bottom_shell_layers(), Slic3r::Layer::bottom_z(), bounding_box(), Slic3r::SVG::Close(), Slic3r::closing(), config(), Slic3r::PrintRegion::config(), Slic3r::contour(), Slic3r::debug_out_path(), Slic3r::diff(), Slic3r::diff_ex_by_piece(), discover_vertical_shells(), Slic3r::SVG::draw(), Slic3r::SVG::draw_outline(), EPSILON, Slic3r::evstAll, Slic3r::expand(), Slic3r::SVG::export_expolygons(), Slic3r::LayerRegion::export_region_fill_surfaces_to_svg_debug(), Slic3r::LayerRegion::export_region_slices_to_svg_debug(), Slic3r::Surface::extra_perimeters, Slic3r::LayerRegion::fill_expolygons, Slic3r::LayerRegion::fill_surfaces, Slic3r::SurfaceCollection::filter_by_type(), Slic3r::SurfaceCollection::filter_by_types(), Slic3r::LayerRegion::flow(), Slic3r::frExternalPerimeter, Slic3r::frPerimeter, Slic3r::frSolidInfill, Slic3r::get_extents(), Slic3r::holes(), Slic3r::intersection(), Slic3r::intersection_ex(), Slic3r::intersection_ex_by_piece(), Slic3r::jtSquare, Slic3r::SurfaceCollection::keep_types(), Slic3r::log_memory_info(), Slic3r::Layer::lslices, m_config, m_layers, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::m_print, Slic3r::Layer::m_regions, num_printing_regions(), Slic3r::offset(), Slic3r::offset2(), Slic3r::offset2_ex(), Slic3r::polygons_append(), Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::print(), Slic3r::Layer::print_z, printing_region(), Slic3r::PrintRegionConfig, Slic3r::range(), Slic3r::LayerRegion::region(), Slic3r::Layer::regions(), scale_, SCALED_EPSILON, Slic3r::Flow::scaled_spacing(), Slic3r::Flow::scaled_width(), Slic3r::shrink_ex(), size(), Slic3r::LayerRegion::slices, Slic3r::stBottom, Slic3r::stBottomBridge, Slic3r::stInternal, Slic3r::stInternalSolid, Slic3r::stInternalVoid, Slic3r::stTop, Slic3r::SurfaceCollection::surfaces, Slic3r::to_expolygons(), Slic3r::to_polygons(), Slic3r::union_(), Slic3r::union_ex(), Slic3r::union_safety_offset_ex(), and Slic3r::Yes.

Referenced by discover_vertical_shells(), and prepare_infill().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ emptylayer_update_msg()

void Slic3r::PrintObjectBase::emptylayer_update_msg ( PrintBase * print,
int type,
const std::string & message,
bool overwrite )
protectedinherited

◆ estimate_curled_extrusions()

void Slic3r::PrintObject::estimate_curled_extrusions ( )
private
1032{
1034 const auto any_region_enables = [this](ConfigOptionBoolsNullable PrintRegionConfig::*option) {
1035 return std::any_of(this->print()->m_print_regions.begin(), this->print()->m_print_regions.end(),
1036 [option](const PrintRegion* region) { return any_enabled(region->config().*option); });
1037 };
1038 // Only the slowdown for curled perimeters reads the curled lines, and they stay empty unless some region has overhang speed on.
1039 if (any_region_enables(&PrintRegionConfig::enable_overhang_speed) &&
1040 any_region_enables(&PrintRegionConfig::slowdown_for_curled_perimeters)) {
1041
1042 // Estimate curling of support material and add it to the malformaition lines of each layer
1043 float support_flow_width = support_material_flow(this, this->config().layer_height).width();
1044 SupportSpotsGenerator::Params params{this->print()->m_config.filament_type.values,
1045 /*float(this->print()->default_object_config().inner_wall_acceleration.getFloat()),*/
1046 this->config().raft_layers.getInt(), this->config().brim_type.value,
1047 float(this->config().brim_width.getFloat())};
1049 m_print->throw_if_canceled();
1050 } else {
1051 for (Layer *layer : m_layers)
1052 layer->curled_lines.clear();
1053 }
1054 //this->set_done(posEstimateCurledExtrusions);
1055 }
1056}
float width() const
Definition Flow.hpp:63
PrintConfig m_config
Definition Print.hpp:1500
PrintRegionPtrs m_print_regions
Definition Print.hpp:1504
void estimate_malformations(LayerPtrs &layers, const Params &params)
Definition SupportSpotsGenerator.cpp:132
ConfigOptionBoolsTempl< true > ConfigOptionBoolsNullable
Definition Config.hpp:2067
layer_height((ConfigOptionInt, faded_layers))((ConfigOptionFloat
Flow support_material_flow(const PrintObject *object, float layer_height)
Definition Flow.cpp:239
@ posEstimateCurledExtrusions
Definition Print.hpp:124

References config(), estimate_curled_extrusions(), Slic3r::SupportSpotsGenerator::estimate_malformations(), Slic3r::layer_height(), layers(), m_layers, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::m_print, Slic3r::posEstimateCurledExtrusions, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::print(), Slic3r::PrintRegionConfig, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::set_started(), and Slic3r::support_material_flow().

Referenced by estimate_curled_extrusions(), and Slic3r::Print::process().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ firstLayerObjGroups()

const std::vector< groupedVolumeSlices > & Slic3r::PrintObject::firstLayerObjGroups ( ) const
inline

References firstLayerObjSliceByGroups.

Referenced by Slic3r::outer_inner_brim_area().

Here is the caller graph for this function:

◆ firstLayerObjGroupsMod()

std::vector< groupedVolumeSlices > & Slic3r::PrintObject::firstLayerObjGroupsMod ( )
inline

References firstLayerObjSliceByGroups.

Referenced by Slic3r::groupingVolumesForBrim().

Here is the caller graph for this function:

◆ firstLayerObjSlice()

const std::vector< VolumeSlices > & Slic3r::PrintObject::firstLayerObjSlice ( ) const
inline

◆ firstLayerObjSliceMod()

std::vector< VolumeSlices > & Slic3r::PrintObject::firstLayerObjSliceMod ( )
inline

References firstLayerObjSliceByVolume.

Referenced by Slic3r::groupingVolumesForBrim().

Here is the caller graph for this function:

◆ generate_belt_brim()

void Slic3r::PrintObject::generate_belt_brim ( )
private
1020{
1021 if (! m_belt_brim_pending)
1022 return;
1023 // belt_brim_obstacles() looks up the layers and support layers of every object
1024 // on the plate by print_z. Another object's support step rebuilds those (and
1025 // temporarily shifts its layer Z values), so this must not overlap with it.
1026 make_belt_brim(*this);
1027 m_print->throw_if_canceled();
1028 m_belt_brim_pending = false;
1029}
bool m_belt_brim_pending
Definition Print.hpp:723
void make_belt_brim(PrintObject &object)
Definition BeltBrim.cpp:426

References generate_belt_brim(), m_belt_brim_pending, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::m_print, and Slic3r::make_belt_brim().

Referenced by generate_belt_brim(), and generate_support_material().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ generate_new_id()

ObjectID Slic3r::ObjectBase::generate_new_id ( )
inlinestaticprivateinherited
100{ return ObjectID(++ s_last_id); }
static size_t s_last_id
Definition ObjectID.hpp:101

References s_last_id.

Referenced by ObjectBase(), and set_new_unique_id().

Here is the caller graph for this function:

◆ generate_support_material()

void Slic3r::PrintObject::generate_support_material ( bool with_belt_brim = true)
private
963{
964 if (this->set_started(posSupportMaterial)) {
965 this->clear_support_layers();
966
967 if(!has_support() && !m_print->get_no_check_flag()) {
968 // BBS: pop a warning if objects have significant amount of overhangs but support material is not enabled
969 // Note: we also need to pop warning if support is disabled and only raft is enabled
970 m_print->set_status(50, L("Checking support necessity"));
971 typedef std::chrono::high_resolution_clock clock_;
972 typedef std::chrono::duration<double, std::ratio<1> > second_;
973 std::chrono::time_point<clock_> t0{ clock_::now() };
974
976
977 double duration{ std::chrono::duration_cast<second_>(clock_::now() - t0).count() };
978 BOOST_LOG_TRIVIAL(info) << std::fixed << std::setprecision(0) << "is_support_necessary takes " << duration << " secs.";
979
980 if (sntype != NoNeedSupp) {
981 std::map<SupportNecessaryType, std::string> messages = {
982 {SharpTail,L("It seems object %1% has floating regions. Please re-orient the object or enable support generation.")},
983 {Cantilever,L("It seems object %1% has a floating cantilever. Please re-orient the object or enable support generation.")},
984 {LargeOverhang,L("It seems object %1% has large overhangs. Please re-orient the object or enable support generation.")} };
985 std::string warning_message = Slic3r::format(messages[sntype], this->model_object()->name);
987 }
988
989#if 0
990 // Printing without supports. Empty layer means some objects or object parts are levitating,
991 // therefore they cannot be printed without supports.
992 for (const Layer *layer : m_layers)
993 if (layer->empty())
994 throw Slic3r::SlicingError("Levitating objects cannot be printed without supports.");
995#endif
996 }
997
998 if ((this->has_support() && m_layers.size() > 1) || (this->has_raft() && !m_layers.empty())) {
999 m_print->set_status(50, L("Generating support"));
1000
1002 m_print->throw_if_canceled();
1003 }
1004 // Orca: the tree support collision/avoidance caches and support nodes are only used while this step runs
1005 // (detect_overhangs() rebuilds them from scratch), so don't keep them resident until the next slice.
1007 // Belt brim rides here rather than in the brim step because its apron
1008 // prologue introduces print_z values below the object's first layer, and
1009 // those must exist before ToolOrdering is built at psWipeTower - one step
1010 // ahead of psSkirtBrim. The brim options already invalidate
1011 // posSupportMaterial, so this needs no extra invalidation edges.
1012 m_belt_brim_pending = true;
1013 if (with_belt_brim)
1014 this->generate_belt_brim();
1016 }
1017}
SupportNecessaryType is_support_necessary()
Definition PrintObject.cpp:5289
void clear_tree_support_preview_cache()
Definition Print.hpp:478
bool has_support() const
Definition Print.hpp:509
void _generate_support_material()
Definition PrintObject.cpp:5085
void generate_belt_brim()
Definition PrintObject.cpp:1019
@ SlicingNeedSupportOn
Definition PrintBase.hpp:70
@ NON_CRITICAL
Definition PrintBase.hpp:62
SupportNecessaryType
Definition Print.hpp:87
@ LargeOverhang
Definition Print.hpp:91
@ SharpTail
Definition Print.hpp:89
@ Cantilever
Definition Print.hpp:90
@ NoNeedSupp
Definition Print.hpp:88
std::string format(const char *fmt, TArgs &&... args)
Definition format.hpp:45
@ posSupportMaterial
Definition Print.hpp:125

References _generate_support_material(), Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::active_step_add_warning(), Slic3r::Cantilever, clear_support_layers(), clear_tree_support_preview_cache(), Slic3r::format(), generate_belt_brim(), generate_support_material(), has_support(), is_support_necessary(), L, Slic3r::LargeOverhang, m_belt_brim_pending, m_layers, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::m_print, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::model_object(), Slic3r::PrintStateBase::NON_CRITICAL, Slic3r::NoNeedSupp, Slic3r::posSupportMaterial, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::set_done(), Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::set_started(), Slic3r::SharpTail, and Slic3r::PrintStateBase::SlicingNeedSupportOn.

Referenced by generate_support_material(), and generate_support_preview().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ generate_support_preview()

void Slic3r::PrintObject::generate_support_preview ( )
4450{
4451 POProfiler profiler;
4452
4453 boost::posix_time::ptime ts1 = boost::posix_time::microsec_clock::local_time();
4454 this->slice();
4455 boost::posix_time::ptime ts2 = boost::posix_time::microsec_clock::local_time();
4456 profiler.duration1 = (ts2 - ts1).total_milliseconds();
4457
4459 boost::posix_time::ptime ts3 = boost::posix_time::microsec_clock::local_time();
4460 profiler.duration2 = (ts3 - ts2).total_milliseconds();
4461}
void slice()
Definition PrintObjectSlice.cpp:884
void generate_support_material(bool with_belt_brim=true)
Definition PrintObject.cpp:962
TreeSupportProfiler profiler
Definition TreeSupport.cpp:227

References generate_support_material(), generate_support_preview(), Slic3r::profiler, and slice().

Referenced by generate_support_preview().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_certain_layers()

void Slic3r::PrintObject::get_certain_layers ( float start,
float end,
std::vector< LayerPtrs > & out,
std::vector< BoundingBox > & boundingbox_objects )
4633{
4634 BoundingBox temp;
4635 LayerPtrs out_temp;
4636 for (const auto &layer : layers()) {
4637 if (layer->print_z < start) continue;
4638
4639 if (layer->print_z > end + EPSILON) break;
4640 temp.merge(layer->loverhangs_bbox);
4641 out_temp.emplace_back(layer);
4642 }
4643 boundingbox_objects.emplace_back(std::move(temp));
4644 out.emplace_back(std::move(out_temp));
4645};
std::vector< Layer * > LayerPtrs
Definition Layer.hpp:27

References EPSILON, get_certain_layers(), layers(), and Slic3r::BoundingBoxBase< PointType, APointsType >::merge().

Referenced by get_certain_layers().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_first_layer_bbox()

BoundingBox Slic3r::PrintObject::get_first_layer_bbox ( float & area,
float & layer_height,
std::string & name )
3193{
3194 BoundingBox bbox;
3195 a = 0;
3196 name = this->model_object()->name;
3197 if (layer_count() > 0) {
3198 auto layer = get_layer(0);
3199 layer_height = layer->height;
3200 // only work for object with single instance
3201 auto shift = instances()[0].shift_without_plate_offset();
3202 for (auto bb : layer->lslices_bboxes)
3203 {
3204 bb.translate(shift.x(), shift.y());
3205 bbox.merge(bb);
3206 }
3207 for (auto slice : layer->lslices) {
3208 a += area(slice);
3209 }
3210 }
3211 // Guard on `defined`: make_brim() can return before assigning this (it does on
3212 // belt printers, where has_brim() is still true but the plate brim is skipped),
3213 // and overwriting a valid bbox with an undefined one corrupted the first-layer
3214 // centre and the GUI's first-layer area readout.
3217 // Belt brim: the apron reaches ahead of the object along the belt.
3218 if (has_belt_brim()) {
3219 const Point shift = instances().empty() ? Point(0, 0) : instances()[0].shift_without_plate_offset();
3220 for (const ExPolygons &areas : m_belt_brim_areas_by_layer)
3221 for (const ExPolygon &ex : areas) {
3222 BoundingBox bb = get_extents(ex.contour);
3223 bb.translate(shift.x(), shift.y());
3224 bbox.merge(bb);
3225 }
3226 for (const BeltBrimBand &band : m_belt_brim_prologue)
3227 for (const ExPolygon &ex : band.areas) {
3228 BoundingBox bb = get_extents(ex.contour);
3229 bb.translate(shift.x(), shift.y());
3230 bbox.merge(bb);
3231 }
3232 }
3233 return bbox;
3234}
std::string name
Definition Model.hpp:384
bool has_belt_brim() const
Definition PrintObject.cpp:1369
BoundingBox firstLayerObjectBrimBoundingBox
Definition Print.hpp:545
bool has_brim() const
Definition Print.hpp:419
double area(const ExPolygon &poly)
Definition ExPolygon.hpp:511

References Slic3r::area(), Slic3r::ExPolygon::contour, firstLayerObjectBrimBoundingBox, Slic3r::get_extents(), get_layer(), has_belt_brim(), has_brim(), instances(), layer_count(), Slic3r::layer_height(), m_belt_brim_areas_by_layer, m_belt_brim_prologue, Slic3r::BoundingBoxBase< PointType, APointsType >::merge(), Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::model_object(), Slic3r::ModelObject::name, slice(), and Slic3r::BoundingBoxBase< PointType, APointsType >::translate().

Here is the call graph for this function:

◆ get_first_layer_bellow_printz()

const Layer * Slic3r::PrintObject::get_first_layer_bellow_printz ( coordf_t print_z,
coordf_t epsilon ) const
5545{
5546 coordf_t limit = print_z + epsilon;
5547 auto it = Slic3r::lower_bound_by_predicate(m_layers.begin(), m_layers.end(), [limit](const Layer *layer) { return layer->print_z < limit; });
5548 return (it == m_layers.begin()) ? nullptr : *(--it);
5549}
constexpr double epsilon()
Definition IMSlider.hpp:31
ForwardIt lower_bound_by_predicate(ForwardIt first, ForwardIt last, LowerThanKeyPredicate lower_than_key)
Definition libslic3r.h:246

References get_first_layer_bellow_printz(), Slic3r::lower_bound_by_predicate(), and m_layers.

Referenced by Slic3r::get_boundary(), and get_first_layer_bellow_printz().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_id()

size_t Slic3r::PrintObject::get_id ( ) const
inline
551{ return m_id; }
size_t m_id
Definition Print.hpp:742

References m_id.

Referenced by Slic3r::get_instance_name().

Here is the caller graph for this function:

◆ get_instances_shift_without_plate_offset()

Points Slic3r::PrintObject::get_instances_shift_without_plate_offset ( ) const
4648{
4649 Points out;
4650 out.reserve(m_instances.size());
4651 for (const auto& instance : m_instances)
4652 out.push_back(instance.shift_without_plate_offset());
4653
4654 return out;
4655}

References get_instances_shift_without_plate_offset(), and m_instances.

Referenced by get_instances_shift_without_plate_offset().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_layer() [1/2]

Layer * Slic3r::PrintObject::get_layer ( int idx)
inline
457{ return m_layers[idx]; }

References m_layers.

◆ get_layer() [2/2]

const Layer * Slic3r::PrintObject::get_layer ( int idx) const
inline

◆ get_layer_at_bottomz() [1/2]

Layer * Slic3r::PrintObject::get_layer_at_bottomz ( coordf_t bottom_z,
coordf_t epsilon )
5568{ return const_cast<Layer*>(std::as_const(*this).get_layer_at_bottomz(bottom_z, epsilon)); }

References get_layer_at_bottomz().

Here is the call graph for this function:

◆ get_layer_at_bottomz() [2/2]

const Layer * Slic3r::PrintObject::get_layer_at_bottomz ( coordf_t bottom_z,
coordf_t epsilon ) const
5556 {
5557 coordf_t limit_upper = bottom_z + epsilon;
5558 coordf_t limit_lower = bottom_z - epsilon;
5559
5560 for (const Layer* layer : m_layers) {
5561 if (layer->bottom_z() > limit_lower)
5562 return layer->bottom_z() < limit_upper ? layer : nullptr;
5563 }
5564
5565 return nullptr;
5566}

References get_layer_at_bottomz(), and m_layers.

Referenced by get_layer_at_bottomz(), and get_layer_at_bottomz().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_layer_at_printz() [1/4]

Layer * Slic3r::PrintObject::get_layer_at_printz ( coordf_t print_z)
5529{ return const_cast<Layer*>(std::as_const(*this).get_layer_at_printz(print_z)); }

References get_layer_at_printz().

Here is the call graph for this function:

◆ get_layer_at_printz() [2/4]

const Layer * Slic3r::PrintObject::get_layer_at_printz ( coordf_t print_z) const
5522 {
5523 auto it = Slic3r::lower_bound_by_predicate(m_layers.begin(), m_layers.end(), [print_z](const Layer *layer) { return layer->print_z < print_z; });
5524 return (it == m_layers.end() || (*it)->print_z != print_z) ? nullptr : *it;
5525}

References get_layer_at_printz(), Slic3r::lower_bound_by_predicate(), and m_layers.

Referenced by Slic3r::Print::_plan_belt_purge(), get_layer_at_printz(), get_layer_at_printz(), get_layer_at_printz(), and get_layer_at_printz().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_layer_at_printz() [3/4]

Layer * Slic3r::PrintObject::get_layer_at_printz ( coordf_t print_z,
coordf_t epsilon )
5542{ return const_cast<Layer*>(std::as_const(*this).get_layer_at_printz(print_z, epsilon)); }

References get_layer_at_printz().

Here is the call graph for this function:

◆ get_layer_at_printz() [4/4]

const Layer * Slic3r::PrintObject::get_layer_at_printz ( coordf_t print_z,
coordf_t epsilon ) const
5534 {
5535 coordf_t limit = print_z - epsilon;
5536 auto it = Slic3r::lower_bound_by_predicate(m_layers.begin(), m_layers.end(), [limit](const Layer *layer) { return layer->print_z < limit; });
5537 return (it == m_layers.end() || (*it)->print_z > print_z + epsilon) ? nullptr : *it;
5538}

References get_layer_at_printz(), Slic3r::lower_bound_by_predicate(), and m_layers.

Here is the call graph for this function:

◆ get_layer_idx_get_printz()

int Slic3r::PrintObject::get_layer_idx_get_printz ( coordf_t print_z,
coordf_t epsilon )
5550 {
5551 coordf_t limit = print_z + epsilon;
5552 auto it = Slic3r::lower_bound_by_predicate(m_layers.begin(), m_layers.end(), [limit](const Layer *layer) { return layer->print_z < limit; });
5553 return (it == m_layers.begin()) ? -1 : std::distance(m_layers.begin(), it);
5554}

References get_layer_idx_get_printz(), Slic3r::lower_bound_by_predicate(), and m_layers.

Referenced by get_layer_idx_get_printz().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_shared_object()

PrintObject * Slic3r::PrintObject::get_shared_object ( ) const
inline
538{ return m_shared_object; }

References PrintObject(), and m_shared_object.

Referenced by Slic3r::outer_inner_brim_area(), and Slic3r::Print::process().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_support_layer()

SupportLayer * Slic3r::PrintObject::get_support_layer ( int idx)
inline
482{ return idx<m_support_layers.size()? m_support_layers[idx]:nullptr; }

References m_support_layers.

◆ get_support_layer_at_printz() [1/2]

SupportLayer * Slic3r::PrintObject::get_support_layer_at_printz ( coordf_t print_z,
coordf_t epsilon )
1339{
1340 return const_cast<SupportLayer*>(std::as_const(*this).get_support_layer_at_printz(print_z, epsilon));
1341}

References get_support_layer_at_printz().

Here is the call graph for this function:

◆ get_support_layer_at_printz() [2/2]

const SupportLayer * Slic3r::PrintObject::get_support_layer_at_printz ( coordf_t print_z,
coordf_t epsilon ) const
1332{
1333 coordf_t limit = print_z - epsilon;
1334 auto it = Slic3r::lower_bound_by_predicate(m_support_layers.begin(), m_support_layers.end(), [limit](const SupportLayer* layer) { return layer->print_z < limit; });
1335 return (it == m_support_layers.end() || (*it)->print_z > print_z + epsilon) ? nullptr : *it;
1336}

References get_support_layer_at_printz(), Slic3r::lower_bound_by_predicate(), and m_support_layers.

Referenced by get_support_layer_at_printz(), and get_support_layer_at_printz().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ has_belt_brim()

bool Slic3r::PrintObject::has_belt_brim ( ) const
1370{
1371 if (! m_print->has_tilted_belt())
1372 return false;
1373 // The purge prism is sacrificial and sits at the plate's edge; its generator sets no_brim, and
1374 // this keeps it brimless whatever its config says, so a brim on the parts never blocks purging.
1375 if (m_config.belt_purge_tower_object.value)
1376 return false;
1377 if (m_config.brim_type == btNoBrim)
1378 return false;
1379 // An inner-only brim has no leading/extra geometry: leading_brim_length and
1380 // extra_brim_width both widen the OUTER ring, which btInnerOnly never emits, so it
1381 // produces nothing unless brim_width itself is positive. Every other brim type is
1382 // satisfied by any one of the three widths. Requiring the width here (instead of
1383 // "any width") stops has_belt_brim() - and therefore Print::validate() - from
1384 // rejecting the prime tower / spiral vase for a brim that would never be drawn.
1385 if (m_config.brim_type == btInnerOnly) {
1386 if (m_config.brim_width.value <= 0.)
1387 return false;
1388 } else if (m_config.brim_width.value <= 0. && m_config.leading_brim_length.value <= 0.
1389 && m_config.extra_brim_width.value <= 0.) {
1390 return false;
1391 }
1392 return ! this->has_raft();
1393}
bool has_raft() const
Definition Print.hpp:510
@ btInnerOnly
Definition PrintConfig.hpp:459
@ btNoBrim
Definition PrintConfig.hpp:461

References Slic3r::btInnerOnly, Slic3r::btNoBrim, has_belt_brim(), has_raft(), m_config, and Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::m_print.

Referenced by get_first_layer_bbox(), and has_belt_brim().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ has_brim()

bool Slic3r::PrintObject::has_brim ( ) const
inline
419 {
420 return ((this->config().brim_type != btNoBrim && this->config().brim_width.value > 0.) || this->config().brim_type == btAutoBrim
421 || (this->config().brim_type == btPainted && !this->model_object()->brim_points.empty()))
422 && ! this->has_raft();
423 }
@ btPainted
Definition PrintConfig.hpp:457
@ btAutoBrim
Definition PrintConfig.hpp:455

References Slic3r::btAutoBrim, Slic3r::btNoBrim, Slic3r::btPainted, config(), and has_raft().

Referenced by get_first_layer_bbox().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ has_raft() [1/2]

bool Slic3r::PrintObject::has_raft ( ) const
inline
510{ return has_raft(m_config); }

References has_raft(), and m_config.

Referenced by has_brim(), has_raft(), and has_support_material().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ has_raft() [2/2]

bool Slic3r::PrintObject::has_raft ( const PrintObjectConfig & config)
inlinestatic
507{ return config.raft_layers > 0; }

References config(), and Slic3r::PrintObjectConfig.

Referenced by Slic3r::TreeSupport3D::generate_raft_contact(), Slic3r::TimelapsePosPicker::get_raft_height(), and has_belt_brim().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ has_support() [1/2]

bool Slic3r::PrintObject::has_support ( ) const
inline
509{ return has_support(m_config); }

References has_support(), and m_config.

Referenced by has_support(), and has_support_material().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ has_support() [2/2]

bool Slic3r::PrintObject::has_support ( const PrintObjectConfig & config)
inlinestatic
506{ return config.enable_support || config.enforce_support_layers > 0; }

References config(), and Slic3r::PrintObjectConfig.

Referenced by generate_support_material().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ has_support_material() [1/2]

bool Slic3r::PrintObject::has_support_material ( ) const
inline
bool has_support_material() const
Definition Print.hpp:511

References has_support_material(), and m_config.

Referenced by has_support_material().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ has_support_material() [2/2]

bool Slic3r::PrintObject::has_support_material ( const PrintObjectConfig & config)
inlinestatic
508{ return has_support(config) || has_raft(config); }

References config(), has_raft(), has_support(), and Slic3r::PrintObjectConfig.

Referenced by Slic3r::outer_inner_brim_area(), printing_extruders(), and Slic3r::Print::update_shrinkage_compensation().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ height()

coord_t Slic3r::PrintObject::height ( ) const
inline
406{ return m_size.z(); }

References m_size.

Referenced by add_layer(), add_support_layer(), add_tree_support_layer(), insert_support_layer(), and Slic3r::Print::is_all_objects_are_short().

Here is the caller graph for this function:

◆ id()

ObjectID Slic3r::ObjectBase::id ( ) const
inlineinherited
69{ return m_id; }

References m_id.

Referenced by Slic3r::CutObjectBase::CutObjectBase(), Slic3r::Model::Model(), Slic3r::ModelInstance::ModelInstance(), Slic3r::ModelMaterial::ModelMaterial(), Slic3r::ModelObject::ModelObject(), Slic3r::ModelObject::ModelObject(), Slic3r::ModelObject::ModelObject(), Slic3r::ModelObject::ModelObject(), Slic3r::ModelObject::ModelObject(), Slic3r::ModelVolume::ModelVolume(), Slic3r::ModelVolume::ModelVolume(), Slic3r::ModelVolume::ModelVolume(), Slic3r::ModelVolume::ModelVolume(), Slic3r::ModelVolume::ModelVolume(), Slic3r::ModelVolume::ModelVolume(), ObjectBase(), Slic3r::PrintBase::SlicingStatus::SlicingStatus(), Slic3r::PrintBase::SlicingStatus::SlicingStatus(), Slic3r::_BBS_3MF_Exporter::_add_cut_information_file_to_archive(), Slic3r::_BBS_3MF_Exporter::_add_model_config_file_to_archive(), Slic3r::_BBS_3MF_Exporter::_add_slice_info_config_file_to_archive(), Slic3r::GUI::Plater::priv::actualize_slicing_warnings(), Slic3r::ModelObjectStatusDB::add(), Slic3r::ModelObjectStatusDB::add_if_new(), Slic3r::Model::add_object(), Slic3r::append_and_translate(), Slic3r::SLAPrint::apply(), Slic3r::GUI::calc_distance(), Slic3r::GUI::calc_surface_offset(), Slic3r::check_model_ids_equal(), Slic3r::check_model_ids_validity(), Slic3r::GUI::GLGizmoTextureDisplacement::close_image_adjust(), Slic3r::Model::collect_reusable_objects(), Slic3r::ModelObject::convert_units(), copy_id(), Slic3r::PrintObjectStatusDB::count(), anonymous_namespace{RaycastManager.cpp}::create_key(), Slic3r::GUI::create_meshes(), anonymous_namespace{EmbossJob.cpp}::create_volume(), Slic3r::GUI::Emboss::create_volume_sources(), Slic3r::GUI::GLGizmoPainterBase::data_changed(), Slic3r::GCode::do_export(), Slic3r::GUI::PartPlate::duplicate_all_instance(), Slic3r::GUI::PartPlateList::duplicate_plate(), Slic3r::GUI::PartPlate::estimate_wipe_tower_footprint(), Slic3r::Print::export_cached_data(), Slic3r::GUI::Plater::export_stl(), Slic3r::find_volume_extents(), Slic3r::PreciseSeam::detail::fragment_location(), Slic3r::GUI::Plater::priv::generate_first_layer_bbox(), Slic3r::ModelObjectStatusDB::get(), Slic3r::Model::get_backup_path(), Slic3r::GUI::GLCanvas3D::get_empty_cells(), Slic3r::ModelInstance::get_labeled_id(), Slic3r::GUI::get_model_volume(), Slic3r::PrintObjectStatusDB::get_range(), Slic3r::GUI::get_selected_volume(), Slic3r::GUI::get_volume(), Slic3r::CutObjectBase::has_same_id(), Slic3r::UndoRedo::StackImpl::immutable_object_id_impl(), Slic3r::GUI::GLGizmoFdmSupports::init_print_instance(), Slic3r::CutObjectBase::is_equal(), Slic3r::GUI::GLGizmoSVG::is_svg_object(), Slic3r::ModelVolume::is_the_only_one_part(), Slic3r::GUI::IMSlider::IsNewPrint(), Slic3r::ModelObject::load(), load_and_construct(), Slic3r::Print::load_cached_data(), Slic3r::GLVolumeCollection::load_object_auxiliary(), Slic3r::GLVolumeCollection::load_object_volume(), Slic3r::GUI::GCodeViewer::load_shells(), Slic3r::UndoRedo::StackImpl::load_snapshot(), Slic3r::GUI::GLCanvas3D::CenterOfMass::model_markers(), Slic3r::model_property_changed(), Slic3r::model_volume_list_changed(), Slic3r::model_volume_list_copy_configs(), Slic3r::ModelConfigObject::object_id_and_timestamp_match(), Slic3r::ObjectWithTimestamp::object_id_and_timestamp_match(), Slic3r::GUI::GLGizmoEmboss::on_mouse_change_selection(), Slic3r::GUI::GLGizmoSimplify::on_render_input_window(), Slic3r::GUI::GLGizmoTextureDisplacement::open_image_adjust(), Slic3r::CutObjectBase::operator<(), Slic3r::Model::operator=(), Slic3r::ModelObject::operator=(), Slic3r::ModelObject::operator=(), Slic3r::CutObjectBase::operator==(), Slic3r::outer_inner_brim_area(), Slic3r::GUI::GLGizmoTextureDisplacement::painted_area_mm2(), Slic3r::GUI::GLGizmoCut3D::perform_cut(), anonymous_namespace{GLGizmoEmboss.cpp}::prepare_volumes_to_slice(), Slic3r::GUI::GLGizmoTextureDisplacement::preview_part(), Slic3r::GCode::process_layer(), Slic3r::GUI::NotificationManager::push_slicing_warning_notification(), Slic3r::GUI::GLGizmoTextureDisplacement::queue_preview_job(), Slic3r::GUI::queue_texture_displacement_debug(), Slic3r::GUI::GLGizmoEmboss::re_emboss(), Slic3r::GUI::GLGizmoTextureDisplacement::rebuild_other_paint_overlay(), Slic3r::GUI::GLGizmoTextureDisplacement::rebuild_painted_colors(), Slic3r::GUI::GLCanvas3D::reload_scene(), Slic3r::_BBS_Backup_Manager::remove_backup(), Slic3r::Model::remove_backup_path_if_exist(), Slic3r::GUI::GLGizmoTextureDisplacement::render_image_adjust_window(), Slic3r::CLI::run(), Slic3r::GUI::GLCanvas3D::LayersEditing::select_object(), Slic3r::ModelMaterial::serialize(), Slic3r::Model::set_backup_path(), Slic3r::GUI::GLGizmoBrimEars::set_brim_data(), Slic3r::GUI::PartPlate::set_hover_id(), Slic3r::GUI::GLGizmoEmboss::set_volume_by_selection(), Slic3r::GUI::GLGizmoSVG::set_volume_by_selection(), Slic3r::sla_instances(), Slic3r::slices_to_regions(), Slic3r::ModelObject::split(), anonymous_namespace{EmbossJob.cpp}::start_create_volume_on_surface_job(), anonymous_namespace{SurfaceDrag.cpp}::start_dragging(), Slic3r::GUI::Emboss::start_update_volume(), Slic3r::UndoRedo::StackImpl::take_snapshot(), Slic3r::GUI::texture_displacement_bake_volume(), Slic3r::GUI::GLGizmoPainterBase::update_contours(), anonymous_namespace{EmbossJob.cpp}::update_name_in_list(), Slic3r::GUI::ObjectList::update_name_in_model(), Slic3r::GUI::update_object_cut_id(), Slic3r::GUI::GLGizmoTextureDisplacement::v2_recommendation(), Slic3r::verify_update_print_object_regions(), wipe_tower_instance_id, and wipe_tower_object_id.

◆ infill()

void Slic3r::PrintObject::infill ( )
private
814{
815 // prerequisites
816 this->prepare_infill();
817
818 if (this->set_started(posInfill)) {
819 m_print->set_status(35, L("Generating infill toolpath"));
820
821 BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start";
822 tbb::parallel_for(
823 tbb::blocked_range<size_t>(0, m_layers.size()),
824 [this](const tbb::blocked_range<size_t>& range) {
825 for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
826 m_print->throw_if_canceled();
827 m_layers[layer_idx]->make_fills(&m_adaptive_fill_octrees, this->m_lightning_generator.get());
828 }
829 }
830 );
831 m_print->throw_if_canceled();
832 BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - end";
833 /* we could free memory now, but this would make this step not idempotent
834 ### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers};
835 */
836 this->set_done(posInfill);
837 }
838}
void prepare_infill()
Definition PrintObject.cpp:627
@ posInfill
Definition Print.hpp:125

References infill(), L, m_layers, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::m_print, Slic3r::posInfill, prepare_infill(), Slic3r::range(), Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::set_done(), and Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::set_started().

Referenced by infill(), and Slic3r::Print::process().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ insert_support_layer()

SupportLayerPtrs::iterator Slic3r::PrintObject::insert_support_layer ( SupportLayerPtrs::iterator pos,
size_t id,
size_t interface_id,
coordf_t height,
coordf_t print_z,
coordf_t slice_z )
1450{
1451 return m_support_layers.insert(pos, new SupportLayer(id, interface_id, this, height, print_z, slice_z));
1452}

References height(), insert_support_layer(), and m_support_layers.

Referenced by insert_support_layer().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ instances() [1/2]

PrintInstances & Slic3r::PrintObject::instances ( )
inline
386{ return m_instances; }

References m_instances.

◆ instances() [2/2]

◆ invalidate_all_steps()

bool Slic3r::PrintObject::invalidate_all_steps ( )
private
1921{
1922 // First call the "invalidate" functions, which may cancel background processing.
1923 const bool inherited_invalidated = Inherited::invalidate_all_steps();
1924 const bool print_invalidated = m_print->invalidate_all_steps();
1925 bool result = inherited_invalidated || print_invalidated;
1926 // Then reset some of the depending values.
1927 m_slicing_params.valid = false;
1929 return result;
1930}

References invalidate_all_steps(), Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::invalidate_all_steps(), m_belt_floor_z_shift_cache_valid, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::m_print, and m_slicing_params.

Referenced by invalidate_all_steps(), and invalidate_state_by_config_options().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ invalidate_all_steps_without_cancel()

bool Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, COUNT >::invalidate_all_steps_without_cancel ( )
inlineprotectedinherited
667 { return m_state.invalidate_all([](){}); }

References m_state.

Referenced by Slic3r::PrintObject::clear_shared_object().

Here is the caller graph for this function:

◆ invalidate_state_by_config_options()

bool Slic3r::PrintObject::invalidate_state_by_config_options ( const ConfigOptionResolver & old_config,
const ConfigOptionResolver & new_config,
const std::vector< t_config_option_key > & opt_keys )
private
1458{
1459 if (opt_keys.empty())
1460 return false;
1461
1462 std::vector<PrintObjectStep> steps;
1463 bool invalidated = false;
1464 for (const t_config_option_key &opt_key : opt_keys) {
1465 if ( opt_key == "brim_width"
1466 || opt_key == "leading_brim_length"
1467 || opt_key == "extra_brim_width"
1468 || opt_key == "brim_object_gap"
1469 || opt_key == "brim_use_efc_outline"
1470 || opt_key == "brim_type"
1471 || opt_key == "brim_ears_max_angle"
1472 || opt_key == "brim_ears_detection_length"
1473 || opt_key == "brim_ears_outer_only"
1474 // BBS: brim generation depends on printing speed
1475 || opt_key == "outer_wall_speed"
1476 || opt_key == "small_perimeter_speed"
1477 || opt_key == "small_perimeter_threshold"
1478 || opt_key == "small_support_perimeter_speed"
1479 || opt_key == "small_support_perimeter_threshold"
1480 || opt_key == "sparse_infill_speed"
1481 || opt_key == "inner_wall_speed"
1482 || opt_key == "support_speed"
1483 || opt_key == "internal_solid_infill_speed"
1484 || opt_key == "top_surface_speed") {
1485 // Brim is printed below supports, support invalidates brim and skirt.
1486 steps.emplace_back(posSupportMaterial);
1487 if (opt_key == "brim_type" || opt_key == "brim_width") {
1488 // Regenerate the walls when the brim starts or stops putting the first layer's outer walls first.
1489 auto outer_walls_first = [](const ConfigOptionResolver &config) {
1490 return brim_puts_outer_walls_first(config.option<ConfigOptionEnum<BrimType>>("brim_type")->value,
1491 config.option<ConfigOptionFloat>("brim_width")->value);
1492 };
1493 if (outer_walls_first(old_config) != outer_walls_first(new_config))
1494 steps.emplace_back(posPerimeters);
1495 }
1496 } else if (
1497 opt_key == "wall_loops"
1498 || opt_key == "alternate_extra_wall"
1499 || opt_key == "top_one_wall_type"
1500 || opt_key == "min_width_top_surface"
1501 || opt_key == "only_one_wall_first_layer"
1502 || opt_key == "extra_perimeters_on_overhangs"
1503 || opt_key == "detect_overhang_wall"
1504 || opt_key == "initial_layer_line_width"
1505 || opt_key == "inner_wall_line_width"
1506 || opt_key == "infill_wall_overlap"
1507 || opt_key == "top_bottom_infill_wall_overlap"
1508 || opt_key == "seam_gap"
1509 || opt_key == "role_based_wipe_speed"
1510 || opt_key == "wipe_on_loops"
1511 || opt_key == "wipe_speed") {
1512 steps.emplace_back(posPerimeters);
1513 } else if (
1514 opt_key == "small_area_infill_flow_compensation_model") {
1515 steps.emplace_back(posSlice);
1516 } else if (opt_key == "gap_infill_speed"
1517 || opt_key == "filter_out_gap_fill" ) {
1518 // Return true if gap-fill speed has changed from zero value to non-zero or from non-zero value to zero.
1519 // todo multi_extruders: Parameter migration between single and double extruder printers
1520 auto is_gap_fill_changed_state_due_to_speed = [&opt_key, &old_config, &new_config]() -> bool {
1521 if (opt_key == "gap_infill_speed") {
1522 const auto *old_gap_fill_speed = old_config.option<ConfigOptionFloatsNullable>(opt_key);
1523 const auto *new_gap_fill_speed = new_config.option<ConfigOptionFloatsNullable>(opt_key);
1524 assert(old_gap_fill_speed && new_gap_fill_speed);
1525 return (old_gap_fill_speed->values.size() != new_gap_fill_speed->values.size())
1526 || (old_gap_fill_speed->values != new_gap_fill_speed->values);
1527 }
1528 return false;
1529 };
1530
1531 // Filtering of unprintable regions in multi-material segmentation depends on if gap-fill is enabled or not.
1532 // So step posSlice is invalidated when gap-fill was enabled/disabled by option "filter_out_gap_fill" or by
1533 // changing "gap_infill_speed" to force recomputation of the multi-material segmentation.
1534 if (this->is_mm_painted() && (opt_key == "filter_out_gap_fill" && (opt_key == "gap_infill_speed" && is_gap_fill_changed_state_due_to_speed())))
1535 steps.emplace_back(posSlice);
1536 steps.emplace_back(posPerimeters);
1537 } else if (
1538 opt_key == "layer_height"
1539 || opt_key == "mmu_segmented_region_max_width"
1540 || opt_key == "mmu_segmented_region_interlocking_depth"
1541 || opt_key == "raft_layers"
1542 || opt_key == "raft_contact_distance"
1543 || opt_key == "slice_closing_radius"
1544 || opt_key == "slicing_mode"
1545 || opt_key == "slowdown_for_curled_perimeters"
1546 || opt_key == "make_overhang_printable"
1547 || opt_key == "make_overhang_printable_angle"
1548 || opt_key == "make_overhang_printable_hole_size"
1549 || opt_key == "interlocking_beam"
1550 || opt_key == "interlocking_orientation"
1551 || opt_key == "interlocking_beam_layer_count"
1552 || opt_key == "interlocking_depth"
1553 || opt_key == "interlocking_boundary_avoidance"
1554 || opt_key == "interlocking_beam_width") {
1555 steps.emplace_back(posSlice);
1556 } else if (
1557 opt_key == "elefant_foot_compensation"
1558 || opt_key == "elefant_foot_compensation_layers"
1559 || opt_key == "elefant_foot_layers_density"
1560 || opt_key == "support_top_z_distance"
1561 || opt_key == "support_bottom_z_distance"
1562 || opt_key == "xy_hole_compensation"
1563 || opt_key == "xy_contour_compensation"
1564 //BBS: [Arthur] the following params affect bottomBridge surface type detection
1565 || opt_key == "support_type"
1566 || opt_key == "bridge_no_support"
1567 || opt_key == "max_bridge_length"
1568 || opt_key == "support_interface_top_layers"
1569 || opt_key == "support_critical_regions_only"
1570 || opt_key == "hole_to_polyhole"
1571 || opt_key == "hole_to_polyhole_threshold"
1572 || opt_key == "hole_to_polyhole_twisted"
1573 || opt_key == "hole_to_polyhole_max_edges"
1574 ) {
1575 steps.emplace_back(posSlice);
1576 } else if (opt_key == "enable_support") {
1577 steps.emplace_back(posSupportMaterial);
1578 if (m_config.support_top_z_distance == 0.) {
1579 // Enabling / disabling supports while soluble support interface is enabled.
1580 // This changes the bridging logic (bridging enabled without supports, disabled with supports).
1581 // Reset everything.
1582 // See GH #1482 for details.
1583 steps.emplace_back(posSlice);
1584 }
1585 } else if (
1586 opt_key == "support_type"
1587 || opt_key == "support_angle"
1588 || opt_key == "support_on_build_plate_only"
1589 || opt_key == "support_critical_regions_only"
1590 || opt_key == "support_remove_small_overhang"
1591 || opt_key == "enforce_support_layers"
1592 || opt_key == "support_filament"
1593 || opt_key == "support_line_width"
1594 || opt_key == "support_interface_top_layers"
1595 || opt_key == "support_interface_bottom_layers"
1596 || opt_key == "support_interface_pattern"
1597 || opt_key == "support_interface_loop_pattern"
1598 || opt_key == "support_interface_filament"
1599 || opt_key == "support_interface_not_for_body"
1600 || opt_key == "support_interface_spacing"
1601 || opt_key == "support_bottom_interface_spacing" //BBS
1602 || opt_key == "support_base_pattern"
1603 || opt_key == "support_style"
1604 || opt_key == "support_object_xy_distance"
1605 || opt_key == "support_object_first_layer_gap"
1606 || opt_key == "support_base_pattern_spacing"
1607 || opt_key == "support_expansion"
1608 || opt_key == "independent_support_layer_height" // Orca
1609 || opt_key == "support_threshold_angle"
1610 || opt_key == "support_threshold_overlap"
1611 || opt_key == "support_ironing"
1612 || opt_key == "support_ironing_pattern"
1613 || opt_key == "support_ironing_flow"
1614 || opt_key == "support_ironing_spacing"
1615 || opt_key == "raft_expansion"
1616 || opt_key == "raft_first_layer_density"
1617 || opt_key == "raft_first_layer_expansion"
1618 || opt_key == "bridge_no_support"
1619 || opt_key == "max_bridge_length"
1620 || opt_key == "initial_layer_line_width"
1621 || opt_key == "tree_support_auto_brim"
1622 || opt_key == "tree_support_brim_width"
1623 || opt_key == "tree_support_top_rate"
1624 || opt_key == "tree_support_branch_distance"
1625 || opt_key == "tree_support_branch_distance_organic"
1626 || opt_key == "tree_support_tip_diameter"
1627 || opt_key == "tree_support_branch_diameter"
1628 || opt_key == "tree_support_branch_diameter_organic"
1629 || opt_key == "tree_support_branch_diameter_angle"
1630 || opt_key == "tree_support_branch_angle"
1631 || opt_key == "tree_support_branch_angle_organic"
1632 || opt_key == "tree_support_angle_slow"
1633 || opt_key == "tree_support_wall_count") {
1634 steps.emplace_back(posSupportMaterial);
1635 } else if (
1636 opt_key == "bottom_shell_layers"
1637 || opt_key == "top_shell_layers"
1638 || opt_key == "top_color_penetration_layers"
1639 || opt_key == "bottom_color_penetration_layers") {
1640
1641 steps.emplace_back(posSlice);
1642#if (0)
1643 const auto *old_shell_layers = old_config.option<ConfigOptionInt>(opt_key);
1644 const auto *new_shell_layers = new_config.option<ConfigOptionInt>(opt_key);
1645 assert(old_shell_layers && new_shell_layers);
1646
1647 bool value_changed = (old_shell_layers->value == 0 && new_shell_layers->value > 0) ||
1648 (old_shell_layers->value > 0 && new_shell_layers->value == 0);
1649
1650 if (value_changed && this->object_extruders().size() > 1) {
1651 steps.emplace_back(posSlice);
1652 }
1653 else if (m_print->config().spiral_mode && opt_key == "bottom_shell_layers") {
1654 // Changing the number of bottom layers when a spiral vase is enabled requires re-slicing the object again.
1655 // Otherwise, holes in the bottom layers could be filled, as is reported in GH #5528.
1656 steps.emplace_back(posSlice);
1657 }
1658#endif
1659 } else if (
1660 opt_key == "interface_shells"
1661 || opt_key == "infill_multiline"
1662 || opt_key == "infill_combination"
1663 || opt_key == "infill_combination_max_layer_height"
1664 || opt_key == "bottom_shell_thickness"
1665 || opt_key == "top_shell_thickness"
1666 || opt_key == "top_surface_expansion_margin"
1667 || opt_key == "top_surface_expansion_direction"
1668 || opt_key == "minimum_sparse_infill_area"
1669 || opt_key == "sparse_infill_filament_id"
1670 || opt_key == "internal_solid_filament_id"
1671 || opt_key == "top_surface_filament_id"
1672 || opt_key == "bottom_surface_filament_id"
1673 || opt_key == "sparse_infill_line_width"
1674 || opt_key == "skin_infill_line_width"
1675 || opt_key == "skeleton_infill_line_width"
1676 || opt_key == "infill_direction"
1677 || opt_key == "solid_infill_direction"
1678 || opt_key == "top_layer_direction"
1679 || opt_key == "bottom_layer_direction"
1680 || opt_key == "align_infill_direction_to_model"
1681 || opt_key == "extra_solid_infills"
1682 || opt_key == "ensure_vertical_shell_thickness"
1683 || opt_key == "bridge_angle"
1684 || opt_key == "internal_bridge_angle" // ORCA: Internal bridge angle override
1685 || opt_key == "relative_bridge_angle" // ORCA: Relative bridge angle
1686 //BBS
1687 || opt_key == "bridge_line_width"
1688 || opt_key == "bridge_density"
1689 || opt_key == "internal_bridge_density") {
1690 steps.emplace_back(posPrepareInfill);
1691 } else if (
1692 opt_key == "top_surface_pattern"
1693 || opt_key == "bottom_surface_pattern"
1694 || opt_key == "top_surface_fill_order"
1695 || opt_key == "bottom_surface_fill_order"
1696 || opt_key == "internal_solid_infill_pattern"
1697 || opt_key == "external_fill_link_max_length"
1698 || opt_key == "infill_anchor"
1699 || opt_key == "infill_anchor_max"
1700 || opt_key == "top_surface_line_width"
1701 || opt_key == "bottom_surface_density"
1702 || opt_key == "initial_layer_line_width"
1703 || opt_key == "small_area_infill_flow_compensation"
1704 || opt_key == "lateral_lattice_angle_1"
1705 || opt_key == "lateral_lattice_angle_2"
1706 || opt_key == "infill_overhang_angle") {
1707 steps.emplace_back(posInfill);
1708 } else if (opt_key == "sparse_infill_pattern"
1709 || opt_key == "adaptive_infill_mode"
1710 || opt_key == "adaptive_infill_interior_density"
1711 || opt_key == "adaptive_infill_gradient"
1712 // Orca: Body centering now also determines bridge anchors during preparation.
1713 // Invalidating preparation also invalidates infill, including top/bottom surfaces.
1714 || opt_key == "center_of_surface_pattern"
1715 || opt_key == "separated_infills"
1716 || opt_key == "sparse_infill_smooth_factor"
1717 || opt_key == "symmetric_infill_y_axis"
1718 || opt_key == "infill_shift_step"
1719 || opt_key == "sparse_infill_rotate_template"
1720 || opt_key == "solid_infill_rotate_template"
1721 || opt_key == "lightning_overhang_angle"
1722 || opt_key == "lightning_prune_angle"
1723 || opt_key == "lightning_straightening_angle"
1724 || opt_key == "skeleton_infill_density"
1725 || opt_key == "skin_infill_density"
1726 || opt_key == "infill_lock_depth"
1727 || opt_key == "skin_infill_depth") {
1728 steps.emplace_back(posPrepareInfill);
1729 } else if (opt_key == "sparse_infill_density") {
1730 // One likely wants to reslice only when switching between zero infill to simulate boolean difference (subtracting volumes),
1731 // normal infill and 100% (solid) infill.
1732 const auto *old_density = old_config.option<ConfigOptionPercent>(opt_key);
1733 const auto *new_density = new_config.option<ConfigOptionPercent>(opt_key);
1734 assert(old_density && new_density);
1735 //FIXME Vojtech is not quite sure about the 100% here, maybe it is not needed.
1736 if (is_approx(old_density->value, 0.) || is_approx(old_density->value, 100.) ||
1737 is_approx(new_density->value, 0.) || is_approx(new_density->value, 100.))
1738 steps.emplace_back(posPerimeters);
1739 steps.emplace_back(posPrepareInfill);
1740 } else if (opt_key == "top_surface_density") {
1741 // ORCA: 0% means no top solid fill, which switches off both the top surface expansion and the wall
1742 // removal over top surfaces. Only crossing zero matters; posPerimeters cascades to posPrepareInfill.
1743 const auto *old_density = old_config.option<ConfigOptionPercent>(opt_key);
1744 const auto *new_density = new_config.option<ConfigOptionPercent>(opt_key);
1745 assert(old_density && new_density);
1746 if (is_approx(old_density->value, 0.) || is_approx(new_density->value, 0.))
1747 steps.emplace_back(posPerimeters);
1748 steps.emplace_back(posInfill);
1749 } else if (opt_key == "top_surface_expansion") {
1750 // ORCA: without the expansion the top fill never reaches the space freed by only_one_wall_top, so the
1751 // walls over top surfaces are kept. Only crossing zero matters; posPerimeters cascades to posPrepareInfill.
1752 const auto *old_expansion = old_config.option<ConfigOptionFloat>(opt_key);
1753 const auto *new_expansion = new_config.option<ConfigOptionFloat>(opt_key);
1754 assert(old_expansion && new_expansion);
1755 if (old_expansion->value <= 0. || new_expansion->value <= 0.)
1756 steps.emplace_back(posPerimeters);
1757 steps.emplace_back(posPrepareInfill);
1758 } else if (opt_key == "internal_solid_infill_line_width") {
1759 // This value is used for calculating perimeter - infill overlap, thus perimeters need to be recalculated.
1760 steps.emplace_back(posPerimeters);
1761 steps.emplace_back(posPrepareInfill);
1762 } else if (
1763 opt_key == "outer_wall_line_width"
1764 || opt_key == "outer_wall_filament_id"
1765 || opt_key == "inner_wall_filament_id"
1766 || opt_key == "fuzzy_skin"
1767 || opt_key == "fuzzy_skin_thickness"
1768 || opt_key == "fuzzy_skin_point_distance"
1769 || opt_key == "fuzzy_skin_first_layer"
1770 || opt_key == "fuzzy_skin_mode"
1771 || opt_key == "fuzzy_skin_noise_type"
1772 || opt_key == "fuzzy_skin_scale"
1773 || opt_key == "fuzzy_skin_octaves"
1774 || opt_key == "fuzzy_skin_persistence"
1775 || opt_key == "detect_overhang_wall"
1776 || opt_key == "unsupported_wall_last"
1777 || opt_key == "overhang_reverse"
1778 || opt_key == "overhang_reverse_internal_only"
1779 || opt_key == "overhang_reverse_threshold"
1780 || opt_key == "wall_direction"
1781 || opt_key == "enable_overhang_speed"
1782 || opt_key == "detect_thin_wall"
1783 || opt_key == "precise_outer_wall") {
1784 steps.emplace_back(posPerimeters);
1785 steps.emplace_back(posSupportMaterial);
1786 } else if (opt_key == "bridge_flow" || opt_key == "internal_bridge_flow") {
1787 steps.emplace_back(posPerimeters);
1788 steps.emplace_back(posInfill);
1789 steps.emplace_back(posSupportMaterial);
1790 } else if (
1791 opt_key == "wall_generator"
1792 || opt_key == "wall_transition_length"
1793 || opt_key == "wall_transition_filter_deviation"
1794 || opt_key == "wall_transition_angle"
1795 || opt_key == "wall_distribution_count"
1796 || opt_key == "wall_maximum_resolution"
1797 || opt_key == "wall_maximum_deviation"
1798 || opt_key == "min_feature_size"
1799 || opt_key == "min_length_factor"
1800 || opt_key == "min_bead_width") {
1801 steps.emplace_back(posSlice);
1802 } else if (
1803 opt_key == "seam_position"
1804 || opt_key == "seam_slope_type"
1805 || opt_key == "seam_slope_conditional"
1806 || opt_key == "scarf_angle_threshold"
1807 || opt_key == "scarf_overhang_threshold"
1808 || opt_key == "scarf_joint_speed"
1809 || opt_key == "seam_slope_start_height"
1810 || opt_key == "seam_slope_entire_loop"
1811 || opt_key == "seam_slope_min_length"
1812 || opt_key == "seam_slope_steps"
1813 || opt_key == "seam_slope_inner_walls"
1814 || opt_key == "support_speed"
1815 || opt_key == "support_interface_speed"
1816 || opt_key == "overhang_1_4_speed"
1817 || opt_key == "overhang_2_4_speed"
1818 || opt_key == "overhang_3_4_speed"
1819 || opt_key == "overhang_4_4_speed"
1820 || opt_key == "bridge_speed"
1821 || opt_key == "internal_bridge_speed"
1822 || opt_key == "outer_wall_speed"
1823 || opt_key == "small_perimeter_speed"
1824 || opt_key == "small_perimeter_threshold"
1825 || opt_key == "small_support_perimeter_speed"
1826 || opt_key == "small_support_perimeter_threshold"
1827 || opt_key == "sparse_infill_speed"
1828 || opt_key == "inner_wall_speed"
1829 || opt_key == "internal_solid_infill_speed"
1830 || opt_key == "top_surface_speed"
1831 || opt_key == "bed_mesh_min"
1832 || opt_key == "bed_mesh_max"
1833 || opt_key == "adaptive_bed_mesh_margin"
1834 || opt_key == "bed_mesh_probe_distance"
1835 || opt_key == "print_flow_ratio"
1836 || opt_key == "first_layer_flow_ratio"
1837 || opt_key == "top_solid_infill_flow_ratio"
1838 || opt_key == "bottom_solid_infill_flow_ratio"
1839 || opt_key == "outer_wall_flow_ratio"
1840 || opt_key == "inner_wall_flow_ratio"
1841 || opt_key == "overhang_flow_ratio"
1842 || opt_key == "sparse_infill_flow_ratio"
1843 || opt_key == "internal_solid_infill_flow_ratio"
1844 || opt_key == "gap_fill_flow_ratio"
1845 || opt_key == "support_flow_ratio"
1846 || opt_key == "support_interface_flow_ratio"
1847 || opt_key == "brim_flow_ratio"
1848 || opt_key == "filament_flow_ratio"
1849 || opt_key == "scarf_joint_flow_ratio"
1850 || opt_key == "wipe_inward"
1851 || opt_key == "wipe_inward_distance"
1852 || opt_key == "spiral_starting_flow_ratio"
1853 || opt_key == "spiral_finishing_flow_ratio") {
1854 invalidated |= m_print->invalidate_step(psGCodeExport);
1855 } else if (
1856 opt_key == "flush_into_infill"
1857 || opt_key == "flush_into_objects"
1858 || opt_key == "flush_into_support"
1859 || opt_key == "belt_purge_tower_object") {
1860 invalidated |= m_print->invalidate_step(psWipeTower);
1861 invalidated |= m_print->invalidate_step(psGCodeExport);
1862 } else {
1863 // for legacy, if we can't handle this option let's invalidate all steps
1864 this->invalidate_all_steps();
1865 invalidated = true;
1866 }
1867 }
1868
1870 for (PrintObjectStep step : steps)
1871 invalidated |= this->invalidate_step(step);
1872 return invalidated;
1873}
bool invalidate_step(PrintObjectStep step)
Definition PrintObject.cpp:1875
std::vector< unsigned int > object_extruders() const
Definition PrintObject.cpp:4562
bool invalidate_all_steps()
Definition PrintObject.cpp:1920
bool is_mm_painted() const
Definition Print.hpp:513
zaa_minimize_perimeter_height ConfigOptionFloat
Definition PrintConfig.hpp:1954
bool brim_puts_outer_walls_first(BrimType brim_type, double brim_width)
Definition PerimeterGenerator.cpp:3107
std::string t_config_option_key
Definition Config.hpp:96
ConfigOptionFloatsTempl< true > ConfigOptionFloatsNullable
Definition Config.hpp:1016
@ psWipeTower
Definition Print.hpp:109
@ psGCodeExport
Definition Print.hpp:118
constexpr bool is_approx(Number value, Number test_value, Number precision=EPSILON)
Definition libslic3r.h:320
void sort_remove_duplicates(std::vector< T > &vec)
Definition libslic3r.h:231
PrintObjectStep
Definition Print.hpp:123
@ posSlice
Definition Print.hpp:124
@ posPerimeters
Definition Print.hpp:124
@ posPrepareInfill
Definition Print.hpp:124

References Slic3r::brim_puts_outer_walls_first(), config(), invalidate_all_steps(), invalidate_state_by_config_options(), invalidate_step(), Slic3r::is_approx(), is_mm_painted(), m_config, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::m_print, object_extruders(), Slic3r::ConfigOptionResolver::option(), Slic3r::posInfill, Slic3r::posPerimeters, Slic3r::posPrepareInfill, Slic3r::posSlice, Slic3r::posSupportMaterial, Slic3r::psGCodeExport, Slic3r::psWipeTower, size(), Slic3r::sort_remove_duplicates(), Slic3r::ConfigOptionSingle< T >::value, and Slic3r::ConfigOptionVector< double >::values.

Referenced by invalidate_state_by_config_options().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ invalidate_step()

bool Slic3r::PrintObject::invalidate_step ( PrintObjectStep step)
private
1876{
1877 bool invalidated = Inherited::invalidate_step(step);
1878
1879 // propagate to dependent steps
1880 if (step == posPerimeters) {
1882 invalidated |= m_print->invalidate_steps({ psSkirtBrim });
1883 } else if (step == posPrepareInfill) {
1885 } else if (step == posInfill) {
1886 invalidated |= this->invalidate_steps({ posIroning, posContouring, posSimplifyInfill });
1887 invalidated |= m_print->invalidate_steps({ psSkirtBrim });
1888 } else if (step == posSlice) {
1889 // posSimplifySupportPath is listed with posSupportMaterial: invalidate_steps() does not
1890 // propagate, so without it a re-slice regenerated the supports but kept the step done,
1891 // and the new support paths were exported unsimplified, unlike a fresh slice.
1892 // posDetectOverhangsForLift reads the layers' overhang regions, which a re-slice
1893 // starts over empty: without it here the step stayed done and the lift logic in
1894 // GCode::needs_retraction() had no overhangs to test against until something else
1895 // invalidated it.
1897 invalidated |= m_print->invalidate_steps({ psSkirtBrim });
1898 m_slicing_params.valid = false;
1899 // The exact belt_floor_z_shift is recomputed when slice() runs again.
1901 } else if (step == posSupportMaterial) {
1902 invalidated |= this->invalidate_steps({ posSimplifySupportPath });
1903 invalidated |= m_print->invalidate_steps({ psSkirtBrim });
1904 // SlicingParameters depend on support config (enable_support /
1905 // raft_layers / enforce_support_layers feed min/max layer height in
1906 // Slicing.cpp), so invalidate them here. The vertex-scan
1907 // belt_floor_z_shift is preserved via m_belt_floor_z_shift_cached.
1908 m_slicing_params.valid = false;
1909 }
1910
1911 // Wipe tower depends on the ordering of extruders, which in turn depends on everything.
1912 // It also decides about what the flush_into_infill / wipe_into_object / flush_into_support features will do,
1913 // and that too depends on many of the settings.
1914 invalidated |= m_print->invalidate_step(psWipeTower);
1915 // Invalidate G-code export in any case.
1916 invalidated |= m_print->invalidate_step(psGCodeExport);
1917 return invalidated;
1918}
bool invalidate_step(PrintObjectStep step)
Definition PrintBase.hpp:657
bool invalidate_steps(StepTypeIterator step_begin, StepTypeIterator step_end)
Definition PrintBase.hpp:660
@ psSkirtBrim
Definition Print.hpp:114
@ posIroning
Definition Print.hpp:125
@ posSimplifySupportPath
Definition Print.hpp:125
@ posSimplifyInfill
Definition Print.hpp:128
@ posSimplifyPath
Definition Print.hpp:125

References invalidate_step(), Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::invalidate_step(), Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::invalidate_steps(), m_belt_floor_z_shift_cache_valid, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::m_print, m_slicing_params, Slic3r::posContouring, Slic3r::posDetectOverhangsForLift, Slic3r::posInfill, Slic3r::posIroning, Slic3r::posPerimeters, Slic3r::posPrepareInfill, Slic3r::posSimplifyInfill, Slic3r::posSimplifyPath, Slic3r::posSimplifySupportPath, Slic3r::posSlice, Slic3r::posSupportMaterial, Slic3r::psGCodeExport, Slic3r::psSkirtBrim, and Slic3r::psWipeTower.

Referenced by invalidate_state_by_config_options(), and invalidate_step().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ invalidate_steps() [1/2]

bool Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, COUNT >::invalidate_steps ( std::initializer_list< PrintObjectStep > il)
inlineprotectedinherited
663 { return m_state.invalidate_multiple(il.begin(), il.end(), PrintObjectBase::cancel_callback(m_print)); }

References Slic3r::PrintObjectBase::cancel_callback(), m_print, and m_state.

Here is the call graph for this function:

◆ invalidate_steps() [2/2]

bool Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, COUNT >::invalidate_steps ( StepTypeIterator step_begin,
StepTypeIterator step_end )
inlineprotectedinherited
661 { return m_state.invalidate_multiple(step_begin, step_end, PrintObjectBase::cancel_callback(m_print)); }

References Slic3r::PrintObjectBase::cancel_callback(), m_print, and m_state.

Referenced by Slic3r::PrintObject::invalidate_step().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ironing()

void Slic3r::PrintObject::ironing ( )
private
841{
842 if (this->set_started(posIroning)) {
843 BOOST_LOG_TRIVIAL(debug) << "Ironing in parallel - start";
844 tbb::parallel_for(
845 // Ironing starting with layer 0 to support ironing all surfaces.
846 tbb::blocked_range<size_t>(0, m_layers.size()),
847 [this](const tbb::blocked_range<size_t>& range) {
848 for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
849 m_print->throw_if_canceled();
850 m_layers[layer_idx]->make_ironing();
851 }
852 }
853 );
854 m_print->throw_if_canceled();
855 BOOST_LOG_TRIVIAL(debug) << "Ironing in parallel - end";
856 this->set_done(posIroning);
857 }
858}

References ironing(), m_layers, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::m_print, Slic3r::posIroning, Slic3r::range(), Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::set_done(), and Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::set_started().

Referenced by ironing(), and Slic3r::Print::process().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ is_fuzzy_skin_painted()

bool Slic3r::PrintObject::is_fuzzy_skin_painted ( ) const
inline
515{ return this->model_object()->is_fuzzy_skin_painted(); }
bool is_fuzzy_skin_painted() const
Definition Model.cpp:1509

References Slic3r::ModelObject::is_fuzzy_skin_painted(), and Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::model_object().

Referenced by slice_volumes().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ is_mm_painted()

bool Slic3r::PrintObject::is_mm_painted ( ) const
inline
513{ return this->model_object()->is_mm_painted(); }
bool is_mm_painted() const
Definition Model.cpp:1504

References Slic3r::ModelObject::is_mm_painted(), and Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::model_object().

Referenced by invalidate_state_by_config_options(), and slice_volumes().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ is_step_done()

bool Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, COUNT >::is_step_done ( PrintObjectStep step) const
inlineinherited

References m_print, m_state, and Slic3r::PrintObjectBase::state_mutex().

Here is the call graph for this function:

◆ is_step_done_unguarded()

bool Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, COUNT >::is_step_done_unguarded ( PrintObjectStep step) const
inlineprotectedinherited
670{ return m_state.is_done_unguarded(step); }

References m_state.

◆ is_step_started_unguarded()

bool Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, COUNT >::is_step_started_unguarded ( PrintObjectStep step) const
inlineprotectedinherited
669{ return m_state.is_started_unguarded(step); }

References m_state.

◆ is_support_necessary()

SupportNecessaryType Slic3r::PrintObject::is_support_necessary ( )
private
5290{
5291 const double cantilevel_dist_thresh = scale_(6);
5292
5293 TreeSupport tree_support(*this, m_slicing_params);
5294 tree_support.support_type = SupportType::stTreeAuto; // need to set support type to fully utilize the power of feature detection
5295 tree_support.detect_overhangs(true);
5296 this->clear_support_layers();
5297 if (tree_support.has_sharp_tails)
5298 return SharpTail;
5299 else if (tree_support.has_cantilever && tree_support.max_cantilever_dist > cantilevel_dist_thresh)
5300 return Cantilever;
5301
5302 return NoNeedSupp;
5303}
@ stTreeAuto
Definition PrintConfig.hpp:379

References Slic3r::Cantilever, clear_support_layers(), Slic3r::TreeSupport::detect_overhangs(), Slic3r::TreeSupport::has_cantilever, Slic3r::TreeSupport::has_sharp_tails, is_support_necessary(), m_slicing_params, Slic3r::TreeSupport::max_cantilever_dist, Slic3r::NoNeedSupp, scale_, Slic3r::SharpTail, Slic3r::stTreeAuto, and Slic3r::TreeSupport::support_type.

Referenced by generate_support_material(), and is_support_necessary().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ layer_count()

◆ layers() [1/2]

LayerPtrs & Slic3r::PrintObject::layers ( )
inline
389{ return m_layers; }

References m_layers.

◆ layers() [2/2]

◆ load_and_construct()

template<class Archive>
void Slic3r::ObjectBase::load_and_construct ( Archive & ar,
cereal::construct< ObjectBase > & construct )
inlinestaticprivateinherited
109{ ObjectID id; ar(id); construct(id); }
ObjectID id() const
Definition ObjectID.hpp:69

References id().

Here is the call graph for this function:

◆ make_perimeters()

void Slic3r::PrintObject::make_perimeters ( )
private
521{
522 // prerequisites
523 this->slice();
524
525 if (! this->set_started(posPerimeters))
526 return;
527
528 m_print->set_status(15, L("Generating walls"));
529 BOOST_LOG_TRIVIAL(info) << "Generating walls..." << log_memory_info();
530
531 // Revert the typed slices into untyped slices.
532 if (m_typed_slices) {
533 for (Layer *layer : m_layers) {
534 layer->restore_untyped_slices();
535 m_print->throw_if_canceled();
536 }
537 m_typed_slices = false;
538 }
539
540 // compare each layer to the one below, and mark those slices needing
541 // one additional inner perimeter, like the top of domed objects-
542
543 // this algorithm makes sure that at least one perimeter is overlapping
544 // but we don't generate any extra perimeter if fill density is zero, as they would be floating
545 // inside the object - infill_only_where_needed should be the method of choice for printing
546 // hollow objects
547 for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
548 const PrintRegion &region = this->printing_region(region_id);
549 //BBS: remove extra_perimeters, always false
550 //if (! region.config().extra_perimeters || region.config().wall_loops == 0 || region.config().sparse_infill_density == 0 || this->layer_count() < 2)
551 continue;
552
553 BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - start";
554 tbb::parallel_for(
555 tbb::blocked_range<size_t>(0, m_layers.size() - 1),
556 [this, &region, region_id](const tbb::blocked_range<size_t>& range) {
557 for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
558 m_print->throw_if_canceled();
559 LayerRegion &layerm = *m_layers[layer_idx]->get_region(region_id);
560 const LayerRegion &upper_layerm = *m_layers[layer_idx+1]->get_region(region_id);
561 const Polygons upper_layerm_polygons = to_polygons(upper_layerm.slices.surfaces);
562 // Filter upper layer polygons in intersection_ppl by their bounding boxes?
563 // my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ];
564 const double total_loop_length = total_length(upper_layerm_polygons);
565 const coord_t perimeter_spacing = layerm.flow(frPerimeter).scaled_spacing();
566 const Flow ext_perimeter_flow = layerm.flow(frExternalPerimeter);
567 const coord_t ext_perimeter_width = ext_perimeter_flow.scaled_width();
568 const coord_t ext_perimeter_spacing = ext_perimeter_flow.scaled_spacing();
569
570 for (Surface &slice : layerm.slices.surfaces) {
571 for (;;) {
572 // compute the total thickness of perimeters
573 const coord_t perimeters_thickness = ext_perimeter_width/2 + ext_perimeter_spacing/2
574 + (region.config().wall_loops-1 + slice.extra_perimeters) * perimeter_spacing;
575 // define a critical area where we don't want the upper slice to fall into
576 // (it should either lay over our perimeters or outside this area)
577 const coord_t critical_area_depth = coord_t(perimeter_spacing * 1.5);
578 const Polygons critical_area = diff(
579 offset(slice.expolygon, float(- perimeters_thickness)),
580 offset(slice.expolygon, float(- perimeters_thickness - critical_area_depth))
581 );
582 // check whether a portion of the upper slices falls inside the critical area
583 const Polylines intersection = intersection_pl(to_polylines(upper_layerm_polygons), critical_area);
584 // only add an additional loop if at least 30% of the slice loop would benefit from it
585 if (total_length(intersection) <= total_loop_length*0.3)
586 break;
587 /*
588 if (0) {
589 require "Slic3r/SVG.pm";
590 Slic3r::SVG::output(
591 "extra.svg",
592 no_arrows => 1,
593 expolygons => union_ex($critical_area),
594 polylines => [ map $_->split_at_first_point, map $_->p, @{$upper_layerm->slices} ],
595 );
596 }
597 */
598 ++ slice.extra_perimeters;
599 }
600 #ifdef DEBUG
601 if (slice.extra_perimeters > 0)
602 printf(" adding %d more perimeter(s) at layer %zu\n", slice.extra_perimeters, layer_idx);
603 #endif
604 }
605 }
606 });
607 m_print->throw_if_canceled();
608 BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - end";
609 }
610
611 BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - start";
612 tbb::parallel_for(
613 tbb::blocked_range<size_t>(0, m_layers.size()),
614 [this](const tbb::blocked_range<size_t>& range) {
615 for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
616 m_print->throw_if_canceled();
617 m_layers[layer_idx]->make_perimeters();
618 }
619 }
620 );
621 m_print->throw_if_canceled();
622 BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - end";
623
624 this->set_done(posPerimeters);
625}

References L, Slic3r::log_memory_info(), m_layers, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::m_print, m_typed_slices, make_perimeters(), num_printing_regions(), Slic3r::posPerimeters, printing_region(), Slic3r::range(), Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::set_started(), and slice().

Referenced by make_perimeters(), and Slic3r::Print::process().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ max_z()

double Slic3r::PrintObject::max_z ( ) const
inline
407{ return m_max_z; }

References m_max_z.

◆ model_object() [1/2]

ModelObject * Slic3r::PrintObjectBase::model_object ( )
inlineinherited
347{ return m_model_object; }

References m_model_object.

◆ model_object() [2/2]

◆ need_z_contouring()

bool Slic3r::PrintObject::need_z_contouring ( ) const
private
861{
862 size_t num_regions = this->num_printing_regions();
863 for (size_t region_id = 0; region_id < num_regions; region_id++) {
864 if (this->printing_region(region_id).config().zaa_enabled)
865 return true;
866 }
867
868 return false;
869}

References config(), need_z_contouring(), num_printing_regions(), and printing_region().

Referenced by need_z_contouring(), and Slic3r::Print::process().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ num_printing_regions()

◆ object_config_from_model_object()

PrintObjectConfig Slic3r::PrintObject::object_config_from_model_object ( const PrintObjectConfig & default_object_config,
const ModelObject & object,
size_t num_extruders,
std::vector< int > & variant_index )
staticprivate
4290{
4291 PrintObjectConfig config = default_object_config;
4292 {
4293 DynamicPrintConfig src_normalized(object.config.get());
4294 src_normalized.normalize_fdm();
4296 }
4297 // Clamp invalid extruders to the default extruder (with index 1).
4298 clamp_exturder_to_default(config.support_filament, num_extruders);
4299 clamp_exturder_to_default(config.support_interface_filament, num_extruders);
4300 return config;
4301}
zaa_minimize_perimeter_height PrintObjectConfig
Definition PrintConfig.hpp:2183
void update_static_print_config_from_dynamic(ConfigBase &config, const DynamicPrintConfig &dest_config, std::vector< int > variant_index, std::set< std::string > &key_set1, int stride)
Definition PrintConfig.cpp:12420
static void clamp_exturder_to_default(ConfigOptionInt &opt, size_t num_extruders)
Definition PrintObject.cpp:4276
std::set< std::string > print_options_with_variant
Definition PrintConfig.cpp:10102

References Slic3r::clamp_exturder_to_default(), config(), Slic3r::DynamicPrintConfig::normalize_fdm(), object_config_from_model_object(), Slic3r::print_options_with_variant, Slic3r::PrintObjectConfig, and Slic3r::update_static_print_config_from_dynamic().

Referenced by object_config_from_model_object(), slicing_parameters(), and Slic3r::Print::update_shrinkage_compensation().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ object_extruders()

std::vector< unsigned int > Slic3r::PrintObject::object_extruders ( ) const
4563{
4564 std::vector<unsigned int> extruders;
4565 extruders.reserve(this->all_regions().size() * 3);
4566
4567 //Orca: Collect extruders from all regions.
4568 for (const PrintRegion &region : this->all_regions())
4569 region.collect_object_printing_extruders(*this->print(), extruders);
4570
4571 const ModelObject* mo = this->model_object();
4572 for (const ModelVolume* mv : mo->volumes) {
4573 std::vector<int> volume_extruders = mv->get_extruders();
4574 for (int extruder : volume_extruders) {
4575 assert(extruder > 0);
4576 extruders.push_back(extruder - 1);
4577 }
4578 }
4579 sort_remove_duplicates(extruders);
4580 return extruders;
4581}
std::vector< std::reference_wrapper< const PrintRegion > > all_regions() const
Definition PrintObject.cpp:197

References all_regions(), Slic3r::ModelVolume::get_extruders(), Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::model_object(), object_extruders(), Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::print(), size(), Slic3r::sort_remove_duplicates(), and Slic3r::ModelObject::volumes.

Referenced by Slic3r::get_default_perimeter_spacing(), invalidate_state_by_config_options(), object_extruders(), printing_extruders(), slicing_parameters(), and update_slicing_parameters().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ object_skirt()

const ExtrusionEntityCollection & Slic3r::PrintObject::object_skirt ( ) const
inline
446 {
447 return m_skirt;
448 }
ExtrusionEntityCollection m_skirt
Definition Print.hpp:716

References m_skirt.

◆ prepare_adaptive_infill_data()

FillAdaptive::RegionOctrees Slic3r::PrintObject::prepare_adaptive_infill_data ( const std::vector< std::pair< const Surface *, const Layer * > > & surfaces_w_layer) const
private
1171{
1172 using namespace FillAdaptive;
1173
1174 // Orca: Each region fills with the octrees of its own line spacing, shared by the regions of equal spacing.
1175 const std::vector<double> line_spacing = adaptive_fill_line_spacing(*this);
1176 std::vector<std::pair<double, bool>> spacings; // Line spacing, support cubic.
1177 RegionOctrees octrees;
1178 octrees.region_set.assign(line_spacing.size(), -1);
1179 for (size_t region_id = 0; region_id < line_spacing.size(); ++ region_id)
1180 if (line_spacing[region_id] > 0.) {
1181 const std::pair<double, bool> spacing(line_spacing[region_id], this->printing_region(region_id).config().sparse_infill_pattern == ipSupportCubic);
1182 const auto it = std::find(spacings.begin(), spacings.end(), spacing);
1183 octrees.region_set[region_id] = int(it - spacings.begin());
1184 if (it == spacings.end())
1185 spacings.push_back(spacing);
1186 }
1187 if (spacings.empty() || this->layers().empty())
1188 return {};
1189 octrees.sets.resize(spacings.size());
1190
1191 indexed_triangle_set mesh = this->model_object()->raw_indexed_triangle_set();
1192 // Rotate mesh and build octree on it with axis-aligned (standart base) cubes.
1193 auto to_octree = transform_to_octree().toRotationMatrix();
1194 // Overhangs below are placed at Layer::bottom_z(), which includes the belt global Z offset.
1195 Transform3d object_trafo = this->trafo_sliced();
1196 object_trafo.translation().z() += m_belt_global_z_offset;
1197 its_transform(mesh, to_octree * object_trafo, true);
1198
1199 // Triangulate internal bridging surfaces.
1200 std::vector<std::vector<Vec3d>> overhangs(std::max(surfaces_w_layer.size(), size_t(1)));
1201 // ^ make sure vector is not empty, even with no briding surfaces we still want to build the adaptive trees later, some continue normally
1202 tbb::parallel_for(tbb::blocked_range<int>(0, surfaces_w_layer.size()),
1203 [this, &to_octree, &overhangs, &surfaces_w_layer](const tbb::blocked_range<int> &range) {
1204 PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
1205 for (int surface_idx = range.begin(); surface_idx < range.end(); ++surface_idx) {
1206 std::vector<Vec3d> &out = overhangs[surface_idx];
1207 m_print->throw_if_canceled();
1208 append(out, triangulate_expolygon_3d(surfaces_w_layer[surface_idx].first->expolygon,
1209 float(surfaces_w_layer[surface_idx].second->bottom_z())));
1210 for (Vec3d &p : out)
1211 p = (to_octree * p).eval();
1212 }
1213 });
1214
1215 // Orca: Each body gets the octree it has when sliced on its own, from its own triangles, for each line spacing
1216 // its regions fill with. Body num_bodies stands for the whole object, which serves an object of a single body
1217 // and the surfaces of bodies that have no octree of their own.
1218 const size_t num_bodies = m_separated_body_bboxes.size();
1219 std::vector<std::pair<size_t, size_t>> to_build; // Set, body.
1220 std::vector<indexed_triangle_set> body_meshes;
1221 std::vector<std::vector<Vec3d>> body_overhangs(num_bodies);
1222 if (num_bodies > 1) {
1223 body_meshes = split_mesh_by_body(*this, mesh, num_bodies);
1224 for (size_t i = 0; i < surfaces_w_layer.size(); ++ i)
1225 if (const int body = separated_body_at(*surfaces_w_layer[i].second, surfaces_w_layer[i].first->expolygon.contour.points.front()); body >= 0)
1226 append(body_overhangs[body], overhangs[i]);
1227 std::vector<std::vector<char>> fills(spacings.size(), std::vector<char>(num_bodies + 1, false));
1228 for (const Layer *layer : m_layers)
1229 for (size_t region_id = 0; region_id < layer->regions().size() && region_id < octrees.region_set.size(); ++ region_id)
1230 if (const int set = octrees.region_set[region_id]; set >= 0)
1231 for (const Surface &surface : layer->regions()[region_id]->fill_surfaces) {
1232 const int body = separated_body_at(*layer, surface.expolygon.contour.points.front());
1233 fills[set][body >= 0 && ! body_meshes[body].indices.empty() ? size_t(body) : num_bodies] = true;
1234 }
1235 for (size_t set = 0; set < spacings.size(); ++ set) {
1236 octrees.sets[set].bodies.resize(num_bodies);
1237 for (size_t body = 0; body <= num_bodies; ++ body)
1238 if (fills[set][body])
1239 to_build.emplace_back(set, body);
1240 }
1241 } else
1242 for (size_t set = 0; set < spacings.size(); ++ set)
1243 to_build.emplace_back(set, num_bodies);
1244
1245 // and gather them.
1246 for (size_t i = 1; i < overhangs.size(); ++ i)
1247 append(overhangs.front(), std::move(overhangs[i]));
1248
1249 tbb::parallel_for(tbb::blocked_range<size_t>(0, to_build.size()), [&](const tbb::blocked_range<size_t> &range) {
1250 for (size_t i = range.begin(); i < range.end(); ++ i) {
1251 m_print->throw_if_canceled();
1252 const auto [set, body] = to_build[i];
1253 const bool object = body == num_bodies;
1254 (object ? octrees.sets[set].object : octrees.sets[set].bodies[body]) =
1255 build_octree(object ? mesh : body_meshes[body], object ? overhangs.front() : body_overhangs[body], spacings[set].first,
1256 spacings[set].second);
1257 }
1258 });
1259 return octrees;
1260}
indexed_triangle_set raw_indexed_triangle_set() const
Definition Model.cpp:1706
Transform3d trafo_sliced() const
Definition PrintObjectSlice.cpp:1735
std::vector< BoundingBox > m_separated_body_bboxes
Definition Print.hpp:708
Eigen::Quaterniond transform_to_octree()
Definition FillAdaptive.cpp:373
std::vector< double > adaptive_fill_line_spacing(const PrintObject &print_object)
Definition FillAdaptive.cpp:304
@ ipSupportCubic
Definition PrintConfig.hpp:131
static int separated_body_at(const Layer &layer, const Point &point)
Definition PrintObject.cpp:1115
static std::vector< indexed_triangle_set > split_mesh_by_body(const PrintObject &object, const indexed_triangle_set &mesh, size_t num_bodies)
Definition PrintObject.cpp:1135
void set(id object, const void *key, id value)
Definition MacIME.mm:24

References config(), Slic3r::ipSupportCubic, m_belt_global_z_offset, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::model_object(), prepare_adaptive_infill_data(), printing_region(), Slic3r::range(), and trafo_sliced().

Referenced by bridge_over_infill(), and prepare_adaptive_infill_data().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ prepare_adaptive_infill_fields()

AdaptiveInfillFields Slic3r::PrintObject::prepare_adaptive_infill_fields ( ) const
private
1275{
1276 AdaptiveInfillFields fields;
1277 std::array<bool, size_t(AdaptiveInfillMode::Count)> modes{};
1278 for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id)
1279 if (const PrintRegionConfig &config = this->printing_region(region_id).config();
1280 config.sparse_infill_density > 0 && config.sparse_infill_density < 100 && is_adaptive_infill_pattern(config.sparse_infill_pattern))
1281 modes[size_t(config.adaptive_infill_mode.value)] = true;
1282 modes[size_t(AdaptiveInfillMode::Disabled)] = false;
1283 if (std::find(modes.begin(), modes.end(), true) == modes.end() || m_layers.empty())
1284 return fields;
1285
1286 std::vector<AdaptiveInfillField::Slice> slices;
1287 slices.reserve(m_layers.size());
1288 BoundingBox bbox;
1289 for (const Layer *layer : m_layers) {
1290 slices.push_back({layer->bottom_z(), layer->print_z, &layer->lslices});
1291 bbox.merge(get_extents(layer->lslices));
1292 }
1293 if (!bbox.defined)
1294 return fields;
1295 for (size_t mode = 0; mode < modes.size(); ++mode) {
1296 if (!modes[mode])
1297 continue;
1298 // Without a field, the infill falls back to the regular pattern.
1299 auto field = std::make_unique<AdaptiveInfillField>(slices, bbox, AdaptiveInfillMode(mode), [this]() { m_print->throw_if_canceled(); });
1300 if (!field->empty())
1301 fields[mode] = std::move(field);
1302 }
1303 return fields;
1304}
bool is_adaptive_infill_pattern(InfillPattern pattern)
Definition PrintConfig.hpp:176
AdaptiveInfillMode
Definition PrintConfig.hpp:290
@ Disabled
Definition PrintConfig.hpp:291
@ Count
Definition PrintConfig.hpp:299
std::array< AdaptiveInfillFieldPtr, size_t(AdaptiveInfillMode::Count)> AdaptiveInfillFields
Definition AdaptiveInfillField.hpp:111

References config(), Slic3r::Count, Slic3r::BoundingBoxBase< PointType, APointsType >::defined, Slic3r::Disabled, Slic3r::get_extents(), Slic3r::is_adaptive_infill_pattern(), m_layers, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::m_print, Slic3r::BoundingBoxBase< PointType, APointsType >::merge(), num_printing_regions(), prepare_adaptive_infill_fields(), printing_region(), and Slic3r::PrintRegionConfig.

Referenced by bridge_over_infill(), and prepare_adaptive_infill_fields().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ prepare_infill()

void Slic3r::PrintObject::prepare_infill ( )
private
628{
629 if (! this->set_started(posPrepareInfill))
630 return;
631 m_print->set_status(25, L("Generating infill regions"));
632 if (m_typed_slices) {
633 // To improve robustness of detect_surfaces_type() when reslicing (working with typed slices), see GH issue #7442.
634 // The preceding step (perimeter generator) only modifies extra_perimeters and the extra perimeters are only used by discover_vertical_shells()
635 // with more than a single region. If this step does not use Surface::extra_perimeters or Surface::extra_perimeters is always zero, it is safe
636 // to reset to the untyped slices before re-runnning detect_surfaces_type().
637 for (Layer* layer : m_layers) {
638 layer->restore_untyped_slices_no_extra_perimeters();
639 m_print->throw_if_canceled();
640 }
641 }
642
643 // This will assign a type (top/bottom/internal) to $layerm->slices.
644 // Then the classifcation of $layerm->slices is transfered onto
645 // the $layerm->fill_surfaces by clipping $layerm->fill_surfaces
646 // by the cummulative area of the previous $layerm->fill_surfaces.
647 this->detect_surfaces_type();
648 m_print->throw_if_canceled();
649
650 // Decide what surfaces are to be filled.
651 // Here the stTop / stBottomBridge / stBottom infill is turned to just stInternal if zero top / bottom infill layers are configured.
652 // Also tiny stInternal surfaces are turned to stInternalSolid.
653 BOOST_LOG_TRIVIAL(info) << "Preparing fill surfaces..." << log_memory_info();
654 for (auto *layer : m_layers)
655 for (auto *region : layer->m_regions) {
656 region->prepare_fill_surfaces();
657 m_print->throw_if_canceled();
658 }
659
660
661 // Add solid fills to ensure the shell vertical thickness.
663 m_print->throw_if_canceled();
664
665 // Debugging output.
666#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
667 for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
668 for (const Layer *layer : m_layers) {
669 LayerRegion *layerm = layer->m_regions[region_id];
670 layerm->export_region_slices_to_svg_debug("3_discover_vertical_shells-final");
671 layerm->export_region_fill_surfaces_to_svg_debug("3_discover_vertical_shells-final");
672 } // for each layer
673 } // for each region
674#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
675
676
677 // Detect, which fill surfaces are near external layers.
678 // They will be split in internal and internal-solid surfaces.
679 // The purpose is to add a configurable number of solid layers to support the TOP surfaces
680 // and to add a configurable number of solid layers above the BOTTOM / BOTTOMBRIDGE surfaces
681 // to close these surfaces reliably.
682 //FIXME Vojtech: Is this a good place to add supporting infills below sloping perimeters?
683 // Orca: Brought this function call before the process_external_surfaces, to allow bridges over holes to expand more than
684 // one perimeter. Example of this is the bridge over the benchy lettering.
686 m_print->throw_if_canceled();
687
688#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
689 for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
690 for (const Layer *layer : m_layers) {
691 LayerRegion *layerm = layer->m_regions[region_id];
692 layerm->export_region_slices_to_svg_debug("5_discover_horizontal_shells-final");
693 layerm->export_region_fill_surfaces_to_svg_debug("5_discover_horizontal_shells-final");
694 } // for each layer
695 } // for each region
696#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
697
698 // this will detect bridges and reverse bridges
699 // and rearrange top/bottom/internal surfaces
700 // It produces enlarged overlapping bridging areas.
701 //
702 // 1) stBottomBridge / stBottom infill is grown by 3mm and clipped by the total infill area. Bridges are detected. The areas may overlap.
703 // 2) stTop is grown by 3mm and clipped by the grown bottom areas. The areas may overlap.
704 // 3) Clip the internal surfaces by the grown top/bottom surfaces.
705 // 4) Merge surfaces with the same style. This will mostly get rid of the overlaps.
706 //FIXME This does not likely merge surfaces, which are supported by a material with different colors, but same properties.
708 m_print->throw_if_canceled();
709
710 // Debugging output.
711#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
712 for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
713 for (const Layer *layer : m_layers) {
714 LayerRegion *layerm = layer->m_regions[region_id];
715 layerm->export_region_slices_to_svg_debug("7_process_external_surfaces-final");
716 layerm->export_region_fill_surfaces_to_svg_debug("7_process_external_surfaces-final");
717 } // for each layer
718 } // for each region
719#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
720
721 // Only active if config->infill_only_where_needed. This step trims the sparse infill,
722 // so it acts as an internal support. It maintains all other infill types intact.
723 // Here the internal surfaces and perimeters have to be supported by the sparse infill.
724 //FIXME The surfaces are supported by a sparse infill, but the sparse infill is only as large as the area to support.
725 // Likely the sparse infill will not be anchored correctly, so it will not work as intended.
726 // Also one wishes the perimeters to be supported by a full infill.
727 this->clip_fill_surfaces();
728 m_print->throw_if_canceled();
729
730#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
731 for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
732 for (const Layer *layer : m_layers) {
733 LayerRegion *layerm = layer->m_regions[region_id];
734 layerm->export_region_slices_to_svg_debug("8_clip_surfaces-final");
735 layerm->export_region_fill_surfaces_to_svg_debug("8_clip_surfaces-final");
736 } // for each layer
737 } // for each region
738#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
739
740 // Orca: precompute the object's 3D connected bodies for separated infills / per-model
741 // centering. Two islands belong to the same body when their slices overlap on adjacent
742 // layers; islands that only overlap in top-down projection but never touch (e.g. interleaved
743 // chain links) stay separate, matching "split to objects". Each layer island then records
744 // the full bounding box of its body, so its infill is centered on that body as if it were
745 // sliced alone. Compute this before bridges so anchors and extrusion share the same origin.
746 bool needs_separated_components = false;
747 for (size_t i = 0; i < this->num_printing_regions(); ++ i) {
748 const PrintRegionConfig &rc = this->printing_region(i).config();
749 if (rc.separated_infills || rc.center_of_surface_pattern == CenterOfSurfacePattern::Each_Model ||
750 (rc.sparse_infill_density > 0 && is_octree_infill_pattern(rc.sparse_infill_pattern))) {
751 needs_separated_components = true;
752 break;
753 }
754 }
755 // Orca: Fast path: the feature only changes anything when the object is made of more than one
756 // connected body. Detect that cheaply the same way as "Split to objects" — more than one
757 // model part, or a single part whose mesh is splittable (is_splittable() is cached). A single
758 // body already shares the object center, i.e. the default, so skip the connectivity pass.
759 if (needs_separated_components) {
760 int parts = 0;
761 const ModelVolume *first_part = nullptr;
762 for (const ModelVolume *v : this->model_object()->volumes)
763 if (v->is_model_part()) { ++ parts; first_part = v; }
764 if (parts <= 1 && ! (first_part != nullptr && first_part->is_splittable()))
765 needs_separated_components = false;
766 }
768 for (Layer *layer : m_layers)
769 layer->lslices_separated_component_ids.clear();
770 if (needs_separated_components) {
771 std::vector<const ExPolygons *> islands;
772 islands.reserve(m_layers.size());
773 for (const Layer *layer : m_layers)
774 islands.emplace_back(&layer->lslices);
775 size_t bodies = 0;
776 std::vector<std::vector<size_t>> ids = connected_bodies(islands, bodies, [this]() { m_print->throw_if_canceled(); });
777 // Orca: Merge the bounding boxes of the islands of each body.
778 m_separated_body_bboxes.assign(bodies, BoundingBox());
779 for (size_t i = 0; i < m_layers.size(); ++ i) {
780 Layer *layer = m_layers[i];
781 for (size_t a = 0; a < layer->lslices.size(); ++ a)
782 m_separated_body_bboxes[ids[i][a]].merge(layer->lslices_bboxes[a]);
783 layer->lslices_separated_component_ids = std::move(ids[i]);
784 }
785 }
786
787 // the following step needs to be done before combination because it may need
788 // to remove only half of the combined infill
789 this->bridge_over_infill();
790 m_print->throw_if_canceled();
791
792 // combine fill surfaces to honor the "infill every N layers" option
793 this->combine_infill();
794 m_print->throw_if_canceled();
795
796#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
797 for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
798 for (const Layer *layer : m_layers) {
799 LayerRegion *layerm = layer->m_regions[region_id];
800 layerm->export_region_slices_to_svg_debug("9_prepare_infill-final");
801 layerm->export_region_fill_surfaces_to_svg_debug("9_prepare_infill-final");
802 } // for each layer
803 } // for each region
804 for (const Layer *layer : m_layers) {
805 layer->export_region_slices_to_svg_debug("9_prepare_infill-final");
806 layer->export_region_fill_surfaces_to_svg_debug("9_prepare_infill-final");
807 } // for each layer
808#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
809
811}
void discover_vertical_shells()
Definition PrintObject.cpp:2507
void combine_infill()
Definition PrintObject.cpp:4962
void detect_surfaces_type()
Definition PrintObject.cpp:1941
void clip_fill_surfaces()
Definition PrintObject.cpp:4665
void process_external_surfaces()
Definition PrintObject.cpp:2423
void discover_horizontal_shells()
Definition PrintObject.cpp:4751
void bridge_over_infill()
Definition PrintObject.cpp:3088
void merge(std::vector< _EpicMesh > &srcs, _EpicMesh &dst)
Definition MeshBoolean.cpp:560
std::vector< std::vector< size_t > > connected_bodies(const std::vector< const ExPolygons * > &layers, size_t &count, const std::function< void()> &throw_if_canceled)
Definition ConnectedBodies.cpp:27
@ Each_Model
Definition PrintConfig.hpp:100
bool is_octree_infill_pattern(InfillPattern pattern)
Definition PrintConfig.hpp:174

References bridge_over_infill(), clip_fill_surfaces(), combine_infill(), Slic3r::connected_bodies(), detect_surfaces_type(), discover_horizontal_shells(), discover_vertical_shells(), Slic3r::Each_Model, Slic3r::LayerRegion::export_region_fill_surfaces_to_svg_debug(), Slic3r::LayerRegion::export_region_slices_to_svg_debug(), Slic3r::is_octree_infill_pattern(), Slic3r::ModelVolume::is_splittable(), L, Slic3r::log_memory_info(), Slic3r::Layer::lslices, Slic3r::Layer::lslices_bboxes, Slic3r::Layer::lslices_separated_component_ids, m_layers, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::m_print, m_separated_body_bboxes, m_typed_slices, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::model_object(), num_printing_regions(), Slic3r::posPrepareInfill, prepare_infill(), printing_region(), Slic3r::PrintRegionConfig, process_external_surfaces(), Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::set_done(), and Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::set_started().

Referenced by infill(), prepare_infill(), and Slic3r::Print::process().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ prepare_lightning_infill_data()

FillLightning::GeneratorPtr Slic3r::PrintObject::prepare_lightning_infill_data ( )
private
1263{
1264 bool has_lightning_infill = false;
1265 for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id)
1266 if (const PrintRegionConfig& config = this->printing_region(region_id).config(); config.sparse_infill_density > 0 && config.sparse_infill_pattern == ipLightning) {
1267 has_lightning_infill = true;
1268 break;
1269 }
1270
1271 return has_lightning_infill ? FillLightning::build_generator(std::as_const(*this), [this]() -> void { this->throw_if_canceled(); }) : FillLightning::GeneratorPtr();
1272}
GeneratorPtr build_generator(const PrintObject &print_object, const std::function< void()> &throw_on_cancel_callback)
Definition FillLightning.cpp:53
std::unique_ptr< Generator, GeneratorDeleter > GeneratorPtr
Definition FillLightning.hpp:22

References Slic3r::FillLightning::build_generator(), config(), Slic3r::ipLightning, num_printing_regions(), prepare_lightning_infill_data(), printing_region(), Slic3r::PrintRegionConfig, and Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::throw_if_canceled().

Referenced by bridge_over_infill(), and prepare_lightning_infill_data().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ print() [1/2]

◆ print() [2/2]

const Print * Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, COUNT >::print ( ) const
inlineinherited
638{ return m_print; }

References m_print, and PrintType.

◆ printing_extruders()

std::vector< unsigned int > Slic3r::PrintObject::printing_extruders ( ) const
4584{
4585 std::vector<unsigned int> extruders = object_extruders();
4586 if (has_support_material()) {
4587 const size_t filament_count = print()->config().filament_diameter.size();
4588 auto append_fixed_filament = [&](const int filament_id) {
4589 if (filament_id > 0)
4590 extruders.emplace_back(std::min<size_t>(size_t(filament_id - 1), filament_count > 0 ? filament_count - 1 : 0));
4591 };
4592 append_fixed_filament(config().support_filament.value);
4593 append_fixed_filament(config().support_interface_filament.value);
4594 }
4595 if (extruders.empty())
4596 extruders.emplace_back(0);
4597 sort_remove_duplicates(extruders);
4598 return extruders;
4599}

References config(), has_support_material(), object_extruders(), Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::print(), printing_extruders(), and Slic3r::sort_remove_duplicates().

Referenced by Slic3r::colliding_bed_exclusion(), detect_extruder_geometric_unprintables(), and printing_extruders().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ printing_region()

◆ process_external_surfaces()

void Slic3r::PrintObject::process_external_surfaces ( )
private
2424{
2425 BOOST_LOG_TRIVIAL(info) << "Processing external surfaces..." << log_memory_info();
2426
2427 // Cached surfaces covered by some extrusion, defining regions, over which the from the surfaces one layer higher are allowed to expand.
2428 std::vector<Polygons> surfaces_covered;
2429 // Is there any printing region, that has zero infill? If so, then we don't want the expansion to be performed over the complete voids, but only
2430 // over voids, which are supported by the layer below.
2431 bool has_voids = false;
2432 for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id)
2433 if (this->printing_region(region_id).config().sparse_infill_density == 0) {
2434 has_voids = true;
2435 break;
2436 }
2437 if (has_voids && m_layers.size() > 1) {
2438 // All but stInternal fill surfaces will get expanded and possibly trimmed.
2439 std::vector<unsigned char> layer_expansions_and_voids(m_layers.size(), false);
2440 for (size_t layer_idx = 1; layer_idx < m_layers.size(); ++ layer_idx) {
2441 const Layer *layer = m_layers[layer_idx];
2442 bool expansions = false;
2443 bool voids = false;
2444 for (const LayerRegion *layerm : layer->regions()) {
2445 for (const Surface &surface : layerm->fill_surfaces.surfaces) {
2446 if (surface.surface_type == stInternal)
2447 voids = true;
2448 else
2449 expansions = true;
2450 if (voids && expansions) {
2451 layer_expansions_and_voids[layer_idx] = true;
2452 goto end;
2453 }
2454 }
2455 }
2456 end:;
2457 }
2458 BOOST_LOG_TRIVIAL(debug) << "Collecting surfaces covered with extrusions in parallel - start";
2459 surfaces_covered.resize(m_layers.size() - 1, Polygons());
2460 auto unsupported_width = - float(scale_(0.3 * EXTERNAL_INFILL_MARGIN));
2461 tbb::parallel_for(
2462 tbb::blocked_range<size_t>(0, m_layers.size() - 1),
2463 [this, &surfaces_covered, &layer_expansions_and_voids, unsupported_width](const tbb::blocked_range<size_t>& range) {
2464 for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx)
2465 if (layer_expansions_and_voids[layer_idx + 1]) {
2466 // Layer above is partially filled with solid infill (top, bottom, bridging...),
2467 // while some sparse inill regions are empty (0% infill).
2468 m_print->throw_if_canceled();
2469 Polygons voids;
2470 for (const LayerRegion *layerm : m_layers[layer_idx]->regions()) {
2471 if (layerm->region().config().sparse_infill_density.value == 0.)
2472 for (const Surface &surface : layerm->fill_surfaces.surfaces)
2473 // Shrink the holes, let the layer above expand slightly inside the unsupported areas.
2474 polygons_append(voids, offset(surface.expolygon, unsupported_width));
2475 }
2476 surfaces_covered[layer_idx] = diff(m_layers[layer_idx]->lslices, voids);
2477 }
2478 }
2479 );
2480 m_print->throw_if_canceled();
2481 BOOST_LOG_TRIVIAL(debug) << "Collecting surfaces covered with extrusions in parallel - end";
2482 }
2483
2484 BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces in parallel - start";
2485 // The regions of a layer do not see each other here, and a layer cut through a fine relief takes far longer than the
2486 // others, so the regions run next to each other instead of one after another, each still over all layers.
2487 tbb::parallel_for(size_t(0), this->num_printing_regions(), [this, &surfaces_covered](size_t region_id) {
2488 tbb::parallel_for(
2489 tbb::blocked_range<size_t>(0, m_layers.size()),
2490 [this, &surfaces_covered, region_id](const tbb::blocked_range<size_t>& range) {
2491 for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
2492 m_print->throw_if_canceled();
2493 // BOOST_LOG_TRIVIAL(trace) << "Processing external surface, layer" << m_layers[layer_idx]->print_z;
2494 m_layers[layer_idx]->get_region(int(region_id))->process_external_surfaces(
2495 // lower layer
2496 (layer_idx == 0) ? nullptr : m_layers[layer_idx - 1],
2497 // lower layer polygons with density > 0%
2498 (layer_idx == 0 || surfaces_covered.empty() || surfaces_covered[layer_idx - 1].empty()) ? nullptr : &surfaces_covered[layer_idx - 1]);
2499 }
2500 }
2501 );
2502 });
2503 m_print->throw_if_canceled();
2504 BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces in parallel - end";
2505}
static constexpr double EXTERNAL_INFILL_MARGIN
Definition libslic3r.h:98

References config(), EXTERNAL_INFILL_MARGIN, Slic3r::log_memory_info(), m_layers, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::m_print, num_printing_regions(), printing_region(), process_external_surfaces(), Slic3r::range(), Slic3r::Layer::regions(), scale_, Slic3r::sparse_infill_density(), Slic3r::stInternal, and Slic3r::Surface::surface_type.

Referenced by prepare_infill(), and process_external_surfaces().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ project_and_append_custom_facets()

void Slic3r::PrintObject::project_and_append_custom_facets ( bool seam,
EnforcerBlockerType type,
std::vector< Polygons > & expolys,
std::vector< std::pair< Vec3f, Vec3f > > * vertical_points = nullptr ) const
5493{
5494 const Transform3d object_trafo = this->trafo_sliced();
5495 for (const ModelVolume* mv : this->model_object()->volumes)
5496 if (mv->is_model_part()) {
5497 const indexed_triangle_set custom_facets = seam
5498 ? mv->seam_facets.get_facets_strict(*mv, type)
5499 : mv->supported_facets.get_facets_strict(*mv, type);
5500 if (! custom_facets.indices.empty()) {
5501 if (seam)
5502 project_triangles_to_slabs(this->layers(), custom_facets,
5503 (object_trafo * mv->get_matrix()).cast<float>(),
5504 seam, out);
5505 else {
5506 std::vector<Polygons> projected;
5507 // Support blockers or enforcers. Project downward facing painted areas upwards to their respective slicing plane.
5508 slice_mesh_slabs(custom_facets, zs_from_layers(this->layers()), object_trafo * mv->get_matrix(), nullptr, &projected, vertical_points, [](){});
5509 // Merge these projections with the output, layer by layer.
5510 assert(! projected.empty());
5511 assert(out.empty() || out.size() == projected.size());
5512 if (out.empty())
5513 out = std::move(projected);
5514 else
5515 for (size_t i = 0; i < out.size(); ++ i)
5516 append(out[i], std::move(projected[i]));
5517 }
5518 }
5519 }
5520}
std::vector< float > zs_from_layers(const LayerContainer &layers)
Definition Layer.hpp:357
static void project_triangles_to_slabs(ConstLayerPtrsAdaptor layers, const indexed_triangle_set &custom_facets, const Transform3f &tr, bool seam, std::vector< Polygons > &out)
Definition PrintObject.cpp:5305
void slice_mesh_slabs(const indexed_triangle_set &mesh, const std::vector< float > &zs, const Transform3d &trafo, std::vector< Polygons > *out_top, std::vector< Polygons > *out_bottom, std::vector< std::pair< Vec3f, Vec3f > > *vertical_points, std::function< void()> throw_on_cancel)
Definition TriangleMeshSlicer.cpp:2197

References Slic3r::append(), layers(), Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::model_object(), project_and_append_custom_facets(), Slic3r::project_triangles_to_slabs(), Slic3r::slice_mesh_slabs(), trafo_sliced(), and Slic3r::zs_from_layers().

Referenced by project_and_append_custom_facets().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ remove_bridges_from_contacts()

template<typename PolysType>
template void Slic3r::PrintObject::remove_bridges_from_contacts< Polygons > ( const Layer * lower_layer,
const Layer * current_layer,
float extrusion_width,
PolysType * overhang_regions,
float max_bridge_length = scale_(10),
bool break_bridge = false )
static
5158{
5159 // Extrusion width accounts for the roundings of the extrudates.
5160 // It is the maximum widh of the extrudate.
5161 float fw = extrusion_width;
5162 Lines overhang_perimeters = to_lines(*overhang_regions);
5163 auto layer_regions = current_layer->regions();
5164 Polygons lower_layer_polygons = to_polygons(lower_layer->lslices);
5165 const PrintObjectConfig& object_config = current_layer->object()->config();
5166
5167 Polygons all_bridges;
5168 for (LayerRegion* layerm : layer_regions)
5169 {
5170 Polygons bridges;
5171 // Surface supporting this layer, expanded by 0.5 * nozzle_diameter, as we consider this kind of overhang to be sufficiently supported.
5172 Polygons lower_grown_slices = offset(lower_layer_polygons,
5173 //FIXME to mimic the decision in the perimeter generator, we should use half the external perimeter width.
5175 Polylines overhang_perimeters = diff_pl(layerm->perimeters.as_polylines(), lower_grown_slices);
5176 // only consider straight overhangs
5177 // only consider overhangs having endpoints inside layer's slices
5178 // convert bridging polylines into polygons by inflating them with their thickness
5179 // since we're dealing with bridges, we can't assume width is larger than spacing,
5180 // so we take the largest value and also apply safety offset to be ensure no gaps
5181 // are left in between
5182 Flow bridge_flow = layerm->bridging_flow(frPerimeter, object_config.thick_bridges);
5183 float w = float(std::max(bridge_flow.scaled_width(), bridge_flow.scaled_spacing()));
5184 for (Polyline& polyline : overhang_perimeters)
5185 if (polyline.is_straight()) {
5186 // This is a bridge
5187 polyline.extend_start(fw);
5188 polyline.extend_end(fw);
5189 // Is the straight perimeter segment supported at both sides?
5190 Point pts[2] = { polyline.first_point(), polyline.last_point() };
5191 bool supported[2] = { false, false };
5192 for (size_t i = 0; i < lower_layer->lslices.size() && !(supported[0] && supported[1]); ++i)
5193 for (int j = 0; j < 2; ++j)
5194 if (!supported[j] && lower_layer->lslices_bboxes[i].contains(pts[j]) && lower_layer->lslices[i].contains(pts[j]))
5195 supported[j] = true;
5196 if (supported[0] && supported[1]) {
5197 Polylines lines;
5198 if (polyline.length() > max_bridge_length + 10) {
5199 if (break_bridge) {
5200 // equally divide the polyline
5201 float len = polyline.length() / ceil(polyline.length() / max_bridge_length);
5202 lines = polyline.equally_spaced_lines(len);
5203 for (auto& line : lines) {
5204 if (line.is_valid())
5205 line.clip_start(fw);
5206 if (line.is_valid())
5207 line.clip_end(fw);
5208 }
5209 }
5210 }
5211 else
5212 lines.push_back(polyline);
5213 // Offset a polyline into a thick line.
5214 polygons_append(bridges, offset(lines, 0.5f * w + 10.f));
5215 }
5216 }
5217 bridges = union_(bridges);
5218
5219 // remove the entire bridges and only support the unsupported edges
5220 //FIXME the brided regions are already collected as layerm->bridged. Use it?
5221 for (const Surface& surface : layerm->fill_surfaces.surfaces)
5222 if (surface.surface_type == stBottomBridge && surface.bridge_angle != -1) {
5223 auto bbox = get_extents(surface.expolygon);
5224 auto bbox_size = bbox.size();
5225 if (bbox_size[0] < max_bridge_length && bbox_size[1] < max_bridge_length)
5226 polygons_append(bridges, surface.expolygon);
5227 else {
5228 if (break_bridge) {
5230 coord_t x0 = bbox.min.x();
5231 coord_t x1 = bbox.max.x();
5232 coord_t y0 = bbox.min.y();
5233 coord_t y1 = bbox.max.y();
5234 const int grid_lw = int(w/2); // grid line width
5235
5236 Vec2f bridge_direction{ cos(surface.bridge_angle),sin(surface.bridge_angle) };
5237 if (fabs(bridge_direction(0)) > fabs(bridge_direction(1)))
5238 { // cut bridge along x-axis if bridge direction is aligned to x-axis more than to y-axis
5239 // Note: surface.bridge_angle may be pi, so we can't compare it to 0 & pi/2.
5240 int step = bbox_size(0) / ceil(bbox_size(0) / max_bridge_length);
5241 for (int x = x0 + step; x < x1; x += step) {
5242 Polygon poly;
5243 poly.points = {Point(x - grid_lw, y0), Point(x + grid_lw, y0), Point(x + grid_lw, y1), Point(x - grid_lw, y1)};
5244 holes.emplace_back(poly);
5245 }
5246 } else {
5247 int step = bbox_size(1) / ceil(bbox_size(1) / max_bridge_length);
5248 for (int y = y0 + step; y < y1; y += step) {
5249 Polygon poly;
5250 poly.points = {Point(x0, y - grid_lw), Point(x0, y + grid_lw), Point(x1, y + grid_lw), Point(x1, y - grid_lw)};
5251 holes.emplace_back(poly);
5252 }
5253 }
5254 auto expoly = diff_ex(surface.expolygon, holes);
5255 polygons_append(bridges, expoly);
5256 }
5257 }
5258 }
5259 //FIXME add the gap filled areas. Extrude the gaps with a bridge flow?
5260 // Remove the unsupported ends of the bridges from the bridged areas.
5261 //FIXME add supports at regular intervals to support long bridges!
5262 bridges = diff(bridges,
5263 // Offset unsupported edges into polygons.
5264 offset(layerm->unsupported_bridge_edges, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS));
5265 append(all_bridges, bridges);
5266 }
5267 if (typeid(overhang_regions) == typeid(ExPolygons*)) {
5268 *(ExPolygons*)overhang_regions = diff_ex(*overhang_regions, all_bridges, ApplySafetyOffset::Yes);
5269 }
5270 else if (typeid(overhang_regions) == typeid(Polygons*)) {
5271 *(Polygons*)overhang_regions = diff(*overhang_regions, all_bridges, ApplySafetyOffset::Yes);
5272 }
5273}
#define SUPPORT_SURFACES_OFFSET_PARAMETERS
Definition PrintObject.cpp:5148
#define SUPPORT_MATERIAL_MARGIN
Definition PrintObject.cpp:5149
Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip)
Definition ClipperUtils.cpp:1099
std::vector< Polyline > Polylines
Definition Polyline.hpp:22
Eigen::Matrix< float, 2, 1, Eigen::DontAlign > Vec2f
Definition Point.hpp:64
bridge_flow((ConfigOptionFloatOrPercent, bridge_line_width))((ConfigOptionFloat
void step(Coord &crd, const Dir d, const long n=1)
Definition MarchingSquares.hpp:213

References Slic3r::append(), Slic3r::Surface::bridge_angle, Slic3r::bridge_flow(), config(), Slic3r::diff(), Slic3r::diff_ex(), Slic3r::diff_pl(), Slic3r::Surface::expolygon, Slic3r::frPerimeter, Slic3r::get_extents(), Slic3r::holes(), Slic3r::Layer::lslices, Slic3r::Layer::lslices_bboxes, Slic3r::Layer::object(), Slic3r::offset(), Slic3r::polygons_append(), Slic3r::PrintObjectConfig, Slic3r::Layer::regions(), remove_bridges_from_contacts(), scale_, Slic3r::stBottomBridge, SUPPORT_MATERIAL_MARGIN, SUPPORT_SURFACES_OFFSET_PARAMETERS, Slic3r::Surface::surface_type, Slic3r::to_lines(), Slic3r::to_polygons(), Slic3r::union_(), and Slic3r::Yes.

Referenced by remove_bridges_from_contacts().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ separated_body_bboxes()

const std::vector< BoundingBox > & Slic3r::PrintObject::separated_body_bboxes ( ) const
inline

◆ serialize()

template<class Archive>
void Slic3r::ObjectBase::serialize ( Archive & ar)
inlineprivateinherited
108{ ar(m_id); }

References m_id.

◆ set_belt_brim()

void Slic3r::PrintObject::set_belt_brim ( std::vector< ExtrusionEntityCollection > && by_layer,
std::vector< ExPolygons > && areas,
std::vector< BeltBrimBand > && prologue )
1420{
1421 m_belt_brim_by_layer = std::move(by_layer);
1422 m_belt_brim_areas_by_layer = std::move(areas);
1423 m_belt_brim_prologue = std::move(prologue);
1424}

References m_belt_brim_areas_by_layer, m_belt_brim_by_layer, m_belt_brim_prologue, and set_belt_brim().

Referenced by set_belt_brim().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_done()

References m_print, m_state, Slic3r::PrintStateBase::NON_CRITICAL, Slic3r::PrintObjectBase::state_mutex(), Slic3r::PrintObjectBase::status_update_warnings(), and Slic3r::PrintObjectBaseWithState< PrintType, PrintObjectStepEnum, COUNT >::throw_if_canceled().

Referenced by Slic3r::PrintObject::detect_overhangs_for_lift(), Slic3r::PrintObject::generate_support_material(), Slic3r::PrintObject::infill(), Slic3r::PrintObject::ironing(), Slic3r::PrintObject::prepare_infill(), and Slic3r::PrintObject::simplify_extrusion_path().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_id()

void Slic3r::PrintObject::set_id ( size_t id)
inline
552{ m_id = id; }

References Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::id(), and m_id.

Here is the call graph for this function:

◆ set_instances()

PrintBase::ApplyStatus Slic3r::PrintObject::set_instances ( PrintInstances && instances)
private
176{
177 for (PrintInstance &i : instances)
178 // Add the center offset, which will be subtracted from the mesh when slicing.
179 i.shift += m_center_offset;
180 // Invalidate and set copies.
182 bool equal_length = instances.size() == m_instances.size();
183 bool equal = equal_length && std::equal(instances.begin(), instances.end(), m_instances.begin(),
184 [](const PrintInstance& lhs, const PrintInstance& rhs) { return lhs.model_instance == rhs.model_instance && lhs.shift == rhs.shift; });
185 if (! equal) {
187 if (m_print->invalidate_steps({ psSkirtBrim, psGCodeExport }) ||
188 (! equal_length && m_print->invalidate_step(psWipeTower)))
190 m_instances = std::move(instances);
191 for (PrintInstance &i : m_instances)
192 i.print_object = this;
193 }
194 return status;
195}
ApplyStatus
Definition PrintBase.hpp:410
@ APPLY_STATUS_UNCHANGED
Definition PrintBase.hpp:412
@ APPLY_STATUS_CHANGED
Definition PrintBase.hpp:415
@ APPLY_STATUS_INVALIDATED
Definition PrintBase.hpp:417

References Slic3r::PrintBase::APPLY_STATUS_CHANGED, Slic3r::PrintBase::APPLY_STATUS_INVALIDATED, Slic3r::PrintBase::APPLY_STATUS_UNCHANGED, instances(), m_center_offset, m_instances, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::m_print, and Slic3r::psWipeTower.

Referenced by PrintObject().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_invalid_id()

void Slic3r::ObjectBase::set_invalid_id ( )
inlineprotectedinherited
90{ m_id = 0; }

References m_id.

Referenced by Slic3r::CutObjectBase::invalidate().

Here is the caller graph for this function:

◆ set_new_unique_id()

void Slic3r::ObjectBase::set_new_unique_id ( )
inlineprotectedinherited
89{ m_id = generate_new_id(); }
static ObjectID generate_new_id()
Definition ObjectID.hpp:100

References generate_new_id(), and m_id.

Referenced by Slic3r::ModelVolume::ModelVolume(), Slic3r::Model::assign_new_unique_ids_recursive(), Slic3r::ModelVolume::assign_new_unique_ids_recursive(), assign_new_unique_ids_recursive(), Slic3r::CutObjectBase::init(), Slic3r::ModelMaterial::set_new_unique_id(), Slic3r::ModelObject::set_new_unique_id(), and Slic3r::ModelVolume::set_new_unique_id().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_shared_object()

void Slic3r::PrintObject::set_shared_object ( PrintObject * object)
3132{
3133 m_shared_object = object;
3134 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, found shared object from %2%")%this%m_shared_object;
3135}

References PrintObject(), and m_shared_object.

Referenced by Slic3r::Print::process().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_started()

bool Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, COUNT >::set_started ( PrintObjectStep step)
inlineprotectedinherited

◆ shared_regions()

const PrintObjectRegions * Slic3r::PrintObject::shared_regions ( ) const
throw ( )
inline
504{ return m_shared_regions; }

References m_shared_regions.

Referenced by Slic3r::FillLightning::Generator::Generator(), and Slic3r::FillLightning::Generator::Generator().

Here is the caller graph for this function:

◆ simplify_extrusion_path()

void Slic3r::PrintObject::simplify_extrusion_path ( )
private
1059{
1060 if (this->set_started(posSimplifyPath)) {
1061 m_print->set_status(75, L("Optimizing toolpath"));
1062 BOOST_LOG_TRIVIAL(debug) << "Simplify extrusion path of object in parallel - start";
1063 //BBS: infill and walls
1064 tbb::parallel_for(
1065 tbb::blocked_range<size_t>(0, m_layers.size()),
1066 [this](const tbb::blocked_range<size_t>& range) {
1067 for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
1068 m_print->throw_if_canceled();
1069 m_layers[layer_idx]->simplify_wall_extrusion_path();
1070 }
1071 }
1072 );
1073 m_print->throw_if_canceled();
1074 BOOST_LOG_TRIVIAL(debug) << "Simplify wall extrusion path of object in parallel - end";
1076 }
1077
1078 if (this->set_started(posSimplifyInfill)) {
1079 m_print->set_status(75, L("Optimizing toolpath"));
1080 BOOST_LOG_TRIVIAL(debug) << "Simplify infill extrusion path of object in parallel - start";
1081 //BBS: infills
1082 tbb::parallel_for(
1083 tbb::blocked_range<size_t>(0, m_layers.size()),
1084 [this](const tbb::blocked_range<size_t>& range) {
1085 for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
1086 m_print->throw_if_canceled();
1087 m_layers[layer_idx]->simplify_infill_extrusion_path();
1088 }
1089 }
1090 );
1091 m_print->throw_if_canceled();
1092 BOOST_LOG_TRIVIAL(debug) << "Simplify infill extrusion path of object in parallel - end";
1094 }
1095
1097 m_print->set_status(75, L("Optimizing toolpath"));
1098 BOOST_LOG_TRIVIAL(debug) << "Simplify extrusion path of support in parallel - start";
1099 tbb::parallel_for(
1100 tbb::blocked_range<size_t>(0, m_support_layers.size()),
1101 [this](const tbb::blocked_range<size_t>& range) {
1102 for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
1103 m_print->throw_if_canceled();
1104 m_support_layers[layer_idx]->simplify_support_extrusion_path();
1105 }
1106 }
1107 );
1108 m_print->throw_if_canceled();
1109 BOOST_LOG_TRIVIAL(debug) << "Simplify extrusion path of support in parallel - end";
1111 }
1112}

References L, m_layers, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::m_print, Slic3r::posSimplifyPath, Slic3r::range(), Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::set_done(), Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::set_started(), and simplify_extrusion_path().

Referenced by simplify_extrusion_path().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ size()

const Vec3crd & Slic3r::PrintObject::size ( ) const
inline
371{ return m_size; }

References m_size.

Referenced by belt_drop_unclaimed_fills(), bridge_over_infill(), discover_vertical_shells(), invalidate_state_by_config_options(), and object_extruders().

Here is the caller graph for this function:

◆ slice()

void Slic3r::PrintObject::slice ( )
885{
886 if (! this->set_started(posSlice))
887 return;
888 //BBS: add flag to reload scene for shell rendering
889 m_print->set_status(5, L("Slicing mesh"), PrintBase::SlicingStatus::RELOAD_SCENE);
890 std::vector<coordf_t> layer_height_profile;
891 this->update_layer_height_profile(*this->model_object(), m_slicing_params, layer_height_profile);
892 m_print->throw_if_canceled();
893 m_typed_slices = false;
894 // The belt state below is only written while belt mode is on (and the min-Z
895 // lift only when there is a rotation or remap). Start every slice from zero,
896 // or a project switched from a belt printer to a normal one, or whose tilt
897 // axis was set to None, keeps the previous offsets: the adaptive infill octree
898 // and the organic support layers (PrintObject.cpp) would still be shifted by
899 // them.
900 m_belt_min_z = 0.;
902 this->clear_layers();
903 m_layers = new_layers(this, generate_object_layers(m_slicing_params, layer_height_profile, m_config.precise_z_height.value));
904 this->slice_volumes();
905 m_print->throw_if_canceled();
906
907 // Belt floor Z-shift: where is the belt surface in final slicer space?
908 //
909 // The belt surface is the model's Z=0 plane. After the belt rotation and the
910 // Z-shift it is the plane Z_belt = shear_factor * from_axis + z_shift_val in
911 // slicer space, with z_shift_val = max(0, -m_belt_min_z), the lift that starts
912 // the slicing frame at the belt below the footprint.
913 if (std::abs(m_slicing_params.belt_floor_shear_factor) > EPSILON) {
914 double z_shift_val = (m_belt_min_z < 0.) ? -m_belt_min_z : 0.;
915 // The belt surface is at Z=0 in centered slicer space and bb.min.z() is
916 // already folded into m_belt_min_z.
917 m_slicing_params.belt_floor_z_shift = z_shift_val;
918 }
919
920 int firstLayerReplacedBy = 0;
921
922#if 0
923 // Fix the model.
924 //FIXME is this the right place to do? It is done repeateadly at the UI and now here at the backend.
925 std::string warning = fix_slicing_errors(this, m_layers, [this](){ m_print->throw_if_canceled(); }, firstLayerReplacedBy);
926 m_print->throw_if_canceled();
927 //BBS: send warning message to slicing callback
928 // This warning is inaccurate, because the empty layers may have been replaced, or the model has supports.
929 //if (!warning.empty()) {
930 // BOOST_LOG_TRIVIAL(info) << warning;
931 // this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, warning, PrintStateBase::SlicingReplaceInitEmptyLayers);
932 //}
933#endif
934
935 // Detect and process holes that should be converted to polyholes
937
938 // BBS: the actual first layer slices stored in layers are re-sorted by volume group and will be used to generate brim
939 groupingVolumesForBrim(this, m_layers, firstLayerReplacedBy);
940
941 // Update bounding boxes, back up raw slices of complex models.
942 tbb::parallel_for(
943 tbb::blocked_range<size_t>(0, m_layers.size()),
944 [this](const tbb::blocked_range<size_t>& range) {
945 for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
946 m_print->throw_if_canceled();
947 Layer &layer = *m_layers[layer_idx];
948 layer.lslices_bboxes.clear();
949 layer.lslices_bboxes.reserve(layer.lslices.size());
950 for (const ExPolygon &expoly : layer.lslices)
951 layer.lslices_bboxes.emplace_back(get_extents(expoly));
952 layer.backup_untyped_slices();
953 }
954 });
955 if (m_layers.empty())
956 throw Slic3r::SlicingError(L("No layers were detected. You might want to repair the model or check its size or thickness and retry.\n"));
957
958 // Belt printer: offset all layer Z values so objects at different positions
959 // along the belt print at different heights on the tilted belt. This is a
960 // post-slicing adjustment: the sliced geometry is the same, only the output Z
961 // coordinates change.
962 {
963 const auto &pcfg = this->print()->config();
964 BOOST_LOG_TRIVIAL(trace) << "Belt global check: belt_printer=" << pcfg.belt_printer.value
965 << " belt_slice_rotation=" << int(pcfg.belt_slice_rotation.value)
966 << " object=" << this->model_object()->name;
967 if (pcfg.belt_printer.value) {
968
969 Point inst_shift = this->instances().empty() ? Point(0, 0)
970 : this->instances().front().shift - this->center_offset();
971 BOOST_LOG_TRIVIAL(trace) << "Belt global: object " << this->model_object()->name
972 << " instances=" << this->instances().size()
973 << " shift=(" << unscale<double>(inst_shift.x()) << ", " << unscale<double>(inst_shift.y()) << ")";
974
975 // Per-object Z-shift compensation, applied regardless of global mode.
976 //
977 // BeltSliceStrategy::apply_preslice_transforms lifts the mesh by max(0, -m_belt_min_z)
978 // so the slicer can slice with slicer_z >= 0. BeltBackTransform inverts
979 // build_forward_transform() which DOES NOT include this per-object
980 // Z-shift (it's not known until vertex scan time). Result: G-code
981 // coords emerge offset by the un-undone Z-shift — the inverse rotation
982 // couples slicer_z back into both machine_y and machine_z. Compensating
983 // layer.print_z by belt_z_shift here makes the back-transform produce
984 // correct machine-frame coordinates whether or not a global mode is active.
985 // The compensation must mirror the Z-shift actually applied, which
986 // is max(0, -m_belt_min_z): when the transformed mesh starts ABOVE
987 // slicer Z=0 (m_belt_min_z > 0 — possible for counter-rotated or
988 // asymmetric geometry whose centered-frame minimum lands positive)
989 // no lift was applied, and an unclamped m_belt_min_z here would
990 // leak straight into the layer Z values, floating the whole object
991 // off the belt by exactly that amount.
992 double belt_z_shift = std::min(m_belt_min_z, 0.); // the belt surface is Z=0 in centered slicer space
993 double global_z_offset = belt_z_shift;
994
995 // Centering correction: trafo_centered pretranslates by
996 // -m_center_offset.{x,y}. Under the belt forward transform, the
997 // Y component of that pretranslate couples into slicer-Z (shear:
998 // tan*c.y, rotation: sin*c.y). BeltBackTransform inverts the
999 // rotation/shear but doesn't undo centering, so this Z component
1000 // leaks into machine output as a position offset whenever
1001 // m_center_offset != 0. When a user moves a volume within an
1002 // assembly such that the combined bbox center shifts, this shows
1003 // up as a small Z translation in the print. Compensate by adding
1004 // the Z component of the centering through the forward transform.
1005 {
1009 0.);
1010 double centering_z_corr = (T_fwd.linear() * c_off).z();
1011 global_z_offset += centering_z_corr;
1012 }
1013
1014 {
1015 // Global pre-slice mode: compute full correction c = (T.linear() - I) * d
1016 // where T is the belt forward transform and d is the bed position, so
1017 // objects at different bed positions print at different machine Z values
1018 // along the inclined belt.
1020 Vec3d d(unscale<double>(inst_shift.x()), unscale<double>(inst_shift.y()), 0.);
1021 Vec3d c = T.linear() * d - d;
1022 global_z_offset += c.z();
1023
1024 BOOST_LOG_TRIVIAL(trace) << "Belt preslice_global: correction=("
1025 << c.x() << ", " << c.y() << ", " << c.z() << ")"
1026 << " belt_z_shift=" << belt_z_shift << " (m_belt_min_z=" << m_belt_min_z << ")";
1027 }
1028
1029 BOOST_LOG_TRIVIAL(trace) << "Belt global: z_offset=" << global_z_offset
1030 << " (" << this->print()->objects().size() << " objects on the plate)";
1031 m_belt_global_z_offset = global_z_offset;
1032 if (std::abs(global_z_offset) > EPSILON) {
1033 for (Layer *layer : m_layers)
1034 layer->print_z += global_z_offset;
1035 // Keep belt floor clipping in sync with the shifted print_z
1036 // values — the support generator sees globally-offset object
1037 // layer print_z, so belt_floor_z_shift must match.
1038 m_slicing_params.belt_floor_z_shift += global_z_offset;
1039 }
1040 if (!m_layers.empty()) {
1041 BOOST_LOG_TRIVIAL(trace) << "Belt global: first_layer_z=" << m_layers.front()->print_z
1042 << " last_layer_z=" << m_layers.back()->print_z
1043 << " num_layers=" << m_layers.size()
1044 << " center_offset=(" << unscale<double>(m_center_offset.x())
1045 << ", " << unscale<double>(m_center_offset.y()) << ")";
1046 }
1047
1048 // Cache the final patched belt_floor_z_shift so a later support-only
1049 // invalidation can rebuild m_slicing_params without losing this exact
1050 // (vertex-scan-derived) value. update_slicing_parameters() will
1051 // restore it after create_from_config() seeds the bbox approximation.
1052 m_belt_floor_z_shift_cached = m_slicing_params.belt_floor_z_shift;
1054 }
1055 }
1056
1057 // BBS
1058 this->set_done(posSlice);
1059}
static Transform3d build_forward_transform(const PrintConfig &config)
Definition BeltTransform.cpp:38
static bool update_layer_height_profile(const ModelObject &model_object, const SlicingParameters &slicing_parameters, std::vector< coordf_t > &layer_height_profile)
Definition PrintObject.cpp:4601
void slice_volumes()
Definition PrintObjectSlice.cpp:1338
double m_belt_min_z
Definition Print.hpp:725
void _transform_hole_to_polyholes()
Definition PrintObject.cpp:238
ConstPrintObjectPtrsAdaptor objects() const
Definition Print.hpp:1134
LayerPtrs new_layers(PrintObject *print_object, const std::vector< coordf_t > &object_layers)
Definition PrintObjectSlice.cpp:82
std::vector< coordf_t > generate_object_layers(const SlicingParameters &slicing_params, const std::vector< coordf_t > &layer_height_profile, bool is_precise_z_height)
Definition Slicing.cpp:816
void groupingVolumesForBrim(PrintObject *object, LayerPtrs &layers, int firstLayerReplacedBy)
Definition PrintObjectSlice.cpp:865
@ RELOAD_SCENE
Definition PrintBase.hpp:464

References _transform_hole_to_polyholes(), clear_layers(), EPSILON, Slic3r::generate_object_layers(), Slic3r::groupingVolumesForBrim(), L, m_belt_global_z_offset, m_belt_min_z, m_config, m_layers, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::m_print, m_slicing_params, m_typed_slices, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::model_object(), Slic3r::new_layers(), Slic3r::posSlice, Slic3r::range(), Slic3r::PrintBase::SlicingStatus::RELOAD_SCENE, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::set_started(), slice(), slice_volumes(), and update_layer_height_profile().

Referenced by generate_support_preview(), get_first_layer_bbox(), make_perimeters(), Slic3r::Print::process(), and slice().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ slice_single_volume_regions()

std::vector< ExPolygons > Slic3r::PrintObject::slice_single_volume_regions ( const ModelVolume * volume) const
1796{
1797 if (volume == nullptr)
1798 return {};
1799 // Match the existing slicing heights and the frame the layers were sliced in (belt
1800 // pre-slice transforms included) without flattening holes.
1801 const std::vector<float> zs = zs_from_layers(this->layers());
1802 MeshSlicingParamsEx params;
1803 params.trafo = this->trafo_sliced();
1804 const Print *print = this->print();
1805 return slice_volume(*volume, zs, params, [print]() { print->throw_if_canceled(); });
1806}
friend class Print
Definition Print.hpp:556
static std::vector< ExPolygons > slice_volume(const ModelVolume &volume, const std::vector< float > &zs, const MeshSlicingParamsEx &params, const std::function< void()> &throw_on_cancel_callback)
Definition PrintObjectSlice.cpp:109

References layers(), Print, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::print(), slice_single_volume_regions(), Slic3r::slice_volume(), Slic3r::MeshSlicingParams::trafo, trafo_sliced(), and Slic3r::zs_from_layers().

Referenced by slice_single_volume_regions().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ slice_support_blockers()

std::vector< Polygons > Slic3r::PrintObject::slice_support_blockers ( ) const
inline
std::vector< Polygons > slice_support_volumes(const ModelVolumeType model_volume_type) const
Definition PrintObjectSlice.cpp:1742
@ SUPPORT_BLOCKER
Definition Model.hpp:360

References slice_support_volumes(), and Slic3r::SUPPORT_BLOCKER.

Referenced by Slic3r::TreeSupport3D::TreeModelVolumes::TreeModelVolumes().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ slice_support_enforcers()

std::vector< Polygons > Slic3r::PrintObject::slice_support_enforcers ( ) const
inline
@ SUPPORT_ENFORCER
Definition Model.hpp:361

References slice_support_volumes(), and Slic3r::SUPPORT_ENFORCER.

Here is the call graph for this function:

◆ slice_support_volumes()

std::vector< Polygons > Slic3r::PrintObject::slice_support_volumes ( const ModelVolumeType model_volume_type) const
1743{
1744 auto it_volume = this->model_object()->volumes.begin();
1745 auto it_volume_end = this->model_object()->volumes.end();
1746 for (; it_volume != it_volume_end && (*it_volume)->type() != model_volume_type; ++ it_volume) ;
1747 std::vector<Polygons> slices;
1748 if (it_volume != it_volume_end) {
1749 // Found at least a single support volume of model_volume_type.
1750 std::vector<float> zs = zs_from_layers(this->layers());
1751 std::vector<char> merge_layers;
1752 bool merge = false;
1753 const Print *print = this->print();
1754 auto throw_on_cancel_callback = std::function<void()>([print](){ print->throw_if_canceled(); });
1755 MeshSlicingParamsEx params;
1756 params.trafo = this->trafo_sliced();
1757 for (; it_volume != it_volume_end; ++ it_volume)
1758 if ((*it_volume)->type() == model_volume_type) {
1759 std::vector<ExPolygons> slices2 = slice_volume(*(*it_volume), zs, params, throw_on_cancel_callback);
1760 if (slices.empty()) {
1761 slices.reserve(slices2.size());
1762 for (ExPolygons &src : slices2)
1763 slices.emplace_back(to_polygons(std::move(src)));
1764 } else if (!slices2.empty()) {
1765 if (merge_layers.empty())
1766 merge_layers.assign(zs.size(), false);
1767 for (size_t i = 0; i < zs.size(); ++ i) {
1768 if (slices[i].empty())
1769 slices[i] = to_polygons(std::move(slices2[i]));
1770 else if (! slices2[i].empty()) {
1771 append(slices[i], to_polygons(std::move(slices2[i])));
1772 merge_layers[i] = true;
1773 merge = true;
1774 }
1775 }
1776 }
1777 }
1778 if (merge) {
1779 std::vector<Polygons*> to_merge;
1780 to_merge.reserve(zs.size());
1781 for (size_t i = 0; i < zs.size(); ++ i)
1782 if (merge_layers[i])
1783 to_merge.emplace_back(&slices[i]);
1784 tbb::parallel_for(
1785 tbb::blocked_range<size_t>(0, to_merge.size()),
1786 [&to_merge](const tbb::blocked_range<size_t> &range) {
1787 for (size_t i = range.begin(); i < range.end(); ++ i)
1788 *to_merge[i] = union_(*to_merge[i]);
1789 });
1790 }
1791 }
1792 return slices;
1793}
ModelVolumePtrs volumes
Definition Model.hpp:393
bool empty(const BoundingBoxBase< PointType, PointsType > &bb)
Definition BoundingBox.hpp:273

References Slic3r::append(), Slic3r::empty(), layers(), Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::model_object(), Print, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::print(), Slic3r::range(), slice_support_volumes(), Slic3r::slice_volume(), Slic3r::to_polygons(), Slic3r::MeshSlicingParams::trafo, trafo_sliced(), and Slic3r::zs_from_layers().

Referenced by slice_support_blockers(), slice_support_enforcers(), and slice_support_volumes().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ slice_volumes()

void Slic3r::PrintObject::slice_volumes ( )
private
1339{
1340 BOOST_LOG_TRIVIAL(info) << "Slicing volumes..." << log_memory_info();
1341 const Print *print = this->print();
1342 const auto throw_on_cancel_callback = std::function<void()>([print](){ print->throw_if_canceled(); });
1343
1344 // Clear old LayerRegions, allocate for new PrintRegions.
1345 for (Layer* layer : m_layers) {
1346 //BBS: should delete all LayerRegionPtr to avoid memory leak
1347 while (!layer->m_regions.empty()) {
1348 if (layer->m_regions.back())
1349 delete layer->m_regions.back();
1350 layer->m_regions.pop_back();
1351 }
1352 layer->m_regions.reserve(m_shared_regions->all_regions.size());
1353 for (const std::unique_ptr<PrintRegion> &pr : m_shared_regions->all_regions)
1354 layer->m_regions.emplace_back(new LayerRegion(layer, pr.get()));
1355 }
1356
1357 std::vector<float> slice_zs = zs_from_layers(m_layers);
1358 std::vector<VolumeSlices> objSliceByVolume;
1359 if (!slice_zs.empty()) {
1360 objSliceByVolume = slice_volumes_inner(
1361 print->config(), this->config(), this->trafo_centered(),
1362 this->model_object()->volumes, m_shared_regions->layer_ranges, slice_zs, throw_on_cancel_callback,
1363 &m_belt_min_z);
1364 }
1365
1366 //BBS: "model_part" volumes are grouded according to their connections
1367 //const auto scaled_resolution = scaled<double>(print->config().resolution.value);
1368 //firstLayerObjSliceByVolume = findPartVolumes(objSliceByVolume, this->model_object()->volumes);
1369 //groupingVolumes(objSliceByVolumeParts, firstLayerObjSliceByGroups, scaled_resolution);
1370 //applyNegtiveVolumes(this->model_object()->volumes, objSliceByVolume, firstLayerObjSliceByGroups, scaled_resolution);
1371 firstLayerObjSliceByVolume = objSliceByVolume;
1372
1373 std::vector<std::vector<ExPolygons>> region_slices =
1374 slices_to_regions(print->config(), *this, this->model_object()->volumes, *m_shared_regions, slice_zs,
1375 std::move(objSliceByVolume), PrintObject::clip_multipart_objects, throw_on_cancel_callback);
1376
1377 for (size_t region_id = 0; region_id < region_slices.size(); ++ region_id) {
1378 std::vector<ExPolygons> &by_layer = region_slices[region_id];
1379 for (size_t layer_id = 0; layer_id < by_layer.size(); ++ layer_id)
1380 m_layers[layer_id]->regions()[region_id]->slices.append(std::move(by_layer[layer_id]), stInternal);
1381 }
1382 region_slices.clear();
1383
1384 BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - removing top empty layers";
1385 while (! m_layers.empty()) {
1386 const Layer *layer = m_layers.back();
1387 if (! layer->empty())
1388 break;
1389 delete layer;
1390 m_layers.pop_back();
1391 }
1392 if (! m_layers.empty())
1393 m_layers.back()->upper_layer = nullptr;
1394 m_print->throw_if_canceled();
1395
1396 this->apply_conical_overhang();
1397
1398 // Is any ModelVolume multi-material painted?
1399 if (const auto& volumes = this->model_object()->volumes;
1400 m_print->config().filament_diameter.size() > 1 && // BBS
1401 std::find_if(volumes.begin(), volumes.end(), [](const ModelVolume* v) { return !v->mmu_segmentation_facets.empty(); }) != volumes.end()) {
1402
1403 // If XY Size compensation is also enabled, notify the user that XY Size compensation
1404 // would not be used because the object is multi-material painted.
1405 if (m_config.xy_hole_compensation.value != 0.f || m_config.xy_contour_compensation.value != 0.f) {
1408 L("An object's XY size compensation will not be used because it is also color-painted.\nXY Size "
1409 "compensation cannot be combined with color-painting."));
1410 BOOST_LOG_TRIVIAL(info) << "xy compensation will not work for object " << this->model_object()->name << " for multi filament.";
1411 }
1412
1413 BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - MMU segmentation";
1414 apply_mm_segmentation(*this, [print]() { print->throw_if_canceled(); });
1415 }
1416
1417 // Is any ModelVolume fuzzy skin painted?
1418 if (this->model_object()->is_fuzzy_skin_painted()) {
1419 // If XY Size compensation is also enabled, notify the user that XY Size compensation
1420 // would not be used because the object has custom fuzzy skin painted.
1421 if (m_config.xy_hole_compensation.value != 0.f || m_config.xy_contour_compensation.value != 0.f) {
1424 _u8L("An object has enabled XY Size compensation which will not be used because it is also fuzzy skin painted.\nXY Size "
1425 "compensation cannot be combined with fuzzy skin painting.") +
1426 "\n" + (_u8L("Object name")) + ": " + this->model_object()->name);
1427 }
1428
1429 BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - Fuzzy skin segmentation";
1430 apply_fuzzy_skin_segmentation(*this, [print]() { print->throw_if_canceled(); });
1431 }
1432
1433 InterlockingGenerator::generate_interlocking_structure(this, [print]() { print->throw_if_canceled(); });
1434 m_print->throw_if_canceled();
1435
1436 BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - make_slices in parallel - begin";
1437 {
1438 // Compensation value, scaled. Only applying the negative scaling here, as the positive scaling has already been applied during slicing.
1439 const size_t num_extruders = print->config().filament_diameter.size();
1440 const auto xy_hole_scaled = (num_extruders > 1 && this->is_mm_painted()) ? scaled<float>(0.f) : scaled<float>(m_config.xy_hole_compensation.value);
1441 const auto xy_contour_scaled = (num_extruders > 1 && this->is_mm_painted()) ? scaled<float>(0.f) : scaled<float>(m_config.xy_contour_compensation.value);
1442 const float elephant_foot_compensation_scaled = (m_config.raft_layers == 0) ?
1443 // Only enable Elephant foot compensation if printing directly on the print bed.
1444 float(scale_(m_config.elefant_foot_compensation.value)) :
1445 0.f;
1446 // Uncompensated slices for the layers in case the Elephant foot compensation is applied.
1447 std::vector<ExPolygons> lslices_elfoot_uncompensated;
1448 lslices_elfoot_uncompensated.resize(elephant_foot_compensation_scaled > 0 ? std::min(m_config.elefant_foot_compensation_layers.value, (int)m_layers.size()) : 0);
1449 //BBS: this part has been changed a lot to support seperated contour and hole size compensation
1450 tbb::parallel_for(
1451 tbb::blocked_range<size_t>(0, m_layers.size()),
1452 [this, xy_hole_scaled, xy_contour_scaled, elephant_foot_compensation_scaled, &lslices_elfoot_uncompensated](const tbb::blocked_range<size_t>& range) {
1453 for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
1454 m_print->throw_if_canceled();
1455 Layer *layer = m_layers[layer_id];
1456 // Apply size compensation and perform clipping of multi-part objects.
1457 float elfoot = elephant_foot_compensation_scaled > 0 && layer_id < m_config.elefant_foot_compensation_layers.value ?
1458 elephant_foot_compensation_scaled - (elephant_foot_compensation_scaled / m_config.elefant_foot_compensation_layers.value) * layer_id :
1459 0.f;
1460 if (layer->m_regions.size() == 1) {
1461 // Optimized version for a single region layer.
1462 // Single region, growing or shrinking.
1463 LayerRegion *layerm = layer->m_regions.front();
1464 if (elfoot > 0) {
1465 // Apply the elephant foot compensation and store the original layer slices without the Elephant foot compensation applied.
1466 ExPolygons expolygons_to_compensate = to_expolygons(std::move(layerm->slices.surfaces));
1467 if (xy_contour_scaled > 0 || xy_hole_scaled > 0) {
1468 expolygons_to_compensate = _shrink_contour_holes(std::max(0.f, xy_contour_scaled),
1469 std::max(0.f, xy_hole_scaled),
1470 expolygons_to_compensate);
1471 }
1472 if (xy_contour_scaled < 0 || xy_hole_scaled < 0) {
1473 expolygons_to_compensate = _shrink_contour_holes(std::min(0.f, xy_contour_scaled),
1474 std::min(0.f, xy_hole_scaled),
1475 expolygons_to_compensate);
1476 }
1477 lslices_elfoot_uncompensated[layer_id] = expolygons_to_compensate;
1478 layerm->slices.set(
1479 union_ex(
1480 Slic3r::elephant_foot_compensation(expolygons_to_compensate,
1481 layerm->flow(frExternalPerimeter), unscale<double>(elfoot))),
1482 stInternal);
1483 } else {
1484 // Apply the XY contour and hole size compensation.
1485 if (xy_contour_scaled != 0.0f || xy_hole_scaled != 0.0f) {
1486 ExPolygons expolygons = to_expolygons(std::move(layerm->slices.surfaces));
1487 if (xy_contour_scaled > 0 || xy_hole_scaled > 0) {
1488 expolygons = _shrink_contour_holes(std::max(0.f, xy_contour_scaled),
1489 std::max(0.f, xy_hole_scaled),
1490 expolygons);
1491 }
1492 if (xy_contour_scaled < 0 || xy_hole_scaled < 0) {
1493 expolygons = _shrink_contour_holes(std::min(0.f, xy_contour_scaled),
1494 std::min(0.f, xy_hole_scaled),
1495 expolygons);
1496 }
1497 layerm->slices.set(std::move(expolygons), stInternal);
1498 }
1499 }
1500 } else {
1501 float max_growth = std::max(xy_hole_scaled, xy_contour_scaled);
1502 float min_growth = std::min(xy_hole_scaled, xy_contour_scaled);
1503 ExPolygons merged_poly_for_holes_growing;
1504 if (max_growth > 0) {
1505 //BBS: merge polygons because region can cut "holes".
1506 //Then, cut them to give them again later to their region
1507 merged_poly_for_holes_growing = layer->merged(float(SCALED_EPSILON));
1508 merged_poly_for_holes_growing = _shrink_contour_holes(std::max(0.f, xy_contour_scaled),
1509 std::max(0.f, xy_hole_scaled),
1510 union_ex(merged_poly_for_holes_growing));
1511
1512 // BBS: clipping regions, priority is given to the first regions.
1513 Polygons processed;
1514 for (size_t region_id = 0; region_id < layer->regions().size(); ++region_id) {
1515 ExPolygons slices = to_expolygons(std::move(layer->m_regions[region_id]->slices.surfaces));
1516 if (max_growth > 0.f) {
1517 slices = intersection_ex(offset_ex(slices, max_growth), merged_poly_for_holes_growing);
1518 }
1519
1520 //BBS: Trim by the slices of already processed regions.
1521 if (region_id > 0)
1522 slices = diff_ex(to_polygons(std::move(slices)), processed);
1523 if (region_id + 1 < layer->regions().size())
1524 // Collect the already processed regions to trim the to be processed regions.
1525 polygons_append(processed, slices);
1526 layer->m_regions[region_id]->slices.set(std::move(slices), stInternal);
1527 }
1528 }
1529 if (min_growth < 0.f || elfoot > 0.f) {
1530 // Apply the negative XY compensation. (the ones that is <0)
1531 ExPolygons trimming;
1532 const float eps = float(scale_(m_config.slice_closing_radius.value) * 1.5);
1533 if (elfoot > 0.f) {
1534 ExPolygons expolygons_to_compensate = offset_ex(layer->merged(eps), -eps);
1535 lslices_elfoot_uncompensated[layer_id] = expolygons_to_compensate;
1536 trimming = Slic3r::elephant_foot_compensation(expolygons_to_compensate,
1537 layer->m_regions.front()->flow(frExternalPerimeter), unscale<double>(elfoot));
1538 } else {
1539 trimming = layer->merged(float(SCALED_EPSILON));
1540 }
1541 if (min_growth < 0.0f)
1542 trimming = _shrink_contour_holes(std::min(0.f, xy_contour_scaled),
1543 std::min(0.f, xy_hole_scaled),
1544 trimming);
1545 //BBS: trim surfaces
1546 for (size_t region_id = 0; region_id < layer->regions().size(); ++region_id) {
1547 // BBS: split trimming result by region
1548 ExPolygons contour_exp = to_expolygons(std::move(layer->regions()[region_id]->slices.surfaces));
1549
1550 layer->regions()[region_id]->slices.set(intersection_ex(contour_exp, to_polygons(trimming)), stInternal);
1551 }
1552 }
1553 }
1554 // Merge all regions' slices to get islands, chain them by a shortest path.
1555 layer->make_slices();
1556 }
1557 });
1558 if (elephant_foot_compensation_scaled > 0.f && ! m_layers.empty()) {
1559 // The Elephant foot has been compensated, therefore the elefant_foot_compensation_layers layer's lslices are shrank with the Elephant foot compensation value.
1560 // Store the uncompensated value there.
1561 assert(m_layers.front()->id() == 0);
1562 //BBS: sort the lslices_elfoot_uncompensated according to shortest path before saving
1563 //Otherwise the travel of the layer layer would be mess.
1564 for (int i = 0; i < lslices_elfoot_uncompensated.size(); i++) {
1565 ExPolygons &expolygons_uncompensated = lslices_elfoot_uncompensated[i];
1566 Points ordering_points;
1567 ordering_points.reserve(expolygons_uncompensated.size());
1568 for (const ExPolygon &ex : expolygons_uncompensated)
1569 ordering_points.push_back(ex.contour.first_point());
1570 std::vector<Points::size_type> order = chain_points(ordering_points);
1571 ExPolygons lslices_sorted;
1572 lslices_sorted.reserve(expolygons_uncompensated.size());
1573 for (size_t i : order)
1574 lslices_sorted.emplace_back(std::move(expolygons_uncompensated[i]));
1575 m_layers[i]->lslices = std::move(lslices_sorted);
1576 }
1577 }
1578 }
1579
1580 m_print->throw_if_canceled();
1581 BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - make_slices in parallel - end";
1582}
static void generate_interlocking_structure(PrintObject *print_object, const std::function< void()> &throw_on_cancel)
Definition InterlockingGenerator.cpp:44
bool is_fuzzy_skin_painted() const
Definition Print.hpp:515
void apply_conical_overhang()
Definition PrintObjectSlice.cpp:1584
static bool clip_multipart_objects
Definition Print.hpp:749
@ CRITICAL
Definition PrintBase.hpp:63
static void apply_mm_segmentation(PrintObject &print_object, ThrowOnCancel throw_on_cancel)
Definition PrintObjectSlice.cpp:1062
static std::vector< std::vector< ExPolygons > > slices_to_regions(const PrintConfig &print_config, const PrintObject &print_object, ModelVolumePtrs model_volumes, const PrintObjectRegions &print_object_regions, const std::vector< float > &zs, std::vector< VolumeSlices > &&volume_slices, const bool clip_multipart_objects, const std::function< void()> &throw_on_cancel_callback)
Definition PrintObjectSlice.cpp:306
std::vector< size_t > chain_points(const Points &points, const Point *start_near)
Definition ShortestPath.cpp:1565
static std::vector< VolumeSlices > slice_volumes_inner(const PrintConfig &print_config, const PrintObjectConfig &print_object_config, const Transform3d &object_trafo, ModelVolumePtrs model_volumes, const std::vector< PrintObjectRegions::LayerRangeRegions > &layer_ranges, const std::vector< float > &zs, const std::function< void()> &throw_on_cancel_callback, double *out_belt_min_z=nullptr)
Definition PrintObjectSlice.cpp:180
void apply_fuzzy_skin_segmentation(PrintObject &print_object, ThrowOnCancel throw_on_cancel)
Definition PrintObjectSlice.cpp:1221
#define _u8L(s)
Definition I18N.hpp:11

References _u8L, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::active_step_add_warning(), apply_conical_overhang(), Slic3r::apply_fuzzy_skin_segmentation(), Slic3r::apply_mm_segmentation(), Slic3r::chain_points(), clip_multipart_objects, Slic3r::PrintStateBase::CRITICAL, Slic3r::Layer::empty(), firstLayerObjSliceByVolume, Slic3r::InterlockingGenerator::generate_interlocking_structure(), is_fuzzy_skin_painted(), is_mm_painted(), L, Slic3r::log_memory_info(), m_belt_min_z, m_config, m_layers, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::m_print, m_shared_regions, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::model_object(), Print, Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::print(), Slic3r::range(), scale_, Slic3r::scaled(), slice_volumes(), Slic3r::slice_volumes_inner(), Slic3r::slices_to_regions(), Slic3r::stInternal, and Slic3r::zs_from_layers().

Referenced by slice(), and slice_volumes().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ slicing_parameters() [1/2]

◆ slicing_parameters() [2/2]

SlicingParameters Slic3r::PrintObject::slicing_parameters ( const DynamicPrintConfig & full_config,
const ModelObject & model_object,
float object_max_z,
const Vec3d & object_shrinkage_compensation,
std::vector< int > variant_index = std::vector<int>() )
static
4502{
4503 PrintConfig print_config;
4504 PrintObjectConfig object_config;
4505 PrintRegionConfig default_region_config;
4506 print_config.apply(full_config, true);
4507 object_config.apply(full_config, true);
4508 default_region_config.apply(full_config, true);
4509 // BBS
4510 size_t filament_extruders = print_config.filament_diameter.size();
4511 object_config = object_config_from_model_object(object_config, model_object, filament_extruders, variant_index);
4512
4513 std::vector<unsigned int> object_extruders;
4514 for (const ModelVolume* model_volume : model_object.volumes)
4515 if (model_volume->is_model_part()) {
4517 print_config,
4518 region_config_from_model_volume(default_region_config, nullptr, *model_volume, filament_extruders, variant_index),
4519 object_config.brim_type != btNoBrim && object_config.brim_width > 0.,
4521 for (const std::pair<const t_layer_height_range, ModelConfig> &range_and_config : model_object.layer_config_ranges)
4522 if (range_and_config.second.has("outer_wall_filament_id") ||
4523 range_and_config.second.has("inner_wall_filament_id") ||
4524 range_and_config.second.has("sparse_infill_filament_id") ||
4525 range_and_config.second.has("internal_solid_filament_id") ||
4526 range_and_config.second.has("top_surface_filament_id") ||
4527 range_and_config.second.has("bottom_surface_filament_id"))
4529 print_config,
4530 region_config_from_model_volume(default_region_config, &range_and_config.second.get(), *model_volume, filament_extruders, variant_index),
4531 object_config.brim_type != btNoBrim && object_config.brim_width > 0.,
4533 }
4535 //FIXME add painting extruders
4536
4537 BeltTransformPipeline::BeltFloorParams belt_floor;
4538 if (object_max_z <= 0.f) {
4539 BoundingBoxf3 bb = model_object.raw_bounding_box();
4540 object_max_z = (float)bb.size().z();
4541 if (print_config.belt_printer.value) {
4542 // Z as placed on the bed, XY around the instance origin: the belt floor
4543 // depends on where the box sits in Z (see update_slicing_parameters()).
4544 if (! model_object.instances.empty()) {
4545 bb = model_object.instance_bounding_box(0, false);
4546 const Vec3d off = model_object.instances.front()->get_offset();
4547 bb.translate(-off.x(), -off.y(), 0.);
4548 }
4549 auto hr = BeltTransformPipeline::compute_belt_height_and_floor(print_config, bb, object_max_z);
4550 object_max_z = (float)hr.object_height;
4551 belt_floor = hr.floor_params;
4552 }
4553 }
4554 SlicingParameters params = SlicingParameters::create_from_config(print_config, object_config, object_max_z, object_extruders, object_shrinkage_compensation);
4555 params.belt_floor_shear_factor = belt_floor.shear_factor;
4556 params.belt_floor_from_axis = belt_floor.from_axis;
4557 params.belt_floor_z_shift = belt_floor.z_shift;
4558 return params;
4559}
static BeltHeightResult compute_belt_height_and_floor(const PrintConfig &config, const BoundingBoxf3 &bbox, double original_height)
Definition BeltTransform.cpp:136
static PrintObjectConfig object_config_from_model_object(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders, std::vector< int > &variant_index)
Definition PrintObject.cpp:4289
void collect_object_printing_extruders(const Print &print, std::vector< unsigned int > &object_extruders) const
Definition PrintRegion.cpp:103
PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_or_parent_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders, std::vector< int > &variant_index)
Definition PrintObject.cpp:4395
static SlicingParameters create_from_config(const PrintConfig &print_config, const PrintObjectConfig &object_config, coordf_t object_height, const std::vector< unsigned int > &object_extruders, const Vec3d &object_shrinkage_compensation)
Definition Slicing.cpp:71

References Slic3r::SlicingParameters::belt_floor_from_axis, Slic3r::SlicingParameters::belt_floor_shear_factor, Slic3r::SlicingParameters::belt_floor_z_shift, Slic3r::btNoBrim, Slic3r::PrintRegion::collect_object_printing_extruders(), Slic3r::BeltTransformPipeline::compute_belt_height_and_floor(), Slic3r::SlicingParameters::create_from_config(), Slic3r::BeltTransformPipeline::BeltFloorParams::from_axis, Slic3r::ModelVolume::is_model_part(), Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::model_object(), object_config_from_model_object(), object_extruders(), Slic3r::PrintObjectConfig, Slic3r::PrintRegionConfig, Slic3r::region_config_from_model_volume(), Slic3r::BeltTransformPipeline::BeltFloorParams::shear_factor, Slic3r::BoundingBox3Base< PointType >::size(), slicing_parameters(), Slic3r::sort_remove_duplicates(), Slic3r::BoundingBox3Base< PointType >::translate(), and Slic3r::BeltTransformPipeline::BeltFloorParams::z_shift.

Here is the call graph for this function:

◆ state_mutex()

std::mutex & Slic3r::PrintObjectBase::state_mutex ( PrintBase * print)
staticprotectedinherited

◆ status_update_warnings()

void Slic3r::PrintObjectBase::status_update_warnings ( PrintBase * print,
int step,
PrintStateBase::WarningLevel warning_level,
const std::string & message,
PrintStateBase::SlicingNotificationType message_id = PrintStateBase::SlicingDefaultNotification )
protectedinherited
167{
168 print->status_update_warnings(step, warning_level, message, this, message_id);
169}

References Slic3r::PrintBase::status_update_warnings().

Referenced by Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::active_step_add_warning(), and Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::set_done().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ step_state_with_timestamp()

PrintStateBase::StateWithTimeStamp Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, COUNT >::step_state_with_timestamp ( PrintObjectStep step) const
inlineinherited
642{ return m_state.state_with_timestamp(step, PrintObjectBase::state_mutex(m_print)); }

References m_print, m_state, and Slic3r::PrintObjectBase::state_mutex().

Here is the call graph for this function:

◆ step_state_with_warnings()

PrintStateBase::StateWithWarnings Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, COUNT >::step_state_with_warnings ( PrintObjectStep step) const
inlineinherited
643{ return m_state.state_with_warnings(step, PrintObjectBase::state_mutex(m_print)); }

References m_print, m_state, and Slic3r::PrintObjectBase::state_mutex().

Here is the call graph for this function:

◆ support_layer_count()

size_t Slic3r::PrintObject::support_layer_count ( ) const
inline
480{ return m_support_layers.size(); }

References m_support_layers.

Referenced by total_layer_count().

Here is the caller graph for this function:

◆ support_layers() [1/2]

SupportLayerPtrs & Slic3r::PrintObject::support_layers ( )
inline
390{ return m_support_layers; }

References m_support_layers.

◆ support_layers() [2/2]

ConstSupportLayerPtrsAdaptor Slic3r::PrintObject::support_layers ( ) const
inline
375{ return ConstSupportLayerPtrsAdaptor(&m_support_layers); }

References m_support_layers.

Referenced by Slic3r::GCode::_do_export(), libvgcode::convert_brim_skirt_to_vertices(), Slic3r::getAllLayersExtrusionPathsFromObject(), Slic3r::make_brim(), and Slic3r::outer_inner_brim_area().

Here is the caller graph for this function:

◆ throw_if_canceled()

void Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, COUNT >::throw_if_canceled ( )
inlineprotectedinherited
684{ if (m_print->canceled()) throw CanceledException(); }

References m_print.

Referenced by Slic3r::PrintObject::_generate_support_material(), and Slic3r::PrintObject::prepare_lightning_infill_data().

Here is the caller graph for this function:

◆ timestamp()

virtual Timestamp Slic3r::ObjectBase::timestamp ( ) const
inlinevirtualinherited

Reimplemented in Slic3r::ModelConfigObject, and Slic3r::ObjectWithTimestamp.

74{ return 0; }

◆ total_layer_count()

size_t Slic3r::PrintObject::total_layer_count ( ) const
inline
453{ return this->layer_count() + this->support_layer_count(); }
size_t support_layer_count() const
Definition Print.hpp:480

References layer_count(), and support_layer_count().

Here is the call graph for this function:

◆ trafo()

const Transform3d & Slic3r::PrintObject::trafo ( ) const
inline
376{ return m_trafo; }

References m_trafo.

Referenced by PrintObject(), Slic3r::group_fills(), Slic3r::LayerRegion::make_perimeters(), Print, Slic3r::Print::process(), Slic3r::LayerRegion::process_external_surfaces(), trafo_centered(), trafo_sliced(), and update_slicing_parameters().

Here is the caller graph for this function:

◆ trafo_centered()

Transform3d Slic3r::PrintObject::trafo_centered ( ) const
inline
379 { Transform3d t = this->trafo(); t.pretranslate(Vec3d(- unscale<double>(m_center_offset.x()), - unscale<double>(m_center_offset.y()), 0)); return t; }

References m_center_offset, trafo(), and Slic3r::unscale().

Referenced by trafo_sliced(), and update_slicing_parameters().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ trafo_sliced()

Transform3d Slic3r::PrintObject::trafo_sliced ( ) const
1736{
1739 return trafo;
1740}
static void apply_preslice_transforms(Transform3d &trafo, const PrintConfig &config, const ModelVolumePtrs &model_volumes, double *out_belt_min_z=nullptr)
Definition BeltSliceStrategy.cpp:12
Transform3d trafo_centered() const
Definition Print.hpp:378

References Slic3r::BeltSliceStrategy::apply_preslice_transforms(), config(), Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::model_object(), Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::print(), trafo(), trafo_centered(), and trafo_sliced().

Referenced by Slic3r::SeamPlacerImpl::compute_global_occlusion(), Slic3r::SeamPlacerImpl::gather_enforcers_blockers(), prepare_adaptive_infill_data(), project_and_append_custom_facets(), Slic3r::segmentation_by_painting(), Slic3r::segmentation_top_and_bottom_layers(), slice_single_volume_regions(), slice_support_volumes(), and trafo_sliced().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ update_layer_height_profile()

bool Slic3r::PrintObject::update_layer_height_profile ( const ModelObject & model_object,
const SlicingParameters & slicing_parameters,
std::vector< coordf_t > & layer_height_profile )
static
4602{
4603 bool updated = false;
4604
4605 if (layer_height_profile.empty()) {
4606 // use the constructor because the assignement is crashing on ASAN OsX
4607 layer_height_profile = std::vector<coordf_t>(model_object.layer_height_profile.get());
4608// layer_height_profile = model_object.layer_height_profile;
4609 // The layer height returned is sampled with high density for the UI layer height painting
4610 // and smoothing tool to work.
4611 updated = true;
4612 }
4613
4614 // Verify the layer_height_profile.
4615 if (!layer_height_profile.empty() &&
4616 // Must not be of even length.
4617 ((layer_height_profile.size() & 1) != 0 ||
4618 // Last entry must be at the top of the object.
4619 std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_parameters.object_print_z_uncompensated_max + slicing_parameters.object_print_z_min) > 1e-3))
4620 layer_height_profile.clear();
4621
4622 if (layer_height_profile.empty() || layer_height_profile[1] != slicing_parameters.first_object_layer_height) {
4623 //layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes);
4624 layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_config_ranges);
4625 // The layer height profile is already compressed.
4626 updated = true;
4627 }
4628
4629 return updated;
4630}
const SlicingParameters & slicing_parameters() const
Definition Print.hpp:496
std::vector< coordf_t > layer_height_profile_from_ranges(const SlicingParameters &slicing_params, const t_layer_config_ranges &layer_config_ranges)
Definition Slicing.cpp:243

References Slic3r::layer_height_profile_from_ranges(), Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::model_object(), slicing_parameters(), and update_layer_height_profile().

Referenced by Slic3r::GUI::GLCanvas3D::LayersEditing::adjust_layer_height_profile(), Slic3r::GUI::GLCanvas3D::LayersEditing::generate_layer_height_texture(), slice(), update_layer_height_profile(), and Slic3r::Print::validate().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ update_slicing_parameters()

void Slic3r::PrintObject::update_slicing_parameters ( )
private
4464{
4465 // Orca: updated function call for XYZ shrinkage compensation
4466 if (!m_slicing_params.valid) {
4467 coordf_t object_height = this->model_object()->max_z();
4468 BeltTransformPipeline::BeltFloorParams belt_floor;
4469 const auto &pcfg = this->print()->config();
4470 if (pcfg.belt_printer.value) {
4471 // The box of the mesh in the frame it is sliced in: XY centred and Z as
4472 // placed on the bed (trafo_centered()). raw_bounding_box() has the
4473 // instance's Z offset removed, and the belt floor is not invariant to a
4474 // Z shift (a point's z and the floor under it move in opposite
4475 // directions under the rotation), so an offset box under-estimates the
4476 // height by twice the shift and the layers stop part way up the object.
4477 BoundingBoxf3 bb;
4478 const Transform3d trafo = this->trafo_centered();
4479 for (const ModelVolume *v : this->model_object()->volumes)
4480 if (v->is_model_part())
4481 bb.merge(v->mesh().transformed_bounding_box(trafo * v->get_matrix()));
4482 auto hr = BeltTransformPipeline::compute_belt_height_and_floor(pcfg, bb, object_height);
4483 object_height = hr.object_height;
4484 belt_floor = hr.floor_params;
4485 }
4487 this->object_extruders(), this->print()->shrinkage_compensation());
4488 // Populate belt floor parameters into slicing params for support clipping.
4489 m_slicing_params.belt_floor_shear_factor = belt_floor.shear_factor;
4490 m_slicing_params.belt_floor_from_axis = belt_floor.from_axis;
4491 m_slicing_params.belt_floor_z_shift = belt_floor.z_shift;
4492 // Prefer the vertex-scan z_shift over the bbox approximation when
4493 // slice() has already produced one (e.g. this rebuild was triggered
4494 // by a support-config change, which doesn't move the belt floor).
4496 m_slicing_params.belt_floor_z_shift = m_belt_floor_z_shift_cached;
4497 }
4498}
double max_z() const
Definition Model.cpp:1638

References Slic3r::BeltTransformPipeline::compute_belt_height_and_floor(), Slic3r::SlicingParameters::create_from_config(), Slic3r::BeltTransformPipeline::BeltFloorParams::from_axis, m_belt_floor_z_shift_cache_valid, m_belt_floor_z_shift_cached, m_config, m_slicing_params, Slic3r::BoundingBox3Base< PointType >::merge(), Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::model_object(), object_extruders(), Slic3r::PrintObjectBaseWithState< Print, PrintObjectStep, posCount >::print(), Slic3r::BeltTransformPipeline::BeltFloorParams::shear_factor, trafo(), trafo_centered(), update_slicing_parameters(), and Slic3r::BeltTransformPipeline::BeltFloorParams::z_shift.

Referenced by update_slicing_parameters().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Print

Member Data Documentation

◆ clip_multipart_objects

bool Slic3r::PrintObject::clip_multipart_objects = true
static

Referenced by slice_volumes().

◆ firstLayerObjectBrimBoundingBox

BoundingBox Slic3r::PrintObject::firstLayerObjectBrimBoundingBox

Referenced by get_first_layer_bbox().

◆ firstLayerObjSliceByGroups

std::vector<groupedVolumeSlices> Slic3r::PrintObject::firstLayerObjSliceByGroups
private

◆ firstLayerObjSliceByVolume

std::vector< VolumeSlices > Slic3r::PrintObject::firstLayerObjSliceByVolume
private

◆ infill_only_where_needed

bool Slic3r::PrintObject::infill_only_where_needed = false
static

◆ m_adaptive_fill_octrees

FillAdaptive::RegionOctrees Slic3r::PrintObject::m_adaptive_fill_octrees
private

Referenced by bridge_over_infill().

◆ m_adaptive_infill_fields

AdaptiveInfillFields Slic3r::PrintObject::m_adaptive_infill_fields
private

◆ m_belt_brim_areas_by_layer

std::vector<ExPolygons> Slic3r::PrintObject::m_belt_brim_areas_by_layer
private

◆ m_belt_brim_by_layer

std::vector<ExtrusionEntityCollection> Slic3r::PrintObject::m_belt_brim_by_layer
private

◆ m_belt_brim_pending

bool Slic3r::PrintObject::m_belt_brim_pending { false }
private
723{ false };

Referenced by generate_belt_brim(), and generate_support_material().

◆ m_belt_brim_prologue

std::vector<BeltBrimBand> Slic3r::PrintObject::m_belt_brim_prologue
private

◆ m_belt_dropped_fills

std::vector<BeltDroppedFill> Slic3r::PrintObject::m_belt_dropped_fills
private

◆ m_belt_floor_z_shift_cache_valid

bool Slic3r::PrintObject::m_belt_floor_z_shift_cache_valid { false }
private

◆ m_belt_floor_z_shift_cached

double Slic3r::PrintObject::m_belt_floor_z_shift_cached { 0.0 }
private
732{ 0.0 };

Referenced by belt_shift_layer_grid(), and update_slicing_parameters().

◆ m_belt_global_z_offset

double Slic3r::PrintObject::m_belt_global_z_offset { 0.0 }
private

◆ m_belt_min_z

double Slic3r::PrintObject::m_belt_min_z { 0.0 }
private
725{ 0.0 };

Referenced by slice(), and slice_volumes().

◆ m_belt_truncated_layers

LayerPtrs Slic3r::PrintObject::m_belt_truncated_layers
private

◆ m_center_offset

Point Slic3r::PrintObject::m_center_offset
private

◆ m_config

◆ m_id

size_t Slic3r::PrintObject::m_id {0}
private
742{0};

Referenced by get_id(), and set_id().

◆ m_instances

◆ m_layers

◆ m_lightning_generator

FillLightning::GeneratorPtr Slic3r::PrintObject::m_lightning_generator
private

Referenced by bridge_over_infill().

◆ m_max_z

double Slic3r::PrintObject::m_max_z
private

Referenced by PrintObject(), and max_z().

◆ m_model_object

ModelObject* Slic3r::PrintObjectBase::m_model_object
protectedinherited

◆ m_print

◆ m_separated_body_bboxes

std::vector<BoundingBox> Slic3r::PrintObject::m_separated_body_bboxes
private

◆ m_shared_object

◆ m_shared_regions

PrintObjectRegions* Slic3r::PrintObject::m_shared_regions { nullptr }
private

◆ m_size

Vec3crd Slic3r::PrintObject::m_size
private

◆ m_skirt

ExtrusionEntityCollection Slic3r::PrintObject::m_skirt
private

Referenced by object_skirt().

◆ m_slicing_params

◆ m_state

◆ m_support_layers

◆ m_trafo

Transform3d Slic3r::PrintObject::m_trafo = Transform3d::Identity()
private

Referenced by PrintObject(), and trafo().

◆ m_tree_support_preview_cache

std::shared_ptr<TreeSupportData> Slic3r::PrintObject::m_tree_support_preview_cache
private

◆ m_typed_slices

bool Slic3r::PrintObject::m_typed_slices = false
private

◆ object_first_layer_wall_extruders

std::vector<int> Slic3r::PrintObject::object_first_layer_wall_extruders

◆ PrintType

◆ s_last_id

size_t Slic3r::ObjectBase::s_last_id = 0
staticprivateinherited

Referenced by generate_new_id().


The documentation for this class was generated from the following files:
  • /home/runner/work/OrcaSlicer/OrcaSlicer/src/libslic3r/Print.hpp
  • /home/runner/work/OrcaSlicer/OrcaSlicer/src/libslic3r/BeltPurge.cpp
  • /home/runner/work/OrcaSlicer/OrcaSlicer/src/libslic3r/Print.cpp
  • /home/runner/work/OrcaSlicer/OrcaSlicer/src/libslic3r/PrintObject.cpp
  • /home/runner/work/OrcaSlicer/OrcaSlicer/src/libslic3r/PrintObjectSlice.cpp