Sync WipeTower from BambuStudio(through ca1881761)

This commit is contained in:
SoftFever
2026-07-26 01:22:53 +08:00
parent a62fb17e03
commit 7a378d2fc4
6 changed files with 1453 additions and 1002 deletions

View File

@@ -998,6 +998,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
std::string change_filament_gcode = gcodegen.config().change_filament_gcode.value;
bool is_used_travel_avoid_perimeter = gcodegen.m_config.prime_tower_skip_points.value;
if (is_nozzle_change && !tcr.nozzle_change_result.is_extruder_change) is_used_travel_avoid_perimeter = false;
// add nozzle change gcode into change filament gcode
std::string nozzle_change_gcode_trans;
@@ -1307,20 +1308,23 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
}
// do unretract after setting current extruder_id
// PETG filaments on a device with a filament switcher get a small (2 mm) pre-extrusion
// before the tool change. has_filament_switcher is a develop-only key read defensively from the
// full config (Orca does not carry it as a static PrintConfig member — same convention as
// enable_filament_dynamic_map); no shipping profile sets it (grep resources/profiles = 0), so
// is_petg_pre_extrusion is always false -> extra_unretract stays 0 -> byte-identical to the plain
// unretract() fleet-wide. The tower-interface contact pre-extrusion length (the
// is_contact_pre_extrusion branch) is NOT applied here; it is only computed as the guard used to
// give the contact path priority over PETG.
// BBS pattern: the wipe tower shifts the toolchange start position outward for the
// tower-interface (contact) pre-extrusion and for the PETG-with-filament-switcher case;
// the pre-extrusion material itself is laid down here as extra unretract on the approach.
// has_filament_switcher is a develop-only key read defensively from the full config (Orca
// does not carry it as a static PrintConfig member — same convention as
// enable_filament_dynamic_map); no shipping profile sets it, so is_petg_pre_extrusion is
// always false fleet-wide.
const ConfigOptionBool* has_filament_switcher_opt = gcodegen.m_print->full_print_config().option<ConfigOptionBool>("has_filament_switcher");
bool is_contact_pre_extrusion = tcr.is_contact && gcodegen.m_config.enable_tower_interface_features;
bool is_petg_pre_extrusion = !is_contact_pre_extrusion
&& gcodegen.config().filament_type.get_at(tcr.new_tool) == "PETG"
&& has_filament_switcher_opt && has_filament_switcher_opt->value;
float extra_unretract = is_petg_pre_extrusion ? 2.f : 0.f;
float extra_unretract = 0.f;
if (is_contact_pre_extrusion)
extra_unretract = gcodegen.m_config.filament_tower_interface_pre_extrusion_length.get_at(tcr.new_tool);
else if (is_petg_pre_extrusion)
extra_unretract = 2.f;
std::string toolchange_unretract_str = (extra_unretract > 0.f) ? gcodegen.unretract(extra_unretract) : gcodegen.unretract();
check_add_eol(toolchange_unretract_str);

View File

@@ -1450,8 +1450,8 @@ void GCodeProcessor::run_post_process()
// flag) runs none of this. It is pure data construction — it only fills m_filament_blocks /
// m_extruder_blocks / m_machine_*_gcode_*_line_id and never touches the exported g-code, so even
// the enable_pre_heating fleet stays byte-identical (nothing reads the blocks until the injection
// pass). In practice it also stays empty/degenerate today because no template/code yet emits the
// MACHINE_*_GCODE_* / NOZZLE_CHANGE_* / CP_TOOLCHANGE_WIPE markers it keys off.
// pass). The wipe tower emits the NOZZLE_CHANGE_* (ramming) and CP_TOOLCHANGE_WIPE markers this
// builder keys off; the MACHINE_*_GCODE_* markers come from the machine g-code templates.
m_filament_blocks.clear();
m_extruder_blocks.clear();
m_machine_start_gcode_end_line_id = (unsigned int) (-1);

File diff suppressed because it is too large Load Diff

View File

