Add nozzle flow variant & make some options multi-variant (#13712)

# Description

Port BBS per-extruder config variants toggle, thanks Bambu!

Also fix invalid num error when resetting multi-variant filament
overrides.

TODOs:

- [x] Make more configs multi-variant (such as speeds)
- [x] Add flow variant (normal/high flow) combo box
- [x] Add botton to sync config to other variant


# Screenshots/Recordings/Graphs

<img width="846" height="1494" alt="image"
src="https://github.com/user-attachments/assets/47abf053-f5b8-4c86-ac0b-c0323ed8b242"
/>

<img width="1478" height="1189" alt="707420e5be01c30af3e6ede1f3dc9b67"
src="https://github.com/user-attachments/assets/11268712-da65-42ab-9cb8-3cede7790a5c"
/>

<img width="1478" height="1189" alt="81d5d3877f4d5bf9dccd44d7f0b30aa7"
src="https://github.com/user-attachments/assets/cda5ea51-07c3-48fc-bfbb-1428dca14e26"
/>


## Tests

<!--
> Please describe the tests that you have conducted to verify the
changes made in this PR.
-->

<!--
> A guide for users on how to download the artifacts from this PR.
-->

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
Fix #10336
This commit is contained in:
SoftFever
2026-06-28 22:30:40 +08:00
committed by GitHub
53 changed files with 2727 additions and 1353 deletions

View File

@@ -337,7 +337,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
if(retraction_length_remaining <=EPSILON) return {retractionBeforeWipe,0.f};
// Calculate wipe speed
double wipe_speed = config.role_based_wipe_speed ? writer.get_current_speed() / 60.0 : config.get_abs_value("wipe_speed");
double wipe_speed = config.role_based_wipe_speed ? writer.get_current_speed() / 60.0 : config.get_abs_value("wipe_speed", gcodegen.config().travel_speed.get_at(gcodegen.cur_extruder_index()));
wipe_speed = std::max(wipe_speed, 10.0);
// Process wipe path & calculate wipe path length
@@ -429,7 +429,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
/* Reduce feedrate a bit; travel speed is often too high to move on existing material.
Too fast = ripping of existing material; too slow = short wipe path, thus more blob. */
double _wipe_speed = gcodegen.config().get_abs_value("wipe_speed");// gcodegen.writer().config.travel_speed.value * 0.8;
double _wipe_speed = gcodegen.config().get_abs_value("wipe_speed", gcodegen.config().travel_speed.get_at(gcodegen.cur_extruder_index()));// gcodegen.writer().config.travel_speed.value * 0.8;
if(gcodegen.config().role_based_wipe_speed)
_wipe_speed = gcodegen.writer().get_current_speed() / 60.0;
if(_wipe_speed < 10)
@@ -515,7 +515,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
outer_wall_line_width = default_line_width == 0.0 ? filament_diameter : default_line_width;
}
Flow outer_wall_flow = Flow(outer_wall_line_width, config.layer_height, config.nozzle_diameter.get_at(extruder_id));
float outer_wall_speed = print.default_region_config().outer_wall_speed.value;
float outer_wall_speed = print.default_region_config().outer_wall_speed.get_at(extruder_id);
outer_wall_volumetric_speed = outer_wall_speed * outer_wall_flow.mm3_per_mm();
if (outer_wall_volumetric_speed > filament_max_volumetric_speed)
outer_wall_volumetric_speed = filament_max_volumetric_speed;
@@ -1602,6 +1602,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
#define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.filament()->extruder_id())
#define FILAMENT_CONFIG(OPT) m_config.OPT.get_at(m_writer.filament()->id())
#define NOZZLE_CONFIG(OPT) m_config.OPT.get_at(cur_extruder_index())
void GCode::PlaceholderParserIntegration::reset()
{
@@ -2806,11 +2807,11 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
print.throw_if_canceled();
m_cooling_buffer = make_unique<CoolingBuffer>(*this);
m_cooling_buffer->set_current_extruder(initial_extruder_id);
int extruder_id = get_extruder_id(initial_extruder_id);
m_cooling_buffer = make_unique<CoolingBuffer>(*this);
m_cooling_buffer->set_current_extruder(initial_extruder_id, extruder_id);
// Orca: Initialise AdaptivePA processor filter
m_pa_processor = std::make_unique<AdaptivePAProcessor>(*this, tool_ordering.all_extruders());
@@ -3245,12 +3246,12 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
if (print.calib_params().mode == CalibMode::Calib_PA_Line) {
std::string gcode;
gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Layer_Change) + "\n";
if ((print.default_object_config().outer_wall_acceleration.value > 0 && print.default_object_config().outer_wall_acceleration.value > 0)) {
gcode += m_writer.set_print_acceleration((unsigned int)floor(print.default_object_config().outer_wall_acceleration.value + 0.5));
if ((NOZZLE_CONFIG(outer_wall_acceleration) > 0 && NOZZLE_CONFIG(outer_wall_acceleration) > 0)) {
gcode += m_writer.set_print_acceleration((unsigned int)floor(NOZZLE_CONFIG(outer_wall_acceleration) + 0.5));
}
if (print.default_object_config().outer_wall_jerk.value > 0) {
double jerk = print.default_object_config().outer_wall_jerk.value;
if (NOZZLE_CONFIG(outer_wall_jerk) > 0) {
double jerk = NOZZLE_CONFIG(outer_wall_jerk);
gcode += m_writer.set_jerk_xy(jerk);
}
@@ -3333,8 +3334,8 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
file.writeln(printing_by_object_gcode);
}
// Reset the cooling buffer internal state (the current position, feed rate, accelerations).
m_cooling_buffer->set_current_extruder(initial_extruder_id, get_extruder_id(initial_extruder_id));
m_cooling_buffer->reset(this->writer().get_position());
m_cooling_buffer->set_current_extruder(initial_extruder_id);
// Process all layers of a single object instance (sequential mode) with a parallel pipeline:
// Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser
// and export G-code into file.
@@ -3645,6 +3646,13 @@ void GCode::check_placeholder_parser_failed()
}
}
size_t GCode::cur_extruder_index() const
{
//TODO: check if the function is duplicated
//just return m_writer.filament()->extruder_id()
return get_extruder_id(m_writer.filament()->id());
}
size_t GCode::get_extruder_id(unsigned int filament_id) const
{
if (m_print) {
@@ -3940,51 +3948,75 @@ void GCode::print_machine_envelope(GCodeOutputStream &file, Print &print)
const auto flavor = print.config().gcode_flavor.value;
if ((flavor == gcfMarlinLegacy || flavor == gcfMarlinFirmware || flavor == gcfRepRapFirmware) &&
print.config().emit_machine_limits_to_gcode.value == true) {
// Get all physical tool ids current print will use
std::unordered_set<unsigned int> used_extruders;
for (const auto& extruder : m_writer.extruders()) {
used_extruders.insert(extruder.extruder_id());
}
// Get the max limit value among used extruders
auto get_max_value = [&used_extruders](const std::string key, const ConfigOptionFloats& v) {
unsigned int stride = 1;
if (printer_options_with_variant_2.count(key) > 0) {
stride = 2;
}
double value = std::numeric_limits<double>::lowest();
for (unsigned int extruder : used_extruders) {
value = std::max(value, v.values[extruder * stride]);
}
assert(value > std::numeric_limits<double>::lowest());
return value;
};
#define MAX_LIMIT(OPT) get_max_value(#OPT, print.config().OPT)
int factor = flavor == gcfRepRapFirmware ? 60 : 1; // RRF M203 and M566 are in mm/min
file.write_format("M201 X%d Y%d Z%d E%d\n",
int(print.config().machine_max_acceleration_x.values.front() + 0.5),
int(print.config().machine_max_acceleration_y.values.front() + 0.5),
int(print.config().machine_max_acceleration_z.values.front() + 0.5),
int(print.config().machine_max_acceleration_e.values.front() + 0.5));
int(MAX_LIMIT(machine_max_acceleration_x) + 0.5),
int(MAX_LIMIT(machine_max_acceleration_y) + 0.5),
int(MAX_LIMIT(machine_max_acceleration_z) + 0.5),
int(MAX_LIMIT(machine_max_acceleration_e) + 0.5));
file.write_format("M203 X%d Y%d Z%d E%d\n",
int(print.config().machine_max_speed_x.values.front() * factor + 0.5),
int(print.config().machine_max_speed_y.values.front() * factor + 0.5),
int(print.config().machine_max_speed_z.values.front() * factor + 0.5),
int(print.config().machine_max_speed_e.values.front() * factor + 0.5));
int(MAX_LIMIT(machine_max_speed_x) * factor + 0.5),
int(MAX_LIMIT(machine_max_speed_y) * factor + 0.5),
int(MAX_LIMIT(machine_max_speed_z) * factor + 0.5),
int(MAX_LIMIT(machine_max_speed_e) * factor + 0.5));
// Now M204 - acceleration. This one is quite hairy thanks to how Marlin guys care about
// Legacy Marlin should export travel acceleration the same as printing acceleration.
// MarlinFirmware has the two separated.
int travel_acc = flavor == gcfMarlinLegacy
? int(print.config().machine_max_acceleration_extruding.values.front() + 0.5)
: int(print.config().machine_max_acceleration_travel.values.front() + 0.5);
? int(MAX_LIMIT(machine_max_acceleration_extruding) + 0.5)
: int(MAX_LIMIT(machine_max_acceleration_travel) + 0.5);
if (flavor == gcfRepRapFirmware)
file.write_format("M204 P%d T%d ; sets acceleration (P, T), mm/sec^2\n",
int(print.config().machine_max_acceleration_extruding.values.front() + 0.5),
int(MAX_LIMIT(machine_max_acceleration_extruding) + 0.5),
travel_acc);
else if (flavor == gcfMarlinFirmware)
// New Marlin uses M204 P[print] R[retract] T[travel]
file.write_format("M204 P%d R%d T%d ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2\n",
int(print.config().machine_max_acceleration_extruding.values.front() + 0.5),
int(print.config().machine_max_acceleration_retracting.values.front() + 0.5),
int(print.config().machine_max_acceleration_travel.values.front() + 0.5));
int(MAX_LIMIT(machine_max_acceleration_extruding) + 0.5),
int(MAX_LIMIT(machine_max_acceleration_retracting) + 0.5),
int(MAX_LIMIT(machine_max_acceleration_travel) + 0.5));
else
file.write_format("M204 P%d R%d T%d\n",
int(print.config().machine_max_acceleration_extruding.values.front() + 0.5),
int(print.config().machine_max_acceleration_retracting.values.front() + 0.5),
int(MAX_LIMIT(machine_max_acceleration_extruding) + 0.5),
int(MAX_LIMIT(machine_max_acceleration_retracting) + 0.5),
travel_acc);
assert(is_decimal_separator_point());
file.write_format(flavor == gcfRepRapFirmware
? "M566 X%.2lf Y%.2lf Z%.2lf E%.2lf ; sets the jerk limits, mm/min\n"
: "M205 X%.2lf Y%.2lf Z%.2lf E%.2lf ; sets the jerk limits, mm/sec\n",
print.config().machine_max_jerk_x.values.front() * factor,
print.config().machine_max_jerk_y.values.front() * factor,
print.config().machine_max_jerk_z.values.front() * factor,
print.config().machine_max_jerk_e.values.front() * factor);
MAX_LIMIT(machine_max_jerk_x) * factor,
MAX_LIMIT(machine_max_jerk_y) * factor,
MAX_LIMIT(machine_max_jerk_z) * factor,
MAX_LIMIT(machine_max_jerk_e) * factor);
// New Marlin uses M205 J[mm] for junction deviation (only apply if it is > 0)
file.write_format(writer().set_junction_deviation(config().machine_max_junction_deviation.values.front()).c_str());
file.write_format(writer().set_junction_deviation(MAX_LIMIT(machine_max_junction_deviation)).c_str());
// Orca: Override input shaping values
if (print.config().input_shaping_emit.value && flavor != gcfMarlinLegacy) {
@@ -3997,6 +4029,7 @@ void GCode::print_machine_envelope(GCodeOutputStream &file, Print &print)
}
}
}
#undef MAX_LIMIT
}
// BBS
@@ -4434,10 +4467,10 @@ std::string GCode::generate_skirt(const Print &print,
if (first_layer && i==loops.first) {
//set skirt start point location
const Point desired_start_point = Skirt::find_start_point(loop, skirt_start_angle);
gcode += this->extrude_loop(loop, "skirt", m_config.support_speed.value, {}, &desired_start_point);
gcode += this->extrude_loop(loop, "skirt", NOZZLE_CONFIG(support_speed), {}, &desired_start_point);
}
else
gcode += this->extrude_loop(loop, "skirt", m_config.support_speed.value);
gcode += this->extrude_loop(loop, "skirt", NOZZLE_CONFIG(support_speed));
// If we only want a single wall on non-first layers, break now
if (!first_layer && print.m_config.single_loop_draft_shield) {
@@ -4499,7 +4532,7 @@ std::string GCode::generate_object_brim(const Print &print, const PrintObject &o
m_avoid_crossing_perimeters.use_external_mp();
for (const ExtrusionEntity* ee : brim.entities)
if (ee != nullptr)
gcode += this->extrude_entity(*ee, "brim", m_config.support_speed.value);
gcode += this->extrude_entity(*ee, "brim", NOZZLE_CONFIG(support_speed));
m_avoid_crossing_perimeters.use_external_mp(false);
m_avoid_crossing_perimeters.disable_once();
for (ObjectID object_id : object_ids)
@@ -4698,12 +4731,12 @@ LayerResult GCode::process_layer(
}
case CalibMode::Calib_VFA_Tower: {
auto _speed = print.calib_params().start + std::floor(print_z / 5.0) * print.calib_params().step;
m_calib_config.set_key_value("outer_wall_speed", new ConfigOptionFloat(std::round(_speed)));
m_calib_config.set_key_value("outer_wall_speed", new ConfigOptionFloatsNullable({std::round(_speed)}));
break;
}
case CalibMode::Calib_Vol_speed_Tower: {
auto _speed = print.calib_params().start + print_z * print.calib_params().step;
m_calib_config.set_key_value("outer_wall_speed", new ConfigOptionFloat(std::round(_speed)));
m_calib_config.set_key_value("outer_wall_speed", new ConfigOptionFloatsNullable({std::round(_speed)}));
break;
}
case CalibMode::Calib_Retraction_tower: {
@@ -4760,16 +4793,16 @@ LayerResult GCode::process_layer(
//BBS
if (first_layer) {
// Orca: we don't need to optimize the Klipper as only set once
if (m_config.default_acceleration.value > 0 && m_config.initial_layer_acceleration.value > 0) {
gcode += m_writer.set_print_acceleration((unsigned int)floor(m_config.initial_layer_acceleration.value + 0.5));
if (NOZZLE_CONFIG(default_acceleration) > 0 && NOZZLE_CONFIG(initial_layer_acceleration) > 0) {
gcode += m_writer.set_print_acceleration((unsigned int)floor(NOZZLE_CONFIG(initial_layer_acceleration) + 0.5));
}
if (m_config.default_jerk.value > 0 && m_config.initial_layer_jerk.value > 0) {
gcode += m_writer.set_jerk_xy(m_config.initial_layer_jerk.value);
if (NOZZLE_CONFIG(default_jerk) > 0 && NOZZLE_CONFIG(initial_layer_jerk) > 0) {
gcode += m_writer.set_jerk_xy(NOZZLE_CONFIG(initial_layer_jerk));
}
if (m_writer.get_gcode_flavor() == gcfMarlinFirmware && m_config.default_junction_deviation.value > 0) {
gcode += m_writer.set_junction_deviation(m_config.default_junction_deviation.value);
if (m_writer.get_gcode_flavor() == gcfMarlinFirmware && NOZZLE_CONFIG(default_junction_deviation) > 0) {
gcode += m_writer.set_junction_deviation(NOZZLE_CONFIG(default_junction_deviation));
}
}
@@ -4790,12 +4823,12 @@ LayerResult GCode::process_layer(
}
// Reset acceleration at sencond layer
// Orca: only set once, don't need to call set_accel_and_jerk
if (m_config.default_acceleration.value > 0 && m_config.initial_layer_acceleration.value > 0) {
gcode += m_writer.set_print_acceleration((unsigned int) floor(m_config.default_acceleration.value + 0.5));
if (NOZZLE_CONFIG(default_acceleration) > 0 && NOZZLE_CONFIG(initial_layer_acceleration) > 0) {
gcode += m_writer.set_print_acceleration((unsigned int) floor(NOZZLE_CONFIG(default_acceleration) + 0.5));
}
if (m_config.default_jerk.value > 0 && m_config.initial_layer_jerk.value > 0) {
gcode += m_writer.set_jerk_xy(m_config.default_jerk.value);
if (NOZZLE_CONFIG(default_jerk) > 0 && NOZZLE_CONFIG(initial_layer_jerk) > 0) {
gcode += m_writer.set_jerk_xy(NOZZLE_CONFIG(default_jerk));
}
// Transition from 1st to 2nd layer. Adjust nozzle temperatures as prescribed by the nozzle dependent
@@ -4850,8 +4883,8 @@ LayerResult GCode::process_layer(
for (const auto &layer_to_print : layers) {
if (layer_to_print.object_layer) {
const auto& regions = layer_to_print.object_layer->regions();
const bool enable_overhang_speed = std::any_of(regions.begin(), regions.end(), [](const LayerRegion* r) {
return r->has_extrusions() && r->region().config().enable_overhang_speed;
const bool enable_overhang_speed = std::any_of(regions.begin(), regions.end(), [this](const LayerRegion* r) {
return r->has_extrusions() && r->region().config().enable_overhang_speed.get_at(cur_extruder_index());
});
if (enable_overhang_speed) {
m_extrusion_quality_estimator.prepare_for_new_layer(layer_to_print.original_object,
@@ -5391,7 +5424,7 @@ LayerResult GCode::process_layer(
this->set_origin(0., 0.);
m_avoid_crossing_perimeters.use_external_mp();
for (const ExtrusionEntity* ee : print.m_supportBrimMap.at(instance_to_print.print_object.id()).entities) {
gcode += this->extrude_entity(*ee, "brim", m_config.support_speed.value);
gcode += this->extrude_entity(*ee, "brim", NOZZLE_CONFIG(support_speed));
}
m_avoid_crossing_perimeters.use_external_mp(false);
// Allow a straight travel move to the first object point.
@@ -5805,11 +5838,11 @@ std::string GCode::extrude_loop(const ExtrusionLoop& loop_ref,
// SoftFever: check loop lenght for small perimeter.
double small_peri_speed = -1;
if (speed == -1 && loop.length() <= SMALL_PERIMETER_LENGTH(m_config.small_perimeter_threshold.value)) {
if(m_config.small_perimeter_speed == 0)
small_peri_speed = m_config.outer_wall_speed * 0.5;
if (speed == -1 && loop.length() <= SMALL_PERIMETER_LENGTH(NOZZLE_CONFIG(small_perimeter_threshold))) {
if(NOZZLE_CONFIG(small_perimeter_speed).value == 0)
small_peri_speed = NOZZLE_CONFIG(outer_wall_speed) * 0.5;
else
small_peri_speed = m_config.small_perimeter_speed.get_abs_value(m_config.outer_wall_speed);
small_peri_speed = NOZZLE_CONFIG(small_perimeter_speed).get_abs_value(NOZZLE_CONFIG(outer_wall_speed));
}
// extrude along the path
@@ -6199,8 +6232,8 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill
if (!support_fills.no_sort)
chain_and_reorder_extrusion_entities(extrusions, m_last_pos.to_point());
const double support_speed = m_config.support_speed.value;
const double support_interface_speed = m_config.get_abs_value("support_interface_speed");
//const double support_speed = m_config.support_speed.value;
//const double support_interface_speed = m_config.get_abs_value("support_interface_speed");
for (const ExtrusionEntity *ee : extrusions) {
ExtrusionRole role = ee->role();
assert(role == erSupportMaterial || role == erSupportMaterialInterface || role == erSupportTransition || role == erIroning);
@@ -6411,47 +6444,47 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
unsigned int acceleration_i = 0;
double jerk = 0;
// adjust acceleration
if (m_config.default_acceleration.value > 0) {
if (NOZZLE_CONFIG(default_acceleration) > 0) {
double acceleration;
if (this->on_first_layer() && m_config.initial_layer_acceleration.value > 0) {
acceleration = m_config.initial_layer_acceleration.value;
if (this->on_first_layer() && NOZZLE_CONFIG(initial_layer_acceleration) > 0) {
acceleration = NOZZLE_CONFIG(initial_layer_acceleration);
#if 0
} else if (this->object_layer_over_raft() && m_config.first_layer_acceleration_over_raft.value > 0) {
acceleration = m_config.first_layer_acceleration_over_raft.value;
#endif
} else if (m_config.get_abs_value("bridge_acceleration") > 0 && is_bridge(path.role())) {
acceleration = m_config.get_abs_value("bridge_acceleration");
} else if (m_config.get_abs_value("sparse_infill_acceleration") > 0 && (path.role() == erInternalInfill)) {
acceleration = m_config.get_abs_value("sparse_infill_acceleration");
} else if (m_config.get_abs_value("internal_solid_infill_acceleration") > 0 && (path.role() == erSolidInfill)) {
acceleration = m_config.get_abs_value("internal_solid_infill_acceleration");
} else if (m_config.outer_wall_acceleration.value > 0 && is_external_perimeter(path.role())) {
acceleration = m_config.outer_wall_acceleration.value;
} else if (m_config.inner_wall_acceleration.value > 0 && is_internal_perimeter(path.role())) {
acceleration = m_config.inner_wall_acceleration.value;
} else if (m_config.top_surface_acceleration.value > 0 && is_top_surface(path.role())) {
acceleration = m_config.top_surface_acceleration.value;
} else if (m_config.get_abs_value_at("bridge_acceleration", cur_extruder_index()) > 0 && is_bridge(path.role())) {
acceleration = m_config.get_abs_value_at("bridge_acceleration", cur_extruder_index());
} else if (m_config.get_abs_value_at("sparse_infill_acceleration", cur_extruder_index()) > 0 && (path.role() == erInternalInfill)) {
acceleration = m_config.get_abs_value_at("sparse_infill_acceleration", cur_extruder_index());
} else if (m_config.get_abs_value_at("internal_solid_infill_acceleration", cur_extruder_index()) > 0 && (path.role() == erSolidInfill)) {
acceleration = m_config.get_abs_value_at("internal_solid_infill_acceleration", cur_extruder_index());
} else if (NOZZLE_CONFIG(outer_wall_acceleration) > 0 && is_external_perimeter(path.role())) {
acceleration = NOZZLE_CONFIG(outer_wall_acceleration);
} else if (NOZZLE_CONFIG(inner_wall_acceleration) > 0 && is_internal_perimeter(path.role())) {
acceleration = NOZZLE_CONFIG(inner_wall_acceleration);
} else if (NOZZLE_CONFIG(top_surface_acceleration) > 0 && is_top_surface(path.role())) {
acceleration = NOZZLE_CONFIG(top_surface_acceleration);
} else {
acceleration = m_config.default_acceleration.value;
acceleration = NOZZLE_CONFIG(default_acceleration);
}
acceleration_i = (unsigned int)floor(acceleration + 0.5);
}
// adjust X Y jerk
if (m_config.default_jerk.value > 0) {
if (this->on_first_layer() && m_config.initial_layer_jerk.value > 0) {
jerk = m_config.initial_layer_jerk.value;
} else if (m_config.outer_wall_jerk.value > 0 && is_external_perimeter(path.role())) {
jerk = m_config.outer_wall_jerk.value;
} else if (m_config.inner_wall_jerk.value > 0 && is_internal_perimeter(path.role())) {
jerk = m_config.inner_wall_jerk.value;
} else if (m_config.top_surface_jerk.value > 0 && is_top_surface(path.role())) {
jerk = m_config.top_surface_jerk.value;
} else if (m_config.infill_jerk.value > 0 && is_infill(path.role())) {
jerk = m_config.infill_jerk.value;
if (NOZZLE_CONFIG(default_jerk) > 0) {
if (this->on_first_layer() && NOZZLE_CONFIG(initial_layer_jerk) > 0) {
jerk = NOZZLE_CONFIG(initial_layer_jerk);
} else if (NOZZLE_CONFIG(outer_wall_jerk) > 0 && is_external_perimeter(path.role())) {
jerk = NOZZLE_CONFIG(outer_wall_jerk);
} else if (NOZZLE_CONFIG(inner_wall_jerk) > 0 && is_internal_perimeter(path.role())) {
jerk = NOZZLE_CONFIG(inner_wall_jerk);
} else if (NOZZLE_CONFIG(top_surface_jerk) > 0 && is_top_surface(path.role())) {
jerk = NOZZLE_CONFIG(top_surface_jerk);
} else if (NOZZLE_CONFIG(infill_jerk) > 0 && is_infill(path.role())) {
jerk = NOZZLE_CONFIG(infill_jerk);
}
else {
jerk = m_config.default_jerk.value;
jerk = NOZZLE_CONFIG(default_jerk);
}
}
@@ -6514,37 +6547,37 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
// set speed
if (speed == -1) {
if (path.role() == erPerimeter) {
speed = m_config.get_abs_value("inner_wall_speed");
speed = NOZZLE_CONFIG(inner_wall_speed);
if (sloped) {
speed = std::min(speed, m_config.scarf_joint_speed.get_abs_value(m_config.get_abs_value("inner_wall_speed")));
speed = std::min(speed, m_config.scarf_joint_speed.get_abs_value(speed));
}
} else if (path.role() == erExternalPerimeter) {
speed = m_config.get_abs_value("outer_wall_speed");
speed = NOZZLE_CONFIG(outer_wall_speed);
if (sloped) {
speed = std::min(speed, m_config.scarf_joint_speed.get_abs_value(m_config.get_abs_value("outer_wall_speed")));
speed = std::min(speed, m_config.scarf_joint_speed.get_abs_value(speed));
}
}
else if(path.role() == erInternalBridgeInfill) {
speed = m_config.get_abs_value("internal_bridge_speed");
speed = m_config.get_abs_value_at("internal_bridge_speed", cur_extruder_index());
} else if (path.role() == erOverhangPerimeter || path.role() == erSupportTransition || path.role() == erBridgeInfill) {
speed = m_config.get_abs_value("bridge_speed");
speed = NOZZLE_CONFIG(bridge_speed);
} else if (path.role() == erInternalInfill) {
speed = m_config.get_abs_value("sparse_infill_speed");
speed = NOZZLE_CONFIG(sparse_infill_speed);
} else if (path.role() == erSolidInfill) {
speed = m_config.get_abs_value("internal_solid_infill_speed");
speed = NOZZLE_CONFIG(internal_solid_infill_speed);
} else if (path.role() == erTopSolidInfill) {
speed = m_config.get_abs_value("top_surface_speed");
speed = NOZZLE_CONFIG(top_surface_speed);
} else if (path.role() == erIroning) {
speed = m_config.get_abs_value("ironing_speed");
} else if (path.role() == erBottomSurface) {
speed = m_config.get_abs_value("initial_layer_infill_speed");
speed = NOZZLE_CONFIG(initial_layer_infill_speed);
} else if (path.role() == erGapFill) {
speed = m_config.get_abs_value("gap_infill_speed");
speed = NOZZLE_CONFIG(gap_infill_speed);
}
else if (path.role() == erSupportMaterial ||
path.role() == erSupportMaterialInterface) {
const double support_speed = m_config.support_speed.value;
const double support_interface_speed = m_config.get_abs_value("support_interface_speed");
const double support_speed = NOZZLE_CONFIG(support_speed);
const double support_interface_speed = NOZZLE_CONFIG(support_interface_speed);
speed = (path.role() == erSupportMaterial) ? support_speed : support_interface_speed;
} else {
throw Slic3r::InvalidArgument("Invalid speed");
@@ -6565,16 +6598,16 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
//BBS: for solid infill of first layer, speed can be higher as long as
//wall lines have be attached
if (path.role() != erBottomSurface) {
speed = is_perimeter(path.role()) ? m_config.get_abs_value("initial_layer_speed") :
m_config.get_abs_value("initial_layer_infill_speed");
speed = is_perimeter(path.role()) ? NOZZLE_CONFIG(initial_layer_speed) :
NOZZLE_CONFIG(initial_layer_infill_speed);
}
} else if (m_config.slow_down_layers > 1 && m_config.raft_layers == 0) {
if (_layer > 0 && _layer < m_config.slow_down_layers) {
const auto first_layer_speed =
is_perimeter(path.role())
? m_config.get_abs_value("initial_layer_speed")
: m_config.get_abs_value("initial_layer_infill_speed");
? NOZZLE_CONFIG(initial_layer_speed)
: NOZZLE_CONFIG(initial_layer_infill_speed);
if (first_layer_speed < speed) {
speed = std::min(
speed,
@@ -6586,8 +6619,8 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
if (_layer > m_config.raft_layers && (_layer - m_config.raft_layers) < m_config.slow_down_layers) {
const auto first_layer_speed
= is_perimeter(path.role()) ? m_config.get_abs_value("initial_layer_speed") :
m_config.get_abs_value("initial_layer_infill_speed");
= is_perimeter(path.role()) ? NOZZLE_CONFIG(initial_layer_speed) :
NOZZLE_CONFIG(initial_layer_infill_speed);
if (first_layer_speed < speed) {
speed = std::min(speed, Slic3r::lerp(first_layer_speed, speed,
(double) (_layer - m_config.raft_layers) / m_config.slow_down_layers));
@@ -6653,10 +6686,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
bool variable_speed = false;
std::vector<ProcessedPoint> new_points {};
if (m_config.enable_overhang_speed && !this->on_first_layer() && !object_layer_over_raft() &&
if (NOZZLE_CONFIG(enable_overhang_speed) && !this->on_first_layer() && !object_layer_over_raft() &&
(is_bridge(path.role()) || is_perimeter(path.role()))) {
bool is_external = is_external_perimeter(path.role());
double ref_speed = is_external ? m_config.get_abs_value("outer_wall_speed") : m_config.get_abs_value("inner_wall_speed");
double ref_speed = is_external ? NOZZLE_CONFIG(outer_wall_speed) : NOZZLE_CONFIG(inner_wall_speed);
if (ref_speed == 0)
ref_speed = FILAMENT_CONFIG(filament_max_volumetric_speed) / _mm3_per_mm;
@@ -6669,46 +6702,46 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
ConfigOptionPercents overhang_overlap_levels({90, 75, 50, 25, 13, 0});
if (m_config.slowdown_for_curled_perimeters){
if (NOZZLE_CONFIG(slowdown_for_curled_perimeters)){
ConfigOptionFloatsOrPercents dynamic_overhang_speeds(
{FloatOrPercent{100, true},
(m_config.get_abs_value("overhang_1_4_speed", ref_speed) < 0.5) ?
(NOZZLE_CONFIG(overhang_1_4_speed).get_abs_value(ref_speed) < 0.5) ?
FloatOrPercent{100, true} :
FloatOrPercent{m_config.get_abs_value("overhang_1_4_speed", ref_speed) * 100 / ref_speed, true},
(m_config.get_abs_value("overhang_2_4_speed", ref_speed) < 0.5) ?
FloatOrPercent{NOZZLE_CONFIG(overhang_1_4_speed).get_abs_value(ref_speed) * 100 / ref_speed, true},
(NOZZLE_CONFIG(overhang_2_4_speed).get_abs_value(ref_speed) < 0.5) ?
FloatOrPercent{100, true} :
FloatOrPercent{m_config.get_abs_value("overhang_2_4_speed", ref_speed) * 100 / ref_speed, true},
(m_config.get_abs_value("overhang_3_4_speed", ref_speed) < 0.5) ?
FloatOrPercent{NOZZLE_CONFIG(overhang_2_4_speed).get_abs_value(ref_speed) * 100 / ref_speed, true},
(NOZZLE_CONFIG(overhang_3_4_speed).get_abs_value(ref_speed) < 0.5) ?
FloatOrPercent{100, true} :
FloatOrPercent{m_config.get_abs_value("overhang_3_4_speed", ref_speed) * 100 / ref_speed, true},
(m_config.get_abs_value("overhang_4_4_speed", ref_speed) < 0.5) ?
FloatOrPercent{NOZZLE_CONFIG(overhang_3_4_speed).get_abs_value(ref_speed) * 100 / ref_speed, true},
(NOZZLE_CONFIG(overhang_4_4_speed).get_abs_value(ref_speed) < 0.5) ?
FloatOrPercent{100, true} :
FloatOrPercent{m_config.get_abs_value("overhang_4_4_speed", ref_speed) * 100 / ref_speed, true},
(m_config.get_abs_value("overhang_4_4_speed", ref_speed) < 0.5) ?
FloatOrPercent{NOZZLE_CONFIG(overhang_4_4_speed).get_abs_value(ref_speed) * 100 / ref_speed, true},
(NOZZLE_CONFIG(overhang_4_4_speed).get_abs_value(ref_speed) < 0.5) ?
FloatOrPercent{100, true} :
FloatOrPercent{m_config.get_abs_value("overhang_4_4_speed", ref_speed) * 100 / ref_speed, true}});
FloatOrPercent{NOZZLE_CONFIG(overhang_4_4_speed).get_abs_value(ref_speed) * 100 / ref_speed, true}});
new_points = m_extrusion_quality_estimator.estimate_extrusion_quality(path, overhang_overlap_levels, dynamic_overhang_speeds,
ref_speed, speed, m_config.slowdown_for_curled_perimeters);
ref_speed, speed, NOZZLE_CONFIG(slowdown_for_curled_perimeters));
}else{
ConfigOptionFloatsOrPercents dynamic_overhang_speeds(
{FloatOrPercent{100, true},
(m_config.get_abs_value("overhang_1_4_speed", ref_speed) < 0.5) ?
(NOZZLE_CONFIG(overhang_1_4_speed).get_abs_value(ref_speed) < 0.5) ?
FloatOrPercent{100, true} :
FloatOrPercent{m_config.get_abs_value("overhang_1_4_speed", ref_speed) * 100 / ref_speed, true},
(m_config.get_abs_value("overhang_2_4_speed", ref_speed) < 0.5) ?
FloatOrPercent{NOZZLE_CONFIG(overhang_1_4_speed).get_abs_value(ref_speed) * 100 / ref_speed, true},
(NOZZLE_CONFIG(overhang_2_4_speed).get_abs_value(ref_speed) < 0.5) ?
FloatOrPercent{100, true} :
FloatOrPercent{m_config.get_abs_value("overhang_2_4_speed", ref_speed) * 100 / ref_speed, true},
(m_config.get_abs_value("overhang_3_4_speed", ref_speed) < 0.5) ?
FloatOrPercent{NOZZLE_CONFIG(overhang_2_4_speed).get_abs_value(ref_speed) * 100 / ref_speed, true},
(NOZZLE_CONFIG(overhang_3_4_speed).get_abs_value(ref_speed) < 0.5) ?
FloatOrPercent{100, true} :
FloatOrPercent{m_config.get_abs_value("overhang_3_4_speed", ref_speed) * 100 / ref_speed, true},
(m_config.get_abs_value("overhang_4_4_speed", ref_speed) < 0.5) ?
FloatOrPercent{NOZZLE_CONFIG(overhang_3_4_speed).get_abs_value(ref_speed) * 100 / ref_speed, true},
(NOZZLE_CONFIG(overhang_4_4_speed).get_abs_value(ref_speed) < 0.5) ?
FloatOrPercent{100, true} :
FloatOrPercent{m_config.get_abs_value("overhang_4_4_speed", ref_speed) * 100 / ref_speed, true},
FloatOrPercent{m_config.get_abs_value("bridge_speed") * 100 / ref_speed, true}});
FloatOrPercent{NOZZLE_CONFIG(overhang_4_4_speed).get_abs_value(ref_speed) * 100 / ref_speed, true},
FloatOrPercent{NOZZLE_CONFIG(bridge_speed) * 100 / ref_speed, true}});
new_points = m_extrusion_quality_estimator.estimate_extrusion_quality(path, overhang_overlap_levels, dynamic_overhang_speeds,
ref_speed, speed, m_config.slowdown_for_curled_perimeters);
ref_speed, speed, NOZZLE_CONFIG(slowdown_for_curled_perimeters));
}
variable_speed = std::any_of(new_points.begin(), new_points.end(),
[speed](const ProcessedPoint &p) { return fabs(double(p.speed) - speed) > 1; }); // Ignore small speed variations (under 1mm/sec)
@@ -7367,40 +7400,40 @@ std::string GCode::travel_to(const Point& point, ExtrusionRole role, std::string
unsigned int acceleration_to_set = 0;
if (this->on_first_layer()) {
unsigned int initial_layer_travel_acceleration = m_config.get_abs_value("initial_layer_travel_acceleration");
double initial_layer_travel_jerk = m_config.get_abs_value("initial_layer_travel_jerk");
unsigned int initial_layer_travel_acceleration = m_config.get_abs_value_at("initial_layer_travel_acceleration", cur_extruder_index());
double initial_layer_travel_jerk = m_config.get_abs_value_at("initial_layer_travel_jerk", cur_extruder_index());
if (m_config.default_acceleration.value > 0 && initial_layer_travel_acceleration > 0) {
if (NOZZLE_CONFIG(default_acceleration) > 0 && initial_layer_travel_acceleration > 0) {
acceleration_to_set = (unsigned int) floor(initial_layer_travel_acceleration + 0.5);
}
if (m_config.default_jerk.value > 0 && initial_layer_travel_jerk > 0) {
if (NOZZLE_CONFIG(default_jerk)> 0 && initial_layer_travel_jerk > 0) {
jerk_to_set = initial_layer_travel_jerk;
}
} else { // ORCA: Handle short-travel acceleration and jerk for outer perimeters (if applicable)
const bool is_short_travel = travel.length() < scale_(EXTRUDER_CONFIG(retraction_minimum_travel));
if (m_config.default_acceleration.value > 0) {
if (NOZZLE_CONFIG(default_acceleration) > 0) {
if (role == erOverhangPerimeter && is_short_travel) {
const double bridge_acceleration = m_config.get_abs_value("bridge_acceleration");
const double bridge_acceleration = m_config.get_abs_value_at("bridge_acceleration", cur_extruder_index());
if (bridge_acceleration > 0)
acceleration_to_set = (unsigned int) floor(bridge_acceleration + 0.5);
} else if (role == erExternalPerimeter && is_short_travel) {
if (m_config.outer_wall_acceleration.value > 0)
acceleration_to_set = (unsigned int) floor(m_config.outer_wall_acceleration.value + 0.5);
if (NOZZLE_CONFIG(outer_wall_acceleration) > 0)
acceleration_to_set = (unsigned int) floor(NOZZLE_CONFIG(outer_wall_acceleration) + 0.5);
} else {
if (m_config.travel_acceleration.value > 0)
acceleration_to_set = (unsigned int) floor(m_config.travel_acceleration.value + 0.5);
if (NOZZLE_CONFIG(travel_acceleration) > 0)
acceleration_to_set = (unsigned int) floor(NOZZLE_CONFIG(travel_acceleration) + 0.5);
}
}
if (m_config.default_jerk.value > 0) {
if (NOZZLE_CONFIG(default_jerk) > 0) {
if ((role == erExternalPerimeter || role == erOverhangPerimeter) && is_short_travel) {
if (m_config.outer_wall_jerk.value > 0)
jerk_to_set = m_config.outer_wall_jerk.value;
if (NOZZLE_CONFIG(outer_wall_jerk) > 0)
jerk_to_set = NOZZLE_CONFIG(outer_wall_jerk);
} else {
if (m_config.travel_jerk.value > 0)
jerk_to_set = m_config.travel_jerk.value;
if (NOZZLE_CONFIG(travel_jerk) > 0)
jerk_to_set = NOZZLE_CONFIG(travel_jerk);
}
}
}