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

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

Inheritance diagram for Slic3r::Print:
Collaboration diagram for Slic3r::Print:

Classes

struct  SkirtBrimGroup
struct  FilamentIndexKey
struct  PrintIndexKey
struct  FilamentIndexKeyHash
struct  PrintIndexKeyHash

Public Types

using SlicingPipelineHookFn = std::function<void(Print&, const PrintObject*, SlicingPipelineStepPlugin)>
enum  ApplyStatus { APPLY_STATUS_UNCHANGED , APPLY_STATUS_CHANGED , APPLY_STATUS_INVALIDATED }
enum  CancelStatus { NOT_CANCELED = 0 , CANCELED_BY_USER = 1 , CANCELED_INTERNAL = 2 }
typedef std::function< void(const SlicingStatus &)> status_callback_type
typedef std::function< void()> cancel_callback_type
using Timestamp = uint64_t

Public Member Functions

 Print ()=default
virtual ~Print ()
PrinterTechnology technology () const noexcept override
void clear () override
bool empty () const override
std::vector< ObjectID > print_object_ids () const override
ApplyStatus apply (const Model &model, DynamicPrintConfig config, bool extruder_applied=false) override
void process (long long *time_cost_with_cache=nullptr, bool use_cache=false) override
std::string export_gcode (const std::string &path_template, GCodeProcessorResult *result, ThumbnailsGeneratorCallback thumbnail_cb=nullptr)
int export_cached_data (const std::string &dir_path, bool with_space=false) override
int load_cached_data (const std::string &directory) override
bool is_step_done (PrintStep step) const
bool is_step_done (PrintObjectStep step) const
bool finished () const override
bool has_infinite_skirt () const
bool has_skirt () const
bool has_brim () const
bool has_tilted_belt () const
bool has_belt_brim () const
bool has_auto_brim () const
StringObjectException validate (std::vector< StringObjectException > *warnings=nullptr, Polygons *collison_polygons=nullptr, std::vector< std::pair< Polygon, float > > *height_polygons=nullptr) const override
std::string slicing_errors_message (const SlicingErrors &errors) const
double skirt_first_layer_height () const
Flow brim_flow () const
Flow skirt_flow () const
std::vector< unsigned int > object_extruders () const
std::vector< unsigned int > support_material_extruders () const
std::vector< unsigned int > extruders (bool conside_custom_gcode=false) const
double max_allowed_layer_height () const
bool has_support_material () const
void auto_assign_extruders (ModelObject *model_object) const
const PrintConfig & config () const
const PrintObjectConfig & default_object_config () const
const PrintRegionConfig & default_region_config () const
ConstPrintObjectPtrsAdaptor objects () const
PrintObject * get_object (size_t idx)
const PrintObject * get_object (size_t idx) const
const PrintObject * get_object (ObjectID object_id) const
std::map< ObjectID, ExtrusionEntityCollection > & get_brimMap ()
unsigned int num_object_instances () const
PrintObjectPtrs & objects_mutable ()
PrintRegionPtrs & print_regions_mutable ()
std::vector< size_t > layers_sorted_for_object (float start, float end, std::vector< LayerPtrs > &layers_of_objects, std::vector< BoundingBox > &boundingBox_for_objects, VecOfPoints &objects_instances_shift)
const ExtrusionEntityCollection & skirt () const
const std::vector< SkirtBrimGroup > & skirt_brim_groups () const
bool has_shared_per_object_skirt () const
const Polygon & first_layer_convex_hull () const
const PrintStatistics & print_statistics () const
PrintStatistics & print_statistics ()
const StatisticsByExtruderCount statistics_by_extruder () const
StatisticsByExtruderCount & statistics_by_extruder ()
bool has_wipe_tower () const
bool has_belt_purge_tower () const
const WipeTowerData & wipe_tower_data (size_t filaments_cnt=0) const
const ToolOrdering & tool_ordering () const
void update_filament_maps_to_config (std::vector< int > f_maps, std::vector< int > f_volume_maps=std::vector< int >{}, std::vector< int > f_nozzle_maps=std::vector< int >{})
void update_to_config_by_nozzle_group_result (const MultiNozzleUtils::LayeredNozzleGroupResult &group_result)
void apply_config_for_render (const DynamicConfig &config)
std::vector< int > get_filament_maps () const
FilamentMapMode get_filament_map_mode () const
std::vector< int > get_filament_volume_maps () const
std::vector< int > get_filament_nozzle_maps () const
size_t get_extruder_id (unsigned int filament_id) const
Polygons get_extruder_shared_printable_polygon () const
void set_nozzle_group_result (std::shared_ptr< MultiNozzleUtils::NozzleGroupResultBase > result)
std::shared_ptr< MultiNozzleUtils::NozzleGroupResultBase > get_nozzle_group_result () const
std::shared_ptr< MultiNozzleUtils::LayeredNozzleGroupResult > get_layered_nozzle_group_result () const
bool is_dynamic_group_reorder () const
const std::map< const PrintObject *, ToolOrdering > & sequential_dynamic_orderings () const
const std::vector< std::vector< DynamicPrintConfig > > & get_extruder_filament_info () const
void set_extruder_filament_info (const std::vector< std::vector< DynamicPrintConfig > > &filament_info)
void set_geometric_unprintable_filaments (const std::vector< std::set< int > > &unprintables_filament_ids)
std::vector< std::set< int > > get_geometric_unprintable_filaments () const
void set_slice_used_filaments (const std::vector< unsigned int > &first_layer_used_filaments, const std::vector< unsigned int > &used_filaments)
std::vector< unsigned int > get_slice_used_filaments (bool first_layer) const
void set_slice_used_mixed_filaments (const std::vector< unsigned int > &used_mixed_filaments)
const std::vector< unsigned int > & get_slice_used_mixed_filaments () const
std::vector< std::set< int > > get_physical_unprintable_filaments (const std::vector< unsigned int > &used_filaments) const
 Determines the unprintable filaments for each extruder based on its physical attributes.
std::map< int, std::set< NozzleVolumeType > > get_filament_unprintable_flow (const std::vector< unsigned int > &used_filaments) const
 Determines the forbidden nozzle volume types for each used filament.
std::vector< double > get_extruder_printable_height () const
std::vector< Polygons > get_extruder_printable_polygons () const
std::vector< Polygons > get_extruder_unprintable_polygons () const
bool enable_timelapse_print () const
std::string output_filename (const std::string &filename_base=std::string()) const override
std::string get_model_name () const
std::string get_plate_number_formatted () const
size_t num_print_regions () const throw ()
const PrintRegion & get_print_region (size_t idx) const
const ToolOrdering & get_tool_ordering () const
void set_plate_origin (Vec3d origin)
const Vec3d get_plate_origin () const
void update_imex_slice_offset ()
Vec2d get_imex_slice_offset () const
void set_gcode_file_ready ()
void set_gcode_file_invalidated ()
void export_gcode_from_previous_file (const std::string &file, GCodeProcessorResult *result, ThumbnailsGeneratorCallback thumbnail_cb=nullptr)
void reload_gcode_moves (GCodeProcessorResult *result) const
int get_modified_count () const
bool is_support_used () const
std::string get_conflict_string () const
ConflictResultOpt get_conflict_result () const
Points first_layer_wipe_tower_corners (bool check_wipe_tower_existance=true) const
bool & is_BBL_printer ()
const bool is_BBL_printer () const
WipeTowerType wipe_tower_type () const
CalibMode & calib_mode ()
const CalibMode calib_mode () const
void set_calib_params (const Calib_Params &params)
const Calib_Params & calib_params () const
Vec2d translate_to_print_space (const Vec2d &point) const
void validate_compacted_wipe_tower_clearance () const
float get_wipe_tower_depth () const
BoundingBoxf get_wipe_tower_bbx () const
Vec2f get_rib_offset () const
const FakeWipeTower & get_fake_wipe_tower () const
void set_check_multi_filaments_compatibility (bool check)
bool need_check_multi_filaments_compatibility () const
Vec2d translate_to_print_space (const Point &point) const
bool is_all_objects_are_short () const
int get_filament_config_indx (int filament_id, int layer_id, bool use_cache=true)
int get_nozzle_config_index (int filament_id, int layer_id)
size_t config_index_generation () const
bool has_same_shrinkage_compensations () const
Vec3d shrinkage_compensation () const
std::tuple< float, float > object_skirt_offset (double margin_height=0) const
PrintStateBase::StateWithTimeStamp step_state_with_timestamp (PrintStep step) const
PrintStateBase::StateWithWarnings step_state_with_warnings (PrintStep step) const
void active_step_add_warning (PrintStateBase::WarningLevel warning_level, const std::string &message, PrintStateBase::SlicingNotificationType message_id=PrintStateBase::SlicingDefaultNotification)
const Model & model () const
virtual void set_task (const TaskParams &params)
virtual void finalize ()
void set_status_default ()
void set_status_silent ()
void set_status_callback (status_callback_type cb)
void set_status (int percent, const std::string &message, unsigned int flags=SlicingStatus::DEFAULT, int warning_step=-1) const
void set_cancel_callback (cancel_callback_type cancel_callback)
CancelStatus cancel_status () const
bool canceled () const
void cancel ()
void cancel_internal ()
void restart ()
const PlaceholderParser & placeholder_parser () const
const DynamicPrintConfig & full_print_config () const
std::string output_filepath (const std::string &path, const std::string &filename_base=std::string()) const
int get_plate_index () const
void set_plate_index (int index)
bool get_no_check_flag () const
void set_no_check_flag (bool no_check)
std::string get_plate_name () const
void set_plate_name (const std::string &name)
ObjectID id () const
virtual Timestamp timestamp () const

Static Public Member Functions

static void set_slicing_pipeline_hook_fn (SlicingPipelineHookFn fn)
static StringObjectException sequential_print_clearance_valid (const Print &print, Polygons *polygons=nullptr, std::vector< std::pair< Polygon, float > > *height_polygons=nullptr)
static StringObjectException compacted_wipe_tower_clearance_valid (const Print &print, Polygons *polygons=nullptr, std::vector< std::pair< Polygon, float > > *height_polygons=nullptr)
static FilamentTempType get_filament_temp_type (const std::string &filament_type)
static int get_hrc_by_nozzle_type (const NozzleType &type)
static std::vector< std::string > get_incompatible_filaments_by_nozzle (const float nozzle_diameter, const std::optional< NozzleVolumeType > nozzle_volume_type=std::nullopt)
static FilamentCompatibilityType check_multi_filaments_compatibility (const std::vector< std::string > &filament_types, const std::vector< int > &nozzle_temperatures, const std::vector< int > &nozzle_temperature_range_lows, const std::vector< int > &nozzle_temperature_range_highs)
static bool is_filaments_compatible (const std::vector< int > &types)
static int get_compatible_filament_type (const std::set< int > &types)

Protected Types

using FilamentIndexMap = std::unordered_map<FilamentIndexKey, int, FilamentIndexKeyHash>
using PrintIndexMap = std::unordered_map<PrintIndexKey, int, PrintIndexKeyHash>

Protected Member Functions

int get_config_index (int filament_id, int layer_id, const std::vector< std::string > &variant_list, const std::vector< int > &self_index_list, FilamentIndexMap *index_map)
int get_config_index (int filament_id, int layer_id, const std::vector< std::string > &variant_list, const std::vector< int > &self_index_list, PrintIndexMap &index_map)
bool invalidate_step (PrintStep step)
bool set_started (PrintStep step)
PrintStateBase::TimeStamp set_done (PrintStep step)
bool invalidate_steps (StepTypeIterator step_begin, StepTypeIterator step_end)
bool invalidate_steps (std::initializer_list< PrintStep > il)
bool invalidate_all_steps ()
bool is_step_started_unguarded (PrintStep step) const
bool is_step_done_unguarded (PrintStep step) const
std::string output_filename (const std::string &format, const std::string &default_ext, const std::string &filename_base, const DynamicConfig *config_override=nullptr) const
std::mutex & state_mutex () const
std::function< void()> cancel_callback ()
void call_cancel_callback ()
void status_update_warnings (int step, PrintStateBase::WarningLevel warning_level, const std::string &message, const PrintObjectBase *print_object=nullptr, PrintStateBase::SlicingNotificationType message_id=PrintStateBase::SlicingDefaultNotification)
void status_update_warnings (int step, PrintStateBase::WarningLevel warning_level, const std::string &message, PrintObjectBase &object, PrintStateBase::SlicingNotificationType message_id=PrintStateBase::SlicingDefaultNotification)
void throw_if_canceled () const
PrintTryCancel make_try_cancel () const
void update_object_placeholders (DynamicConfig &config, const std::string &default_ext) const
void set_new_unique_id ()
void set_invalid_id ()
void copy_id (const ObjectBase &rhs)
virtual void assign_new_unique_ids_recursive ()

Protected Attributes

Model m_model
DynamicPrintConfig m_full_print_config
DynamicPrintConfig m_ori_full_print_config
PlaceholderParser m_placeholder_parser
int m_plate_index { 0 }
bool m_no_check = false
std::string m_plate_name
status_callback_type m_status_callback

Private Types

typedef PrintBaseWithState< PrintStep, psCount > Inherited
typedef std::pair< PrintObject *, bool > PrintObjectInfo

Private Member Functions

bool has_tpu_filament () const
bool invalidate_state_by_config_options (const ConfigOptionResolver &new_config, const std::vector< t_config_option_key > &opt_keys)
void _make_skirt ()
void _make_wipe_tower ()
void _plan_belt_purge ()
void _align_belt_purge_layers ()
void finalize_first_layer_convex_hull ()
void update_filament_self_index_cache ()
bool collect_filament_variant_uses (const MultiNozzleUtils::LayeredNozzleGroupResult &group_result, const DynamicPrintConfig &config, std::unordered_map< int, std::vector< FilamentVariantUse > > &uses) const
Polygons first_layer_islands () const
void run_pipeline_hook (SlicingPipelineStepPlugin step, const PrintObject *object)
void append_tool_change_extruders (std::vector< unsigned int > &extruders) const
void append_wipe_tower_extruder (std::vector< unsigned int > &extruders) const
void update_shrinkage_compensation (size_t num_extruders, std::vector< int > &variant_index)
template<class Archive>
void serialize (Archive &ar)

Static Private Member Functions

static StringObjectException check_multi_filament_valid (const Print &print)
static void append_model_object_extruders (const ModelObject &mo, std::vector< unsigned int > &extruders)
static bool append_support_extruders (const PrintObjectConfig &config, unsigned int num_extruders, std::vector< unsigned int > &extruders)
static ObjectID generate_new_id ()
template<class Archive>
static void load_and_construct (Archive &ar, cereal::construct< ObjectBase > &construct)

Private Attributes

bool m_pipeline_plugin_active { false }
PrintConfig m_config
PrintObjectConfig m_default_object_config
PrintRegionConfig m_default_region_config
PrintObjectPtrs m_objects
PrintRegionPtrs m_print_regions
bool m_same_shrinkage_compensations { false }
Vec3d m_shrinkage_compensation { Vec3d::Ones() }
bool m_isBBLPrinter = false
ExtrusionEntityCollection m_skirt
std::vector< SkirtBrimGroup > m_skirt_brim_groups
bool m_has_shared_per_object_skirt { false }
std::map< ObjectID, ExtrusionEntityCollection > m_brimMap
std::map< ObjectID, unsigned int > m_brimFilamentMap
std::map< ObjectInstanceID, ExtrusionEntityCollection > m_brimMapByInstance
std::map< ObjectInstanceID, ExPolygons > m_objectBrimAreasByInstance
Polygon m_first_layer_convex_hull
Points m_skirt_convex_hull
std::vector< std::vector< DynamicPrintConfig > > m_extruder_filament_info
std::shared_ptr< MultiNozzleUtils::NozzleGroupResultBase > m_nozzle_group_result
std::shared_ptr< MultiNozzleUtils::LayeredNozzleGroupResult > m_layered_nozzle_group_result
std::map< const PrintObject *, ToolOrdering > m_sequential_dynamic_orderings
FilamentIndexMap m_filament_index_map
PrintIndexMap m_nozzle_index_map
size_t m_config_index_generation {0}
std::set< int > m_missing_nozzle_group_logged
std::vector< int > m_filament_self_index
ToolOrdering m_tool_ordering
WipeTowerData m_wipe_tower_data {m_tool_ordering}
PrintStatistics m_print_statistics
bool m_support_used {false}
StatisticsByExtruderCount m_statistics_by_extruder_count
std::vector< unsigned int > m_slice_used_filaments
std::vector< unsigned int > m_slice_used_filaments_first_layer
std::vector< unsigned int > m_slice_used_mixed_filaments
Vec3d m_origin {0, 0, 0}
Vec2d m_imex_slice_offset { Vec2d::Zero() }
int m_modified_count {0}
ConflictResultOpt m_conflict_result
FakeWipeTower m_fake_wipe_tower
bool m_has_auto_filament_map_result {false}
std::vector< std::set< int > > m_geometric_unprintable_filaments
Calib_Params m_calib_params
bool m_need_check_multi_filaments_compatibility {true}
PrintState< PrintStep, COUNT > m_state
std::atomic< CancelStatus > m_cancel_status
cancel_callback_type m_cancel_callback = [](){}
std::mutex m_state_mutex
friend PrintTryCancel
ObjectID m_id

Static Private Attributes

static SlicingPipelineHookFn s_slicing_pipeline_hook_fn = nullptr
static size_t s_last_id = 0

Friends

class GCode
class PrintObject

Detailed Description


Class Documentation

◆ Slic3r::Print::SkirtBrimGroup

struct Slic3r::Print::SkirtBrimGroup
Collaboration diagram for Slic3r::Print::SkirtBrimGroup:
Class Members
vector< Brim > brims
vector< ObjectInstanceID > instances
ExtrusionEntityCollection skirt

Member Typedef Documentation

◆ cancel_callback_type

typedef std::function<void()> Slic3r::PrintBase::cancel_callback_type
inherited

◆ FilamentIndexMap

using Slic3r::Print::FilamentIndexMap = std::unordered_map<FilamentIndexKey, int, FilamentIndexKeyHash>
protected

◆ Inherited

◆ PrintIndexMap

using Slic3r::Print::PrintIndexMap = std::unordered_map<PrintIndexKey, int, PrintIndexKeyHash>
protected

◆ PrintObjectInfo

typedef std::pair<PrintObject *, bool> Slic3r::Print::PrintObjectInfo
private

◆ SlicingPipelineHookFn

◆ status_callback_type

typedef std::function<void(const SlicingStatus&)> Slic3r::PrintBase::status_callback_type
inherited

◆ Timestamp

using Slic3r::ObjectBase::Timestamp = uint64_t
inherited

Member Enumeration Documentation

◆ ApplyStatus

Enumerator
APPLY_STATUS_UNCHANGED 
APPLY_STATUS_CHANGED 
APPLY_STATUS_INVALIDATED 
410 {
411 // No change after the Print::apply() call.
413 // Some of the Print / PrintObject / PrintObjectInstance data was changed,
414 // but no result was invalidated (only data influencing not yet calculated results were changed).
416 // Some data was changed, which in turn invalidated already calculated steps.
418 };
@ APPLY_STATUS_UNCHANGED
Definition PrintBase.hpp:412
@ APPLY_STATUS_CHANGED
Definition PrintBase.hpp:415
@ APPLY_STATUS_INVALIDATED
Definition PrintBase.hpp:417

◆ CancelStatus

Enumerator
NOT_CANCELED 
CANCELED_BY_USER 
CANCELED_INTERNAL 
498 {
499 // No cancelation, background processing should run.
500 NOT_CANCELED = 0,
501 // Canceled by user from the user interface (user pressed the "Cancel" button or user closed the application).
503 // Canceled internally from Print::apply() through the Print/PrintObject::invalidate_step() or ::invalidate_all_steps().
505 };
@ NOT_CANCELED
Definition PrintBase.hpp:500
@ CANCELED_INTERNAL
Definition PrintBase.hpp:504
@ CANCELED_BY_USER
Definition PrintBase.hpp:502

Constructor & Destructor Documentation

◆ Print()

Slic3r::Print::Print ( )
default

Referenced by check_multi_filament_valid(), compacted_wipe_tower_clearance_valid(), sequential_print_clearance_valid(), and wipe_tower_data().

Here is the caller graph for this function:

◆ ~Print()

virtual Slic3r::Print::~Print ( )
inlinevirtual
1067{ this->clear(); }
void clear() override
Definition Print.cpp:235

References clear().

Here is the call graph for this function:

Member Function Documentation

◆ _align_belt_purge_layers()

void Slic3r::Print::_align_belt_purge_layers ( )
private
86{
87 PrintObject *prism = nullptr;
88 for (PrintObject *po : m_objects)
89 if (po->config().belt_purge_tower_object.value && !po->layers().empty()) {
90 prism = po;
91 break;
92 }
93 if (prism == nullptr || prism->layers().empty())
94 return;
95
96 const double h = prism->config().layer_height.value;
97 if (h <= EPSILON)
98 return;
99
100 // Grid residue of an object's layer grid: identical for all of an object's
101 // layers above the first since they step by h.
102 auto grid_offset = [h](const PrintObject *po) -> double {
103 if (po->layers().empty())
104 return 0.;
105 const double z = po->layers().front()->print_z;
106 return z - std::floor(z / h) * h; // in [0, h)
107 };
108
109 // Reference grid: the tallest non-prism object (proxy for the object with
110 // the most toolchange layers — minimizes how far the rest must move).
111 const PrintObject *ref = nullptr;
112 double ref_top = -std::numeric_limits<double>::max();
113 for (const PrintObject *po : m_objects) {
114 if (po->config().belt_purge_tower_object.value || po->layers().empty())
115 continue;
116 const double top = po->layers().back()->print_z;
117 if (top > ref_top) {
118 ref_top = top;
119 ref = po;
120 }
121 }
122 if (ref == nullptr)
123 return;
124
125 const double ref_offset = grid_offset(ref);
126
127 // Snap every object (printed objects AND the prism) onto the reference grid.
128 for (PrintObject *po : m_objects) {
129 if (po->layers().empty())
130 continue;
131 double delta = ref_offset - grid_offset(po);
132 if (delta > 0.5 * h)
133 delta -= h;
134 else if (delta <= -0.5 * h)
135 delta += h;
136 po->belt_shift_layer_grid(delta); // no-op for the reference object (delta ~ 0)
137 }
138}
#define EPSILON
Definition ColorUtils.cpp:64
PrintObjectPtrs m_objects
Definition Print.hpp:1503
friend class PrintObject
Definition Print.hpp:1591

References Slic3r::PrintObject::config(), Slic3r::ConstVectorOfPtrsAdaptor< T >::empty(), EPSILON, Slic3r::PrintObject::layers(), m_objects, and PrintObject.

Referenced by _plan_belt_purge().

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

◆ _make_skirt()

void Slic3r::Print::_make_skirt ( )
private
4026{
4027 // Belt printer: skirt is not compatible.
4028 if (m_config.belt_printer.value)
4029 return;
4030
4031 const bool generate_skirt = this->has_skirt() || this->has_infinite_skirt();
4032
4033 // First off we need to decide how tall the skirt must be.
4034 // The skirt_height option from config is expressed in layers, but our
4035 // object might have different layer heights, so we need to find the print_z
4036 // of the highest layer involved.
4037 // Note that unless has_infinite_skirt() == true
4038 // the actual skirt might not reach this $skirt_height_z value since the print
4039 // order of objects on each layer is not guaranteed and will not generally
4040 // include the thickest object first. It is just guaranteed that a skirt is
4041 // prepended to the first 'n' layers (with 'n' = skirt_height).
4042 // $skirt_height_z in this case is the highest possible skirt height for safety.
4043 coordf_t skirt_height_z = 0.;
4044 if (generate_skirt) {
4045 for (const PrintObject *object : m_objects) {
4046 size_t skirt_layers = this->has_infinite_skirt() ?
4047 object->layer_count() :
4048 std::min(size_t(m_config.skirt_height.value), object->layer_count());
4049 skirt_height_z = std::max(skirt_height_z, object->m_layers[skirt_layers-1]->print_z);
4050 }
4051 }
4052
4053 struct ObjectSkirtHull {
4054 PrintObject* object;
4055 Polygon hull;
4056 };
4057
4058 // Orca: Build one local occupied hull per object from object/support
4059 // geometry up to skirt height. Instances translate this hull later.
4060 std::vector<ObjectSkirtHull> object_convex_hulls;
4061 for (PrintObject *object : m_objects) {
4062 Points object_points;
4063 // Get object layers up to skirt_height_z.
4064 for (const Layer *layer : object->m_layers) {
4065 if (generate_skirt && layer->print_z > skirt_height_z)
4066 break;
4067 for (const ExPolygon &expoly : layer->lslices)
4068 // Collect the outer contour points only, ignore holes for the calculation of the convex hull.
4069 append(object_points, expoly.contour.points);
4070 }
4071 // Get support layers up to skirt_height_z.
4072 for (const SupportLayer *layer : object->support_layers()) {
4073 if (generate_skirt && layer->print_z > skirt_height_z)
4074 break;
4075 layer->support_fills.collect_points(object_points);
4076 }
4077
4078 object_convex_hulls.push_back({ object, Slic3r::Geometry::convex_hull(object_points) });
4079 }
4080
4081 if (object_convex_hulls.empty())
4082 return;
4083
4084 this->throw_if_canceled();
4085
4086 // Skirt may be printed on several layers, having distinct layer heights,
4087 // but loops must be aligned so can't vary width/spacing
4088 // TODO: use each extruder's own flow
4090 Flow flow = this->skirt_flow();
4091 float spacing = flow.spacing();
4092 double mm3_per_mm = flow.mm3_per_mm();
4093
4094 std::vector<size_t> extruders;
4095 std::vector<double> extruders_e_per_mm;
4096 {
4097 auto set_extruders = this->extruders();
4098 extruders.reserve(set_extruders.size());
4099 extruders_e_per_mm.reserve(set_extruders.size());
4100 for (auto &extruder_id : set_extruders) {
4101 extruders.push_back(extruder_id);
4102 extruders_e_per_mm.push_back(Extruder((unsigned int)extruder_id, &m_config, m_config.single_extruder_multi_material).e_per_mm(mm3_per_mm));
4103 }
4104 }
4105
4106 // Initial skirt centerline offset from the occupied outline.
4107 // The skirt will touch the occupied outline if skirt_distance is zero.
4108 // Generate loops inward to outward; callers reverse them before G-code export.
4109 // Loop while we have less skirts than required or any extruder hasn't reached the min length if any.
4110 auto append_skirt_loops_for_hull = [&](const Polygon& hull, ExtrusionEntityCollection& dst, bool collect_skirt_hull) {
4111 float distance = float(scale_(m_config.skirt_distance.value - spacing/2.));
4112 std::vector<coordf_t> extruded_length(extruders.size(), 0.);
4113 for (size_t i = m_config.skirt_loops, extruder_idx = 0; i > 0; -- i) {
4114 this->throw_if_canceled();
4115 // Offset the skirt outside.
4116 distance += float(scale_(spacing));
4117 // Generate the skirt centerline.
4118 Polygon loop;
4119 {
4120 // Orca: the hull already represents the occupied outline used for this skirt.
4121 Polygons loops = offset(hull, distance, jtRound, float(scale_(0.1)));
4122 Geometry::simplify_polygons(loops, scale_(0.05), &loops);
4123 if (loops.empty())
4124 break;
4125 loop = loops.front();
4126 }
4127 // Extrude the skirt loop.
4128 ExtrusionLoop eloop(elrSkirt);
4129 eloop.paths.emplace_back(ExtrusionPath(
4130 ExtrusionPath(
4131 erSkirt,
4132 (float)mm3_per_mm, // this will be overridden at G-code export time
4133 flow.width(),
4134 (float)initial_layer_print_height // this will be overridden at G-code export time
4135 )));
4136 eloop.paths.back().polyline = Polyline3(loop.split_at_first_point());
4137 dst.append(eloop);
4138 if (m_config.min_skirt_length.value > 0) {
4139 // The skirt length is limited. Sum the total amount of filament length extruded, in mm.
4140 extruded_length[extruder_idx] += unscale<double>(loop.length()) * extruders_e_per_mm[extruder_idx];
4141 if (extruded_length[extruder_idx] < m_config.min_skirt_length.value) {
4142 // Not extruded enough yet with the current extruder. Add another loop.
4143 if (i == 1)
4144 ++ i;
4145 } else {
4146 assert(extruded_length[extruder_idx] >= m_config.min_skirt_length.value);
4147 // Enough extruded with the current extruder. Extrude with the next one,
4148 // until the prescribed number of skirt loops is extruded.
4149 if (extruder_idx + 1 < extruders.size())
4150 ++ extruder_idx;
4151 }
4152 } else {
4153 // The skirt length is not limited, extrude the skirt with the 1st extruder only.
4154 }
4155 }
4156
4157 if (collect_skirt_hull)
4158 for (Polygon &poly : offset(hull, distance + 0.5f * float(scale_(spacing)), jtRound, float(scale_(0.1))))
4159 append(m_skirt_convex_hull, std::move(poly.points));
4160 };
4161
4162 m_skirt.clear();
4163 m_skirt_brim_groups.clear();
4165 for (const ObjectSkirtHull& object_hull : object_convex_hulls)
4166 object_hull.object->m_skirt.clear();
4167
4168 if (m_config.skirt_type == stCombined || m_config.skirt_type == stPerObject) {
4169 struct SkirtBrimGroupItem {
4170 Points occupied_points;
4171 ObjectID object_id;
4172 size_t instance_id;
4173 bool emits_skirt;
4174 };
4175
4176 // Orca: Each object instance can emit skirt/brim. Wipe tower is only an
4177 // obstacle here; it may merge nearby items, but does not emit anything.
4178 std::vector<SkirtBrimGroupItem> group_items;
4179 const coord_t grouping_offset = scale_(m_config.skirt_distance.value + m_config.skirt_loops.value * spacing);
4180 for (const ObjectSkirtHull& object_hull : object_convex_hulls) {
4181 PrintObject* object = object_hull.object;
4182 std::vector<size_t> object_item_indices;
4183 object_item_indices.reserve(object->instances().size());
4184 for (size_t instance_idx = 0; instance_idx < object->instances().size(); ++instance_idx) {
4185 const PrintInstance &instance = object->instances()[instance_idx];
4186 Points copy_points = object_hull.hull.points;
4187 for (Point &pt : copy_points)
4188 pt += instance.shift;
4189 if (copy_points.size() < 3)
4190 continue;
4191
4192 object_item_indices.push_back(group_items.size());
4193 group_items.push_back({ std::move(copy_points), object->id(), instance_idx, true });
4194 }
4195
4196 for (size_t item_idx : object_item_indices) {
4197 const ObjectInstanceID key{ object->id(), group_items[item_idx].instance_id };
4198 if (auto instance_brim_it = m_objectBrimAreasByInstance.find(key); instance_brim_it != m_objectBrimAreasByInstance.end())
4199 for (const ExPolygon& area : instance_brim_it->second)
4200 append(group_items[item_idx].occupied_points, area.contour.points);
4201 }
4202 }
4203
4204 // Orca: the wipe tower contributes occupied area, but does not emit a skirt by itself.
4205 Points wipe_tower_points = this->first_layer_wipe_tower_corners();
4206 if (wipe_tower_points.size() >= 3)
4207 group_items.push_back({ std::move(wipe_tower_points), ObjectID(), size_t(-1), false });
4208
4209 std::vector<size_t> parent(group_items.size());
4210 std::iota(parent.begin(), parent.end(), 0);
4211 // Orca: Use union-find so touching items can be merged while scanning.
4212 auto find_parent = [&parent](size_t idx) {
4213 while (parent[idx] != idx) {
4214 parent[idx] = parent[parent[idx]];
4215 idx = parent[idx];
4216 }
4217 return idx;
4218 };
4219 auto unite = [&parent, &find_parent](size_t a, size_t b) {
4220 a = find_parent(a);
4221 b = find_parent(b);
4222 if (a != b)
4223 parent[b] = a;
4224 };
4225
4226 // Orca: Combined skirt starts with all items in the same group.
4227 if (m_config.skirt_type == stCombined && !group_items.empty())
4228 for (size_t i = 1; i < group_items.size(); ++i)
4229 unite(0, i);
4230
4231 auto build_skirt_brim_groups = [&]() {
4232 struct SkirtBrimGroupData {
4233 Points points;
4234 std::vector<ObjectInstanceID> instances;
4235 bool emits_skirt = false;
4236 };
4237
4238 std::map<size_t, SkirtBrimGroupData> grouped;
4239 for (size_t i = 0; i < group_items.size(); ++i) {
4240 SkirtBrimGroupData& group = grouped[find_parent(i)];
4241 append(group.points, group_items[i].occupied_points);
4242 if (group_items[i].object_id.valid())
4243 group.instances.push_back({ group_items[i].object_id, group_items[i].instance_id });
4244 group.emits_skirt = group.emits_skirt || group_items[i].emits_skirt;
4245 }
4246 return grouped;
4247 };
4248
4249 bool groups_changed = m_config.skirt_type == stPerObject;
4250 while (groups_changed) {
4251 groups_changed = false;
4252 auto grouped_points = build_skirt_brim_groups();
4253 std::vector<std::pair<size_t, Polygon>> group_envelopes;
4254 for (const auto& [root, group] : grouped_points) {
4255 if (group.points.size() < 3)
4256 continue;
4257
4258 // Orca: Only skirt-emitting groups are expanded by skirt distance;
4259 // obstacle-only groups stay at their occupied outline.
4260 Polygon envelope = Geometry::convex_hull(group.points);
4261 if (group.emits_skirt) {
4262 // Orca: If the expanded skirt outline touches another group
4263 // or obstacle, merge them and run the pass again.
4264 Polygons envelopes = offset(envelope, grouping_offset, jtRound, float(scale_(0.1)));
4265 if (envelopes.empty())
4266 continue;
4267 envelope = std::move(envelopes.front());
4268 }
4269 group_envelopes.emplace_back(root, std::move(envelope));
4270 }
4271
4272 for (size_t i = 0; i < group_envelopes.size(); ++i) {
4273 for (size_t j = i + 1; j < group_envelopes.size(); ++j) {
4274 const size_t root_i = find_parent(group_envelopes[i].first);
4275 const size_t root_j = find_parent(group_envelopes[j].first);
4276 if (root_i != root_j && !intersection(group_envelopes[i].second, group_envelopes[j].second).empty()) {
4277 unite(root_i, root_j);
4278 groups_changed = true;
4279 }
4280 }
4281 }
4282 }
4283
4284 std::vector<std::vector<BedExcludeRegion>> brim_exclusion_regions;
4286 brim_exclusion_regions = translated_bed_exclusion_volumes_by_extruder(*this);
4287 for (std::vector<BedExcludeRegion> &regions : brim_exclusion_regions)
4288 regions.erase(std::remove_if(regions.begin(), regions.end(),
4289 [](const BedExcludeRegion &region) { return !region.is_collision_volume(); }), regions.end());
4290 }
4291 const bool has_brim_exclusions = std::any_of(
4292 brim_exclusion_regions.begin(), brim_exclusion_regions.end(),
4293 [](const std::vector<BedExcludeRegion> &regions) { return !regions.empty(); });
4294 const bool has_nozzle_specific_brim_exclusions = has_brim_exclusions &&
4296
4297 auto make_brims_for_skirt_brim_group =
4298 [this, &brim_exclusion_regions, has_nozzle_specific_brim_exclusions]
4299 (const std::vector<ObjectInstanceID>& group_instances) {
4300 std::vector<SkirtBrimGroup::Brim> brims;
4301 std::vector<ObjectInstanceID> brim_instances;
4302 for (const ObjectInstanceID& instance : group_instances) {
4303 const auto brim_it = m_brimMapByInstance.find(instance);
4304 if (brim_it != m_brimMapByInstance.end() && !brim_it->second.empty())
4305 brim_instances.push_back(instance);
4306 }
4307
4308 auto brim_filament = [this](const ObjectInstanceID& instance) -> std::optional<unsigned int> {
4309 const auto it = m_brimFilamentMap.find(instance.object_id);
4310 return it == m_brimFilamentMap.end() ? std::nullopt :
4311 std::optional<unsigned int>(it->second);
4312 };
4313 auto compatible_brim_nozzles =
4314 [this, &brim_filament, &brim_exclusion_regions, has_nozzle_specific_brim_exclusions]
4315 (const ObjectInstanceID& first, const ObjectInstanceID& second) {
4316 if (!has_nozzle_specific_brim_exclusions)
4317 return true;
4318 const std::optional<unsigned int> first_filament = brim_filament(first);
4319 const std::optional<unsigned int> second_filament = brim_filament(second);
4320 return first_filament.has_value() && second_filament.has_value() &&
4322 *this, { *first_filament, *second_filament },
4323 brim_exclusion_regions.size(), 0).size() == 1;
4324 };
4325
4326 const bool combine_group_brims = m_config.combine_brims && brim_instances.size() > 1;
4327 if (!combine_group_brims) {
4328 for (const ObjectInstanceID& instance : brim_instances) {
4329 const std::optional<unsigned int> filament = brim_filament(instance);
4330 if (filament.has_value())
4331 brims.push_back({ m_brimMapByInstance.at(instance), { instance }, *filament });
4332 }
4333 return brims;
4334 }
4335
4336 std::vector<size_t> brim_parent(brim_instances.size());
4337 std::iota(brim_parent.begin(), brim_parent.end(), 0);
4338 auto find_brim_parent = [&brim_parent](size_t idx) {
4339 while (brim_parent[idx] != idx) {
4340 brim_parent[idx] = brim_parent[brim_parent[idx]];
4341 idx = brim_parent[idx];
4342 }
4343 return idx;
4344 };
4345 auto unite_brims = [&brim_parent, &find_brim_parent](size_t a, size_t b) {
4346 a = find_brim_parent(a);
4347 b = find_brim_parent(b);
4348 if (a != b)
4349 brim_parent[b] = a;
4350 };
4351
4352 const coord_t brim_contact_distance = coord_t(brim_flow().scaled_spacing() * 2.);
4353 for (size_t i = 0; i < brim_instances.size(); ++i) {
4354 const auto area_i = m_objectBrimAreasByInstance.find(brim_instances[i]);
4355 if (area_i == m_objectBrimAreasByInstance.end())
4356 continue;
4357 for (size_t j = i + 1; j < brim_instances.size(); ++j) {
4358 const auto area_j = m_objectBrimAreasByInstance.find(brim_instances[j]);
4359 if (area_j != m_objectBrimAreasByInstance.end() &&
4360 compatible_brim_nozzles(brim_instances[i], brim_instances[j]) &&
4361 !intersection_ex(offset_ex(area_i->second, brim_contact_distance, jtRound, SCALED_RESOLUTION), area_j->second).empty())
4362 unite_brims(i, j);
4363 }
4364 }
4365
4366 std::map<size_t, std::vector<ObjectInstanceID>> combined_brim_ids;
4367 for (size_t i = 0; i < brim_instances.size(); ++i)
4368 combined_brim_ids[find_brim_parent(i)].push_back(brim_instances[i]);
4369
4370 for (const auto& [_, instances] : combined_brim_ids) {
4371 const std::optional<unsigned int> filament = brim_filament(instances.front());
4372 if (!filament.has_value())
4373 continue;
4374 if (instances.size() == 1) {
4375 const ObjectInstanceID& instance = instances.front();
4376 brims.push_back({ m_brimMapByInstance.at(instance), { instance }, *filament });
4377 continue;
4378 }
4379
4380 ExPolygons combined_area;
4381 for (const ObjectInstanceID& instance : instances)
4382 expolygons_append(combined_area, m_objectBrimAreasByInstance.at(instance));
4383 combined_area = union_ex(combined_area);
4384 const float scaled_resolution = float(scaled(m_config.resolution.value));
4385 const float brim_cleanup_delta = std::max(scaled_resolution, float(SCALED_EPSILON));
4386 combined_area = offset2_ex(combined_area, brim_cleanup_delta, -brim_cleanup_delta, jtRound, scaled_resolution);
4387
4388 // The group has one known physical nozzle. Clip after union and
4389 // cleanup so the geometry consumed by path generation remains
4390 // valid for the exact filament recorded on the combined brim.
4391 const std::vector<size_t> physical_extruders = bed_exclusion_physical_extruders(
4392 *this, { *filament }, brim_exclusion_regions.size(), 0);
4393 const coord_t width_spacing_delta = std::max<coord_t>(
4394 0, brim_flow().scaled_width() - brim_flow().scaled_spacing());
4395 const coord_t exclusion_clearance =
4396 (width_spacing_delta + 1) / 2 + coord_t(SCALED_EPSILON);
4398 brim_exclusion_regions, physical_extruders, 0.0,
4399 std::max(0.0, skirt_first_layer_height()), Point(0, 0), exclusion_clearance);
4400 if (!exclusions.empty())
4401 combined_area = diff_ex(combined_area, exclusions);
4402 if (combined_area.empty())
4403 continue;
4404
4405 Polygons islands_area;
4406 brims.push_back({ makeBrimInfillFromPlateCoordinates(combined_area, *this, islands_area),
4407 instances, *filament });
4408 }
4409
4410 return brims;
4411 };
4412
4413 auto grouped_points = build_skirt_brim_groups();
4414 for (auto& [_, group] : grouped_points) {
4415 if (!group.emits_skirt || group.points.size() < 3)
4416 continue;
4417 if (generate_skirt && m_config.skirt_type == stPerObject && group.instances.size() > 1)
4419 // Orca: Group points already include the occupied outline, so don't
4420 // add skirt distance here again.
4421 ExtrusionEntityCollection group_skirt;
4422 if (generate_skirt)
4423 append_skirt_loops_for_hull(Geometry::convex_hull(group.points), group_skirt, true);
4424 std::vector<SkirtBrimGroup::Brim> group_brims = make_brims_for_skirt_brim_group(group.instances);
4425 if (!group_skirt.empty()) {
4426 group_skirt.reverse();
4427 // Orca: Keep m_skirt filled for code that still reads a flat
4428 // skirt collection.
4429 m_skirt.append(group_skirt.entities);
4430 }
4431 if (!group_skirt.empty() || !group_brims.empty())
4432 m_skirt_brim_groups.push_back({ std::move(group_skirt), std::move(group.instances), std::move(group_brims) });
4433 }
4434 }
4435}
#define _(s)
Definition 3mf.cpp:306
void throw_if_canceled() const
Definition PrintBase.hpp:552
double skirt_first_layer_height() const
Definition Print.cpp:3058
Flow skirt_flow() const
Definition Print.cpp:3084
std::vector< SkirtBrimGroup > m_skirt_brim_groups
Definition Print.hpp:1513
ExtrusionEntityCollection m_skirt
Definition Print.hpp:1512
Points m_skirt_convex_hull
Definition Print.hpp:1529
bool has_skirt() const
Definition Print.cpp:841
bool has_infinite_skirt() const
Definition Print.cpp:830
std::map< ObjectInstanceID, ExPolygons > m_objectBrimAreasByInstance
Definition Print.hpp:1522
Flow brim_flow() const
Definition Print.cpp:3063
PrintConfig m_config
Definition Print.hpp:1500
std::map< ObjectID, unsigned int > m_brimFilamentMap
Definition Print.hpp:1518
bool empty() const override
Definition Print.hpp:1077
std::vector< unsigned int > extruders(bool conside_custom_gcode=false) const
Definition Print.cpp:770
bool m_has_shared_per_object_skirt
Definition Print.hpp:1514
std::map< ObjectInstanceID, ExtrusionEntityCollection > m_brimMapByInstance
Definition Print.hpp:1520
Points first_layer_wipe_tower_corners(bool check_wipe_tower_existance=true) const
Definition Print.cpp:4462
#define SCALED_EPSILON
Definition libslic3r.h:109
#define scale_(val)
Definition libslic3r.h:106
int64_t coord_t
Definition libslic3r.h:48
#define SCALED_RESOLUTION
Definition libslic3r.h:87
void poly(std::vector< std::pair< Vec2d, Vec2d > > &out, std::initializer_list< Vec2d > p)
Definition DesignSketchTool.cpp:7751
Polygon convex_hull(Points pts)
Definition ConvexHull.cpp:21
void simplify_polygons(const Polygons &polygons, double tolerance, Polygons *retval)
Definition Geometry.cpp:71
float distance(SpiralVase::SpiralPoint a, SpiralVase::SpiralPoint b)
Definition SpiralVase.cpp:19
Kernel::Point_3 Point
Definition MeshRepair.cpp:81
bool has_bed_exclude_volumes(const DynamicPrintConfig &cfg)
Definition PrintConfig.cpp:14346
static std::vector< std::vector< BedExcludeRegion > > translated_bed_exclusion_volumes_by_extruder(const Print &print)
Definition Print.cpp:143
@ Extruder
Definition DevConfigUtil.h:34
@ jtRound
Definition ClipperUtils.hpp:21
@ stPerObject
Definition PrintConfig.hpp:474
@ stCombined
Definition PrintConfig.hpp:474
BoundingBox scaled(const BoundingBoxf &bb)
Definition BoundingBox.hpp:284
double coordf_t
Definition GUI_ObjectList.hpp:46
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
Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:769
double area(const ExPolygon &poly)
Definition ExPolygon.hpp:511
zaa_minimize_perimeter_height initial_layer_print_height((ConfigOptionFloatsNullable, initial_layer_speed))((ConfigOptionFloatsNullable
@ elrSkirt
Definition ExtrusionEntity.hpp:58
@ erSkirt
Definition ExtrusionEntity.hpp:39
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 offset_ex(const Slic3r::Polygons &polygons, const float delta, JoinType joinType, double miterLimit)
Definition ClipperUtils.cpp:605
ExtrusionEntityCollection makeBrimInfillFromPlateCoordinates(const ExPolygons &singleBrimArea, const Print &print, const Polygons &islands_area)
Definition Brim.cpp:923
Slic3r::ExPolygons intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:878
std::vector< size_t > bed_exclusion_physical_extruders(const Print &print, const std::vector< unsigned int > &filament_ids, const size_t physical_extruder_count, const int layer_id)
Definition ExclusionVolumeGeometry.cpp:119
ExPolygons active_bed_exclusion_footprints(const std::vector< BedExcludeRegion > &regions, const double z_min, const double z_max, const Point &translation, const coord_t clearance)
Definition ExclusionVolumeGeometry.cpp:85
std::vector< Point, PointsAllocator< Point > > Points
Definition Point.hpp:73
BedExcludeVolumeMode active_bed_exclude_volume_mode(const DynamicPrintConfig &cfg)
Definition PrintConfig.cpp:14364
T unscale(Q v)
Definition libslic3r.h:138
Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, PolyFillType fill_type)
Definition ClipperUtils.cpp:1011
void expolygons_append(ExPolygons &dst, const ExPolygons &src)
Definition ExPolygon.hpp:447
void append(std::vector< T, Alloc > &dest, const std::vector< T, Alloc2 > &src)
Definition libslic3r.h:157
ExPolygons offset2_ex(const ExPolygons &expolygons, const float delta1, const float delta2, JoinType joinType, double miterLimit)
Definition ClipperUtils.cpp:652
@ Shared
Definition PrintConfig.hpp:643
Slic3r::Polygon Polygon
Definition Emboss.cpp:101

References _, _make_skirt(), Slic3r::active_bed_exclude_volume_mode(), Slic3r::active_bed_exclusion_footprints(), Slic3r::append(), Slic3r::area(), Slic3r::bed_exclusion_physical_extruders(), brim_flow(), Slic3r::ExPolygon::contour, Slic3r::Geometry::convex_hull(), Slic3r::diff_ex(), Slic3r::Extruder::e_per_mm(), Slic3r::elrSkirt, Slic3r::ExtrusionEntityCollection::empty(), empty(), Slic3r::ExtrusionEntityCollection::entities, Slic3r::erSkirt, Slic3r::expolygons_append(), extruders(), first_layer_wipe_tower_corners(), Slic3r::has_bed_exclude_volumes(), has_infinite_skirt(), has_skirt(), Slic3r::initial_layer_print_height(), Slic3r::PrintObject::instances(), Slic3r::intersection(), Slic3r::intersection_ex(), Slic3r::jtRound, Slic3r::Polygon::length(), Slic3r::Layer::lslices, m_brimFilamentMap, m_brimMapByInstance, m_config, m_has_shared_per_object_skirt, m_objectBrimAreasByInstance, m_objects, m_skirt, m_skirt_brim_groups, m_skirt_convex_hull, Slic3r::makeBrimInfillFromPlateCoordinates(), Slic3r::Flow::mm3_per_mm(), Slic3r::offset(), Slic3r::offset2_ex(), Slic3r::offset_ex(), Slic3r::ExtrusionLoop::paths, Slic3r::MultiPoint::points, Slic3r::Layer::print_z, PrintObject, Slic3r::ExtrusionEntityCollection::reverse(), scale_, Slic3r::scaled(), SCALED_EPSILON, SCALED_RESOLUTION, Slic3r::Shared, Slic3r::PrintInstance::shift, Slic3r::Geometry::simplify_polygons(), skirt_first_layer_height(), skirt_flow(), Slic3r::Flow::spacing(), Slic3r::Polygon::split_at_first_point(), Slic3r::stCombined, Slic3r::stPerObject, Slic3r::PrintBaseWithState< PrintStep, psCount >::throw_if_canceled(), Slic3r::translated_bed_exclusion_volumes_by_extruder(), Slic3r::union_ex(), Slic3r::unscale(), and Slic3r::Flow::width().

Referenced by _make_skirt().

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

◆ _make_wipe_tower()

void Slic3r::Print::_make_wipe_tower ( )
private
5236{
5237 m_wipe_tower_data.clear();
5238
5239 // BBS
5240 const unsigned int number_of_extruders = (unsigned int)(m_config.filament_colour.values.size());
5241
5242 const bool is_wipe_tower_type2 = this->wipe_tower_type() == WipeTowerType::Type2;
5243 // Let the ToolOrdering class know there will be initial priming extrusions at the start of the print.
5244 m_wipe_tower_data.tool_ordering = ToolOrdering(*this, (unsigned int) -1, is_wipe_tower_type2);
5245 m_wipe_tower_data.tool_ordering.sort_and_build_data(*this, (unsigned int)-1, is_wipe_tower_type2);
5246
5247 if (!m_wipe_tower_data.tool_ordering.has_wipe_tower())
5248 // Don't generate any wipe tower.
5249 return;
5250
5251 // Check whether there are any layers in m_tool_ordering, which are marked with has_wipe_tower,
5252 // they print neither object, nor support. Each such layer needs a virtual support layer
5253 // counterpart in m_objects.front() so that GCode::collect_layers_to_print picks it up and the
5254 // wipe tower G-code is actually emitted for that z. Such layers appear in two scenarios:
5255 // - above the raft, between raft top and the first real object layer
5256 // (see https://github.com/prusa3d/PrusaSlicer/issues/607);
5257 // - between two real wipe-tower layers, when one object is fully floating above another and
5258 // the support_top_z_distance / support_bottom_z_distance gap leaves interior z values with
5259 // neither object nor support (continuity fill in ToolOrdering::fill_wipe_tower_partitions).
5260 // The previous implementation only handled the first contiguous run starting at the first
5261 // virtual layer, which made the second scenario silently produce empty wipe-tower layers.
5262 {
5263 auto &support_layers = m_objects.front()->support_layers();
5264 auto it_layer = support_layers.begin();
5265 const size_t idx_end = m_wipe_tower_data.tool_ordering.layer_tools().size();
5266 for (size_t i = 0; i < idx_end; ++ i) {
5267 LayerTools &lt = const_cast<LayerTools&>(m_wipe_tower_data.tool_ordering.layer_tools()[i]);
5268 if (! (lt.has_wipe_tower && ! lt.has_object && ! lt.has_support))
5269 continue;
5270 while (it_layer != support_layers.end() && (*it_layer)->print_z + EPSILON < lt.print_z)
5271 ++ it_layer;
5272 if (it_layer != support_layers.end() && std::abs((*it_layer)->print_z - lt.print_z) < EPSILON) {
5273 lt.has_support = true;
5274 ++ it_layer;
5275 continue;
5276 }
5277 lt.has_support = true;
5278 double height = lt.print_z - (i == 0 ? 0. : m_wipe_tower_data.tool_ordering.layer_tools()[i-1].print_z);
5279 //FIXME the support layer ID is set to -1, as Vojtech hopes it is not being used anyway.
5280 it_layer = m_objects.front()->insert_support_layer(it_layer, -1, 0, height, lt.print_z, lt.print_z - 0.5 * height);
5281 ++ it_layer;
5282 }
5283 }
5284 this->throw_if_canceled();
5285
5286 if (!is_wipe_tower_type2) {
5287 // in BBL machine, wipe tower is only use to prime extruder. So just use a global wipe volume.
5288 WipeTower wipe_tower(m_config, m_plate_index, m_origin, m_wipe_tower_data.tool_ordering.first_extruder(),
5289 m_wipe_tower_data.tool_ordering.empty() ? 0.f : m_wipe_tower_data.tool_ordering.back().print_z, m_wipe_tower_data.tool_ordering.all_extruders());
5290 // Orca: the tower's first-layer flow follows the user's first-layer flow ratio (BBS reads
5291 // its initial_layer_flow_ratio here — STUDIO-14254; first_layer_flow_ratio is Orca's analog,
5292 // default 1.0 in both). Honor the set_other_flow_ratios gate that governs the option
5293 // everywhere else.
5294 wipe_tower.set_first_layer_flow_ratio(m_default_object_config.set_other_flow_ratios
5295 ? float(m_default_region_config.first_layer_flow_ratio)
5296 : 1.f);
5297 wipe_tower.set_has_tpu_filament(this->has_tpu_filament());
5298 // Per-layer filament->nozzle grouping. sort_and_build_data() above publishes it on the Print
5299 // for by-layer prints; by-object prints publish only later (psSkirtBrim), so fall back to the
5300 // ToolOrdering's own copy there. set_extruder() below dereferences it, so it must be set first.
5301 auto print_group_result = get_layered_nozzle_group_result();
5302 const MultiNozzleUtils::LayeredNozzleGroupResult &nozzle_group_result =
5303 print_group_result ? *print_group_result : m_wipe_tower_data.tool_ordering.get_layered_nozzle_group_result();
5304 wipe_tower.set_nozzle_group_result(nozzle_group_result);
5305 {
5306 // Orca: acceleration options are object-scope (PrintConfig members in BBS), so resolve
5307 // the per-variant columns here; initial_layer_travel_acceleration is FloatOrPercent
5308 // over travel_acceleration and needs the full config to resolve.
5309 std::vector<double> first_layer_travel_accels;
5310 for (size_t i = 0; i < m_config.initial_layer_travel_acceleration.values.size(); ++i)
5311 first_layer_travel_accels.emplace_back(m_full_print_config.get_abs_value_at("initial_layer_travel_acceleration", i));
5312 wipe_tower.set_accelerations(m_default_object_config.default_acceleration.values,
5313 m_default_object_config.initial_layer_acceleration.values,
5314 m_default_object_config.travel_acceleration.values,
5315 first_layer_travel_accels);
5316 }
5317 // Feed the has_filament_switcher device flag (develop-only dynamic key, read defensively from
5318 // the full config — no shipping profile sets it) and the shared printable bed used by the PETG
5319 // pre-extrusion offset clamp. Both are inert unless has_filament_switcher is set.
5320 {
5321 const ConfigOptionBool* hfs = m_full_print_config.option<ConfigOptionBool>("has_filament_switcher");
5322 wipe_tower.set_has_filament_switcher(hfs && hfs->value);
5323 }
5324 wipe_tower.set_shared_print_bed(this->get_extruder_shared_printable_polygon());
5325 // Set the extruder & material properties at the wipe tower object.
5326 for (size_t i = 0; i < number_of_extruders; ++i)
5327 wipe_tower.set_extruder(i, m_config);
5328
5329 // BBS: remove priming logic
5330 // m_wipe_tower_data.priming = Slic3r::make_unique<std::vector<WipeTower::ToolChangeResult>>(
5331 // wipe_tower.prime((float)this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false));
5332
5333 std::set<int> used_filament_ids;
5334
5335 // Lets go through the wipe tower layers and determine pairs of extruder changes for each
5336 // to pass to wipe_tower (so that it can use it for planning the layout of the tower)
5337 {
5338 // Get wiping matrix to get number of extruders and convert vector<double> to vector<float>:
5339 bool is_mutli_extruder = m_config.nozzle_diameter.values.size() > 1;
5340 size_t nozzle_nums = m_config.nozzle_diameter.values.size();
5341 using FlushMatrix = std::vector<std::vector<float>>;
5342 std::vector<FlushMatrix> multi_extruder_flush;
5343 for (size_t nozzle_id = 0; nozzle_id < nozzle_nums; ++nozzle_id) {
5344 std::vector<float> flush_matrix(cast<float>(get_flush_volumes_matrix(m_config.flush_volumes_matrix.values, nozzle_id, nozzle_nums)));
5345 std::vector<std::vector<float>> wipe_volumes;
5346 for (unsigned int i = 0; i < number_of_extruders; ++i)
5347 wipe_volumes.push_back(std::vector<float>(flush_matrix.begin() + i * number_of_extruders, flush_matrix.begin() + (i + 1) * number_of_extruders));
5348
5349 multi_extruder_flush.emplace_back(wipe_volumes);
5350 }
5351
5352 // Per-carousel-slot purge tracking via NozzleStatusRecorder (BBS pattern); the layered
5353 // group result set on the tower above resolves each filament to its nozzle slot per layer.
5354 MultiNozzleUtils::NozzleStatusRecorder nozzle_recorder;
5355
5356 std::vector<int>filament_maps = get_filament_maps();
5357 int layer_idx = -1;
5358
5359 unsigned int current_filament_id = m_wipe_tower_data.tool_ordering.first_extruder();
5360 // Initialize NozzleStatusRecorder with the first filament's carousel slot
5361 {
5362 auto nozzle = nozzle_group_result.get_nozzle_for_filament(current_filament_id, layer_idx);
5363 if (nozzle)
5364 nozzle_recorder.set_nozzle_status(nozzle->group_id, current_filament_id, nozzle->extruder_id);
5365 }
5366
5367 for (auto& layer_tools : m_wipe_tower_data.tool_ordering.layer_tools()) { // for all layers
5368 ++layer_idx;
5369
5370 if (!layer_tools.has_wipe_tower) continue;
5371 bool first_layer = &layer_tools == &m_wipe_tower_data.tool_ordering.front();
5372 wipe_tower.plan_toolchange((float)layer_tools.print_z, (float)layer_tools.wipe_tower_layer_height, current_filament_id, current_filament_id);
5373
5374 used_filament_ids.insert(layer_tools.extruders.begin(), layer_tools.extruders.end());
5375
5376 for (const auto filament_id : layer_tools.extruders) {
5377 if (filament_id == current_filament_id)
5378 continue;
5379
5380 float volume_to_purge = 0;
5381
5382 // Per-carousel-slot purge tracking via NozzleStatusRecorder
5383 {
5384 auto nozzle_info = nozzle_group_result.get_nozzle_for_filament(filament_id, layer_idx);
5385 if (nozzle_info) {
5386 int extruder_id = nozzle_info->extruder_id;
5387 int nozzle_id = nozzle_info->group_id;
5388 int prev_nozzle_filament = nozzle_recorder.get_filament_in_nozzle(nozzle_id);
5389
5390 if (!nozzle_recorder.is_nozzle_empty(nozzle_id) &&
5391 static_cast<int>(filament_id) != prev_nozzle_filament) {
5392 volume_to_purge = multi_extruder_flush[extruder_id][prev_nozzle_filament][filament_id];
5393 // Fast purge mode uses flush_multiplier_fast; Default is inert.
5394 float flush_multiplier = (m_config.prime_volume_mode == PrimeVolumeMode::pvmFast)
5395 ? m_config.flush_multiplier_fast.get_at(extruder_id)
5396 : m_config.flush_multiplier.get_at(extruder_id);
5397 volume_to_purge *= flush_multiplier;
5398 volume_to_purge = layer_tools.wiping_extrusions().mark_wiping_extrusions(
5399 *this, current_filament_id, filament_id, volume_to_purge);
5400 }
5401 nozzle_recorder.set_nozzle_status(nozzle_id, filament_id, extruder_id);
5402 }
5403 }
5404
5405 //During the filament change, the extruder will extrude an extra length of grab_length for the corresponding detection, so the purge can reduce this length.
5406 int grab_extruder_id = filament_maps[filament_id] - 1;
5407 float grab_purge_volume = m_config.grab_length.get_at(grab_extruder_id) * 2.4; //(diameter/2)^2*PI=2.4
5408 volume_to_purge = std::max(0.f, volume_to_purge - grab_purge_volume);
5409
5410 // Prime volume per-filament: the tower now picks extruder-change vs nozzle-change
5411 // (carousel) internally per plan layer, so pass both candidates (BBS pattern).
5412 float wipe_volume_ec = filament_id < m_config.filament_prime_volume.values.size()
5413 ? m_config.filament_prime_volume.values[filament_id]
5414 : (float) m_config.prime_volume;
5415 float wipe_volume_nc = filament_id < m_config.filament_prime_volume_nc.values.size()
5416 ? m_config.filament_prime_volume_nc.values[filament_id]
5417 : (float) m_config.prime_volume;
5418 if (m_config.prime_volume_mode == PrimeVolumeMode::pvmSaving) {
5419 wipe_volume_ec = 15.f;
5420 wipe_volume_nc = 15.f;
5421 }
5422
5423 wipe_tower.plan_toolchange((float)layer_tools.print_z, (float)layer_tools.wipe_tower_layer_height, current_filament_id, filament_id,
5424 wipe_volume_ec, wipe_volume_nc, volume_to_purge);
5425 current_filament_id = filament_id;
5426 }
5427 layer_tools.wiping_extrusions().ensure_perimeters_infills_order(*this);
5428
5429 // if enable timelapse, slice all layer
5430 if (m_config.enable_wrapping_detection || enable_timelapse_print()) {
5431 if (layer_tools.wipe_tower_partitions == 0) wipe_tower.set_last_layer_extruder_fill(false);
5432 continue;
5433 }
5434
5435 if (&layer_tools == &m_wipe_tower_data.tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0)
5436 break;
5437 }
5438 }
5439
5440 wipe_tower.set_used_filament_ids(std::vector<int>(used_filament_ids.begin(), used_filament_ids.end()));
5441
5442 std::vector<int> categories;
5443 for (size_t i = 0; i < m_config.filament_adhesiveness_category.values.size(); ++i) {
5444 categories.push_back(m_config.filament_adhesiveness_category.get_at(i));
5445 }
5446 wipe_tower.set_filament_categories(categories);
5447
5448 // Generate the wipe tower layers.
5449 m_wipe_tower_data.tool_changes.reserve(m_wipe_tower_data.tool_ordering.layer_tools().size());
5450 wipe_tower.generate_new(m_wipe_tower_data.tool_changes);
5451 m_wipe_tower_data.depth = wipe_tower.get_depth();
5452 m_wipe_tower_data.width = wipe_tower.width();
5453 m_wipe_tower_data.brim_width = wipe_tower.get_brim_width();
5454 m_wipe_tower_data.bbx = wipe_tower.get_bbx();
5455 m_wipe_tower_data.rib_offset = wipe_tower.get_rib_offset();
5456
5457 // Unload the current filament over the purge tower.
5458 coordf_t layer_height = m_objects.front()->config().layer_height.value;
5459 bool generate_final_purge = true;
5460 if (m_wipe_tower_data.tool_ordering.back().wipe_tower_partitions > 0) {
5461 // The wipe tower goes up to the last layer of the print.
5462 if (wipe_tower.layer_finished()) {
5463 // The wipe tower is printed to the top of the print and it has no space left for the final extruder purge.
5464 // Lift Z to the next layer.
5465 wipe_tower.set_layer(float(m_wipe_tower_data.tool_ordering.back().print_z + layer_height), float(layer_height), 0, false,
5466 true);
5467 } else {
5468 // There is yet enough space at this layer of the wipe tower for the final purge.
5469 }
5470 } else {
5471 // The wipe tower does not reach the last print layer.
5472 // Skip final purge to avoid generating purge lines in mid-air.
5473 assert(m_wipe_tower_data.tool_ordering.back().wipe_tower_partitions == 0);
5474 generate_final_purge = false;
5475 }
5476 m_wipe_tower_data.final_purge = generate_final_purge
5477 ? Slic3r::make_unique<WipeTower::ToolChangeResult>(wipe_tower.tool_change((unsigned int)(-1)))
5478 : Slic3r::make_unique<WipeTower::ToolChangeResult>();
5479
5480 m_wipe_tower_data.used_filament = wipe_tower.get_used_filament();
5481 m_wipe_tower_data.number_of_toolchanges = wipe_tower.get_number_of_toolchanges();
5482 m_wipe_tower_data.construct_mesh(wipe_tower.width(), wipe_tower.get_depth(), wipe_tower.get_height(), wipe_tower.get_brim_width(), config().wipe_tower_wall_type.value == WipeTowerWallType::wtwRib,
5483 wipe_tower.get_rib_width(), wipe_tower.get_rib_length(), config().wipe_tower_fillet_wall.value);
5484 const Vec3d origin = this->get_plate_origin();
5485 m_fake_wipe_tower.rib_offset = wipe_tower.get_rib_offset();
5486 m_fake_wipe_tower.set_fake_extrusion_data(wipe_tower.position() + m_fake_wipe_tower.rib_offset, wipe_tower.width(), wipe_tower.get_height(), wipe_tower.get_layer_height(),
5487 m_wipe_tower_data.depth,
5488 m_wipe_tower_data.brim_width, {scale_(origin.x()), scale_(origin.y())});
5489 m_fake_wipe_tower.outer_wall = wipe_tower.get_outer_wall();
5490 } else {
5491 // Get wiping matrix to get number of extruders and convert vector<double> to vector<float>:
5492 std::vector<float> flush_matrix(cast<float>(m_config.flush_volumes_matrix.values));
5493 // Extract purging volumes for each extruder pair:
5494 std::vector<std::vector<float>> wipe_volumes;
5495 for (unsigned int i = 0; i<number_of_extruders; ++i)
5496 wipe_volumes.push_back(std::vector<float>(flush_matrix.begin()+i*number_of_extruders, flush_matrix.begin()+(i+1)*number_of_extruders));
5497
5498 // Orca: itertate over wipe_volumes and change the non-zero values to the prime_volume
5499 if ((!m_config.purge_in_prime_tower || !m_config.single_extruder_multi_material) && is_wipe_tower_type2) {
5500 for (unsigned int i = 0; i < number_of_extruders; ++i) {
5501 for (unsigned int j = 0; j < number_of_extruders; ++j) {
5502 if (wipe_volumes[i][j] > 0) {
5503 wipe_volumes[i][j] = m_config.prime_volume;
5504 }
5505 }
5506 }
5507 }
5508 // Initialize the wipe tower.
5509 WipeTower2 wipe_tower(m_config, m_default_region_config, m_plate_index, m_origin, wipe_volumes,
5510 m_wipe_tower_data.tool_ordering.first_extruder());
5511
5512 // wipe_tower.set_retract();
5513 // wipe_tower.set_zhop();
5514
5515 // Set the extruder & material properties at the wipe tower object.
5516 for (size_t i = 0; i < number_of_extruders; ++i)
5517 wipe_tower.set_extruder(i, m_config);
5518
5520 wipe_tower.prime((float)this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false));
5521
5522 // Lets go through the wipe tower layers and determine pairs of extruder changes for each
5523 // to pass to wipe_tower (so that it can use it for planning the layout of the tower)
5524 {
5525 unsigned int current_extruder_id = m_wipe_tower_data.tool_ordering.all_extruders().back();
5526 for (auto &layer_tools : m_wipe_tower_data.tool_ordering.layer_tools()) { // for all layers
5527 if (!layer_tools.has_wipe_tower)
5528 continue;
5529 bool first_layer = &layer_tools == &m_wipe_tower_data.tool_ordering.front();
5530 wipe_tower.plan_toolchange((float) layer_tools.print_z, (float) layer_tools.wipe_tower_layer_height, current_extruder_id,
5531 current_extruder_id, false);
5532 for (const auto extruder_id : layer_tools.extruders) {
5533 if ((first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back()) || extruder_id !=
5534 current_extruder_id) {
5535 float volume_to_wipe = m_config.prime_volume;
5536 if (m_config.purge_in_prime_tower && m_config.single_extruder_multi_material) {
5537 volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id]; // total volume to wipe after this toolchange
5538 volume_to_wipe *= m_config.flush_multiplier.get_at(0);
5539 // Not all of that can be used for infill purging:
5540 volume_to_wipe -= (float) m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
5541
5542 // try to assign some infills/objects for the wiping:
5543 volume_to_wipe = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, current_extruder_id, extruder_id,
5544 volume_to_wipe);
5545
5546 // add back the minimal amount toforce on the wipe tower:
5547 volume_to_wipe += (float) m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
5548 }
5549
5550 // request a toolchange at the wipe tower with at least volume_to_wipe purging amount
5551 wipe_tower.plan_toolchange((float) layer_tools.print_z, (float) layer_tools.wipe_tower_layer_height,
5552 current_extruder_id, extruder_id, volume_to_wipe);
5553 current_extruder_id = extruder_id;
5554 }
5555 }
5556 layer_tools.wiping_extrusions().ensure_perimeters_infills_order(*this);
5557 if (&layer_tools == &m_wipe_tower_data.tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0)
5558 break;
5559 }
5560 }
5561
5562 // Generate the wipe tower layers.
5563 m_wipe_tower_data.tool_changes.reserve(m_wipe_tower_data.tool_ordering.layer_tools().size());
5564 wipe_tower.generate(m_wipe_tower_data.tool_changes);
5565 m_wipe_tower_data.depth = wipe_tower.get_depth();
5566 m_wipe_tower_data.width = wipe_tower.width();
5567 m_wipe_tower_data.z_and_depth_pairs = wipe_tower.get_z_and_depth_pairs();
5568 m_wipe_tower_data.brim_width = wipe_tower.get_brim_width();
5569 m_wipe_tower_data.height = wipe_tower.get_wipe_tower_height();
5570 m_wipe_tower_data.bbx = wipe_tower.get_bbx();
5571 m_wipe_tower_data.rib_offset = wipe_tower.get_rib_offset();
5572
5573 // Unload the current filament over the purge tower.
5574 coordf_t layer_height = m_objects.front()->config().layer_height.value;
5575 bool generate_final_purge = true;
5576 if (m_wipe_tower_data.tool_ordering.back().wipe_tower_partitions > 0) {
5577 // The wipe tower goes up to the last layer of the print.
5578 if (wipe_tower.layer_finished()) {
5579 // The wipe tower is printed to the top of the print and it has no space left for the final extruder purge.
5580 // Lift Z to the next layer.
5581 wipe_tower.set_layer(float(m_wipe_tower_data.tool_ordering.back().print_z + layer_height), float(layer_height), 0, false,
5582 true);
5583 } else {
5584 // There is yet enough space at this layer of the wipe tower for the final purge.
5585 }
5586 } else {
5587 // The wipe tower does not reach the last print layer.
5588 // Skip final purge to avoid generating purge lines in mid-air.
5589 assert(m_wipe_tower_data.tool_ordering.back().wipe_tower_partitions == 0);
5590 generate_final_purge = false;
5591 }
5592 m_wipe_tower_data.final_purge = generate_final_purge
5593 ? Slic3r::make_unique<WipeTower::ToolChangeResult>(wipe_tower.tool_change((unsigned int)(-1)))
5594 : Slic3r::make_unique<WipeTower::ToolChangeResult>();
5595
5596 m_wipe_tower_data.used_filament = wipe_tower.get_used_filament();
5597 m_wipe_tower_data.number_of_toolchanges = wipe_tower.get_number_of_toolchanges();
5598 // Only a cone wall prints the stabilization cone, whatever cone angle the config holds.
5599 const float cone_angle = config().wipe_tower_wall_type.value == WipeTowerWallType::wtwCone ?
5600 static_cast<float>(config().wipe_tower_cone_angle.value) : 0.f;
5601 m_wipe_tower_data.construct_mesh(wipe_tower.width(), wipe_tower.get_depth(),
5602 wipe_tower.get_wipe_tower_height(), wipe_tower.get_brim_width(),
5603 config().wipe_tower_wall_type.value == WipeTowerWallType::wtwRib,
5604 wipe_tower.get_rib_width(), wipe_tower.get_rib_length(),
5605 config().wipe_tower_fillet_wall.value, cone_angle);
5606 const Vec3d origin = Vec3d::Zero();
5607 // FakeWipeTower::pos is a bed-frame translation applied after rotation
5608 // (getFakeExtrusionPathsFromWipeTower2 rotates about the local origin), so the
5609 // tower-local rib offset must be rotated into the bed frame first.
5610 m_fake_wipe_tower.rib_offset = Eigen::Rotation2Df(Geometry::deg2rad((float)config().wipe_tower_rotation_angle.value)) *
5611 wipe_tower.get_rib_offset();
5612 m_fake_wipe_tower.set_fake_extrusion_data(wipe_tower.position() + m_fake_wipe_tower.rib_offset, wipe_tower.width(), wipe_tower.get_wipe_tower_height(),
5614 m_wipe_tower_data.z_and_depth_pairs, m_wipe_tower_data.brim_width,
5615 config().wipe_tower_rotation_angle, cone_angle,
5616 {scale_(origin.x()), scale_(origin.y())});
5617 }
5618
5619 // The clamps and checks above work from estimates; re-test the exact generated footprint
5620 // so an off-plate tower fails with a clear error instead of exporting unprintable G-code
5621 // (validate() only sees the mesh on its next run).
5622 if (m_wipe_tower_data.wipe_tower_mesh_data) {
5623 Polygon footprint = m_wipe_tower_data.wipe_tower_mesh_data->bottom; // includes brim and rib offset
5624 footprint.rotate(Geometry::deg2rad(m_config.wipe_tower_rotation_angle.value));
5625 footprint.translate(Point(scale_(m_config.wipe_tower_x.get_at(m_plate_index)),
5626 scale_(m_config.wipe_tower_y.get_at(m_plate_index))));
5627 const Polygons printable_polys = this->get_extruder_shared_printable_polygon();
5628 if (!printable_polys.empty() && !diff(Polygons{footprint}, printable_polys).empty()) {
5629 const BoundingBox fp = get_extents(footprint);
5630 const BoundingBox pr = get_extents(printable_polys);
5631 BOOST_LOG_TRIVIAL(error) << boost::format("wipe tower footprint [%1%,%2%]-[%3%,%4%] leaves printable [%5%,%6%]-[%7%,%8%]") %
5632 unscaled(fp.min.x()) % unscaled(fp.min.y()) % unscaled(fp.max.x()) % unscaled(fp.max.y()) %
5633 unscaled(pr.min.x()) % unscaled(pr.min.y()) % unscaled(pr.max.x()) % unscaled(pr.max.y());
5634 throw Slic3r::SlicingError(L("Prime tower is partially outside the printable area, and it cannot be printed.\n"));
5635 }
5636 // The cutter/purge corner is a physical obstacle — the brim must stay out like the body.
5637 if (!intersection(get_bed_excluded_area(m_config), Polygons{footprint}).empty())
5638 throw Slic3r::SlicingError(L("Prime tower is too close to an exclusion area, and collisions will be caused.\n"));
5639 }
5640}
#define L(s)
Definition ExtrusionEntity.cpp:25
DynamicPrintConfig m_full_print_config
Definition PrintBase.hpp:562
int m_plate_index
Definition PrintBase.hpp:567
bool enable_timelapse_print() const
Definition Print.cpp:5229
std::vector< int > get_filament_maps() const
Definition Print.cpp:4888
Vec3d m_origin
Definition Print.hpp:1570
PrintRegionConfig m_default_region_config
Definition Print.hpp:1502
WipeTowerData m_wipe_tower_data
Definition Print.hpp:1557
std::shared_ptr< MultiNozzleUtils::LayeredNozzleGroupResult > get_layered_nozzle_group_result() const
Definition Print.hpp:1221
WipeTowerType wipe_tower_type() const
Definition Print.hpp:1343
Polygons get_extruder_shared_printable_polygon() const
Definition Print.cpp:5021
PrintObjectConfig m_default_object_config
Definition Print.hpp:1501
FakeWipeTower m_fake_wipe_tower
Definition Print.hpp:1578
bool has_tpu_filament() const
Definition Print.cpp:250
const PrintConfig & config() const
Definition Print.hpp:1131
constexpr T deg2rad(const T angle)
Definition Geometry.hpp:312
coord_t height(const BoundingBox &box)
Definition Arrange.cpp:1185
Eigen::Matrix< double, 3, 1, Eigen::DontAlign > Vec3d
Definition Point.hpp:68
layer_height((ConfigOptionInt, faded_layers))((ConfigOptionFloat
std::unique_ptr< T > make_unique(Args &&... args)
Definition libslic3r.h:239
BoundingBox get_extents(const ExPolygonsWithIds &e)
Definition Emboss.cpp:1374
std::vector< std::vector< float > > FlushMatrix
Definition ToolOrderUtils.hpp:17
static std::vector< T > get_flush_volumes_matrix(const std::vector< T > &fv_matrix, size_t extruder_id=-1, size_t nozzle_nums=1)
Definition PrintConfig.hpp:2685
@ pvmFast
Definition PrintConfig.hpp:659
@ pvmSaving
Definition PrintConfig.hpp:658
constexpr Eigen::Matrix< Tout, 2, EigenArgs... > unscaled(const Slic3r::Point &v) noexcept
Definition Arrange.cpp:81
@ Type2
Definition PrintConfig.hpp:115
Polygons get_bed_excluded_area(const PrintConfig &cfg)
Definition PrintConfig.cpp:14376
zaa_minimize_perimeter_height flush_multiplier((ConfigOptionEnum< PrimeVolumeMode >, prime_volume_mode))((ConfigOptionFloats
@ wtwRib
Definition PrintConfig.hpp:608
@ wtwCone
Definition PrintConfig.hpp:607
zaa_minimize_perimeter_height wipe_tower_fillet_wall((ConfigOptionInt, wipe_tower_filament))((ConfigOptionFloats
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
@ WipeTower
Definition Types.hpp:158

References _make_wipe_tower(), Slic3r::cast(), config(), Slic3r::Geometry::deg2rad(), Slic3r::diff(), enable_timelapse_print(), EPSILON, Slic3r::flush_multiplier(), Slic3r::WipeTower2::generate(), Slic3r::WipeTower::generate_new(), Slic3r::WipeTower2::get_bbx(), Slic3r::WipeTower::get_bbx(), Slic3r::get_bed_excluded_area(), Slic3r::WipeTower2::get_brim_width(), Slic3r::WipeTower::get_brim_width(), Slic3r::WipeTower2::get_depth(), Slic3r::WipeTower::get_depth(), Slic3r::get_extents(), get_extruder_shared_printable_polygon(), Slic3r::MultiNozzleUtils::NozzleStatusRecorder::get_filament_in_nozzle(), get_filament_maps(), Slic3r::get_flush_volumes_matrix(), Slic3r::WipeTower::get_height(), Slic3r::WipeTower::get_layer_height(), get_layered_nozzle_group_result(), Slic3r::MultiNozzleUtils::LayeredNozzleGroupResult::get_nozzle_for_filament(), Slic3r::WipeTower2::get_number_of_toolchanges(), Slic3r::WipeTower::get_number_of_toolchanges(), Slic3r::WipeTower::get_outer_wall(), get_plate_origin(), Slic3r::WipeTower2::get_rib_length(), Slic3r::WipeTower::get_rib_length(), Slic3r::WipeTower2::get_rib_offset(), Slic3r::WipeTower::get_rib_offset(), Slic3r::WipeTower2::get_rib_width(), Slic3r::WipeTower::get_rib_width(), Slic3r::WipeTower2::get_used_filament(), Slic3r::WipeTower::get_used_filament(), Slic3r::WipeTower2::get_wipe_tower_height(), Slic3r::WipeTower2::get_z_and_depth_pairs(), Slic3r::LayerTools::has_object, Slic3r::LayerTools::has_support, has_tpu_filament(), Slic3r::LayerTools::has_wipe_tower, Slic3r::initial_layer_print_height(), Slic3r::intersection(), Slic3r::MultiNozzleUtils::NozzleStatusRecorder::is_nozzle_empty(), L, Slic3r::WipeTower2::layer_finished(), Slic3r::WipeTower::layer_finished(), Slic3r::layer_height(), m_config, m_default_object_config, m_default_region_config, m_fake_wipe_tower, Slic3r::PrintBaseWithState< PrintStep, psCount >::m_full_print_config, m_objects, m_origin, Slic3r::PrintBaseWithState< PrintStep, psCount >::m_plate_index, m_wipe_tower_data, Slic3r::make_unique(), Slic3r::BoundingBoxBase< PointType, APointsType >::max, Slic3r::BoundingBoxBase< PointType, APointsType >::min, Slic3r::WipeTower2::plan_toolchange(), Slic3r::WipeTower::plan_toolchange(), Slic3r::WipeTower2::position(), Slic3r::WipeTower::position(), Slic3r::WipeTower2::prime(), Slic3r::LayerTools::print_z, Slic3r::pvmFast, Slic3r::pvmSaving, Slic3r::MultiPoint::rotate(), scale_, Slic3r::WipeTower::set_accelerations(), Slic3r::WipeTower2::set_extruder(), Slic3r::WipeTower::set_extruder(), Slic3r::WipeTower::set_filament_categories(), Slic3r::WipeTower::set_first_layer_flow_ratio(), Slic3r::WipeTower::set_has_filament_switcher(), Slic3r::WipeTower::set_has_tpu_filament(), Slic3r::WipeTower::set_last_layer_extruder_fill(), Slic3r::WipeTower2::set_layer(), Slic3r::WipeTower::set_layer(), Slic3r::WipeTower::set_nozzle_group_result(), Slic3r::MultiNozzleUtils::NozzleStatusRecorder::set_nozzle_status(), Slic3r::WipeTower::set_shared_print_bed(), Slic3r::WipeTower::set_used_filament_ids(), skirt_first_layer_height(), Slic3r::PrintBaseWithState< PrintStep, psCount >::throw_if_canceled(), Slic3r::WipeTower2::tool_change(), Slic3r::WipeTower::tool_change(), Slic3r::MultiPoint::translate(), Slic3r::Type2, Slic3r::unscaled(), Slic3r::ConfigOptionSingle< T >::value, Slic3r::WipeTower2::width(), Slic3r::WipeTower::width(), Slic3r::wipe_tower_fillet_wall(), wipe_tower_type(), Slic3r::wtwCone, and Slic3r::wtwRib.

Referenced by _make_wipe_tower().

Here is the caller graph for this function:

◆ _plan_belt_purge()

void Slic3r::Print::_plan_belt_purge ( )
private
147{
148 m_wipe_tower_data.clear();
149
150 // psWipeTower may be invalidated without posSlice (for example after a
151 // filament-map or tool-ordering change). Restore a prism shortened by the
152 // previous plan so a newly higher toolchange can use its original layers.
153 for (PrintObject *po : m_objects)
154 if (po->config().belt_purge_tower_object.value)
155 po->belt_undo_purge_plan();
156
157 // Must run before ToolOrdering is built: LayerTools merge per-object layer
158 // print_z values, and the prism only absorbs purge where its (snapped)
159 // layers coincide with the toolchange layers.
161
162 const unsigned int number_of_extruders = (unsigned int) m_config.filament_colour.values.size();
163
164 // No initial priming extrusions: there is no tower to prime on.
165 m_wipe_tower_data.tool_ordering = ToolOrdering(*this, (unsigned int) -1, false);
166 m_wipe_tower_data.tool_ordering.sort_and_build_data(*this, (unsigned int) -1, false);
167
168 if (m_wipe_tower_data.tool_ordering.empty() || m_wipe_tower_data.tool_ordering.last_extruder() == unsigned(-1))
169 throw Slic3r::SlicingError(_u8L("The print is empty. The model is not printable with current print settings."));
170
171 // Is there any filament change at all? Not ToolOrdering::has_wipe_tower(): that reads the
172 // FIRST layer's flag, and on a belt the first layer may be a brim apron band, which carries
173 // neither object nor support and so never gets the flag even when the print changes filament.
174 {
175 bool any_change = false;
176 unsigned int cur = m_wipe_tower_data.tool_ordering.first_extruder();
177 for (const auto &lt : m_wipe_tower_data.tool_ordering.layer_tools())
178 for (const unsigned int e : lt.extruders)
179 if (e != cur) { any_change = true; cur = e; }
180 if (! any_change)
181 return;
182 }
183
184 this->throw_if_canceled();
185
186 // Flush volumes per filament pair, mirroring the generic wipe tower path:
187 // full flush matrix for single extruder multi material with purging enabled,
188 // plain prime volume otherwise.
189 std::vector<float> flush_matrix(cast<float>(
190 get_flush_volumes_matrix(m_config.flush_volumes_matrix.values, 0, m_config.nozzle_diameter.values.size())));
191 std::vector<std::vector<float>> wipe_volumes;
192 for (unsigned int i = 0; i < number_of_extruders; ++i)
193 wipe_volumes.push_back(std::vector<float>(flush_matrix.begin() + i * number_of_extruders,
194 flush_matrix.begin() + (i + 1) * number_of_extruders));
195 const bool use_flush_matrix = m_config.purge_in_prime_tower && m_config.single_extruder_multi_material;
196 const float flush_multiplier = (float) m_config.flush_multiplier.get_at(0);
197
198 // Cancel the purge prism early: pre-scan the tool ordering for the highest
199 // print_z that actually has a toolchange, then drop the prism's layers above
200 // it so the tower stops at the last color swap (saves filament/time). This
201 // MUST happen before the marking loop below: ensure_perimeters_infills_order
202 // force-overrides the prism's extrusions on every layer (it is a dedicated
203 // flush object), so truncating afterwards would leave dangling overrides
204 // pointing into deleted layers.
205 {
206 // The tool ordering covers the WHOLE print, and the prism is a printed
207 // object in it. Left unbounded, the scan below sees the prism's own
208 // toolchanges on layers above every model object -- the prism runs past
209 // them by design (ramp/height compensation at the tilted ends) -- so
210 // last_tc_z lands at the prism's own top and the truncation cancels
211 // nothing. The tower ends up justifying its own existence.
212 //
213 // Nothing above the tallest printed object can require a color change,
214 // so bound the scan there. On MCTEST5 that is 197 toolchanges spanning
215 // z=154.00..193.20 with the tallest object topping out at 153.80, i.e.
216 // 39.4 mm of tower that no swap ever needed.
217 // Support layers count too: on a belt they can extend above the object's
218 // own top, and a toolchange there is a real one.
219 double obj_top_z = -1.;
220 for (const PrintObject *po : m_objects) {
221 if (po->config().belt_purge_tower_object.value)
222 continue;
223 if (!po->layers().empty())
224 obj_top_z = std::max(obj_top_z, po->layers().back()->print_z);
225 if (!po->support_layers().empty())
226 obj_top_z = std::max(obj_top_z, po->support_layers().back()->print_z);
227 }
228
229 double last_tc_z = -1.;
230 unsigned int cur_ext = m_wipe_tower_data.tool_ordering.first_extruder();
231 for (const auto &lt : m_wipe_tower_data.tool_ordering.layer_tools()) {
232 // layer_tools() is ordered by print_z ascending.
233 if (obj_top_z >= 0. && lt.print_z > obj_top_z + EPSILON)
234 break;
235 for (const unsigned int e : lt.extruders)
236 if (e != cur_ext) { last_tc_z = lt.print_z; cur_ext = e; }
237 }
238 // Deliberately NOT cancelling the prism outright when no object toolchange
239 // exists: belt_truncate_layers_above(0.) empties m_layers, and an object
240 // with zero layers is not something the rest of the pipeline expects. The
241 // GUI already declines to create a prism unless more than one filament is
242 // in use, so this case is a stale prism, not a hot path -- leave it whole
243 // rather than risk a zero-layer object.
244 if (last_tc_z >= 0.)
245 for (PrintObject *po : m_objects)
246 if (po->config().belt_purge_tower_object.value && !po->layers().empty()) {
247 po->belt_truncate_layers_above(last_tc_z);
248 break;
249 }
250 }
251
252 // The prism only absorbs purge at toolchange layers whose print_z coincides
253 // with one of its own layers.
254 PrintObject *prism_po = nullptr;
255 for (PrintObject *po : m_objects)
256 if (po->config().belt_purge_tower_object.value && !po->layers().empty()) { prism_po = po; break; }
257
258 float total_leftover = 0.f;
259 float worst_layer_leftover = 0.f;
260 double worst_layer_z = 0.;
261
262 unsigned int current_extruder_id = m_wipe_tower_data.tool_ordering.first_extruder();
263 for (auto &layer_tools : m_wipe_tower_data.tool_ordering.layer_tools()) {
264 float layer_leftover = 0.f;
265 for (const unsigned int extruder_id : layer_tools.extruders) {
266 if (extruder_id == current_extruder_id)
267 continue;
268 float volume_to_wipe = use_flush_matrix ?
269 wipe_volumes[current_extruder_id][extruder_id] * flush_multiplier :
270 (float) m_config.prime_volume;
271 float leftover = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, current_extruder_id, extruder_id,
272 volume_to_wipe);
273 layer_leftover += leftover;
274 current_extruder_id = extruder_id;
275 }
276
277 // Plastic saving: drop the prism's fills that no toolchange on this layer
278 // claimed. At this point the prism's OVERRIDDEN fills are exactly the
279 // purge; the rest would print as solid infill in the prism's own filament
280 // for nothing -- which is the whole prism on a layer with no toolchange
281 // (141 of 692 layers on MCTEST5 before the truncation fix). Perimeters are
282 // left alone so the bar keeps a continuous wall along the belt.
283 //
284 // Non-destructive: the entities are stashed with their positions and put
285 // back by belt_restore_dropped_fills() at the top of the next plan. An
286 // earlier version deleted them outright, which broke replanning when a
287 // later tool ordering needed what this one had not claimed -- that is why
288 // it was removed rather than kept.
289 if (prism_po != nullptr) {
290 const auto &we = layer_tools.wiping_extrusions();
291 prism_po->belt_drop_unclaimed_fills(
292 prism_po->get_layer_at_printz(layer_tools.print_z, EPSILON),
293 [&we, prism_po](const ExtrusionEntity *e) { return we.is_entity_overridden(e, prism_po, 0); });
294 }
295
296 layer_tools.wiping_extrusions().ensure_perimeters_infills_order(*this);
297 if (layer_leftover > 0.f) {
298 total_leftover += layer_leftover;
299 if (layer_leftover > worst_layer_leftover) {
300 worst_layer_leftover = layer_leftover;
301 worst_layer_z = layer_tools.print_z;
302 }
303 }
304 this->throw_if_canceled();
305 }
306
307 if (total_leftover > 1.f) {
310 Slic3r::format(_u8L("The belt purge tower cannot absorb the full purge volume: %1% mm³ in total could not "
311 "be purged (worst layer: %2% mm³ at height %3%). The print may show color bleeding. "
312 "Increase the belt purge tower width, or reduce flushing volumes."),
313 int(std::ceil(total_leftover)), int(std::ceil(worst_layer_leftover)),
314 Slic3r::float_to_string_decimal_point(worst_layer_z, 2)));
315 }
316}
void active_step_add_warning(PrintStateBase::WarningLevel warning_level, const std::string &message, PrintStateBase::SlicingNotificationType message_id=PrintStateBase::SlicingDefaultNotification)
Definition PrintBase.hpp:599
@ CRITICAL
Definition PrintBase.hpp:63
void _align_belt_purge_layers()
Definition BeltPurge.cpp:85
std::string float_to_string_decimal_point(double value, int precision)
Definition LocalesUtils.cpp:108
std::string format(const char *fmt, TArgs &&... args)
Definition format.hpp:45
#define _u8L(s)
Definition I18N.hpp:11

References _align_belt_purge_layers(), _u8L, Slic3r::PrintBaseWithState< PrintStep, psCount >::active_step_add_warning(), Slic3r::PrintObject::belt_drop_unclaimed_fills(), Slic3r::cast(), Slic3r::PrintStateBase::CRITICAL, EPSILON, Slic3r::float_to_string_decimal_point(), Slic3r::flush_multiplier(), Slic3r::format(), Slic3r::get_flush_volumes_matrix(), Slic3r::PrintObject::get_layer_at_printz(), m_config, m_objects, m_wipe_tower_data, PrintObject, and Slic3r::PrintBaseWithState< PrintStep, psCount >::throw_if_canceled().

Here is the call graph for this function:

◆ active_step_add_warning()

void Slic3r::PrintBaseWithState< PrintStep, COUNT >::active_step_add_warning ( PrintStateBase::WarningLevel warning_level,
const std::string & message,
PrintStateBase::SlicingNotificationType message_id = PrintStateBase::SlicingDefaultNotification )
inlineinherited
601 {
603 if (active_step.second)
604 // Update UI.
605 this->status_update_warnings(static_cast<int>(active_step.first), warning_level, message, nullptr, message_id);
606 }
Definition PrintBase.hpp:592
std::mutex & state_mutex() const
Definition PrintBase.hpp:538
void status_update_warnings(int step, PrintStateBase::WarningLevel warning_level, const std::string &message, const PrintObjectBase *print_object=nullptr, PrintStateBase::SlicingNotificationType message_id=PrintStateBase::SlicingDefaultNotification)
Definition PrintBase.cpp:131

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

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

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

◆ append_model_object_extruders()

void Slic3r::Print::append_model_object_extruders ( const ModelObject & mo,
std::vector< unsigned int > & extruders )
staticprivate
704{
705 for (const ModelVolume* mv : mo.volumes) {
706 std::vector<int> volume_extruders = mv->get_extruders();
707 for (int extruder : volume_extruders) {
708 assert(extruder > 0);
709 extruders.push_back(extruder - 1);
710 }
711 }
712
713 // layer range
714 for (auto layer_range : mo.layer_config_ranges) {
715 if (layer_range.second.has("extruder")) {
716 //BBS: actually when user doesn't change filament by height range(value is default 0), height range should not save key "extruder".
717 //Don't know why height range always save key "extruder" because of no change(should only save difference)...
718 //Add protection here to avoid overflow
719 auto value = layer_range.second.option("extruder")->getInt();
720 if (value > 0)
721 extruders.push_back(value - 1);
722 }
723 }
724}

References extruders(), Slic3r::ModelVolume::get_extruders(), Slic3r::ModelObject::layer_config_ranges, and Slic3r::ModelObject::volumes.

Referenced by object_extruders(), and update_shrinkage_compensation().

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

◆ append_support_extruders()

bool Slic3r::Print::append_support_extruders ( const PrintObjectConfig & config,
unsigned int num_extruders,
std::vector< unsigned int > & extruders )
staticprivate
750{
751 bool uses_current_extruder = false;
752 assert(config.support_filament >= 0);
753 if (config.support_filament == 0)
754 uses_current_extruder = true;
755 else {
756 unsigned int i = (unsigned int)config.support_filament - 1;
757 extruders.emplace_back((i >= num_extruders) ? 0 : i);
758 }
759 assert(config.support_interface_filament >= 0);
760 if (config.support_interface_filament == 0)
761 uses_current_extruder = true;
762 else {
763 unsigned int i = (unsigned int)config.support_interface_filament - 1;
764 extruders.emplace_back((i >= num_extruders) ? 0 : i);
765 }
766 return uses_current_extruder;
767}

References config(), extruders(), and Slic3r::PrintObjectConfig.

Referenced by support_material_extruders(), and update_shrinkage_compensation().

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

◆ append_tool_change_extruders()

void Slic3r::Print::append_tool_change_extruders ( std::vector< unsigned int > & extruders) const
private
784{
785 //BBS
786 int num_extruders = m_config.filament_colour.size();
787 if (m_model.plates_custom_gcodes.find(m_model.curr_plate_index) != m_model.plates_custom_gcodes.end()) {
788 for (auto item : m_model.plates_custom_gcodes.at(m_model.curr_plate_index).gcodes) {
789 if (item.type == CustomGCode::Type::ToolChange && item.extruder <= num_extruders)
790 extruders.push_back((unsigned int)(item.extruder - 1));
791 }
792 }
793}
Model m_model
Definition PrintBase.hpp:561
@ ToolChange
Definition CustomGCode.hpp:21

References extruders(), m_config, Slic3r::PrintBaseWithState< PrintStep, psCount >::m_model, and Slic3r::CustomGCode::ToolChange.

Referenced by extruders(), and update_shrinkage_compensation().

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

◆ append_wipe_tower_extruder()

void Slic3r::Print::append_wipe_tower_extruder ( std::vector< unsigned int > & extruders) const
private
796{
797 // If a wipe tower filament is explicitly set, ensure it participates in tool ordering.
798 if (has_wipe_tower() && config().wipe_tower_filament != 0 && extruders.size() > 1) {
799 assert(config().wipe_tower_filament > 0 && config().wipe_tower_filament <= int(config().filament_diameter.size()));
800 extruders.emplace_back(config().wipe_tower_filament - 1); // config value is 1-based
801 }
802}
bool has_wipe_tower() const
Definition Print.cpp:5183

References config(), extruders(), and has_wipe_tower().

Referenced by extruders(), and update_shrinkage_compensation().

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

◆ apply()

Print::ApplyStatus Slic3r::Print::apply ( const Model & model,
DynamicPrintConfig config,
bool extruder_applied = false )
overridevirtual

Implements Slic3r::PrintBase.

1336{
1337#ifdef _DEBUG
1339#endif /* _DEBUG */
1340
1341 //BBS: add more logs
1342 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", Line %1%: enter")%__LINE__;
1343 // Normalize the config.
1344 new_full_config.option("print_settings_id", true);
1345 new_full_config.option("filament_settings_id", true);
1346 new_full_config.option("printer_settings_id", true);
1347
1348 // BBS
1349 std::vector <unsigned int> used_filaments = this->extruders(true);
1350 std::unordered_set <unsigned int> used_filament_set(used_filaments.begin(), used_filaments.end());
1351
1352 // A mixed slot is virtual: the filaments actually consumed are its components, so add them
1353 // to the used set or they would be treated as unused and stripped from the config.
1354 {
1355 auto* is_mixed_opt = new_full_config.option<ConfigOptionBools>("filament_is_mixed");
1356 auto* comp_strs_opt = new_full_config.option<ConfigOptionStrings>("filament_mixed_components");
1357 if (is_mixed_opt && comp_strs_opt && has_any_mixed_filament(is_mixed_opt->values)) {
1358 auto expanded = expand_mixed_filaments(used_filaments, is_mixed_opt->values, comp_strs_opt->values);
1359 used_filament_set.insert(expanded.begin(), expanded.end());
1360 }
1361 }
1362
1363 //new_full_config.normalize_fdm(used_filaments);
1364 new_full_config.normalize_fdm_1();
1365 t_config_option_keys changed_keys = new_full_config.normalize_fdm_2(objects().size(), used_filaments.size());
1366 if (changed_keys.size() > 0) {
1367 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", got changed_keys, size=%1%")%changed_keys.size();
1368 for (int i = 0; i < changed_keys.size(); i++)
1369 {
1370 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", i=%1%, key=%2%")%i %changed_keys[i];
1371 }
1372 }
1373 // On belt printers the support tilt follows the slicing rotation. The GUI keeps the two in
1374 // sync, but a CLI or 3MF edit of the rotation alone would otherwise leave supports on a stale tilt.
1375 if (const auto *belt_opt = new_full_config.option<ConfigOptionBool>("belt_printer"); belt_opt && belt_opt->value) {
1376 const auto *axis_opt = new_full_config.option<ConfigOptionEnum<BeltRotationAxis>>("belt_slice_rotation");
1377 const auto *angle_opt = new_full_config.option<ConfigOptionFloat>("belt_slice_rotation_angle");
1378 if (axis_opt && angle_opt) {
1379 const auto tilt = BeltTransformPipeline::physical_tilt(axis_opt->value, angle_opt->value);
1380 new_full_config.set_key_value("build_plate_tilt_x", new ConfigOptionFloat(tilt.tilt_x_deg));
1381 new_full_config.set_key_value("build_plate_tilt_y", new ConfigOptionFloat(tilt.tilt_y_deg));
1382 }
1383 }
1384 const ConfigOption* enable_support_option = new_full_config.option("enable_support");
1385 if (enable_support_option && enable_support_option->getBool())
1386 m_support_used = true;
1387 else
1388 m_support_used = false;
1389
1390 {
1391 const auto& o = model.objects;
1392 const auto opt_has_scarf_joint_seam = [](const DynamicConfig& c) {
1393 return c.has("seam_slope_type") && c.opt_enum<SeamScarfType>("seam_slope_type") != SeamScarfType::None;
1394 };
1395 const bool has_scarf_joint_seam = std::any_of(o.begin(), o.end(), [&new_full_config, &opt_has_scarf_joint_seam](ModelObject* obj) {
1396 return obj->get_config_value<ConfigOptionEnum<SeamScarfType>>(new_full_config, "seam_slope_type")->value != SeamScarfType::None ||
1397 std::any_of(obj->volumes.begin(), obj->volumes.end(), [&opt_has_scarf_joint_seam](const ModelVolume* v) { return opt_has_scarf_joint_seam(v->config.get());}) ||
1398 std::any_of(obj->layer_config_ranges.begin(), obj->layer_config_ranges.end(), [&opt_has_scarf_joint_seam](const auto& r) { return opt_has_scarf_joint_seam(r.second.get());});
1399 });
1400
1402 new_full_config.set("has_scarf_joint_seam", true);
1403 }
1404
1405 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", has_scarf_joint_seam:" << has_scarf_joint_seam;
1406 }
1407
1408 //apply extruder related values
1409 std::vector<int> print_variant_index;
1410 std::vector<std::vector<NozzleVolumeType>> nozzle_volume_types;
1411 int extruder_count = 1, extruder_volume_type_count = 1;
1412 bool different_extruder = false;
1413 // Filled only when the filament arrays are rebuilt from a persisted selector result below;
1414 // print_config_diffs then keys the retract overrides per expanded slot.
1415 std::vector<int> dynamic_slot_indices;
1416
1417 different_extruder = new_full_config.support_different_extruders(extruder_count);
1418 extruder_volume_type_count = new_full_config.get_extruder_nozzle_volume_count(extruder_count, nozzle_volume_types);
1419 if (!extruder_applied) {
1420 if ((extruder_count > 1) || different_extruder) {
1421 // variant_2 must be processed first, because variant_1 will make `printer_extruder_id` and `printer_extruder_variant` half of the size that makes `get_index_for_extruder` no longer work properly
1422 new_full_config.update_values_to_printer_extruders(new_full_config, extruder_count, extruder_volume_type_count, nozzle_volume_types, printer_options_with_variant_2, "printer_extruder_id", "printer_extruder_variant", 2);
1423 new_full_config.update_values_to_printer_extruders(new_full_config, extruder_count, extruder_volume_type_count, nozzle_volume_types, printer_options_with_variant_1, "printer_extruder_id", "printer_extruder_variant");
1424 //update print config related with variants
1425 print_variant_index = new_full_config.update_values_to_printer_extruders(new_full_config, extruder_count, extruder_volume_type_count, nozzle_volume_types, print_options_with_variant, "print_extruder_id", "print_extruder_variant");
1426 }
1427 else
1428 print_variant_index.resize(1, 0);
1429
1430 m_ori_full_print_config = new_full_config;
1431
1432 std::set<std::string> filament_keys = filament_options_with_variant;
1433 filament_keys.insert("filament_self_index");
1434 // A persisted selector result with an actual migration means the last slice rebuilt the
1435 // per-slot filament arrays from it (one slot per variant a filament prints through).
1436 // Reproduce that exact expansion here so an unchanged config diffs empty — the expanded
1437 // keys invalidate the wipe tower / g-code export, and the placeholder parser aliases
1438 // the full config — instead of trimming back to one slot per filament.
1439 auto group_result = this->get_layered_nozzle_group_result();
1440 std::unordered_map<int, std::vector<FilamentVariantUse>> filament_variant_uses;
1441 if (group_result && group_result->is_support_dynamic_nozzle_map()
1442 && collect_filament_variant_uses(*group_result, m_ori_full_print_config, filament_variant_uses))
1443 new_full_config.update_filament_config_values_for_multiple_extruders(m_ori_full_print_config, filament_variant_uses,
1444 extruder_count, extruder_volume_type_count, filament_keys,
1445 "filament_self_index", "filament_extruder_variant",
1446 &dynamic_slot_indices);
1447 // Orca: also on a printer with a single extruder variant once a filament defines several
1448 // (e.g. Standard and High Flow), so each filament takes its variant for that extruder.
1449 else if ((extruder_count > 1) || different_extruder || new_full_config.has_multi_variant_filament())
1450 new_full_config.update_values_to_printer_extruders_for_multiple_filaments(m_ori_full_print_config, extruder_count, extruder_volume_type_count, filament_keys,
1451 "filament_self_index", "filament_extruder_variant");
1452 }
1453 else {
1454 //should not come here, we can not get the result of print_variant, for the values have been updated
1455 //we just use the default values here
1456 auto variant_opt = dynamic_cast<const ConfigOptionStrings *>(new_full_config.option("printer_extruder_variant"));
1457 print_variant_index.resize(variant_opt->values.size());
1458 for (int e_index = 0; e_index < variant_opt->values.size(); e_index++)
1459 {
1460 print_variant_index[e_index] = e_index;
1461 }
1462 }
1463
1464 // Fill in physical_extruder_map when the printer profile has not authored one. It has one
1465 // entry per logical extruder, so it is sized from the nozzle count -- not from
1466 // printer_extruder_id, which is indexed by variant slot.
1467 //
1468 // IMEX printers only. physical_extruder_map carries two readings in this tree (see
1469 // IMEXHelpers.hpp): the IMEX paths need one entry per logical extruder, the inherited BBL
1470 // paths read it through the clamping get_at(), which collapses every logical extruder to
1471 // physical 0 while the profile default is the single-element {0}. Deriving the identity
1472 // unconditionally would impose the IMEX reading on printers that mean the other one and
1473 // change what they emit -- {first_tools}/{first_filaments}/{first_non_support_*} and
1474 // {most_used_physical_extruder_id}/{curr_physical_extruder_id} in custom start G-code, plus
1475 // the `; physical_extruder_map =` line in the G-code config block -- for every multi-nozzle
1476 // profile that never authored a map. Non-IMEX printers therefore keep the config value
1477 // exactly as it arrives, which is what upstream slices with.
1478 const auto* is_imex_opt = new_full_config.option<ConfigOptionBool>("is_imex");
1479 if (is_imex_opt && is_imex_opt->value) {
1480 auto* pem = new_full_config.option<ConfigOptionInts>("physical_extruder_map", true);
1481 if (pem) {
1482 pem->values = effective_physical_extruder_map(pem, extruder_count).values;
1483 // m_ori_full_print_config was snapshotted above, before this derivation, and the
1484 // selector write-back path rebuilds m_full_print_config from that snapshot. Without
1485 // mirroring the derived map into it, m_full_print_config keeps the unexpanded default
1486 // while every later apply re-derives the expanded one, so an unchanged config diffs
1487 // on physical_extruder_map forever and invalidates all steps on every re-apply.
1488 if (auto* ori_pem = m_ori_full_print_config.option<ConfigOptionInts>("physical_extruder_map", true))
1489 ori_pem->values = pem->values;
1490 }
1491 }
1492
1493 auto opt_filament_map = new_full_config.option<ConfigOptionInts>("filament_map");
1494 std::vector<int> filament_maps = opt_filament_map ? opt_filament_map->values : std::vector<int>();
1495
1496 // Find modified keys of the various configs. Resolve overrides extruder retract values by filament profiles.
1497 DynamicPrintConfig filament_overrides;
1498 //BBS: add plate index
1499 t_config_option_keys print_diff = print_config_diffs(m_config, new_full_config, filament_overrides, this->m_plate_index, filament_maps,
1500 dynamic_slot_indices.empty() ? nullptr : &dynamic_slot_indices);
1501 // Orca: filament_map_2 is engine-derived state, never a user input: the rebuild below
1502 // recomputes it from filament_map/filament_volume_map/the variant slots on every apply
1503 // (all of which are diffed and invalidation-listed on their own), and the grouping
1504 // write-back overwrites it during process(). The incoming full config only ever carries
1505 // the ConfigDef default, so diffing it would invalidate every print step on each apply
1506 // for any multi-extruder printer and permanently invalidate fresh slice results.
1507 print_diff.erase(std::remove(print_diff.begin(), print_diff.end(), "filament_map_2"), print_diff.end());
1508 t_config_option_keys full_config_diff = full_print_config_diffs(m_full_print_config, new_full_config, this->m_plate_index);
1509 // Collect changes to object and region configs.
1510 t_config_option_keys object_diff = m_default_object_config.diff(new_full_config);
1511 t_config_option_keys region_diff = m_default_region_config.diff(new_full_config);
1512
1513 //BBS: process the filament_map related logic
1514 std::unordered_set<std::string> print_diff_set(print_diff.begin(), print_diff.end());
1515 if (!print_diff_set.empty() && print_diff_set.find("filament_map_mode") == print_diff_set.end())
1516 {
1517 FilamentMapMode map_mode = new_full_config.option<ConfigOptionEnum<FilamentMapMode>>("filament_map_mode", true)->value;
1518 if (is_auto_filament_map_mode(map_mode)) {
1519 if (print_diff_set.find("filament_map") != print_diff_set.end()) {
1520 print_diff_set.erase("filament_map");
1521 //full_config_diff.erase("filament_map");
1522 ConfigOptionInts* old_opt = m_full_print_config.option<ConfigOptionInts>("filament_map", true);
1523 ConfigOptionInts* new_opt = new_full_config.option<ConfigOptionInts>("filament_map", true);
1524 old_opt->set(new_opt);
1525 m_config.filament_map = *new_opt;
1526 }
1527 if (print_diff_set.find("filament_volume_map") != print_diff_set.end()) {
1528 print_diff_set.erase("filament_volume_map");
1529 //full_config_diff.erase("filament_volume_map");
1530 ConfigOptionInts* old_opt = m_full_print_config.option<ConfigOptionInts>("filament_volume_map", true);
1531 ConfigOptionInts* new_opt = new_full_config.option<ConfigOptionInts>("filament_volume_map", true);
1532 old_opt->set(new_opt);
1533 m_config.filament_volume_map = *new_opt;
1534 }
1535 if (print_diff_set.find("filament_nozzle_map") != print_diff_set.end()) {
1536 print_diff_set.erase("filament_nozzle_map");
1537 //full_config_diff.erase("filament_nozzle_map");
1538 ConfigOptionInts* old_opt = m_full_print_config.option<ConfigOptionInts>("filament_nozzle_map", true);
1539 ConfigOptionInts* new_opt = new_full_config.option<ConfigOptionInts>("filament_nozzle_map", true);
1540 old_opt->set(new_opt);
1541 m_config.filament_nozzle_map = *new_opt;
1542 }
1543 }
1544 else {
1545 print_diff_set.erase("extruder_ams_count");
1546 if (map_mode == fmmManual) {
1547 // filament_nozzle_map is an engine output, not a GUI input, in manual mode
1548 print_diff_set.erase("filament_nozzle_map");
1549 }
1550 std::vector<int> old_filament_map = m_config.filament_map.values;
1551 std::vector<int> new_filament_map = new_full_config.option<ConfigOptionInts>("filament_map", true)->values;
1552
1553 if (old_filament_map.size() == new_filament_map.size())
1554 {
1555 bool same_map = true;
1556 for (size_t index = 0; index < old_filament_map.size(); index++)
1557 {
1558 if ((old_filament_map[index] == new_filament_map[index])
1559 || (used_filament_set.find(index) == used_filament_set.end()))
1560 continue;
1561 else {
1562 same_map = false;
1563 break;
1564 }
1565 }
1566 if (same_map) {
1567 print_diff_set.erase("filament_map");
1568
1569 // The extruder retract overrides are keyed by the (unchanged) filament map;
1570 // recompute them and drop diffs whose recomputed value matches the current
1571 // config, so a cosmetic reordering of unused filaments does not invalidate.
1572 const auto& retract_keys = print_config_def.extruder_retract_keys();
1573 const std::string filament_prefix = "filament_";
1574 std::vector<int> old_f_map_indices(old_filament_map.size(), 0);
1575 for (size_t i = 0; i < old_filament_map.size(); i++)
1576 old_f_map_indices[i] = old_filament_map[i] - 1;
1577
1578 for (const auto& rk : retract_keys) {
1579 if (print_diff_set.find(rk) == print_diff_set.end())
1580 continue;
1581 const ConfigOption* opt_old = m_config.option(rk);
1582 const ConfigOption* opt_new_m = new_full_config.option(rk);
1583 const ConfigOption* opt_new_f = new_full_config.option(filament_prefix + rk);
1584 if (opt_old && opt_new_m && opt_new_f) {
1585 std::unique_ptr<ConfigOption> opt_recomputed(opt_new_m->clone());
1586 opt_recomputed->apply_override(opt_new_f, old_f_map_indices);
1587 if (*opt_old == *opt_recomputed)
1588 print_diff_set.erase(rk);
1589 }
1590 }
1591 }
1592 }
1593 }
1594 if (print_diff_set.size() != print_diff.size())
1595 print_diff.assign(print_diff_set.begin(), print_diff_set.end());
1596 }
1597
1598 //filament_map_2
1599 // Orca: seed with 0-based extruder indices so the copy stays a valid slot map even when the
1600 // variant options are absent below and the rebuild loop is skipped (unit tests, degenerate
1601 // presets); the loop overwrites every entry when it runs.
1602 m_config.filament_map_2.values = filament_maps;
1603 for (auto& v : m_config.filament_map_2.values)
1604 --v;
1605 auto opt_extruder_type = dynamic_cast<const ConfigOptionEnumsGeneric*>(new_full_config.option("extruder_type"));
1606 auto opt_filament_volume_maps = dynamic_cast<const ConfigOptionInts*>(new_full_config.option("filament_volume_map"));
1607 auto opt_nozzle_volume_type = dynamic_cast<const ConfigOptionEnumsGeneric*>(new_full_config.option("nozzle_volume_type"));
1608 for (int index = 0; opt_extruder_type && opt_nozzle_volume_type && index < filament_maps.size(); index++)
1609 {
1610 ExtruderType extruder_type = (ExtruderType)(opt_extruder_type->get_at(filament_maps[index] - 1));
1611 NozzleVolumeType nozzle_volume_type = (NozzleVolumeType)(opt_nozzle_volume_type->get_at(filament_maps[index] - 1));
1612 // Orca: honour the per-filament volume map only when a producer sized it to the filament
1613 // count; mis-sized maps (stale project values, CLI runs until the per-filament synthesis
1614 // lands there) must not be indexed per filament (see
1615 // update_values_to_printer_extruders_for_multiple_filaments for the same guard).
1616 if ((extruder_volume_type_count > extruder_count) && opt_filament_volume_maps
1617 && opt_filament_volume_maps->values.size() == filament_maps.size())
1618 nozzle_volume_type = (NozzleVolumeType)(opt_filament_volume_maps->values[index]);
1619 // Orca: when the process variant columns cannot be matched (degenerate
1620 // print_extruder_id), key the override by plain extruder index like the seeding
1621 // above instead of poisoning the map with -1.
1622 int slot_index = new_full_config.get_index_for_extruder(filament_maps[index], "print_extruder_id", extruder_type, nozzle_volume_type, "print_extruder_variant");
1623 m_config.filament_map_2.values[index] = slot_index >= 0 ? slot_index : filament_maps[index] - 1;
1624 }
1625
1626 // Do not use the ApplyStatus as we will use the max function when updating apply_status.
1627 unsigned int apply_status = APPLY_STATUS_UNCHANGED;
1628 auto update_apply_status = [&apply_status](bool invalidated)
1629 { apply_status = std::max<unsigned int>(apply_status, invalidated ? APPLY_STATUS_INVALIDATED : APPLY_STATUS_CHANGED); };
1630 if (! (print_diff.empty() && object_diff.empty() && region_diff.empty())) {
1631 update_apply_status(false);
1632 //BBS: add more logs
1633 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", got print_diff %1%, object_diff %2%, region_diff %3%, set status to APPLY_STATUS_CHANGED")%print_diff.size() %object_diff.size() %region_diff.size();
1634 }
1635
1636 // Grab the lock for the Print / PrintObject milestones.
1637 std::scoped_lock<std::mutex> lock(this->state_mutex());
1638
1639 // The following call may stop the background processing.
1640 if (! print_diff.empty())
1641 update_apply_status(this->invalidate_state_by_config_options(new_full_config, print_diff));
1642
1643 // Apply variables to placeholder parser. The placeholder parser is used by G-code export,
1644 // which should be stopped if print_diff is not empty.
1645 size_t num_extruders = m_config.filament_diameter.size();
1646 bool num_extruders_changed = false;
1647 if (! full_config_diff.empty()) {
1648 //BBS: add more logs
1649 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" %1%: found full_config_diff changed.")%__LINE__;
1650 update_apply_status(this->invalidate_step(psGCodeExport));
1651 m_placeholder_parser.clear_config();
1652 // clear_config() wiped the constructor-set "version"; restore it for custom G-code.
1653 m_placeholder_parser.set("version", std::string(SoftFever_VERSION));
1654 // Set the profile aliases for the PrintBase::output_filename()
1655 m_placeholder_parser.set("print_preset", new_full_config.option("print_settings_id")->clone());
1656 m_placeholder_parser.set("filament_preset", new_full_config.option("filament_settings_id")->clone());
1657 m_placeholder_parser.set("printer_preset", new_full_config.option("printer_settings_id")->clone());
1658 // We want the filament overrides to be applied over their respective extruder parameters by the PlaceholderParser.
1659 // see "Placeholders do not respect filament overrides." GH issue #3649
1660 m_placeholder_parser.apply_config(filament_overrides);
1661 // It is also safe to change m_config now after this->invalidate_state_by_config_options() call.
1662 m_config.apply_only(new_full_config, print_diff, true);
1663 //FIXME use move semantics once ConfigBase supports it.
1664 // Some filament_overrides may contain values different from new_full_config, but equal to m_config.
1665 // As long as these config options don't reallocate memory when copying, we are safe overriding a value, which is in use by a worker thread.
1666 m_config.apply(filament_overrides);
1667 // Handle changes to object config defaults
1668 m_default_object_config.apply_only(new_full_config, object_diff, true);
1669 // Handle changes to regions config defaults
1670 m_default_region_config.apply_only(new_full_config, region_diff, true);
1671 //m_full_print_config = std::move(new_full_config);
1672 m_full_print_config = new_full_config;
1674 if (num_extruders != m_config.filament_diameter.size()) {
1675 num_extruders = m_config.filament_diameter.size();
1676 num_extruders_changed = true;
1677 }
1678 }
1679 else if (! print_diff.empty()) {
1680 // Orca: m_config can diverge from an unchanged full config (e.g. the in-slice retract
1681 // override recompute writing different values than the apply-time computation). The
1682 // invalidation above already fired for print_diff, so repair m_config here as well;
1683 // otherwise the divergence is never corrected and every subsequent apply of the same
1684 // config invalidates the result again, forever.
1685 m_placeholder_parser.apply_config(filament_overrides);
1686 m_config.apply_only(new_full_config, print_diff, true);
1687 m_config.apply(filament_overrides);
1688 }
1689
1690 ModelObjectStatusDB model_object_status_db;
1691
1692 // 1) Synchronize model objects.
1693 bool print_regions_reshuffled = false;
1694 if (model.id() != m_model.id()) {
1695 // Kill everything, initialize from scratch.
1696 // Stop background processing.
1697 this->call_cancel_callback();
1698 update_apply_status(this->invalidate_all_steps());
1699 for (PrintObject *object : m_objects) {
1700 model_object_status_db.add(*object->model_object(), ModelObjectStatus::Deleted);
1701 update_apply_status(object->invalidate_all_steps());
1702 delete object;
1703 }
1704 m_objects.clear();
1705 print_regions_reshuffled = true;
1706 m_model.assign_copy(model);
1707 for (const ModelObject *model_object : m_model.objects)
1708 model_object_status_db.add(*model_object, ModelObjectStatus::New);
1709 } else {
1710 //BBS: replace model custom gcode with current plate custom gcode
1711 m_model.curr_plate_index = model.curr_plate_index;
1712 if (m_model.get_curr_plate_custom_gcodes() != model.get_curr_plate_custom_gcodes()) {
1713 update_apply_status(num_extruders_changed ||
1714 // Tool change G-codes are applied as color changes for a single extruder printer, no need to invalidate tool ordering.
1715 //FIXME The tool ordering may be invalidated unnecessarily if the custom_gcode_per_print_z.mode is not applicable
1716 // to the active print / model state, and then it is reset, so it is being applicable, but empty, thus the effect is the same.
1717 (num_extruders > 1 && custom_per_printz_gcodes_tool_changes_differ(m_model.get_curr_plate_custom_gcodes().gcodes, model.get_curr_plate_custom_gcodes().gcodes)) ?
1718 // The Tool Ordering and the Wipe Tower are no more valid.
1719 this->invalidate_steps({ psWipeTower, psGCodeExport }) :
1720 // There is no change in Tool Changes stored in custom_gcode_per_print_z, therefore there is no need to update Tool Ordering.
1721 this->invalidate_step(psGCodeExport));
1722 m_model.plates_custom_gcodes[m_model.curr_plate_index] = model.get_curr_plate_custom_gcodes();
1723 }
1725 // The object list did not change.
1726 for (const ModelObject *model_object : m_model.objects)
1727 model_object_status_db.add(*model_object, ModelObjectStatus::Old);
1729 // Add new objects. Their volumes and configs will be synchronized later.
1730 //BBS: we don't need to set invalid here, we judge it by comparing the print_object list
1731 //update_apply_status(this->invalidate_step(psGCodeExport));
1732 for (const ModelObject *model_object : m_model.objects)
1733 model_object_status_db.add(*model_object, ModelObjectStatus::Old);
1734 for (size_t i = m_model.objects.size(); i < model.objects.size(); ++ i) {
1735 model_object_status_db.add(*model.objects[i], ModelObjectStatus::New);
1736 m_model.objects.emplace_back(ModelObject::new_copy(*model.objects[i]));
1737 m_model.objects.back()->set_model(&m_model);
1738 }
1739 //BBS: add more logs
1740 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" %1%: new model objects added.")%__LINE__;
1741 } else {
1742 //BBS: add more logs
1743 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" %1%: model object changed.")%__LINE__;
1744 // Reorder the objects, add new objects.
1745 // First stop background processing before shuffling or deleting the PrintObjects in the object list.
1746 this->call_cancel_callback();
1747 //BBS: we don't need to set invalid here, we judge it by comparing the print_object list
1748 //update_apply_status(this->invalidate_step(psGCodeExport));
1749 // Second create a new list of objects.
1750 std::vector<ModelObject*> model_objects_old(std::move(m_model.objects));
1751 m_model.objects.clear();
1752 m_model.objects.reserve(model.objects.size());
1753 auto by_id_lower = [](const ModelObject *lhs, const ModelObject *rhs){ return lhs->id() < rhs->id(); };
1754 std::sort(model_objects_old.begin(), model_objects_old.end(), by_id_lower);
1755 for (const ModelObject *mobj : model.objects) {
1756 auto it = std::lower_bound(model_objects_old.begin(), model_objects_old.end(), mobj, by_id_lower);
1757 if (it == model_objects_old.end() || (*it)->id() != mobj->id()) {
1758 // New ModelObject added.
1759 m_model.objects.emplace_back(ModelObject::new_copy(*mobj));
1760 m_model.objects.back()->set_model(&m_model);
1761 model_object_status_db.add(*mobj, ModelObjectStatus::New);
1762 } else {
1763 // Existing ModelObject re-added (possibly moved in the list).
1764 m_model.objects.emplace_back(*it);
1765 model_object_status_db.add(*mobj, ModelObjectStatus::Moved);
1766 }
1767 }
1768 bool deleted_any = false;
1769 for (ModelObject *&model_object : model_objects_old)
1770 if (model_object_status_db.add_if_new(*model_object, ModelObjectStatus::Deleted))
1771 deleted_any = true;
1772 else
1773 // Do not delete this ModelObject instance.
1774 model_object = nullptr;
1775 if (deleted_any) {
1776 // Delete PrintObjects of the deleted ModelObjects.
1777 PrintObjectPtrs print_objects_old = std::move(m_objects);
1778 m_objects.clear();
1779 m_objects.reserve(print_objects_old.size());
1780 for (PrintObject *print_object : print_objects_old) {
1781 const ModelObjectStatus &status = model_object_status_db.get(*print_object->model_object());
1782 if (status.status == ModelObjectStatus::Deleted) {
1783 update_apply_status(print_object->invalidate_all_steps());
1784 delete print_object;
1785 } else
1786 m_objects.emplace_back(print_object);
1787 }
1788 for (ModelObject *model_object : model_objects_old)
1789 delete model_object;
1790 print_regions_reshuffled = true;
1791 }
1792 }
1793 }
1794
1795 // 2) Map print objects including their transformation matrices.
1796 PrintObjectStatusDB print_object_status_db(m_objects);
1797
1798 // 3) Synchronize ModelObjects & PrintObjects.
1799 const std::initializer_list<ModelVolumeType> solid_or_modifier_types { ModelVolumeType::MODEL_PART, ModelVolumeType::NEGATIVE_VOLUME, ModelVolumeType::PARAMETER_MODIFIER };
1800 const std::initializer_list<ModelVolumeType> precise_seam_types {
1804 };
1805 for (size_t idx_model_object = 0; idx_model_object < model.objects.size(); ++ idx_model_object) {
1806 ModelObject &model_object = *m_model.objects[idx_model_object];
1807 ModelObjectStatus &model_object_status = const_cast<ModelObjectStatus&>(model_object_status_db.reuse(model_object));
1808 const ModelObject &model_object_new = *model.objects[idx_model_object];
1809 if (model_object_status.status == ModelObjectStatus::New)
1810 // PrintObject instances will be added in the next loop.
1811 continue;
1812 // Update the ModelObject instance, possibly invalidate the linked PrintObjects.
1813 assert(model_object_status.status == ModelObjectStatus::Old || model_object_status.status == ModelObjectStatus::Moved);
1814 // Check whether a model part volume was added or removed, their transformations or order changed.
1815 // Only volume IDs, volume types, transformation matrices and their order are checked, configuration and other parameters are NOT checked.
1816 bool solid_or_modifier_differ = model_volume_list_changed(model_object, model_object_new, solid_or_modifier_types) ||
1817 model_mmu_segmentation_data_changed(model_object, model_object_new) ||
1818 (model_object_new.is_mm_painted() && num_extruders_changed) ||
1819 model_fuzzy_skin_data_changed(model_object, model_object_new);
1820 bool supports_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER) ||
1821 model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER);
1822 bool precise_seam_differ = model_volume_list_changed(model_object, model_object_new, precise_seam_types);
1823 bool layer_height_ranges_differ = ! layer_height_ranges_equal(model_object.layer_config_ranges, model_object_new.layer_config_ranges, model_object_new.layer_height_profile.empty());
1824 bool model_origin_translation_differ = model_object.origin_translation != model_object_new.origin_translation;
1825 bool brim_points_differ = model_brim_points_data_changed(model_object, model_object_new);
1826 auto print_objects_range = print_object_status_db.get_range(model_object);
1827 // The list actually can be empty if all instances are out of the print bed.
1828 //assert(print_objects_range.begin() != print_objects_range.end());
1829 // All PrintObjects in print_objects_range shall point to the same prints_objects_regions
1830 if (print_objects_range.begin() != print_objects_range.end()) {
1831 model_object_status.print_object_regions = print_objects_range.begin()->print_object->m_shared_regions;
1832 model_object_status.print_object_regions->ref_cnt_inc();
1833 }
1834 if (solid_or_modifier_differ || model_origin_translation_differ || layer_height_ranges_differ ||
1835 ! model_object.layer_height_profile.timestamp_matches(model_object_new.layer_height_profile)) {
1836 // The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
1837 model_object_status.print_object_regions_status =
1838 model_object_status.print_object_regions == nullptr || model_origin_translation_differ || layer_height_ranges_differ ?
1839 // Drop print_objects_regions.
1841 // Reuse bounding boxes of print_objects_regions for ModelVolumes with unmodified transformation.
1843 for (const PrintObjectStatus &print_object_status : print_objects_range) {
1844 update_apply_status(print_object_status.print_object->invalidate_all_steps());
1845 const_cast<PrintObjectStatus&>(print_object_status).status = PrintObjectStatus::Deleted;
1846 }
1847 if (model_object_status.print_object_regions_status == ModelObjectStatus::PrintObjectRegionsStatus::PartiallyValid)
1848 // Drop everything from PrintObjectRegions but those VolumeExtents (of their particular ModelVolumes) that are still valid.
1849 print_objects_regions_invalidate_keep_some_volumes(*model_object_status.print_object_regions, model_object.volumes, model_object_new.volumes);
1850 else if (model_object_status.print_object_regions != nullptr)
1851 model_object_status.print_object_regions->clear();
1852 // Copy content of the ModelObject including its ID, do not change the parent.
1853 model_object.assign_copy(model_object_new);
1854 } else {
1855 model_object_status.print_object_regions_status = ModelObjectStatus::PrintObjectRegionsStatus::Valid;
1856 if (supports_differ || model_custom_supports_data_changed(model_object, model_object_new)) {
1857 // First stop background processing before shuffling or deleting the ModelVolumes in the ModelObject's list.
1858 if (supports_differ) {
1859 this->call_cancel_callback();
1860 update_apply_status(false);
1861 }
1862 // Invalidate just the supports step.
1863 for (const PrintObjectStatus &print_object_status : print_objects_range)
1864 update_apply_status(print_object_status.print_object->invalidate_step(posSupportMaterial));
1865 }
1866 if (precise_seam_differ) {
1867 // First stop background processing before shuffling or deleting the ModelVolumes in the ModelObject's list.
1868 this->call_cancel_callback();
1869 update_apply_status(false);
1870 // Invalidate seam placement (affects G-code export).
1871 update_apply_status(this->invalidate_step(psGCodeExport));
1872 } else if (model_custom_seam_data_changed(model_object, model_object_new)) {
1873 update_apply_status(this->invalidate_step(psGCodeExport));
1874 }
1875 // Synchronize both families once, after cancellation and all affected-step invalidations.
1876 // This also handles type changes between supports and Precise Seam before copying configs below.
1877 if (supports_differ || precise_seam_differ)
1878 model_volume_list_update_supports_and_seams(model_object, model_object_new);
1879 if (brim_points_differ) {
1880 model_object.brim_points = model_object_new.brim_points;
1881 update_apply_status(this->invalidate_all_steps());
1882 }
1883 }
1884 if (! solid_or_modifier_differ) {
1885 // Synchronize Object's config.
1886 bool object_config_changed = ! model_object.config.timestamp_matches(model_object_new.config);
1887 if (object_config_changed)
1888 model_object.config.assign_config(model_object_new.config);
1889 if (! object_diff.empty() || object_config_changed || num_extruders_changed ) {
1890 PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_extruders, print_variant_index);
1891 for (const PrintObjectStatus &print_object_status : print_object_status_db.get_range(model_object)) {
1892 t_config_option_keys diff = print_object_status.print_object->config().diff(new_config);
1893 if (! diff.empty()) {
1894 update_apply_status(print_object_status.print_object->invalidate_state_by_config_options(print_object_status.print_object->config(), new_config, diff));
1895 print_object_status.print_object->config_apply_only(new_config, diff, true);
1896 }
1897 }
1898 }
1899 // Synchronize (just copy) the remaining data of ModelVolumes (name, config, custom supports data).
1900 //FIXME What to do with m_material_id?
1901 model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::MODEL_PART);
1902 model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::PARAMETER_MODIFIER);
1903 // Synchronize Precise Seam modifier volumes
1904 model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, precise_seam_types);
1905 layer_height_ranges_copy_configs(model_object.layer_config_ranges /* dst */, model_object_new.layer_config_ranges /* src */);
1906 // Copy the ModelObject name, input_file and instances. The instances will be compared against PrintObject instances in the next step.
1907 model_object.name = model_object_new.name;
1908 model_object.input_file = model_object_new.input_file;
1909 // Only refresh ModelInstances if there is any change.
1910 if (model_object.instances.size() != model_object_new.instances.size() ||
1911 ! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(), [](auto l, auto r){ return l->id() == r->id(); })) {
1912 // G-code generator accesses model_object.instances to generate sequential print ordering matching the Plater object list.
1913 update_apply_status(this->invalidate_step(psGCodeExport));
1914 model_object.clear_instances();
1915 model_object.instances.reserve(model_object_new.instances.size());
1916 for (const ModelInstance *model_instance : model_object_new.instances) {
1917 model_object.instances.emplace_back(new ModelInstance(*model_instance));
1918 model_object.instances.back()->set_model_object(&model_object);
1919 }
1920 } else if (! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(),
1921 [](auto l, auto r){ return l->print_volume_state == r->print_volume_state && l->printable == r->printable &&
1922 l->get_transformation().get_matrix().isApprox(r->get_transformation().get_matrix()); })) {
1923 // If some of the instances changed, the bounding box of the updated ModelObject is likely no more valid.
1924 // This is safe as the ModelObject's bounding box is only accessed from this function, which is called from the main thread only.
1925 model_object.invalidate_bounding_box();
1926 // Synchronize the content of instances.
1927 auto new_instance = model_object_new.instances.begin();
1928 for (auto old_instance = model_object.instances.begin(); old_instance != model_object.instances.end(); ++ old_instance, ++ new_instance) {
1929 if (is_printable_filament_changed(new_full_config, (*old_instance)->convex_hull_2d(), (*new_instance)->convex_hull_2d())) {
1930 update_apply_status(this->invalidate_steps({psWipeTower, psGCodeExport}));
1931 }
1932 (*old_instance)->set_transformation((*new_instance)->get_transformation());
1933 (*old_instance)->print_volume_state = (*new_instance)->print_volume_state;
1934 (*old_instance)->printable = (*new_instance)->printable;
1935 }
1936 }
1937 }
1938 }
1939
1940 // 4) Generate PrintObjects from ModelObjects and their instances.
1941 {
1942 this->update_shrinkage_compensation(num_extruders, print_variant_index);
1943 PrintObjectPtrs print_objects_new;
1944 print_objects_new.reserve(std::max(m_objects.size(), m_model.objects.size()));
1945 bool new_objects = false;
1946 bool belt_instances_shifted = false;
1947 // Walk over all new model objects and check, whether there are matching PrintObjects.
1948 for (ModelObject *model_object : m_model.objects) {
1949 ModelObjectStatus &model_object_status = const_cast<ModelObjectStatus&>(model_object_status_db.reuse(*model_object));
1950 // Orca: Updated for XYZ filament shrink compensation
1951 // Belt printers: force each instance into its own PrintObject so each
1952 // gets independent layer Z values (its bed position is folded into them).
1953 bool belt_force_separate = m_config.belt_printer.value;
1954 model_object_status.print_instances = print_objects_from_model_object(*model_object, this->shrinkage_compensation(), belt_force_separate);
1955 std::vector<const PrintObjectStatus*> old;
1956 old.reserve(print_object_status_db.count(*model_object));
1957 for (const PrintObjectStatus &print_object_status : print_object_status_db.get_range(*model_object))
1958 if (print_object_status.status != PrintObjectStatus::Deleted)
1959 old.emplace_back(&print_object_status);
1960 // Generate a list of trafos and XY offsets for instances of a ModelObject
1961 // Producing the config for PrintObject on demand, caching it at print_object_last.
1962 const PrintObject *print_object_last = nullptr;
1963 auto print_object_apply_config = [this, &print_object_last, model_object, num_extruders, &print_variant_index](PrintObject *print_object) {
1964 print_object->config_apply(print_object_last ?
1965 print_object_last->config() :
1966 PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_extruders, print_variant_index));
1967 print_object_last = print_object;
1968 };
1969 if (old.empty()) {
1970 // Simple case, just generate new instances.
1971 for (PrintObjectTrafoAndInstances &print_instances : model_object_status.print_instances) {
1972 PrintObject *print_object = new PrintObject(this, model_object, print_instances.trafo, std::move(print_instances.instances));
1973 print_object_apply_config(print_object);
1974 print_objects_new.emplace_back(print_object);
1975 // print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New));
1976 new_objects = true;
1977 }
1978 continue;
1979 }
1980 // Complex case, try to merge the two lists.
1981 // Sort the old lexicographically by their trafos.
1982 std::sort(old.begin(), old.end(), [](const PrintObjectStatus *lhs, const PrintObjectStatus *rhs){ return transform3d_lower(lhs->trafo, rhs->trafo); });
1983 // Merge the old / new lists.
1984 auto it_old = old.begin();
1985 for (PrintObjectTrafoAndInstances &new_instances : model_object_status.print_instances) {
1986 for (; it_old != old.end() && transform3d_lower((*it_old)->trafo, new_instances.trafo); ++ it_old);
1987 if (it_old == old.end() || ! transform3d_equal((*it_old)->trafo, new_instances.trafo)) {
1988 // This is a new instance (or a set of instances with the same trafo). Just add it.
1989 PrintObject *print_object = new PrintObject(this, model_object, new_instances.trafo, std::move(new_instances.instances));
1990 print_object_apply_config(print_object);
1991 print_objects_new.emplace_back(print_object);
1992 // print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New));
1993 new_objects = true;
1994 if (it_old != old.end())
1995 const_cast<PrintObjectStatus*>(*it_old)->status = PrintObjectStatus::Deleted;
1996 } else {
1997 // The PrintObject already exists and the copies differ.
1998 PrintBase::ApplyStatus status = (*it_old)->print_object->set_instances(std::move(new_instances.instances));
1999 if (status != PrintBase::APPLY_STATUS_UNCHANGED) {
2000 size_t extruder_num = new_full_config.option<ConfigOptionFloats>("nozzle_diameter")->size();
2001 update_apply_status(status == PrintBase::APPLY_STATUS_INVALIDATED);
2002 belt_instances_shifted = true;
2003 }
2004 print_objects_new.emplace_back((*it_old)->print_object);
2005 const_cast<PrintObjectStatus*>(*it_old)->status = PrintObjectStatus::Reused;
2006 }
2007 }
2008 }
2009 if (m_objects != print_objects_new) {
2010 //BBS: add more logs
2011 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" %1%: found print object changed.")%__LINE__;
2012 this->call_cancel_callback();
2013 update_apply_status(this->invalidate_all_steps());
2014 m_objects = print_objects_new;
2015 // Delete the PrintObjects marked as Unknown or Deleted.
2016 bool deleted_objects = false;
2017 for (const PrintObjectStatus &pos : print_object_status_db)
2018 if (pos.status == PrintObjectStatus::Unknown || pos.status == PrintObjectStatus::Deleted) {
2019 update_apply_status(pos.print_object->invalidate_all_steps());
2020 delete pos.print_object;
2021 deleted_objects = true;
2022 }
2023 if (new_objects || deleted_objects)
2024 update_apply_status(this->invalidate_steps({ psSkirtBrim, psWipeTower, psGCodeExport }));
2025 // A belt brim is clipped against the other objects on the plate (BeltBrim.cpp,
2026 // belt_brim_obstacles), and it is rebuilt with its object's support step: an
2027 // object that arrived or left changes every other brim owner's brim.
2028 if ((new_objects || deleted_objects) && m_config.belt_printer.value)
2029 for (PrintObject *object : m_objects)
2030 if (object->has_belt_brim())
2031 update_apply_status(object->invalidate_step(posSupportMaterial));
2032 if (new_objects)
2033 update_apply_status(false);
2034 print_regions_reshuffled = true;
2035 }
2036 print_object_status_db.clear();
2037
2038 // BBS
2039 for (PrintObject* object : m_objects) {
2040 auto ept_iter = std::find(print_diff.begin(), print_diff.end(), "enable_prime_tower");
2041 if (/*object->config().adaptive_layer_height &&*/ ept_iter != print_diff.end()) {
2042 update_apply_status(object->invalidate_step(posSlice));
2043 }
2044 }
2045
2046 // Belt printer: when any object's instances shifted, re-slice every object.
2047 // The global Z offset follows each object's position along the belt, and the
2048 // belt brims are clipped against the other objects.
2049 if (belt_instances_shifted && m_config.belt_printer.value) {
2050 for (PrintObject *object : m_objects)
2051 update_apply_status(object->invalidate_step(posSlice));
2052 }
2053 }
2054
2055 //BBS: check the config again
2056 int new_used_filaments = this->extruders(true).size();
2057 t_config_option_keys new_changed_keys = new_full_config.normalize_fdm_2(objects().size(), new_used_filaments);
2058 if (new_changed_keys.size() > 0) {
2059 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", got new_changed_keys, size=%1%")%new_changed_keys.size();
2060 for (int i = 0; i < new_changed_keys.size(); i++)
2061 {
2062 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", i=%1%, key=%2%")%i %new_changed_keys[i];
2063 }
2064
2065 update_apply_status(false);
2066
2067 // The following call may stop the background processing.
2068 update_apply_status(this->invalidate_state_by_config_options(new_full_config, new_changed_keys));
2069
2070 update_apply_status(this->invalidate_step(psGCodeExport));
2071
2072 if (full_config_diff.empty()) {
2073 //BBS: previous empty
2074 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" %1%: full_config_diff previous empty, need to apply now.")%__LINE__;
2075
2076 m_placeholder_parser.clear_config();
2077 // clear_config() wiped the constructor-set "version"; restore it for custom G-code.
2078 m_placeholder_parser.set("version", std::string(SoftFever_VERSION));
2079 // Set the profile aliases for the PrintBase::output_filename()
2080 m_placeholder_parser.set("print_preset", new_full_config.option("print_settings_id")->clone());
2081 m_placeholder_parser.set("filament_preset", new_full_config.option("filament_settings_id")->clone());
2082 m_placeholder_parser.set("printer_preset", new_full_config.option("printer_settings_id")->clone());
2083
2084 //m_placeholder_parser.apply_config(filament_overrides);
2085 }
2086 // It is also safe to change m_config now after this->invalidate_state_by_config_options() call.
2087 m_config.apply_only(new_full_config, new_changed_keys, true);
2088 // Handle changes to object config defaults
2089 m_default_object_config.apply_only(new_full_config, new_changed_keys, true);
2090 // Handle changes to regions config defaults
2091 m_default_region_config.apply_only(new_full_config, new_changed_keys, true);
2092 // Orca: keep the pre-expansion snapshot in sync with this late normalization pass.
2093 // The engine map write-back rebuilds m_full_print_config from m_ori_full_print_config
2094 // after slicing; a stale snapshot would resurrect the un-normalized values (e.g.
2095 // enable_prime_tower on a single-filament print) in the dumped config and spuriously
2096 // re-invalidate the g-code on the next apply.
2097 m_ori_full_print_config.apply_only(new_full_config, new_changed_keys, true);
2098 m_full_print_config = std::move(new_full_config);
2100 }
2101
2102 // Per-part gradient: compute the per-slot enable bit vector once for this Print::apply pass.
2103 // Used by generate_print_object_regions to decide which volumes deserve their own PrintRegion.
2104 std::vector<bool> slot_per_part_enabled;
2105 {
2106 const auto &is_mixed_vec = m_config.filament_is_mixed.values;
2107 const auto &grad_vec = m_config.filament_mixed_gradient.values;
2108 const auto &per_part_vec = m_config.filament_mixed_gradient_per_part.values;
2109 const auto &components_vec = m_config.filament_mixed_components.values;
2110 slot_per_part_enabled.assign(is_mixed_vec.size(), false);
2111 for (size_t i = 0; i < is_mixed_vec.size(); ++i) {
2112 if (! is_mixed_vec[i])
2113 continue;
2114 std::vector<unsigned int> comps = parse_mixed_components(i < components_vec.size() ? components_vec[i] : "");
2115 if (comps.size() != 2)
2116 continue;
2117 if (i >= grad_vec.size() || ! grad_vec[i])
2118 continue;
2119 if (i >= per_part_vec.size() || ! per_part_vec[i])
2120 continue;
2121 slot_per_part_enabled[i] = true;
2122 }
2123 }
2124
2125 // All regions now have distinct settings.
2126 // Check whether applying the new region config defaults we would get different regions,
2127 // update regions or create regions from scratch.
2128 for (auto it_print_object = m_objects.begin(); it_print_object != m_objects.end();) {
2129 // Find the range of PrintObjects sharing the same associated ModelObject.
2130 auto it_print_object_end = it_print_object;
2131 PrintObject &print_object = *(*it_print_object);
2132 const ModelObject &model_object = *print_object.model_object();
2133 ModelObjectStatus &model_object_status = const_cast<ModelObjectStatus&>(model_object_status_db.reuse(model_object));
2134 PrintObjectRegions *print_object_regions = model_object_status.print_object_regions;
2135 for (++ it_print_object_end; it_print_object_end != m_objects.end() && (*it_print_object)->model_object() == (*it_print_object_end)->model_object(); ++ it_print_object_end)
2136 assert((*it_print_object_end)->m_shared_regions == nullptr || (*it_print_object_end)->m_shared_regions == print_object_regions);
2137 if (print_object_regions == nullptr) {
2138 print_object_regions = new PrintObjectRegions{};
2139 model_object_status.print_object_regions = print_object_regions;
2140 print_object_regions->ref_cnt_inc();
2141 }
2142 std::vector<unsigned int> painting_extruders;
2143 if (const auto &volumes = print_object.model_object()->volumes;
2144 num_extruders > 1 &&
2145 std::find_if(volumes.begin(), volumes.end(), [](const ModelVolume *v) { return ! v->mmu_segmentation_facets.empty(); }) != volumes.end()) {
2146
2147 std::array<bool, static_cast<size_t>(EnforcerBlockerType::ExtruderMax) + 1> used_facet_states{};
2148 for (const ModelVolume *volume : volumes) {
2149 const std::vector<bool> &volume_used_facet_states = volume->mmu_segmentation_facets.get_data().used_states;
2150
2151 // Paint data saved before the painted state range was extended deserializes a
2152 // shorter used_states vector, so merge over the common prefix.
2153 for (size_t state_idx = 0; state_idx < std::min(volume_used_facet_states.size(), used_facet_states.size()); ++state_idx)
2154 used_facet_states[state_idx] |= volume_used_facet_states[state_idx];
2155 }
2156
2157 for (size_t state_idx = static_cast<size_t>(EnforcerBlockerType::Extruder1); state_idx < used_facet_states.size(); ++state_idx) {
2158 if (used_facet_states[state_idx])
2159 painting_extruders.emplace_back(state_idx);
2160 }
2161 }
2162 if (model_object_status.print_object_regions_status == ModelObjectStatus::PrintObjectRegionsStatus::Valid) {
2163 // Verify that the trafo for regions & volume bounding boxes thus for regions is still applicable.
2164 auto invalidate = [it_print_object, it_print_object_end, update_apply_status]() {
2165 for (auto it = it_print_object; it != it_print_object_end; ++ it)
2166 if ((*it)->m_shared_regions != nullptr)
2167 update_apply_status((*it)->invalidate_all_steps());
2168 };
2169 if (print_object_regions && ! trafos_differ_in_rotation_by_z_and_mirroring_by_xy_only(print_object_regions->trafo_bboxes, model_object_status.print_instances.front().trafo)) {
2170 invalidate();
2171 print_object_regions->clear();
2172 model_object_status.print_object_regions_status = ModelObjectStatus::PrintObjectRegionsStatus::Invalid;
2173 print_regions_reshuffled = true;
2174 } else if (print_object_regions &&
2176 print_object.model_object()->volumes,
2178 num_extruders,
2179 *print_object_regions,
2180 [it_print_object, it_print_object_end, &update_apply_status](const PrintRegionConfig &old_config, const PrintRegionConfig &new_config, const t_config_option_keys &diff_keys) {
2181 for (auto it = it_print_object; it != it_print_object_end; ++it)
2182 if ((*it)->m_shared_regions != nullptr)
2183 update_apply_status((*it)->invalidate_state_by_config_options(old_config, new_config, diff_keys));
2184 },
2185 print_variant_index)) {
2186 // Per-part gradient: PrintRegionConfig alone cannot reveal a change in which slots
2187 // have per-part enabled, so compare against the snapshot taken when these regions
2188 // were generated and regenerate on any difference (slot toggled, per-part moved
2189 // between slots, eligibility changed via components / gradient / is_mixed).
2190 if (print_object_regions->last_slot_per_part_enabled != slot_per_part_enabled) {
2191 invalidate();
2192 model_object_status.print_object_regions_status = ModelObjectStatus::PrintObjectRegionsStatus::PartiallyValid;
2193 print_regions_reshuffled = true;
2194 }
2195 // Regions are valid, just keep them.
2196 } else {
2197 // Regions were reshuffled.
2198 invalidate();
2199 // At least reuse layer ranges and bounding boxes of ModelVolumes.
2200 model_object_status.print_object_regions_status = ModelObjectStatus::PrintObjectRegionsStatus::PartiallyValid;
2201 print_regions_reshuffled = true;
2202 }
2203 }
2204 if (print_object_regions == nullptr || model_object_status.print_object_regions_status != ModelObjectStatus::PrintObjectRegionsStatus::Valid) {
2205 // Layer ranges with their associated configurations. Remove overlaps between the ranges
2206 // and create the regions from scratch.
2207 print_object_regions = generate_print_object_regions(
2208 print_object_regions,
2209 print_object.model_object()->volumes,
2210 LayerRanges(print_object.model_object()->layer_config_ranges),
2212 model_object_status.print_instances.front().trafo,
2213 num_extruders ,
2214 print_object.is_mm_painted() ? 0.f : float(print_object.config().xy_contour_compensation.value),
2215 painting_extruders,
2216 print_variant_index,
2217 print_object.is_fuzzy_skin_painted(),
2218 slot_per_part_enabled);
2219 }
2220 for (auto it = it_print_object; it != it_print_object_end; ++it)
2221 if ((*it)->m_shared_regions) {
2222 assert((*it)->m_shared_regions == print_object_regions);
2223 } else {
2224 (*it)->m_shared_regions = print_object_regions;
2225 print_object_regions->ref_cnt_inc();
2226 }
2227 it_print_object = it_print_object_end;
2228 }
2229
2230 if (print_regions_reshuffled) {
2231 // Update Print::m_print_regions from objects.
2232 struct cmp { bool operator() (const PrintRegion *l, const PrintRegion *r) const { return l->config_hash() == r->config_hash() && l->config() == r->config(); } };
2233 std::set<const PrintRegion*, cmp> region_set;
2234 m_print_regions.clear();
2235 PrintObjectRegions *print_object_regions = nullptr;
2236 for (PrintObject *print_object : m_objects)
2237 if (print_object_regions != print_object->m_shared_regions) {
2238 print_object_regions = print_object->m_shared_regions;
2239 for (std::unique_ptr<Slic3r::PrintRegion> &print_region : print_object_regions->all_regions)
2240 if (auto it = region_set.find(print_region.get()); it == region_set.end()) {
2241 int print_region_id = int(m_print_regions.size());
2242 m_print_regions.emplace_back(print_region.get());
2243 print_region->m_print_region_id = print_region_id;
2244 } else {
2245 print_region->m_print_region_id = (*it)->print_region_id();
2246 }
2247 }
2248 }
2249
2250 // Update SlicingParameters for each object where the SlicingParameters is not valid.
2251 // If it is not valid, then it is ensured that PrintObject.m_slicing_params is not in use
2252 // (posSlicing and posSupportMaterial was invalidated).
2253 for (PrintObject *object : m_objects)
2254 {
2255 object->update_slicing_parameters();
2256 m_support_used |= object->config().enable_support;
2257 }
2258
2259#ifdef _DEBUG
2261#endif /* _DEBUG */
2262
2263 //BBS: add timestamp logic
2264 if (apply_status != APPLY_STATUS_UNCHANGED)
2266 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" %1%: finished, this %2%, m_modified_count %3%, apply_status %4%, m_support_used %5%")%__LINE__ %this %m_modified_count %apply_status %m_support_used;
2267 return static_cast<ApplyStatus>(apply_status);
2268}
static PhysicalTilt physical_tilt(BeltRotationAxis axis, double angle_deg)
Definition BeltTransform.hpp:73
std::vector< int > values
Definition Config.hpp:418
void set(const ConfigOption *rhs) override
Definition Config.hpp:420
bool invalidate_all_steps()
Definition PrintBase.hpp:622
bool invalidate_steps(StepTypeIterator step_begin, StepTypeIterator step_end)
Definition PrintBase.hpp:618
void call_cancel_callback()
Definition PrintBase.hpp:540
ApplyStatus
Definition PrintBase.hpp:410
const Model & model() const
Definition PrintBase.hpp:420
DynamicPrintConfig m_ori_full_print_config
Definition PrintBase.hpp:563
PlaceholderParser m_placeholder_parser
Definition PrintBase.hpp:564
const std::vector< std::string > & extruder_retract_keys() const
Definition PrintConfig.hpp:855
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
int m_modified_count
Definition Print.hpp:1575
ConstPrintObjectPtrsAdaptor objects() const
Definition Print.hpp:1134
Vec3d shrinkage_compensation() const
Definition Print.hpp:1398
void update_shrinkage_compensation(size_t num_extruders, std::vector< int > &variant_index)
Definition PrintApply.cpp:1266
bool invalidate_step(PrintStep step)
Definition Print.cpp:663
bool collect_filament_variant_uses(const MultiNozzleUtils::LayeredNozzleGroupResult &group_result, const DynamicPrintConfig &config, std::unordered_map< int, std::vector< FilamentVariantUse > > &uses) const
Definition Print.cpp:4756
PrintRegionPtrs m_print_regions
Definition Print.hpp:1504
bool m_support_used
Definition Print.hpp:1561
bool invalidate_state_by_config_options(const ConfigOptionResolver &new_config, const std::vector< t_config_option_key > &opt_keys)
Definition Print.cpp:263
void update_filament_self_index_cache()
Definition Print.cpp:5073
Vec3d pos(const Pt &p)
Definition ReprojectPointsOnMesh.hpp:16
zaa_minimize_perimeter_height ConfigOptionFloat
Definition PrintConfig.hpp:1954
bool model_mmu_segmentation_data_changed(const ModelObject &mo, const ModelObject &mo_new)
Definition Model.cpp:4286
bool model_brim_points_data_changed(const ModelObject &mo, const ModelObject &mo_new)
Definition Model.cpp:4300
NozzleVolumeType
Definition PrintConfig.hpp:618
static void layer_height_ranges_copy_configs(t_layer_config_ranges &lr_dst, const t_layer_config_ranges &lr_src)
Definition PrintApply.cpp:144
bool model_custom_seam_data_changed(const ModelObject &mo, const ModelObject &mo_new)
Definition Model.cpp:4279
bool has_any_mixed_filament(const std::vector< unsigned char > &is_mixed)
Definition FilamentMixer.cpp:486
void check_model_ids_validity(const Model &model)
Definition Model.cpp:4375
std::set< std::string > printer_options_with_variant_1
Definition PrintConfig.cpp:10236
bool model_object_list_extended(const Model &model_old, const Model &model_new)
Definition Model.cpp:4187
void check_model_ids_equal(const Model &model1, const Model &model2)
Definition Model.cpp:4399
ConfigOptionEnumsGenericTempl< false > ConfigOptionEnumsGeneric
Definition Config.hpp:2311
@ PARAMETER_MODIFIER
Definition Model.hpp:359
@ PRECISE_SEAM_BLOCKED
Definition Model.hpp:368
@ PRECISE_SEAM_LEFT
Definition Model.hpp:365
@ MODEL_PART
Definition Model.hpp:357
@ SUPPORT_BLOCKER
Definition Model.hpp:360
@ PRECISE_SEAM_ENFORCED
Definition Model.hpp:367
@ PRECISE_SEAM_CENTER
Definition Model.hpp:364
@ NEGATIVE_VOLUME
Definition Model.hpp:358
@ PRECISE_SEAM_NEUTRAL
Definition Model.hpp:369
@ PRECISE_SEAM_RIGHT
Definition Model.hpp:366
@ SUPPORT_ENFORCER
Definition Model.hpp:361
static bool custom_per_printz_gcodes_tool_changes_differ(const std::vector< CustomGCode::Item > &va, const std::vector< CustomGCode::Item > &vb)
Definition PrintApply.cpp:251
zaa_minimize_perimeter_height PrintRegionConfig
Definition PrintConfig.hpp:2183
static PrintObjectRegions * generate_print_object_regions(PrintObjectRegions *print_object_regions_old, const ModelVolumePtrs &model_volumes, const LayerRanges &model_layer_ranges, const PrintRegionConfig &default_region_config, const Transform3d &trafo, size_t num_extruders, const float xy_contour_compensation, const std::vector< unsigned int > &painting_extruders, std::vector< int > &variant_index, const bool has_painted_fuzzy_skin, const std::vector< bool > &slot_per_part_enabled={})
Definition PrintApply.cpp:1042
static bool layer_height_ranges_equal(const t_layer_config_ranges &lr1, const t_layer_config_ranges &lr2, bool check_layer_height)
Definition PrintApply.cpp:233
static t_config_option_keys print_config_diffs(const PrintConfig &current_config, const DynamicPrintConfig &new_full_config, DynamicPrintConfig &filament_overrides, int plate_index, std::vector< int > &filament_maps, const std::vector< int > *dynamic_override_indices=nullptr)
Definition PrintApply.cpp:283
@ ExtruderMax
Definition TriangleSelector.hpp:66
@ Extruder1
Definition TriangleSelector.hpp:34
@ psWipeTower
Definition Print.hpp:109
@ psSkirtBrim
Definition Print.hpp:114
@ psGCodeExport
Definition Print.hpp:118
static bool transform3d_lower(const Transform3d &lhs, const Transform3d &rhs)
Definition PrintApply.cpp:158
std::vector< unsigned int > parse_mixed_components(const std::string &str)
Definition FilamentMixer.cpp:172
std::vector< std::string > t_config_option_keys
Definition Config.hpp:97
bool model_fuzzy_skin_data_changed(const ModelObject &mo, const ModelObject &mo_new)
Definition Model.cpp:4293
static t_config_option_keys full_print_config_diffs(const DynamicPrintConfig &current_full_config, const DynamicPrintConfig &new_full_config, int plate_index)
Definition PrintApply.cpp:342
static bool transform3d_equal(const Transform3d &lhs, const Transform3d &rhs)
Definition PrintApply.cpp:172
zaa_minimize_perimeter_height PrintObjectConfig
Definition PrintConfig.hpp:2183
const PrintConfigDef print_config_def
Definition PrintConfig.cpp:10099
ConfigOptionInts effective_physical_extruder_map(const ConfigOptionInts *explicit_pem, int nozzle_count)
Definition IMEXHelpers.cpp:52
static void model_volume_list_copy_configs(ModelObject &model_object_dst, const ModelObject &model_object_src, TypeFilterFn type_filter)
Definition PrintApply.cpp:97
ConfigOptionIntsTempl< false > ConfigOptionInts
Definition Config.hpp:1154
zaa_minimize_perimeter_height nozzle_volume_type((ConfigOptionStrings, extruder_ams_count))((ConfigOptionStrings
ExtruderType
Definition PrintConfig.hpp:612
static std::vector< PrintObjectTrafoAndInstances > print_objects_from_model_object(const ModelObject &model_object, const Vec3d &shrinkage_compensation, bool force_separate_instances=false)
Definition PrintApply.cpp:192
ConfigOptionBoolsTempl< false > ConfigOptionBools
Definition Config.hpp:2066
std::set< std::string > printer_options_with_variant_2
Definition PrintConfig.cpp:10270
FilamentMapMode
Definition PrintConfig.hpp:632
@ fmmManual
Definition PrintConfig.hpp:635
static bool is_printable_filament_changed(const DynamicPrintConfig &new_full_config, const Polygon &old_poly, const Polygon &new_poly)
Definition PrintApply.cpp:370
static bool trafos_differ_in_rotation_by_z_and_mirroring_by_xy_only(const Transform3d &t1, const Transform3d &t2)
Definition PrintApply.cpp:631
std::vector< PrintObject * > PrintObjectPtrs
Definition Print.hpp:1008
bool model_volume_list_changed(const ModelObject &model_object_old, const ModelObject &model_object_new, TypeFilterFn type_filter)
Definition Model.cpp:4198
bool model_custom_supports_data_changed(const ModelObject &mo, const ModelObject &mo_new)
Definition Model.cpp:4272
@ posSlice
Definition Print.hpp:124
@ posSupportMaterial
Definition Print.hpp:125
void print_objects_regions_invalidate_keep_some_volumes(PrintObjectRegions &print_object_regions, ModelVolumePtrs old_volumes, ModelVolumePtrs new_volumes)
Definition PrintApply.cpp:742
zaa_minimize_perimeter_height has_scarf_joint_seam((ConfigOptionFloat, machine_hotend_change_time))((ConfigOptionFloat
std::set< std::string > filament_options_with_variant
Definition PrintConfig.cpp:10150
bool verify_update_print_object_regions(ModelVolumePtrs model_volumes, const PrintRegionConfig &default_region_config, size_t num_extruders, PrintObjectRegions &print_object_regions, const std::function< void(const PrintRegionConfig &, const PrintRegionConfig &, const t_config_option_keys &)> &callback_invalidate, std::vector< int > &variant_index)
Definition PrintApply.cpp:814
bool model_object_list_equal(const Model &model_old, const Model &model_new)
Definition Model.cpp:4175
SeamScarfType
Definition PrintConfig.hpp:399
@ None
Definition PrintConfig.hpp:400
std::set< std::string > print_options_with_variant
Definition PrintConfig.cpp:10102
ConfigOptionFloatsTempl< false > ConfigOptionFloats
Definition Config.hpp:1015
bool is_auto_filament_map_mode(FilamentMapMode mode)
Definition PrintConfig.hpp:649
void model_volume_list_update_supports_and_seams(ModelObject &model_object_dst, const ModelObject &model_object_new)
Definition PrintApply.cpp:48
std::vector< unsigned int > expand_mixed_filaments(const std::vector< unsigned int > &extruders_0based, const std::vector< unsigned char > &is_mixed, const std::vector< std::string > &comp_strs)
Definition FilamentMixer.cpp:520
@ lock
Definition GLGizmoEmboss.cpp:209
@ New
Definition PrintApply.cpp:486
@ Old
Definition PrintApply.cpp:485
@ Moved
Definition PrintApply.cpp:487
@ Deleted
Definition PrintApply.cpp:488
@ PartiallyValid
Definition PrintApply.cpp:494
@ Deleted
Definition PrintApply.cpp:549
@ Reused
Definition PrintApply.cpp:550
@ Unknown
Definition PrintApply.cpp:548

References apply(), Slic3r::check_model_ids_validity(), Slic3r::expand_mixed_filaments(), extruders(), Slic3r::ConfigOption::getBool(), Slic3r::has_any_mixed_filament(), Slic3r::has_scarf_joint_seam(), Slic3r::ModelObject::layer_config_ranges, m_support_used, Slic3r::PrintBaseWithState< PrintStep, psCount >::model(), Slic3r::None, Slic3r::DynamicPrintConfig::normalize_fdm_1(), Slic3r::DynamicPrintConfig::normalize_fdm_2(), objects(), Slic3r::ConfigBase::option(), Slic3r::BeltTransformPipeline::physical_tilt(), Slic3r::ConfigBase::set(), Slic3r::DynamicConfig::set_key_value(), Slic3r::ConfigOptionSingle< T >::value, and Slic3r::ConfigOptionVector< unsigned char >::values.

Referenced by apply(), Slic3r::GUI::Plater::load_gcode(), and anonymous_namespace{OrcaSlicer_profile_validator.cpp}::slice_two_color_cube_and_export().

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

◆ apply_config_for_render()

void Slic3r::Print::apply_config_for_render ( const DynamicConfig & config)
4884{
4885 m_config.apply(config);
4886}

References apply_config_for_render(), config(), and m_config.

Referenced by apply_config_for_render(), and Slic3r::GUI::Plater::load_gcode().

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:

◆ auto_assign_extruders()

void Slic3r::Print::auto_assign_extruders ( ModelObject * model_object) const
3117{
3118 // only assign extruders if object has more than one volume
3119 if (model_object->volumes.size() < 2)
3120 return;
3121
3122// size_t extruders = m_config.nozzle_diameter.values.size();
3123 for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) {
3124 ModelVolume *volume = model_object->volumes[volume_id];
3125 //FIXME Vojtech: This assigns an extruder ID even to a modifier volume, if it has a material assigned.
3126 if ((volume->is_model_part() || volume->is_modifier()) && ! volume->material_id().empty() && ! volume->config.has("extruder"))
3127 volume->config.set("extruder", int(volume_id + 1));
3128 }
3129}

References Slic3r::ModelVolume::config, Slic3r::ModelConfig::has(), Slic3r::ModelVolume::is_model_part(), Slic3r::ModelVolume::is_modifier(), Slic3r::ModelVolume::material_id(), Slic3r::ModelConfig::set(), and Slic3r::ModelObject::volumes.

Referenced by anonymous_namespace{OrcaSlicer_profile_validator.cpp}::slice_two_color_cube_and_export().

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

◆ brim_flow()

Flow Slic3r::Print::brim_flow ( ) const
3064{
3065 ConfigOptionFloatOrPercent width = m_config.initial_layer_line_width;
3066 if (width.value <= 0)
3067 width = m_print_regions.front()->config().inner_wall_line_width;
3068 if (width.value <= 0)
3069 width = m_objects.front()->config().line_width;
3070
3071 /* We currently use a random region's perimeter extruder.
3072 While this works for most cases, we should probably consider all of the perimeter
3073 extruders and take the one with, say, the smallest index.
3074 The same logic should be applied to the code that selects the extruder during G-code
3075 generation as well. */
3078 // Flow::new_from_config_width takes care of the percent to value substitution
3079 width,
3080 (float)nozzle_diameter_for_filament(m_config, m_print_regions.front()->config().outer_wall_filament_id, this->is_BBL_printer()),
3081 (float)this->skirt_first_layer_height());
3082}
static Flow new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height)
Definition Flow.cpp:136
coord_t width(const BoundingBox &box)
Definition Arrange.cpp:1184
@ frPerimeter
Definition Flow.hpp:20
double nozzle_diameter_for_filament(const PrintConfig &config, int filament_id, bool is_bbl_printer)
Definition PrintConfig.cpp:148

References Slic3r::frPerimeter, m_config, m_objects, m_print_regions, Slic3r::Flow::new_from_config_width(), and Slic3r::nozzle_diameter_for_filament().

Referenced by _make_skirt(), Slic3r::make_belt_brim(), Slic3r::make_brim(), Slic3r::makeBrimInfillImpl(), Slic3r::outer_inner_brim_area(), and Slic3r::tryExPolygonOffset().

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

◆ calib_mode() [1/2]

CalibMode & Slic3r::Print::calib_mode ( )
inline
1344{ return m_calib_params.mode; }
Calib_Params m_calib_params
Definition Print.hpp:1584

References m_calib_params.

Referenced by Slic3r::GCode::_do_export(), Slic3r::GCode::process_layer(), and Slic3r::GCode::set_object_info().

Here is the caller graph for this function:

◆ calib_mode() [2/2]

const CalibMode Slic3r::Print::calib_mode ( ) const
inline
1345{ return m_calib_params.mode; }

References m_calib_params.

◆ calib_params()

const Calib_Params & Slic3r::Print::calib_params ( ) const
inline
1347{ return m_calib_params; }

References m_calib_params.

Referenced by Slic3r::GCode::_do_export(), Slic3r::GCode::generate_timelapse_gcode(), and Slic3r::GCode::process_layer().

Here is the caller graph for this function:

◆ call_cancel_callback()

void Slic3r::PrintBase::call_cancel_callback ( )
inlineprotectedinherited
cancel_callback_type m_cancel_callback
Definition PrintBase.hpp:580

References m_cancel_callback.

◆ cancel()

void Slic3r::PrintBase::cancel ( )
inlineinherited
std::atomic< CancelStatus > m_cancel_status
Definition PrintBase.hpp:577

References CANCELED_BY_USER, and m_cancel_status.

Referenced by Slic3r::GUI::GLGizmoFdmSupports::invalid_support_volumes(), and Slic3r::GUI::GLGizmoFdmSupports::on_shutdown().

Here is the caller graph for this function:

◆ cancel_callback()

std::function< void()> Slic3r::PrintBase::cancel_callback ( )
inlineprotectedinherited

◆ cancel_internal()

void Slic3r::PrintBase::cancel_internal ( )
inlineinherited

◆ cancel_status()

CancelStatus Slic3r::PrintBase::cancel_status ( ) const
inlineinherited
506{ return m_cancel_status.load(std::memory_order_acquire); }

References m_cancel_status.

◆ canceled()

bool Slic3r::PrintBase::canceled ( ) const
inlineinherited
508{ return m_cancel_status.load(std::memory_order_acquire) != NOT_CANCELED; }

References m_cancel_status, and NOT_CANCELED.

Referenced by Slic3r::GCode::do_export(), and Slic3r::plugin_hooks::anonymous_namespace{PluginHooks.cpp}::install_slicing_pipeline_hook().

Here is the caller graph for this function:

◆ check_multi_filament_valid()

StringObjectException Slic3r::Print::check_multi_filament_valid ( const Print & print)
staticprivate
1837{
1838 auto print_config = print.config();
1839 const std::string incompatible_temp_msg = L("Selected nozzle temperatures are incompatible. Each filament's nozzle temperature must fall within the recommended nozzle temperature range of the other filaments. Otherwise, nozzle clogging or printer damage may occur.");
1840 const std::string invalid_temp_range_msg = L("Invalid recommended nozzle temperature range. The lower bound must be lower than the upper bound.");
1841 const std::string incompatible_temp_msg_preferences_enable = L("If you still want to print, you can enable the option in Preferences / Control / Slicing / Remove mixed temperature restriction.");
1842 if(print_config.print_sequence == PrintSequence::ByObject) {// use ByObject valid under ByObject print sequence
1843 bool has_incompatible_object = false;
1844 bool enable_mix_printing = !print.need_check_multi_filaments_compatibility();
1845 StringObjectException ret;
1846
1847 for (const auto &objectID_t : print.print_object_ids()) {
1848 std::set<int> obj_used_extruder_ids;
1849 auto print_object = print.get_object(objectID_t);// current object
1850 if (print_object){
1851 auto object_extruders_t = print_object->object_extruders(); // object used extruder
1852 for (unsigned int extruder : object_extruders_t) {
1853 obj_used_extruder_ids.insert(static_cast<int>(extruder));
1854 }
1855 }
1856
1857 if (print_object->has_support_material()) { // extruder used by supports
1858 auto num_extruders = (unsigned int) print_config.filament_diameter.size();
1859 assert(print_object->config().support_filament >= 0);
1860 if (print_object->config().support_filament >= 1 && (unsigned int)print_object->config().support_filament < num_extruders + 1)
1861 obj_used_extruder_ids.insert((unsigned int) print_object->config().support_filament - 1);//0-based extruder id
1862 assert(print_object->config().support_interface_filament >= 0);
1863 if (print_object->config().support_interface_filament >= 1 && (unsigned int)print_object->config().support_interface_filament < num_extruders + 1)
1864 obj_used_extruder_ids.insert((unsigned int) print_object->config().support_interface_filament - 1);
1865 }
1866 std::vector<std::string> filament_types;
1867 std::vector<int> nozzle_temperatures;
1868 std::vector<int> nozzle_temperature_range_lows;
1869 std::vector<int> nozzle_temperature_range_highs;
1870 filament_types.reserve(obj_used_extruder_ids.size());
1871 nozzle_temperatures.reserve(obj_used_extruder_ids.size());
1872 nozzle_temperature_range_lows.reserve(obj_used_extruder_ids.size());
1873 nozzle_temperature_range_highs.reserve(obj_used_extruder_ids.size());
1874
1875 for (const auto &extruder_idx : obj_used_extruder_ids) {
1876 filament_types.push_back(print_config.filament_type.get_at(extruder_idx));
1877 nozzle_temperatures.push_back(print_config.nozzle_temperature.get_at(extruder_idx));
1878 nozzle_temperature_range_lows.push_back(print_config.nozzle_temperature_range_low.get_at(extruder_idx));
1879 nozzle_temperature_range_highs.push_back(print_config.nozzle_temperature_range_high.get_at(extruder_idx));
1880 }
1881
1882 auto compatibility = check_multi_filaments_compatibility(
1883 filament_types,
1884 nozzle_temperatures,
1885 nozzle_temperature_range_lows,
1886 nozzle_temperature_range_highs); // check for each object
1888 ret.string = invalid_temp_range_msg;
1889 return ret;
1890 }
1891 if (compatibility != FilamentCompatibilityType::Compatible) {
1892 has_incompatible_object = true;
1893 break;
1894 }
1895 }
1896 if (has_incompatible_object){
1897 if (enable_mix_printing) {
1898 ret.string = incompatible_temp_msg;
1899 ret.is_warning = true;
1900 } else
1901 ret.string = incompatible_temp_msg + " " + incompatible_temp_msg_preferences_enable;
1902 }
1903 return ret;
1904 }
1905 std::vector<unsigned int> extruders = print.extruders();
1906 std::vector<std::string> filament_types;
1907 std::vector<int> nozzle_temperatures;
1908 std::vector<int> nozzle_temperature_range_lows;
1909 std::vector<int> nozzle_temperature_range_highs;
1910 filament_types.reserve(extruders.size());
1911 nozzle_temperatures.reserve(extruders.size());
1912 nozzle_temperature_range_lows.reserve(extruders.size());
1913 nozzle_temperature_range_highs.reserve(extruders.size());
1914 for (const auto& extruder_idx : extruders) {
1915 filament_types.push_back(print_config.filament_type.get_at(extruder_idx));
1916 nozzle_temperatures.push_back(print_config.nozzle_temperature.get_at(extruder_idx));
1917 nozzle_temperature_range_lows.push_back(print_config.nozzle_temperature_range_low.get_at(extruder_idx));
1918 nozzle_temperature_range_highs.push_back(print_config.nozzle_temperature_range_high.get_at(extruder_idx));
1919 }
1920
1921 auto compatibility = check_multi_filaments_compatibility(
1922 filament_types,
1923 nozzle_temperatures,
1924 nozzle_temperature_range_lows,
1925 nozzle_temperature_range_highs);
1926 bool enable_mix_printing = !print.need_check_multi_filaments_compatibility();
1927
1928 StringObjectException ret;
1929
1931 ret.string = invalid_temp_range_msg;
1932 return ret;
1933 }
1934
1935 if(compatibility != FilamentCompatibilityType::Compatible){
1936 if(enable_mix_printing){
1937 ret.string = incompatible_temp_msg;
1938 ret.is_warning = true;
1939 }
1940 else{
1941 ret.string = incompatible_temp_msg + " " + incompatible_temp_msg_preferences_enable;
1942 }
1943 }
1944
1945 return ret;
1946}
static FilamentCompatibilityType check_multi_filaments_compatibility(const std::vector< std::string > &filament_types, const std::vector< int > &nozzle_temperatures, const std::vector< int > &nozzle_temperature_range_lows, const std::vector< int > &nozzle_temperature_range_highs)
Definition Print.cpp:1740
@ InvalidTemperatureRange
Definition Print.hpp:1049
@ Compatible
Definition Print.hpp:1045
@ ByObject
Definition PrintConfig.hpp:312

References Print(), Slic3r::ByObject, check_multi_filaments_compatibility(), Slic3r::Compatible, config(), extruders(), get_object(), Slic3r::InvalidTemperatureRange, Slic3r::StringObjectException::is_warning, L, need_check_multi_filaments_compatibility(), print_object_ids(), and Slic3r::StringObjectException::string.

Referenced by validate().

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

◆ check_multi_filaments_compatibility()

FilamentCompatibilityType Slic3r::Print::check_multi_filaments_compatibility ( const std::vector< std::string > & filament_types,
const std::vector< int > & nozzle_temperatures,
const std::vector< int > & nozzle_temperature_range_lows,
const std::vector< int > & nozzle_temperature_range_highs )
static
1745{
1746 const size_t filament_count = filament_types.size();
1747 if (filament_count < 2)
1749
1750 std::vector<int> resolved_temperatures(filament_count, 0);
1751 std::vector<int> resolved_range_lows(filament_count, 0);
1752 std::vector<int> resolved_range_highs(filament_count, 0);
1753 for (size_t i = 0; i < filament_count; ++i) {
1754 int range_low = (i < nozzle_temperature_range_lows.size()) ? nozzle_temperature_range_lows[i] : 0;
1755 int range_high = (i < nozzle_temperature_range_highs.size()) ? nozzle_temperature_range_highs[i] : 0;
1756
1757 if (range_low == 0 || range_high == 0) {
1758 int default_low = range_low;
1759 int default_high = range_high;
1760 MaterialType::get_temperature_range(filament_types[i], default_low, default_high);
1761 if (range_low == 0)
1762 range_low = default_low;
1763 if (range_high == 0)
1764 range_high = default_high;
1765 }
1766
1767 if (range_low >= range_high)
1769
1770 int print_temperature = (i < nozzle_temperatures.size()) ? nozzle_temperatures[i] : 0;
1771
1772 resolved_temperatures[i] = print_temperature;
1773 resolved_range_lows[i] = range_low;
1774 resolved_range_highs[i] = range_high;
1775 }
1776
1777 for (size_t i = 0; i < filament_count; ++i) {
1778 for (size_t j = i + 1; j < filament_count; ++j) {
1779 const bool i_temp_is_compatible_with_j =
1780 resolved_temperatures[i] >= resolved_range_lows[j] &&
1781 resolved_temperatures[i] <= resolved_range_highs[j];
1782 const bool j_temp_is_compatible_with_i =
1783 resolved_temperatures[j] >= resolved_range_lows[i] &&
1784 resolved_temperatures[j] <= resolved_range_highs[i];
1785
1786 if (i_temp_is_compatible_with_j && j_temp_is_compatible_with_i)
1787 continue;
1788
1789 // Range-only rule: any pair outside mutual recommended ranges is incompatible.
1791 }
1792 }
1793
1795}
static bool get_temperature_range(const std::string &type, int &min_temp, int &max_temp)
Definition MaterialType.cpp:108
@ HighLowMixed
Definition Print.hpp:1046

References Slic3r::Compatible, Slic3r::MaterialType::get_temperature_range(), Slic3r::HighLowMixed, and Slic3r::InvalidTemperatureRange.

Referenced by check_multi_filament_valid(), and Slic3r::GUI::CalibrationPresetPage::is_filaments_compatiable().

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

◆ clear()

void Slic3r::Print::clear ( )
overridevirtual

Implements Slic3r::PrintBase.

236{
237 std::scoped_lock<std::mutex> lock(this->state_mutex());
238 // The following call should stop background processing if it is running.
239 this->invalidate_all_steps();
240 for (PrintObject *object : m_objects)
241 delete object;
242 m_objects.clear();
243 m_print_regions.clear();
244 m_model.clear_objects();
247 m_shrinkage_compensation = Vec3d::Ones();
248}
bool m_same_shrinkage_compensations
Definition Print.hpp:1505
StatisticsByExtruderCount m_statistics_by_extruder_count
Definition Print.hpp:1562
Vec3d m_shrinkage_compensation
Definition Print.hpp:1506

References Slic3r::PrintBaseWithState< PrintStep, psCount >::invalidate_all_steps(), Slic3r::PrintBaseWithState< PrintStep, psCount >::m_model, m_objects, m_print_regions, m_same_shrinkage_compensations, m_shrinkage_compensation, m_statistics_by_extruder_count, PrintObject, and Slic3r::PrintBaseWithState< PrintStep, psCount >::state_mutex().

Referenced by ~Print().

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

◆ collect_filament_variant_uses()

bool Slic3r::Print::collect_filament_variant_uses ( const MultiNozzleUtils::LayeredNozzleGroupResult & group_result,
const DynamicPrintConfig & config,
std::unordered_map< int, std::vector< FilamentVariantUse > > & uses ) const
private
4759{
4760 auto opt_filament_type = config.option<ConfigOptionStrings>("filament_type");
4761 auto opt_extruder_type = dynamic_cast<const ConfigOptionEnumsGeneric*>(config.option("extruder_type"));
4762 if (!opt_filament_type || !opt_extruder_type)
4763 return false;
4764
4765 const size_t filament_count = opt_filament_type->values.size();
4766 const size_t extruder_count = opt_extruder_type->values.size();
4767 auto add_use = [&](std::set<FilamentVariantUse> &variant_set, const MultiNozzleUtils::NozzleInfo &nozzle) {
4768 // Orca: a persisted result can outlive a printer swap; never index the extruder
4769 // arrays with a stale nozzle record.
4770 if (nozzle.extruder_id < 0 || static_cast<size_t>(nozzle.extruder_id) >= extruder_count)
4771 return;
4772 FilamentVariantUse use;
4773 use.extruder_type = static_cast<ExtruderType>(opt_extruder_type->get_at(nozzle.extruder_id));
4774 use.nozzle_volume_type = nozzle.volume_type;
4775 use.extruder_id = nozzle.extruder_id;
4776 variant_set.insert(use);
4777 };
4778 for (size_t f_index = 0; f_index < filament_count; ++f_index) {
4779 std::set<FilamentVariantUse> variant_set;
4780 for (const MultiNozzleUtils::NozzleInfo &nozzle : group_result.get_nozzles_for_filament(static_cast<int>(f_index)))
4781 add_use(variant_set, nozzle);
4782 // A filament the plan never routes (not printed) still needs a deterministic slot: take
4783 // its default-map assignment from the result itself, so both the slice-time write-back
4784 // and the apply-time reproduction resolve the same slot even when the surrounding
4785 // filament_map has not round-tripped through the plate config in between.
4786 if (variant_set.empty()) {
4787 if (auto default_nozzle = group_result.get_nozzle_for_filament(static_cast<int>(f_index), -1); default_nozzle.has_value())
4788 add_use(variant_set, *default_nozzle);
4789 }
4790 // Filaments still without a variant stay absent from the map: the slot rebuild then
4791 // resolves them from their static filament_map / filament_volume_map assignment.
4792 if (!variant_set.empty())
4793 uses[static_cast<int>(f_index)] = std::vector<FilamentVariantUse>(variant_set.begin(), variant_set.end());
4794 }
4795 return true;
4796}

References collect_filament_variant_uses(), config(), Slic3r::FilamentVariantUse::extruder_id, Slic3r::FilamentVariantUse::extruder_type, Slic3r::ConfigOptionVector< T >::get_at(), Slic3r::MultiNozzleUtils::LayeredNozzleGroupResult::get_nozzle_for_filament(), Slic3r::MultiNozzleUtils::LayeredNozzleGroupResult::get_nozzles_for_filament(), Slic3r::FilamentVariantUse::nozzle_volume_type, and Slic3r::ConfigOptionVector< T >::values.

Referenced by collect_filament_variant_uses(), and update_to_config_by_nozzle_group_result().

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

◆ compacted_wipe_tower_clearance_valid()

StringObjectException Slic3r::Print::compacted_wipe_tower_clearance_valid ( const Print & print,
Polygons * polygons = nullptr,
std::vector< std::pair< Polygon, float > > * height_polygons = nullptr )
static
1429{
1430 const PrintConfig &config = print.config();
1431 if (! wipe_tower_sparse_layers_skipped(config) || config.print_sequence != PrintSequence::ByLayer || ! print.has_wipe_tower())
1432 return {};
1433
1434 const CompactedTowerZone zone = compacted_wipe_tower_zone(config, estimated_wipe_tower_footprint(print));
1435 if (zone.empty())
1436 return {};
1437
1438 StringObjectException exception;
1439 Polygons offenders;
1440 bool body_tier_used = false;
1441 for (const PrintObject *object : print.objects()) {
1442 const double object_top = unscaled<double>(object->max_z());
1443 for (const PrintInstance &instance : object->instances()) {
1444 const Polygon inst_hull = compacted_tower_print_instance_hull(*object, instance);
1445 if (inst_hull.points.empty())
1446 continue;
1447 const CompactedTowerClearance clearance = compacted_wipe_tower_clearance(config, zone, inst_hull, object_top);
1448 body_tier_used = body_tier_used || compacted_tower_body_tier(clearance);
1449 // Every tier the precise check applies is applied here too, otherwise an object standing
1450 // within the toolhead radius would pass here and then be rejected mid-slice, which is the
1451 // one outcome this check exists to prevent. The compacted tower base is unknown before
1452 // slicing, so the rise is measured from the plate rather than from the tower top; that
1453 // overstates it by the tower's own height and makes this check err strict, never lax.
1454 if (object_top <= clearance.allowed_rise + EPSILON)
1455 continue;
1456
1457 // Height-limit and too-close cases share one user-facing message: both mean the layout
1458 // violates the "No sparse layers" clearance rule, and the remedies are the same.
1459 const std::string msg = compacted_wipe_tower_clearance_error();
1460 if (exception.string.empty()) {
1461 exception.string = msg;
1462 exception.object = instance.model_instance;
1463 } else {
1464 // Same wording for every offender; keep a single copy and drop the object pointer.
1465 exception.object = nullptr;
1466 }
1467 const Polygon outline = compacted_wipe_tower_offender_outline(inst_hull, clearance.body_clearance);
1468 offenders.emplace_back(outline);
1469 if (height_polygons)
1470 height_polygons->emplace_back(outline, float(clearance.allowed_rise));
1471 }
1472 }
1473
1474 // Draw the tower's keep-out ring alongside the offending objects, so the collision area reads as
1475 // "this object reaches into the space the toolhead needs around the tower" rather than as a lone
1476 // highlighted object. Emitted only on a real collision; the plater discards polygons otherwise.
1477 // Only the rings some object on this plate is actually measured against are drawn, so that a ring
1478 // and an object outline touching always means that object is over its limit.
1479 if (polygons && ! offenders.empty()) {
1480 append(*polygons, compacted_wipe_tower_rings(zone, body_tier_used));
1481 append(*polygons, offenders);
1482 }
1483 return exception;
1484}
static std::string compacted_wipe_tower_clearance_error()
Definition Print.cpp:1349
CompactedTowerClearance compacted_wipe_tower_clearance(const PrintConfig &config, const CompactedTowerZone &zone, const Polygon &inst_hull, double object_rise)
Definition Print.cpp:1295
CompactedTowerZone compacted_wipe_tower_zone(const PrintConfig &config, const Polygon &tower_footprint)
Definition Print.cpp:1242
static Polygon compacted_tower_print_instance_hull(const PrintObject &object, const PrintInstance &instance)
Definition Print.cpp:1356
@ ByLayer
Definition PrintConfig.hpp:311
bool compacted_tower_body_tier(const CompactedTowerClearance &clearance)
Definition Print.hpp:1666
bool wipe_tower_sparse_layers_skipped(const PrintConfig &config)
Definition WipeTower.cpp:53
Polygons compacted_wipe_tower_rings(const CompactedTowerZone &zone, bool any_body_tier)
Definition Print.cpp:1234
static Polygon estimated_wipe_tower_footprint(const Print &print)
Definition Print.cpp:1374
Polygon compacted_wipe_tower_offender_outline(const Polygon &inst_hull, double body_clearance)
Definition Print.cpp:1337

References Print(), Slic3r::CompactedTowerClearance::allowed_rise, Slic3r::append(), Slic3r::CompactedTowerClearance::body_clearance, Slic3r::ByLayer, Slic3r::compacted_tower_body_tier(), Slic3r::compacted_tower_print_instance_hull(), Slic3r::compacted_wipe_tower_clearance(), Slic3r::compacted_wipe_tower_clearance_error(), Slic3r::compacted_wipe_tower_offender_outline(), Slic3r::compacted_wipe_tower_rings(), Slic3r::compacted_wipe_tower_zone(), config(), Slic3r::CompactedTowerZone::empty(), EPSILON, Slic3r::estimated_wipe_tower_footprint(), has_wipe_tower(), Slic3r::StringObjectException::object, objects(), Slic3r::MultiPoint::points, PrintObject, Slic3r::StringObjectException::string, Slic3r::unscaled(), and Slic3r::wipe_tower_sparse_layers_skipped().

Referenced by validate().

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

◆ config()

const PrintConfig & Slic3r::Print::config ( ) const
inline
1131{ return m_config; }

References m_config.

Referenced by Slic3r::FillLightning::Generator::Generator(), Slic3r::FillLightning::Generator::Generator(), Slic3r::ToolOrdering::ToolOrdering(), Slic3r::TreeSupport3D::TreeModelVolumes::TreeModelVolumes(), Slic3r::TreeSupport3D::TreeSupportMeshGroupSettings::TreeSupportMeshGroupSettings(), Slic3r::GCode::_do_export(), _make_wipe_tower(), Slic3r::GCode::_print_first_layer_bed_temperature(), Slic3r::GCode::_print_first_layer_extruder_temperatures(), Slic3r::GUI::GLCanvas3D::_render_overlays(), Slic3r::FillAdaptive::adaptive_fill_line_spacing(), Slic3r::SeamPlacer::align_seam_points(), Slic3r::GCode::append_full_config(), append_support_extruders(), append_wipe_tower_extruder(), apply_config_for_render(), Slic3r::ToolOrdering::assign_custom_gcodes(), Slic3r::LayerRegion::bridging_flow(), Slic3r::build_filament_group_context(), check_multi_filament_valid(), collect_filament_variant_uses(), Slic3r::GCode::collect_layers_to_print(), Slic3r::PrintRegion::collect_object_printing_extruders(), Slic3r::combine_object_brims(), compacted_wipe_tower_clearance_valid(), libvgcode::convert_brim_skirt_to_vertices(), Slic3r::GCode::do_export(), Slic3r::TreeSupport::draw_circles(), Slic3r::WipingExtrusions::ensure_perimeters_infills_order(), Slic3r::estimated_wipe_tower_footprint(), export_gcode_from_previous_file(), Slic3r::ToolOrdering::generate_first_layer_tool_order(), Slic3r::GCode::generate_object_skirt_group(), Slic3r::GCode::generate_timelapse_gcode(), Slic3r::get_default_perimeter_spacing(), Slic3r::get_imex_active_tools(), Slic3r::get_path_of_change_filament(), Slic3r::ToolOrdering::get_recommended_filament_maps(), Slic3r::get_wipe_tower_extrusions_extents(), Slic3r::getObjectExtruderMap(), Slic3r::group_fills(), Slic3r::groupingVolumesForBrim(), Slic3r::TimelapsePosPicker::init(), Slic3r::BeltGCode::init_belt_writer(), Slic3r::DoExport::init_ooze_prevention(), Slic3r::plugin_hooks::anonymous_namespace{PluginHooks.cpp}::install_slicing_pipeline_hook(), is_all_objects_are_short(), Slic3r::layered_print_cleareance_valid(), Slic3r::GUI::GCodeViewer::load_as_gcode(), Slic3r::make_belt_brim(), Slic3r::make_brim(), Slic3r::LayerRegion::make_perimeters(), Slic3r::Skirt::make_skirt_loops_per_extruder_other_layers(), Slic3r::WipingExtrusions::mark_wiping_extrusions(), Slic3r::GCode::mass_load_limited_machine_acceleration(), Slic3r::multi_material_segmentation_by_painting(), object_skirt_offset(), Slic3r::outer_inner_brim_area(), output_filename(), Slic3r::GCode::print_machine_envelope(), process(), Slic3r::GCode::process_belt_brim_layer(), Slic3r::GCode::process_layer(), Slic3r::GCode::process_layer_change_gcode(), Slic3r::GCode::process_layers(), Slic3r::GCode::process_layers(), Slic3r::CLI::run(), Slic3r::segmentation_top_and_bottom_layers(), sequential_print_clearance_valid(), Slic3r::GCode::set_object_info(), Slic3r::Layer::simplify_support_loop(), Slic3r::Layer::simplify_support_multi_path(), Slic3r::Layer::simplify_support_path(), Slic3r::ToolOrdering::sort_and_build_data(), Slic3r::support_exclusion_areas_for_layers(), Slic3r::support_material_flow(), Slic3r::support_material_interface_flow(), Slic3r::support_transition_flow(), Slic3r::translated_bed_exclusion_volumes(), Slic3r::translated_bed_exclusion_volumes_by_extruder(), Slic3r::tryExPolygonOffset(), Slic3r::GUI::GLCanvas3D::update_compacted_wipe_tower_clearance(), Slic3r::GUI::GLCanvas3D::update_sequential_clearance(), update_shrinkage_compensation(), validate(), Slic3r::DoExport::validate_custom_gcode(), and Slic3r::BeltGCode::write_belt_header().

◆ config_index_generation()

size_t Slic3r::Print::config_index_generation ( ) const
inline
1391{ return m_config_index_generation; }
size_t m_config_index_generation
Definition Print.hpp:1547

References m_config_index_generation.

◆ 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:

◆ default_object_config()

const PrintObjectConfig & Slic3r::Print::default_object_config ( ) const
inline
1132{ return m_default_object_config; }

References m_default_object_config, and Slic3r::PrintObjectConfig.

Referenced by Slic3r::GCode::_do_export(), and Slic3r::get_outer_wall_volumetric_speed().

Here is the caller graph for this function:

◆ default_region_config()

const PrintRegionConfig & Slic3r::Print::default_region_config ( ) const
inline
1133{ return m_default_region_config; }

References m_default_region_config, and Slic3r::PrintRegionConfig.

Referenced by Slic3r::GCode::_do_export(), Slic3r::GCode::emit_belt_brim_bands(), Slic3r::GCode::generate_object_brim(), and Slic3r::get_outer_wall_volumetric_speed().

Here is the caller graph for this function:

◆ empty()

bool Slic3r::Print::empty ( ) const
inlineoverridevirtual

Implements Slic3r::PrintBase.

1077{ return m_objects.empty(); }

References m_objects.

Referenced by _make_skirt(), Slic3r::ToolOrdering::get_recommended_filament_maps(), sequential_print_clearance_valid(), and validate_compacted_wipe_tower_clearance().

Here is the caller graph for this function:

◆ enable_timelapse_print()

bool Slic3r::Print::enable_timelapse_print ( ) const
5230{
5231 return m_config.timelapse_type.value == TimelapseType::tlSmooth;
5232}
@ tlSmooth
Definition PrintConfig.hpp:470

References enable_timelapse_print(), m_config, and Slic3r::tlSmooth.

Referenced by _make_wipe_tower(), enable_timelapse_print(), and has_wipe_tower().

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

◆ export_cached_data()

int Slic3r::Print::export_cached_data ( const std::string & dir_path,
bool with_space = false )
overridevirtual

Reimplemented from Slic3r::PrintBase.

6635{
6636 int ret = 0;
6637 boost::filesystem::path directory_path(directory);
6638
6639 auto convert_layer_to_json = [](json& layer_json, const Layer* layer) {
6640 json slice_polygons_json = json::array(), slice_bboxs_json = json::array(), overhang_polygons_json = json::array(), layer_regions_json = json::array();
6641 layer_json[JSON_LAYER_PRINT_Z] = layer->print_z;
6642 layer_json[JSON_LAYER_HEIGHT] = layer->height;
6643 layer_json[JSON_LAYER_SLICE_Z] = layer->slice_z;
6644 layer_json[JSON_LAYER_ID] = layer->id();
6645 //layer_json["slicing_errors"] = layer->slicing_errors;
6646
6647 //sliced_polygons
6648 for (const ExPolygon& slice_polygon : layer->lslices) {
6649 json slice_polygon_json = slice_polygon;
6650 slice_polygons_json.push_back(std::move(slice_polygon_json));
6651 }
6652 layer_json[JSON_LAYER_SLICED_POLYGONS] = std::move(slice_polygons_json);
6653
6654 //sliced_bbox
6655 for (const BoundingBox& slice_bbox : layer->lslices_bboxes) {
6656 json bbox_json = json::array();
6657
6658 bbox_json = slice_bbox;
6659 slice_bboxs_json.push_back(std::move(bbox_json));
6660 }
6661 layer_json[JSON_LAYER_SLLICED_BBOXES] = std::move(slice_bboxs_json);
6662
6663 //overhang_polygons
6664 for (const ExPolygon& overhang_polygon : layer->loverhangs) {
6665 json overhang_polygon_json = overhang_polygon;
6666 overhang_polygons_json.push_back(std::move(overhang_polygon_json));
6667 }
6668 layer_json[JSON_LAYER_OVERHANG_POLYGONS] = std::move(overhang_polygons_json);
6669
6670 //overhang_box
6671 layer_json[JSON_LAYER_OVERHANG_BBOX] = layer->loverhangs_bbox;
6672
6673 for (const LayerRegion *layer_region : layer->regions()) {
6674 json region_json = *layer_region;
6675
6676 layer_regions_json.push_back(std::move(region_json));
6677 }
6678 layer_json[JSON_LAYER_REGIONS] = std::move(layer_regions_json);
6679
6680 return;
6681 };
6682
6683 //firstly clear this directory
6684 if (fs::exists(directory_path)) {
6685 fs::remove_all(directory_path);
6686 }
6687 try {
6688 if (!fs::create_directory(directory_path)) {
6689 BOOST_LOG_TRIVIAL(error) << boost::format("create directory %1% failed")%directory;
6691 }
6692 }
6693 catch (...)
6694 {
6695 BOOST_LOG_TRIVIAL(error) << boost::format("create directory %1% failed")%directory;
6697 }
6698
6699 int count = 0;
6700 std::vector<std::string> filename_vector;
6701 std::vector<json> json_vector;
6702 for (PrintObject *obj : m_objects) {
6703 const ModelObject* model_obj = obj->model_object();
6704 if (obj->get_shared_object()) {
6705 BOOST_LOG_TRIVIAL(info) << boost::format("shared object %1%, skip directly")%model_obj->name;
6706 continue;
6707 }
6708
6709 const PrintInstance &print_instance = obj->instances()[0];
6710 const ModelInstance *model_instance = print_instance.model_instance;
6711 size_t identify_id = (model_instance->loaded_id > 0)?model_instance->loaded_id: model_instance->id().id;
6712 std::string file_name = directory +"/obj_"+std::to_string(identify_id)+".json";
6713
6714 BOOST_LOG_TRIVIAL(info) << boost::format("begin to dump object %1%, identify_id %2% to %3%")%model_obj->name %identify_id %file_name;
6715
6716 try {
6717 json root_json, layers_json = json::array(), support_layers_json = json::array(), first_layer_groups = json::array();
6718
6719 root_json[JSON_OBJECT_NAME] = model_obj->name;
6720 root_json[JSON_IDENTIFY_ID] = identify_id;
6721
6722 //export the layers
6723 std::vector<json> layers_json_vector(obj->layer_count());
6724 tbb::parallel_for(
6725 tbb::blocked_range<size_t>(0, obj->layer_count()),
6726 [&layers_json_vector, obj, convert_layer_to_json](const tbb::blocked_range<size_t>& layer_range) {
6727 for (size_t layer_index = layer_range.begin(); layer_index < layer_range.end(); ++ layer_index) {
6728 const Layer *layer = obj->get_layer(layer_index);
6729 json layer_json;
6730 convert_layer_to_json(layer_json, layer);
6731 layers_json_vector[layer_index] = std::move(layer_json);
6732 }
6733 }
6734 );
6735 for (int l_index = 0; l_index < layers_json_vector.size(); l_index++) {
6736 layers_json.push_back(std::move(layers_json_vector[l_index]));
6737 }
6738 layers_json_vector.clear();
6739 /*for (const Layer *layer : obj->layers()) {
6740 // for each layer
6741 json layer_json;
6742
6743 convert_layer_to_json(layer_json, layer);
6744
6745 layers_json.push_back(std::move(layer_json));
6746 }*/
6747
6748 root_json[JSON_LAYERS] = std::move(layers_json);
6749
6750 //export the support layers
6751 std::vector<json> support_layers_json_vector(obj->support_layer_count());
6752 tbb::parallel_for(
6753 tbb::blocked_range<size_t>(0, obj->support_layer_count()),
6754 [&support_layers_json_vector, obj, convert_layer_to_json](const tbb::blocked_range<size_t>& support_layer_range) {
6755 for (size_t s_layer_index = support_layer_range.begin(); s_layer_index < support_layer_range.end(); ++ s_layer_index) {
6756 const SupportLayer *support_layer = obj->get_support_layer(s_layer_index);
6757 json support_layer_json, support_islands_json = json::array(), support_fills_json, supportfills_entities_json = json::array();
6758
6759 convert_layer_to_json(support_layer_json, support_layer);
6760
6761 support_layer_json[JSON_SUPPORT_LAYER_INTERFACE_ID] = support_layer->interface_id();
6762 support_layer_json[JSON_SUPPORT_LAYER_TYPE] = support_layer->support_type;
6763
6764 //support_islands
6765 for (const ExPolygon& support_island : support_layer->support_islands) {
6766 json support_island_json = support_island;
6767 support_islands_json.push_back(std::move(support_island_json));
6768 }
6769 support_layer_json[JSON_SUPPORT_LAYER_ISLANDS] = std::move(support_islands_json);
6770
6771 //support_fills
6772 support_fills_json[JSON_EXTRUSION_NO_SORT] = support_layer->support_fills.no_sort;
6773 support_fills_json[JSON_EXTRUSION_ENTITY_TYPE] = JSON_EXTRUSION_TYPE_COLLECTION;
6774 for (const ExtrusionEntity* extrusion_entity : support_layer->support_fills.entities) {
6775 json supportfill_entity_json, supportfill_entity_paths_json = json::array();
6776 bool ret = convert_extrusion_to_json(supportfill_entity_json, supportfill_entity_paths_json, extrusion_entity);
6777 if (!ret)
6778 continue;
6779
6780 supportfills_entities_json.push_back(std::move(supportfill_entity_json));
6781 }
6782 support_fills_json[JSON_EXTRUSION_ENTITIES] = std::move(supportfills_entities_json);
6783 support_layer_json[JSON_SUPPORT_LAYER_FILLS] = std::move(support_fills_json);
6784
6785 support_layers_json_vector[s_layer_index] = std::move(support_layer_json);
6786 }
6787 }
6788 );
6789 for (int s_index = 0; s_index < support_layers_json_vector.size(); s_index++) {
6790 support_layers_json.push_back(std::move(support_layers_json_vector[s_index]));
6791 }
6792 support_layers_json_vector.clear();
6793
6794 /*for (const SupportLayer *support_layer : obj->support_layers()) {
6795 json support_layer_json, support_islands_json = json::array(), support_fills_json, supportfills_entities_json = json::array();
6796
6797 convert_layer_to_json(support_layer_json, support_layer);
6798
6799 support_layer_json[JSON_SUPPORT_LAYER_INTERFACE_ID] = support_layer->interface_id();
6800
6801 //support_islands
6802 for (const ExPolygon& support_island : support_layer->support_islands.expolygons) {
6803 json support_island_json = support_island;
6804 support_islands_json.push_back(std::move(support_island_json));
6805 }
6806 support_layer_json[JSON_SUPPORT_LAYER_ISLANDS] = std::move(support_islands_json);
6807
6808 //support_fills
6809 support_fills_json[JSON_EXTRUSION_NO_SORT] = support_layer->support_fills.no_sort;
6810 support_fills_json[JSON_EXTRUSION_ENTITY_TYPE] = JSON_EXTRUSION_TYPE_COLLECTION;
6811 for (const ExtrusionEntity* extrusion_entity : support_layer->support_fills.entities) {
6812 json supportfill_entity_json, supportfill_entity_paths_json = json::array();
6813 bool ret = convert_extrusion_to_json(supportfill_entity_json, supportfill_entity_paths_json, extrusion_entity);
6814 if (!ret)
6815 continue;
6816
6817 supportfills_entities_json.push_back(std::move(supportfill_entity_json));
6818 }
6819 support_fills_json[JSON_EXTRUSION_ENTITIES] = std::move(supportfills_entities_json);
6820 support_layer_json[JSON_SUPPORT_LAYER_FILLS] = std::move(support_fills_json);
6821
6822 support_layers_json.push_back(std::move(support_layer_json));
6823 } // for each layer*/
6824 root_json[JSON_SUPPORT_LAYERS] = std::move(support_layers_json);
6825
6826 const std::vector<groupedVolumeSlices> &first_layer_obj_groups = obj->firstLayerObjGroups();
6827 for (size_t s_group_index = 0; s_group_index < first_layer_obj_groups.size(); ++ s_group_index) {
6828 groupedVolumeSlices group = first_layer_obj_groups[s_group_index];
6829
6830 //convert the id
6831 for (ObjectID& obj_id : group.volume_ids)
6832 {
6833 const ModelVolume* currentModelVolumePtr = nullptr;
6834 //BBS: support shared object logic
6835 const PrintObject* shared_object = obj->get_shared_object();
6836 if (!shared_object)
6837 shared_object = obj;
6838 const ModelVolumePtrs& volumes_ptr = shared_object->model_object()->volumes;
6839 size_t volume_count = volumes_ptr.size();
6840 for (size_t index = 0; index < volume_count; index ++) {
6841 currentModelVolumePtr = volumes_ptr[index];
6842 if (currentModelVolumePtr->id() == obj_id) {
6843 obj_id.id = index;
6844 break;
6845 }
6846 }
6847 }
6848
6849 json first_layer_group_json;
6850
6851 first_layer_group_json = group;
6852 first_layer_groups.push_back(std::move(first_layer_group_json));
6853 }
6854 root_json[JSON_FIRSTLAYER_GROUPS] = std::move(first_layer_groups);
6855
6856 filename_vector.push_back(file_name);
6857 json_vector.push_back(std::move(root_json));
6858 /*boost::nowide::ofstream c;
6859 c.open(file_name, std::ios::out | std::ios::trunc);
6860 if (with_space)
6861 c << root_json.dump(1, '\t') << std::endl;
6862 else
6863 c << root_json.dump(0) << std::endl;
6864 c.close();*/
6865 count ++;
6866 BOOST_LOG_TRIVIAL(info) << boost::format("will dump object %1%'s json to %2%.")%model_obj->name%file_name;
6867 }
6868 catch(std::exception &err) {
6869 BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": save to "<<file_name<<" got a generic exception, reason = " << err.what();
6871 }
6872 }
6873
6874 boost::mutex mutex;
6875 tbb::parallel_for(
6876 tbb::blocked_range<size_t>(0, filename_vector.size()),
6877 [filename_vector, &json_vector, with_space, &ret, &mutex](const tbb::blocked_range<size_t>& output_range) {
6878 for (size_t object_index = output_range.begin(); object_index < output_range.end(); ++ object_index) {
6879 try {
6880 boost::nowide::ofstream c;
6881 c.open(filename_vector[object_index], std::ios::out | std::ios::trunc);
6882 if (with_space)
6883 c << json_vector[object_index].dump(1, '\t') << std::endl;
6884 else
6885 c << json_vector[object_index].dump(0) << std::endl;
6886 c.close();
6887 }
6888 catch(std::exception &err) {
6889 BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": save to "<<filename_vector[object_index]<<" got a generic exception, reason = " << err.what();
6890 boost::unique_lock l(mutex);
6891 ret = CLI_EXPORT_CACHE_WRITE_FAILED;
6892 }
6893 }
6894 }
6895 );
6896
6897 BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(": total printobject count %1%, saved %2%, ret=%3%")%m_objects.size() %count %ret;
6898 return ret;
6899}
static unsigned int count
Definition AuxiliaryDataViewModel.cpp:246
#define JSON_LAYER_SLLICED_BBOXES
Definition Print.cpp:5918
#define JSON_FIRSTLAYER_GROUPS
Definition Print.cpp:5907
#define JSON_LAYER_HEIGHT
Definition Print.cpp:5915
#define JSON_LAYER_SLICE_Z
Definition Print.cpp:5914
#define JSON_LAYERS
Definition Print.cpp:5903
#define JSON_LAYER_PRINT_Z
Definition Print.cpp:5913
#define JSON_LAYER_OVERHANG_BBOX
Definition Print.cpp:5920
#define JSON_IDENTIFY_ID
Definition Print.cpp:5900
#define JSON_LAYER_OVERHANG_POLYGONS
Definition Print.cpp:5919
#define JSON_SUPPORT_LAYERS
Definition Print.cpp:5904
#define JSON_LAYER_REGIONS
Definition Print.cpp:5906
#define JSON_LAYER_ID
Definition Print.cpp:5916
#define JSON_LAYER_SLICED_POLYGONS
Definition Print.cpp:5917
#define JSON_OBJECT_NAME
Definition Print.cpp:5899
ObjectID id() const
Definition ObjectID.hpp:69
#define CLI_EXPORT_CACHE_DIRECTORY_CREATE_FAILED
Definition Utils.hpp:74
#define CLI_EXPORT_CACHE_WRITE_FAILED
Definition Utils.hpp:75
nlohmann::json json
Definition AssembleList.cpp:42
std::vector< ModelVolume * > ModelVolumePtrs
Definition Model.hpp:139

References CLI_EXPORT_CACHE_DIRECTORY_CREATE_FAILED, count, export_cached_data(), Slic3r::ObjectBase::id(), Slic3r::ObjectID::id, JSON_FIRSTLAYER_GROUPS, JSON_IDENTIFY_ID, JSON_LAYER_HEIGHT, JSON_LAYER_ID, JSON_LAYER_OVERHANG_BBOX, JSON_LAYER_OVERHANG_POLYGONS, JSON_LAYER_PRINT_Z, JSON_LAYER_REGIONS, JSON_LAYER_SLICE_Z, JSON_LAYER_SLICED_POLYGONS, JSON_LAYER_SLLICED_BBOXES, JSON_LAYERS, JSON_OBJECT_NAME, JSON_SUPPORT_LAYERS, Slic3r::ModelInstance::loaded_id, m_objects, Slic3r::PrintInstance::model_instance, Slic3r::PrintObjectBase::model_object(), Slic3r::ModelObject::name, PrintObject, Slic3r::groupedVolumeSlices::volume_ids, and Slic3r::ModelObject::volumes.

Referenced by export_cached_data().

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

◆ export_gcode()

std::string Slic3r::Print::export_gcode ( const std::string & path_template,
GCodeProcessorResult * result,
ThumbnailsGeneratorCallback thumbnail_cb = nullptr )
3978{
3979 // output everything to a G-code file
3980 // The following call may die if the filename_format template substitution fails.
3981 std::string path = this->output_filepath(path_template);
3982 std::string message;
3983 if (!path.empty() && result == nullptr) {
3984 // Only show the path if preview_data is not set -> running from command line.
3985 message = L("Exporting G-code");
3986 message += " to ";
3987 message += path;
3988 } else
3989 message = L("Generating G-code");
3990 this->set_status(80, message);
3991
3992 // The following line may die for multiple reasons.
3993 // Factory: use BeltGCode for belt printers, plain GCode otherwise.
3994 std::unique_ptr<GCode> gcode;
3995 if (m_config.belt_printer.value)
3996 gcode = std::make_unique<BeltGCode>();
3997 else
3998 gcode = std::make_unique<GCode>();
3999 //BBS: compute plate offset for gcode-generator
4000 const Vec3d origin = this->get_plate_origin();
4001 // IMEX firmware-managed zones: writer offset gets the IMEX shift so emitted gcode is
4002 // centered at bed origin; processor offset stays at plate_origin so the gcode-preview
4003 // visualizer renders the centered slice at the bed center rather than the prepare-view
4004 // zone placement. Both arms reduce to set_gcode_offset() semantics when shift is zero.
4005 // Derived here rather than read as pushed-in state, so an exporter reached without a
4006 // preceding process() (or after a config change that only invalidated psGCodeExport)
4007 // still emits in the frame the current config asks for.
4009 const Vec2d imex_off = this->get_imex_slice_offset();
4010 gcode->set_gcode_offset_with_imex_shift(origin(0), origin(1), imex_off.x(), imex_off.y());
4011 gcode->do_export(this, path.c_str(), result, thumbnail_cb);
4012 gcode->export_layer_filaments(result);
4013 //BBS
4014 if (result != nullptr) {
4015 result->conflict_result = m_conflict_result;
4016 // Surface the slicer's per-filament nozzle grouping onto the post-slice result
4017 // the device GUI reads. This is the static L/R + rack subset the multi-nozzle path computes;
4018 // null for single-nozzle prints where nothing computes it. It is assigned after g-code
4019 // generation and read by no emitter, so it does not affect the emitted g-code.
4020 result->nozzle_group_result = this->get_layered_nozzle_group_result();
4021 }
4022 return path.c_str();
4023}
void set_status(int percent, const std::string &message, unsigned int flags=SlicingStatus::DEFAULT, int warning_step=-1) const
Definition PrintBase.cpp:123
std::string output_filepath(const std::string &path, const std::string &filename_base=std::string()) const
Definition PrintBase.cpp:106
void update_imex_slice_offset()
Definition Print.cpp:4522
ConflictResultOpt m_conflict_result
Definition Print.hpp:1577
Vec2d get_imex_slice_offset() const
Definition Print.hpp:1307
const Vec3d get_plate_origin() const
Definition Print.hpp:1295
@ gcode
Definition Time.hpp:14
Eigen::Matrix< double, 2, 1, Eigen::DontAlign > Vec2d
Definition Point.hpp:67

References Slic3r::GCodeProcessorResult::conflict_result, export_gcode(), get_imex_slice_offset(), get_layered_nozzle_group_result(), get_plate_origin(), L, m_config, m_conflict_result, Slic3r::GCodeProcessorResult::nozzle_group_result, Slic3r::PrintBaseWithState< PrintStep, psCount >::output_filepath(), Slic3r::PrintBaseWithState< PrintStep, psCount >::set_status(), and update_imex_slice_offset().

Referenced by export_gcode(), Slic3r::GUI::CalibUtils::process_and_store_3mf(), Slic3r::CLI::run(), and anonymous_namespace{OrcaSlicer_profile_validator.cpp}::slice_two_color_cube_and_export().

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

◆ export_gcode_from_previous_file()

void Slic3r::Print::export_gcode_from_previous_file ( const std::string & file,
GCodeProcessorResult * result,
ThumbnailsGeneratorCallback thumbnail_cb = nullptr )
5711{
5712 {
5713 LifecycleEventContext ctx;
5714 ctx.id = std::to_string(m_model.id().id);
5715 ctx.name = get_model_name();
5716 ctx.code = LifecycleEvtCode::Ok;
5717 ctx.msg = file;
5718 ctx.cancellation_check = [this]() { return canceled(); };
5720 }
5721
5722 try {
5723 GCodeProcessor processor;
5725 const Vec3d origin = this->get_plate_origin();
5726 processor.set_xy_offset(origin(0), origin(1));
5727 // Reloaded sliced projects re-estimate with the same nozzle-grouping slot context as the
5728 // original export; process_file re-derives the device-side nozzle grouping onto the result
5729 // (via ensure_nozzle_group_result), so the multi-nozzle send/monitor mapping survives here.
5730 if (result != nullptr && result->nozzle_group_result)
5731 processor.initialize_from_context(result->nozzle_group_result);
5732 processor.configure_exclusion_volume_path_check(this->config());
5733 //processor.enable_producers(true);
5734 processor.process_file(file);
5735
5736 // filament seq is loaded from file, processor result will override the value
5737 auto filament_seq_loaded = result->filament_change_sequence;
5738 auto nozzle_seq_loaded = result->nozzle_change_sequence;
5739 *result = std::move(processor.extract_result());
5740 result->filament_change_sequence = filament_seq_loaded;
5741 result->nozzle_change_sequence = nozzle_seq_loaded;
5742 } catch (std::exception &ex) {
5743 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": found errors when process gcode file %1%") %file.c_str();
5744 {
5745 LifecycleEventContext ctx;
5746 ctx.id = std::to_string(m_model.id().id);
5747 ctx.name = get_model_name();
5748 ctx.code = LifecycleEvtCode::Error;
5749 ctx.msg = file + "\n" + ex.what();
5750 ctx.cancellation_check = [this]() { return canceled(); };
5752 }
5753 throw Slic3r::RuntimeError(
5754 // TRN %1% is a file path.
5755 Slic3r::format(L("Failed to process the G-code file %1% from the previous 3MF file."), file) + "\n");
5756 }
5757
5758 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": process the G-code file %1% successfully")%file.c_str();
5759
5760 {
5761 LifecycleEventContext ctx;
5762 ctx.id = std::to_string(m_model.id().id);
5763 ctx.name = get_model_name();
5764 ctx.code = LifecycleEvtCode::Ok;
5765 ctx.msg = file;
5766 ctx.cancellation_check = [this]() { return canceled(); };
5768 }
5769}
static bool s_IsBBLPrinter
Definition GCodeProcessor.hpp:650
bool canceled() const
Definition PrintBase.hpp:508
std::string get_model_name() const
Definition Print.cpp:5677
bool & is_BBL_printer()
Definition Print.hpp:1341
@ GCodeExportFinished
Definition LifecycleEvents.hpp:32
@ GCodeExportStarted
Definition LifecycleEvents.hpp:31
@ Error
Definition LifecycleEvents.hpp:68
@ Ok
Definition LifecycleEvents.hpp:68
void fire_lifecycle_event(LifecycleEvent event, const LifecycleEventContext &ctx)
Definition LifecycleEvents.hpp:234

References Slic3r::PrintBaseWithState< PrintStep, psCount >::canceled(), Slic3r::LifecycleEventContext::cancellation_check, Slic3r::LifecycleEventContext::code, config(), Slic3r::GCodeProcessor::configure_exclusion_volume_path_check(), Slic3r::Error, export_gcode_from_previous_file(), Slic3r::GCodeProcessor::extract_result(), Slic3r::GCodeProcessorResult::filament_change_sequence, Slic3r::fire_lifecycle_event(), Slic3r::format(), Slic3r::GCodeExportFinished, Slic3r::GCodeExportStarted, get_model_name(), get_plate_origin(), Slic3r::LifecycleEventContext::id, Slic3r::GCodeProcessor::initialize_from_context(), is_BBL_printer(), L, Slic3r::PrintBaseWithState< PrintStep, psCount >::m_model, Slic3r::LifecycleEventContext::msg, Slic3r::LifecycleEventContext::name, Slic3r::GCodeProcessorResult::nozzle_change_sequence, Slic3r::GCodeProcessorResult::nozzle_group_result, Slic3r::Ok, Slic3r::GCodeProcessor::process_file(), Slic3r::GCodeProcessor::s_IsBBLPrinter, and Slic3r::GCodeProcessor::set_xy_offset().

Referenced by export_gcode_from_previous_file().

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

◆ extruders()

std::vector< unsigned int > Slic3r::Print::extruders ( bool conside_custom_gcode = false) const
771{
772 std::vector<unsigned int> extruders = this->object_extruders();
774
775 if (conside_custom_gcode)
779 return extruders;
780}
void append_wipe_tower_extruder(std::vector< unsigned int > &extruders) const
Definition Print.cpp:795
std::vector< unsigned int > support_material_extruders() const
Definition Print.cpp:727
void append_tool_change_extruders(std::vector< unsigned int > &extruders) const
Definition Print.cpp:783
std::vector< unsigned int > object_extruders() const
Definition Print.cpp:686
void sort_remove_duplicates(std::vector< T > &vec)
Definition libslic3r.h:231

References Slic3r::append(), append_tool_change_extruders(), append_wipe_tower_extruder(), extruders(), object_extruders(), Slic3r::sort_remove_duplicates(), and support_material_extruders().

Referenced by Slic3r::GCode::_do_export(), _make_skirt(), Slic3r::GCode::_print_first_layer_extruder_temperatures(), append_model_object_extruders(), append_support_extruders(), append_tool_change_extruders(), append_wipe_tower_extruder(), apply(), check_multi_filament_valid(), Slic3r::estimated_wipe_tower_footprint(), extruders(), is_dynamic_group_reorder(), Slic3r::layered_print_cleareance_valid(), Slic3r::GUI::Preview::load_print_as_fff(), max_allowed_layer_height(), object_extruders(), output_filename(), Slic3r::GCode::process_layer(), support_material_extruders(), Slic3r::GUI::GLCanvas3D::update_compacted_wipe_tower_clearance(), update_shrinkage_compensation(), validate(), and wipe_tower_data().

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

◆ finalize()

virtual void Slic3r::PrintBase::finalize ( )
inlinevirtualinherited

Reimplemented in Slic3r::SLAPrint.

441{}

◆ finalize_first_layer_convex_hull()

void Slic3r::Print::finalize_first_layer_convex_hull ( )
private
4645{
4647 if (m_first_layer_convex_hull.empty()) {
4648 // Neither skirt nor brim was extruded. Collect points of printed objects from 1st layer.
4649 for (Polygon &poly : this->first_layer_islands())
4650 append(m_first_layer_convex_hull.points, std::move(poly.points));
4651 }
4652 append(m_first_layer_convex_hull.points, this->first_layer_wipe_tower_corners());
4654}
Polygons first_layer_islands() const
Definition Print.cpp:4437
Polygon m_first_layer_convex_hull
Definition Print.hpp:1528

References Slic3r::append(), Slic3r::Geometry::convex_hull(), finalize_first_layer_convex_hull(), first_layer_islands(), m_first_layer_convex_hull, and m_skirt_convex_hull.

Referenced by finalize_first_layer_convex_hull().

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

◆ finished()

bool Slic3r::Print::finished ( ) const
inlineoverridevirtual

Implements Slic3r::PrintBase.

1096{ return this->is_step_done(psGCodeExport); }
bool is_step_done(PrintStep step) const
Definition Print.hpp:1092

References is_step_done(), and Slic3r::psGCodeExport.

Referenced by output_filename().

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

◆ first_layer_convex_hull()

const Polygon & Slic3r::Print::first_layer_convex_hull ( ) const
inline
1178{ return m_first_layer_convex_hull; }

References m_first_layer_convex_hull.

Referenced by Slic3r::GCode::_do_export().

Here is the caller graph for this function:

◆ first_layer_islands()

Polygons Slic3r::Print::first_layer_islands ( ) const
private
4438{
4439 Polygons islands;
4440 for (PrintObject *object : m_objects) {
4441 Polygons object_islands;
4442 for (ExPolygon &expoly : object->m_layers.front()->lslices)
4443 object_islands.push_back(expoly.contour);
4444 if (!object->support_layers().empty()) {
4445 if (object->support_layers().front()->support_type==stInnerNormal)
4446 object->support_layers().front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON));
4447 else if(object->support_layers().front()->support_type==stInnerTree) {
4448 ExPolygons &expolys_first_layer = object->m_support_layers.front()->lslices;
4449 for (ExPolygon &expoly : expolys_first_layer) { object_islands.push_back(expoly.contour); }
4450 }
4451 }
4452 islands.reserve(islands.size() + object_islands.size() * object->instances().size());
4453 for (const PrintInstance &instance : object->instances())
4454 for (Polygon &poly : object_islands) {
4455 islands.push_back(poly);
4456 islands.back().translate(instance.shift);
4457 }
4458 }
4459 return islands;
4460}
@ stInnerTree
Definition Layer.hpp:299
@ stInnerNormal
Definition Layer.hpp:298

References first_layer_islands(), m_objects, PrintObject, SCALED_EPSILON, Slic3r::stInnerNormal, and Slic3r::stInnerTree.

Referenced by finalize_first_layer_convex_hull(), and first_layer_islands().

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

◆ first_layer_wipe_tower_corners()

Points Slic3r::Print::first_layer_wipe_tower_corners ( bool check_wipe_tower_existance = true) const
4463{
4464 Points corners;
4465 if (check_wipe_tower_existance && (!has_wipe_tower() || m_wipe_tower_data.tool_changes.empty()))
4466 return corners;
4467 {
4468 double width = m_wipe_tower_data.bbx.max.x() - m_wipe_tower_data.bbx.min.x();
4469 double depth = m_wipe_tower_data.bbx.max.y() -m_wipe_tower_data.bbx.min.y();
4470 Vec2d pt0 = m_wipe_tower_data.bbx.min + m_wipe_tower_data.rib_offset.cast<double>();
4471
4472 // First the corners.
4473 std::vector<Vec2d> pts = { pt0,
4474 Vec2d(pt0.x()+width, pt0.y()),
4475 Vec2d(pt0.x()+width, pt0.y()+depth),
4476 Vec2d(pt0.x(),pt0.y()+depth)
4477 };
4478
4479 // Now the stabilization cone, which only a Type2 tower with a cone wall prints.
4480 Vec2d center = (pts[0] + pts[2])/2.;
4481 const bool has_cone = wipe_tower_type() == WipeTowerType::Type2 && m_config.wipe_tower_wall_type.value == WipeTowerWallType::wtwCone;
4482 const double cone_angle = has_cone ? m_config.wipe_tower_cone_angle.value : 0.;
4483 const auto [cone_R, cone_x_scale] = WipeTower2::get_wipe_tower_cone_base(m_config.prime_tower_width, m_wipe_tower_data.height, m_wipe_tower_data.depth, cone_angle);
4484 double r = cone_R + m_wipe_tower_data.brim_width;
4485 for (double alpha = 0.; alpha<2*M_PI; alpha += M_PI/20.)
4486 pts.emplace_back(center + r*Vec2d(std::cos(alpha)/cone_x_scale, std::sin(alpha)));
4487
4488 for (Vec2d& pt : pts) {
4489 pt = Eigen::Rotation2Dd(Geometry::deg2rad(m_config.wipe_tower_rotation_angle.value)) * pt;
4490 //Orca: offset the wipe tower to the plate origin
4491 pt += Vec2d(m_config.wipe_tower_x.get_at(m_plate_index) + m_origin(0), m_config.wipe_tower_y.get_at(m_plate_index) + m_origin(1));
4492 corners.emplace_back(Point(scale_(pt.x()), scale_(pt.y())));
4493 }
4494 }
4495 return corners;
4496}
#define M_PI
Definition ExtrusionSimulator.cpp:31
static std::pair< double, double > get_wipe_tower_cone_base(double width, double height, double depth, double angle_deg)
Definition WipeTower2.cpp:2151
void center(ExPolygonsWithIds &e)
Definition Emboss.cpp:1382

References Slic3r::center(), Slic3r::Geometry::deg2rad(), first_layer_wipe_tower_corners(), Slic3r::WipeTower2::get_wipe_tower_cone_base(), has_wipe_tower(), m_config, m_origin, M_PI, Slic3r::PrintBaseWithState< PrintStep, psCount >::m_plate_index, m_wipe_tower_data, scale_, Slic3r::Type2, wipe_tower_type(), and Slic3r::wtwCone.

Referenced by _make_skirt(), first_layer_wipe_tower_corners(), and Slic3r::CLI::run().

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

◆ full_print_config()

◆ 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:

◆ get_brimMap()

std::map< ObjectID, ExtrusionEntityCollection > & Slic3r::Print::get_brimMap ( )
inline
1146{ return m_brimMap; }
std::map< ObjectID, ExtrusionEntityCollection > m_brimMap
Definition Print.hpp:1516

References m_brimMap.

◆ get_compatible_filament_type()

int Slic3r::Print::get_compatible_filament_type ( const std::set< int > & types)
static
1815{
1816 bool has_high_temperature_filament = false;
1817 bool has_low_temperature_filament = false;
1818
1819 for (const auto& type : filament_types) {
1820 if (type == FilamentTempType::HighTemp)
1821 has_high_temperature_filament = true;
1822 else if (type == FilamentTempType::LowTemp)
1823 has_low_temperature_filament = true;
1824 }
1825
1826 if (has_high_temperature_filament && has_low_temperature_filament)
1827 return HighLowCompatible;
1828 else if (has_high_temperature_filament)
1829 return HighTemp;
1830 else if (has_low_temperature_filament)
1831 return LowTemp;
1832 return HighLowCompatible;
1833}
@ HighLowCompatible
Definition Print.hpp:1040
@ LowTemp
Definition Print.hpp:1039
@ HighTemp
Definition Print.hpp:1038

References Slic3r::HighLowCompatible, Slic3r::HighTemp, and Slic3r::LowTemp.

Referenced by Slic3r::get_instance_arrange_poly().

Here is the caller graph for this function:

◆ get_config_index() [1/2]

int Slic3r::Print::get_config_index ( int filament_id,
int layer_id,
const std::vector< std::string > & variant_list,
const std::vector< int > & self_index_list,
FilamentIndexMap * index_map )
protected
5115{
5116 const MultiNozzleUtils::LayeredNozzleGroupResult *group_result = m_layered_nozzle_group_result.get();
5117 // Orca: defensive — when no grouping producer has published a result yet, fall back to the
5118 // static identity: one filament-variant column per filament.
5119 if (!group_result)
5120 return filament_id;
5121 auto nozzle_info = group_result->get_nozzle_for_filament(filament_id, layer_id);
5122 if (!nozzle_info.has_value()) {
5123 // Orca: this fallback runs per-filament/per-layer in the g-code hot path — log once per filament
5124 // (reset each slice) instead of flooding thousands of identical lines that bury the real error.
5125 // Without the cache, the log set is left alone too; the cached caller reports the same filament.
5126 if (index_map && m_missing_nozzle_group_logged.insert(filament_id).second)
5127 BOOST_LOG_TRIVIAL(error) << __FUNCTION__
5128 << boost::format(", Line %1%: could not found group_nozzle_info corresponding to filament_id %2%, layer_id %3% (further occurrences for this filament suppressed)") % __LINE__ % filament_id %
5129 layer_id;
5130 return 0;
5131 }
5132
5133 ExtruderType extruder_type = ExtruderType(m_config.extruder_type.get_at(nozzle_info->extruder_id));
5134 NozzleVolumeType nozzle_volume_type = nozzle_info->volume_type;
5135 if (!index_map)
5136 return get_config_index_base(nozzle_volume_type, extruder_type, filament_id + 1, variant_list, self_index_list);
5137
5138 FilamentIndexKey key{filament_id, extruder_type, nozzle_volume_type};
5139 auto iter = index_map->find(key);
5140 if (iter == index_map->end()) {
5141 int index = get_config_index_base(nozzle_volume_type, extruder_type, filament_id + 1, variant_list, self_index_list);
5142 (*index_map)[key] = index;
5143 return index;
5144 } else {
5145 return iter->second;
5146 }
5147}
std::shared_ptr< MultiNozzleUtils::LayeredNozzleGroupResult > m_layered_nozzle_group_result
Definition Print.hpp:1537
std::set< int > m_missing_nozzle_group_logged
Definition Print.hpp:1551
int get_config_index_base(NozzleVolumeType volume_type, ExtruderType extruder_type, int variant_id_1based, const std::vector< std::string > &variant_list, const std::vector< int > &variant_ids_1based)
Definition PrintConfig.cpp:833
Definition Print.hpp:1404

References get_config_index(), Slic3r::get_config_index_base(), Slic3r::MultiNozzleUtils::LayeredNozzleGroupResult::get_nozzle_for_filament(), m_config, m_layered_nozzle_group_result, m_missing_nozzle_group_logged, and Slic3r::nozzle_volume_type().

Referenced by get_config_index(), get_config_index(), get_filament_config_indx(), and get_nozzle_config_index().

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

◆ get_config_index() [2/2]

int Slic3r::Print::get_config_index ( int filament_id,
int layer_id,
const std::vector< std::string > & variant_list,
const std::vector< int > & self_index_list,
PrintIndexMap & index_map )
protected
5150{
5151 const MultiNozzleUtils::LayeredNozzleGroupResult *group_result = m_layered_nozzle_group_result.get();
5152 // Orca: same static fallback as the filament overload; the slot degenerates to the filament's
5153 // extruder column (filament_map is 1 based, get_extruder_id guards the filament id range).
5154 if (!group_result)
5155 return (int)get_extruder_id(filament_id);
5156 auto nozzle_info = group_result->get_nozzle_for_filament(filament_id, layer_id);
5157 if (!nozzle_info.has_value()) {
5158 // Orca: this fallback runs per-filament/per-layer in the g-code hot path — log once per filament
5159 // (reset each slice) instead of flooding thousands of identical lines that bury the real error.
5160 if (m_missing_nozzle_group_logged.insert(filament_id).second)
5161 BOOST_LOG_TRIVIAL(error) << __FUNCTION__
5162 << boost::format(", Line %1%: could not found group_nozzle_info corresponding to filament_id %2%, layer_id %3% (further occurrences for this filament suppressed)") % __LINE__ % filament_id %
5163 layer_id;
5164 return 0;
5165 }
5166
5167 int extruder_id = nozzle_info->extruder_id + 1; // to 1 based
5168 ExtruderType extruder_type = ExtruderType(m_config.extruder_type.get_at(nozzle_info->extruder_id));
5169 NozzleVolumeType nozzle_volume_type = nozzle_info->volume_type;
5170
5171 PrintIndexKey key{filament_id, extruder_id, extruder_type, nozzle_volume_type};
5172 auto iter = index_map.find(key);
5173 if (iter == index_map.end()) {
5174 int index = get_config_index_base(nozzle_volume_type, extruder_type, extruder_id, variant_list, self_index_list);
5175 index_map[key] = index;
5176 return index;
5177 } else {
5178 return index_map[key];
5179 }
5180}
size_t get_extruder_id(unsigned int filament_id) const
Definition Print.cpp:5008
Definition Print.hpp:1416

References get_config_index(), Slic3r::get_config_index_base(), get_extruder_id(), Slic3r::MultiNozzleUtils::LayeredNozzleGroupResult::get_nozzle_for_filament(), m_config, m_layered_nozzle_group_result, m_missing_nozzle_group_logged, and Slic3r::nozzle_volume_type().

Here is the call graph for this function:

◆ get_conflict_result()

ConflictResultOpt Slic3r::Print::get_conflict_result ( ) const
inline
1335{ return m_conflict_result; }

References m_conflict_result.

◆ get_conflict_string()

std::string Slic3r::Print::get_conflict_string ( ) const
inline
1322 {
1323 std::string result;
1324 if (m_conflict_result) {
1325 result = "Found gcode path conflicts between object " + m_conflict_result.value()._objName1 + " and " + m_conflict_result.value()._objName2;
1326 }
1327
1328 return result;
1329 }

References m_conflict_result.

Referenced by Slic3r::CLI::run().

Here is the caller graph for this function:

◆ get_extruder_filament_info()

const std::vector< std::vector< DynamicPrintConfig > > & Slic3r::Print::get_extruder_filament_info ( ) const
inline
1237{ return m_extruder_filament_info; }
std::vector< std::vector< DynamicPrintConfig > > m_extruder_filament_info
Definition Print.hpp:1531

References m_extruder_filament_info.

Referenced by Slic3r::build_filament_group_context().

Here is the caller graph for this function:

◆ get_extruder_id()

size_t Slic3r::Print::get_extruder_id ( unsigned int filament_id) const
5009{
5010 std::vector<int> filament_map = get_filament_maps();
5011 if (filament_id < filament_map.size()) {
5012 return filament_map[filament_id] - 1;
5013 }
5014 return 0;
5015}
zaa_minimize_perimeter_height filament_map((ConfigOptionInts, filament_volume_map))((ConfigOptionInts

References Slic3r::filament_map(), get_extruder_id(), and get_filament_maps().

Referenced by get_config_index(), get_extruder_id(), Slic3r::Layer::is_perimeter_compatible(), and Slic3r::segmentation_top_and_bottom_layers().

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

◆ get_extruder_printable_height()

std::vector< double > Slic3r::Print::get_extruder_printable_height ( ) const
4980{
4981 return m_config.extruder_printable_height.values;
4982}

References get_extruder_printable_height(), and m_config.

Referenced by get_extruder_printable_height().

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

◆ get_extruder_printable_polygons()

std::vector< Polygons > Slic3r::Print::get_extruder_printable_polygons ( ) const
4985{
4986 std::vector<Polygons> extruder_printable_polys;
4987 std::vector<std::vector<Vec2d>> extruder_printable_areas = m_config.extruder_printable_area.values;
4988 for (const auto &e_printable_area : extruder_printable_areas) {
4989 Polygons ploys = {Polygon::new_scale(e_printable_area)};
4990 extruder_printable_polys.emplace_back(ploys);
4991 }
4992 return extruder_printable_polys;
4993}
static Polygon new_scale(const std::vector< Vec2d > &points)
Definition Polygon.hpp:38

References get_extruder_printable_polygons(), m_config, and Slic3r::Polygon::new_scale().

Referenced by get_extruder_printable_polygons(), and Slic3r::outer_inner_brim_area().

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

◆ get_extruder_shared_printable_polygon()

Polygons Slic3r::Print::get_extruder_shared_printable_polygon ( ) const
5022{
5023 const std::vector<Vec2ds>& extruder_printable_areas = m_config.extruder_printable_area.values;
5024 if (m_config.nozzle_diameter.size() < 2 || extruder_printable_areas.empty())
5025 return {Polygon::new_scale(m_config.printable_area.values)};
5026 for (const Vec2ds& area : extruder_printable_areas)
5027 if (area.size() < 3)
5028 return {Polygon::new_scale(m_config.printable_area.values)};
5029
5030 Polygons shared_printable_polys = {Polygon::new_scale(extruder_printable_areas.front())};
5031 for (size_t i = 1; i < extruder_printable_areas.size(); ++i)
5032 shared_printable_polys = intersection(shared_printable_polys, Polygons{Polygon::new_scale(extruder_printable_areas[i])});
5033 return shared_printable_polys;
5034}
std::vector< Vec2d > Vec2ds
Definition Point.hpp:78

References Slic3r::area(), get_extruder_shared_printable_polygon(), Slic3r::intersection(), m_config, and Slic3r::Polygon::new_scale().

Referenced by Slic3r::GCode::_do_export(), _make_wipe_tower(), Slic3r::WipeTowerIntegration::append_tcr(), get_extruder_shared_printable_polygon(), Slic3r::layered_print_cleareance_valid(), and Slic3r::WipeTowerIntegration::shared_printable_area().

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

◆ get_extruder_unprintable_polygons()

std::vector< Polygons > Slic3r::Print::get_extruder_unprintable_polygons ( ) const
4996{
4997 std::vector<Vec2d> printable_area = m_config.printable_area.values;
4998 Polygon printable_poly = Polygon::new_scale(printable_area);
4999 std::vector<std::vector<Vec2d>> extruder_printable_areas = m_config.extruder_printable_area.values;
5000 std::vector<Polygons> extruder_unprintable_polys;
5001 for (const auto &e_printable_area : extruder_printable_areas) {
5002 Polygons ploys = diff(printable_poly, Polygon::new_scale(e_printable_area));
5003 extruder_unprintable_polys.emplace_back(ploys);
5004 }
5005 return extruder_unprintable_polys;
5006}
zaa_minimize_perimeter_height printable_area((ConfigOptionPointsGroups, extruder_printable_area))((ConfigOptionBool

References Slic3r::diff(), get_extruder_unprintable_polygons(), m_config, Slic3r::Polygon::new_scale(), and Slic3r::printable_area().

Referenced by get_extruder_unprintable_polygons().

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

◆ get_fake_wipe_tower()

const FakeWipeTower & Slic3r::Print::get_fake_wipe_tower ( ) const
inline
1354{ return m_fake_wipe_tower; }

References m_fake_wipe_tower.

Referenced by Slic3r::GCode::_do_export(), and Slic3r::outer_inner_brim_area().

Here is the caller graph for this function:

◆ get_filament_config_indx()

int Slic3r::Print::get_filament_config_indx ( int filament_id,
int layer_id,
bool use_cache = true )
5069{
5070 return get_config_index(filament_id, layer_id, m_config.filament_extruder_variant.values, m_filament_self_index, use_cache ? &m_filament_index_map : nullptr);
5071}
std::vector< int > m_filament_self_index
Definition Print.hpp:1553
int get_config_index(int filament_id, int layer_id, const std::vector< std::string > &variant_list, const std::vector< int > &self_index_list, FilamentIndexMap *index_map)
Definition Print.cpp:5114
FilamentIndexMap m_filament_index_map
Definition Print.hpp:1544

References get_config_index(), get_filament_config_indx(), m_config, m_filament_index_map, and m_filament_self_index.

Referenced by get_filament_config_indx().

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

◆ get_filament_map_mode()

FilamentMapMode Slic3r::Print::get_filament_map_mode ( ) const
4904{
4905 return m_config.filament_map_mode;
4906}

References get_filament_map_mode(), and m_config.

Referenced by Slic3r::bed_exclusion_physical_extruders(), Slic3r::colliding_bed_exclusion(), get_filament_map_mode(), Slic3r::layered_print_cleareance_valid(), and sequential_print_clearance_valid().

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

◆ get_filament_maps()

std::vector< int > Slic3r::Print::get_filament_maps ( ) const
4889{
4890 return m_config.filament_map.values;
4891}

References get_filament_maps(), and m_config.

Referenced by _make_wipe_tower(), Slic3r::bed_exclusion_physical_extruders(), Slic3r::colliding_bed_exclusion(), get_extruder_id(), get_filament_maps(), Slic3r::GUI::GCodeViewer::load_as_gcode(), and Slic3r::CLI::run().

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

◆ get_filament_nozzle_maps()

std::vector< int > Slic3r::Print::get_filament_nozzle_maps ( ) const
4894{
4895 return m_config.filament_nozzle_map.values;
4896}

References get_filament_nozzle_maps(), and m_config.

Referenced by get_filament_nozzle_maps(), and Slic3r::CLI::run().

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

◆ get_filament_temp_type()

FilamentTempType Slic3r::Print::get_filament_temp_type ( const std::string & filament_type)
static
4555{
4556 // Range-based classification only: do not use filament_info.json.
4557 int min_temp, max_temp;
4558 if (MaterialType::get_temperature_range(filament_type, min_temp, max_temp)) {
4559 if (max_temp <= 250)
4561 else if (max_temp < 280)
4563 else
4565 }
4566
4567 return Undefine;
4568}
zaa_minimize_perimeter_height filament_type((ConfigOptionBools, filament_soluble))((ConfigOptionStrings
@ Undefine
Definition Print.hpp:1041

References Slic3r::filament_type(), get_filament_temp_type(), Slic3r::MaterialType::get_temperature_range(), Slic3r::HighLowCompatible, Slic3r::HighTemp, Slic3r::LowTemp, and Slic3r::Undefine.

Referenced by get_filament_temp_type(), and Slic3r::get_instance_arrange_poly().

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

◆ get_filament_unprintable_flow()

std::map< int, std::set< NozzleVolumeType > > Slic3r::Print::get_filament_unprintable_flow ( const std::vector< unsigned int > & used_filaments) const

Determines the forbidden nozzle volume types for each used filament.

A filament may declare the extruder variants it supports. Every volume type offered by the printer's extruders that the filament does not support is forbidden for that filament. Hybrid volumes are ignored on both sides, and filaments declaring no variants are unrestricted.

Parameters
used_filamentsTotally used filaments when slicing
Returns
A map from used filament index to the set of nozzle volume types it cannot print on
4944{
4945 std::map<int, std::set<NozzleVolumeType>> ret;
4946 std::vector<std::string> extruder_variant_list = m_config.printer_extruder_variant.values;
4947 // A filament that declares no extruder variants carries no flow restriction.
4948 const ConfigOptionStrings *filament_variant_opt = m_ori_full_print_config.option<ConfigOptionStrings>("filament_extruder_variant");
4949 if (filament_variant_opt == nullptr)
4950 return ret;
4951 std::vector<std::string> filament_variant_list = filament_variant_opt->values;
4952 std::vector<int> filament_self_index;
4953 if (!m_ori_full_print_config.has("filament_self_index"))
4954 filament_self_index.resize(filament_variant_list.size(), 1);
4955 else
4956 filament_self_index = m_ori_full_print_config.option<ConfigOptionInts>("filament_self_index")->values;
4957 std::unordered_set<int> used_fils_set(used_filaments.begin(), used_filaments.end());
4958
4959 std::unordered_map<int, std::set<NozzleVolumeType>> filament_variant_map;
4960 for(int i = 0; i < filament_variant_list.size(); ++i){
4961 NozzleVolumeType volume = convert_to_nvt_type(filament_variant_list[i]);
4962 if(volume != nvtHybrid) filament_variant_map[filament_self_index[i]].insert(volume);
4963 }
4964
4965 for (auto iter : filament_variant_map) {
4966 int fil_idx = iter.first - 1;
4967 if (used_fils_set.find(fil_idx) == used_fils_set.end()) continue;
4968 const std::set<NozzleVolumeType> &volumes = iter.second;
4969 for (int exd_idx = 0; exd_idx < extruder_variant_list.size(); ++exd_idx) {
4970 auto exd_volume = convert_to_nvt_type(extruder_variant_list[exd_idx]);
4971 assert(exd_volume != nvtHybrid);
4972 if (volumes.find(exd_volume) == volumes.end() && exd_volume != nvtHybrid) ret[fil_idx].insert(exd_volume);
4973 }
4974 }
4975 return ret;
4976}
@ nvtHybrid
Definition PrintConfig.hpp:621
NozzleVolumeType convert_to_nvt_type(const std::string &variant_str)
Definition PrintConfig.cpp:912

References Slic3r::convert_to_nvt_type(), get_filament_unprintable_flow(), m_config, Slic3r::PrintBaseWithState< PrintStep, psCount >::m_ori_full_print_config, Slic3r::nvtHybrid, and Slic3r::ConfigOptionVector< T >::values.

Referenced by get_filament_unprintable_flow().

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

◆ get_filament_volume_maps()

std::vector< int > Slic3r::Print::get_filament_volume_maps ( ) const
4899{
4900 return m_config.filament_volume_map.values;
4901}

References get_filament_volume_maps(), and m_config.

Referenced by get_filament_volume_maps(), and Slic3r::CLI::run().

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

◆ get_geometric_unprintable_filaments()

std::vector< std::set< int > > Slic3r::Print::get_geometric_unprintable_filaments ( ) const
inline
std::vector< std::set< int > > m_geometric_unprintable_filaments
Definition Print.hpp:1581

References m_geometric_unprintable_filaments.

◆ get_hrc_by_nozzle_type()

int Slic3r::Print::get_hrc_by_nozzle_type ( const NozzleType & type)
static
4571{
4572 static std::map<std::string, int>nozzle_type_to_hrc;
4573 if (nozzle_type_to_hrc.empty()) {
4574 fs::path file_path = fs::path(resources_dir()) / "info" / "nozzle_info.json";
4575 boost::nowide::ifstream in(file_path.string());
4576 //std::ifstream in(file_path.string());
4577 json j;
4578 try {
4579 j = json::parse(in);
4580 in.close();
4581 for (const auto& elem : j["nozzle_hrc"].items())
4582 nozzle_type_to_hrc[elem.key()] = elem.value();
4583 }
4584 catch (const json::parse_error& err) {
4585 in.close();
4586 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": parse " << file_path.string() << " got a nlohmann::detail::parse_error, reason = " << err.what();
4587 nozzle_type_to_hrc = {
4588 {"hardened_steel",55},
4589 {"stainless_steel",20},
4590 {"tungsten_carbide", 85},
4591 {"brass",2},
4592 {"undefine",0}
4593 };
4594 }
4595 }
4596 auto iter = nozzle_type_to_hrc.find(NozzleTypeEumnToStr[type]);
4597 if (iter != nozzle_type_to_hrc.end())
4598 return iter->second;
4599 //0 represents undefine
4600 return 0;
4601}
static std::unordered_map< NozzleType, std::string > NozzleTypeEumnToStr
Definition PrintConfig.hpp:538
const std::string & resources_dir()
Definition utils.cpp:296

References get_hrc_by_nozzle_type(), Slic3r::NozzleTypeEumnToStr, and Slic3r::resources_dir().

Referenced by get_hrc_by_nozzle_type(), Slic3r::GUI::SelectMachineDialog::is_nozzle_hrc_matched(), Slic3r::GUI::is_same_nozzle_type(), Slic3r::GUI::SyncAmsInfoDialog::is_same_nozzle_type(), Slic3r::GUI::SelectMachineDialog::update_show_status(), and Slic3r::GCodeProcessor::update_slice_warnings().

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

◆ get_imex_slice_offset()

Vec2d Slic3r::Print::get_imex_slice_offset ( ) const
inline
1307{ return m_imex_slice_offset; }
Vec2d m_imex_slice_offset
Definition Print.hpp:1573

References m_imex_slice_offset.

Referenced by export_gcode().

Here is the caller graph for this function:

◆ get_incompatible_filaments_by_nozzle()

std::vector< std::string > Slic3r::Print::get_incompatible_filaments_by_nozzle ( const float nozzle_diameter,
const std::optional< NozzleVolumeType > nozzle_volume_type = std::nullopt )
static
4604{
4605 static std::map<std::string, std::map<std::string, std::vector<std::string>>> incompatible_filaments;
4606 if(incompatible_filaments.empty()){
4607 fs::path file_path = fs::path(resources_dir()) / "info" / "nozzle_incompatibles.json";
4608 boost::nowide::ifstream in(file_path.string());
4609 json j;
4610 try {
4611 j = json::parse(in);
4612 for(auto& [volume_type, diameter_list] : j["incompatible_nozzles"].items()) {
4613 for(auto& [diameter, filaments]: diameter_list.items()){
4614 incompatible_filaments[volume_type][diameter] = filaments.get<std::vector<std::string>>();
4615 }
4616 }
4617 }
4618 catch(const json::parse_error& err){
4619 in.close();
4620 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": parse " << file_path.string() << " got a nlohmann::detail::parse_error, reason = " << err.what();
4621
4624 }
4625 }
4626 std::ostringstream oss;
4627 oss << std::fixed << std::setprecision(1) << nozzle_diameter;
4628 std::string diameter_str = oss.str();
4629
4630 if(nozzle_volume_type.has_value()){
4631 return incompatible_filaments[get_nozzle_volume_type_string(nozzle_volume_type.value())][diameter_str];
4632 }
4633
4634 std::vector<std::string> incompatible_filaments_list;
4635 for(auto& [volume_type, diameter_list] : incompatible_filaments){
4636 auto iter = diameter_list.find(diameter_str);
4637 if(iter != diameter_list.end()){
4638 append(incompatible_filaments_list, iter->second);
4639 }
4640 }
4641 return incompatible_filaments_list;
4642}
zaa_minimize_perimeter_height nozzle_diameter((ConfigOptionBool, reduce_infill_retraction))((ConfigOptionBool
@ nvtHighFlow
Definition PrintConfig.hpp:620
@ nvtStandard
Definition PrintConfig.hpp:619
std::string get_nozzle_volume_type_string(NozzleVolumeType nozzle_volume_type)
Definition PrintConfig.cpp:860

References Slic3r::append(), get_incompatible_filaments_by_nozzle(), Slic3r::get_nozzle_volume_type_string(), Slic3r::nozzle_diameter(), Slic3r::nozzle_volume_type(), Slic3r::nvtHighFlow, Slic3r::nvtStandard, and Slic3r::resources_dir().

Referenced by Slic3r::GUI::PartPlate::check_compatible_of_nozzle_and_filament(), and get_incompatible_filaments_by_nozzle().

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

◆ get_layered_nozzle_group_result()

std::shared_ptr< MultiNozzleUtils::LayeredNozzleGroupResult > Slic3r::Print::get_layered_nozzle_group_result ( ) const
inline

References m_layered_nozzle_group_result.

Referenced by Slic3r::GCode::_do_export(), _make_wipe_tower(), Slic3r::WipeTowerIntegration::append_tcr(), Slic3r::bed_exclusion_physical_extruders(), and export_gcode().

Here is the caller graph for this function:

◆ get_model_name()

std::string Slic3r::Print::get_model_name ( ) const
5678{
5679 if (model().model_info != nullptr)
5680 {
5681 return model().model_info->model_name;
5682 } else {
5683 return "";
5684 }
5685}
std::shared_ptr< ModelInfo > model_info
Definition Model.hpp:1713

References get_model_name(), and Slic3r::PrintBaseWithState< PrintStep, psCount >::model().

Referenced by Slic3r::GCode::_do_export(), Slic3r::GCode::do_export(), export_gcode_from_previous_file(), get_model_name(), output_filename(), and process().

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

◆ get_modified_count()

int Slic3r::Print::get_modified_count ( ) const
inline
1318{return m_modified_count;}

References m_modified_count.

Referenced by Slic3r::GUI::GCodeViewer::load_shells().

Here is the caller graph for this function:

◆ get_no_check_flag()

bool Slic3r::PrintBase::get_no_check_flag ( ) const
inlineinherited
528{ return m_no_check; }
bool m_no_check
Definition PrintBase.hpp:568

References m_no_check.

◆ get_nozzle_config_index()

int Slic3r::Print::get_nozzle_config_index ( int filament_id,
int layer_id )
5108{
5109 // Orca: print_extruder_id/print_extruder_variant are PrintRegionConfig members in this codebase;
5110 // the process-wide expanded values live in the default region config (regions never override them).
5111 return get_config_index(filament_id, layer_id, m_default_region_config.print_extruder_variant.values, m_default_region_config.print_extruder_id.values, m_nozzle_index_map);
5112}
PrintIndexMap m_nozzle_index_map
Definition Print.hpp:1546

References get_config_index(), get_nozzle_config_index(), m_default_region_config, and m_nozzle_index_map.

Referenced by get_nozzle_config_index().

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

◆ get_nozzle_group_result()

std::shared_ptr< MultiNozzleUtils::NozzleGroupResultBase > Slic3r::Print::get_nozzle_group_result ( ) const
inline
1220{ return m_nozzle_group_result; }
std::shared_ptr< MultiNozzleUtils::NozzleGroupResultBase > m_nozzle_group_result
Definition Print.hpp:1534

References m_nozzle_group_result.

Referenced by Slic3r::bed_exclusion_physical_extruders().

Here is the caller graph for this function:

◆ get_object() [1/3]

const PrintObject * Slic3r::Print::get_object ( ObjectID object_id) const
inline
1139 {
1140 auto it = std::find_if(m_objects.begin(), m_objects.end(),
1141 [object_id](const PrintObject *obj) { return obj->id() == object_id; });
1142 return (it == m_objects.end()) ? nullptr : *it;
1143 }

References m_objects, and PrintObject.

Here is the call graph for this function:

◆ get_object() [2/3]

PrintObject * Slic3r::Print::get_object ( size_t idx)
inline
1135{ return const_cast<PrintObject*>(m_objects[idx]); }

References m_objects, and PrintObject.

Referenced by Slic3r::GCode::_do_export(), check_multi_filament_valid(), Slic3r::TreeSupport3D::generate_support_areas(), Slic3r::TreeSupport3D::group_meshes(), Slic3r::make_brim(), Slic3r::GUI::Plater::priv::on_process_completed(), Slic3r::GUI::Plater::priv::on_slicing_update(), Slic3r::outer_inner_brim_area(), and Slic3r::TreeSupport3D::precalculate().

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

◆ get_object() [3/3]

const PrintObject * Slic3r::Print::get_object ( size_t idx) const
inline
1136{ return m_objects[idx]; }

References m_objects, and PrintObject.

Here is the call graph for this function:

◆ get_physical_unprintable_filaments()

std::vector< std::set< int > > Slic3r::Print::get_physical_unprintable_filaments ( const std::vector< unsigned int > & used_filaments) const

Determines the unprintable filaments for each extruder based on its physical attributes.

Currently, the criteria for determining unprintable filament include the following:

  1. TPU filaments can only be placed in the master extruder and must be grouped alone.
  2. We only support at most 1 tpu filament.
  3. An extruder can only accommodate filament with a hardness requirement lower than that of its nozzle.
Parameters
used_filamentsTotally used filaments when slicing
Returns
A vector of sets representing unprintable filaments for each extruder.Return an empty vecto if extruder num is 1
4909{
4910 int extruder_num = m_config.nozzle_diameter.size();
4911 std::vector<std::set<int>>physical_unprintables(extruder_num);
4912 if (extruder_num < 2)
4913 return physical_unprintables;
4914
4915 auto get_unprintable_extruder_id = [&](unsigned int filament_idx) -> int {
4916 // filament_printable may be shorter than the filament count; get_at() clamps.
4917 int status = m_config.filament_printable.get_at(filament_idx);
4918 for (int i = 0; i < extruder_num; ++i) {
4919 if (!(status >> i & 1)) {
4920 return i;
4921 }
4922 }
4923 return -1;
4924 };
4925
4926
4927 std::set<int> tpu_filaments;
4928 for (auto f : used_filaments) {
4929 if (m_config.filament_type.get_at(f) == "TPU")
4930 tpu_filaments.insert(f);
4931 }
4932
4933 for (auto f : used_filaments) {
4934 int extruder_id = get_unprintable_extruder_id(f);
4935 if (extruder_id == -1)
4936 continue;
4937 physical_unprintables[extruder_id].insert(f);
4938 }
4939
4940 return physical_unprintables;
4941}
static double f(double x, double z_sin, double z_cos, bool vertical, bool flip)
Definition FillGyroid.cpp:124

References Slic3r::f(), get_physical_unprintable_filaments(), and m_config.

Referenced by get_physical_unprintable_filaments().

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

◆ get_plate_index()

int Slic3r::PrintBase::get_plate_index ( ) const
inlineinherited
526{ return m_plate_index; }

References m_plate_index.

Referenced by Slic3r::GCode::_do_export(), Slic3r::GCode::append_full_config(), Slic3r::estimated_wipe_tower_footprint(), Slic3r::get_wipe_tower_extrusions_extents(), Slic3r::layered_print_cleareance_valid(), and Slic3r::GCode::process_layer().

Here is the caller graph for this function:

◆ get_plate_name()

std::string Slic3r::PrintBase::get_plate_name ( ) const
inlineinherited
532{ return m_plate_name; }
std::string m_plate_name
Definition PrintBase.hpp:571

References m_plate_name.

Referenced by Slic3r::GCode::_do_export().

Here is the caller graph for this function:

◆ get_plate_number_formatted()

std::string Slic3r::Print::get_plate_number_formatted ( ) const
5688{
5689 std::string plate_number = std::to_string(get_plate_index() + 1);
5690 static const size_t n_zero = 2;
5691
5692 return std::string(n_zero - std::min(n_zero, plate_number.length()), '0') + plate_number;
5693}
int get_plate_index() const
Definition PrintBase.hpp:526

References Slic3r::PrintBaseWithState< PrintStep, psCount >::get_plate_index(), and get_plate_number_formatted().

Referenced by Slic3r::GCode::_do_export(), get_plate_number_formatted(), and output_filename().

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

◆ get_plate_origin()

◆ get_print_region()

const PrintRegion & Slic3r::Print::get_print_region ( size_t idx) const
inline
1290{ return *m_print_regions[idx]; }

References m_print_regions.

Referenced by Slic3r::GCode::_do_export(), Slic3r::GCode::extrude_infill(), Slic3r::GCode::extrude_perimeters(), and Slic3r::GCode::process_layer().

Here is the caller graph for this function:

◆ get_rib_offset()

Vec2f Slic3r::Print::get_rib_offset ( ) const
inline
1353{ return m_wipe_tower_data.rib_offset; }

References m_wipe_tower_data.

Referenced by Slic3r::GCode::_do_export().

Here is the caller graph for this function:

◆ get_slice_used_filaments()

std::vector< unsigned int > Slic3r::Print::get_slice_used_filaments ( bool first_layer) const
inline
std::vector< unsigned int > m_slice_used_filaments_first_layer
Definition Print.hpp:1565
std::vector< unsigned int > m_slice_used_filaments
Definition Print.hpp:1564

References m_slice_used_filaments, and m_slice_used_filaments_first_layer.

Referenced by Slic3r::GCode::_do_export(), and Slic3r::GCode::get_highest_bed_temperature().

Here is the caller graph for this function:

◆ get_slice_used_mixed_filaments()

const std::vector< unsigned int > & Slic3r::Print::get_slice_used_mixed_filaments ( ) const
inline
std::vector< unsigned int > m_slice_used_mixed_filaments
Definition Print.hpp:1567

References m_slice_used_mixed_filaments.

◆ get_tool_ordering()

const ToolOrdering & Slic3r::Print::get_tool_ordering ( ) const
inline
1291{ return m_wipe_tower_data.tool_ordering; }

References m_wipe_tower_data.

Referenced by Slic3r::CLI::run().

Here is the caller graph for this function:

◆ get_wipe_tower_bbx()

BoundingBoxf Slic3r::Print::get_wipe_tower_bbx ( ) const
inline
1352{ return m_wipe_tower_data.bbx; }

References m_wipe_tower_data.

Referenced by Slic3r::GCode::_do_export().

Here is the caller graph for this function:

◆ get_wipe_tower_depth()

float Slic3r::Print::get_wipe_tower_depth ( ) const
inline
1351{ return m_wipe_tower_data.depth; }

References m_wipe_tower_data.

Referenced by Slic3r::GCode::_do_export().

Here is the caller graph for this function:

◆ has_auto_brim()

bool Slic3r::Print::has_auto_brim ( ) const
inline
1111 {
1112 return std::any_of(m_objects.begin(), m_objects.end(), [](PrintObject* object) { return object->config().brim_type == btAutoBrim; });
1113 }

References m_objects, and PrintObject.

Here is the call graph for this function:

◆ has_belt_brim()

bool Slic3r::Print::has_belt_brim ( ) const
868{
869 return std::any_of(m_objects.begin(), m_objects.end(), [](PrintObject *object) { return object->has_belt_brim(); });
870}

References m_objects, and PrintObject.

Referenced by validate().

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

◆ has_belt_purge_tower()

bool Slic3r::Print::has_belt_purge_tower ( ) const
49{
50 // Its own purge-tower "type", gated by the belt-only enable_belt_purge_tower
51 // option (not the classic enable_prime_tower).
52 if (!(m_config.belt_printer.value
53 && m_config.enable_belt_purge_tower.value
54 && !m_config.spiral_mode.value
55 && m_config.filament_diameter.values.size() > 1))
56 return false;
57
58 return std::any_of(m_objects.begin(), m_objects.end(), [](const PrintObject *object) {
59 return object->config().belt_purge_tower_object.value;
60 });
61}

References m_config, m_objects, and PrintObject.

Referenced by validate().

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

◆ has_brim()

bool Slic3r::Print::has_brim ( ) const
850{
851 return std::any_of(m_objects.begin(), m_objects.end(), [](PrintObject *object) { return object->has_brim(); });
852}

References m_objects, and PrintObject.

Referenced by Slic3r::PrintRegion::collect_object_printing_extruders(), libvgcode::convert(), libvgcode::convert_brim_skirt_to_vertices(), Slic3r::TreeSupport::draw_circles(), and Slic3r::GCode::process_layer().

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

◆ has_infinite_skirt()

bool Slic3r::Print::has_infinite_skirt ( ) const
831{
832 // Belt printer: no skirt support.
833 if (m_config.belt_printer.value)
834 return false;
835 // Orca: unclear why (m_config.ooze_prevention && this->extruders().size() > 1) logic is here, removed.
836 // return (m_config.draft_shield == dsEnabled && m_config.skirt_loops > 0) || (m_config.ooze_prevention && this->extruders().size() > 1);
837
838 return (m_config.draft_shield == dsEnabled && m_config.skirt_loops > 0);
839}
@ dsEnabled
Definition PrintConfig.hpp:478

References Slic3r::dsEnabled, and m_config.

Referenced by _make_skirt(), Slic3r::combine_object_brims(), libvgcode::convert_brim_skirt_to_vertices(), Slic3r::TreeSupport::draw_circles(), Slic3r::Skirt::make_skirt_loops_per_extruder_1st_layer(), Slic3r::Skirt::make_skirt_loops_per_extruder_other_layers(), and Slic3r::GCode::process_layer().

Here is the caller graph for this function:

◆ has_same_shrinkage_compensations()

bool Slic3r::Print::has_same_shrinkage_compensations ( ) const
inline

References m_same_shrinkage_compensations.

Referenced by validate().

Here is the caller graph for this function:

◆ has_shared_per_object_skirt()

bool Slic3r::Print::has_shared_per_object_skirt ( ) const
inline

◆ has_skirt()

bool Slic3r::Print::has_skirt ( ) const
842{
843 // Belt printer: no skirt support.
844 if (m_config.belt_printer.value)
845 return false;
846 return (m_config.skirt_height > 0);
847}

References m_config.

Referenced by _make_skirt(), Slic3r::combine_object_brims(), libvgcode::convert(), Slic3r::Skirt::make_skirt_loops_per_extruder_1st_layer(), and Slic3r::Skirt::make_skirt_loops_per_extruder_other_layers().

Here is the caller graph for this function:

◆ has_support_material()

bool Slic3r::Print::has_support_material ( ) const
3107{
3108 for (const PrintObject *object : m_objects)
3109 if (object->has_support_material())
3110 return true;
3111 return false;
3112}

References m_objects, and PrintObject.

Referenced by Slic3r::GCode::_do_export().

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

◆ has_tilted_belt()

bool Slic3r::Print::has_tilted_belt ( ) const
855{
856 if (! m_config.belt_printer.value)
857 return false;
858 // A Z rotation leaves the belt floor flat (BeltTransform forces shear = 0) and no
859 // rotation at all means the machine is geometrically a flat bed.
860 const BeltRotationAxis axis = m_config.belt_slice_rotation.value;
861 if (axis != BeltRotationAxis::X && axis != BeltRotationAxis::Y)
862 return false;
863 const double tilt = std::abs(m_config.belt_slice_rotation_angle.value);
864 return tilt >= BELT_BRIM_MIN_TILT_DEG && tilt <= BELT_BRIM_MAX_TILT_DEG;
865}
constexpr double BELT_BRIM_MIN_TILT_DEG
Definition BeltBrim.hpp:49
BeltRotationAxis
Definition PrintConfig.hpp:341
@ X
Definition PrintConfig.hpp:343
@ Y
Definition PrintConfig.hpp:344
constexpr double BELT_BRIM_MAX_TILT_DEG
Definition BeltBrim.hpp:50

References Slic3r::BELT_BRIM_MAX_TILT_DEG, Slic3r::BELT_BRIM_MIN_TILT_DEG, m_config, Slic3r::X, and Slic3r::Y.

Referenced by validate().

Here is the caller graph for this function:

◆ has_tpu_filament()

bool Slic3r::Print::has_tpu_filament ( ) const
private
251{
252 for (unsigned int filament_id : m_wipe_tower_data.tool_ordering.all_extruders()) {
253 std::string filament_name = m_config.filament_type.get_at(filament_id);
254 if (filament_name == "TPU") {
255 return true;
256 }
257 }
258 return false;
259}

References m_config, and m_wipe_tower_data.

Referenced by _make_wipe_tower().

Here is the caller graph for this function:

◆ has_wipe_tower()

bool Slic3r::Print::has_wipe_tower ( ) const
5184{
5185 // Belt printers never get the classic wipe tower: its G-code is generated
5186 // directly in machine XY coordinates and bypasses the belt rotation
5187 // transform. Purging is routed into the belt purge prism instead
5188 // (see has_belt_purge_tower() / _plan_belt_purge()).
5189 if (m_config.belt_printer.value)
5190 return false;
5191
5192 if (m_config.enable_prime_tower.value == true) {
5193 if (m_config.enable_wrapping_detection.value && m_config.wrapping_exclude_area.values.size() > 2)
5194 return true;
5195
5197 return true;
5198
5199 return !m_config.spiral_mode.value && m_config.filament_diameter.values.size() > 1;
5200 }
5201 return false;
5202}

References enable_timelapse_print(), has_wipe_tower(), and m_config.

Referenced by Slic3r::GCode::_do_export(), append_wipe_tower_extruder(), compacted_wipe_tower_clearance_valid(), first_layer_wipe_tower_corners(), has_wipe_tower(), Slic3r::layered_print_cleareance_valid(), Slic3r::outer_inner_brim_area(), Slic3r::CLI::run(), Slic3r::GUI::GLCanvas3D::update_compacted_wipe_tower_clearance(), and validate().

Here is the call graph for this function:
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.

◆ invalidate_all_steps()

bool Slic3r::PrintBaseWithState< PrintStep, COUNT >::invalidate_all_steps ( )
inlineprotectedinherited
623 { return m_state.invalidate_all(this->cancel_callback()); }
std::function< void()> cancel_callback()
Definition PrintBase.hpp:539

References Slic3r::PrintBase::cancel_callback(), and m_state.

Referenced by Slic3r::Print::clear(), and Slic3r::Print::invalidate_state_by_config_options().

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

◆ invalidate_state_by_config_options()

bool Slic3r::Print::invalidate_state_by_config_options ( const ConfigOptionResolver & new_config,
const std::vector< t_config_option_key > & opt_keys )
private
264{
265 if (opt_keys.empty())
266 return false;
267
268 // Cache the plenty of parameters, which influence the G-code generator only,
269 // or they are only notes not influencing the generated G-code.
270 static std::unordered_set<std::string> steps_gcode = {
271 // Belt printer G-code axis remap (only affects G-code output, not slicing).
272 "gcode_remap_x",
273 "gcode_remap_y",
274 "gcode_remap_z",
275 // Machine-frame transform (derived from belt tilt; only affects G-code output).
276 "belt_frame_tilt_decouple", "belt_frame_tilt_angle",
277 // Only inflates the GUI bed volume, like printable_area.
278 "belt_printer_infinite_y",
279 //BBS
280 "additional_cooling_fan_speed",
281 "reduce_crossing_wall",
282 "max_travel_detour_distance",
283 "printable_area",
284 "thumbnail_size",
285 "before_layer_change_gcode",
286 "enable_pressure_advance",
287 "pressure_advance",
288 "enable_overhang_bridge_fan",
289 "overhang_fan_speed",
290 "overhang_fan_threshold",
291 "slow_down_for_layer_cooling",
292 "default_acceleration",
293 "deretraction_speed",
294 "close_fan_the_first_x_layers",
295 "machine_end_gcode",
296 "printing_by_object_gcode",
297 "filament_end_gcode",
298 "post_process",
299 // "plugins" is the derived manifest backing the plugin-picker options; on its own it only
300 // affects G-code export. The specific option (e.g. slicing_pipeline_plugin) drives any re-slice.
301 "plugins",
302 "extruder_clearance_height_to_rod",
303 "extruder_clearance_height_to_lid",
304 "extruder_clearance_radius",
305 "nozzle_height",
306 "extruder_colour",
307 "filament_flow_ratio",
308 "reduce_fan_stop_start_freq",
309 "dont_slow_down_outer_wall",
310 "fan_cooling_layer_time",
311 "full_fan_speed_layer",
312 "initial_layer_fan_speed",
313 "fan_kickstart",
314 "part_cooling_fan_min_pwm",
315 "fan_speedup_overhangs",
316 "fan_speedup_time",
317 "filament_colour",
318 "default_filament_colour",
319 "filament_diameter",
320 "volumetric_speed_coefficients",
321 "filament_density",
322 "filament_cost",
323 "filament_notes",
324 "outer_wall_acceleration",
325 "inner_wall_acceleration",
326 "initial_layer_acceleration",
327 "top_surface_acceleration",
328 "bridge_acceleration",
329 "travel_acceleration",
330 "sparse_infill_acceleration",
331 "internal_solid_infill_acceleration",
332 // BBS
333 "supertack_plate_temp_initial_layer",
334 "cool_plate_temp_initial_layer",
335 "textured_cool_plate_temp_initial_layer",
336 "eng_plate_temp_initial_layer",
337 "hot_plate_temp_initial_layer",
338 "textured_plate_temp_initial_layer",
339 "gcode_add_line_number",
340 "layer_change_gcode",
341 "time_lapse_gcode",
342 "wrapping_detection_gcode",
343 "fan_min_speed",
344 "fan_max_speed",
345 "printable_height",
346 "slow_down_min_speed",
347 "max_volumetric_extrusion_rate_slope",
348 "max_volumetric_extrusion_rate_slope_segment_length",
349 "extrusion_rate_smoothing_external_perimeter_only",
350 "reduce_infill_retraction",
351 "filename_format",
352 "retraction_minimum_travel",
353 "retract_before_wipe",
354 // Orca:
355 "retract_after_wipe",
356 "retract_when_changing_layer",
357 "retraction_length",
358 "retract_length_toolchange",
359 "z_hop",
360 "travel_slope",
361 "retract_lift_above",
362 "retract_lift_below",
363 "retract_lift_enforce",
364 "retract_restart_extra",
365 "retract_restart_extra_toolchange",
366 "retraction_speed",
367 "use_firmware_retraction",
368 "slow_down_layer_time",
369 "standby_temperature_delta",
370 "preheat_time",
371 "preheat_steps",
372 "machine_start_gcode",
373 "filament_start_gcode",
374 "change_filament_gcode",
375 "wipe",
376 // BBS
377 "wipe_distance",
378 "curr_bed_type",
379 "nozzle_volume",
380 "nozzle_hrc",
381 "required_nozzle_HRC",
382 "upward_compatible_machine",
383 "is_infill_first",
384 // Orca
385 "chamber_temperature",
386 "chamber_minimal_temperature",
387 "thumbnails",
388 "thumbnails_format",
389 "center_of_surface_pattern",
390 "separated_infills",
391 "seam_gap",
392 "role_based_wipe_speed",
393 "wipe_speed",
394 "use_relative_e_distances",
395 "accel_to_decel_enable",
396 "accel_to_decel_factor",
397 "wipe_on_loops",
398 "wipe_inward",
399 "wipe_inward_distance",
400 "gcode_comments",
401 "high_precision_gcode",
402 "gcode_label_objects",
403 "exclude_object",
404 "exclude_object_tolerance",
405 "support_material_interface_fan_speed",
406 "internal_bridge_fan_speed", // ORCA: Add support for separate internal bridge fan speed control
407 "ironing_fan_speed",
408 "single_extruder_multi_material_priming",
409 "activate_air_filtration",
410 "activate_air_filtration_during_print",
411 "activate_air_filtration_on_completion",
412 "during_print_exhaust_fan_speed",
413 "complete_print_exhaust_fan_speed",
414 "activate_chamber_temp_control",
415 "manual_filament_change",
416 "disable_m73",
417 "use_firmware_retraction",
418 "enable_long_retraction_when_cut",
419 "long_retractions_when_cut",
420 "retraction_distances_when_cut",
421 "filament_long_retractions_when_cut",
422 "filament_retraction_distances_when_cut",
423 "grab_length",
424 "bed_temperature_formula",
425 "filament_notes",
426 "process_notes",
427 "printer_notes",
428 "use_3mf"
429 };
430
431 static std::unordered_set<std::string> steps_ignore;
432 static const std::unordered_set<std::string> steps_exclusion_geometry = {
433 "bed_exclude_area",
434 "bed_exclude_volumes",
435 "bed_exclude_volume_mode",
436 "extruder_bed_exclude_volumes",
437 "extruder_offset"
438 };
439
440 std::vector<PrintStep> steps;
441 std::vector<PrintObjectStep> osteps;
442 bool invalidated = false;
443
444 for (const t_config_option_key &opt_key : opt_keys) {
445 if (steps_exclusion_geometry.find(opt_key) != steps_exclusion_geometry.end()) {
446 // Exclusions are consumed while generating brims, support, tree
447 // support and wipe-tower geometry, not only during final export.
448 steps.emplace_back(psWipeTower);
449 steps.emplace_back(psSkirtBrim);
450 osteps.emplace_back(posSupportMaterial);
451 osteps.emplace_back(posSimplifySupportPath);
452 } else if (steps_gcode.find(opt_key) != steps_gcode.end()) {
453 // These options only affect G-code export or they are just notes without influence on the generated G-code,
454 // so there is nothing to invalidate.
455 steps.emplace_back(psGCodeExport);
456 } else if (steps_ignore.find(opt_key) != steps_ignore.end()) {
457 // These steps have no influence on the G-code whatsoever. Just ignore them.
458 } else if (
459 opt_key == "skirt_type"
460 || opt_key == "skirt_loops"
461 || opt_key == "skirt_speed"
462 || opt_key == "skirt_height"
463 || opt_key == "min_skirt_length"
464 || opt_key == "single_loop_draft_shield"
465 || opt_key == "draft_shield"
466 || opt_key == "skirt_distance"
467 || opt_key == "skirt_start_angle"
468 || opt_key == "ooze_prevention"
469 || opt_key == "wipe_tower_x"
470 || opt_key == "wipe_tower_y"
471 || opt_key == "wipe_tower_rotation_angle") {
472 // The tower gcode itself is position-independent (position and rotation are applied
473 // at export), except that the wait_for_temp_on_wipe_tower park bakes a bed-relative
474 // side choice into it (WipeTower2::toolchange_Change) — regenerate it when the tower
475 // moves. Gating on the old config is safe: both inputs of wait_for_temp_enabled
476 // invalidate psWipeTower themselves when they are part of the same diff.
477 if ((opt_key == "wipe_tower_x" || opt_key == "wipe_tower_y" || opt_key == "wipe_tower_rotation_angle")
479 steps.emplace_back(psWipeTower);
480 steps.emplace_back(psSkirtBrim);
481 } else if (
482 opt_key == "slicing_pipeline_plugin"
483 || opt_key == "print_plugin_config_overrides"
484 || opt_key == "initial_layer_print_height"
485 || opt_key == "nozzle_diameter"
486 || opt_key == "filament_shrink"
487 || opt_key == "filament_shrinkage_compensation_z"
488 || opt_key == "resolution"
489 || opt_key == "precise_z_height"
490 // Spiral Vase forces different kind of slicing than the normal model:
491 // In Spiral Vase mode, holes are closed and only the largest area contour is kept at each layer.
492 // Therefore toggling the Spiral Vase on / off requires complete reslicing.
493 || opt_key == "spiral_mode"
494 // Build plate tilt changes slicing plane orientation.
495 || opt_key == "build_plate_tilt_x"
496 || opt_key == "build_plate_tilt_y"
497 // Belt printer transform options change the mesh geometry before slicing.
498 || opt_key == "belt_printer"
499 || opt_key == "belt_slice_rotation"
500 || opt_key == "belt_slice_rotation_angle") {
501 osteps.emplace_back(posSlice);
502 } else if (
503 opt_key == "belt_support_floor_offset") {
504 osteps.emplace_back(posSupportMaterial);
505 } else if (
506 opt_key == "print_sequence"
507 || opt_key == "filament_type"
508 || opt_key == "chamber_temperature"
509 || opt_key == "nozzle_temperature_initial_layer"
510 || opt_key == "filament_minimal_purge_on_wipe_tower"
511 || opt_key == "filament_max_volumetric_speed"
512 || opt_key == "filament_adaptive_volumetric_speed"
513 || opt_key == "filament_loading_speed"
514 || opt_key == "filament_loading_speed_start"
515 || opt_key == "filament_unloading_speed"
516 || opt_key == "filament_unloading_speed_start"
517 || opt_key == "filament_toolchange_delay"
518 || opt_key == "filament_cooling_moves"
519 || opt_key == "filament_stamping_loading_speed"
520 || opt_key == "filament_stamping_distance"
521 || opt_key == "filament_cooling_initial_speed"
522 || opt_key == "filament_cooling_final_speed"
523 || opt_key == "filament_ramming_parameters"
524 || opt_key == "filament_multitool_ramming"
525 || opt_key == "filament_multitool_ramming_volume"
526 || opt_key == "filament_multitool_ramming_flow"
527 || opt_key == "filament_max_volumetric_speed"
528 || opt_key == "gcode_flavor"
529 || opt_key == "single_extruder_multi_material"
530 || opt_key == "nozzle_temperature"
531 // BBS
532 || opt_key == "supertack_plate_temp"
533 || opt_key == "cool_plate_temp"
534 || opt_key == "textured_cool_plate_temp"
535 || opt_key == "eng_plate_temp"
536 || opt_key == "hot_plate_temp"
537 || opt_key == "textured_plate_temp"
538 || opt_key == "enable_prime_tower"
539 || opt_key == "enable_belt_purge_tower"
540 || opt_key == "enable_wrapping_detection"
541 || opt_key == "prime_tower_enable_framework"
542 || opt_key == "prime_tower_width"
543 || opt_key == "prime_tower_brim_width"
544 || opt_key == "wipe_tower_type"
545 || opt_key == "prime_tower_skip_points"
546 || opt_key == "prime_tower_flat_ironing"
547 || opt_key == "enable_tower_interface_features"
548 || opt_key == "first_layer_print_sequence"
549 || opt_key == "other_layers_print_sequence"
550 || opt_key == "other_layers_print_sequence_nums"
551 || opt_key == "toolchange_ordering"
552 || opt_key == "toolchange_cyclic_order"
553 || opt_key == "toolchange_cyclic_first_layer"
554 || opt_key == "extruder_ams_count"
555 || opt_key == "extruder_nozzle_stats"
556 || opt_key == "filament_map_mode"
557 || opt_key == "filament_map"
558 || opt_key == "filament_nozzle_map"
559 || opt_key == "filament_volume_map"
560 || opt_key == "filament_adhesiveness_category"
561 || opt_key == "filament_tower_interface_pre_extrusion_dist"
562 || opt_key == "filament_tower_interface_pre_extrusion_length"
563 || opt_key == "filament_tower_ironing_area"
564 || opt_key == "filament_tower_interface_purge_volume"
565 || opt_key == "filament_tower_interface_print_temp"
566 || opt_key == "wipe_tower_bridging"
567 || opt_key == "wipe_tower_extra_flow"
568 || opt_key == "wipe_tower_no_sparse_layers"
569 || opt_key == "wipe_tower_sparse_layers_combination"
570 || opt_key == "flush_volumes_matrix"
571 || opt_key == "prime_volume"
572 || opt_key == "flush_into_infill"
573 || opt_key == "flush_into_support"
574 || opt_key == "belt_purge_tower_width"
575 || opt_key == "initial_layer_infill_speed"
576 || opt_key == "travel_speed"
577 || opt_key == "travel_speed_z"
578 || opt_key == "initial_layer_speed"
579 || opt_key == "initial_layer_travel_speed"
580 || opt_key == "initial_layer_travel_acceleration"
581 || opt_key == "initial_layer_travel_jerk"
582 || opt_key == "slow_down_layers"
583 || opt_key == "idle_temperature"
584 || opt_key == "wipe_tower_cone_angle"
585 || opt_key == "wipe_tower_extra_spacing"
586 || opt_key == "wipe_tower_max_purge_speed"
587 || opt_key == "wipe_tower_wall_type"
588 || opt_key == "wipe_tower_extra_rib_length"
589 || opt_key == "wipe_tower_rib_width"
590 || opt_key == "wipe_tower_fillet_wall"
591 || opt_key == "wipe_tower_filament"
592 || opt_key == "wiping_volumes_extruders"
593 || opt_key == "enable_filament_ramming"
594 || opt_key == "tool_change_on_wipe_tower"
595 || opt_key == "wait_for_temp_on_wipe_tower"
596 || opt_key == "purge_in_prime_tower"
597 || opt_key == "z_offset"
598 || opt_key == "support_multi_bed_types"
599 ) {
600 steps.emplace_back(psWipeTower);
601 steps.emplace_back(psSkirtBrim);
602 } else if (opt_key == "filament_soluble"
603 || opt_key == "filament_is_support"
604 || opt_key == "filament_printable"
605 || opt_key == "filament_change_length"
606 || opt_key == "independent_support_layer_height") {
607 steps.emplace_back(psWipeTower);
608 // Soluble support interface / non-soluble base interface produces non-soluble interface layers below soluble interface layers.
609 // Thus switching between soluble / non-soluble interface layer material may require recalculation of supports.
610 //FIXME Killing supports on any change of "filament_soluble" is rough. We should check for each object whether that is necessary.
611 osteps.emplace_back(posSupportMaterial);
612 osteps.emplace_back(posSimplifySupportPath);
613 } else if (
614 opt_key == "initial_layer_line_width"
615 || opt_key == "min_layer_height"
616 || opt_key == "max_layer_height"
617 //|| opt_key == "resolution"
618 //BBS: when enable arc fitting, we must re-generate perimeter
619 || opt_key == "enable_arc_fitting"
620 || opt_key == "print_order"
621 || opt_key == "wall_sequence") {
622 osteps.emplace_back(posPerimeters);
623 osteps.emplace_back(posEstimateCurledExtrusions);
624 osteps.emplace_back(posInfill);
625 osteps.emplace_back(posSupportMaterial);
626 osteps.emplace_back(posSimplifyPath);
627 osteps.emplace_back(posSimplifyInfill);
628 osteps.emplace_back(posSimplifySupportPath);
629 steps.emplace_back(psSkirtBrim);
630 }
631 else if (opt_key == "z_hop_types") {
632 osteps.emplace_back(posDetectOverhangsForLift);
633 }
634 // The bed-zone colour scheme and the advisory margin bands are drawn from the printer
635 // config but never reach the slice: compute_imex_zone_layout keeps the margin out of
636 // primary_zone_box, which is the only part of the layout update_imex_slice_offset reads.
637 // imex_tool_layout does move that box, so it is left to the fallback below.
638 else if (opt_key == "imex_viz_theme" || opt_key == "imex_carriage_margin") {
639 } else {
640 // for legacy, if we can't handle this option let's invalidate all steps
641 //FIXME invalidate all steps of all objects as well?
642 invalidated |= this->invalidate_all_steps();
643 // Continue with the other opt_keys to possibly invalidate any object specific steps.
644 }
645 }
646
648 for (PrintStep step : steps)
649 invalidated |= this->invalidate_step(step);
651 for (PrintObjectStep ostep : osteps)
652 for (PrintObject *object : m_objects)
653 invalidated |= object->invalidate_step(ostep);
654
655 return invalidated;
656}
static bool wait_for_temp_enabled(const PrintConfig &config)
Definition WipeTower2.cpp:1035
std::string t_config_option_key
Definition Config.hpp:96
PrintStep
Definition Print.hpp:108
PrintObjectStep
Definition Print.hpp:123
@ posInfill
Definition Print.hpp:125
@ posPerimeters
Definition Print.hpp:124
@ posEstimateCurledExtrusions
Definition Print.hpp:124
@ posSimplifySupportPath
Definition Print.hpp:125
@ posDetectOverhangsForLift
Definition Print.hpp:127
@ posSimplifyInfill
Definition Print.hpp:128
@ posSimplifyPath
Definition Print.hpp:125

References Slic3r::PrintBaseWithState< PrintStep, psCount >::invalidate_all_steps(), invalidate_step(), m_config, m_objects, Slic3r::posDetectOverhangsForLift, Slic3r::posEstimateCurledExtrusions, Slic3r::posInfill, Slic3r::posPerimeters, Slic3r::posSimplifyInfill, Slic3r::posSimplifyPath, Slic3r::posSimplifySupportPath, Slic3r::posSlice, Slic3r::posSupportMaterial, PrintObject, Slic3r::psGCodeExport, Slic3r::psSkirtBrim, Slic3r::psWipeTower, Slic3r::sort_remove_duplicates(), and Slic3r::WipeTower2::wait_for_temp_enabled().

Here is the call graph for this function:

◆ invalidate_step()

bool Slic3r::Print::invalidate_step ( PrintStep step)
protected
664{
665 bool invalidated = Inherited::invalidate_step(step);
666 // Propagate to dependent steps.
667 if (step != psGCodeExport)
669 return invalidated;
670}
bool invalidate_step(PrintStep step)
Definition PrintBase.hpp:615

References Slic3r::PrintBaseWithState< PrintStep, psCount >::invalidate_step(), and Slic3r::psGCodeExport.

Referenced by invalidate_state_by_config_options(), and set_gcode_file_invalidated().

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

◆ invalidate_steps() [1/2]

bool Slic3r::PrintBaseWithState< PrintStep, COUNT >::invalidate_steps ( std::initializer_list< PrintStep > il)
inlineprotectedinherited
621 { return m_state.invalidate_multiple(il.begin(), il.end(), this->cancel_callback()); }

References m_state.

◆ invalidate_steps() [2/2]

bool Slic3r::PrintBaseWithState< PrintStep, COUNT >::invalidate_steps ( StepTypeIterator step_begin,
StepTypeIterator step_end )
inlineprotectedinherited
619 { return m_state.invalidate_multiple(step_begin, step_end, this->cancel_callback()); }

References Slic3r::PrintBase::cancel_callback(), and m_state.

Here is the call graph for this function:

◆ is_all_objects_are_short()

bool Slic3r::Print::is_all_objects_are_short ( ) const
inline
1379 {
1380 return std::all_of(this->objects().begin(), this->objects().end(), [&](PrintObject* obj) { return obj->height() < scale_(this->config().nozzle_height.value); });
1381 }
T * begin(Slic3r::Mat< N, M, T > &mat)
Definition Point.hpp:838
T * end(Slic3r::Mat< N, M, T > &mat)
Definition Point.hpp:841
zaa_minimize_perimeter_height nozzle_height((ConfigOptionStrings, extruder_colour))((ConfigOptionPoints

References config(), Slic3r::PrintObject::height(), Slic3r::nozzle_height(), objects(), PrintObject, and scale_.

Referenced by object_skirt_offset(), and sequential_print_clearance_valid().

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

◆ is_BBL_printer() [1/2]

◆ is_BBL_printer() [2/2]

const bool Slic3r::Print::is_BBL_printer ( ) const
inline
1342{ return m_isBBLPrinter; }

References m_isBBLPrinter.

◆ is_dynamic_group_reorder()

bool Slic3r::Print::is_dynamic_group_reorder ( ) const
5052{
5053 const auto *opt = m_full_print_config.option<ConfigOptionBool>("enable_filament_dynamic_map");
5054 const bool enabled = opt && opt->value;
5055 if (!enabled || m_config.filament_map_mode != FilamentMapMode::fmmAutoForFlush || m_config.nozzle_diameter.size() <= 1)
5056 return false;
5057
5058 // Dynamic regrouping and mixed-color slots are incompatible: a mixed slot is resolved to
5059 // different physical components per layer, so a group assignment made up-front would be wrong.
5060 const auto &is_mixed = m_config.filament_is_mixed.values;
5061 for (unsigned int filament_id : extruders()) {
5062 if (filament_id < is_mixed.size() && is_mixed[filament_id])
5063 return false;
5064 }
5065 return true;
5066}
@ fmmAutoForFlush
Definition PrintConfig.hpp:633

References extruders(), Slic3r::fmmAutoForFlush, is_dynamic_group_reorder(), m_config, and Slic3r::PrintBaseWithState< PrintStep, psCount >::m_full_print_config.

Referenced by Slic3r::GCode::_do_export(), and is_dynamic_group_reorder().

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

◆ is_filaments_compatible()

bool Slic3r::Print::is_filaments_compatible ( const std::vector< int > & types)
static
1798{
1799 bool has_high_temperature_filament = false;
1800 bool has_low_temperature_filament = false;
1801
1802 for (const auto& type : filament_types) {
1803 if (type == FilamentTempType::HighTemp)
1804 has_high_temperature_filament = true;
1805 else if (type == FilamentTempType::LowTemp)
1806 has_low_temperature_filament = true;
1807 }
1808
1809 if (has_high_temperature_filament && has_low_temperature_filament)
1810 return false;
1811
1812 return true;
1813}

References Slic3r::HighTemp, and Slic3r::LowTemp.

Referenced by Slic3r::arrangement::AutoArranger< TBin >::objfunc().

Here is the caller graph for this function:

◆ is_step_done() [1/2]

bool Slic3r::Print::is_step_done ( PrintObjectStep step) const
675{
676 if (m_objects.empty())
677 return false;
678 std::scoped_lock<std::mutex> lock(this->state_mutex());
679 for (const PrintObject *object : m_objects)
680 if (! object->is_step_done_unguarded(step))
681 return false;
682 return true;
683}

References m_objects, PrintObject, and Slic3r::PrintBaseWithState< PrintStep, psCount >::state_mutex().

Here is the call graph for this function:

◆ is_step_done() [2/2]

bool Slic3r::Print::is_step_done ( PrintStep step) const
inline
1092{ return Inherited::is_step_done(step); }
bool is_step_done(PrintStep step) const
Definition PrintBase.hpp:594

References Slic3r::PrintBaseWithState< PrintStep, psCount >::is_step_done().

Referenced by libvgcode::convert(), Slic3r::GCode::do_export(), finished(), Slic3r::layered_print_cleareance_valid(), Slic3r::GUI::Preview::load_print_as_fff(), process(), Slic3r::GUI::GLCanvas3D::reload_scene(), Slic3r::GUI::GLCanvas3D::update_compacted_wipe_tower_clearance(), and wipe_tower_data().

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

◆ is_step_done_unguarded()

bool Slic3r::PrintBaseWithState< PrintStep, COUNT >::is_step_done_unguarded ( PrintStep step) const
inlineprotectedinherited
626{ return m_state.is_done_unguarded(step); }

References m_state.

◆ is_step_started_unguarded()

bool Slic3r::PrintBaseWithState< PrintStep, COUNT >::is_step_started_unguarded ( PrintStep step) const
inlineprotectedinherited
625{ return m_state.is_started_unguarded(step); }

References m_state.

◆ is_support_used()

bool Slic3r::Print::is_support_used ( ) const
inline
1320{return m_support_used;}

References m_support_used.

◆ layers_sorted_for_object()

std::vector< size_t > Slic3r::Print::layers_sorted_for_object ( float start,
float end,
std::vector< LayerPtrs > & layers_of_objects,
std::vector< BoundingBox > & boundingBox_for_objects,
VecOfPoints & objects_instances_shift )
874{
875 std::vector<size_t> idx_of_object_sorted;
876 size_t idx = 0;
877 for (const auto &object : m_objects) {
878 idx_of_object_sorted.push_back(idx++);
879 object->get_certain_layers(start, end, layers_of_objects, boundingBox_for_objects);
880 }
881 std::sort(idx_of_object_sorted.begin(), idx_of_object_sorted.end(),
882 [boundingBox_for_objects](auto left, auto right) { return boundingBox_for_objects[left].area() > boundingBox_for_objects[right].area(); });
883
884 objects_instances_shift.clear();
885 objects_instances_shift.reserve(m_objects.size());
886 for (const auto& object : m_objects)
887 objects_instances_shift.emplace_back(object->get_instances_shift_without_plate_offset());
888
889 return idx_of_object_sorted;
890};

References m_objects.

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

References id().

Here is the call graph for this function:

◆ load_cached_data()

int Slic3r::Print::load_cached_data ( const std::string & directory)
overridevirtual

Reimplemented from Slic3r::PrintBase.

6903{
6904 int ret = 0;
6905 boost::filesystem::path directory_path(directory);
6906
6907 if (!fs::exists(directory_path)) {
6908 BOOST_LOG_TRIVIAL(info) << boost::format("directory %1% not exist.")%directory;
6910 }
6911
6912 auto find_region = [](PrintObject* object, size_t config_hash) -> const PrintRegion* {
6913 int regions_count = object->num_printing_regions();
6914 for (int index = 0; index < regions_count; index++ )
6915 {
6916 const PrintRegion& print_region = object->printing_region(index);
6917 if (print_region.config_hash() == config_hash ) {
6918 return &print_region;
6919 }
6920 }
6921 return NULL;
6922 };
6923
6924 int count = 0;
6925 std::vector<std::pair<std::string, PrintObject*>> object_filenames;
6926 for (PrintObject *obj : m_objects) {
6927 const ModelObject* model_obj = obj->model_object();
6928 const PrintInstance &print_instance = obj->instances()[0];
6929 const ModelInstance *model_instance = print_instance.model_instance;
6930
6931 obj->clear_layers();
6932 obj->clear_support_layers();
6933
6934 int identify_id = model_instance->loaded_id;
6935 if (identify_id <= 0) {
6936 //for old 3mf
6937 identify_id = model_instance->id().id;
6938 BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(": object %1%'s loaded_id is 0, need to use the instance_id %2%")%model_obj->name %identify_id;
6939 //continue;
6940 }
6941 std::string file_name = directory +"/obj_"+std::to_string(identify_id)+".json";
6942
6943 if (!fs::exists(file_name)) {
6944 BOOST_LOG_TRIVIAL(info) << __FUNCTION__<<boost::format(": file %1% not exist, maybe a shared object, skip it")%file_name;
6945 continue;
6946 }
6947 object_filenames.push_back({file_name, obj});
6948 }
6949
6950 boost::mutex mutex;
6951 std::vector<json> object_jsons(object_filenames.size());
6952 tbb::parallel_for(
6953 tbb::blocked_range<size_t>(0, object_filenames.size()),
6954 [object_filenames, &ret, &object_jsons, &mutex](const tbb::blocked_range<size_t>& filename_range) {
6955 for (size_t filename_index = filename_range.begin(); filename_index < filename_range.end(); ++ filename_index) {
6956 try {
6957 json root_json;
6958 boost::nowide::ifstream ifs(object_filenames[filename_index].first);
6959 ifs >> root_json;
6960 object_jsons[filename_index] = std::move(root_json);
6961 }
6962 catch(std::exception &err) {
6963 BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": load from "<<object_filenames[filename_index].first<<" got a generic exception, reason = " << err.what();
6964 boost::unique_lock l(mutex);
6965 ret = CLI_IMPORT_CACHE_LOAD_FAILED;
6966 }
6967 }
6968 }
6969 );
6970
6971 if (ret) {
6972 BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< boost::format(": load json failed.");
6973 return ret;
6974 }
6975
6976 for (int obj_index = 0; obj_index < object_jsons.size(); obj_index++) {
6977 json& root_json = object_jsons[obj_index];
6978 PrintObject *obj = object_filenames[obj_index].second;
6979
6980 try {
6981 //boost::nowide::ifstream ifs(file_name);
6982 //ifs >> root_json;
6983
6984 std::string name = root_json.at(JSON_OBJECT_NAME);
6985 int identify_id = root_json.at(JSON_IDENTIFY_ID);
6986 int layer_count = 0, support_layer_count = 0, firstlayer_group_count = 0;
6987
6988 layer_count = root_json[JSON_LAYERS].size();
6989 support_layer_count = root_json[JSON_SUPPORT_LAYERS].size();
6990 firstlayer_group_count = root_json[JSON_FIRSTLAYER_GROUPS].size();
6991
6992 BOOST_LOG_TRIVIAL(info) << __FUNCTION__<<boost::format(":will load %1%, identify_id %2%, layer_count %3%, support_layer_count %4%, firstlayer_group_count %5%")
6993 %name %identify_id %layer_count %support_layer_count %firstlayer_group_count;
6994
6995 Layer* previous_layer = NULL;
6996 //create layer and layer regions
6997 for (int index = 0; index < layer_count; index++)
6998 {
6999 json& layer_json = root_json[JSON_LAYERS][index];
7000 Layer* new_layer = obj->add_layer(layer_json[JSON_LAYER_ID], layer_json[JSON_LAYER_HEIGHT], layer_json[JSON_LAYER_PRINT_Z], layer_json[JSON_LAYER_SLICE_Z]);
7001 if (!new_layer) {
7002 BOOST_LOG_TRIVIAL(error) <<__FUNCTION__<< boost::format(":create_layer failed, out of memory");
7003 return CLI_OUT_OF_MEMORY;
7004 }
7005 if (previous_layer) {
7006 previous_layer->upper_layer = new_layer;
7007 new_layer->lower_layer = previous_layer;
7008 }
7009 previous_layer = new_layer;
7010
7011 //layer regions
7012 int layer_regions_count = layer_json[JSON_LAYER_REGIONS].size();
7013 for (int region_index = 0; region_index < layer_regions_count; region_index++)
7014 {
7015 json& region_json = layer_json[JSON_LAYER_REGIONS][region_index];
7016 size_t config_hash = region_json[JSON_LAYER_REGION_CONFIG_HASH];
7017 const PrintRegion *print_region = find_region(obj, config_hash);
7018
7019 if (!print_region){
7020 BOOST_LOG_TRIVIAL(error) <<__FUNCTION__<< boost::format(":can not find print region of object %1%, layer %2%, print_z %3%, layer_region %4%")
7021 %name % index %new_layer->print_z %region_index;
7022 //delete new_layer;
7024 }
7025
7026 new_layer->add_region(print_region);
7027 }
7028
7029 }
7030
7031 //load the layer data parallel
7032 BOOST_LOG_TRIVIAL(info) << __FUNCTION__<<boost::format(": load the layers in parallel");
7033 tbb::parallel_for(
7034 tbb::blocked_range<size_t>(0, obj->layer_count()),
7035 [&root_json, &obj](const tbb::blocked_range<size_t>& layer_range) {
7036 for (size_t layer_index = layer_range.begin(); layer_index < layer_range.end(); ++ layer_index) {
7037 const json& layer_json = root_json[JSON_LAYERS][layer_index];
7038 Layer* layer = obj->get_layer(layer_index);
7039 extract_layer(layer_json, *layer);
7040 }
7041 }
7042 );
7043
7044 //support layers
7045 Layer* previous_support_layer = NULL;
7046 //create support_layers
7047 for (int index = 0; index < support_layer_count; index++)
7048 {
7049 json& layer_json = root_json[JSON_SUPPORT_LAYERS][index];
7050 SupportLayer* new_support_layer = obj->add_support_layer(layer_json[JSON_LAYER_ID], layer_json[JSON_SUPPORT_LAYER_INTERFACE_ID], layer_json[JSON_LAYER_HEIGHT], layer_json[JSON_LAYER_PRINT_Z]);
7051 if (!new_support_layer) {
7052 BOOST_LOG_TRIVIAL(error) <<__FUNCTION__<< boost::format(":add_support_layer failed, out of memory");
7053 return CLI_OUT_OF_MEMORY;
7054 }
7055 if (previous_support_layer) {
7056 previous_support_layer->upper_layer = new_support_layer;
7057 new_support_layer->lower_layer = previous_support_layer;
7058 }
7059 previous_support_layer = new_support_layer;
7060 }
7061
7062 BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(": finished load layers, start to load support_layers.");
7063 tbb::parallel_for(
7064 tbb::blocked_range<size_t>(0, obj->support_layer_count()),
7065 [&root_json, &obj](const tbb::blocked_range<size_t>& support_layer_range) {
7066 for (size_t layer_index = support_layer_range.begin(); layer_index < support_layer_range.end(); ++ layer_index) {
7067 const json& layer_json = root_json[JSON_SUPPORT_LAYERS][layer_index];
7068 SupportLayer* support_layer = obj->get_support_layer(layer_index);
7069 extract_support_layer(layer_json, *support_layer);
7070 }
7071 }
7072 );
7073
7074 //load first group volumes
7075 std::vector<groupedVolumeSlices>& firstlayer_objgroups = obj->firstLayerObjGroupsMod();
7076 for (int index = 0; index < firstlayer_group_count; index++)
7077 {
7078 json& firstlayer_group_json = root_json[JSON_FIRSTLAYER_GROUPS][index];
7079 groupedVolumeSlices firstlayer_group = firstlayer_group_json;
7080 //convert the id
7081 for (ObjectID& obj_id : firstlayer_group.volume_ids)
7082 {
7083 ModelVolume* currentModelVolumePtr = nullptr;
7084 ModelVolumePtrs& volumes_ptr = obj->model_object()->volumes;
7085 size_t volume_count = volumes_ptr.size();
7086 if (obj_id.id < volume_count) {
7087 currentModelVolumePtr = volumes_ptr[obj_id.id];
7088 obj_id = currentModelVolumePtr->id();
7089 }
7090 else {
7091 BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< boost::format(": can not find volume_id %1% from object file %2% in firstlayer groups, volume_count %3%!")
7092 %obj_id.id %object_filenames[obj_index].first %volume_count;
7094 }
7095 }
7096 firstlayer_objgroups.push_back(std::move(firstlayer_group));
7097 }
7098
7099 count ++;
7100 BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(": load object %1% from %2% successfully.")%count%object_filenames[obj_index].first;
7101 }
7102 catch(nlohmann::detail::parse_error &err) {
7103 BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": parse "<<object_filenames[obj_index].first<<" got a nlohmann::detail::parse_error, reason = " << err.what();
7105 }
7106 catch(std::exception &err) {
7107 BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": load from "<<object_filenames[obj_index].first<<" got a generic exception, reason = " << err.what();
7109 }
7110 }
7111
7112 object_jsons.clear();
7113 object_filenames.clear();
7114 BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(": total printobject count %1%, loaded %2%, ret=%3%")%m_objects.size() %count %ret;
7115 return ret;
7116}
#define JSON_SUPPORT_LAYER_INTERFACE_ID
Definition Print.cpp:5924
#define JSON_LAYER_REGION_CONFIG_HASH
Definition Print.cpp:5927
#define CLI_IMPORT_CACHE_DATA_CAN_NOT_USE
Definition Utils.hpp:77
#define CLI_OUT_OF_MEMORY
Definition Utils.hpp:57
#define CLI_IMPORT_CACHE_LOAD_FAILED
Definition Utils.hpp:78
#define CLI_IMPORT_CACHE_NOT_FOUND
Definition Utils.hpp:76

References CLI_IMPORT_CACHE_NOT_FOUND, Slic3r::PrintRegion::config_hash(), count, Slic3r::ObjectBase::id(), Slic3r::ObjectID::id, load_cached_data(), Slic3r::ModelInstance::loaded_id, m_objects, Slic3r::PrintInstance::model_instance, Slic3r::ModelObject::name, and PrintObject.

Referenced by load_cached_data().

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

◆ make_try_cancel()

PrintTryCancel Slic3r::PrintBase::make_try_cancel ( ) const
inlineprotectedinherited
554{ return PrintTryCancel(this); }
friend PrintTryCancel
Definition PrintBase.hpp:587

References PrintTryCancel.

◆ max_allowed_layer_height()

double Slic3r::Print::max_allowed_layer_height ( ) const
813{
814 double nozzle_diameter_max = 0.;
815 for (unsigned int extruder_id : this->extruders())
816 nozzle_diameter_max = std::max(nozzle_diameter_max, m_config.nozzle_diameter.get_at(extruder_id));
817 return nozzle_diameter_max;
818}

References extruders(), and m_config.

Here is the call graph for this function:

◆ model()

const Model & Slic3r::PrintBase::model ( ) const
inlineinherited
420{ return m_model; }

References m_model.

Referenced by Slic3r::ToolOrdering::ToolOrdering(), Slic3r::GUI::Plater::priv::actualize_object_warnings(), apply(), Slic3r::ToolOrdering::assign_custom_gcodes(), Slic3r::GCode::do_export(), Slic3r::Print::sequential_print_clearance_valid(), and Slic3r::sort_object_instances_by_model_order().

Here is the caller graph for this function:

◆ need_check_multi_filaments_compatibility()

bool Slic3r::Print::need_check_multi_filaments_compatibility ( ) const
inline
bool m_need_check_multi_filaments_compatibility
Definition Print.hpp:1586

References m_need_check_multi_filaments_compatibility.

Referenced by Slic3r::GCode::_do_export(), and check_multi_filament_valid().

Here is the caller graph for this function:

◆ num_object_instances()

unsigned int Slic3r::Print::num_object_instances ( ) const
805{
806 unsigned int instances = 0;
807 for (const PrintObject *print_object : m_objects)
808 instances += (unsigned int)print_object->instances().size();
809 return instances;
810}

References m_objects, and PrintObject.

Referenced by Slic3r::GCode::_do_export(), Slic3r::GCode::generate_timelapse_gcode(), and Slic3r::sort_object_instances_by_model_order().

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

◆ num_print_regions()

size_t Slic3r::Print::num_print_regions ( ) const
throw ( )
inline
1289{ return m_print_regions.size(); }

References m_print_regions.

Referenced by Slic3r::GCode::_do_export(), and Slic3r::GCode::process_layer().

Here is the caller graph for this function:

◆ object_extruders()

std::vector< unsigned int > Slic3r::Print::object_extruders ( ) const
687{
688 std::vector<unsigned int> extruders;
689 extruders.reserve(m_print_regions.size() * m_objects.size() * 3);
690
691 //Orca: Collect extruders from all regions.
692 for (const PrintObject *object : m_objects)
693 for (const PrintRegion &region : object->all_regions())
694 region.collect_object_printing_extruders(*this, extruders);
695
696 for (const PrintObject* object : m_objects)
697 append_model_object_extruders(*object->model_object(), extruders);
699 return extruders;
700}
static void append_model_object_extruders(const ModelObject &mo, std::vector< unsigned int > &extruders)
Definition Print.cpp:703

References append_model_object_extruders(), extruders(), m_objects, m_print_regions, PrintObject, and Slic3r::sort_remove_duplicates().

Referenced by Slic3r::ToolOrdering::ToolOrdering(), Slic3r::ToolOrdering::assign_custom_gcodes(), extruders(), and support_material_extruders().

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

◆ object_skirt_offset()

std::tuple< float, float > Slic3r::Print::object_skirt_offset ( double margin_height = 0) const
5797{
5798 if (config().skirt_loops == 0 || config().skirt_type != stPerObject || m_objects.empty())
5799 return std::make_tuple(0, 0);
5800
5801 float max_nozzle_diameter = *std::max_element(m_config.nozzle_diameter.values.begin(), m_config.nozzle_diameter.values.end());
5802 float max_layer_height = *std::max_element(config().max_layer_height.values.begin(), config().max_layer_height.values.end());
5803 float line_width = m_config.initial_layer_line_width.get_abs_value(max_nozzle_diameter);
5804 float object_skirt_witdh = skirt_flow().width() + (config().skirt_loops - 1) * skirt_flow().spacing();
5805 float object_skirt_offset = 0;
5806
5808 object_skirt_offset = config().skirt_distance + object_skirt_witdh;
5809 else if (config().draft_shield == dsEnabled || config().skirt_height * max_layer_height > config().nozzle_height - margin_height)
5810 object_skirt_offset = config().skirt_distance + line_width;
5811 else if (config().skirt_distance + object_skirt_witdh > config().extruder_clearance_radius/2)
5812 object_skirt_offset = (config().skirt_distance + object_skirt_witdh - config().extruder_clearance_radius/2);
5813 else
5814 return std::make_tuple(0, 0);
5815
5816 return std::make_tuple(object_skirt_offset, object_skirt_witdh);
5817}
float spacing() const
Definition Flow.hpp:69
float width() const
Definition Flow.hpp:63
bool is_all_objects_are_short() const
Definition Print.hpp:1379
std::tuple< float, float > object_skirt_offset(double margin_height=0) const
Definition Print.cpp:5796
zaa_minimize_perimeter_height skirt_type((ConfigOptionFloat, skirt_speed))((ConfigOptionBool
zaa_minimize_perimeter_height skirt_height((ConfigOptionInt, skirt_loops))((ConfigOptionEnum< SkirtType >

References config(), Slic3r::dsEnabled, is_all_objects_are_short(), m_config, m_objects, Slic3r::nozzle_height(), object_skirt_offset(), skirt_flow(), Slic3r::skirt_height(), Slic3r::skirt_type(), and Slic3r::stPerObject.

Referenced by object_skirt_offset(), sequential_print_clearance_valid(), and Slic3r::GUI::GLCanvas3D::update_sequential_clearance().

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

◆ objects()

ConstPrintObjectPtrsAdaptor Slic3r::Print::objects ( ) const
inline
1134{ return ConstPrintObjectPtrsAdaptor(&m_objects); }

References m_objects.

Referenced by Slic3r::ToolOrdering::ToolOrdering(), Slic3r::GCode::_do_export(), Slic3r::GUI::GLCanvas3D::_render_overlays(), apply(), Slic3r::GCode::assign_object_label_ids(), Slic3r::belt_brim_obstacles(), Slic3r::chain_print_object_instances(), Slic3r::chain_print_object_instances_best_of(), Slic3r::chain_print_object_instances_snake(), Slic3r::GCode::collect_layers_to_print(), compacted_wipe_tower_clearance_valid(), libvgcode::convert(), libvgcode::convert_brim_skirt_to_vertices(), Slic3r::TreeSupport::draw_circles(), Slic3r::WipingExtrusions::ensure_perimeters_infills_order(), Slic3r::ToolOrdering::generate_first_layer_tool_order(), Slic3r::generate_tree_support_3D(), Slic3r::get_boundary_external(), Slic3r::get_imex_active_tools(), Slic3r::get_path_of_change_filament(), Slic3r::get_perimeter_spacing_external(), Slic3r::getObjectExtruderMap(), Slic3r::SeamPlacer::init(), Slic3r::GUI::GLGizmoFdmSupports::init_print_instance(), is_all_objects_are_short(), Slic3r::GUI::IMSlider::IsNewPrint(), Slic3r::layered_print_cleareance_valid(), Slic3r::GUI::GCodeViewer::load_as_gcode(), Slic3r::GUI::Preview::load_print_as_fff(), Slic3r::GUI::GCodeViewer::load_shells(), Slic3r::WipingExtrusions::mark_wiping_extrusions(), Slic3r::outer_inner_brim_area(), Slic3r::CLI::run(), sequential_print_clearance_valid(), Slic3r::set_mass_locator(), Slic3r::GCode::set_object_info(), Slic3r::ToolOrdering::sort_and_build_data(), Slic3r::sort_object_instances_by_model_order(), Slic3r::GUI::SelectMachineDialog::start_timelapse_storage_check(), and Slic3r::GUI::Preview::update_layers_slider().

Here is the caller graph for this function:

◆ objects_mutable()

PrintObjectPtrs & Slic3r::Print::objects_mutable ( )
inline
1153{ return m_objects; }

References m_objects.

◆ output_filename() [1/2]

std::string Slic3r::Print::output_filename ( const std::string & filename_base = std::string()) const
overridevirtual

Implements Slic3r::PrintBase.

5646{
5647 // Set the placeholders for the data know first after the G-code export is finished.
5648 // These values will be just propagated into the output file name.
5649 DynamicConfig config = this->finished() ? this->print_statistics().config() : this->print_statistics().placeholders();
5650 config.set_key_value("num_filaments", new ConfigOptionInt((int)m_config.nozzle_diameter.size()));
5651 config.set_key_value("num_extruders", new ConfigOptionInt((int) m_config.nozzle_diameter.size()));
5652 config.set_key_value("plate_name", new ConfigOptionString(get_plate_name()));
5653 config.set_key_value("plate_number", new ConfigOptionString(get_plate_number_formatted()));
5654 config.set_key_value("model_name", new ConfigOptionString(get_model_name()));
5655
5656 // the same type of filament contains multiple names, support exporting according to the filament name
5657 auto full_print_config = this->full_print_config();
5658 const ConfigOptionStrings* filament_settings_id = full_print_config.option<ConfigOptionStrings>("filament_settings_id");
5659 std::string filament_name = "";
5660 auto extruders = this->extruders(true);
5661 if(!extruders.empty()) {
5662 // first extruder is the default extruder
5663 int extruder_id = extruders.front();
5664 if(filament_settings_id->values.size() > extruder_id) {
5665 filament_name = filament_settings_id->values[extruder_id];
5666 }
5667 }
5668 size_t end_pos = filament_name.find_first_of("@");
5669 if (end_pos != std::string::npos) {
5670 filament_name = filament_name.substr(0, end_pos);
5671 }
5672 config.set_key_value("filament_name", new ConfigOptionString(filament_name));
5673
5674 return this->PrintBase::output_filename(m_config.filename_format.value, ".gcode", filename_base, &config);
5675}
const DynamicPrintConfig & full_print_config() const
Definition PrintBase.hpp:518
std::string get_plate_name() const
Definition PrintBase.hpp:532
virtual std::string output_filename(const std::string &filename_base=std::string()) const =0
const PrintStatistics & print_statistics() const
Definition Print.hpp:1180
bool finished() const override
Definition Print.hpp:1096
std::string get_plate_number_formatted() const
Definition Print.cpp:5687
zaa_minimize_perimeter_height ConfigOptionString
Definition PrintConfig.hpp:1702
static DynamicConfig placeholders()
Definition Print.cpp:5842
DynamicConfig config() const
Definition Print.cpp:5819

References config(), extruders(), finished(), Slic3r::PrintBaseWithState< PrintStep, psCount >::full_print_config(), get_model_name(), Slic3r::PrintBaseWithState< PrintStep, psCount >::get_plate_name(), get_plate_number_formatted(), m_config, output_filename(), Slic3r::PrintBase::output_filename(), print_statistics(), and Slic3r::ConfigOptionVector< T >::values.

Referenced by output_filename(), and anonymous_namespace{OrcaSlicer_profile_validator.cpp}::slice_two_color_cube_and_export().

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

◆ output_filename() [2/2]

std::string Slic3r::PrintBase::output_filename ( const std::string & format,
const std::string & default_ext,
const std::string & filename_base,
const DynamicConfig * config_override = nullptr ) const
protectedinherited
82{
83 DynamicConfig cfg;
84 if (config_override != nullptr)
85 cfg = *config_override;
86 cfg.set_key_value("version", new ConfigOptionString(std::string(SoftFever_VERSION)));
89 this->update_object_placeholders(cfg, default_ext);
90 if (! filename_base.empty()) {
91 cfg.set_key_value("input_filename", new ConfigOptionString(filename_base + default_ext));
92 cfg.set_key_value("input_filename_base", new ConfigOptionString(filename_base));
93 }
94 try {
95 boost::filesystem::path filename = format.empty() ?
96 cfg.opt_string("input_filename_base") + default_ext :
97 this->placeholder_parser().process(format, 0, &cfg);
98 if (filename.extension().empty())
99 filename.replace_extension(default_ext);
100 return filename.string();
101 } catch (std::runtime_error &err) {
102 throw Slic3r::PlaceholderParserError(L("Processing of the filename_format template failed.") + "\n" + err.what());
103 }
104}
void update_user_name()
Definition PlaceholderParser.hpp:88
std::string process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override, DynamicConfig *config_outputs, ContextData *context) const
Definition PlaceholderParser.cpp:2682
void update_timestamp()
Definition PlaceholderParser.hpp:85
void update_object_placeholders(DynamicConfig &config, const std::string &default_ext) const
Definition PrintBase.cpp:37
const PlaceholderParser & placeholder_parser() const
Definition PrintBase.hpp:517

References Slic3r::ConfigOptionString, Slic3r::format(), L, Slic3r::DynamicConfig::opt_string(), placeholder_parser(), Slic3r::PlaceholderParser::process(), Slic3r::DynamicConfig::set_key_value(), update_object_placeholders(), Slic3r::PlaceholderParser::update_timestamp(), and Slic3r::PlaceholderParser::update_user_name().

Here is the call graph for this function:

◆ output_filepath()

std::string Slic3r::PrintBase::output_filepath ( const std::string & path,
const std::string & filename_base = std::string() ) const
inherited
107{
108 // if we were supplied no path, generate an automatic one based on our first object's input file
109 if (path.empty())
110 // get the first input file name
111 return (boost::filesystem::path(m_model.propose_export_file_name_and_path()).parent_path() / this->output_filename(filename_base)).make_preferred().string();
112
113 // if we were supplied a directory, use it and append our automatically generated filename
114 boost::filesystem::path p(path);
115 if (boost::filesystem::is_directory(p))
116 return (p / this->output_filename(filename_base)).make_preferred().string();
117
118 // if we were supplied a file which is not a directory, use it
119 return path;
120}

References m_model, and output_filename().

Here is the call graph for this function:

◆ placeholder_parser()

const PlaceholderParser & Slic3r::PrintBase::placeholder_parser ( ) const
inlineinherited
517{ return m_placeholder_parser; }

References m_placeholder_parser.

Referenced by Slic3r::GCode::_do_export(), and output_filename().

Here is the caller graph for this function:

◆ print_object_ids()

std::vector< ObjectID > Slic3r::Print::print_object_ids ( ) const
overridevirtual

Implements Slic3r::PrintBase.

821{
822 std::vector<ObjectID> out;
823 // Reserve one more for the caller to append the ID of the Print itself.
824 out.reserve(m_objects.size() + 1);
825 for (const PrintObject *print_object : m_objects)
826 out.emplace_back(print_object->id());
827 return out;
828}

References m_objects, and PrintObject.

Referenced by Slic3r::GCode::_do_export(), check_multi_filament_valid(), and Slic3r::make_brim().

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

◆ print_regions_mutable()

PrintRegionPtrs & Slic3r::Print::print_regions_mutable ( )
inline
1154{ return m_print_regions; }

References m_print_regions.

◆ print_statistics() [1/2]

PrintStatistics & Slic3r::Print::print_statistics ( )
inline
1181{ return m_print_statistics; }
PrintStatistics m_print_statistics
Definition Print.hpp:1560

References m_print_statistics.

◆ print_statistics() [2/2]

const PrintStatistics & Slic3r::Print::print_statistics ( ) const
inline
1180{ return m_print_statistics; }

References m_print_statistics.

Referenced by Slic3r::GUI::Plater::load_gcode(), output_filename(), and Slic3r::GUI::GCodeViewer::render_all_plates_stats().

Here is the caller graph for this function:

◆ process()

void Slic3r::Print::process ( long long * time_cost_with_cache = nullptr,
bool use_cache = false )
overridevirtual

Implements Slic3r::PrintBase.

3262{
3263 long long start_time = 0, end_time = 0;
3264 if (time_cost_with_cache)
3265 *time_cost_with_cache = 0;
3266
3267 {
3268 const auto* sp = this->config().option<ConfigOptionStrings>("slicing_pipeline_plugin");
3269 m_pipeline_plugin_active = s_slicing_pipeline_hook_fn && sp && !sp->values.empty();
3270 }
3271
3273
3274 // IMEX firmware-managed zones: settle the emission-frame shift from the applied config
3275 // before anything runs. Zero for every printer that is not IMEX + firmware-managed.
3277
3278 //compute the PrintObject with the same geometries
3279 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, enter, use_cache=%2%, object size=%3%")%this%use_cache%m_objects.size();
3280 if (m_objects.empty())
3281 return;
3282
3283 // Belt purge prism: _plan_belt_purge() (psWipeTower) truncates the prism's
3284 // layers and drops its unclaimed fills, stashing both so a replan can undo
3285 // them. The object steps below regenerate per-layer content over m_layers
3286 // ONLY, so if any of them is about to rerun the stashes must go back first;
3287 // otherwise truncated layers keep stale perimeters/fills and dropped fills
3288 // are re-inserted next to freshly generated ones. Every object-step
3289 // invalidation also invalidates psWipeTower, so "psWipeTower not done" is
3290 // exactly "some object step may rerun" -- and when it IS done nothing below
3291 // regenerates, and the plan's edits have to stay.
3292 if (!this->is_step_done(psWipeTower))
3293 for (PrintObject *obj : m_objects)
3294 obj->belt_undo_purge_plan();
3295
3296 {
3297 LifecycleEventContext ctx;
3298 ctx.id = std::to_string(m_model.id().id);
3299 ctx.name = get_model_name();
3300 ctx.code = LifecycleEvtCode::Ok;
3301 ctx.cancellation_check = [this]() { return canceled(); };
3303 }
3304
3305 for (PrintObject *obj : m_objects)
3306 obj->clear_shared_object();
3307
3308 //add the print_object share check logic
3309 auto is_print_object_the_same = [](const PrintObject* object1, const PrintObject* object2) -> bool{
3310 if (object1->trafo().matrix() != object2->trafo().matrix())
3311 return false;
3312 const ModelObject* model_obj1 = object1->model_object();
3313 const ModelObject* model_obj2 = object2->model_object();
3314 if (model_obj1->volumes.size() != model_obj2->volumes.size())
3315 return false;
3316 bool has_extruder1 = model_obj1->config.has("extruder");
3317 bool has_extruder2 = model_obj2->config.has("extruder");
3318 if ((has_extruder1 != has_extruder2)
3319 || (has_extruder1 && model_obj1->config.extruder() != model_obj2->config.extruder()))
3320 return false;
3321 for (int index = 0; index < model_obj1->volumes.size(); index++) {
3322 const ModelVolume &model_volume1 = *model_obj1->volumes[index];
3323 const ModelVolume &model_volume2 = *model_obj2->volumes[index];
3324 if (model_volume1.type() != model_volume2.type())
3325 return false;
3326 if (model_volume1.mesh_ptr() != model_volume2.mesh_ptr())
3327 return false;
3328 if (!(model_volume1.get_transformation() == model_volume2.get_transformation()))
3329 return false;
3330 has_extruder1 = model_volume1.config.has("extruder");
3331 has_extruder2 = model_volume2.config.has("extruder");
3332 if ((has_extruder1 != has_extruder2)
3333 || (has_extruder1 && model_volume1.config.extruder() != model_volume2.config.extruder()))
3334 return false;
3335 if (!model_volume1.supported_facets.equals(model_volume2.supported_facets))
3336 return false;
3337 if (!model_volume1.seam_facets.equals(model_volume2.seam_facets))
3338 return false;
3339 if (!model_volume1.mmu_segmentation_facets.equals(model_volume2.mmu_segmentation_facets))
3340 return false;
3341 if (!model_volume1.fuzzy_skin_facets.equals(model_volume2.fuzzy_skin_facets))
3342 return false;
3343 if (model_volume1.config.get() != model_volume2.config.get())
3344 return false;
3345 }
3346 //if (!object1->config().equals(object2->config()))
3347 // return false;
3348 if (model_obj1->layer_height_profile.get() != model_obj2->layer_height_profile.get())
3349 return false;
3350 if (model_obj1->config.get() != model_obj2->config.get())
3351 return false;
3352 return true;
3353 };
3354 int object_count = m_objects.size();
3355 std::set<PrintObject*> need_slicing_objects;
3356 std::set<PrintObject*> re_slicing_objects;
3357 // Belt global modes couple each object's bed position into its layer Z values,
3358 // so sharing layers between "identical" objects is wrong.
3359 bool belt_no_share = m_config.belt_printer.value;
3360 if (!use_cache) {
3361 for (int index = 0; index < object_count; index++)
3362 {
3363 PrintObject *obj = m_objects[index];
3364 if (!belt_no_share) {
3365 for (PrintObject *slicing_obj : need_slicing_objects)
3366 {
3367 if (is_print_object_the_same(obj, slicing_obj)) {
3368 obj->set_shared_object(slicing_obj);
3369 break;
3370 }
3371 }
3372 }
3373 if (!obj->get_shared_object())
3374 need_slicing_objects.insert(obj);
3375 }
3376 }
3377 else {
3378 for (int index = 0; index < object_count; index++)
3379 {
3380 PrintObject *obj = m_objects[index];
3381 if (obj->layer_count() > 0)
3382 need_slicing_objects.insert(obj);
3383 }
3384 for (int index = 0; index < object_count; index++)
3385 {
3386 PrintObject *obj = m_objects[index];
3387 bool found_shared = false;
3388 if (need_slicing_objects.find(obj) == need_slicing_objects.end()) {
3389 if (!belt_no_share) {
3390 for (PrintObject *slicing_obj : need_slicing_objects)
3391 {
3392 if (is_print_object_the_same(obj, slicing_obj)) {
3393 obj->set_shared_object(slicing_obj);
3394 found_shared = true;
3395 break;
3396 }
3397 }
3398 }
3399 if (!found_shared) {
3400 BOOST_LOG_TRIVIAL(warning) << boost::format("Also can not find the shared object, identify_id %1%, maybe shared object is skipped")%obj->model_object()->instances[0]->loaded_id;
3401 //throw Slic3r::SlicingError("Cannot find the cached data.");
3402 //don't report errot, set use_cache to false, and reslice these objects
3403 need_slicing_objects.insert(obj);
3404 re_slicing_objects.insert(obj);
3405 //use_cache = false;
3406 }
3407 }
3408 }
3409 }
3410
3411 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": total object counts %1% in current print, need to slice %2%")%m_objects.size()%need_slicing_objects.size();
3412 BOOST_LOG_TRIVIAL(info) << "Starting the slicing process." << log_memory_info();
3413 if (!use_cache) {
3414 // Fire the SlicingPipeline hook for `obj` iff it just (re)computed `pstep` this pass.
3415 auto hook_after = [this](PrintObject* obj, bool was_done, PrintObjectStep pstep, SlicingPipelineStepPlugin sstep) {
3416 if (m_pipeline_plugin_active && !was_done && obj->is_step_done(pstep))
3417 run_pipeline_hook(sstep, obj);
3418 };
3419
3420 // SlicingPipeline: dedicated slice loop so the Slice boundary is hookable before perimeters.
3421 for (PrintObject *obj : m_objects) {
3422 if (need_slicing_objects.count(obj) != 0) {
3423 const bool was_done = obj->is_step_done(posSlice);
3424 obj->slice();
3425 hook_after(obj, was_done, posSlice, SlicingPipelineStepPlugin::posSlice);
3426 // re-snapshot each layer's raw_slices AFTER the Slice hook ran, so the
3427 // plugin's mutation becomes the untyped baseline. Without this, a later
3428 // perimeter-only re-run (make_perimeters -> restore_untyped_slices) reverts
3429 // slices to the PRE-hook geometry while posSlice stays cached (the hook does
3430 // not re-fire), silently un-applying the mutation; raw_slices consumers
3431 // (sharp-tail support, ToolOrdering) also read this backup directly. Gated on
3432 // an active plugin AND a genuine (re)slice, so the inactive path is untouched
3433 // and re-backing-up an unmutated layer is a harmless identical copy.
3434 if (m_pipeline_plugin_active && !was_done && obj->is_step_done(posSlice))
3435 for (Layer *layer : obj->layers())
3436 layer->backup_untyped_slices();
3437 } else {
3438 if (obj->set_started(posSlice)) obj->set_done(posSlice); // shared/duplicate — no hook
3439 }
3440 }
3441 for (PrintObject *obj : m_objects) {
3442 if (need_slicing_objects.count(obj) != 0) {
3443 const bool was_done = obj->is_step_done(posPerimeters);
3444 obj->make_perimeters(); // slice() inside is a no-op: posSlice already DONE
3445 hook_after(obj, was_done, posPerimeters, SlicingPipelineStepPlugin::posPerimeters);
3446 } else {
3447 if (obj->set_started(posPerimeters)) obj->set_done(posPerimeters);
3448 }
3449 }
3450 for (PrintObject *obj : m_objects) {
3451 if (need_slicing_objects.count(obj) != 0) {
3452 const bool was_done = obj->is_step_done(posEstimateCurledExtrusions);
3453 obj->estimate_curled_extrusions();
3455 }
3456 else {
3457 if (obj->set_started(posEstimateCurledExtrusions))
3458 obj->set_done(posEstimateCurledExtrusions);
3459 }
3460 }
3461 for (PrintObject *obj : m_objects) {
3462 if (need_slicing_objects.count(obj) != 0) {
3463 // split prepare_infill (fill-surface prep) from infill (make_fills) so a
3464 // plugin can mutate fill surfaces at the PrepareInfill seam and have make_fills
3465 // consume them (unlike the Infill seam, which fires after the fills are already
3466 // built). infill() re-invokes prepare_infill() as a no-op once posPrepareInfill
3467 // is DONE, so this is a mechanical split mirroring the slice/perimeters loop.
3468 const bool prepare_was_done = obj->is_step_done(posPrepareInfill);
3469 obj->prepare_infill();
3470 hook_after(obj, prepare_was_done, posPrepareInfill, SlicingPipelineStepPlugin::posPrepareInfill);
3471 const bool was_done = obj->is_step_done(posInfill);
3472 obj->infill();
3473 hook_after(obj, was_done, posInfill, SlicingPipelineStepPlugin::posInfill);
3474 }
3475 else {
3476 if (obj->set_started(posPrepareInfill))
3477 obj->set_done(posPrepareInfill);
3478 if (obj->set_started(posInfill))
3479 obj->set_done(posInfill);
3480 }
3481 }
3482 for (PrintObject *obj : m_objects) {
3483 if (need_slicing_objects.count(obj) != 0) {
3484 const bool was_done = obj->is_step_done(posIroning);
3485 obj->ironing();
3486 hook_after(obj, was_done, posIroning, SlicingPipelineStepPlugin::posIroning);
3487 }
3488 else {
3489 if (obj->set_started(posIroning))
3490 obj->set_done(posIroning);
3491 }
3492 }
3493
3494 // Z-Contouring
3495 for (PrintObject *obj : m_objects) {
3496 bool need_contouring = need_slicing_objects.count(obj) != 0 && obj->need_z_contouring();
3497 if (need_contouring) {
3498 const bool was_done = obj->is_step_done(posContouring);
3499 obj->contour_z();
3500 hook_after(obj, was_done, posContouring, SlicingPipelineStepPlugin::posContouring);
3501 } else {
3502 if (obj->set_started(posContouring))
3503 obj->set_done(posContouring);
3504 }
3505 }
3506
3507 // SlicingPipeline: support runs in the parallel block below; the hook must fire in a
3508 // sequential loop afterward. Snapshot per-object done-state just before the parallel_for.
3509 std::vector<char> sup_was_done(m_objects.size(), 1);
3511 for (size_t i = 0; i < m_objects.size(); ++i)
3512 sup_was_done[i] = m_objects[i]->is_step_done(posSupportMaterial) ? 1 : 0;
3513
3514 tbb::parallel_for(tbb::blocked_range<int>(0, int(m_objects.size())),
3515 [this, need_slicing_objects](const tbb::blocked_range<int>& range) {
3516 for (int i = range.begin(); i < range.end(); i++) {
3517 PrintObject* obj = m_objects[i];
3518 if (need_slicing_objects.count(obj) != 0) {
3519 // The belt brim follows sequentially below.
3520 obj->generate_support_material(false);
3521 }
3522 else {
3523 if (obj->set_started(posSupportMaterial))
3524 obj->set_done(posSupportMaterial);
3525 }
3526 }
3527 }
3528 );
3529 // The belt brim keeps clear of every object's layers and support layers,
3530 // so it runs once no support step is rebuilding them any more.
3531 for (PrintObject *obj : m_objects)
3532 obj->generate_belt_brim();
3533
3535 for (size_t i = 0; i < m_objects.size(); ++i)
3536 if (need_slicing_objects.count(m_objects[i]) != 0 && !sup_was_done[i]
3537 && m_objects[i]->is_step_done(posSupportMaterial))
3539
3540 for (PrintObject* obj : m_objects) {
3541 if (need_slicing_objects.count(obj) != 0) {
3542 const bool was_done = obj->is_step_done(posDetectOverhangsForLift);
3543 obj->detect_overhangs_for_lift();
3545 }
3546 else {
3547 if (obj->set_started(posDetectOverhangsForLift))
3548 obj->set_done(posDetectOverhangsForLift);
3549 }
3550 }
3551 }
3552 else {
3553 for (PrintObject *obj : m_objects) {
3554 if (re_slicing_objects.count(obj) == 0) {
3555 if (obj->set_started(posSlice))
3556 obj->set_done(posSlice);
3557 if (obj->set_started(posPerimeters))
3558 obj->set_done(posPerimeters);
3559 if (obj->set_started(posPrepareInfill))
3560 obj->set_done(posPrepareInfill);
3561 if (obj->set_started(posInfill))
3562 obj->set_done(posInfill);
3563 if (obj->set_started(posIroning))
3564 obj->set_done(posIroning);
3565 if (obj->set_started(posContouring))
3566 obj->set_done(posContouring);
3567 if (obj->set_started(posSupportMaterial))
3568 obj->set_done(posSupportMaterial);
3569 if (obj->set_started(posDetectOverhangsForLift))
3570 obj->set_done(posDetectOverhangsForLift);
3571 }
3572 else {
3573 obj->make_perimeters();
3574 obj->infill();
3575 obj->ironing();
3576 obj->generate_support_material();
3577 obj->detect_overhangs_for_lift();
3578 obj->estimate_curled_extrusions();
3579 }
3580 }
3581 }
3582
3583 for (PrintObject *obj : m_objects)
3584 {
3585 if (need_slicing_objects.count(obj) == 0) {
3586 obj->copy_layers_from_shared_object();
3587 obj->copy_layers_overhang_from_shared_object();
3588 }
3589 }
3590
3591
3592
3593 if (this->set_started(psWipeTower)) {
3594 {
3595 std::vector<std::set<int>> geometric_unprintables(m_config.nozzle_diameter.size());
3596 for (PrintObject* obj : m_objects) {
3597 std::vector<std::set<int>> obj_geometric_unprintables = obj->detect_extruder_geometric_unprintables();
3598 for (size_t idx = 0; idx < obj_geometric_unprintables.size(); ++idx) {
3599 if (idx < geometric_unprintables.size()) {
3600 geometric_unprintables[idx].insert(obj_geometric_unprintables[idx].begin(), obj_geometric_unprintables[idx].end());
3601 }
3602 }
3603 }
3604 this->set_geometric_unprintable_filaments(geometric_unprintables);
3605 }
3606
3607 m_wipe_tower_data.clear();
3608 m_tool_ordering.clear();
3610 this->_plan_belt_purge();
3611 }
3612 else if (this->has_wipe_tower()) {
3613 this->_make_wipe_tower();
3614 }
3615 else if (this->config().print_sequence != PrintSequence::ByObject) {
3616 // Initialize the tool ordering, so it could be used by the G-code preview slider for planning tool changes and filament switches.
3617 m_tool_ordering = ToolOrdering(*this, -1, false);
3618 m_tool_ordering.sort_and_build_data(*this, -1, false);
3619 if (m_tool_ordering.empty() || m_tool_ordering.last_extruder() == unsigned(-1))
3620 throw Slic3r::SlicingError(L("The print is empty. The model is not printable with current print settings."));
3621
3622 }
3623 this->set_done(psWipeTower);
3625 }
3626
3627 if (this->has_wipe_tower()) {
3628 m_fake_wipe_tower.set_pos({ m_config.wipe_tower_x.get_at(m_plate_index), m_config.wipe_tower_y.get_at(m_plate_index) });
3629 // Validated on every process() run rather than only when the wipe tower step is (re)generated.
3630 // Moving the tower changes only wipe_tower_x/y, which invalidates psSkirtBrim but not psWipeTower,
3631 // so a validate call living inside _make_wipe_tower would be skipped and keep using the stale
3632 // position, missing a fresh collision. The tower geometry (tool_changes) is stored in the local
3633 // frame and is position independent, so re-checking here with the current position is correct.
3635 }
3636
3637 if (this->set_started(psSkirtBrim)) {
3638 this->set_status(70, L("Generating skirt & brim"));
3639
3640 if (time_cost_with_cache)
3641 start_time = (long long)Slic3r::Utils::get_current_time_utc();
3642
3643 m_skirt.clear();
3644 m_skirt_brim_groups.clear();
3646 m_skirt_convex_hull.clear();
3648 m_first_layer_convex_hull.points.clear();
3649 for (PrintObject *object : m_objects) object->m_skirt.clear();
3650
3651 //BBS: get the objects' indices when GCodes are generated
3652 ToolOrdering tool_ordering;
3653 unsigned int initial_extruder_id = (unsigned int)-1;
3654 bool has_wipe_tower = false;
3655 std::vector<const PrintInstance*> print_object_instances_ordering;
3656 std::vector<const PrintInstance*>::const_iterator print_object_instance_sequential_active;
3657 std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> layers_to_print = GCode::collect_layers_to_print(*this);
3658 std::vector<unsigned int> printExtruders;
3659 // Per-object first-layer mixed-slot resolutions for the by-object remap below
3660 // (BBS reads them from m_sequential_print_data->object_tool_ordering_map).
3661 std::map<ObjectID, std::map<unsigned int, unsigned int>> seq_mixed_resolution;
3662 // Cleared on every process so a print-sequence or selector-mode change can never leave
3663 // stale object pointers behind; repopulated below only by the sequential selector path.
3666 // Order object instances for sequential print.
3667 print_object_instances_ordering = sort_object_instances_by_model_order(*this);
3668 // A mixed slot is virtual; only its components reach a nozzle. These per-object orderings
3669 // are unsorted (no resolve_mixed_filaments), so expand the slots here for the grouping, the
3670 // unprintable sets and the slice-used lists. Because the expansion happens here rather than
3671 // on the sorted orderings, the first-layer used set lists every component of a mixed slot,
3672 // not just the one layer 0 resolves to. No-op without mixed filaments.
3673 const auto &is_mixed = m_config.filament_is_mixed.values;
3674 const auto &comp_strs = m_config.filament_mixed_components.values;
3675 const bool has_mixed = has_any_mixed_filament(is_mixed);
3676 std::vector<unsigned int> first_layer_used_filaments;
3677 std::vector<std::vector<unsigned int>> all_filaments;
3678 for (print_object_instance_sequential_active = print_object_instances_ordering.begin(); print_object_instance_sequential_active != print_object_instances_ordering.end(); ++print_object_instance_sequential_active) {
3679 tool_ordering = ToolOrdering(*(*print_object_instance_sequential_active)->print_object, initial_extruder_id);
3680 for (size_t idx = 0; idx < tool_ordering.layer_tools().size(); ++idx) {
3681 auto layer_filament = tool_ordering.layer_tools()[idx].extruders;
3682 if (has_mixed)
3683 layer_filament = expand_mixed_filaments(layer_filament, is_mixed, comp_strs);
3684 all_filaments.emplace_back(layer_filament);
3685 if (idx == 0)
3686 first_layer_used_filaments.insert(first_layer_used_filaments.end(), layer_filament.begin(), layer_filament.end());
3687 }
3688 }
3689 sort_remove_duplicates(first_layer_used_filaments);
3690 auto used_filaments = collect_sorted_used_filaments(all_filaments);
3691 this->set_slice_used_filaments(first_layer_used_filaments,used_filaments);
3692
3693 auto physical_unprintables = this->get_physical_unprintable_filaments(used_filaments);
3694 auto geometric_unprintables = this->get_geometric_unprintable_filaments();
3695 if (has_mixed)
3696 expand_mixed_slots_in_unprintables(geometric_unprintables, is_mixed, comp_strs);
3697 auto filament_unprintable_volumes = this->get_filament_unprintable_flow(used_filaments);
3698 // Selector (per-layer regroup) prints skip the static grouping: their print-wide result
3699 // is stitched from the per-object plans after the ordering loop below.
3700 const bool dynamic_reorder = this->is_dynamic_group_reorder();
3701 if (!dynamic_reorder) {
3702 std::vector<int>filament_maps = this->get_filament_maps();
3703 auto map_mode = get_filament_map_mode();
3704 // Grouping returns a nozzle-aware result; the 1-based extruder map for the by-object
3705 // path is derived from it. It is computed in every static map mode (in manual modes it
3706 // mirrors the user's assignment) and published print-wide: GCode's per-nozzle
3707 // placeholder and config-index lookups read it via get_layered_nozzle_group_result(),
3708 // and without it sequential exports on multi-nozzle printers see an empty nozzle table
3709 // (e.g. nozzle_diameter_at_nozzle_id[]) and custom g-code fails to resolve.
3710 auto grouping_result = ToolOrdering::get_recommended_filament_maps(all_filaments, this, map_mode, physical_unprintables, geometric_unprintables, filament_unprintable_volumes);
3711 this->set_nozzle_group_result(std::make_shared<MultiNozzleUtils::LayeredNozzleGroupResult>(grouping_result));
3712 // Orca: the sequential write-back stays gated to auto modes. In manual modes the
3713 // config maps already carry the user's assignment (the per-object ToolOrdering below
3714 // consumes them directly), so a write-back would only re-store the pre-slice values;
3715 // keeping the gate avoids churning the config on every sequential manual slice.
3716 if (map_mode < FilamentMapMode::fmmManual) {
3717 auto derived_maps = grouping_result.get_extruder_map(false);
3718 if (!derived_maps.empty()) {
3719 filament_maps = derived_maps;
3720 // Write the maps back: used filaments adopt the engine's extruder/nozzle
3721 // choice, unused ones keep their config assignment.
3722 // Orca: the config maps are the merge base; fall back to a synthesized base
3723 // when no producer sized them to the filament count (CLI runs until the
3724 // per-filament synthesis lands there), where indexing per filament would
3725 // run out of bounds.
3726 std::vector<int> base_filament_map = m_config.filament_map.values;
3727 if (base_filament_map.size() != derived_maps.size())
3728 base_filament_map.assign(derived_maps.size(), 1);
3729 std::vector<int> base_volume_map = m_config.filament_volume_map.values;
3730 if (base_volume_map.size() != derived_maps.size())
3731 base_volume_map.assign(derived_maps.size(), (int)nvtStandard);
3732 update_filament_maps_to_config(FilamentGroupUtils::update_used_filament_values(base_filament_map, derived_maps, used_filaments),
3733 FilamentGroupUtils::update_used_filament_values(base_volume_map, grouping_result.get_volume_map(), used_filaments),
3734 grouping_result.get_nozzle_map());
3735 }
3736 }
3737 // check map valid both in auto and mannual mode
3738 std::transform(filament_maps.begin(), filament_maps.end(), filament_maps.begin(), [](int value) {return value - 1; });
3739 }
3740
3741 // print_object_instances_ordering = sort_object_instances_by_max_z(print);
3742 const PrintObject *prev_planned_object = nullptr;
3743 unsigned int seq_last_extruder = (unsigned int)-1;
3744 MultiNozzleUtils::NozzleStatusRecorder nozzle_status;
3745 std::vector<std::vector<int>> nozzle_map_per_layer;
3746 std::vector<std::vector<unsigned int>> stitched_layer_filaments;
3747 print_object_instance_sequential_active = print_object_instances_ordering.begin();
3748 std::vector<unsigned int> used_mixed_filaments;
3749 for (; print_object_instance_sequential_active != print_object_instances_ordering.end(); ++print_object_instance_sequential_active) {
3750 const PrintObject *print_object = (*print_object_instance_sequential_active)->print_object;
3751 if (dynamic_reorder) {
3752 if (print_object != prev_planned_object) {
3753 // Plan each unique object once, threading the physical nozzle occupancy and
3754 // the previous object's last filament into the next plan; repeated instances
3755 // of an object reuse the plan, mirroring the export loop's reuse.
3756 ToolOrdering ordering(*print_object, seq_last_extruder);
3757 ordering.set_nozzle_status(nozzle_status);
3758 ordering.sort_and_build_data(*print_object, seq_last_extruder);
3759 nozzle_status = ordering.get_nozzle_status();
3760 if (ordering.last_extruder() != static_cast<unsigned int>(-1))
3761 seq_last_extruder = ordering.last_extruder();
3762 const auto &object_maps = ordering.get_layered_nozzle_group_result().get_layer_filament_nozzle_maps();
3763 nozzle_map_per_layer.insert(nozzle_map_per_layer.end(), object_maps.begin(), object_maps.end());
3764 // Orca: the stitch input comes from the same orderings that produced the
3765 // per-layer maps — the collection loop above is per-instance and seeded -1,
3766 // so its layers are misaligned with these plans. layer_tools() of a sorted
3767 // ordering already carries the planned per-layer filament order.
3768 for (const auto &layer_tool : ordering.layer_tools())
3769 stitched_layer_filaments.emplace_back(layer_tool.extruders);
3770 m_sequential_dynamic_orderings[print_object] = std::move(ordering);
3771 prev_planned_object = print_object;
3772 }
3774 } else {
3775 tool_ordering = ToolOrdering(*print_object, initial_extruder_id);
3776 tool_ordering.sort_and_build_data(*print_object, initial_extruder_id);
3777 if (!tool_ordering.layer_tools().empty())
3778 seq_mixed_resolution[print_object->id()] = tool_ordering.layer_tools().front().mixed_filament_resolution;
3779 }
3780 // Only sorted orderings have run resolve_mixed_filaments, so only they know which
3781 // mixed slots actually print.
3782 append(used_mixed_filaments, tool_ordering.used_mixed_filaments());
3783 if ((initial_extruder_id = tool_ordering.first_extruder()) != static_cast<unsigned int>(-1)) {
3784 append(printExtruders, tool_ordering.tools_for_layer(layers_to_print.front().first).extruders);
3785 }
3786 }
3787 sort_remove_duplicates(used_mixed_filaments);
3788 this->set_slice_used_mixed_filaments(used_mixed_filaments);
3789 if (dynamic_reorder && m_objects.size() > 1) {
3790 // Stitch the per-object plans into one print-wide selector result. A single-object
3791 // sequential print publishes (and writes back) from its own ordering instead: the
3792 // per-object publish gate treats one object as not sequential.
3793 auto stitched = ToolOrdering::build_sequential_group_result(this, std::move(nozzle_map_per_layer), stitched_layer_filaments,
3794 stitched_layer_filaments, used_filaments, physical_unprintables,
3795 geometric_unprintables, filament_unprintable_volumes);
3796 this->set_nozzle_group_result(std::make_shared<MultiNozzleUtils::LayeredNozzleGroupResult>(stitched));
3798 }
3799 }
3800 else {
3801 tool_ordering = this->tool_ordering();
3802 tool_ordering.assign_custom_gcodes(*this);
3803
3804 std::vector<unsigned int> first_layer_used_filaments;
3805 if (!tool_ordering.layer_tools().empty())
3806 first_layer_used_filaments = tool_ordering.layer_tools().front().extruders;
3807
3808 this->set_slice_used_filaments(first_layer_used_filaments, tool_ordering.all_extruders());
3809 this->set_slice_used_mixed_filaments(tool_ordering.used_mixed_filaments());
3810 has_wipe_tower = this->has_wipe_tower() && tool_ordering.has_wipe_tower();
3811 initial_extruder_id = tool_ordering.first_extruder();
3812 print_object_instances_ordering = chain_print_object_instances(*this);
3813 append(printExtruders, tool_ordering.tools_for_layer(layers_to_print.front().first).extruders);
3814 }
3815
3816 auto objectExtruderMap = getObjectExtruderMap(*this);
3817 // Resolve mixed filament virtual slots to physical components so brim
3818 // extruder matching works correctly (mixed slot IDs are not present
3819 // in printExtruders after ToolOrdering::resolve_mixed_filaments).
3820 {
3821 const LayerTools *first_lt = nullptr;
3822 if (m_config.print_sequence != PrintSequence::ByObject && !tool_ordering.layer_tools().empty())
3823 first_lt = &tool_ordering.layer_tools().front();
3824 for (auto &[obj_id, ext_1based] : objectExtruderMap) {
3825 if (ext_1based == 0)
3826 continue;
3827 const std::map<unsigned int, unsigned int> *resolution = nullptr;
3828 if (first_lt)
3829 resolution = &first_lt->mixed_filament_resolution;
3830 else if (auto obj_it = seq_mixed_resolution.find(obj_id); obj_it != seq_mixed_resolution.end())
3831 resolution = &obj_it->second;
3832 if (resolution) {
3833 auto it = resolution->find(ext_1based - 1);
3834 if (it != resolution->end())
3835 ext_1based = it->second + 1;
3836 }
3837 }
3838 }
3839 std::vector<std::pair<ObjectID, unsigned int>> objPrintVec;
3840 for (const PrintInstance* instance : print_object_instances_ordering) {
3841 const ObjectID& print_object_ID = instance->print_object->id();
3842 bool existObject = false;
3843 for (auto& objIDPair : objPrintVec) {
3844 if (print_object_ID == objIDPair.first) existObject = true;
3845 }
3846 if (!existObject && objectExtruderMap.find(print_object_ID) != objectExtruderMap.end())
3847 objPrintVec.push_back(std::make_pair(print_object_ID, objectExtruderMap.at(print_object_ID)));
3848 }
3849 // Orca: Build both the old object-keyed brim map and the per-instance
3850 // maps used by skirt/brim groups.
3851 m_brimMap.clear();
3852 m_brimFilamentMap.clear();
3853 m_brimMapByInstance.clear();
3854 for (const auto& [object_id, filament] : objPrintVec)
3855 if (filament > 0)
3856 m_brimFilamentMap.emplace(object_id, filament - 1);
3857 m_first_layer_convex_hull.points.clear();
3858 if (this->has_brim()) {
3859 Polygons islands_area;
3860 make_brim(*this, this->make_try_cancel(), islands_area, m_brimMap,
3861 m_brimMapByInstance, objPrintVec, printExtruders,
3863 for (Polygon& poly_ex : islands_area)
3864 poly_ex.douglas_peucker(SCALED_RESOLUTION);
3865 for (Polygon &poly : union_(this->first_layer_islands(), islands_area))
3866 append(m_first_layer_convex_hull.points, std::move(poly.points));
3867 }
3868
3869
3870 // Belt brim: bound the first-layer convex hull by the lowest apron band, so
3871 // bed levelling and the initial purge line account for brim that reaches
3872 // ahead of every object.
3873 if (this->has_belt_brim()) {
3874 for (PrintObject *object : m_objects) {
3875 if (! object->has_belt_brim() || object->belt_brim_prologue().empty())
3876 continue;
3877 const BeltBrimBand &lowest = object->belt_brim_prologue().front();
3878 for (const PrintInstance &instance : object->instances())
3879 for (const ExPolygon &ex : lowest.areas) {
3880 Polygon poly = ex.contour;
3881 poly.translate(instance.shift);
3882 append(m_first_layer_convex_hull.points, std::move(poly.points));
3883 }
3884 }
3885 }
3886
3887 // Unchanged for belt printers: _make_skirt() already returns early for them, and
3888 // the belt brim does not populate m_brimMapByInstance, which is what the
3889 // skirt/brim grouping reads.
3890 if (has_skirt() || has_infinite_skirt() || has_brim()) {
3891 // Generate skirt/brim groups after brim so per-object and draft-shield footprints
3892 // include brims when grouping and offsetting skirt loops.
3893 assert(m_skirt.empty());
3894 _make_skirt();
3895 if (m_config.print_sequence == PrintSequence::ByObject &&
3896 m_config.skirt_type == stPerObject &&
3897 this->has_shared_per_object_skirt()) {
3898 throw Slic3r::SlicingError(L("Per-object skirts cannot fit between the objects in By object print sequence.\n\nMove the objects farther apart, reduce brim/skirt size, switch Skirt type to Combined, or switch Print sequence to By layer."));
3899 }
3900 }
3901
3903 this->set_done(psSkirtBrim);
3905
3906 if (time_cost_with_cache) {
3907 end_time = (long long)Slic3r::Utils::get_current_time_utc();
3908 *time_cost_with_cache = *time_cost_with_cache + end_time - start_time;
3909 }
3910 }
3911 //BBS
3912 for (PrintObject *obj : m_objects) {
3913 if (((!use_cache)&&(need_slicing_objects.count(obj) != 0))
3914 || (use_cache &&(re_slicing_objects.count(obj) != 0))){
3915 const bool was_done = obj->is_step_done(posSimplifyPath);
3916 obj->simplify_extrusion_path();
3917 // Unlike every other seam (all inside the `if (!use_cache)` block above), this loop is
3918 // shared with the use_cache path (re_slicing_objects), so `!use_cache` must be checked
3919 // explicitly here to keep hooks from ever firing on cache-loaded (plugin-final) objects.
3920 if (!use_cache && m_pipeline_plugin_active && !was_done && obj->is_step_done(posSimplifyPath))
3922 }
3923 else {
3924 if (obj->set_started(posSimplifyPath))
3925 obj->set_done(posSimplifyPath);
3926 if (obj->set_started(posSimplifyInfill))
3927 obj->set_done(posSimplifyInfill);
3928 if (obj->set_started(posSimplifySupportPath))
3929 obj->set_done(posSimplifySupportPath);
3930 }
3931 }
3932
3933 // BBS
3934 bool has_adaptive_layer_height = false;
3935 for (PrintObject* obj : m_objects) {
3936 if (obj->model_object()->layer_height_profile.empty() == false) {
3937 has_adaptive_layer_height = true;
3938 break;
3939 }
3940 }
3941 if(!m_no_check /*&& !has_adaptive_layer_height*/)
3942 {
3943 using Clock = std::chrono::high_resolution_clock;
3944 auto startTime = Clock::now();
3945 std::optional<const FakeWipeTower *> wipe_tower_opt = {};
3946 if (this->has_wipe_tower()) {
3947 m_fake_wipe_tower.set_pos({m_config.wipe_tower_x.get_at(m_plate_index), m_config.wipe_tower_y.get_at(m_plate_index)});
3948 wipe_tower_opt = std::make_optional<const FakeWipeTower *>(&m_fake_wipe_tower);
3949 }
3950 auto conflictRes = ConflictChecker::find_inter_of_lines_in_diff_objs(m_objects, wipe_tower_opt);
3951 auto endTime = Clock::now();
3952 volatile double seconds = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count() / (double) 1000;
3953 BOOST_LOG_TRIVIAL(info) << "gcode path conflicts check takes " << seconds << " secs.";
3954
3955 m_conflict_result = conflictRes;
3956 if (conflictRes.has_value()) {
3957 BOOST_LOG_TRIVIAL(error) << boost::format("gcode path conflicts found between %1% and %2%")%conflictRes.value()._objName1 %conflictRes.value()._objName2;
3958 }
3959 }
3960
3961 BOOST_LOG_TRIVIAL(info) << "Slicing process finished." << log_memory_info();
3962
3963 {
3964 LifecycleEventContext ctx;
3965 ctx.id = std::to_string(m_model.id().id);
3966 ctx.name = get_model_name();
3967 ctx.code = LifecycleEvtCode::Ok;
3968 ctx.cancellation_check = [this]() { return canceled(); };
3970 }
3971}
static std::vector< LayerToPrint > collect_layers_to_print(const PrintObject &object, bool skip_empty_first_layer=false)
Definition GCode.cpp:2286
PrintStateBase::TimeStamp set_done(PrintStep step)
Definition PrintBase.hpp:609
bool set_started(PrintStep step)
Definition PrintBase.hpp:608
PrintTryCancel make_try_cancel() const
Definition PrintBase.hpp:554
std::map< int, std::set< NozzleVolumeType > > get_filament_unprintable_flow(const std::vector< unsigned int > &used_filaments) const
Determines the forbidden nozzle volume types for each used filament.
Definition Print.cpp:4943
void update_filament_maps_to_config(std::vector< int > f_maps, std::vector< int > f_volume_maps=std::vector< int >{}, std::vector< int > f_nozzle_maps=std::vector< int >{})
Definition Print.cpp:4656
bool has_belt_brim() const
Definition Print.cpp:867
std::vector< std::set< int > > get_geometric_unprintable_filaments() const
Definition Print.hpp:1241
void update_to_config_by_nozzle_group_result(const MultiNozzleUtils::LayeredNozzleGroupResult &group_result)
Definition Print.cpp:4798
void _make_skirt()
Definition Print.cpp:4025
std::map< const PrintObject *, ToolOrdering > m_sequential_dynamic_orderings
Definition Print.hpp:1541
void set_geometric_unprintable_filaments(const std::vector< std::set< int > > &unprintables_filament_ids)
Definition Print.hpp:1240
void finalize_first_layer_convex_hull()
Definition Print.cpp:4644
const ToolOrdering & tool_ordering() const
Definition Print.hpp:1191
void run_pipeline_hook(SlicingPipelineStepPlugin step, const PrintObject *object)
Definition Print.hpp:1489
void set_slice_used_filaments(const std::vector< unsigned int > &first_layer_used_filaments, const std::vector< unsigned int > &used_filaments)
Definition Print.hpp:1243
static SlicingPipelineHookFn s_slicing_pipeline_hook_fn
Definition Print.hpp:1487
bool has_brim() const
Definition Print.cpp:849
void _plan_belt_purge()
Definition BeltPurge.cpp:146
FilamentMapMode get_filament_map_mode() const
Definition Print.cpp:4903
void set_slice_used_mixed_filaments(const std::vector< unsigned int > &used_mixed_filaments)
Definition Print.hpp:1248
bool has_belt_purge_tower() const
Definition BeltPurge.cpp:48
ToolOrdering m_tool_ordering
Definition Print.hpp:1556
bool is_dynamic_group_reorder() const
Definition Print.cpp:5051
void set_nozzle_group_result(std::shared_ptr< MultiNozzleUtils::NozzleGroupResultBase > result)
Definition Print.cpp:5036
std::vector< std::set< int > > get_physical_unprintable_filaments(const std::vector< unsigned int > &used_filaments) const
Determines the unprintable filaments for each extruder based on its physical attributes.
Definition Print.cpp:4908
bool m_pipeline_plugin_active
Definition Print.hpp:1488
void _make_wipe_tower()
Definition Print.cpp:5235
void validate_compacted_wipe_tower_clearance() const
Definition Print.cpp:1496
static MultiNozzleUtils::LayeredNozzleGroupResult build_sequential_group_result(Print *print, std::vector< std::vector< int > > nozzle_map_per_layer, const std::vector< std::vector< unsigned int > > &layer_filaments, const std::vector< std::vector< unsigned int > > &layer_sequences, const std::vector< unsigned int > &used_filaments, const std::vector< std::set< int > > &physical_unprintables, const std::vector< std::set< int > > &geometric_unprintables, const std::map< int, std::set< NozzleVolumeType > > &unprintable_volumes)
Definition ToolOrdering.cpp:2210
static MultiNozzleUtils::LayeredNozzleGroupResult get_recommended_filament_maps(const std::vector< std::vector< unsigned int > > &layer_filaments, const Print *print, const FilamentMapMode mode, const std::vector< std::set< int > > &physical_unprintables, const std::vector< std::set< int > > &geometric_unprintables, const std::map< int, std::set< NozzleVolumeType > > &unprintable_volumes={}, const std::unordered_map< int, int > &nozzle_status={})
Definition ToolOrdering.cpp:1752
std::vector< int > update_used_filament_values(const std::vector< int > &old_values, const std::vector< int > &new_values, const std::vector< unsigned int > &used_filaments)
Definition FilamentGroupUtils.cpp:346
time_t get_current_time_utc()
Definition Time.cpp:183
Slic3r::Polygons union_(const Slic3r::Polygons &subject)
Definition ClipperUtils.cpp:801
zaa_minimize_perimeter_height print_sequence((ConfigOptionEnum< PrintOrder >, print_order))((ConfigOptionInts
std::vector< const PrintInstance * > sort_object_instances_by_model_order(const Print &print, bool init_order)
Definition GCode.cpp:3285
std::vector< const PrintInstance * > chain_print_object_instances(const std::vector< const PrintObject * > &print_objects, const Point *start_near)
Definition ShortestPath.cpp:2533
void expand_mixed_slots_in_unprintables(std::vector< std::set< int > > &unprintables, const std::vector< unsigned char > &is_mixed, const std::vector< std::string > &comp_strs)
Definition FilamentMixer.cpp:595
auto range(Cont &&cont)
Definition libslic3r.h:405
zaa_minimize_perimeter_height resolution((ConfigOptionFloats, retraction_minimum_travel))((ConfigOptionBools
std::vector< unsigned int > collect_sorted_used_filaments(const std::vector< std::vector< unsigned int > > &layer_filaments)
Definition FilamentGroup.cpp:563
std::map< ObjectID, unsigned int > getObjectExtruderMap(const Print &print)
Definition Print.cpp:3237
std::string log_memory_info(bool ignore_loglevel)
Definition utils.cpp:1749
@ SliceGeometryFinished
Definition LifecycleEvents.hpp:30
@ SliceStarted
Definition LifecycleEvents.hpp:29
void name_tbb_thread_pool_threads_set_locale()
Definition Thread.cpp:225
@ posIroning
Definition Print.hpp:125
@ posContouring
Definition Print.hpp:125
@ posPrepareInfill
Definition Print.hpp:124
SlicingPipelineStepPlugin
Definition Print.hpp:132
@ posIroning
Definition Print.hpp:133
@ psWipeTower
Definition Print.hpp:134
@ posInfill
Definition Print.hpp:133
@ posDetectOverhangsForLift
Definition Print.hpp:134
@ posPerimeters
Definition Print.hpp:133
@ posEstimateCurledExtrusions
Definition Print.hpp:133
@ psSkirtBrim
Definition Print.hpp:134
@ posPrepareInfill
Definition Print.hpp:133
@ posSlice
Definition Print.hpp:133
@ posSimplifyPath
Definition Print.hpp:134
@ posSupportMaterial
Definition Print.hpp:134
@ posContouring
Definition Print.hpp:133
void make_brim(const Print &print, PrintTryCancel try_cancel, Polygons &islands_area, std::map< ObjectID, ExtrusionEntityCollection > &brimMap, std::map< ObjectInstanceID, ExtrusionEntityCollection > &brimMapByInstance, std::vector< std::pair< ObjectID, unsigned int > > &objPrintVec, std::vector< unsigned int > &printExtruders, std::map< ObjectInstanceID, ExPolygons > *objectBrimAreasByInstanceOut)
Definition Brim.cpp:929
static ConflictResultOpt find_inter_of_lines_in_diff_objs(PrintObjectPtrs objs, std::optional< const FakeWipeTower * > wtdptr)
Definition ConflictChecker.cpp:238

References Slic3r::PrintBaseWithState< PrintStep, psCount >::canceled(), Slic3r::LifecycleEventContext::cancellation_check, Slic3r::LifecycleEventContext::code, Slic3r::ModelObject::config, Slic3r::ModelVolume::config, config(), Slic3r::PrintObject::contour_z(), Slic3r::FacetsAnnotation::equals(), Slic3r::PrintObject::estimate_curled_extrusions(), Slic3r::ModelConfig::extruder(), Slic3r::fire_lifecycle_event(), Slic3r::ModelVolume::fuzzy_skin_facets, Slic3r::LayerHeightProfile::get(), Slic3r::ModelConfig::get(), get_model_name(), Slic3r::PrintObject::get_shared_object(), Slic3r::ModelVolume::get_transformation(), Slic3r::ModelConfig::has(), Slic3r::LifecycleEventContext::id, Slic3r::PrintObject::infill(), Slic3r::ModelObject::instances, Slic3r::PrintObject::ironing(), is_step_done(), Slic3r::PrintObjectBaseWithState< PrintType, PrintObjectStepEnum, COUNT >::is_step_done(), Slic3r::PrintObject::layer_count(), Slic3r::ModelObject::layer_height_profile, Slic3r::PrintObject::layers(), Slic3r::log_memory_info(), m_config, Slic3r::PrintBaseWithState< PrintStep, psCount >::m_model, m_objects, m_pipeline_plugin_active, Slic3r::PrintObject::make_perimeters(), Slic3r::ModelVolume::mesh_ptr(), Slic3r::ModelVolume::mmu_segmentation_facets, Slic3r::PrintObjectBase::model_object(), Slic3r::LifecycleEventContext::name, Slic3r::name_tbb_thread_pool_threads_set_locale(), Slic3r::PrintObject::need_z_contouring(), Slic3r::Ok, Slic3r::posContouring, Slic3r::posDetectOverhangsForLift, Slic3r::posEstimateCurledExtrusions, Slic3r::posInfill, Slic3r::posIroning, Slic3r::posPerimeters, Slic3r::posPrepareInfill, Slic3r::posSlice, Slic3r::posSupportMaterial, Slic3r::PrintObject::prepare_infill(), PrintObject, Slic3r::psWipeTower, Slic3r::range(), run_pipeline_hook(), s_slicing_pipeline_hook_fn, Slic3r::ModelVolume::seam_facets, Slic3r::PrintObjectBaseWithState< PrintType, PrintObjectStepEnum, COUNT >::set_done(), Slic3r::PrintObject::set_shared_object(), Slic3r::PrintObjectBaseWithState< PrintType, PrintObjectStepEnum, COUNT >::set_started(), Slic3r::PrintObject::slice(), Slic3r::SliceStarted, Slic3r::ModelVolume::supported_facets, Slic3r::PrintObject::trafo(), Slic3r::ModelVolume::type(), update_imex_slice_offset(), and Slic3r::ModelObject::volumes.

Referenced by Slic3r::GUI::CalibUtils::process_and_store_3mf(), and anonymous_namespace{OrcaSlicer_profile_validator.cpp}::slice_two_color_cube_and_export().

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

◆ reload_gcode_moves()

void Slic3r::Print::reload_gcode_moves ( GCodeProcessorResult * result) const
5772{
5773 GCodeProcessor processor;
5775 const Vec3d origin = this->get_plate_origin();
5776 processor.set_xy_offset(origin(0), origin(1));
5777 // Estimate the per-move times with the same nozzle-grouping slot context as the export.
5778 if (result->nozzle_group_result)
5779 processor.initialize_from_context(result->nozzle_group_result);
5780 try {
5781 processor.process_file(result->filename);
5782 } catch (const std::exception& ex) {
5783 // The edited file is what gets printed, so failing to preview it must not fail the slice.
5784 BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": cannot re-read the G-code file " << result->filename << ": " << ex.what();
5785 std::lock_guard<std::mutex> lock(result->result_mutex);
5786 result->lines_ends.clear();
5787 return;
5788 }
5789
5790 GCodeProcessorResult& reloaded = processor.result();
5791 std::lock_guard<std::mutex> lock(result->result_mutex);
5792 result->moves = std::move(reloaded.moves);
5793 result->lines_ends = std::move(reloaded.lines_ends);
5794}

References Slic3r::GCodeProcessorResult::filename, get_plate_origin(), Slic3r::GCodeProcessor::initialize_from_context(), is_BBL_printer(), Slic3r::GCodeProcessorResult::lines_ends, Slic3r::GCodeProcessorResult::moves, Slic3r::GCodeProcessorResult::nozzle_group_result, Slic3r::GCodeProcessor::process_file(), reload_gcode_moves(), Slic3r::GCodeProcessor::result(), Slic3r::GCodeProcessorResult::result_mutex, Slic3r::GCodeProcessor::s_IsBBLPrinter, and Slic3r::GCodeProcessor::set_xy_offset().

Referenced by reload_gcode_moves().

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

◆ restart()

void Slic3r::PrintBase::restart ( )
inlineinherited

References m_cancel_status, and NOT_CANCELED.

Referenced by PrintBase(), and Slic3r::GUI::GLGizmoFdmSupports::run_thread().

Here is the caller graph for this function:

◆ run_pipeline_hook()

void Slic3r::Print::run_pipeline_hook ( SlicingPipelineStepPlugin step,
const PrintObject * object )
inlineprivate
1489 {
1491 s_slicing_pipeline_hook_fn(*this, object, step);
1492 }

References m_pipeline_plugin_active, PrintObject, and s_slicing_pipeline_hook_fn.

Referenced by process().

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

◆ sequential_dynamic_orderings()

const std::map< const PrintObject *, ToolOrdering > & Slic3r::Print::sequential_dynamic_orderings ( ) const
inline

References m_sequential_dynamic_orderings.

Referenced by Slic3r::GCode::_do_export().

Here is the caller graph for this function:

◆ sequential_print_clearance_valid()

StringObjectException Slic3r::Print::sequential_print_clearance_valid ( const Print & print,
Polygons * polygons = nullptr,
std::vector< std::pair< Polygon, float > > * height_polygons = nullptr )
static
893{
894 StringObjectException single_object_exception;
895 const auto& print_config = print.config();
896 const Vec3d print_origin = print.get_plate_origin();
897 const std::vector<std::vector<BedExcludeRegion>> exclusion_volumes = translated_bed_exclusion_volumes_by_extruder(print);
898
899 struct print_instance_info
900 {
901 const PrintInstance *print_instance;
902 BoundingBox bounding_box;
903 Polygon hull_polygon;
904 int object_index;
905 double arrange_score;
906 double height;
907 };
908 auto find_object_index = [](const Model& model, const ModelObject* obj) {
909 for (int index = 0; index < model.objects.size(); index++)
910 {
911 if (model.objects[index] == obj)
912 return index;
913 }
914 return -1;
915 };
916
917 auto [object_skirt_offset, _] = print.object_skirt_offset();
918 std::vector<struct print_instance_info> print_instance_with_bounding_box;
919 {
920 // sequential_print_horizontal_clearance_valid
921 Polygons convex_hulls_other;
922 if (polygons != nullptr)
923 polygons->clear();
924 std::vector<size_t> intersecting_idxs;
925
926 // Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects
927 // exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
928 float obj_distance = print.is_all_objects_are_short() ? scale_(std::max(0.5f * MAX_OUTER_NOZZLE_DIAMETER, object_skirt_offset) - 0.1) : scale_(0.5 * print.config().extruder_clearance_radius.value + object_skirt_offset - 0.1);
929
930 for (const PrintObject *print_object : print.objects()) {
931 assert(! print_object->model_object()->instances.empty());
932 assert(! print_object->instances().empty());
933
934 // Orca: check convex hull intersection for each instance individually to handle rotation/offset differences correctly
935 // Now we check that no instance of convex_hull intersects any of the previously checked object instances.
936 for (const PrintInstance &instance : print_object->instances()) {
937 Polygon convex_hull0 = print_object->model_object()->convex_hull_2d(Geometry::assemble_transform(
938 { 0.0, 0.0, instance.model_instance->get_offset().z() }, instance.model_instance->get_rotation(), instance.model_instance->get_scaling_factor(), instance.model_instance->get_mirror()));
939
940 Polygon convex_hull_no_offset = convex_hull0, convex_hull;
941 auto tmp = offset(convex_hull_no_offset, obj_distance, jtRound, scale_(0.1));
942 if (!tmp.empty()) { // tmp may be empty due to clipper's bug, see STUDIO-2452
943 convex_hull = tmp.front();
944 // instance.shift is a position of a centered object, while model object may not be centered.
945 // Convert the shift from the PrintObject's coordinates into ModelObject's coordinates by removing the centering offset.
946 convex_hull.translate(instance.shift - print_object->center_offset());
947 }
948 const std::optional<BedExclusionCollision> exclusion_collision = colliding_bed_exclusion(
949 print, *print_object, *instance.model_instance, exclusion_volumes);
950 if (exclusion_collision.has_value()) {
951 const std::string collision_message = !exclusion_collision->collision_volume ?
952 (boost::format(L("%1% is too close to exclusion area. There may be collisions when printing.")) % instance.model_instance->get_object()->name).str() :
953 is_auto_filament_map_mode(print.get_filament_map_mode()) && print.is_BBL_printer() ?
954 // TRN %1% is the object name.
955 (boost::format(L("%1% intersects collision volumes for every available extruder.")) % instance.model_instance->get_object()->name).str() :
956 // TRN %1% is the object name, %2% the extruder number counted from 1.
957 (boost::format(L("%1% intersects a collision volume for extruder %2%.")) % instance.model_instance->get_object()->name % (exclusion_collision->extruder_id + 1)).str();
958 if (single_object_exception.string.empty()) {
959 single_object_exception.string = collision_message;
960 // single_object_exception.object = instance.model_instance->get_object();
961 //ORCA: Pass ModelInstance instead of ModelObject
962 single_object_exception.object = instance.model_instance;
963 }
964 else {
965 single_object_exception.string += "\n" + collision_message;
966 single_object_exception.object = nullptr;
967 }
968 }
969
970 // if output needed, collect indices (inside convex_hulls_other) of intersecting hulls
971 for (size_t i = 0; i < convex_hulls_other.size(); ++i) {
972 if (! intersection(convex_hulls_other[i], convex_hull).empty()) {
973 bool has_exception = false;
974 if (single_object_exception.string.empty()) {
975 single_object_exception.string = (boost::format(L("%1% is too close to others, and collisions may be caused.")) %instance.model_instance->get_object()->name).str();
976 // single_object_exception.object = instance.model_instance->get_object();
977 //ORCA: Pass ModelInstance instead of ModelObject for better selection
978 single_object_exception.object = instance.model_instance;
979 has_exception = true;
980 }
981 else {
982 single_object_exception.string += "\n"+(boost::format(L("%1% is too close to others, and collisions may be caused.")) %instance.model_instance->get_object()->name).str();
983 // single_object_exception.object = nullptr;
984 // ORCA: Keep the first object so jump works
985 // has_exception = true;
986 has_exception = true;
987 }
988
989 if (polygons) {
990 intersecting_idxs.emplace_back(i);
991 intersecting_idxs.emplace_back(convex_hulls_other.size());
992 }
993
994 if (has_exception) break;
995 }
996 }
997 struct print_instance_info print_info {&instance, convex_hull.bounding_box(), convex_hull};
998 print_info.height = instance.print_object->height();
999 print_info.object_index = find_object_index(print.model(), print_object->model_object());
1000 print_instance_with_bounding_box.push_back(std::move(print_info));
1001 convex_hulls_other.emplace_back(std::move(convex_hull));
1002 }
1003 }
1004 if (!intersecting_idxs.empty()) {
1005 // use collected indices (inside convex_hulls_other) to update output
1006 std::sort(intersecting_idxs.begin(), intersecting_idxs.end());
1007 intersecting_idxs.erase(std::unique(intersecting_idxs.begin(), intersecting_idxs.end()), intersecting_idxs.end());
1008 for (size_t i : intersecting_idxs) {
1009 polygons->emplace_back(std::move(convex_hulls_other[i]));
1010 }
1011 }
1012 }
1013
1014 // calc sort order
1015 double hc1 = scale_(print.config().extruder_clearance_height_to_lid); // height to lid
1016 double hc2 = scale_(print.config().extruder_clearance_height_to_rod); // height to rod
1017 double printable_height = scale_(print.config().printable_height);
1018
1019#if 0 //do not sort anymore, use the order in object list
1020 auto bed_points = get_bed_shape(print_config);
1021 float bed_width = bed_points[1].x() - bed_points[0].x();
1022 // 如果扩大以后的多边形的距离小于这个值,就需要严格保证从左到右的打印顺序,否则会撞工具头右侧
1023 float unsafe_dist = scale_(print_config.extruder_clearance_max_radius.value - print_config.extruder_clearance_radius.value);
1024 struct VecHash
1025 {
1026 size_t operator()(const Vec2i32 &n1) const
1027 {
1028 return std::hash<coord_t>()(int(n1(0) * 100 + 100)) + std::hash<coord_t>()(int(n1(1) * 100 + 100)) * 101;
1029 }
1030 };
1031 std::unordered_set<Vec2i32, VecHash> left_right_pair; // pairs in this vector must strictly obey the left-right order
1032 for (size_t i = 0; i < print_instance_with_bounding_box.size();i++) {
1033 auto &inst = print_instance_with_bounding_box[i];
1034 inst.index = i;
1035 Point pt = inst.bounding_box.center();
1036 inst.arrange_score = pt.x() / 2 + pt.y(); // we prefer print row-by-row, so cost on x-direction is smaller
1037 }
1038 for (size_t i = 0; i < print_instance_with_bounding_box.size(); i++) {
1039 auto &inst = print_instance_with_bounding_box[i];
1040 auto &l = print_instance_with_bounding_box[i];
1041 for (size_t j = 0; j < print_instance_with_bounding_box.size(); j++) {
1042 if (j != i) {
1043 auto &r = print_instance_with_bounding_box[j];
1044 auto ly1 = l.bounding_box.min.y();
1045 auto ly2 = l.bounding_box.max.y();
1046 auto ry1 = r.bounding_box.min.y();
1047 auto ry2 = r.bounding_box.max.y();
1048 auto lx1 = l.bounding_box.min.x();
1049 auto rx1 = r.bounding_box.min.x();
1050 auto lx2 = l.bounding_box.max.x();
1051 auto rx2 = r.bounding_box.max.x();
1052 auto inter_min = std::max(ly1, ry1);
1053 auto inter_max = std::min(ly2, ry2);
1054 auto inter_y = inter_max - inter_min;
1055
1056 // 如果y方向的重合超过轮廓的膨胀量,说明两个物体在一行,应该先打左边的物体,即先比较二者的x坐标。
1057 // If the overlap in the y direction exceeds the expansion of the contour, it means that the two objects are in a row and the object on the left should be hit first, that is, the x coordinates of the two should be compared first.
1058 if (inter_y > scale_(0.5 * print.config().extruder_clearance_radius.value)) {
1059 if (std::max(rx1 - lx2, lx1 - rx2) < unsafe_dist) {
1060 if (lx1 > rx1) {
1061 left_right_pair.insert({j, i});
1062 BOOST_LOG_TRIVIAL(debug) << "in-a-row, print_instance " << r.print_instance->model_instance->get_object()->name << "(" << r.arrange_score << ")"
1063 << " -> " << l.print_instance->model_instance->get_object()->name << "(" << l.arrange_score << ")";
1064 } else {
1065 left_right_pair.insert({i, j});
1066 BOOST_LOG_TRIVIAL(debug) << "in-a-row, print_instance " << l.print_instance->model_instance->get_object()->name << "(" << l.arrange_score << ")"
1067 << " -> " << r.print_instance->model_instance->get_object()->name << "(" << r.arrange_score << ")";
1068 }
1069 }
1070 }
1071 if (l.height > hc1 && r.height < hc1) {
1072 // 当前物体超过了顶盖高度,必须后打
1073 left_right_pair.insert({j, i});
1074 BOOST_LOG_TRIVIAL(debug) << "height>hc1, print_instance " << r.print_instance->model_instance->get_object()->name << "(" << r.arrange_score << ")"
1075 << " -> " << l.print_instance->model_instance->get_object()->name << "(" << l.arrange_score << ")";
1076 }
1077 else if (l.height > hc2 && l.height > r.height && l.arrange_score<r.arrange_score) {
1078 // 如果当前物体的高度超过滑杆,且比r高,就给它加一点代价,尽量让高的物体后打(只有物体高度超过滑杆时才有必要按高度来)
1079 if (l.arrange_score < r.arrange_score)
1080 l.arrange_score = r.arrange_score + 10;
1081 BOOST_LOG_TRIVIAL(debug) << "height>hc2, print_instance " << inst.print_instance->model_instance->get_object()->name
1082 << ", right=" << r.print_instance->model_instance->get_object()->name << ", l.score: " << l.arrange_score
1083 << ", r.score: " << r.arrange_score;
1084 }
1085 }
1086 }
1087 }
1088 // 多做几次代价传播,因为前一次有些值没有更新。
1089 // TODO 更好的办法是建立一颗树,一步到位。不过我暂时没精力搞,先就这样吧
1090 for (int k=0;k<5;k++)
1091 for (auto p : left_right_pair) {
1092 auto &l = print_instance_with_bounding_box[p(0)];
1093 auto &r = print_instance_with_bounding_box[p(1)];
1094 if(r.arrange_score<l.arrange_score)
1095 r.arrange_score = l.arrange_score + 10;
1096 }
1097
1098 BOOST_LOG_TRIVIAL(debug) << "bed width: " << unscale_(bed_width) << ", unsafe_dist:" << unscale_(unsafe_dist) << ", height_to_lid: " << unscale_(hc1) << ", height_to_rod:" << unscale_(hc2) << ", final dependency:";
1099 for (auto p : left_right_pair) {
1100 auto &l = print_instance_with_bounding_box[p(0)];
1101 auto &r = print_instance_with_bounding_box[p(1)];
1102 BOOST_LOG_TRIVIAL(debug) << "print_instance " << I18N::translate(l.print_instance->model_instance->get_object()->name) << "(" << l.arrange_score << ")"
1103 << " -> " << I18N::translate(r.print_instance->model_instance->get_object()->name) << "(" << r.arrange_score << ")";
1104 }
1105 // sort the print instance
1106 std::sort(print_instance_with_bounding_box.begin(), print_instance_with_bounding_box.end(),
1107 [](print_instance_info& l, print_instance_info& r) {return l.arrange_score < r.arrange_score;});
1108
1109 for (auto &inst : print_instance_with_bounding_box)
1110 BOOST_LOG_TRIVIAL(debug) << "after sorting print_instance " << inst.print_instance->model_instance->get_object()->name << ", score: " << inst.arrange_score
1111 << ", height:"<< inst.height;
1112#else
1113 // sort the print instance
1114 std::sort(print_instance_with_bounding_box.begin(), print_instance_with_bounding_box.end(),
1115 [](print_instance_info& l, print_instance_info& r) {return l.object_index < r.object_index;});
1116
1117 for (auto &inst : print_instance_with_bounding_box)
1118 BOOST_LOG_TRIVIAL(debug) << "after sorting print_instance " << inst.print_instance->model_instance->get_object()->name << ", object_index: " << inst.object_index
1119 << ", height:"<< inst.height;
1120
1121#endif
1122 // sequential_print_vertical_clearance_valid
1123 {
1124 // Ignore the last instance printed.
1125 //print_instance_with_bounding_box.pop_back();
1126 /*bool has_interlaced_objects = false;
1127 for (int k = 0; k < print_instance_count; k++)
1128 {
1129 auto inst = print_instance_with_bounding_box[k].print_instance;
1130 auto bbox = print_instance_with_bounding_box[k].bounding_box;
1131 auto iy1 = bbox.min.y();
1132 auto iy2 = bbox.max.y();
1133
1134 for (int i = 0; i < k; i++)
1135 {
1136 auto& p = print_instance_with_bounding_box[i].print_instance;
1137 auto bbox2 = print_instance_with_bounding_box[i].bounding_box;
1138 auto py1 = bbox2.min.y();
1139 auto py2 = bbox2.max.y();
1140 auto inter_min = std::max(iy1, py1); // min y of intersection
1141 auto inter_max = std::min(iy2, py2); // max y of intersection. length=max_y-min_y>0 means intersection exists
1142 if (inter_max - inter_min > 0) {
1143 has_interlaced_objects = true;
1144 break;
1145 }
1146 }
1147 if (has_interlaced_objects)
1148 break;
1149 }*/
1150
1151 // if objects are not overlapped on y-axis, they will not collide even if they are taller than extruder_clearance_height_to_rod
1152 int print_instance_count = print_instance_with_bounding_box.size();
1153 std::map<const PrintInstance*, std::pair<Polygon, float>> too_tall_instances;
1154 for (int k = 0; k < print_instance_count; k++)
1155 {
1156 auto inst = print_instance_with_bounding_box[k].print_instance;
1157 // 只需要考虑喷嘴到滑杆的偏移量,这个比整个工具头的碰撞半径要小得多
1158 // Only the offset from the nozzle to the slide bar needs to be considered, which is much smaller than the collision radius of the entire tool head.
1159 auto bbox = print_instance_with_bounding_box[k].bounding_box.inflated(-scale_(0.5 * print.config().extruder_clearance_radius.value + object_skirt_offset));
1160 auto iy1 = bbox.min.y();
1161 auto iy2 = bbox.max.y();
1162 (const_cast<ModelInstance*>(inst->model_instance))->arrange_order = k+1;
1163 double height = (k == (print_instance_count - 1))?printable_height:hc1;
1164 /*if (has_interlaced_objects) {
1165 if ((k < (print_instance_count - 1)) && (inst->print_object->height() > hc2)) {
1166 too_tall_instances[inst] = std::make_pair(print_instance_with_bounding_box[k].hull_polygon, unscaled<double>(hc2));
1167 }
1168 }
1169 else {
1170 if ((k < (print_instance_count - 1)) && (inst->print_object->height() > hc1)) {
1171 too_tall_instances[inst] = std::make_pair(print_instance_with_bounding_box[k].hull_polygon, unscaled<double>(hc1));
1172 }
1173 }*/
1174
1175 for (int i = k+1; i < print_instance_count; i++)
1176 {
1177 auto& p = print_instance_with_bounding_box[i].print_instance;
1178 auto bbox2 = print_instance_with_bounding_box[i].bounding_box;
1179 auto py1 = bbox2.min.y();
1180 auto py2 = bbox2.max.y();
1181 auto inter_min = std::max(iy1, py1); // min y of intersection
1182 auto inter_max = std::min(iy2, py2); // max y of intersection. length=max_y-min_y>0 means intersection exists
1183 if (inter_max - inter_min > 0) {
1184 height = hc2;
1185 break;
1186 }
1187 }
1188 if (height < inst->print_object->max_z())
1189 too_tall_instances[inst] = std::make_pair(print_instance_with_bounding_box[k].hull_polygon, unscaled<double>(height));
1190 }
1191
1192 if (too_tall_instances.size() > 0) {
1193 //return {, inst->model_instance->get_object()};
1194 for (auto& iter: too_tall_instances) {
1195 if (single_object_exception.string.empty()) {
1196 single_object_exception.string = (boost::format(L("%1% is too tall, and collisions will be caused.")) %iter.first->model_instance->get_object()->name).str();
1197 single_object_exception.object = iter.first->model_instance->get_object();
1198 }
1199 else {
1200 single_object_exception.string += "\n" + (boost::format(L("%1% is too tall, and collisions will be caused.")) %iter.first->model_instance->get_object()->name).str();
1201 single_object_exception.object = nullptr;
1202 }
1203 if (height_polygons)
1204 height_polygons->emplace_back(std::move(iter.second));
1205 }
1206 }
1207 }
1208
1209 return single_object_exception;
1210}
#define MAX_OUTER_NOZZLE_DIAMETER
Definition Print.hpp:72
#define unscale_(val)
Definition libslic3r.h:107
std::string translate(const std::string &s)
Definition I18N.hpp:18
zaa_minimize_perimeter_height printable_height((ConfigOptionFloatsNullable, extruder_printable_height))((ConfigOptionPoint
Points get_bed_shape(const DynamicPrintConfig &config, bool use_share)
Definition PrintConfig.cpp:13837
BoundingBoxf3 bounding_box(const CutAOI &cut, const CutMesh &mesh)
Definition CutSurface.cpp:2649

References Print(), _, Slic3r::Geometry::assemble_transform(), Slic3r::bounding_box(), Slic3r::colliding_bed_exclusion(), config(), Slic3r::Geometry::convex_hull(), empty(), Slic3r::get_bed_shape(), get_filament_map_mode(), get_plate_origin(), Slic3r::intersection(), is_all_objects_are_short(), Slic3r::is_auto_filament_map_mode(), is_BBL_printer(), Slic3r::jtRound, L, MAX_OUTER_NOZZLE_DIAMETER, Slic3r::PrintBase::model(), Slic3r::PrintBaseWithState< PrintStep, psCount >::model(), Slic3r::StringObjectException::object, object_skirt_offset(), objects(), Slic3r::offset(), Slic3r::printable_height(), PrintObject, scale_, Slic3r::StringObjectException::string, Slic3r::I18N::translate(), Slic3r::translated_bed_exclusion_volumes_by_extruder(), unscale_, and Slic3r::unscaled().

Referenced by validate().

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

◆ serialize()

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

References m_id.

◆ set_calib_params()

void Slic3r::Print::set_calib_params ( const Calib_Params & params)
658 {
659 m_calib_params = params;
660 m_calib_params.mode = params.mode;
661}

References m_calib_params, and Slic3r::Calib_Params::mode.

Referenced by Slic3r::GUI::CalibUtils::process_and_store_3mf().

Here is the caller graph for this function:

◆ set_cancel_callback()

void Slic3r::PrintBase::set_cancel_callback ( cancel_callback_type cancel_callback)
inlineinherited

References cancel_callback(), and m_cancel_callback.

Here is the call graph for this function:

◆ set_check_multi_filaments_compatibility()

void Slic3r::Print::set_check_multi_filaments_compatibility ( bool check)
inline
bool check(ModelVolumeType volume_type)
Definition GLGizmoEmboss.cpp:515

References m_need_check_multi_filaments_compatibility.

Referenced by Slic3r::CLI::run().

Here is the caller graph for this function:

◆ set_done()

PrintStateBase::TimeStamp Slic3r::PrintBaseWithState< PrintStep, COUNT >::set_done ( PrintStep step)
inlineprotectedinherited
609 {
611 if (status.second)
613 return status.first;
614 }

References m_state, Slic3r::PrintStateBase::NON_CRITICAL, Slic3r::PrintBase::state_mutex(), Slic3r::PrintBase::status_update_warnings(), and Slic3r::PrintBase::throw_if_canceled().

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

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

◆ set_extruder_filament_info()

void Slic3r::Print::set_extruder_filament_info ( const std::vector< std::vector< DynamicPrintConfig > > & filament_info)
inline
1238{ m_extruder_filament_info = filament_info; }

References m_extruder_filament_info.

Referenced by Slic3r::CLI::run().

Here is the caller graph for this function:

◆ set_gcode_file_invalidated()

void Slic3r::Print::set_gcode_file_invalidated ( )
5704{
5706 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": done");
5707}

References invalidate_step(), Slic3r::psGCodeExport, and set_gcode_file_invalidated().

Referenced by set_gcode_file_invalidated().

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

◆ set_gcode_file_ready()

void Slic3r::Print::set_gcode_file_ready ( )
5697{
5699 this->set_done(psGCodeExport);
5700 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": done");
5701}

References Slic3r::psGCodeExport, Slic3r::PrintBaseWithState< PrintStep, psCount >::set_done(), set_gcode_file_ready(), and Slic3r::PrintBaseWithState< PrintStep, psCount >::set_started().

Referenced by Slic3r::GUI::Plater::load_gcode(), and set_gcode_file_ready().

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

◆ set_geometric_unprintable_filaments()

void Slic3r::Print::set_geometric_unprintable_filaments ( const std::vector< std::set< int > > & unprintables_filament_ids)
inline
1240{ m_geometric_unprintable_filaments = unprintables_filament_ids; }

References m_geometric_unprintable_filaments.

◆ 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_no_check_flag()

void Slic3r::PrintBase::set_no_check_flag ( bool no_check)
inlineinherited
529{ m_no_check = no_check; }

References m_no_check.

Referenced by Slic3r::CLI::run().

Here is the caller graph for this function:

◆ set_nozzle_group_result()

void Slic3r::Print::set_nozzle_group_result ( std::shared_ptr< MultiNozzleUtils::NozzleGroupResultBase > result)
5037{
5038 m_nozzle_group_result = std::move(result);
5039 m_layered_nozzle_group_result = std::dynamic_pointer_cast<MultiNozzleUtils::LayeredNozzleGroupResult>(m_nozzle_group_result);
5041}

References m_config_index_generation, m_layered_nozzle_group_result, m_nozzle_group_result, and set_nozzle_group_result().

Referenced by set_nozzle_group_result().

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

◆ set_plate_index()

void Slic3r::PrintBase::set_plate_index ( int index)
inlineinherited
527{ m_plate_index = index; }

References m_plate_index.

◆ set_plate_name()

void Slic3r::PrintBase::set_plate_name ( const std::string & name)
inlineinherited
533{ m_plate_name = name; }

References m_plate_name.

◆ set_plate_origin()

void Slic3r::Print::set_plate_origin ( Vec3d origin)
inline
1294{ m_origin = origin; }

References m_origin.

◆ set_slice_used_filaments()

void Slic3r::Print::set_slice_used_filaments ( const std::vector< unsigned int > & first_layer_used_filaments,
const std::vector< unsigned int > & used_filaments )
inline
1243 {
1244 m_slice_used_filaments_first_layer = first_layer_used_filaments;
1245 m_slice_used_filaments = used_filaments;
1246 }

References m_slice_used_filaments, and m_slice_used_filaments_first_layer.

◆ set_slice_used_mixed_filaments()

void Slic3r::Print::set_slice_used_mixed_filaments ( const std::vector< unsigned int > & used_mixed_filaments)
inline
1248 {
1249 m_slice_used_mixed_filaments = used_mixed_filaments;
1250 }

References m_slice_used_mixed_filaments.

◆ set_slicing_pipeline_hook_fn()

void Slic3r::Print::set_slicing_pipeline_hook_fn ( SlicingPipelineHookFn fn)
inlinestatic
1064{ s_slicing_pipeline_hook_fn = std::move(fn); }

References s_slicing_pipeline_hook_fn.

Referenced by Slic3r::plugin_hooks::anonymous_namespace{PluginHooks.cpp}::install_slicing_pipeline_hook(), and Slic3r::plugin_hooks::uninstall().

Here is the caller graph for this function:

◆ set_started()

bool Slic3r::PrintBaseWithState< PrintStep, COUNT >::set_started ( PrintStep step)
inlineprotectedinherited
608{ return m_state.set_started(step, this->state_mutex(), [this](){ this->throw_if_canceled(); }); }

References m_state, Slic3r::PrintBase::state_mutex(), and Slic3r::PrintBase::throw_if_canceled().

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

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

◆ set_status()

void Slic3r::PrintBase::set_status ( int percent,
const std::string & message,
unsigned int flags = SlicingStatus::DEFAULT,
int warning_step = -1 ) const
inherited
124{
126 m_status_callback(SlicingStatus(percent, message, flags, warning_step));
127 else
128 BOOST_LOG_TRIVIAL(debug) <<boost::format("Percent %1%: %2%\n")%percent %message.c_str();
129}
status_callback_type m_status_callback
Definition PrintBase.hpp:574
Definition PrintBase.hpp:443

References m_status_callback.

Referenced by Slic3r::TreeSupport3D::generate_support_areas(), Slic3r::SLAPrint::StatusReporter::operator()(), Slic3r::GCode::process_layers(), and Slic3r::GUI::GLGizmoFdmSupports::run_thread().

Here is the caller graph for this function:

◆ set_status_callback()

void Slic3r::PrintBase::set_status_callback ( status_callback_type cb)
inlineinherited
488{ m_status_callback = cb; }

References m_status_callback.

Referenced by Slic3r::CLI::run().

Here is the caller graph for this function:

◆ set_status_default()

void Slic3r::PrintBase::set_status_default ( )
inlineinherited
484{ m_status_callback = nullptr; }

References m_status_callback.

◆ set_status_silent()

void Slic3r::PrintBase::set_status_silent ( )
inlineinherited
486{ m_status_callback = [](const SlicingStatus&){}; }

References m_status_callback.

Referenced by anonymous_namespace{OrcaSlicer_profile_validator.cpp}::slice_two_color_cube_and_export().

Here is the caller graph for this function:

◆ set_task()

virtual void Slic3r::PrintBase::set_task ( const TaskParams & params)
inlinevirtualinherited

Reimplemented in Slic3r::SLAPrint.

434{}

◆ shrinkage_compensation()

Vec3d Slic3r::Print::shrinkage_compensation ( ) const
inline
1398{ return m_shrinkage_compensation; }

References m_shrinkage_compensation.

Referenced by validate().

Here is the caller graph for this function:

◆ skirt()

const ExtrusionEntityCollection & Slic3r::Print::skirt ( ) const
inline
1170{ return m_skirt; }

References m_skirt.

Referenced by libvgcode::convert_brim_skirt_to_vertices(), and Slic3r::get_print_extrusions_extents().

Here is the caller graph for this function:

◆ skirt_brim_groups()

const std::vector< SkirtBrimGroup > & Slic3r::Print::skirt_brim_groups ( ) const
inline
1171{ return m_skirt_brim_groups; }

References m_skirt_brim_groups.

Referenced by Slic3r::GCode::_do_export(), Slic3r::find_skirt_brim_group_idx(), Slic3r::GCode::generate_object_brim(), and Slic3r::GCode::generate_object_skirt_group().

Here is the caller graph for this function:

◆ skirt_first_layer_height()

double Slic3r::Print::skirt_first_layer_height ( ) const
3059{
3060 return m_config.initial_layer_print_height.value;
3061}

References m_config.

Referenced by _make_skirt(), _make_wipe_tower(), and Slic3r::outer_inner_brim_area().

Here is the caller graph for this function:

◆ skirt_flow()

Flow Slic3r::Print::skirt_flow ( ) const
3085{
3086
3087 // Orca: fall back to m_config if no objects are present
3088 ConfigOptionFloatOrPercent width = m_config.initial_layer_line_width;
3089 if (width.value <= 0)
3090 width = m_objects.empty() ? m_config.initial_layer_line_width : m_objects.front()->config().line_width;
3091
3092 /* We currently use a random object's support material extruder.
3093 While this works for most cases, we should probably consider all of the support material
3094 extruders and take the one with, say, the smallest index;
3095 The same logic should be applied to the code that selects the extruder during G-code
3096 generation as well. */
3099 // Flow::new_from_config_width takes care of the percent to value substitution
3100 width,
3101 // ORCA: resolve the actual nozzle the support filament is printed with (dual-nozzle printers).
3102 (float)nozzle_diameter_for_filament(m_config, m_objects.empty() ? 0 : m_objects.front()->config().support_filament, this->is_BBL_printer()),
3103 (float)this->skirt_first_layer_height());
3104}

References Slic3r::frPerimeter, m_config, m_objects, Slic3r::Flow::new_from_config_width(), and Slic3r::nozzle_diameter_for_filament().

Referenced by _make_skirt(), Slic3r::GCode::generate_skirt(), and object_skirt_offset().

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

◆ slicing_errors_message()

std::string Slic3r::Print::slicing_errors_message ( const SlicingErrors & errors) const
1953{
1954 std::string message;
1955 for (const SlicingError &error : errors.errors_) {
1956 std::string object_name;
1957 for (const PrintObject *object : m_objects)
1958 if (object->id().id == error.objectId()) {
1959 object_name = object->model_object()->name;
1960 break;
1961 }
1962 if (!message.empty())
1963 message += "\n";
1964 message += object_name.empty() ? std::string(error.what()) : object_name + ": " + error.what();
1965 }
1966 return message;
1967}

References Slic3r::SlicingErrors::errors_, m_objects, Slic3r::SlicingError::objectId(), and PrintObject.

Referenced by Slic3r::CLI::run().

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

◆ state_mutex()

◆ statistics_by_extruder() [1/2]

StatisticsByExtruderCount & Slic3r::Print::statistics_by_extruder ( )
inline

◆ statistics_by_extruder() [2/2]

const StatisticsByExtruderCount Slic3r::Print::statistics_by_extruder ( ) const
inline

◆ status_update_warnings() [1/2]

void Slic3r::PrintBase::status_update_warnings ( int step,
PrintStateBase::WarningLevel warning_level,
const std::string & message,
const PrintObjectBase * print_object = nullptr,
PrintStateBase::SlicingNotificationType message_id = PrintStateBase::SlicingDefaultNotification )
protectedinherited
133{
134 if (this->m_status_callback) {
135 auto status = print_object ? SlicingStatus(*print_object, step, message, message_id, warning_level) : SlicingStatus(*this, step, message, message_id, warning_level);
136 m_status_callback(status);
137 }
138 else if (! message.empty())
139 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", Print warning: %1%\n")% message.c_str();
140}
void step(Coord &crd, const Dir d, const long n=1)
Definition MarchingSquares.hpp:213

References m_status_callback, and PrintObjectBase.

Referenced by Slic3r::PrintBaseWithState< PrintStep, psCount >::active_step_add_warning(), Slic3r::PrintBaseWithState< PrintStep, psCount >::set_done(), and Slic3r::PrintObjectBase::status_update_warnings().

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

◆ status_update_warnings() [2/2]

void Slic3r::PrintBase::status_update_warnings ( int step,
PrintStateBase::WarningLevel warning_level,
const std::string & message,
PrintObjectBase & object,
PrintStateBase::SlicingNotificationType message_id = PrintStateBase::SlicingDefaultNotification )
protectedinherited
145{
146 //BBS: add object it into slicing status
147 if (this->m_status_callback) {
148 m_status_callback(SlicingStatus(object, step, message, message_id, warning_level));
149 }
150 else if (!message.empty())
151 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", PrintObject warning: %1%\n")% message.c_str();
152}

References m_status_callback, and PrintObjectBase.

Here is the call graph for this function:

◆ step_state_with_timestamp()

PrintStateBase::StateWithTimeStamp Slic3r::PrintBaseWithState< PrintStep, COUNT >::step_state_with_timestamp ( PrintStep step) const
inlineinherited
595{ return m_state.state_with_timestamp(step, this->state_mutex()); }

References m_state, and Slic3r::PrintBase::state_mutex().

Here is the call graph for this function:

◆ step_state_with_warnings()

PrintStateBase::StateWithWarnings Slic3r::PrintBaseWithState< PrintStep, COUNT >::step_state_with_warnings ( PrintStep step) const
inlineinherited
596{ return m_state.state_with_warnings(step, this->state_mutex()); }

References m_state, and Slic3r::PrintBase::state_mutex().

Here is the call graph for this function:

◆ support_material_extruders()

std::vector< unsigned int > Slic3r::Print::support_material_extruders ( ) const
728{
729 std::vector<unsigned int> extruders;
730 bool support_uses_current_extruder = false;
731 // BBS
732 auto num_extruders = (unsigned int)m_config.filament_diameter.size();
733
734 for (PrintObject *object : m_objects) {
735 if (object->has_support_material() && append_support_extruders(object->config(), num_extruders, extruders))
736 support_uses_current_extruder = true;
737 }
738
739 if (support_uses_current_extruder)
740 // Add all object extruders to the support extruders as it is not know which one will be used to print supports.
742
744 return extruders;
745}
static bool append_support_extruders(const PrintObjectConfig &config, unsigned int num_extruders, std::vector< unsigned int > &extruders)
Definition Print.cpp:749

References Slic3r::append(), append_support_extruders(), extruders(), m_config, m_objects, object_extruders(), PrintObject, and Slic3r::sort_remove_duplicates().

Referenced by extruders().

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

◆ technology()

PrinterTechnology Slic3r::Print::technology ( ) const
inlineoverridevirtualnoexcept

Implements Slic3r::PrintBase.

1069{ return ptFFF; }
@ ptFFF
Definition Config.hpp:231

References Slic3r::ptFFF.

◆ throw_if_canceled()

void Slic3r::PrintBase::throw_if_canceled ( ) const
inlineprotectedinherited
552{ if (m_cancel_status.load(std::memory_order_acquire)) throw CanceledException(); }

References m_cancel_status.

Referenced by Slic3r::GCode::_do_export(), Slic3r::GCode::process_layers(), Slic3r::PrintBaseWithState< PrintStep, psCount >::set_done(), and Slic3r::PrintBaseWithState< PrintStep, psCount >::set_started().

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

◆ tool_ordering()

const ToolOrdering & Slic3r::Print::tool_ordering ( ) const
inline
1191{ return m_tool_ordering; }

References m_tool_ordering.

Referenced by Slic3r::GCode::_do_export(), and Slic3r::OozePrevention::pre_toolchange().

Here is the caller graph for this function:

◆ translate_to_print_space() [1/2]

Vec2d Slic3r::Print::translate_to_print_space ( const Point & point) const
4549 {
4550 return Vec2d(unscaled(point.x()) - m_origin(0) - m_imex_slice_offset.x(),
4551 unscaled(point.y()) - m_origin(1) - m_imex_slice_offset.y());
4552}

References m_imex_slice_offset, m_origin, translate_to_print_space(), and Slic3r::unscaled().

Here is the call graph for this function:

◆ translate_to_print_space() [2/2]

Vec2d Slic3r::Print::translate_to_print_space ( const Vec2d & point) const
4543 {
4544 //const BoundingBoxf bed_bbox(config().printable_area.values);
4545 return Vec2d(point(0) - m_origin(0) - m_imex_slice_offset.x(),
4546 point(1) - m_origin(1) - m_imex_slice_offset.y());
4547}

References m_imex_slice_offset, m_origin, and translate_to_print_space().

Referenced by Slic3r::GCode::_do_export(), Slic3r::polygon_to_string(), Slic3r::GCode::set_object_info(), translate_to_print_space(), and translate_to_print_space().

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

◆ update_filament_maps_to_config()

void Slic3r::Print::update_filament_maps_to_config ( std::vector< int > f_maps,
std::vector< int > f_volume_maps = std::vector<int>{},
std::vector< int > f_nozzle_maps = std::vector<int>{} )
4657{
4658 if ((m_config.filament_map.values != f_maps) || (m_config.filament_volume_map.values != f_volume_maps) || (m_config.filament_nozzle_map.values != f_nozzle_maps))
4659 {
4660 BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": filament maps changed after pre-slicing.");
4661 m_ori_full_print_config.option<ConfigOptionInts>("filament_map", true)->values = f_maps;
4662 m_config.filament_map.values = f_maps;
4663
4664 if (!f_volume_maps.empty()) {
4665 m_ori_full_print_config.option<ConfigOptionInts>("filament_volume_map", true)->values = f_volume_maps;
4666 m_config.filament_volume_map.values = f_volume_maps;
4667 }
4668 else {
4669 m_ori_full_print_config.option<ConfigOptionInts>("filament_volume_map", true)->values.resize(f_maps.size(), nvtStandard);
4670 m_config.filament_volume_map.values.resize(f_maps.size(), nvtStandard);
4671 }
4672
4673 if (!f_nozzle_maps.empty()) {
4674 m_ori_full_print_config.option<ConfigOptionInts>("filament_nozzle_map", true)->values = f_nozzle_maps;
4675 m_config.filament_nozzle_map.values = f_nozzle_maps;
4676 }
4677 }
4678
4679 {
4680 int extruder_count = 1, extruder_volume_type_count = 1;
4681 bool support_multi = m_ori_full_print_config.support_different_extruders(extruder_count);
4682 // Orca: resolve the filament variants wherever Print::apply does, a multi-variant filament
4683 // on a single-variant printer included.
4684 const bool expand_filaments = (extruder_count > 1) || support_multi || m_ori_full_print_config.has_multi_variant_filament();
4685 std::vector<std::vector<NozzleVolumeType>> nozzle_volume_types;
4686 extruder_volume_type_count = m_ori_full_print_config.get_extruder_nozzle_volume_count(extruder_count, nozzle_volume_types);
4687
4688 //filament_map_2
4689 // Orca: seed with 0-based extruder indices so the override keying below degenerates to the
4690 // plain per-extruder slot when the rebuild loop is skipped; the loop overwrites every
4691 // entry when it runs.
4692 m_config.filament_map_2.values = f_maps;
4693 for (auto& v : m_config.filament_map_2.values)
4694 --v;
4695 auto opt_extruder_type = dynamic_cast<const ConfigOptionEnumsGeneric*>(m_ori_full_print_config.option("extruder_type"));
4696 auto opt_nozzle_volume_type = dynamic_cast<const ConfigOptionEnumsGeneric*>(m_ori_full_print_config.option("nozzle_volume_type"));
4697 // Orca: the loop tolerates configs without the extruder options (unit tests, degenerate
4698 // presets); the backfill and the override are bounds-checked because the change block above
4699 // is skipped when the maps are unchanged, in which case the stored map may be shorter than
4700 // the filament count.
4701 auto* ori_volume_map = m_ori_full_print_config.option<ConfigOptionInts>("filament_volume_map", true);
4702 for (int index = 0; opt_extruder_type && opt_nozzle_volume_type && index < f_maps.size(); index++)
4703 {
4704 ExtruderType extruder_type = (ExtruderType)(opt_extruder_type->get_at(f_maps[index] - 1));
4705 NozzleVolumeType nozzle_volume_type = (NozzleVolumeType)(opt_nozzle_volume_type->get_at(f_maps[index] - 1));
4706 if (f_volume_maps.empty()) {
4707 // No per-filament map supplied: backfill from the extruder's own volume type.
4708 if (m_config.filament_volume_map.values.size() > index)
4709 m_config.filament_volume_map.values[index] = nozzle_volume_type;
4710 if (ori_volume_map->values.size() > index)
4711 ori_volume_map->values[index] = nozzle_volume_type;
4712 }
4713 else if ((extruder_volume_type_count > extruder_count) && (m_config.filament_volume_map.values.size() > index))
4714 nozzle_volume_type = (NozzleVolumeType)(m_config.filament_volume_map.values[index]);
4715 // Orca: when the process variant columns cannot be matched (degenerate
4716 // print_extruder_id), key the override by plain extruder index like the seeding
4717 // above instead of poisoning the map with -1.
4718 int slot_index = m_ori_full_print_config.get_index_for_extruder(f_maps[index], "print_extruder_id", extruder_type, nozzle_volume_type, "print_extruder_variant");
4719 m_config.filament_map_2.values[index] = slot_index >= 0 ? slot_index : f_maps[index] - 1;
4720 }
4721
4723 std::set<std::string> filament_keys = filament_options_with_variant;
4724 filament_keys.insert("filament_self_index");
4725 if (expand_filaments)
4726 m_full_print_config.update_values_to_printer_extruders_for_multiple_filaments(m_full_print_config, extruder_count, extruder_volume_type_count, filament_keys, "filament_self_index", "filament_extruder_variant");
4727
4728 const std::vector<std::string> &extruder_retract_keys = print_config_def.extruder_retract_keys();
4729 const std::string filament_prefix = "filament_";
4730 t_config_option_keys print_diff;
4731 DynamicPrintConfig filament_overrides;
4732 for (auto& opt_key: extruder_retract_keys)
4733 {
4734 const ConfigOption *opt_new_filament = m_full_print_config.option(filament_prefix + opt_key);
4735 const ConfigOption *opt_new_machine = m_full_print_config.option(opt_key);
4736 const ConfigOption *opt_old_machine = m_config.option(opt_key);
4737
4738 if (opt_new_filament)
4739 compute_filament_override_value(opt_key, opt_old_machine, opt_new_machine, opt_new_filament, m_full_print_config, print_diff, filament_overrides, m_config.filament_map_2.values);
4740 }
4741
4742 if (expand_filaments) {
4744 keys.push_back("filament_self_index");
4745 m_config.apply_only(m_full_print_config, keys, true);
4746 }
4747 if (!print_diff.empty()) {
4748 m_placeholder_parser.apply_config(filament_overrides);
4749 m_config.apply(filament_overrides);
4750 }
4751 }
4754}
void resize(size_t n, const ConfigOption *opt_default=nullptr) override
Definition Config.hpp:687
bool m_has_auto_filament_map_result
Definition Print.hpp:1579
void compute_filament_override_value(const std::string &opt_key, const ConfigOption *opt_old_machine, const ConfigOption *opt_new_machine, const ConfigOption *opt_new_filament, const DynamicPrintConfig &new_full_config, t_config_option_keys &diff_keys, DynamicPrintConfig &filament_overrides, std::vector< int > &f_map_indices)
Definition PrintConfig.cpp:12385

References Slic3r::compute_filament_override_value(), Slic3r::filament_options_with_variant, m_config, Slic3r::PrintBaseWithState< PrintStep, psCount >::m_full_print_config, m_has_auto_filament_map_result, Slic3r::PrintBaseWithState< PrintStep, psCount >::m_ori_full_print_config, Slic3r::PrintBaseWithState< PrintStep, psCount >::m_placeholder_parser, Slic3r::nozzle_volume_type(), Slic3r::nvtStandard, Slic3r::print_config_def, update_filament_maps_to_config(), and update_filament_self_index_cache().

Referenced by update_filament_maps_to_config(), and update_to_config_by_nozzle_group_result().

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

◆ update_filament_self_index_cache()

void Slic3r::Print::update_filament_self_index_cache ( )
private
5074{
5075 m_missing_nozzle_group_logged.clear(); // reset the per-slice get_config_index log dedupe
5077
5078 std::vector<int> values;
5079 if (m_full_print_config.has("filament_self_index")) {
5080 values = m_full_print_config.option<ConfigOptionInts>("filament_self_index")->values;
5081 } else if (m_ori_full_print_config.has("filament_self_index")) {
5082 values = m_ori_full_print_config.option<ConfigOptionInts>("filament_self_index")->values;
5083 } else {
5084 values = m_config.filament_self_index.values;
5085 }
5086
5087 size_t expected_size = m_config.filament_extruder_variant.values.size();
5088 m_filament_self_index.clear();
5089 if (expected_size == 0) {
5090 m_filament_index_map.clear();
5091 m_nozzle_index_map.clear();
5092 return;
5093 }
5094 m_filament_self_index.resize(expected_size, 1);
5095 if (!values.empty()) {
5096 for (size_t i = 0; i < expected_size; ++i) {
5097 int v = i < values.size() ? values[i] : 1;
5098 if (v <= 0)
5099 v = 1;
5100 m_filament_self_index[i] = v;
5101 }
5102 }
5103 m_filament_index_map.clear();
5104 m_nozzle_index_map.clear();
5105}

References m_config, m_config_index_generation, m_filament_index_map, m_filament_self_index, Slic3r::PrintBaseWithState< PrintStep, psCount >::m_full_print_config, m_missing_nozzle_group_logged, m_nozzle_index_map, Slic3r::PrintBaseWithState< PrintStep, psCount >::m_ori_full_print_config, and update_filament_self_index_cache().

Referenced by update_filament_maps_to_config(), update_filament_self_index_cache(), and update_to_config_by_nozzle_group_result().

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

◆ update_imex_slice_offset()

void Slic3r::Print::update_imex_slice_offset ( )
4523{
4524 m_imex_slice_offset = Vec2d::Zero();
4525 if (!m_config.is_imex.value || !m_config.imex_firmware_managed_zones.value || m_objects.empty())
4526 return;
4527
4528 // Already the resolved plate-or-process mode: the plate config overrides the process
4529 // preset's key on the way in, exactly as PartPlate::get_imex_mode() prefers the plate.
4530 const std::string& active_mode = m_objects.front()->config().imex_parallel_mode.value;
4531 const ImexZoneLayout layout = compute_imex_zone_layout(m_full_print_config, active_mode,
4532 std::string(),
4533 get_extents(m_config.printable_area.values));
4534 // Empty / Primary mode and an empty layout both come back as Vec2d::Zero() here.
4535 m_imex_slice_offset = compute_imex_slice_offset(true, active_mode, layout.primary_zone_box);
4536}
Vec2d compute_imex_slice_offset(bool firmware_managed, const std::string &parallel_mode, const std::optional< BoundingBoxf > &primary_zone_box)
Definition IMEXHelpers.cpp:634
ImexZoneLayout compute_imex_zone_layout(const DynamicPrintConfig &printer_cfg, const std::string &plate_mode, const std::string &process_mode, const BoundingBoxf &bed_extents)
Definition IMEXZones.cpp:17

References Slic3r::compute_imex_slice_offset(), Slic3r::compute_imex_zone_layout(), Slic3r::get_extents(), m_config, Slic3r::PrintBaseWithState< PrintStep, psCount >::m_full_print_config, m_imex_slice_offset, m_objects, Slic3r::ImexZoneLayout::primary_zone_box, and update_imex_slice_offset().

Referenced by export_gcode(), process(), and update_imex_slice_offset().

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

◆ update_object_placeholders()

void Slic3r::PrintBase::update_object_placeholders ( DynamicConfig & config,
const std::string & default_ext ) const
protectedinherited
38{
39 // get the first input file name
40 std::string input_file;
41 std::string first_object_name;
42 std::vector<std::string> v_scale;
43 int num_objects = 0;
44 int num_instances = 0;
45 for (const ModelObject *model_object : m_model.objects) {
46 ModelInstance *printable = nullptr;
47 for (ModelInstance *model_instance : model_object->instances)
48 if (model_instance->is_printable()) {
49 printable = model_instance;
50 ++ num_instances;
51 }
52 if (printable) {
53 ++ num_objects;
54 if (num_objects == 1)
55 first_object_name = model_object->name;
56 // CHECK_ME -> Is the following correct ?
57 v_scale.push_back("x:" + boost::lexical_cast<std::string>(printable->get_scaling_factor(X) * 100) +
58 "% y:" + boost::lexical_cast<std::string>(printable->get_scaling_factor(Y) * 100) +
59 "% z:" + boost::lexical_cast<std::string>(printable->get_scaling_factor(Z) * 100) + "%");
60 if (input_file.empty())
61 input_file = model_object->name.empty() ? model_object->input_file : model_object->name;
62 }
63 }
64
65 config.set_key_value("num_objects", new ConfigOptionInt(num_objects));
66 config.set_key_value("num_instances", new ConfigOptionInt(num_instances));
67
68 config.set_key_value("scale", new ConfigOptionStrings(v_scale));
69 config.set_key_value("first_object_name", new ConfigOptionString(first_object_name));
70 if (! input_file.empty()) {
71 // get basename with and without suffix
72 const std::string input_filename = boost::filesystem::path(input_file).filename().string();
73 const std::string input_filename_base = input_filename.substr(0, input_filename.find_last_of("."));
74 config.set_key_value("input_filename", new ConfigOptionString(input_filename_base + default_ext));
75 config.set_key_value("input_filename_base", new ConfigOptionString(input_filename_base));
76 }
77}
@ Y
Definition libslic3r.h:142
@ Z
Definition libslic3r.h:143
@ X
Definition libslic3r.h:141

References Slic3r::ConfigOptionString, Slic3r::ModelInstance::get_scaling_factor(), Slic3r::ModelObject::input_file, Slic3r::ModelObject::instances, Slic3r::ModelInstance::is_printable(), m_model, Slic3r::ModelObject::name, Slic3r::DynamicConfig::set_key_value(), Slic3r::X, Slic3r::Y, and Slic3r::Z.

Referenced by Slic3r::GCode::_do_export(), and output_filename().

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

◆ update_shrinkage_compensation()

void Slic3r::Print::update_shrinkage_compensation ( size_t num_extruders,
std::vector< int > & variant_index )
private
1267{
1268 const auto set_compensation = [this](unsigned int extruder) {
1269 // 100% from the user is the equivalent to 0 in orca.
1270 const double xy_compensation = 100.0 / m_config.filament_shrink.get_at(extruder);
1271 const double z_compensation = 100.0 / m_config.filament_shrinkage_compensation_z.get_at(extruder);
1272 m_shrinkage_compensation = { xy_compensation, xy_compensation, z_compensation };
1273 };
1274 const auto same_shrinkage = [this](unsigned int a, unsigned int b) {
1275 return m_config.filament_shrink.get_at(a) == m_config.filament_shrink.get_at(b) &&
1276 m_config.filament_shrinkage_compensation_z.get_at(a) == m_config.filament_shrinkage_compensation_z.get_at(b);
1277 };
1278 // Usually all filaments share the same shrinkage, then it does not matter which of them print.
1279 bool uniform = true;
1280 for (unsigned int extruder = 1; extruder < num_extruders && uniform; ++ extruder)
1281 uniform = same_shrinkage(0, extruder);
1282 if (uniform) {
1284 set_compensation(0);
1285 return;
1286 }
1287
1288 std::vector<unsigned int> extruders;
1289 const auto append_feature_extruders = [num_extruders, &extruders](const PrintRegionConfig &config) {
1290 for (int id : { config.outer_wall_filament_id.value, config.inner_wall_filament_id.value, config.sparse_infill_filament_id.value,
1291 config.internal_solid_filament_id.value, config.top_surface_filament_id.value, config.bottom_surface_filament_id.value }) {
1292 const unsigned int i = (unsigned int)std::max(0, id - 1);
1293 extruders.emplace_back(i >= num_extruders ? 0 : i);
1294 }
1295 };
1296 for (const ModelObject *model_object : m_model.objects) {
1297 if (std::none_of(model_object->instances.begin(), model_object->instances.end(), [](const ModelInstance *mi) { return mi->is_printable(); }))
1298 continue;
1299 std::vector<const ModelVolume*> modifiers;
1300 std::copy_if(model_object->volumes.begin(), model_object->volumes.end(), std::back_inserter(modifiers), [](const ModelVolume *mv) { return mv->is_modifier(); });
1301 for (const ModelVolume *part : model_object->volumes) {
1302 if (! part->is_model_part())
1303 continue;
1304 std::vector<PrintRegionConfig> parents { region_config_from_model_volume(m_default_region_config, nullptr, *part, num_extruders, variant_index) };
1305 for (const auto &[range, range_config] : model_object->layer_config_ranges)
1306 parents.emplace_back(region_config_from_model_volume(m_default_region_config, &range_config.get(), *part, num_extruders, variant_index));
1307 for (const PrintRegionConfig &parent : parents) {
1308 append_feature_extruders(parent);
1309 for (const ModelVolume *modifier : modifiers)
1310 append_feature_extruders(region_config_from_model_volume(parent, nullptr, *modifier, num_extruders, variant_index));
1311 }
1312 }
1314 const PrintObjectConfig object_config = PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_extruders, variant_index);
1315 if (PrintObject::has_support_material(object_config))
1316 append_support_extruders(object_config, (unsigned int)num_extruders, extruders);
1317 }
1319 // A mixed slot prints with its physical components.
1320 const auto expand_and_sort = [this, &extruders]() {
1321 extruders = expand_mixed_filaments(extruders, m_config.filament_is_mixed.values, m_config.filament_mixed_components.values);
1322 };
1323 expand_and_sort();
1324 // The wipe tower filament may also be flushed into the objects.
1326 expand_and_sort();
1327
1329 std::all_of(extruders.begin(), extruders.end(), [&](unsigned int extruder) { return same_shrinkage(extruders.front(), extruder); });
1330 m_shrinkage_compensation = Vec3d::Ones();
1332 set_compensation(extruders.front());
1333}
bool has_support_material() const
Definition Print.hpp:511
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

References append_model_object_extruders(), append_support_extruders(), append_tool_change_extruders(), append_wipe_tower_extruder(), config(), Slic3r::expand_mixed_filaments(), extruders(), Slic3r::PrintObject::has_support_material(), Slic3r::ModelObject::instances, Slic3r::ModelVolume::is_model_part(), Slic3r::ModelObject::layer_config_ranges, m_config, m_default_object_config, m_default_region_config, Slic3r::PrintBaseWithState< PrintStep, psCount >::m_model, m_same_shrinkage_compensations, m_shrinkage_compensation, Slic3r::PrintObject::object_config_from_model_object(), Slic3r::PrintObjectConfig, Slic3r::PrintRegionConfig, Slic3r::range(), Slic3r::region_config_from_model_volume(), update_shrinkage_compensation(), and Slic3r::ModelObject::volumes.

Referenced by update_shrinkage_compensation().

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

◆ update_to_config_by_nozzle_group_result()

void Slic3r::Print::update_to_config_by_nozzle_group_result ( const MultiNozzleUtils::LayeredNozzleGroupResult & group_result)
4799{
4800 std::vector<int> derived_maps = group_result.get_extruder_map(false); // 1-based
4801 if (derived_maps.empty())
4802 return;
4803
4804 if (!group_result.is_support_dynamic_nozzle_map()) {
4805 // No filament actually migrated between nozzles, so the plan reduces to a single
4806 // grouping: write all maps like the static paths do, and the next apply re-derives
4807 // identical slots from the written maps.
4808 std::vector<int> base_filament_map = m_config.filament_map.values;
4809 if (base_filament_map.size() != derived_maps.size())
4810 base_filament_map.assign(derived_maps.size(), 1);
4811 std::vector<int> base_volume_map = m_config.filament_volume_map.values;
4812 if (base_volume_map.size() != derived_maps.size())
4813 base_volume_map.assign(derived_maps.size(), (int)nvtStandard);
4814 const std::vector<unsigned int> used_filaments = group_result.get_used_filaments();
4815 update_filament_maps_to_config(FilamentGroupUtils::update_used_filament_values(base_filament_map, derived_maps, used_filaments),
4816 FilamentGroupUtils::update_used_filament_values(base_volume_map, group_result.get_volume_map(), used_filaments),
4817 group_result.get_nozzle_map());
4818 return;
4819 }
4820
4821 // Orca: keep the coarse per-filament extruder map published even though the per-layer truth
4822 // lives in the grouping result: the pre-export consumers, the plate read-back after slicing
4823 // and the preview panel all key on filament_map. The write is direct — the full map
4824 // write-back's single-slot rebuild would undo the per-variant expansion below.
4825 m_ori_full_print_config.option<ConfigOptionInts>("filament_map", true)->values = derived_maps;
4826 m_config.filament_map.values = derived_maps;
4827
4828 std::unordered_map<int, std::vector<FilamentVariantUse>> filament_variant_uses;
4829 if (!collect_filament_variant_uses(group_result, m_ori_full_print_config, filament_variant_uses)) {
4830 // Degenerate config (no filament/extruder typing): fall back to the single-slot
4831 // write-back so the maps and overrides stay coherent.
4832 update_filament_maps_to_config(derived_maps);
4833 return;
4834 }
4835
4836 int extruder_count = 1, extruder_volume_type_count = 1;
4837 m_ori_full_print_config.support_different_extruders(extruder_count);
4838 std::vector<std::vector<NozzleVolumeType>> nozzle_volume_types;
4839 extruder_volume_type_count = m_ori_full_print_config.get_extruder_nozzle_volume_count(extruder_count, nozzle_volume_types);
4840
4841 // Note: filament_map_2 keeps its apply-time (static) derivation here; the per-slot machine
4842 // indices below key the override merge instead, so nothing on this path reads it. Its other
4843 // consumers are the three-map write-back (which recomputes it) and the diagnostic copy in
4844 // the g-code header; the time estimator resolves per-(extruder x volume-type) machine limits
4845 // from the live nozzle occupancy instead (see GCodeProcessor::get_machine_config_idx).
4847 std::set<std::string> filament_keys = filament_options_with_variant;
4848 filament_keys.insert("filament_self_index");
4849 std::vector<int> slot_machine_indices;
4850 m_full_print_config.update_filament_config_values_for_multiple_extruders(m_full_print_config, filament_variant_uses,
4851 extruder_count, extruder_volume_type_count,
4852 filament_keys, "filament_self_index", "filament_extruder_variant",
4853 &slot_machine_indices);
4854
4855 const std::vector<std::string> &extruder_retract_keys = print_config_def.extruder_retract_keys();
4856 const std::string filament_prefix = "filament_";
4857 t_config_option_keys print_diff;
4858 DynamicPrintConfig filament_overrides;
4859 for (auto& opt_key: extruder_retract_keys)
4860 {
4861 const ConfigOption *opt_new_filament = m_full_print_config.option(filament_prefix + opt_key);
4862 const ConfigOption *opt_new_machine = m_full_print_config.option(opt_key);
4863 const ConfigOption *opt_old_machine = m_config.option(opt_key);
4864
4865 if (opt_new_filament)
4866 compute_filament_override_value(opt_key, opt_old_machine, opt_new_machine, opt_new_filament, m_full_print_config, print_diff, filament_overrides, slot_machine_indices);
4867 }
4868
4869 {
4871 keys.push_back("filament_self_index");
4872 m_config.apply_only(m_full_print_config, keys, true);
4873 }
4874 if (!print_diff.empty()) {
4875 m_placeholder_parser.apply_config(filament_overrides);
4876 m_config.apply(filament_overrides);
4877 }
4878
4881}

References collect_filament_variant_uses(), Slic3r::compute_filament_override_value(), Slic3r::filament_options_with_variant, Slic3r::MultiNozzleUtils::LayeredNozzleGroupResult::get_extruder_map(), Slic3r::MultiNozzleUtils::LayeredNozzleGroupResult::get_nozzle_map(), Slic3r::MultiNozzleUtils::LayeredNozzleGroupResult::get_used_filaments(), Slic3r::MultiNozzleUtils::LayeredNozzleGroupResult::get_volume_map(), Slic3r::MultiNozzleUtils::NozzleGroupResultBase::is_support_dynamic_nozzle_map(), m_config, Slic3r::PrintBaseWithState< PrintStep, psCount >::m_full_print_config, m_has_auto_filament_map_result, Slic3r::PrintBaseWithState< PrintStep, psCount >::m_ori_full_print_config, Slic3r::PrintBaseWithState< PrintStep, psCount >::m_placeholder_parser, Slic3r::nvtStandard, Slic3r::print_config_def, update_filament_maps_to_config(), update_filament_self_index_cache(), update_to_config_by_nozzle_group_result(), and Slic3r::FilamentGroupUtils::update_used_filament_values().

Referenced by update_to_config_by_nozzle_group_result().

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

◆ validate()

StringObjectException Slic3r::Print::validate ( std::vector< StringObjectException > * warnings = nullptr,
Polygons * collison_polygons = nullptr,
std::vector< std::pair< Polygon, float > > * height_polygons = nullptr ) const
overridevirtual

Reimplemented from Slic3r::PrintBase.

1970{
1971 auto add_warning = [warnings](StringObjectException w) {
1972 w.is_warning = true;
1973 if (warnings != nullptr)
1974 warnings->push_back(std::move(w));
1975 };
1976 auto warn = [&](std::string msg, std::string opt_key = "", const ObjectBase* object = nullptr) {
1977 StringObjectException w;
1978 w.string = std::move(msg);
1979 w.opt_key = std::move(opt_key);
1980 w.object = object;
1981 add_warning(std::move(w));
1982 };
1983
1984 std::vector<unsigned int> extruders = this->extruders();
1985 unsigned int nozzles = m_config.nozzle_diameter.size();
1986
1987 if (m_objects.empty())
1988 return {std::string()};
1989
1990 if (extruders.empty())
1991 return { L("No extrusions under current settings.") };
1992
1993 // A flow ratio of 0 would extrude nothing, and a nil one would write NaN extrusion lengths.
1994 {
1995 const std::vector<double> &flow_ratios = m_config.filament_flow_ratio.values;
1996 const std::vector<int> &self_index = m_config.filament_self_index.values;
1997 for (size_t column = 0; column < flow_ratios.size(); ++column) {
1998 // filament_self_index maps a column to its filament once the filament arrays hold one column per variant
1999 const unsigned int filament_id = self_index.size() == flow_ratios.size() ? self_index[column] - 1 : column;
2000 if (flow_ratios[column] > 0. || !std::binary_search(extruders.begin(), extruders.end(), filament_id))
2001 continue;
2002 StringObjectException err;
2003 // TRN %1% is the filament number as shown in the filament list, starting from 1.
2004 err.string = Slic3r::format(L("The flow ratio of filament %1% must be greater than 0."), filament_id + 1);
2005 err.opt_key = "filament_flow_ratio";
2006 return err;
2007 }
2008 }
2009
2010 // IDEX/IQEX: block multi-color in non-primary parallel modes when the active
2011 // configuration can't physically support it (no within-gantry toolchange path
2012 // or MMU lane sharing). See imex_multicolor_block_reason() for the full rule.
2013 // Both IMEX checks below need the active mode's tools string and its declared primary,
2014 // so imex_resolve_routing() derives them once for the whole block -- and is the same call
2015 // the plater's pre-slice warning makes, so the two cannot describe the plate differently.
2016 // Note this block is NOT gated on extruders.size() > 1: the primary
2017 // routing check applies to a single-filament plate too, which is its most common case.
2018 if (m_config.is_imex.value && !m_objects.empty()) {
2019 const std::string& parallel_mode = m_objects.front()->config().imex_parallel_mode.value;
2020 if (!parallel_mode.empty() && parallel_mode != kImexPrimaryMode) {
2021 // Tool changes added from the layer slider switch heads mid-print as a painted color
2022 // does, so they count here, as the plate's warning badge counts them.
2023 const std::vector<unsigned int> imex_extruders = this->extruders(/*conside_custom_gcode=*/true);
2024 std::vector<int> used_filaments_0b;
2025 std::vector<int> used_slots_1b;
2026 used_filaments_0b.reserve(imex_extruders.size());
2027 used_slots_1b.reserve(imex_extruders.size());
2028 for (unsigned int e : imex_extruders) {
2029 used_filaments_0b.push_back((int)e);
2030 used_slots_1b.push_back((int)e + 1);
2031 }
2032
2033 // The active mode's tools string, its declared primary, and whether this plate's
2034 // filaments reach that primary are all derived ONCE, here. The plater's pre-slice
2035 // warning (collect_imex_warnings) makes the identical call, so the sentence it
2036 // shows before slicing and the refusal below cannot describe the plate
2037 // differently. An unresolved mode, and a mode the tools array is too short to
2038 // cover, both yield an empty roster and no primary.
2039 const ImexRouting routing = imex_resolve_routing(m_config, parallel_mode, used_slots_1b,
2040 m_config.physical_extruder_map);
2041
2042 // A mixed filament is blended at the nozzle by its component toolheads, and a
2043 // parallel mode is already using those toolheads to print copies or mirrors, so
2044 // the two cannot run at once -- regardless of where the components are routed.
2045 //
2046 // Runs before BOTH rules below. Against the routing rule it would otherwise be
2047 // reported as merely unrouted: mixed slots are kept at the tail of the filament
2048 // arrays, past the physical filament count, so on a printer whose logical extruder
2049 // count equals that count they fall outside physical_extruder_map and resolve to no
2050 // head. Against the multi-color rule, a mixed slot plus any second filament trips
2051 // the >1 gate and earns a lecture about gantry topology the user never configured.
2052 // Being unsupported outright, this dominates both.
2053 {
2054 const auto& is_mixed = m_config.filament_is_mixed.values;
2055 if (std::any_of(used_filaments_0b.begin(), used_filaments_0b.end(),
2056 [&](int slot) { return slot >= 0 && slot < (int) is_mixed.size() && is_mixed[slot]; })) {
2057 StringObjectException err;
2058 // "Mixed filament" is the term the rest of the UI uses -- the button that
2059 // creates one, and the sibling refusal for the wipe tower filament.
2060 err.string = L("Mixed filaments are not supported in IDEX/IQEX parallel modes. "
2061 "Switch this plate to Primary mode.");
2062 err.object = m_objects.front();
2063 return err;
2064 }
2065 }
2066
2067 // Multi-color has the more specific rule and its remedies are self-contained
2068 // (an MMU manifold sharing one head cannot be fixed by switching mode), so it runs
2069 // ahead of the routing block below, which would otherwise mask it with advice that
2070 // leads to this error on the next slice. validate() returns on the first error.
2071 if (used_filaments_0b.size() > 1) {
2072 const std::string reason = imex_multicolor_block_reason(
2073 parallel_mode,
2074 routing.active_tools,
2075 m_config.imex_tools_per_gantry.value,
2076 used_filaments_0b,
2077 m_config.physical_extruder_map);
2078 if (!reason.empty())
2079 return { reason };
2080 }
2081
2082 // The IMEX Primary tool prints the sliced paths directly, so it can only use a
2083 // filament the printer's physical_extruder_map routes to it. The ghost filament
2084 // picker already enforces this for the secondary tools; the primary's filament
2085 // comes from the ordinary object filament selector, which has no IMEX awareness,
2086 // so nothing detected the mismatch. collect_imex_warnings() reads the SAME
2087 // routing.primary_logical off the same derivation, and discards it into a display
2088 // fallback so its warning still has a filament to name.
2089 //
2090 // Blocks rather than warns, matching the multi-color rule above -- but on intent,
2091 // not on physics, and the distinction matters to anyone tempted to relax it. The
2092 // emitter does not use the declared primary: it re-derives an effective one from
2093 // the filament actually in use (GCode.cpp's initial_extruder_id -> pem lookups), so
2094 // a plate whose only filament sits on a Span tool sharing the primary's gantry does
2095 // produce coherent G-code. It is refused anyway. A parallel mode exists to run
2096 // carriages in parallel; a single-colour plate riding one span lane is not that, and
2097 // silently accepting it would make the mode's declared roster meaningless.
2098 //
2099 // Where the routed head is absent from the mode's active tools the plate is broken
2100 // outright, not merely off-intent: the 1st->2nd layer temperature
2101 // branch (GCode.cpp, mutually exclusive with the standard path) skips it too, so
2102 // that head holds nozzle_temperature_initial_layer for the whole job. First-layer
2103 // temperatures are unaffected -- _print_first_layer_extruder_temperatures is not
2104 // IMEX-branched -- so this is a stuck-hot nozzle, not a cold one.
2105 //
2106 // `primary_unrouted` is the whole condition: a declared primary, a populated
2107 // routing map, and nothing used reaching it. Blended slots would also read as
2108 // unrouted -- they sit past the map by construction -- but they never get here,
2109 // the mixed-filament rule above returns first.
2110 if (routing.primary_unrouted) {
2111 std::string routed_list;
2112 for (int head : routing.routed_heads)
2113 routed_list += (routed_list.empty() ? "T" : ", T") + std::to_string(head);
2114 // Print::apply() normalises physical_extruder_map through
2115 // effective_physical_extruder_map (PrintApply.cpp) before validate() runs, so
2116 // an unauthored map arrives here as the identity and every slot is in range.
2117 // What is left is a printer with more filaments than logical extruders, where
2118 // the tail slots fall outside the map. Narrow, but without this the sentence
2119 // ends in a dangling "on .".
2120 StringObjectException err;
2121 if (routed_list.empty())
2122 err.string = Slic3r::format(
2123 // TRN %1% is the IDEX/IQEX mode name, %2% the tool number.
2124 L("IDEX/IQEX mode \"%1%\" prints with T%2%, but none of this plate's filaments are on a configured tool. "
2125 "Assign a filament loaded on T%2%, or switch this plate to Primary mode."),
2126 parallel_mode, routing.primary_phys);
2127 else
2128 err.string = Slic3r::format(
2129 // TRN %1% is the IDEX/IQEX mode name, %2% the tool number, %3% a list of tools such as "T1, T2".
2130 L("IDEX/IQEX mode \"%1%\" prints with T%2%, but this plate's filaments are on %3%. "
2131 "Assign a filament loaded on T%2%, or switch this plate to Primary mode."),
2132 parallel_mode, routing.primary_phys, routed_list);
2133 // Gives the notification a "Jump to <object>" link, which selects the object
2134 // and switches to Prepare -- directly enabling the first suggested remedy.
2135 err.object = m_objects.front();
2136 return err;
2137 }
2138 }
2139 }
2140
2141 // Belt printer validation: incompatible features.
2142 if (m_config.belt_printer.value) {
2143 for (const PrintObject *object : m_objects) {
2144 if (object->config().raft_layers > 0)
2145 return { L("Raft is not compatible with belt printer mode.") };
2146 }
2147 if (m_config.draft_shield != dsDisabled)
2148 return { L("Draft shield is not compatible with belt printer mode.") };
2149
2150 // Belt brim spans many layers and owns the layers below the object, which
2151 // spiral vase cannot share. The prime tower setting is no obstacle: belt
2152 // printers never print the classic tower, and the belt purge prism is an
2153 // ordinary object that never takes a brim.
2154 if (this->has_belt_brim()) {
2155 if (m_config.spiral_mode.value)
2156 return { L("Brim is not compatible with spiral vase mode on a belt printer. "
2157 "Disable one of them.") };
2158 }
2159
2160 for (const PrintObject *object : m_objects) {
2161 const PrintObjectConfig &ocfg = object->config();
2162 // Mirror PrintObject::has_belt_brim(): an inner-only brim needs a positive
2163 // brim_width (leading/extra widen only the outer ring), so keep this
2164 // predicate in step or the belt-brim warnings below would fire for a brim
2165 // that has_belt_brim() rejects.
2166 const bool wants_brim = ocfg.brim_type != btNoBrim
2167 && (ocfg.brim_type == btInnerOnly
2168 ? ocfg.brim_width.value > 0.
2169 : (ocfg.brim_width.value > 0. || ocfg.leading_brim_length.value > 0.
2170 || ocfg.extra_brim_width.value > 0.));
2171 if (! wants_brim)
2172 continue;
2173
2174 if (! this->has_tilted_belt()) {
2175 if (std::abs(m_config.belt_slice_rotation_angle.value) > BELT_BRIM_MAX_TILT_DEG)
2176 warn(L("The belt is too steep for a brim, so no brim will be generated."),
2177 "brim_width", object->model_object());
2178 else
2179 warn(L("A brim is only generated when the belt is tilted. Set a belt tilt angle, "
2180 "or remove the brim setting."),
2181 "brim_type", object->model_object());
2182 }
2183
2184 if (ocfg.brim_type == btAutoBrim || ocfg.brim_type == btEar || ocfg.brim_type == btPainted)
2185 warn(L("Belt printers support outer and inner brim only. Auto, Mouse ear and Painted "
2186 "brim are printed as Outer brim only, using Brim width."),
2187 "brim_type", object->model_object());
2188
2189 if (ocfg.leading_brim_length.value > 0. && ocfg.brim_object_gap.value > 0.)
2190 warn(L("Brim-object gap separates the leading brim from the object's leading edge, "
2191 "which is the edge it is meant to anchor. Set the gap to 0 when using leading "
2192 "brim length."),
2193 "brim_object_gap", object->model_object());
2194 }
2195 if (this->has_belt_brim() && m_objects.size() > 1)
2196 warn(L("Leading brim length extends ahead of each object along the belt, and Arrange does "
2197 "not reserve that space. Leave room between objects."),
2198 "leading_brim_length");
2199 } else {
2200 // "Leading edge only" describes where a part meets a moving belt, so it has no
2201 // meaning on a fixed bed. Brim.cpp prints it as an ordinary outer brim rather
2202 // than silently producing nothing; say so.
2203 for (const PrintObject *object : m_objects)
2204 if (object->config().brim_type == btLeadingEdgeOnly)
2205 warn(L("\"Leading edge only\" brim applies to belt printers. On this printer it is "
2206 "printed as an ordinary outer brim."),
2207 "brim_type", object->model_object());
2208 }
2209
2210 // Orca: a gradient mixed filament only renders its gradient with "Mixed color sublayer" on;
2211 // without it ToolOrdering::resolve_mixed_filaments prints one whole component per layer and
2212 // the gradient is dropped silently. extruders() already covers painting, height ranges,
2213 // per-feature filament ids and supports, and still lists mixed slots under their own id here.
2214 if (!m_config.enable_mixed_color_sublayer.value) {
2215 const auto &is_mixed = m_config.filament_is_mixed.values;
2216 const auto &gradient = m_config.filament_mixed_gradient.values;
2217 if (std::any_of(extruders.begin(), extruders.end(), [&](unsigned int e) {
2218 return e < is_mixed.size() && is_mixed[e] && e < gradient.size() && gradient[e]; }))
2219 warn(L("A gradient mixed filament is used, but 'Mixed color sublayer' is disabled. The gradient will not be printed."),
2220 "enable_mixed_color_sublayer");
2221 }
2222
2223 if (nozzles < 2 && extruders.size() > 1) {
2224 auto ret = check_multi_filament_valid(*this);
2225 if (!ret.string.empty())
2226 {
2228 if (ret.is_warning) {
2229 add_warning(ret);
2230 }else
2231 return ret;
2232 }
2233 }
2234
2235 if (m_config.print_sequence == PrintSequence::ByObject && (m_objects.size() > 1 || m_objects[0]->instances().size() > 1)) {
2236 if (m_config.timelapse_type == TimelapseType::tlSmooth)
2237 return {L("Smooth mode of timelapse is not supported when \"by object\" sequence is enabled.")};
2238
2239 if (m_config.enable_wrapping_detection) {
2240 StringObjectException clumping_detection_setting_err;
2241 clumping_detection_setting_err.string = L("Clumping detection is not supported when \"by object\" sequence is enabled.");
2242 clumping_detection_setting_err.opt_key = "enable_wrapping_detection";
2243 return clumping_detection_setting_err;
2244 }
2245
2246 //BBS: refine seq-print validation logic
2247 auto ret = sequential_print_clearance_valid(*this, collison_polygons, height_polygons);
2248 if (!ret.string.empty()) {
2250 return ret;
2251 }
2252 }
2253 else {
2254 //BBS
2255 StringObjectException layer_warning;
2256 auto ret = layered_print_cleareance_valid(*this, &layer_warning);
2257 if (!ret.string.empty()) {
2259 return ret;
2260 }
2261 if (!layer_warning.string.empty())
2262 add_warning(layer_warning);
2263
2264 // Orca: a compacted prime tower drags the nozzle back down to the plate on every toolchange, so
2265 // tall objects collide with it much like they do in sequential printing. Checking it here rather
2266 // than only during slicing is what lets the plater show the collision area and the height limit
2267 // while the plate is still being arranged.
2268 ret = compacted_wipe_tower_clearance_valid(*this, collison_polygons, height_polygons);
2269 if (!ret.string.empty()) {
2271 return ret;
2272 }
2273 }
2274
2275 if (m_config.belt_printer.value && m_config.enable_belt_purge_tower.value
2276 && m_config.print_sequence == PrintSequence::ByObject
2277 && extruders.size() > 1) {
2278 StringObjectException warningtemp;
2279 warningtemp.string = L("The belt purge tower is not generated in \"By object\" print sequence; "
2280 "filament changes will not be purged.");
2281 warningtemp.opt_key = "enable_belt_purge_tower";
2282 warningtemp.is_warning = true;
2283 add_warning(warningtemp);
2284 }
2285
2286 // The purge tower is a model object the GUI creates and sizes; libslic3r only purges
2287 // into one that exists. A project sliced without it (the CLI on a project saved before
2288 // the tower was generated) changes filament with nowhere to purge.
2289 if (m_config.belt_printer.value && m_config.enable_belt_purge_tower.value
2290 && m_config.print_sequence != PrintSequence::ByObject
2291 && ! m_config.spiral_mode.value && this->object_extruders().size() > 1 && ! this->has_belt_purge_tower()) {
2292 StringObjectException warningtemp;
2293 warningtemp.string = L("The belt purge tower is enabled but the project has no purge tower object; "
2294 "filament changes will not be purged. Open the project in the application "
2295 "to generate the tower.");
2296 warningtemp.opt_key = "enable_belt_purge_tower";
2297 warningtemp.is_warning = true;
2298 add_warning(warningtemp);
2299 }
2300
2301 if (m_config.belt_printer.value && m_config.enable_belt_purge_tower.value) {
2302 const size_t prism_count = std::count_if(m_objects.begin(), m_objects.end(), [](const PrintObject *object) {
2303 return object->config().belt_purge_tower_object.value;
2304 });
2305 if (prism_count > 1)
2306 return {L("The project contains multiple managed belt purge towers. Reload the plate or toggle the belt purge tower off and on to regenerate it.")};
2307 }
2308
2309 if (m_config.enable_prime_tower) {
2310 for (const PrintObject* object : m_objects) {
2311 if (object->config().precise_z_height.value) {
2312 warn(L("Enabling both precise Z height and the prime tower may cause slicing errors."), "precise_z_height");
2313 break;
2314 }
2315 }
2316 } else {
2317 if (m_config.enable_wrapping_detection)
2318 warn(L("A prime tower is required for clumping detection; otherwise, there may be flaws on the model."), "enable_prime_tower");
2319 }
2320
2321 if (m_config.spiral_mode) {
2322 size_t total_copies_count = 0;
2323 for (const PrintObject* object : m_objects)
2324 total_copies_count += object->instances().size();
2325 // #4043
2326 if (total_copies_count > 1 && m_config.print_sequence != PrintSequence::ByObject)
2327 return {L("Please select \"By object\" print sequence to print multiple objects in spiral vase mode."), nullptr, "spiral_mode"};
2328 // A mixed (virtual) filament always resolves to multiple physical components, which
2329 // spiral vase cannot print.
2330 const auto &is_mixed = m_config.filament_is_mixed.values;
2331 for (const PrintObject *object : m_objects)
2332 for (unsigned int ext : object->object_extruders())
2333 if (ext < is_mixed.size() && is_mixed[ext])
2334 return {L("Spiral (vase) mode does not work when an object contains more than one material."), nullptr, "spiral_mode"};
2335 assert(m_objects.size() == 1);
2336 const auto all_regions = m_objects.front()->all_regions();
2337 if (all_regions.size() > 1) {
2338 // Orca: make sure regions are not compatible
2339 if (std::any_of(all_regions.begin() + 1, all_regions.end(), [this, ra = all_regions.front()](const auto rb) {
2340 return !Layer::is_perimeter_compatible(*this, ra, rb);
2341 })) {
2342 return {L("Spiral (vase) mode does not work when an object contains more than one material."), nullptr, "spiral_mode"};
2343 }
2344 }
2345 }
2346
2347 // Cache of layer height profiles for checking:
2348 // 1) Whether all layers are synchronized if printing with wipe tower and / or unsynchronized supports.
2349 // 2) Whether layer height is constant for Organic supports.
2350 // 3) Whether build volume Z is not violated.
2351 std::vector<std::vector<coordf_t>> layer_height_profiles;
2352 auto layer_height_profile = [this, &layer_height_profiles](const size_t print_object_idx) -> const std::vector<coordf_t>& {
2353 const PrintObject &print_object = *m_objects[print_object_idx];
2354 if (layer_height_profiles.empty())
2355 layer_height_profiles.assign(m_objects.size(), std::vector<coordf_t>());
2356 std::vector<coordf_t> &profile = layer_height_profiles[print_object_idx];
2357 if (profile.empty())
2358 PrintObject::update_layer_height_profile(*print_object.model_object(), print_object.slicing_parameters(), profile);
2359 return profile;
2360 };
2361
2362 // Checks that the print does not exceed the max print height.
2363 // For belt printers the slicing-frame Z spans the sheared X-length and
2364 // is not comparable to printable_height (which is gantry clearance in the
2365 // build-volume frame). Compare against the model's pre-shear Z instead,
2366 // mirroring the bbox computed in PrintObject::update_slicing_parameters.
2367 // The machine-frame transform only changes how that height is written to
2368 // G-code, not how much room there is under the gantry.
2369 const bool belt_printer = this->config().belt_printer.value;
2370 const double shrinkage_compensation_z = this->shrinkage_compensation().z();
2371 for (size_t print_object_idx = 0; print_object_idx < m_objects.size(); ++ print_object_idx) {
2372 const PrintObject &print_object = *m_objects[print_object_idx];
2373
2374 double effective_max_z = 0;
2375 bool last_layer_below_max = false;
2376 bool have_height = false;
2377
2378 if (belt_printer) {
2379 const double raw_z = print_object.model_object()->max_z();
2380 effective_max_z = raw_z;
2381 have_height = raw_z > 0;
2382 } else {
2383 //FIXME It is quite expensive to generate object layers just to get the print height!
2384 auto layers = generate_object_layers(print_object.slicing_parameters(), layer_height_profile(print_object_idx), print_object.config().precise_z_height.value);
2385 if (!layers.empty()) {
2386 // The object layers start above the raft.
2387 const double raft_z = print_object.slicing_parameters().object_print_z_min;
2388 effective_max_z = raft_z + layers.back();
2389 last_layer_below_max = layers.size() >= 2 &&
2390 raft_z + 0.5 * (layers[layers.size() - 2] + layers.back()) <= this->config().printable_height + EPSILON;
2391 have_height = true;
2392 }
2393 }
2394
2395 if (!have_height)
2396 continue;
2397
2398 // The layers of a regular printer already carry the Z shrinkage compensation, the belt height does not.
2399 const double compensated_max_z = belt_printer ? effective_max_z * shrinkage_compensation_z : effective_max_z;
2400 const double uncompensated_max_z = belt_printer ? effective_max_z : print_object.slicing_parameters().object_print_z_uncompensated_max;
2401 if (shrinkage_compensation_z != 1. && compensated_max_z > this->config().printable_height + EPSILON &&
2402 uncompensated_max_z <= this->config().printable_height + EPSILON) {
2403 // The object exceeds the maximum build volume height because of shrinkage compensation.
2404 return StringObjectException{
2405 Slic3r::format(_u8L("While the object %1% itself fits the build volume, it exceeds the maximum build volume height because of material shrinkage compensation."), print_object.model_object()->name),
2406 print_object.model_object(),
2407 ""
2408 };
2409 } else if (effective_max_z > this->config().printable_height + EPSILON) {
2410 return StringObjectException{
2411 last_layer_below_max ?
2412 Slic3r::format(_u8L("While the object %1% itself fits the build volume, its last layer exceeds the maximum build volume height."), print_object.model_object()->name) +
2413 " " + _u8L("You might want to reduce the size of your model or change current print settings and retry.") :
2414 Slic3r::format(_u8L("The object %1% exceeds the maximum build volume height."), print_object.model_object()->name),
2415 print_object.model_object(),
2416 ""
2417 };
2418 }
2419 }
2420
2421 // Some of the objects has variable layer height applied by painting or by a table.
2422 bool has_custom_layering = std::find_if(m_objects.begin(), m_objects.end(),
2423 [](const PrintObject *object) { return object->model_object()->has_custom_layering(); })
2424 != m_objects.end();
2425
2426 // Custom layering is not allowed for tree supports as of now.
2427 for (size_t print_object_idx = 0; print_object_idx < m_objects.size(); ++ print_object_idx)
2428 if (const PrintObject &print_object = *m_objects[print_object_idx];
2429 print_object.has_support_material() && is_tree(print_object.config().support_type.value) && (print_object.config().support_style.value == smsTreeOrganic ||
2430 // Orca: use organic as default
2431 print_object.config().support_style.value == smsDefault) &&
2432 print_object.model_object()->has_custom_layering()) {
2433 if (const std::vector<coordf_t> &layers = layer_height_profile(print_object_idx); ! layers.empty())
2434 if (! check_object_layers_fixed(print_object.slicing_parameters(), layers))
2435 return {_u8L("Variable layer height is not supported with Organic supports.") };
2436 }
2437
2438 if ((this->has_wipe_tower() || this->has_belt_purge_tower()) && ! m_objects.empty()) {
2439 // Orca: wipe_tower_filament (issue #10971) is inserted into the tool order after
2440 // resolve_mixed_filaments has expanded every mixed (virtual) slot, so a mixed slot here
2441 // would reach the G-code as a tool change to a slot no nozzle carries. The GUI hides
2442 // mixed slots from the option; this guards loaded projects and the CLI.
2443 if (this->has_wipe_tower() && m_config.wipe_tower_filament > 0) {
2444 const auto &is_mixed = m_config.filament_is_mixed.values;
2445 const size_t wipe_idx = size_t(m_config.wipe_tower_filament - 1);
2446 if (wipe_idx < is_mixed.size() && is_mixed[wipe_idx])
2447 return { L("The wipe tower filament cannot be a mixed filament."), nullptr, "wipe_tower_filament" };
2448 }
2449
2450 // Make sure all extruders use same diameter filament and have the same nozzle diameter
2451 // EPSILON comparison is used for nozzles and 10 % tolerance is used for filaments
2452 double first_nozzle_diam = m_config.nozzle_diameter.get_at(extruders.front());
2453 double first_filament_diam = m_config.filament_diameter.get_at(extruders.front());
2454 for (const auto& extruder_idx : extruders) {
2455 double nozzle_diam = m_config.nozzle_diameter.get_at(extruder_idx);
2456 double filament_diam = m_config.filament_diameter.get_at(extruder_idx);
2457 if (nozzle_diam - EPSILON > first_nozzle_diam || nozzle_diam + EPSILON < first_nozzle_diam
2458 || std::abs((filament_diam - first_filament_diam) / first_filament_diam) > 0.1) {
2459 // return { L("Different nozzle diameters and different filament diameters may not work well when prime tower is enabled. It's very experimental, please proceed with caucious.") };
2460 warn(L("Different nozzle diameters and different filament diameters may not work well when the prime tower is enabled. It's very experimental, so please proceed with caution."), "nozzle_diameter");
2461 break;
2462 }
2463 }
2464
2465 // The following two constraints come from the classic wipe tower G-code
2466 // generator; purging into the belt purge prism uses normal object
2467 // extrusions and does not need them.
2468 if (this->has_wipe_tower()) {
2469 if (! m_config.use_relative_e_distances)
2470 return { L("The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).") };
2471
2472 if (m_config.ooze_prevention && m_config.single_extruder_multi_material)
2473 return {L("Ooze prevention is only supported with the wipe tower when 'single_extruder_multi_material' is off.")};
2474 }
2475
2476#if 0
2477 if (m_config.gcode_flavor != gcfRepRapSprinter && m_config.gcode_flavor != gcfRepRapFirmware &&
2478 m_config.gcode_flavor != gcfRepetier && m_config.gcode_flavor != gcfMarlinLegacy && m_config.gcode_flavor != gcfMarlinFirmware)
2479 return { L("The prime tower is currently only supported for the Marlin, RepRap/Sprinter, RepRapFirmware and Repetier G-code flavors.")};
2480
2481 if ((m_config.print_sequence == PrintSequence::ByObject) && extruders.size() > 1)
2482 return { L("A prime tower is not supported in \u201cBy object\u201d print."), nullptr, "enable_prime_tower" };
2483
2484 // BBS: When prime tower is on, object layer and support layer must be aligned. So support gap should be multiple of object layer height.
2485 for (size_t i = 0; i < m_objects.size(); i++) {
2486 const PrintObject* object = m_objects[i];
2487 const SlicingParameters& slicing_params = object->slicing_parameters();
2488 if (object->config().adaptive_layer_height) {
2489 return { L("A prime tower is not supported when adaptive layer height is on. It requires that all objects have the same layer height."), object, "adaptive_layer_height" };
2490 }
2491
2492 if (!object->config().enable_support)
2493 continue;
2494
2495 double gap_layers = slicing_params.gap_object_support / slicing_params.layer_height;
2496 if (gap_layers - (int)gap_layers > EPSILON) {
2497 return {L("A prime tower requires any \u201csupport gap\u201d to be a multiple of layer height."), object};
2498 }
2499 }
2500#endif
2501
2502 if (m_objects.size() > 1) {
2503 const SlicingParameters &slicing_params0 = m_objects.front()->slicing_parameters();
2504 size_t tallest_object_idx = 0;
2505 for (size_t i = 1; i < m_objects.size(); ++ i) {
2506 const PrintObject *object = m_objects[i];
2507 const SlicingParameters &slicing_params = object->slicing_parameters();
2508 if (std::abs(slicing_params.first_print_layer_height - slicing_params0.first_print_layer_height) > EPSILON ||
2509 std::abs(slicing_params.layer_height - slicing_params0.layer_height ) > EPSILON)
2510 return {L("A prime tower requires that all objects have the same layer height."), object, "initial_layer_print_height"};
2511 if (slicing_params.raft_layers() != slicing_params0.raft_layers())
2512 return {L("A prime tower requires that all objects are printed over the same number of raft layers."), object, "raft_layers"};
2513 // BBS: support gap can be multiple of object layer height, remove _L()
2514#if 0
2515 if (slicing_params0.gap_object_support != slicing_params.gap_object_support ||
2516 slicing_params0.gap_support_object != slicing_params.gap_support_object)
2517 return {L("The prime tower is only supported for multiple objects if they are printed with the same support_top_z_distance."), object};
2518#endif
2519 if (!equal_layering(slicing_params, slicing_params0))
2520 return { L("A prime tower requires that all objects are sliced with the same layer height."), object };
2521 if (has_custom_layering) {
2522 auto &lh = layer_height_profile(i);
2523 auto &lh_tallest = layer_height_profile(tallest_object_idx);
2524 if (*(lh.end() - 2) > *(lh_tallest.end() - 2))
2525 tallest_object_idx = i;
2526 }
2527 }
2528
2529 // BBS: remove obsolete logics and _L()
2530 if (has_custom_layering) {
2531 std::vector<std::vector<coordf_t>> layer_z_series;
2532 layer_z_series.assign(m_objects.size(), std::vector<coordf_t>());
2533
2534 for (size_t idx_object = 0; idx_object < m_objects.size(); ++idx_object) {
2535 layer_z_series[idx_object] = generate_object_layers(m_objects[idx_object]->slicing_parameters(), layer_height_profiles[idx_object], m_objects[idx_object]->config().precise_z_height.value);
2536 }
2537
2538 for (size_t idx_object = 0; idx_object < m_objects.size(); ++idx_object) {
2539 if (idx_object == tallest_object_idx) continue;
2540 // Check that the layer height profiles are equal. This will happen when one object is
2541 // a copy of another, or when a layer height modifier is used the same way on both objects.
2542 // The latter case might create a floating point inaccuracy mismatch, so compare
2543 // element-wise using an epsilon check.
2544 size_t i = 0;
2545 const coordf_t eps = 0.5 * EPSILON; // layers closer than EPSILON will be merged later. Let's make
2546 // this check a bit more sensitive to make sure we never consider two different layers as one.
2547 while (i < layer_height_profiles[idx_object].size() && i < layer_height_profiles[tallest_object_idx].size()) {
2548 // BBS: remove the break condition, because a variable layer height object and a new object will not be checked when slicing
2549 //if (i % 2 == 0 && layer_height_profiles[tallest_object_idx][i] > layer_height_profiles[idx_object][layer_height_profiles[idx_object].size() - 2])
2550 // break;
2551 if (std::abs(layer_height_profiles[idx_object][i] - layer_height_profiles[tallest_object_idx][i]) > eps)
2552 return {L("The prime tower is only supported if all objects have the same variable layer height.")};
2553 ++i;
2554 }
2555 }
2556 }
2557 }
2558 }
2559
2560 {
2561 // Find the smallest used nozzle diameter and the number of unique nozzle diameters.
2562 double min_nozzle_diameter = std::numeric_limits<double>::max();
2563 double max_nozzle_diameter = 0;
2564 for (unsigned int extruder_id : extruders) {
2565 double dmr = m_config.nozzle_diameter.get_at(extruder_id);
2566 min_nozzle_diameter = std::min(min_nozzle_diameter, dmr);
2567 max_nozzle_diameter = std::max(max_nozzle_diameter, dmr);
2568 }
2569
2570 // BBS: remove L()
2571#if 0
2572 // We currently allow one to assign extruders with a higher index than the number
2573 // of physical extruders the machine is equipped with, as the Printer::apply() clamps them.
2574 unsigned int total_extruders_count = m_config.nozzle_diameter.size();
2575 for (const auto& extruder_idx : extruders)
2576 if ( extruder_idx >= total_extruders_count )
2577 return {L("One or more object were assigned an extruder that the printer does not have.")};
2578#endif
2579
2580 auto validate_extrusion_width = [min_nozzle_diameter, max_nozzle_diameter](const ConfigBase &config, const char *opt_key, double layer_height, std::string &err_msg) -> bool {
2581 double extrusion_width_min = config.get_abs_value(opt_key, min_nozzle_diameter);
2582 double extrusion_width_max = config.get_abs_value(opt_key, max_nozzle_diameter);
2583 if (extrusion_width_min == 0) {
2584 // Default "auto-generated" extrusion width is always valid.
2585 } else if (extrusion_width_min <= layer_height) {
2586 err_msg = L("Line width too small");
2587 return false;
2588 } else if (extrusion_width_max > max_nozzle_diameter * MAX_LINE_WIDTH_MULTIPLIER) {
2589 err_msg = L("Line width too large");
2590 return false;
2591 }
2592 return true;
2593 };
2594 for (PrintObject *object : m_objects) {
2595 if (object->has_support_material()) {
2596 // BBS: remove useless logics and L()
2597#if 0
2598 if ((object->config().support_filament == 0 || object->config().support_interface_filament == 0) && max_nozzle_diameter - min_nozzle_diameter > EPSILON) {
2599 // The object has some form of support and either support_filament or support_interface_filament
2600 // will be printed with the current tool without a forced tool change. Play safe, assert that all object nozzles
2601 // are of the same diameter.
2602 return {L("Printing with multiple extruders of differing nozzle diameters. "
2603 "If support is to be printed with the current filament (support_filament == 0 or support_interface_filament == 0), "
2604 "all nozzles have to be of the same diameter."), object, "support_filament"};
2605 }
2606#endif
2607
2608 // BBS
2609#if 0
2610 if (this->has_wipe_tower() && object->config().independent_support_layer_height) {
2611 return {L("A prime tower requires that support has the same layer height as the object."), object, "support_filament"};
2612 }
2613#endif
2614
2615 // Prusa: Fixing crashes with invalid tip diameter or branch diameter
2616 // https://github.com/prusa3d/PrusaSlicer/commit/96b3ae85013ac363cd1c3e98ec6b7938aeacf46d
2617 if (is_tree(object->config().support_type.value)) {
2618 if (object->config().support_style == smsTreeOrganic ||
2619 // Orca: use organic as default
2620 object->config().support_style == smsDefault) {
2621
2622 // Orca: check the support wall count and the base pattern
2623 if (object->config().tree_support_wall_count > 1 &&
2624 object->config().support_base_pattern != SupportMaterialPattern::smpNone &&
2625 object->config().support_base_pattern != SupportMaterialPattern::smpDefault)
2626 warn(L("For Organic supports, two walls are supported only with the Hollow/Default base pattern."), "support_base_pattern");
2627
2628 // Orca: check if the Lightning base pattern selected
2629 if (object->config().support_base_pattern == SupportMaterialPattern::smpLightning)
2630 warn(L("The Lightning base pattern is not supported by this support type; Rectilinear will be used instead."), "support_base_pattern");
2631
2632 float extrusion_width = std::min(
2633 support_material_flow(object).width(),
2635 if (object->config().tree_support_tip_diameter < extrusion_width - EPSILON)
2636 return { L("Organic support tree tip diameter must not be smaller than support material extrusion width."), object, "tree_support_tip_diameter" };
2637 if (object->config().tree_support_branch_diameter_organic < 2. * extrusion_width - EPSILON)
2638 return { L("Organic support branch diameter must not be smaller than 2x support material extrusion width."), object, "tree_support_branch_diameter_organic" };
2639 if (object->config().tree_support_branch_diameter_organic < object->config().tree_support_tip_diameter)
2640 return { L("Organic support branch diameter must not be smaller than support tree tip diameter."), object, "tree_support_branch_diameter_organic" };
2641 }
2642 } else if (object->config().support_base_pattern == SupportMaterialPattern::smpLightning) {
2643 // Orca: check if the Lightning base pattern selected
2644 warn(L("The Lightning base pattern is not supported by this support type; Rectilinear will be used instead."), "support_base_pattern");
2645 } else if (object->config().support_base_pattern == SupportMaterialPattern::smpNone) {
2646 // Orca: check if the Hollow base pattern selected
2647 warn(L("The Hollow base pattern is not supported by this support type; Rectilinear will be used instead."), "support_base_pattern");
2648 }
2649 }
2650
2651 // Do we have custom support data that would not be used?
2652 // Notify the user in that case.
2653 if (! object->has_support()) {
2654 for (const ModelVolume* mv : object->model_object()->volumes) {
2655 bool has_enforcers = mv->is_support_enforcer() ||
2656 (mv->is_model_part() && mv->supported_facets.has_facets(*mv, EnforcerBlockerType::ENFORCER));
2657 if (has_enforcers) {
2658 warn(L("Support enforcers are used but support is not enabled. Please enable support."), "", object);
2659 break;
2660 }
2661 }
2662 }
2663
2664 double initial_layer_print_height = m_config.initial_layer_print_height.value;
2665 double first_layer_min_nozzle_diameter;
2666 if (object->has_raft()) {
2667 // if we have raft layers, only support material extruder is used on first layer
2668 size_t first_layer_extruder = object->config().raft_layers == 1
2669 ? object->config().support_interface_filament-1
2670 : object->config().support_filament-1;
2671 first_layer_min_nozzle_diameter = (first_layer_extruder == size_t(-1)) ?
2672 min_nozzle_diameter :
2673 m_config.nozzle_diameter.get_at(first_layer_extruder);
2674 } else {
2675 // if we don't have raft layers, any nozzle diameter is potentially used in first layer
2676 first_layer_min_nozzle_diameter = min_nozzle_diameter;
2677 }
2678 if (initial_layer_print_height > first_layer_min_nozzle_diameter)
2679 return {L("Layer height cannot exceed nozzle diameter."), object, "initial_layer_print_height"};
2680
2681 // validate layer_height
2682 double layer_height = object->config().layer_height.value;
2683 if (layer_height > min_nozzle_diameter)
2684 return {L("Layer height cannot exceed nozzle diameter."), object, "layer_height"};
2685
2686 // Validate extrusion widths.
2687 std::string err_msg;
2688 if (!validate_extrusion_width(object->config(), "line_width", layer_height, err_msg))
2689 return {err_msg, object, "line_width"};
2690 if (object->has_support() || object->has_raft()) {
2691 if (!validate_extrusion_width(object->config(), "support_line_width", layer_height, err_msg))
2692 return {err_msg, object, "support_line_width"};
2693 }
2694 for (const char *opt_key : { "inner_wall_line_width", "outer_wall_line_width", "sparse_infill_line_width", "internal_solid_infill_line_width", "top_surface_line_width","skin_infill_line_width" ,"skeleton_infill_line_width"})
2695 for (const PrintRegion &region : object->all_regions())
2696 if (!validate_extrusion_width(region.config(), opt_key, layer_height, err_msg))
2697 return {err_msg, object, opt_key};
2698
2699 const bool allow_thin_bridge_width = object->config().thick_bridges && object->config().thick_internal_bridges;
2700 for (const PrintRegion &region : object->all_regions()) {
2701 const auto &bridge_width_opt = region.config().bridge_line_width;
2702 for (FlowRole bridge_role : { frPerimeter, frInfill, frSolidInfill, frTopSolidInfill }) {
2703 const double nozzle_diameter = nozzle_diameter_for_filament(m_config, region.extruder(bridge_role), this->is_BBL_printer());
2704 const double bridge_width = bridge_width_opt.get_abs_value(nozzle_diameter);
2705 if (bridge_width <= 0.)
2706 continue;
2707 if (bridge_width > nozzle_diameter) {
2708 err_msg = L("Bridge line width must not exceed nozzle diameter");
2709 return { err_msg, object, "bridge_line_width" };
2710 }
2711 if (!allow_thin_bridge_width && bridge_width <= layer_height) {
2712 err_msg = L("Line width too small");
2713 return { err_msg, object, "bridge_line_width" };
2714 }
2715 }
2716 }
2717 }
2718 }
2719
2720 // Orca: G92 E0 is not supported when using absolute extruder addressing
2721 // This check is modified from PrusaSlicer, the original author is Vojtech Bubnik
2722 // Orca: case‑sensitive match for exactly "G92 E0" (uppercase G and E only)
2723 // because gcode is case sensitive and G92 e0 satisfies the regex but causes a slicing error
2724 // https://github.com/OrcaSlicer/OrcaSlicer/issues/13927
2725
2726 // Matches any case of "G92 E0" (original pattern)
2727 static const boost::regex regex_g92e0 {
2728 "^[ \\t]*[gG]92[ \\t]*[eE](0(\\.0*)?|\\.0+)[ \\t]*(;.*)?$"
2729 };
2730 // Matches only the exact uppercase "G92 E0"
2731 static const boost::regex regex_g92e0_correct {
2732 "^[ \\t]*G92[ \\t]*E(0(\\.0*)?|\\.0+)[ \\t]*(;.*)?$"
2733 };
2734
2735 const bool before_has_g92_any = boost::regex_search(
2736 m_config.before_layer_change_gcode.value, regex_g92e0);
2737 const bool layer_has_g92_any = boost::regex_search(
2738 m_config.layer_change_gcode.value, regex_g92e0);
2739
2740 if (m_config.use_relative_e_distances) {
2741 // Relative mode: "G92 E0" is required to reset extruder position.
2742 const bool before_has_g92_exact = boost::regex_search(
2743 m_config.before_layer_change_gcode.value, regex_g92e0_correct);
2744 const bool layer_has_g92_exact = boost::regex_search(
2745 m_config.layer_change_gcode.value, regex_g92e0_correct);
2746
2747 // Wrong case found?
2748 if (before_has_g92_any && !before_has_g92_exact)
2749 return {L("\"G92 E0\" was found in before_layer_change_gcode, but the G or E are not uppercase. "
2750 "Please change them to the exact uppercase \"G92 E0\"."),
2751 nullptr, "before_layer_change_gcode"};
2752 if (layer_has_g92_any && !layer_has_g92_exact)
2753 return {L("\"G92 E0\" was found in layer_change_gcode, but the G or E are not uppercase. "
2754 "Please change them to the exact uppercase \"G92 E0\"."),
2755 nullptr, "layer_change_gcode"};
2756
2757 // Only Marlin flavours need the reset; BBL printers do not.
2758 if ((m_config.gcode_flavor == gcfMarlinLegacy || m_config.gcode_flavor == gcfMarlinFirmware) &&
2759 !is_BBL_printer() &&
2760 !before_has_g92_exact && !layer_has_g92_exact)
2761 return {L("Relative extruder addressing requires resetting the extruder position at each layer to "
2762 "prevent loss of floating point accuracy. Add \"G92 E0\" to layer_gcode."),
2763 nullptr, "before_layer_change_gcode"};
2764 } else {
2765 // Absolute mode: any occurrence of "G92 E0" is incompatible.
2766 if (before_has_g92_any)
2767 return {L("\"G92 E0\" was found in before_layer_change_gcode, which is incompatible with absolute extruder "
2768 "addressing."),
2769 nullptr, "before_layer_change_gcode"};
2770 if (layer_has_g92_any)
2771 return {L("\"G92 E0\" was found in layer_change_gcode, which is incompatible with absolute extruder "
2772 "addressing."),
2773 nullptr, "layer_change_gcode"};
2774 }
2775
2776 const ConfigOptionDef* bed_type_def = print_config_def.get("curr_bed_type");
2777 assert(bed_type_def != nullptr);
2778
2779 // ORCA: check if bed type is compatible with all selected filaments
2780 if (is_BBL_printer() || m_config.support_multi_bed_types.value) {
2781 const t_config_enum_values* bed_type_keys_map = bed_type_def->enum_keys_map;
2782 for (unsigned int extruder_id : extruders) {
2783 const ConfigOptionInts* bed_temp_opt = m_config.option<ConfigOptionInts>(get_bed_temp_key(m_config.curr_bed_type));
2784 for (unsigned int extruder_id : extruders) {
2785 int curr_bed_temp = bed_temp_opt->get_at(extruder_id);
2786 if (curr_bed_temp == 0 && bed_type_keys_map != nullptr) {
2787 std::string bed_type_name;
2788 for (auto item : *bed_type_keys_map) {
2789 if (item.second == m_config.curr_bed_type) {
2790 bed_type_name = item.first;
2791 break;
2792 }
2793 }
2794
2795 StringObjectException except;
2796 except.string = Slic3r::format(L("Plate %d: %s does not support filament %s"), this->get_plate_index() + 1, L(bed_type_name), extruder_id + 1);
2797 except.string += "\n";
2799 except.params.push_back(std::to_string(this->get_plate_index() + 1));
2800 except.params.push_back(L(bed_type_name));
2801 except.params.push_back(std::to_string(extruder_id+1));
2802 except.object = nullptr;
2803 return except;
2804 }
2805 }
2806 }
2807 }
2808
2809 // check if print speed/accel/jerk is higher than the maximum speed of the printer
2810 if (warnings) {
2811 // The motion-ability checks are mutually exclusive (gated on warning_key), so collect the
2812 // single one that fires into a local and push it once - separate from the precise-wall and
2813 // shrinkage warnings below.
2814 StringObjectException motion_warning;
2815 try {
2816 auto check_extruder = [&](const int extruder_id) {
2817 auto check_motion_ability_object_setting = [&](const std::vector<std::string>& keys_to_check, double limit) -> std::string {
2818 std::string warning_key;
2819 for (const auto& key : keys_to_check) {
2820 if (m_default_object_config.get_abs_value_at(key, extruder_id) > limit) {
2821 warning_key = key;
2822 break;
2823 }
2824 }
2825 return warning_key;
2826 };
2827 auto check_motion_ability_region_setting = [&](const std::vector<std::string>& keys_to_check, double limit) -> std::string {
2828 std::string warning_key;
2829 for (const auto& key : keys_to_check) {
2830 if (m_default_region_config.get_abs_value_at(key, extruder_id) > limit) {
2831 warning_key = key;
2832 break;
2833 }
2834 }
2835 return warning_key;
2836 };
2837 std::string warning_key;
2838
2839 const auto max_junction_deviation = m_config.machine_max_junction_deviation.values[0]; // TODO: fix this
2840 const bool ignore_jerk_validation = m_config.gcode_flavor == gcfMarlinFirmware && max_junction_deviation > 0;
2841
2842 // check jerk
2843 if (!ignore_jerk_validation) {
2844 if (m_default_object_config.default_jerk.get_at(extruder_id) == 1 || m_default_object_config.outer_wall_jerk.get_at(extruder_id) == 1 ||
2845 m_default_object_config.inner_wall_jerk.get_at(extruder_id) == 1) {
2846 motion_warning.string = L("Setting the jerk speed too low could lead to artifacts on curved surfaces");
2847 if (m_default_object_config.outer_wall_jerk.get_at(extruder_id) == 1)
2848 warning_key = "outer_wall_jerk";
2849 else if (m_default_object_config.inner_wall_jerk.get_at(extruder_id) == 1)
2850 warning_key = "inner_wall_jerk";
2851 else
2852 warning_key = "default_jerk";
2853
2854 motion_warning.opt_key = warning_key;
2855 }
2856
2857 if (warning_key.empty() && m_default_object_config.default_jerk.get_at(extruder_id) > 0) {
2858 std::vector<std::string> jerk_to_check = {"default_jerk", "outer_wall_jerk", "inner_wall_jerk", "infill_jerk",
2859 "top_surface_jerk", "initial_layer_jerk", "travel_jerk"};
2860 const auto max_jerk = std::min(m_config.machine_max_jerk_x.values[0], m_config.machine_max_jerk_y.values[0]);
2861 warning_key.clear();
2862 warning_key = check_motion_ability_object_setting(jerk_to_check, max_jerk);
2863 if (!warning_key.empty()) {
2864 motion_warning.string = L(
2865 "The jerk setting exceeds the printer's maximum jerk (machine_max_jerk_x/machine_max_jerk_y).\n"
2866 "Orca will automatically cap the jerk speed to ensure it doesn't surpass the printer's capabilities.\n"
2867 "You can adjust the maximum jerk setting in your printer's configuration to get higher speeds.");
2868 motion_warning.opt_key = warning_key;
2869 }
2870 }
2871 }
2872
2873 // Check junction deviation
2874 // Orca: Only marlin FW supports max junction deviation. Dont display warning if firmware is not supporting it.
2875 const bool support_max_junction_deviation = ( m_config.gcode_flavor == gcfMarlinFirmware);
2876 if (warning_key.empty() && m_default_object_config.default_junction_deviation.get_at(extruder_id) > max_junction_deviation && support_max_junction_deviation) {
2877 motion_warning.string = L( "Junction deviation setting exceeds the printer's maximum value (machine_max_junction_deviation).\n"
2878 "Orca will automatically cap the junction deviation to ensure it doesn't surpass the printer's capabilities.\n"
2879 "You can adjust the machine_max_junction_deviation value in your printer's configuration to get higher limits.");
2880 motion_warning.opt_key = "default_junction_deviation";
2881 }
2882
2883 // check acceleration
2884 const auto max_accel = m_config.machine_max_acceleration_extruding.values[0];
2885 if (warning_key.empty() && m_default_object_config.default_acceleration.get_at(extruder_id) > 0 && max_accel > 0) {
2886 const bool support_travel_acc = (m_config.gcode_flavor == gcfRepetier || m_config.gcode_flavor == gcfMarlinFirmware ||
2887 m_config.gcode_flavor == gcfRepRapFirmware);
2888
2889 std::vector<std::string> accel_to_check;
2890 if (!support_travel_acc)
2891 accel_to_check = {
2892 "default_acceleration",
2893 "inner_wall_acceleration",
2894 "outer_wall_acceleration",
2895 "bridge_acceleration",
2896 "initial_layer_acceleration",
2897 "sparse_infill_acceleration",
2898 "internal_solid_infill_acceleration",
2899 "top_surface_acceleration",
2900 "travel_acceleration",
2901 };
2902 else
2903 accel_to_check = {
2904 "default_acceleration",
2905 "inner_wall_acceleration",
2906 "outer_wall_acceleration",
2907 "bridge_acceleration",
2908 "initial_layer_acceleration",
2909 "sparse_infill_acceleration",
2910 "internal_solid_infill_acceleration",
2911 "top_surface_acceleration",
2912 };
2913 warning_key = check_motion_ability_object_setting(accel_to_check, max_accel);
2914 if (!warning_key.empty()) {
2915 motion_warning.string = L("The acceleration setting exceeds the printer's maximum acceleration "
2916 "(machine_max_acceleration_extruding).\nOrca will "
2917 "automatically cap the acceleration speed to ensure it doesn't surpass the printer's "
2918 "capabilities.\nYou can adjust the "
2919 "machine_max_acceleration_extruding value in your printer's configuration to get higher speeds.");
2920 motion_warning.opt_key = warning_key;
2921 }
2922 if (support_travel_acc) {
2923 const auto max_travel = m_config.machine_max_acceleration_travel.values[0];
2924 if (max_travel > 0) {
2925 accel_to_check = {
2926 "travel_acceleration",
2927 };
2928 warning_key = check_motion_ability_object_setting(accel_to_check, max_travel);
2929 if (!warning_key.empty()) {
2930 motion_warning.string = L(
2931 "The travel acceleration setting exceeds the printer's maximum travel acceleration "
2932 "(machine_max_acceleration_travel).\nOrca will "
2933 "automatically cap the travel acceleration speed to ensure it doesn't surpass the printer's "
2934 "capabilities.\nYou can adjust the "
2935 "machine_max_acceleration_travel value in your printer's configuration to get higher speeds.");
2936 motion_warning.opt_key = warning_key;
2937 }
2938 }
2939 }
2940 }
2941
2942 // check speed
2943 // Orca: disable the speed check for now as we don't cap the speed
2944 // if (warning_key.empty()) {
2945 // auto speed_to_check = {"inner_wall_speed", "outer_wall_speed", "sparse_infill_speed", "internal_solid_infill_speed",
2946 // "top_surface_speed", "bridge_speed", "internal_bridge_speed", "gap_infill_speed"};
2947 // const auto max_speed = std::min(m_config.machine_max_speed_x.values[0], m_config.machine_max_speed_y.values[0]);
2948 // warning_key.clear();
2949 // warning_key = check_motion_ability_region_setting(speed_to_check, max_speed);
2950 // if (warning_key.empty() && m_config.travel_speed > max_speed)
2951 // warning_key = "travel_speed";
2952 // if (!warning_key.empty()) {
2953 // motion_warning.string = L(
2954 // "The speed setting exceeds the printer's maximum speed (machine_max_speed_x/machine_max_speed_y).\nOrca will "
2955 // "automatically cap the print speed to ensure it doesn't surpass the printer's capabilities.\nYou can adjust the "
2956 // "maximum speed setting in your printer's configuration to get higher speeds.");
2957 // motion_warning.opt_key = warning_key;
2958 // }
2959 // }
2960 };
2961 check_extruder(0); // TODO: check used extruder variants
2962
2963 // check wall sequence and precise outer wall
2964 if (m_default_region_config.precise_outer_wall && m_default_region_config.wall_sequence != WallSequence::InnerOuter)
2965 warn(L("The precise wall option will be ignored for outer-inner or inner-outer-inner wall sequences."), "precise_outer_wall");
2966
2967 // check adaptive pressure advance model of every extruder variant column of the used filaments
2968 const std::vector<int> &self_index = m_config.filament_self_index.values;
2969 const size_t pa_columns = std::max(m_config.adaptive_pressure_advance_model.size(), size_t(extruders.back()) + 1);
2970 for (size_t column = 0; column < pa_columns; ++column) {
2971 // filament_self_index maps a column to its filament once the filament arrays hold one column per variant
2972 const unsigned int filament_id = self_index.size() == pa_columns ? self_index[column] - 1 : column;
2973 if (!std::binary_search(extruders.begin(), extruders.end(), filament_id))
2974 continue;
2975 if (m_config.adaptive_pressure_advance.get_at(column) &&
2976 m_config.enable_pressure_advance.get_at(column)) {
2977
2978 const std::string pa_model = m_config.adaptive_pressure_advance_model.get_at(column);
2979 if (!pa_model.empty()) {
2980 std::string validation_error = AdaptivePAProcessor::validate_adaptive_pa_model(pa_model);
2981 if (!validation_error.empty()) {
2982 warn(L("The Adaptive Pressure Advance model for one or more extruders may contain invalid values."),
2983 "adaptive_pressure_advance_model");
2984 break;
2985 }
2986 }
2987 }
2988 }
2989
2990 } catch (std::exception& e) {
2991 BOOST_LOG_TRIVIAL(warning) << "Orca: validate motion ability failed: " << e.what() << std::endl;
2992 }
2993 if (!motion_warning.string.empty())
2994 add_warning(motion_warning);
2995 }
2997 warn(L("Filament shrinkage will not be used because filament shrinkage for the used filaments does not match."));
2998 return {};
2999}
static std::string validate_adaptive_pa_model(const std::string &model_str)
Validates an adaptive pressure advance model string.
Definition AdaptivePAProcessor.cpp:303
const ConfigOptionDef * get(const t_config_option_key &opt_key) const
Definition Config.hpp:2662
ObjectBase()
Definition ObjectID.hpp:79
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
static StringObjectException sequential_print_clearance_valid(const Print &print, Polygons *polygons=nullptr, std::vector< std::pair< Polygon, float > > *height_polygons=nullptr)
Definition Print.cpp:892
bool has_tilted_belt() const
Definition Print.cpp:854
static StringObjectException check_multi_filament_valid(const Print &print)
Definition Print.cpp:1836
bool has_same_shrinkage_compensations() const
Definition Print.hpp:1396
static StringObjectException compacted_wipe_tower_clearance_valid(const Print &print, Polygons *polygons=nullptr, std::vector< std::pair< Polygon, float > > *height_polygons=nullptr)
Definition Print.cpp:1428
static constexpr float MAX_LINE_WIDTH_MULTIPLIER
Definition libslic3r.h:78
@ gcfMarlinFirmware
Definition PrintConfig.hpp:56
@ gcfRepetier
Definition PrintConfig.hpp:55
@ gcfRepRapFirmware
Definition PrintConfig.hpp:54
@ gcfMarlinLegacy
Definition PrintConfig.hpp:52
@ gcfRepRapSprinter
Definition PrintConfig.hpp:57
bool check_object_layers_fixed(const SlicingParameters &slicing_params, const std::vector< coordf_t > &layer_height_profile)
Definition Slicing.cpp:879
@ btInnerOnly
Definition PrintConfig.hpp:459
@ btEar
Definition PrintConfig.hpp:456
@ btPainted
Definition PrintConfig.hpp:457
@ btLeadingEdgeOnly
Definition PrintConfig.hpp:465
@ btNoBrim
Definition PrintConfig.hpp:461
@ btAutoBrim
Definition PrintConfig.hpp:455
static std::string get_bed_temp_key(const BedType type)
Definition PrintConfig.hpp:732
static StringObjectException layered_print_cleareance_valid(const Print &print, StringObjectException *warning)
Definition Print.cpp:1584
@ STRING_EXCEPT_OBJECT_COLLISION_IN_SEQ_PRINT
Definition PrintBase.hpp:30
@ STRING_EXCEPT_OBJECT_COLLISION_IN_LAYER_PRINT
Definition PrintBase.hpp:31
@ STRING_EXCEPT_FILAMENTS_DIFFERENT_TEMP
Definition PrintBase.hpp:29
@ STRING_EXCEPT_FILAMENT_NOT_MATCH_BED_TYPE
Definition PrintBase.hpp:28
bool equal_layering(const SlicingParameters &sp1, const SlicingParameters &sp2)
Definition Slicing.hpp:127
std::string imex_multicolor_block_reason(const std::string &parallel_mode, const std::string &active_tools_str, int tools_per_gantry, const std::vector< int > &used_filaments_0b, const ConfigOptionInts &pem)
Definition IMEXHelpers.cpp:104
FlowRole
Definition Flow.hpp:18
@ frTopSolidInfill
Definition Flow.hpp:23
@ frSolidInfill
Definition Flow.hpp:22
@ frInfill
Definition Flow.hpp:21
zaa_minimize_perimeter_height belt_printer((ConfigOptionBool, belt_printer_infinite_y))((ConfigOptionEnum< BeltRotationAxis >
@ ENFORCER
Definition TriangleSelector.hpp:27
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
bool is_tree(SupportType stype)
Definition PrintConfig.hpp:381
Flow support_material_flow(const PrintObject *object, float layer_height)
Definition Flow.cpp:239
@ smpNone
Definition PrintConfig.hpp:359
@ smpDefault
Definition PrintConfig.hpp:356
@ smpLightning
Definition PrintConfig.hpp:358
ImexRouting imex_resolve_routing(const ConfigBase &cfg, const std::string &parallel_mode, const std::vector< int > &used_slots_1b, const ConfigOptionInts &pem)
Definition IMEXHelpers.cpp:497
Flow support_material_interface_flow(const PrintObject *object, float layer_height)
Definition Flow.cpp:269
constexpr const char * kImexPrimaryMode
Definition IMEXHelpers.hpp:27
@ InnerOuter
Definition PrintConfig.hpp:246
@ smsDefault
Definition PrintConfig.hpp:363
@ smsTreeOrganic
Definition PrintConfig.hpp:363
std::map< std::string, int > t_config_enum_values
Definition Config.hpp:2072
@ dsDisabled
Definition PrintConfig.hpp:478

References _u8L, Slic3r::ImexRouting::active_tools, Slic3r::BELT_BRIM_MAX_TILT_DEG, Slic3r::belt_printer(), Slic3r::btAutoBrim, Slic3r::btEar, Slic3r::btInnerOnly, Slic3r::btLeadingEdgeOnly, Slic3r::btNoBrim, Slic3r::btPainted, Slic3r::ByObject, check_multi_filament_valid(), Slic3r::check_object_layers_fixed(), compacted_wipe_tower_clearance_valid(), config(), Slic3r::PrintObject::config(), Slic3r::dsDisabled, Slic3r::ENFORCER, Slic3r::ConfigOptionDef::enum_keys_map, EPSILON, Slic3r::equal_layering(), extruders(), Slic3r::SlicingParameters::first_print_layer_height, Slic3r::format(), Slic3r::frInfill, Slic3r::frPerimeter, Slic3r::frSolidInfill, Slic3r::frTopSolidInfill, Slic3r::SlicingParameters::gap_object_support, Slic3r::SlicingParameters::gap_support_object, Slic3r::gcfMarlinFirmware, Slic3r::gcfMarlinLegacy, Slic3r::gcfRepetier, Slic3r::gcfRepRapFirmware, Slic3r::gcfRepRapSprinter, Slic3r::generate_object_layers(), Slic3r::ConfigOptionVector< int >::get_at(), Slic3r::get_bed_temp_key(), Slic3r::PrintBaseWithState< PrintStep, psCount >::get_plate_index(), has_belt_brim(), has_belt_purge_tower(), has_same_shrinkage_compensations(), has_tilted_belt(), has_wipe_tower(), Slic3r::imex_multicolor_block_reason(), Slic3r::imex_resolve_routing(), Slic3r::initial_layer_print_height(), Slic3r::InnerOuter, is_BBL_printer(), Slic3r::is_tree(), Slic3r::StringObjectException::is_warning, Slic3r::kImexPrimaryMode, L, Slic3r::layer_height(), Slic3r::SlicingParameters::layer_height, Slic3r::layered_print_cleareance_valid(), m_config, m_default_object_config, m_default_region_config, m_objects, MAX_LINE_WIDTH_MULTIPLIER, Slic3r::ModelObject::max_z(), Slic3r::PrintObjectBase::model_object(), Slic3r::ModelObject::name, Slic3r::nozzle_diameter(), Slic3r::nozzle_diameter_for_filament(), Slic3r::StringObjectException::object, Slic3r::SlicingParameters::object_print_z_min, Slic3r::SlicingParameters::object_print_z_uncompensated_max, Slic3r::StringObjectException::opt_key, Slic3r::StringObjectException::params, Slic3r::ImexRouting::primary_phys, Slic3r::ImexRouting::primary_unrouted, Slic3r::print_config_def, Slic3r::printable_height(), PrintObject, Slic3r::PrintObjectConfig, Slic3r::SlicingParameters::raft_layers(), Slic3r::ImexRouting::routed_heads, sequential_print_clearance_valid(), shrinkage_compensation(), Slic3r::PrintObject::slicing_parameters(), Slic3r::smpDefault, Slic3r::smpLightning, Slic3r::smpNone, Slic3r::smsDefault, Slic3r::smsTreeOrganic, Slic3r::StringObjectException::string, Slic3r::STRING_EXCEPT_FILAMENT_NOT_MATCH_BED_TYPE, Slic3r::STRING_EXCEPT_FILAMENTS_DIFFERENT_TEMP, Slic3r::STRING_EXCEPT_OBJECT_COLLISION_IN_LAYER_PRINT, Slic3r::STRING_EXCEPT_OBJECT_COLLISION_IN_SEQ_PRINT, Slic3r::support_material_flow(), Slic3r::support_material_interface_flow(), Slic3r::tlSmooth, Slic3r::StringObjectException::type, Slic3r::PrintObject::update_layer_height_profile(), and Slic3r::AdaptivePAProcessor::validate_adaptive_pa_model().

Referenced by anonymous_namespace{OrcaSlicer_profile_validator.cpp}::slice_two_color_cube_and_export().

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

◆ validate_compacted_wipe_tower_clearance()

void Slic3r::Print::validate_compacted_wipe_tower_clearance ( ) const
1497{
1498 // Nothing to check when the tower is not compacted: it then follows the object as usual and the
1499 // regular by-layer clearance check already covers it. Asking wipe_tower_sparse_layers_skipped()
1500 // rather than the raw option keeps this from rejecting plates whose tower is in fact full height.
1502 return;
1503
1504 const std::vector<std::vector<WipeTower::ToolChangeResult>> &tool_changes = m_wipe_tower_data.tool_changes;
1505 if (tool_changes.empty() || m_objects.empty())
1506 return;
1507
1508 // Same accumulation the G-code emitter runs, so validation and output cannot disagree.
1509 const std::vector<float> tower_z = compute_compacted_wipe_tower_z(tool_changes, float(m_config.z_offset.value));
1510
1511 // Wipe tower footprint: build it from the ACTUAL tool-change extrusions rather than the nominal
1512 // width x depth rectangle returned by first_layer_wipe_tower_corners(). With a rib wall the printed
1513 // wall bulges past the nominal box and the first-layer brim reaches even further; the nominal box
1514 // (m_wipe_tower_data.bbx) undercounts that outermost extent by several millimetres, which is
1515 // exactly the extent that decides how close the sweeping rod comes to a neighbouring object. The
1516 // extrusion end-points are stored in the wipe-tower local frame, so we map them to the bed frame
1517 // with the same transform the G-code emitter applies. The two emitters differ in where rib_offset
1518 // enters: WipeTowerIntegration::append_tcr() (type 1) rotates the point and then adds the offset,
1519 // append_tcr2() (type 2) adds it before rotating. On a rotated rib-wall tower the two land several
1520 // millimetres apart, which is exactly the margin this check measures, so follow the emitter in use.
1521 const Eigen::Rotation2Dd wt_rot(Geometry::deg2rad(m_config.wipe_tower_rotation_angle.value));
1522 const Vec2d wt_translate(m_config.wipe_tower_x.get_at(m_plate_index) + m_origin(0),
1523 m_config.wipe_tower_y.get_at(m_plate_index) + m_origin(1));
1524 const Vec2d rib_off = m_wipe_tower_data.rib_offset.cast<double>();
1525 const bool rib_off_rotates = this->wipe_tower_type() == WipeTowerType::Type2;
1526 auto to_bed = [&wt_rot, &wt_translate, &rib_off, rib_off_rotates](const Vec2d &pt) {
1527 return rib_off_rotates ? Vec2d(wt_rot * (pt + rib_off) + wt_translate) : Vec2d(wt_rot * pt + wt_translate + rib_off);
1528 };
1529
1530 Points tower_pts;
1531 for (const std::vector<WipeTower::ToolChangeResult> &layer : tool_changes) {
1532 if (layer.empty() || wipe_tower_layer_is_sparse(layer))
1533 continue;
1534 for (const WipeTower::ToolChangeResult &tcr : layer)
1535 for (size_t i = 0; i < tcr.extrusions.size(); ++i) {
1536 // A zero width marks a travel end-point. Keep it only when it opens a real extrusion, so
1537 // the hull covers the deposited material and nothing else; travels reach a bit further out
1538 // than the walls do.
1539 const WipeTower::Extrusion &e = tcr.extrusions[i];
1540 if (e.width == 0.f && (i + 1 == tcr.extrusions.size() || tcr.extrusions[i + 1].width == 0.f))
1541 continue;
1542 const Vec2d p = to_bed(Vec2d(e.pos.x(), e.pos.y()));
1543 tower_pts.emplace_back(scale_(p.x()), scale_(p.y()));
1544 }
1545 }
1546 if (tower_pts.empty())
1547 return;
1548
1549 const CompactedTowerZone zone = compacted_wipe_tower_zone(m_config, Geometry::convex_hull(tower_pts));
1550 if (zone.empty())
1551 return;
1552
1553 for (const PrintObject *object : m_objects) {
1554 const double object_top = unscaled<double>(object->max_z());
1555 for (const PrintInstance &instance : object->instances()) {
1556 const Polygon inst_hull = compacted_tower_print_instance_hull(*object, instance);
1557 if (inst_hull.points.empty())
1558 continue;
1559
1560 // Report the worst layer rather than the first offending one, it is the one that explains the
1561 // collision best. The rise has to be known before the clearance: it is what selects the
1562 // horizontal tier, the nozzle cone being out of the head body's reach.
1563 double max_rise = 0.;
1564 for (size_t i = 0; i < tool_changes.size(); ++i) {
1565 if (tool_changes[i].empty() || wipe_tower_layer_is_sparse(tool_changes[i]))
1566 continue;
1567 // Nothing above the current layer exists yet, so a tall object only counts up to it.
1568 const double rise = std::min(object_top, double(tool_changes[i].front().print_z)) - tower_z[i];
1569 if (rise > max_rise)
1570 max_rise = rise;
1571 }
1572
1573 const CompactedTowerClearance clearance = compacted_wipe_tower_clearance(m_config, zone, inst_hull, max_rise);
1574 if (max_rise <= clearance.allowed_rise + EPSILON)
1575 continue;
1576 // Same wording as compacted_wipe_tower_clearance_valid(): height-limit and too-close
1577 // share one message, since both are layout violations of "No sparse layers".
1578 throw Slic3r::SlicingError(compacted_wipe_tower_clearance_error());
1579 }
1580 }
1581}
bool wipe_tower_layer_is_sparse(const std::vector< WipeTower::ToolChangeResult > &layer_tool_changes)
Definition WipeTower.cpp:59
std::vector< float > compute_compacted_wipe_tower_z(const std::vector< std::vector< WipeTower::ToolChangeResult > > &tool_changes, float base_z)
Definition WipeTower.cpp:64

References Slic3r::CompactedTowerClearance::allowed_rise, Slic3r::ByLayer, Slic3r::compacted_tower_print_instance_hull(), Slic3r::compacted_wipe_tower_clearance(), Slic3r::compacted_wipe_tower_clearance_error(), Slic3r::compacted_wipe_tower_zone(), Slic3r::compute_compacted_wipe_tower_z(), Slic3r::Geometry::convex_hull(), Slic3r::Geometry::deg2rad(), Slic3r::CompactedTowerZone::empty(), empty(), EPSILON, m_config, m_objects, m_origin, Slic3r::PrintBaseWithState< PrintStep, psCount >::m_plate_index, m_wipe_tower_data, Slic3r::MultiPoint::points, Slic3r::WipeTower::Extrusion::pos, PrintObject, scale_, Slic3r::Type2, Slic3r::unscaled(), Slic3r::WipeTower::Extrusion::width, Slic3r::wipe_tower_layer_is_sparse(), Slic3r::wipe_tower_sparse_layers_skipped(), and wipe_tower_type().

Here is the call graph for this function:

◆ wipe_tower_data()

const WipeTowerData & Slic3r::Print::wipe_tower_data ( size_t filaments_cnt = 0) const
5206{
5207 // Until the tower is generated, size it with the estimate the GUI/CLI placement uses, so
5208 // validation cannot reject a position the clamp just accepted.
5209 if (is_step_done(psWipeTower) || filaments_cnt == 0)
5210 return m_wipe_tower_data;
5211
5212 double max_height = 0.;
5213 double layer_height = std::numeric_limits<double>::max();
5214 for (const PrintObject *object : m_objects) {
5215 max_height = std::max(max_height, unscale_(double(object->size().z())));
5216 layer_height = std::min(layer_height, object->config().layer_height.value);
5217 }
5218 if (max_height < EPSILON)
5219 return m_wipe_tower_data;
5220
5221 const WipeTowerFootprint footprint = estimate_wipe_tower_footprint(m_config, this->wipe_tower_type(), this->extruders(true), layer_height, max_height);
5222 WipeTowerData &data = const_cast<Print *>(this)->m_wipe_tower_data;
5223 data.depth = float(footprint.depth);
5224 data.width = float(footprint.width);
5225 data.brim_width = float(footprint.brim_width);
5226 return m_wipe_tower_data;
5227}
Print()=default
WipeTowerFootprint estimate_wipe_tower_footprint(const ConfigBase &config, WipeTowerType tower_type, const std::vector< unsigned int > &filament_slots, double layer_height, double max_object_height)
Definition WipeTowerEstimate.cpp:86

References Print(), Slic3r::WipeTowerData::brim_width, Slic3r::WipeTowerFootprint::brim_width, Slic3r::WipeTowerData::depth, Slic3r::WipeTowerFootprint::depth, EPSILON, Slic3r::estimate_wipe_tower_footprint(), extruders(), is_step_done(), Slic3r::layer_height(), m_config, m_objects, m_wipe_tower_data, PrintObject, Slic3r::psWipeTower, unscale_, Slic3r::WipeTowerData::width, Slic3r::WipeTowerFootprint::width, wipe_tower_data(), and wipe_tower_type().

Referenced by Slic3r::GCode::_do_export(), libvgcode::convert(), Slic3r::estimated_wipe_tower_footprint(), Slic3r::get_wipe_tower_extrusions_extents(), Slic3r::get_wipe_tower_priming_extrusions_extents(), Slic3r::layered_print_cleareance_valid(), Slic3r::GCode::process_layer_change_gcode(), Slic3r::GUI::GLCanvas3D::reload_scene(), Slic3r::GUI::GLCanvas3D::update_compacted_wipe_tower_clearance(), and wipe_tower_data().

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

◆ wipe_tower_type()

WipeTowerType Slic3r::Print::wipe_tower_type ( ) const
inline
1343{ return is_BBL_printer() ? WipeTowerType::Type1 : m_config.wipe_tower_type.value; }
@ Type1
Definition PrintConfig.hpp:114

References is_BBL_printer(), m_config, and Slic3r::Type1.

Referenced by Slic3r::GCode::_do_export(), _make_wipe_tower(), first_layer_wipe_tower_corners(), Slic3r::layered_print_cleareance_valid(), validate_compacted_wipe_tower_clearance(), and wipe_tower_data().

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

◆ GCode

friend class GCode
friend

References GCode.

Referenced by GCode.

◆ PrintObject

Member Data Documentation

◆ m_brimFilamentMap

std::map<ObjectID, unsigned int> Slic3r::Print::m_brimFilamentMap
private

Referenced by _make_skirt().

◆ m_brimMap

std::map<ObjectID, ExtrusionEntityCollection> Slic3r::Print::m_brimMap
private

◆ m_brimMapByInstance

std::map<ObjectInstanceID, ExtrusionEntityCollection> Slic3r::Print::m_brimMapByInstance
private

Referenced by _make_skirt().

◆ m_calib_params

Calib_Params Slic3r::Print::m_calib_params
private

◆ m_cancel_callback

cancel_callback_type Slic3r::PrintBase::m_cancel_callback = [](){}
privateinherited

◆ m_cancel_status

std::atomic<CancelStatus> Slic3r::PrintBase::m_cancel_status
privateinherited

◆ m_config

◆ m_config_index_generation

size_t Slic3r::Print::m_config_index_generation {0}
private

◆ m_conflict_result

ConflictResultOpt Slic3r::Print::m_conflict_result
private

◆ m_default_object_config

PrintObjectConfig Slic3r::Print::m_default_object_config
private

◆ m_default_region_config

◆ m_extruder_filament_info

std::vector<std::vector<DynamicPrintConfig> > Slic3r::Print::m_extruder_filament_info
private

◆ m_fake_wipe_tower

FakeWipeTower Slic3r::Print::m_fake_wipe_tower
private

◆ m_filament_index_map

FilamentIndexMap Slic3r::Print::m_filament_index_map
private

◆ m_filament_self_index

std::vector<int> Slic3r::Print::m_filament_self_index
private

◆ m_first_layer_convex_hull

Polygon Slic3r::Print::m_first_layer_convex_hull
private

◆ m_full_print_config

DynamicPrintConfig Slic3r::PrintBase::m_full_print_config
protectedinherited

Referenced by PrintBase(), and full_print_config().

◆ m_geometric_unprintable_filaments

std::vector<std::set<int> > Slic3r::Print::m_geometric_unprintable_filaments
private

◆ m_has_auto_filament_map_result

bool Slic3r::Print::m_has_auto_filament_map_result {false}
private

◆ m_has_shared_per_object_skirt

bool Slic3r::Print::m_has_shared_per_object_skirt { false }
private
1514{ false };

Referenced by _make_skirt(), and has_shared_per_object_skirt().

◆ m_id

ObjectID Slic3r::ObjectBase::m_id
privateinherited

◆ m_imex_slice_offset

Vec2d Slic3r::Print::m_imex_slice_offset { Vec2d::Zero() }
private

◆ m_isBBLPrinter

bool Slic3r::Print::m_isBBLPrinter = false
private

Referenced by is_BBL_printer(), and is_BBL_printer().

◆ m_layered_nozzle_group_result

std::shared_ptr<MultiNozzleUtils::LayeredNozzleGroupResult> Slic3r::Print::m_layered_nozzle_group_result
private

◆ m_missing_nozzle_group_logged

std::set<int> Slic3r::Print::m_missing_nozzle_group_logged
private

◆ m_model

Model Slic3r::PrintBase::m_model
protectedinherited

◆ m_modified_count

int Slic3r::Print::m_modified_count {0}
private
1575{0};

Referenced by get_modified_count().

◆ m_need_check_multi_filaments_compatibility

bool Slic3r::Print::m_need_check_multi_filaments_compatibility {true}
private

◆ m_no_check

bool Slic3r::PrintBase::m_no_check = false
protectedinherited

◆ m_nozzle_group_result

std::shared_ptr<MultiNozzleUtils::NozzleGroupResultBase> Slic3r::Print::m_nozzle_group_result
private

◆ m_nozzle_index_map

PrintIndexMap Slic3r::Print::m_nozzle_index_map
private

◆ m_objectBrimAreasByInstance

std::map<ObjectInstanceID, ExPolygons> Slic3r::Print::m_objectBrimAreasByInstance
private

Referenced by _make_skirt().

◆ m_objects

◆ m_ori_full_print_config

DynamicPrintConfig Slic3r::PrintBase::m_ori_full_print_config
protectedinherited

◆ m_origin

◆ m_pipeline_plugin_active

bool Slic3r::Print::m_pipeline_plugin_active { false }
private
1488{ false };

Referenced by process(), and run_pipeline_hook().

◆ m_placeholder_parser

PlaceholderParser Slic3r::PrintBase::m_placeholder_parser
protectedinherited

Referenced by PrintBase(), and placeholder_parser().

◆ m_plate_index

int Slic3r::PrintBase::m_plate_index { 0 }
protectedinherited
567{ 0 };

Referenced by get_plate_index(), and set_plate_index().

◆ m_plate_name

std::string Slic3r::PrintBase::m_plate_name
protectedinherited

Referenced by get_plate_name(), and set_plate_name().

◆ m_print_regions

◆ m_print_statistics

PrintStatistics Slic3r::Print::m_print_statistics
private

◆ m_same_shrinkage_compensations

bool Slic3r::Print::m_same_shrinkage_compensations { false }
private

◆ m_sequential_dynamic_orderings

std::map<const PrintObject*, ToolOrdering> Slic3r::Print::m_sequential_dynamic_orderings
private

◆ m_shrinkage_compensation

Vec3d Slic3r::Print::m_shrinkage_compensation { Vec3d::Ones() }
private
1506{ Vec3d::Ones() };

Referenced by clear(), shrinkage_compensation(), and update_shrinkage_compensation().

◆ m_skirt

ExtrusionEntityCollection Slic3r::Print::m_skirt
private

Referenced by _make_skirt(), and skirt().

◆ m_skirt_brim_groups

std::vector<SkirtBrimGroup> Slic3r::Print::m_skirt_brim_groups
private

Referenced by _make_skirt(), and skirt_brim_groups().

◆ m_skirt_convex_hull

Points Slic3r::Print::m_skirt_convex_hull
private

◆ m_slice_used_filaments

std::vector<unsigned int> Slic3r::Print::m_slice_used_filaments
private

◆ m_slice_used_filaments_first_layer

std::vector<unsigned int> Slic3r::Print::m_slice_used_filaments_first_layer
private

◆ m_slice_used_mixed_filaments

std::vector<unsigned int> Slic3r::Print::m_slice_used_mixed_filaments
private

◆ m_state

◆ m_state_mutex

std::mutex Slic3r::PrintBase::m_state_mutex
mutableprivateinherited

Referenced by state_mutex().

◆ m_statistics_by_extruder_count

StatisticsByExtruderCount Slic3r::Print::m_statistics_by_extruder_count
private

◆ m_status_callback

◆ m_support_used

bool Slic3r::Print::m_support_used {false}
private
1561{false};

Referenced by apply(), and is_support_used().

◆ m_tool_ordering

ToolOrdering Slic3r::Print::m_tool_ordering
private

Referenced by tool_ordering().

◆ m_wipe_tower_data

◆ PrintTryCancel

friend Slic3r::PrintBase::PrintTryCancel
privateinherited

Referenced by make_try_cancel().

◆ s_last_id

size_t Slic3r::ObjectBase::s_last_id = 0
staticprivateinherited

Referenced by generate_new_id().

◆ s_slicing_pipeline_hook_fn

Print::SlicingPipelineHookFn Slic3r::Print::s_slicing_pipeline_hook_fn = nullptr
staticprivate

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/PrintApply.cpp