@@ -12,7 +12,7 @@
#include "libslic3r/Polyline.hpp"
#include "libslic3r/TriangleMesh.hpp"
#include <unordered_set>
#include "libslic3r/MultiNozzleUtils.hpp"
namespace Slic3r
{
@@ -84,7 +84,6 @@ public:
bool priming;
bool is_tool_change{false};
bool is_contact{false};
Vec2f tool_change_start_pos;
// Pass a polyline so that normal G-code generator can do a wipe for us.
@@ -108,6 +107,7 @@ public:
// executing the gcode finish_layer_tcr.
bool is_finish_first = false;
bool is_contact = false;
NozzleChangeResult nozzle_change_result;
// Sum the total length of the extrusion.
@@ -122,6 +122,8 @@ public:
}
return e_length;
}
// Orca: set by WipeTower2 (non-BBL tower) to force a travel to the tower even when the
// previous position is unknown; read by WipeTowerIntegration::append_tcr2 (GCode.cpp).
bool force_travel = false;
};
@@ -162,15 +164,12 @@ public:
bool priming,
size_t old_tool,
bool is_finish,
bool is_tool_change,
float purge_volume,
bool is_contact = false) const;
bool is_tool_change, float purge_volume, bool is_contact) const;
ToolChangeResult construct_block_tcr(WipeTowerWriter& writer,
bool priming,
size_t filament_id,
bool is_finish,
float purge_volume) const;
bool is_finish, float purge_volume) const;
// x -- x coordinates of wipe tower in mm ( left bottom corner )
@@ -184,9 +183,14 @@ public:
// Set the extruder properties.
void set_extruder(size_t idx, const PrintConfig& config);
void set_shared_print_bed(const Polygons &bed) { m_shared_print_bed = bed; }
// Orca: has_filament_switcher is not a static PrintConfig member here, so it is pushed in from
// Print via a setter rather than read in the ctor. Device-set only.
void set_has_filament_switcher(bool v) { m_has_filament_switcher = v; }
// Appends into internal structure m_plan containing info about the future wipe tower
// to be used before building begins. The entries must be added ordered in z.
void plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, float wipe_volume = 0.f, float prime_volume = 0.f);
void plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, float wipe_volume_ec = 0.f, float wipe_volume_nc = 0.f, float prime_volume = 0.f);
// Iterates through prepared m_plan, generates ToolChangeResults and appends them to "result"
void generate(std::vector<std::vector<ToolChangeResult>> &result);
@@ -219,9 +223,6 @@ public:
}
}
void set_wipe_volume(std::vector<std::vector<float>>& wiping_matrix) {
wipe_volumes = wiping_matrix;
}
// Switch to a next layer.
void set_layer(
@@ -250,7 +251,6 @@ public:
// Calculate extrusion flow from desired line width, nozzle diameter, filament diameter and layer_height:
m_extrusion_flow = extrusion_flow(layer_height);
// Advance m_layer_info iterator, making sure we got it right
while (!m_plan.empty() && m_layer_info->z < print_z - WT_EPSILON && m_layer_info+1 != m_plan.end())
++m_layer_info;
@@ -309,20 +309,9 @@ public:
std::vector<float> get_used_filament() const { return m_used_filament_length; }
int get_number_of_toolchanges() const { return m_num_tool_changes; }
void set_filament_map(const std::vector<int> &filament_map) { m_filament_map = filament_map; }
// Vortek H2C: filament_id → physical nozzle_id for carousel rotation detection
void set_filament_nozzle_map(const std::vector<int> &nozzle_map) { m_filament_nozzle_map = nozzle_map; }
void set_has_tpu_filament(bool has_tpu) { m_has_tpu_filament = has_tpu; }
bool has_tpu_filament() const { return m_has_tpu_filament; }
// Orca: has_filament_switcher is not a static PrintConfig member, so it is pushed in from Print
// via a setter rather than read in the ctor. Device-set only.
void set_has_filament_switcher(bool v) { m_has_filament_switcher = v; }
// The region every extruder can reach, used to clamp the PETG pre-extrusion offset to the
// printable bed.
void set_shared_print_bed(const Polygons &bed) { m_shared_print_bed = bed; }
struct FilamentParameters {
std::string material = "PLA";
int category;
@@ -331,15 +320,15 @@ public:
bool is_support = false;
int nozzle_temperature = 0;
int nozzle_temperature_initial_layer = 0;
int interface_print_temperature = 0;
float loading_speed = 0.f;
float loading_speed_start = 0.f;
float unloading_speed = 0.f;
float unloading_speed_start = 0.f;
float delay = 0.f ;
int cooling_moves = 0;
float cooling_initial_speed = 0.f;
float cooling_final_speed = 0.f;
// BBS: remove useless config
//float loading_speed = 0.f;
//float loading_speed_start = 0.f;
//float unloading_speed = 0.f;
//float unloading_speed_start = 0.f;
//float delay = 0.f ;
//int cooling_moves = 0;
//float cooling_initial_speed = 0.f;
//float cooling_final_speed = 0.f;
float ramming_line_width_multiplicator = 1.f;
float ramming_step_multiplicator = 1.f;
float max_e_speed = std::numeric_limits<float>::max();
@@ -349,41 +338,41 @@ public:
float retract_length;
float retract_speed;
float wipe_dist;
float tower_interface_pre_extrusion_dist = 0.f;
float tower_interface_pre_extrusion_length = 0.f;
// Outward shift of the wipe start for a PETG pre-extrusion on filament-switcher devices;
// set from filament_tower_interface_pre_extrusion_dist.
float petg_pre_extrusion_offset_dist = 0.f;
float tower_ironing_area = 4.f;
float tower_interface_purge_length = 0.f;
// Distance (in mm of filament) that a hotend is allowed to pre-cool before the
// tower is reached; drives the prime-tower heating-during-wipe model (multi-nozzle only).
float filament_cooling_before_tower = 0.f;
// .first = extruder change, .second = nozzle change (carousel)
std::pair<float,float> max_e_ramming_speed{0.f, 0.f};
std::pair<float,float> ramming_travel_time{0.f, 0.f};
std::pair<int,int> precool_target_temp{0, 0};
std::pair<std::vector<float>,std::vector<float>> precool_t;
std::pair<std::vector<float>,std::vector<float>> precool_t_first_layer;
std::pair<float,float> max_e_ramming_speed;//[0]extruder change [1]nozzle change
std::pair<float, float> ramming_travel_time; // Travel time after ramming
std::pair<std::vector<float>,std::vector<float>> precool_t;//Pre-cooling time, set to 0 to ensure the ramming speed is controlled solely by ramming volumetric speed.
std::pair<std::vector<float>, std::vector<float>> precool_t_first_layer;
std::pair<int,int> precool_target_temp;
float filament_cooling_before_tower = 0.f;
float flat_iron_area;
float filament_tower_interface_print_temp;
float filament_tower_interface_pre_extrusion_dist = 0;
float filament_tower_interface_pre_extrusion_length = 0;
float filament_petg_pre_extrusion_offset_dist = 0;
};
void set_used_filament_ids(const std::vector<int> &used_filament_ids) { m_used_filament_ids = used_filament_ids; };
void set_used_filament_ids(const std::vector<int> &used_filament_ids) { m_used_filament_ids = used_filament_ids; };
void set_filament_categories(const std::vector<int> & filament_categories) { m_filament_categories = filament_categories;};
std::vector<int> m_used_filament_ids;
void set_nozzle_group_result(const MultiNozzleUtils::LayeredNozzleGroupResult &multi_nozzle_group_result) { m_multi_nozzle_group_result = &multi_nozzle_group_result; };
std::vector<int> m_used_filament_ids;
std::vector<int> m_filament_categories;
const MultiNozzleUtils::LayeredNozzleGroupResult *m_multi_nozzle_group_result{nullptr};
enum class WipeTowerLayerType : unsigned char { Normal, Contact, Solid, Contact_UP};// Contact layer should be solid and reduce feed
struct WipeTowerBlock
{
int block_id{0};
int filament_adhesiveness_category{0};
std::vector<float> layer_depths;
std::vector<bool> solid_infill;
//std::vector<bool> solid_infill;
std::vector<float> finish_depth{0}; // the start pos of finish frame for every layer
std::vector<WipeTowerLayerType> layers_type; // type of the layer, normal, Contact or Solid
float depth{0};
float start_depth{0};
float cur_depth{0};
int last_filament_change_id{-1};
int last_filament_change_id{-1};
int last_nozzle_change_id{-1};
};
@@ -403,25 +392,33 @@ public:
WipeTowerBlock* get_block_by_category(int filament_adhesiveness_category, bool create);
void add_depth_to_block(int filament_id, int filament_adhesiveness_category, float depth, bool is_nozzle_change = false);
int get_filament_category(int filament_id);
bool is_in_same_extruder(int filament_id_1, int filament_id_2);
// Vortek H2C: format BBS-compatible NOZZLE_CHANGE_START/END tag with OF/NF/ON/NN payload
std::string format_nozzle_change_tag(bool start, int old_filament_id, int new_filament_id) const;
void reset_block_status();
int get_wall_filament_for_all_layer();
// for generate new wipe tower
void generate_new(std::vector<std::vector<WipeTower::ToolChangeResult>> &result);
void plan_tower_new();
void generate_wipe_tower_blocks();
void generate_wipe_tower_blocks(bool add_solid_flag);
void update_all_layer_depth(float wipe_tower_depth);
void set_nozzle_last_layer_id();
void set_first_layer_flow_ratio(const float flow_ratio);
// Orca: default/initial-layer/travel acceleration are object-scope options here (PrintConfig
// members in BBS), so Print pushes the resolved per-variant columns in via this setter.
void set_accelerations(const std::vector<double> &normal, const std::vector<double> &first_layer_normal,
const std::vector<double> &travel, const std::vector<double> &first_layer_travel);
void calc_block_infill_gap();
ToolChangeResult tool_change_new(size_t new_tool, bool solid_change = false, bool solid_nozzlechange=false);
NozzleChangeResult nozzle_change_new(int old_filament_id, int new_filament_id, bool solid_change = false);
NozzleChangeResult ramming(int old_filament_id, int new_filament_id, bool solid_change = false, bool extruder_change = true); // extruder_chang means nozzle_change
ToolChangeResult finish_layer_new(bool extrude_perimeter = true, bool extrude_fill = true, bool extrude_fill_wall = true);
ToolChangeResult finish_block(const WipeTowerBlock &block, int filament_id, bool extrude_fill = true);
ToolChangeResult finish_block_solid(const WipeTowerBlock &block, int filament_id, bool extrude_fill = true ,bool interface_solid =false);
ToolChangeResult finish_block_solid(const WipeTowerBlock &block, int filament_id, bool extrude_fill = true, WipeTowerLayerType layer_type = WipeTowerLayerType::Normal);
void toolchange_wipe_new(WipeTowerWriter &writer, const box_coordinates &cleaning_box, float wipe_length,bool solid_toolchange=false);
Vec2f get_rib_offset() const { return m_rib_offset; }
bool is_need_ramming(int filament_id_1, int filament_id_2, int layer_id) const;
bool is_same_extruder(int filament_id_1, int filament_id_2, int layer_id) const;
bool is_same_nozzle(int filament_id_1, int filament_id_2, int layer_id) const;
int get_nozzle_id(int filament_id, int layer_id) const;
int get_extruder_id(int filament_id, int layer_id) const;
private:
enum wipe_shape // A fill-in direction
@@ -441,7 +438,6 @@ private:
bool m_enable_wrapping_detection = false;
bool m_enable_timelapse_print = false;
bool m_semm = true; // Are we using a single extruder multimaterial printer?
bool m_purge_in_prime_tower = false; // Do we purge in the prime tower?
Vec2f m_wipe_tower_pos; // Left front corner of the wipe tower in mm.
float m_wipe_tower_width; // Width of the wipe tower.
float m_wipe_tower_depth = 0.f; // Depth of the wipe tower
@@ -459,12 +455,11 @@ private:
float m_travel_speed = 0.f;
float m_first_layer_speed = 0.f;
size_t m_first_layer_idx = size_t(-1);
std::vector<double> m_filaments_change_length;
Vec2f m_origin;
std::vector<int> m_last_layer_id;
std::pair<std::vector<double>,std::vector<double>> m_filaments_change_length;//[0]extruder change [1]nozzle change
size_t m_cur_layer_id;
NozzleChangeResult m_nozzle_change_result;
std::vector<int> m_filament_map;
std::vector<int> m_filament_nozzle_map; // Vortek H2C: filament_id → physical nozzle_id
bool m_has_tpu_filament{false};
bool m_is_multi_extruder{false};
bool m_use_gap_wall{false};
@@ -475,33 +470,32 @@ private:
bool m_used_fillet{false};
Vec2f m_rib_offset{Vec2f(0.f, 0.f)};
bool m_tower_framework{false};
bool m_need_reverse_travel{false};
bool m_enable_tower_interface_features{false};
// G-code generator parameters.
float m_cooling_tube_retraction = 0.f;
float m_cooling_tube_length = 0.f;
float m_parking_pos_retraction = 0.f;
float m_extra_loading_move = 0.f;
// BBS: remove useless config
//float m_cooling_tube_retraction = 0.f;
//float m_cooling_tube_length = 0.f;
//float m_parking_pos_retraction = 0.f;
//float m_extra_loading_move = 0.f;
float m_bridging = 0.f;
bool m_no_sparse_layers = false;
bool m_set_extruder_trimpot = false;
// BBS: remove useless config
//bool m_set_extruder_trimpot = false;
bool m_adhesion = true;
GCodeFlavor m_gcode_flavor;
// Multi-nozzle prime-tower heating during wipe. m_is_multiple_nozzle gates the whole
// feature; it is false for every current (single-nozzle) printer (extruder_max_nozzle_count
// defaults to 1), so the pre-heat/pre-cool path is inert and wipe-tower g-code is unchanged.
bool m_is_multiple_nozzle = false;
std::vector<double> m_hotend_heating_rate; // config.hotend_heating_rate (deg/s per extruder)
std::vector<int> m_physical_extruder_map; // logical extruder -> physical tool number (M104 T param)
// Per-extruder printable-height clamp. m_printable_height = config.extruder_printable_height
// (per-extruder Z limit; empty for single-extruder printers, [320,325] for H2D). m_last_layer_id
// records, per extruder, the last wipe-tower layer that uses it. is_valid_last_layer() is gated on
// m_is_multi_extruder so single-extruder wipe-tower g-code is unchanged; the clamp only bites a
// multi-extruder wipe tower whose final per-extruder layer exceeds that extruder's printable
// height (near the Z limit).
std::vector<double> m_printable_height;
std::vector<int> m_last_layer_id;
bool m_is_multiple_nozzle = false;
std::vector<unsigned int> m_normal_accels;
std::vector<unsigned int> m_first_layer_normal_accels;
std::vector<unsigned int> m_travel_accels;
std::vector<unsigned int> m_first_layer_travel_accels;
unsigned int m_max_accels;
bool m_accel_to_decel_enable;
float m_accel_to_decel_factor;
bool m_enable_arc_fitting = true;
std::vector<double> m_hotend_heating_rate;
std::vector<double> m_hotend_cooling_rate;
Polygons m_shared_print_bed;
// Bed properties
enum {
@@ -512,10 +506,11 @@ private:
float m_bed_width; // width of the bed bounding box
Vec2f m_bed_bottom_left; // bottom-left corner coordinates (for rectangular beds)
float m_first_layer_flow_ratio;
float m_perimeter_width = 0.4f * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill.
float m_nozzle_change_perimeter_width = 0.4f * Width_To_Nozzle_Ratio;
float m_extrusion_flow = 0.038f; //0.029f;// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter.
std::unordered_map<int, std::pair<float,float>> m_block_infill_gap_width; // categories to infill_gap: toolchange gap, nozzlechange gap
// Extruder specific parameters.
std::vector<FilamentParameters> m_filpar;
@@ -528,50 +523,52 @@ private:
// A fill-in direction (positive Y, negative Y) alternates with each layer.
wipe_shape m_current_shape = SHAPE_NORMAL;
size_t m_current_tool = 0;
// Orca: support mmu wipe tower
std::vector<std::vector<float>> wipe_volumes;
// BBS
//const std::vector<std::vector<float>> wipe_volumes;
float m_depth_traversed = 0.f; // Current y position at the wipe tower.
bool m_current_layer_finished = false;
bool m_left_to_right = true;
float m_extra_spacing = 1.f;
float m_tpu_fixed_spacing = 2;
std::vector<Vec2f> m_wall_skip_points;
float m_max_speed = 5400.f; // the maximum printing speed on the prime tower.
std::vector<std::vector<Vec2f>> m_wall_skip_points;
std::map<float,Polylines> m_outer_wall;
std::vector<double> m_printable_height;
bool is_first_layer() const { return size_t(m_layer_info - m_plan.begin()) == m_first_layer_idx; }
bool is_valid_last_layer(int tool, int layer_id, double layer_z) const;
bool m_flat_ironing=false;
bool m_enable_tower_interface_features=false;
bool m_enable_tower_interface_cooldown_during_tower=false;
// Filament-switcher device flag + shared printable bed for the PETG pre-extrusion offset.
// m_has_filament_switcher is false for the whole shipping fleet (no profile sets the key), so
// the PETG branch in get_next_pos never runs -> no change fleet-wide.
bool m_has_filament_switcher=false;
Polygons m_shared_print_bed;
bool m_prev_layer_had_interface=false;
bool m_current_layer_has_interface=false;
bool m_contact_ironing = false;
bool m_has_filament_switcher = false;
float m_contact_speed = 20 * 60.f;
std::vector<int> m_physical_extruder_map;
// Calculates length of extrusion line to extrude given volume
float volume_to_length(float volume, float line_width, float layer_height) const {
return std::max(0.f, volume / (layer_height * (line_width - layer_height * (1.f - float(M_PI) / 4.f))));
}
// Calculates volume of extrusion line
float length_to_volume(float length,float line_width, float layer_height) const
{
return std::max(0.f, length * (layer_height * (line_width - layer_height * (1.f - float(M_PI) / 4.f))));
}
// Calculates depth for all layers and propagates them downwards
void plan_tower();
// Goes through m_plan and recalculates depths and width of the WT to make it exactly square - experimental
void make_wipe_tower_square();
Vec2f get_next_pos(const WipeTower::box_coordinates &cleaning_box, float wipe_length, bool interface_layer, size_t interface_tool);
Vec2f get_next_pos(const WipeTower::box_coordinates &cleaning_box, float wipe_length, bool solid_toolchange);
// Goes through m_plan, calculates border and finish_layer extrusions and subtracts them from last wipe
void save_on_last_wipe();
bool is_tpu_filament(int filament_id) const;
bool is_petg_filament(int filament_id) const;
bool is_need_reverse_travel(int filament_id, bool extruder_change) const;
bool is_need_reverse_travel(int filament, bool extruder_change) const;
// BBS
box_coordinates align_perimeter(const box_coordinates& perimeter_box);
void set_for_wipe_tower_writer(WipeTowerWriter &writer);
// to store information about tool changes for a given layer
struct WipeTowerInfo{
@@ -584,6 +581,7 @@ private:
float wipe_volume;
float wipe_length;
float nozzle_change_depth{0};
float nozzle_change_length{0};
// BBS
float purge_volume;
ToolChange(size_t old, size_t newtool, float depth=0.f, float ramming_depth=0.f, float fwl=0.f, float wv=0.f, float wl = 0, float pv = 0)
@@ -613,7 +611,7 @@ private:
// ot -1 if there is no such toolchange.
int first_toolchange_to_nonsoluble_nonsupport(
const std::vector<WipeTowerInfo::ToolChange>& tool_changes) const;
WipeTowerInfo::ToolChange set_toolchange(int old_tool, int new_tool, float layer_height, float wipe_volume, float purge_volume,int layer_id);
void toolchange_Unload(
WipeTowerWriter &writer,
const box_coordinates &cleaning_box,
@@ -633,13 +631,10 @@ private:
WipeTowerWriter &writer,
const box_coordinates &cleaning_box,
float wipe_volume);
void get_wall_skip_points(const WipeTowerInfo &layer);
// Per-extruder printable-height clamp (see m_printable_height). is_valid_last_layer returns
// false only for a multi-extruder wipe tower's final per-extruder layer that exceeds that
// extruder's printable height; returns true (no clamp) in every other case.
bool is_valid_last_layer(int tool, int layer_id, double layer_z) const;
void set_nozzle_last_layer_id();
void get_wall_skip_points(const WipeTowerInfo &layer,int layer_id);
void get_all_wall_skip_points();
ToolChangeResult merge_tcr(ToolChangeResult &first, ToolChangeResult &second);
float get_block_gap_width(int tool, bool is_nozzlechangle = false);
};

View File

@@ -129,13 +129,13 @@ public:
std::vector<PathFittingData> fitting_result;
//BBS: simplify points by arc fitting
void simplify_by_fitting_arc(double tolerance);
//BBS:
void reset_to_linear_move();
//BBS:
Polylines equally_spaced_lines(double distance) const;
private:
void append_fitting_result_after_append_points();
void append_fitting_result_after_append_polyline(const Polyline& src);
void reset_to_linear_move();
bool split_fitting_result_before_index(const size_t index, Point &new_endpoint, std::vector<PathFittingData>& data) const;
bool split_fitting_result_after_index(const size_t index, Point &new_startpoint, std::vector<PathFittingData>& data) const;
};

View File

@@ -4017,10 +4017,33 @@ void Print::_make_wipe_tower()
// in BBL machine, wipe tower is only use to prime extruder. So just use a global wipe volume.
WipeTower wipe_tower(m_config, m_plate_index, m_origin, m_wipe_tower_data.tool_ordering.first_extruder(),
m_wipe_tower_data.tool_ordering.empty() ? 0.f : m_wipe_tower_data.tool_ordering.back().print_z, m_wipe_tower_data.tool_ordering.all_extruders());
// Orca: the tower's first-layer flow follows the user's first-layer flow ratio (BBS reads
// its initial_layer_flow_ratio here — STUDIO-14254; first_layer_flow_ratio is Orca's analog,
// default 1.0 in both). Honor the set_other_flow_ratios gate that governs the option
// everywhere else.
wipe_tower.set_first_layer_flow_ratio(m_default_object_config.set_other_flow_ratios
? float(m_default_region_config.first_layer_flow_ratio)
: 1.f);
wipe_tower.set_has_tpu_filament(this->has_tpu_filament());
wipe_tower.set_filament_map(this->get_filament_maps());
// Vortek H2C: pass nozzle-level map for carousel rotation detection in tool_change_new()
wipe_tower.set_filament_nozzle_map(this->get_filament_nozzle_maps());
// Per-layer filament->nozzle grouping. sort_and_build_data() above publishes it on the Print
// for by-layer prints; by-object prints publish only later (psSkirtBrim), so fall back to the
// ToolOrdering's own copy there. set_extruder() below dereferences it, so it must be set first.
auto print_group_result = get_layered_nozzle_group_result();
const MultiNozzleUtils::LayeredNozzleGroupResult &nozzle_group_result =
print_group_result ? *print_group_result : m_wipe_tower_data.tool_ordering.get_layered_nozzle_group_result();
wipe_tower.set_nozzle_group_result(nozzle_group_result);
{
// Orca: acceleration options are object-scope (PrintConfig members in BBS), so resolve
// the per-variant columns here; initial_layer_travel_acceleration is FloatOrPercent
// over travel_acceleration and needs the full config to resolve.
std::vector<double> first_layer_travel_accels;
for (size_t i = 0; i < m_config.initial_layer_travel_acceleration.values.size(); ++i)
first_layer_travel_accels.emplace_back(m_full_print_config.get_abs_value_at("initial_layer_travel_acceleration", i));
wipe_tower.set_accelerations(m_default_object_config.default_acceleration.values,
m_default_object_config.initial_layer_acceleration.values,
m_default_object_config.travel_acceleration.values,
first_layer_travel_accels);
}
// Feed the has_filament_switcher device flag (develop-only dynamic key, read defensively from
// the full config — no shipping profile sets it) and the shared printable bed used by the PETG
// pre-extrusion offset clamp. Both are inert unless has_filament_switcher is set.
@@ -4056,27 +4079,19 @@ void Print::_make_wipe_tower()
multi_extruder_flush.emplace_back(wipe_volumes);
}
// Use NozzleStatusRecorder for per-carousel-slot tracking (BBS pattern).
// The original Orca code tracked per-extruder (2 slots), which collapsed all
// carousel filaments into one slot and caused massive redundant AMS flushing.
auto group_result = get_layered_nozzle_group_result();
// Per-carousel-slot purge tracking via NozzleStatusRecorder (BBS pattern); the layered
// group result set on the tower above resolves each filament to its nozzle slot per layer.
MultiNozzleUtils::NozzleStatusRecorder nozzle_recorder;
// Fallback (group_result == null) per-physical-nozzle tracking, matching the original
// pre-port behavior: remembers the last filament loaded in each physical nozzle slot.
std::vector<unsigned int> nozzle_cur_filament_ids(nozzle_nums, (unsigned int) -1);
std::vector<int>filament_maps = get_filament_maps();
int layer_idx = -1;
unsigned int current_filament_id = m_wipe_tower_data.tool_ordering.first_extruder();
// Initialize NozzleStatusRecorder with the first filament's carousel slot
if (group_result) {
auto nozzle = group_result->get_nozzle_for_filament(current_filament_id, layer_idx);
{
auto nozzle = nozzle_group_result.get_nozzle_for_filament(current_filament_id, layer_idx);
if (nozzle)
nozzle_recorder.set_nozzle_status(nozzle->group_id, current_filament_id, nozzle->extruder_id);
} else {
size_t cur_nozzle_id = filament_maps[current_filament_id] - 1;
nozzle_cur_filament_ids[cur_nozzle_id] = current_filament_id;
}
for (auto& layer_tools : m_wipe_tower_data.tool_ordering.layer_tools()) { // for all layers
@@ -4095,8 +4110,8 @@ void Print::_make_wipe_tower()
float volume_to_purge = 0;
// Per-carousel-slot purge tracking via NozzleStatusRecorder
if (group_result) {
auto nozzle_info = group_result->get_nozzle_for_filament(filament_id, layer_idx);
{
auto nozzle_info = nozzle_group_result.get_nozzle_for_filament(filament_id, layer_idx);
if (nozzle_info) {
int extruder_id = nozzle_info->extruder_id;
int nozzle_id = nozzle_info->group_id;
@@ -4115,22 +4130,6 @@ void Print::_make_wipe_tower()
}
nozzle_recorder.set_nozzle_status(nozzle_id, filament_id, extruder_id);
}
} else {
// Fallback: original Orca per-physical-nozzle path (non-carousel printers).
// Flush source is the last filament that occupied THIS nozzle, guarded so the
// first use of a nozzle incurs no flush.
int nozzle_id = filament_maps[filament_id] - 1;
unsigned int pre_filament_id = nozzle_cur_filament_ids[nozzle_id];
if (pre_filament_id != (unsigned int) -1 && pre_filament_id != filament_id) {
volume_to_purge = multi_extruder_flush[nozzle_id][pre_filament_id][filament_id];
float flush_multiplier = (m_config.prime_volume_mode == PrimeVolumeMode::pvmFast)
? m_config.flush_multiplier_fast.get_at(nozzle_id)
: m_config.flush_multiplier.get_at(nozzle_id);
volume_to_purge *= flush_multiplier;
volume_to_purge = layer_tools.wiping_extrusions().mark_wiping_extrusions(
*this, current_filament_id, filament_id, volume_to_purge);
}
nozzle_cur_filament_ids[nozzle_id] = filament_id;
}
//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.
@@ -4138,29 +4137,21 @@ void Print::_make_wipe_tower()
float grab_purge_volume = m_config.grab_length.get_at(grab_extruder_id) * 2.4; //(diameter/2)^2*PI=2.4
volume_to_purge = std::max(0.f, volume_to_purge - grab_purge_volume);
// Select prime volume per-filament: nozzle change (carousel rotation) uses
// filament_prime_volume_nc, filament change (same nozzle slot) uses filament_prime_volume.
// Prime volume per-filament: the tower now picks extruder-change vs nozzle-change
// (carousel) internally per plan layer, so pass both candidates (BBS pattern).
float wipe_volume_ec = filament_id < m_config.filament_prime_volume.values.size()
? m_config.filament_prime_volume.values[filament_id]
: (float) m_config.prime_volume;
float wipe_volume_nc = filament_id < m_config.filament_prime_volume_nc.values.size()
? m_config.filament_prime_volume_nc.values[filament_id]
: (float) m_config.prime_volume;
float prime_volume = wipe_volume_ec;
if (group_result) {
bool is_nozzle_change = group_result->are_filaments_same_extruder(current_filament_id, filament_id, layer_idx) &&
!group_result->are_filaments_same_nozzle(current_filament_id, filament_id, layer_idx);
if (is_nozzle_change) {
prime_volume = wipe_volume_nc;
}
}
if (m_config.prime_volume_mode == PrimeVolumeMode::pvmSaving) {
prime_volume = 15.f;
wipe_volume_ec = 15.f;
wipe_volume_nc = 15.f;
}
wipe_tower.plan_toolchange((float)layer_tools.print_z, (float)layer_tools.wipe_tower_layer_height, current_filament_id, filament_id,
prime_volume, volume_to_purge);
wipe_volume_ec, wipe_volume_nc, volume_to_purge);
current_filament_id = filament_id;
}
layer_tools.wiping_extrusions().ensure_perimeters_infills_order(*this);