mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-15 15:03:49 +00:00
feat(engine): resolve per-variant config columns for raw filament reads on the export path
When a per-layer nozzle grouping migrates a filament across nozzle variants, the write-back turns two groups of config arrays from filament-indexed into column-indexed: the per-variant filament options (one column per variant a filament uses) and the merged extruder retract overrides (resized to the column count by apply_override). Export-path readers that still indexed them with the raw filament id read a neighbor's column for every filament ordered after a migrating one: toolchange/standby temperatures (M104/M109), retraction lengths and feedrates, wipe distance, z-hop types, air-filtration keys, and - through the Extruder's cached flow term - the extrusion E of every move. Now every such read resolves its column through the existing layer-aware resolver (get_filament_config_index -> Print::get_filament_config_indx), which returns the raw filament id whenever no per-layer grouping result is published, so static prints are byte-inert by construction. The Extruder itself has no layer knowledge, so it gains an injected config column (set_config_index, default = filament id) that the generator refreshes at the only two resolution-changing events - layer change and writer toolchange - and that re-syncs the cached e_per_mm3 flow term. Old-filament reads resolve at the current layer, which is safe because the per-layer maps are gap-filled carry-forward. Whole-array placeholder copies (toolchange temperature overrides) are rebuilt in filament order, mirroring the existing per-variant placeholder remap. The resolvers move to the public section so non-friend helpers (ooze prevention) can resolve too. Documented, deliberately unchanged: the wipe tower's per-filament parameter rows (no layer dimension; tower x per-layer grouping is a follow-up), travel_slope's physical-extruder read, estimator pre-heat bookkeeping temps, and index-0 header diagnostics. Verification: new Extruder column-injection scenario (defaults, column follow + flow-cache rescale, filament-indexed reads unaffected, reset semantics) and a migrating write-back case proving the column shift for filaments ordered after a migrator and the resolver tracking it (11 + 14 assertions); suites green (libslic3r 48998/169, fff_print 655/61); 20/20 pinned-slice byte gate bit-identical (incl. sequential repro x2 deterministic).
This commit is contained in:
@@ -13,11 +13,20 @@ Extruder::Extruder(unsigned int id, GCodeConfig *config, bool share_extruder) :
|
||||
{
|
||||
reset();
|
||||
|
||||
m_config_index = int(m_id);
|
||||
// cache values that are going to be called often
|
||||
m_e_per_mm3 = this->filament_flow_ratio();
|
||||
m_e_per_mm3 /= this->filament_crossection();
|
||||
}
|
||||
|
||||
void Extruder::set_config_index(int idx)
|
||||
{
|
||||
m_config_index = idx < 0 ? int(m_id) : idx;
|
||||
// keep the cached flow term reading the same column as the getters
|
||||
m_e_per_mm3 = this->filament_flow_ratio();
|
||||
m_e_per_mm3 /= this->filament_crossection();
|
||||
}
|
||||
|
||||
unsigned int Extruder::extruder_id() const
|
||||
{
|
||||
assert(m_config);
|
||||
@@ -162,28 +171,28 @@ double Extruder::filament_cost() const
|
||||
|
||||
double Extruder::filament_flow_ratio() const
|
||||
{
|
||||
return m_config->filament_flow_ratio.get_at(m_id);
|
||||
return m_config->filament_flow_ratio.get_at(m_config_index);
|
||||
}
|
||||
|
||||
// Return a "retract_before_wipe" percentage as a factor clamped to <0, 1>
|
||||
double Extruder::retract_before_wipe() const
|
||||
{
|
||||
return std::min(1., std::max(0., m_config->retract_before_wipe.get_at(m_id) * 0.01));
|
||||
return std::min(1., std::max(0., m_config->retract_before_wipe.get_at(m_config_index) * 0.01));
|
||||
}
|
||||
|
||||
double Extruder::retraction_length() const
|
||||
{
|
||||
return m_config->retraction_length.get_at(m_id);
|
||||
return m_config->retraction_length.get_at(m_config_index);
|
||||
}
|
||||
|
||||
double Extruder::retract_lift() const
|
||||
{
|
||||
return m_config->z_hop.get_at(m_id);
|
||||
return m_config->z_hop.get_at(m_config_index);
|
||||
}
|
||||
|
||||
int Extruder::retract_speed() const
|
||||
{
|
||||
return int(floor(m_config->retraction_speed.get_at(m_id)+0.5));
|
||||
return int(floor(m_config->retraction_speed.get_at(m_config_index)+0.5));
|
||||
}
|
||||
|
||||
bool Extruder::use_firmware_retraction() const
|
||||
@@ -193,13 +202,13 @@ bool Extruder::use_firmware_retraction() const
|
||||
|
||||
int Extruder::deretract_speed() const
|
||||
{
|
||||
int speed = int(floor(m_config->deretraction_speed.get_at(m_id)+0.5));
|
||||
int speed = int(floor(m_config->deretraction_speed.get_at(m_config_index)+0.5));
|
||||
return (speed > 0) ? speed : this->retract_speed();
|
||||
}
|
||||
|
||||
double Extruder::retract_restart_extra() const
|
||||
{
|
||||
return m_config->retract_restart_extra.get_at(m_id);
|
||||
return m_config->retract_restart_extra.get_at(m_config_index);
|
||||
}
|
||||
|
||||
double Extruder::retract_length_toolchange() const
|
||||
@@ -214,6 +223,8 @@ double Extruder::retract_restart_extra_toolchange() const
|
||||
|
||||
double Extruder::travel_slope() const
|
||||
{
|
||||
// Orca: deliberately keyed by the physical extruder, not the filament column — this read
|
||||
// predates the per-variant merge and switching it would change existing multi-extruder output.
|
||||
return m_config->travel_slope.get_at(extruder_id()) * PI / 180;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,13 @@ public:
|
||||
|
||||
unsigned int id() const { return m_id; }
|
||||
|
||||
// Column of the per-variant filament/override arrays the getters read. Defaults to the
|
||||
// filament id (one column per filament); the g-code generator refreshes it on layer changes
|
||||
// and toolchanges when a per-layer nozzle grouping gives a filament several variant columns.
|
||||
int config_index() const { return m_config_index; }
|
||||
// idx < 0 resets to the filament id. Re-syncs the cached e_per_mm3 flow term.
|
||||
void set_config_index(int idx);
|
||||
|
||||
unsigned int extruder_id() const;
|
||||
double extrude(double dE);
|
||||
double retract(double length, double restart_extra);
|
||||
@@ -86,6 +93,8 @@ private:
|
||||
GCodeConfig *m_config;
|
||||
// Print-wide global ID of this extruder.
|
||||
unsigned int m_id;
|
||||
// Column into the per-variant filament/override arrays; equals m_id unless refreshed.
|
||||
int m_config_index{0};
|
||||
// Current state of the extruder axis, may be resetted if use_relative_e_distances.
|
||||
double m_E;
|
||||
// Current state of the extruder tachometer, used to output the extruded_volume() and used_filament() statistics.
|
||||
|
||||
@@ -419,12 +419,14 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
|
||||
int OozePrevention::_get_temp(const GCode &gcodegen) const
|
||||
{
|
||||
// Resolve the filament's per-variant config column (equals its id on static prints).
|
||||
size_t fi = gcodegen.get_filament_config_index((int)gcodegen.writer().filament()->id());
|
||||
// First layer temperature should be used when on the first layer (obviously) and when
|
||||
// "other layers" is set to zero (which means it should not be used).
|
||||
return (gcodegen.layer() == nullptr || gcodegen.layer()->id() == 0
|
||||
|| gcodegen.config().nozzle_temperature.get_at(gcodegen.writer().filament()->id()) == 0)
|
||||
? gcodegen.config().nozzle_temperature_initial_layer.get_at(gcodegen.writer().filament()->id())
|
||||
: gcodegen.config().nozzle_temperature.get_at(gcodegen.writer().filament()->id());
|
||||
|| gcodegen.config().nozzle_temperature.get_at(fi) == 0)
|
||||
? gcodegen.config().nozzle_temperature_initial_layer.get_at(fi)
|
||||
: gcodegen.config().nozzle_temperature.get_at(fi);
|
||||
}
|
||||
|
||||
// Orca:
|
||||
@@ -567,7 +569,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
amount of retraction. In other words, how far do we move in XY at wipe_speed
|
||||
for the time needed to consume retraction_length at retraction_speed? */
|
||||
// BBS
|
||||
double wipe_dist = scale_(gcodegen.config().wipe_distance.get_at(gcodegen.writer().filament()->id()));
|
||||
double wipe_dist = scale_(gcodegen.config().wipe_distance.get_at(gcodegen.get_filament_config_index((int)gcodegen.writer().filament()->id())));
|
||||
|
||||
/* Take the stored wipe path and replace first point with the current actual position
|
||||
(they might be different, for example, in case of loop clipping). */
|
||||
@@ -629,9 +631,11 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
}
|
||||
|
||||
// set volumetric speed of outer wall ,ignore per obejct & region ,just use default setting
|
||||
static float get_outer_wall_volumetric_speed(const FullPrintConfig& config, const Print& print, int filament_id, int extruder_id) {
|
||||
// filament_variant_idx selects the per-variant column of filament_max_volumetric_speed
|
||||
// (equals filament_id unless a per-layer nozzle grouping expanded the filament arrays).
|
||||
static float get_outer_wall_volumetric_speed(const FullPrintConfig& config, const Print& print, int filament_id, int filament_variant_idx, int extruder_id) {
|
||||
float outer_wall_volumetric_speed = 0;
|
||||
float filament_max_volumetric_speed = config.filament_max_volumetric_speed.get_at(filament_id);
|
||||
float filament_max_volumetric_speed = config.filament_max_volumetric_speed.get_at(filament_variant_idx);
|
||||
const double filament_diameter = config.filament_diameter.get_at(filament_id);
|
||||
float outer_wall_line_width = print.default_region_config().get_abs_value("outer_wall_line_width", filament_diameter);
|
||||
if (outer_wall_line_width == 0.0) {
|
||||
@@ -928,7 +932,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
|
||||
std::string toolchange_gcode_str;
|
||||
|
||||
ZHopType z_hope_type = ZHopType(gcodegen.config().z_hop_types.get_at(gcodegen.writer().filament()->id()));
|
||||
ZHopType z_hope_type = ZHopType(gcodegen.config().z_hop_types.get_at(gcodegen.get_filament_config_index((int)gcodegen.writer().filament()->id())));
|
||||
LiftType auto_lift_type = LiftType::NormalLift;
|
||||
if (z_hope_type == ZHopType::zhtAuto || z_hope_type == ZHopType::zhtSpiral || z_hope_type == ZHopType::zhtSlope)
|
||||
auto_lift_type = LiftType::SpiralLift;
|
||||
@@ -1001,25 +1005,32 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
GCodeWriter& gcode_writer = gcodegen.m_writer;
|
||||
FullPrintConfig& full_config = gcodegen.m_config;
|
||||
|
||||
// Per-variant filament arrays can hold one column per variant a filament uses
|
||||
// under a per-layer nozzle grouping; resolve the column instead of the raw id.
|
||||
// The old filament resolves at the current layer, which is safe because the
|
||||
// per-layer maps are gap-filled carry-forward.
|
||||
size_t old_fi = (old_filament_id != -1) ? gcodegen.get_filament_config_index(old_filament_id) : 0;
|
||||
size_t new_fi = gcodegen.get_filament_config_index(new_filament_id);
|
||||
|
||||
// set volumetric speed of outer wall ,ignore per obejct,just use default setting
|
||||
float outer_wall_volumetric_speed = get_outer_wall_volumetric_speed(full_config, *gcodegen.m_print, new_filament_id, gcodegen.get_extruder_id(new_filament_id));
|
||||
float outer_wall_volumetric_speed = get_outer_wall_volumetric_speed(full_config, *gcodegen.m_print, new_filament_id, (int)new_fi, gcodegen.get_extruder_id(new_filament_id));
|
||||
config.set_key_value("outer_wall_volumetric_speed", new ConfigOptionFloat(outer_wall_volumetric_speed));
|
||||
|
||||
float old_retract_length = (old_filament_id != -1) ? full_config.retraction_length.get_at(old_filament_id) : 0;
|
||||
float new_retract_length = full_config.retraction_length.get_at(new_filament_id);
|
||||
float old_retract_length = (old_filament_id != -1) ? full_config.retraction_length.get_at(old_fi) : 0;
|
||||
float new_retract_length = full_config.retraction_length.get_at(new_fi);
|
||||
float old_retract_length_toolchange = (old_filament_id != -1) ? full_config.retract_length_toolchange.get_at(old_filament_id) : 0;
|
||||
float new_retract_length_toolchange = full_config.retract_length_toolchange.get_at(new_filament_id);
|
||||
int old_filament_temp = (old_filament_id != -1) ? (gcodegen.on_first_layer()? full_config.nozzle_temperature_initial_layer.get_at(old_filament_id) : full_config.nozzle_temperature.get_at(old_filament_id)) : 210;
|
||||
int new_filament_temp = gcodegen.on_first_layer() ? full_config.nozzle_temperature_initial_layer.get_at(new_filament_id) : full_config.nozzle_temperature.get_at(new_filament_id);
|
||||
int old_filament_temp = (old_filament_id != -1) ? (gcodegen.on_first_layer()? full_config.nozzle_temperature_initial_layer.get_at(old_fi) : full_config.nozzle_temperature.get_at(old_fi)) : 210;
|
||||
int new_filament_temp = gcodegen.on_first_layer() ? full_config.nozzle_temperature_initial_layer.get_at(new_fi) : full_config.nozzle_temperature.get_at(new_fi);
|
||||
Vec3d nozzle_pos = gcode_writer.get_position();
|
||||
|
||||
float purge_volume = tcr.purge_volume < EPSILON ? 0 : std::max(tcr.purge_volume, g_min_purge_volume);
|
||||
float filament_area = float((M_PI / 4.f) * pow(full_config.filament_diameter.get_at(new_filament_id), 2));
|
||||
float purge_length = purge_volume / filament_area;
|
||||
|
||||
int old_filament_e_feedrate = (old_filament_id != -1) ? (int)(60.0 * full_config.filament_max_volumetric_speed.get_at(old_filament_id) / filament_area) : 200;
|
||||
int old_filament_e_feedrate = (old_filament_id != -1) ? (int)(60.0 * full_config.filament_max_volumetric_speed.get_at(old_fi) / filament_area) : 200;
|
||||
old_filament_e_feedrate = old_filament_e_feedrate == 0 ? 100 : old_filament_e_feedrate;
|
||||
int new_filament_e_feedrate = (int)(60.0 * full_config.filament_max_volumetric_speed.get_at(new_filament_id) / filament_area);
|
||||
int new_filament_e_feedrate = (int)(60.0 * full_config.filament_max_volumetric_speed.get_at(new_fi) / filament_area);
|
||||
new_filament_e_feedrate = new_filament_e_feedrate == 0 ? 100 : new_filament_e_feedrate;
|
||||
float wipe_avoid_pos_x = 0.f;
|
||||
{
|
||||
@@ -1055,7 +1066,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
config.set_key_value("new_retract_length", new ConfigOptionFloat(new_retract_length));
|
||||
// Expose the old filament's nozzle-change retract length (filament_retract_length_nc; nil/-1 -> 0).
|
||||
config.set_key_value("filament_retract_length_nc", new ConfigOptionFloat(
|
||||
(old_filament_id != -1) ? (float) full_config.filament_retract_length_nc.get_at(old_filament_id) : 0.f));
|
||||
(old_filament_id != -1) ? (float) full_config.filament_retract_length_nc.get_at(old_fi) : 0.f));
|
||||
config.set_key_value("old_retract_length_toolchange", new ConfigOptionFloat(old_retract_length_toolchange));
|
||||
config.set_key_value("new_retract_length_toolchange", new ConfigOptionFloat(new_retract_length_toolchange));
|
||||
// Current parked-retract length of the incoming filament's extruder.
|
||||
@@ -1069,12 +1080,19 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
new_filament_temp = interface_temp;
|
||||
config.set_key_value("new_filament_temp", new ConfigOptionInt(new_filament_temp));
|
||||
if (full_config.enable_tower_interface_features && tcr.is_contact) {
|
||||
auto temps = full_config.nozzle_temperature.values;
|
||||
// Rebuild in filament order: the config arrays may carry per-variant columns,
|
||||
// while these placeholder vectors are consumed indexed by filament id.
|
||||
size_t num_filaments = full_config.filament_type.values.size();
|
||||
std::vector<int> temps(num_filaments);
|
||||
std::vector<int> first_layer_temps(num_filaments);
|
||||
for (size_t i = 0; i < num_filaments; ++i) {
|
||||
size_t fi_i = gcodegen.get_filament_config_index((int)i);
|
||||
temps[i] = full_config.nozzle_temperature.get_at(fi_i);
|
||||
first_layer_temps[i] = full_config.nozzle_temperature_initial_layer.get_at(fi_i);
|
||||
}
|
||||
if (new_filament_id >= 0 && new_filament_id < (int)temps.size())
|
||||
temps[new_filament_id] = interface_temp;
|
||||
config.set_key_value("temperature", new ConfigOptionInts(temps));
|
||||
|
||||
auto first_layer_temps = full_config.nozzle_temperature_initial_layer.values;
|
||||
if (new_filament_id >= 0 && new_filament_id < (int)first_layer_temps.size())
|
||||
first_layer_temps[new_filament_id] = interface_temp;
|
||||
config.set_key_value("first_layer_temperature", new ConfigOptionInts(first_layer_temps));
|
||||
@@ -1415,10 +1433,12 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
}
|
||||
|
||||
if (toolchange_temp_override > 0) {
|
||||
int base_temp = gcodegen.on_first_layer() ? gcodegen.config().nozzle_temperature_initial_layer.get_at(new_extruder_id)
|
||||
: gcodegen.config().nozzle_temperature.get_at(new_extruder_id);
|
||||
// new_extruder_id is the incoming filament id; resolve its per-variant config column.
|
||||
size_t new_fi = gcodegen.get_filament_config_index(new_extruder_id);
|
||||
int base_temp = gcodegen.on_first_layer() ? gcodegen.config().nozzle_temperature_initial_layer.get_at(new_fi)
|
||||
: gcodegen.config().nozzle_temperature.get_at(new_fi);
|
||||
if (std::abs(tcr.print_z) < EPSILON)
|
||||
base_temp = gcodegen.config().nozzle_temperature_initial_layer.get_at(new_extruder_id);
|
||||
base_temp = gcodegen.config().nozzle_temperature_initial_layer.get_at(new_fi);
|
||||
const std::string t_token = " T" + std::to_string(new_extruder_id);
|
||||
std::string out;
|
||||
out.reserve(toolchange_gcode_str.size());
|
||||
@@ -2344,11 +2364,13 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
|
||||
m_processor.result().support_traditional_timelapse = m_support_traditional_timelapse;
|
||||
|
||||
bool activate_long_retraction_when_cut = false;
|
||||
for (const auto& filament : m_writer.extruders())
|
||||
for (const auto& filament : m_writer.extruders()) {
|
||||
size_t fi = get_filament_config_index((int)filament.id());
|
||||
activate_long_retraction_when_cut |= (
|
||||
m_config.long_retractions_when_cut.get_at(filament.id())
|
||||
&& m_config.retraction_distances_when_cut.get_at(filament.id()) > 0
|
||||
m_config.long_retractions_when_cut.get_at(fi)
|
||||
&& m_config.retraction_distances_when_cut.get_at(fi) > 0
|
||||
);
|
||||
}
|
||||
|
||||
m_processor.result().long_retraction_when_cut = activate_long_retraction_when_cut;
|
||||
|
||||
@@ -3330,7 +3352,9 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
this->placeholder_parser().set("during_print_exhaust_fan_speed_num", new ConfigOptionInts(during_print_exhaust_fan_speed_num));
|
||||
|
||||
//BBS: calculate the volumetric speed of outer wall. Ignore pre-object setting and multi-filament, and just use the default setting
|
||||
float outer_wall_volumetric_speed = get_outer_wall_volumetric_speed(m_config, print, initial_non_support_extruder_id, get_extruder_id(initial_non_support_extruder_id));
|
||||
float outer_wall_volumetric_speed = get_outer_wall_volumetric_speed(m_config, print, initial_non_support_extruder_id,
|
||||
(int) get_filament_config_index((int) initial_non_support_extruder_id),
|
||||
get_extruder_id(initial_non_support_extruder_id));
|
||||
this->placeholder_parser().set("outer_wall_volumetric_speed", new ConfigOptionFloat(outer_wall_volumetric_speed));
|
||||
|
||||
auto first_layer_filaments = print.get_slice_used_filaments(true);
|
||||
@@ -3479,10 +3503,11 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
|
||||
// Orca: when activate_air_filtration is set on any extruder, find and set the highest during_print_exhaust_fan_speed
|
||||
for (const auto &extruder : m_writer.extruders()) {
|
||||
if (m_config.activate_air_filtration.get_at(extruder.id()) && m_config.activate_air_filtration_during_print.get_at(extruder.id())) {
|
||||
size_t fi = get_filament_config_index((int)extruder.id());
|
||||
if (m_config.activate_air_filtration.get_at(fi) && m_config.activate_air_filtration_during_print.get_at(fi)) {
|
||||
activate_air_filtration_during_print = true;
|
||||
during_print_exhaust_fan_speed = std::max(during_print_exhaust_fan_speed,
|
||||
m_config.during_print_exhaust_fan_speed.get_at(extruder.id()));
|
||||
m_config.during_print_exhaust_fan_speed.get_at(fi));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3619,7 +3644,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
} else {
|
||||
file.write(this->retract());
|
||||
}
|
||||
file.write(m_writer.travel_to_z(m_max_layer_z + m_writer.config.z_hop.get_at(initial_extruder_id)));
|
||||
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))));
|
||||
file.write(this->travel_to(Point(0, 0), erNone, "move to origin position for next object"));
|
||||
m_enable_cooling_markers = true;
|
||||
// Disable motion planner when traveling to first object point.
|
||||
@@ -3820,9 +3845,10 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
|
||||
// Orca: when activate_air_filtration is set on any extruder, find and set the highest complete_print_exhaust_fan_speed
|
||||
for (const auto& extruder : m_writer.extruders()) {
|
||||
if (m_config.activate_air_filtration.get_at(extruder.id()) && m_config.activate_air_filtration_on_completion.get_at(extruder.id())) {
|
||||
size_t fi = get_filament_config_index((int)extruder.id());
|
||||
if (m_config.activate_air_filtration.get_at(fi) && m_config.activate_air_filtration_on_completion.get_at(fi)) {
|
||||
activate_air_filtration_on_completion = true;
|
||||
complete_print_exhaust_fan_speed = std::max(complete_print_exhaust_fan_speed, m_config.complete_print_exhaust_fan_speed.get_at(extruder.id()));
|
||||
complete_print_exhaust_fan_speed = std::max(complete_print_exhaust_fan_speed, m_config.complete_print_exhaust_fan_speed.get_at(fi));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4439,7 +4465,7 @@ void GCode::_print_first_layer_extruder_temperatures(GCodeOutputStream &file, Pr
|
||||
bool include_g10 = print.config().gcode_flavor == gcfRepRapFirmware;
|
||||
if (custom_gcode_sets_temperature(gcode, 104, 109, include_g10, temp_by_gcode)) {
|
||||
// Set the extruder temperature at m_writer, but throw away the generated G-code as it will be written with the custom G-code.
|
||||
int temp = print.config().nozzle_temperature_initial_layer.get_at(first_printing_extruder_id);
|
||||
int temp = print.config().nozzle_temperature_initial_layer.get_at(get_filament_config_index((int)first_printing_extruder_id));
|
||||
if (temp_by_gcode >= 0 && temp_by_gcode < 1000)
|
||||
temp = temp_by_gcode;
|
||||
m_writer.set_temperature(temp, wait, first_printing_extruder_id);
|
||||
@@ -4447,13 +4473,13 @@ void GCode::_print_first_layer_extruder_temperatures(GCodeOutputStream &file, Pr
|
||||
// Custom G-code does not set the extruder temperature. Do it now.
|
||||
if (print.config().single_extruder_multi_material.value) {
|
||||
// Set temperature of the first printing extruder only.
|
||||
int temp = print.config().nozzle_temperature_initial_layer.get_at(first_printing_extruder_id);
|
||||
int temp = print.config().nozzle_temperature_initial_layer.get_at(get_filament_config_index((int)first_printing_extruder_id));
|
||||
if (temp > 0)
|
||||
file.write(m_writer.set_temperature(temp, wait, first_printing_extruder_id));
|
||||
} else {
|
||||
// Set temperatures of all the printing extruders.
|
||||
for (unsigned int tool_id : print.extruders()) {
|
||||
int temp = print.config().nozzle_temperature_initial_layer.get_at(tool_id);
|
||||
int temp = print.config().nozzle_temperature_initial_layer.get_at(get_filament_config_index((int)tool_id));
|
||||
if (m_ooze_prevention.enable && tool_id != first_printing_extruder_id) {
|
||||
if (print.config().idle_temperature.get_at(tool_id) == 0)
|
||||
temp += print.config().standby_temperature_delta.value;
|
||||
@@ -5237,6 +5263,10 @@ LayerResult GCode::process_layer(
|
||||
layer_ptr = support_layer;
|
||||
const Layer& layer = *layer_ptr;
|
||||
m_cur_layer_idx = layer.id();
|
||||
// A per-layer nozzle grouping can move the active filament to another variant column on a
|
||||
// layer boundary without a toolchange, so re-resolve the writer's config column here.
|
||||
if (Extruder *cur_filament = m_writer.filament())
|
||||
cur_filament->set_config_index((int)get_filament_config_index((int)cur_filament->id()));
|
||||
LayerResult result { {}, layer.id(), false, last_layer };
|
||||
if (layer_tools.extruders.empty())
|
||||
// Nothing to extrude.
|
||||
@@ -5501,8 +5531,9 @@ LayerResult GCode::process_layer(
|
||||
extruder.id() != m_writer.filament()->id())
|
||||
// In single extruder multi material mode, set the temperature for the current extruder only.
|
||||
continue;
|
||||
int temperature = print.config().nozzle_temperature.get_at(extruder.id());
|
||||
if (temperature > 0 && temperature != print.config().nozzle_temperature_initial_layer.get_at(extruder.id()))
|
||||
size_t fi = get_filament_config_index((int)extruder.id());
|
||||
int temperature = print.config().nozzle_temperature.get_at(fi);
|
||||
if (temperature > 0 && temperature != print.config().nozzle_temperature_initial_layer.get_at(fi))
|
||||
gcode += m_writer.set_temperature(temperature, false, extruder.id());
|
||||
}
|
||||
|
||||
@@ -8612,6 +8643,8 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
|
||||
}
|
||||
|
||||
gcode += m_writer.toolchange(new_filament_id);
|
||||
if (Extruder *fil = m_writer.filament())
|
||||
fil->set_config_index((int)get_filament_config_index((int)fil->id()));
|
||||
return gcode;
|
||||
}
|
||||
|
||||
@@ -8657,12 +8690,15 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
|
||||
gcode += m_ooze_prevention.pre_toolchange(*this);
|
||||
|
||||
// BBS
|
||||
float new_retract_length = m_config.retraction_length.get_at(new_filament_id);
|
||||
// Per-variant filament arrays can hold one column per variant a filament uses under a
|
||||
// per-layer nozzle grouping; resolve the column instead of indexing by the filament id.
|
||||
size_t new_fi = get_filament_config_index((int)new_filament_id);
|
||||
float new_retract_length = m_config.retraction_length.get_at(new_fi);
|
||||
float new_retract_length_toolchange = m_config.retract_length_toolchange.get_at(new_filament_id);
|
||||
int new_filament_temp = this->on_first_layer() ? m_config.nozzle_temperature_initial_layer.get_at(new_filament_id) : m_config.nozzle_temperature.get_at(new_filament_id);
|
||||
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);
|
||||
// BBS: if print_z == 0 use first layer temperature
|
||||
if (abs(print_z) < EPSILON)
|
||||
new_filament_temp = m_config.nozzle_temperature_initial_layer.get_at(new_filament_id);
|
||||
new_filament_temp = m_config.nozzle_temperature_initial_layer.get_at(new_fi);
|
||||
if (toolchange_temp_override > 0)
|
||||
new_filament_temp = toolchange_temp_override;
|
||||
|
||||
@@ -8685,9 +8721,12 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
|
||||
old_filament_id = m_writer.filament() != nullptr ? m_writer.filament()->id() : m_start_gcode_filament;
|
||||
old_extruder_id = m_writer.filament() != nullptr ? m_writer.filament()->extruder_id() : get_extruder_id(m_start_gcode_filament);
|
||||
|
||||
old_retract_length = m_config.retraction_length.get_at(old_filament_id);
|
||||
// Resolving the old filament at the current layer is safe: the per-layer nozzle maps are
|
||||
// gap-filled carry-forward, so its current-layer column matches the nozzle it occupies.
|
||||
size_t old_fi = get_filament_config_index(old_filament_id);
|
||||
old_retract_length = m_config.retraction_length.get_at(old_fi);
|
||||
old_retract_length_toolchange = m_config.retract_length_toolchange.get_at(old_filament_id);
|
||||
old_filament_temp = this->on_first_layer()? m_config.nozzle_temperature_initial_layer.get_at(old_filament_id) : m_config.nozzle_temperature.get_at(old_filament_id);
|
||||
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);
|
||||
|
||||
//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.
|
||||
float grab_purge_volume = m_config.grab_length.get_at(new_extruder_id) * 2.4;
|
||||
@@ -8707,7 +8746,7 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
|
||||
}
|
||||
wipe_volume = std::max(0.f, wipe_volume-grab_purge_volume);
|
||||
|
||||
old_filament_e_feedrate = (int) (60.0 * m_config.filament_max_volumetric_speed.get_at(old_filament_id) / filament_area);
|
||||
old_filament_e_feedrate = (int) (60.0 * m_config.filament_max_volumetric_speed.get_at(old_fi) / filament_area);
|
||||
old_filament_e_feedrate = old_filament_e_feedrate == 0 ? 100 : old_filament_e_feedrate;
|
||||
//BBS: must clean m_start_gcode_filament
|
||||
m_start_gcode_filament = -1;
|
||||
@@ -8719,11 +8758,11 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
|
||||
old_filament_e_feedrate = 200;
|
||||
}
|
||||
float wipe_length = wipe_volume / filament_area;
|
||||
int new_filament_e_feedrate = (int)(60.0 * m_config.filament_max_volumetric_speed.get_at(new_filament_id) / filament_area);
|
||||
int new_filament_e_feedrate = (int)(60.0 * m_config.filament_max_volumetric_speed.get_at(new_fi) / filament_area);
|
||||
new_filament_e_feedrate = new_filament_e_feedrate == 0 ? 100 : new_filament_e_feedrate;
|
||||
|
||||
// set volumetric speed of outer wall ,ignore per obejct,just use default setting
|
||||
float outer_wall_volumetric_speed = get_outer_wall_volumetric_speed(m_config, *m_print, new_filament_id, get_extruder_id(new_filament_id));
|
||||
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));
|
||||
float wipe_avoid_pos_x = 110.f;
|
||||
// Logical nozzle grouping (null on paths that don't populate it) + null-safe nozzle ids.
|
||||
auto group_result = m_print->get_layered_nozzle_group_result();
|
||||
@@ -8747,7 +8786,7 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
|
||||
dyn_config.set_key_value("nozzle_volume_types", new ConfigOptionStrings(get_nozzle_volume_types_by_nozzle_id(group_result.get())));
|
||||
// Old filament's nozzle-change retract length (filament_retract_length_nc; nil/-1 -> 0).
|
||||
dyn_config.set_key_value("filament_retract_length_nc", new ConfigOptionFloat(
|
||||
(old_filament_id != -1) ? (float) m_config.filament_retract_length_nc.get_at(old_filament_id) : 0.f));
|
||||
(old_filament_id != -1) ? (float) m_config.filament_retract_length_nc.get_at(get_filament_config_index(old_filament_id)) : 0.f));
|
||||
// Current parked-retract length of the incoming filament's extruder.
|
||||
dyn_config.set_key_value("new_extruder_retracted_length",
|
||||
new ConfigOptionFloat(m_writer.get_extruder_retracted_length((int) new_filament_id)));
|
||||
@@ -8789,13 +8828,20 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
|
||||
dyn_config.set_key_value("filament_tower_interface_print_temp", new ConfigOptionInt(interface_temp));
|
||||
}
|
||||
if (toolchange_temp_override > 0) {
|
||||
auto temps = m_config.nozzle_temperature.values;
|
||||
// Rebuild in filament order: the config arrays may carry per-variant columns, while
|
||||
// these placeholder vectors are consumed indexed by filament id.
|
||||
size_t num_filaments = m_print->config().filament_type.values.size();
|
||||
std::vector<int> temps(num_filaments);
|
||||
std::vector<int> first_layer_temps(num_filaments);
|
||||
for (size_t i = 0; i < num_filaments; ++i) {
|
||||
size_t fi_i = get_filament_config_index((int)i);
|
||||
temps[i] = m_config.nozzle_temperature.get_at(fi_i);
|
||||
first_layer_temps[i] = m_config.nozzle_temperature_initial_layer.get_at(fi_i);
|
||||
}
|
||||
if (new_filament_id < temps.size())
|
||||
temps[new_filament_id] = toolchange_temp_override;
|
||||
dyn_config.set_key_value("temperature", new ConfigOptionInts(temps));
|
||||
dyn_config.set_key_value("nozzle_temperature", new ConfigOptionInts(temps));
|
||||
|
||||
auto first_layer_temps = m_config.nozzle_temperature_initial_layer.values;
|
||||
if (new_filament_id < first_layer_temps.size())
|
||||
first_layer_temps[new_filament_id] = toolchange_temp_override;
|
||||
dyn_config.set_key_value("first_layer_temperature", new ConfigOptionInts(first_layer_temps));
|
||||
@@ -8882,6 +8928,8 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
|
||||
//BBS: don't add T[next extruder] if there is no T cmd on filament change
|
||||
//We inform the writer about what is happening, but we may not use the resulting gcode.
|
||||
std::string toolchange_command = m_writer.toolchange(new_filament_id);
|
||||
if (Extruder *fil = m_writer.filament())
|
||||
fil->set_config_index((int)get_filament_config_index((int)fil->id()));
|
||||
if (!custom_gcode_changes_tool(toolchange_gcode_parsed, m_writer.toolchange_prefix(), new_filament_id))
|
||||
gcode += toolchange_command;
|
||||
else {
|
||||
@@ -8890,8 +8938,9 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
|
||||
|
||||
// Set the temperature if the wipe tower didn't (not needed for non-single extruder MM)
|
||||
if (m_config.single_extruder_multi_material && !m_config.enable_prime_tower) {
|
||||
int temp = (m_layer_index <= 0 ? m_config.nozzle_temperature_initial_layer.get_at(new_filament_id) :
|
||||
m_config.nozzle_temperature.get_at(new_filament_id));
|
||||
size_t new_fi = get_filament_config_index(new_filament_id);
|
||||
int temp = (m_layer_index <= 0 ? m_config.nozzle_temperature_initial_layer.get_at(new_fi) :
|
||||
m_config.nozzle_temperature.get_at(new_fi));
|
||||
|
||||
gcode += m_writer.set_temperature(temp, false);
|
||||
}
|
||||
@@ -8918,13 +8967,20 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
|
||||
config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z));
|
||||
config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(new_filament_id)));
|
||||
if (toolchange_temp_override > 0) {
|
||||
auto temps = m_config.nozzle_temperature.values;
|
||||
// Rebuild in filament order: the config arrays may carry per-variant columns, while
|
||||
// these placeholder vectors are consumed indexed by filament id.
|
||||
size_t num_filaments = m_print->config().filament_type.values.size();
|
||||
std::vector<int> temps(num_filaments);
|
||||
std::vector<int> first_layer_temps(num_filaments);
|
||||
for (size_t i = 0; i < num_filaments; ++i) {
|
||||
size_t fi_i = get_filament_config_index((int)i);
|
||||
temps[i] = m_config.nozzle_temperature.get_at(fi_i);
|
||||
first_layer_temps[i] = m_config.nozzle_temperature_initial_layer.get_at(fi_i);
|
||||
}
|
||||
if (new_filament_id < temps.size())
|
||||
temps[new_filament_id] = toolchange_temp_override;
|
||||
config.set_key_value("temperature", new ConfigOptionInts(temps));
|
||||
config.set_key_value("nozzle_temperature", new ConfigOptionInts(temps));
|
||||
|
||||
auto first_layer_temps = m_config.nozzle_temperature_initial_layer.values;
|
||||
if (new_filament_id < first_layer_temps.size())
|
||||
first_layer_temps[new_filament_id] = toolchange_temp_override;
|
||||
config.set_key_value("first_layer_temperature", new ConfigOptionInts(first_layer_temps));
|
||||
|
||||
@@ -264,6 +264,12 @@ public:
|
||||
// append full config to the given string
|
||||
static void append_full_config(const Print& print, std::string& str);
|
||||
|
||||
// Per-filament config-slot resolvers for the current layer (m_cur_layer_idx): the filament
|
||||
// resolver keys filament-indexed arrays, the nozzle resolver keys (extruder x volume-type)
|
||||
// slot arrays. Both degenerate to filament_id / extruder index on single-volume printers.
|
||||
size_t get_filament_config_index(int filament_id) const;
|
||||
size_t get_nozzle_config_index(int filament_id) const;
|
||||
|
||||
// Object and support extrusions of the same PrintObject at the same print_z.
|
||||
// public, so that it could be accessed by free helper functions from GCode.cpp
|
||||
struct LayerToPrint
|
||||
@@ -395,11 +401,6 @@ private:
|
||||
void check_placeholder_parser_failed();
|
||||
size_t cur_extruder_index() const;
|
||||
size_t get_extruder_id(unsigned int filament_id) const;
|
||||
// Per-filament config-slot resolvers for the current layer (m_cur_layer_idx): the filament
|
||||
// resolver keys filament-indexed arrays, the nozzle resolver keys (extruder x volume-type)
|
||||
// slot arrays. Both degenerate to filament_id / extruder index on single-volume printers.
|
||||
size_t get_filament_config_index(int filament_id) const;
|
||||
size_t get_nozzle_config_index(int filament_id) const;
|
||||
void update_placeholder_parser_with_variant_params();
|
||||
|
||||
void set_last_pos(const Point &pos) { m_last_pos = Point3(pos, 0); m_last_pos_defined = true; }
|
||||
|
||||
@@ -2969,6 +2969,8 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
|
||||
for (size_t i = 0; i < filament_count; ++ i) {
|
||||
m_extruder_offsets[i] = to_3d(config.extruder_offset.get_at(filament_map[i] - 1).cast<float>().eval(), 0.f);
|
||||
m_extruder_colors[i] = static_cast<unsigned char>(i);
|
||||
// Orca: pre-heat bookkeeping reads the filament's first per-variant column by design;
|
||||
// it feeds estimation-side heat-up modelling only, never the emitted commands.
|
||||
m_filament_nozzle_temp_first_layer[i] = static_cast<int>(config.nozzle_temperature_initial_layer.get_at(i));
|
||||
m_filament_nozzle_temp[i] = static_cast<int>(config.nozzle_temperature.get_at(i));
|
||||
if (m_filament_nozzle_temp[i] == 0) {
|
||||
|
||||
@@ -1559,6 +1559,10 @@ void WipeTower::set_extruder(size_t idx, const PrintConfig& config)
|
||||
//while (m_filpar.size() < idx+1) // makes sure the required element is in the vector
|
||||
m_filpar.push_back(FilamentParameters());
|
||||
|
||||
// Orca: one row per filament, indexed by the raw filament id. Under a per-layer nozzle
|
||||
// grouping the per-variant arrays may hold several columns per filament; the tower has no
|
||||
// layer dimension here, so it keeps the filament's first column (tower x per-layer
|
||||
// grouping is a documented follow-up).
|
||||
m_filpar[idx].material = config.filament_type.get_at(idx);
|
||||
m_filpar[idx].is_soluble = config.wipe_tower_filament == 0 ? config.filament_soluble.get_at(idx) : (idx != size_t(config.wipe_tower_filament - 1));
|
||||
// BBS
|
||||
|
||||
@@ -1333,6 +1333,10 @@ void WipeTower2::set_extruder(size_t idx, const PrintConfig& config)
|
||||
//while (m_filpar.size() < idx+1) // makes sure the required element is in the vector
|
||||
m_filpar.push_back(FilamentParameters());
|
||||
|
||||
// Orca: one row per filament, indexed by the raw filament id. Under a per-layer nozzle
|
||||
// grouping the per-variant arrays may hold several columns per filament; the tower has no
|
||||
// layer dimension here, so it keeps the filament's first column (tower x per-layer
|
||||
// grouping is a documented follow-up).
|
||||
m_filpar[idx].material = config.filament_type.get_at(idx);
|
||||
if (m_wipe_tower_filament > 0)
|
||||
m_filpar[idx].is_soluble = (idx != size_t(m_wipe_tower_filament - 1));
|
||||
|
||||
@@ -404,3 +404,55 @@ TEST_CASE("EXTRUDER_LIMIT per-extruder clamping and max fallback", "[GCodeWriter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("Extruder reads the injected config column", "[GCodeWriter][H2C]") {
|
||||
GIVEN("A writer whose per-variant arrays hold three columns for two filaments") {
|
||||
GCodeWriter writer;
|
||||
// Column layout after a migrating regroup: filament 0 -> column 0, filament 1 ->
|
||||
// columns 1 (its first variant) and 2 (its second variant).
|
||||
writer.config.retraction_length.values = {0.8, 0.5, 1.2};
|
||||
writer.config.z_hop.values = {0.4, 0.6, 0.9};
|
||||
writer.config.retraction_speed.values = {30., 40., 50.};
|
||||
writer.config.filament_flow_ratio.values = {0.98, 1.0, 1.02};
|
||||
// Filament-indexed arrays keep one entry per filament.
|
||||
writer.config.filament_diameter.values = {1.75, 1.75};
|
||||
writer.set_extruders({0, 1});
|
||||
writer.toolchange(1);
|
||||
Extruder *fil = writer.filament();
|
||||
REQUIRE(fil != nullptr);
|
||||
REQUIRE(fil->id() == 1);
|
||||
const double crossection = 1.75 * 1.75 * 0.25 * PI;
|
||||
|
||||
WHEN("no column has been injected") {
|
||||
THEN("the getters read the filament id's column") {
|
||||
REQUIRE(fil->config_index() == 1);
|
||||
REQUIRE_THAT(fil->retraction_length(), Catch::Matchers::WithinAbs(0.5, 1e-9));
|
||||
REQUIRE_THAT(fil->retract_lift(), Catch::Matchers::WithinAbs(0.6, 1e-9));
|
||||
REQUIRE(fil->retract_speed() == 40);
|
||||
REQUIRE_THAT(fil->e_per_mm3(), Catch::Matchers::WithinRel(1.0 / crossection, 1e-9));
|
||||
}
|
||||
}
|
||||
WHEN("the second variant column is injected") {
|
||||
fil->set_config_index(2);
|
||||
THEN("the getters follow the column and the flow cache is rescaled") {
|
||||
REQUIRE(fil->config_index() == 2);
|
||||
REQUIRE_THAT(fil->retraction_length(), Catch::Matchers::WithinAbs(1.2, 1e-9));
|
||||
REQUIRE_THAT(fil->retract_lift(), Catch::Matchers::WithinAbs(0.9, 1e-9));
|
||||
REQUIRE(fil->retract_speed() == 50);
|
||||
REQUIRE_THAT(fil->e_per_mm3(), Catch::Matchers::WithinRel(1.02 / crossection, 1e-9));
|
||||
}
|
||||
THEN("filament-indexed reads keep using the filament id") {
|
||||
REQUIRE_THAT(fil->filament_diameter(), Catch::Matchers::WithinAbs(1.75, 1e-9));
|
||||
}
|
||||
}
|
||||
WHEN("a negative index is injected") {
|
||||
fil->set_config_index(2);
|
||||
fil->set_config_index(-1);
|
||||
THEN("resolution resets to the filament id") {
|
||||
REQUIRE(fil->config_index() == 1);
|
||||
REQUIRE_THAT(fil->retraction_length(), Catch::Matchers::WithinAbs(0.5, 1e-9));
|
||||
REQUIRE_THAT(fil->e_per_mm3(), Catch::Matchers::WithinRel(1.0 / crossection, 1e-9));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -827,6 +827,78 @@ TEST_CASE("Selector write-back expands migrating filaments and survives re-apply
|
||||
REQUIRE(print.config().retraction_length.values == merged_retract);
|
||||
}
|
||||
|
||||
TEST_CASE("Filaments ordered after a migrator shift columns and the resolver tracks them", "[Print][H2C][Dynamic]")
|
||||
{
|
||||
// When a mid-list filament expands to two columns, every later filament's values move one
|
||||
// column to the right — a raw get_at(filament_id) lands in the migrator's second column.
|
||||
// The layer-aware resolver must return the shifted column for both the expanded filament
|
||||
// arrays and the merged machine overrides.
|
||||
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
||||
config.option<ConfigOptionFloats>("nozzle_diameter", true)->values = {0.4, 0.4};
|
||||
config.option<ConfigOptionStrings>("extruder_nozzle_stats", true)->values = {"Standard#1", "Standard#1|High Flow#2"};
|
||||
config.option<ConfigOptionEnumsGeneric>("extruder_type", true)->values = {etDirectDrive, etDirectDrive};
|
||||
config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type", true)->values = {nvtStandard, nvtHybrid};
|
||||
config.option<ConfigOptionStrings>("extruder_variant_list", true)->values = {"Direct Drive Standard,Direct Drive High Flow",
|
||||
"Direct Drive Standard,Direct Drive High Flow"};
|
||||
config.option<ConfigOptionInts>("print_extruder_id", true)->values = {1, 1, 2, 2};
|
||||
config.option<ConfigOptionStrings>("print_extruder_variant", true)->values = {"Direct Drive Standard", "Direct Drive High Flow",
|
||||
"Direct Drive Standard", "Direct Drive High Flow"};
|
||||
// Three filaments: 0 -> extruder 1 (Std), 1 -> extruder 2, migrating Standard -> High Flow
|
||||
// between layers, 2 -> extruder 2 (Std) — ordered AFTER the migrator.
|
||||
config.option<ConfigOptionStrings>("filament_type", true)->values = {"PLA", "PLA", "PLA"};
|
||||
config.option<ConfigOptionFloats>("filament_diameter", true)->values = {1.75, 1.75, 1.75};
|
||||
config.option<ConfigOptionStrings>("filament_colour", true)->values = {"#FF0000", "#00FF00", "#0000FF"};
|
||||
config.option<ConfigOptionInts>("filament_map", true)->values = {1, 2, 2};
|
||||
config.option<ConfigOptionInts>("filament_volume_map", true)->values = {(int) nvtStandard, (int) nvtStandard, (int) nvtStandard};
|
||||
config.option<ConfigOptionInts>("filament_self_index", true)->values = {1, 1, 2, 2, 3, 3};
|
||||
config.option<ConfigOptionStrings>("filament_extruder_variant", true)->values = {"Direct Drive Standard", "Direct Drive High Flow",
|
||||
"Direct Drive Standard", "Direct Drive High Flow",
|
||||
"Direct Drive Standard", "Direct Drive High Flow"};
|
||||
config.option<ConfigOptionInts>("nozzle_temperature", true)->values = {200, 210, 220, 230, 240, 250};
|
||||
config.option<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = {0.5, 0.5, 0.7, 0.9, 1.4, 1.4};
|
||||
config.option<ConfigOptionFloats>("retraction_length", true)->values = {0.8, 0.9, 1.0, 1.1};
|
||||
|
||||
Model model;
|
||||
model.add_object("cube", "", make_cube(20, 20, 20))->add_instance();
|
||||
|
||||
Print print;
|
||||
print.apply(model, config);
|
||||
|
||||
std::vector<NozzleInfo> nozzle_list;
|
||||
{
|
||||
NozzleInfo n;
|
||||
n.diameter = "0.4";
|
||||
n.volume_type = nvtStandard; n.extruder_id = 0; n.group_id = 0; nozzle_list.push_back(n);
|
||||
n.volume_type = nvtStandard; n.extruder_id = 1; n.group_id = 1; nozzle_list.push_back(n);
|
||||
n.volume_type = nvtHighFlow; n.extruder_id = 1; n.group_id = 2; nozzle_list.push_back(n);
|
||||
}
|
||||
// Filament 1: Standard nozzle on layer 0, High Flow nozzle on layer 1; filament 2 stays Standard.
|
||||
std::vector<std::vector<int>> layer_maps = {{0, 1, 1}, {0, 2, 1}};
|
||||
std::vector<std::vector<unsigned int>> layer_seqs = {{0, 1, 2}, {0, 1, 2}};
|
||||
auto group = LayeredNozzleGroupResult::create(layer_maps, nozzle_list, {0, 1, 2}, layer_seqs);
|
||||
REQUIRE(group.has_value());
|
||||
REQUIRE(group->is_support_dynamic_nozzle_map());
|
||||
print.set_nozzle_group_result(std::make_shared<LayeredNozzleGroupResult>(*group));
|
||||
|
||||
print.update_to_config_by_nozzle_group_result(*group);
|
||||
|
||||
// Filament 1 holds columns 1-2; filament 2's values shift to column 3.
|
||||
REQUIRE(print.config().filament_self_index.values == std::vector<int>{1, 2, 2, 3});
|
||||
REQUIRE(print.config().nozzle_temperature.values == std::vector<int>{200, 220, 230, 240});
|
||||
// The migrator resolves per layer to its two columns.
|
||||
REQUIRE(print.get_filament_config_indx(1, 0) == 1);
|
||||
REQUIRE(print.get_filament_config_indx(1, 1) == 2);
|
||||
// The filament after it no longer lives at its raw index on any layer.
|
||||
REQUIRE(print.get_filament_config_indx(2, 0) == 3);
|
||||
REQUIRE(print.get_filament_config_indx(2, 1) == 3);
|
||||
// Merged machine override: filament 2's value sits in the shifted column, while a raw
|
||||
// get_at(2) would read the migrator's High Flow column.
|
||||
const std::vector<double> merged = print.config().retraction_length.values;
|
||||
REQUIRE(merged.size() == 4);
|
||||
REQUIRE_THAT(merged[3], Catch::Matchers::WithinAbs(1.4, 1e-9));
|
||||
REQUIRE_THAT(merged[2], Catch::Matchers::WithinAbs(0.9, 1e-9));
|
||||
}
|
||||
|
||||
TEST_CASE("Selector slicing keeps the result valid across re-apply", "[Print][H2C][Dynamic]")
|
||||
{
|
||||
// The dynamic counterpart of the static re-apply test above: a full process() run through
|
||||
|
||||
Reference in New Issue
Block a user