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

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

Inheritance diagram for Slic3r::BeltGCode:
Collaboration diagram for Slic3r::BeltGCode:

Public Member Functions

void do_export (Print *print, const char *path, GCodeProcessorResult *result=nullptr, ThumbnailsGeneratorCallback thumbnail_cb=nullptr)
void export_layer_filaments (GCodeProcessorResult *result)
void set_gcode_offset (double x, double y)
void set_gcode_offset_with_imex_shift (double x, double y, double imex_x, double imex_y)
const Vec2d & origin () const
void set_origin (const Vec2d &pointf)
void set_origin (const coordf_t x, const coordf_t y)
Point last_pos () const
Vec2d point_to_gcode (const Point &point) const
Vec3d point_to_gcode (const Point3 &point) const
Point gcode_to_point (const Vec2d &point) const
Vec2d point_to_gcode_quantized (const Point &point) const
Vec3d point_to_gcode_quantized (const Point3 &point) const
const FullPrintConfig & config () const
const Layer * layer () const
GCodeWriter & writer ()
const GCodeWriter & writer () const
PlaceholderParser & placeholder_parser ()
const PlaceholderParser & placeholder_parser () const
std::string placeholder_parser_process (const std::string &name, const std::string &templ, unsigned int current_filament_id, const DynamicConfig *config_override=nullptr)
bool enable_cooling_markers () const
std::string extrusion_role_to_string_for_parser (const ExtrusionRole &)
float interpolate_value_across_layers (float start_value, float end_value, float step=0.0f) const
unsigned int layer_count () const
void set_layer_count (unsigned int value)
void apply_print_config (const PrintConfig &print_config)
std::string travel_to (const Point &point, ExtrusionRole role, std::string comment, double z=DBL_MAX)
bool needs_retraction (const Polyline &travel, ExtrusionRole role, LiftType &lift_type)
std::string retract (bool toolchange=false, bool is_last_retraction=false, LiftType lift_type=LiftType::NormalLift, bool apply_instantly=false, ExtrusionRole role=erNone)
std::string unretract (float extra_retract=0.f)
std::string set_extruder (unsigned int extruder_id, double print_z, bool by_object=false, int toolchange_temp_override=-1, bool defer_temp_wait=false)
std::string set_filament_pressure_advance (unsigned int filament_id, int tool=-1)
bool is_BBL_Printer ()
WipeTowerType wipe_tower_type ()
void assign_object_label_ids (Print &print)
std::string set_object_info (Print *print)
size_t get_filament_config_index (int filament_id) const
size_t get_filament_config_index (int filament_id, size_t layer_id) const
size_t get_nozzle_config_index (int filament_id) const

Static Public Member Functions

static void append_full_config (const Print &print, std::string &str)

Protected Member Functions

void init_belt_writer (Print &print) override
void write_belt_header (GCodeOutputStream &file, const Print &print) override
void on_set_origin (const PrintObject *obj, const Point &inst_shift) override
virtual bool should_disable_arc_fitting () const
void _do_export (Print &print, GCodeOutputStream &file, ThumbnailsGeneratorCallback thumbnail_cb)
std::string generate_skirt (const Print &print, const ExtrusionEntityCollection &skirt, const Point &offset, const float skirt_start_angle, const LayerTools &layer_tools, const Layer &layer, unsigned int extruder_id, std::vector< coordf_t > &skirt_done)
std::string generate_object_skirt_group (const Print &print, const PrintObject &object, size_t instance_id, const LayerTools &layer_tools, const Layer &layer, unsigned int extruder_id)
std::string generate_object_brim (const Print &print, const PrintObject &object, size_t instance_id, bool first_layer, unsigned int extruder_id, const Layer *object_layer)
LayerResult process_belt_brim_layer (const Print &print, const std::vector< LayerToPrint > &layers, const LayerTools &layer_tools, const bool last_layer, const size_t single_object_instance_idx, const int most_used_extruder)
std::string process_layer_change_gcode (const Print &print, coordf_t print_z, int most_used_extruder, bool has_wipe_tower)
std::string emit_belt_brim_bands (const Print &print, const std::vector< LayerToPrint > &layers, const size_t single_object_instance_idx, const unsigned int extruder_id)
LayerResult process_layer (const Print &print, const std::vector< LayerToPrint > &layers, const LayerTools &layer_tools, const bool last_layer, const std::vector< const PrintInstance * > *ordering, const int most_used_extruder, const size_t single_object_idx=size_t(-1), const bool prime_extruder=false)
void process_layers (const Print &print, const ToolOrdering &tool_ordering, const std::vector< const PrintInstance * > &print_object_instances_ordering, const std::vector< std::pair< coordf_t, std::vector< LayerToPrint > > > &layers_to_print, GCodeOutputStream &output_stream)
void process_layers (const Print &print, const ToolOrdering &tool_ordering, std::vector< LayerToPrint > layers_to_print, const size_t single_object_idx, GCodeOutputStream &output_stream, const bool prime_extruder=false)
void check_placeholder_parser_failed ()
size_t cur_extruder_index () const
size_t get_extruder_id (unsigned int filament_id) const
void update_placeholder_parser_with_variant_params ()
void set_last_pos (const Point &pos)
void set_last_pos (const Point3 &pos)
bool last_pos_defined () const
void set_extruders (const std::vector< unsigned int > &extruder_ids)
std::string preamble ()
std::string change_layer (coordf_t print_z)
void mass_load_limited_machine_acceleration (const PrintStatistics &curr_print_statistics, const Print &print, double &y_acceleration_limit_res, double &accumulated_mass_res)
std::string extrude_entity (const ExtrusionEntity &entity, const std::string &description="", double speed=-1., const std::vector< const ExtrusionEntity * > &region_perimeters={}, const WipeInwardSupport *wipe_support=nullptr)
std::string extrude_loop (const ExtrusionLoop &loop, const std::string &description, double speed=-1., const std::vector< const ExtrusionEntity * > &region_perimeters={}, const Point *start_point=nullptr, const WipeInwardSupport *wipe_support=nullptr)
std::string extrude_multi_path (const ExtrusionMultiPath &multipath, const std::string &description="", double speed=-1.)
std::string extrude_path (const ExtrusionPath &path, const std::string &description="", double speed=-1.)
std::vector< InstanceToPrint > sort_print_object_instances (std::vector< ObjectByExtruder > &objects_by_extruder, const std::vector< LayerToPrint > &layers, const std::vector< const PrintInstance * > *ordering, const size_t single_object_instance_idx)
std::string extrude_perimeters (const Print &print, const std::vector< ObjectByExtruder::Island::Region > &by_region, bool is_first_layer, bool is_infill_first, bool unsupported_loops_only=false)
std::string extrude_infill (const Print &print, const std::vector< ObjectByExtruder::Island::Region > &by_region, bool ironing)
std::string extrude_support (const ExtrusionEntityCollection &support_fills, const ExtrusionRole support_extrusion_role)
void compute_farthest_point (const std::vector< LayerToPrint > &layers, int most_used_extruder, const std::map< std::pair< const SupportLayer *, ExtrusionRole >, unsigned int > &support_filaments)
DynamicConfig timelapse_gcode_config (const Print &print, coordf_t print_z, int most_used_extruder, const std::vector< const PrintObject * > *printed_objects, bool skip_pos_pick)
std::string generate_timelapse_gcode (const Print &print, coordf_t print_z, int most_used_extruder, const std::set< size_t > *layer_object_label_ids, const std::vector< const PrintObject * > *printed_objects, bool skip_pos_pick=false)
LiftType to_lift_type (ZHopType z_hop_types)
const std::string & instance_name (const PrintInstance &instance)
std::string _encode_label_ids_to_base64 (std::vector< size_t > ids)
std::optional< ToolchangePositionState > capture_toolchange_position ()
void synchronize_toolchange_position (const ToolchangePositionState &before, const std::string &emitted_gcode)
int get_bed_temperature (const int extruder_id, const bool is_first_layer, const BedType bed_type) const
int get_highest_bed_temperature (const bool is_first_layer, const Print &print) const
void update_layer_related_config (int layer_id)
double calc_max_volumetric_speed (const double layer_height, const double line_width, const std::string co_str)
std::string _extrude (const ExtrusionPath &path, const std::string &path_description="", double speed=-1)
bool _needSAFC (const ExtrusionPath &path)
void print_machine_envelope (GCodeOutputStream &file, Print &print)
void _print_first_layer_bed_temperature (GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait)
void _print_first_layer_extruder_temperatures (GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait)
bool on_first_layer () const
bool on_first_layer (const Vec3d &point_slicing_mm) const
int effective_layer_index_for_point (const Vec3d &point_slicing_mm) const
double first_layer_band_mm () const
bool belt_height_above_floor (const Vec3d &point_slicing_mm, double &height_mm) const
int belt_layer_past_first_layer_band (const Layer *object_layer) const
int layer_id () const
bool object_layer_over_raft () const

Static Protected Member Functions

static std::vector< LayerToPrint > collect_layers_to_print (const PrintObject &object, bool skip_empty_first_layer=false)
static std::vector< std::pair< coordf_t, std::vector< LayerToPrint > > > collect_layers_to_print (const Print &print)

Protected Attributes

double m_multi_flow_segment_path_average_mm3_per_mm = 0
bool m_multi_flow_segment_path_pa_set = false
double m_last_mm3_mm = 0
std::set< ObjectInstanceID > m_objsWithBrim
SeamPlacer m_seam_placer
std::map< unsigned int, IslandOrderCacheEntry > m_ordering_cache
ExtrusionQualityEstimator m_extrusion_quality_estimator
Vec2d m_origin
FullPrintConfig m_config
DynamicConfig m_calib_config
double m_scaled_resolution
GCodeWriter m_writer
struct Slic3r::GCode::PlaceholderParserIntegration m_placeholder_parser_integration
OozePrevention m_ooze_prevention
Wipe m_wipe
AvoidCrossingPerimeters m_avoid_crossing_perimeters
ExclusionVolumeTravelAvoidance m_exclusion_volume_travel_avoidance
RetractWhenCrossingPerimeters m_retract_when_crossing_perimeters
TimelapsePosPicker m_timelapse_pos_picker
bool m_timelapse_uses_pos { false }
bool m_timelapse_uses_clear_to_x0 { false }
FarthestPointTimelapseContext m_farthest_point_timelapse
bool m_enable_loop_clipping
bool m_resonance_avoidance
bool m_enable_cooling_markers
bool m_enable_exclude_object
std::vector< size_t > m_label_objects_ids
std::unordered_map< const PrintInstance *, std::string > m_instance_names
std::array< bool, ExtrusionRole::erCount > m_is_role_based_fan_on
std::array< int, ExtrusionRole::erCount > m_role_based_fan_marker_layer
bool m_enable_extrusion_role_markers
ExtrusionRole m_last_processor_extrusion_role
unsigned int m_layer_count
int m_layer_index
const Layer * m_layer
bool m_object_layer_over_raft
ExtrusionRole m_last_extrusion_role
ExtrusionRole m_last_notgapfill_extrusion_role
float m_last_height { 0.0f }
float m_last_layer_z { 0.0f }
float m_max_layer_z { 0.0f }
float m_last_width { 0.0f }
double m_last_layer_accumulated_mass { 0.0 }
Point3 m_last_pos
bool m_last_pos_defined
std::unique_ptr< CoolingBuffer > m_cooling_buffer
std::unique_ptr< SpiralVase > m_spiral_vase
std::unique_ptr< PressureEqualizer > m_pressure_equalizer
std::unique_ptr< AdaptivePAProcessor > m_pa_processor
std::string m_imex_parallel_mode
std::map< int, int > m_imex_head_filament_map
std::unique_ptr< WipeTowerIntegration > m_wipe_tower
std::unique_ptr< SmallAreaInfillFlowCompensator > m_small_area_infill_flow_compensator
std::vector< std::vector< coordf_t > > m_skirt_group_done
bool m_brim_done
bool m_second_layer_things_done
std::pair< const PrintObject *, Point > m_last_obj_copy
int m_timelapse_warning_code = 0
bool m_support_traditional_timelapse = true
Print * m_print {nullptr}
std::vector< const PrintObject * > m_printed_objects
GCodeProcessor m_processor
std::optional< GCodeProcessor::PositionState > m_pending_start_gcode_position
std::unique_ptr< FanMover > m_fan_mover
Print * m_curr_print = nullptr
unsigned int m_toolchange_count
coordf_t m_nominal_z
double m_sub_layer_flow_ratio = 0.0
double m_sub_layer_height = 0.0
bool m_need_change_layer_lift_z = false
int m_start_gcode_filament = -1
std::string m_filament_instances_code
size_t m_cur_layer_idx {0}
ConfigIndexCache m_filament_index_cache
ConfigIndexCache m_nozzle_index_cache
std::optional< coordf_t > m_belt_brim_z
const PrintObject * m_belt_floor_object {nullptr}
bool m_belt_in_band {false}
std::set< unsigned int > m_initial_layer_extruders
std::vector< std::vector< unsigned int > > m_sorted_layer_filaments

Detailed Description

Member Function Documentation

◆ _do_export()

void Slic3r::GCode::_do_export ( Print & print,
GCodeOutputStream & file,
ThumbnailsGeneratorCallback thumbnail_cb )
protectedinherited
3387{
3388 PROFILE_FUNC();
3389
3390 m_print = &print;
3391 m_timelapse_pos_picker.init(&print,m_writer.get_xy_offset().cast<coord_t>());
3392 {
3393 // Runs the costly per-layer snapshot picker only for a template that names its results.
3394 const std::string &templ = print.config().time_lapse_gcode.value;
3395 m_timelapse_uses_pos = templ.find("timelapse_pos") != std::string::npos ||
3396 templ.find("has_timelapse_safe_pos") != std::string::npos;
3397 m_timelapse_uses_clear_to_x0 = templ.find("clear_to_x0") != std::string::npos;
3398 }
3399 // init as filament map
3401
3402 DoExport::init_gcode_processor(print.config(), m_processor, print.get_layered_nozzle_group_result());
3404 const bool is_bbl_printers = print.is_BBL_printer();
3405 const bool skip_config_block = print.config().gcode_skip_config_block;
3406 const WipeTowerType wipe_tower_type = print.wipe_tower_type();
3407 m_calib_config.clear();
3408 // Orca: Calibration overrides are reapplied after object/region settings in _extrude().
3409 // Keep inward wiping from masking retraction and pressure advance artifacts.
3410 switch (print.calib_mode()) {
3416 m_calib_config.set_key_value("wipe_inward", new ConfigOptionBool(false));
3417 break;
3418 default:
3419 break;
3420 }
3421 // resets analyzer's tracking data
3422 m_last_height = 0.f;
3423 m_last_layer_z = 0.f;
3424 m_max_layer_z = 0.f;
3425 m_last_width = 0.f;
3427 m_is_role_based_fan_on.fill(false);
3429
3430 m_fan_mover.reset();
3431 m_ordering_cache.clear();
3432
3433 // Belt printer: initialize belt-specific writer via virtual hook.
3434 this->init_belt_writer(print);
3435 m_writer.set_is_bbl_machine(is_bbl_printers);
3436
3437 // G-code axis remap. Only belt printers get one (see
3438 // BeltTransformPipeline::axis_remap_enabled): a remap left in a profile must
3439 // not change a non-belt print. Sync the writer's remap state to the current
3440 // export UNCONDITIONALLY — even at the identity mapping (0,1,2) — so a reused
3441 // writer never retains a stale non-identity mapping from a prior export.
3442 // has_axis_remap() returns false at identity, so identity/default output stays
3443 // unchanged.
3444 {
3445 const bool remap = BeltTransformPipeline::axis_remap_enabled(print.config());
3446 int rx = remap ? int(print.config().gcode_remap_x.value) : int(RemapAxis::PosX);
3447 int ry = remap ? int(print.config().gcode_remap_y.value) : int(RemapAxis::PosY);
3448 int rz = remap ? int(print.config().gcode_remap_z.value) : int(RemapAxis::PosZ);
3449 m_writer.set_axis_remap(rx, ry, rz);
3450 BoundingBoxf bbox_bed(print.config().printable_area.values);
3451 m_writer.set_build_volume_max(Vec3d(bbox_bed.max.x(), bbox_bed.max.y(),
3452 print.config().printable_height.value));
3453 }
3454
3455 // Belt writers only: travel-speed selection becomes per-point (see
3456 // GCodeWriter::uses_pointwise_travel_speed()), which must not change for
3457 // non-belt printers. The writer gets the same test the extrusions use, so a
3458 // travel is judged against the belt surface (belt_height_above_floor) exactly
3459 // like the path it leads to. Writer points carry the G-code origin and
3460 // extruder offset that point_to_gcode() added; the belt surface is described
3461 // in the object's own frame.
3462 if (print.config().belt_printer.value) {
3463 m_writer.set_first_layer_point_test([this](const Vec3d &point_logical) {
3464 const Vec2d extruder_offset = m_writer.filament() != nullptr ? EXTRUDER_CONFIG(extruder_offset) : Vec2d::Zero();
3465 // Undo what point_to_gcode() added (m_origin, minus the extruder offset) and
3466 // what the writer then took off (its XY offset, the plate origin).
3467 const Vec2d plate_offset = m_writer.get_xy_offset().cast<double>();
3468 return this->on_first_layer(Vec3d(point_logical.x() - (m_origin.x() - plate_offset.x()) + extruder_offset.x(),
3469 point_logical.y() - (m_origin.y() - plate_offset.y()) + extruder_offset.y(),
3470 point_logical.z()));
3471 });
3472 }
3473
3474 // How many times will be change_layer() called?
3475 // change_layer() in turn increments the progress bar status.
3476 m_layer_count = 0;
3477 // On a belt, collect_layers_to_print() drops the layers that print nothing (an
3478 // object's empty lead-in), so they must not be counted here either or the layer
3479 // count in the file disagrees with its layer changes.
3480 const bool belt = print.config().belt_printer.value;
3481 if (print.config().print_sequence == PrintSequence::ByObject) {
3482 // Add each of the object's layers separately.
3483 for (auto object : print.objects()) {
3484 std::vector<coordf_t> zs;
3485 zs.reserve(object->layers().size() + object->support_layers().size());
3486 for (auto layer : object->layers())
3487 if (! belt || belt_object_layer_prints_something(*object, *layer))
3488 zs.push_back(layer->print_z);
3489 for (auto layer : object->support_layers())
3490 if (! belt || layer->has_extrusions())
3491 zs.push_back(layer->print_z);
3492 // Belt brim apron bands each get their own change_layer() call.
3493 for (const BeltBrimBand &band : object->belt_brim_prologue())
3494 zs.push_back(band.print_z);
3495 if (zs.empty())
3496 continue;
3497 std::sort(zs.begin(), zs.end());
3498 //BBS: merge numerically very close Z values.
3499 auto end_it = std::unique(zs.begin(), zs.end());
3500 unsigned int temp_layer_count = (unsigned int)(end_it - zs.begin());
3501 for (auto it = zs.begin(); it != end_it - 1; it++) {
3502 if (abs(*it - *(it + 1)) < EPSILON)
3503 temp_layer_count--;
3504 }
3505 m_layer_count += (unsigned int)(object->instances().size() * temp_layer_count);
3506 }
3507 } else {
3508 // Print all objects with the same print_z together.
3509 std::vector<coordf_t> zs;
3510 for (auto object : print.objects()) {
3511 zs.reserve(zs.size() + object->layers().size() + object->support_layers().size());
3512 for (auto layer : object->layers())
3513 if (! belt || belt_object_layer_prints_something(*object, *layer))
3514 zs.push_back(layer->print_z);
3515 for (auto layer : object->support_layers())
3516 if (! belt || layer->has_extrusions())
3517 zs.push_back(layer->print_z);
3518 // See the ByObject branch: apron bands are real printed layers.
3519 for (const BeltBrimBand &band : object->belt_brim_prologue())
3520 zs.push_back(band.print_z);
3521 }
3522 if (!zs.empty())
3523 {
3524 std::sort(zs.begin(), zs.end());
3525 //BBS: merge numerically very close Z values.
3526 auto end_it = std::unique(zs.begin(), zs.end());
3527 m_layer_count = (unsigned int)(end_it - zs.begin());
3528 for (auto it = zs.begin(); it != end_it - 1; it++) {
3529 if (abs(*it - *(it + 1)) < EPSILON)
3530 m_layer_count--;
3531 }
3532 }
3533 }
3534 print.throw_if_canceled();
3535
3537 this->apply_print_config(print.config());
3538 m_config.apply(print.default_object_config());
3539 m_config.apply(print.default_region_config());
3540
3541 //m_volumetric_speed = DoExport::autospeed_volumetric_limit(print);
3542 print.throw_if_canceled();
3543
3544 if (print.config().spiral_mode.value)
3545 m_spiral_vase = make_unique<SpiralVase>(print.config());
3546
3547 if (print.config().max_volumetric_extrusion_rate_slope.value > 0){
3550 } else
3552
3553 if (m_config.small_area_infill_flow_compensation.value && !m_config.small_area_infill_flow_compensation_model.empty())
3555
3556 // Prepare the helper object for replacing placeholders in custom G-code and output filename.
3557 m_placeholder_parser_integration.parser = print.placeholder_parser();
3558 m_placeholder_parser_integration.parser.update_timestamp();
3559 m_placeholder_parser_integration.parser.update_user_name();
3560 m_placeholder_parser_integration.context.rng = std::mt19937(std::chrono::high_resolution_clock::now().time_since_epoch().count());
3561 // Enable passing global variables between PlaceholderParser invocations.
3562 m_placeholder_parser_integration.context.global_config = std::make_unique<DynamicConfig>();
3563 print.update_object_placeholders(m_placeholder_parser_integration.parser.config_writable(), ".gcode");
3564
3565 // Get optimal tool ordering to minimize tool switches of a multi-exruder print.
3566 // For a print by objects, find the 1st printing object.
3567 ToolOrdering tool_ordering;
3568 unsigned int initial_extruder_id = (unsigned int)-1;
3569 //BBS: first non-support filament extruder
3570 unsigned int initial_non_support_extruder_id = (unsigned int) -1;
3571 unsigned int final_extruder_id = (unsigned int)-1;
3572 bool has_wipe_tower = false;
3573 print.m_statistics_by_extruder_count.clear();
3574 std::vector<int> first_filaments;
3575 std::vector<int> first_non_support_filaments;
3576 std::vector<const PrintInstance*> print_object_instances_ordering;
3577 std::vector<const PrintInstance*>::const_iterator print_object_instance_sequential_active;
3578 std::vector<const PrintInstance *>::const_iterator first_has_extrude_print_object;
3579 //resize
3580 first_non_support_filaments.resize(print.config().nozzle_diameter.size(), -1);
3581 first_filaments.resize(print.config().nozzle_diameter.size(), -1);
3582 float max_additional_fan = 0.f;
3583 // Sequential selector prints consume the per-object plans cached by Print::process — they were
3584 // planned with cross-object nozzle-status threading and match the published stitched result; a
3585 // fresh construction here would re-plan from a different seed. Static sequential prints keep
3586 // the fresh per-object construction (byte-identical output).
3587 const auto &seq_dynamic_orderings = print.sequential_dynamic_orderings();
3588 const bool use_seq_dynamic_cache = print.is_dynamic_group_reorder() && !seq_dynamic_orderings.empty();
3589 if (print.config().print_sequence == PrintSequence::ByObject) {
3590 // Order object instances for sequential print.
3591 print_object_instances_ordering = sort_object_instances_by_model_order(print);
3592// print_object_instances_ordering = sort_object_instances_by_max_z(print);
3593 // Find the 1st printing object, find its tool ordering and the initial extruder ID.
3594 print_object_instance_sequential_active = print_object_instances_ordering.begin();
3595 first_has_extrude_print_object = print_object_instance_sequential_active;
3596 bool find_fist_non_support_filament = false;
3597 for (; print_object_instance_sequential_active != print_object_instances_ordering.end(); ++ print_object_instance_sequential_active) {
3598 auto cached_ordering = use_seq_dynamic_cache ? seq_dynamic_orderings.find((*print_object_instance_sequential_active)->print_object) : seq_dynamic_orderings.end();
3599 if (cached_ordering != seq_dynamic_orderings.end()) {
3600 tool_ordering = cached_ordering->second;
3601 } else {
3602 tool_ordering = ToolOrdering(*(*print_object_instance_sequential_active)->print_object, initial_extruder_id);
3603 tool_ordering.sort_and_build_data(*(*print_object_instance_sequential_active)->print_object,initial_extruder_id);
3604 }
3605 float temp_max_additional_fan = tool_ordering.cal_max_additional_fan(print.config());
3606 if(temp_max_additional_fan > max_additional_fan )
3607 max_additional_fan = temp_max_additional_fan;
3608 if (!find_fist_non_support_filament && tool_ordering.first_extruder() != (unsigned int) -1) {
3609 //BBS: try to find the non-support filament extruder if is multi color and initial_extruder is support filament
3610 if (initial_extruder_id == (unsigned int) -1) {
3611 initial_extruder_id = tool_ordering.first_extruder();
3612 first_has_extrude_print_object = print_object_instance_sequential_active;
3613 }
3614
3615 find_fist_non_support_filament = tool_ordering.cal_non_support_filaments(print.config(), initial_non_support_extruder_id, first_non_support_filaments, first_filaments);
3616 }
3617 }
3618 if (initial_extruder_id == static_cast<unsigned int>(-1))
3619 // No object to print was found, cancel the G-code export.
3620 throw Slic3r::SlicingError(_(L("No object can be printed. It may be too small.")));
3621 // We don't allow switching of extruders per layer by Model::custom_gcode_per_print_z in sequential mode.
3622 // Use the extruder IDs collected from Regions.
3623 this->set_extruders(print.extruders());
3624
3625 has_wipe_tower = print.has_wipe_tower() && tool_ordering.has_wipe_tower();
3626 } else {
3627 // Find tool ordering for all the objects at once, and the initial extruder ID.
3628 // If the tool ordering has been pre-calculated by Print class for wipe tower already, reuse it.
3629 tool_ordering = print.tool_ordering();
3630 tool_ordering.assign_custom_gcodes(print);
3631 float temp_max_additional_fan = tool_ordering.cal_max_additional_fan(print.config());
3632 if(temp_max_additional_fan > max_additional_fan )
3633 max_additional_fan = temp_max_additional_fan;
3634 if (tool_ordering.all_extruders().empty())
3635 // No object to print was found, cancel the G-code export.
3636 throw Slic3r::SlicingError(_(L("No object can be printed. It may be too small.")));
3637 has_wipe_tower = print.has_wipe_tower() && tool_ordering.has_wipe_tower();
3638 // Orca: support all extruder priming
3639 initial_extruder_id = (wipe_tower_type == WipeTowerType::Type2 && has_wipe_tower && !print.config().single_extruder_multi_material_priming) ?
3640 // The priming towers will be skipped.
3641 tool_ordering.all_extruders().back() :
3642 // Don't skip the priming towers.
3643 tool_ordering.first_extruder();
3644
3645 //BBS: try to find the non-support filament extruder if is multi color and initial_extruder is support filament
3646 if (initial_extruder_id != static_cast<unsigned int>(-1)) {
3647 // BBS: try to find the non-support filament extruder if is multi color and initial_extruder is support filament
3648 // check if has non support filaments
3649 tool_ordering.cal_non_support_filaments(print.config(), initial_non_support_extruder_id, first_non_support_filaments, first_filaments);
3650 }
3651
3652 // In non-sequential print, the printing extruders may have been modified by the extruder switches stored in Model::custom_gcode_per_print_z.
3653 // Therefore initialize the printing extruders from there.
3654 this->set_extruders(tool_ordering.all_extruders());
3655 print_object_instances_ordering =
3656 // By default, order object instances using nearest-neighbor chaining plus
3657 // 2-opt and crossing-removal post-processing.
3658 (print.config().print_order == PrintOrder::Default ? chain_print_object_instances(print)
3659 // Snake: serpentine row traversal + 2-opt
3660 : (print.config().print_order == PrintOrder::Snake ? chain_print_object_instances_snake(print)
3661 // Best of all: run every strategy, pick the shortest total path
3662 : (print.config().print_order == PrintOrder::BestOfStrategies ? chain_print_object_instances_best_of(print)
3663 // Otherwise same order as the object list
3665
3666
3667
3668
3669 }
3670 if (initial_extruder_id == (unsigned int)-1) {
3671 // Nothing to print!
3672 initial_extruder_id = 0;
3673 initial_non_support_extruder_id = 0;
3674 }
3675
3676 //could not find non support filmanet, use fisrt print filament
3677 if (initial_non_support_extruder_id == (unsigned int) -1)
3678 initial_non_support_extruder_id = initial_extruder_id;
3679
3680 print.throw_if_canceled();
3681
3682 // Taken before the object loop, which reassigns initial_extruder_id when printing by object.
3683 const int compat_first_layer_bed_temp = m_config.bed_temperature_formula == BedTempFormula::btfHighestTemp ?
3684 get_highest_bed_temperature(true, print) :
3685 get_bed_temperature(initial_extruder_id, true, print.config().curr_bed_type);
3686 const int compat_first_layer_nozzle_temp =
3687 print.config().nozzle_temperature_initial_layer.get_at(get_filament_config_index((int) initial_extruder_id));
3688
3689 int extruder_id = get_extruder_id(initial_extruder_id);
3690
3692 m_cooling_buffer->set_current_extruder(initial_extruder_id, extruder_id);
3693
3694 // Orca: Initialise AdaptivePA processor filter
3695 m_pa_processor = std::make_unique<AdaptivePAProcessor>(*this);
3696
3697 // Update output variables after the extruders were initialized.
3699
3700 // Let the start-up script prime the 1st printing tool.
3701
3702 auto match_physical_extruder_for_each_filament = [](std::vector<int> &filaments, const FullPrintConfig &config) {
3703 // match the filament to the physical extruder
3704 std::vector<int> physicial_first_filaments;
3705 physicial_first_filaments.resize(filaments.size());
3706 for (int extruder_id = 0; extruder_id < filaments.size(); extruder_id++) {
3707 physicial_first_filaments[config.physical_extruder_map.get_at(extruder_id)] = filaments[extruder_id];
3708 }
3709 filaments = physicial_first_filaments;
3710 };
3711 match_physical_extruder_for_each_filament(first_filaments, m_config);
3712 this->placeholder_parser().set("first_tools", new ConfigOptionInts(first_filaments));
3713 this->placeholder_parser().set("first_filaments", new ConfigOptionInts(first_filaments));
3714 this->placeholder_parser().set("initial_tool", initial_extruder_id);
3715 this->placeholder_parser().set("initial_extruder", initial_extruder_id);
3716 //BBS
3717 match_physical_extruder_for_each_filament(first_non_support_filaments, m_config);
3718
3719 // Logical nozzle grouping for this print (null on paths that don't populate it).
3720 auto group_result = m_print->get_layered_nozzle_group_result();
3721 std::vector<int> first_non_support_hotends;
3722 first_non_support_hotends.reserve(first_non_support_filaments.size());
3723 for (int filament_id : first_non_support_filaments)
3724 first_non_support_hotends.push_back(filament_id < 0 ? -1 :
3725 first_hotend_id_for_gcode_placeholder(group_result, is_bbl_printers, filament_id, (int) get_extruder_id(filament_id)));
3726
3727 this->placeholder_parser().set("first_non_support_tools", new ConfigOptionInts(first_non_support_filaments));
3728 this->placeholder_parser().set("first_non_support_filaments", new ConfigOptionInts(first_non_support_filaments));
3729 this->placeholder_parser().set("first_non_support_hotend", new ConfigOptionInts(first_non_support_hotends));
3730 this->placeholder_parser().set("initial_no_support_tool", initial_non_support_extruder_id);
3731 this->placeholder_parser().set("initial_no_support_extruder", initial_non_support_extruder_id);
3732 this->placeholder_parser().set("initial_no_support_hotend",
3733 first_hotend_id_for_gcode_placeholder(group_result, is_bbl_printers, (int) initial_non_support_extruder_id,
3734 (int) get_extruder_id(initial_non_support_extruder_id)));
3735 this->placeholder_parser().set("current_extruder", initial_extruder_id);
3736 this->placeholder_parser().set("current_hotend",
3737 first_hotend_id_for_gcode_placeholder(group_result, is_bbl_printers, (int) initial_extruder_id, extruder_id));
3738 this->placeholder_parser().set("current_filament_id", (int) initial_extruder_id);
3739 this->placeholder_parser().set("current_extruder_id", extruder_id);
3740 this->placeholder_parser().set("current_nozzle_id",
3741 first_nozzle_id_for_gcode_placeholder(group_result, (int) initial_extruder_id, extruder_id));
3742 // Initial filament/nozzle vocabulary
3743 this->placeholder_parser().set("initial_filament_id", (int) initial_extruder_id);
3744 this->placeholder_parser().set("initial_no_support_filament_id", (int) initial_non_support_extruder_id);
3745 this->placeholder_parser().set("initial_nozzle_id", first_nozzle_id_for_gcode_placeholder(group_result, (int) initial_extruder_id, extruder_id));
3746 this->placeholder_parser().set("nozzle_diameter_at_nozzle_id", new ConfigOptionFloats(get_nozzle_diameters_by_nozzle_id(group_result.get())));
3747 this->placeholder_parser().set("nozzle_volume_types", new ConfigOptionStrings(get_nozzle_volume_types_by_nozzle_id(group_result.get())));
3748 //Orca: set the key for compatibilty, scalar values for the initial extruder (variant-aware)
3749 {
3750 size_t fi = get_filament_config_index(initial_extruder_id);
3751 this->placeholder_parser().set("retraction_distance_when_cut", m_config.retraction_distances_when_cut.get_at(fi));
3752 this->placeholder_parser().set("long_retraction_when_cut", m_config.long_retractions_when_cut.get_at(fi));
3753 this->placeholder_parser().set("retraction_distance_when_ec", m_config.retraction_distances_when_ec.get_at(fi));
3754 this->placeholder_parser().set("long_retraction_when_ec", m_config.long_retractions_when_ec.get_at(fi));
3755 }
3756 this->placeholder_parser().set("temperature", new ConfigOptionInts(print.config().nozzle_temperature));
3757
3758 // retraction_distances_when_cut (array), flush_volumetric_speeds, flush_temperatures and
3759 // filament_cooling_before_tower are set by update_placeholder_parser_with_variant_params()
3760 // with variant-aware remapping.
3761 this->placeholder_parser().set("long_retractions_when_cut",new ConfigOptionBools(m_config.long_retractions_when_cut));
3762 this->placeholder_parser().set("retraction_distances_when_ec", new ConfigOptionFloatsNullable(m_config.retraction_distances_when_ec));
3763 this->placeholder_parser().set("long_retractions_when_ec",new ConfigOptionBoolsNullable(m_config.long_retractions_when_ec));
3764
3765 this->placeholder_parser().set("max_additional_fan", max_additional_fan);
3766 this->placeholder_parser().set("first_x_layer_fan_speed", new ConfigOptionFloats(m_config.first_x_layer_fan_speed));
3767 this->placeholder_parser().set("close_additional_fan_first_x_layers", new ConfigOptionInts(m_config.close_additional_fan_first_x_layers));
3768 this->placeholder_parser().set("additional_fan_full_speed_layer", new ConfigOptionInts(m_config.additional_fan_full_speed_layer));
3769
3770 //Set variable for total layer count so it can be used in custom gcode.
3771 this->placeholder_parser().set("total_layer_count", m_layer_count);
3772 // Useful for sequential prints.
3773 this->placeholder_parser().set("current_object_idx", 0);
3774 // For the start / end G-code to do the priming and final filament pull in case there is no wipe tower provided.
3775 this->placeholder_parser().set("has_wipe_tower", has_wipe_tower);
3776
3777 // Position where the nozzle waits to heat, just outside the wipe tower. The tower side is chosen
3778 // from the full-bed midpoint, then X is clamped to the region every extruder can reach (shared printable
3779 // polygon) = the full printable_area for all current single/dual printers, so existing output is unchanged.
3780 Vec2f wipe_tower_wait_pos = Vec2f::Zero();
3781 bool wipe_tower_wait_pos_valid = false;
3782 if (has_wipe_tower) {
3783 BoundingBoxf bbx = print.wipe_tower_data().bbx;
3784 bbx.translate(print.get_fake_wipe_tower().pos.cast<double>());
3785 BoundingBoxf printer_bed_bbx(m_config.printable_area.values);
3786 if (bbx.center().x() < printer_bed_bbx.center().x())
3787 wipe_tower_wait_pos = Vec2f(float(bbx.max.x() + 2.f), float(bbx.center().y()));
3788 else
3789 wipe_tower_wait_pos = Vec2f(float(bbx.min.x() - 2.f), float(bbx.center().y()));
3790 auto printer_bbx = unscaled(get_extents(print.get_extruder_shared_printable_polygon()));
3791 if (wipe_tower_wait_pos.x() < printer_bbx.min[0]) wipe_tower_wait_pos.x() = float(printer_bbx.min[0]);
3792 if (wipe_tower_wait_pos.x() > printer_bbx.max[0]) wipe_tower_wait_pos.x() = float(printer_bbx.max[0]);
3793 wipe_tower_wait_pos_valid = true;
3794 }
3795 this->placeholder_parser().set("wipe_tower_wait_pos_x", new ConfigOptionFloat(wipe_tower_wait_pos.x()));
3796 this->placeholder_parser().set("wipe_tower_wait_pos_y", new ConfigOptionFloat(wipe_tower_wait_pos.y()));
3797 this->placeholder_parser().set("wipe_tower_wait_pos_valid", new ConfigOptionBool(wipe_tower_wait_pos_valid));
3798 // Bambu Studio's names for the wait position, used by its profiles.
3799 this->placeholder_parser().set("wipe_tower_center_pos_x", new ConfigOptionFloat(wipe_tower_wait_pos.x()));
3800 this->placeholder_parser().set("wipe_tower_center_pos_y", new ConfigOptionFloat(wipe_tower_wait_pos.y()));
3801 this->placeholder_parser().set("wipe_tower_center_pos_valid", new ConfigOptionBool(wipe_tower_wait_pos_valid));
3802 this->placeholder_parser().set("has_single_extruder_multi_material_priming", wipe_tower_type == WipeTowerType::Type2 && has_wipe_tower && print.config().single_extruder_multi_material_priming);
3803 this->placeholder_parser().set("total_toolchanges", DoExport::resolve_total_toolchanges(print.wipe_tower_data(), print.tool_ordering()));
3804 this->placeholder_parser().set("num_extruders", int(print.config().nozzle_diameter.values.size()));
3805 this->placeholder_parser().set("retract_length", new ConfigOptionFloats(print.config().retraction_length));
3806
3807 //Orca: support max MAXIMUM_EXTRUDER_NUMBER extruders/filaments
3808 std::vector<unsigned char> is_extruder_used(std::max(size_t(MAXIMUM_EXTRUDER_NUMBER), print.config().filament_diameter.size()), 0);
3809 for (unsigned int extruder : tool_ordering.all_extruders())
3810 is_extruder_used[extruder] = true;
3811
3812 // Orca IMEX: mark the LOGICAL filament slot each active SECONDARY carriage
3813 // will load during the print. is_extruder_used is logical-slot indexed
3814 // (consumed as `is_extruder_used[N]` in machine_start_gcode templates), but
3815 // get_imex_active_tools returns PHYSICAL extruder indices. On any printer
3816 // with physical_extruder_map size > 1 (MMU/AFC), writing the physical index
3817 // into a logical-indexed array marks the wrong slot.
3818 //
3819 // The primary is intentionally SKIPPED here — its filament is already
3820 // covered by tool_ordering.all_extruders() above (which lists the slots the
3821 // objects on the plate are actually assigned to). Marking the primary
3822 // again via the pem `first_filament_for_physical_head` fallback would
3823 // pollute is_extruder_used with the *first* slot routed to the primary's
3824 // physical extruder, which is generally not the slot the user assigned to
3825 // the printing object. Same skip-primary pattern as the IMEX PA emission
3826 // path in _do_export().
3827 //
3828 // For secondaries: translate physical -> logical via the per-plate
3829 // imex_head_filament_map (set by the IMEX ghost picker), with
3830 // first_filament_for_physical_head as the fallback when no override is set.
3831 if (print.config().is_imex.value && !print.objects().empty()
3832 && initial_extruder_id != (unsigned int)-1) {
3833 const auto plate_head_map = parse_imex_head_filament_map(
3834 print.objects().front()->config().imex_head_filament_map.value);
3835 const ConfigOptionInts& pem = print.config().physical_extruder_map;
3836 // Bounds-checked, not get_at(): a clamp would suppress whichever head sits at pem[0].
3837 // A miss stays -1 and matches no head, so nothing is skipped -- see IMEXHelpers.hpp.
3838 const int primary_physical =
3839 ((int) initial_extruder_id >= 0 &&
3840 (int) initial_extruder_id < (int) pem.values.size())
3841 ? pem.values[(int) initial_extruder_id]
3842 : -1;
3843 // Bound by the array, not by the filament count. The array is padded to
3844 // MAXIMUM_EXTRUDER_NUMBER on purpose: start G-code addresses HEADS through it
3845 // (fdm_toolchanger_common.json gates M104 T0..T5 on it), and a parallel copy print has
3846 // more heads than filaments by definition. PlaceholderParser clamps an out-of-range
3847 // first_layer_temperature read to filament 0, which is the right temperature when every
3848 // head is printing the same filament. Narrowing this to the filament count leaves the
3849 // secondary carriages unheated.
3850 for (int logical : imex_secondary_logical_slots(
3851 get_imex_active_tools(print), primary_physical, plate_head_map, pem))
3852 if (logical >= 0 && logical < (int) is_extruder_used.size())
3853 is_extruder_used[logical] = true;
3854 }
3855
3856 this->placeholder_parser().set("is_extruder_used", new ConfigOptionBools(is_extruder_used));
3857
3858 {
3859 BoundingBoxf bbox_bed(print.config().printable_area.values);
3860 Vec2f plate_offset = m_writer.get_xy_offset();
3861 this->placeholder_parser().set("print_bed_min", new ConfigOptionFloats({ bbox_bed.min.x(), bbox_bed.min.y()}));
3862 this->placeholder_parser().set("print_bed_max", new ConfigOptionFloats({ bbox_bed.max.x(), bbox_bed.max.y()}));
3863 this->placeholder_parser().set("print_bed_size", new ConfigOptionFloats({ bbox_bed.size().x(), bbox_bed.size().y() }));
3864
3865 BoundingBoxf bbox;
3866 auto pts = std::make_unique<ConfigOptionPoints>();
3867 if (print.calib_mode() == CalibMode::Calib_PA_Pattern) {
3868 //PA_Pattern can have any size or arrangement - not dependent on 3mf model size
3869 bbox = bbox_bed;
3870 bbox.offset(-25.0);
3871 // add 4 corner points of bbox into pts
3872 pts->values.reserve(4);
3873 pts->values.emplace_back(bbox.min.x(), bbox.min.y());
3874 pts->values.emplace_back(bbox.max.x(), bbox.min.y());
3875 pts->values.emplace_back(bbox.max.x(), bbox.max.y());
3876 pts->values.emplace_back(bbox.min.x(), bbox.max.y());
3877
3878 } else if (print.calib_mode() == CalibMode::Calib_PA_Line) {
3879 // Derive X bounds from the actual calibration geometry.
3880 CalibPressureAdvanceLine temp_pa_line_forsize(this);
3881 BoundingBoxf pattern_extents = temp_pa_line_forsize.print_extents(bbox_bed);
3882
3883 bbox = bbox_bed;
3884 bbox.offset(-25.0);
3885 bbox.min.x() = std::max(pattern_extents.min.x(), bbox.min.x());
3886 bbox.max.x() = std::min(pattern_extents.max.x(), bbox.max.x());
3887
3888 pts->values.reserve(4);
3889 pts->values.emplace_back(bbox.min.x(), bbox.min.y());
3890 pts->values.emplace_back(bbox.max.x(), bbox.min.y());
3891 pts->values.emplace_back(bbox.max.x(), bbox.max.y());
3892 pts->values.emplace_back(bbox.min.x(), bbox.max.y());
3893
3894 } else {
3895 // Convex hull of the 1st layer extrusions, for bed leveling and placing the initial purge line.
3896 // It encompasses the object extrusions, support extrusions, skirt, brim, wipe tower.
3897 // It does NOT encompass user extrusions generated by custom G-code,
3898 // therefore it does NOT encompass the initial purge line.
3899 // It does NOT encompass MMU/MMU2 starting (wipe) areas.
3900 pts->values.reserve(print.first_layer_convex_hull().size());
3901 for (const Point &pt : print.first_layer_convex_hull().points)
3902 pts->values.emplace_back(print.translate_to_print_space(pt));
3903 bbox = BoundingBoxf((pts->values));
3904 }
3905 this->placeholder_parser().set("first_layer_print_convex_hull", pts.release());
3906 this->placeholder_parser().set("first_layer_print_min", new ConfigOptionFloats({bbox.min.x(), bbox.min.y()}));
3907 this->placeholder_parser().set("first_layer_print_max", new ConfigOptionFloats({bbox.max.x(), bbox.max.y()}));
3908 this->placeholder_parser().set("first_layer_print_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() }));
3909
3910 {
3911 // use first layer convex_hull union with each object's bbox to check whether in head detect zone
3912 Polygons object_projections;
3913 for (auto& obj : print.objects()) {
3914 for (auto& instance : obj->instances()) {
3915 const auto& bbox = instance.get_bounding_box();
3916 Point min_p{ coord_t(scale_(bbox.min.x())),coord_t(scale_(bbox.min.y())) };
3917 Point max_p{ coord_t(scale_(bbox.max.x())),coord_t(scale_(bbox.max.y())) };
3918 Polygon instance_projection = {
3919 {min_p.x(),min_p.y()},
3920 {max_p.x(),min_p.y()},
3921 {max_p.x(),max_p.y()},
3922 {min_p.x(),max_p.y()}
3923 };
3924 object_projections.emplace_back(std::move(instance_projection));
3925 }
3926 }
3927 object_projections.emplace_back(print.first_layer_convex_hull());
3928
3929 Polygons project_polys = union_(object_projections);
3930 Polygon head_wrap_detect_zone;
3931 for (auto& point : print.config().head_wrap_detect_zone.values)
3932 head_wrap_detect_zone.append(scale_(point).cast<coord_t>() + scale_(plate_offset).cast<coord_t>());
3933
3934 this->placeholder_parser().set("in_head_wrap_detect_zone", !intersection_pl(project_polys, {head_wrap_detect_zone}).empty());
3935 }
3936
3937 {
3938 coordf_t max_print_z = 0;
3939 for (auto& obj : print.objects()) {
3940 max_print_z = std::max(max_print_z, (*std::max_element(obj->layers().begin(), obj->layers().end(), [](Layer* a, Layer* b) { return a->print_z < b->print_z; }))->print_z);
3941 }
3942 this->placeholder_parser().set("max_print_z", new ConfigOptionInt(std::ceil(max_print_z)));
3943 }
3944
3945 BoundingBoxf mesh_bbox(m_config.bed_mesh_min, m_config.bed_mesh_max);
3946 auto mesh_margin = m_config.adaptive_bed_mesh_margin.value;
3947 mesh_bbox.min = mesh_bbox.min.cwiseMax((bbox.min.array() - mesh_margin).matrix());
3948 mesh_bbox.max = mesh_bbox.max.cwiseMin((bbox.max.array() + mesh_margin).matrix());
3949 this->placeholder_parser().set("adaptive_bed_mesh_min", new ConfigOptionFloats({mesh_bbox.min.x(), mesh_bbox.min.y()}));
3950 this->placeholder_parser().set("adaptive_bed_mesh_max", new ConfigOptionFloats({mesh_bbox.max.x(), mesh_bbox.max.y()}));
3951
3952 auto probe_dist_x = std::max(1., m_config.bed_mesh_probe_distance.value.x());
3953 auto probe_dist_y = std::max(1., m_config.bed_mesh_probe_distance.value.y());
3954 int probe_count_x = std::max(3, (int) std::ceil(mesh_bbox.size().x() / probe_dist_x) + 1);
3955 int probe_count_y = std::max(3, (int) std::ceil(mesh_bbox.size().y() / probe_dist_y) + 1);
3956 auto bed_mesh_algo = "bicubic";
3957 if (probe_count_x * probe_count_y <= 6) { // lagrange needs up to a total of 6 mesh points
3958 bed_mesh_algo = "lagrange";
3959 }
3960 else
3961 if(print.config().gcode_flavor == gcfKlipper){
3962 // bicubic needs 4 probe points per axis
3963 probe_count_x = std::max(probe_count_x,4);
3964 probe_count_y = std::max(probe_count_y,4);
3965 }
3966 this->placeholder_parser().set("bed_mesh_probe_count", new ConfigOptionInts({probe_count_x, probe_count_y}));
3967 this->placeholder_parser().set("bed_mesh_algo", bed_mesh_algo);
3968 // get center without wipe tower
3969 BoundingBoxf bbox_wo_wt; // bounding box without wipe tower
3970 for (auto &objPtr : print.objects()) {
3971 BBoxData data;
3972 bbox_wo_wt.merge(unscaled(objPtr->get_first_layer_bbox(data.area, data.layer_height, data.name)));
3973 }
3974 auto center = bbox_wo_wt.center();
3975 this->placeholder_parser().set("first_layer_center_no_wipe_tower", new ConfigOptionFloats{ {center.x(),center.y()}});
3976 }
3977 bool activate_chamber_temp_control = false;
3978 auto max_chamber_temp = 0;
3979 for (const auto &extruder : m_writer.extruders()) {
3980 activate_chamber_temp_control |= m_config.activate_chamber_temp_control.get_at(extruder.id());
3981 max_chamber_temp = std::max(max_chamber_temp, m_config.chamber_temperature.get_at(extruder.id()));
3982 }
3983 {
3984 BedType curr_bed_type = m_config.curr_bed_type;
3985
3986 int min_temperature_vitrification = std::numeric_limits<int>::max();
3987 for (const auto& extruder : m_writer.extruders())
3988 min_temperature_vitrification = std::min(min_temperature_vitrification, m_config.temperature_vitrification.get_at(extruder.id()));
3989
3990
3991 std::string first_layer_bed_temp_str;
3994 int target_bed_temp = 0;
3995 if (m_config.bed_temperature_formula == BedTempFormula::btfHighestTemp)
3996 target_bed_temp = get_highest_bed_temperature(true, print);
3997 else
3998 target_bed_temp = get_bed_temperature(initial_extruder_id, true, curr_bed_type);
3999
4000 this->placeholder_parser().set("bbl_bed_temperature_gcode", new ConfigOptionBool(false));
4001 this->placeholder_parser().set("bed_temperature_initial_layer", new ConfigOptionInts(*first_bed_temp_opt));
4002 this->placeholder_parser().set("bed_temperature", new ConfigOptionInts(*bed_temp_opt));
4003 this->placeholder_parser().set("bed_temperature_initial_layer_single", new ConfigOptionInt(target_bed_temp));
4004 this->placeholder_parser().set("bed_temperature_initial_layer_vector", new ConfigOptionString());
4005 this->placeholder_parser().set("chamber_temperature", new ConfigOptionInts(m_config.chamber_temperature));
4006 this->placeholder_parser().set("overall_chamber_temperature", new ConfigOptionInt(max_chamber_temp));
4007 this->placeholder_parser().set("chamber_minimal_temperature", new ConfigOptionInts(m_config.chamber_minimal_temperature));
4008 this->placeholder_parser().set("enable_high_low_temp_mix", new ConfigOptionBool(!print.need_check_multi_filaments_compatibility()));
4009 this->placeholder_parser().set("min_vitrification_temperature", new ConfigOptionInt(min_temperature_vitrification));
4010
4011 // SoftFever: support variables `first_layer_temperature` and `first_layer_bed_temperature`
4012 this->placeholder_parser().set("first_layer_bed_temperature", new ConfigOptionInts(*first_bed_temp_opt));
4013 this->placeholder_parser().set("first_layer_temperature", new ConfigOptionInts(m_config.nozzle_temperature_initial_layer));
4014 this->placeholder_parser().set("max_print_height",new ConfigOptionInt(m_config.printable_height));
4015 this->placeholder_parser().set("z_offset", new ConfigOptionFloat(m_config.z_offset));
4016 this->placeholder_parser().set("model_name", new ConfigOptionString(print.get_model_name()));
4017 this->placeholder_parser().set("plate_number", new ConfigOptionString(print.get_plate_number_formatted()));
4018 this->placeholder_parser().set("plate_name", new ConfigOptionString(print.get_plate_name()));
4019 this->placeholder_parser().set("first_layer_height", new ConfigOptionFloat(m_config.initial_layer_print_height.value));
4020
4021 auto used_filaments = print.get_slice_used_filaments(false);
4022 this->placeholder_parser().set("is_all_bbl_filament", std::all_of(used_filaments.begin(), used_filaments.end(), [&](auto idx) {
4023 return m_config.filament_vendor.get_at(idx) == "Bambu Lab";
4024 }));
4025
4026 //add during_print_exhaust_fan_speed
4027 std::vector<int> during_print_exhaust_fan_speed_num;
4028 during_print_exhaust_fan_speed_num.reserve(m_config.during_print_exhaust_fan_speed.size());
4029 for (const auto& item : m_config.during_print_exhaust_fan_speed.values)
4030 during_print_exhaust_fan_speed_num.emplace_back((int)(item / 100.0 * 255));
4031 this->placeholder_parser().set("during_print_exhaust_fan_speed_num", new ConfigOptionInts(during_print_exhaust_fan_speed_num));
4032
4033 //BBS: calculate the volumetric speed of outer wall. Ignore pre-object setting and multi-filament, and just use the default setting
4034 float outer_wall_volumetric_speed = get_outer_wall_volumetric_speed(m_config, print, initial_non_support_extruder_id,
4035 (int) get_filament_config_index((int) initial_non_support_extruder_id),
4036 get_extruder_id(initial_non_support_extruder_id));
4037 this->placeholder_parser().set("outer_wall_volumetric_speed", new ConfigOptionFloat(outer_wall_volumetric_speed));
4038
4039 auto first_layer_filaments = print.get_slice_used_filaments(true);
4040 bool has_tpu_in_first_layer = std::any_of(first_layer_filaments.begin(), first_layer_filaments.end(), [&](unsigned int idx) { return m_config.filament_type.get_at(idx) == "TPU"; });
4041 this->placeholder_parser().set("has_tpu_in_first_layer", new ConfigOptionBool(has_tpu_in_first_layer));
4042
4043 if (print.calib_params().mode == CalibMode::Calib_PA_Line) {
4044 this->placeholder_parser().set("scan_first_layer", new ConfigOptionBool(false));
4045 }
4046 }
4047 { // hold chamber temp for flat print: Flag
4048 double print_area_sum_threshold = 40000.0, pring_hight_threshold = 0.3; // thresholds in mm^2 and mm as units
4049
4050 double area_sum_temp = 0.0;
4051 coordf_t max_hight_temp = -1.0;
4052 for (ObjectID print_object_ID_t : print.print_object_ids()) {
4053 const PrintObject *print_object = print.get_object(print_object_ID_t);
4054 // object hight
4055 if (!print_object->layers().empty() && print_object->layers().back()->print_z > max_hight_temp) max_hight_temp = print_object->layers().back()->print_z;
4056 // object area
4057 if (!print_object->layers().empty() && print_object->layers().front()->print_z < print.config().initial_layer_print_height + EPSILON &&
4058 !print_object->layers().front()->lslices.empty()) {
4059 ExPolygons temp_Expolys = print_object->layers().front()->lslices;
4060 for (ExPolygon &temp_Expoly : temp_Expolys) { area_sum_temp += temp_Expoly.area(); }
4061 }
4062 // suport area
4063 if (!print_object->support_layers().empty() && print_object->support_layers().front()->print_z < print.config().initial_layer_print_height + EPSILON &&
4064 !print_object->support_layers().front()->support_islands.empty()) {
4065 ExPolygons temp_Expolys = print_object->support_layers().front()->support_islands;
4066 for (ExPolygon &temp_Expoly : temp_Expolys) { area_sum_temp += temp_Expoly.area(); }
4067 }
4068 // brim area
4069 if (print.m_brimMap.find(print_object_ID_t) != print.m_brimMap.end() && !print.m_brimMap.at(print_object_ID_t).entities.empty()) { // contain brim
4070 for (const ExtrusionEntity *entities_temp : print.m_brimMap.at(print_object_ID_t).entities) {
4071 Polygons temp_Expolys;
4072 entities_temp->polygons_covered_by_spacing(temp_Expolys, 0.0f);
4073 for (Polygon &temp_Expoly : temp_Expolys) { area_sum_temp += temp_Expoly.area(); }
4074 }
4075 }
4076 }
4077 // wipe tower area
4078 if (has_wipe_tower && print.wipe_tower_data().wipe_tower_mesh_data) {
4079 Polygon temp_Expoly = print.wipe_tower_data().wipe_tower_mesh_data->bottom;
4080 area_sum_temp += temp_Expoly.area();
4081 }
4082 bool hold_chamber_temp_for_flat_print = max_hight_temp > 0 && max_hight_temp < pring_hight_threshold && area_sum_temp > print_area_sum_threshold * 1.0e10;
4083 this->placeholder_parser().set("hold_chamber_temp_for_flat_print", new ConfigOptionBool(hold_chamber_temp_for_flat_print));
4084 }
4085
4092 // The final print statistics are known only after the whole G-code is generated, so they are filled in during post-processing too.
4093 // The running extruded_* totals are left alone; profiles use them as numbers.
4102
4103 // Sync variant-mapped params into placeholder_parser before processing start gcode
4105
4106 // IDEX/IQEX: set {imex_mode}, {imex_mode_index}, {imex_mode_gcode} placeholders before
4107 // any G-code script is processed so they are available in machine_start_gcode,
4108 // filament_start_gcode, filament_end_gcode, change_filament_gcode, etc.
4109 // {imex_mode_names} and {imex_mode_gcodes} array placeholders are already accessible
4110 // via the printer config (e.g. {imex_mode_names[0]}, {imex_mode_gcodes[1]}).
4111 std::string imex_active_mode;
4112 int imex_active_mode_index = 0;
4113 std::string imex_active_mode_gcode;
4114 m_imex_parallel_mode.clear();
4116 if (print.config().is_imex.value && !print.objects().empty()) {
4117 const std::string& raw = print.objects().front()->config().imex_parallel_mode.value;
4118 imex_active_mode = raw.empty() ? kImexPrimaryMode : raw;
4120 print.objects().front()->config().imex_head_filament_map.value);
4121 // Look `name` up in the printer's mode table, filling in its index, script and
4122 // tool roster. False means the name matches no row, i.e. the mode is unresolved.
4123 // find_imex_mode() owns the "which row, and what if a sibling array is short"
4124 // rule for every IMEX consumer; see IMEXHelpers.hpp.
4125 std::string imex_active_mode_tools;
4126 auto resolve_mode = [&](const std::string& name) {
4127 const ImexMode mode = find_imex_mode(print.config(), name);
4128 if (!mode.found())
4129 return false;
4130 imex_active_mode_index = mode.index;
4131 imex_active_mode_gcode = mode.gcode;
4132 imex_active_mode_tools = mode.active_tools;
4133 return true;
4134 };
4135 // An unresolved mode name falls back to Primary rather than being carried into the
4136 // parallel-mode paths. `imex_parallel_mode` is a plain string stored on the plate and
4137 // matched against the printer's `imex_mode_names` by value, so it goes stale whenever
4138 // the two drift apart -- a mode renamed or deleted after a plate was set to it, or a
4139 // project shared between printer presets that name their modes differently.
4140 //
4141 // Without the fallback the exporter took every "not Primary" branch while every lookup
4142 // keyed on the mode name came back empty, and wrote a file that is wrong rather than
4143 // merely unconfigured: the 1st->2nd layer temperature branch is mutually exclusive with
4144 // the standard one, so an empty active-tool roster meant NO head got its transition and
4145 // all of them held nozzle_temperature_initial_layer for the whole print; and
4146 // imex_suppresses_bare_toolchange() dropped the initial T<n> on the assumption that a
4147 // mode script would select the tool, while the mode script -- also looked up by name --
4148 // did not exist. Print::validate()'s IMEX rules did not catch it either: they resolve
4149 // the same name to an empty tools string, which yields no declared primary and skips
4150 // the guard. Primary is the one interpretation that is always well-formed: single
4151 // carriage, ordinary temperatures, ordinary tool changes.
4152 //
4153 // Warned rather than silent, and warned rather than blocked. Silent is not an option:
4154 // the plate still labels itself with the stale mode and draws no zones (PartPlate's
4155 // zone builder returns early on the same unresolved state), so a user who asked for two
4156 // parts in copy mode would get one with nothing anywhere saying why. Blocking is not an
4157 // option either -- opening someone else's 3MF on a differently-named preset is a
4158 // legitimate way to arrive here, and the Primary interpretation prints correctly, so
4159 // refusing to slice would be a regression for a case that has a good answer.
4160 //
4161 // The second trigger below is the ragged-table case, and it lands here rather than
4162 // anywhere else because the damage is identical: the mode's name resolves, but the
4163 // printer's `imex_mode_active_tools` is too short to reach its row (or the row is
4164 // explicitly empty), so the roster every branch above keys on comes back empty and
4165 // the export takes the parallel path with nothing in it. Nothing used to report
4166 // that -- half the lookup sites in the tree turned a short tools array into "mode
4167 // not found" and the other half did not, and this one did not. One rule now, in
4168 // find_imex_mode(), and one warning, here.
4169 const bool imex_mode_resolved = resolve_mode(imex_active_mode);
4170 if (imex_active_mode != kImexPrimaryMode && (!imex_mode_resolved || imex_active_mode_tools.empty())) {
4171 print.active_step_add_warning(
4173 imex_mode_resolved
4174 ? Slic3r::format(_(L("The IDEX/IQEX mode \"%1%\" has no tools assigned on the selected printer. "
4175 "Printing in Primary mode instead. Assign the mode's tools in Printer "
4176 "Settings, or pick another mode from the plate's IDEX/IQEX button.")),
4177 imex_active_mode)
4178 : Slic3r::format(_(L("This plate is set to the IDEX/IQEX mode \"%1%\", which the selected printer "
4179 "does not define. Printing in Primary mode instead. Pick a mode from the "
4180 "plate's IDEX/IQEX button, or restore the mode in Printer Settings.")),
4181 imex_active_mode));
4182 imex_active_mode = kImexPrimaryMode;
4183 imex_active_mode_index = 0;
4184 imex_active_mode_gcode.clear();
4185 imex_active_mode_tools.clear();
4186 // Primary is a real row in the table (always the first one), so it may carry its own
4187 // setup script; resolve it the same way any other selected mode would be.
4188 resolve_mode(imex_active_mode);
4189 }
4190 m_imex_parallel_mode = imex_active_mode;
4191 }
4192 this->placeholder_parser().set("imex_mode", imex_active_mode);
4193 this->placeholder_parser().set("imex_mode_index", imex_active_mode_index);
4194 this->placeholder_parser().set("imex_mode_gcode", imex_active_mode_gcode);
4195
4196 // IDEX/IQEX: process mode G-code BEFORE machine_start_gcode so that any
4197 // {global abc = 1} declarations in the mode script are visible to machine_start_gcode.
4198 // The processed result is buffered here and written to the file after temp setup below.
4199 std::string imex_processed_gcode;
4200 if (!imex_active_mode_gcode.empty())
4201 imex_processed_gcode = this->placeholder_parser_process(
4202 "imex_mode_gcode", imex_active_mode_gcode, initial_extruder_id);
4203 // Expand the file header only after the start-up placeholders and writer state are ready.
4204 // Use the regular custom G-code path so invalid placeholders report the template name.
4205 if (!print.config().file_start_gcode.value.empty())
4206 file.writeln(this->placeholder_parser_process("file_start_gcode", print.config().file_start_gcode.value, initial_extruder_id));
4207
4208 // Orca: Don't output Header block if BTT thumbnail is identified in the list
4209 // Get the thumbnails value as a string
4210 std::string thumbnails_value = print.config().option<ConfigOptionString>("thumbnails")->value;
4211 // search string for the BTT_TFT label
4212 bool has_BTT_thumbnail = (thumbnails_value.find("BTT_TFT") != std::string::npos);
4213
4214 if(!has_BTT_thumbnail){
4215 file.write_format("; HEADER_BLOCK_START\n");
4216 // Write information on the generator.
4217 file.write_format("; generated by %s on %s\n", Slic3r::header_slic3r_generated().c_str(), Slic3r::Utils::local_timestamp().c_str());
4218 if (is_bbl_printers)
4220 //BBS: total layer number
4222 //Orca: extra check for bbl printer
4223 if (is_bbl_printers) {
4224 if (print.calib_params().mode == CalibMode::Calib_None) { // Don't support skipping in cali mode
4225 // list all label_object_id with sorted order here
4227 m_label_objects_ids.clear();
4228 m_label_objects_ids.reserve(print.num_object_instances());
4229 for (const PrintObject *print_object : print.objects())
4230 for (const PrintInstance &print_instance : print_object->instances())
4231 m_label_objects_ids.push_back(print_instance.model_instance->get_labeled_id());
4232
4233 std::sort(m_label_objects_ids.begin(), m_label_objects_ids.end());
4234
4235 std::string objects_id_list = "; model label id: ";
4236 for (auto it = m_label_objects_ids.begin(); it != m_label_objects_ids.end(); it++)
4237 objects_id_list += (std::to_string(*it) + (it != m_label_objects_ids.end() - 1 ? "," : "\n"));
4238 file.writeln(objects_id_list);
4239 } else {
4241 m_label_objects_ids.clear();
4242 }
4243 }
4244
4245 {
4246 std::string filament_density_list = "; filament_density: ";
4247 (filament_density_list+=m_config.filament_density.serialize()) +='\n';
4248 file.writeln(filament_density_list);
4249
4250 std::string filament_diameter_list = "; filament_diameter: ";
4251 (filament_diameter_list += m_config.filament_diameter.serialize()) += '\n';
4252 file.writeln(filament_diameter_list);
4253
4254 coordf_t max_height_z = -1;
4255 for (const auto& object : print.objects())
4256 max_height_z = std::max(object->layers().back()->print_z, max_height_z);
4257
4258 std::ostringstream max_height_z_tip;
4259 max_height_z_tip<<"; max_z_height: " << std::fixed << std::setprecision(2) << max_height_z << '\n';
4260 file.writeln(max_height_z_tip.str());
4261 }
4262
4263 {
4264 auto used_filaments = print.get_slice_used_filaments(false);
4265 std::ostringstream out;
4266 out << "; filament: ";
4267 for (size_t idx = 0; idx < used_filaments.size(); ++idx) {
4268 if (idx != 0)
4269 out << ',';
4270 out << used_filaments[idx] + 1;
4271 }
4272 file.writeln(out.str());
4273 }
4274
4275 file.write_format("; HEADER_BLOCK_END\n\n");
4276 }
4277
4278 // BBS: write global config at the beginning of gcode file because printer
4279 // need these config information
4280 // Append full config, delimited by two 'phony' configuration keys
4281 // CONFIG_BLOCK_START and CONFIG_BLOCK_END. The delimiters are structured
4282 // as configuration key / value pairs to be parsable by older versions of
4283 // PrusaSlicer G-code viewer.
4284 {
4285 if (is_bbl_printers && !skip_config_block) {
4286 file.write("; CONFIG_BLOCK_START\n");
4287 std::string full_config;
4288 append_full_config(print, full_config);
4289 if (!full_config.empty())
4290 file.write(full_config);
4291
4292 // SoftFever: write compatiple image
4293 file.write_format("; first_layer_bed_temperature = %d\n", compat_first_layer_bed_temp);
4294 file.write_format("; first_layer_temperature = %d\n", compat_first_layer_nozzle_temp);
4295 file.write("; CONFIG_BLOCK_END\n\n");
4296 } else if (thumbnail_cb != nullptr) {
4297 // generate the thumbnails
4298 auto [thumbnails, errors] = GCodeThumbnails::make_and_check_thumbnail_list(print.full_print_config());
4299
4300 if (errors != enum_bitmask<ThumbnailError>()) {
4301 std::string error_str = _(L("Invalid thumbnails value:"));
4302 error_str += GCodeThumbnails::get_error_string(errors);
4303 throw Slic3r::ExportError(error_str);
4304 }
4305
4306 if (!thumbnails.empty())
4308 thumbnail_cb, print.get_plate_index(), thumbnails, [&file](const char* sz) { file.write(sz); }, [&print]() { print.throw_if_canceled(); });
4309 }
4310 }
4311
4312 // Belt printer: the tilt and transform settings the G-code viewer reads back. They
4313 // are comments outside the config block, so they go after the thumbnails that a
4314 // BTT TFT firmware needs first, and are written whether or not that header block is.
4315 this->write_belt_header(file, print);
4316
4317 // Write some terse information on the slicing parameters.
4318 const PrintObject *first_object = print.objects().front();
4319 const double layer_height = first_object->config().layer_height.value;
4320 const double initial_layer_print_height = print.config().initial_layer_print_height.value;
4321 for (size_t region_id = 0; region_id < print.num_print_regions(); ++ region_id) {
4322 const PrintRegion &region = print.get_print_region(region_id);
4323 file.write_format("; external perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frExternalPerimeter, layer_height).width());
4324 file.write_format("; perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, layer_height).width());
4325 file.write_format("; infill extrusion width = %.2fmm\n", region.flow(*first_object, frInfill, layer_height).width());
4326 file.write_format("; solid infill extrusion width = %.2fmm\n", region.flow(*first_object, frSolidInfill, layer_height).width());
4327 file.write_format("; top infill extrusion width = %.2fmm\n", region.flow(*first_object, frTopSolidInfill, layer_height).width());
4328 if (print.has_support_material())
4329 file.write_format("; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width());
4330 if (print.config().initial_layer_line_width.value > 0)
4331 file.write_format("; first layer extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, initial_layer_print_height, true).width());
4332 file.write_format("\n");
4333 }
4334
4335 file.write_format("; EXECUTABLE_BLOCK_START\n");
4336
4337 // Set for every printer, as the object label comments use these ids too.
4339 // SoftFever
4341 file.write(set_object_info(&print));
4342
4343 // adds tags for time estimators
4345
4346 // Emit machine envelope limits for the Marlin firmware.
4347 this->print_machine_envelope(file, print);
4348
4349 // Disable fan.
4350 if (m_config.auxiliary_fan.value && print.config().close_fan_the_first_x_layers.get_at(initial_extruder_id)) {
4351 file.write(m_writer.set_fan(0));
4352 //BBS: disable additional fan
4353 file.write(m_writer.set_additional_fan(0));
4354 }
4355
4356 std::string machine_start_gcode = this->placeholder_parser_process("machine_start_gcode", print.config().machine_start_gcode.value, initial_extruder_id);
4357 if (print.config().gcode_flavor != gcfKlipper) {
4358 // Set bed temperature if the start G-code does not contain any bed temp control G-codes.
4359 this->_print_first_layer_bed_temperature(file, print, machine_start_gcode, initial_extruder_id, true);
4360 // Set extruder(s) temperature before and after start G-code.
4361 this->_print_first_layer_extruder_temperatures(file, print, machine_start_gcode, initial_extruder_id, false);
4362 }
4363
4364 // adds tag for processor
4366
4367 // Orca: set chamber temperature at the beginning of gcode file
4368 if (activate_chamber_temp_control && max_chamber_temp > 0){
4369 int temp_out =0;
4370 if(!custom_gcode_sets_temperature(machine_start_gcode,141,191,false,temp_out))
4371 file.write(m_writer.set_chamber_temperature(max_chamber_temp, true)); // set chamber_temperature
4372 }
4373
4374 // Write IMEX mode G-code first (processed above), then machine_start_gcode.
4375 if (!imex_processed_gcode.empty())
4376 file.writeln(imex_processed_gcode);
4377
4378 // Write the custom start G-code
4379 file.writeln(machine_start_gcode);
4380 // Mark the end of the machine start g-code so the GCodeProcessor usage-block builder knows where user
4381 // g-code ends and can start attributing filament/extruder usage. Gated on enable_pre_heating: only the
4382 // injector fleet (H2D/X2D/H2D-Pro/H2C) emits it; the byte-frozen fleet (X1/P1/A1/H2S, flag false) never
4383 // does, so their g-code is byte-identical. Without this marker handle_filament_change early-returns and
4384 // no blocks are built, so this line is what actually activates the pre-heat injector.
4385 if (m_config.enable_pre_heating.value)
4386 file.write_format(";%s\n", GCodeProcessor::Machine_Start_GCode_End_Tag.c_str());
4387
4388 //BBS: gcode writer doesn't know where the real position of extruder is after inserting custom gcode
4389 m_writer.set_current_position_clear(false);
4391
4392 if (is_bbl_printers) {
4393 m_writer.init_extruder(initial_non_support_extruder_id);
4394 // add the missing filament start gcode in machine start gcode
4395 {
4396 DynamicConfig config;
4397 config.set_key_value("filament_extruder_id", new ConfigOptionInt((int)(initial_non_support_extruder_id)));
4398 config.set_key_value("current_filament_id", new ConfigOptionInt((int)(initial_non_support_extruder_id)));
4399 config.set_key_value("current_extruder_id", new ConfigOptionInt((int) get_extruder_id(initial_non_support_extruder_id)));
4400 config.set_key_value("current_nozzle_id", new ConfigOptionInt(first_nozzle_id_for_gcode_placeholder(group_result, (int) initial_non_support_extruder_id, (int) get_extruder_id(initial_non_support_extruder_id))));
4401 config.set_key_value("nozzle_diameter_at_nozzle_id", new ConfigOptionFloats(get_nozzle_diameters_by_nozzle_id(group_result.get())));
4402 config.set_key_value("nozzle_volume_types", new ConfigOptionStrings(get_nozzle_volume_types_by_nozzle_id(group_result.get())));
4403 config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
4404 config.set_key_value("layer_z", new ConfigOptionFloat(m_writer.get_position().z() - m_config.z_offset.value));
4405 config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z));
4406 std::string filament_start_gcode = this->placeholder_parser_process("filament_start_gcode", print.config().filament_start_gcode.get_at(initial_non_support_extruder_id), initial_non_support_extruder_id,&config);
4407 file.writeln(filament_start_gcode);
4408 // Mark the first filament used in print. Multi-nozzle printers (H2C) get ";VT%d H%d" where
4409 // H = dynamic ? nozzle_id : -1; existing single-nozzle printers keep the bare ";VT%d" so their
4410 // g-code stays byte-identical.
4412 int initial_nozzle_id = -1;
4413 if (group_result && group_result->is_support_dynamic_nozzle_map()) {
4414 auto initial_nozzle = group_result->get_first_nozzle_for_filament(initial_extruder_id);
4415 initial_nozzle_id = initial_nozzle ? initial_nozzle->group_id : -1;
4416 }
4417 file.write_format(";VT%d H%d\n", initial_extruder_id, initial_nozzle_id);
4418 } else {
4419 file.write_format(";VT%d\n", initial_extruder_id);
4420 }
4421 }
4422 // Orca: add missing PA settings for initial filament
4423 file.write(set_filament_pressure_advance(initial_non_support_extruder_id));
4424 }
4425
4426 //flush FanMover buffer to avoid modifying the start gcode if it's manual.
4427 if (!machine_start_gcode.empty() && this->m_fan_mover.get() != nullptr)
4428 file.write(this->m_fan_mover.get()->process_gcode("", true));
4429
4430 // Process filament-specific gcode.
4431 /* if (has_wipe_tower) {
4432 // Wipe tower will control the extruder switching, it will call the filament_start_gcode.
4433 } else {
4434 DynamicConfig config;
4435 config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(initial_extruder_id)));
4436 file.writeln(this->placeholder_parser_process("filament_start_gcode", print.config().filament_start_gcode.values[initial_extruder_id], initial_extruder_id, &config));
4437 }
4438*/
4439 if (is_bbl_printers) {
4440 this->_print_first_layer_extruder_temperatures(file, print, machine_start_gcode, initial_extruder_id, true);
4441 }
4442
4443 // Orca: when air filtration is supported, check if it needs to be activated during printing and set the exhaust fan speed accordingly
4444 if (m_config.support_air_filtration.value) {
4445 bool activate_air_filtration_during_print = false;
4446 int during_print_exhaust_fan_speed = 0;
4447
4448 // Orca: when activate_air_filtration is set on any extruder, find and set the highest during_print_exhaust_fan_speed
4449 for (const auto &extruder : m_writer.extruders()) {
4450 size_t fi = get_filament_config_index((int)extruder.id());
4451 if (m_config.activate_air_filtration.get_at(fi) && m_config.activate_air_filtration_during_print.get_at(fi)) {
4452 activate_air_filtration_during_print = true;
4453 during_print_exhaust_fan_speed = std::max(during_print_exhaust_fan_speed,
4454 m_config.during_print_exhaust_fan_speed.get_at(fi));
4455 }
4456 }
4457
4458 if (activate_air_filtration_during_print)
4459 file.write(m_writer.set_exhaust_fan(during_print_exhaust_fan_speed));
4460 }
4461
4462 print.throw_if_canceled();
4463
4464 // Set other general things.
4465 file.write(this->preamble());
4466
4467 // Calculate wiping points if needed
4469 print.throw_if_canceled();
4470
4471 // Collect custom seam data from all objects.
4472 std::function<void(void)> throw_if_canceled_func = [&print]() { print.throw_if_canceled(); };
4473 m_seam_placer.init(print, throw_if_canceled_func);
4474 // Precise Seam: init() only prepares its warning; issue it here, inside the active export step.
4475 if (!m_seam_placer.precise_seam_warning().empty())
4476 print.active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL,
4477 m_seam_placer.precise_seam_warning(),
4479
4480 // BBS: get path for change filament
4481 if (m_writer.multiple_extruders) {
4482 std::vector<Vec2d> points = get_path_of_change_filament(print);
4483 if (points.size() == 3) {
4484 travel_point_1 = points[0];
4485 travel_point_2 = points[1];
4486 travel_point_3 = points[2];
4487 }
4488 }
4489
4490 // Orca: support extruder priming
4491 if (wipe_tower_type != WipeTowerType::Type2 || ! (has_wipe_tower && print.config().single_extruder_multi_material_priming))
4492 {
4493 // Set initial extruder only after custom start G-code.
4494 // Ugly hack: Do not set the initial extruder if the extruder is primed using the MMU priming towers at the edge of the print bed.
4495 file.write(this->set_extruder(initial_extruder_id, 0.));
4496
4497 // IMEX parallel modes only: emit PA for all secondary active tools.
4498 // In primary mode, regular tool-change PA handles each tool as it becomes active.
4499 // In parallel modes no tool changes occur, so every carriage must be addressed
4500 // explicitly here before printing starts.
4502 && !m_config.physical_extruder_map.values.empty()) {
4503 // initial_physical: pem-translate the print's initial logical extruder so the
4504 // loop can skip the primary head (which emitted PA via the normal path).
4505 // Then pem-invert each active physical head back to its first routed filament
4506 // for the PA setting lookup. Guarded on non-empty pem above.
4507 // Bounds-checked, not get_at() -- see the note on imex_pem_tool_for in
4508 // IMEXHelpers.hpp. A clamped initial_physical would skip whichever active head
4509 // equals pem[0], leaving it with no PA at all.
4510 const int initial_physical =
4511 ((int) initial_extruder_id >= 0 &&
4512 (int) initial_extruder_id < (int) m_config.physical_extruder_map.values.size())
4513 ? m_config.physical_extruder_map.values[(int) initial_extruder_id]
4514 : -1;
4515 // enable_pressure_advance and pressure_advance are indexed by COLUMN, not by
4516 // filament slot. filament_diameter is one entry per slot and is never
4517 // variant-expanded, so it is the slot-space bound; translate the slot to its column
4518 // with get_filament_config_index(), as the second-layer temperature loop below does.
4519 const int num_pa_filament_slots = (int) print.config().filament_diameter.values.size();
4520 for (int tool_idx : get_imex_active_tools(print)) {
4521 // Unlike the second-layer temperature loop, the primary is skipped here:
4522 // set_extruder() above already emitted its PA with the pem tool qualifier.
4523 if (tool_idx == initial_physical) continue;
4524 const int logical = resolve_filament_for_head(
4525 m_imex_head_filament_map, m_config.physical_extruder_map, tool_idx);
4526 // resolve_filament_for_head() answers in pem's index space -- one entry per
4527 // NOZZLE -- while these two options are indexed per FILAMENT SLOT. The spaces
4528 // diverge when the printer has more nozzles than the project has filaments, and
4529 // get_at() would clamp a past-the-end index to filament 0 and pin its PA onto a
4530 // secondary carriage. Same guard the second-layer temperature loop uses.
4531 if (logical < 0 || logical >= num_pa_filament_slots) continue;
4532 const size_t pa_fi = get_filament_config_index(logical);
4533 if (!print.config().enable_pressure_advance.get_at(pa_fi)) continue;
4534 file.write(m_writer.set_pressure_advance(
4535 print.config().pressure_advance.get_at(pa_fi),
4536 tool_idx));
4537 }
4538 }
4539 }
4540
4541 // Direct output writes are parsed synchronously. Preserve the startup
4542 // endpoint before the generator establishes its first model-space point,
4543 // so that first travel can be planned from the position the G-code actually
4544 // left the machine at rather than the default last_pos value.
4545 GCodeProcessor::PositionState startup_position = m_processor.get_current_position();
4546 const Vec2f plate_offset = m_writer.get_xy_offset();
4547 startup_position.position.x() += plate_offset.x();
4548 startup_position.position.y() += plate_offset.y();
4549 m_pending_start_gcode_position = !m_last_pos_defined && startup_position.has_known_xy() ?
4550 std::optional<GCodeProcessor::PositionState>(startup_position) : std::nullopt;
4551
4552 this->m_objsWithBrim.clear();
4553 m_brim_done = false;
4554
4555 // Orca: Track brims by instance. When a combined brim is printed, all of
4556 // its instances are marked done together.
4557 for (const Print::SkirtBrimGroup& group : print.skirt_brim_groups()) {
4558 for (const Print::SkirtBrimGroup::Brim& brim : group.brims) {
4559 if (brim.brim.empty())
4560 continue;
4561 for (const ObjectInstanceID& instance : brim.instances)
4562 this->m_objsWithBrim.insert(instance);
4563 }
4564 }
4565 if (this->m_objsWithBrim.empty()) m_brim_done = true;
4566
4567 // SoftFever: calib
4568 if (print.calib_params().mode == CalibMode::Calib_PA_Line) {
4569 std::string gcode;
4571 if ((NOZZLE_CONFIG(outer_wall_acceleration) > 0 && NOZZLE_CONFIG(outer_wall_acceleration) > 0)) {
4572 gcode += m_writer.set_print_acceleration((unsigned int)floor(NOZZLE_CONFIG(outer_wall_acceleration) + 0.5));
4573 }
4574
4575 if (NOZZLE_CONFIG(outer_wall_jerk) > 0) {
4576 double jerk = NOZZLE_CONFIG(outer_wall_jerk);
4577 gcode += m_writer.set_jerk_xy(jerk);
4578 }
4579
4580 auto params = print.calib_params();
4581
4582 CalibPressureAdvanceLine pa_test(this);
4583
4584 auto fast_speed = CalibPressureAdvance::find_optimal_PA_speed(print.full_print_config(), pa_test.line_width(), pa_test.height_layer());
4585 auto slow_speed = std::max(10.0, fast_speed / 10.0);
4586 if (fast_speed < slow_speed + 5)
4587 fast_speed = slow_speed + 5;
4588
4589 pa_test.set_speed(fast_speed, slow_speed);
4590 pa_test.draw_numbers() = print.calib_params().print_numbers;
4591
4592 // ORCA-Belt: the PA line test draws directly on the build surface in
4593 // logical bed coordinates — on a belt printer that surface is the
4594 // belt plane, not the slicing plane.
4595 const bool belt_world_coords = print.config().belt_printer.value;
4596 if (belt_world_coords)
4597 install_belt_kinematics(m_writer, print.config(), /*world_coordinates=*/true);
4598 gcode += pa_test.generate_test(params.start, params.step, std::llround(std::ceil((params.end - params.start) / params.step)) + 1);
4599 if (belt_world_coords)
4600 install_belt_kinematics(m_writer, print.config(), /*world_coordinates=*/false);
4601
4602 file.write(gcode);
4603 } else {
4604 //BBS: open spaghetti detector
4605 if (is_bbl_printers) {
4606 // if (print.config().spaghetti_detector.value)
4607 file.write("M981 S1 P20000 ;open spaghetti detector\n");
4608 }
4609
4610 // Do all objects for each layer.
4611 if (print.config().print_sequence == PrintSequence::ByObject && !has_wipe_tower) {
4612 size_t finished_objects = 0;
4613 print_object_instance_sequential_active = first_has_extrude_print_object;
4614 const PrintObject *prev_object = (*print_object_instance_sequential_active)->print_object;
4615 for (; print_object_instance_sequential_active != print_object_instances_ordering.end(); ++ print_object_instance_sequential_active) {
4616 const PrintObject &object = *(*print_object_instance_sequential_active)->print_object;
4617 if (&object != prev_object || tool_ordering.first_extruder() != final_extruder_id) {
4618 auto cached_ordering = use_seq_dynamic_cache ? seq_dynamic_orderings.find(&object) : seq_dynamic_orderings.end();
4619 if (cached_ordering != seq_dynamic_orderings.end()) {
4620 // Never re-plan a selector object mid-export: the cached plan is what the
4621 // published stitched result was built from.
4622 tool_ordering = cached_ordering->second;
4623 } else {
4624 tool_ordering = ToolOrdering(object, final_extruder_id);
4625 tool_ordering.sort_and_build_data(object, final_extruder_id);
4626 }
4627 unsigned int new_extruder_id = tool_ordering.first_extruder();
4628 if (new_extruder_id == (unsigned int)-1)
4629 // Skip this object.
4630 continue;
4631 initial_extruder_id = new_extruder_id;
4632 final_extruder_id = tool_ordering.last_extruder();
4633 assert(final_extruder_id != (unsigned int)-1);
4634 }
4635 print.throw_if_canceled();
4636 this->set_origin(unscale((*print_object_instance_sequential_active)->shift));
4637 this->on_set_origin((*print_object_instance_sequential_active)->print_object,
4638 (*print_object_instance_sequential_active)->shift);
4639
4640 // BBS: prime extruder if extruder change happens before this object instance
4641 bool prime_extruder = false;
4642 if (finished_objects > 0) {
4643 // Move to the origin position for the copy we're going to print.
4644 // This happens before Z goes down to layer 0 again, so that no collision happens hopefully.
4645 m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer
4646 m_avoid_crossing_perimeters.use_external_mp_once();
4647 // BBS. change tool before moving to origin point.
4648 if (m_writer.need_toolchange(initial_extruder_id)) {
4649 const PrintObjectConfig& object_config = object.config();
4650 coordf_t initial_layer_print_height = print.config().initial_layer_print_height.value;
4651
4652 if (m_enable_exclude_object && print.config().support_object_skip_flush.value) {
4653 m_filament_instances_code = _encode_label_ids_to_base64({(*print_object_instance_sequential_active)->model_instance->get_labeled_id()});
4654 }
4655
4656 file.write(this->set_extruder(initial_extruder_id, initial_layer_print_height, true));
4657 prime_extruder = true;
4658 } else {
4659 file.write(this->retract());
4660 }
4661 file.write(m_writer.travel_to_z(m_max_layer_z + m_writer.config.z_hop.get_at(get_filament_config_index((int)initial_extruder_id))));
4662 file.write(this->travel_to(Point(0, 0), erNone, "move to origin position for next object"));
4664 // Disable motion planner when traveling to first object point.
4665 m_avoid_crossing_perimeters.disable_once();
4666 // Ff we are printing the bottom layer of an object, and we have already finished
4667 // another one, set first layer temperatures. This happens before the Z move
4668 // is triggered, so machine has more time to reach such temperatures.
4669 this->placeholder_parser().set("current_object_idx", int(finished_objects));
4670 std::string printing_by_object_gcode = this->placeholder_parser_process("printing_by_object_gcode", print.config().printing_by_object_gcode.value, initial_extruder_id);
4671 // Set first layer bed and extruder temperatures, don't wait for it to reach the temperature.
4672 this->_print_first_layer_bed_temperature(file, print, printing_by_object_gcode, initial_extruder_id, false);
4673 this->_print_first_layer_extruder_temperatures(file, print, printing_by_object_gcode, initial_extruder_id, false);
4674 file.writeln(printing_by_object_gcode);
4675 }
4676 // Reset the cooling buffer internal state (the current position, feed rate, accelerations).
4677 m_cooling_buffer->set_current_extruder(initial_extruder_id, get_extruder_id(initial_extruder_id));
4678 m_cooling_buffer->reset(this->writer().get_position());
4679 // The belt first-layer band is tracked per object as well: if the previous
4680 // object ended inside the band, this one has to open its own.
4681 m_belt_in_band = false;
4682 // Process all layers of a single object instance (sequential mode) with a parallel pipeline:
4683 // Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser
4684 // and export G-code into file.
4685 tool_ordering.cal_most_used_extruder(print.config());
4686 m_printed_objects.emplace_back(&object);
4687 this->process_layers(print, tool_ordering, collect_layers_to_print(object, print.config().belt_printer.value), *print_object_instance_sequential_active - object.instances().data(), file,
4688 prime_extruder);
4689 {
4690 // save the flush statitics stored in tool ordering by object
4691 print.m_statistics_by_extruder_count.stats_by_single_extruder += tool_ordering.get_filament_change_stats(ToolOrdering::FilamentChangeMode::SingleExt);
4692 print.m_statistics_by_extruder_count.stats_by_multi_extruder_best += tool_ordering.get_filament_change_stats(ToolOrdering::FilamentChangeMode::MultiExtBest);
4693 print.m_statistics_by_extruder_count.stats_by_multi_extruder_curr += tool_ordering.get_filament_change_stats(ToolOrdering::FilamentChangeMode::MultiExtCurr);
4694 // save sorted filament sequences
4695 const auto& layer_tools = tool_ordering.layer_tools();
4696 for (const auto& lt : layer_tools)
4697 m_sorted_layer_filaments.emplace_back(lt.extruders);
4698 }
4699
4700 // Orca: disable power loss recovery if it was enabled earlier
4701 {
4702 const auto plr_mode = print.config().enable_power_loss_recovery.value;
4704 file.write(m_writer.enable_power_loss_recovery(PowerLossRecoveryMode::Disable));
4705 }
4706 }
4707 ++ finished_objects;
4708 // Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed.
4709 // Reset it when starting another object from 1st layer.
4711 prev_object = &object;
4712 }
4713 } else {
4714 // Sort layers by Z.
4715 // All extrusion moves with the same top layer height are extruded uninterrupted.
4716 std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> layers_to_print = collect_layers_to_print(print);
4717 // Prusa Multi-Material wipe tower.
4718 if (has_wipe_tower && ! layers_to_print.empty()) {
4719 m_wipe_tower.reset(new WipeTowerIntegration(print.config(), print.get_plate_index(), print.get_plate_origin(), *print.wipe_tower_data().priming.get(),
4720 print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get(), print.get_slice_used_filaments(false)));
4721 m_wipe_tower->set_wipe_tower_depth(print.get_wipe_tower_depth());
4722 m_wipe_tower->set_wipe_tower_bbx(print.get_wipe_tower_bbx());
4723 m_wipe_tower->set_rib_offset(print.get_rib_offset());
4724 //BBS
4725 file.write(m_writer.travel_to_z(initial_layer_print_height + m_config.z_offset.value, "Move to the first layer height"));
4726
4727 if (wipe_tower_type == WipeTowerType::Type2 && print.config().single_extruder_multi_material_priming) {
4728 file.write(m_wipe_tower->prime(*this));
4729 // Verify, whether the print overaps the priming extrusions.
4730 BoundingBoxf bbox_print(get_print_extrusions_extents(print));
4731 coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON;
4732 for (const PrintObject *print_object : print.objects())
4733 bbox_print.merge(get_print_object_extrusions_extents(*print_object, twolayers_printz));
4734 bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz));
4735 BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print));
4736 bbox_prime.offset(0.5f);
4737 bool overlap = bbox_prime.overlap(bbox_print);
4738
4739 if (print.config().gcode_flavor == gcfMarlinLegacy || print.config().gcode_flavor == gcfMarlinFirmware) {
4740 file.write(this->retract());
4741 file.write("M300 S800 P500\n"); // Beep for 500ms, tone 800Hz.
4742 if (overlap) {
4743 // Wait for the user to remove the priming extrusions.
4744 file.write("M1 Remove priming towers and click button.\n");
4745 } else {
4746 // Just wait for a bit to let the user check, that the priming succeeded.
4747 //TODO Add a message explaining what the printer is waiting for. This needs a firmware fix.
4748 file.write("M1 S10\n");
4749 }
4750 }
4751 else {
4752 // This is not Marlin, M1 command is probably not supported.
4753 if (overlap) {
4754 print.active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL,
4755 _(L("Your print is very close to the priming regions. "
4756 "Make sure there is no collision.")));
4757 } else {
4758 // Just continue printing, no action necessary.
4759 }
4760 }
4761 }
4762 print.throw_if_canceled();
4763 }
4764
4765 tool_ordering.cal_most_used_extruder(print.config());
4766
4767 // Process all layers of all objects (non-sequential mode) with a parallel pipeline:
4768 // Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser
4769 // and export G-code into file.
4770 this->process_layers(print, tool_ordering, print_object_instances_ordering, layers_to_print, file);
4771 {
4772 //save the flush statitics stored in tool ordering
4773 print.m_statistics_by_extruder_count.stats_by_single_extruder = tool_ordering.get_filament_change_stats(ToolOrdering::FilamentChangeMode::SingleExt);
4774 print.m_statistics_by_extruder_count.stats_by_multi_extruder_best = tool_ordering.get_filament_change_stats(ToolOrdering::FilamentChangeMode::MultiExtBest);
4775 print.m_statistics_by_extruder_count.stats_by_multi_extruder_curr = tool_ordering.get_filament_change_stats(ToolOrdering::FilamentChangeMode::MultiExtCurr);
4776 // save sorted filament sequences
4777 const auto& layer_tools = tool_ordering.layer_tools();
4778 for (const auto& lt : layer_tools)
4779 m_sorted_layer_filaments.emplace_back(lt.extruders);
4780 }
4781
4782 // Orca: disable power loss recovery
4783 if (m_second_layer_things_done && print.config().enable_power_loss_recovery.value == PowerLossRecoveryMode::Enable) {
4784 file.write(m_writer.enable_power_loss_recovery(PowerLossRecoveryMode::Disable));
4785 }
4786 if (m_wipe_tower)
4787 // Purge the extruder, pull out the active filament.
4788 file.write(m_wipe_tower->finalize(*this));
4789 }
4790 }
4791 //BBS: the last retraction
4792 // Write end commands to file.
4793 file.write(this->retract(false, true));
4794
4795 // if needed, write the gcode_label_objects_end
4796 {
4797 std::string gcode;
4798 m_writer.add_object_change_labels(gcode);
4799 file.write(gcode);
4800 }
4801
4802 file.write(m_writer.set_fan(0));
4803 //BBS: make sure the additional fan is closed when end
4804 if(m_config.auxiliary_fan.value)
4805 file.write(m_writer.set_additional_fan(0));
4806 if (is_bbl_printers) {
4807 //BBS: close spaghetti detector
4808 //Note: M981 is also used to tell xcam the last layer is finished, so we need always send it even if spaghetti option is disabled.
4809 //if (print.config().spaghetti_detector.value)
4810 file.write("M981 S0 P20000 ; close spaghetti detector\n");
4811 }
4812
4813 // adds tag for processor
4815
4816 // Mark the start of the machine end g-code so the usage-block builder closes its open blocks here and
4817 // ignores filament changes inside the end g-code. Same enable_pre_heating gate as the start marker →
4818 // byte-frozen fleet unaffected.
4819 if (m_config.enable_pre_heating.value)
4820 file.write_format(";%s\n", GCodeProcessor::Machine_End_GCode_Start_Tag.c_str());
4821
4822 // Process filament-specific gcode in extruder order.
4823 {
4824 DynamicConfig config;
4825 config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
4826 //BBS
4827 config.set_key_value("layer_z", new ConfigOptionFloat(m_writer.get_position()(2) - m_config.z_offset.value));
4828 config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z));
4829 config.set_key_value("nozzle_diameter_at_nozzle_id", new ConfigOptionFloats(get_nozzle_diameters_by_nozzle_id(group_result.get())));
4830 config.set_key_value("nozzle_volume_types", new ConfigOptionStrings(get_nozzle_volume_types_by_nozzle_id(group_result.get())));
4831
4832 if (print.config().single_extruder_multi_material) {
4833 // Process the filament_end_gcode for the active filament only.
4834 int extruder_id = m_writer.filament()->id();
4835 config.set_key_value("filament_extruder_id", new ConfigOptionInt(extruder_id));
4836 config.set_key_value("current_filament_id", new ConfigOptionInt(extruder_id));
4837 config.set_key_value("current_extruder_id", new ConfigOptionInt((int) get_extruder_id(extruder_id)));
4838 config.set_key_value("current_nozzle_id", new ConfigOptionInt(nozzle_id_for_gcode_placeholder(group_result, extruder_id, (int) get_extruder_id(extruder_id), m_layer_index)));
4839 file.writeln(this->placeholder_parser_process("filament_end_gcode", print.config().filament_end_gcode.get_at(extruder_id), extruder_id, &config));
4840 } else {
4841 for (const std::string &end_gcode : print.config().filament_end_gcode.values) {
4842 int extruder_id = (unsigned int)(&end_gcode - &print.config().filament_end_gcode.values.front());
4843 config.set_key_value("filament_extruder_id", new ConfigOptionInt(extruder_id));
4844 config.set_key_value("current_filament_id", new ConfigOptionInt(extruder_id));
4845 config.set_key_value("current_extruder_id", new ConfigOptionInt((int) get_extruder_id(extruder_id)));
4846 config.set_key_value("current_nozzle_id", new ConfigOptionInt(nozzle_id_for_gcode_placeholder(group_result, extruder_id, (int) get_extruder_id(extruder_id), m_layer_index)));
4847 file.writeln(this->placeholder_parser_process("filament_end_gcode", end_gcode, extruder_id, &config));
4848 }
4849 }
4850 file.writeln(this->placeholder_parser_process("machine_end_gcode", print.config().machine_end_gcode, m_writer.filament()->id(), &config));
4851 }
4852 file.write(m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100%
4853 file.write(m_writer.postamble());
4854
4855 if (activate_chamber_temp_control && max_chamber_temp > 0)
4856 file.write(m_writer.set_chamber_temperature(0, false)); //close chamber_temperature
4857
4858 // Orca: when air filtration is supported, check if it needs to be activated after print completion and set the exhaust fan speed accordingly
4859 if (m_config.support_air_filtration.value) {
4862
4863 // Orca: when activate_air_filtration is set on any extruder, find and set the highest complete_print_exhaust_fan_speed
4864 for (const auto& extruder : m_writer.extruders()) {
4865 size_t fi = get_filament_config_index((int)extruder.id());
4866 if (m_config.activate_air_filtration.get_at(fi) && m_config.activate_air_filtration_on_completion.get_at(fi)) {
4868 complete_print_exhaust_fan_speed = std::max(complete_print_exhaust_fan_speed, m_config.complete_print_exhaust_fan_speed.get_at(fi));
4869 }
4870 }
4871
4873 file.write(m_writer.set_exhaust_fan(complete_print_exhaust_fan_speed));
4874 }
4875
4876 // adds tags for time estimators
4878 file.write_format("; EXECUTABLE_BLOCK_END\n\n");
4879
4880 print.throw_if_canceled();
4881
4882 // Some firmwares only scan the last N lines for the time estimate, so the stats are written
4883 // after the config block.
4884 if (!is_bbl_printers && !skip_config_block) {
4885 file.write("; CONFIG_BLOCK_START\n");
4886 std::string full_config;
4887 append_full_config(print, full_config);
4888 if (!full_config.empty())
4889 file.write(full_config);
4890
4891 // SoftFever: write compatiple info
4892 file.write_format("; first_layer_bed_temperature = %d\n", compat_first_layer_bed_temp);
4893 file.write_format("; bed_shape = %s\n", print.full_print_config().opt_serialize("printable_area").c_str());
4894 file.write_format("; first_layer_temperature = %d\n", compat_first_layer_nozzle_temp);
4895 file.write_format("; first_layer_height = %.3f\n", print.config().initial_layer_print_height.value);
4896
4897 //SF TODO
4898// file.write_format("; variable_layer_height = %d\n", print.ad.adaptive_layer_height ? 1 : 0);
4899
4900 file.write("; CONFIG_BLOCK_END\n\n");
4901 }
4902
4903 // Get filament stats.
4905 // Const inputs
4906 has_wipe_tower, print.wipe_tower_data(),
4907 m_writer.extruders(),
4908 // Modifies
4909 print.m_print_statistics,
4910 // Const input (tool-change fallback for non-wipe-tower prints)
4911 print.tool_ordering()));
4912 print.m_print_statistics.initial_tool = initial_extruder_id;
4913 print.m_print_statistics.initial_no_support_tool = initial_non_support_extruder_id;
4914 if (!is_bbl_printers) {
4915 file.write_format("; total filament used [g] = %.2lf\n",
4916 print.m_print_statistics.total_weight);
4917 file.write_format("; total filament cost = %.2lf\n",
4918 print.m_print_statistics.total_cost);
4919 if (print.m_print_statistics.total_toolchanges > 0)
4920 file.write_format("; total filament change = %i\n",
4921 print.m_print_statistics.total_toolchanges);
4922 file.write_format("; total layers count = %i\n", m_layer_count);
4923 file.write_format(
4924 ";%s\n",
4927 .c_str());
4928 file.write("\n");
4929 }
4930 file.write("\n");
4931
4932 print.throw_if_canceled();
4933}
#define _(s)
Definition 3mf.cpp:306
static unsigned int count
Definition AuxiliaryDataViewModel.cpp:246
#define EPSILON
Definition ColorUtils.cpp:64
#define L(s)
Definition ExtrusionEntity.cpp:25
#define EXTRUDER_CONFIG(OPT)
Definition GCode.cpp:2155
#define NOZZLE_CONFIG(OPT)
Definition GCode.cpp:2157
static bool axis_remap_enabled(const PrintConfig &config)
Definition BeltTransform.hpp:54
static float find_optimal_PA_speed(const DynamicPrintConfig &config, double line_width, double layer_height, int extruder_id=0, int filament_idx=0)
Definition calib.cpp:29
std::vector< int > values
Definition Config.hpp:418
static std::string role_to_string(ExtrusionRole role)
Definition ExtrusionEntity.cpp:673
static const std::string Machine_End_GCode_Start_Tag
Definition GCodeProcessor.hpp:597
static int get_gcode_last_filament(const std::string &gcode_str)
Definition GCodeProcessor.cpp:4221
static const std::string & reserved_tag(ETags tag)
Definition GCodeProcessor.hpp:636
@ First_Line_M73_Placeholder
Definition GCodeProcessor.hpp:613
@ Normal_Print_Time_Placeholder
Definition GCodeProcessor.hpp:628
@ Total_Weight_Placeholder
Definition GCodeProcessor.hpp:630
@ Estimated_Printing_Time_Placeholder
Definition GCodeProcessor.hpp:615
@ Print_Time_Sec_Placeholder
Definition GCodeProcessor.hpp:626
@ Layer_Change
Definition GCodeProcessor.hpp:609
@ Total_Layer_Number_Placeholder
Definition GCodeProcessor.hpp:616
@ Print_Time_Hour_Placeholder
Definition GCodeProcessor.hpp:624
@ Role
Definition GCodeProcessor.hpp:604
@ Total_Wipe_Tower_Cost_Placeholder
Definition GCodeProcessor.hpp:632
@ Used_Filament_Length_Placeholder
Definition GCodeProcessor.hpp:627
@ Print_Time_Total_Sec_Placeholder
Definition GCodeProcessor.hpp:622
@ Silent_Print_Time_Placeholder
Definition GCodeProcessor.hpp:629
@ Total_Cost_Placeholder
Definition GCodeProcessor.hpp:631
@ Total_Wipe_Tower_Filament_Placeholder
Definition GCodeProcessor.hpp:633
@ Last_Line_M73_Placeholder
Definition GCodeProcessor.hpp:614
@ Print_Time_Minute_Placeholder
Definition GCodeProcessor.hpp:625
@ Print_Time_Day_Placeholder
Definition GCodeProcessor.hpp:623
static const std::string Machine_Start_GCode_End_Tag
Definition GCodeProcessor.hpp:596
std::string _encode_label_ids_to_base64(std::vector< size_t > ids)
Definition GCode.cpp:10266
std::string set_extruder(unsigned int extruder_id, double print_z, bool by_object=false, int toolchange_temp_override=-1, bool defer_temp_wait=false)
Definition GCode.cpp:10840
bool m_enable_cooling_markers
Definition GCode.hpp:815
bool m_brim_done
Definition GCode.hpp:901
std::unique_ptr< WipeTowerIntegration > m_wipe_tower
Definition GCode.hpp:894
std::string m_filament_instances_code
Definition GCode.hpp:939
void assign_object_label_ids(Print &print)
Definition GCode.cpp:11355
void print_machine_envelope(GCodeOutputStream &file, Print &print)
Definition GCode.cpp:5407
std::string travel_to(const Point &point, ExtrusionRole role, std::string comment, double z=DBL_MAX)
Definition GCode.cpp:10285
virtual void on_set_origin(const PrintObject *obj, const Point &inst_shift)
Definition GCode.hpp:443
GCodeProcessor m_processor
Definition GCode.hpp:919
WipeTowerType wipe_tower_type()
Definition GCode.cpp:2655
DynamicConfig m_calib_config
Definition GCode.hpp:740
std::string preamble()
Definition GCode.cpp:8413
friend class WipeTowerIntegration
Definition GCode.hpp:1045
std::vector< std::vector< unsigned int > > m_sorted_layer_filaments
Definition GCode.hpp:967
float m_max_layer_z
Definition GCode.hpp:849
static std::vector< LayerToPrint > collect_layers_to_print(const PrintObject &object, bool skip_empty_first_layer=false)
Definition GCode.cpp:2286
GCodeWriter m_writer
Definition GCode.hpp:743
bool m_timelapse_uses_clear_to_x0
Definition GCode.hpp:782
Print * m_print
Definition GCode.hpp:914
int m_layer_index
Definition GCode.hpp:835
bool m_second_layer_things_done
Definition GCode.hpp:903
std::string set_object_info(Print *print)
Definition GCode.cpp:11371
std::string set_filament_pressure_advance(unsigned int filament_id, int tool=-1)
Definition GCode.cpp:11327
bool m_timelapse_uses_pos
Definition GCode.hpp:781
void process_layers(const Print &print, const ToolOrdering &tool_ordering, const std::vector< const PrintInstance * > &print_object_instances_ordering, const std::vector< std::pair< coordf_t, std::vector< LayerToPrint > > > &layers_to_print, GCodeOutputStream &output_stream)
Definition GCode.cpp:5131
bool m_enable_extrusion_role_markers
Definition GCode.hpp:828
Vec2d m_origin
Definition GCode.hpp:738
void set_origin(const Vec2d &pointf)
Definition GCode.cpp:8401
size_t get_filament_config_index(int filament_id) const
Definition GCode.cpp:5039
std::string retract(bool toolchange=false, bool is_last_retraction=false, LiftType lift_type=LiftType::NormalLift, bool apply_instantly=false, ExtrusionRole role=erNone)
Definition GCode.cpp:10688
double m_last_layer_accumulated_mass
Definition GCode.hpp:853
static void append_full_config(const Print &print, std::string &str)
Definition GCode.cpp:8241
float m_last_height
Definition GCode.hpp:847
bool m_belt_in_band
Definition GCode.hpp:964
void apply_print_config(const PrintConfig &print_config)
Definition GCode.cpp:8204
std::vector< size_t > m_label_objects_ids
Definition GCode.hpp:818
bool m_enable_exclude_object
Definition GCode.hpp:817
std::map< unsigned int, IslandOrderCacheEntry > m_ordering_cache
Definition GCode.hpp:730
float m_last_width
Definition GCode.hpp:850
std::unique_ptr< SpiralVase > m_spiral_vase
Definition GCode.hpp:881
std::string m_imex_parallel_mode
Definition GCode.hpp:889
PlaceholderParser & placeholder_parser()
Definition GCode.hpp:289
std::map< int, int > m_imex_head_filament_map
Definition GCode.hpp:892
std::set< ObjectInstanceID > m_objsWithBrim
Definition GCode.hpp:682
void update_placeholder_parser_with_variant_params()
Definition GCode.cpp:10771
std::string placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_filament_id, const DynamicConfig *config_override=nullptr)
Definition GCode.cpp:5319
int get_bed_temperature(const int extruder_id, const bool is_first_layer, const BedType bed_type) const
Definition GCode.cpp:5497
std::unique_ptr< SmallAreaInfillFlowCompensator > m_small_area_infill_flow_compensator
Definition GCode.hpp:896
std::array< bool, ExtrusionRole::erCount > m_is_role_based_fan_on
Definition GCode.hpp:824
int m_start_gcode_filament
Definition GCode.hpp:938
virtual void init_belt_writer(Print &print)
Definition GCode.hpp:441
void set_extruders(const std::vector< unsigned int > &extruder_ids)
Definition GCode.cpp:8353
TimelapsePosPicker m_timelapse_pos_picker
Definition GCode.hpp:780
int get_highest_bed_temperature(const bool is_first_layer, const Print &print) const
Definition GCode.cpp:5504
float m_last_layer_z
Definition GCode.hpp:848
FullPrintConfig m_config
Definition GCode.hpp:739
bool m_last_pos_defined
Definition GCode.hpp:866
void _print_first_layer_extruder_temperatures(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait)
Definition GCode.cpp:5553
std::vector< const PrintObject * > m_printed_objects
Definition GCode.hpp:916
struct Slic3r::GCode::PlaceholderParserIntegration m_placeholder_parser_integration
AvoidCrossingPerimeters m_avoid_crossing_perimeters
Definition GCode.hpp:777
GCodeWriter & writer()
Definition GCode.hpp:287
size_t get_extruder_id(unsigned int filament_id) const
Definition GCode.cpp:5031
SeamPlacer m_seam_placer
Definition GCode.hpp:684
bool on_first_layer() const
Definition GCode.hpp:982
const FullPrintConfig & config() const
Definition GCode.hpp:285
OozePrevention m_ooze_prevention
Definition GCode.hpp:775
std::unique_ptr< CoolingBuffer > m_cooling_buffer
Definition GCode.hpp:880
std::unique_ptr< PressureEqualizer > m_pressure_equalizer
Definition GCode.hpp:882
std::unique_ptr< FanMover > m_fan_mover
Definition GCode.hpp:926
virtual void write_belt_header(GCodeOutputStream &file, const Print &print)
Definition GCode.hpp:442
std::optional< GCodeProcessor::PositionState > m_pending_start_gcode_position
Definition GCode.hpp:923
const Layer * layer() const
Definition GCode.hpp:286
void _print_first_layer_bed_temperature(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait)
Definition GCode.cpp:5520
std::array< int, ExtrusionRole::erCount > m_role_based_fan_marker_layer
Definition GCode.hpp:825
unsigned int m_layer_count
Definition GCode.hpp:833
std::unique_ptr< AdaptivePAProcessor > m_pa_processor
Definition GCode.hpp:884
void update_layer_related_config(int layer_id)
Definition GCode.cpp:10750
void append(const Point &point)
Definition MultiPoint.hpp:90
void set(const std::string &key, const std::string &value)
Definition PlaceholderParser.hpp:54
void set_deferred(const std::string &key, const std::string &tag)
Definition PlaceholderParser.hpp:65
static double area(const Points &pts)
Definition Polygon.cpp:65
@ SlicingPreciseSeamWarning
Definition PrintBase.hpp:74
@ CRITICAL
Definition PrintBase.hpp:63
@ NON_CRITICAL
Definition PrintBase.hpp:62
@ SingleExt
Definition ToolOrdering.hpp:266
@ MultiExtCurr
Definition ToolOrdering.hpp:268
@ MultiExtBest
Definition ToolOrdering.hpp:267
#define scale_(val)
Definition libslic3r.h:106
int64_t coord_t
Definition libslic3r.h:48
static constexpr size_t MAXIMUM_EXTRUDER_NUMBER
Definition libslic3r.h:70
static void init_ooze_prevention(const Print &print, OozePrevention &ooze_prevention)
Definition GCode.cpp:3173
static std::string update_print_stats_and_format_filament_stats(const bool has_wipe_tower, const WipeTowerData &wipe_tower_data, const std::vector< Extruder > &extruders, PrintStatistics &print_statistics, const ToolOrdering &tool_ordering)
Definition GCode.cpp:3206
static int resolve_total_toolchanges(const WipeTowerData &wipe_tower_data, const ToolOrdering &tool_ordering)
Definition GCode.cpp:3197
static void init_gcode_processor(const PrintConfig &config, GCodeProcessor &processor, const std::shared_ptr< MultiNozzleUtils::NozzleGroupResultBase > &nozzle_group_result=nullptr)
Definition GCode.cpp:3100
std::pair< GCodeThumbnailDefinitionsList, ThumbnailErrors > make_and_check_thumbnail_list(const std::string &thumbnails_string, const std::string_view def_ext)
Definition Thumbnails.cpp:546
void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb, int plate_id, const std::vector< std::pair< GCodeThumbnailsFormat, Vec2d > > &thumbnails_list, WriteToOutput output, ThrowIfCanceledCallback throw_if_canceled)
Definition Thumbnails.hpp:48
std::string get_error_string(const ThumbnailErrors &errors)
Definition Thumbnails.cpp:609
@ gcode
Definition Time.hpp:14
std::string local_timestamp(TimeFormat fmt=TimeFormat::gcode)
Definition Time.hpp:35
Kernel::Point_3 Point
Definition MeshRepair.cpp:81
coord_t width(const BoundingBox &box)
Definition Arrange.cpp:1184
Slic3r::Polygons union_(const Slic3r::Polygons &subject)
Definition ClipperUtils.cpp:801
@ gcfMarlinFirmware
Definition PrintConfig.hpp:56
@ gcfKlipper
Definition PrintConfig.hpp:53
@ gcfMarlinLegacy
Definition PrintConfig.hpp:52
zaa_minimize_perimeter_height ConfigOptionFloat
Definition PrintConfig.hpp:1954
static int nozzle_id_for_gcode_placeholder(const std::shared_ptr< MultiNozzleUtils::LayeredNozzleGroupResult > &group_result, int filament_id, int extruder_id, int layer_id=-1)
Definition GCode.cpp:241
ConfigOptionBoolsTempl< true > ConfigOptionBoolsNullable
Definition Config.hpp:2067
@ Calib_PA_Line
Definition calib.hpp:25
@ Calib_PA_Pattern
Definition calib.hpp:26
@ Calib_Auto_PA_Line
Definition calib.hpp:28
@ Calib_Retraction_tower
Definition calib.hpp:33
@ Calib_None
Definition calib.hpp:24
@ Calib_PA_Tower
Definition calib.hpp:27
static std::string get_bed_temp_key(const BedType type)
Definition PrintConfig.hpp:732
@ Enable
Definition PrintConfig.hpp:233
@ Disable
Definition PrintConfig.hpp:234
static void set_mass_locator(GCodeProcessor &processor, const Print &print)
Definition GCode.cpp:2664
Eigen::Matrix< double, 3, 1, Eigen::DontAlign > Vec3d
Definition Point.hpp:68
double coordf_t
Definition GUI_ObjectList.hpp:46
std::vector< const PrintInstance * > chain_print_object_instances_snake(const std::vector< const PrintObject * > &print_objects, const Point *start_near)
Definition OrderingStrategies.cpp:440
std::vector< ExPolygon > ExPolygons
Definition ExPolygon.hpp:20
std::map< int, int > parse_imex_head_filament_map(const std::string &s)
Definition IMEXHelpers.cpp:457
void install_belt_kinematics(GCodeWriter &writer, const PrintConfig &config, bool world_coordinates)
Definition BeltKinematics.cpp:24
std::vector< const PrintInstance * > sort_object_instances_by_model_order(const Print &print, bool init_order)
Definition GCode.cpp:3285
zaa_minimize_perimeter_height complete_print_exhaust_fan_speed((ConfigOptionFloatOrPercent, initial_layer_line_width))((ConfigOptionFloat
layer_height((ConfigOptionInt, faded_layers))((ConfigOptionFloat
zaa_minimize_perimeter_height single_extruder_multi_material_priming((ConfigOptionEnum< ToolChangeOrderingType >, toolchange_ordering))((ConfigOptionString
std::vector< const PrintInstance * > chain_print_object_instances(const std::vector< const PrintObject * > &print_objects, const Point *start_near)
Definition ShortestPath.cpp:2533
BedType
Definition PrintConfig.hpp:507
void center(ExPolygonsWithIds &e)
Definition Emboss.cpp:1382
static std::vector< Vec2d > get_path_of_change_filament(const Print &print)
Definition GCode.cpp:324
@ frTopSolidInfill
Definition Flow.hpp:23
@ frExternalPerimeter
Definition Flow.hpp:19
@ frPerimeter
Definition Flow.hpp:20
@ frSolidInfill
Definition Flow.hpp:22
@ frInfill
Definition Flow.hpp:21
BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_t max_print_z)
Definition PrintExtents.cpp:147
static std::vector< double > get_nozzle_diameters_by_nozzle_id(const MultiNozzleUtils::NozzleGroupResultBase *group_result)
Definition GCode.cpp:280
ImexMode find_imex_mode(const ConfigBase &cfg, const std::string &name)
Definition IMEXHelpers.cpp:665
std::unique_ptr< T > make_unique(Args &&... args)
Definition libslic3r.h:239
BoundingBox get_extents(const ExPolygonsWithIds &e)
Definition Emboss.cpp:1374
BoundingBoxf get_print_object_extrusions_extents(const PrintObject &print_object, const coordf_t max_print_z)
Definition PrintExtents.cpp:119
zaa_minimize_perimeter_height activate_air_filtration_on_completion((ConfigOptionInts, during_print_exhaust_fan_speed))((ConfigOptionInts
zaa_minimize_perimeter_height initial_layer_print_height((ConfigOptionFloatsNullable, initial_layer_speed))((ConfigOptionFloatsNullable
ConfigOptionFloatsTempl< true > ConfigOptionFloatsNullable
Definition Config.hpp:1016
static int first_nozzle_id_for_gcode_placeholder(const std::shared_ptr< MultiNozzleUtils::LayeredNozzleGroupResult > &group_result, int filament_id, int extruder_id)
Definition GCode.cpp:267
@ erCustom
Definition ExtrusionEntity.hpp:45
@ erNone
Definition ExtrusionEntity.hpp:27
std::vector< Polygon, PointsAllocator< Polygon > > Polygons
Definition Polygon.hpp:22
std::string header_slic3r_generated()
Definition utils.cpp:1470
zaa_minimize_perimeter_height extruder_offset((ConfigOptionBools, reduce_fan_stop_start_freq))((ConfigOptionBools
std::vector< const PrintInstance * > chain_print_object_instances_best_of(const std::vector< const PrintObject * > &print_objects, const Point *start_near)
Definition OrderingStrategies.cpp:454
Flow support_material_flow(const PrintObject *object, float layer_height)
Definition Flow.cpp:239
@ ByObject
Definition PrintConfig.hpp:312
zaa_minimize_perimeter_height PrintObjectConfig
Definition PrintConfig.hpp:2183
static std::vector< int > get_imex_active_tools(const Print &print)
Definition GCode.cpp:3365
ConfigOptionIntsTempl< false > ConfigOptionInts
Definition Config.hpp:1154
BoundingBoxf get_print_extrusions_extents(const Print &print)
Definition PrintExtents.cpp:112
int resolve_filament_for_head(const std::map< int, int > &plate_map, const ConfigOptionInts &pem, int physical)
Definition IMEXHelpers.cpp:556
constexpr Eigen::Matrix< Tout, 2, EigenArgs... > unscaled(const Slic3r::Point &v) noexcept
Definition Arrange.cpp:81
ConfigOptionBoolsTempl< false > ConfigOptionBools
Definition Config.hpp:2066
WipeTowerType
Definition PrintConfig.hpp:113
@ Type2
Definition PrintConfig.hpp:115
zaa_minimize_perimeter_height curr_bed_type((ConfigOptionInts, cool_plate_temp))((ConfigOptionInts
@ Default
Definition PrintConfig.hpp:319
@ BestOfStrategies
Definition PrintConfig.hpp:321
@ Snake
Definition PrintConfig.hpp:322
static std::vector< std::string > get_nozzle_volume_types_by_nozzle_id(const MultiNozzleUtils::NozzleGroupResultBase *group_result)
Definition GCode.cpp:296
constexpr const char * kImexPrimaryMode
Definition IMEXHelpers.hpp:27
zaa_minimize_perimeter_height machine_start_gcode((ConfigOptionStrings, filament_start_gcode))((ConfigOptionBool
zaa_minimize_perimeter_height ConfigOptionString
Definition PrintConfig.hpp:1702
std::string format(const char *fmt, TArgs &&... args)
Definition format.hpp:45
T unscale(Q v)
Definition libslic3r.h:138
static int first_hotend_id_for_gcode_placeholder(const std::shared_ptr< MultiNozzleUtils::LayeredNozzleGroupResult > &group_result, bool is_bbl_printer, int filament_id, int extruder_id)
Definition GCode.cpp:254
Vec2d travel_point_2
Definition GCode.cpp:322
Vec2d travel_point_1
Definition GCode.cpp:321
@ btfHighestTemp
Definition PrintConfig.hpp:226
static bool belt_object_layer_prints_something(const PrintObject &object, const Layer &layer)
Definition GCode.cpp:2273
Vec2d travel_point_3
Definition GCode.cpp:323
static float get_outer_wall_volumetric_speed(const FullPrintConfig &config, const Print &print, int filament_id, int filament_variant_idx, int extruder_id)
Definition GCode.cpp:760
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip)
Definition ClipperUtils.cpp:1111
RemapAxis
Definition PrintConfig.hpp:349
@ PosX
Definition PrintConfig.hpp:350
@ PosY
Definition PrintConfig.hpp:350
@ PosZ
Definition PrintConfig.hpp:350
Eigen::Matrix< float, 2, 1, Eigen::DontAlign > Vec2f
Definition Point.hpp:64
Eigen::Matrix< double, 2, 1, Eigen::DontAlign > Vec2d
Definition Point.hpp:67
static std::string get_bed_temp_1st_layer_key(const BedType type)
Definition PrintConfig.hpp:755
auto cast(const BoundingBoxBase< Tin > &b)
Definition BoundingBox.hpp:299
BoundingBoxf get_wipe_tower_priming_extrusions_extents(const Print &print)
Definition PrintExtents.cpp:181
static bool custom_gcode_sets_temperature(const std::string &gcode, const int mcode_set_temp_dont_wait, const int mcode_set_temp_and_wait, const bool include_g10, int &temp_out)
Definition GCode.cpp:780
ConfigOptionFloatsTempl< false > ConfigOptionFloats
Definition Config.hpp:1015
static bool is_multi_nozzle_printer(const FullPrintConfig &config)
Definition GCode.cpp:216
zaa_minimize_perimeter_height activate_chamber_temp_control((ConfigOptionInts, chamber_temperature))((ConfigOptionInts
std::vector< int > imex_secondary_logical_slots(const std::vector< int > &active_physicals, int primary_physical, const std::map< int, int > &plate_head_filament_map, const ConfigOptionInts &pem)
Definition IMEXHelpers.cpp:539
Slic3r::Polygon Polygon
Definition Emboss.cpp:101

References _, _encode_label_ids_to_base64(), _print_first_layer_bed_temperature(), _print_first_layer_extruder_temperatures(), Slic3r::activate_air_filtration_on_completion(), Slic3r::activate_chamber_temp_control(), Slic3r::PrintBaseWithState< PrintStepEnum, COUNT >::active_step_add_warning(), Slic3r::ImexMode::active_tools, Slic3r::ToolOrdering::all_extruders(), Slic3r::MultiPoint::append(), append_full_config(), apply_print_config(), Slic3r::BBoxData::area, Slic3r::Polygon::area(), Slic3r::ToolOrdering::assign_custom_gcodes(), assign_object_label_ids(), Slic3r::BeltTransformPipeline::axis_remap_enabled(), Slic3r::ConstVectorOfPtrsAdaptor< T >::back(), Slic3r::WipeTowerData::bbx, Slic3r::belt_object_layer_prints_something(), Slic3r::BestOfStrategies, Slic3r::Print::SkirtBrimGroup::Brim::brim, Slic3r::btfHighestTemp, Slic3r::ByObject, Slic3r::ToolOrdering::cal_max_additional_fan(), Slic3r::ToolOrdering::cal_most_used_extruder(), Slic3r::ToolOrdering::cal_non_support_filaments(), Slic3r::Calib_Auto_PA_Line, Slic3r::Print::calib_mode(), Slic3r::Calib_None, Slic3r::Calib_PA_Line, Slic3r::Calib_PA_Pattern, Slic3r::Calib_PA_Tower, Slic3r::Print::calib_params(), Slic3r::Calib_Retraction_tower, Slic3r::cast(), Slic3r::BoundingBoxBase< PointType, APointsType >::center(), Slic3r::center(), Slic3r::chain_print_object_instances(), Slic3r::chain_print_object_instances_best_of(), Slic3r::chain_print_object_instances_snake(), Slic3r::StatisticsByExtruderCount::clear(), collect_layers_to_print(), Slic3r::complete_print_exhaust_fan_speed(), config(), Slic3r::Print::config(), Slic3r::PrintObject::config(), Slic3r::ConfigOptionFloat, Slic3r::ConfigOptionString, count, Slic3r::PrintStateBase::CRITICAL, Slic3r::curr_bed_type(), Slic3r::custom_gcode_sets_temperature(), Slic3r::Default, Slic3r::Print::default_object_config(), Slic3r::Print::default_region_config(), Slic3r::Disable, Slic3r::CalibPressureAdvanceLine::draw_numbers(), Slic3r::ConstVectorOfPtrsAdaptor< T >::empty(), Slic3r::ExtrusionEntityCollection::empty(), Slic3r::Enable, EPSILON, Slic3r::erCustom, Slic3r::erNone, Slic3r::GCodeProcessor::Estimated_Printing_Time_Placeholder, Slic3r::GCodeThumbnails::export_thumbnails_to_file(), EXTRUDER_CONFIG, Slic3r::extruder_offset(), Slic3r::Print::extruders(), Slic3r::WipeTowerData::final_purge, Slic3r::find_imex_mode(), Slic3r::CalibPressureAdvance::find_optimal_PA_speed(), Slic3r::ToolOrdering::first_extruder(), Slic3r::first_hotend_id_for_gcode_placeholder(), Slic3r::Print::first_layer_convex_hull(), Slic3r::GCodeProcessor::First_Line_M73_Placeholder, Slic3r::first_nozzle_id_for_gcode_placeholder(), Slic3r::PrintRegion::flow(), Slic3r::format(), Slic3r::ImexMode::found(), Slic3r::frExternalPerimeter, Slic3r::frInfill, Slic3r::ConstVectorOfPtrsAdaptor< T >::front(), Slic3r::frPerimeter, Slic3r::frSolidInfill, Slic3r::frTopSolidInfill, Slic3r::PrintBase::full_print_config(), Slic3r::gcfKlipper, Slic3r::gcfMarlinFirmware, Slic3r::gcfMarlinLegacy, Slic3r::ImexMode::gcode, Slic3r::CalibPressureAdvanceLine::generate_test(), Slic3r::get_bed_temp_1st_layer_key(), Slic3r::get_bed_temp_key(), get_bed_temperature(), Slic3r::GCodeThumbnails::get_error_string(), Slic3r::get_extents(), get_extruder_id(), Slic3r::Print::get_extruder_shared_printable_polygon(), Slic3r::Print::get_fake_wipe_tower(), Slic3r::ToolOrdering::get_filament_change_stats(), get_filament_config_index(), Slic3r::GCodeProcessor::get_gcode_last_filament(), get_highest_bed_temperature(), Slic3r::get_imex_active_tools(), Slic3r::Print::get_layered_nozzle_group_result(), Slic3r::Print::get_model_name(), Slic3r::get_nozzle_diameters_by_nozzle_id(), Slic3r::get_nozzle_volume_types_by_nozzle_id(), Slic3r::Print::get_object(), Slic3r::get_outer_wall_volumetric_speed(), Slic3r::get_path_of_change_filament(), Slic3r::PrintBase::get_plate_index(), Slic3r::PrintBase::get_plate_name(), Slic3r::Print::get_plate_number_formatted(), Slic3r::Print::get_plate_origin(), Slic3r::get_print_extrusions_extents(), Slic3r::get_print_object_extrusions_extents(), Slic3r::Print::get_print_region(), Slic3r::Print::get_rib_offset(), Slic3r::Print::get_slice_used_filaments(), Slic3r::Print::get_wipe_tower_bbx(), Slic3r::Print::get_wipe_tower_depth(), Slic3r::get_wipe_tower_extrusions_extents(), Slic3r::get_wipe_tower_priming_extrusions_extents(), Slic3r::GCodeProcessor::PositionState::has_known_xy(), Slic3r::Print::has_support_material(), Slic3r::Print::has_wipe_tower(), Slic3r::ToolOrdering::has_wipe_tower(), Slic3r::header_slic3r_generated(), Slic3r::CalibPressureAdvanceLine::height_layer(), Slic3r::imex_secondary_logical_slots(), Slic3r::ImexMode::index, init_belt_writer(), Slic3r::DoExport::init_gcode_processor(), Slic3r::DoExport::init_ooze_prevention(), Slic3r::initial_layer_print_height(), Slic3r::PrintStatistics::initial_no_support_tool, Slic3r::PrintStatistics::initial_tool, Slic3r::install_belt_kinematics(), Slic3r::Print::SkirtBrimGroup::Brim::instances, Slic3r::intersection_pl(), Slic3r::Print::is_BBL_printer(), Slic3r::Print::is_dynamic_group_reorder(), Slic3r::is_multi_nozzle_printer(), Slic3r::kImexPrimaryMode, L, Slic3r::ToolOrdering::last_extruder(), Slic3r::GCodeProcessor::Last_Line_M73_Placeholder, layer(), Slic3r::GCodeProcessor::Layer_Change, Slic3r::BBoxData::layer_height, Slic3r::layer_height(), Slic3r::ToolOrdering::layer_tools(), Slic3r::PrintObject::layers(), Slic3r::CalibPressureAdvanceLine::line_width(), Slic3r::Utils::local_timestamp(), Slic3r::Layer::lslices, m_avoid_crossing_perimeters, m_belt_in_band, m_brim_done, Slic3r::Print::m_brimMap, m_calib_config, m_config, m_cooling_buffer, m_enable_cooling_markers, m_enable_exclude_object, m_enable_extrusion_role_markers, m_fan_mover, m_filament_instances_code, m_imex_head_filament_map, m_imex_parallel_mode, m_is_role_based_fan_on, m_label_objects_ids, m_last_height, m_last_layer_accumulated_mass, m_last_layer_z, m_last_pos_defined, m_last_width, m_layer_count, m_layer_index, m_max_layer_z, m_objsWithBrim, m_ooze_prevention, m_ordering_cache, m_origin, m_pa_processor, m_pending_start_gcode_position, m_placeholder_parser_integration, m_pressure_equalizer, m_print, Slic3r::Print::m_print_statistics, m_printed_objects, m_processor, m_role_based_fan_marker_layer, m_seam_placer, m_second_layer_things_done, m_small_area_infill_flow_compensator, m_sorted_layer_filaments, m_spiral_vase, m_start_gcode_filament, Slic3r::Print::m_statistics_by_extruder_count, m_timelapse_pos_picker, m_timelapse_uses_clear_to_x0, m_timelapse_uses_pos, m_wipe_tower, m_writer, Slic3r::GCodeProcessor::Machine_End_GCode_Start_Tag, Slic3r::machine_start_gcode(), Slic3r::GCodeProcessor::Machine_Start_GCode_End_Tag, Slic3r::GCodeThumbnails::make_and_check_thumbnail_list(), Slic3r::make_unique(), Slic3r::BoundingBoxBase< PointType, APointsType >::max, MAXIMUM_EXTRUDER_NUMBER, Slic3r::BoundingBoxBase< PointType, APointsType >::merge(), Slic3r::BoundingBoxBase< PointType, APointsType >::min, Slic3r::Calib_Params::mode, Slic3r::ToolOrdering::MultiExtBest, Slic3r::ToolOrdering::MultiExtCurr, Slic3r::BBoxData::name, Slic3r::Print::need_check_multi_filaments_compatibility(), Slic3r::PrintStateBase::NON_CRITICAL, Slic3r::GCodeProcessor::Normal_Print_Time_Placeholder, NOZZLE_CONFIG, Slic3r::nozzle_id_for_gcode_placeholder(), Slic3r::Print::num_object_instances(), Slic3r::Print::num_print_regions(), Slic3r::Print::objects(), Slic3r::BoundingBoxBase< PointType, APointsType >::offset(), on_first_layer(), on_set_origin(), Slic3r::ConfigBase::opt_serialize(), Slic3r::BoundingBoxBase< PointType, APointsType >::overlap(), Slic3r::parse_imex_head_filament_map(), placeholder_parser(), Slic3r::PrintBase::placeholder_parser(), placeholder_parser_process(), Slic3r::MultiPoint::points, Slic3r::FakeWipeTower::pos, Slic3r::GCodeProcessor::PositionState::position, Slic3r::PosX, Slic3r::PosY, Slic3r::PosZ, preamble(), Slic3r::WipeTowerData::priming, Print, Slic3r::CalibPressureAdvanceLine::print_extents(), print_machine_envelope(), Slic3r::Calib_Params::print_numbers, Slic3r::Print::print_object_ids(), Slic3r::GCodeProcessor::Print_Time_Day_Placeholder, Slic3r::GCodeProcessor::Print_Time_Hour_Placeholder, Slic3r::GCodeProcessor::Print_Time_Minute_Placeholder, Slic3r::GCodeProcessor::Print_Time_Sec_Placeholder, Slic3r::GCodeProcessor::Print_Time_Total_Sec_Placeholder, Slic3r::Layer::print_z, Slic3r::PrintObjectConfig, process_layers(), Slic3r::GCodeProcessor::reserved_tag(), Slic3r::resolve_filament_for_head(), Slic3r::DoExport::resolve_total_toolchanges(), retract(), Slic3r::GCodeProcessor::Role, Slic3r::ExtrusionEntity::role_to_string(), scale_, Slic3r::Print::sequential_dynamic_orderings(), Slic3r::PlaceholderParser::set(), Slic3r::PlaceholderParser::set_deferred(), set_extruder(), set_extruders(), set_filament_pressure_advance(), Slic3r::set_mass_locator(), set_object_info(), set_origin(), Slic3r::CalibPressureAdvanceLine::set_speed(), Slic3r::GCodeProcessor::Silent_Print_Time_Placeholder, Slic3r::single_extruder_multi_material_priming(), Slic3r::ToolOrdering::SingleExt, Slic3r::BoundingBoxBase< PointType, APointsType >::size(), Slic3r::MultiPoint::size(), Slic3r::Print::skirt_brim_groups(), Slic3r::PrintStateBase::SlicingPreciseSeamWarning, Slic3r::Snake, Slic3r::ToolOrdering::sort_and_build_data(), Slic3r::sort_object_instances_by_model_order(), Slic3r::StatisticsByExtruderCount::stats_by_multi_extruder_best, Slic3r::StatisticsByExtruderCount::stats_by_multi_extruder_curr, Slic3r::StatisticsByExtruderCount::stats_by_single_extruder, Slic3r::SupportLayer::support_islands, Slic3r::PrintObject::support_layers(), Slic3r::support_material_flow(), Slic3r::PrintBase::throw_if_canceled(), Slic3r::WipeTowerData::tool_changes, Slic3r::Print::tool_ordering(), Slic3r::PrintStatistics::total_cost, Slic3r::GCodeProcessor::Total_Cost_Placeholder, Slic3r::GCodeProcessor::Total_Layer_Number_Placeholder, Slic3r::PrintStatistics::total_toolchanges, Slic3r::PrintStatistics::total_weight, Slic3r::GCodeProcessor::Total_Weight_Placeholder, Slic3r::GCodeProcessor::Total_Wipe_Tower_Cost_Placeholder, Slic3r::GCodeProcessor::Total_Wipe_Tower_Filament_Placeholder, Slic3r::BoundingBoxBase< PointType, APointsType >::translate(), Slic3r::Print::translate_to_print_space(), Slic3r::travel_point_1, Slic3r::travel_point_2, Slic3r::travel_point_3, travel_to(), Slic3r::Type2, Slic3r::union_(), Slic3r::unscale(), Slic3r::unscaled(), update_layer_related_config(), Slic3r::PrintBase::update_object_placeholders(), update_placeholder_parser_with_variant_params(), Slic3r::DoExport::update_print_stats_and_format_filament_stats(), Slic3r::GCodeProcessor::Used_Filament_Length_Placeholder, Slic3r::ConfigOptionVector< int >::values, Slic3r::Flow::width(), Slic3r::Print::wipe_tower_data(), Slic3r::WipeTowerData::wipe_tower_mesh_data, wipe_tower_type(), Slic3r::Print::wipe_tower_type(), WipeTowerIntegration, Slic3r::GCode::GCodeOutputStream::write(), write_belt_header(), Slic3r::GCode::GCodeOutputStream::write_format(), Slic3r::GCode::GCodeOutputStream::writeln(), and writer().

Referenced by do_export().

Here is the caller graph for this function:

◆ _encode_label_ids_to_base64()

std::string Slic3r::GCode::_encode_label_ids_to_base64 ( std::vector< size_t > ids)
protectedinherited
10267{
10268 assert(m_label_objects_ids.size() < 64);
10269
10270 uint64_t bitset = 0;
10271 for (size_t id : ids) {
10272 auto index = std::lower_bound(m_label_objects_ids.begin(), m_label_objects_ids.end(), id);
10273 if (index != m_label_objects_ids.end() && *index == id)
10274 bitset |= (1ull << (index - m_label_objects_ids.begin()));
10275 else
10276 throw Slic3r::LogicError("Unknown label object id!");
10277 }
10278 if (bitset == 0)
10279 throw Slic3r::LogicError("Label object id error!");
10280
10281 return encodeBase64(bitset);
10282}
std::string encodeBase64(uint64_t value)
Definition GCode.cpp:10253

References Slic3r::encodeBase64(), and m_label_objects_ids.

Referenced by _do_export(), and generate_timelapse_gcode().

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

◆ _extrude()

std::string Slic3r::GCode::_extrude ( const ExtrusionPath & path,
const std::string & path_description = "",
double speed = -1 )
protectedinherited
9186{
9187 std::string gcode;
9188
9189 const std::string bridge_description = is_bridge(path.role()) ? path_description + " (bridge)" : std::string();
9190 const std::string &description = bridge_description.empty() ? path_description : bridge_description;
9191
9192 // First-layer plane evaluation: compute the path's slicing-frame point
9193 // once and reuse for every per-path call site below. When the plane
9194 // evaluator is inactive (non-belt printers, or belt printers without
9195 // a Z-axis shear) `path_on_first_layer` falls back to the legacy
9196 // layer-id check, so behavior is bit-identical to the pre-feature path.
9197 // A belt brim apron band has no Layer of its own, so it publishes its Z
9198 // through m_belt_brim_z instead; without that the plane would be probed at
9199 // Z=0 and the apron mis-classified for fan and speed.
9200 const Vec3d path_point_mm{
9201 unscale<double>(path.first_point().x()),
9202 unscale<double>(path.first_point().y()),
9203 m_layer ? m_layer->print_z : (m_belt_brim_z ? *m_belt_brim_z : 0.0)
9204 };
9205 const bool path_on_first_layer = this->on_first_layer(path_point_mm);
9206
9207 const ExtrusionPathSloped* sloped = dynamic_cast<const ExtrusionPathSloped*>(&path);
9208
9209 const auto get_sloped_z = [&sloped, this](double z_ratio) {
9210 const auto height = sloped->height;
9211 return lerp(m_nominal_z - height, m_nominal_z, z_ratio);
9212 };
9213
9214 bool slope_need_z_travel = false;
9215 if (sloped != nullptr && !sloped->is_flat()) {
9216 auto target_z = get_sloped_z(sloped->slope_begin.z_ratio);
9217 slope_need_z_travel = m_writer.will_move_z(target_z);
9218 }
9219 // Move to first point of extrusion path
9220 // path is 2D. But in slope lift case, lift z is done in travel_to function.
9221 // Add m_need_change_layer_lift_z when change_layer in case of no lift if m_last_pos is equal to path.first_point() by chance
9222 Point first_point = path.first_point();
9223 if (!m_last_pos_defined || m_last_pos.to_point() != first_point || m_need_change_layer_lift_z ||
9224 slope_need_z_travel) {
9225 const bool _last_pos_undefined = !m_last_pos_defined;
9226
9227 double z = DBL_MAX;
9228 if (sloped != nullptr) {
9229 z = get_sloped_z(sloped->slope_begin.z_ratio);
9230 } else if (path.z_contoured && !path.polyline.lines().empty()) {
9231 z = unscale_(path.polyline.lines().begin()->a.z()) + m_nominal_z;
9232 }
9233
9234 gcode += this->travel_to(first_point, path.role(), "move to first " + description + " point", z);
9235
9236 // Orca: ensure Z matches planned layer height
9237 if (!slope_need_z_travel && (_last_pos_undefined || m_need_change_layer_lift_z)) {
9238 const std::string z_sync_comment = _last_pos_undefined ?
9239 "ensure Z matches planned layer height" : ""; // no comment for normal layer-Z lift
9240 gcode += this->writer().travel_to_z(m_nominal_z, z_sync_comment, true);
9241 }
9243 }
9244
9245 if (path.z_contoured && !path.polyline.lines().empty()) {
9246 double current_z = m_writer.get_position().z();
9247 double first_z = unscale_(path.polyline.lines().begin()->a.z()) + m_nominal_z;
9248 if (GCodeFormatter::quantize_xyz(first_z, m_config.high_precision_gcode.value) !=
9249 GCodeFormatter::quantize_xyz(current_z, m_config.high_precision_gcode.value)) {
9250 gcode += m_writer.travel_to_z(first_z, "set Z for contouring", true);
9251 }
9252 }
9253 if (!path.z_contoured && sloped == nullptr) {
9254 double current_z = m_writer.get_position().z();
9255 if (GCodeFormatter::quantize_xyz(current_z, m_config.high_precision_gcode.value) !=
9256 GCodeFormatter::quantize_xyz(m_nominal_z, m_config.high_precision_gcode.value)) {
9257 gcode += this->writer().travel_to_z(m_nominal_z, "reset Z after contouring", true);
9258 }
9259 }
9260
9261 // if needed, write the gcode_label_objects_end then gcode_label_objects_start
9262 // should be already done by travel_to, but just in case
9263 m_writer.add_object_change_labels(gcode);
9264
9265 // compensate retraction
9266 gcode += this->unretract();
9267 m_config.apply(m_calib_config);
9268
9269 // Orca: optimize for Klipper, set acceleration and jerk in one command
9270 unsigned int acceleration_i = 0;
9271 double jerk = 0;
9272 // adjust acceleration
9273 const size_t nozzle = get_nozzle_config_index(m_writer.filament()->id());
9274 if (m_config.default_acceleration.get_at(nozzle) > 0) {
9275 const ExtrusionRole role = path.role();
9276 const double bridge_acceleration = is_bridge(role) ?
9277 m_config.bridge_acceleration.get_at(nozzle).get_abs_value(m_config.outer_wall_acceleration.get_at(nozzle)) : 0.;
9278 const double sparse_infill_acceleration = role == erInternalInfill ?
9279 m_config.sparse_infill_acceleration.get_at(nozzle).get_abs_value(m_config.default_acceleration.get_at(nozzle)) : 0.;
9280 const double internal_solid_infill_acceleration = role == erSolidInfill ?
9281 m_config.internal_solid_infill_acceleration.get_at(nozzle).get_abs_value(m_config.default_acceleration.get_at(nozzle)) : 0.;
9282 double acceleration;
9283 if (path_on_first_layer && m_config.initial_layer_acceleration.get_at(nozzle) > 0) {
9284 acceleration = m_config.initial_layer_acceleration.get_at(nozzle);
9285#if 0
9286 } else if (this->object_layer_over_raft() && m_config.first_layer_acceleration_over_raft.value > 0) {
9287 acceleration = m_config.first_layer_acceleration_over_raft.value;
9288#endif
9289 } else if (bridge_acceleration > 0) {
9290 acceleration = bridge_acceleration;
9291 } else if (sparse_infill_acceleration > 0) {
9292 acceleration = sparse_infill_acceleration;
9293 } else if (internal_solid_infill_acceleration > 0) {
9294 acceleration = internal_solid_infill_acceleration;
9295 } else if (m_config.outer_wall_acceleration.get_at(nozzle) > 0 && is_external_perimeter(role)) {
9296 acceleration = m_config.outer_wall_acceleration.get_at(nozzle);
9297 } else if (m_config.inner_wall_acceleration.get_at(nozzle) > 0 && is_internal_perimeter(role)) {
9298 acceleration = m_config.inner_wall_acceleration.get_at(nozzle);
9299 } else if (m_config.top_surface_acceleration.get_at(nozzle) > 0 && is_top_surface(role)) {
9300 acceleration = m_config.top_surface_acceleration.get_at(nozzle);
9301 } else {
9302 acceleration = m_config.default_acceleration.get_at(nozzle);
9303 }
9304 acceleration_i = (unsigned int)floor(acceleration + 0.5);
9305 }
9306
9307 // adjust X Y jerk
9308 if (NOZZLE_CONFIG(default_jerk) > 0) {
9309 if (path_on_first_layer && NOZZLE_CONFIG(initial_layer_jerk) > 0) {
9310 jerk = NOZZLE_CONFIG(initial_layer_jerk);
9311 } else if (NOZZLE_CONFIG(outer_wall_jerk) > 0 && is_external_perimeter(path.role())) {
9312 jerk = NOZZLE_CONFIG(outer_wall_jerk);
9313 } else if (NOZZLE_CONFIG(inner_wall_jerk) > 0 && is_internal_perimeter(path.role())) {
9314 jerk = NOZZLE_CONFIG(inner_wall_jerk);
9315 } else if (NOZZLE_CONFIG(top_surface_jerk) > 0 && is_top_surface(path.role())) {
9316 jerk = NOZZLE_CONFIG(top_surface_jerk);
9317 } else if (NOZZLE_CONFIG(infill_jerk) > 0 && is_infill(path.role())) {
9318 jerk = NOZZLE_CONFIG(infill_jerk);
9319 }
9320 else {
9321 jerk = NOZZLE_CONFIG(default_jerk);
9322 }
9323 }
9324
9325 if (m_writer.get_gcode_flavor() == gcfKlipper) {
9326 gcode += m_writer.set_accel_and_jerk(acceleration_i, jerk);
9327
9328 } else {
9329 gcode += m_writer.set_print_acceleration(acceleration_i);
9330 gcode += m_writer.set_jerk_xy(jerk);
9331 }
9332
9333 // calculate effective extrusion length per distance unit (e_per_mm)
9335 // We set _mm3_per_mm to effectove flow = Geometric volume * print flow ratio * filament flow ratio * role-based-flow-ratios
9336 auto _mm3_per_mm = path.mm3_per_mm * this->config().print_flow_ratio;
9337 _mm3_per_mm *= filament_flow_ratio;
9338
9339 if (path.role() == erTopSolidInfill) {
9341 } else if (path.role() == erBottomSurface) {
9342 _mm3_per_mm *= m_config.bottom_solid_infill_flow_ratio;
9343 } else if (path.role() == erInternalBridgeInfill) {
9344 _mm3_per_mm *= m_config.internal_bridge_flow;
9345 } else if (path.role() == erBrim) {
9346 _mm3_per_mm *= m_config.brim_flow_ratio;
9347 } else if (sloped) {
9348 _mm3_per_mm *= m_config.scarf_joint_flow_ratio;
9349 }
9350
9351 if (m_config.set_other_flow_ratios) {
9352 if (path.role() == erExternalPerimeter) {
9353 _mm3_per_mm *= m_config.outer_wall_flow_ratio;
9354 } else if (path.role() == erPerimeter) {
9355 _mm3_per_mm *= m_config.inner_wall_flow_ratio;
9356 } else if (path.role() == erOverhangPerimeter) {
9357 _mm3_per_mm *= m_config.overhang_flow_ratio;
9358 } else if (path.role() == erInternalInfill) {
9359 _mm3_per_mm *= m_config.sparse_infill_flow_ratio;
9360 } else if (path.role() == erSolidInfill) {
9361 _mm3_per_mm *= m_config.internal_solid_infill_flow_ratio;
9362 } else if (path.role() == erGapFill) {
9363 _mm3_per_mm *= m_config.gap_fill_flow_ratio;
9364 } else if (path.role() == erSupportMaterial) { // Should this condition also cover erSupportTransition?
9365 _mm3_per_mm *= m_config.support_flow_ratio;
9366 } else if (path.role() == erSupportMaterialInterface) {
9367 _mm3_per_mm *= m_config.support_interface_flow_ratio;
9368 }
9369
9370 // Additionally, adjust the value if we are on the first layer (except for brims and skirts)
9371 if (path_on_first_layer && (path.role() != erBrim && path.role() != erSkirt)) {
9372 _mm3_per_mm *= m_config.first_layer_flow_ratio;
9373 }
9374 }
9375
9376 // Mixed-color sublayer: this path belongs to one sub-layer of a split layer, so scale the
9377 // flow down to that sub-layer's share of the nominal layer height and report the sub-height
9378 // as the effective extrusion height. Inert (ratio == 0) outside the sublayer emission block.
9379 float effective_height = path.height;
9380 if (m_sub_layer_flow_ratio > 0.0) {
9381 _mm3_per_mm *= m_sub_layer_flow_ratio;
9382 effective_height = static_cast<float>(m_sub_layer_height);
9383 }
9384
9385 // _mm3_per_mm already includes the filament flow ratio, so convert it with the bare cross-section
9386 // rather than e_per_mm3(), which would apply the ratio a second time.
9387 double e_per_mm = _mm3_per_mm / m_writer.filament()->filament_crossection();
9388
9389 // set speed
9390 if (speed == -1) {
9391 if (path.role() == erPerimeter) {
9393 if (sloped) {
9394 speed = std::min(speed, m_config.scarf_joint_speed.get_abs_value(speed));
9395 }
9396 } else if (path.role() == erExternalPerimeter) {
9398 if (sloped) {
9399 speed = std::min(speed, m_config.scarf_joint_speed.get_abs_value(speed));
9400 }
9401 } else if(path.role() == erInternalBridgeInfill) {
9402 speed = m_config.internal_bridge_speed.get_at(nozzle).get_abs_value(m_config.bridge_speed.get_at(nozzle));
9403 } else if (path.role() == erOverhangPerimeter || path.role() == erSupportTransition || path.role() == erBridgeInfill) {
9404 speed = NOZZLE_CONFIG(bridge_speed);
9405 } else if (path.role() == erInternalInfill) {
9407 } else if (path.role() == erSolidInfill) {
9409 } else if (path.role() == erTopSolidInfill) {
9410 speed = NOZZLE_CONFIG(top_surface_speed);
9411 } else if (path.role() == erIroning) {
9412 const size_t filament_idx = get_filament_config_index(m_writer.filament()->id());
9413 speed = m_config.filament_ironing_speed.is_nil(filament_idx)
9414 ? m_config.ironing_speed.value
9415 : m_config.filament_ironing_speed.get_at(filament_idx);
9416 } else if (path.role() == erBottomSurface) {
9418 } else if (path.role() == erGapFill) {
9419 speed = NOZZLE_CONFIG(gap_infill_speed);
9420 } else if (path.role() == erSupportMaterial) {
9421 speed = NOZZLE_CONFIG(support_speed);
9422 } else if (path.role() == erSupportMaterialInterface) {
9423 speed = NOZZLE_CONFIG(support_interface_speed);
9424 } else {
9425 throw Slic3r::InvalidArgument("Invalid speed");
9426 }
9427 }
9428 //BBS: if not set the speed, then use the filament_max_volumetric_speed directly
9429 double filament_max_volumetric_speed = FILAMENT_CONFIG(filament_max_volumetric_speed);
9431 double fitted_value = calc_max_volumetric_speed(path.height, path.width, FILAMENT_CONFIG(volumetric_speed_coefficients));
9432 filament_max_volumetric_speed = std::min(filament_max_volumetric_speed, fitted_value);
9433 }
9434
9435 if (speed == 0)
9436 speed = filament_max_volumetric_speed / _mm3_per_mm;
9437 // Use the belt-aware effective layer index when on a belt printer so
9438 // the speed fade tracks perpendicular distance from the plane on
9439 // belt printers; otherwise this falls back to the slicing layer id.
9440 const int _layer = this->effective_layer_index_for_point(path_point_mm);
9441 // Belt printers: a tilted layer runs from the belt to the top of the part, so the
9442 // "first layers" the fan stays off for are a band along the belt. Mark where the
9443 // extrusion enters and leaves it, per segment, for the cooling buffer.
9444 const bool belt_band_tags = m_enable_cooling_markers && m_config.belt_printer.value;
9445 const int belt_band_layers = belt_band_tags ? m_config.close_fan_the_first_x_layers.get_at(m_writer.filament()->id()) : 0;
9446 auto tag_belt_band = [this, &gcode, belt_band_tags, belt_band_layers, z = path_point_mm.z()](coord_t x, coord_t y) {
9447 if (! belt_band_tags)
9448 return;
9449 const bool in_band = this->effective_layer_index_for_point(Vec3d(unscale<double>(x), unscale<double>(y), z)) < belt_band_layers;
9450 if (in_band != m_belt_in_band) {
9451 gcode += in_band ? ";_BELT_BAND_START\n" : ";_BELT_BAND_END\n";
9452 m_belt_in_band = in_band;
9453 }
9454 };
9455 if (path_on_first_layer || object_layer_over_raft()) {
9456 //BBS: for solid infill of first layer, speed can be higher as long as
9457 //wall lines have be attached
9458 if (path.role() != erBottomSurface) {
9459 const bool use_first_layer_speed = is_perimeter(path.role()) || path.role() == erBrim;
9460 speed = use_first_layer_speed ? NOZZLE_CONFIG(initial_layer_speed) :
9462 }
9463 } else if (m_config.slow_down_layers > 1 && m_config.raft_layers == 0) {
9464 if (_layer > 0 && _layer < m_config.slow_down_layers) {
9465 const auto first_layer_speed =
9466 is_perimeter(path.role())
9467 ? NOZZLE_CONFIG(initial_layer_speed)
9469 if (first_layer_speed < speed) {
9470 speed = std::min(
9471 speed,
9472 Slic3r::lerp(first_layer_speed, speed,
9473 (double) (_layer) / m_config.slow_down_layers));
9474 }
9475 }
9476 } else if (m_config.slow_down_layers > 1 && m_config.raft_layers > 0 ) {
9477
9478 if (_layer > m_config.raft_layers && (_layer - m_config.raft_layers) < m_config.slow_down_layers) {
9479 const auto first_layer_speed
9480 = is_perimeter(path.role()) ? NOZZLE_CONFIG(initial_layer_speed) :
9482 if (first_layer_speed < speed) {
9483 speed = std::min(speed, Slic3r::lerp(first_layer_speed, speed,
9484 (double) (_layer - m_config.raft_layers) / m_config.slow_down_layers));
9485 }
9486 }
9487 }
9488 // Override skirt speed if set
9489 if (path.role() == erSkirt) {
9490 const double skirt_speed = m_config.skirt_speed.value;
9491 if (skirt_speed > 0.0)
9492 speed = skirt_speed;
9493 }
9494 //BBS: remove this config
9495 //else if (this->object_layer_over_raft())
9496 // speed = m_config.get_abs_value("first_layer_speed_over_raft", speed);
9497 //if (m_config.max_volumetric_speed.value > 0) {
9498 // // cap speed with max_volumetric_speed anyway (even if user is not using autospeed)
9499 // speed = std::min(
9500 // speed,
9501 // m_config.max_volumetric_speed.value / _mm3_per_mm
9502 // );
9503 //}
9504 if (FILAMENT_CONFIG(filament_max_volumetric_speed) > 0) {
9505 // cap speed with max_volumetric_speed anyway (even if user is not using autospeed)
9506 speed = std::min(speed, FILAMENT_CONFIG(filament_max_volumetric_speed) / _mm3_per_mm);
9507 }
9508 // ORCA: resonance‑avoidance on short external perimeters
9509{
9510 double ref_speed = speed; // stash the pre‑cap speed
9511 if (path.role() == erExternalPerimeter
9512 && m_config.resonance_avoidance.value) {
9513
9514 // if our original speed was above “max”, disable RA for this loop
9515 if (ref_speed > m_config.max_resonance_avoidance_speed.value) {
9516 m_resonance_avoidance = false;
9517 }
9518
9519 // re‑apply volumetric cap
9520 if (FILAMENT_CONFIG(filament_max_volumetric_speed) > 0) {
9521 speed = std::min(
9522 speed,
9523 FILAMENT_CONFIG(filament_max_volumetric_speed) / _mm3_per_mm
9524 );
9525 }
9526
9527 // if still in avoidance mode and under "max", adjust speed:
9528 // - speeds in lower half of range: clamp down to "min"
9529 // - speeds in upper half of range: boost up to "max"
9530 if (m_resonance_avoidance && speed < m_config.max_resonance_avoidance_speed.value) {
9531 if (speed < m_config.min_resonance_avoidance_speed.value +
9532 ((m_config.max_resonance_avoidance_speed.value - m_config.min_resonance_avoidance_speed.value) / 2)) {
9533 speed = std::min(speed, m_config.min_resonance_avoidance_speed.value);
9534 } else {
9535 speed = m_config.max_resonance_avoidance_speed.value;
9536 }
9537 }
9538
9539 // reset flag for next segment
9540 m_resonance_avoidance = true;
9541 }
9542}
9543
9544 bool requires_segmented_extrusion = false;
9545 // A uniform fan state can be represented by the normal path-wide marker. Only a
9546 // transition within the path requires point-by-point emission.
9547 bool path_overhang_fan_active = false;
9548 std::vector<ProcessedPoint> new_points {};
9549
9550 const bool need_overhang_detection = NOZZLE_CONFIG(enable_overhang_speed) ||
9551 (FILAMENT_CONFIG(enable_overhang_bridge_fan) && m_enable_cooling_markers);
9552
9553 if (need_overhang_detection && !path_on_first_layer && !object_layer_over_raft() &&
9554 (is_bridge(path.role()) || is_perimeter(path.role()))) {
9555 bool is_external = is_external_perimeter(path.role());
9556 double ref_speed = is_external ? NOZZLE_CONFIG(outer_wall_speed) : NOZZLE_CONFIG(inner_wall_speed);
9557 if (ref_speed == 0)
9558 ref_speed = FILAMENT_CONFIG(filament_max_volumetric_speed) / _mm3_per_mm;
9559
9560 if (FILAMENT_CONFIG(filament_max_volumetric_speed) > 0) {
9561 ref_speed = std::min(ref_speed, FILAMENT_CONFIG(filament_max_volumetric_speed) / _mm3_per_mm);
9562 }
9563 if (sloped) {
9564 ref_speed = std::min(ref_speed, m_config.scarf_joint_speed.get_abs_value(ref_speed));
9565 }
9566
9567 ConfigOptionPercents overhang_overlap_levels({90, 75, 50, 25, 13, 0});
9568
9569 // ORCA: Lets the path be split where the overhang fan switches, not only where the speed changes.
9570 // Bridges and overhang perimeters are cooled regardless of overlap.
9571 float fan_overlap_threshold = -1.f;
9572 if (FILAMENT_CONFIG(enable_overhang_bridge_fan) && m_enable_cooling_markers && path.role() != erBridgeInfill &&
9573 path.role() != erOverhangPerimeter)
9574 fan_overlap_threshold = overhang_fan_overlap_threshold(FILAMENT_CONFIG(overhang_fan_threshold));
9575
9577 ConfigOptionFloatsOrPercents dynamic_overhang_speeds(
9578 {FloatOrPercent{100, true},
9579 (NOZZLE_CONFIG(overhang_1_4_speed).get_abs_value(ref_speed) < 0.5) ?
9580 FloatOrPercent{100, true} :
9581 FloatOrPercent{NOZZLE_CONFIG(overhang_1_4_speed).get_abs_value(ref_speed) * 100 / ref_speed, true},
9582 (NOZZLE_CONFIG(overhang_2_4_speed).get_abs_value(ref_speed) < 0.5) ?
9583 FloatOrPercent{100, true} :
9584 FloatOrPercent{NOZZLE_CONFIG(overhang_2_4_speed).get_abs_value(ref_speed) * 100 / ref_speed, true},
9585 (NOZZLE_CONFIG(overhang_3_4_speed).get_abs_value(ref_speed) < 0.5) ?
9586 FloatOrPercent{100, true} :
9587 FloatOrPercent{NOZZLE_CONFIG(overhang_3_4_speed).get_abs_value(ref_speed) * 100 / ref_speed, true},
9588 (NOZZLE_CONFIG(overhang_4_4_speed).get_abs_value(ref_speed) < 0.5) ?
9589 FloatOrPercent{100, true} :
9590 FloatOrPercent{NOZZLE_CONFIG(overhang_4_4_speed).get_abs_value(ref_speed) * 100 / ref_speed, true},
9591 (NOZZLE_CONFIG(overhang_4_4_speed).get_abs_value(ref_speed) < 0.5) ?
9592 FloatOrPercent{100, true} :
9593 FloatOrPercent{NOZZLE_CONFIG(overhang_4_4_speed).get_abs_value(ref_speed) * 100 / ref_speed, true}});
9594
9595 new_points = m_extrusion_quality_estimator.estimate_extrusion_quality(path, overhang_overlap_levels, dynamic_overhang_speeds,
9597 fan_overlap_threshold);
9598 }else{
9599 ConfigOptionFloatsOrPercents dynamic_overhang_speeds(
9600 {FloatOrPercent{100, true},
9601 (NOZZLE_CONFIG(overhang_1_4_speed).get_abs_value(ref_speed) < 0.5) ?
9602 FloatOrPercent{100, true} :
9603 FloatOrPercent{NOZZLE_CONFIG(overhang_1_4_speed).get_abs_value(ref_speed) * 100 / ref_speed, true},
9604 (NOZZLE_CONFIG(overhang_2_4_speed).get_abs_value(ref_speed) < 0.5) ?
9605 FloatOrPercent{100, true} :
9606 FloatOrPercent{NOZZLE_CONFIG(overhang_2_4_speed).get_abs_value(ref_speed) * 100 / ref_speed, true},
9607 (NOZZLE_CONFIG(overhang_3_4_speed).get_abs_value(ref_speed) < 0.5) ?
9608 FloatOrPercent{100, true} :
9609 FloatOrPercent{NOZZLE_CONFIG(overhang_3_4_speed).get_abs_value(ref_speed) * 100 / ref_speed, true},
9610 (NOZZLE_CONFIG(overhang_4_4_speed).get_abs_value(ref_speed) < 0.5) ?
9611 FloatOrPercent{100, true} :
9612 FloatOrPercent{NOZZLE_CONFIG(overhang_4_4_speed).get_abs_value(ref_speed) * 100 / ref_speed, true},
9613 FloatOrPercent{NOZZLE_CONFIG(bridge_speed) * 100 / ref_speed, true}});
9614
9615 new_points = m_extrusion_quality_estimator.estimate_extrusion_quality(path, overhang_overlap_levels, dynamic_overhang_speeds,
9617 fan_overlap_threshold);
9618 }
9620 for (ProcessedPoint &point : new_points)
9621 point.speed = speed;
9622 requires_segmented_extrusion = std::any_of(new_points.begin(), new_points.end(),
9623 [speed](const ProcessedPoint &point) { return fabs(double(point.speed) - speed) > 1; }); // Ignore small speed variations (under 1mm/sec)
9624 if (FILAMENT_CONFIG(enable_overhang_bridge_fan) && m_enable_cooling_markers && fan_overlap_threshold >= 0.f &&
9625 !new_points.empty()) {
9626 path_overhang_fan_active = new_points.front().overlap <= fan_overlap_threshold;
9627 // An overhang-fan state change needs separate extrusion moves so the cooling buffer can insert
9628 // the corresponding fan command between them.
9629 requires_segmented_extrusion = requires_segmented_extrusion ||
9630 std::adjacent_find(new_points.begin(), new_points.end(), [fan_overlap_threshold](const ProcessedPoint &previous,
9631 const ProcessedPoint &current) {
9632 return (previous.overlap <= fan_overlap_threshold) != (current.overlap <= fan_overlap_threshold);
9633 }) != new_points.end();
9634 }
9635 }
9636
9637 double F = speed * 60; // convert mm/sec to mm/min
9638
9639 // Orca: Dynamic PA
9640 // If adaptive PA is enabled, by default evaluate PA on all extrusion moves
9641 // The PA settings and the PA_CHANGE tags use the filament config index of the filament's extruder variant
9642 const unsigned int pa_config_index = (unsigned int) get_filament_config_index(m_writer.filament()->id());
9643 const bool adaptive_pa = m_config.adaptive_pressure_advance.get_at(pa_config_index) && m_config.enable_pressure_advance.get_at(pa_config_index);
9644 const bool adaptive_pa_overhangs = adaptive_pa && m_config.adaptive_pressure_advance_overhangs.get_at(pa_config_index);
9645 bool is_pa_calib = m_curr_print->calib_mode() == CalibMode::Calib_PA_Line ||
9646 m_curr_print->calib_mode() == CalibMode::Calib_PA_Pattern ||
9647 m_curr_print->calib_mode() == CalibMode::Calib_PA_Tower;
9648 bool evaluate_adaptive_pa = false;
9649 bool role_change = (m_last_extrusion_role != path.role());
9650 if (!is_pa_calib && adaptive_pa) {
9651 evaluate_adaptive_pa = true;
9652 // If we have already emmited a PA change because the m_multi_flow_segment_path_pa_set is set
9653 // skip re-issuing the PA change tag.
9654 if (m_multi_flow_segment_path_pa_set && evaluate_adaptive_pa)
9655 evaluate_adaptive_pa = false;
9656 // TODO: Explore forcing evaluation of PA if a role change is happening mid extrusion.
9657 // TODO: This would enable adapting PA for overhang perimeters as they are part of the current loop
9658 // TODO: The issue with simply enabling PA evaluation on a role change is that the speed change
9659 // TODO: is issued before the overhang perimeter role change is triggered
9660 // TODO: because for some reason (maybe path segmentation upstream?) there is a short path extruded
9661 // TODO: with the overhang speed and flow before the role change is flagged in the path.role() function.
9662 if(role_change)
9663 evaluate_adaptive_pa = true;
9664 }
9665 // Orca: End of dynamic PA trigger flag segment
9666
9667 //Orca: process custom gcode for extrusion role change
9668 if (path.role() != m_last_extrusion_role) {
9669 const auto current_filament_id = m_writer.filament()->id();
9670 const std::string& machine_role_change_gcode = m_config.change_extrusion_role_gcode.value;
9671 const std::string& filament_role_change_gcode = m_config.filament_change_extrusion_role_gcode.get_at(current_filament_id);
9672 const std::string& process_role_change_gcode = m_config.process_change_extrusion_role_gcode.value;
9673
9674 if (!machine_role_change_gcode.empty() || !filament_role_change_gcode.empty() || !process_role_change_gcode.empty()) {
9675 DynamicConfig config;
9676 config.set_key_value("extrusion_role", new ConfigOptionString(extrusion_role_to_string_for_parser(path.role())));
9677 config.set_key_value("last_extrusion_role", new ConfigOptionString(extrusion_role_to_string_for_parser(m_last_extrusion_role)));
9678 config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index + 1));
9679 config.set_key_value("layer_z", new ConfigOptionFloat(m_layer == nullptr ? m_last_height : m_layer->print_z));
9680
9681 const auto append_role_gcode = [this, current_filament_id, &config, &gcode](const std::string& key, const std::string& templ) {
9682 if (templ.empty())
9683 return;
9684 gcode += this->placeholder_parser_process(key, templ, current_filament_id, &config) + "\n";
9685 };
9686
9687 append_role_gcode("change_extrusion_role_gcode", machine_role_change_gcode);
9688 append_role_gcode("filament_change_extrusion_role_gcode", filament_role_change_gcode);
9689 append_role_gcode("process_change_extrusion_role_gcode", process_role_change_gcode);
9690 }
9691 }
9692
9693 // extrude arc or line
9695 if (path.role() != m_last_extrusion_role) {
9696 char buf[32];
9697 sprintf(buf, ";_EXTRUSION_ROLE:%d\n", int(path.role()));
9698 gcode += buf;
9699 }
9700 }
9701
9702 m_last_extrusion_role = path.role();
9703
9704 // adds processor tags and updates processor tracking data
9705 // PrusaMultiMaterial::Writer may generate GCodeProcessor::Height_Tag lines without updating m_last_height
9706 // so, if the last role was erWipeTower we force export of GCodeProcessor::Height_Tag lines
9707 bool last_was_wipe_tower = (m_last_processor_extrusion_role == erWipeTower);
9708 char buf[64];
9710
9711 if (path.role() != m_last_processor_extrusion_role) {
9712 m_last_processor_extrusion_role = path.role();
9714 gcode += buf;
9715 }
9716
9717 if (last_was_wipe_tower || m_last_width != path.width) {
9718 m_last_width = path.width;
9720 gcode += buf;
9721 }
9722
9723 if (last_was_wipe_tower || std::abs(m_last_height - effective_height) > EPSILON) {
9724 m_last_height = effective_height;
9726 gcode += buf;
9727 }
9728
9729 // Orca: Dynamic PA
9730 // Post processor flag generation code segment when option to emit only at role changes is enabled
9731 // Variables published to the post processor:
9732 // 1) Tag to trigger a PA evaluation (because a role change was identified and the user has requested dynamic PA adjustments)
9733 // 2) Current filament config index (to identify the PA model for the currently used filament and its extruder variant)
9734 // 3) mm3_per_mm value (to then multiply by the final model print speed after slowdown for cooling is applied)
9735 // 4) the current acceleration (to pass to the model for evaluation)
9736 // 5) whether this is an external perimeter (for future use)
9737 // 6) whether this segment is triggered because of a role change (to aid in calculation of average speed for the role)
9738 // This tag simplifies the creation of the gcode post processor while also keeping the feature decoupled from other tags.
9739 if (evaluate_adaptive_pa) {
9740 bool isOverhangPerimeter = (path.role() == erOverhangPerimeter);
9742 sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n",
9744 pa_config_index,
9746 acceleration_i,
9747 ((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)),
9748 role_change,
9749 isOverhangPerimeter);
9750 gcode += buf;
9751 } else if(_mm3_per_mm >0 ){ // Triggered when extruding a single segment path (like a line).
9752 // Check if mm3_mm value is greater than zero as the wipe before external perimeter
9753 // is a zero mm3_mm path to force de-retraction to happen and we dont want
9754 // to issue a zero flow PA change command for this
9755 sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n",
9757 pa_config_index,
9758 _mm3_per_mm,
9759 acceleration_i,
9760 ((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)),
9761 role_change,
9762 isOverhangPerimeter);
9763 gcode += buf;
9764 }
9765 }
9766
9767
9768 auto overhang_fan_threshold = FILAMENT_CONFIG(overhang_fan_threshold);
9769 auto enable_overhang_bridge_fan = FILAMENT_CONFIG(enable_overhang_bridge_fan);
9770
9771 if (enable_overhang_bridge_fan &&
9772 (path.role() == erBridgeInfill || path.role() == erOverhangPerimeter ||
9773 (overhang_fan_threshold == Overhang_threshold_none && is_external_perimeter(path.role()))))
9774 path_overhang_fan_active = true;
9775
9776 // { "0%", Overhang_threshold_none },
9777 // { "10%", Overhang_threshold_1_4 },
9778 // { "25%", Overhang_threshold_2_4 },
9779 // { "50%", Overhang_threshold_3_4 },
9780 // { "75%", Overhang_threshold_4_4 },
9781 // { "95%", Overhang_threshold_bridge }
9782 std::string comment;
9784 comment = ";_EXTRUDE_SET_SPEED";
9785 if (is_external_perimeter(path.role()))
9786 comment += ";_EXTERNAL_PERIMETER";
9787 }
9788
9789 // Change fan speed based on current extrusion role
9790 auto append_role_based_fan_marker = [this, &gcode](const ExtrusionRole role, const std::string_view& marker_prefix, const bool fan_on) {
9792
9793 if (fan_on) {
9794 // Orca: CoolingBuffer consumes role fan markers per layer, so continuing
9795 // role-based fan regions need a fresh START marker on each new layer.
9797 gcode += ";";
9798 gcode += marker_prefix;
9799 gcode += "_FAN_START\n";
9800 m_is_role_based_fan_on[role] = true;
9802 }
9803 } else {
9804 if (m_is_role_based_fan_on[role]) {
9805 gcode += ";";
9806 gcode += marker_prefix;
9807 gcode += "_FAN_END\n";
9808 m_is_role_based_fan_on[role] = false;
9810 }
9811 }
9812 };
9813 auto apply_role_based_fan_speed = [
9814 &path, &append_role_based_fan_marker,
9815 supp_interface_fan_speed = FILAMENT_CONFIG(support_material_interface_fan_speed),
9817 ] {
9818 append_role_based_fan_marker(erSupportMaterialInterface, "_SUPP_INTERFACE"sv,
9819 supp_interface_fan_speed >= 0 && path.role() == erSupportMaterialInterface);
9820 append_role_based_fan_marker(erIroning, "_IRONING"sv,
9821 ironing_fan_speed >= 0 && path.role() == erIroning);
9822 };
9823
9824 // Farthest-point timelapse inline hook. When the photo head reaches (within 0.5 mm of) the
9825 // farthest-from-camera point while extruding, take the snapshot in place (skip_pos_pick → M971 inline
9826 // photo, no travel). Early-returns unless the subsystem is enabled AND the farthest point is on the
9827 // photo head, so it is a no-op for every printer that leaves the toggle off.
9828 auto check_and_insert_timelapse = [this, &gcode](const Point &endpoint_scaled) {
9829 if (!m_farthest_point_timelapse.enabled || !m_farthest_point_timelapse.farthest_is_photo_head || m_farthest_point_timelapse.inserted_this_layer)
9830 return;
9831 // Inline M971 only when current extruder is the photo head; other nozzles may pass through nearby
9832 // coordinates but their physical position differs.
9833 if (!m_writer.filament() || get_extruder_id(m_writer.filament()->id()) != m_farthest_point_timelapse.most_used_extruder)
9834 return;
9835 // Compare in the global print frame to match farthest_gcode_pos, which is unscale(farthest_point)
9836 // without the per-extruder nozzle offset. Using point_to_gcode() here would subtract extruder_offset
9837 // on this side only, leaving a constant mismatch that could prevent the inline photo from ever
9838 // triggering on machines with a non-zero photo-head extruder_offset.
9839 Vec2d endpoint_mm = unscale(endpoint_scaled) + m_origin;
9840 if ((endpoint_mm - m_farthest_point_timelapse.farthest_gcode_pos).norm() >= 0.5)
9841 return;
9842 if (!m_print || !m_layer)
9843 return;
9844 std::string timelapse_gcode = generate_timelapse_gcode(*m_print, m_layer->print_z, m_farthest_point_timelapse.most_used_extruder,
9845 &m_farthest_point_timelapse.layer_object_label_ids, &m_printed_objects, true);
9846 if (!timelapse_gcode.empty()) {
9847 gcode += timelapse_gcode;
9848 m_farthest_point_timelapse.inserted_this_layer = true;
9849 }
9850 };
9851
9852 if (!requires_segmented_extrusion) {
9853 // F is mm per minute.
9854 if( (std::abs(writer().get_current_speed() - F) > EPSILON) || (std::abs(_mm3_per_mm - m_last_mm3_mm) > EPSILON) ){
9855 // ORCA: Adaptive PA code segment when adjusting PA within the same feature
9856 // There is a speed change coming out of an overhang region
9857 // or a flow change, so emit the flag to evaluate PA for the upcomming extrusion
9858 // Emit tag before new speed is set so the post processor reads the next speed immediately and uses it.
9859 // Dont emit tag if it has just already been emitted from a role change above
9860 if(_mm3_per_mm >0 && adaptive_pa_overhangs && !evaluate_adaptive_pa){
9861 if(writer().get_current_speed() > F){ // Ramping down speed - use overhang logic where the minimum speed is used between current and upcoming extrusion
9862 if(m_config.gcode_comments){
9863 sprintf(buf, "; Ramp down-non-variable\n");
9864 gcode += buf;
9865 }
9866 sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n",
9868 pa_config_index,
9869 _mm3_per_mm,
9870 acceleration_i,
9871 ((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)),
9872 1, // Force a dummy "role change" & "overhang perimeter" for the post processor, as, while technically it is not a role change,
9873 // the properties of the extrusion in the overhang are different so it behaves similarly to a role
9874 // change for the Adaptive PA post processor.
9875 1);
9876 }else{ // Ramping up speed - use baseline logic where max speed is used between current and upcoming extrusion
9877 if(m_config.gcode_comments){
9878 sprintf(buf, "; Ramp up-non-variable\n");
9879 gcode += buf;
9880 }
9881 sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n",
9883 pa_config_index,
9884 _mm3_per_mm,
9885 acceleration_i,
9886 ((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)),
9887 1, // Force a dummy "role change" & "overhang perimeter" for the post processor, as, while technically it is not a role change,
9888 // the properties of the extrusion in the overhang are different so it is technically similar to a role
9889 // change for the Adaptive PA post processor.
9890 0);
9891 }
9892 gcode += buf;
9893 m_last_mm3_mm = _mm3_per_mm;
9894 }
9895 // ORCA: End of adaptive PA code segment
9896 }
9897
9898 m_writer.set_speed(gcode, F, "", comment);
9899 {
9901 if (enable_overhang_bridge_fan) {
9902 append_role_based_fan_marker(erOverhangPerimeter, "_OVERHANG"sv, path_overhang_fan_active);
9903
9904 // ORCA: Add support for separate internal bridge fan speed control
9905 append_role_based_fan_marker(erInternalBridgeInfill, "_INTERNAL_BRIDGE"sv, path.role() == erInternalBridgeInfill);
9906 }
9907
9908 apply_role_based_fan_speed();
9909 }
9910 // BBS: use G1 if not enable arc fitting or has no arc fitting result or in spiral_mode mode or we are doing sloped extrusion
9911 // Attention: G2 and G3 is not supported in spiral_mode mode
9912 if (m_config.enable_arc_fitting.value == ArcFittingMode::Disabled || path.polyline.fitting_result.empty() || m_config.spiral_mode || sloped != nullptr || path.z_contoured || this->should_disable_arc_fitting()) {
9913 double path_length = 0.;
9914 double total_length = sloped == nullptr ? 0. : path.polyline.length() * SCALING_FACTOR;
9915 double saved_z = m_writer.get_position().z();
9916
9917 for (const Line3& line : path.polyline.lines()) {
9918 std::string flow_description;
9919 const double line_length = line.length() * SCALING_FACTOR;
9920 if (line_length < EPSILON)
9921 continue;
9922 path_length += line_length;
9923 auto dE = e_per_mm * line_length;
9924 if (_needSAFC(path)) {
9925 auto oldE = dE;
9926 dE = m_small_area_infill_flow_compensator->modify_flow(line_length, dE, path.role());
9927
9928 if (m_config.gcode_comments && oldE > 0 && oldE != dE) {
9929 flow_description = description + Slic3r::format(" | Old Flow Value: %0.5f Length: %0.5f",oldE, line_length);
9930 }
9931 }
9932 tag_belt_band((line.a.x() + line.b.x()) / 2, (line.a.y() + line.b.y()) / 2);
9933 if (path.z_contoured) {
9934 // ZAA: Z anti-aliased extrusion with variable Z per point
9935 Vec2d dest2d = this->point_to_gcode(line.b.to_point());
9936 coordf_t z_diff = unscale_(line.b.z());
9937
9938 double extrusion_ratio = 1;
9939 if (path.role() != erIroning) {
9940 extrusion_ratio = (path.height + z_diff) / path.height;
9941 }
9942
9943 double e = dE * extrusion_ratio;
9944
9945 double z = m_nominal_z + z_diff;
9946 if (z < 0.1) {
9947 throw RuntimeError("GCode: very low z");
9948 }
9949 m_writer.extrude_to_xyz(gcode, Vec3d(dest2d.x(), dest2d.y(), z), e,
9950 flow_description.empty() ? description : flow_description);
9951
9952 } else if (sloped == nullptr) {
9953 // Normal extrusion
9954 m_writer.extrude_to_xy(gcode,
9955 this->point_to_gcode(line.b.to_point()),
9956 dE,
9957 flow_description.empty() ? description : flow_description, path.is_force_no_extrusion());
9958 } else {
9959 // Sloped extrusion
9960 const auto [z_ratio, e_ratio] = sloped->interpolate(path_length / total_length);
9961 Vec2d dest2d = this->point_to_gcode(line.b.to_point());
9962 Vec3d dest3d(dest2d(0), dest2d(1), get_sloped_z(z_ratio));
9963 m_writer.extrude_to_xyz(gcode,
9964 dest3d,
9965 dE * e_ratio,
9966 flow_description.empty() ? description : flow_description, path.is_force_no_extrusion());
9967 }
9968 check_and_insert_timelapse(line.b.to_point()); // Inline farthest-point snapshot
9969 }
9970 } else {
9971 // BBS: start to generate gcode from arc fitting data which includes line and arc
9972 const std::vector<PathFittingData>& fitting_result = path.polyline.fitting_result;
9973 for (size_t fitting_index = 0; fitting_index < fitting_result.size(); fitting_index++) {
9974 std::string flow_description;
9975 switch (fitting_result[fitting_index].path_type) {
9977 size_t start_index = fitting_result[fitting_index].start_point_index;
9978 size_t end_index = fitting_result[fitting_index].end_point_index;
9979 for (size_t point_index = start_index + 1; point_index < end_index + 1; point_index++) {
9980 flow_description.clear();
9981 const Line line = Line(path.polyline.points[point_index - 1].to_point(), path.polyline.points[point_index].to_point());
9982 const double line_length = line.length() * SCALING_FACTOR;
9983 if (line_length < EPSILON)
9984 continue;
9985 auto dE = e_per_mm * line_length;
9986 if (_needSAFC(path)) {
9987 auto oldE = dE;
9988 dE = m_small_area_infill_flow_compensator->modify_flow(line_length, dE, path.role());
9989
9990 if (m_config.gcode_comments && oldE > 0 && oldE != dE) {
9991 flow_description = description + Slic3r::format(" | Old Flow Value: %0.5f Length: %0.5f",oldE, line_length);
9992 }
9993 }
9994 m_writer.extrude_to_xy(gcode,
9995 this->point_to_gcode(line.b),
9996 dE,
9997 flow_description.empty() ? description : flow_description, path.is_force_no_extrusion());
9998 check_and_insert_timelapse(line.b); // Inline farthest-point snapshot
9999 }
10000 break;
10001 }
10004 const ArcSegment& arc = fitting_result[fitting_index].arc_data;
10005 const double arc_length = fitting_result[fitting_index].arc_data.length * SCALING_FACTOR;
10006 if (arc_length < EPSILON)
10007 continue;
10008 const Vec2d center_offset = this->point_to_gcode(arc.center) - this->point_to_gcode(arc.start_point);
10009 auto dE = e_per_mm * arc_length;
10010 if (_needSAFC(path)) {
10011 auto oldE = dE;
10012 dE = m_small_area_infill_flow_compensator->modify_flow(arc_length, dE, path.role());
10013
10014 if (m_config.gcode_comments && oldE > 0 && oldE != dE) {
10015 flow_description = description + Slic3r::format(" | Old Flow Value: %0.5f Length: %0.5f",oldE, arc_length);
10016 }
10017 }
10018 m_writer.extrude_arc_to_xy(gcode,
10019 this->point_to_gcode(arc.end_point),
10020 center_offset,
10021 dE,
10022 arc.direction == ArcDirection::Arc_Dir_CCW,
10023 flow_description.empty() ? description : flow_description, path.is_force_no_extrusion());
10024 check_and_insert_timelapse(arc.end_point); // Inline farthest-point snapshot
10025 break;
10026 }
10027 default:
10028 // BBS: should never happen that a empty path_type has been stored
10029 assert(0);
10030 break;
10031 }
10032 }
10033 }
10034 }
10035 } else {
10036 double last_set_speed = new_points[0].speed * 60.0;
10037
10038 double total_length = 0;
10039 if (sloped != nullptr) {
10040 // Calculate total extrusion length
10041 Points3 p;
10042 p.reserve(new_points.size());
10043 std::transform(new_points.begin(), new_points.end(), std::back_inserter(p), [](const ProcessedPoint& pp) { return pp.p; });
10044 Polyline3 l(p);
10045 total_length = l.length() * SCALING_FACTOR;
10046 }
10047 m_writer.set_speed(gcode, last_set_speed, "", comment);
10048 Vec3d prev = this->point_to_gcode_quantized(new_points[0].p);
10049 bool pre_fan_enabled = false;
10050 bool cur_fan_enabled = false;
10051 if( m_enable_cooling_markers && enable_overhang_bridge_fan)
10052 pre_fan_enabled = overhang_fan_active(new_points[0].overlap, overhang_fan_threshold, path.role());
10053
10054 if(path.role() == erInternalBridgeInfill) // ORCA: Add support for separate internal bridge fan speed control
10055 pre_fan_enabled = true;
10056
10057 double path_length = 0.;
10058 for (size_t i = 1; i < new_points.size(); i++) {
10059 std::string flow_description;
10060 const ProcessedPoint &processed_point = new_points[i];
10061 const ProcessedPoint &pre_processed_point = new_points[i-1];
10062 Vec3d p = this->point_to_gcode_quantized(processed_point.p);
10063 tag_belt_band((pre_processed_point.p.x() + processed_point.p.x()) / 2, (pre_processed_point.p.y() + processed_point.p.y()) / 2);
10065 if (enable_overhang_bridge_fan) {
10066 cur_fan_enabled = overhang_fan_active(processed_point.overlap, overhang_fan_threshold, path.role());
10067 append_role_based_fan_marker(erOverhangPerimeter, "_OVERHANG"sv, pre_fan_enabled && cur_fan_enabled);
10068 pre_fan_enabled = cur_fan_enabled;
10069
10070 // ORCA: Add support for separate internal bridge fan speed control
10071 append_role_based_fan_marker(erInternalBridgeInfill, "_INTERNAL_BRIDGE"sv, path.role() == erInternalBridgeInfill);
10072 }
10073
10074 apply_role_based_fan_speed();
10075 }
10076
10077 const double line_length = (p - prev).norm();
10078 if(line_length < EPSILON)
10079 continue;
10080 path_length += line_length;
10081 double new_speed = pre_processed_point.speed * 60.0;
10082
10083 if ((std::abs(last_set_speed - new_speed) > EPSILON) || (std::abs(_mm3_per_mm - m_last_mm3_mm) > EPSILON)) {
10084 // ORCA: Adaptive PA code segment when adjusting PA within the same feature
10085 // There is a speed change or flow change so emit the flag to evaluate PA for the upcomming extrusion
10086 // Emit tag before new speed is set so the post processor reads the next speed immediately and uses it.
10087 if(_mm3_per_mm >0 && adaptive_pa_overhangs){
10088 if(last_set_speed > new_speed){ // Ramping down speed - use overhang logic where the minimum speed is used between current and upcoming extrusion
10089 if(m_config.gcode_comments) {
10090 sprintf(buf, "; Ramp up-variable\n");
10091 gcode += buf;
10092 }
10093 sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n",
10095 pa_config_index,
10096 _mm3_per_mm,
10097 acceleration_i,
10098 ((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)),
10099 1, // Force a dummy "role change" & "overhang perimeter" for the post processor, as, while technically it is not a role change,
10100 // the properties of the extrusion in the overhang are different so it is technically similar to a role
10101 // change for the Adaptive PA post processor.
10102 1);
10103 }else{ // Ramping up speed - use baseline logic where max speed is used between current and upcoming extrusion
10104 if(m_config.gcode_comments) {
10105 sprintf(buf, "; Ramp down-variable\n");
10106 gcode += buf;
10107 }
10108 sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n",
10110 pa_config_index,
10111 _mm3_per_mm,
10112 acceleration_i,
10113 ((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)),
10114 1, // Force a dummy "role change" & "overhang perimeter" for the post processor, as, while technically it is not a role change,
10115 // the properties of the extrusion in the overhang are different so it is technically similar to a role
10116 // change for the Adaptive PA post processor.
10117 0);
10118 }
10119 gcode += buf;
10120 m_last_mm3_mm = _mm3_per_mm;
10121 }
10122 }// ORCA: End of adaptive PA code segment
10123
10124 // Ignore small speed variations - emit speed change if the delta between current and new is greater than 60mm/min / 1mm/sec
10125 // Reset speed to F if delta to F is less than 1mm/sec
10126 if ((std::abs(last_set_speed - new_speed) > 60)) {
10127 m_writer.set_speed(gcode, new_speed, "", comment);
10128 last_set_speed = new_speed;
10129 } else if ((std::abs(F - new_speed) <= 60)) {
10130 m_writer.set_speed(gcode, F, "", comment);
10131 last_set_speed = F;
10132 }
10133 auto dE = e_per_mm * line_length;
10134 if (_needSAFC(path)) {
10135 auto oldE = dE;
10136 dE = m_small_area_infill_flow_compensator->modify_flow(line_length, dE, path.role());
10137
10138 if (m_config.gcode_comments && oldE > 0 && oldE != dE) {
10139 flow_description = description + Slic3r::format(" | Old Flow Value: %0.5f Length: %0.5f",oldE, line_length);
10140 }
10141 }
10142 if (path.z_contoured) {
10143 Vec2d dest2d = p.head<2>();
10144 coordf_t z_diff = unscale_(processed_point.p.z());
10145
10146 double extrusion_ratio = 1;
10147 if (path.role() != erIroning) {
10148 extrusion_ratio = (path.height + z_diff) / path.height;
10149 }
10150
10151 double e = dE * extrusion_ratio;
10152
10153 double z = m_nominal_z + z_diff;
10154 if (z < 0.1) {
10155 throw RuntimeError("GCode: very low z");
10156 }
10157 m_writer.extrude_to_xyz(gcode, Vec3d(dest2d.x(), dest2d.y(), z), e,
10158 flow_description.empty() ? description : flow_description);
10159 } else if (sloped == nullptr) {
10160 // Normal extrusion
10161 m_writer.extrude_to_xy(gcode, p.head<2>(), dE, flow_description.empty() ? description : flow_description);
10162 } else {
10163 // Sloped extrusion
10164 const auto [z_ratio, e_ratio] = sloped->interpolate(path_length / total_length);
10165 Vec3d dest3d(p(0), p(1), get_sloped_z(z_ratio));
10166 m_writer.extrude_to_xyz(gcode, dest3d, dE * e_ratio, flow_description.empty() ? description : flow_description);
10167 }
10168
10169 // Inline farthest-point snapshot on the segmented emission path. Inert unless the
10170 // farthest-point subsystem is on, matching the other paths.
10171 check_and_insert_timelapse(processed_point.p.to_point());
10172
10173 prev = p;
10174
10175 }
10176 }
10178 gcode += ";_EXTRUDE_END\n";
10179 }
10180
10181 if (path.role() != ExtrusionRole::erGapFill) {
10183 }
10184
10185 this->set_last_pos(path.last_point());
10186 return gcode;
10187}
#define FILAMENT_CONFIG(OPT)
Definition GCode.cpp:2156
static double quantize_xyz(double v, bool high_precision)
Definition GCodeWriter.hpp:342
@ PA_Change
Definition GCodeProcessor.hpp:621
@ Width
Definition GCodeProcessor.hpp:608
@ Height
Definition GCodeProcessor.hpp:607
std::string travel_to_z(double z, const std::string &comment=std::string(), bool force=false)
Definition GCodeWriter.cpp:1156
Point3 m_last_pos
Definition GCode.hpp:865
bool object_layer_over_raft() const
Definition GCode.hpp:1029
double m_sub_layer_height
Definition GCode.hpp:936
std::string generate_timelapse_gcode(const Print &print, coordf_t print_z, int most_used_extruder, const std::set< size_t > *layer_object_label_ids, const std::vector< const PrintObject * > *printed_objects, bool skip_pos_pick=false)
Definition GCode.cpp:6466
void set_last_pos(const Point &pos)
Definition GCode.hpp:544
bool _needSAFC(const ExtrusionPath &path)
Definition GCode.cpp:9120
double m_sub_layer_flow_ratio
Definition GCode.hpp:935
bool m_resonance_avoidance
Definition GCode.hpp:811
int effective_layer_index_for_point(const Vec3d &point_slicing_mm) const
Definition GCode.hpp:996
double calc_max_volumetric_speed(const double layer_height, const double line_width, const std::string co_str)
Definition GCode.cpp:9139
size_t get_nozzle_config_index(int filament_id) const
Definition GCode.cpp:5055
coordf_t m_nominal_z
Definition GCode.hpp:931
FarthestPointTimelapseContext m_farthest_point_timelapse
Definition GCode.hpp:807
ExtrusionRole m_last_extrusion_role
Definition GCode.hpp:843
const Layer * m_layer
Definition GCode.hpp:838
std::optional< coordf_t > m_belt_brim_z
Definition GCode.hpp:950
ExtrusionRole m_last_processor_extrusion_role
Definition GCode.hpp:830
ExtrusionQualityEstimator m_extrusion_quality_estimator
Definition GCode.hpp:732
std::string unretract(float extra_retract=0.f)
Definition GCode.hpp:311
std::string extrusion_role_to_string_for_parser(const ExtrusionRole &)
Definition GCode.cpp:10190
ExtrusionRole m_last_notgapfill_extrusion_role
Definition GCode.hpp:845
Print * m_curr_print
Definition GCode.hpp:929
bool m_multi_flow_segment_path_pa_set
Definition GCode.hpp:589
double m_multi_flow_segment_path_average_mm3_per_mm
Definition GCode.hpp:588
bool m_need_change_layer_lift_z
Definition GCode.hpp:937
Vec2d point_to_gcode_quantized(const Point &point) const
Definition GCode.cpp:11503
double m_last_mm3_mm
Definition GCode.hpp:592
Vec2d point_to_gcode(const Point &point) const
Definition GCode.cpp:11479
double SCALING_FACTOR
Definition libslic3r.cpp:3
#define unscale_(val)
Definition libslic3r.h:107
void arc(std::vector< std::pair< Vec2d, Vec2d > > &out, double cx, double cy, double rx, double ry, double a0, double a1, int n=14)
Definition DesignSketchTool.cpp:7759
@ RuntimeError
Definition PluginStatus.hpp:14
coord_t height(const BoundingBox &box)
Definition Arrange.cpp:1185
top_solid_infill_flow_ratio((ConfigOptionFloat, bottom_solid_infill_flow_ratio))((ConfigOptionFloatOrPercent
bool is_top_surface(ExtrusionRole role)
Definition ExtrusionEntity.hpp:90
overhang_4_4_speed((ConfigOptionBool, only_one_wall_top))((ConfigOptionFloatOrPercent
enable_overhang_speed((ConfigOptionFloatsOrPercentsNullable, overhang_1_4_speed))((ConfigOptionFloatsOrPercentsNullable
zaa_minimize_perimeter_height filament_flow_ratio((ConfigOptionBools, enable_pressure_advance))((ConfigOptionFloats
static bool overhang_fan_active(float overlap, int overhang_fan_threshold, ExtrusionRole role)
Definition GCode.cpp:9175
zaa_minimize_perimeter_height ironing_fan_speed((ConfigOptionStrings, filament_notes))((ConfigOptionString
bool is_infill(ExtrusionRole role)
Definition ExtrusionEntity.hpp:79
@ Disabled
Definition PrintConfig.hpp:239
static float overhang_fan_overlap_threshold(int overhang_fan_threshold)
Definition GCode.cpp:9163
bool is_internal_perimeter(ExtrusionRole role)
Definition ExtrusionEntity.hpp:69
overhang_2_4_speed((ConfigOptionFloatsOrPercentsNullable, overhang_3_4_speed))((ConfigOptionFloatsOrPercentsNullable
internal_solid_infill_speed((ConfigOptionBool, detect_thin_wall))((ConfigOptionFloatOrPercent
@ Overhang_threshold_none
Definition PrintConfig.hpp:498
zaa_minimize_perimeter_height support_material_interface_fan_speed((ConfigOptionInts, internal_bridge_fan_speed))((ConfigOptionInts
ColorRGB lerp(const ColorRGB &a, const ColorRGB &b, float t)
Definition Color.cpp:244
bool is_bridge(ExtrusionRole role)
Definition ExtrusionEntity.hpp:105
sparse_infill_speed((ConfigOptionPercent, skeleton_infill_density))((ConfigOptionPercent
bool is_external_perimeter(ExtrusionRole role)
Definition ExtrusionEntity.hpp:74
ExtrusionRole
Definition ExtrusionEntity.hpp:26
@ erBottomSurface
Definition ExtrusionEntity.hpp:34
@ erExternalPerimeter
Definition ExtrusionEntity.hpp:29
@ erGapFill
Definition ExtrusionEntity.hpp:38
@ erSupportMaterialInterface
Definition ExtrusionEntity.hpp:42
@ erSupportMaterial
Definition ExtrusionEntity.hpp:41
@ erWipeTower
Definition ExtrusionEntity.hpp:44
@ erBrim
Definition ExtrusionEntity.hpp:40
@ erSolidInfill
Definition ExtrusionEntity.hpp:32
@ erBridgeInfill
Definition ExtrusionEntity.hpp:36
@ erInternalBridgeInfill
Definition ExtrusionEntity.hpp:37
@ erTopSolidInfill
Definition ExtrusionEntity.hpp:33
@ erSkirt
Definition ExtrusionEntity.hpp:39
@ erSupportTransition
Definition ExtrusionEntity.hpp:43
@ erOverhangPerimeter
Definition ExtrusionEntity.hpp:30
@ erIroning
Definition ExtrusionEntity.hpp:35
@ erPerimeter
Definition ExtrusionEntity.hpp:28
@ erInternalInfill
Definition ExtrusionEntity.hpp:31
bool is_perimeter(ExtrusionRole role)
Definition ExtrusionEntity.hpp:62
double total_length(const Polygons &polylines)
Definition Polygon.hpp:127
bool is_decimal_separator_point()
Definition LocalesUtils.cpp:78
slowdown_for_curled_perimeters((ConfigOptionBool, hole_to_polyhole))((ConfigOptionFloatOrPercent
ConfigOptionPercentsTempl< false > ConfigOptionPercents
Definition Config.hpp:1334
outer_wall_speed((ConfigOptionFloat, infill_direction))((ConfigOptionFloat
zaa_minimize_perimeter_height filament_adaptive_volumetric_speed((ConfigOptionStrings, volumetric_speed_coefficients))((ConfigOptionInts
@ Arc_move_cw
Definition ArcFitter.hpp:16
@ Arc_move_ccw
Definition ArcFitter.hpp:17
@ Linear_move
Definition ArcFitter.hpp:15
ConfigOptionFloatsOrPercentsTempl< false > ConfigOptionFloatsOrPercents
Definition Config.hpp:1531
std::vector< Point3, PointsAllocator< Point3 > > Points3
Definition Point.hpp:76
@ F
Definition libslic3r.h:145
zaa_minimize_perimeter_height initial_layer_infill_speed((ConfigOptionInts, nozzle_temperature_initial_layer))((ConfigOptionInts
@ Arc_Dir_CCW
Definition Circle.hpp:63
inner_wall_speed((ConfigOptionInt, wall_loops))((ConfigOptionBool

References _needSAFC(), Slic3r::Arc_Dir_CCW, Slic3r::Arc_move_ccw, Slic3r::Arc_move_cw, Slic3r::Line::b, calc_max_volumetric_speed(), Slic3r::Calib_PA_Line, Slic3r::Calib_PA_Pattern, Slic3r::Calib_PA_Tower, Slic3r::Circle::center, config(), Slic3r::ConfigOptionFloat, Slic3r::ConfigOptionString, Slic3r::Disabled, effective_layer_index_for_point(), Slic3r::enable_overhang_speed(), Slic3r::ArcSegment::end_point, EPSILON, Slic3r::erBottomSurface, Slic3r::erBridgeInfill, Slic3r::erBrim, Slic3r::erExternalPerimeter, Slic3r::erGapFill, Slic3r::erInternalBridgeInfill, Slic3r::erInternalInfill, Slic3r::erIroning, Slic3r::erOverhangPerimeter, Slic3r::erPerimeter, Slic3r::erSkirt, Slic3r::erSolidInfill, Slic3r::erSupportMaterial, Slic3r::erSupportMaterialInterface, Slic3r::erSupportTransition, Slic3r::erTopSolidInfill, Slic3r::erWipeTower, extrusion_role_to_string_for_parser(), Slic3r::F, Slic3r::filament_adaptive_volumetric_speed(), FILAMENT_CONFIG, Slic3r::filament_flow_ratio(), Slic3r::ExtrusionPath::first_point(), Slic3r::Polyline3::fitting_result, Slic3r::format(), Slic3r::gcfKlipper, generate_timelapse_gcode(), get_extruder_id(), get_filament_config_index(), get_nozzle_config_index(), Slic3r::GCodeProcessor::Height, Slic3r::ExtrusionPath::height, Slic3r::initial_layer_infill_speed(), Slic3r::inner_wall_speed(), Slic3r::internal_solid_infill_speed(), Slic3r::ExtrusionPathSloped::interpolate(), Slic3r::ironing_fan_speed(), Slic3r::is_bridge(), Slic3r::is_decimal_separator_point(), Slic3r::is_external_perimeter(), Slic3r::ExtrusionPathSloped::is_flat(), Slic3r::ExtrusionPath::is_force_no_extrusion(), Slic3r::is_infill(), Slic3r::is_internal_perimeter(), Slic3r::is_perimeter(), Slic3r::is_top_surface(), Slic3r::ExtrusionPath::last_point(), Slic3r::Line::length(), Slic3r::MultiPoint3::length(), Slic3r::lerp(), Slic3r::Linear_move, Slic3r::Polyline3::lines(), m_belt_brim_z, m_belt_in_band, m_calib_config, m_config, m_curr_print, m_enable_cooling_markers, m_enable_extrusion_role_markers, m_extrusion_quality_estimator, m_farthest_point_timelapse, m_is_role_based_fan_on, m_last_extrusion_role, m_last_height, m_last_mm3_mm, m_last_notgapfill_extrusion_role, m_last_pos, m_last_pos_defined, m_last_processor_extrusion_role, m_last_width, m_layer, m_layer_index, m_multi_flow_segment_path_average_mm3_per_mm, m_multi_flow_segment_path_pa_set, m_need_change_layer_lift_z, m_nominal_z, m_origin, m_print, m_printed_objects, m_resonance_avoidance, m_role_based_fan_marker_layer, m_small_area_infill_flow_compensator, m_sub_layer_flow_ratio, m_sub_layer_height, m_writer, Slic3r::ExtrusionPath::mm3_per_mm, NOZZLE_CONFIG, object_layer_over_raft(), on_first_layer(), Slic3r::outer_wall_speed(), Slic3r::overhang_2_4_speed(), Slic3r::overhang_4_4_speed(), Slic3r::overhang_fan_active(), Slic3r::overhang_fan_overlap_threshold(), Slic3r::Overhang_threshold_none, Slic3r::ProcessedPoint::overlap, Slic3r::ProcessedPoint::p, Slic3r::GCodeProcessor::PA_Change, placeholder_parser_process(), point_to_gcode(), point_to_gcode_quantized(), Slic3r::MultiPoint3::points, Slic3r::ExtrusionPath::polyline, Slic3r::GCodeFormatter::quantize_xyz(), Slic3r::GCodeProcessor::reserved_tag(), Slic3r::GCodeProcessor::Role, Slic3r::ExtrusionPath::role(), Slic3r::ExtrusionEntity::role_to_string(), SCALING_FACTOR, set_last_pos(), Slic3r::ExtrusionPathSloped::slope_begin, Slic3r::slowdown_for_curled_perimeters(), Slic3r::sparse_infill_speed(), Slic3r::ProcessedPoint::speed, Slic3r::ArcSegment::start_point, Slic3r::support_material_interface_fan_speed(), Slic3r::Point3::to_point(), Slic3r::top_solid_infill_flow_ratio(), Slic3r::total_length(), travel_to(), Slic3r::GCodeWriter::travel_to_z(), unretract(), Slic3r::unscale(), unscale_, Slic3r::GCodeProcessor::Width, Slic3r::ExtrusionPath::width, writer(), Slic3r::ExtrusionPath::z_contoured, and Slic3r::ExtrusionPathSloped::Slope::z_ratio.

Referenced by extrude_loop(), extrude_multi_path(), and extrude_path().

Here is the caller graph for this function:

◆ _needSAFC()

bool Slic3r::GCode::_needSAFC ( const ExtrusionPath & path)
protectedinherited
9121{
9122 if (!m_small_area_infill_flow_compensator || !m_config.small_area_infill_flow_compensation.value)
9123 return false;
9124
9125 static const InfillPattern supported_patterns[] = {
9130 };
9131
9132 return std::any_of(std::begin(supported_patterns), std::end(supported_patterns), [&](const InfillPattern pattern) {
9133 return (this->on_first_layer() && this->config().bottom_surface_pattern == pattern) ||
9134 (path.role() == erSolidInfill && this->config().internal_solid_infill_pattern == pattern) ||
9135 (path.role() == erTopSolidInfill && this->config().top_surface_pattern == pattern);
9136 });
9137}
InfillPattern
Definition PrintConfig.hpp:126
@ ipAlignedRectilinear
Definition PrintConfig.hpp:128
@ ipMonotonic
Definition PrintConfig.hpp:127
@ ipMonotonicLine
Definition PrintConfig.hpp:127
@ ipRectilinear
Definition PrintConfig.hpp:128
top_surface_pattern((ConfigOptionEnum< InfillPattern >, bottom_surface_pattern))((ConfigOptionEnum< SurfaceFillOrder >
internal_solid_infill_pattern((ConfigOptionFloatOrPercent, outer_wall_line_width))((ConfigOptionFloatsNullable

References config(), Slic3r::erSolidInfill, Slic3r::erTopSolidInfill, Slic3r::internal_solid_infill_pattern(), Slic3r::ipAlignedRectilinear, Slic3r::ipMonotonic, Slic3r::ipMonotonicLine, Slic3r::ipRectilinear, m_config, m_small_area_infill_flow_compensator, on_first_layer(), Slic3r::ExtrusionPath::role(), and Slic3r::top_surface_pattern().

Referenced by _extrude().

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

◆ _print_first_layer_bed_temperature()

void Slic3r::GCode::_print_first_layer_bed_temperature ( GCodeOutputStream & file,
Print & print,
const std::string & gcode,
unsigned int first_printing_extruder_id,
bool wait )
protectedinherited
5521{
5522 // Initial bed temperature based on the first extruder.
5523 // BBS
5524 std::vector<int> temps_per_bed;
5525 int bed_temp = 0;
5526 if (m_config.bed_temperature_formula.value == BedTempFormula::btfHighestTemp) {
5527 bed_temp = get_highest_bed_temperature(true, print);
5528 }
5529 else {
5530 bed_temp = get_bed_temperature(first_printing_extruder_id, true, print.config().curr_bed_type);
5531 }
5532 // Is the bed temperature set by the provided custom G-code?
5533 int temp_by_gcode = -1;
5534 bool temp_set_by_gcode = custom_gcode_sets_temperature(gcode, 140, 190, false, temp_by_gcode);
5535 // BBS
5536#if 0
5537 if (temp_set_by_gcode && temp_by_gcode >= 0 && temp_by_gcode < 1000)
5538 temp = temp_by_gcode;
5539#endif
5540
5541 // Always call m_writer.set_bed_temperature() so it will set the internal "current" state of the bed temp as if
5542 // the custom start G-code emited these.
5543 std::string set_temp_gcode = m_writer.set_bed_temperature(bed_temp, wait);
5544 if (! temp_set_by_gcode)
5545 file.write(set_temp_gcode);
5546}

References Slic3r::btfHighestTemp, Slic3r::Print::config(), Slic3r::custom_gcode_sets_temperature(), get_bed_temperature(), get_highest_bed_temperature(), m_config, m_writer, Print, and Slic3r::GCode::GCodeOutputStream::write().

Referenced by _do_export().

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

◆ _print_first_layer_extruder_temperatures()

void Slic3r::GCode::_print_first_layer_extruder_temperatures ( GCodeOutputStream & file,
Print & print,
const std::string & gcode,
unsigned int first_printing_extruder_id,
bool wait )
protectedinherited
5554{
5555 // Is the bed temperature set by the provided custom G-code?
5556 int temp_by_gcode = -1;
5557 bool include_g10 = print.config().gcode_flavor == gcfRepRapFirmware;
5558 if (custom_gcode_sets_temperature(gcode, 104, 109, include_g10, temp_by_gcode)) {
5559 // Set the extruder temperature at m_writer, but throw away the generated G-code as it will be written with the custom G-code.
5560 int temp = print.config().nozzle_temperature_initial_layer.get_at(get_filament_config_index((int)first_printing_extruder_id));
5561 if (temp_by_gcode >= 0 && temp_by_gcode < 1000)
5562 temp = temp_by_gcode;
5563 m_writer.set_temperature(temp, wait, first_printing_extruder_id);
5564 } else {
5565 // Custom G-code does not set the extruder temperature. Do it now.
5566 if (print.config().single_extruder_multi_material.value) {
5567 // Set temperature of the first printing extruder only.
5568 int temp = print.config().nozzle_temperature_initial_layer.get_at(get_filament_config_index((int)first_printing_extruder_id));
5569 if (temp > 0)
5570 file.write(m_writer.set_temperature(temp, wait, first_printing_extruder_id));
5571 } else {
5572 // Set temperatures of all the printing extruders.
5573 for (unsigned int tool_id : print.extruders()) {
5574 int temp = print.config().nozzle_temperature_initial_layer.get_at(get_filament_config_index((int)tool_id));
5575 if (m_ooze_prevention.enable && tool_id != first_printing_extruder_id) {
5576 if (print.config().idle_temperature.get_at(tool_id) == 0)
5577 temp += print.config().standby_temperature_delta.value;
5578 else
5579 temp = print.config().idle_temperature.get_at(tool_id);
5580 }
5581 if (temp > 0)
5582 file.write(m_writer.set_temperature(temp, wait, tool_id));
5583 }
5584 }
5585 }
5586}
@ gcfRepRapFirmware
Definition PrintConfig.hpp:54

References Slic3r::Print::config(), Slic3r::custom_gcode_sets_temperature(), Slic3r::Print::extruders(), Slic3r::gcfRepRapFirmware, get_filament_config_index(), m_ooze_prevention, m_writer, Print, and Slic3r::GCode::GCodeOutputStream::write().

Referenced by _do_export().

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

◆ append_full_config()

void Slic3r::GCode::append_full_config ( const Print & print,
std::string & str )
staticinherited
8242{
8243 DynamicPrintConfig cfg = print.full_print_config();
8244 { // correct the flush_volumes_matrix with flush_multiplier values
8245 // Fast purge mode uses flush_multiplier_fast; Default is inert.
8246 std::vector<double> temp_cfg_flush_multiplier = (print.config().prime_volume_mode == PrimeVolumeMode::pvmFast)
8247 ? cfg.option<ConfigOptionFloats>("flush_multiplier_fast")->values
8248 : cfg.option<ConfigOptionFloats>("flush_multiplier")->values;
8249 std::vector<double> temp_flush_volumes_matrix = cfg.option<ConfigOptionFloats>("flush_volumes_matrix")->values;
8250 auto temp_filament_color = cfg.option<ConfigOptionStrings>("filament_colour")->values;
8251 size_t heads_count_tmp = temp_cfg_flush_multiplier.size(),
8252 matrix_value_count = temp_flush_volumes_matrix.size() / temp_cfg_flush_multiplier.size(),
8253 filament_count_tmp = temp_filament_color.size();
8254 if (filament_count_tmp * filament_count_tmp * heads_count_tmp == temp_flush_volumes_matrix.size()) {
8255 for (size_t idx = 0; idx < heads_count_tmp; ++idx) {
8256 double temp_cfg_flush_multiplier_idx = temp_cfg_flush_multiplier[idx];
8257 size_t temp_begin_t = idx * matrix_value_count, temp_end_t = (idx + 1) * matrix_value_count;
8258 std::transform(temp_flush_volumes_matrix.begin() + temp_begin_t, temp_flush_volumes_matrix.begin() + temp_end_t,
8259 temp_flush_volumes_matrix.begin() + temp_begin_t,
8260 [temp_cfg_flush_multiplier_idx](double inputx) { return std::round(inputx * temp_cfg_flush_multiplier_idx); });
8261 }
8262 cfg.option<ConfigOptionFloats>("flush_volumes_matrix")->values = temp_flush_volumes_matrix;
8263 } else if (filament_count_tmp == 1) {
8264 } // Not applicable to flush matrix situations
8265 else { // flush_volumes_matrix value count error?
8266 throw Slic3r::SlicingError(_(L("Flush volumes matrix do not match to the correct size!")));
8267 }
8268 }
8269 // filament_map_2 (the per-filament (extruder x volume-type) slot map) is computed during apply /
8270 // write-back and lives in the applied print config only; the pre-expansion snapshot this dump is
8271 // built from still carries the registered default. Copy the real values so the header line is
8272 // diagnostic (nothing parses it back).
8273 cfg.option<ConfigOptionInts>("filament_map_2", true)->values = print.config().filament_map_2.values;
8274 // Sorted list of config keys, which shall not be stored into the G-code. Initializer list.
8275 static const std::set<std::string_view> banned_keys( {
8276 // filament_extruder_compatibility is a device-side (blacklist) compatibility hint read from the
8277 // loaded presets, never consumed by slicing or firmware. Keep it out of the G-code config block so
8278 // the config key stays inert to g-code (byte-identical output).
8279 "filament_extruder_compatibility"sv,
8280 // The fast-purge / prime-volume-mode keys are new static-member registrations. Excluding them from
8281 // the config block keeps registration byte-identical for the shipping fleet (default
8282 // prime_volume_mode==Default leaves the slicing body unchanged; only the config-dump would otherwise
8283 // gain lines). They only affect output on the pvmFast / pvmSaving branch.
8284 "prime_volume_mode"sv,
8285 "flush_multiplier_fast"sv,
8286 "filament_flush_temp_fast"sv,
8287 "support_fast_purge_mode"sv,
8288 // deretract_speed_extruder_change is a device/firmware-facing machine-profile key with no slicer
8289 // consumer. Banning it from the g-code config dump keeps the H2D/A2L/X2D/P2S leaves that carry it
8290 // byte-identical, while still registering it as a known, validated config key.
8291 "deretract_speed_extruder_change"sv,
8292 // farthest_point_timelapse is a newly-registered printer key (corexy-only timelapse refinement).
8293 // Excluding it from the config block keeps the config-dump byte-identical for the whole shipping
8294 // fleet — otherwise every printer's dump would gain a `= 0` line vs the previous baseline, and
8295 // H2C/H2D would gain a non-timelapse `= 1` line. The key is read at slice time from m_config, so
8296 // banning it from the dump has no effect on the feature; the only H2C/H2D delta is the M9711/M971
8297 // snapshot reposition.
8298 "farthest_point_timelapse"sv,
8299 // The IMEX (IDEX/IQEX parallel printing) keys are newly-registered printer/process keys whose
8300 // defaults are non-nil, so leaving them in the dump would add fourteen `; imex_* = <default>`
8301 // lines to every printer's config block — an ordinary single-nozzle machine included. Excluding
8302 // them keeps the config-dump byte-identical for the whole shipping fleet; the IMEX printers pay
8303 // the same price the timelapse/prime-volume keys above already pay, and the active mode is still
8304 // visible in the body of their g-code through the injected imex_mode_gcodes macro. Every key is
8305 // read at slice time from m_config (get_imex_active_tools / find_imex_mode / the zone layout
8306 // helpers) and never parsed back out of the dump, so banning them costs the feature nothing:
8307 // the g-code viewer's overlay reads the loaded presets, not the config block, and the two
8308 // per-plate process keys round-trip through the 3MF's model_settings.config plate metadata
8309 // (bbs_3mf.cpp IMEX_PARALLEL_MODE_ATTR / IMEX_HEAD_FILAMENT_MAP_ATTR), not through this dump.
8310 "is_imex"sv,
8311 "imex_firmware_managed_zones"sv,
8312 "imex_gantry_count"sv,
8313 "imex_tools_per_gantry"sv,
8314 "imex_tool_layout"sv,
8315 "imex_nozzle_clearance_x"sv,
8316 "imex_nozzle_clearance_y"sv,
8317 "imex_carriage_margin"sv,
8318 "imex_viz_theme"sv,
8319 "imex_mode_names"sv,
8320 "imex_mode_active_tools"sv,
8321 "imex_mode_gcodes"sv,
8322 "imex_parallel_mode"sv,
8323 "imex_head_filament_map"sv,
8324 "compatible_printers"sv,
8325 "compatible_prints"sv,
8326 "filament_colour_type"sv,
8327 "print_host"sv,
8328 "print_host_webui"sv,
8329 "printhost_apikey"sv,
8330 "printhost_cafile"sv,
8331 "printhost_user"sv,
8332 "printhost_password"sv,
8333 "printhost_port"sv
8334 });
8335 auto is_banned = [](const std::string &key) {
8336 return banned_keys.find(key) != banned_keys.end();
8337 };
8338 std::ostringstream ss;
8339 for (const std::string& key : cfg.keys()) {
8340 if (!is_banned(key) && !cfg.option(key)->is_nil()) {
8341 if (key == "wipe_tower_x" || key == "wipe_tower_y") {
8342 ss << std::fixed << std::setprecision(3) << "; " << key << " = " << dynamic_cast<const ConfigOptionFloats*>(cfg.option(key))->get_at(print.get_plate_index()) << "\n";
8343 }
8344 if(key == "extruder_colour")
8345 ss << "; " << key << " = " << cfg.opt_serialize("filament_colour") << "\n";
8346 else
8347 ss << "; " << key << " = " << cfg.opt_serialize(key) << "\n";
8348 }
8349 }
8350 str += ss.str();
8351}
const double & get_at(size_t i) const
Definition Config.hpp:676
@ pvmFast
Definition PrintConfig.hpp:659

References _, Slic3r::Print::config(), Slic3r::PrintBase::full_print_config(), Slic3r::PrintBase::get_plate_index(), Slic3r::ConfigOption::is_nil(), Slic3r::DynamicConfig::keys(), L, Slic3r::ConfigBase::opt_serialize(), Slic3r::ConfigBase::option(), Print, Slic3r::pvmFast, and Slic3r::ConfigOptionVector< double >::size().

Referenced by _do_export().

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

◆ apply_print_config()

void Slic3r::GCode::apply_print_config ( const PrintConfig & print_config)
inherited
8205{
8206 m_writer.apply_print_config(print_config);
8207 m_config.apply(print_config);
8208 m_scaled_resolution = scaled<double>(print_config.resolution.value);
8209 m_enable_exclude_object = m_config.exclude_object;
8210
8211#if ORCA_CHECK_GCODE_PLACEHOLDERS
8212 // If the gcode value is empty, set a value so that the check code within the parser is run
8213 for (auto opt : std::initializer_list<ConfigOptionString*>{
8214 &m_config.machine_start_gcode,
8215 &m_config.machine_end_gcode,
8216 &m_config.before_layer_change_gcode,
8217 &m_config.layer_change_gcode,
8218 &m_config.time_lapse_gcode,
8219 &m_config.change_filament_gcode,
8220 &m_config.change_extrusion_role_gcode,
8221 &m_config.process_change_extrusion_role_gcode,
8222 &m_config.printing_by_object_gcode,
8223 &m_config.machine_pause_gcode,
8224 &m_config.template_custom_gcode,
8225 }) {
8226 if (opt->empty())
8227 opt->set(new ConfigOptionString(";VALUE FOR TESTING"));
8228 }
8229 for (auto opt : std::initializer_list<ConfigOptionStrings*>{
8230 &m_config.filament_start_gcode,
8231 &m_config.filament_end_gcode,
8232 &m_config.filament_change_extrusion_role_gcode
8233 }) {
8234 if (opt->empty())
8235 for (int i = 0; i < opt->size(); ++i)
8236 opt->set_at(new ConfigOptionString(";VALUE FOR TESTING"), i, 0);
8237 }
8238#endif
8239}
double m_scaled_resolution
Definition GCode.hpp:742
BoundingBox scaled(const BoundingBoxf &bb)
Definition BoundingBox.hpp:284

References Slic3r::ConfigOptionString, m_config, m_enable_exclude_object, m_scaled_resolution, m_writer, and Slic3r::scaled().

Referenced by _do_export().

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

◆ assign_object_label_ids()

void Slic3r::GCode::assign_object_label_ids ( Print & print)
inherited
11356{
11357 m_instance_names.clear();
11358 size_t object_id = 0;
11359 size_t unique_id = 0;
11360 for (PrintObject *object : print.objects()) {
11361 object->set_id(object_id++);
11362 size_t inst_id = 0;
11363 for (PrintInstance &inst : object->instances()) {
11364 inst.id = inst_id++;
11365 inst.unique_id = unique_id++;
11366 }
11367 }
11368}
std::unordered_map< const PrintInstance *, std::string > m_instance_names
Definition GCode.hpp:820

References m_instance_names, Slic3r::Print::objects(), and Print.

Referenced by _do_export().

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

◆ belt_height_above_floor()

bool Slic3r::GCode::belt_height_above_floor ( const Vec3d & point_slicing_mm,
double & height_mm ) const
protectedinherited
11451{
11452 // The owning object, which is what carries the belt description. During
11453 // object-brim and coincident-apron emission m_layer still points at whichever
11454 // object was visited last (or at nothing at all), so those paths publish the
11455 // owner explicitly -- otherwise a brim's speed would depend on plate order.
11456 const PrintObject *object = m_belt_floor_object != nullptr ? m_belt_floor_object
11457 : (m_layer != nullptr ? m_layer->object() : nullptr);
11458 if (object == nullptr)
11459 return false;
11460 const SlicingParameters &sp = object->slicing_parameters();
11461 // Deliberately NOT BeltFloorContext: its init() folds in
11462 // belt_support_floor_offset, a support-generator diagnostic. Letting that
11463 // option move the model's first-layer speed band would be a surprising
11464 // coupling -- a negative value would switch the slowdown off entirely.
11465 // The belt surface itself is just shear * u + z_shift.
11466 if (std::abs(sp.belt_floor_shear_factor) < EPSILON)
11467 return false;
11468 const double u = sp.belt_floor_from_axis == 0 ? point_slicing_mm.x() : point_slicing_mm.y();
11469 const double floor_z = sp.belt_floor_shear_factor * u + sp.belt_floor_z_shift;
11470 // Measured along the slicing Z, not perpendicular to the belt: layers are
11471 // horizontal slabs in the sliced frame, so the slab holding the material that
11472 // rests on the belt at this point is the one within one layer height of it.
11473 // A perpendicular measure would shrink the band by 1/cos(tilt).
11474 height_mm = point_slicing_mm.z() - floor_z;
11475 return true;
11476}
const PrintObject * m_belt_floor_object
Definition GCode.hpp:956

References Slic3r::SlicingParameters::belt_floor_from_axis, Slic3r::SlicingParameters::belt_floor_shear_factor, Slic3r::SlicingParameters::belt_floor_z_shift, EPSILON, m_belt_floor_object, and m_layer.

Referenced by belt_layer_past_first_layer_band(), effective_layer_index_for_point(), and on_first_layer().

Here is the caller graph for this function:

◆ belt_layer_past_first_layer_band()

int Slic3r::GCode::belt_layer_past_first_layer_band ( const Layer * object_layer) const
protectedinherited
11426{
11427 if (object_layer == nullptr)
11428 return -1;
11429 // The belt surface is linear in the sliced XY, so a bbox's lowest point above
11430 // it is at one of its corners.
11431 double min_height = std::numeric_limits<double>::max();
11432 bool known = false;
11433 for (const BoundingBox &bb : object_layer->lslices_bboxes) {
11434 const double xs[2] = { unscale<double>(bb.min.x()), unscale<double>(bb.max.x()) };
11435 const double ys[2] = { unscale<double>(bb.min.y()), unscale<double>(bb.max.y()) };
11436 for (double x : xs)
11437 for (double y : ys) {
11438 double h;
11439 if (! this->belt_height_above_floor(Vec3d(x, y, object_layer->print_z), h))
11440 return -1;
11441 known = true;
11442 min_height = std::min(min_height, h);
11443 }
11444 }
11445 if (! known)
11446 return -1;
11447 return min_height >= this->first_layer_band_mm() - EPSILON ? 1 : 0;
11448}
bool belt_height_above_floor(const Vec3d &point_slicing_mm, double &height_mm) const
Definition GCode.cpp:11450
double first_layer_band_mm() const
Definition GCode.hpp:1007

References belt_height_above_floor(), EPSILON, first_layer_band_mm(), Slic3r::Layer::lslices_bboxes, Slic3r::BoundingBoxBase< PointType, APointsType >::max, Slic3r::BoundingBoxBase< PointType, APointsType >::min, Slic3r::Layer::print_z, and Slic3r::unscale().

Referenced by process_layer().

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

◆ calc_max_volumetric_speed()

double Slic3r::GCode::calc_max_volumetric_speed ( const double layer_height,
const double line_width,
const std::string co_str )
protectedinherited
9140{
9141 std::vector<double> cs;
9142 std::stringstream ss(co_str);
9143 std::string token;
9144
9145 while (std::getline(ss, token, ' ')) {
9146 try {
9147 cs.push_back(std::stod(token));
9148 } catch (...) {
9149 std::cerr << "Transformation failed: " << token << std::endl;
9150 }
9151 }
9152 if (cs.size() != 6 || std::all_of(cs.begin(), cs.end(), [](double v) { return v == 0; })) return std::numeric_limits<double>::max();
9153
9154 const double x = layer_height;
9155 const double y = line_width;
9156
9157 double res = cs[0] * x * x + cs[1] * y * y + cs[2] * x * y + cs[3] * x + cs[4] * y + cs[5];
9158 return res;
9159}

References Slic3r::layer_height().

Referenced by _extrude().

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

◆ capture_toolchange_position()

std::optional< GCode::ToolchangePositionState > Slic3r::GCode::capture_toolchange_position ( )
protectedinherited
8367{
8368 if (m_exclusion_volume_travel_avoidance.empty() || !m_last_pos_defined || m_writer.filament() == nullptr)
8369 return std::nullopt;
8370
8371 const Vec2d plate_offset = m_writer.get_xy_offset().cast<double>();
8373 this->point_to_gcode(m_last_pos.to_point()) - plate_offset
8374 };
8375}
ExclusionVolumeTravelAvoidance m_exclusion_volume_travel_avoidance
Definition GCode.hpp:778

References m_exclusion_volume_travel_avoidance, m_last_pos, m_last_pos_defined, m_writer, and point_to_gcode().

Referenced by Slic3r::WipeTowerIntegration::append_tcr(), Slic3r::WipeTowerIntegration::append_tcr2(), and set_extruder().

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

◆ change_layer()

std::string Slic3r::GCode::change_layer ( coordf_t print_z)
protectedinherited
8428{
8429 std::string gcode;
8430 if (m_layer_count > 0)
8431 // Increment a progress bar indicator.
8432 gcode += m_writer.update_progress(++ m_layer_index, m_layer_count);
8433 //BBS
8434 coordf_t z = print_z + m_config.z_offset.value; // in unscaled coordinates
8435 if (FILAMENT_CONFIG(retract_when_changing_layer) && m_writer.will_move_z(z)) {
8436 LiftType lift_type = this->to_lift_type(ZHopType(FILAMENT_CONFIG(z_hop_types)));
8437 //BBS: force to use SpiralLift when change layer if lift type is auto
8438 gcode += this->retract(false, false, ZHopType(FILAMENT_CONFIG(z_hop_types)) == ZHopType::zhtAuto ? LiftType::SpiralLift : lift_type);
8439 }
8440
8441 m_writer.add_object_change_labels(gcode);
8442
8443 if (m_spiral_vase) {
8444 //BBS: force to normal lift immediately in spiral vase mode
8445 std::ostringstream comment;
8446 comment << "move to next layer (" << m_layer_index << ")";
8447 gcode += m_writer.travel_to_z(z, comment.str());
8448 }
8449
8451
8452 m_nominal_z = z;
8453 m_writer.get_position().z() = z;
8454
8455 // forget last wiping path as wiping after raising Z is pointless
8456 // BBS. Dont forget wiping path to reduce stringing.
8457 //m_wipe.reset_path();
8458
8459 return gcode;
8460}
LiftType to_lift_type(ZHopType z_hop_types)
Definition GCode.cpp:10549
ZHopType
Definition PrintConfig.hpp:582
@ zhtAuto
Definition PrintConfig.hpp:583
LiftType
Definition PrintConfig.hpp:429
@ SpiralLift
Definition PrintConfig.hpp:431
zaa_minimize_perimeter_height retract_when_changing_layer((ConfigOptionFloat, skirt_distance))((ConfigOptionInt

References FILAMENT_CONFIG, m_config, m_layer_count, m_layer_index, m_need_change_layer_lift_z, m_nominal_z, m_spiral_vase, m_writer, retract(), Slic3r::retract_when_changing_layer(), Slic3r::SpiralLift, to_lift_type(), and Slic3r::zhtAuto.

Referenced by Slic3r::WipeTowerIntegration::finalize(), process_belt_brim_layer(), and process_layer().

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

◆ check_placeholder_parser_failed()

void Slic3r::GCode::check_placeholder_parser_failed ( )
protectedinherited
5013{
5014 if (! m_placeholder_parser_integration.failed_templates.empty()) {
5015 // G-code export proceeded, but some of the PlaceholderParser substitutions failed.
5016 std::string msg = Slic3r::format(_(L("Failed to generate G-code for invalid custom G-code.\n\n")));
5017 for (const auto &name_and_error : m_placeholder_parser_integration.failed_templates)
5018 msg += name_and_error.first + " " + name_and_error.second + "\n";
5019 msg += Slic3r::format(_(L("Please check the custom G-code or use the default custom G-code.")));
5020 throw Slic3r::PlaceholderParserError(msg);
5021 }
5022}

References _, Slic3r::format(), L, and m_placeholder_parser_integration.

Referenced by do_export(), and process_layers().

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

◆ collect_layers_to_print() [1/2]

std::vector< std::pair< coordf_t, std::vector< GCode::LayerToPrint > > > Slic3r::GCode::collect_layers_to_print ( const Print & print)
staticprotectedinherited
2465{
2466 struct OrderingItem {
2467 coordf_t print_z;
2468 size_t object_idx;
2469 size_t layer_idx;
2470 };
2471
2472 std::vector<std::vector<LayerToPrint>> per_object(print.objects().size(), std::vector<LayerToPrint>());
2473 std::vector<OrderingItem> ordering;
2474
2475 std::vector<Slic3r::SlicingError> errors;
2476
2477 for (size_t i = 0; i < print.objects().size(); ++i) {
2478 try {
2479 per_object[i] = collect_layers_to_print(*print.objects()[i], print.config().belt_printer.value);
2480 } catch (const Slic3r::SlicingError &e) {
2481 errors.push_back(e);
2482 continue;
2483 }
2484 // On a belt an object may be left without a layer to print at all.
2485 if (per_object[i].empty())
2486 continue;
2487 OrderingItem ordering_item;
2488 ordering_item.object_idx = i;
2489 ordering.reserve(ordering.size() + per_object[i].size());
2490 const LayerToPrint& front = per_object[i].front();
2491 for (const LayerToPrint& ltp : per_object[i]) {
2492 ordering_item.print_z = ltp.print_z();
2493 ordering_item.layer_idx = &ltp - &front;
2494 ordering.emplace_back(ordering_item);
2495 }
2496 }
2497
2498 if (!errors.empty()) { throw Slic3r::SlicingErrors(errors); }
2499
2500 std::sort(ordering.begin(), ordering.end(), [](const OrderingItem& oi1, const OrderingItem& oi2) { return oi1.print_z < oi2.print_z; });
2501
2502 std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> layers_to_print;
2503
2504 // Merge numerically very close Z values.
2505 for (size_t i = 0; i < ordering.size();) {
2506 // Find the last layer with roughly the same print_z.
2507 size_t j = i + 1;
2508 coordf_t zmax = ordering[i].print_z + EPSILON;
2509 for (; j < ordering.size() && ordering[j].print_z <= zmax; ++j);
2510 // Merge into layers_to_print.
2511 std::pair<coordf_t, std::vector<LayerToPrint>> merged;
2512 // Assign an average print_z to the set of layers with nearly equal print_z.
2513 merged.first = 0.5 * (ordering[i].print_z + ordering[j - 1].print_z);
2514 merged.second.assign(print.objects().size(), LayerToPrint());
2515 for (; i < j; ++i) {
2516 const OrderingItem& oi = ordering[i];
2517 assert(merged.second[oi.object_idx].layer() == nullptr);
2518 merged.second[oi.object_idx] = std::move(per_object[oi.object_idx][oi.layer_idx]);
2519 }
2520 layers_to_print.emplace_back(std::move(merged));
2521 }
2522
2523 return layers_to_print;
2524}
double coordf_t
Definition libslic3r.h:51
bool empty(const BoundingBoxBase< PointType, PointsType > &bb)
Definition BoundingBox.hpp:273
Definition GCode.hpp:359

References collect_layers_to_print(), Slic3r::Print::config(), Slic3r::empty(), EPSILON, Slic3r::Print::objects(), Print, and Slic3r::ConstVectorOfPtrsAdaptor< T >::size().

Here is the call graph for this function:

◆ collect_layers_to_print() [2/2]

std::vector< GCode::LayerToPrint > Slic3r::GCode::collect_layers_to_print ( const PrintObject & object,
bool skip_empty_first_layer = false )
staticprotectedinherited
2287{
2288 std::vector<GCode::LayerToPrint> layers_to_print;
2289 layers_to_print.reserve(object.layers().size() + object.support_layers().size());
2290
2291 /*
2292 // Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um.
2293 // This is the same logic as in support generator.
2294 //FIXME should we use the printing extruders instead?
2295 double gap_over_supports = object.config().support_top_z_distance;
2296 // FIXME should we test object.config().support_material_synchronize_layers ? Currently the support layers are synchronized with object layers iff soluble supports.
2297 assert(!object.has_support() || gap_over_supports != 0. || object.config().support_material_synchronize_layers);
2298 if (gap_over_supports != 0.) {
2299 gap_over_supports = std::max(0., gap_over_supports);
2300 // Not a soluble support,
2301 double support_layer_height_min = 1000000.;
2302 for (auto lh : object.print()->config().min_layer_height.values)
2303 support_layer_height_min = std::min(support_layer_height_min, std::max(0.01, lh));
2304 gap_over_supports += support_layer_height_min;
2305 }*/
2306
2307 std::vector<std::pair<double, double>> warning_ranges;
2308
2309 // Pair the object layers with the support layers by z.
2310 //
2311 // Belt printers add a third stream: brim apron bands, which sit on the belt AHEAD of
2312 // the part and so print below the object's first layer. They are merged here rather
2313 // than pushed as standalone records, because a band's print_z can coincide with a
2314 // support layer of this same object - and the print-wide merge downstream keeps only
2315 // one record per object per z, so a standalone band would be silently overwritten.
2316 size_t idx_object_layer = 0;
2317 size_t idx_support_layer = 0;
2318 size_t idx_brim_band = 0;
2319 const auto &brim_bands = object.belt_brim_prologue(); // ordered by ascending print_z
2320 const LayerToPrint* last_extrusion_layer = nullptr;
2321 while (idx_object_layer < object.layers().size() || idx_support_layer < object.support_layers().size()
2322 || idx_brim_band < brim_bands.size()) {
2323 LayerToPrint layer_to_print;
2324 double print_z_min = std::numeric_limits<double>::max();
2325 if (idx_object_layer < object.layers().size()) {
2326 layer_to_print.object_layer = object.layers()[idx_object_layer++];
2327 print_z_min = std::min(print_z_min, layer_to_print.object_layer->print_z);
2328 }
2329
2330 if (idx_support_layer < object.support_layers().size()) {
2331 layer_to_print.support_layer = object.support_layers()[idx_support_layer++];
2332 print_z_min = std::min(print_z_min, layer_to_print.support_layer->print_z);
2333 }
2334
2335 if (idx_brim_band < brim_bands.size()) {
2336 layer_to_print.belt_brim_band = &brim_bands[idx_brim_band++];
2337 print_z_min = std::min(print_z_min, layer_to_print.belt_brim_band->print_z);
2338 }
2339
2340 if (layer_to_print.object_layer && layer_to_print.object_layer->print_z > print_z_min + EPSILON) {
2341 layer_to_print.object_layer = nullptr;
2342 --idx_object_layer;
2343 }
2344
2345 if (layer_to_print.support_layer && layer_to_print.support_layer->print_z > print_z_min + EPSILON) {
2346 layer_to_print.support_layer = nullptr;
2347 --idx_support_layer;
2348 }
2349
2350 if (layer_to_print.belt_brim_band && layer_to_print.belt_brim_band->print_z > print_z_min + EPSILON) {
2351 layer_to_print.belt_brim_band = nullptr;
2352 --idx_brim_band;
2353 }
2354
2355 layer_to_print.original_object = &object;
2356 layers_to_print.push_back(layer_to_print);
2357
2358 bool has_extrusions = (layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions())
2359 || (layer_to_print.support_layer && layer_to_print.support_layer->has_extrusions())
2360 || (layer_to_print.belt_brim_band && ! layer_to_print.belt_brim_band->fills.empty());
2361
2362 // Check that there are extrusions on the very first layer. The case with empty
2363 // first layer may result in skirt/brim in the air and maybe other issues.
2364 // Skip this check for belt printers. The shear transform tilts the
2365 // model so the first horizontal layer plane intersects only a thin
2366 // sliver of the model (width ≈ first_layer_height / shear_factor).
2367 // This sliver is often narrower than the nozzle diameter, producing
2368 // zero perimeters and an empty first layer — which is expected, not
2369 // an error. In global shear mode the object may also start above
2370 // Z=0 on the tilted belt surface.
2371 if (layers_to_print.size() == 1u) {
2372 if (!has_extrusions && !skip_empty_first_layer)
2373 throw Slic3r::SlicingError(_(L("One object has an empty first layer and can't be printed. Please Cut the bottom or enable supports.")), object.id().id);
2374 }
2375
2376 // In case there are extrusions on this layer, check there is a layer to lay it on.
2377 if ((layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions())
2378 // Allow empty support layers, as the support generator may produce no extrusions for non-empty support regions.
2379 || (layer_to_print.support_layer /* && layer_to_print.support_layer->has_extrusions() */)) {
2380 double top_cd = object.config().support_top_z_distance;
2381 double bottom_cd = object.config().support_bottom_z_distance == 0. ? top_cd : object.config().support_bottom_z_distance;
2382 //if (!object.print()->config().independent_support_layer_height)
2383 { // the actual support gap may be larger than the configured one due to rounding to layer height for organic support, regardless of independent support layer height
2384 top_cd = std::ceil(top_cd / object.config().layer_height) * object.config().layer_height;
2385 bottom_cd = std::ceil(bottom_cd / object.config().layer_height) * object.config().layer_height;
2386 }
2387 double extra_gap = (layer_to_print.support_layer ? bottom_cd : top_cd);
2388
2389 // raft contact distance should not trigger any warning
2390 if (last_extrusion_layer && last_extrusion_layer->support_layer) {
2391 double raft_gap = object.config().raft_contact_distance.value;
2392 //if (!object.print()->config().independent_support_layer_height)
2393 {
2394 raft_gap = std::ceil(raft_gap / object.config().layer_height) * object.config().layer_height;
2395 }
2396 extra_gap = std::max(extra_gap, object.config().raft_contact_distance.value);
2397 }
2398 double maximal_print_z = (last_extrusion_layer ? last_extrusion_layer->print_z() : 0.)
2399 + layer_to_print.layer()->height
2400 + std::max(0., extra_gap);
2401 // Negative support_contact_z is not taken into account, it can result in false positives in cases
2402
2403 if (has_extrusions && layer_to_print.print_z() > maximal_print_z + 2. * EPSILON) {
2404 // Belt printers: a *leading* empty range (no prior extrusion layer, so the
2405 // gap starts at Z=0) is not a floating object — it is just the belt lead-in.
2406 // The part rests on the conveyor as it advances, so the first material can
2407 // legitimately appear well above Z=0. This empty-layer check assumes a fixed
2408 // bed, where material with nothing below it is unprintable; that assumption
2409 // does not hold on a belt for the lead-in. Suppress only this leading case,
2410 // and keep flagging genuine *internal* gaps (which on a belt may still be an
2411 // over-angle overhang that would print into air).
2412 const bool belt_leading_gap = object.print()->config().belt_printer.value
2413 && last_extrusion_layer == nullptr;
2414 if (!belt_leading_gap)
2415 warning_ranges.emplace_back(std::make_pair((last_extrusion_layer ? last_extrusion_layer->print_z() : 0.), layers_to_print.back().print_z()));
2416 }
2417 }
2418 // Remember last layer with extrusions.
2419 if (has_extrusions)
2420 last_extrusion_layer = &layers_to_print.back();
2421 }
2422
2423 // ORCA-Belt: objects print at their position along the belt, so the first
2424 // extrusions legitimately start far above Z=0. Drop the spurious
2425 // "empty layers from the bed" range while keeping genuine mid-print gaps.
2426 if (skip_empty_first_layer && !warning_ranges.empty() && warning_ranges.front().first == 0.)
2427 warning_ranges.erase(warning_ranges.begin());
2428
2429 if (! warning_ranges.empty()) {
2430 std::string warning;
2431 size_t i = 0;
2432 for (i = 0; i < std::min(warning_ranges.size(), size_t(5)); ++i)
2433 warning += Slic3r::format(_(L("The object has empty layers between %1% and %2% and can\u2019t be printed.")),
2434 warning_ranges[i].first, warning_ranges[i].second) + "\n";
2435 warning += Slic3r::format(_(L("Object: %1%")), object.model_object()->name) + "\n"
2436 + _(L("Parts of the object at these heights may be too thin or the object may have a faulty mesh."));
2437
2438 const_cast<Print*>(object.print())->active_step_add_warning(
2440 }
2441
2442 // Belt printers: drop the layers that print nothing at all. An object's slicing
2443 // frame starts at the belt below its leading end, so its first layers are empty,
2444 // and with several objects along the belt those empty layers fall between other
2445 // objects' printing layers. A layer change with no moves is noise in the file, and
2446 // the preview (libvgcode) numbers its layers from the moves it sees, so a gap folds
2447 // every later layer into the one before it. Both print sequences collect their
2448 // layers here, so neither writes such a layer.
2449 if (object.print()->config().belt_printer.value)
2450 layers_to_print.erase(
2451 std::remove_if(layers_to_print.begin(), layers_to_print.end(), [&object](const LayerToPrint &ltp) {
2452 return ! ((ltp.object_layer != nullptr && belt_object_layer_prints_something(object, *ltp.object_layer)) ||
2453 (ltp.support_layer != nullptr && ltp.support_layer->has_extrusions()) ||
2454 (ltp.belt_brim_band != nullptr && ! ltp.belt_brim_band->fills.empty()));
2455 }),
2456 layers_to_print.end());
2457
2458 return layers_to_print;
2459}
friend class Print
Definition GCode.hpp:1047
@ SlicingEmptyGcodeLayers
Definition PrintBase.hpp:71
zaa_minimize_perimeter_height belt_printer((ConfigOptionBool, belt_printer_infinite_y))((ConfigOptionEnum< BeltRotationAxis >

References _, Slic3r::GCode::LayerToPrint::belt_brim_band, Slic3r::belt_printer(), config(), Slic3r::PrintStateBase::CRITICAL, Slic3r::ExtrusionEntityCollection::empty(), EPSILON, Slic3r::BeltBrimBand::fills, Slic3r::format(), Slic3r::Layer::has_extrusions(), Slic3r::SupportLayer::has_extrusions(), Slic3r::Layer::height, L, Slic3r::GCode::LayerToPrint::layer(), Slic3r::layer_height(), Slic3r::GCode::LayerToPrint::object_layer, Slic3r::GCode::LayerToPrint::original_object, Print, Slic3r::BeltBrimBand::print_z, Slic3r::GCode::LayerToPrint::print_z(), Slic3r::Layer::print_z, Slic3r::PrintStateBase::SlicingEmptyGcodeLayers, and Slic3r::GCode::LayerToPrint::support_layer.

Referenced by _do_export(), and collect_layers_to_print().

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

◆ compute_farthest_point()

void Slic3r::GCode::compute_farthest_point ( const std::vector< LayerToPrint > & layers,
int most_used_extruder,
const std::map< std::pair< const SupportLayer *, ExtrusionRole >, unsigned int > & support_filaments )
protectedinherited
6306{
6307 m_farthest_point_timelapse.farthest_point = Point(0, 0);
6308 m_farthest_point_timelapse.farthest_gcode_pos = Vec2d(0, 0);
6309 m_farthest_point_timelapse.farthest_extruder_id = 0;
6310 m_farthest_point_timelapse.farthest_is_photo_head = false;
6311
6312 // Track farthest point for external perimeters and fallback (infill/support) separately
6313 int64_t max_dist_sq_ext = -1;
6314 Point farthest_point_ext;
6315 int farthest_extruder_ext = 0;
6316
6317 int64_t max_dist_sq_fallback = -1;
6318 Point farthest_point_fallback;
6319 int farthest_extruder_fallback = 0;
6320
6321 // Recursive visitor: call fn(const ExtrusionPath&) for every leaf path
6322 auto for_each_path = [](const ExtrusionEntity *entity, const auto &fn, const auto &self) -> void {
6323 if (entity->is_collection()) {
6324 for (const auto *child : static_cast<const ExtrusionEntityCollection *>(entity)->entities)
6325 self(child, fn, self);
6326 } else if (entity->is_loop()) {
6327 for (const ExtrusionPath &p : static_cast<const ExtrusionLoop *>(entity)->paths)
6328 fn(p);
6329 } else if (const auto *mp = dynamic_cast<const ExtrusionMultiPath *>(entity)) {
6330 for (const ExtrusionPath &p : mp->paths)
6331 fn(p);
6332 } else {
6333 fn(static_cast<const ExtrusionPath &>(*entity));
6334 }
6335 };
6336
6337 auto is_ext_perimeter_role = [](ExtrusionRole role) -> bool {
6338 return role == erExternalPerimeter;
6339 };
6340 auto is_fallback_role = [](ExtrusionRole role) -> bool {
6341 return role == erInternalInfill || role == erSolidInfill || role == erTopSolidInfill;
6342 };
6343 auto is_candidate_support_role = [](ExtrusionRole role) -> bool {
6344 return role == erSupportMaterial || role == erSupportMaterialInterface || role == erSupportTransition;
6345 };
6346
6347 // Update a (max_dist_sq, point, extruder_id) triple
6348 auto update_max = [](int64_t &max_dsq, Point &out_point, int &out_ext,
6349 const Point &p, const Point &shift, int extruder_id) {
6350 Point global = p + shift;
6351 int64_t dsq = (int64_t)global.x() * global.x() + (int64_t)global.y() * global.y();
6352 if (dsq > max_dsq) {
6353 max_dsq = dsq;
6354 out_point = global;
6355 out_ext = extruder_id;
6356 }
6357 };
6358
6359 // Collect candidate endpoints from one ExtrusionPath, handling arc fitting.
6360 // Orca: ExtrusionPath::polyline is a Polyline3 (Point3 points) rather than a 2D Polyline, so each
6361 // stored point is projected to 2D via to_point() before the distance test (Z is irrelevant here).
6362 auto collect_from_path = [&update_max](int64_t &max_dsq, Point &out_point, int &out_ext,
6363 const ExtrusionPath &path, const Point &shift, int extruder_id) {
6364 const Polyline3 &poly = path.polyline;
6365 if (poly.points.empty()) return;
6366
6367 if (!poly.fitting_result.empty()) {
6368 if (poly.fitting_result.front().start_point_index < poly.points.size())
6369 update_max(max_dsq, out_point, out_ext,
6370 poly.points[poly.fitting_result.front().start_point_index].to_point(), shift, extruder_id);
6371
6372 for (const PathFittingData &seg : poly.fitting_result) {
6373 if (seg.path_type == EMovePathType::Linear_move) {
6374 for (size_t i = seg.start_point_index; i <= seg.end_point_index && i < poly.points.size(); ++i)
6375 update_max(max_dsq, out_point, out_ext, poly.points[i].to_point(), shift, extruder_id);
6376 } else if (seg.path_type == EMovePathType::Arc_move_cw || seg.path_type == EMovePathType::Arc_move_ccw) {
6377 update_max(max_dsq, out_point, out_ext, seg.arc_data.end_point, shift, extruder_id);
6378 }
6379 }
6380 } else {
6381 for (const Point3 &pt : poly.points)
6382 update_max(max_dsq, out_point, out_ext, pt.to_point(), shift, extruder_id);
6383 }
6384 };
6385
6386 // Single pass: scan all paths, collecting ext-perimeter and fallback candidates.
6387 // Use original_object (not ltp.object()) to get instance shifts, because
6388 // ltp.object() may return a shared/merged PrintObject whose instances() only
6389 // reflects one copy's shift. original_object preserves the per-ModelObject
6390 // PrintObject with correct instance positions.
6391 for (const LayerToPrint &ltp : layers) {
6392 const PrintObject *print_obj = ltp.original_object;
6393 if (!print_obj) continue;
6394
6395 for (const PrintInstance &inst : print_obj->instances()) {
6396 const Point &shift = inst.shift;
6397
6398 if (ltp.object_layer) {
6399 for (const LayerRegion *region : ltp.object_layer->regions()) {
6400 const PrintRegionConfig &rcfg = region->region().config();
6401
6402 for (const ExtrusionEntity *entity : region->perimeters.entities) {
6403 for_each_path(entity, [&](const ExtrusionPath &path) {
6404 if (is_ext_perimeter_role(path.role()))
6405 collect_from_path(max_dist_sq_ext, farthest_point_ext, farthest_extruder_ext,
6406 path, shift, (int)get_extruder_id(rcfg.outer_wall_filament_id.value - 1));
6407 }, for_each_path);
6408 }
6409
6410 for (const ExtrusionEntity *entity : region->fills.entities) {
6411 for_each_path(entity, [&](const ExtrusionPath &path) {
6412 if (!is_fallback_role(path.role())) return;
6413 int eid = (path.role() == erInternalInfill)
6414 ? (int)get_extruder_id(rcfg.sparse_infill_filament_id.value - 1)
6415 : (int)get_extruder_id(rcfg.internal_solid_filament_id.value - 1);
6416 collect_from_path(max_dist_sq_fallback, farthest_point_fallback, farthest_extruder_fallback,
6417 path, shift, eid);
6418 }, for_each_path);
6419 }
6420 }
6421 }
6422
6423 if (ltp.support_layer) {
6424 for (const ExtrusionEntity *entity : ltp.support_layer->support_fills.entities) {
6425 for_each_path(entity, [&](const ExtrusionPath &path) {
6426 if (!is_candidate_support_role(path.role())) return;
6427 auto support_filament = support_filaments.find({ ltp.support_layer, path.role() });
6428 if (support_filament == support_filaments.end()) return;
6429 int eid = (int)get_extruder_id(support_filament->second);
6430 collect_from_path(max_dist_sq_fallback, farthest_point_fallback, farthest_extruder_fallback,
6431 path, shift, eid);
6432 }, for_each_path);
6433 }
6434 }
6435 }
6436 }
6437
6438 // Prefer external perimeter result; fall back to infill/support
6439 int64_t max_dist_sq;
6440 if (max_dist_sq_ext > 0) {
6441 max_dist_sq = max_dist_sq_ext;
6442 m_farthest_point_timelapse.farthest_point = farthest_point_ext;
6443 m_farthest_point_timelapse.farthest_extruder_id = farthest_extruder_ext;
6444 } else {
6445 max_dist_sq = max_dist_sq_fallback;
6446 m_farthest_point_timelapse.farthest_point = farthest_point_fallback;
6447 m_farthest_point_timelapse.farthest_extruder_id = farthest_extruder_fallback;
6448 }
6449
6450 if (max_dist_sq > 0) {
6451 m_farthest_point_timelapse.farthest_gcode_pos = unscale(m_farthest_point_timelapse.farthest_point);
6452 // Single nozzle with AMS: all virtual extruders share one physical nozzle,
6453 // so the nozzle is always the photo head regardless of which filament is used.
6454 bool single_nozzle = (m_config.nozzle_diameter.size() <= 1);
6455 m_farthest_point_timelapse.farthest_is_photo_head = single_nozzle || (m_farthest_point_timelapse.farthest_extruder_id == most_used_extruder);
6456 }
6457}
void poly(std::vector< std::pair< Vec2d, Vec2d > > &out, std::initializer_list< Vec2d > p)
Definition DesignSketchTool.cpp:7751
zaa_minimize_perimeter_height PrintRegionConfig
Definition PrintConfig.hpp:2183

References Slic3r::PathFittingData::arc_data, Slic3r::Arc_move_ccw, Slic3r::Arc_move_cw, Slic3r::PrintRegion::config(), Slic3r::ArcSegment::end_point, Slic3r::PathFittingData::end_point_index, Slic3r::erExternalPerimeter, Slic3r::erInternalInfill, Slic3r::erSolidInfill, Slic3r::erSupportMaterial, Slic3r::erSupportMaterialInterface, Slic3r::erSupportTransition, Slic3r::erTopSolidInfill, get_extruder_id(), Slic3r::PrintObject::instances(), Slic3r::ExtrusionEntity::is_collection(), Slic3r::ExtrusionEntity::is_loop(), Slic3r::Linear_move, m_config, m_farthest_point_timelapse, Slic3r::PathFittingData::path_type, Slic3r::PrintRegionConfig, Slic3r::ExtrusionPath::role(), Slic3r::PathFittingData::start_point_index, Slic3r::Point3::to_point(), and Slic3r::unscale().

Referenced by process_layer().

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

◆ config()

◆ cur_extruder_index()

size_t Slic3r::GCode::cur_extruder_index ( ) const
protectedinherited
5025{
5026 //TODO: check if the function is duplicated
5027 //just return m_writer.filament()->extruder_id()
5028 return get_extruder_id(m_writer.filament()->id());
5029}

References get_extruder_id(), and m_writer.

Here is the call graph for this function:

◆ do_export()

void Slic3r::GCode::do_export ( Print * print,
const char * path,
GCodeProcessorResult * result = nullptr,
ThumbnailsGeneratorCallback thumbnail_cb = nullptr )
inherited
2823{
2824 PROFILE_CLEAR();
2825
2826 // BBS
2827 m_curr_print = print;
2828 m_skirt_group_done.clear();
2830 m_exclusion_volume_travel_avoidance.init(print->config(), print->get_plate_origin());
2831
2832 GCodeWriter::full_gcode_comment = print->config().gcode_comments;
2833 CNumericLocalesSetter locales_setter;
2834
2835 // Does the file exist? If so, we hope that it is still valid.
2836 if (print->is_step_done(psGCodeExport) && boost::filesystem::exists(boost::filesystem::path(path)))
2837 return;
2838
2839 BOOST_LOG_TRIVIAL(info) << boost::format("Will export G-code to %1% soon")%path;
2840
2841 GCodeProcessor::s_IsBBLPrinter = print->is_BBL_printer();
2842 m_writer.set_is_bbl_machine(print->is_BBL_printer());
2843 print->set_started(psGCodeExport);
2844
2845 {
2846 LifecycleEventContext ctx;
2847 ctx.id = std::to_string(print->model().id().id);
2848 ctx.name = print->get_model_name();
2849 ctx.code = LifecycleEvtCode::Ok;
2850 ctx.msg = path;
2851 ctx.cancellation_check = [print]() { return print->canceled(); };
2853 }
2854
2855 // check if any custom gcode contains keywords used by the gcode processor to
2856 // produce time estimation and gcode toolpaths
2857 std::vector<std::pair<std::string, std::string>> validation_res = DoExport::validate_custom_gcode(*print);
2858 if (!validation_res.empty()) {
2859 std::string reports;
2860 for (const auto& [source, keyword] : validation_res) {
2861 reports += source + ": \"" + keyword + "\"\n";
2862 }
2863 //print->active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL,
2864 // _(L("In the custom G-code were found reserved keywords:")) + "\n" +
2865 // reports +
2866 // _(L("This may cause problems in g-code visualization and printing time estimation.")));
2867 std::string temp = "Dangerous keywords in custom Gcode: " + reports + "\nThis may cause problems in g-code visualization and printing time estimation.";
2868 BOOST_LOG_TRIVIAL(warning) << temp;
2869 }
2870
2871 BOOST_LOG_TRIVIAL(info) << "Exporting G-code..." << log_memory_info();
2872
2873 // Remove the old g-code if it exists.
2874 boost::nowide::remove(path);
2875
2876 fs::path file_path(path);
2877 fs::path folder = file_path.parent_path();
2878 if (!fs::exists(folder)) {
2879 fs::create_directory(folder);
2880 BOOST_LOG_TRIVIAL(error) << "[WARNING]: the parent path " + folder.string() +" is not there, create it!" << std::endl;
2881 }
2882
2883 std::string path_tmp(path);
2884 path_tmp += ".tmp";
2885
2886 m_processor.initialize(path_tmp);
2887 m_processor.set_print(print);
2888 GCodeOutputStream file(boost::nowide::fopen(path_tmp.c_str(), "wb"), m_processor);
2889 if (! file.is_open()) {
2890 // TRN %1% is a file path.
2891 std::string err_msg = Slic3r::format(_(L("G-code export to %1% failed.\nCannot open the file for writing.")), path) + "\n";
2892 BOOST_LOG_TRIVIAL(error) << err_msg << std::endl;
2893 if (!fs::exists(folder)) {
2894 //fs::create_directory(folder);
2895 // TRN %1% is a folder path.
2896 std::string add_err_msg = Slic3r::format(_(L("The folder %1% does not exist.")), folder.string());
2897 BOOST_LOG_TRIVIAL(error) << add_err_msg << std::endl;
2898 err_msg += add_err_msg;
2899 }
2900 {
2901 LifecycleEventContext ctx;
2902 ctx.id = std::to_string(print->model().id().id);
2903 ctx.name = print->get_model_name();
2904 ctx.code = LifecycleEvtCode::Error;
2905 ctx.msg = std::string(path) + "\n" + err_msg;
2906 ctx.cancellation_check = [print]() { return print->canceled(); };
2908 }
2909 throw Slic3r::RuntimeError(err_msg);
2910 }
2911
2912 try {
2913 this->_do_export(*print, file, thumbnail_cb);
2914 file.flush();
2915 if (file.is_error()) {
2916 file.close();
2917 boost::nowide::remove(path_tmp.c_str());
2918 // TRN %1% is a file path.
2919 throw Slic3r::RuntimeError(Slic3r::format(_(L("G-code export to %1% failed.\nIs the disk full?")), path) + "\n");
2920 }
2921 } catch (std::exception &ex) {
2922 // Rethrow on any exception. std::runtime_exception and CanceledException are expected to be thrown.
2923 // Close and remove the file.
2924 file.close();
2925 boost::nowide::remove(path_tmp.c_str());
2926 {
2927 LifecycleEventContext ctx;
2928 ctx.id = std::to_string(print->model().id().id);
2929 ctx.name = print->get_model_name();
2930 ctx.code = LifecycleEvtCode::Error;
2931 ctx.msg = std::string(path) + "\n" + ex.what();
2932 ctx.cancellation_check = [print]() { return print->canceled(); };
2934 }
2935 throw;
2936 }
2937 file.close();
2938
2940
2941#if ORCA_CHECK_GCODE_PLACEHOLDERS
2942 if (!m_placeholder_error_messages.empty()){
2943 std::ostringstream message;
2944 message << "Some EditGcodeDialog defs were not specified properly. Do so in PrintConfig under SlicingStatesConfigDef:" << std::endl;
2945 for (const auto& error : m_placeholder_error_messages) {
2946 message << std::endl << error.first << ": " << std::endl;
2947 for (const auto& str : error.second)
2948 message << str << ", ";
2949 message.seekp(-2, std::ios_base::end);
2950 message << std::endl;
2951 }
2952 throw Slic3r::PlaceholderParserError(message.str());
2953 }
2954#endif
2955
2956 BOOST_LOG_TRIVIAL(debug) << "Start processing gcode, " << log_memory_info();
2957 // Post-process the G-code to update time stamps.
2958
2960 if (m_config.printer_structure.value == PrinterStructure::psI3 && m_spiral_vase) {
2962 }
2963 if (m_config.printer_structure.value == PrinterStructure::psI3 && print->config().print_sequence == PrintSequence::ByObject) {
2964 m_timelapse_warning_code += (1 << 1);
2965 }
2966 if (m_config.timelapse_type.value == TimelapseType::tlSmooth && !m_config.enable_prime_tower.value) {
2967 m_timelapse_warning_code += (1 << 2);
2968 }
2969 m_processor.result().timelapse_warning_code = m_timelapse_warning_code;
2970 m_processor.result().support_traditional_timelapse = m_support_traditional_timelapse;
2971
2972 bool activate_long_retraction_when_cut = false;
2973 for (const auto& filament : m_writer.extruders()) {
2974 size_t fi = get_filament_config_index((int)filament.id());
2975 activate_long_retraction_when_cut |= (
2976 m_config.long_retractions_when_cut.get_at(fi)
2977 && m_config.retraction_distances_when_cut.get_at(fi) > 0
2978 );
2979 }
2980
2981 m_processor.result().long_retraction_when_cut = activate_long_retraction_when_cut;
2982
2983 { //BBS:check bed and filament compatible
2984 const ConfigOptionInts *bed_temp_opt = m_config.option<ConfigOptionInts>(get_bed_temp_1st_layer_key(m_config.curr_bed_type));
2985 std::vector<int> conflict_filament;
2986 for(auto extruder_id : m_initial_layer_extruders){
2987 int cur_bed_temp = bed_temp_opt->get_at(extruder_id);
2988 if (cur_bed_temp == 0) {
2989 conflict_filament.push_back(extruder_id);
2990 }
2991 }
2992
2993 m_processor.result().filament_printable_reuslt = FilamentPrintableResult(conflict_filament, bed_type_to_gcode_string(m_config.curr_bed_type));
2994 }
2995 // check gcode is valid in machine printabele area and multi_extruder printabele area
2996 int extruder_size = m_print->config().nozzle_diameter.values.size();
2997 std::vector<Polygons> extruder_unprintable_polys = m_print->get_extruder_unprintable_polygons();
2998 Pointfs plate_printable_area = m_print->config().printable_area.values;
2999 Pointfs wrapping_exclude_area_points = m_print->config().wrapping_exclude_area.values;
3000 m_processor.check_multi_extruder_gcode_valid(extruder_size, plate_printable_area, m_print->config().printable_height.value, wrapping_exclude_area_points,
3001 extruder_unprintable_polys, m_print->get_extruder_printable_height(), m_print->get_filament_maps(),
3002 m_print->get_physical_unprintable_filaments(m_print->get_slice_used_filaments(false)));
3003
3004 // Hand the per-filament nozzle grouping to the processor BEFORE finalize, so the
3005 // pre-heat injector's second pass can resolve filament->nozzle->extruder (get_nozzle_from_id /
3006 // is_support_dynamic_nozzle_map). Print::_do_export re-assigns it onto the extracted result afterwards
3007 // (Print.cpp) for the device GUI, but that is too late for the in-finalize injector. Null for
3008 // single-nozzle prints, where the injector is gated off anyway (enable_pre_heating false).
3009 m_processor.result().nozzle_group_result = m_print->get_layered_nozzle_group_result();
3010
3011 m_processor.finalize(true);
3012 if (m_processor.get_result().exclusion_volume_path_conflict) {
3013 const int extruder_id = m_processor.get_result().exclusion_volume_conflict_extruder_id;
3014 std::string warning = extruder_id >= 0 ?
3016 // TRN %1% is the extruder number counted from 1.
3017 _(L("A G-code move intersects a collision volume for extruder %1%. This may cause a printer collision.")),
3018 std::to_string(extruder_id + 1)) :
3019 _(L("A G-code move intersects a collision volume. This may cause a printer collision."));
3020 if (m_processor.get_result().exclusion_volume_conflict_used_unknown_z) {
3021 warning += "\n" + Slic3r::format(
3022 _(L("The intersecting move is at G-code line %1%. Its Z position is unknown after homing, so Orca checked it against every height.")),
3023 std::to_string(m_processor.get_result().exclusion_volume_conflict_gcode_id));
3024 }
3025 print->active_step_add_warning(
3027 warning,
3029 }
3030// DoExport::update_print_estimated_times_stats(m_processor, print->m_print_statistics);
3031 DoExport::update_print_estimated_stats(m_processor, m_writer.extruders(), print->m_print_statistics, print->config());
3032 // Printed-mass safety check. Flushed filament leaves the bed, so subtract it
3033 // from the total to get the mass actually resting on the plate. Gated on machine_max_printed_mass
3034 // (>0 only for A2L machines), so no existing printer's gcode_check_result changes.
3035 if (m_print->config().machine_max_printed_mass.value > EPSILON) {
3036 double mass_on_bed_total = print->m_print_statistics.total_weight;
3037 for (auto volume : m_processor.get_result().print_statistics.flush_per_filament) {
3038 size_t extruder_id = volume.first;
3039 auto extruder = std::find_if(m_writer.extruders().begin(), m_writer.extruders().end(), [extruder_id](const Extruder &extr) { return extr.id() == extruder_id; });
3040 if (extruder == m_writer.extruders().end()) continue;
3041 mass_on_bed_total -= (volume.second * extruder->filament_density() * 0.001);
3042 } // flushed weight will not be keeped on the hot bed, exclude it
3043 if (mass_on_bed_total > m_print->config().machine_max_printed_mass.value) {
3044 m_processor.result().gcode_check_result.error_code |= (1 << 11); // printed weight over limit
3045 }
3046 }
3047 if (result != nullptr) {
3048 *result = std::move(m_processor.extract_result());
3049 // set the filename to the correct value
3050 result->filename = path;
3051 }
3052
3053 //BBS: add some log for error output
3054 BOOST_LOG_TRIVIAL(debug) << boost::format("Finished processing gcode to %1% ") % path_tmp;
3055
3056 std::error_code ret = rename_file(path_tmp, path);
3057 if (ret) {
3058 {
3059 LifecycleEventContext ctx;
3060 ctx.id = std::to_string(print->model().id().id);
3061 ctx.name = print->get_model_name();
3062 ctx.code = LifecycleEvtCode::Error;
3063 ctx.msg = std::string(path) + "\nFailed to rename the output G-code file: " + ret.message();
3064 ctx.cancellation_check = [print]() { return print->canceled(); };
3066 }
3067 throw Slic3r::RuntimeError(
3068 // TRN %1% is the temporary file path, %2% the output file path, %3% the system error message.
3069 Slic3r::format(_(L("Failed to rename the output G-code file from %1% to %2%.\nError: %3%\nIs %1% locked?")),
3070 path_tmp, path, ret.message()) + '\n');
3071 }
3072 else {
3073 BOOST_LOG_TRIVIAL(info) << boost::format("rename_file from %1% to %2% successfully")% path_tmp % path;
3074 }
3075
3076 BOOST_LOG_TRIVIAL(info) << "Exporting G-code finished" << log_memory_info();
3077 print->set_done(psGCodeExport);
3078
3079 {
3080 LifecycleEventContext ctx;
3081 ctx.id = std::to_string(print->model().id().id);
3082 ctx.name = print->get_model_name();
3083 ctx.code = LifecycleEvtCode::Ok;
3084 ctx.msg = path;
3085 ctx.cancellation_check = [print]() { return print->canceled(); };
3087 }
3088
3089 // Orca: label_object_enabled reflects whether objects are labeled in the g-code (EXCLUDE_OBJECT /
3090 // M486), which is driven by exclude_object for every printer
3091 if(result != nullptr)
3092 result->label_object_enabled = m_enable_exclude_object;
3093 // Write the profiler measurements to file
3094 PROFILE_UPDATE();
3095 PROFILE_OUTPUT(debug_out_path("gcode-export-profile.txt").c_str());
3096}
static bool s_IsBBLPrinter
Definition GCodeProcessor.hpp:650
static bool full_gcode_comment
Definition GCodeWriter.hpp:159
Definition GCode.hpp:411
std::set< unsigned int > m_initial_layer_extruders
Definition GCode.hpp:966
void check_placeholder_parser_failed()
Definition GCode.cpp:5012
bool m_support_traditional_timelapse
Definition GCode.hpp:911
int m_timelapse_warning_code
Definition GCode.hpp:910
std::vector< std::vector< coordf_t > > m_skirt_group_done
Definition GCode.hpp:899
void _do_export(Print &print, GCodeOutputStream &file, ThumbnailsGeneratorCallback thumbnail_cb)
Definition GCode.cpp:3386
@ SlicingExclusionVolumeToolpath
Definition PrintBase.hpp:73
static void update_print_estimated_stats(const GCodeProcessor &processor, const std::vector< Extruder > &extruders, PrintStatistics &print_statistics, const PrintConfig &config)
Definition GCode.cpp:2536
static std::vector< std::pair< std::string, std::string > > validate_custom_gcode(const Print &print)
Definition GCode.cpp:2581
@ Extruder
Definition DevConfigUtil.h:34
std::string debug_out_path(const char *name,...)
Definition utils.cpp:382
std::vector< Vec2d > Pointfs
Definition Point.hpp:77
@ psGCodeExport
Definition Print.hpp:118
@ tlSmooth
Definition PrintConfig.hpp:470
std::string log_memory_info(bool ignore_loglevel)
Definition utils.cpp:1749
@ GCodeExportFinished
Definition LifecycleEvents.hpp:32
@ GCodeExportStarted
Definition LifecycleEvents.hpp:31
@ Error
Definition LifecycleEvents.hpp:68
@ Ok
Definition LifecycleEvents.hpp:68
static std::string bed_type_to_gcode_string(const BedType type)
Definition PrintConfig.hpp:701
std::error_code rename_file(const std::string &from, const std::string &to)
Definition utils.cpp:745
@ psI3
Definition PrintConfig.hpp:560
void fire_lifecycle_event(LifecycleEvent event, const LifecycleEventContext &ctx)
Definition LifecycleEvents.hpp:234

References _, _do_export(), Slic3r::PrintBaseWithState< PrintStepEnum, COUNT >::active_step_add_warning(), Slic3r::bed_type_to_gcode_string(), Slic3r::ByObject, Slic3r::PrintBase::canceled(), Slic3r::LifecycleEventContext::cancellation_check, check_placeholder_parser_failed(), Slic3r::GCode::GCodeOutputStream::close(), Slic3r::LifecycleEventContext::code, Slic3r::Print::config(), Slic3r::PrintStateBase::CRITICAL, Slic3r::debug_out_path(), EPSILON, Slic3r::Error, Slic3r::GCodeProcessorResult::filename, Slic3r::fire_lifecycle_event(), Slic3r::GCode::GCodeOutputStream::flush(), Slic3r::format(), Slic3r::GCodeWriter::full_gcode_comment, Slic3r::GCodeExportFinished, Slic3r::GCodeExportStarted, Slic3r::ConfigOptionVector< int >::get_at(), Slic3r::get_bed_temp_1st_layer_key(), get_filament_config_index(), Slic3r::Print::get_model_name(), Slic3r::Print::get_plate_origin(), Slic3r::LifecycleEventContext::id, Slic3r::ObjectBase::id(), Slic3r::ObjectID::id, Slic3r::Print::is_BBL_printer(), Slic3r::GCode::GCodeOutputStream::is_error(), Slic3r::GCode::GCodeOutputStream::is_open(), Slic3r::Print::is_step_done(), L, Slic3r::GCodeProcessorResult::label_object_enabled, Slic3r::log_memory_info(), m_config, m_curr_print, m_enable_exclude_object, m_exclusion_volume_travel_avoidance, m_initial_layer_extruders, m_pending_start_gcode_position, m_print, Slic3r::Print::m_print_statistics, m_processor, m_skirt_group_done, m_spiral_vase, m_support_traditional_timelapse, m_timelapse_warning_code, m_writer, Slic3r::PrintBase::model(), Slic3r::LifecycleEventContext::msg, Slic3r::LifecycleEventContext::name, Slic3r::Ok, Print, Slic3r::psGCodeExport, Slic3r::psI3, Slic3r::rename_file(), Slic3r::GCodeProcessor::s_IsBBLPrinter, Slic3r::PrintBaseWithState< PrintStepEnum, COUNT >::set_done(), Slic3r::PrintBaseWithState< PrintStepEnum, COUNT >::set_started(), Slic3r::PrintStateBase::SlicingExclusionVolumeToolpath, Slic3r::tlSmooth, Slic3r::PrintStatistics::total_weight, Slic3r::DoExport::update_print_estimated_stats(), and Slic3r::DoExport::validate_custom_gcode().

Here is the call graph for this function:

◆ effective_layer_index_for_point()

int Slic3r::GCode::effective_layer_index_for_point ( const Vec3d & point_slicing_mm) const
inlineprotectedinherited
996 {
997 double h;
998 if (this->belt_height_above_floor(point_slicing_mm, h)) {
999 const double lh = this->first_layer_band_mm();
1000 return h <= 0. ? 0 : int(std::floor(h / lh));
1001 }
1002 return on_first_layer() ? 0 : layer_id();
1003 }
int layer_id() const
Definition GCode.hpp:1023

References belt_height_above_floor(), first_layer_band_mm(), layer_id(), and on_first_layer().

Referenced by _extrude().

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

◆ emit_belt_brim_bands()

std::string Slic3r::GCode::emit_belt_brim_bands ( const Print & print,
const std::vector< LayerToPrint > & layers,
const size_t single_object_instance_idx,
const unsigned int extruder_id )
protectedinherited
6229{
6230 std::string gcode;
6231 for (const LayerToPrint &ltp : layers) {
6232 const BeltBrimBand *band = ltp.belt_brim_band;
6233 if (band == nullptr || band->fills.empty() || ltp.original_object == nullptr)
6234 continue;
6235 const PrintObject &object = *ltp.original_object;
6236 // belt_brim_filament() is 1-based.
6237 if (! object.has_belt_brim() || static_cast<unsigned int>(object.belt_brim_filament() - 1) != extruder_id)
6238 continue;
6239 // Speeds, flow and retraction all read m_config.
6240 m_config.apply(print.default_region_config());
6241 m_config.apply(object.config(), true);
6242 // Apron bands have no Layer at all (m_layer is null here), so the belt
6243 // floor owner has to be named the same way the object brim names it.
6244 BeltFloorObjectGuard floor_owner{ m_belt_floor_object, &object };
6245 const size_t i_begin = single_object_instance_idx == size_t(-1) ? 0 : single_object_instance_idx;
6246 const size_t i_end = single_object_instance_idx == size_t(-1) ? object.instances().size()
6247 : single_object_instance_idx + 1;
6248 for (size_t i = i_begin; i < i_end && i < object.instances().size(); ++ i) {
6249 // Band geometry is object-local, like the object's own extrusions.
6250 const Point &offset = object.instances()[i].shift;
6251 this->set_origin(unscale(offset));
6252 this->on_set_origin(&object, offset);
6253 m_avoid_crossing_perimeters.use_external_mp();
6254 for (const ExtrusionEntity *ee : band->fills.entities)
6255 if (ee != nullptr)
6256 gcode += this->extrude_entity(*ee, "brim", NOZZLE_CONFIG(support_speed));
6257 m_avoid_crossing_perimeters.use_external_mp(false);
6258 m_avoid_crossing_perimeters.disable_once();
6259 }
6260 }
6261 return gcode;
6262}
std::string extrude_entity(const ExtrusionEntity &entity, const std::string &description="", double speed=-1., const std::vector< const ExtrusionEntity * > &region_perimeters={}, const WipeInwardSupport *wipe_support=nullptr)
Definition GCode.cpp:8840
Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, JoinType joinType, double miterLimit)
Definition ClipperUtils.cpp:582
Definition GCode.hpp:957

References config(), Slic3r::Print::default_region_config(), Slic3r::ExtrusionEntityCollection::empty(), Slic3r::ExtrusionEntityCollection::entities, extrude_entity(), Slic3r::BeltBrimBand::fills, m_avoid_crossing_perimeters, m_belt_floor_object, m_config, NOZZLE_CONFIG, Slic3r::offset(), on_set_origin(), Print, set_origin(), and Slic3r::unscale().

Referenced by process_belt_brim_layer().

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

◆ enable_cooling_markers()

bool Slic3r::GCode::enable_cooling_markers ( ) const
inlineinherited
294{ return m_enable_cooling_markers; }

References m_enable_cooling_markers.

Referenced by Slic3r::Wipe::wipe().

Here is the caller graph for this function:

◆ export_layer_filaments()

void Slic3r::GCode::export_layer_filaments ( GCodeProcessorResult * result)
inherited
4937{
4938 if (result == nullptr)
4939 return;
4940
4941 const std::vector<int>filament_map = m_config.filament_map.values; // 1 based
4942 std::vector<int>prev_filament(m_config.nozzle_diameter.size(), -1);
4943 for (size_t idx = 0; idx < m_sorted_layer_filaments.size(); ++idx) {
4944 for (auto f : m_sorted_layer_filaments[idx]) {
4945 // Mirror the guard in the sibling sequence loop below (the `filament_id < filament_map.size() &&
4946 // filament_map[filament_id] > 0` check): the H2C dynamic engine can yield a
4947 // per-layer filament set whose ids fall outside filament_map, or a filament_map value beyond the
4948 // physical-extruder count (prev_filament is sized nozzle_diameter.size()). Either an unguarded read
4949 // (filament_map[f], prev_filament[extruder_idx]) or the write below would be out of bounds and
4950 // corrupt the heap. Skipping such filaments keeps every access in range; on any valid slice the
4951 // guard never fires, so the shipping/static path is byte-identical.
4952 if (f >= filament_map.size() || filament_map[f] <= 0)
4953 continue;
4954 int extruder_idx = filament_map[f] - 1;
4955 if (extruder_idx >= (int) prev_filament.size())
4956 continue;
4957 if (prev_filament[extruder_idx] != -1 && f != prev_filament[extruder_idx]) {
4958 std::pair<int, int> from_to_pair = { prev_filament[extruder_idx],f };
4959 auto iter = result->filament_change_count_map.find(from_to_pair);
4960 if (iter == result->filament_change_count_map.end())
4961 result->filament_change_count_map.emplace(from_to_pair, 1);
4962 else
4963 iter->second += 1;
4964 }
4965 prev_filament[extruder_idx] = f;
4966 }
4967
4968 // now we do not need sorted data, so we sort the filaments in id order
4969 auto layer_filaments = m_sorted_layer_filaments[idx];
4970 std::sort(layer_filaments.begin(), layer_filaments.end());
4971 auto iter = result->layer_filaments.find(layer_filaments);
4972 if (iter == result->layer_filaments.end()) {
4973 result->layer_filaments[layer_filaments].emplace_back(idx, idx);
4974 }
4975 else {
4976 // if layer id is sequential, expand the range
4977 if (iter->second.back().second == idx - 1)
4978 iter->second.back().second = idx;
4979 else
4980 iter->second.emplace_back(idx, idx);
4981 }
4982 }
4983
4984 result->filament_change_sequence.clear();
4985 result->nozzle_change_sequence.clear();
4986
4987 int prev_sequence_filament = -1;
4988 int prev_sequence_nozzle = -1;
4989 for (size_t layer_idx = 0; layer_idx < m_sorted_layer_filaments.size(); ++layer_idx) {
4990 for (unsigned int filament_id : m_sorted_layer_filaments[layer_idx]) {
4991 int nozzle_id = 0;
4992 if (filament_id < filament_map.size() && filament_map[filament_id] > 0)
4993 nozzle_id = filament_map[filament_id] - 1;
4994 if (prev_sequence_nozzle != nozzle_id || prev_sequence_filament != static_cast<int>(filament_id)) {
4995 result->nozzle_change_sequence.emplace_back(static_cast<unsigned int>(nozzle_id));
4996 result->filament_change_sequence.emplace_back(filament_id);
4997 prev_sequence_nozzle = nozzle_id;
4998 prev_sequence_filament = static_cast<int>(filament_id);
4999 }
5000 }
5001 }
5002
5003 result->used_mixed_filaments = m_print->get_slice_used_mixed_filaments();
5004
5005 result->optimal_assignment.clear();
5006 result->optimal_assignment.reserve(filament_map.size());
5007 for (int nozzle_id : filament_map)
5008 result->optimal_assignment.emplace_back(nozzle_id > 0 ? nozzle_id - 1 : 0);
5009}
zaa_minimize_perimeter_height filament_map((ConfigOptionInts, filament_volume_map))((ConfigOptionInts
static double f(double x, double z_sin, double z_cos, bool vertical, bool flip)
Definition FillGyroid.cpp:124

References Slic3r::f(), Slic3r::GCodeProcessorResult::filament_change_count_map, Slic3r::GCodeProcessorResult::filament_change_sequence, Slic3r::filament_map(), Slic3r::GCodeProcessorResult::layer_filaments, m_config, m_print, m_sorted_layer_filaments, Slic3r::GCodeProcessorResult::nozzle_change_sequence, Slic3r::GCodeProcessorResult::optimal_assignment, and Slic3r::GCodeProcessorResult::used_mixed_filaments.

Here is the call graph for this function:

◆ extrude_entity()

std::string Slic3r::GCode::extrude_entity ( const ExtrusionEntity & entity,
const std::string & description = "",
double speed = -1.,
const std::vector< const ExtrusionEntity * > & region_perimeters = {},
const WipeInwardSupport * wipe_support = nullptr )
protectedinherited
8845{
8846 if (const ExtrusionPath* path = dynamic_cast<const ExtrusionPath*>(&entity))
8847 return this->extrude_path(*path, description, speed);
8848 else if (const ExtrusionMultiPath* multipath = dynamic_cast<const ExtrusionMultiPath*>(&entity))
8849 return this->extrude_multi_path(*multipath, description, speed);
8850 else if (const ExtrusionLoop* loop = dynamic_cast<const ExtrusionLoop*>(&entity))
8851 return this->extrude_loop(*loop, description, speed, region_perimeters, nullptr, wipe_support);
8852 else
8853 throw Slic3r::InvalidArgument("Invalid argument supplied to extrude()");
8854 return "";
8855}
std::string extrude_loop(const ExtrusionLoop &loop, const std::string &description, double speed=-1., const std::vector< const ExtrusionEntity * > &region_perimeters={}, const Point *start_point=nullptr, const WipeInwardSupport *wipe_support=nullptr)
Definition GCode.cpp:8486
std::string extrude_path(const ExtrusionPath &path, const std::string &description="", double speed=-1.)
Definition GCode.cpp:8857
std::string extrude_multi_path(const ExtrusionMultiPath &multipath, const std::string &description="", double speed=-1.)
Definition GCode.cpp:8803

References extrude_loop(), extrude_multi_path(), and extrude_path().

Referenced by emit_belt_brim_bands(), extrude_infill(), extrude_perimeters(), and generate_object_brim().

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

◆ extrude_infill()

std::string Slic3r::GCode::extrude_infill ( const Print & print,
const std::vector< ObjectByExtruder::Island::Region > & by_region,
bool ironing )
protectedinherited
8934{
8935 std::string gcode;
8936 std::vector<const ExtrusionEntity*> extrusions;
8937 std::vector<std::unique_ptr<ExtrusionEntity>> reversed;
8938 const char* extrusion_name = ironing ? "ironing" : "infill";
8939 for (const ObjectByExtruder::Island::Region &region : by_region)
8940 if (! region.infills.empty()) {
8941 extrusions.clear();
8942 extrusions.reserve(region.infills.size());
8943 for (const ExtrusionEntity *ee : region.infills)
8944 if ((ee->role() == erIroning) == ironing)
8945 extrusions.emplace_back(ee);
8946 if (! extrusions.empty()) {
8947 m_config.apply(print.get_print_region(&region - &by_region.front()).config());
8948 reversed.clear();
8949 chain_and_reorder_extrusion_entities(extrusions, m_last_pos.to_point(), reversed);
8950 // The reversed copies are in chain order.
8951 auto next_reversed = reversed.begin();
8952 for (const ExtrusionEntity *fill : extrusions) {
8953 ExtrusionEntity *own_copy = next_reversed != reversed.end() && next_reversed->get() == fill ? (next_reversed++)->get() : nullptr;
8954 auto *eec = dynamic_cast<const ExtrusionEntityCollection*>(fill);
8955 if (eec) {
8956 // A reversed copy is owned here and can be moved from.
8957 ExtrusionEntityCollection chained = own_copy ? std::move(static_cast<ExtrusionEntityCollection&>(*own_copy)) : ExtrusionEntityCollection(*eec);
8958 if (!chained.no_sort)
8959 chain_and_reorder_extrusion_entities(chained.entities, m_last_pos.to_point());
8960 for (ExtrusionEntity *ee : chained.entities)
8961 gcode += this->extrude_entity(*ee, extrusion_name);
8962 } else
8963 gcode += this->extrude_entity(*fill, extrusion_name);
8964 }
8965 }
8966 }
8967 return gcode;
8968}
void chain_and_reorder_extrusion_entities(std::vector< ExtrusionEntity * > &entities, const Point &start_near)
Definition ShortestPath.cpp:1501
id get(id object, const void *key)
Definition MacIME.mm:22

References Slic3r::chain_and_reorder_extrusion_entities(), Slic3r::PrintRegion::config(), Slic3r::ExtrusionEntityCollection::entities, Slic3r::erIroning, extrude_entity(), Slic3r::Print::get_print_region(), m_config, m_last_pos, Slic3r::ExtrusionEntityCollection::no_sort, Print, and Slic3r::ExtrusionEntity::role().

Here is the call graph for this function:

◆ extrude_loop()

std::string Slic3r::GCode::extrude_loop ( const ExtrusionLoop & loop,
const std::string & description,
double speed = -1.,
const std::vector< const ExtrusionEntity * > & region_perimeters = {},
const Point * start_point = nullptr,
const WipeInwardSupport * wipe_support = nullptr )
protectedinherited
8492{
8493 // get a copy; don't modify the orientation of the original loop object otherwise
8494 // next copies (if any) would not detect the correct orientation
8495 ExtrusionLoop loop = loop_ref;
8496
8497 bool is_hole = (loop.loop_role() & elrHole) == elrHole;
8498
8499 if (m_config.spiral_mode && !is_hole) {
8500 // if spiral vase, we have to ensure that all contour are in the same orientation.
8501 if (m_config.wall_direction == WallDirection::CounterClockwise)
8502 loop.make_counter_clockwise();
8503 else
8504 loop.make_clockwise();
8505 }
8506 //if (loop.loop_role() == elrSkirt && (this->m_layer->id() % 2 == 1))
8507 // loop.reverse();
8508
8509 // find the point of the loop that is closest to the current extruder position
8510 // or randomize if requested;
8511 // or, if `start_point` is specified, start the loop at point closest to it
8512 Point last_pos = start_point ? *start_point : this->last_pos();
8513 float seam_overhang = std::numeric_limits<float>::lowest();
8514 if (!m_config.spiral_mode && description == "perimeter") {
8515 assert(m_layer != nullptr);
8516 m_seam_placer.place_seam(m_layer, loop, last_pos, seam_overhang);
8517 } else
8518 loop.split_at(last_pos, false);
8519
8520 const auto seam_scarf_type = m_config.seam_slope_type.value;
8521 // Belt printers never get a scarf joint. The scarf starts one layer height
8522 // below the layer, which on a tilted belt is a step backwards along the belt
8523 // axis into the previous layer's wall at the seam (0.28 mm at 45 degrees per
8524 // 0.2 mm layer); with an aligned seam that ram repeats at the same spot on
8525 // every layer and knocks the part loose.
8526 bool enable_seam_slope = ((seam_scarf_type == SeamScarfType::External && !is_hole) || seam_scarf_type == SeamScarfType::All) &&
8527 !m_config.spiral_mode && !m_config.belt_printer.value &&
8528 (loop.role() == erExternalPerimeter || (loop.role() == erPerimeter && m_config.seam_slope_inner_walls)) &&
8529 layer_id() > 0;
8531 if (enable_seam_slope && m_config.seam_slope_conditional.value) {
8532 enable_seam_slope = loop.is_smooth(m_config.scarf_angle_threshold.value * M_PI / 180., nozzle_diameter);
8533 }
8534
8535 if (enable_seam_slope && m_config.seam_slope_conditional.value && m_config.scarf_overhang_threshold.value > 0.0f) {
8536 const auto _line_width = loop.role() == erExternalPerimeter ? m_config.outer_wall_line_width.get_abs_value(nozzle_diameter) :
8537 m_config.inner_wall_line_width.get_abs_value(nozzle_diameter);
8538 enable_seam_slope = seam_overhang < m_config.scarf_overhang_threshold.value * 0.01f * _line_width;
8539 }
8540
8541 // clip the path to avoid the extruder to get exactly on the first point of the loop;
8542 // if polyline was shorter than the clipping distance we'd get a null polyline, so
8543 // we discard it in that case
8544 const double seam_gap = scale_(m_config.seam_gap.get_abs_value(nozzle_diameter));
8545 const bool seam_gap_applied = enable_seam_slope || m_enable_loop_clipping;
8546 const double seam_gap_distance_mm = seam_gap_applied ? unscale_(seam_gap) : 0.0;
8547 double seam_scarf_distance_mm = 0.0;
8548 const double clip_length = m_enable_loop_clipping && !enable_seam_slope ? seam_gap : 0;
8549
8550 // get paths
8551 ExtrusionPaths paths;
8552 loop.clip_end(clip_length, &paths);
8553 if (paths.empty()) return "";
8554
8555 // SoftFever: check loop lenght for small perimeter.
8556 double small_peri_speed = -1;
8557 if (speed == -1 && loop.length() <= SMALL_PERIMETER_LENGTH(NOZZLE_CONFIG(small_perimeter_threshold))) {
8558 if(NOZZLE_CONFIG(small_perimeter_speed).value == 0)
8559 small_peri_speed = NOZZLE_CONFIG(outer_wall_speed) * 0.5;
8560 else
8561 small_peri_speed = NOZZLE_CONFIG(small_perimeter_speed).get_abs_value(NOZZLE_CONFIG(outer_wall_speed));
8562 }
8563
8564 // extrude along the path
8565 std::string gcode;
8566
8567 // Orca:
8568 // Port of "wipe inside before extruding an external perimeter" feature from super slicer
8569 // If region perimeters size not greater than or equal to 2, then skip the wipe inside move as we will extrude in mid air
8570 // as no neighbouring perimeter exists. If an internal perimeter exists, we should find 2 perimeters touching the de-retraction point
8571 // 1 - the currently printed external perimeter and 2 - the neighbouring internal perimeter.
8572 if (m_config.wipe_before_external_loop.value && !paths.empty() && paths.front().size() > 1 && paths.back().size() > 1 && paths.front().role() == erExternalPerimeter && region_perimeters.size() > 1) {
8573 const bool is_full_loop_ccw = loop.polygon().is_counter_clockwise();
8574 bool is_hole_loop = (loop.loop_role() & ExtrusionLoopRole::elrHole) != 0;
8575 const double nozzle_diam = nozzle_diameter;
8576
8577 // note: previous & next are inverted to extrude "in the opposite direction, and we are "rewinding"
8578 Point previous_point = Point(paths.front().polyline.points[1].x(), paths.front().polyline.points[1].y());
8579 Point current_point = Point(paths.front().polyline.points.front().x(), paths.front().polyline.points.front().y());
8580 Point next_point = Point(paths.back().polyline.points.back().x(), paths.back().polyline.points.back().y());
8581
8582 // can happen if seam_gap is null
8583 if (next_point == current_point) {
8584 const Point3 &p3 = paths.back().polyline.points[paths.back().polyline.points.size() - 2];
8585 next_point = Point(p3.x(), p3.y());
8586 }
8587
8588 Point a = next_point; // second point
8589 Point b = previous_point; // second to last point
8590 if ((is_hole_loop ? !is_full_loop_ccw : is_full_loop_ccw)) {
8591 // swap points
8592 std::swap(a, b);
8593 }
8594
8595 double angle = current_point.ccw_angle(a, b) / 3;
8596
8597 // turn outwards if contour, turn inwwards if hole
8598 if (is_hole_loop ? !is_full_loop_ccw : is_full_loop_ccw) angle *= -1;
8599
8600 Vec2d current_pos = current_point.cast<double>();
8601 Vec2d next_pos = next_point.cast<double>();
8602 Vec2d vec_dist = next_pos - current_pos;
8603 double vec_norm = vec_dist.norm();
8604 // Offset distance is the minimum between half the nozzle diameter or half the line width for the upcomming perimeter
8605 // This is to mimimize potential instances where the de-retraction is performed on top of a neighbouring
8606 // thin perimeter due to arachne reducing line width.
8607 coordf_t dist = std::min(scaled(nozzle_diam) * 0.5, scaled(paths.front().width) * 0.5);
8608
8609 // FIXME Hiding the seams will not work nicely for very densely discretized contours!
8610 Point pt = (current_pos + vec_dist * (2 * dist / vec_norm)).cast<coord_t>();
8611 pt.rotate(angle, current_point);
8612 pt = (current_pos + vec_dist * (2 * dist / vec_norm)).cast<coord_t>();
8613 pt.rotate(angle, current_point);
8614
8615 // Search region perimeters for lines that are touching the de-retraction point.
8616 // If an internal perimeter exists, we should find 2 perimeters touching the de-retraction point
8617 // 1: the currently printed external perimeter and 2: the neighbouring internal perimeter.
8618 int discoveredTouchingLines = 0;
8619 for (const ExtrusionEntity* ee : region_perimeters){
8620 auto potential_touching_line = ee->as_polyline();
8621 AABBTreeLines::LinesDistancer<Line> potential_touching_line_distancer{potential_touching_line.lines()};
8622 auto touching_line = potential_touching_line_distancer.all_lines_in_radius(pt, scale_(nozzle_diam));
8623 if(touching_line.size()){
8624 discoveredTouchingLines ++;
8625 if(discoveredTouchingLines > 1) break; // found 2 touching lines. End the search early.
8626 }
8627 }
8628 // found 2 perimeters touching the de-retraction point. Its safe to deretract as the point will be
8629 // inside the model
8630 if(discoveredTouchingLines > 1){
8631 // use extrude instead of travel_to_xy to trigger the unretract
8632 ExtrusionPath fake_path_wipe(Polyline3(Points3{Point3(pt), Point3(current_point)}), paths.front());
8633 fake_path_wipe.set_force_no_extrusion(true);
8634 fake_path_wipe.mm3_per_mm = 0;
8635 //fake_path_wipe.set_extrusion_role(erExternalPerimeter);
8636 gcode += extrude_path(fake_path_wipe, "move inwards before retraction/seam", speed);
8637 }
8638 }
8639
8640
8641 const auto speed_for_path = [&speed, &small_peri_speed](const ExtrusionPath& path) {
8642 // don't apply small perimeter setting for overhangs/bridges/non-perimeters
8643 const bool is_small_small_perimeter = small_peri_speed > 0 && !is_bridge(path.role()) && is_perimeter(path.role());
8644 return is_small_small_perimeter ? small_peri_speed : speed;
8645 };
8646
8647
8648 //Orca: Adaptive PA: calculate average mm3_per_mm value over the length of the loop.
8649 //This is used for adaptive PA
8650 m_multi_flow_segment_path_pa_set = false; // always emit PA on the first path of the loop
8652 double weighted_sum_mm3_per_mm = 0.0;
8653 double total_multipath_length = 0.0;
8654 for (const ExtrusionPath& path : paths) {
8655 if(!path.is_force_no_extrusion()){
8656 double path_length = unscale<double>(path.length()); //path length in mm
8657 weighted_sum_mm3_per_mm += path.mm3_per_mm * path_length;
8658 total_multipath_length += path_length;
8659 }
8660 }
8661 if (total_multipath_length > 0.0)
8662 m_multi_flow_segment_path_average_mm3_per_mm = weighted_sum_mm3_per_mm / total_multipath_length;
8663 // Orca: end of multipath average mm3_per_mm value calculation
8664
8665 if (!enable_seam_slope) {
8666 for (const ExtrusionPath& path : paths) {
8667 gcode += this->_extrude(path, description, speed_for_path(path));
8668 // Orca: Adaptive PA - dont adapt PA after the first multipath extrusion is completed
8669 // as we have already set the PA value to the average flow over the totality of the path
8670 // in the first extrude move
8671 // TODO: testing is needed with slope seams and adaptive PA.
8673 }
8674 } else {
8675 // Create seam slope
8676 double start_slope_ratio;
8677 if (m_config.seam_slope_start_height.percent) {
8678 start_slope_ratio = m_config.seam_slope_start_height.value / 100.;
8679 } else {
8680 // Get the ratio against current layer height
8681 double h = paths.front().height;
8682 start_slope_ratio = m_config.seam_slope_start_height.value / h;
8683 }
8684 if (start_slope_ratio >= 1)
8685 start_slope_ratio = 0.99;
8686
8687 double loop_length = 0.;
8688 for (const auto & path : paths) {
8689 loop_length += unscale_(path.length());
8690 }
8691
8692 const bool slope_entire_loop = m_config.seam_slope_entire_loop;
8693 const double slope_min_length = slope_entire_loop ? loop_length : std::min(m_config.seam_slope_min_length.value, loop_length);
8694 const int slope_steps = m_config.seam_slope_steps;
8695 const double slope_max_segment_length = scale_(slope_min_length / slope_steps);
8696 seam_scarf_distance_mm = slope_min_length;
8697
8698 // Calculate the sloped loop
8699 ExtrusionLoopSloped new_loop(paths, seam_gap, slope_min_length, slope_max_segment_length, start_slope_ratio, loop.loop_role());
8700 new_loop.clip_slope(seam_gap);
8701
8702 // Then extrude it
8703 for (const ExtrusionPath* path : new_loop.get_all_paths()) {
8704 gcode += this->_extrude(*path, description, speed_for_path(*path));
8705 // Orca: Adaptive PA - dont adapt PA after the first pultipath extrusion is completed
8706 // as we have already set the PA value to the average flow over the totality of the path
8707 // in the first extrude move
8709 }
8710
8711 // Fix path for wipe
8712 if (!new_loop.ends.empty()) {
8713 paths.clear();
8714 // The start slope part is ignored as it overlaps with the end part
8715 paths.reserve(new_loop.paths.size() + new_loop.ends.size());
8716 paths.insert(paths.end(), new_loop.paths.begin(), new_loop.paths.end());
8717 paths.insert(paths.end(), new_loop.ends.begin(), new_loop.ends.end());
8718 }
8719 }
8720
8721 if (description == "perimeter") {
8722 m_processor.result().print_statistics.total_seam_gap_distance += static_cast<float>(seam_gap_distance_mm);
8723 m_processor.result().print_statistics.total_seam_scarf_distance += static_cast<float>(seam_scarf_distance_mm);
8724 }
8725
8726 // Orca: share the post-extrusion nozzle position between wipe_inward and wipe_on_loops.
8727 const bool is_ccw = loop.is_counter_clockwise();
8728
8729 std::optional<Point> wipe_on_loops_dest;
8730 if (m_config.wipe_on_loops.value && paths.back().role() == erExternalPerimeter &&
8731 m_layer != nullptr && m_config.wall_loops.value > 1 && paths.front().size() >= 2 &&
8732 paths.back().polyline.points.size() >= 2)
8733 wipe_on_loops_dest = wipe_on_loops_destination(paths, scale_(nozzle_diameter), is_ccw, is_hole);
8734
8735 bool wipe_inward_applied = false;
8736 // Orca: store loop paths in print order because inward offsets use this orientation.
8737 if (m_wipe.enable && FILAMENT_CONFIG(wipe)) {
8738 m_wipe.update_path(paths);
8739
8740 // Orca: loop wipe paths retain print direction. Their material side is
8741 // therefore left for CCW contours and right for CW contours, with the
8742 // result inverted for holes. Only external perimeters are eligible.
8743 // Calibration overrides are applied during extrusion, after the region
8744 // context was created. Check the effective setting again at execution.
8745 if (m_config.wipe_inward && m_config.wipe_inward_distance.value > 0. &&
8746 wipe_support != nullptr && !wipe_support->inner_lines.empty() &&
8747 // A loop's role is its first path's role. An overhanging start must
8748 // not hide ordinary external-wall segments elsewhere in the loop.
8749 std::any_of(paths.begin(), paths.end(),
8750 [](const ExtrusionPath &path) { return is_external_perimeter(path.role()); }) &&
8751 m_wipe.path.points.size() >= 2) {
8752 // Orca: use the actual extrusion width from the path, not the config
8753 // value — outer_wall_line_width=0 (Auto) would make get_abs_value
8754 // return 0 and silently disable the feature, and Arachne may produce
8755 // a different width than the config default.
8756 const double outer_wall_line_width = paths.front().width;
8757 const double requested_offset = m_config.wipe_inward_distance.get_abs_value(outer_wall_line_width);
8758 const double offset_dist = scale_(std::min(requested_offset, outer_wall_line_width));
8759 if (offset_dist > SCALED_EPSILON) {
8760 const Point seam_start = paths.front().first_point();
8761 const Point seam_end = paths.back().last_point();
8762 const Point wipe_start = wipe_on_loops_dest.value_or(seam_end);
8763 const double max_wipe_length = scale_(FILAMENT_CONFIG(wipe_distance));
8764 // Orca: Wipe::wipe() replaces points[0] with last_pos and executes
8765 // from points[1]. The helper preserves that sentinel and atomically
8766 // replaces the remaining points, or leaves the path untouched.
8767 // Orca: a configured wall count does not guarantee that Arachne
8768 // generated an adjacent wall for this particular loop. Only
8769 // earlier entities are considered because later walls have
8770 // not been printed yet (for example with Outer/Inner order).
8771 // Inner walls determine the material side; every earlier wall
8772 // remains available to validate the executable wipe path.
8773 const double support_distance = scale_(std::max(nozzle_diameter, outer_wall_line_width));
8774 Polyline inward_path = m_wipe.path;
8776 inward_path, seam_start, seam_end, wipe_start,
8777 wipe_offset_direction(is_ccw, is_hole), offset_dist, max_wipe_length,
8778 wipe_support->inner_lines, wipe_support->printed_lines,
8779 m_wipe.path.lines(), support_distance)) {
8780 m_wipe.path = std::move(inward_path);
8781 wipe_inward_applied = true;
8782 }
8783 }
8784 }
8785 }
8786
8787 // Orca: make the configured inward move before leaving the loop.
8788 if (wipe_on_loops_dest) {
8789 gcode += m_writer.extrude_to_xy(
8790 this->point_to_gcode(*wipe_on_loops_dest), 0, "move inwards before travel", true);
8791 this->set_last_pos(*wipe_on_loops_dest);
8792 }
8793
8794 // Execute the accepted path before another extrusion replaces it. Wiping
8795 // must not force retraction or Z-hop across a short travel to the next wall.
8796 // Ordinary travel planning decides whether to retract from the new position.
8797 if (wipe_inward_applied)
8798 gcode += m_wipe.wipe(*this, 0.);
8799
8800 return gcode;
8801}
#define M_PI
Definition ExtrusionSimulator.cpp:31
Point last_pos() const
Definition GCode.hpp:279
std::string _extrude(const ExtrusionPath &path, const std::string &path_description="", double speed=-1)
Definition GCode.cpp:9185
bool m_enable_loop_clipping
Definition GCode.hpp:809
Wipe m_wipe
Definition GCode.hpp:776
#define SCALED_EPSILON
Definition libslic3r.h:109
#define SMALL_PERIMETER_LENGTH(LENGTH)
Definition libslic3r.h:94
T dist(const boost::polygon::point_data< T > &p1, const boost::polygon::point_data< T > &p2)
Definition Geometry.cpp:298
static bool is_ccw(const Polygon &poly)
Definition Geometry.hpp:66
zaa_minimize_perimeter_height nozzle_diameter((ConfigOptionBool, reduce_infill_retraction))((ConfigOptionBool
zaa_minimize_perimeter_height wipe((ConfigOptionInts, nozzle_temperature_range_low))((ConfigOptionInts
bool offset_wipe_path_toward_support(Polyline &polyline, Point seam_start, Point seam_end, Point wipe_start, int preferred_dir, double offset_dist, double max_wipe_length, const Lines &target_perimeter_lines, const Lines &printed_perimeter_lines, const Lines &current_perimeter_lines, double max_support_distance)
Definition WipePathHelpers.cpp:677
std::vector< ExtrusionPath > ExtrusionPaths
Definition ExtrusionEntity.hpp:395
@ CounterClockwise
Definition PrintConfig.hpp:255
small_perimeter_speed((ConfigOptionFloatsNullable, small_perimeter_threshold))((ConfigOptionFloatsOrPercentsNullable
@ elrHole
Definition ExtrusionEntity.hpp:55
double angle(const Eigen::MatrixBase< Derived > &v1, const Eigen::MatrixBase< Derived2 > &v2)
Definition Point.hpp:138
int wipe_offset_direction(bool is_ccw, bool is_hole)
Definition WipePathHelpers.cpp:189
std::optional< Point > wipe_on_loops_destination(const ExtrusionPaths &paths, double nozzle_diam_scaled, bool is_ccw, bool is_hole)
Definition WipePathHelpers.cpp:886
@ All
Definition PrintConfig.hpp:402
@ External
Definition PrintConfig.hpp:401

References _extrude(), Slic3r::All, Slic3r::AABBTreeLines::LinesDistancer< LineType >::all_lines_in_radius(), Slic3r::angle(), Slic3r::cast(), Slic3r::Point::ccw_angle(), Slic3r::ExtrusionLoop::clip_end(), Slic3r::ExtrusionLoopSloped::clip_slope(), Slic3r::CounterClockwise, Slic3r::Geometry::dist(), Slic3r::elrHole, Slic3r::ExtrusionLoopSloped::ends, Slic3r::erExternalPerimeter, Slic3r::erPerimeter, Slic3r::External, extrude_path(), EXTRUDER_CONFIG, FILAMENT_CONFIG, Slic3r::ExtrusionLoopSloped::get_all_paths(), Slic3r::WipeInwardSupport::inner_lines, Slic3r::is_bridge(), Slic3r::Geometry::is_ccw(), Slic3r::ExtrusionLoop::is_counter_clockwise(), Slic3r::Polygon::is_counter_clockwise(), Slic3r::is_perimeter(), Slic3r::ExtrusionLoop::is_smooth(), last_pos(), layer_id(), Slic3r::ExtrusionLoop::length(), Slic3r::ExtrusionLoop::loop_role(), m_config, m_enable_loop_clipping, m_layer, m_multi_flow_segment_path_average_mm3_per_mm, m_multi_flow_segment_path_pa_set, M_PI, m_processor, m_seam_placer, m_wipe, m_writer, Slic3r::ExtrusionLoop::make_clockwise(), Slic3r::ExtrusionLoop::make_counter_clockwise(), Slic3r::ExtrusionPath::mm3_per_mm, NOZZLE_CONFIG, Slic3r::nozzle_diameter(), Slic3r::offset_wipe_path_toward_support(), Slic3r::outer_wall_speed(), Slic3r::ExtrusionLoop::paths, point_to_gcode(), Slic3r::ExtrusionLoop::polygon(), Slic3r::WipeInwardSupport::printed_lines, Slic3r::ExtrusionLoop::role(), Slic3r::Point::rotate(), scale_, Slic3r::scaled(), SCALED_EPSILON, Slic3r::ExtrusionPath::set_force_no_extrusion(), set_last_pos(), SMALL_PERIMETER_LENGTH, Slic3r::small_perimeter_speed(), Slic3r::ExtrusionLoop::split_at(), Slic3r::unscale(), unscale_, Slic3r::wipe(), Slic3r::wipe_offset_direction(), and Slic3r::wipe_on_loops_destination().

Referenced by extrude_entity(), extrude_support(), and generate_skirt().

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

◆ extrude_multi_path()

std::string Slic3r::GCode::extrude_multi_path ( const ExtrusionMultiPath & multipath,
const std::string & description = "",
double speed = -1. )
protectedinherited
8804{
8805 // extrude along the path
8806 std::string gcode;
8807
8808 //Orca: calculate multipath average mm3_per_mm value over the length of the path.
8809 //This is used for adaptive PA
8810 m_multi_flow_segment_path_pa_set = false; // always emit PA on the first path of the multi-path
8812 double weighted_sum_mm3_per_mm = 0.0;
8813 double total_multipath_length = 0.0;
8814 for (const ExtrusionPath& path : multipath.paths) {
8815 if(!path.is_force_no_extrusion()){
8816 double path_length = unscale<double>(path.length()); //path length in mm
8817 weighted_sum_mm3_per_mm += path.mm3_per_mm * path_length;
8818 total_multipath_length += path_length;
8819 }
8820 }
8821 if (total_multipath_length > 0.0)
8822 m_multi_flow_segment_path_average_mm3_per_mm = weighted_sum_mm3_per_mm / total_multipath_length;
8823 // Orca: end of multipath average mm3_per_mm value calculation
8824
8825 for (const ExtrusionPath &path : multipath.paths){
8826 gcode += this->_extrude(path, description, speed);
8827 // Orca: Adaptive PA - dont adapt PA after the first pultipath extrusion is completed
8828 // as we have already set the PA value to the average flow over the totality of the path
8829 // in the first extrude move.
8831 }
8832
8833 // Orca: multipath wipes retrace the extrusion in reverse order.
8834 if (m_wipe.enable && FILAMENT_CONFIG(wipe))
8835 m_wipe.update_path(multipath.paths, true);
8836
8837 return gcode;
8838}

References _extrude(), FILAMENT_CONFIG, Slic3r::ExtrusionPath::is_force_no_extrusion(), Slic3r::ExtrusionPath::length(), m_multi_flow_segment_path_average_mm3_per_mm, m_multi_flow_segment_path_pa_set, m_wipe, Slic3r::ExtrusionPath::mm3_per_mm, Slic3r::ExtrusionMultiPath::paths, Slic3r::unscale(), and Slic3r::wipe().

Referenced by extrude_entity(), and extrude_support().

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

◆ extrude_path()

std::string Slic3r::GCode::extrude_path ( const ExtrusionPath & path,
const std::string & description = "",
double speed = -1. )
protectedinherited
8858{
8859 // Orca: Reset average multipath flow as this is a single line, single extrude volumetric speed path
8862 // description += ExtrusionEntity::role_to_string(path.role());
8863 std::string gcode = this->_extrude(path, description, speed);
8864 if (m_wipe.enable && FILAMENT_CONFIG(wipe)) {
8865 m_wipe.reset_path();
8866 m_wipe.path = path.polyline.to_polyline();
8867 if (is_tree(this->config().support_type) && is_support(path.role())) {
8868 if ((m_wipe.path.first_point() - m_wipe.path.last_point()).cast<double>().norm() > scale_(0.2)) {
8869 double min_dist = scale_(0.2);
8870 int i = 0;
8871 for (; i < path.polyline.points.size(); i++) {
8872 double dist = (path.polyline.points[i] - path.last_point3()).cast<double>().norm();
8873 if (dist < min_dist) min_dist = dist;
8874 if (min_dist < scale_(0.2) && dist > min_dist) break;
8875 }
8876 m_wipe.path = Polyline3(Points3(path.polyline.points.begin() + i - 1, path.polyline.points.end())).to_polyline();
8877 }
8878 } else
8879 m_wipe.path.reverse();
8880 }
8881
8882 return gcode;
8883}
bool is_tree(SupportType stype)
Definition PrintConfig.hpp:381
bool is_support(ExtrusionRole role)
Definition ExtrusionEntity.hpp:113

References _extrude(), config(), Slic3r::Geometry::dist(), FILAMENT_CONFIG, Slic3r::is_support(), Slic3r::is_tree(), Slic3r::ExtrusionPath::last_point3(), m_multi_flow_segment_path_average_mm3_per_mm, m_multi_flow_segment_path_pa_set, m_wipe, Slic3r::MultiPoint3::points, Slic3r::ExtrusionPath::polyline, Slic3r::ExtrusionPath::role(), scale_, Slic3r::Polyline3::to_polyline(), and Slic3r::wipe().

Referenced by extrude_entity(), extrude_loop(), and extrude_support().

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

◆ extrude_perimeters()

std::string Slic3r::GCode::extrude_perimeters ( const Print & print,
const std::vector< ObjectByExtruder::Island::Region > & by_region,
bool is_first_layer,
bool is_infill_first,
bool unsupported_loops_only = false )
protectedinherited
8887{
8888 std::string gcode;
8889 for (const ObjectByExtruder::Island::Region &region : by_region)
8890 if (! region.perimeters.empty()) {
8891 m_config.apply(print.get_print_region(&region - &by_region.front()).config());
8892 // BBS: for first layer, we always print wall firstly to get better bed adhesive force
8893 // This behaviour is same with cura
8894 const bool should_print = is_first_layer ? !is_infill_first
8895 : (m_config.is_infill_first == is_infill_first);
8896 if (!should_print) continue;
8897
8898 // Build the printed prefix once in emission order, scoped to this
8899 // region. Disabled or zero-length wipes need no support geometry.
8900 std::optional<WipeInwardSupport> wipe_support;
8901 if (m_wipe.enable && FILAMENT_CONFIG(wipe) && m_config.wipe_inward &&
8902 m_config.wipe_inward_distance.value > 0. &&
8903 scale_(FILAMENT_CONFIG(wipe_distance)) > SCALED_EPSILON)
8904 wipe_support.emplace();
8905
8906 // ORCA: loops flagged as extruded in mid air, out of reach of the layer below, are held back
8907 // for a second pass after the infill that anchors them. Infill already precedes infill first walls.
8908 const bool defer_unsupported = !is_infill_first;
8909 auto waits_for_infill = [](const ExtrusionEntity *ee) {
8910 return ee->is_loop() && static_cast<const ExtrusionLoop *>(ee)->print_after_infill;
8911 };
8912
8913 // The deferred pass runs after the infill, so the loops the first pass emitted are
8914 // already down and belong in the prefix an inward wipe may land on.
8915 if (wipe_support && defer_unsupported && unsupported_loops_only)
8916 for (const ExtrusionEntity* ee : region.perimeters)
8917 if (!waits_for_infill(ee))
8918 wipe_support->append(*ee);
8919
8920 for (const ExtrusionEntity* ee : region.perimeters) {
8921 if (defer_unsupported && waits_for_infill(ee) != unsupported_loops_only)
8922 continue;
8923 gcode += this->extrude_entity(*ee, "perimeter", -1., region.perimeters,
8924 wipe_support ? &*wipe_support : nullptr);
8925 if (wipe_support)
8926 wipe_support->append(*ee);
8927 }
8928 }
8929 return gcode;
8930}
is_infill_first((ConfigOptionBool, small_area_infill_flow_compensation))((ConfigOptionEnum< WallDirection >

References Slic3r::PrintRegion::config(), extrude_entity(), FILAMENT_CONFIG, Slic3r::Print::get_print_region(), Slic3r::is_infill_first(), m_config, m_wipe, Print, scale_, SCALED_EPSILON, and Slic3r::wipe().

Here is the call graph for this function:

◆ extrude_support()

std::string Slic3r::GCode::extrude_support ( const ExtrusionEntityCollection & support_fills,
const ExtrusionRole support_extrusion_role )
protectedinherited
8971{
8972 static constexpr const char* support_label = "support material";
8973 static constexpr const char* support_interface_label = "support material interface";
8974 static constexpr const char* support_transition_label = "support transition";
8975 static constexpr const char* support_ironing_label = "support ironing";
8976
8977 // Not static: it captures `this` by reference.
8978 const auto speed_for_path = [&](double length, ExtrusionRole role, double default_speed = -1.0) {
8979 if (!is_support(role) || length > SMALL_PERIMETER_LENGTH(NOZZLE_CONFIG(small_support_perimeter_threshold)))
8980 return default_speed;
8981
8982 double small_perimeter_speed = -1.0;
8983
8984 const auto base_speed = (role == erSupportMaterialInterface)
8985 ? NOZZLE_CONFIG(support_interface_speed) : NOZZLE_CONFIG(support_speed);
8986
8988 small_perimeter_speed = base_speed * 0.5;
8989 else
8991
8992 return small_perimeter_speed > 0 ? small_perimeter_speed : default_speed;
8993 };
8994
8995 // Supports are removed after printing and gain nothing from high precision G-code but a larger file, so they are
8996 // written with the standard precision, as the wipe tower is. Support ironing keeps it, as it smooths the surface
8997 // the object is printed on.
8998 const bool was_standard_precision = m_writer.force_standard_precision(support_extrusion_role != erIroning);
8999 ScopeGuard restore_precision([this, was_standard_precision] { m_writer.force_standard_precision(was_standard_precision); });
9000
9001 std::string gcode;
9002 if (!support_fills.entities.empty()) {
9003
9004 std::vector<const ExtrusionEntity*> extrusions;
9005 extrusions.reserve(support_fills.entities.size());
9006 for (const ExtrusionEntity* ee : support_fills.entities) {
9007 const auto role = ee->role();
9008 if ((role == support_extrusion_role) || (support_extrusion_role == erMixed && role != erIroning)) {
9009 extrusions.emplace_back(ee);
9010 }
9011 }
9012 if (extrusions.empty())
9013 return gcode;
9014
9015 std::vector<std::unique_ptr<ExtrusionEntity>> reversed;
9016 //ORCA: Respect no_sort to preserve support base outline->fill order.
9017 if (!support_fills.no_sort)
9018 chain_and_reorder_extrusion_entities(extrusions, m_last_pos.to_point(), reversed);
9019
9020 for (const ExtrusionEntity *ee : extrusions) {
9021 ExtrusionRole role = ee->role();
9022 assert(is_support(role) || role == erIroning);
9023
9024 const char* label = (role == erSupportMaterial) ? support_label :
9025 ((role == erSupportMaterialInterface) ? support_interface_label :
9026 ((role == erIroning) ? support_ironing_label : support_transition_label));
9027
9028 const ExtrusionPath* path = dynamic_cast<const ExtrusionPath*>(ee);
9029 const ExtrusionMultiPath* multipath = dynamic_cast<const ExtrusionMultiPath*>(ee);
9030 const ExtrusionLoop* loop = dynamic_cast<const ExtrusionLoop*>(ee);
9031 const ExtrusionEntityCollection* collection = dynamic_cast<const ExtrusionEntityCollection*>(ee);
9032
9033 if (path) {
9034 gcode += extrude_path(*path, label, speed_for_path(path->length(), role));
9035 }
9036 else if (multipath) {
9037 gcode += extrude_multi_path(*multipath, label, speed_for_path(multipath->length(), role));
9038 }
9039 else if (loop) {
9040 gcode += extrude_loop(*loop, label, speed_for_path(loop->length(), role));
9041 }
9042 else if (collection) {
9043 gcode += extrude_support(*collection, support_extrusion_role);
9044 }
9045 else {
9046 throw Slic3r::InvalidArgument("Unknown extrusion type");
9047 }
9048 }
9049 }
9050 return gcode;
9051}
std::string extrude_support(const ExtrusionEntityCollection &support_fills, const ExtrusionRole support_extrusion_role)
Definition GCode.cpp:8970
small_support_perimeter_speed((ConfigOptionFloatsNullable, small_support_perimeter_threshold))((ConfigOptionFloatsNullable
double length(const Points &pts)
Definition MultiPoint.hpp:181
@ erMixed
Definition ExtrusionEntity.hpp:47

References Slic3r::chain_and_reorder_extrusion_entities(), Slic3r::ExtrusionEntityCollection::entities, Slic3r::erIroning, Slic3r::erMixed, Slic3r::erSupportMaterial, Slic3r::erSupportMaterialInterface, extrude_loop(), extrude_multi_path(), extrude_path(), extrude_support(), Slic3r::is_support(), Slic3r::ExtrusionLoop::length(), Slic3r::ExtrusionMultiPath::length(), Slic3r::ExtrusionPath::length(), Slic3r::length(), m_last_pos, m_writer, Slic3r::ExtrusionEntityCollection::no_sort, NOZZLE_CONFIG, Slic3r::ExtrusionEntity::role(), SMALL_PERIMETER_LENGTH, Slic3r::small_perimeter_speed(), and Slic3r::small_support_perimeter_speed().

Referenced by extrude_support().

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

◆ extrusion_role_to_string_for_parser()

std::string Slic3r::GCode::extrusion_role_to_string_for_parser ( const ExtrusionRole & role)
inherited
10191{
10192 switch (role) {
10193 case erPerimeter: return "Perimeter";
10194 case erExternalPerimeter: return "ExternalPerimeter";
10195 case erOverhangPerimeter: return "OverhangPerimeter";
10196 case erInternalInfill: return "InternalInfill";
10197 case erSolidInfill: return "SolidInfill";
10198 case erTopSolidInfill: return "TopSolidInfill";
10199 case erBottomSurface: return "BottomSurface";
10200 case erBridgeInfill:
10201 case erInternalBridgeInfill: return "BridgeInfill";
10202 case erGapFill: return "GapFill";
10203 case erIroning: return "Ironing";
10204 case erSkirt: return "Skirt";
10205 case erBrim: return "Brim";
10206 case erSupportMaterial: return "SupportMaterial";
10207 case erSupportMaterialInterface: return "SupportMaterialInterface";
10208 case erSupportTransition: return "SupportTransition";
10209 case erWipeTower: return "WipeTower";
10210 case erCustom:
10211 case erMixed:
10212 case erCount:
10213 case erNone:
10214 default: return "Mixed";
10215 }
10216}
@ erCount
Definition ExtrusionEntity.hpp:48

References Slic3r::erBottomSurface, Slic3r::erBridgeInfill, Slic3r::erBrim, Slic3r::erCount, Slic3r::erCustom, Slic3r::erExternalPerimeter, Slic3r::erGapFill, Slic3r::erInternalBridgeInfill, Slic3r::erInternalInfill, Slic3r::erIroning, Slic3r::erMixed, Slic3r::erNone, Slic3r::erOverhangPerimeter, Slic3r::erPerimeter, Slic3r::erSkirt, Slic3r::erSolidInfill, Slic3r::erSupportMaterial, Slic3r::erSupportMaterialInterface, Slic3r::erSupportTransition, Slic3r::erTopSolidInfill, and Slic3r::erWipeTower.

Referenced by _extrude().

Here is the caller graph for this function:

◆ first_layer_band_mm()

double Slic3r::GCode::first_layer_band_mm ( ) const
inlineprotectedinherited
1007 {
1008 const double band = m_config.initial_layer_print_height.value;
1009 return band > 0. ? band : 0.2;
1010 }

References m_config.

Referenced by belt_layer_past_first_layer_band(), and effective_layer_index_for_point().

Here is the caller graph for this function:

◆ gcode_to_point()

Point Slic3r::GCode::gcode_to_point ( const Vec2d & point) const
inherited
11494{
11495 Vec2d pt = point - m_origin;
11496 if (const Extruder *extruder = m_writer.filament(); extruder)
11497 // This function may be called at the very start from toolchange G-code when the extruder is not assigned yet.
11498 pt += m_config.extruder_offset.get_at(extruder->extruder_id());
11499 return scaled<coord_t>(pt);
11500
11501}

References m_config, m_origin, m_writer, and Slic3r::scaled().

Referenced by Slic3r::WipeTowerIntegration::append_tcr(), Slic3r::WipeTowerIntegration::append_tcr2(), placeholder_parser_process(), synchronize_toolchange_position(), and travel_to().

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

◆ generate_object_brim()

std::string Slic3r::GCode::generate_object_brim ( const Print & print,
const PrintObject & object,
size_t instance_id,
bool first_layer,
unsigned int extruder_id,
const Layer * object_layer )
protectedinherited
5998{
5999 // Belt printers lay the brim onto the tilted belt over many layers, so there is
6000 // nothing special about the first one. The bands that coincide with an object
6001 // layer are emitted here; those below the object's first layer are apron and go
6002 // through process_belt_brim_layer() instead.
6003 if (object.has_belt_brim()) {
6004 if (object_layer == nullptr)
6005 return {};
6006 const std::vector<ExtrusionEntityCollection> &by_layer = object.belt_brim_by_layer();
6007 const size_t layer_idx = object_layer->id();
6008 if (layer_idx >= by_layer.size() || by_layer[layer_idx].empty())
6009 return {};
6010 std::string gcode;
6011 // The band geometry is in the object's local slicing frame, exactly like its
6012 // perimeters, so it needs this instance's origin. The caller does not set it
6013 // until later, and the plate brim path deliberately uses (0, 0) because its
6014 // geometry is already in plate coordinates.
6015 m_config.apply(print.default_region_config());
6016 m_config.apply(object.config(), true);
6017 // m_layer is not switched to this object until after brim emission, so name
6018 // the belt-floor owner explicitly or the classification borrows whichever
6019 // object was visited last.
6020 BeltFloorObjectGuard floor_owner{ m_belt_floor_object, &object };
6021 const Point &offset = object.instances()[instance_id].shift;
6022 this->set_origin(unscale(offset));
6023 this->on_set_origin(&object, offset);
6024 m_avoid_crossing_perimeters.use_external_mp();
6025 for (const ExtrusionEntity *ee : by_layer[layer_idx].entities)
6026 if (ee != nullptr)
6027 gcode += this->extrude_entity(*ee, "brim", NOZZLE_CONFIG(support_speed));
6028 m_avoid_crossing_perimeters.use_external_mp(false);
6029 m_avoid_crossing_perimeters.disable_once();
6030 return gcode;
6031 }
6032
6033 if (!first_layer)
6034 return {};
6035
6036 const bool wait_for_owner_filament = has_bed_exclude_volumes(m_config) &&
6038 auto emit_brim = [this, extruder_id, wait_for_owner_filament](const ExtrusionEntityCollection& brim,
6039 const std::vector<ObjectInstanceID>& instances,
6040 const unsigned int brim_filament_id) {
6041 std::string gcode;
6042 // Combined brims have an explicit owner filament. Waiting for that
6043 // filament is only required when each nozzle has different obstacles.
6044 // Otherwise retain Orca's established first-visit emission order.
6045 if (wait_for_owner_filament && brim_filament_id != extruder_id)
6046 return gcode;
6047 const bool already_emitted = std::none_of(instances.begin(), instances.end(), [this](const ObjectInstanceID& instance) {
6048 return m_objsWithBrim.find(instance) != m_objsWithBrim.end();
6049 });
6050 if (already_emitted || brim.empty())
6051 return gcode;
6052
6053 this->set_origin(0., 0.);
6054 m_avoid_crossing_perimeters.use_external_mp();
6055 for (const ExtrusionEntity* ee : brim.entities)
6056 if (ee != nullptr)
6057 gcode += this->extrude_entity(*ee, "brim", NOZZLE_CONFIG(support_speed));
6058 m_avoid_crossing_perimeters.use_external_mp(false);
6059 m_avoid_crossing_perimeters.disable_once();
6060 for (const ObjectInstanceID& instance : instances)
6061 m_objsWithBrim.erase(instance);
6062 return gcode;
6063 };
6064
6065 const ObjectInstanceID object_instance_id{ object.id(), instance_id };
6066 const size_t group_idx = find_skirt_brim_group_idx(print, object.id(), instance_id);
6067 if (group_idx != size_t(-1)) {
6068 std::string gcode;
6069 for (const Print::SkirtBrimGroup::Brim& brim : print.skirt_brim_groups()[group_idx].brims)
6070 if (std::find(brim.instances.begin(), brim.instances.end(), object_instance_id) != brim.instances.end())
6071 gcode += emit_brim(brim.brim, brim.instances, brim.filament_id);
6072 return gcode;
6073 }
6074
6075 return {};
6076}
bool has_bed_exclude_volumes(const DynamicPrintConfig &cfg)
Definition PrintConfig.cpp:14346
BedExcludeVolumeMode active_bed_exclude_volume_mode(const DynamicPrintConfig &cfg)
Definition PrintConfig.cpp:14364
static size_t find_skirt_brim_group_idx(const Print &print, ObjectID object_id, size_t instance_id)
Definition GCode.cpp:5956
@ Shared
Definition PrintConfig.hpp:643

References Slic3r::active_bed_exclude_volume_mode(), config(), Slic3r::Print::default_region_config(), extrude_entity(), Slic3r::find_skirt_brim_group_idx(), Slic3r::has_bed_exclude_volumes(), Slic3r::Layer::id(), m_avoid_crossing_perimeters, m_belt_floor_object, m_config, m_objsWithBrim, NOZZLE_CONFIG, Slic3r::offset(), on_set_origin(), Print, set_origin(), Slic3r::Shared, Slic3r::Print::skirt_brim_groups(), and Slic3r::unscale().

Here is the call graph for this function:

◆ generate_object_skirt_group()

std::string Slic3r::GCode::generate_object_skirt_group ( const Print & print,
const PrintObject & object,
size_t instance_id,
const LayerTools & layer_tools,
const Layer & layer,
unsigned int extruder_id )
protectedinherited
5975{
5976 if (print.config().skirt_type != stPerObject || !layer_tools.has_skirt || print.skirt_brim_groups().empty())
5977 return {};
5978
5979 const size_t group_idx = find_skirt_brim_group_idx(print, object.id(), instance_id);
5980 if (group_idx == size_t(-1) || print.skirt_brim_groups()[group_idx].skirt.empty())
5981 return {};
5982
5983 LayerTools object_skirt_tools = layer_tools;
5984 object_skirt_tools.extruders = { extruder_id };
5985 object_skirt_tools.has_skirt = true;
5986
5987 return generate_skirt(print, print.skirt_brim_groups()[group_idx].skirt, Point(0, 0), object.config().skirt_start_angle,
5988 object_skirt_tools, layer, extruder_id, m_skirt_group_done[group_idx]);
5989}
std::string generate_skirt(const Print &print, const ExtrusionEntityCollection &skirt, const Point &offset, const float skirt_start_angle, const LayerTools &layer_tools, const Layer &layer, unsigned int extruder_id, std::vector< coordf_t > &skirt_done)
Definition GCode.cpp:5888
@ stPerObject
Definition PrintConfig.hpp:474

References config(), Slic3r::Print::config(), Slic3r::LayerTools::extruders, Slic3r::find_skirt_brim_group_idx(), generate_skirt(), Slic3r::LayerTools::has_skirt, layer(), m_skirt_group_done, Print, Slic3r::Print::skirt_brim_groups(), and Slic3r::stPerObject.

Here is the call graph for this function:

◆ generate_skirt()

std::string Slic3r::GCode::generate_skirt ( const Print & print,
const ExtrusionEntityCollection & skirt,
const Point & offset,
const float skirt_start_angle,
const LayerTools & layer_tools,
const Layer & layer,
unsigned int extruder_id,
std::vector< coordf_t > & skirt_done )
protectedinherited
5896{
5897
5898 bool first_layer = (layer.id() == 0 && abs(layer.bottom_z()) < EPSILON);
5899 std::string gcode;
5900 // Extrude skirt at the print_z of the raft layers and normal object layers
5901 // not at the print_z of the interlaced support material layers.
5902 // Map from extruder ID to <begin, end> index of skirt loops to be extruded with that extruder.
5903 std::map<unsigned int, std::pair<size_t, size_t>> skirt_loops_per_extruder;
5904 skirt_loops_per_extruder = first_layer ?
5905 Skirt::make_skirt_loops_per_extruder_1st_layer(print, skirt, layer_tools, skirt_done) :
5906 Skirt::make_skirt_loops_per_extruder_other_layers(print, skirt, layer_tools, skirt_done);
5907
5908 if (auto loops_it = skirt_loops_per_extruder.find(extruder_id); loops_it != skirt_loops_per_extruder.end()) {
5909 const std::pair<size_t, size_t> loops = loops_it->second;
5910
5911 set_origin(unscaled(offset));
5912
5913 m_avoid_crossing_perimeters.use_external_mp();
5914 Flow layer_skirt_flow = print.skirt_flow().with_height(float(skirt_done.back() - (skirt_done.size() == 1 ? 0. : skirt_done[skirt_done.size() - 2])));
5915 double mm3_per_mm = layer_skirt_flow.mm3_per_mm();
5916 // Decide where to start looping:
5917 // - If it’s the first layer or if we do NOT want a single-wall skirt/draft shield,
5918 // start from loops.first (all loops).
5919 // - Otherwise, if single_loop_draft_shield == true (and not the first layer),
5920 // start from loops.second - 1 (just one loop).
5921 const size_t start_idx = (first_layer || !print.m_config.single_loop_draft_shield)
5922 ? loops.first
5923 : (loops.second - 1);
5924
5925 // Loop over the skirt loops and extrude
5926 for (size_t i = start_idx; i < loops.second; ++i) {
5927 // Adjust flow according to this layer's layer height.
5928 ExtrusionLoop loop = *dynamic_cast<const ExtrusionLoop*>(skirt.entities[i]);
5929 for (ExtrusionPath &path : loop.paths) {
5930 path.height = layer_skirt_flow.height();
5931 path.mm3_per_mm = mm3_per_mm;
5932 }
5933
5934 //FIXME using the support_speed of the 1st object printed.
5935 if (first_layer && i==loops.first) {
5936 //set skirt start point location
5937 const Point desired_start_point = Skirt::find_start_point(loop, skirt_start_angle);
5938 gcode += this->extrude_loop(loop, "skirt", NOZZLE_CONFIG(support_speed), {}, &desired_start_point);
5939 }
5940 else
5941 gcode += this->extrude_loop(loop, "skirt", NOZZLE_CONFIG(support_speed));
5942
5943 // If we only want a single wall on non-first layers, break now
5944 if (!first_layer && print.m_config.single_loop_draft_shield) {
5945 break;
5946 }
5947 }
5948 m_avoid_crossing_perimeters.use_external_mp(false);
5949 // Allow a straight travel move to the first object point if this is the first layer (but don't in next layers).
5950 if (first_layer && loops.first == 0)
5951 m_avoid_crossing_perimeters.disable_once();
5952 }
5953 return gcode;
5954}
static std::map< unsigned int, std::pair< size_t, size_t > > make_skirt_loops_per_extruder_other_layers(const Print &print, const ExtrusionEntityCollection &skirt, const LayerTools &layer_tools, std::vector< coordf_t > &skirt_done)
Definition GCode.cpp:5796
static std::map< unsigned int, std::pair< size_t, size_t > > make_skirt_loops_per_extruder_1st_layer(const Print &print, const ExtrusionEntityCollection &skirt, const LayerTools &layer_tools, std::vector< coordf_t > &skirt_done)
Definition GCode.cpp:5776
@ Skirt
Definition Types.hpp:155

References Slic3r::ExtrusionEntityCollection::entities, EPSILON, extrude_loop(), Slic3r::Skirt::find_start_point(), Slic3r::ExtrusionPath::height, Slic3r::Flow::height(), layer(), m_avoid_crossing_perimeters, Slic3r::Print::m_config, Slic3r::Skirt::make_skirt_loops_per_extruder_1st_layer(), Slic3r::Skirt::make_skirt_loops_per_extruder_other_layers(), Slic3r::ExtrusionPath::mm3_per_mm, Slic3r::Flow::mm3_per_mm(), NOZZLE_CONFIG, Slic3r::offset(), Slic3r::ExtrusionLoop::paths, Print, set_origin(), Slic3r::Print::skirt_flow(), Slic3r::unscaled(), and Slic3r::Flow::with_height().

Referenced by generate_object_skirt_group().

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

◆ generate_timelapse_gcode()

std::string Slic3r::GCode::generate_timelapse_gcode ( const Print & print,
coordf_t print_z,
int most_used_extruder,
const std::set< size_t > * layer_object_label_ids,
const std::vector< const PrintObject * > * printed_objects,
bool skip_pos_pick = false )
protectedinherited
6470{
6471 if (!m_writer.filament())
6472 return {};
6473
6474 std::string timelapse_gcode;
6475 if (!print.config().time_lapse_gcode.value.empty()) {
6476 const DynamicConfig config = timelapse_gcode_config(print, print_z, most_used_extruder, printed_objects, skip_pos_pick);
6477 timelapse_gcode = this->placeholder_parser_process("time_lapse_gcode", print.config().time_lapse_gcode.value, m_writer.filament()->id(), &config) + "\n";
6478 }
6479
6480 if (!timelapse_gcode.empty()) {
6481 m_writer.set_current_position_clear(false);
6482
6483 double temp_z_after_tool_change;
6484 if (GCodeProcessor::get_last_z_from_gcode(timelapse_gcode, temp_z_after_tool_change)) {
6485 Vec3d pos = m_writer.get_position();
6486 pos(2) = temp_z_after_tool_change;
6487 m_writer.set_position(pos);
6488 }
6489 }
6490
6491 // (layer_object_label_ids->size() < 64) this restriction comes from _encode_label_ids_to_base64()
6492 if (layer_object_label_ids &&
6493 is_BBL_Printer() &&
6494 (print.num_object_instances() <= g_max_label_object) && // Don't support too many objects on one plate
6495 (print.num_object_instances() > 1) && // Don't support skipping single object
6496 (!layer_object_label_ids->empty()) &&
6497 (print.calib_params().mode == CalibMode::Calib_None)) {
6498 std::ostringstream oss;
6499 for (auto it = layer_object_label_ids->begin(); it != layer_object_label_ids->end(); ++it) {
6500 if (it != layer_object_label_ids->begin()) oss << ",";
6501 oss << *it;
6502 }
6503
6504 std::string start_str = std::string("; object ids of layer ") + std::to_string(m_layer_index + 1) + (" start: ") + oss.str() + "\n";
6505 start_str += "M624 " + _encode_label_ids_to_base64(std::vector<size_t>(layer_object_label_ids->begin(), layer_object_label_ids->end())) + "\n";
6506
6507 std::string end_str = std::string("; object ids of this layer") + std::to_string(m_layer_index + 1) + (" end: ") + oss.str() + "\n";
6508 end_str += "M625\n";
6509
6510 timelapse_gcode = start_str + timelapse_gcode + end_str;
6511 }
6512
6513 return timelapse_gcode;
6514}
static bool get_last_z_from_gcode(const std::string &gcode_str, double &z)
Definition GCodeProcessor.cpp:4256
DynamicConfig timelapse_gcode_config(const Print &print, coordf_t print_z, int most_used_extruder, const std::vector< const PrintObject * > *printed_objects, bool skip_pos_pick)
Definition GCode.cpp:6516
bool is_BBL_Printer()
Definition GCode.cpp:2648
Vec3d pos(const Pt &p)
Definition ReprojectPointsOnMesh.hpp:16
static const size_t g_max_label_object
Definition GCode.cpp:211

References _encode_label_ids_to_base64(), Slic3r::Calib_None, Slic3r::Print::calib_params(), config(), Slic3r::Print::config(), Slic3r::g_max_label_object, Slic3r::GCodeProcessor::get_last_z_from_gcode(), is_BBL_Printer(), m_layer_index, m_writer, Slic3r::Calib_Params::mode, Slic3r::Print::num_object_instances(), placeholder_parser_process(), Print, and timelapse_gcode_config().

Referenced by _extrude().

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

◆ get_bed_temperature()

int Slic3r::GCode::get_bed_temperature ( const int extruder_id,
const bool is_first_layer,
const BedType bed_type ) const
protectedinherited
5498{
5499 std::string bed_temp_key = is_first_layer ? get_bed_temp_1st_layer_key(bed_type) : get_bed_temp_key(bed_type);
5500 const ConfigOptionInts* bed_temp_opt = m_config.option<ConfigOptionInts>(bed_temp_key);
5501 return bed_temp_opt->get_at(extruder_id);
5502}

References Slic3r::ConfigOptionVector< int >::get_at(), Slic3r::get_bed_temp_1st_layer_key(), Slic3r::get_bed_temp_key(), and m_config.

Referenced by _do_export(), _print_first_layer_bed_temperature(), get_highest_bed_temperature(), and process_layer().

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

◆ get_extruder_id()

size_t Slic3r::GCode::get_extruder_id ( unsigned int filament_id) const
protectedinherited
5032{
5033 if (m_print) {
5034 return m_print->get_extruder_id(filament_id);
5035 }
5036 return 0;
5037}

References m_print.

Referenced by _do_export(), _extrude(), Slic3r::WipeTowerIntegration::append_tcr(), compute_farthest_point(), cur_extruder_index(), get_nozzle_config_index(), and set_extruder().

Here is the caller graph for this function:

◆ get_filament_config_index() [1/2]

size_t Slic3r::GCode::get_filament_config_index ( int filament_id) const
inherited
5040{
5041 if (m_print)
5042 return m_filament_index_cache.get(filament_id, m_cur_layer_idx, m_print->config_index_generation(),
5043 [&] { return m_print->get_filament_config_indx(filament_id, m_cur_layer_idx); });
5044 // Orca: without a Print the filament-indexed arrays are unexpanded, so the
5045 // filament id itself is the only meaningful column.
5046 return filament_id;
5047}
ConfigIndexCache m_filament_index_cache
Definition GCode.hpp:944
size_t m_cur_layer_idx
Definition GCode.hpp:943

References m_cur_layer_idx, m_filament_index_cache, and m_print.

Referenced by _do_export(), _extrude(), Slic3r::OozePrevention::_get_temp(), _print_first_layer_extruder_temperatures(), Slic3r::WipeTowerIntegration::append_tcr(), Slic3r::WipeTowerIntegration::append_tcr2(), do_export(), process_layer(), set_extruder(), set_filament_pressure_advance(), update_placeholder_parser_with_variant_params(), and Slic3r::Wipe::wipe().

Here is the caller graph for this function:

◆ get_filament_config_index() [2/2]

size_t Slic3r::GCode::get_filament_config_index ( int filament_id,
size_t layer_id ) const
inherited
5050{
5051 // Orca: uncached, as the stages after the generator run concurrently with it.
5052 return m_print ? m_print->get_filament_config_indx(filament_id, (int) layer_id, false) : filament_id;
5053}

References layer_id(), and m_print.

Here is the call graph for this function:

◆ get_highest_bed_temperature()

int Slic3r::GCode::get_highest_bed_temperature ( const bool is_first_layer,
const Print & print ) const
protectedinherited
5505{
5506 auto bed_type = m_config.curr_bed_type;
5507 int bed_temp = 0;
5508 // Only the filaments printed on the first layer touch the bed, so a filament that starts higher up, such as a
5509 // support interface, must not raise the bed temperature for the rest of the print.
5510 for (auto fidx : print.get_slice_used_filaments(true)) {
5511 bed_temp = std::max(bed_temp, get_bed_temperature(fidx, is_first_layer, bed_type));
5512 }
5513 return bed_temp;
5514}

References get_bed_temperature(), Slic3r::Print::get_slice_used_filaments(), m_config, and Print.

Referenced by _do_export(), _print_first_layer_bed_temperature(), and process_layer().

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

◆ get_nozzle_config_index()

size_t Slic3r::GCode::get_nozzle_config_index ( int filament_id) const
inherited
5056{
5057 if (m_print)
5058 return m_nozzle_index_cache.get(filament_id, m_cur_layer_idx, m_print->config_index_generation(),
5059 [&] { return m_print->get_nozzle_config_index(filament_id, m_cur_layer_idx); });
5060 // Orca: same reasoning; degenerate to the filament's extruder column.
5061 return get_extruder_id(filament_id);
5062}
ConfigIndexCache m_nozzle_index_cache
Definition GCode.hpp:945

References get_extruder_id(), m_cur_layer_idx, m_nozzle_index_cache, and m_print.

Referenced by _extrude(), Slic3r::Wipe::calculateWipeRetractionLengths(), process_layer(), travel_to(), and Slic3r::Wipe::wipe().

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

◆ init_belt_writer()

void Slic3r::BeltGCode::init_belt_writer ( Print & print)
overrideprotectedvirtual

Reimplemented from Slic3r::GCode.

14{
15 // Axis remap and build volume max are set by base GCode after init_belt_writer
16 // returns; set_kinematics() replays them, so install order does not matter.
17 install_belt_kinematics(m_writer, print.config());
18 m_writer.set_force_normal_lift(true);
19}

References Slic3r::Print::config(), Slic3r::install_belt_kinematics(), Slic3r::GCode::m_writer, and Slic3r::GCode::Print.

Here is the call graph for this function:

◆ instance_name()

const std::string & Slic3r::GCode::instance_name ( const PrintInstance & instance)
protectedinherited
5881{
5882 auto [it, inserted] = m_instance_names.try_emplace(&instance);
5883 if (inserted)
5884 it->second = get_instance_name(instance.print_object, instance.id);
5885 return it->second;
5886}
std::string get_instance_name(const PrintObject *object, size_t inst_id)
Definition GCode.cpp:5874

References Slic3r::get_instance_name(), Slic3r::PrintInstance::id, m_instance_names, and Slic3r::PrintInstance::print_object.

Referenced by set_object_info().

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

◆ interpolate_value_across_layers()

float Slic3r::GCode::interpolate_value_across_layers ( float start_value,
float end_value,
float step = 0.0f ) const
inherited
10222{
10223 float ratio;
10224 // ORCA-Belt: counter-rotated calibration objects stand on a support wedge,
10225 // so support-only layers below the object would stretch a layer-index
10226 // interpolation. Use the object's own Z span instead, so the value ramps
10227 // across the test geometry only.
10228 if (m_config.belt_printer.value && m_layer != nullptr && !m_layer->object()->layers().empty()) {
10229 const auto& layers = m_layer->object()->layers();
10230 // Skip empty ghost layers the grid may produce below the object.
10231 double z_min = layers.front()->print_z;
10232 for (const Layer* l : layers)
10233 if (!l->lslices.empty()) { z_min = l->print_z; break; }
10234 const double z_max = layers.back()->print_z;
10235 if (m_layer->print_z <= z_min + EPSILON || z_max - z_min <= EPSILON)
10236 return start_value;
10237 ratio = float(std::min(1.0, (m_layer->print_z - z_min) / (z_max - z_min)));
10238 } else if (m_layer_index <= 1) {
10239 return start_value;
10240 } else {
10241 ratio = m_layer_index / (m_layer_count - 1.f);
10242 }
10243 if (step > 0.f) {
10244 // Discrete equal-width bands. band is clamped to the last band so the result can't overshoot the range:
10245 // at the top layer ratio * n_bands == n_bands, which would otherwise index one band past the end.
10246 const int n_bands = std::lround(std::abs(end_value - start_value) / step) + 1;
10247 const int band = std::min(n_bands - 1, static_cast<int>(ratio * n_bands));
10248 return start_value + (end_value >= start_value ? 1.f : -1.f) * band * step;
10249 }
10250 return start_value + ratio * (end_value - start_value);
10251}

References EPSILON, m_config, m_layer, m_layer_count, and m_layer_index.

Referenced by process_layer().

Here is the caller graph for this function:

◆ is_BBL_Printer()

bool Slic3r::GCode::is_BBL_Printer ( )
inherited
2649{
2650 if (m_curr_print)
2651 return m_curr_print->is_BBL_printer();
2652 return false;
2653}

References m_curr_print.

Referenced by generate_timelapse_gcode(), and process_layer().

Here is the caller graph for this function:

◆ last_pos()

Point Slic3r::GCode::last_pos ( ) const
inlineinherited
279{ return m_last_pos.to_point(); }

References m_last_pos.

Referenced by Slic3r::WipeTowerIntegration::append_tcr2(), Slic3r::Wipe::calculateWipeRetractionLengths(), extrude_loop(), Slic3r::AvoidCrossingPerimeters::travel_to(), travel_to(), and Slic3r::Wipe::wipe().

Here is the caller graph for this function:

◆ last_pos_defined()

bool Slic3r::GCode::last_pos_defined ( ) const
inlineprotectedinherited
554{ return m_last_pos_defined; }

References m_last_pos_defined.

Referenced by Slic3r::WipeTowerIntegration::append_tcr2(), and synchronize_toolchange_position().

Here is the caller graph for this function:

◆ layer()

◆ layer_count()

unsigned int Slic3r::GCode::layer_count ( ) const
inlineinherited
303{ return m_layer_count; }

References m_layer_count.

Referenced by Slic3r::precomputed_layers_source().

Here is the caller graph for this function:

◆ layer_id()

int Slic3r::GCode::layer_id ( ) const
inlineprotectedinherited
1023 {
1024 if (m_layer == nullptr)
1025 return -1;
1026 return m_layer->id();
1027 }

References m_layer.

Referenced by effective_layer_index_for_point(), extrude_loop(), get_filament_config_index(), process_layer(), sort_print_object_instances(), and update_layer_related_config().

Here is the caller graph for this function:

◆ mass_load_limited_machine_acceleration()

void Slic3r::GCode::mass_load_limited_machine_acceleration ( const PrintStatistics & curr_print_statistics,
const Print & print,
double & y_acceleration_limit_res,
double & accumulated_mass_res )
protectedinherited
6273{
6274 double curr_acceleration_y_config = 1e10;
6275 auto &machine_max_acceleration_y = print.config().machine_max_acceleration_y.values;
6276 for (auto &temp : machine_max_acceleration_y)
6277 if (curr_acceleration_y_config > temp) curr_acceleration_y_config = temp;
6278 accumulated_mass_res = curr_print_statistics.total_weight;
6279 // mass in g, acceleration in mm/s2
6280 double machine_max_force_Y = print.config().machine_max_force_Y.getFloat(),
6281 machine_bed_mass_Y = print.config().machine_bed_mass_Y.getFloat();
6282 if (machine_max_force_Y > EPSILON && machine_bed_mass_Y > EPSILON) {
6283 // This item is not applicable to this printer FIXME-other printers need acceleration limit?
6284 double virtual_force_g_mms2 = machine_max_force_Y * 1e6, // x N =x * 1e6 g*mm/s2
6285 curr_acceleration_temp = curr_acceleration_y_config; // temps
6286 if (accumulated_mass_res > EPSILON) {
6287 curr_acceleration_temp = virtual_force_g_mms2 / (machine_bed_mass_Y + accumulated_mass_res);
6288 y_acceleration_limit_res = std::min(curr_acceleration_temp, curr_acceleration_y_config);
6289 } else {
6290 y_acceleration_limit_res = curr_acceleration_y_config;
6291 BOOST_LOG_TRIVIAL(info) << "mass_load_limited_machine_acceleration: Printed mass not detected";
6292 }
6293 } else {
6294 y_acceleration_limit_res = curr_acceleration_y_config;
6295 }
6296}

References Slic3r::Print::config(), EPSILON, Print, and Slic3r::PrintStatistics::total_weight.

Referenced by process_layer_change_gcode().

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

◆ needs_retraction()

bool Slic3r::GCode::needs_retraction ( const Polyline & travel,
ExtrusionRole role,
LiftType & lift_type )
inherited
10565{
10566 if (travel.length() < scale_(FILAMENT_CONFIG(retraction_minimum_travel))) {
10567 // skip retraction if the move is shorter than the configured threshold
10568 return false;
10569 }
10570
10571 //BBS: input travel polyline must be in current plate coordinate system
10572 auto is_through_overhang = [this](const Polyline& travel) {
10573 BoundingBox travel_bbox = get_extents(travel);
10574 travel_bbox.inflated(1);
10575 travel_bbox.defined = true;
10576
10577 // do not scale for z
10578 const float protect_z = 0.4;
10579 std::pair<float, float> z_range;
10580 z_range.second = m_layer ? m_layer->print_z : 0.f;
10581 z_range.first = std::max(0.f, z_range.second - protect_z);
10582 std::vector<LayerPtrs> layers_of_objects;
10583 std::vector<BoundingBox> boundingBox_for_objects;
10584 VecOfPoints objects_instances_shift;
10585 std::vector<size_t> idx_of_object_sorted = m_curr_print->layers_sorted_for_object(z_range.first, z_range.second, layers_of_objects, boundingBox_for_objects, objects_instances_shift);
10586
10587 std::vector<bool> is_layers_of_objects_sorted(layers_of_objects.size(), false);
10588
10589 for (size_t idx : idx_of_object_sorted) {
10590 for (const Point & instance_shift : objects_instances_shift[idx]) {
10591 BoundingBox instance_bbox = boundingBox_for_objects[idx];
10592 if (!instance_bbox.defined) //BBS: Don't need to check when bounding box of overhang area is empty(undefined)
10593 continue;
10594
10595 instance_bbox.offset(scale_(EPSILON));
10596 instance_bbox.translate(instance_shift.x(), instance_shift.y());
10597 if (!instance_bbox.overlap(travel_bbox))
10598 continue;
10599
10600 Polygons temp;
10601 temp.emplace_back(instance_bbox.polygon());
10602 if (intersection_pl(travel, temp).empty())
10603 continue;
10604
10605 if (!is_layers_of_objects_sorted[idx]) {
10606 std::sort(layers_of_objects[idx].begin(), layers_of_objects[idx].end(), [](auto left, auto right) { return left->loverhangs_bbox.area() > right->loverhangs_bbox.area();});
10607 is_layers_of_objects_sorted[idx] = true;
10608 }
10609
10610 for (const auto& layer : layers_of_objects[idx]) {
10611 for (ExPolygon overhang : layer->loverhangs) {
10612 overhang.translate(instance_shift);
10613 BoundingBox bbox1 = get_extents(overhang);
10614
10615 if (!bbox1.overlap(travel_bbox))
10616 continue;
10617
10618 if (intersection_pl(travel, overhang).empty())
10619 continue;
10620
10621 return true;
10622 }
10623 }
10624 }
10625 }
10626 return false;
10627 };
10628
10629 float max_z_hop = 0.f;
10630 for (int i = 0; i < m_config.z_hop.size(); i++)
10631 max_z_hop = std::max(max_z_hop, (float)m_config.z_hop.get_at(i));
10632 float travel_len_thresh = scale_(max_z_hop / tan(this->writer().filament()->travel_slope()));
10633 float accum_len = 0.f;
10634 Polyline clipped_travel;
10635
10636 clipped_travel.append(Polyline(travel.points[0], travel.points[1]));
10637 if (clipped_travel.length() > travel_len_thresh)
10638 clipped_travel.points.back() = clipped_travel.points.front()+(clipped_travel.points.back() - clipped_travel.points.front()) * (travel_len_thresh / clipped_travel.length());
10639 //BBS: translate to current plate coordinate system
10640 clipped_travel.translate(Point::new_scale(double(m_origin.x() - m_writer.get_xy_offset().x()), double(m_origin.y() - m_writer.get_xy_offset().y())));
10641
10642 //BBS: force to retract when leave from external perimeter for a long travel
10643 //Better way is judging whether the travel move direction is same with last extrusion move.
10645 if (ZHopType(FILAMENT_CONFIG(z_hop_types)) == ZHopType::zhtAuto) {
10646 lift_type = is_through_overhang(clipped_travel) ? LiftType::SpiralLift : LiftType::SlopeLift;
10647 }
10648 else {
10649 lift_type = to_lift_type(ZHopType(FILAMENT_CONFIG(z_hop_types)));
10650 }
10651 return true;
10652 }
10653
10654 if (role == erSupportMaterial || role == erSupportTransition) {
10655 const SupportLayer* support_layer = dynamic_cast<const SupportLayer*>(m_layer);
10656 //FIXME support_layer->support_islands.contains should use some search structure!
10657 if (support_layer != NULL)
10658 // skip retraction if this is a travel move inside a support material island
10659 //FIXME not retracting over a long path may cause oozing, which in turn may result in missing material
10660 // at the end of the extrusion path!
10661 for (const ExPolygon& support_island : support_layer->support_islands)
10662 if (support_island.contains(travel))
10663 return false;
10664 //reduce the retractions in lightning infills for tree support
10665 if (support_layer != NULL && support_layer->support_type==stInnerTree)
10666 for (auto &area : support_layer->base_areas)
10667 if (area.contains(travel))
10668 return false;
10669 }
10670 //BBS: need retract when long moving to print perimeter to avoid dropping of material
10671 if (!is_perimeter(role) && m_config.reduce_infill_retraction && m_layer != nullptr &&
10672 m_config.sparse_infill_density.value > 0 && m_retract_when_crossing_perimeters.travel_inside_internal_regions(*m_layer, travel))
10673 // Skip retraction if travel is contained in an internal slice *and*
10674 // internal infill is enabled (so that stringing is entirely not visible).
10675 //FIXME any_internal_region_slice_contains() is potentionally very slow, it shall test for the bounding boxes first.
10676 return false;
10677
10678 // retract if reduce_infill_retraction is disabled or doesn't apply when role is perimeter
10679 if (ZHopType(FILAMENT_CONFIG(z_hop_types)) == ZHopType::zhtAuto) {
10680 lift_type = is_through_overhang(clipped_travel) ? LiftType::SpiralLift : LiftType::SlopeLift;
10681 }
10682 else {
10683 lift_type = to_lift_type(ZHopType(FILAMENT_CONFIG(z_hop_types)));
10684 }
10685 return true;
10686}
RetractWhenCrossingPerimeters m_retract_when_crossing_perimeters
Definition GCode.hpp:779
static Point new_scale(coordf_t x, coordf_t y)
Definition Point.hpp:214
T * begin(Slic3r::Mat< N, M, T > &mat)
Definition Point.hpp:838
T * end(Slic3r::Mat< N, M, T > &mat)
Definition Point.hpp:841
double area(const ExPolygon &poly)
Definition ExPolygon.hpp:511
@ SlopeLift
Definition PrintConfig.hpp:432
std::vector< Points, PointsAllocator< Points > > VecOfPoints
Definition Point.hpp:81
@ stInnerTree
Definition Layer.hpp:299
zaa_minimize_perimeter_height travel_slope((ConfigOptionFloats, retract_lift_above))((ConfigOptionFloats
@ right
Definition MarchingSquares.hpp:190
@ left
Definition MarchingSquares.hpp:188

References Slic3r::Polyline::append(), Slic3r::area(), Slic3r::SupportLayer::base_areas, Slic3r::ExPolygon::contains(), Slic3r::BoundingBoxBase< PointType, APointsType >::defined, Slic3r::empty(), EPSILON, Slic3r::erPerimeter, Slic3r::erSupportMaterial, Slic3r::erSupportTransition, FILAMENT_CONFIG, Slic3r::get_extents(), Slic3r::BoundingBox::inflated(), Slic3r::intersection_pl(), Slic3r::is_perimeter(), layer(), Slic3r::MultiPoint::length(), m_config, m_curr_print, m_last_processor_extrusion_role, m_layer, m_origin, m_retract_when_crossing_perimeters, m_writer, Slic3r::Point::new_scale(), Slic3r::BoundingBoxBase< PointType, APointsType >::offset(), Slic3r::BoundingBoxBase< PointType, APointsType >::overlap(), Slic3r::MultiPoint::points, Slic3r::BoundingBox::polygon(), scale_, Slic3r::SlopeLift, Slic3r::SpiralLift, Slic3r::stInnerTree, Slic3r::SupportLayer::support_islands, Slic3r::SupportLayer::support_type, to_lift_type(), Slic3r::BoundingBoxBase< PointType, APointsType >::translate(), Slic3r::ExPolygon::translate(), Slic3r::MultiPoint::translate(), Slic3r::travel_slope(), writer(), and Slic3r::zhtAuto.

Referenced by travel_to().

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

◆ object_layer_over_raft()

bool Slic3r::GCode::object_layer_over_raft ( ) const
inlineprotectedinherited
1029{ return m_object_layer_over_raft; }
bool m_object_layer_over_raft
Definition GCode.hpp:840

References m_object_layer_over_raft.

Referenced by _extrude().

Here is the caller graph for this function:

◆ on_first_layer() [1/2]

bool Slic3r::GCode::on_first_layer ( ) const
inlineprotectedinherited
982{ return m_layer != nullptr && m_layer->id() == 0 && abs(m_layer->bottom_z()) < EPSILON; }

References EPSILON, and m_layer.

Referenced by _do_export(), _extrude(), _needSAFC(), Slic3r::WipeTowerIntegration::append_tcr(), Slic3r::WipeTowerIntegration::append_tcr2(), effective_layer_index_for_point(), on_first_layer(), retract(), set_extruder(), and travel_to().

Here is the caller graph for this function:

◆ on_first_layer() [2/2]

bool Slic3r::GCode::on_first_layer ( const Vec3d & point_slicing_mm) const
inlineprotectedinherited
987 {
988 double h;
989 if (this->belt_height_above_floor(point_slicing_mm, h))
990 return h <= m_config.initial_layer_print_height.value + EPSILON;
991 return on_first_layer();
992 }

References belt_height_above_floor(), EPSILON, m_config, and on_first_layer().

Here is the call graph for this function:

◆ on_set_origin()

void Slic3r::BeltGCode::on_set_origin ( const PrintObject * obj,
const Point & inst_shift )
overrideprotectedvirtual

Reimplemented from Slic3r::GCode.

35{
36 // Matches the per-instance Z-offset added in PrintObjectSlice.cpp: transform
37 // the origin through the belt pipeline so that back_transform(T * origin) =
38 // origin (correct machine position). The back_transform applied during
39 // G-code emission is the inverse of the forward transform.
40
41 // Adjust origin: transform through belt forward pipeline so that
42 // the back-transform correctly recovers model-space positions.
44 Vec2d cur_origin = this->origin();
45 Vec3d origin3d(cur_origin.x(), cur_origin.y(), 0.);
46 Vec3d adjusted = T.linear() * origin3d;
47 this->set_origin(Vec2d(adjusted.x(), adjusted.y()));
48}
static Transform3d build_forward_transform(const PrintConfig &config)
Definition BeltTransform.cpp:38
const Vec2d & origin() const
Definition GCode.hpp:276
Eigen::Transform< double, 3, Eigen::Affine, Eigen::DontAlign > Transform3d
Definition Point.hpp:96

References Slic3r::BeltTransformPipeline::build_forward_transform(), Slic3r::GCode::m_config, Slic3r::GCode::origin(), and Slic3r::GCode::set_origin().

Here is the call graph for this function:

◆ origin()

const Vec2d & Slic3r::GCode::origin ( ) const
inlineinherited
276{ return m_origin; }

References m_origin.

Referenced by Slic3r::BeltGCode::on_set_origin(), Slic3r::AvoidCrossingPerimeters::travel_to(), and Slic3r::wipe_tower_point_to_object_point().

Here is the caller graph for this function:

◆ placeholder_parser() [1/2]

PlaceholderParser & Slic3r::GCode::placeholder_parser ( )
inlineinherited
289{ return m_placeholder_parser_integration.parser; }

References m_placeholder_parser_integration.

Referenced by _do_export(), Slic3r::WipeTowerIntegration::append_tcr(), set_extruder(), and update_placeholder_parser_with_variant_params().

Here is the caller graph for this function:

◆ placeholder_parser() [2/2]

const PlaceholderParser & Slic3r::GCode::placeholder_parser ( ) const
inlineinherited

◆ placeholder_parser_process()

std::string Slic3r::GCode::placeholder_parser_process ( const std::string & name,
const std::string & templ,
unsigned int current_filament_id,
const DynamicConfig * config_override = nullptr )
inherited
5320{
5321 // Orca: Added CMake config option since debug is rarely used in current workflow.
5322 // Also changed from throwing error immediately to storing messages till slicing is completed
5323 // to raise all errors at the same time.
5324#if ORCA_CHECK_GCODE_PLACEHOLDERS
5325 if (config_override) {
5326 const auto& custom_gcode_placeholders = custom_gcode_specific_placeholders();
5327
5328 // 1-st check: custom G-code "name" have to be present in s_CustomGcodeSpecificPlaceholders;
5329 //if (custom_gcode_placeholders.count(name) > 0) {
5330 // const auto& placeholders = custom_gcode_placeholders.at(name);
5331 if (auto it = custom_gcode_placeholders.find(name); it != custom_gcode_placeholders.end()) {
5332 const auto& placeholders = it->second;
5333
5334 for (const std::string& key : config_override->keys()) {
5335 // 2-nd check: "key" have to be present in s_CustomGcodeSpecificPlaceholders for "name" custom G-code ;
5336 if (std::find(placeholders.begin(), placeholders.end(), key) == placeholders.end()) {
5337 auto& vector = m_placeholder_error_messages[name + " - option not specified for custom gcode type (s_CustomGcodeSpecificPlaceholders)"];
5338 if (std::find(vector.begin(), vector.end(), key) == vector.end())
5339 vector.emplace_back(key);
5340 }
5341 // 3-rd check: "key" have to be present in CustomGcodeSpecificConfigDef for "key" placeholder;
5343 auto& vector = m_placeholder_error_messages[name + " - option has no definition (CustomGcodeSpecificConfigDef)"];
5344 if (std::find(vector.begin(), vector.end(), key) == vector.end())
5345 vector.emplace_back(key);
5346 }
5347 }
5348 }
5349 else {
5350 auto& vector = m_placeholder_error_messages[name + " - gcode type not found in s_CustomGcodeSpecificPlaceholders"];
5351 if (vector.empty())
5352 vector.emplace_back("");
5353 }
5354 }
5355#endif
5356
5358 try {
5359 ppi.update_from_gcodewriter(m_writer);
5360 std::string output = ppi.parser.process(templ, current_filament_id, config_override, &ppi.output_config, &ppi.context);
5361 ppi.validate_output_vector_variables();
5362 const CustomGCodeMotionStateChanges motion_state_changes = custom_gcode_motion_state_changes(output);
5363 if (motion_state_changes.acceleration)
5364 m_writer.invalidate_acceleration();
5365 if (motion_state_changes.jerk)
5366 m_writer.invalidate_jerk();
5367
5368 if (const std::vector<double> &pos = ppi.opt_position->values; ppi.position != pos) {
5369 // Update G-code writer.
5370 m_writer.set_position({ pos[0], pos[1], pos[2] });
5371 this->set_last_pos(this->gcode_to_point({ pos[0], pos[1] }));
5372 }
5373
5374 for (const Extruder &e : m_writer.extruders()) {
5375 unsigned int eid = e.id();
5376 assert(eid < ppi.num_extruders);
5377 if ( eid < ppi.num_extruders) {
5378 if (! m_writer.config.use_relative_e_distances && ! is_approx(ppi.e_position[eid], ppi.opt_e_position->values[eid]))
5379 const_cast<Extruder&>(e).set_position(ppi.opt_e_position->values[eid]);
5380 if (! is_approx(ppi.e_retracted[eid], ppi.opt_e_retracted->values[eid]) ||
5381 ! is_approx(ppi.e_restart_extra[eid], ppi.opt_e_restart_extra->values[eid]))
5382 const_cast<Extruder&>(e).set_retracted(ppi.opt_e_retracted->values[eid], ppi.opt_e_restart_extra->values[eid]);
5383 }
5384 }
5385
5386 return output;
5387 }
5388 catch (std::runtime_error &err)
5389 {
5390 // Collect the names of failed template substitutions for error reporting.
5391 auto it = ppi.failed_templates.find(name);
5392 if (it == ppi.failed_templates.end())
5393 // Only if there was no error reported for this template, store the first error message into the map to be reported.
5394 // We don't want to collect error message for each and every occurence of a single custom G-code section.
5395 ppi.failed_templates.insert(it, std::make_pair(name, std::string(err.what())));
5396 // Insert the macro error message into the G-code.
5397 return
5398 std::string("\n!!!!! Failed to process the custom G-code template ") + name + "\n" +
5399 err.what() +
5400 "!!!!! End of an error report for the custom G-code template " + name + "\n\n";
5401 }
5402}
bool has(const t_config_option_key &opt_key) const
Definition Config.hpp:2661
Point gcode_to_point(const Vec2d &point) const
Definition GCode.cpp:11493
static CustomGCodeMotionStateChanges custom_gcode_motion_state_changes(const std::string &gcode)
Definition GCode.cpp:855
const std::map< t_custom_gcode_key, t_config_option_keys > & custom_gcode_specific_placeholders()
Definition PrintConfig.cpp:13669
constexpr bool is_approx(Number value, Number test_value, Number precision=EPSILON)
Definition libslic3r.h:320
const CustomGcodeSpecificConfigDef custom_gcode_specific_config_def
Definition PrintConfig.cpp:13806

References Slic3r::CustomGCodeMotionStateChanges::acceleration, Slic3r::GCode::PlaceholderParserIntegration::context, Slic3r::custom_gcode_motion_state_changes(), Slic3r::custom_gcode_specific_config_def, Slic3r::custom_gcode_specific_placeholders(), Slic3r::GCode::PlaceholderParserIntegration::e_position, Slic3r::GCode::PlaceholderParserIntegration::e_restart_extra, Slic3r::GCode::PlaceholderParserIntegration::e_retracted, Slic3r::GCode::PlaceholderParserIntegration::failed_templates, gcode_to_point(), Slic3r::is_approx(), Slic3r::CustomGCodeMotionStateChanges::jerk, Slic3r::DynamicConfig::keys(), m_placeholder_parser_integration, m_writer, Slic3r::GCode::PlaceholderParserIntegration::num_extruders, Slic3r::GCode::PlaceholderParserIntegration::opt_e_position, Slic3r::GCode::PlaceholderParserIntegration::opt_e_restart_extra, Slic3r::GCode::PlaceholderParserIntegration::opt_e_retracted, Slic3r::GCode::PlaceholderParserIntegration::opt_position, Slic3r::GCode::PlaceholderParserIntegration::output_config, Slic3r::GCode::PlaceholderParserIntegration::parser, Slic3r::GCode::PlaceholderParserIntegration::position, Slic3r::PlaceholderParser::process(), set_last_pos(), Slic3r::GCode::PlaceholderParserIntegration::update_from_gcodewriter(), Slic3r::GCode::PlaceholderParserIntegration::validate_output_vector_variables(), and Slic3r::ConfigOptionVector< double >::values.

Referenced by _do_export(), _extrude(), Slic3r::WipeTowerIntegration::append_tcr(), Slic3r::WipeTowerIntegration::append_tcr2(), Slic3r::ProcessLayer::emit_custom_gcode_per_print_z(), generate_timelapse_gcode(), process_belt_brim_layer(), process_layer(), process_layer_change_gcode(), and set_extruder().

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

◆ point_to_gcode() [1/2]

Vec2d Slic3r::GCode::point_to_gcode ( const Point & point) const
inherited
11480{
11482 return unscale(point) + m_origin - extruder_offset;
11483}

References EXTRUDER_CONFIG, Slic3r::extruder_offset(), m_origin, and Slic3r::unscale().

Referenced by _extrude(), capture_toolchange_position(), extrude_loop(), point_to_gcode_quantized(), point_to_gcode_quantized(), travel_to(), and Slic3r::Wipe::wipe().

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

◆ point_to_gcode() [2/2]

Vec3d Slic3r::GCode::point_to_gcode ( const Point3 & point) const
inherited
11486{
11488 Vec2d xy = unscale(point.to_point()) + m_origin - extruder_offset;
11489 return Vec3d(xy.x(), xy.y(), unscale_(point.z()));
11490}

References EXTRUDER_CONFIG, Slic3r::extruder_offset(), m_origin, Slic3r::Point3::to_point(), Slic3r::unscale(), and unscale_.

Here is the call graph for this function:

◆ point_to_gcode_quantized() [1/2]

Vec2d Slic3r::GCode::point_to_gcode_quantized ( const Point & point) const
inherited
11504{
11505 Vec2d p = this->point_to_gcode(point);
11506 const bool high_precision = m_writer.high_precision();
11507 return { GCodeFormatter::quantize_xyz(p.x(), high_precision), GCodeFormatter::quantize_xyz(p.y(), high_precision) };
11508}

References m_writer, point_to_gcode(), and Slic3r::GCodeFormatter::quantize_xyz().

Referenced by _extrude().

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

◆ point_to_gcode_quantized() [2/2]

Vec3d Slic3r::GCode::point_to_gcode_quantized ( const Point3 & point) const
inherited
11511{
11512 Vec3d p = this->point_to_gcode(point);
11513 const bool high_precision = m_writer.high_precision();
11514 return { GCodeFormatter::quantize_xyz(p.x(), high_precision), GCodeFormatter::quantize_xyz(p.y(), high_precision),
11515 GCodeFormatter::quantize_xyz(p.z(), high_precision) };
11516}

References m_writer, point_to_gcode(), and Slic3r::GCodeFormatter::quantize_xyz().

Here is the call graph for this function:

◆ preamble()

std::string Slic3r::GCode::preamble ( )
protectedinherited
8414{
8415 std::string gcode = m_writer.preamble();
8416
8417 /* Perform a *silent* move to z_offset: we need this to initialize the Z
8418 position of our writer object so that any initial lift taking place
8419 before the first layer change will raise the extruder from the correct
8420 initial Z instead of 0. */
8421 m_writer.travel_to_z(m_config.z_offset.value);
8422
8423 return gcode;
8424}

References m_config, and m_writer.

Referenced by _do_export().

Here is the caller graph for this function:

◆ print_machine_envelope()

void Slic3r::GCode::print_machine_envelope ( GCodeOutputStream & file,
Print & print )
protectedinherited
5408{
5409 const auto flavor = print.config().gcode_flavor.value;
5410 if ((flavor == gcfMarlinLegacy || flavor == gcfMarlinFirmware || flavor == gcfRepRapFirmware) &&
5411 print.config().emit_machine_limits_to_gcode.value == true) {
5412
5413 // Get all physical tool ids current print will use
5414 std::unordered_set<unsigned int> used_extruders;
5415 for (const auto& extruder : m_writer.extruders()) {
5416 used_extruders.insert(extruder.extruder_id());
5417 }
5418
5419 // Get the max limit value among used extruders
5420 auto get_max_value = [&used_extruders](const std::string key, const ConfigOptionFloats& v) {
5421 unsigned int stride = 1;
5422 if (printer_options_with_variant_2.count(key) > 0) {
5423 stride = 2;
5424 }
5425
5426 double value = std::numeric_limits<double>::lowest();
5427 for (unsigned int extruder : used_extruders) {
5428 value = std::max(value, v.values[extruder * stride]);
5429 }
5430
5431 assert(value > std::numeric_limits<double>::lowest());
5432 return value;
5433 };
5434#define MAX_LIMIT(OPT) get_max_value(#OPT, print.config().OPT)
5435
5436 int factor = flavor == gcfRepRapFirmware ? 60 : 1; // RRF M203 and M566 are in mm/min
5437 file.write_format("M201 X%d Y%d Z%d E%d\n",
5438 int(MAX_LIMIT(machine_max_acceleration_x) + 0.5),
5439 int(MAX_LIMIT(machine_max_acceleration_y) + 0.5),
5440 int(MAX_LIMIT(machine_max_acceleration_z) + 0.5),
5441 int(MAX_LIMIT(machine_max_acceleration_e) + 0.5));
5442 file.write_format("M203 X%d Y%d Z%d E%d\n",
5443 int(MAX_LIMIT(machine_max_speed_x) * factor + 0.5),
5444 int(MAX_LIMIT(machine_max_speed_y) * factor + 0.5),
5445 int(MAX_LIMIT(machine_max_speed_z) * factor + 0.5),
5446 int(MAX_LIMIT(machine_max_speed_e) * factor + 0.5));
5447
5448 // Now M204 - acceleration. This one is quite hairy thanks to how Marlin guys care about
5449 // Legacy Marlin should export travel acceleration the same as printing acceleration.
5450 // MarlinFirmware has the two separated.
5451 int travel_acc = flavor == gcfMarlinLegacy
5452 ? int(MAX_LIMIT(machine_max_acceleration_extruding) + 0.5)
5453 : int(MAX_LIMIT(machine_max_acceleration_travel) + 0.5);
5454 if (flavor == gcfRepRapFirmware)
5455 file.write_format("M204 P%d T%d ; sets acceleration (P, T), mm/sec^2\n",
5456 int(MAX_LIMIT(machine_max_acceleration_extruding) + 0.5),
5457 travel_acc);
5458 else if (flavor == gcfMarlinFirmware)
5459 // New Marlin uses M204 P[print] R[retract] T[travel]
5460 file.write_format("M204 P%d R%d T%d ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2\n",
5461 int(MAX_LIMIT(machine_max_acceleration_extruding) + 0.5),
5462 int(MAX_LIMIT(machine_max_acceleration_retracting) + 0.5),
5463 int(MAX_LIMIT(machine_max_acceleration_travel) + 0.5));
5464 else
5465 file.write_format("M204 P%d R%d T%d\n",
5466 int(MAX_LIMIT(machine_max_acceleration_extruding) + 0.5),
5467 int(MAX_LIMIT(machine_max_acceleration_retracting) + 0.5),
5468 travel_acc);
5469
5471 file.write_format(flavor == gcfRepRapFirmware
5472 ? "M566 X%.2lf Y%.2lf Z%.2lf E%.2lf ; sets the jerk limits, mm/min\n"
5473 : "M205 X%.2lf Y%.2lf Z%.2lf E%.2lf ; sets the jerk limits, mm/sec\n",
5474 MAX_LIMIT(machine_max_jerk_x) * factor,
5475 MAX_LIMIT(machine_max_jerk_y) * factor,
5476 MAX_LIMIT(machine_max_jerk_z) * factor,
5477 MAX_LIMIT(machine_max_jerk_e) * factor);
5478
5479 // New Marlin uses M205 J[mm] for junction deviation (only apply if it is > 0)
5480 file.write_format(writer().set_junction_deviation(MAX_LIMIT(machine_max_junction_deviation)).c_str());
5481
5482 // Orca: Override input shaping values
5483 if (print.config().input_shaping_emit.value && flavor != gcfMarlinLegacy) {
5484 const bool input_shaping_disable = print.config().input_shaping_type.value == InputShaperType::Disable;
5485 file.write_format(writer().set_input_shaping('X', print.config().input_shaping_damp_x.value,
5486 print.config().input_shaping_freq_x.value, print.config().opt_serialize("input_shaping_type")).c_str());
5487 if (flavor != gcfRepRapFirmware && !input_shaping_disable) {
5488 file.write_format(writer().set_input_shaping('Y', print.config().input_shaping_damp_y.value,
5489 print.config().input_shaping_freq_y.value, "").c_str());
5490 }
5491 }
5492 }
5493#undef MAX_LIMIT
5494}
#define MAX_LIMIT(OPT)
std::set< std::string > printer_options_with_variant_2
Definition PrintConfig.cpp:10270
@ Disable
Definition PrintConfig.hpp:578

References Slic3r::Print::config(), Slic3r::Disable, Slic3r::gcfMarlinFirmware, Slic3r::gcfMarlinLegacy, Slic3r::gcfRepRapFirmware, Slic3r::is_decimal_separator_point(), m_writer, MAX_LIMIT, Print, Slic3r::printer_options_with_variant_2, Slic3r::GCode::GCodeOutputStream::write_format(), and writer().

Referenced by _do_export().

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

◆ process_belt_brim_layer()

LayerResult Slic3r::GCode::process_belt_brim_layer ( const Print & print,
const std::vector< LayerToPrint > & layers,
const LayerTools & layer_tools,
const bool last_layer,
const size_t single_object_instance_idx,
const int most_used_extruder )
protectedinherited
6134{
6135 // layer_id 0 is deliberate, not a placeholder. CoolingBuffer reads it for the
6136 // initial_layer_fan_speed override and the close_fan_the_first_x_layers gate
6137 // (CoolingBuffer.cpp), and every apron band is first-layer material by the only
6138 // definition that means anything on a belt: it lies on the belt plane itself. Numbering
6139 // the bands 1, 2, 3... would ramp the fan up while still printing on the belt.
6140 // spiral_vase_enable false: spiral vase is refused alongside belt brim in
6141 // Print::validate(). cooling_buffer_flush true: an apron layer is a complete layer, and
6142 // the default (object_layer || raft_layer || last_layer) is false here, so fan and
6143 // slowdown would otherwise never be applied to it.
6144 LayerResult result { {}, 0, false, true };
6145 if (layer_tools.extruders.empty())
6146 // Nothing to extrude.
6147 return result;
6148
6149 coordf_t print_z = 0.;
6150 coordf_t height = 0.;
6151 for (const LayerToPrint &ltp : layers)
6152 if (ltp.belt_brim_band != nullptr) {
6153 print_z = ltp.belt_brim_band->print_z;
6154 height = ltp.belt_brim_band->height;
6155 break;
6156 }
6157
6158 // Apron bands precede object layer 0 and have no layer id of their own; they take the
6159 // filament and nozzle assignment in effect at the first object layer.
6160 m_cur_layer_idx = 0;
6161
6162 // Publish the band's Z for _extrude()'s first-layer-plane probe, and make sure
6163 // it cannot leak past this layer even if an extrusion throws.
6164 struct BeltBrimZGuard {
6165 std::optional<coordf_t> &slot;
6166 ~BeltBrimZGuard() { slot.reset(); }
6167 } z_guard { m_belt_brim_z };
6168 m_belt_brim_z = print_z;
6169 m_layer = nullptr;
6170
6171 std::string gcode;
6172 const unsigned int extruder_id = layer_tools.extruders.front();
6173 if (m_writer.filament() == nullptr || m_writer.filament()->id() != extruder_id)
6174 gcode += this->set_extruder(extruder_id, print_z);
6175
6176 // An apron band is a real printed layer: it is counted in m_layer_count, it advances
6177 // m_layer_index through change_layer(), and the G-code viewer needs its Z/height tags.
6178 // Keep the same caches and hooks the ordinary path maintains, or the first object layer
6179 // would compute its height against a stale pre-apron Z and layer-change templates would
6180 // skip these layers entirely.
6181 {
6182 char buf[64];
6184 sprintf(buf, ";Z:%g\n", print_z);
6185 gcode += buf;
6186 const float band_height = float(height);
6187 sprintf(buf, ";%s%g\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height).c_str(), band_height);
6188 gcode += buf;
6189 m_last_layer_z = float(print_z);
6191 m_last_height = band_height;
6192 }
6193
6194 if (! m_config.before_layer_change_gcode.value.empty()) {
6195 DynamicConfig config;
6196 config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index + 1));
6197 config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
6198 config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z));
6199 gcode += this->placeholder_parser_process("before_layer_change_gcode",
6200 print.config().before_layer_change_gcode.value, m_writer.filament()->id(), &config) + "\n";
6201 }
6202
6203 gcode += this->change_layer(print_z);
6204
6205 if (! m_config.layer_change_gcode.value.empty())
6206 gcode += this->process_layer_change_gcode(print, print_z, most_used_extruder, layer_tools.has_wipe_tower && m_wipe_tower);
6207
6208 // Objects sharing this apron Z may use different brim filaments; print each in its own tool.
6209 for (const unsigned int brim_extruder : layer_tools.extruders) {
6210 if (m_writer.filament() == nullptr || m_writer.filament()->id() != brim_extruder)
6211 gcode += this->set_extruder(brim_extruder, print_z);
6212 gcode += this->emit_belt_brim_bands(print, layers, single_object_instance_idx, brim_extruder);
6213 }
6214
6215 result.gcode = std::move(gcode);
6216 return result;
6217}
std::string process_layer_change_gcode(const Print &print, coordf_t print_z, int most_used_extruder, bool has_wipe_tower)
Definition GCode.cpp:6078
std::string emit_belt_brim_bands(const Print &print, const std::vector< LayerToPrint > &layers, const size_t single_object_instance_idx, const unsigned int extruder_id)
Definition GCode.cpp:6225
std::string change_layer(coordf_t print_z)
Definition GCode.cpp:8427

References change_layer(), config(), Slic3r::Print::config(), Slic3r::ConfigOptionFloat, emit_belt_brim_bands(), Slic3r::LayerTools::extruders, Slic3r::LayerResult::gcode, Slic3r::LayerTools::has_wipe_tower, Slic3r::GCodeProcessor::Height, Slic3r::GCodeProcessor::Layer_Change, m_belt_brim_z, m_config, m_cur_layer_idx, m_last_height, m_last_layer_z, m_layer, m_layer_index, m_max_layer_z, m_wipe_tower, m_writer, placeholder_parser_process(), Print, process_layer_change_gcode(), Slic3r::GCodeProcessor::reserved_tag(), and set_extruder().

Referenced by process_layer().

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

◆ process_layer()

LayerResult Slic3r::GCode::process_layer ( const Print & print,
const std::vector< LayerToPrint > & layers,
const LayerTools & layer_tools,
const bool last_layer,
const std::vector< const PrintInstance * > * ordering,
const int most_used_extruder,
const size_t single_object_idx = size_t(-1),
const bool prime_extruder = false )
protectedinherited
6572{
6573 assert(! layers.empty());
6574 // Either printing all copies of all objects, or just a single copy of a single object.
6575 assert(single_object_instance_idx == size_t(-1) || layers.size() == 1);
6576
6577 // First object, support and raft layer, if available.
6578 const Layer *object_layer = nullptr;
6579 const SupportLayer *support_layer = nullptr;
6580 const SupportLayer *raft_layer = nullptr;
6581 for (const LayerToPrint &l : layers) {
6582 if (l.object_layer && ! object_layer)
6583 object_layer = l.object_layer;
6584 if (l.support_layer) {
6585 if (! support_layer)
6586 support_layer = l.support_layer;
6587 if (! raft_layer && support_layer->id() < support_layer->object()->slicing_parameters().raft_layers())
6588 raft_layer = support_layer;
6589 }
6590 }
6591
6592 // Belt printers: a brim-only apron layer has neither an object nor a support
6593 // layer, so it must be handled before layer_ptr is dereferenced below.
6594 if (object_layer == nullptr && support_layer == nullptr &&
6595 std::any_of(layers.begin(), layers.end(),
6596 [](const LayerToPrint &l) { return l.belt_brim_band != nullptr; }))
6597 return this->process_belt_brim_layer(print, layers, layer_tools, last_layer, single_object_instance_idx, most_used_extruder);
6598
6599 const Layer* layer_ptr = nullptr;
6600 if (object_layer != nullptr)
6601 layer_ptr = object_layer;
6602 else if (support_layer != nullptr)
6603 layer_ptr = support_layer;
6604 const Layer& layer = *layer_ptr;
6605 m_cur_layer_idx = layer.id();
6606 // A per-layer nozzle grouping can move the active filament to another variant column on a
6607 // layer boundary without a toolchange, so re-resolve the writer's config column here.
6608 if (Extruder *cur_filament = m_writer.filament())
6609 cur_filament->set_config_index((int)get_filament_config_index((int)cur_filament->id()));
6610 LayerResult result { {}, layer.id(), false, last_layer };
6611 if (layer_tools.extruders.empty())
6612 // Nothing to extrude.
6613 return result;
6614
6615 // Extract 1st object_layer and support_layer of this set of layers with an equal print_z.
6616 coordf_t print_z = layer.print_z;
6617 //BBS: using layer id to judge whether the layer is first layer is wrong. Because if the normal
6618 //support is attached above the object, and support layers has independent layer height, then the lowest support
6619 //interface layer id is 0.
6620 bool first_layer = (layer.id() == 0 && abs(layer.bottom_z()) < EPSILON);
6621 m_writer.set_is_first_layer(first_layer);
6622 unsigned int first_extruder_id = layer_tools.extruders.front();
6623
6624 // Initialize config with the 1st object to be printed at this layer.
6625 m_config.apply(layer.object()->config(), true);
6626
6627 // Check whether it is possible to apply the spiral vase logic for this layer.
6628 // Just a reminder: A spiral vase mode is allowed for a single object, single material print only.
6630 if (m_spiral_vase && layers.size() == 1 && support_layer == nullptr) {
6631 bool enable = (layer.id() > 0 || !print.has_brim()) && (layer.id() >= (size_t)print.config().skirt_height.value && ! print.has_infinite_skirt());
6632 if (enable) {
6633 for (const LayerRegion *layer_region : layer.regions())
6634 if (size_t(layer_region->region().config().bottom_shell_layers.value) > layer.id() ||
6635 layer_region->perimeters.items_count() > 1u ||
6636 layer_region->fills.items_count() > 0) {
6637 enable = false;
6638 break;
6639 }
6640 }
6641 result.spiral_vase_enable = enable;
6642 // If we're going to apply spiralvase to this layer, disable loop clipping.
6643 m_enable_loop_clipping = !enable;
6644 }
6645
6646 std::string gcode;
6647 assert(is_decimal_separator_point()); // for the sprintfs
6648
6649 // add tag for processor
6651 // export layer z
6652 char buf[80];
6653 sprintf(buf, print.is_BBL_printer() ? "; Z_HEIGHT: %g\n" : ";Z:%g\n", print_z);
6654 gcode += buf;
6655 // export layer height
6656 float height = first_layer ? static_cast<float>(print_z) : static_cast<float>(print_z) - m_last_layer_z;
6657 sprintf(buf, ";%s%g\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height).c_str(), height);
6658 gcode += buf;
6659 // update caches
6660 m_last_layer_z = static_cast<float>(print_z);
6663
6664 // Set new layer - this will change Z and force a retraction if retract_when_changing_layer is enabled.
6665 if (! m_config.before_layer_change_gcode.value.empty()) {
6666 DynamicConfig config;
6667 config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index + 1));
6668 config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
6669 config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z));
6670 gcode += this->placeholder_parser_process("before_layer_change_gcode",
6671 print.config().before_layer_change_gcode.value, m_writer.filament()->id(), &config)
6672 + "\n";
6673 }
6674
6675 PrinterStructure printer_structure = m_config.printer_structure.value;
6676 bool is_i3_printer = printer_structure == PrinterStructure::psI3;
6677 bool is_multi_extruder = m_config.nozzle_diameter.size() > 1;
6678
6679 // Farthest-point timelapse enable-fold. Corexy-only: gated OFF for i3 (psI3 → A1/A2L), for
6680 // smooth timelapse, and whenever the toggle is unset. When false every downstream hook (compute,
6681 // _extrude inline photo, process_layer case gates, template ternary) is inert, so the whole shipping
6682 // fleet that does not set farthest_point_timelapse is byte-identical to the pre-existing behavior.
6683 m_farthest_point_timelapse.enabled = m_config.farthest_point_timelapse.value
6684 && m_config.timelapse_type.value == TimelapseType::tlTraditional
6685 && printer_structure != PrinterStructure::psI3;
6686 m_farthest_point_timelapse.most_used_extruder = most_used_extruder;
6687 m_farthest_point_timelapse.inserted_this_layer = false;
6688
6689 bool need_insert_timelapse_gcode_for_traditional = false;
6690 if ((!m_wipe_tower || !m_wipe_tower->enable_timelapse_print()) && (is_BBL_Printer() || !m_config.time_lapse_gcode.value.empty())) {
6691 need_insert_timelapse_gcode_for_traditional = ((is_i3_printer && !m_spiral_vase) || is_multi_extruder);
6692 }
6693
6694 bool has_insert_timelapse_gcode = false;
6695 bool has_wipe_tower = (layer_tools.has_wipe_tower && m_wipe_tower);
6696
6697
6698 ZHopType z_hope_type = ZHopType(FILAMENT_CONFIG(z_hop_types));
6699 LiftType auto_lift_type = LiftType::NormalLift;
6700 if (z_hope_type == ZHopType::zhtAuto || z_hope_type == ZHopType::zhtSpiral || z_hope_type == ZHopType::zhtSlope)
6701 auto_lift_type = LiftType::SpiralLift;
6702
6703 // BBS: don't use lazy_raise when enable spiral vase
6704 gcode += this->change_layer(print_z); // this will increase m_layer_index
6707 m_layer = &layer;
6709
6710 if (!need_insert_timelapse_gcode_for_traditional && !m_config.time_lapse_gcode.value.empty() && !is_BBL_Printer()) {
6711 const DynamicConfig config = timelapse_gcode_config(print, print_z, most_used_extruder, &m_printed_objects, false);
6712 gcode += this->placeholder_parser_process("time_lapse_gcode",
6713 print.config().time_lapse_gcode.value, m_writer.filament()->id(), &config)
6714 + "\n";
6715 }
6716
6717 if (!m_config.layer_change_gcode.value.empty())
6718 gcode += this->process_layer_change_gcode(print, print_z, most_used_extruder, has_wipe_tower);
6719
6720 //BBS: set layer time fan speed after layer change gcode
6721 gcode += ";_SET_FAN_SPEED_CHANGING_LAYER\n";
6722
6723 // Belt printers: ordinary-layer apron bands (a band whose print_z coincides with an
6724 // object/support layer, so it takes this path rather than the brim-only branch) are
6725 // NOT emitted here anymore. They used to be laid down with whatever tool happened to
6726 // be active; instead they are now emitted inside the extruder loop below, in their
6727 // own brim-filament pass and before that pass's object extrusion, so the brim goes
6728 // down first with the correct tool. See the emit_belt_brim_for_extruder call.
6729
6730 //Calibration Layer-specific GCode
6731 // ORCA-Belt: on belt printers the calibration object is counter-rotated to
6732 // stand upright in slicing space on top of a support wedge, so its first
6733 // layer starts above Z=0 (at its position along the belt) with support-only
6734 // layers below it. Reference the per-height calibration bands to the bottom
6735 // of the object so they keep their designed meaning; on regular printers
6736 // the object base is at Z=0 and calib_z == print_z.
6737 double calib_z = print_z;
6738 if (m_config.belt_printer.value && print.calib_mode() != CalibMode::Calib_None) {
6739 // Skip empty ghost layers the grid may produce below the object.
6740 for (const Layer* l : layer.object()->layers())
6741 if (!l->lslices.empty()) {
6742 calib_z = print_z - (l->print_z - l->height);
6743 break;
6744 }
6745 }
6746 switch (print.calib_mode()) {
6748 gcode += writer().set_pressure_advance(this->interpolate_value_across_layers(static_cast<float>(print.calib_params().start),
6749 static_cast<float>(print.calib_params().end),
6750 static_cast<float>(print.calib_params().step)));
6751 break;
6752 }
6754 // ORCA-Belt: the sectioned variant prints each temperature as its
6755 // own object in native belt orientation, with the temperature
6756 // encoded in the object name ("temp_230") — step per object
6757 // instead of ramping per layer band.
6758 int sectioned_temp = 0;
6759 if (m_config.belt_printer.value &&
6760 sscanf(layer.object()->model_object()->name.c_str(), "temp_%d", &sectioned_temp) == 1 &&
6761 sectioned_temp > 0) {
6762 gcode += writer().set_temperature(static_cast<unsigned int>(sectioned_temp));
6763 } else {
6764 gcode += writer().set_temperature(this->interpolate_value_across_layers(static_cast<float>(print.calib_params().start), static_cast<float>(print.calib_params().end), 5.0f));
6765 }
6766 break;
6767 }
6769 // Step the outer wall speed from start to end across the tower's layers. Plater::calib_VFA sizes the
6770 // geometry so each speed step spans one visual block (a fixed number of layers), so the layer-based
6771 // stepping stays aligned with the blocks regardless of nozzle size / layer height.
6772 float _speed = this->interpolate_value_across_layers(static_cast<float>(print.calib_params().start),
6773 static_cast<float>(print.calib_params().end),
6774 static_cast<float>(print.calib_params().step));
6775 m_calib_config.set_key_value("outer_wall_speed", new ConfigOptionFloatsNullable({std::round(_speed)}));
6776 break;
6777 }
6779 auto _speed = print.calib_params().start + std::max(0.0, calib_z) * print.calib_params().step;
6780 m_calib_config.set_key_value("outer_wall_speed", new ConfigOptionFloatsNullable({std::round(_speed)}));
6781 break;
6782 }
6784 auto _length = print.calib_params().start + std::floor(std::max(0.0,calib_z-0.4)) * print.calib_params().step;
6785 DynamicConfig _cfg;
6786 _cfg.set_key_value("retraction_length", new ConfigOptionFloats{_length});
6787 writer().config.apply(_cfg);
6788 sprintf(buf, "; Calib_Retraction_tower: Z_HEIGHT: %g, length:%g\n", calib_z, _length);
6789 gcode += buf;
6790 break;
6791 }
6793 if (m_layer_index == 1){
6794 gcode += writer().set_input_shaping('A', print.calib_params().start, 0.f, print.calib_params().shaper_type);
6795 } else {
6796 if (print.calib_params().freqStartX == print.calib_params().freqStartY && print.calib_params().freqEndX == print.calib_params().freqEndY) {
6797 gcode += writer().set_input_shaping('A', 0.f, this->interpolate_value_across_layers(print.calib_params().freqStartX, print.calib_params().freqEndX), "");
6798 } else {
6799 gcode += writer().set_input_shaping('X', 0.f, this->interpolate_value_across_layers(print.calib_params().freqStartX, print.calib_params().freqEndX), "");
6800 gcode += writer().set_input_shaping('Y', 0.f, this->interpolate_value_across_layers(print.calib_params().freqStartY, print.calib_params().freqEndY), "");
6801 }
6802 }
6803 break;
6804 }
6806 if (m_layer_index == 1){
6807 gcode += writer().set_input_shaping('X', 0.f, print.calib_params().freqStartX, print.calib_params().shaper_type);
6808 gcode += writer().set_input_shaping('Y', 0.f, print.calib_params().freqStartY, print.calib_params().shaper_type);
6809 } else {
6810 gcode += writer().set_input_shaping('A', this->interpolate_value_across_layers(print.calib_params().start, print.calib_params().end), 0.f, "");
6811 }
6812 break;
6813 }
6815 if (m_writer.get_gcode_flavor() == gcfMarlinFirmware &&
6816 !m_config.machine_max_junction_deviation.values.empty() &&
6817 m_config.machine_max_junction_deviation.values.front() > 0) {
6818 gcode += writer().set_junction_deviation(this->interpolate_value_across_layers(print.calib_params().start, print.calib_params().end));
6819 } else {
6820 gcode += writer().set_jerk_xy(this->interpolate_value_across_layers(print.calib_params().start, print.calib_params().end));
6821 }
6822 break;
6823 }
6824 }
6825
6826 //BBS
6827 if (first_layer) {
6828 // Orca: we don't need to optimize the Klipper as only set once
6829 if (NOZZLE_CONFIG(default_acceleration) > 0 && NOZZLE_CONFIG(initial_layer_acceleration) > 0) {
6830 gcode += m_writer.set_print_acceleration((unsigned int)floor(NOZZLE_CONFIG(initial_layer_acceleration) + 0.5));
6831 }
6832
6833 if (NOZZLE_CONFIG(default_jerk) > 0 && NOZZLE_CONFIG(initial_layer_jerk) > 0) {
6834 gcode += m_writer.set_jerk_xy(NOZZLE_CONFIG(initial_layer_jerk));
6835 }
6836
6837 if (m_writer.get_gcode_flavor() == gcfMarlinFirmware && NOZZLE_CONFIG(default_junction_deviation) > 0) {
6838 gcode += m_writer.set_junction_deviation(NOZZLE_CONFIG(default_junction_deviation));
6839 }
6840 }
6841
6842 // Belt printers: defer the temperature/PLR transition until the entire layer
6843 // is past the first-layer band above the belt. Elsewhere (non-belt printers,
6844 // support-only layers) the legacy `!first_layer` predicate applies, so
6845 // behavior is bit-identical to the pre-feature path.
6846 bool past_first_layer_band = !first_layer;
6847 if (int past = this->belt_layer_past_first_layer_band(object_layer); past >= 0)
6848 past_first_layer_band = past > 0;
6849
6850 if (past_first_layer_band && !m_second_layer_things_done) {
6851 // Orca: set power loss recovery
6852 const auto plr_mode = print.config().enable_power_loss_recovery.value;
6853 gcode += m_writer.enable_power_loss_recovery(plr_mode);
6854
6855 if (print.is_BBL_printer()) {
6856 // BBS: open first layer inspection at second layer
6857 if (print.config().scan_first_layer.value) {
6858 // BBS: retract first to avoid droping when scan model
6859 gcode += this->retract();
6860 gcode += "M976 S1 P1 ; scan model before printing 2nd layer\n";
6861 gcode += "M400 P100\n";
6862 gcode += this->unretract();
6863 }
6864 }
6865 // Reset acceleration at sencond layer
6866 // Orca: only set once, don't need to call set_accel_and_jerk
6867 if (NOZZLE_CONFIG(default_acceleration) > 0 && NOZZLE_CONFIG(initial_layer_acceleration) > 0) {
6868 gcode += m_writer.set_print_acceleration((unsigned int) floor(NOZZLE_CONFIG(default_acceleration) + 0.5));
6869 }
6870
6871 if (NOZZLE_CONFIG(default_jerk) > 0 && NOZZLE_CONFIG(initial_layer_jerk) > 0) {
6872 gcode += m_writer.set_jerk_xy(NOZZLE_CONFIG(default_jerk));
6873 }
6874
6875 // Transition from 1st to 2nd layer: set non-initial-layer nozzle temperatures.
6876 // IMEX parallel modes: secondary carriages (T1-T3) never receive tool-change commands,
6877 // so they're not in m_writer.extruders() and multiple_extruders==false (max id==0).
6878 // Use the static set_temperature path for IMEX so the T index is always emitted.
6879 // In primary mode, fall through to the standard path — tool changes handle temps normally.
6881 // All active tools need explicit temps — none receive tool-change commands,
6882 // so we can't rely on the condition used for non-IMEX (temp != initial_layer_temp).
6883 // A tool whose initial and regular temps are the same still needs to be set here.
6884 // Mutually exclusive with the `else` below, so a head skipped here gets no
6885 // transition at all. `tool_idx` is physical; the printing head uses this layer's
6886 // own filament, the parallel carriages resolve through the head map.
6887 // nozzle_temperature is indexed by column: bound in slot space, or an out-of-slot
6888 // logical reaches get_filament_config_index and comes back as filament 0.
6889 // filament_diameter is the slot-space yardstick -- one entry per filament slot,
6890 // never variant-expanded -- and is the bound the pressure advance loop uses too.
6891 const int num_filament_slots = (int) print.config().filament_diameter.values.size();
6892 // Bounds-checked, not get_at() -- see IMEXHelpers.hpp. A clamp would hand the
6893 // "initial" branch below to whichever secondary sits on pem[0], giving it the wrong
6894 // filament's transition temperature and never its own.
6895 const int initial_physical =
6896 ((int) first_extruder_id >= 0 &&
6897 (int) first_extruder_id < (int) m_config.physical_extruder_map.values.size())
6898 ? m_config.physical_extruder_map.values[(int) first_extruder_id]
6899 : -1;
6900 for (int tool_idx : get_imex_active_tools(print)) {
6901 const int logical = (tool_idx == initial_physical)
6902 ? (int)first_extruder_id
6904 m_imex_head_filament_map, m_config.physical_extruder_map, tool_idx);
6905 if (logical < 0 || logical >= num_filament_slots) continue;
6906 // Variant-expanded printers column each filament; index as the `else` does.
6907 int temperature = print.config().nozzle_temperature.get_at(get_filament_config_index(logical));
6908 if (temperature > 0)
6909 gcode += GCodeWriter::set_temperature(temperature, m_writer.config.gcode_flavor, false,
6910 tool_idx, "set IMEX tool temperature");
6911 }
6912 } else {
6913 // Adjust nozzle temperatures as prescribed by the nozzle dependent
6914 // nozzle_temperature_initial_layer vs. temperature settings.
6915 for (const Extruder& extruder : m_writer.extruders()) {
6916 if ((print.config().single_extruder_multi_material.value || m_ooze_prevention.enable) &&
6917 extruder.id() != m_writer.filament()->id())
6918 // In single extruder multi material mode, set the temperature for the current extruder only.
6919 continue;
6920 size_t fi = get_filament_config_index((int)extruder.id());
6921 int temperature = print.config().nozzle_temperature.get_at(fi);
6922 if (temperature > 0 && temperature != print.config().nozzle_temperature_initial_layer.get_at(fi))
6923 gcode += m_writer.set_temperature(temperature, false, extruder.id());
6924 }
6925 }
6926
6927 // BBS
6928 int bed_temp = 0;
6929 if (m_config.bed_temperature_formula == BedTempFormula::btfHighestTemp)
6930 bed_temp = get_highest_bed_temperature(false,print);
6931 else
6932 bed_temp = get_bed_temperature(first_extruder_id, false, m_config.curr_bed_type);
6933 gcode += m_writer.set_bed_temperature(bed_temp);
6934 // Mark the temperature transition from 1st to 2nd layer to be finished.
6936 }
6937
6938 if (single_object_instance_idx == size_t(-1)) {
6939 // Normal (non-sequential) print.
6940 gcode += ProcessLayer::emit_custom_gcode_per_print_z(*this, layer_tools.custom_gcode, m_writer.filament()->id(), first_extruder_id, print.config());
6941 }
6942
6943 // BBS: get next extruder according to flush and soluble
6944 auto get_next_extruder = [&](int current_extruder,const std::vector<unsigned int>&extruders) {
6945 std::vector<float> flush_matrix(cast<float>(get_flush_volumes_matrix(m_config.flush_volumes_matrix.values, 0, m_config.nozzle_diameter.values.size())));
6946 const unsigned int number_of_extruders = (unsigned int)(sqrt(flush_matrix.size()) + EPSILON);
6947 // Extract purging volumes for each extruder pair:
6948 std::vector<std::vector<float>> wipe_volumes;
6949 for (unsigned int i = 0; i < number_of_extruders; ++i)
6950 wipe_volumes.push_back(std::vector<float>(flush_matrix.begin() + i * number_of_extruders, flush_matrix.begin() + (i + 1) * number_of_extruders));
6951 unsigned int next_extruder = current_extruder;
6952 float min_flush = std::numeric_limits<float>::max();
6953 for (auto extruder_id : extruders) {
6954 if (print.config().filament_soluble.get_at(extruder_id) || extruder_id == current_extruder)
6955 continue;
6956 if (wipe_volumes[current_extruder][extruder_id] < min_flush) {
6957 next_extruder = extruder_id;
6958 min_flush = wipe_volumes[current_extruder][extruder_id];
6959 }
6960 }
6961 return next_extruder;
6962 };
6963
6964 const bool overhang_fan = overhang_fan_enabled(m_config, m_enable_cooling_markers);
6965 auto overhang_speed = [this](const LayerRegion &region) {
6966 return bool(region.region().config().enable_overhang_speed.get_at(get_nozzle_config_index(m_writer.filament()->id())));
6967 };
6968 for (const auto &layer_to_print : layers)
6969 if (layer_to_print.object_layer && prepares_overhang_estimator(*layer_to_print.object_layer, overhang_fan, overhang_speed))
6970 m_extrusion_quality_estimator.prepare_for_new_layer(layer_to_print.original_object, layer_to_print.object_layer);
6971
6972 // Group extrusions by an extruder, then by an object, an island and a region.
6973 std::map<unsigned int, std::vector<ObjectByExtruder>> by_extruder;
6974 // Farthest-point timelapse: per-support-layer/role → extruder map, consumed only by
6975 // compute_farthest_point (below, gated on m_farthest_point_timelapse.enabled). Populated alongside the
6976 // existing support-extruder assignment; unused (and thus output-neutral) when the subsystem is off.
6977 std::map<std::pair<const SupportLayer *, ExtrusionRole>, unsigned int> support_filaments;
6978 std::vector<std::unique_ptr<ExtrusionEntityCollection>> split_perimeter_storage;
6979 bool is_anything_overridden = const_cast<LayerTools&>(layer_tools).wiping_extrusions().is_anything_overridden();
6980 for (const LayerToPrint &layer_to_print : layers) {
6981 if (layer_to_print.support_layer != nullptr) {
6982 const SupportLayer &support_layer = *layer_to_print.support_layer;
6983 const PrintObject& object = *layer_to_print.original_object;
6984 if (! support_layer.support_fills.entities.empty()) {
6985 ExtrusionRole role = support_layer.support_fills.role();
6986 bool has_support = role == erMixed || role == erSupportMaterial || role == erSupportTransition;
6987 bool has_interface = role == erMixed || role == erSupportMaterialInterface;
6988 // Extruder ID of the support base. -1 if "don't care".
6989 unsigned int support_extruder = object.config().support_filament.value - 1;
6990 // Shall the support be printed with the active extruder, preferably with non-soluble, to avoid tool changes?
6991 bool support_dontcare = object.config().support_filament.value == 0;
6992 // Extruder ID of the support interface. -1 if "don't care".
6993 unsigned int interface_extruder = object.config().support_interface_filament.value - 1;
6994 // Shall the support interface be printed with the active extruder, preferably with non-soluble, to avoid tool changes?
6995 bool interface_dontcare = object.config().support_interface_filament.value == 0;
6996
6997 // BBS: apply wiping overridden extruders
6998 WipingExtrusions& wiping_extrusions = const_cast<LayerTools&>(layer_tools).wiping_extrusions();
6999 if (support_dontcare) {
7000 int extruder_override = wiping_extrusions.get_support_extruder_overrides(&object);
7001 if (extruder_override >= 0) {
7002 support_extruder = extruder_override;
7003 support_dontcare = false;
7004 }
7005 }
7006
7007 if (interface_dontcare) {
7008 int extruder_override = wiping_extrusions.get_support_interface_extruder_overrides(&object);
7009 if (extruder_override >= 0) {
7010 interface_extruder = extruder_override;
7011 interface_dontcare = false;
7012 }
7013 }
7014
7015 // BBS: try to print support base with a filament other than interface filament
7016 if (support_dontcare && !interface_dontcare) {
7017 unsigned int dontcare_extruder = first_extruder_id;
7018 for (unsigned int extruder_id : layer_tools.extruders) {
7019 if (print.config().filament_soluble.get_at(extruder_id))
7020 continue;
7021
7022 //BBS: now we don't consider interface filament used in other object
7023 if (extruder_id == interface_extruder)
7024 continue;
7025
7026 dontcare_extruder = extruder_id;
7027 break;
7028 }
7029 #if 0
7030 //BBS: not found a suitable extruder in current layer ,dontcare_extruider==first_extruder_id==interface_extruder
7031 if (dontcare_extruder == interface_extruder && (object.config().support_interface_not_for_body && object.config().support_interface_filament.value!=0)) {
7032 // BBS : get a suitable extruder from other layer
7033 auto all_extruders = print.extruders();
7034 dontcare_extruder = get_next_extruder(dontcare_extruder, all_extruders);
7035 }
7036 #endif
7037
7038 if (support_dontcare)
7039 support_extruder = dontcare_extruder;
7040 }
7041 else if (support_dontcare || interface_dontcare) {
7042 // Some support will be printed with "don't care" material, preferably non-soluble.
7043 // Is the current extruder assigned a soluble filament?
7044 unsigned int dontcare_extruder = first_extruder_id;
7045 if (print.config().filament_soluble.get_at(dontcare_extruder)) {
7046 // The last extruder printed on the previous layer extrudes soluble filament.
7047 // Try to find a non-soluble extruder on the same layer.
7048 for (unsigned int extruder_id : layer_tools.extruders)
7049 if (! print.config().filament_soluble.get_at(extruder_id)) {
7050 dontcare_extruder = extruder_id;
7051 break;
7052 }
7053 }
7054 if (print.config().filament_is_support.get_at(dontcare_extruder)) {
7055 // The last extruder printed on the previous layer extrudes support filament.
7056 // Try to find a non-support extruder on the same layer.
7057 for (unsigned int extruder_id : layer_tools.extruders)
7058 if (!print.config().filament_is_support.get_at(extruder_id)) {
7059 dontcare_extruder = extruder_id;
7060 break;
7061 }
7062 }
7063 if (support_dontcare)
7064 support_extruder = dontcare_extruder;
7065 if (interface_dontcare)
7066 interface_extruder = dontcare_extruder;
7067 }
7068 // Both the support and the support interface are printed with the same extruder, therefore
7069 // the interface may be interleaved with the support base.
7070 bool single_extruder = ! has_support || support_extruder == interface_extruder;
7071 // Farthest-point timelapse: record the extruder for each support role so
7072 // compute_farthest_point can attribute farthest support points correctly.
7073 if (has_support) {
7074 support_filaments[{ &support_layer, erSupportMaterial }] = support_extruder;
7075 support_filaments[{ &support_layer, erSupportTransition }] = support_extruder;
7076 }
7077 if (has_interface) {
7078 support_filaments[{ &support_layer, erSupportMaterialInterface }] =
7079 single_extruder ? (has_support ? support_extruder : interface_extruder) : interface_extruder;
7080 }
7081 // Assign an extruder to the base.
7082 ObjectByExtruder &obj = object_by_extruder(by_extruder, has_support ? support_extruder : interface_extruder, &layer_to_print - layers.data(), layers.size());
7083 obj.support = &support_layer.support_fills;
7084 obj.support_extrusion_role = single_extruder ? erMixed : erSupportMaterial;
7085 if (! single_extruder && has_interface) {
7086 ObjectByExtruder &obj_interface = object_by_extruder(by_extruder, interface_extruder, &layer_to_print - layers.data(), layers.size());
7087 obj_interface.support = &support_layer.support_fills;
7088 obj_interface.support_extrusion_role = erSupportMaterialInterface;
7089 }
7090 }
7091 }
7092
7093 if (layer_to_print.object_layer != nullptr) {
7094 const Layer &layer = *layer_to_print.object_layer;
7095 // We now define a strategy for building perimeters and fills. The separation
7096 // between regions doesn't matter in terms of printing order, as we follow
7097 // another logic instead:
7098 // - we group all extrusions by extruder so that we minimize toolchanges
7099 // - we start from the last used extruder
7100 // - for each extruder, we group extrusions by island
7101 // - for each island, we extrude perimeters first, unless user set the infill_first
7102 // option
7103 // (Still, we have to keep track of regions because we need to apply their config)
7104 size_t n_slices = layer.lslices.size();
7105 const std::vector<BoundingBox> &layer_surface_bboxes = layer.lslices_bboxes;
7106 // Traverse the slices in an increasing order of bounding box size, so that the islands inside another islands are tested first,
7107 // so we can just test a point inside ExPolygon::contour and we may skip testing the holes.
7108 std::vector<size_t> slices_test_order;
7109 slices_test_order.reserve(n_slices);
7110 for (size_t i = 0; i < n_slices; ++ i)
7111 slices_test_order.emplace_back(i);
7112 std::sort(slices_test_order.begin(), slices_test_order.end(), [&layer_surface_bboxes](size_t i, size_t j) {
7113 const Vec2d s1 = layer_surface_bboxes[i].size().cast<double>();
7114 const Vec2d s2 = layer_surface_bboxes[j].size().cast<double>();
7115 return s1.x() * s1.y() < s2.x() * s2.y();
7116 });
7117 auto point_inside_surface = [&layer, &layer_surface_bboxes](const size_t i, const Point &point) {
7118 const BoundingBox &bbox = layer_surface_bboxes[i];
7119 return point(0) >= bbox.min(0) && point(0) < bbox.max(0) &&
7120 point(1) >= bbox.min(1) && point(1) < bbox.max(1) &&
7121 layer.lslices[i].contour.contains(point);
7122 };
7123
7124 for (size_t region_id = 0; region_id < layer.regions().size(); ++ region_id) {
7125 const LayerRegion *layerm = layer.regions()[region_id];
7126 if (layerm == nullptr)
7127 continue;
7128 // PrintObjects own the PrintRegions, thus the pointer to PrintRegion would be unique to a PrintObject, they would not
7129 // identify the content of PrintRegion accross the whole print uniquely. Translate to a Print specific PrintRegion.
7130 const PrintRegion &region = print.get_print_region(layerm->region().print_region_id());
7131
7132 // Now we must process perimeters and infills and create islands of extrusions in by_region std::map.
7133 // It is also necessary to save which extrusions are part of MM wiping and which are not.
7134 // The process is almost the same for perimeters and infills - we will do it in a cycle that repeats twice:
7135 std::vector<unsigned int> printing_extruders;
7137 for (const ExtrusionEntity *ee : (entity_type == ObjectByExtruder::Island::Region::INFILL) ? layerm->fills.entities : layerm->perimeters.entities) {
7138 // extrusions represents infill or perimeter extrusions of a single island.
7139 assert(dynamic_cast<const ExtrusionEntityCollection*>(ee) != nullptr);
7140 const auto *extrusions = static_cast<const ExtrusionEntityCollection*>(ee);
7141 if (extrusions->entities.empty()) // This shouldn't happen but first_point() would fail.
7142 continue;
7143
7144 auto process_extrusions = [&](const ExtrusionEntityCollection *current_extrusions,
7145 const ExtrusionEntityCollection *overrides_key,
7146 bool use_overrides) {
7147 // This extrusion is part of certain Region, which tells us which extruder should be used for it.
7148 int correct_extruder_id = layer_tools.extruder(*current_extrusions, region);
7149
7150 const WipingExtrusions::ExtruderPerCopy *entity_overrides = nullptr;
7151 if (! layer_tools.has_extruder(correct_extruder_id)) {
7152 // A mixed-color slot is absent from layer_tools.extruders by design:
7153 // resolve_mixed_filaments() replaced it with its physical components,
7154 // and the sublayer block emits its geometry separately. Reassigning it
7155 // to the last extruder here would print it in the wrong colour, so only
7156 // fall back for genuinely stale (dontcare) extruders.
7157 if (!layer_tools.is_mixed_slot(correct_extruder_id)) {
7158 // this entity is not overridden, but its extruder is not in layer_tools - we'll print it
7159 // by last extruder on this layer (could happen e.g. when a wiping object is taller than others - dontcare extruders are eradicated from layer_tools)
7160 correct_extruder_id = layer_tools.extruders.back();
7161 }
7162 }
7163 printing_extruders.clear();
7164 if (is_anything_overridden && use_overrides) {
7165 entity_overrides = const_cast<LayerTools&>(layer_tools).wiping_extrusions().get_extruder_overrides(overrides_key, layer_to_print.original_object, correct_extruder_id, layer_to_print.object()->instances().size());
7166 if (entity_overrides == nullptr) {
7167 printing_extruders.emplace_back(correct_extruder_id);
7168 } else {
7169 printing_extruders.reserve(entity_overrides->size());
7170 for (int extruder : *entity_overrides)
7171 printing_extruders.emplace_back(extruder >= 0 ?
7172 // at least one copy is overridden to use this extruder
7173 extruder :
7174 // at least one copy would normally be printed with this extruder (see get_extruder_overrides function for explanation)
7175 static_cast<unsigned int>(- extruder - 1));
7176 Slic3r::sort_remove_duplicates(printing_extruders);
7177 }
7178 } else {
7179 printing_extruders.emplace_back(correct_extruder_id);
7180 }
7181
7182 // Now we must add this extrusion into the by_extruder map, once for each extruder that will print it.
7183 for (unsigned int extruder : printing_extruders) {
7184 std::vector<ObjectByExtruder::Island> &islands = object_islands_by_extruder(
7185 by_extruder,
7186 extruder,
7187 &layer_to_print - layers.data(),
7188 layers.size(), n_slices + 1);
7189 for (size_t i = 0; i <= n_slices; ++i) {
7190 bool last = i == n_slices;
7191 size_t island_idx = last ? n_slices : slices_test_order[i];
7192 if (last || point_inside_surface(island_idx, current_extrusions->first_point())) {
7193 if (islands[island_idx].by_region.empty())
7194 islands[island_idx].by_region.assign(print.num_print_regions(), ObjectByExtruder::Island::Region());
7195 islands[island_idx].by_region[region.print_region_id()].append(entity_type, current_extrusions, entity_overrides);
7196 break;
7197 }
7198 }
7199 }
7200 };
7201
7202 const auto is_inner = [](const ExtrusionEntity *entity) { return is_inner_wall(*entity); };
7203 const size_t inner_count = entity_type == ObjectByExtruder::Island::Region::PERIMETERS ?
7204 std::count_if(extrusions->entities.begin(), extrusions->entities.end(), is_inner) : 0;
7205 bool split_mixed_perimeters =
7206 region.config().outer_wall_filament_id.value != region.config().inner_wall_filament_id.value &&
7207 inner_count > 0 && inner_count < extrusions->entities.size();
7208
7209 if (split_mixed_perimeters) {
7210 auto outer_perimeters = std::make_unique<ExtrusionEntityCollection>();
7211 auto inner_perimeters = std::make_unique<ExtrusionEntityCollection>();
7212 for (const ExtrusionEntity *entity : extrusions->entities)
7213 (is_inner(entity) ? inner_perimeters : outer_perimeters)->append(*entity);
7214
7215 split_perimeter_storage.emplace_back(std::move(outer_perimeters));
7216 process_extrusions(split_perimeter_storage.back().get(), nullptr, false);
7217 split_perimeter_storage.emplace_back(std::move(inner_perimeters));
7218 process_extrusions(split_perimeter_storage.back().get(), nullptr, false);
7219 } else {
7220 process_extrusions(extrusions, extrusions, true);
7221 }
7222 }
7223 }
7224 } // for regions
7225 }
7226 } // for objects
7227
7228 // Farthest-point timelapse: once all this layer's extrusions are grouped, find the point
7229 // farthest from the camera. No-op when the subsystem is disabled.
7230 if (m_farthest_point_timelapse.enabled)
7231 compute_farthest_point(layers, most_used_extruder, support_filaments);
7232
7233 // Per filament: instances to print, and the visit sequence over them. Island-level ordering
7234 // may visit an instance more than once per layer; otherwise one visit per instance.
7235 std::map<unsigned int, std::pair<std::vector<InstanceToPrint>, std::vector<InstanceVisit>>> filament_to_print_instances;
7236 {
7237 // Order individual islands rather than whole instances. Off for by-object sequencing,
7238 // sequential printing, and the explicit AsObjectList order, which tour whole instances.
7239 const bool island_level_ordering = print.config().print_sequence != PrintSequence::ByObject &&
7240 single_object_instance_idx == size_t(-1) &&
7241 print.config().print_order != PrintOrder::AsObjectList;
7242 // A mixed-color slot is absent from layer_tools.extruders by design: resolve_mixed_filaments()
7243 // replaced it with its physical components. Its geometry is still keyed under the slot in
7244 // by_extruder though, and the sublayer emitter looks the plan up by slot id, so append the
7245 // slots here. Appending rather than merging leaves the flush-optimized order untouched.
7246 std::vector<unsigned int> plan_filaments = layer_tools.extruders;
7247 for (const auto &grp : layer_tools.mixed_sub_layer_groups)
7248 if (std::find(plan_filaments.begin(), plan_filaments.end(), grp.mixed_slot_0based) == plan_filaments.end())
7249 plan_filaments.push_back(grp.mixed_slot_0based);
7250
7251 for (unsigned int filament_id : plan_filaments) {
7252 auto objects_by_extruder_it = by_extruder.find(filament_id);
7253 if (objects_by_extruder_it == by_extruder.end()) continue;
7254
7255 auto &filament_plan = filament_to_print_instances[filament_id];
7256
7257 if (!island_level_ordering) {
7258 // One visit per instance, printing all of its islands.
7259 filament_plan.first = sort_print_object_instances(objects_by_extruder_it->second, layers, ordering, single_object_instance_idx);
7260 filament_plan.second.reserve(filament_plan.first.size());
7261 for (size_t i = 0; i < filament_plan.first.size(); ++i)
7262 filament_plan.second.push_back({i, {}, true});
7263 continue;
7264 }
7265
7266 int plate_idx = print.get_plate_index();
7267 Point wt_pos(print.config().wipe_tower_x.get_at(plate_idx), print.config().wipe_tower_y.get_at(plate_idx));
7268
7269 // Build the instances and one tour node per non-empty island (a single node for
7270 // instances without chainable islands). Positions quantized to 1 mm so small
7271 // centroid drift between layers still hits the tour cache below.
7272 std::vector<GCode::ObjectByExtruder> &objects_by_extruder = objects_by_extruder_it->second;
7273 std::vector<InstanceToPrint> &instances = filament_plan.first;
7274 std::vector<IslandOrderNode> nodes;
7275 std::vector<std::pair<size_t, bool>> layout; // Per instance, see IslandOrderCacheEntry
7276 std::vector<size_t> node_instances;
7277 auto quantize_to_mm = [](const Point &pt) -> Point {
7278 const coord_t grid = coord_t(scale_(1.));
7279 // Round to the nearest 1 mm symmetrically (integer division truncates toward
7280 // zero, which would make the bucket straddling the origin twice as wide).
7281 auto q = [grid](coord_t v) -> coord_t {
7282 return ((v >= 0 ? v + grid / 2 : v - grid / 2) / grid) * grid;
7283 };
7284 return Point(q(pt.x()), q(pt.y()));
7285 };
7286 for (ObjectByExtruder &object_by_extruder : objects_by_extruder) {
7287 if (object_by_extruder.islands.empty() && (object_by_extruder.support == nullptr || object_by_extruder.support->empty())) continue;
7288
7289 const size_t layer_id = &object_by_extruder - objects_by_extruder.data();
7290 const PrintObject *print_object = layers[layer_id].original_object;
7291 if (print_object == nullptr)
7292 continue;
7293 const Layer *obj_layer = layers[layer_id].object_layer;
7294 std::vector<ObjectByExtruder::Island> &islands = object_by_extruder.islands;
7295 const bool islands_chainable = obj_layer != nullptr && islands.size() == obj_layer->lslices.size() + 1;
7296 for (size_t instance_id = 0; instance_id < print_object->instances().size(); ++instance_id) {
7297 const size_t instance_idx = instances.size();
7298 instances.emplace_back(object_by_extruder, layer_id, *print_object, instance_id,
7299 print_object->instances()[instance_id].model_instance->get_labeled_id());
7300 layout.emplace_back(islands.size(), ! islands.empty() && ! islands.back().by_region.empty());
7301 const Point &shift = print_object->instances()[instance_id].shift;
7302 const size_t first_node = nodes.size();
7303 if (islands_chainable)
7304 for (size_t i = 0; i + 1 < islands.size(); ++i)
7305 if (!islands[i].by_region.empty()) {
7306 nodes.push_back({print_object->id(), instance_id, i,
7307 quantize_to_mm(obj_layer->lslices[i].contour.centroid() + shift)});
7308 node_instances.emplace_back(instance_idx);
7309 }
7310 if (nodes.size() == first_node) {
7311 // No chainable islands: tour the whole instance as one stop.
7312 nodes.push_back({print_object->id(), instance_id, size_t(-1), quantize_to_mm(shift)});
7313 node_instances.emplace_back(instance_idx);
7314 }
7315 }
7316 }
7317
7318 // Reuse the cached tour while this filament's island layout is unchanged.
7319 auto &cache_entry = m_ordering_cache[filament_id];
7320 if (! (cache_entry.nodes == nodes && cache_entry.layout == layout)) {
7321 cache_entry.nodes = nodes;
7322 cache_entry.layout = layout;
7323 Points node_points;
7324 node_points.reserve(nodes.size());
7325 for (const IslandOrderNode &node : nodes)
7326 node_points.emplace_back(node.pos);
7327 std::vector<size_t> tour = order_points_with_strategy(node_points, print.config().print_order, &wt_pos);
7328 // Chained starting near the wipe tower, reversed so the layer ends near it.
7329 std::reverse(tour.begin(), tour.end());
7330
7331 // Group consecutive tour stops of the same instance into visits.
7332 std::vector<InstanceVisit> visits;
7333 std::vector<bool> instance_seen(instances.size(), false);
7334 std::vector<int> last_visit_of_instance(instances.size(), -1);
7335 for (size_t node_idx : tour) {
7336 const size_t instance_idx = node_instances[node_idx];
7337 if (visits.empty() || visits.back().instance_idx != instance_idx) {
7338 visits.push_back({instance_idx, {}, !instance_seen[instance_idx]});
7339 instance_seen[instance_idx] = true;
7340 }
7341 if (nodes[node_idx].island_idx != size_t(-1))
7342 visits.back().islands.emplace_back(nodes[node_idx].island_idx);
7343 last_visit_of_instance[instance_idx] = int(visits.size()) - 1;
7344 }
7345 // The trailing catch-all island has no geometry to chain by; append it to the
7346 // instance's last visit.
7347 for (size_t i = 0; i < instances.size(); ++i) {
7348 if (last_visit_of_instance[i] < 0)
7349 continue;
7350 InstanceVisit &last_visit = visits[size_t(last_visit_of_instance[i])];
7351 if (last_visit.islands.empty())
7352 // A visit without explicit islands already prints everything.
7353 continue;
7354 std::vector<ObjectByExtruder::Island> &islands = instances[i].object_by_extruder.islands;
7355 if (! islands.empty() && ! islands.back().by_region.empty())
7356 last_visit.islands.emplace_back(islands.size() - 1);
7357 }
7358 cache_entry.visits = std::move(visits);
7359 }
7360 filament_plan.second = cache_entry.visits;
7361 }
7362 }
7363
7364 std::set<size_t> layer_object_label_ids;
7365 for (auto iter = filament_to_print_instances.begin(); iter != filament_to_print_instances.end(); ++iter) {
7366 for (const InstanceToPrint &instance : iter->second.first) {
7367 layer_object_label_ids.insert(instance.label_object_id);
7368 }
7369 }
7370
7371 m_farthest_point_timelapse.layer_object_label_ids = layer_object_label_ids;
7372
7373 // skip_pos_pick: when true the head takes an inline photo at its current spot instead of moving to a
7374 // picked safe position. Delegates to the extracted member so the per-extrusion farthest-point hook in
7375 // _extrude() shares the exact same generation path. Every existing call site uses the default (false).
7376 auto insert_timelapse_gcode = [this, print_z, &print, &most_used_extruder, &layer_object_label_ids,&printed_objects = std::as_const(m_printed_objects)](bool skip_pos_pick = false) -> std::string {
7377 return generate_timelapse_gcode(print, print_z, most_used_extruder, &layer_object_label_ids, &printed_objects, skip_pos_pick);
7378 };
7379
7380 // With farthest-point-is-photo-head active, the snapshot is deferred to the inline _extrude
7381 // hook / layer-end fallback, so skip the layer-start placement here. The extra clause is inert (true)
7382 // whenever the subsystem is off → pre-existing behavior byte-for-byte.
7383 if (!need_insert_timelapse_gcode_for_traditional && is_BBL_Printer()
7384 && !(m_farthest_point_timelapse.enabled && m_farthest_point_timelapse.farthest_is_photo_head)) { // Equivalent to the timelapse gcode placed in layer_change_gcode
7386 gcode += this->retract(false, false, auto_lift_type, true);
7387 }
7388 gcode += insert_timelapse_gcode();
7389 }
7390
7391 if (m_wipe_tower)
7392 m_wipe_tower->set_is_first_print(true);
7393
7394 auto insert_wrapping_detection_gcode = [this, &print, &print_z, &most_used_extruder]() -> std::string {
7395 std::string wrapping_gcode;
7396 if (print.config().enable_wrapping_detection && !print.config().wrapping_detection_gcode.value.empty()) {
7397 DynamicConfig config;
7398 config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
7399 config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
7400 config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z));
7401 config.set_key_value("most_used_physical_extruder_id", new ConfigOptionInt(m_config.physical_extruder_map.get_at(most_used_extruder)));
7402 config.set_key_value("curr_physical_extruder_id", new ConfigOptionInt(m_config.physical_extruder_map.get_at(m_writer.filament()->extruder_id())));
7403 wrapping_gcode = this->placeholder_parser_process("wrapping_detection_gcode", print.config().wrapping_detection_gcode.value, m_writer.filament()->id(), &config) +"\n";
7404 }
7405 m_writer.set_current_position_clear(false);
7406
7407 double temp_z_after_tool_change;
7408 if (GCodeProcessor::get_last_z_from_gcode(wrapping_gcode, temp_z_after_tool_change)) {
7409 Vec3d pos = m_writer.get_position();
7410 pos(2) = temp_z_after_tool_change;
7411 m_writer.set_position(pos);
7412 }
7413 return wrapping_gcode;
7414 };
7415
7416 bool has_insert_wrapping_detection_gcode = false;
7417
7418 // Extrude the skirt, brim, support, perimeters, infill ordered by the extruders.
7419 m_skirt_group_done.resize(print.skirt_brim_groups().size());
7420
7421 // Belt brim bookkeeping. A coincident belt_brim_by_layer band must be emitted
7422 // exactly once, in its object's brim-filament pass; this records which have gone
7423 // down so the in-visit emit and the end-of-layer orphan sweep never double it.
7424 // Key = (LayerToPrint index, instance_id).
7425 std::set<std::pair<size_t, size_t>> belt_brim_emitted;
7426
7427 // Emit every ORDINARY-layer apron band (belt_brim_prologue band coinciding with an
7428 // object/support layer) whose brim filament is this pass's extruder, so each band
7429 // prints in the correct tool's pass (Finding B). extruder_id is 0-based (the
7430 // reindexed tool domain).
7431 auto emit_belt_brim_for_extruder = [this, &print, &layers, single_object_instance_idx](unsigned int extruder_id) -> std::string {
7432 return this->emit_belt_brim_bands(print, layers, single_object_instance_idx, extruder_id);
7433 };
7434
7435 for (unsigned int extruder_id : layer_tools.extruders)
7436 {
7437 if (print.config().skirt_type == stCombined && !print.skirt_brim_groups().empty()) {
7438 for (size_t group_idx = 0; group_idx < print.skirt_brim_groups().size(); ++group_idx) {
7439 const Print::SkirtBrimGroup& group = print.skirt_brim_groups()[group_idx];
7440 if (group.skirt.empty())
7441 continue;
7442
7443 std::string skirt_gcode = generate_skirt(print, group.skirt, Point(0, 0), layer.object()->config().skirt_start_angle,
7444 layer_tools, layer, extruder_id, m_skirt_group_done[group_idx]);
7445 gcode += std::move(skirt_gcode);
7446 }
7447 }
7448
7449 if (print.config().print_sequence == PrintSequence::ByLayer && m_enable_exclude_object && print.config().support_object_skip_flush.value) {
7450 std::set<size_t> all_label_ids;
7451 for (InstanceToPrint &instance : filament_to_print_instances[extruder_id].first)
7452 all_label_ids.insert(instance.label_object_id);
7453 // This extruder may also be printing sub-layers on behalf of a mixed slot, whose
7454 // instances live under the slot id. Their labels belong in the same skip set, or
7455 // exclude-object would not skip that geometry.
7456 for (const auto &grp : layer_tools.mixed_sub_layer_groups)
7457 for (unsigned int comp : grp.components_0based)
7458 if (comp == extruder_id) {
7459 auto mit = filament_to_print_instances.find(grp.mixed_slot_0based);
7460 if (mit != filament_to_print_instances.end())
7461 for (const InstanceToPrint &inst : mit->second.first)
7462 all_label_ids.insert(inst.label_object_id);
7463 break;
7464 }
7465 // Orca: A scheduled extruder may have no object instances on this layer.
7466 // Clear any pending mask so it cannot be emitted for the wrong toolchange.
7468 if (!all_label_ids.empty()) {
7469 std::vector<size_t> filament_instances_id(all_label_ids.begin(), all_label_ids.end());
7471 }
7472 }
7473
7474 // The inline _extrude hook may already have taken the snapshot mid-extrusion on a
7475 // previous extruder; sync so we don't insert it a second time.
7476 has_insert_timelapse_gcode |= m_farthest_point_timelapse.inserted_this_layer;
7477
7478 std::string gcode_toolchange;
7479 if (has_wipe_tower) {
7480 if (!m_wipe_tower->is_empty_wipe_tower_gcode(*this, extruder_id, extruder_id == layer_tools.extruders.back())) {
7481 if (need_insert_timelapse_gcode_for_traditional && !has_insert_timelapse_gcode
7482 && !(m_farthest_point_timelapse.enabled && m_farthest_point_timelapse.farthest_is_photo_head)) {
7483 bool should_insert = true;
7484 if (m_config.nozzle_diameter.values.size() == 2){
7485 bool curr_is_photo_head = writer().filament() &&
7486 get_extruder_id(writer().filament()->id()) == most_used_extruder;
7487 // Case B (farthest point printed by a non-photo head): the firmware
7488 // snapshots at the picked safe pos, so insert when leaving the photo head instead.
7489 // is_case_b is false whenever the subsystem is off → should_insert == pre-existing value.
7490 bool is_case_b = m_farthest_point_timelapse.enabled && !m_farthest_point_timelapse.farthest_is_photo_head;
7491 should_insert = is_case_b ? !curr_is_photo_head : curr_is_photo_head;
7492 }
7493
7494 if (should_insert) {
7495 gcode += this->retract(false, false, auto_lift_type, true);
7496 m_writer.add_object_change_labels(gcode);
7497
7498 gcode += insert_timelapse_gcode();
7499 has_insert_timelapse_gcode = true;
7500 }
7501 }
7502
7503 if (print.config().enable_wrapping_detection && !has_insert_wrapping_detection_gcode) {
7504 gcode += this->retract(false, false, auto_lift_type, true);
7505 gcode += insert_wrapping_detection_gcode();
7506 has_insert_wrapping_detection_gcode = true;
7507 }
7508 gcode_toolchange = m_wipe_tower->tool_change(*this, extruder_id, extruder_id == layer_tools.extruders.back());
7509 }
7510 } else {
7511 // Same case-A/case-B split for the non-wipe-tower path. With the subsystem off,
7512 // is_case_b is false and should_insert == (curr == most_used) = pre-existing behavior.
7513 if (need_insert_timelapse_gcode_for_traditional &&
7514 !has_insert_timelapse_gcode &&
7515 !(m_farthest_point_timelapse.enabled && m_farthest_point_timelapse.farthest_is_photo_head) &&
7516 m_writer.need_toolchange(extruder_id) &&
7517 m_config.nozzle_diameter.values.size() == 2 &&
7518 writer().filament()) {
7519 bool curr_is_photo_head = get_extruder_id(writer().filament()->id()) == most_used_extruder;
7520 bool is_case_b = m_farthest_point_timelapse.enabled && !m_farthest_point_timelapse.farthest_is_photo_head;
7521 bool should_insert = is_case_b ? !curr_is_photo_head : curr_is_photo_head;
7522 if (should_insert) {
7523 gcode += this->retract(false, false, auto_lift_type, true);
7524 m_writer.add_object_change_labels(gcode);
7525
7526 gcode += insert_timelapse_gcode();
7527 has_insert_timelapse_gcode = true;
7528 }
7529 }
7530
7531 if (print.config().enable_wrapping_detection && !has_insert_wrapping_detection_gcode) {
7532 gcode += this->retract(false, false, auto_lift_type, true);
7533 gcode += insert_wrapping_detection_gcode();
7534 has_insert_wrapping_detection_gcode = true;
7535 }
7536
7537 gcode_toolchange = this->set_extruder(extruder_id, print_z);
7538 }
7539
7540 if (!gcode_toolchange.empty()) {
7541 // Disable vase mode for layers that has toolchange
7542 result.spiral_vase_enable = false;
7543 }
7544
7545 gcode += std::move(gcode_toolchange);
7546
7547 // let analyzer tag generator aware of a role type change
7548 if (layer_tools.has_wipe_tower && m_wipe_tower)
7550
7551 // Belt printers: now that this pass's tool is selected, lay down any ordinary-layer
7552 // apron band whose brim filament is this extruder, before the object extrusion at
7553 // this Z (brim goes down first, with the correct tool). Restore the origin so the
7554 // object-setup code below is unaffected.
7555 if (print.has_belt_brim()) {
7556 const Vec2d saved_origin = m_origin;
7557 gcode += emit_belt_brim_for_extruder(extruder_id);
7558 this->set_origin(saved_origin);
7559 }
7560
7561 auto &filament_plan = filament_to_print_instances[extruder_id];
7562 std::vector<InstanceToPrint> &instances_to_print = filament_plan.first;
7563 const std::vector<InstanceVisit> &instance_visits = filament_plan.second;
7564
7565 // We are almost ready to print. However, we must go through all the objects twice to print the overridden extrusions first (infill/perimeter wiping feature):
7566 std::vector<ObjectByExtruder::Island::Region> by_region_per_copy_cache;
7567 for (int print_wipe_extrusions = is_anything_overridden; print_wipe_extrusions>=0; --print_wipe_extrusions) {
7568 if (is_anything_overridden && print_wipe_extrusions == 0)
7569 gcode+="; PURGING FINISHED\n";
7570
7571 for (const InstanceVisit &visit : instance_visits) {
7572 InstanceToPrint &instance_to_print = instances_to_print[visit.instance_idx];
7573 const auto& inst = instance_to_print.print_object.instances()[instance_to_print.instance_id];
7574 const LayerToPrint &layer_to_print = layers[instance_to_print.layer_id];
7575 if (visit.first_visit && print_wipe_extrusions == (is_anything_overridden ? 1 : 0)) {
7576 gcode += generate_object_skirt_group(print, instance_to_print.print_object, instance_to_print.instance_id, layer_tools, layer, extruder_id);
7577 const PrintObject &vobj = instance_to_print.print_object;
7578 if (vobj.has_belt_brim()) {
7579 // Coincident belt brim: emit once, only in this object's brim-filament
7580 // pass (extruder_id and belt_brim_filament()-1 are both 0-based here),
7581 // and dedup on the LayerToPrint index (not Layer::id()) so the orphan
7582 // sweep below never re-emits it.
7583 if (extruder_id == (unsigned int)(vobj.belt_brim_filament() - 1) &&
7584 belt_brim_emitted.insert({ instance_to_print.layer_id, instance_to_print.instance_id }).second)
7585 gcode += generate_object_brim(print, vobj, instance_to_print.instance_id, first_layer, extruder_id,
7586 layer_to_print.object_layer);
7587 } else {
7588 gcode += generate_object_brim(print, vobj, instance_to_print.instance_id, first_layer, extruder_id,
7589 layer_to_print.object_layer);
7590 }
7591 }
7592
7593 // To control print speed of the 1st object layer printed over raft interface.
7594 bool object_layer_over_raft = layer_to_print.object_layer && layer_to_print.object_layer->id() > 0 &&
7595 instance_to_print.print_object.slicing_parameters().raft_layers() == layer_to_print.object_layer->id();
7596 m_config.apply(print.default_region_config());
7597 m_config.apply(instance_to_print.print_object.config(), true);
7598 m_layer = layer_to_print.layer();
7600 if (m_config.reduce_crossing_wall)
7602
7603 if (this->config().gcode_label_objects) {
7604 gcode += std::string("; printing object ") + instance_to_print.print_object.model_object()->name +
7605 " id:" + std::to_string(instance_to_print.print_object.get_id()) + " copy " +
7606 std::to_string(inst.id) + "\n";
7607 }
7608 // exclude objects
7610 if (is_BBL_Printer()) {
7611 m_writer.set_object_start_str(
7612 std::string("; start printing object, unique label id: ") +
7613 std::to_string(instance_to_print.label_object_id) + "\n" + "M624 " +
7614 _encode_label_ids_to_base64({instance_to_print.label_object_id}) + "\n");
7615 } else {
7616 const auto gflavor = print.config().gcode_flavor.value;
7617 if (gflavor == gcfKlipper) {
7618 m_writer.set_object_start_str(std::string("EXCLUDE_OBJECT_START NAME=") +
7619 instance_name(inst) + "\n");
7620 }
7621 else if (gflavor == gcfMarlinLegacy || gflavor == gcfMarlinFirmware || gflavor == gcfRepRapFirmware) {
7622 std::string str = std::string("M486 S") + std::to_string(inst.unique_id) + "\n";
7623 m_writer.set_object_start_str(str);
7624 }
7625 }
7626 }
7627
7628 // Orca(#7946): set current obj regardless of the `enable_overhang_speed` value, because
7629 // `enable_overhang_speed` is a PrintRegionConfig and here we don't have a region yet.
7630 // And no side effect doing this even if `enable_overhang_speed` is off, so don't bother
7631 // checking anything here.
7632 m_extrusion_quality_estimator.set_current_object(&instance_to_print.print_object);
7633
7634 // When starting a new object, use the external motion planner for the first travel move.
7635 const Point &offset = instance_to_print.print_object.instances()[instance_to_print.instance_id].shift;
7636 std::pair<const PrintObject*, Point> this_object_copy(&instance_to_print.print_object, offset);
7637 if (m_last_obj_copy != this_object_copy)
7638 m_avoid_crossing_perimeters.use_external_mp_once();
7639 m_last_obj_copy = this_object_copy;
7640 this->set_origin(unscale(offset));
7641 this->on_set_origin(&instance_to_print.print_object, offset);
7642 if (visit.first_visit && instance_to_print.object_by_extruder.support != nullptr) {
7643 m_layer = layers[instance_to_print.layer_id].support_layer;
7645
7646 // When starting a new object, use the external motion planner for the first travel move.
7647 const Point& offset = instance_to_print.print_object.instances()[instance_to_print.instance_id].shift;
7648 std::pair<const PrintObject*, Point> this_object_copy(&instance_to_print.print_object, offset);
7649 if (m_last_obj_copy != this_object_copy)
7650 m_avoid_crossing_perimeters.use_external_mp_once();
7651 m_last_obj_copy = this_object_copy;
7652 this->set_origin(unscale(offset));
7653 this->on_set_origin(&instance_to_print.print_object, offset);
7654 ExtrusionEntityCollection support_eec;
7655
7656 // BBS
7657 WipingExtrusions& wiping_extrusions = const_cast<LayerTools&>(layer_tools).wiping_extrusions();
7658 bool support_overridden = wiping_extrusions.is_support_overridden(layer_to_print.original_object);
7659 bool support_intf_overridden = wiping_extrusions.is_support_interface_overridden(layer_to_print.original_object);
7660
7661 ExtrusionRole support_extrusion_role = instance_to_print.object_by_extruder.support_extrusion_role;
7662 bool is_overridden = support_extrusion_role == erSupportMaterialInterface ? support_intf_overridden : support_overridden;
7663 if (is_overridden == (print_wipe_extrusions != 0)) {
7664 gcode += this->extrude_support(
7665 // support_extrusion_role is erSupportMaterial, erSupportTransition, erSupportMaterialInterface or erMixed for all extrusion paths.
7666 *instance_to_print.object_by_extruder.support, support_extrusion_role);
7667
7668 // Make sure ironing is the last
7669 if (support_extrusion_role == erMixed || support_extrusion_role == erSupportMaterialInterface) {
7670 gcode += this->extrude_support(*instance_to_print.object_by_extruder.support, erIroning);
7671 }
7672 }
7673
7674 m_layer = layer_to_print.layer();
7676 }
7677 // Sequential tool path ordering of multiple parts within the same object, aka. perimeter tracking (#5511)
7678 // Island print order. Use the islands the tour assigned to this visit; if none,
7679 // chain all islands nearest-neighbor from the current nozzle position (last_pos(),
7680 // in this instance's frame after set_origin() above). Empty islands are skipped;
7681 // the trailing catch-all island has no centroid to chain by and always goes last.
7682 std::vector<ObjectByExtruder::Island> &islands = instance_to_print.object_by_extruder.islands;
7683 std::vector<size_t> island_order;
7684 island_order.reserve(visit.islands.size());
7685 for (size_t idx : visit.islands) // Never index past the islands (see IslandOrderCacheEntry)
7686 if (idx < islands.size())
7687 island_order.emplace_back(idx);
7688 else
7689 BOOST_LOG_TRIVIAL(error) << "island tour refers to island " << idx << " of " << islands.size() << ", skipped";
7690 if (island_order.empty()) {
7691 island_order.reserve(islands.size());
7692 if (layer_to_print.object_layer != nullptr && islands.size() == layer_to_print.object_layer->lslices.size() + 1) {
7693 for (size_t i = 0; i + 1 < islands.size(); ++i)
7694 if (!islands[i].by_region.empty())
7695 island_order.emplace_back(i);
7696 if (island_order.size() > 1) {
7697 Points island_centroids;
7698 island_centroids.reserve(island_order.size());
7699 for (size_t i : island_order)
7700 island_centroids.emplace_back(layer_to_print.object_layer->lslices[i].contour.centroid());
7701 const Point start_near = this->last_pos();
7702 std::vector<size_t> chain = chain_points(island_centroids, this->last_pos_defined() ? &start_near : nullptr);
7703 std::vector<size_t> ordered;
7704 ordered.reserve(island_order.size());
7705 for (size_t k : chain)
7706 ordered.emplace_back(island_order[k]);
7707 island_order = std::move(ordered);
7708 }
7709 if (!islands.back().by_region.empty())
7710 island_order.emplace_back(islands.size() - 1);
7711 } else {
7712 // Unexpected islands layout, keep the stored order.
7713 for (size_t i = 0; i < islands.size(); ++i)
7714 island_order.emplace_back(i);
7715 }
7716 }
7717 for (size_t island_idx : island_order) {
7718 ObjectByExtruder::Island &island = islands[island_idx];
7719 const auto& by_region_specific = is_anything_overridden ? island.by_region_per_copy(by_region_per_copy_cache, static_cast<unsigned int>(instance_to_print.instance_id), extruder_id, print_wipe_extrusions != 0) : island.by_region;
7720 // When starting a new object, use the external motion planner for the first travel move.
7721 const Point& offset = instance_to_print.print_object.instances()[instance_to_print.instance_id].shift;
7722 std::pair<const PrintObject*, Point> this_object_copy(&instance_to_print.print_object, offset);
7723 if (m_last_obj_copy != this_object_copy)
7724 m_avoid_crossing_perimeters.use_external_mp_once();
7725 m_last_obj_copy = this_object_copy;
7726 this->set_origin(unscale(offset));
7727 this->on_set_origin(&instance_to_print.print_object, offset);
7728 //FIXME the following code prints regions in the order they are defined, the path is not optimized in any way.
7729
7730 auto has_infill = [](const std::vector<ObjectByExtruder::Island::Region> &by_region) {
7731 for (auto region : by_region) {
7732 if (!region.infills.empty())
7733 return true;
7734 }
7735 return false;
7736 };
7737 {
7738 // Print perimeters of regions that has is_infill_first == false
7739 gcode += this->extrude_perimeters(print, by_region_specific, first_layer, false);
7740 if (!has_wipe_tower && need_insert_timelapse_gcode_for_traditional && printer_structure == PrinterStructure::psI3
7741 && !has_insert_timelapse_gcode && has_infill(by_region_specific)) {
7742 gcode += this->retract(false, false, auto_lift_type, true);
7743
7744 gcode += insert_timelapse_gcode();
7745 has_insert_timelapse_gcode = true;
7746 }
7747 // Then print infill
7748 gcode += this->extrude_infill(print, by_region_specific, false);
7749 // Then the walls left hanging in mid air, now that the infill can anchor them
7750 gcode += this->extrude_perimeters(print, by_region_specific, first_layer, false, true);
7751 // Then print perimeters of regions that has is_infill_first == true
7752 gcode += this->extrude_perimeters(print, by_region_specific, first_layer, true);
7753 }
7754 // ironing
7755 gcode += this->extrude_infill(print,by_region_specific, true);
7756 }
7757
7758 if (this->config().gcode_label_objects) {
7759 gcode += std::string("; stop printing object ") +
7760 instance_to_print.print_object.model_object()->name +
7761 " id:" + std::to_string(instance_to_print.print_object.get_id()) + " copy " +
7762 std::to_string(inst.id) + "\n";
7763 }
7764 // exclude objects
7765 // Don't set m_gcode_label_objects_end if you don't had to write the m_gcode_label_objects_start.
7766 if (!m_writer.is_object_start_str_empty()) {
7767 m_writer.set_object_start_str("");
7768 } else if (m_enable_exclude_object) {
7769 if (is_BBL_Printer()) {
7770 m_writer.set_object_end_str(std::string("; stop printing object, unique label id: ") +
7771 std::to_string(instance_to_print.label_object_id) + "\n" +
7772 "M625\n");
7773 } else {
7774 const auto gflavor = print.config().gcode_flavor.value;
7775 if (gflavor == gcfKlipper) {
7776 m_writer.set_object_end_str(std::string("EXCLUDE_OBJECT_END NAME=") +
7777 instance_name(inst) + "\n");
7778 } else if (gflavor == gcfMarlinLegacy || gflavor == gcfMarlinFirmware || gflavor == gcfRepRapFirmware) {
7779 m_writer.set_object_end_str(std::string("M486 S-1\n"));
7780 }
7781 }
7782 }
7783 }
7784 }
7785
7786 // Mixed-color sublayer extrusion: if this extruder is a component of a mixed sublayer
7787 // group, extrude the mixed slot's geometry at the appropriate sub-Z with scaled flow.
7788 // Ported from BambuStudio and adapted to Orca's instance loop and its finer-grained
7789 // per-role region filament options.
7790 for (const auto &grp : layer_tools.mixed_sub_layer_groups) {
7791 int sub_idx = -1;
7792 for (size_t k = 0; k < grp.components_0based.size(); ++k) {
7793 if (grp.components_0based[k] == extruder_id) {
7794 sub_idx = static_cast<int>(k);
7795 break;
7796 }
7797 }
7798 if (sub_idx < 0)
7799 continue;
7800
7801 auto mixed_instances_it = filament_to_print_instances.find(grp.mixed_slot_0based);
7802 if (mixed_instances_it == filament_to_print_instances.end() || mixed_instances_it->second.first.empty())
7803 continue;
7804
7805 double lh = grp.layer_height > 0. ? grp.layer_height : static_cast<double>(height);
7806 double cumulative_h = 0.0;
7807 for (int i = 0; i < sub_idx; ++i)
7808 cumulative_h += grp.sub_heights[i];
7809 double default_sub_h = grp.sub_heights[sub_idx];
7810 double default_sub_z = print_z - lh + cumulative_h + default_sub_h;
7811
7812 m_sub_layer_flow_ratio = default_sub_h / lh;
7813 m_sub_layer_height = default_sub_h;
7814 m_nominal_z = default_sub_z;
7815
7816 gcode += this->set_extruder(extruder_id, default_sub_z);
7817
7818 for (InstanceToPrint &instance_to_print : mixed_instances_it->second.first) {
7819 const bool use_per_volume = grp.is_gradient
7820 && !grp.per_volume_gradient.empty()
7821 && std::any_of(grp.per_volume_gradient.begin(), grp.per_volume_gradient.end(),
7822 [&](const auto &kv) { return kv.first.obj == &instance_to_print.print_object; });
7823
7824 // --- Shared instance preamble (mirrors Orca's main instance loop) ---
7825 const LayerToPrint &layer_to_print = layers[instance_to_print.layer_id];
7826 const auto &inst = instance_to_print.print_object.instances()[instance_to_print.instance_id];
7827
7828 bool object_layer_over_raft = layer_to_print.object_layer && layer_to_print.object_layer->id() > 0 &&
7829 instance_to_print.print_object.slicing_parameters().raft_layers() == layer_to_print.object_layer->id();
7830 m_config.apply(print.default_region_config());
7831 m_config.apply(instance_to_print.print_object.config(), true);
7832 m_layer = layer_to_print.layer();
7834 if (m_config.reduce_crossing_wall)
7836
7837 if (this->config().gcode_label_objects) {
7838 gcode += std::string("; printing object ") + instance_to_print.print_object.model_object()->name +
7839 " id:" + std::to_string(instance_to_print.print_object.get_id()) + " copy " +
7840 std::to_string(inst.id) + "\n";
7841 }
7843 if (is_BBL_Printer()) {
7844 m_writer.set_object_start_str(
7845 std::string("; start printing object, unique label id: ") +
7846 std::to_string(instance_to_print.label_object_id) + "\n" + "M624 " +
7847 _encode_label_ids_to_base64({instance_to_print.label_object_id}) + "\n");
7848 } else {
7849 const auto gflavor = print.config().gcode_flavor.value;
7850 if (gflavor == gcfKlipper) {
7851 m_writer.set_object_start_str(std::string("EXCLUDE_OBJECT_START NAME=") +
7852 instance_name(inst) + "\n");
7853 } else if (gflavor == gcfMarlinLegacy || gflavor == gcfMarlinFirmware || gflavor == gcfRepRapFirmware) {
7854 m_writer.set_object_start_str(std::string("M486 S") + std::to_string(inst.unique_id) + "\n");
7855 }
7856 }
7857 }
7858
7859 m_extrusion_quality_estimator.set_current_object(&instance_to_print.print_object);
7860
7861 const Point &offset = inst.shift;
7862 std::pair<const PrintObject*, Point> this_object_copy(&instance_to_print.print_object, offset);
7863 if (m_last_obj_copy != this_object_copy)
7864 m_avoid_crossing_perimeters.use_external_mp_once();
7865 m_last_obj_copy = this_object_copy;
7866 this->set_origin(unscale(offset));
7867 // Same as the main instance loop: a belt printer rotates the origin through
7868 // the belt transform (BeltGCode::on_set_origin).
7869 this->on_set_origin(&instance_to_print.print_object, offset);
7870
7871 // --- Build emission plan ---
7872 // Each entry represents one travel_to_z + extrude pass. Per-object mode produces
7873 // exactly 1 entry (all regions, single sub_z); per-volume mode produces N entries
7874 // for tagged volumes plus an optional entry for untagged residue.
7875 struct SubLayerEmitEntry {
7876 double sub_h;
7877 double sub_z;
7878 std::function<bool(size_t region_idx)> region_filter;
7879 bool skip = false;
7880 };
7881 std::vector<SubLayerEmitEntry> emit_plan;
7882
7883 auto compute_sub_zh = [&](double r1, double r2, double &out_sub_h, double &out_sub_z) {
7884 std::vector<double> sub_heights_local(grp.components_0based.size());
7885 for (size_t ci = 0; ci < grp.components_0based.size(); ++ci)
7886 sub_heights_local[ci] = (static_cast<int>(ci) == grp.gradient_first_sorted_idx) ? r1 * lh : r2 * lh;
7887 double cum = 0.0;
7888 for (int ci = 0; ci < sub_idx; ++ci)
7889 cum += sub_heights_local[ci];
7890 out_sub_h = sub_heights_local[sub_idx];
7891 out_sub_z = print_z - lh + cum + out_sub_h;
7892 };
7893
7894 auto gradient_ratios = [](const auto &g) -> std::pair<double, double> {
7895 double t = (g.total_layers > 0) ? (2.0 * g.current_idx + 1.0) / (2.0 * g.total_layers) : 0.5;
7896 // Custom curve wins over linear range when present; OFF path stays bit-identical.
7897 double r1 = g.curve.empty()
7898 ? (g.gradient_start + (g.gradient_end - g.gradient_start) * t)
7899 : sample_gradient_curve(g.curve, t);
7900 return {r1, 1.0 - r1};
7901 };
7902
7903 // Orca splits BBS's three role filaments into five; a region belongs to the slot
7904 // when any of its roles is assigned to it.
7905 auto region_uses_slot = [](const PrintRegionConfig &rcfg, unsigned int slot_1b) {
7906 return (unsigned int)rcfg.outer_wall_filament_id.value == slot_1b
7907 || (unsigned int)rcfg.inner_wall_filament_id.value == slot_1b
7908 || (unsigned int)rcfg.sparse_infill_filament_id.value == slot_1b
7909 || (unsigned int)rcfg.internal_solid_filament_id.value == slot_1b
7910 || (unsigned int)rcfg.top_surface_filament_id.value == slot_1b
7911 || (unsigned int)rcfg.bottom_surface_filament_id.value == slot_1b;
7912 };
7913
7914 double obj_sub_z = default_sub_z;
7915
7916 if (use_per_volume) {
7917 const PrintObject *po = &instance_to_print.print_object;
7918 const unsigned int slot_1b = grp.mixed_slot_0based + 1;
7919
7920 // Discover tagged volumes and untagged presence for this instance.
7921 std::set<ObjectID> tagged_volumes_present;
7922 bool has_untagged_for_slot = false;
7923 for (ObjectByExtruder::Island &island : instance_to_print.object_by_extruder.islands) {
7924 for (size_t r = 0; r < island.by_region.size(); ++r) {
7925 const auto &region = island.by_region[r];
7926 if (region.perimeters.empty() && region.infills.empty())
7927 continue;
7928 const PrintRegion &pr = print.get_print_region(r);
7929 if (!region_uses_slot(pr.config(), slot_1b))
7930 continue;
7931 ObjectID vid = pr.gradient_volume_id();
7932 if (vid.valid())
7933 tagged_volumes_present.insert(vid);
7934 else
7935 has_untagged_for_slot = true;
7936 }
7937 }
7938
7939 // One entry per tagged volume.
7940 for (const ObjectID &target_vid : tagged_volumes_present) {
7941 auto vg_it = grp.per_volume_gradient.find({po, target_vid});
7942 if (vg_it == grp.per_volume_gradient.end())
7943 continue;
7944 const auto &vg = vg_it->second;
7945 auto [r1, r2] = gradient_ratios(vg);
7946
7947 bool vol_no_split = false;
7948 bool skip_entry = false;
7949 const size_t n = grp.components_0based.size();
7950 if (n == 2 && vg.current_idx + 1 == vg.total_layers) {
7951 const size_t dom_idx = (r1 >= r2) ? 0 : 1;
7952 const unsigned int first_sorted_comp = grp.components_0based[grp.gradient_first_sorted_idx];
7953 const unsigned int other_comp = grp.components_0based[1 - grp.gradient_first_sorted_idx];
7954 const unsigned int dom_0b = (dom_idx == 0) ? first_sorted_comp : other_comp;
7955 const unsigned int oth_0b = (dom_idx == 0) ? other_comp : first_sorted_comp;
7956 if (dom_0b < oth_0b) {
7957 vol_no_split = true;
7958 if (extruder_id != dom_0b)
7959 skip_entry = true;
7960 }
7961 }
7962
7963 double vol_sub_h = default_sub_h;
7964 double vol_sub_z = default_sub_z;
7965 if (vol_no_split) {
7966 vol_sub_h = lh;
7967 vol_sub_z = print_z;
7968 } else {
7969 compute_sub_zh(r1, r2, vol_sub_h, vol_sub_z);
7970 }
7971
7972 emit_plan.push_back({vol_sub_h, vol_sub_z,
7973 [target_vid, &print](size_t r) {
7974 return print.get_print_region(r).gradient_volume_id() == target_vid;
7975 },
7976 skip_entry});
7977 }
7978
7979 // Optional entry for untagged regions (modifier / painted / fuzzy_skin).
7980 if (has_untagged_for_slot) {
7981 double obj_sub_h = default_sub_h;
7982 auto og_it = grp.per_object_gradient.find(po);
7983 if (og_it != grp.per_object_gradient.end()) {
7984 auto [r1, r2] = gradient_ratios(og_it->second);
7985 compute_sub_zh(r1, r2, obj_sub_h, obj_sub_z);
7986 }
7987 emit_plan.push_back({obj_sub_h, obj_sub_z,
7988 [&print](size_t r) {
7989 return !print.get_print_region(r).gradient_volume_id().valid();
7990 },
7991 false});
7992 }
7993 } else {
7994 // Legacy per-object path: single entry, no region filter.
7995 double legacy_sub_h = default_sub_h;
7996 obj_sub_z = default_sub_z;
7997 if (grp.is_gradient) {
7998 auto og_it = grp.per_object_gradient.find(&instance_to_print.print_object);
7999 if (og_it != grp.per_object_gradient.end()) {
8000 auto [r1, r2] = gradient_ratios(og_it->second);
8001 compute_sub_zh(r1, r2, legacy_sub_h, obj_sub_z);
8002 }
8003 }
8004 emit_plan.push_back({legacy_sub_h, obj_sub_z, nullptr, false});
8005 }
8006
8007 // --- Unified emission loop ---
8008 auto plan_has_infill = [](const std::vector<ObjectByExtruder::Island::Region> &by_region) {
8009 for (const auto &r : by_region)
8010 if (!r.infills.empty())
8011 return true;
8012 return false;
8013 };
8014
8015 for (auto &entry : emit_plan) {
8016 if (entry.skip)
8017 continue;
8018 m_sub_layer_flow_ratio = entry.sub_h / lh;
8019 m_sub_layer_height = entry.sub_h;
8020 m_nominal_z = entry.sub_z;
8021 // Use the same lazy-Z mechanism as change_layer(): set the flag so travel_to
8022 // fires even when m_last_pos coincides with the first extrusion point,
8023 // ensuring Z reaches sub_z via the combined XY+Z move.
8025
8026 for (ObjectByExtruder::Island &island : instance_to_print.object_by_extruder.islands) {
8027 const auto &src = island.by_region;
8028 std::vector<ObjectByExtruder::Island::Region> subset_storage;
8029 if (entry.region_filter) {
8030 subset_storage.resize(src.size());
8031 for (size_t r = 0; r < src.size(); ++r)
8032 if (entry.region_filter(r))
8033 subset_storage[r] = src[r];
8034 }
8035 const auto &by_region_specific = entry.region_filter ? subset_storage : src;
8036
8037 // Orca resolves infill-first per region inside extrude_perimeters()
8038 // (unlike BBS, which branches on a single global flag), so mirror the
8039 // main instance loop's ordering exactly.
8040 gcode += this->extrude_perimeters(print, by_region_specific, first_layer, false);
8041 if (!has_wipe_tower && need_insert_timelapse_gcode_for_traditional
8042 && printer_structure == PrinterStructure::psI3
8043 && !has_insert_timelapse_gcode && plan_has_infill(by_region_specific)) {
8044 gcode += this->retract(false, false, auto_lift_type, true);
8045 gcode += insert_timelapse_gcode();
8046 has_insert_timelapse_gcode = true;
8047 }
8048 gcode += this->extrude_infill(print, by_region_specific, false);
8049 gcode += this->extrude_perimeters(print, by_region_specific, first_layer, false, true);
8050 gcode += this->extrude_perimeters(print, by_region_specific, first_layer, true);
8051 // ironing
8052 gcode += this->extrude_infill(print, by_region_specific, true);
8053 }
8054 }
8055
8056 // --- Shared support ---
8057 if (instance_to_print.object_by_extruder.support && !instance_to_print.object_by_extruder.support->empty()) {
8058 if (use_per_volume) {
8059 m_nominal_z = obj_sub_z;
8061 }
8062 ExtrusionRole support_role = instance_to_print.object_by_extruder.support_extrusion_role;
8063 gcode += this->extrude_support(*instance_to_print.object_by_extruder.support, support_role);
8064 // Make sure ironing is the last (Orca names this role erIroning, not erSupportIroning).
8065 if (support_role == erMixed || support_role == erSupportMaterialInterface)
8066 gcode += this->extrude_support(*instance_to_print.object_by_extruder.support, erIroning);
8067 }
8068
8069 // --- Shared instance footer (mirrors Orca's main instance loop) ---
8070 if (!m_writer.is_object_start_str_empty()) {
8071 m_writer.set_object_start_str("");
8072 } else if (m_enable_exclude_object) {
8073 if (is_BBL_Printer()) {
8074 m_writer.set_object_end_str(std::string("; stop printing object, unique label id: ") +
8075 std::to_string(instance_to_print.label_object_id) + "\n" +
8076 "M625\n");
8077 } else {
8078 const auto gflavor = print.config().gcode_flavor.value;
8079 if (gflavor == gcfKlipper) {
8080 m_writer.set_object_end_str(std::string("EXCLUDE_OBJECT_END NAME=") +
8081 instance_name(inst) + "\n");
8082 } else if (gflavor == gcfMarlinLegacy || gflavor == gcfMarlinFirmware || gflavor == gcfRepRapFirmware) {
8083 m_writer.set_object_end_str(std::string("M486 S-1\n"));
8084 }
8085 }
8086 }
8087 }
8088
8090 m_sub_layer_height = 0.0;
8091 }
8092 // Flush any pending object end label before leaving the sublayer block, otherwise the
8093 // wipe tower's add_object_end_labels may consume it into a local temp string and the
8094 // M625 would be lost for BBL printers.
8095 if (!layer_tools.mixed_sub_layer_groups.empty()) {
8096 m_writer.add_object_end_labels(gcode);
8097 m_nominal_z = print_z;
8099 }
8100
8101 }
8102
8103 // Belt brim orphan sweep (Finding C). A coincident belt_brim_by_layer band lives on
8104 // an object layer, but that layer can yield no InstanceVisit above - a zero-extrusion
8105 // lead-in slice with no coinciding support - so the in-visit emit never fired and the
8106 // band would be dropped. Emit any such band exactly once here, keyed the same way as
8107 // the in-visit emit so already-printed bands are skipped. These orphan layers carry
8108 // no object material, so ending on the brim's position is harmless; we still save and
8109 // restore m_origin, and only toolchange when the brim filament differs from the active
8110 // one - a no-op on single-extruder prints, keeping their output unchanged.
8111 if (print.has_belt_brim()) {
8112 const Vec2d saved_origin = m_origin;
8113 for (const LayerToPrint &ltp : layers) {
8114 const PrintObject *obj = ltp.original_object;
8115 if (obj == nullptr || ! obj->has_belt_brim() || ltp.object_layer == nullptr)
8116 continue;
8117 const size_t ltp_idx = size_t(&ltp - layers.data());
8118 const unsigned int brim0 = (unsigned int)(obj->belt_brim_filament() - 1);
8119 const size_t i_begin = single_object_instance_idx == size_t(-1) ? 0 : single_object_instance_idx;
8120 const size_t i_end = single_object_instance_idx == size_t(-1) ? obj->instances().size()
8121 : single_object_instance_idx + 1;
8122 for (size_t instance_id = i_begin; instance_id < i_end && instance_id < obj->instances().size(); ++ instance_id) {
8123 if (! belt_brim_emitted.insert({ ltp_idx, instance_id }).second)
8124 continue;
8125 if (m_writer.filament() == nullptr || m_writer.filament()->id() != brim0)
8126 gcode += this->set_extruder(brim0, print_z);
8127 gcode += generate_object_brim(print, *obj, instance_id, first_layer, brim0, ltp.object_layer);
8128 }
8129 }
8130 this->set_origin(saved_origin);
8131 }
8132
8133 if (first_layer) {
8134 for (auto iter = by_extruder.begin(); iter != by_extruder.end(); ++iter) {
8135 if (!iter->second.empty())
8136 m_initial_layer_extruders.insert(iter->first);
8137 }
8138 }
8139
8140#if 0
8141 // Apply spiral vase post-processing if this layer contains suitable geometry
8142 // (we must feed all the G-code into the post-processor, including the first
8143 // bottom non-spiral layers otherwise it will mess with positions)
8144 // we apply spiral vase at this stage because it requires a full layer.
8145 // Just a reminder: A spiral vase mode is allowed for a single object per layer, single material print only.
8146 if (m_spiral_vase)
8147 gcode = m_spiral_vase->process_layer(std::move(gcode));
8148
8149 // Apply cooling logic; this may alter speeds.
8150 if (m_cooling_buffer)
8151 gcode = m_cooling_buffer->process_layer(std::move(gcode), layer.id(),
8152 // Flush the cooling buffer at each object layer or possibly at the last layer, even if it contains just supports (This should not happen).
8153 object_layer || last_layer);
8154
8155 file.write(gcode);
8156#endif
8157
8158 BOOST_LOG_TRIVIAL(trace) << "Exported layer " << layer.id() << " print_z " << print_z <<
8160
8161 // The inline _extrude hook may have taken the snapshot; sync.
8162 has_insert_timelapse_gcode |= m_farthest_point_timelapse.inserted_this_layer;
8163
8164 // Farthest-point-is-photo-head layer-end fallback. If the head never passed within tolerance of
8165 // the farthest point during extrusion (so the inline hook never fired) insert the snapshot here —
8166 // single-extruder prints have no traditional multi-extruder fallback below. Guarded on enabled, so this
8167 // block never runs for a printer with the subsystem off.
8168 if (m_farthest_point_timelapse.enabled && m_farthest_point_timelapse.farthest_is_photo_head && !has_insert_timelapse_gcode) {
8170 gcode += this->retract(false, false, auto_lift_type, true);
8171 }
8172 m_writer.add_object_change_labels(gcode);
8173 gcode += insert_timelapse_gcode();
8174 has_insert_timelapse_gcode = true;
8175 }
8176
8177 if (need_insert_timelapse_gcode_for_traditional && !has_insert_timelapse_gcode) {
8178 // The traditional model of thin-walled object will have flaws for I3
8180 && printer_structure == PrinterStructure::psI3
8181 && m_config.timelapse_type.value == TimelapseType::tlTraditional)
8183
8184 // The traditional model will have flaws for multi_extruder when switching extruder
8185 if (m_config.nozzle_diameter.values.size() == 2
8187 && m_config.timelapse_type.value == TimelapseType::tlTraditional
8188 && (writer().filament() && get_extruder_id(writer().filament()->id()) != most_used_extruder)) {
8190 }
8192 gcode += this->retract(false, false, auto_lift_type, true);
8193 }
8194 m_writer.add_object_change_labels(gcode);
8195
8196 gcode += insert_timelapse_gcode();
8197 }
8198
8199 result.gcode = std::move(gcode);
8200 result.cooling_buffer_flush = object_layer || raft_layer || last_layer;
8201 return result;
8202}
std::string set_input_shaping(char axis, float damp, float freq, std::string type) const
Definition GCodeWriter.cpp:641
GCodeConfig config
Definition GCodeWriter.hpp:28
std::string set_jerk_xy(double jerk)
Definition GCodeWriter.cpp:483
std::string set_pressure_advance(double pa, int tool=-1) const
Definition GCodeWriter.cpp:599
static std::string set_temperature(unsigned int temperature, GCodeFlavor flavor, bool wait=false, int tool=-1, std::string comment=std::string())
Definition GCodeWriter.cpp:317
Extruder * filament(size_t extruder_id)
Definition GCodeWriter.hpp:46
std::string set_junction_deviation(double junction_deviation)
Definition GCodeWriter.cpp:581
void compute_farthest_point(const std::vector< LayerToPrint > &layers, int most_used_extruder, const std::map< std::pair< const SupportLayer *, ExtrusionRole >, unsigned int > &support_filaments)
Definition GCode.cpp:6304
bool last_pos_defined() const
Definition GCode.hpp:554
const std::string & instance_name(const PrintInstance &instance)
Definition GCode.cpp:5880
friend std::vector< ObjectByExtruder::Island > & object_islands_by_extruder(std::map< unsigned int, std::vector< ObjectByExtruder > > &by_extruder, unsigned int extruder_id, size_t object_idx, size_t num_objects, size_t num_islands)
Definition GCode.cpp:5600
float interpolate_value_across_layers(float start_value, float end_value, float step=0.0f) const
Definition GCode.cpp:10221
friend ObjectByExtruder & object_by_extruder(std::map< unsigned int, std::vector< ObjectByExtruder > > &by_extruder, unsigned int extruder_id, size_t object_idx, size_t num_objects)
Definition GCode.cpp:5588
std::string extrude_infill(const Print &print, const std::vector< ObjectByExtruder::Island::Region > &by_region, bool ironing)
Definition GCode.cpp:8933
int belt_layer_past_first_layer_band(const Layer *object_layer) const
Definition GCode.cpp:11425
LayerResult process_belt_brim_layer(const Print &print, const std::vector< LayerToPrint > &layers, const LayerTools &layer_tools, const bool last_layer, const size_t single_object_instance_idx, const int most_used_extruder)
Definition GCode.cpp:6127
std::string extrude_perimeters(const Print &print, const std::vector< ObjectByExtruder::Island::Region > &by_region, bool is_first_layer, bool is_infill_first, bool unsupported_loops_only=false)
Definition GCode.cpp:8886
std::pair< const PrintObject *, Point > m_last_obj_copy
Definition GCode.hpp:905
std::vector< InstanceToPrint > sort_print_object_instances(std::vector< ObjectByExtruder > &objects_by_extruder, const std::vector< LayerToPrint > &layers, const std::vector< const PrintInstance * > *ordering, const size_t single_object_instance_idx)
Definition GCode.cpp:5613
std::string generate_object_brim(const Print &print, const PrintObject &object, size_t instance_id, bool first_layer, unsigned int extruder_id, const Layer *object_layer)
Definition GCode.cpp:5991
std::string generate_object_skirt_group(const Print &print, const PrintObject &object, size_t instance_id, const LayerTools &layer_tools, const Layer &layer, unsigned int extruder_id)
Definition GCode.cpp:5969
Definition GCode.hpp:689
boost::container::small_vector< int32_t, 3 > ExtruderPerCopy
Definition ToolOrdering.hpp:49
static std::string emit_custom_gcode_per_print_z(GCode &gcodegen, const CustomGCode::Item *custom_gcode, unsigned int current_extruder_id, unsigned int first_extruder_id, const PrintConfig &config)
Definition GCode.cpp:5669
void trace(unsigned int level, const char *message)
Definition utils.cpp:247
@ Calib_Vol_speed_Tower
Definition calib.hpp:31
@ Calib_Input_shaping_freq
Definition calib.hpp:34
@ Calib_Temp_Tower
Definition calib.hpp:30
@ Calib_Cornering
Definition calib.hpp:36
@ Calib_Input_shaping_damp
Definition calib.hpp:35
@ Calib_VFA_Tower
Definition calib.hpp:32
@ stCombined
Definition PrintConfig.hpp:474
@ zhtSlope
Definition PrintConfig.hpp:585
@ zhtSpiral
Definition PrintConfig.hpp:586
bool is_inner_wall(const ExtrusionEntity &entity)
Definition ExtrusionEntity.hpp:167
static bool prepares_overhang_estimator(const Layer &layer, bool overhang_fan, OverhangSpeed overhang_speed)
Definition GCode.cpp:5074
@ NormalLift
Definition PrintConfig.hpp:430
std::vector< size_t > order_points_with_strategy(const Points &points, PrintOrder print_order, const Point *start_near)
Definition OrderingStrategies.cpp:501
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
@ tlTraditional
Definition PrintConfig.hpp:469
std::vector< ArithmeticOnly< T > > grid(const T &start, const T &stop, const T &stride)
A set of equidistant values starting from 'start' (inclusive), ending in the closest multiple of 'str...
Definition MTUtils.hpp:127
@ ByLayer
Definition PrintConfig.hpp:311
std::vector< size_t > chain_points(const Points &points, const Point *start_near)
Definition ShortestPath.cpp:1565
static bool overhang_fan_enabled(const PrintConfig &config, bool cooling_markers)
Definition GCode.cpp:5123
@ AsObjectList
Definition PrintConfig.hpp:320
void sort_remove_duplicates(std::vector< T > &vec)
Definition libslic3r.h:231
std::vector< Point, PointsAllocator< Point > > Points
Definition Point.hpp:73
double sample_gradient_curve(const GradientCurve &c, double t)
Definition FilamentMixer.cpp:357
PrinterStructure
Definition PrintConfig.hpp:557
void append(std::vector< T, Alloc > &dest, const std::vector< T, Alloc2 > &src)
Definition libslic3r.h:157
zaa_minimize_perimeter_height gcode_label_objects((ConfigOptionBool, exclude_object))((ConfigOptionFloat
Definition GCode.hpp:635
Definition GCode.hpp:701
Definition GCode.hpp:599

References Slic3r::append(), Slic3r::AsObjectList, belt_layer_past_first_layer_band(), Slic3r::btfHighestTemp, Slic3r::ByObject, Slic3r::Calib_Cornering, Slic3r::Calib_Input_shaping_damp, Slic3r::Calib_Input_shaping_freq, Slic3r::Print::calib_mode(), Slic3r::Calib_None, Slic3r::Calib_PA_Tower, Slic3r::Print::calib_params(), Slic3r::Calib_Retraction_tower, Slic3r::Calib_Temp_Tower, Slic3r::Calib_VFA_Tower, Slic3r::Calib_Vol_speed_Tower, Slic3r::cast(), change_layer(), compute_farthest_point(), config(), Slic3r::GCodeWriter::config, Slic3r::Print::config(), Slic3r::PrintRegion::config(), Slic3r::ConfigOptionFloat, Slic3r::LayerTools::custom_gcode, Slic3r::ProcessLayer::emit_custom_gcode_per_print_z(), Slic3r::Calib_Params::end, Slic3r::ExtrusionEntityCollection::entities, EPSILON, Slic3r::erMixed, Slic3r::erSupportMaterial, Slic3r::erSupportMaterialInterface, Slic3r::erSupportTransition, Slic3r::LayerTools::extruder(), Slic3r::LayerTools::extruders, Slic3r::Print::extruders(), FILAMENT_CONFIG, Slic3r::LayerRegion::fills, Slic3r::ExtrusionEntityCollection::first_point(), Slic3r::Calib_Params::freqEndX, Slic3r::Calib_Params::freqEndY, Slic3r::Calib_Params::freqStartX, Slic3r::Calib_Params::freqStartY, Slic3r::gcfMarlinFirmware, get_bed_temperature(), get_filament_config_index(), Slic3r::get_flush_volumes_matrix(), get_highest_bed_temperature(), Slic3r::get_imex_active_tools(), get_nozzle_config_index(), Slic3r::PrintBase::get_plate_index(), Slic3r::Print::get_print_region(), Slic3r::WipingExtrusions::get_support_extruder_overrides(), Slic3r::WipingExtrusions::get_support_interface_extruder_overrides(), Slic3r::grid(), Slic3r::Print::has_brim(), Slic3r::LayerTools::has_extruder(), Slic3r::Print::has_infinite_skirt(), Slic3r::LayerTools::has_wipe_tower, Slic3r::GCodeProcessor::Height, Slic3r::Layer::id(), Slic3r::ObjectBase::id(), Slic3r::GCode::ObjectByExtruder::Island::Region::INFILL, Slic3r::PrintObject::instances(), interpolate_value_across_layers(), is_BBL_Printer(), Slic3r::Print::is_BBL_printer(), Slic3r::is_decimal_separator_point(), Slic3r::is_inner_wall(), Slic3r::LayerTools::is_mixed_slot(), Slic3r::GCode::InstanceVisit::islands, Slic3r::kImexPrimaryMode, layer(), Slic3r::GCodeProcessor::Layer_Change, layer_id(), Slic3r::Layer::lslices, m_calib_config, m_config, m_cur_layer_idx, m_enable_cooling_markers, m_enable_loop_clipping, m_extrusion_quality_estimator, m_farthest_point_timelapse, m_imex_head_filament_map, m_imex_parallel_mode, m_last_height, m_last_layer_z, m_layer, m_layer_index, m_max_layer_z, m_object_layer_over_raft, m_ooze_prevention, m_ordering_cache, m_printed_objects, m_second_layer_things_done, m_spiral_vase, m_wipe_tower, m_writer, Slic3r::BoundingBoxBase< PointType, APointsType >::max, Slic3r::BoundingBoxBase< PointType, APointsType >::min, Slic3r::LayerTools::mixed_sub_layer_groups, Slic3r::NormalLift, NOZZLE_CONFIG, Slic3r::Print::num_print_regions(), Slic3r::Layer::object(), object_by_extruder, object_islands_by_extruder, Slic3r::order_points_with_strategy(), Slic3r::overhang_fan_enabled(), Slic3r::GCode::ObjectByExtruder::Island::Region::PERIMETERS, Slic3r::LayerRegion::perimeters, placeholder_parser_process(), Slic3r::prepares_overhang_estimator(), Print, Slic3r::PrintRegion::print_region_id(), Slic3r::GCode::LayerToPrint::print_z(), process_belt_brim_layer(), process_layer_change_gcode(), Slic3r::psI3, Slic3r::SlicingParameters::raft_layers(), Slic3r::LayerRegion::region(), Slic3r::GCodeProcessor::reserved_tag(), Slic3r::resolve_filament_for_head(), retract(), Slic3r::ExtrusionEntityCollection::role(), scale_, Slic3r::GCodeWriter::set_input_shaping(), Slic3r::GCodeWriter::set_jerk_xy(), Slic3r::GCodeWriter::set_junction_deviation(), Slic3r::DynamicConfig::set_key_value(), Slic3r::GCodeWriter::set_pressure_advance(), Slic3r::GCodeWriter::set_temperature(), Slic3r::Calib_Params::shaper_type, Slic3r::PrintObject::slicing_parameters(), sort_print_object_instances(), Slic3r::sort_remove_duplicates(), Slic3r::LayerResult::spiral_vase_enable, Slic3r::SpiralLift, Slic3r::Calib_Params::start, Slic3r::Calib_Params::step, Slic3r::GCode::ObjectByExtruder::support, Slic3r::GCode::ObjectByExtruder::support_extrusion_role, Slic3r::SupportLayer::support_fills, timelapse_gcode_config(), Slic3r::tlTraditional, unretract(), update_layer_related_config(), update_placeholder_parser_with_variant_params(), writer(), Slic3r::zhtAuto, Slic3r::zhtSlope, and Slic3r::zhtSpiral.

Referenced by process_layers().

Here is the caller graph for this function:

◆ process_layer_change_gcode()

std::string Slic3r::GCode::process_layer_change_gcode ( const Print & print,
coordf_t print_z,
int most_used_extruder,
bool has_wipe_tower )
protectedinherited
6079{
6080 DynamicConfig config;
6081 config.set_key_value("most_used_physical_extruder_id", new ConfigOptionInt(m_config.physical_extruder_map.get_at(most_used_extruder)));
6082 config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
6083 config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
6084 config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z));
6085 {
6086 const int cur_filament_id = (int) m_writer.filament()->id();
6087 const auto group_result = m_print->get_layered_nozzle_group_result();
6088 config.set_key_value("current_filament_id", new ConfigOptionInt(cur_filament_id));
6089 config.set_key_value("current_nozzle_id", new ConfigOptionInt(
6090 nozzle_id_for_gcode_placeholder(group_result, cur_filament_id, (int) m_writer.filament()->extruder_id(), m_layer_index)));
6091 }
6092
6093 PrintStatistics curr_print_statistics;
6094 for (const Extruder &extruder : m_writer.extruders()) {
6095 double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? print.wipe_tower_data().used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter
6096 double filament_weight = extruded_volume * extruder.filament_density() * 0.001;
6097 if (filament_weight > 0.)
6098 curr_print_statistics.total_weight += filament_weight;
6099 }
6100 double curr_y_acceleration_limit = -1, curr_accumulated_mass = -1;
6101 mass_load_limited_machine_acceleration(curr_print_statistics, print, curr_y_acceleration_limit, curr_accumulated_mass);
6102 // Runs before this layer prints, so the mass added since the last layer change is the previous layer's.
6103 double previous_layer_mass = curr_print_statistics.total_weight - m_last_layer_accumulated_mass;
6104 if (previous_layer_mass <= EPSILON) previous_layer_mass = 0.0;
6105 m_last_layer_accumulated_mass = curr_print_statistics.total_weight;
6106 config.set_key_value("curr_y_acceleration_limit", new ConfigOptionFloat(curr_y_acceleration_limit));
6107 config.set_key_value("curr_accumulated_mass", new ConfigOptionFloat(curr_accumulated_mass));
6108 config.set_key_value("previous_layer_mass", new ConfigOptionFloat(previous_layer_mass));
6109 // Bambu Studio's name for previous_layer_mass.
6110 config.set_key_value("curr_layer_mass", new ConfigOptionFloat(previous_layer_mass));
6111
6112 return this->placeholder_parser_process("layer_change_gcode",
6113 print.config().layer_change_gcode.value, m_writer.filament()->id(), &config)
6114 + "\n";
6115}
void mass_load_limited_machine_acceleration(const PrintStatistics &curr_print_statistics, const Print &print, double &y_acceleration_limit_res, double &accumulated_mass_res)
Definition GCode.cpp:6268

References config(), Slic3r::Print::config(), Slic3r::ConfigOptionFloat, EPSILON, m_config, m_last_layer_accumulated_mass, m_layer_index, m_max_layer_z, m_print, m_writer, mass_load_limited_machine_acceleration(), Slic3r::nozzle_id_for_gcode_placeholder(), placeholder_parser_process(), Print, Slic3r::PrintStatistics::total_weight, and Slic3r::Print::wipe_tower_data().

Referenced by process_belt_brim_layer(), and process_layer().

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

◆ process_layers() [1/2]

void Slic3r::GCode::process_layers ( const Print & print,
const ToolOrdering & tool_ordering,
const std::vector< const PrintInstance * > & print_object_instances_ordering,
const std::vector< std::pair< coordf_t, std::vector< LayerToPrint > > > & layers_to_print,
GCodeOutputStream & output_stream )
protectedinherited
5137{
5138 // The pipeline is variable: The vase mode filter is optional.
5139 size_t layer_to_print_idx = 0;
5140 const auto source = precomputed_layers_source(layer_to_print_idx, layers_to_print.size(), m_pressure_equalizer != nullptr,
5142 [&layers_to_print](size_t index) -> const std::vector<LayerToPrint> & { return layers_to_print[index].second; });
5143 const auto generator = tbb::make_filter<PrecomputedLayer, LayerResult>(slic3r_tbb_filtermode::serial_in_order,
5144 [this, &print, &tool_ordering, &print_object_instances_ordering, &layers_to_print](PrecomputedLayer precomputed) -> LayerResult {
5145 if (precomputed.index == size_t(-1))
5147 const std::pair<coordf_t, std::vector<LayerToPrint>>& layer = layers_to_print[precomputed.index];
5148 const LayerTools& layer_tools = tool_ordering.tools_for_layer(layer.first);
5149 print.set_status(80, Slic3r::format(_(L("Generating G-code: layer %1%")), std::to_string(precomputed.index + 1)));
5150 if (m_wipe_tower && layer_tools.has_wipe_tower)
5151 m_wipe_tower->next_layer();
5152 //BBS
5154 print.throw_if_canceled();
5155 m_extrusion_quality_estimator.set_precomputed_layers(std::move(precomputed.overhang_layers));
5156 return this->process_layer(print, layer.second, layer_tools, &layer == &layers_to_print.back(), &print_object_instances_ordering, tool_ordering.get_most_used_extruder(), size_t(-1));
5157 });
5158 if (m_spiral_vase) {
5160 float max_xy_smoothing = m_config.get_abs_value("spiral_mode_max_xy_smoothing", nozzle_diameter);
5161 this->m_spiral_vase->set_max_xy_smoothing(max_xy_smoothing);
5162 }
5163 const auto spiral_mode = tbb::make_filter<LayerResult, LayerResult>(slic3r_tbb_filtermode::serial_in_order,
5164 [&spiral_mode = *this->m_spiral_vase.get(), &layers_to_print](LayerResult in) -> LayerResult {
5165 if (in.nop_layer_result)
5166 return in;
5167
5168 spiral_mode.enable(in.spiral_vase_enable);
5169 bool last_layer = in.layer_id == layers_to_print.size() - 1;
5170 return { spiral_mode.process_layer(std::move(in.gcode), last_layer), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush};
5171 });
5172 const auto pressure_equalizer = tbb::make_filter<LayerResult, LayerResult>(slic3r_tbb_filtermode::serial_in_order,
5173 [pressure_equalizer = this->m_pressure_equalizer.get()](LayerResult in) -> LayerResult {
5174 return pressure_equalizer->process_layer(std::move(in));
5175 });
5176 const auto cooling = tbb::make_filter<LayerResult, std::string>(slic3r_tbb_filtermode::serial_in_order,
5177 [&cooling_buffer = *this->m_cooling_buffer.get()](LayerResult in) -> std::string {
5178 if (in.nop_layer_result)
5179 return in.gcode;
5180 return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush);
5181 });
5182 const auto pa_processor_filter = tbb::make_filter<std::string, std::string>(slic3r_tbb_filtermode::serial_in_order,
5183 [&pa_processor = *this->m_pa_processor](std::string in) -> std::string {
5184 return pa_processor.process_layer(std::move(in));
5185 }
5186 );
5187
5188 const auto output = tbb::make_filter<std::string, void>(slic3r_tbb_filtermode::serial_in_order,
5189 [&output_stream](std::string s) { output_stream.write(s); }
5190 );
5191
5192 const auto fan_mover = tbb::make_filter<std::string, std::string>(slic3r_tbb_filtermode::serial_in_order,
5193 [&fan_mover = this->m_fan_mover, &config = this->config(), &writer = this->m_writer](std::string in)->std::string {
5194
5195 CNumericLocalesSetter locales_setter;
5196
5197 if (config.fan_speedup_time.value != 0 || config.fan_kickstart.value > 0) {
5198 if (fan_mover.get() == nullptr)
5199 fan_mover.reset(new Slic3r::FanMover(
5200 writer,
5201 std::abs((float)config.fan_speedup_time.value),
5202 config.fan_speedup_time.value > 0,
5203 config.use_relative_e_distances.value,
5204 config.fan_speedup_overhangs.value,
5205 (float)config.fan_kickstart.value));
5206 //flush as it's a whole layer
5207 return fan_mover->process_gcode(in, true);
5208 }
5209 return in;
5210 });
5211
5212 // The pipeline elements are joined using const references, thus no copying is performed.
5214 tbb::parallel_pipeline(12, source & generator & spiral_mode & pressure_equalizer & cooling & fan_mover & output);
5215 else if (m_spiral_vase)
5216 tbb::parallel_pipeline(12, source & generator & spiral_mode & cooling & fan_mover & output);
5217 else if (m_pressure_equalizer)
5218 tbb::parallel_pipeline(12, source & generator & pressure_equalizer & cooling & fan_mover & pa_processor_filter & output);
5219 else
5220 tbb::parallel_pipeline(12, source & generator & cooling & fan_mover & pa_processor_filter & output);
5221 // The estimator's precomputed data points into this print's layers.
5222 m_extrusion_quality_estimator.set_precomputed_layers({});
5223
5224}
LayerResult process_layer(const Print &print, const std::vector< LayerToPrint > &layers, const LayerTools &layer_tools, const bool last_layer, const std::vector< const PrintInstance * > *ordering, const int most_used_extruder, const size_t single_object_idx=size_t(-1), const bool prime_extruder=false)
Definition GCode.cpp:6558
static auto precomputed_layers_source(size_t &next_index, size_t layer_count, bool nop_layer, bool overhang_fan, LayersAt layers_at)
Definition GCode.cpp:5101
static LayerResult make_nop_layer_result()
Definition GCode.hpp:225

References _, check_placeholder_parser_failed(), Slic3r::Print::config(), EXTRUDER_CONFIG, Slic3r::format(), Slic3r::ToolOrdering::get_most_used_extruder(), Slic3r::LayerTools::has_wipe_tower, L, layer(), m_config, m_enable_cooling_markers, m_extrusion_quality_estimator, m_pressure_equalizer, m_spiral_vase, m_wipe_tower, Slic3r::LayerResult::make_nop_layer_result(), Slic3r::nozzle_diameter(), Slic3r::overhang_fan_enabled(), Slic3r::precomputed_layers_source(), Print, process_layer(), Slic3r::PrintBase::set_status(), Slic3r::PrintBase::throw_if_canceled(), and Slic3r::ToolOrdering::tools_for_layer().

Referenced by _do_export().

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

◆ process_layers() [2/2]

void Slic3r::GCode::process_layers ( const Print & print,
const ToolOrdering & tool_ordering,
std::vector< LayerToPrint > layers_to_print,
const size_t single_object_idx,
GCodeOutputStream & output_stream,
const bool prime_extruder = false )
protectedinherited
5237{
5238 // The pipeline is variable: The vase mode filter is optional.
5239 size_t layer_to_print_idx = 0;
5240 const auto source = precomputed_layers_source(layer_to_print_idx, layers_to_print.size(), m_pressure_equalizer != nullptr,
5242 [&layers_to_print](size_t index) { return std::vector<LayerToPrint>{layers_to_print[index]}; });
5243 const auto generator = tbb::make_filter<PrecomputedLayer, LayerResult>(slic3r_tbb_filtermode::serial_in_order,
5244 [this, &print, &tool_ordering, &layers_to_print, single_object_idx, prime_extruder](PrecomputedLayer precomputed) -> LayerResult {
5245 if (precomputed.index == size_t(-1))
5247 LayerToPrint &layer = layers_to_print[precomputed.index];
5248 print.set_status(80, Slic3r::format(_(L("Generating G-code: layer %1%")), std::to_string(precomputed.index + 1)));
5249 //BBS
5251 print.throw_if_canceled();
5252 m_extrusion_quality_estimator.set_precomputed_layers(std::move(precomputed.overhang_layers));
5253 return this->process_layer(print, { std::move(layer) }, tool_ordering.tools_for_layer(layer.print_z()), &layer == &layers_to_print.back(), nullptr, tool_ordering.get_most_used_extruder(), single_object_idx, prime_extruder);
5254 });
5255 if (m_spiral_vase) {
5257 float max_xy_smoothing = m_config.get_abs_value("spiral_mode_max_xy_smoothing", nozzle_diameter);
5258 this->m_spiral_vase->set_max_xy_smoothing(max_xy_smoothing);
5259 }
5260 const auto spiral_mode = tbb::make_filter<LayerResult, LayerResult>(slic3r_tbb_filtermode::serial_in_order,
5261 [&spiral_mode = *this->m_spiral_vase.get(), &layers_to_print](LayerResult in)->LayerResult {
5262 if (in.nop_layer_result)
5263 return in;
5264 spiral_mode.enable(in.spiral_vase_enable);
5265 bool last_layer = in.layer_id == layers_to_print.size() - 1;
5266 return { spiral_mode.process_layer(std::move(in.gcode), last_layer), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush };
5267 });
5268 const auto pressure_equalizer = tbb::make_filter<LayerResult, LayerResult>(slic3r_tbb_filtermode::serial_in_order,
5269 [pressure_equalizer = this->m_pressure_equalizer.get()](LayerResult in) -> LayerResult {
5270 return pressure_equalizer->process_layer(std::move(in));
5271 });
5272 const auto cooling = tbb::make_filter<LayerResult, std::string>(slic3r_tbb_filtermode::serial_in_order,
5273 [&cooling_buffer = *this->m_cooling_buffer.get()](LayerResult in)->std::string {
5274 if (in.nop_layer_result)
5275 return in.gcode;
5276 return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush);
5277 });
5278 const auto pa_processor_filter = tbb::make_filter<std::string, std::string>(slic3r_tbb_filtermode::serial_in_order,
5279 [&pa_processor = *this->m_pa_processor](std::string in) -> std::string {
5280 return pa_processor.process_layer(std::move(in));
5281 }
5282 );
5283
5284 const auto output = tbb::make_filter<std::string, void>(slic3r_tbb_filtermode::serial_in_order,
5285 [&output_stream](std::string s) { output_stream.write(s); }
5286 );
5287
5288 const auto fan_mover = tbb::make_filter<std::string, std::string>(slic3r_tbb_filtermode::serial_in_order,
5289 [&fan_mover = this->m_fan_mover, &config = this->config(), &writer = this->m_writer](std::string in)->std::string {
5290
5291 if (config.fan_speedup_time.value != 0 || config.fan_kickstart.value > 0) {
5292 if (fan_mover.get() == nullptr)
5293 fan_mover.reset(new Slic3r::FanMover(
5294 writer,
5295 std::abs((float)config.fan_speedup_time.value),
5296 config.fan_speedup_time.value > 0,
5297 config.use_relative_e_distances.value,
5298 config.fan_speedup_overhangs.value,
5299 (float)config.fan_kickstart.value));
5300 //flush as it's a whole layer
5301 return fan_mover->process_gcode(in, true);
5302 }
5303 return in;
5304 });
5305
5306 // The pipeline elements are joined using const references, thus no copying is performed.
5308 tbb::parallel_pipeline(12, source & generator & spiral_mode & pressure_equalizer & cooling & fan_mover & output);
5309 else if (m_spiral_vase)
5310 tbb::parallel_pipeline(12, source & generator & spiral_mode & cooling & fan_mover & output);
5311 else if (m_pressure_equalizer)
5312 tbb::parallel_pipeline(12, source & generator & pressure_equalizer & cooling & fan_mover & pa_processor_filter & output);
5313 else
5314 tbb::parallel_pipeline(12, source & generator & cooling & fan_mover & pa_processor_filter & output);
5315 // The estimator's precomputed data points into this print's layers.
5316 m_extrusion_quality_estimator.set_precomputed_layers({});
5317}

References Slic3r::Print::config(), m_enable_cooling_markers, m_pressure_equalizer, Slic3r::overhang_fan_enabled(), Slic3r::precomputed_layers_source(), and Print.

Here is the call graph for this function:

◆ retract()

std::string Slic3r::GCode::retract ( bool toolchange = false,
bool is_last_retraction = false,
LiftType lift_type = LiftType::NormalLift,
bool apply_instantly = false,
ExtrusionRole role = erNone )
inherited
10689{
10690 std::string gcode;
10691
10692 if (m_writer.filament() == nullptr)
10693 return gcode;
10694
10695 // wipe (if it's enabled for this extruder and we have a stored wipe path and no-zero wipe distance)
10696 if (FILAMENT_CONFIG(wipe) && m_wipe.has_path() && scale_(FILAMENT_CONFIG(wipe_distance)) > SCALED_EPSILON) {
10697 Wipe::RetractionValues wipeRetractions = m_wipe.calculateWipeRetractionLengths(*this, toolchange);
10698 gcode += toolchange ? m_writer.retract_for_toolchange(true, wipeRetractions.retraction_length_before_wipe) :
10699 m_writer.retract(true, wipeRetractions.retraction_length_before_wipe);
10700 gcode += m_wipe.wipe(*this, wipeRetractions.retraction_length_during_wipe, toolchange, is_last_retraction);
10701
10702 // Orca: wipeRetractions.retraction_length_after_wipe is not being used explicitly,
10703 // the remaining retraction after wipe is handled by the subsequent m_writer.retract() call
10704 }
10705
10706 /* The parent class will decide whether we need to perform an actual retraction
10707 (the extruder might be already retracted fully or partially). We call these
10708 methods even if we performed wipe, since this will ensure the entire retraction
10709 length is honored in case wipe path was too short. */
10710 if ((!this->on_first_layer() || this->config().bottom_surface_pattern != InfillPattern::ipHilbertCurve) &&
10712 gcode += toolchange ? m_writer.retract_for_toolchange() : m_writer.retract();
10713
10714 gcode += m_writer.reset_e();
10715 // Orca: check if should + can lift (roughly from SuperSlicer)
10716 RetractLiftEnforceType retract_lift_type = RetractLiftEnforceType(EXTRUDER_CONFIG(retract_lift_enforce));
10717
10718 bool needs_lift = toolchange
10719 || m_writer.filament()->retraction_length() > 0
10720 || m_config.use_firmware_retraction;
10721
10723
10724 // assume we can lift on retraction; conditions left explicit
10725 bool can_lift = true;
10726
10727 if (retract_lift_type == RetractLiftEnforceType::rletAllSurfaces) {
10728 can_lift = true;
10729 }
10730 else if (this->m_layer_index == 0 && (retract_lift_type == RetractLiftEnforceType::rletBottomOnly || retract_lift_type == RetractLiftEnforceType::rletTopAndBottom)) {
10731 can_lift = true;
10732 }
10733 else if (retract_lift_type == RetractLiftEnforceType::rletTopOnly || retract_lift_type == RetractLiftEnforceType::rletTopAndBottom) {
10734 can_lift = last_fill_extrusion_role_top_infill;
10735 }
10736 else {
10737 can_lift = false;
10738 }
10739
10740 if (needs_lift && can_lift) {
10741 if (apply_instantly)
10742 gcode += m_writer.eager_lift(lift_type);
10743 else
10744 gcode += m_writer.lazy_lift(lift_type, m_spiral_vase != nullptr);
10745 }
10746
10747 return gcode;
10748}
@ ipHilbertCurve
Definition PrintConfig.hpp:134
RetractLiftEnforceType
Definition PrintConfig.hpp:590
@ rletBottomOnly
Definition PrintConfig.hpp:593
@ rletTopOnly
Definition PrintConfig.hpp:592
@ rletAllSurfaces
Definition PrintConfig.hpp:591
@ rletTopAndBottom
Definition PrintConfig.hpp:594

References config(), Slic3r::erIroning, Slic3r::erTopSolidInfill, EXTRUDER_CONFIG, FILAMENT_CONFIG, Slic3r::ipHilbertCurve, m_config, m_last_notgapfill_extrusion_role, m_layer_index, m_spiral_vase, m_wipe, m_writer, on_first_layer(), Slic3r::Wipe::RetractionValues::retraction_length_before_wipe, Slic3r::Wipe::RetractionValues::retraction_length_during_wipe, Slic3r::rletAllSurfaces, Slic3r::rletBottomOnly, Slic3r::rletTopAndBottom, Slic3r::rletTopOnly, scale_, SCALED_EPSILON, Slic3r::top_surface_pattern(), and Slic3r::wipe().

Referenced by _do_export(), Slic3r::WipeTowerIntegration::append_tcr(), Slic3r::WipeTowerIntegration::append_tcr2(), change_layer(), process_layer(), set_extruder(), and travel_to().

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

◆ set_extruder()

std::string Slic3r::GCode::set_extruder ( unsigned int extruder_id,
double print_z,
bool by_object = false,
int toolchange_temp_override = -1,
bool defer_temp_wait = false )
inherited
10841{
10842 int new_extruder_id = get_extruder_id(new_filament_id);
10843 if (!m_writer.need_toolchange(new_filament_id))
10844 return "";
10845
10846 const std::optional<ToolchangePositionState> position_before_toolchange =
10848
10849 // if we are running a single-extruder setup, just set the extruder and return nothing
10850 if (!m_writer.multiple_extruders) {
10851 this->placeholder_parser().set("current_extruder", new_filament_id);
10852 // Orca: keep the global current-tool identity coherent even on the single-extruder path (see append_tcr).
10853 this->placeholder_parser().set("current_filament_id", (int) new_filament_id);
10854 this->placeholder_parser().set("current_extruder_id", new_extruder_id);
10855 this->placeholder_parser().set("current_nozzle_id",
10856 nozzle_id_for_gcode_placeholder(m_print->get_layered_nozzle_group_result(), (int) new_filament_id, new_extruder_id, m_layer_index));
10857 this->placeholder_parser().set("current_hotend",
10858 hotend_id_for_gcode_placeholder(m_print->get_layered_nozzle_group_result(), m_print->is_BBL_printer(), (int) new_filament_id,
10859 new_extruder_id, m_layer_index));
10860 {
10861 size_t fi = get_filament_config_index(new_filament_id);
10862 this->placeholder_parser().set("retraction_distance_when_ec", m_config.retraction_distances_when_ec.get_at(fi));
10863 this->placeholder_parser().set("long_retraction_when_ec", m_config.long_retractions_when_ec.get_at(fi));
10864 }
10865
10866 std::string gcode;
10867 // Append the filament start G-code.
10868 const std::string &filament_start_gcode = m_config.filament_start_gcode.get_at(new_filament_id);
10869 if (! filament_start_gcode.empty()) {
10870 // Process the filament_start_gcode for the filament.
10871 DynamicConfig config;
10872 config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
10873 config.set_key_value("layer_z", new ConfigOptionFloat(this->writer().get_position().z() - m_config.z_offset.value));
10874 config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z));
10875 config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(new_filament_id)));
10876 {
10877 size_t fi = get_filament_config_index(new_filament_id);
10878 config.set_key_value("retraction_distance_when_cut",
10879 new ConfigOptionFloat(m_config.retraction_distances_when_cut.get_at(fi)));
10880 config.set_key_value("long_retraction_when_cut", new ConfigOptionBool(m_config.long_retractions_when_cut.get_at(fi)));
10881 }
10882
10883 gcode += this->placeholder_parser_process("filament_start_gcode", filament_start_gcode, new_filament_id, &config);
10884 check_add_eol(gcode);
10885 }
10886 // In IMEX parallel modes each carriage needs an explicit tool address. In primary
10887 // mode, and on a non-IMEX printer, imex_pem_tool_for() returns -1 and the qualifier
10888 // is omitted, because regular tool changes already handle PA there.
10889 gcode += set_filament_pressure_advance(new_filament_id,
10890 imex_pem_tool_for((int) new_filament_id, m_imex_parallel_mode, m_config.physical_extruder_map));
10891
10892 // Same suppression as the long multi-extruder path: at print-start in IMEX
10893 // parallel modes the user's mode_gcode + machine_start_gcode is responsible
10894 // for tool selection, so the slicer's bare T<n> would be a duplicate. Mid-
10895 // print toolchanges (count > 1) emit normally — Print::validate() blocks
10896 // multi-color in configurations where mid-print T<n> wouldn't make sense
10897 // (see imex_multicolor_block_reason).
10898 const std::string toolchange_command = m_writer.toolchange(new_filament_id, new_extruder_id);
10900 gcode += toolchange_command;
10901 if (Extruder *fil = m_writer.filament())
10902 fil->set_config_index((int)get_filament_config_index((int)fil->id()));
10903 if (position_before_toolchange)
10904 this->synchronize_toolchange_position(*position_before_toolchange, gcode);
10905 return gcode;
10906 }
10907
10908 // BBS. Should be placed before retract.
10910
10911 // prepend retraction on the current extruder
10912 std::string gcode = this->retract(true, false);
10913
10914 // Always reset the extrusion path, even if the tool change retract is set to zero.
10915 m_wipe.reset_path();
10916
10917 // BBS: insert skip object label before change filament while by object
10918 if (by_object)
10919 m_writer.add_object_change_labels(gcode);
10920
10921 bool add_change_filament_624 = false;
10922 if (m_writer.filament() != nullptr) {
10923 // Process the custom filament_end_gcode. set_extruder() is only called if there is no wipe tower
10924 // so it should not be injected twice.
10925 unsigned int old_filament_id = m_writer.filament()->id();
10926 const std::string &filament_end_gcode = m_config.filament_end_gcode.get_at(old_filament_id);
10927 if (! filament_end_gcode.empty()) {
10928 DynamicConfig config;
10929 config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
10930 config.set_key_value("layer_z", new ConfigOptionFloat(m_writer.get_position().z() - m_config.z_offset.value));
10931 config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z));
10932 config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(get_extruder_id(old_filament_id))));
10933 if (!m_filament_instances_code.empty()) {
10934 gcode += ("M624 " + m_filament_instances_code + "\n");
10936 add_change_filament_624 = true;
10937 }
10938 gcode += placeholder_parser_process("filament_end_gcode", filament_end_gcode, old_filament_id, &config);
10939 check_add_eol(gcode);
10940 }
10941 }
10942
10943
10944 // If ooze prevention is enabled, park current extruder in the nearest
10945 // standby point and set it to the standby temperature.
10946 //
10947 // Same-physical short-circuit: on an IMEX printer, skip the standby cool-down when the
10948 // old and new filament both route to the same physical extruder via physical_extruder_map
10949 // (AFC/MMU lane swaps where the active heater stays selected and only the lane changes).
10950 // The cool-down → re-heat round trip is pointless there — same nozzle, same heater, just a
10951 // different filament feeding it. post_toolchange below still emits M109 to the new
10952 // filament's print temp, so per-lane temperature differences are still handled.
10953 //
10954 // Gated on is_imex so ooze prevention behaves exactly as upstream on every other printer.
10955 // 73 shipping profiles enable it by default (Snapmaker, Flashforge, Prusa and others) and
10956 // none of them are asking for this optimisation.
10957 auto same_physical_extruder = [&](int a, int b) {
10958 if (!m_config.is_imex.value)
10959 return false;
10960 const auto& pem = m_config.physical_extruder_map.values;
10961 return !pem.empty() && a >= 0 && b >= 0
10962 && a < (int)pem.size() && b < (int)pem.size()
10963 && pem[a] == pem[b];
10964 };
10965 if (m_ooze_prevention.enable && m_writer.filament() != nullptr
10966 && !same_physical_extruder(m_writer.filament()->id(), (int)new_filament_id))
10967 gcode += m_ooze_prevention.pre_toolchange(*this);
10968
10969 // BBS
10970 // Per-variant filament arrays can hold one column per variant a filament uses under a
10971 // per-layer nozzle grouping; resolve the column instead of indexing by the filament id.
10972 size_t new_fi = get_filament_config_index((int)new_filament_id);
10973 float new_retract_length = m_config.retraction_length.get_at(new_fi);
10974 float new_retract_length_toolchange = m_config.retract_length_toolchange.get_at(new_fi);
10975 int new_filament_temp = this->on_first_layer() ? m_config.nozzle_temperature_initial_layer.get_at(new_fi) : m_config.nozzle_temperature.get_at(new_fi);
10976 // BBS: if print_z == 0 use first layer temperature
10977 if (abs(print_z) < EPSILON)
10978 new_filament_temp = m_config.nozzle_temperature_initial_layer.get_at(new_fi);
10979 if (toolchange_temp_override > 0)
10980 new_filament_temp = toolchange_temp_override;
10981
10982 // With wait_for_temp_on_wipe_tower the blocking M109 is deferred to the wipe tower, so raise
10983 // the incoming filament's target here — ahead of the tool change rather than after it — and
10984 // let the heat-up overlap the change itself as well as the travel to the tower. The command
10985 // always carries an explicit tool index (the option is off for single extruder MM, so the
10986 // writer emits one), leaving the outgoing filament that pre_toolchange just dropped to its
10987 // standby temperature alone. nozzle_temperature == 0 means "use the first layer temperature".
10988 if (defer_temp_wait) {
10989 // Target what the tower will wait on. It waits on the first layer temperature not only on
10990 // the first layer but also while priming, which runs before any layer is set: there
10991 // on_first_layer() is false and print_z is the initial layer height, so neither test above
10992 // catches it. nozzle_temperature == 0 means "use the first layer temperature" as well.
10993 int preheat_temp = new_filament_temp;
10994 if (toolchange_temp_override <= 0 && (m_layer == nullptr || preheat_temp <= 0))
10995 preheat_temp = m_config.nozzle_temperature_initial_layer.get_at(new_fi);
10996 if (preheat_temp > 0)
10997 gcode += m_writer.set_temperature(preheat_temp, false, new_filament_id);
10998 }
10999
11000 Vec3d nozzle_pos = m_writer.get_position();
11001 float old_retract_length, old_retract_length_toolchange, wipe_volume;
11002 int old_filament_temp, old_filament_e_feedrate;
11003
11004 float filament_area = float((M_PI / 4.f) * pow(m_config.filament_diameter.get_at(new_filament_id), 2));
11005 //BBS: add handling for filament change in start gcode
11006 int old_filament_id = -1;
11007 int old_extruder_id = -1;
11008 if (m_writer.filament() != nullptr || m_start_gcode_filament != -1) {
11009 std::vector<float> flush_matrix(cast<float>(get_flush_volumes_matrix(m_config.flush_volumes_matrix.values, new_extruder_id, m_config.nozzle_diameter.values.size())));
11010 const unsigned int number_of_extruders = (unsigned int) (m_config.filament_colour.values.size()); // if is multi_extruder only use the fist extruder matrix
11011 if (m_writer.filament() != nullptr)
11012 assert(m_writer.filament()->id() < number_of_extruders);
11013 else
11014 assert(m_start_gcode_filament < number_of_extruders);
11015
11016 old_filament_id = m_writer.filament() != nullptr ? m_writer.filament()->id() : m_start_gcode_filament;
11017 old_extruder_id = m_writer.filament() != nullptr ? m_writer.filament()->extruder_id() : get_extruder_id(m_start_gcode_filament);
11018
11019 // Resolving the old filament at the current layer is safe: the per-layer nozzle maps are
11020 // gap-filled carry-forward, so its current-layer column matches the nozzle it occupies.
11021 size_t old_fi = get_filament_config_index(old_filament_id);
11022 old_retract_length = m_config.retraction_length.get_at(old_fi);
11023 old_retract_length_toolchange = m_config.retract_length_toolchange.get_at(old_fi);
11024 old_filament_temp = this->on_first_layer()? m_config.nozzle_temperature_initial_layer.get_at(old_fi) : m_config.nozzle_temperature.get_at(old_fi);
11025
11026 //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.
11027 float grab_purge_volume = m_config.grab_length.get_at(new_extruder_id) * 2.4;
11028 if (old_extruder_id != new_extruder_id) {
11029 //calc flush volume between the same extruder id
11030 int old_filament_id_in_new_extruder = m_writer.filament(new_extruder_id) != nullptr ? m_writer.filament(new_extruder_id)->id() : -1;
11031 if (old_filament_id_in_new_extruder == -1)
11032 wipe_volume = 0;
11033 else {
11034 size_t flush_idx = size_t(old_filament_id_in_new_extruder) * number_of_extruders + new_filament_id;
11035 wipe_volume = flush_idx < flush_matrix.size() ? flush_matrix[flush_idx] : 0.f;
11036 wipe_volume *= m_config.flush_multiplier.get_at(new_extruder_id);
11037 }
11038 }
11039 else {
11040 size_t flush_idx = size_t(old_filament_id) * number_of_extruders + new_filament_id;
11041 wipe_volume = flush_idx < flush_matrix.size() ? flush_matrix[flush_idx] : 0.f;
11042 wipe_volume *= m_config.flush_multiplier.get_at(new_extruder_id); // if is multi_extruder only use the fist extruder matrix
11043 }
11044 wipe_volume = std::max(0.f, wipe_volume-grab_purge_volume);
11045
11046 old_filament_e_feedrate = (int) (60.0 * m_config.filament_max_volumetric_speed.get_at(old_fi) / filament_area);
11047 old_filament_e_feedrate = old_filament_e_feedrate == 0 ? 100 : old_filament_e_feedrate;
11048 //BBS: must clean m_start_gcode_filament
11050 } else {
11051 old_retract_length = 0.f;
11052 old_retract_length_toolchange = 0.f;
11053 old_filament_temp = 0;
11054 wipe_volume = 0.f;
11055 old_filament_e_feedrate = 200;
11056 }
11057 float wipe_length = wipe_volume / filament_area;
11058 int new_filament_e_feedrate = (int)(60.0 * m_config.filament_max_volumetric_speed.get_at(new_fi) / filament_area);
11059 new_filament_e_feedrate = new_filament_e_feedrate == 0 ? 100 : new_filament_e_feedrate;
11060
11061 // set volumetric speed of outer wall ,ignore per obejct,just use default setting
11062 float outer_wall_volumetric_speed = get_outer_wall_volumetric_speed(m_config, *m_print, new_filament_id, (int)new_fi, get_extruder_id(new_filament_id));
11063 float wipe_avoid_pos_x = 110.f;
11064 // Logical nozzle grouping (null on paths that don't populate it) + null-safe nozzle ids.
11065 auto group_result = m_print->get_layered_nozzle_group_result();
11066 int old_nozzle_id = nozzle_id_for_gcode_placeholder(group_result, old_filament_id, old_extruder_id, m_layer_index);
11067 int next_nozzle_id = nozzle_id_for_gcode_placeholder(group_result, (int) new_filament_id, new_extruder_id, m_layer_index);
11068 DynamicConfig dyn_config;
11069 dyn_config.set_key_value("outer_wall_volumetric_speed", new ConfigOptionFloat(outer_wall_volumetric_speed));
11070 dyn_config.set_key_value("previous_extruder", new ConfigOptionInt(old_filament_id));
11071 dyn_config.set_key_value("next_extruder", new ConfigOptionInt((int)new_filament_id));
11072 dyn_config.set_key_value("current_hotend", new ConfigOptionInt(
11073 hotend_id_for_gcode_placeholder(group_result, m_print->is_BBL_printer(), old_filament_id, old_extruder_id, m_layer_index)));
11074 dyn_config.set_key_value("next_hotend", new ConfigOptionInt(
11075 hotend_id_for_gcode_placeholder(group_result, m_print->is_BBL_printer(), (int) new_filament_id, new_extruder_id, m_layer_index)));
11076 dyn_config.set_key_value("current_nozzle_id", new ConfigOptionInt(old_nozzle_id));
11077 dyn_config.set_key_value("next_nozzle_id", new ConfigOptionInt(next_nozzle_id));
11078 dyn_config.set_key_value("current_filament_id", new ConfigOptionInt(old_filament_id));
11079 dyn_config.set_key_value("next_filament_id", new ConfigOptionInt((int)new_filament_id));
11080 // Orca: nozzle-volume variant of the old/new extruder (see append_tcr). Null-safe: old_extruder_id may be -1.
11081 {
11082 const auto &extruder_variants = m_config.printer_extruder_variant.values;
11083 dyn_config.set_key_value("old_extruder_variant", new ConfigOptionString(
11084 (old_extruder_id >= 0 && old_extruder_id < (int) extruder_variants.size()) ? extruder_variants[old_extruder_id] : std::string()));
11085 dyn_config.set_key_value("new_extruder_variant", new ConfigOptionString(
11086 (new_extruder_id >= 0 && new_extruder_id < (int) extruder_variants.size()) ? extruder_variants[new_extruder_id] : std::string()));
11087 }
11088 dyn_config.set_key_value("nozzle_diameter_at_nozzle_id", new ConfigOptionFloats(get_nozzle_diameters_by_nozzle_id(group_result.get())));
11089 dyn_config.set_key_value("nozzle_volume_types", new ConfigOptionStrings(get_nozzle_volume_types_by_nozzle_id(group_result.get())));
11090 // Old filament's nozzle-change retract length (filament_retract_length_nc; nil/-1 -> 0).
11091 dyn_config.set_key_value("filament_retract_length_nc", new ConfigOptionFloat(
11092 (old_filament_id != -1) ? (float) m_config.filament_retract_length_nc.get_at(get_filament_config_index(old_filament_id)) : 0.f));
11093 // Current parked-retract length of the incoming filament's extruder.
11094 dyn_config.set_key_value("new_extruder_retracted_length",
11095 new ConfigOptionFloat(m_writer.get_extruder_retracted_length((int) new_filament_id)));
11096 dyn_config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
11097 dyn_config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
11098 dyn_config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z));
11099 dyn_config.set_key_value("relative_e_axis", new ConfigOptionBool(m_config.use_relative_e_distances));
11100 dyn_config.set_key_value("toolchange_count", new ConfigOptionInt((int)m_toolchange_count));
11101 //BBS: fan speed is useless placeholer now, but we don't remove it to avoid
11102 //slicing error in old change_filament_gcode in old 3MF
11103 dyn_config.set_key_value("fan_speed", new ConfigOptionInt((int)0));
11104 dyn_config.set_key_value("old_retract_length", new ConfigOptionFloat(old_retract_length));
11105 dyn_config.set_key_value("new_retract_length", new ConfigOptionFloat(new_retract_length));
11106 dyn_config.set_key_value("old_retract_length_toolchange", new ConfigOptionFloat(old_retract_length_toolchange));
11107 dyn_config.set_key_value("new_retract_length_toolchange", new ConfigOptionFloat(new_retract_length_toolchange));
11108 dyn_config.set_key_value("old_filament_temp", new ConfigOptionInt(old_filament_temp));
11109 dyn_config.set_key_value("new_filament_temp", new ConfigOptionInt(new_filament_temp));
11110 dyn_config.set_key_value("x_after_toolchange", new ConfigOptionFloat(nozzle_pos(0)));
11111 dyn_config.set_key_value("y_after_toolchange", new ConfigOptionFloat(nozzle_pos(1)));
11112 dyn_config.set_key_value("z_after_toolchange", new ConfigOptionFloat(nozzle_pos(2)));
11113 dyn_config.set_key_value("first_flush_volume", new ConfigOptionFloat(wipe_length / 2.f));
11114 dyn_config.set_key_value("second_flush_volume", new ConfigOptionFloat(wipe_length / 2.f));
11115 dyn_config.set_key_value("old_filament_e_feedrate", new ConfigOptionInt(old_filament_e_feedrate));
11116 dyn_config.set_key_value("new_filament_e_feedrate", new ConfigOptionInt(new_filament_e_feedrate));
11117 dyn_config.set_key_value("travel_point_1_x", new ConfigOptionFloat(float(travel_point_1.x())));
11118 dyn_config.set_key_value("travel_point_1_y", new ConfigOptionFloat(float(travel_point_1.y())));
11119 dyn_config.set_key_value("travel_point_2_x", new ConfigOptionFloat(float(travel_point_2.x())));
11120 dyn_config.set_key_value("travel_point_2_y", new ConfigOptionFloat(float(travel_point_2.y())));
11121 dyn_config.set_key_value("travel_point_3_x", new ConfigOptionFloat(float(travel_point_3.x())));
11122 dyn_config.set_key_value("travel_point_3_y", new ConfigOptionFloat(float(travel_point_3.y())));
11123 dyn_config.set_key_value("wipe_avoid_perimeter", new ConfigOptionBool(false));
11124 dyn_config.set_key_value("wipe_avoid_pos_x", new ConfigOptionFloat(wipe_avoid_pos_x));
11125 dyn_config.set_key_value("is_prime_tower_interface", new ConfigOptionBool(false));
11126 dyn_config.set_key_value("filament_tower_interface_purge_volume", new ConfigOptionFloat(m_config.filament_tower_interface_purge_volume.get_at(new_filament_id)));
11127 {
11128 int interface_temp = m_config.filament_tower_interface_print_temp.get_at(new_filament_id);
11129 if (interface_temp == -1)
11130 interface_temp = m_config.nozzle_temperature_range_high.get_at(new_fi);
11131 dyn_config.set_key_value("filament_tower_interface_print_temp", new ConfigOptionInt(interface_temp));
11132 }
11133 if (toolchange_temp_override > 0) {
11134 // Rebuild in filament order: the config arrays may carry per-variant columns, while
11135 // these placeholder vectors are consumed indexed by filament id.
11136 size_t num_filaments = m_print->config().filament_type.values.size();
11137 std::vector<int> temps(num_filaments);
11138 std::vector<int> first_layer_temps(num_filaments);
11139 for (size_t i = 0; i < num_filaments; ++i) {
11140 size_t fi_i = get_filament_config_index((int)i);
11141 temps[i] = m_config.nozzle_temperature.get_at(fi_i);
11142 first_layer_temps[i] = m_config.nozzle_temperature_initial_layer.get_at(fi_i);
11143 }
11144 if (new_filament_id < temps.size())
11145 temps[new_filament_id] = toolchange_temp_override;
11146 dyn_config.set_key_value("temperature", new ConfigOptionInts(temps));
11147 dyn_config.set_key_value("nozzle_temperature", new ConfigOptionInts(temps));
11148 if (new_filament_id < first_layer_temps.size())
11149 first_layer_temps[new_filament_id] = toolchange_temp_override;
11150 dyn_config.set_key_value("first_layer_temperature", new ConfigOptionInts(first_layer_temps));
11151 dyn_config.set_key_value("nozzle_temperature_initial_layer", new ConfigOptionInts(first_layer_temps));
11152 }
11153
11154 {
11155 size_t num_filaments = m_print->config().filament_type.values.size();
11156 // Fast purge mode uses filament_flush_temp_fast; Default is inert.
11157 bool use_fast_flush = m_print->config().prime_volume_mode == PrimeVolumeMode::pvmFast;
11158 std::vector<double> flush_v_speed(num_filaments);
11159 std::vector<int> flush_temps(num_filaments);
11160 std::vector<double> filament_cooling_before_tower(num_filaments);
11161 for (size_t idx = 0; idx < num_filaments; ++idx) {
11162 size_t fi = get_filament_config_index(idx);
11163 flush_v_speed[idx] = m_print->config().filament_flush_volumetric_speed.get_at(fi);
11164 if (flush_v_speed[idx] == 0)
11165 flush_v_speed[idx] = m_print->config().filament_max_volumetric_speed.get_at(fi);
11166 flush_temps[idx] = use_fast_flush ? m_print->config().filament_flush_temp_fast.get_at(fi)
11167 : m_print->config().filament_flush_temp.get_at(fi);
11168 if (flush_temps[idx] == 0)
11169 flush_temps[idx] = m_print->config().nozzle_temperature_range_high.get_at(fi);
11170 filament_cooling_before_tower[idx] = m_print->config().filament_cooling_before_tower.get_at(fi);
11171 }
11172 std::fill(filament_cooling_before_tower.begin(), filament_cooling_before_tower.end(), 0);
11173 dyn_config.set_key_value("flush_volumetric_speeds", new ConfigOptionFloats(flush_v_speed));
11174 dyn_config.set_key_value("flush_temperatures", new ConfigOptionInts(flush_temps));
11175 dyn_config.set_key_value("filament_cooling_before_tower", new ConfigOptionFloats(filament_cooling_before_tower));
11176 }
11177 dyn_config.set_key_value("flush_length", new ConfigOptionFloat(wipe_length));
11178
11179 int flush_count = std::min(g_max_flush_count, (int)std::round(wipe_volume / g_purge_volume_one_time));
11180 float flush_unit = wipe_length / flush_count;
11181 int flush_idx = 0;
11182 for (; flush_idx < flush_count; flush_idx++) {
11183 char key_value[64] = { 0 };
11184 snprintf(key_value, sizeof(key_value), "flush_length_%d", flush_idx + 1);
11185 dyn_config.set_key_value(key_value, new ConfigOptionFloat(flush_unit));
11186 }
11187
11188 for (; flush_idx < g_max_flush_count; flush_idx++) {
11189 char key_value[64] = { 0 };
11190 snprintf(key_value, sizeof(key_value), "flush_length_%d", flush_idx + 1);
11191 dyn_config.set_key_value(key_value, new ConfigOptionFloat(0.f));
11192 }
11193
11194 // Process the custom change_filament_gcode.
11195 std::string change_filament_gcode = m_config.change_filament_gcode.value;
11196
11197 // Move the lift gcode here which is in the change_filament_gcode originally
11199
11200 std::string toolchange_gcode_parsed;
11201 //Orca: Ignore change_filament_gcode if is the first call for a tool change and manual_filament_change is enabled
11202 if (!change_filament_gcode.empty() && !(m_config.manual_filament_change.value && m_toolchange_count == 1)) {
11203 dyn_config.set_key_value("toolchange_z", new ConfigOptionFloat(print_z));
11204
11205 toolchange_gcode_parsed = placeholder_parser_process("change_filament_gcode", change_filament_gcode, new_filament_id, &dyn_config);
11206 check_add_eol(toolchange_gcode_parsed);
11207 // toolchange_gcode_parsed is still read below (get_last_z_from_gcode, custom_gcode_changes_tool).
11208 gcode += wrap_custom_gcode_for_fan_mover(m_config, toolchange_gcode_parsed);
11209
11210 //BBS
11211 {
11212 //BBS: gcode writer doesn't know where the extruder is and whether fan speed is changed after inserting tool change gcode
11213 //Set this flag so that normal lift will be used the first time after tool change.
11214 gcode += ";_FORCE_RESUME_FAN_SPEED\n";
11215 m_writer.set_current_position_clear(false);
11216 //BBS: check whether custom gcode changes the z position. Update if changed
11217 double temp_z_after_tool_change;
11218 if (GCodeProcessor::get_last_z_from_gcode(toolchange_gcode_parsed, temp_z_after_tool_change)) {
11219 Vec3d pos = m_writer.get_position();
11220 pos(2) = temp_z_after_tool_change;
11221 m_writer.set_position(pos);
11222 }
11223 }
11224 }
11225
11226 // BBS. Reset old extruder E-value.
11227 // Keep retract length because Custom GCode will guarantee retract length be the same as toolchange
11228 if (m_config.single_extruder_multi_material) {
11229 m_writer.reset_e();
11230 }
11231
11232 //BBS: don't add T[next extruder] if there is no T cmd on filament change
11233 //We inform the writer about what is happening, but we may not use the resulting gcode.
11234 std::string toolchange_command = m_writer.toolchange(new_filament_id, next_nozzle_id);
11235 if (Extruder *fil = m_writer.filament())
11236 fil->set_config_index((int)get_filament_config_index((int)fil->id()));
11237 // See imex_suppresses_bare_toolchange() — only suppress the print-start initial
11238 // T<n> in parallel modes (the user's imex_mode_gcode + machine_start_gcode owns
11239 // tool activation there). Mid-print T<n> emits normally; Print::validate blocks
11240 // multi-color setups where it wouldn't make sense.
11242 if (!custom_gcode_changes_tool(toolchange_gcode_parsed, m_writer.toolchange_prefix(), new_filament_id)
11243 && !suppress_imex_bare)
11244 gcode += toolchange_command;
11245 else {
11246 // user provided his own toolchange gcode, no need to do anything
11247 }
11248
11249 // Set the temperature if the wipe tower didn't (not needed for non-single extruder MM)
11250 if (m_config.single_extruder_multi_material && !m_config.enable_prime_tower) {
11251 size_t new_fi = get_filament_config_index(new_filament_id);
11252 int temp = (m_layer_index <= 0 ? m_config.nozzle_temperature_initial_layer.get_at(new_fi) :
11253 m_config.nozzle_temperature.get_at(new_fi));
11254
11255 gcode += m_writer.set_temperature(temp, false);
11256 }
11257
11258 this->placeholder_parser().set("current_extruder", new_filament_id);
11259 this->placeholder_parser().set("current_hotend",
11260 hotend_id_for_gcode_placeholder(group_result, m_print->is_BBL_printer(), (int) new_filament_id, new_extruder_id, m_layer_index));
11261 // Orca: keep the global current-tool identity coherent for later contexts (see append_tcr).
11262 this->placeholder_parser().set("current_filament_id", (int) new_filament_id);
11263 this->placeholder_parser().set("current_extruder_id", new_extruder_id);
11264 this->placeholder_parser().set("current_nozzle_id", next_nozzle_id);
11265 {
11266 size_t fi = get_filament_config_index(new_filament_id);
11267 this->placeholder_parser().set("retraction_distance_when_cut", m_config.retraction_distances_when_cut.get_at(fi));
11268 this->placeholder_parser().set("long_retraction_when_cut", m_config.long_retractions_when_cut.get_at(fi));
11269 this->placeholder_parser().set("retraction_distance_when_ec", m_config.retraction_distances_when_ec.get_at(fi));
11270 this->placeholder_parser().set("long_retraction_when_ec", m_config.long_retractions_when_ec.get_at(fi));
11271 }
11272
11273
11274 // Append the filament start G-code.
11275 const std::string &filament_start_gcode = m_config.filament_start_gcode.get_at(new_filament_id);
11276 if (! filament_start_gcode.empty()) {
11277 // Process the filament_start_gcode for the new filament.
11278 DynamicConfig config;
11279 config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
11280 config.set_key_value("layer_z", new ConfigOptionFloat(this->writer().get_position().z() - m_config.z_offset.value));
11281 config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z));
11282 config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(new_filament_id)));
11283 if (toolchange_temp_override > 0) {
11284 // Rebuild in filament order: the config arrays may carry per-variant columns, while
11285 // these placeholder vectors are consumed indexed by filament id.
11286 size_t num_filaments = m_print->config().filament_type.values.size();
11287 std::vector<int> temps(num_filaments);
11288 std::vector<int> first_layer_temps(num_filaments);
11289 for (size_t i = 0; i < num_filaments; ++i) {
11290 size_t fi_i = get_filament_config_index((int)i);
11291 temps[i] = m_config.nozzle_temperature.get_at(fi_i);
11292 first_layer_temps[i] = m_config.nozzle_temperature_initial_layer.get_at(fi_i);
11293 }
11294 if (new_filament_id < temps.size())
11295 temps[new_filament_id] = toolchange_temp_override;
11296 config.set_key_value("temperature", new ConfigOptionInts(temps));
11297 config.set_key_value("nozzle_temperature", new ConfigOptionInts(temps));
11298 if (new_filament_id < first_layer_temps.size())
11299 first_layer_temps[new_filament_id] = toolchange_temp_override;
11300 config.set_key_value("first_layer_temperature", new ConfigOptionInts(first_layer_temps));
11301 config.set_key_value("nozzle_temperature_initial_layer", new ConfigOptionInts(first_layer_temps));
11302 }
11303 gcode += this->placeholder_parser_process("filament_start_gcode", filament_start_gcode, new_filament_id, &config);
11304 if (add_change_filament_624) {
11305 gcode += "M625\n";
11306 add_change_filament_624 = false;
11307 }
11308 check_add_eol(gcode);
11309 }
11310 // Set the new extruder to the operating temperature. With defer_temp_wait the target was
11311 // already raised before the tool change and the blocking wait belongs to the wipe tower
11312 // generator, so there is nothing left to restore here.
11313 if (m_ooze_prevention.enable && !defer_temp_wait)
11314 gcode += m_ooze_prevention.post_toolchange(*this);
11315
11316 // See the note on the single-extruder path above: -1 outside IMEX parallel modes.
11317 gcode += set_filament_pressure_advance(new_filament_id,
11318 imex_pem_tool_for((int) new_filament_id, m_imex_parallel_mode, m_config.physical_extruder_map));
11319 //Orca: tool changer or IDEX's firmware may change Z position, so we set it to unknown/undefined
11320 m_last_pos_defined = false;
11321 if (position_before_toolchange)
11322 this->synchronize_toolchange_position(*position_before_toolchange, gcode);
11323
11324 return gcode;
11325}
void synchronize_toolchange_position(const ToolchangePositionState &before, const std::string &emitted_gcode)
Definition GCode.cpp:8377
std::optional< ToolchangePositionState > capture_toolchange_position()
Definition GCode.cpp:8366
unsigned int m_toolchange_count
Definition GCode.hpp:930
static int hotend_id_for_gcode_placeholder(const std::shared_ptr< MultiNozzleUtils::LayeredNozzleGroupResult > &group_result, bool is_bbl_printer, int filament_id, int extruder_id, int layer_id=-1)
Definition GCode.cpp:229
static const float g_purge_volume_one_time
Definition GCode.cpp:209
bool imex_suppresses_bare_toolchange(const std::string &parallel_mode, unsigned int toolchange_count)
Definition IMEXHelpers.cpp:97
zaa_minimize_perimeter_height change_filament_gcode((ConfigOptionString, change_extrusion_role_gcode))((ConfigOptionString
static const int g_max_flush_count
Definition GCode.cpp:210
static std::string wrap_custom_gcode_for_fan_mover(const FullPrintConfig &config, const std::string &gcode)
Definition GCode.cpp:1095
static void check_add_eol(std::string &gcode)
Definition GCode.cpp:447
int imex_pem_tool_for(int filament_id, const std::string &parallel_mode, const ConfigOptionInts &pem)
Definition IMEXHelpers.cpp:75
static bool custom_gcode_changes_tool(const std::string &custom_gcode, const std::string &tch_prefix, unsigned next_extruder)
Definition GCode.cpp:455

References capture_toolchange_position(), Slic3r::cast(), Slic3r::change_filament_gcode(), Slic3r::check_add_eol(), config(), Slic3r::ConfigOptionFloat, Slic3r::ConfigOptionString, Slic3r::custom_gcode_changes_tool(), EPSILON, Slic3r::g_max_flush_count, Slic3r::g_purge_volume_one_time, get_extruder_id(), get_filament_config_index(), Slic3r::get_flush_volumes_matrix(), Slic3r::GCodeProcessor::get_last_z_from_gcode(), Slic3r::get_nozzle_diameters_by_nozzle_id(), Slic3r::get_nozzle_volume_types_by_nozzle_id(), Slic3r::get_outer_wall_volumetric_speed(), Slic3r::hotend_id_for_gcode_placeholder(), Slic3r::imex_pem_tool_for(), Slic3r::imex_suppresses_bare_toolchange(), m_config, m_filament_instances_code, m_imex_parallel_mode, m_last_pos_defined, m_layer, m_layer_index, m_max_layer_z, m_ooze_prevention, M_PI, m_print, m_start_gcode_filament, m_toolchange_count, m_wipe, m_writer, Slic3r::nozzle_id_for_gcode_placeholder(), on_first_layer(), placeholder_parser(), placeholder_parser_process(), Slic3r::pvmFast, retract(), Slic3r::PlaceholderParser::set(), set_filament_pressure_advance(), Slic3r::DynamicConfig::set_key_value(), Slic3r::SpiralLift, synchronize_toolchange_position(), Slic3r::travel_point_1, Slic3r::travel_point_2, Slic3r::travel_point_3, Slic3r::wrap_custom_gcode_for_fan_mover(), and writer().

Referenced by _do_export(), Slic3r::WipeTowerIntegration::append_tcr2(), and process_belt_brim_layer().

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

◆ set_extruders()

void Slic3r::GCode::set_extruders ( const std::vector< unsigned int > & extruder_ids)
protectedinherited
8354{
8355 m_writer.set_extruders(extruder_ids);
8356
8357 // enable wipe path generation if any extruder has wipe enabled
8358 m_wipe.enable = false;
8359 for (auto id : extruder_ids)
8360 if (m_config.wipe.get_at(id)) {
8361 m_wipe.enable = true;
8362 break;
8363 }
8364}

References m_config, m_wipe, and m_writer.

Referenced by _do_export().

Here is the caller graph for this function:

◆ set_filament_pressure_advance()

std::string Slic3r::GCode::set_filament_pressure_advance ( unsigned int filament_id,
int tool = -1 )
inherited
11328{
11329 const size_t fi = get_filament_config_index(filament_id);
11330 if (!m_config.enable_pressure_advance.get_at(fi))
11331 return {};
11332 // Orca: Adaptive PA
11333 // Reset Adaptive PA processor last PA value
11334 m_pa_processor->resetPreviousPA(m_config.pressure_advance.get_at(fi));
11335 // tool >= 0 addresses one IMEX carriage explicitly; -1 omits the qualifier, so every
11336 // caller that does not pass one emits exactly what it did before.
11337 return m_writer.set_pressure_advance(m_config.pressure_advance.get_at(fi), tool);
11338}

References get_filament_config_index(), m_config, m_pa_processor, and m_writer.

Referenced by _do_export(), Slic3r::WipeTowerIntegration::append_tcr(), Slic3r::WipeTowerIntegration::append_tcr2(), and set_extruder().

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

◆ set_gcode_offset()

void Slic3r::GCode::set_gcode_offset ( double x,
double y )
inlineinherited
264{ m_writer.set_xy_offset(x, y); m_processor.set_xy_offset(x, y);}

References m_processor, and m_writer.

◆ set_gcode_offset_with_imex_shift()

void Slic3r::GCode::set_gcode_offset_with_imex_shift ( double x,
double y,
double imex_x,
double imex_y )
inlineinherited
270 {
271 m_writer.set_xy_offset(x + imex_x, y + imex_y);
272 m_processor.set_xy_offset(x, y);
273 }

References m_processor, and m_writer.

◆ set_last_pos() [1/2]

void Slic3r::GCode::set_last_pos ( const Point & pos)
inlineprotectedinherited
544 {
545 m_last_pos = Point3(pos, 0);
546 m_last_pos_defined = true;
548 }

References m_last_pos, m_last_pos_defined, and m_pending_start_gcode_position.

Referenced by _extrude(), Slic3r::WipeTowerIntegration::append_tcr(), Slic3r::WipeTowerIntegration::append_tcr2(), extrude_loop(), placeholder_parser_process(), synchronize_toolchange_position(), travel_to(), and Slic3r::Wipe::wipe().

Here is the caller graph for this function:

◆ set_last_pos() [2/2]

void Slic3r::GCode::set_last_pos ( const Point3 & pos)
inlineprotectedinherited

◆ set_layer_count()

void Slic3r::GCode::set_layer_count ( unsigned int value)
inlineinherited
304{ m_layer_count = value; }

References m_layer_count.

◆ set_object_info()

std::string Slic3r::GCode::set_object_info ( Print * print)
inherited
11371 {
11372 const auto gflavor = print->config().gcode_flavor.value;
11373 if (print->is_BBL_printer() ||
11374 (gflavor != gcfKlipper && gflavor != gcfMarlinLegacy && gflavor != gcfMarlinFirmware && gflavor != gcfRepRapFirmware))
11375 return "";
11376 std::ostringstream gcode;
11377 // Orca: check if we are in pa calib mode
11378 if (print->calib_mode() == CalibMode::Calib_PA_Pattern) {
11379 BoundingBoxf bbox_bed(print->config().printable_area.values);
11380 bbox_bed.offset(-25.0);
11381 Polygon polygon_bed;
11382 polygon_bed.append(Point(bbox_bed.min.x(), bbox_bed.min.y()));
11383 polygon_bed.append(Point(bbox_bed.max.x(), bbox_bed.min.y()));
11384 polygon_bed.append(Point(bbox_bed.max.x(), bbox_bed.max.y()));
11385 polygon_bed.append(Point(bbox_bed.min.x(), bbox_bed.max.y()));
11386 gcode << "EXCLUDE_OBJECT_DEFINE NAME="
11387 << "Orca-PA-Calibration-Test"
11388 << " CENTER=" << 0 << "," << 0 << " POLYGON=" << polygon_to_string(polygon_bed, print, true) << "\n";
11389 } else if (print->calib_mode() == CalibMode::Calib_PA_Line) {
11390 // PA_Line has only one object, no EXCLUDE_OBJECT_DEFINE needed
11391 } else {
11392 for (PrintObject* object : print->objects()) {
11393 for (PrintInstance& inst : object->instances()) {
11394 // Outlines are in plate coordinates. On a belt printer that is the frame after
11395 // the slicing rotation has been undone and before the G-code axis remap and
11396 // machine-frame shear: where the object stands on the belt, which is what an
11397 // object picker shows. Klipper cancels by name, so nothing depends on more.
11398 auto bbox = inst.get_bounding_box();
11399 auto center = print->translate_to_print_space(Vec2d(bbox.center().x(), bbox.center().y()));
11400 const std::string &inst_name = instance_name(inst);
11401 if (gflavor == gcfKlipper) {
11402 gcode << "EXCLUDE_OBJECT_DEFINE NAME=" << inst_name << " CENTER=" << center.x() << "," << center.y()
11403 << " POLYGON=" << polygon_to_string(inst.get_convex_hull_2d(), print) << "\n";
11404 } else if (gflavor == gcfMarlinLegacy || gflavor == gcfMarlinFirmware || gflavor == gcfRepRapFirmware) {
11405 gcode << "M486 S" << std::to_string(inst.unique_id);
11406 if (gflavor == gcfRepRapFirmware)
11407 gcode << " A"
11408 << "\"" << inst_name << "\"";
11409 else
11410 gcode << "\nM486 A" << inst_name;
11411 gcode << "\nM486 S-1\n";
11412 }
11413 }
11414 }
11415 }
11416
11417 return gcode.str();
11418}
std::string polygon_to_string(const Polygon &polygon, Print *print, bool is_print_space=false)
Definition GCode.cpp:11340

References Slic3r::MultiPoint::append(), Slic3r::Print::calib_mode(), Slic3r::Calib_PA_Line, Slic3r::Calib_PA_Pattern, Slic3r::center(), Slic3r::Print::config(), Slic3r::gcfKlipper, Slic3r::gcfMarlinFirmware, Slic3r::gcfMarlinLegacy, Slic3r::gcfRepRapFirmware, instance_name(), Slic3r::Print::is_BBL_printer(), Slic3r::BoundingBoxBase< PointType, APointsType >::max, Slic3r::BoundingBoxBase< PointType, APointsType >::min, Slic3r::Print::objects(), Slic3r::BoundingBoxBase< PointType, APointsType >::offset(), Slic3r::polygon_to_string(), Print, and Slic3r::Print::translate_to_print_space().

Referenced by _do_export().

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

◆ set_origin() [1/2]

void Slic3r::GCode::set_origin ( const coordf_t x,
const coordf_t y )
inlineinherited
278{ this->set_origin(Vec2d(x, y)); }

References set_origin().

Referenced by set_origin().

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

◆ set_origin() [2/2]

void Slic3r::GCode::set_origin ( const Vec2d & pointf)
inherited
8402{
8403 // if origin increases (goes towards right), last_pos decreases because it goes towards left
8404 const Point3 translate(
8405 scale_(m_origin(0) - pointf(0)),
8406 scale_(m_origin(1) - pointf(1))
8407 );
8409 m_wipe.path.translate(translate.to_point());
8410 m_origin = pointf;
8411}
void translate(ExPolygonsWithIds &e, const Point &p)
Definition Emboss.cpp:1368

References m_last_pos, m_origin, m_wipe, scale_, and Slic3r::translate().

Referenced by _do_export(), emit_belt_brim_bands(), generate_object_brim(), generate_skirt(), and Slic3r::BeltGCode::on_set_origin().

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

◆ should_disable_arc_fitting()

virtual bool Slic3r::GCode::should_disable_arc_fitting ( ) const
inlineprotectedvirtualinherited
448 { return ! m_writer.kinematics().supports_arc_moves(); }

References m_writer.

◆ sort_print_object_instances()

std::vector< GCode::InstanceToPrint > Slic3r::GCode::sort_print_object_instances ( std::vector< ObjectByExtruder > & objects_by_extruder,
const std::vector< LayerToPrint > & layers,
const std::vector< const PrintInstance * > * ordering,
const size_t single_object_instance_idx )
protectedinherited
5620{
5621 std::vector<InstanceToPrint> out;
5622
5623 if (ordering == nullptr) {
5624 // Sequential print, single object is being printed.
5625 for (ObjectByExtruder &object_by_extruder : objects_by_extruder) {
5626 const size_t layer_id = &object_by_extruder - objects_by_extruder.data();
5627 //BBS:add the support of shared print object
5628 const PrintObject *print_object = layers[layer_id].original_object;
5629 //const PrintObject *print_object = layers[layer_id].object();
5630 if (print_object)
5631 out.emplace_back(object_by_extruder, layer_id, *print_object, single_object_instance_idx, print_object->instances()[single_object_instance_idx].model_instance->get_labeled_id());
5632 }
5633 } else {
5634 // Create mapping from PrintObject* to ObjectByExtruder*.
5635 std::vector<std::pair<const PrintObject*, ObjectByExtruder*>> sorted;
5636 sorted.reserve(objects_by_extruder.size());
5637 for (ObjectByExtruder &object_by_extruder : objects_by_extruder) {
5638 const size_t layer_id = &object_by_extruder - objects_by_extruder.data();
5639 //BBS:add the support of shared print object
5640 const PrintObject *print_object = layers[layer_id].original_object;
5641 //const PrintObject *print_object = layers[layer_id].object();
5642 if (print_object)
5643 sorted.emplace_back(print_object, &object_by_extruder);
5644 }
5645 std::sort(sorted.begin(), sorted.end());
5646
5647 if (! sorted.empty()) {
5648 out.reserve(sorted.size());
5649 for (const PrintInstance *instance : *ordering) {
5650 const PrintObject &print_object = *instance->print_object;
5651 //BBS:add the support of shared print object
5652 //const PrintObject* print_obj_ptr = &print_object;
5653 //if (print_object.get_shared_object())
5654 // print_obj_ptr = print_object.get_shared_object();
5655 std::pair<const PrintObject*, ObjectByExtruder*> key(&print_object, nullptr);
5656 auto it = std::lower_bound(sorted.begin(), sorted.end(), key);
5657 if (it != sorted.end() && it->first == &print_object)
5658 // ObjectByExtruder for this PrintObject was found.
5659 out.emplace_back(*it->second, it->second - objects_by_extruder.data(), print_object, instance - print_object.instances().data(), instance->model_instance->get_labeled_id());
5660 }
5661 }
5662 }
5663 return out;
5664}

References Slic3r::PrintObject::instances(), layer_id(), and object_by_extruder.

Referenced by process_layer().

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

◆ synchronize_toolchange_position()

void Slic3r::GCode::synchronize_toolchange_position ( const ToolchangePositionState & before,
const std::string & emitted_gcode )
protectedinherited
8378{
8379 if (m_writer.filament() == nullptr)
8380 return;
8381
8382 const std::optional<Vec2d> emitted_xy = emitted_xy_after_gcode(emitted_gcode, before.emitted_xy);
8383 if (!emitted_xy)
8384 return;
8385
8386 // GCodeWriter stores coordinates before applying the per-plate output
8387 // offset. Convert the parsed file coordinates back into that space, then
8388 // into model space using the newly active nozzle offset.
8389 const Vec2d writer_xy = *emitted_xy + m_writer.get_xy_offset().cast<double>();
8390 Vec3d writer_position = m_writer.get_position();
8391 writer_position.head<2>() = writer_xy;
8392 m_writer.set_position(writer_position);
8393 // Updating the known XY origin must not cancel Orca's deliberate
8394 // post-toolchange Z restore. set_last_pos() normally marks the entire
8395 // position as defined, so preserve the caller's state across the XY sync.
8397 this->set_last_pos(this->gcode_to_point(writer_xy));
8399}
std::optional< Vec2d > emitted_xy_after_gcode(const std::string &gcode, const Vec2d &initial_xy)
Definition GCode.cpp:161

References Slic3r::GCode::ToolchangePositionState::emitted_xy, gcode_to_point(), last_pos_defined(), m_last_pos_defined, m_writer, and set_last_pos().

Referenced by Slic3r::WipeTowerIntegration::append_tcr(), Slic3r::WipeTowerIntegration::append_tcr2(), and set_extruder().

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

◆ timelapse_gcode_config()

DynamicConfig Slic3r::GCode::timelapse_gcode_config ( const Print & print,
coordf_t print_z,
int most_used_extruder,
const std::vector< const PrintObject * > * printed_objects,
bool skip_pos_pick )
protectedinherited
6519{
6520 assert(m_writer.filament());
6521 PosPickCtx ctx;
6522 ctx.curr_pos = { (coord_t)(scale_(m_writer.get_position().x())),(coord_t)(scale_(m_writer.get_position().y())) };
6523 ctx.curr_layer = this->layer();
6524 ctx.curr_extruder_id = m_writer.filament()->extruder_id();
6525 ctx.picture_extruder_id = most_used_extruder;
6526 if (m_farthest_point_timelapse.enabled) {
6527 // farthest_point is stored in the global print frame (includes plate origin); the picker works in
6528 // the plate-relative frame, so subtract the plate origin here.
6529 Vec3d po = print.get_plate_origin();
6530 ctx.farthest_point = m_farthest_point_timelapse.farthest_point - Point(scale_(po.x()), scale_(po.y()));
6531 }
6532 if (m_config.print_sequence == PrintSequence::ByObject && printed_objects)
6533 ctx.printed_objects = *printed_objects;
6534
6535 auto timelapse_pos = (skip_pos_pick || !m_timelapse_uses_pos) ? DefaultTimelapsePos : m_timelapse_pos_picker.pick_pos(ctx);
6536
6537 DynamicConfig config;
6538 config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
6539 config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
6540 config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z));
6541 config.set_key_value("most_used_physical_extruder_id", new ConfigOptionInt(m_config.physical_extruder_map.get_at(most_used_extruder)));
6542 config.set_key_value("curr_physical_extruder_id", new ConfigOptionInt(m_config.physical_extruder_map.get_at(ctx.curr_extruder_id)));
6543 config.set_key_value("timelapse_pos_x", new ConfigOptionInt((int)timelapse_pos.x()));
6544 config.set_key_value("timelapse_pos_y", new ConfigOptionInt((int)timelapse_pos.y()));
6545 config.set_key_value("has_timelapse_safe_pos", new ConfigOptionBool(timelapse_pos != DefaultTimelapsePos));
6546 config.set_key_value("timelapse_inline_photo", new ConfigOptionBool(skip_pos_pick));
6547 config.set_key_value("farthest_point_timelapse_enabled", new ConfigOptionBool(m_farthest_point_timelapse.enabled));
6548 config.set_key_value("clear_to_x0", new ConfigOptionBool(m_timelapse_uses_clear_to_x0 && m_timelapse_pos_picker.get_is_clear_to_x0(ctx)));
6549 return config;
6550}
const Point DefaultTimelapsePos
Definition TimelapsePosPicker.hpp:18

References Slic3r::ByObject, config(), Slic3r::ConfigOptionFloat, Slic3r::PosPickCtx::curr_extruder_id, Slic3r::PosPickCtx::curr_layer, Slic3r::PosPickCtx::curr_pos, Slic3r::DefaultTimelapsePos, Slic3r::PosPickCtx::farthest_point, Slic3r::Print::get_plate_origin(), layer(), m_config, m_farthest_point_timelapse, m_layer_index, m_max_layer_z, m_timelapse_pos_picker, m_timelapse_uses_clear_to_x0, m_timelapse_uses_pos, m_writer, Slic3r::PosPickCtx::picture_extruder_id, Print, Slic3r::PosPickCtx::printed_objects, and scale_.

Referenced by generate_timelapse_gcode(), and process_layer().

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

◆ to_lift_type()

LiftType Slic3r::GCode::to_lift_type ( ZHopType z_hop_types)
protectedinherited
10549 {
10550 switch (z_hop_types)
10551 {
10553 return LiftType::NormalLift;
10554 case ZHopType::zhtSlope:
10555 return LiftType::SlopeLift;
10557 return LiftType::SpiralLift;
10558 default:
10559 // if no corresponding lift type, use normal lift
10560 return LiftType::NormalLift;
10561 }
10562};
@ zhtNormal
Definition PrintConfig.hpp:584

References Slic3r::NormalLift, Slic3r::SlopeLift, Slic3r::SpiralLift, Slic3r::zhtNormal, Slic3r::zhtSlope, and Slic3r::zhtSpiral.

Referenced by Slic3r::WipeTowerIntegration::append_tcr2(), change_layer(), and needs_retraction().

Here is the caller graph for this function:

◆ travel_to()

std::string Slic3r::GCode::travel_to ( const Point & point,
ExtrusionRole role,
std::string comment,
double z = DBL_MAX )
inherited
10286{
10287 const std::optional<GCodeProcessor::PositionState> start_gcode_position =
10289 !m_writer.is_current_position_clear() && m_pending_start_gcode_position.has_value() ?
10290 m_pending_start_gcode_position : std::nullopt;
10291
10292 /* Define the travel move as a line between current position and the taget point.
10293 This is expressed in print coordinates, so it will need to be translated by
10294 this->origin in order to get G-code coordinates. */
10295 const Point travel_start = start_gcode_position ?
10296 this->gcode_to_point(to_2d(start_gcode_position->position)) : this->last_pos();
10297 Polyline travel { travel_start, point };
10298
10299 // check whether a straight travel move would need retraction
10300 LiftType lift_type = LiftType::SpiralLift;
10301 bool needs_retraction = this->needs_retraction(travel, role, lift_type);
10302 // check whether wipe could be disabled without causing visible stringing
10303 bool could_be_wipe_disabled = false;
10304 // Save state of use_external_mp_once for the case that will be needed to call twice m_avoid_crossing_perimeters.travel_to.
10305 const bool used_external_mp_once = m_avoid_crossing_perimeters.used_external_mp_once();
10306 std::string gcode;
10307 bool exclusion_volume_travel_rerouted = false;
10308
10309 const auto active_extruder_id = [this]() {
10310 if (m_writer.filament() == nullptr)
10311 return -1;
10312 const size_t id = m_writer.filament()->extruder_id();
10313 return id < m_config.nozzle_diameter.size() ? int(id) : -1;
10314 };
10315 const auto to_gcode_point = [this](const Point &local_point) {
10316 const Vec2d point = this->point_to_gcode(local_point);
10317 return Point(scale_(point.x()), scale_(point.y()));
10318 };
10319 const auto to_local_point = [this](const Point &gcode_point) {
10320 return this->gcode_to_point(unscaled(gcode_point));
10321 };
10322 const auto to_gcode_polyline = [&to_gcode_point](const Polyline &local_path) {
10323 Polyline result;
10324 result.points.reserve(local_path.points.size());
10325 for (const Point &local_point : local_path.points)
10326 result.append(to_gcode_point(local_point));
10327 return result;
10328 };
10329 const auto to_local_polyline = [&to_local_point](const Polyline &gcode_path) {
10330 Polyline result;
10331 result.points.reserve(gcode_path.points.size());
10332 for (const Point &gcode_point : gcode_path.points)
10333 result.append(to_local_point(gcode_point));
10334 return result;
10335 };
10336 const auto nominal_writer_z = [this]() {
10337 return m_writer.get_position().z() - m_writer.get_zhop();
10338 };
10339 const auto route_around_exclusion_volumes = [&](
10340 Polyline &path,
10341 const std::optional<double> &start_z_override,
10342 const bool include_planned_lift = false) {
10343 if (m_exclusion_volume_travel_avoidance.empty() || path.size() < 2)
10344 return false;
10345
10346 const double target_z = z == DBL_MAX ? m_nominal_z : z;
10347 double route_start_z = start_z_override.value_or(nominal_writer_z());
10348 double route_end_z = target_z;
10349 if (include_planned_lift) {
10350 const Vec2d destination_xy = this->point_to_gcode(path.back());
10351 std::tie(route_start_z, route_end_z) = m_writer.planned_travel_z(
10352 Vec3d(destination_xy.x(), destination_xy.y(), target_z),
10354 }
10355 const ExclusionVolumeTravelAvoidance::Result result =
10357 to_gcode_polyline(path),
10358 route_start_z,
10359 route_end_z,
10360 active_extruder_id());
10361 if (!result.rerouted())
10362 return false;
10363
10364 path = to_local_polyline(result.path);
10365 return true;
10366 };
10367
10368 // Orca: we don't need to optimize the Klipper as only set once
10369 double jerk_to_set = 0.0;
10370 unsigned int acceleration_to_set = 0;
10371
10372 if (this->on_first_layer()) {
10373 unsigned int initial_layer_travel_acceleration = m_config.get_abs_value_at("initial_layer_travel_acceleration", get_nozzle_config_index(m_writer.filament()->id()));
10374 double initial_layer_travel_jerk = m_config.get_abs_value_at("initial_layer_travel_jerk", get_nozzle_config_index(m_writer.filament()->id()));
10375
10376 if (NOZZLE_CONFIG(default_acceleration) > 0 && initial_layer_travel_acceleration > 0) {
10377 acceleration_to_set = (unsigned int) floor(initial_layer_travel_acceleration + 0.5);
10378 }
10379 if (NOZZLE_CONFIG(default_jerk)> 0 && initial_layer_travel_jerk > 0) {
10380 jerk_to_set = initial_layer_travel_jerk;
10381 }
10382 } else { // ORCA: Handle short-travel acceleration and jerk for outer perimeters (if applicable)
10383 const bool is_short_travel = travel.length() < scale_(EXTRUDER_CONFIG(retraction_minimum_travel));
10384
10385 if (NOZZLE_CONFIG(default_acceleration) > 0) {
10386 if (role == erOverhangPerimeter && is_short_travel) {
10387 const double bridge_acceleration = m_config.get_abs_value_at("bridge_acceleration", get_nozzle_config_index(m_writer.filament()->id()));
10388
10389 if (bridge_acceleration > 0)
10390 acceleration_to_set = (unsigned int) floor(bridge_acceleration + 0.5);
10391 } else if (role == erExternalPerimeter && is_short_travel) {
10392 if (NOZZLE_CONFIG(outer_wall_acceleration) > 0)
10393 acceleration_to_set = (unsigned int) floor(NOZZLE_CONFIG(outer_wall_acceleration) + 0.5);
10394 } else {
10395 if (NOZZLE_CONFIG(travel_acceleration) > 0)
10396 acceleration_to_set = (unsigned int) floor(NOZZLE_CONFIG(travel_acceleration) + 0.5);
10397 }
10398 }
10399
10400 if (NOZZLE_CONFIG(default_jerk) > 0) {
10401 if ((role == erExternalPerimeter || role == erOverhangPerimeter) && is_short_travel) {
10402 if (NOZZLE_CONFIG(outer_wall_jerk) > 0)
10403 jerk_to_set = NOZZLE_CONFIG(outer_wall_jerk);
10404 } else {
10405 if (NOZZLE_CONFIG(travel_jerk) > 0)
10406 jerk_to_set = NOZZLE_CONFIG(travel_jerk);
10407 }
10408 }
10409 }
10410
10411 if (m_writer.get_gcode_flavor() == gcfKlipper) {
10412 gcode += m_writer.set_accel_and_jerk(acceleration_to_set, jerk_to_set);
10413 } else {
10414 gcode += m_writer.set_travel_acceleration(acceleration_to_set);
10415 gcode += m_writer.set_jerk_xy(jerk_to_set);
10416 }
10417
10418 // if a retraction would be needed, try to use reduce_crossing_wall to plan a
10419 // multi-hop travel path inside the configuration space
10420 if (m_config.reduce_crossing_wall
10421 && !m_avoid_crossing_perimeters.disabled_once()
10422 && m_layer != nullptr // A brim apron layer has no Layer to avoid crossing
10423 && m_writer.is_current_position_clear())
10424 //BBS: don't generate detour travel paths when current position is unclea
10425 {
10426 travel = m_avoid_crossing_perimeters.travel_to(*this, point, &could_be_wipe_disabled);
10427 // check again whether the new travel path still needs a retraction
10428 needs_retraction = this->needs_retraction(travel, role, lift_type);
10429 //if (needs_retraction && m_layer_index > 1) exit(0);
10430 }
10431
10432 const std::optional<double> start_gcode_z =
10433 start_gcode_position && start_gcode_position->has_known_z() ?
10434 std::optional<double>(start_gcode_position->position.z()) : std::nullopt;
10435 if (route_around_exclusion_volumes(travel, start_gcode_z)) {
10436 exclusion_volume_travel_rerouted = true;
10437 needs_retraction = this->needs_retraction(travel, role, lift_type);
10438 }
10439
10440 // Re-allow reduce_crossing_wall for the next travel moves
10441 m_avoid_crossing_perimeters.reset_once_modifiers();
10442
10443 // generate G-code for the travel move
10444 if (needs_retraction) {
10445 // ORCA: Fix scenario where wipe is disabled when avoid crossing perimeters was enabled even though a retraction move was performed.
10446 // This replicates the existing behaviour of always wiping when retracting
10447 /*if (m_config.reduce_crossing_wall && could_be_wipe_disabled)
10448 m_wipe.reset_path();*/
10449
10450 Point last_post_before_retract = this->last_pos();
10451 gcode += this->retract(false, false, lift_type, false, role);
10452 // When "Wipe while retracting" is enabled, then extruder moves to another position, and travel from this position can cross perimeters.
10453 // Because of it, it is necessary to call avoid crossing perimeters again with new starting point after calling retraction()
10454 // FIXME Lukas H.: Try to predict if this second calling of avoid crossing perimeters will be needed or not. It could save computations.
10455 if (last_post_before_retract != this->last_pos()) {
10456 travel = Polyline { this->last_pos(), point };
10457 if (m_config.reduce_crossing_wall && m_layer != nullptr) { // Belt brim apron layers have no Layer.
10458 // If in the previous call of m_avoid_crossing_perimeters.travel_to was use_external_mp_once set to true restore this value for next call.
10459 if (used_external_mp_once)
10460 m_avoid_crossing_perimeters.use_external_mp_once();
10461 travel = m_avoid_crossing_perimeters.travel_to(*this, point);
10462 // If state of use_external_mp_once was changed reset it to right value.
10463 if (used_external_mp_once)
10464 m_avoid_crossing_perimeters.reset_once_modifiers();
10465 }
10466 }
10467
10468 exclusion_volume_travel_rerouted =
10469 route_around_exclusion_volumes(travel, std::nullopt, true) ||
10470 (last_post_before_retract == this->last_pos() && exclusion_volume_travel_rerouted);
10471 } else {
10472 // Reset the wipe path when traveling, so one would not wipe along an old path.
10473 m_wipe.reset_path();
10474 // if (m_config.reduce_crossing_wall) {
10475 // // If in the previous call of m_avoid_crossing_perimeters.travel_to was use_external_mp_once set to true restore this value for next call.
10476 // if (used_external_mp_once) m_avoid_crossing_perimeters.use_external_mp_once();
10477 // travel = m_avoid_crossing_perimeters.travel_to(*this, point);
10478 // // If state of use_external_mp_once was changed reset it to right value.
10479 // if (used_external_mp_once) m_avoid_crossing_perimeters.reset_once_modifiers();
10480 // }
10481 }
10482
10483
10484 // if needed, write the gcode_label_objects_end then gcode_label_objects_start
10485 m_writer.add_object_change_labels(gcode);
10486
10487 // use G1 because we rely on paths being straight (G0 may make round paths)
10488 if (travel.size() >= 2) {
10489 // Orca: use `travel_to_xyz` to ensure we start at the correct z, in case we moved z in custom/filament change gcode
10490 if (false/*m_spiral_vase*/) {
10491 // No lazy z lift for spiral vase mode
10492 for (size_t i = 1; i < travel.size(); ++i) {
10493 gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment);
10494 }
10495 } else {
10496 if (travel.size() == 2) {
10497 // No extra movements emitted by avoid_crossing_perimeters, simply move to the end point with z change
10498 const auto& dest2d = this->point_to_gcode(travel.points.back());
10499 Vec3d dest3d(dest2d(0), dest2d(1), z == DBL_MAX ? m_nominal_z : z);
10500 gcode += m_writer.travel_to_xyz(dest3d, comment, m_need_change_layer_lift_z);
10502 } else if (exclusion_volume_travel_rerouted && z != DBL_MAX) {
10503 // Preserve the original sloped-Z move over the longer detour.
10504 // The router activates every volume touched by the complete Z
10505 // range, so this interpolation remains conservative.
10506 const double start_z = m_writer.get_position().z();
10507 const double total_length = std::max(travel.length(), 1.0);
10508 double traveled = 0.0;
10509 for (size_t i = 1; i < travel.size(); ++i) {
10510 traveled += (travel.points[i] - travel.points[i - 1]).cast<double>().norm();
10511 const Vec2d dest2d = this->point_to_gcode(travel.points[i]);
10512 const Vec3d dest3d(
10513 dest2d.x(),
10514 dest2d.y(),
10515 start_z + (z - start_z) * std::min(1.0, traveled / total_length));
10516 gcode += m_writer.travel_to_xyz(dest3d, comment, i == 1 && m_need_change_layer_lift_z);
10517 if (i == 1)
10519 }
10520 } else {
10521 // Extra movements emitted by avoid_crossing_perimeters, lift the z to normal height at the beginning, then apply the z
10522 // ratio at the last point
10523 for (size_t i = 1; i < travel.size(); ++i) {
10524 if (i == 1) {
10525 // Lift to normal z at beginning
10526 Vec2d dest2d = this->point_to_gcode(travel.points[i]);
10527 Vec3d dest3d(dest2d(0), dest2d(1), m_nominal_z);
10528 gcode += m_writer.travel_to_xyz(dest3d, comment, m_need_change_layer_lift_z);
10530 } else if (z != DBL_MAX && i == travel.size() - 1) {
10531 // Apply z_ratio for the very last point
10532 Vec2d dest2d = this->point_to_gcode(travel.points[i]);
10533 Vec3d dest3d(dest2d(0), dest2d(1), z);
10534 gcode += m_writer.travel_to_xyz(dest3d, comment);
10535 } else {
10536 // For all points in between, no z change
10537 gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment);
10538 }
10539 }
10540 }
10541 }
10542 this->set_last_pos(travel.points.back());
10543 }
10544
10545 return gcode;
10546}
bool needs_retraction(const Polyline &travel, ExtrusionRole role, LiftType &lift_type)
Definition GCode.cpp:10564
zaa_minimize_perimeter_height initial_layer_travel_jerk((ConfigOptionBool, bbl_calib_mark_logo))((ConfigOptionBool
Eigen::Matrix< typename Derived::Scalar, 2, 1, Eigen::DontAlign > to_2d(const Eigen::MatrixBase< Derived > &ptN)
Definition Point.hpp:147

References Slic3r::Polyline::append(), Slic3r::MultiPoint::back(), Slic3r::cast(), Slic3r::erExternalPerimeter, Slic3r::erOverhangPerimeter, EXTRUDER_CONFIG, Slic3r::gcfKlipper, gcode_to_point(), get_nozzle_config_index(), Slic3r::initial_layer_travel_jerk(), last_pos(), Slic3r::MultiPoint::length(), m_avoid_crossing_perimeters, m_config, m_exclusion_volume_travel_avoidance, m_last_pos_defined, m_layer, m_need_change_layer_lift_z, m_nominal_z, m_pending_start_gcode_position, m_wipe, m_writer, needs_retraction(), NOZZLE_CONFIG, on_first_layer(), Slic3r::ExclusionVolumeTravelAvoidance::Result::path, point_to_gcode(), Slic3r::MultiPoint::points, Slic3r::ExclusionVolumeTravelAvoidance::Result::rerouted(), retract(), scale_, set_last_pos(), Slic3r::MultiPoint::size(), Slic3r::SpiralLift, Slic3r::to_2d(), Slic3r::total_length(), and Slic3r::unscaled().

Referenced by _do_export(), _extrude(), Slic3r::WipeTowerIntegration::append_tcr(), Slic3r::WipeTowerIntegration::append_tcr2(), and Slic3r::WipeTowerIntegration::travel_to_tower_gap().

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

◆ unretract()

std::string Slic3r::GCode::unretract ( float extra_retract = 0.f)
inlineinherited
311{ return m_writer.unlift() + m_writer.unretract(extra_retract); }

References m_writer.

Referenced by _extrude(), Slic3r::WipeTowerIntegration::append_tcr(), Slic3r::WipeTowerIntegration::append_tcr2(), and process_layer().

Here is the caller graph for this function:

◆ update_layer_related_config()

void Slic3r::GCode::update_layer_related_config ( int layer_id)
protectedinherited
10750 {
10751 auto group_result = m_print->get_layered_nozzle_group_result();
10752 // Orca: defensive — with no published group result the statically applied config maps stay
10753 // authoritative.
10754 if(!group_result)
10755 return;
10756
10757 auto extruder_map = group_result->get_extruder_map(false,layer_id);
10758 auto volume_map = group_result->get_volume_map(layer_id);
10759 auto nozzle_map = group_result->get_nozzle_map(layer_id);
10760
10761 m_config.filament_map.values = extruder_map;
10762 m_config.filament_volume_map.values = volume_map;
10763 m_config.filament_nozzle_map.values = nozzle_map;
10764
10765 m_writer.config.filament_map.values = extruder_map;
10766 m_writer.config.filament_volume_map.values = volume_map;
10767 m_writer.config.filament_nozzle_map.values = nozzle_map;
10768
10769}

References layer_id(), m_config, m_print, and m_writer.

Referenced by _do_export(), and process_layer().

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

◆ update_placeholder_parser_with_variant_params()

void Slic3r::GCode::update_placeholder_parser_with_variant_params ( )
protectedinherited
10772{
10773 if (!m_print)
10774 return;
10775
10776 size_t num_filaments = m_config.filament_type.values.size();
10777 if (num_filaments == 0)
10778 return;
10779
10780 // Helper: remap config arrays from variant index space to filament_id index space.
10781 // After remapping, gcode templates can use param[filament_id] directly.
10782 std::vector<size_t> config_index(num_filaments);
10783 for (size_t i = 0; i < num_filaments; ++i)
10784 config_index[i] = get_filament_config_index(i);
10785 auto remap_by_filament = [&](const auto &src) {
10786 std::decay_t<decltype(src.values)> dst(num_filaments);
10787 for (size_t i = 0; i < num_filaments; ++i)
10788 dst[i] = src.get_at(config_index[i]);
10789 return dst;
10790 };
10791
10792 // --- filament_options_with_variant: gcode indexes by filament_id ---
10793 this->placeholder_parser().set("filament_max_volumetric_speed", new ConfigOptionFloats(remap_by_filament(m_config.filament_max_volumetric_speed)));
10794 this->placeholder_parser().set("filament_pre_cooling_temperature", new ConfigOptionInts(remap_by_filament(m_config.filament_pre_cooling_temperature)));
10795 this->placeholder_parser().set("filament_pre_cooling_temperature_nc", new ConfigOptionInts(remap_by_filament(m_config.filament_pre_cooling_temperature_nc)));
10796 this->placeholder_parser().set("filament_cooling_before_tower", new ConfigOptionFloats(remap_by_filament(m_config.filament_cooling_before_tower)));
10797 this->placeholder_parser().set("nozzle_temperature_initial_layer", new ConfigOptionInts(remap_by_filament(m_config.nozzle_temperature_initial_layer)));
10798 this->placeholder_parser().set("nozzle_temperature", new ConfigOptionInts(remap_by_filament(m_config.nozzle_temperature)));
10799 // first_layer_temperature is a legacy alias of nozzle_temperature_initial_layer
10800 this->placeholder_parser().set("first_layer_temperature", new ConfigOptionInts(remap_by_filament(m_config.nozzle_temperature_initial_layer)));
10801 this->placeholder_parser().set("pressure_advance", new ConfigOptionFloats(remap_by_filament(m_config.pressure_advance)));
10802 this->placeholder_parser().set("enable_pressure_advance", new ConfigOptionBools(remap_by_filament(m_config.enable_pressure_advance)));
10803 this->placeholder_parser().set("fan_min_speed", new ConfigOptionFloats(remap_by_filament(m_config.fan_min_speed)));
10804 this->placeholder_parser().set("fan_max_speed", new ConfigOptionFloats(remap_by_filament(m_config.fan_max_speed)));
10805 this->placeholder_parser().set("additional_cooling_fan_speed", new ConfigOptionInts(remap_by_filament(m_config.additional_cooling_fan_speed)));
10806 this->placeholder_parser().set("filament_minimal_purge_on_wipe_tower", new ConfigOptionFloats(remap_by_filament(m_config.filament_minimal_purge_on_wipe_tower)));
10807 this->placeholder_parser().set("filament_multitool_ramming", new ConfigOptionBools(remap_by_filament(m_config.filament_multitool_ramming)));
10808 this->placeholder_parser().set("filament_multitool_ramming_volume", new ConfigOptionFloats(remap_by_filament(m_config.filament_multitool_ramming_volume)));
10809 this->placeholder_parser().set("filament_multitool_ramming_flow", new ConfigOptionFloats(remap_by_filament(m_config.filament_multitool_ramming_flow)));
10810 this->placeholder_parser().set("nozzle_temperature_range_low", new ConfigOptionInts(remap_by_filament(m_config.nozzle_temperature_range_low)));
10811 const auto nozzle_temperature_range_high = remap_by_filament(m_config.nozzle_temperature_range_high);
10812 this->placeholder_parser().set("nozzle_temperature_range_high", new ConfigOptionInts(nozzle_temperature_range_high));
10813
10814 // --- printer_options_with_variant_1: in m_config these are already merged as filament-indexed ---
10815 this->placeholder_parser().set("retraction_distances_when_cut", new ConfigOptionFloats(remap_by_filament(m_config.retraction_distances_when_cut)));
10816 // hotend_cooling_rate / hotend_heating_rate: gcode uses [filament_map[x]-1] (extruder_id), no remap needed
10817
10818 // --- filament_map: per-layer dynamic, sync from m_config to placeholder_parser ---
10819 this->placeholder_parser().set("filament_map", new ConfigOptionInts(m_config.filament_map));
10820
10821 // --- flush_volumetric_speeds / flush_temperatures: derived with fallback ---
10822 {
10823 // Fast purge mode uses filament_flush_temp_fast; Default is inert.
10824 bool use_fast_flush = m_config.prime_volume_mode == PrimeVolumeMode::pvmFast;
10825 auto flush_v_speed = remap_by_filament(m_config.filament_flush_volumetric_speed);
10826 auto filament_max_v = remap_by_filament(m_config.filament_max_volumetric_speed);
10827 auto flush_temps = remap_by_filament(use_fast_flush ? m_config.filament_flush_temp_fast
10828 : m_config.filament_flush_temp);
10829 for (size_t i = 0; i < num_filaments; ++i) {
10830 if (flush_v_speed[i] == 0)
10831 flush_v_speed[i] = filament_max_v[i];
10832 if (flush_temps[i] == 0)
10833 flush_temps[i] = nozzle_temperature_range_high[i];
10834 }
10835 this->placeholder_parser().set("flush_volumetric_speeds", new ConfigOptionFloats(flush_v_speed));
10836 this->placeholder_parser().set("flush_temperatures", new ConfigOptionInts(flush_temps));
10837 }
10838}
zaa_minimize_perimeter_height nozzle_temperature_range_high((ConfigOptionFloats, wipe_distance))((ConfigOptionBool

References get_filament_config_index(), m_config, m_print, Slic3r::nozzle_temperature_range_high(), placeholder_parser(), Slic3r::pvmFast, and Slic3r::PlaceholderParser::set().

Referenced by _do_export(), and process_layer().

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

◆ wipe_tower_type()

WipeTowerType Slic3r::GCode::wipe_tower_type ( )
inherited
2656{
2657 if (m_curr_print)
2658 return m_curr_print->wipe_tower_type();
2659 return WipeTowerType::Type2;
2660}

References m_curr_print, and Slic3r::Type2.

Referenced by _do_export(), Slic3r::WipeTowerIntegration::finalize(), Slic3r::WipeTowerIntegration::prime(), and Slic3r::WipeTowerIntegration::tool_change().

Here is the caller graph for this function:

◆ write_belt_header()

void Slic3r::BeltGCode::write_belt_header ( GCodeOutputStream & file,
const Print & print )
overrideprotectedvirtual

Reimplemented from Slic3r::GCode.

22{
23 const auto &full_cfg = print.full_print_config();
24 // Slicing rotation: the belt tilt (axis + angle) and the single source of truth
25 // for the physical tilt the G-code viewer uses to enable belt view.
26 file.write_format("; belt_slice_rotation = %s\n", full_cfg.opt_serialize("belt_slice_rotation").c_str());
27 file.write_format("; belt_slice_rotation_angle = %.1f\n", print.config().belt_slice_rotation_angle.value);
28 // Machine-frame transform: shear (cot) + scale (1/|sin|) derived from the belt
29 // tilt angle (or belt_frame_tilt_angle when decoupled).
30 file.write_format("; belt_frame_tilt_decouple = %d\n", print.config().belt_frame_tilt_decouple.value ? 1 : 0);
31 file.write_format("; belt_frame_tilt_angle = %.1f\n", print.config().belt_frame_tilt_angle.value);
32}

References Slic3r::Print::config(), Slic3r::PrintBase::full_print_config(), Slic3r::GCode::Print, and Slic3r::GCode::GCodeOutputStream::write_format().

Here is the call graph for this function:

◆ writer() [1/2]

◆ writer() [2/2]

const GCodeWriter & Slic3r::GCode::writer ( ) const
inlineinherited
288{ return m_writer; }

References m_writer.

Member Data Documentation

◆ m_avoid_crossing_perimeters

◆ m_belt_brim_z

std::optional<coordf_t> Slic3r::GCode::m_belt_brim_z
protectedinherited

◆ m_belt_floor_object

const PrintObject* Slic3r::GCode::m_belt_floor_object {nullptr}
protectedinherited

◆ m_belt_in_band

bool Slic3r::GCode::m_belt_in_band {false}
protectedinherited
964{false};

Referenced by _do_export(), and _extrude().

◆ m_brim_done

bool Slic3r::GCode::m_brim_done
protectedinherited

Referenced by GCode(), and _do_export().

◆ m_calib_config

DynamicConfig Slic3r::GCode::m_calib_config
protectedinherited

Referenced by _do_export(), _extrude(), and process_layer().

◆ m_config

◆ m_cooling_buffer

std::unique_ptr<CoolingBuffer> Slic3r::GCode::m_cooling_buffer
protectedinherited

Referenced by _do_export().

◆ m_cur_layer_idx

size_t Slic3r::GCode::m_cur_layer_idx {0}
protectedinherited

◆ m_curr_print

Print* Slic3r::GCode::m_curr_print = nullptr
protectedinherited

◆ m_enable_cooling_markers

bool Slic3r::GCode::m_enable_cooling_markers
protectedinherited

◆ m_enable_exclude_object

bool Slic3r::GCode::m_enable_exclude_object
protectedinherited

◆ m_enable_extrusion_role_markers

bool Slic3r::GCode::m_enable_extrusion_role_markers
protectedinherited

Referenced by GCode(), _do_export(), and _extrude().

◆ m_enable_loop_clipping

bool Slic3r::GCode::m_enable_loop_clipping
protectedinherited

Referenced by GCode(), extrude_loop(), and process_layer().

◆ m_exclusion_volume_travel_avoidance

ExclusionVolumeTravelAvoidance Slic3r::GCode::m_exclusion_volume_travel_avoidance
protectedinherited

◆ m_extrusion_quality_estimator

ExtrusionQualityEstimator Slic3r::GCode::m_extrusion_quality_estimator
protectedinherited

◆ m_fan_mover

std::unique_ptr<FanMover> Slic3r::GCode::m_fan_mover
protectedinherited

Referenced by _do_export().

◆ m_farthest_point_timelapse

FarthestPointTimelapseContext Slic3r::GCode::m_farthest_point_timelapse
protectedinherited

◆ m_filament_index_cache

ConfigIndexCache Slic3r::GCode::m_filament_index_cache
mutableprotectedinherited

◆ m_filament_instances_code

std::string Slic3r::GCode::m_filament_instances_code
protectedinherited

◆ m_imex_head_filament_map

std::map<int,int> Slic3r::GCode::m_imex_head_filament_map
protectedinherited

Referenced by _do_export(), and process_layer().

◆ m_imex_parallel_mode

std::string Slic3r::GCode::m_imex_parallel_mode
protectedinherited

◆ m_initial_layer_extruders

std::set<unsigned int> Slic3r::GCode::m_initial_layer_extruders
protectedinherited

Referenced by do_export().

◆ m_instance_names

std::unordered_map<const PrintInstance*, std::string> Slic3r::GCode::m_instance_names
protectedinherited

◆ m_is_role_based_fan_on

std::array<bool, ExtrusionRole::erCount> Slic3r::GCode::m_is_role_based_fan_on
protectedinherited

Referenced by _do_export(), and _extrude().

◆ m_label_objects_ids

std::vector<size_t> Slic3r::GCode::m_label_objects_ids
protectedinherited

◆ m_last_extrusion_role

ExtrusionRole Slic3r::GCode::m_last_extrusion_role
protectedinherited

Referenced by GCode(), and _extrude().

◆ m_last_height

float Slic3r::GCode::m_last_height { 0.0f }
protectedinherited

◆ m_last_layer_accumulated_mass

double Slic3r::GCode::m_last_layer_accumulated_mass { 0.0 }
protectedinherited
853{ 0.0 };

Referenced by _do_export(), and process_layer_change_gcode().

◆ m_last_layer_z

float Slic3r::GCode::m_last_layer_z { 0.0f }
protectedinherited
848{ 0.0f };

Referenced by _do_export(), process_belt_brim_layer(), and process_layer().

◆ m_last_mm3_mm

double Slic3r::GCode::m_last_mm3_mm = 0
protectedinherited

Referenced by _extrude().

◆ m_last_notgapfill_extrusion_role

ExtrusionRole Slic3r::GCode::m_last_notgapfill_extrusion_role
protectedinherited

Referenced by _extrude(), and retract().

◆ m_last_obj_copy

std::pair<const PrintObject*, Point> Slic3r::GCode::m_last_obj_copy
protectedinherited

Referenced by GCode().

◆ m_last_pos

◆ m_last_pos_defined

◆ m_last_processor_extrusion_role

ExtrusionRole Slic3r::GCode::m_last_processor_extrusion_role
protectedinherited

Referenced by GCode(), _extrude(), and needs_retraction().

◆ m_last_width

float Slic3r::GCode::m_last_width { 0.0f }
protectedinherited
850{ 0.0f };

Referenced by GCode(), _do_export(), and _extrude().

◆ m_layer

◆ m_layer_count

unsigned int Slic3r::GCode::m_layer_count
protectedinherited

◆ m_layer_index

◆ m_max_layer_z

float Slic3r::GCode::m_max_layer_z { 0.0f }
protectedinherited

◆ m_multi_flow_segment_path_average_mm3_per_mm

double Slic3r::GCode::m_multi_flow_segment_path_average_mm3_per_mm = 0
protectedinherited

◆ m_multi_flow_segment_path_pa_set

bool Slic3r::GCode::m_multi_flow_segment_path_pa_set = false
protectedinherited

◆ m_need_change_layer_lift_z

bool Slic3r::GCode::m_need_change_layer_lift_z = false
protectedinherited

◆ m_nominal_z

coordf_t Slic3r::GCode::m_nominal_z
protectedinherited

◆ m_nozzle_index_cache

ConfigIndexCache Slic3r::GCode::m_nozzle_index_cache
mutableprotectedinherited

Referenced by get_nozzle_config_index().

◆ m_object_layer_over_raft

bool Slic3r::GCode::m_object_layer_over_raft
protectedinherited

◆ m_objsWithBrim

std::set<ObjectInstanceID> Slic3r::GCode::m_objsWithBrim
protectedinherited

Referenced by _do_export(), and generate_object_brim().

◆ m_ooze_prevention

OozePrevention Slic3r::GCode::m_ooze_prevention
protectedinherited

◆ m_ordering_cache

std::map<unsigned int, IslandOrderCacheEntry> Slic3r::GCode::m_ordering_cache
protectedinherited

Referenced by _do_export(), and process_layer().

◆ m_origin

Vec2d Slic3r::GCode::m_origin
protectedinherited

◆ m_pa_processor

std::unique_ptr<AdaptivePAProcessor> Slic3r::GCode::m_pa_processor
protectedinherited

◆ m_pending_start_gcode_position

std::optional<GCodeProcessor::PositionState> Slic3r::GCode::m_pending_start_gcode_position
protectedinherited

◆ m_placeholder_parser_integration

struct Slic3r::GCode::PlaceholderParserIntegration Slic3r::GCode::m_placeholder_parser_integration
protectedinherited

◆ m_pressure_equalizer

std::unique_ptr<PressureEqualizer> Slic3r::GCode::m_pressure_equalizer
protectedinherited

◆ m_print

◆ m_printed_objects

std::vector<const PrintObject*> Slic3r::GCode::m_printed_objects
protectedinherited

Referenced by _do_export(), _extrude(), and process_layer().

◆ m_processor

GCodeProcessor Slic3r::GCode::m_processor
protectedinherited

◆ m_resonance_avoidance

bool Slic3r::GCode::m_resonance_avoidance
protectedinherited

Referenced by GCode(), and _extrude().

◆ m_retract_when_crossing_perimeters

RetractWhenCrossingPerimeters Slic3r::GCode::m_retract_when_crossing_perimeters
protectedinherited

Referenced by needs_retraction().

◆ m_role_based_fan_marker_layer

std::array<int, ExtrusionRole::erCount> Slic3r::GCode::m_role_based_fan_marker_layer
protectedinherited

Referenced by _do_export(), and _extrude().

◆ m_scaled_resolution

double Slic3r::GCode::m_scaled_resolution
protectedinherited

Referenced by apply_print_config().

◆ m_seam_placer

SeamPlacer Slic3r::GCode::m_seam_placer
protectedinherited

Referenced by _do_export(), and extrude_loop().

◆ m_second_layer_things_done

bool Slic3r::GCode::m_second_layer_things_done
protectedinherited

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

◆ m_skirt_group_done

std::vector<std::vector<coordf_t> > Slic3r::GCode::m_skirt_group_done
protectedinherited

◆ m_small_area_infill_flow_compensator

std::unique_ptr<SmallAreaInfillFlowCompensator> Slic3r::GCode::m_small_area_infill_flow_compensator
protectedinherited

Referenced by _do_export(), _extrude(), and _needSAFC().

◆ m_sorted_layer_filaments

std::vector<std::vector<unsigned int> > Slic3r::GCode::m_sorted_layer_filaments
protectedinherited

◆ m_spiral_vase

std::unique_ptr<SpiralVase> Slic3r::GCode::m_spiral_vase
protectedinherited

◆ m_start_gcode_filament

int Slic3r::GCode::m_start_gcode_filament = -1
protectedinherited

Referenced by _do_export(), and set_extruder().

◆ m_sub_layer_flow_ratio

double Slic3r::GCode::m_sub_layer_flow_ratio = 0.0
protectedinherited

Referenced by _extrude().

◆ m_sub_layer_height

double Slic3r::GCode::m_sub_layer_height = 0.0
protectedinherited

Referenced by _extrude().

◆ m_support_traditional_timelapse

bool Slic3r::GCode::m_support_traditional_timelapse = true
protectedinherited

Referenced by do_export().

◆ m_timelapse_pos_picker

TimelapsePosPicker Slic3r::GCode::m_timelapse_pos_picker
protectedinherited

◆ m_timelapse_uses_clear_to_x0

bool Slic3r::GCode::m_timelapse_uses_clear_to_x0 { false }
protectedinherited
782{ false };

Referenced by _do_export(), and timelapse_gcode_config().

◆ m_timelapse_uses_pos

bool Slic3r::GCode::m_timelapse_uses_pos { false }
protectedinherited
781{ false };

Referenced by _do_export(), and timelapse_gcode_config().

◆ m_timelapse_warning_code

int Slic3r::GCode::m_timelapse_warning_code = 0
protectedinherited

Referenced by do_export().

◆ m_toolchange_count

unsigned int Slic3r::GCode::m_toolchange_count
protectedinherited

◆ m_wipe

◆ m_wipe_tower

std::unique_ptr<WipeTowerIntegration> Slic3r::GCode::m_wipe_tower
protectedinherited

◆ m_writer


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