mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-08-05 01:02:08 +00:00
Calibrations improvements (#14759)
This commit is contained in:
@@ -5530,7 +5530,9 @@ LayerResult GCode::process_layer(
|
||||
//Calibration Layer-specific GCode
|
||||
switch (print.calib_mode()) {
|
||||
case CalibMode::Calib_PA_Tower: {
|
||||
gcode += writer().set_pressure_advance(print.calib_params().start + static_cast<int>(print_z) * print.calib_params().step);
|
||||
gcode += writer().set_pressure_advance(this->interpolate_value_across_layers(static_cast<float>(print.calib_params().start),
|
||||
static_cast<float>(print.calib_params().end),
|
||||
static_cast<float>(print.calib_params().step)));
|
||||
break;
|
||||
}
|
||||
case CalibMode::Calib_Temp_Tower: {
|
||||
@@ -5538,7 +5540,12 @@ LayerResult GCode::process_layer(
|
||||
break;
|
||||
}
|
||||
case CalibMode::Calib_VFA_Tower: {
|
||||
auto _speed = print.calib_params().start + std::floor(print_z / 5.0) * print.calib_params().step;
|
||||
// Step the outer wall speed from start to end across the tower's layers. Plater::calib_VFA sizes the
|
||||
// geometry so each speed step spans one visual block (a fixed number of layers), so the layer-based
|
||||
// stepping stays aligned with the blocks regardless of nozzle size / layer height.
|
||||
float _speed = this->interpolate_value_across_layers(static_cast<float>(print.calib_params().start),
|
||||
static_cast<float>(print.calib_params().end),
|
||||
static_cast<float>(print.calib_params().step));
|
||||
m_calib_config.set_key_value("outer_wall_speed", new ConfigOptionFloatsNullable({std::round(_speed)}));
|
||||
break;
|
||||
}
|
||||
@@ -8343,29 +8350,22 @@ std::string GCode::extrusion_role_to_string_for_parser(const ExtrusionRole & rol
|
||||
}
|
||||
|
||||
// Calculate the interpolated value for the current layer between start_value and end_value.
|
||||
// Step will create equal layers steps from first to last value.
|
||||
// Step > 0 splits the range into equal-width bands from first to last value (both inclusive).
|
||||
// Step = 0 means gradual interpolation finishing at last value.
|
||||
float GCode::interpolate_value_across_layers(float start_value, float end_value, float step) const
|
||||
{
|
||||
if (m_layer_index <= 1) {
|
||||
return start_value;
|
||||
}
|
||||
else {
|
||||
bool use_steps = step > 0.f;
|
||||
if (use_steps) {
|
||||
if (start_value > end_value) {
|
||||
start_value += step;
|
||||
} else {
|
||||
end_value += step;
|
||||
}
|
||||
}
|
||||
float ratio = m_layer_index / (m_layer_count - 1.f);
|
||||
float value = start_value + ratio * (end_value - start_value);
|
||||
if (use_steps) {
|
||||
value = trunc(value / step) * step;
|
||||
}
|
||||
return value;
|
||||
const float ratio = m_layer_index / (m_layer_count - 1.f);
|
||||
if (step > 0.f) {
|
||||
// Discrete equal-width bands. band is clamped to the last band so the result can't overshoot the range:
|
||||
// at the top layer ratio * n_bands == n_bands, which would otherwise index one band past the end.
|
||||
const int n_bands = std::lround(std::abs(end_value - start_value) / step) + 1;
|
||||
const int band = std::min(n_bands - 1, static_cast<int>(ratio * n_bands));
|
||||
return start_value + (end_value >= start_value ? 1.f : -1.f) * band * step;
|
||||
}
|
||||
return start_value + ratio * (end_value - start_value);
|
||||
}
|
||||
|
||||
std::string encodeBase64(uint64_t value)
|
||||
|
||||
@@ -42,10 +42,22 @@ struct Calib_Params
|
||||
std::string shaper_type;
|
||||
std::vector<double> accelerations;
|
||||
std::vector<double> speeds;
|
||||
// Resolved layer height for the VFA tower (0 = auto: nozzle_diameter / 2). Each speed block is a
|
||||
// fixed number of layers tall, so this also determines the physical block height / tower height.
|
||||
double vfa_layer_height = 0.0;
|
||||
// Scale the calibration model to the nozzle diameter and set the layer height accordingly (temp tower / VFA).
|
||||
// When false the 0.4 mm / 0.2 mm reference model is printed as-is.
|
||||
bool nozzle_based_resize = true;
|
||||
|
||||
CalibMode mode;
|
||||
};
|
||||
|
||||
// Number of printed layers per speed block in the VFA tower. The base model has 5 mm blocks designed
|
||||
// for a 0.2 mm layer height (0.4 mm nozzle), i.e. 25 layers per block.
|
||||
static constexpr int vfa_layers_per_block = 25;
|
||||
static constexpr double vfa_base_block_height = 5.0;
|
||||
static constexpr double vfa_base_nozzle_diameter = 0.4;
|
||||
|
||||
enum FlowRatioCalibrationType {
|
||||
COMPLETE_CALIBRATION = 0,
|
||||
FINE_CALIBRATION,
|
||||
|
||||
@@ -14183,7 +14183,7 @@ void Plater::calib_temp(const Calib_Params& params) {
|
||||
}
|
||||
}
|
||||
|
||||
if (std::abs(nozzle_scale - 1.0) > EPSILON)
|
||||
if (params.nozzle_based_resize && std::abs(nozzle_scale - 1.0) > EPSILON)
|
||||
model().objects[0]->scale(nozzle_scale, nozzle_scale, nozzle_scale);
|
||||
|
||||
model().objects[0]->ensure_on_bed();
|
||||
@@ -14191,7 +14191,9 @@ void Plater::calib_temp(const Calib_Params& params) {
|
||||
printer_config->set_key_value("resonance_avoidance", new ConfigOptionBool{false});
|
||||
set_config_values<int, ConfigOptionInts>(filament_config, "nozzle_temperature_initial_layer", (int) start_temp);
|
||||
set_config_values<int, ConfigOptionInts>(filament_config, "nozzle_temperature", (int) start_temp);
|
||||
model().objects[0]->config.set_key_value("layer_height", new ConfigOptionFloat(nozzle_diameter/2));
|
||||
// When resizing is disabled the 0.4 mm / 0.2 mm reference model is printed as-is (preset layer height kept).
|
||||
if (params.nozzle_based_resize)
|
||||
model().objects[0]->config.set_key_value("layer_height", new ConfigOptionFloat(nozzle_diameter/2));
|
||||
model().objects[0]->config.set_key_value("brim_type", new ConfigOptionEnum<BrimType>(btOuterOnly));
|
||||
model().objects[0]->config.set_key_value("brim_width", new ConfigOptionFloat(5.0));
|
||||
model().objects[0]->config.set_key_value("brim_object_gap", new ConfigOptionFloat(0.0));
|
||||
@@ -14202,7 +14204,8 @@ void Plater::calib_temp(const Calib_Params& params) {
|
||||
|
||||
auto print_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
||||
print_config->set_key_value("enable_wrapping_detection", new ConfigOptionBool(false));
|
||||
print_config->set_key_value("initial_layer_print_height", new ConfigOptionFloat(nozzle_diameter/2));
|
||||
if (params.nozzle_based_resize)
|
||||
print_config->set_key_value("initial_layer_print_height", new ConfigOptionFloat(nozzle_diameter/2));
|
||||
|
||||
|
||||
changed_objects({ 0 });
|
||||
@@ -14366,6 +14369,42 @@ void Plater::calib_VFA(const Calib_Params& params)
|
||||
auto print_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
||||
auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config;
|
||||
auto printer_config = &wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
||||
|
||||
const ConfigOptionFloats* nozzle_diameter_config = printer_config->option<ConfigOptionFloats>("nozzle_diameter");
|
||||
size_t nozzle_id = static_cast<size_t>(std::max(params.extruder_id, 0));
|
||||
double nozzle_diameter = vfa_base_nozzle_diameter;
|
||||
if (nozzle_diameter_config && !nozzle_diameter_config->values.empty()) {
|
||||
nozzle_id = std::min(nozzle_id, nozzle_diameter_config->values.size() - 1);
|
||||
nozzle_diameter = nozzle_diameter_config->values[nozzle_id];
|
||||
}
|
||||
if (nozzle_diameter <= 0.0)
|
||||
nozzle_diameter = vfa_base_nozzle_diameter;
|
||||
|
||||
// Resolved layer height: use the (possibly auto-adjusted) value from the dialog, else default to nozzle/2.
|
||||
double layer_height = params.vfa_layer_height > 0.0 ? params.vfa_layer_height : nozzle_diameter / 2.0;
|
||||
|
||||
// cut upper (on the unscaled model, using the base block height); the scaling below keeps the physical
|
||||
// block height (vfa_layers_per_block * layer_height) in sync with the speed stepping in GCode::process_layer.
|
||||
// Subtract EPSILON (as the temperature tower does) so the cut lands just below the flat block surface instead
|
||||
// of exactly on it, which would otherwise add a degenerate extra layer.
|
||||
auto obj_bb = model().objects[0]->bounding_box_exact();
|
||||
auto height = vfa_base_block_height * ((params.end - params.start) / params.step + 1) - EPSILON;
|
||||
if (height < obj_bb.size().z()) {
|
||||
cut_horizontal(0, 0, height, ModelObjectCutAttribute::KeepLower);
|
||||
}
|
||||
|
||||
// When resizing is enabled, XY scales with the nozzle (footprint / line width) and Z scales so each base
|
||||
// block becomes vfa_layers_per_block layers of the resolved layer height. When disabled the 0.4 mm / 0.2 mm
|
||||
// reference model is printed as-is (preset layer height kept).
|
||||
if (params.nozzle_based_resize) {
|
||||
const double xy_scale = nozzle_diameter / vfa_base_nozzle_diameter;
|
||||
const double z_scale = (vfa_layers_per_block * layer_height) / vfa_base_block_height;
|
||||
if (std::abs(xy_scale - 1.0) > EPSILON || std::abs(z_scale - 1.0) > EPSILON)
|
||||
model().objects[0]->scale(xy_scale, xy_scale, z_scale);
|
||||
}
|
||||
|
||||
model().objects[0]->ensure_on_bed();
|
||||
|
||||
printer_config->set_key_value("resonance_avoidance", new ConfigOptionBool{false});
|
||||
filament_config->set_key_value("slow_down_layer_time", new ConfigOptionFloats { 0.0 });
|
||||
set_config_values<bool, ConfigOptionBoolsNullable>(print_config, "enable_overhang_speed", false);
|
||||
@@ -14379,6 +14418,10 @@ void Plater::calib_VFA(const Calib_Params& params)
|
||||
print_config->set_key_value("spiral_mode", new ConfigOptionBool(true));
|
||||
print_config->set_key_value("enable_wrapping_detection", new ConfigOptionBool(false));
|
||||
print_config->set_key_value("precise_z_height", new ConfigOptionBool(false));
|
||||
if (params.nozzle_based_resize) {
|
||||
print_config->set_key_value("initial_layer_print_height", new ConfigOptionFloat(layer_height));
|
||||
model().objects[0]->config.set_key_value("layer_height", new ConfigOptionFloat(layer_height));
|
||||
}
|
||||
model().objects[0]->config.set_key_value("brim_type", new ConfigOptionEnum<BrimType>(btOuterOnly));
|
||||
model().objects[0]->config.set_key_value("brim_width", new ConfigOptionFloat(3.0));
|
||||
model().objects[0]->config.set_key_value("brim_object_gap", new ConfigOptionFloat(0.0));
|
||||
@@ -14389,14 +14432,11 @@ void Plater::calib_VFA(const Calib_Params& params)
|
||||
wxGetApp().get_tab(Preset::TYPE_PRINT)->update_ui_from_settings();
|
||||
wxGetApp().get_tab(Preset::TYPE_FILAMENT)->update_ui_from_settings();
|
||||
|
||||
// cut upper
|
||||
auto obj_bb = model().objects[0]->bounding_box_exact();
|
||||
auto height = 5 * ((params.end - params.start) / params.step + 1);
|
||||
if (height < obj_bb.size().z()) {
|
||||
cut_horizontal(0, 0, height, ModelObjectCutAttribute::KeepLower);
|
||||
}
|
||||
|
||||
p->background_process.fff_print()->set_calib_params(params);
|
||||
// Pass the resolved layer height on (only meaningful when resized). GCode's VFA stepping is layer-based, so
|
||||
// it does not require it, but keep it consistent with the geometry.
|
||||
Calib_Params calib_params = params;
|
||||
calib_params.vfa_layer_height = params.nozzle_based_resize ? layer_height : 0.0;
|
||||
p->background_process.fff_print()->set_calib_params(calib_params);
|
||||
}
|
||||
|
||||
void Plater::calib_input_shaping_freq(const Calib_Params& params)
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
#include "Widgets/HyperLink.hpp"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/Flow.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
@@ -34,6 +36,23 @@ int GetTextMax(wxWindow* parent, const std::vector<wxString>& labels)
|
||||
return text_size.x + parent->FromDIP(10);
|
||||
}
|
||||
|
||||
CheckBox* add_scale_checkbox(wxWindow* parent, wxSizer* settings_sizer)
|
||||
{
|
||||
auto row = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto cb = new CheckBox(parent);
|
||||
cb->SetValue(true);
|
||||
auto text = new wxStaticText(parent, wxID_ANY, _L("Auto-scale for nozzle"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
|
||||
cb->SetToolTip(_L("This model is designed around a 0.4 mm nozzle with a 0.2 mm layer height. \n"
|
||||
"When the scaling option is enabled (recommended), it dynamically resizes to match your current nozzle diameter"
|
||||
" and an appropriate layer height, making the test both accurate and easy to read.\n"
|
||||
"Turn scaling off only if you wish to print the reference model exactly as-is."));
|
||||
text->SetToolTip(cb->GetToolTipText());
|
||||
row->Add(cb , 0, wxALL | wxALIGN_CENTER_VERTICAL, parent->FromDIP(2));
|
||||
row->Add(text, 0, wxALL | wxALIGN_CENTER_VERTICAL, parent->FromDIP(2));
|
||||
settings_sizer->Add(row, 0, wxLEFT | wxTOP, parent->FromDIP(3));
|
||||
return cb;
|
||||
}
|
||||
|
||||
std::vector<std::string> get_shaper_type_values()
|
||||
{
|
||||
if (auto* preset_bundle = wxGetApp().preset_bundle) {
|
||||
@@ -402,6 +421,9 @@ Temp_Calibration_Dlg::Temp_Calibration_Dlg(wxWindow* parent, wxWindowID id, Plat
|
||||
temp_step_sizer->Add(m_tiStep , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
settings_sizer->Add(temp_step_sizer, 0, wxLEFT, FromDIP(3));
|
||||
|
||||
// Resize the model to the nozzle diameter (recommended)
|
||||
m_cbResize = add_scale_checkbox(this, settings_sizer);
|
||||
|
||||
settings_sizer->AddSpacer(FromDIP(5));
|
||||
|
||||
v_sizer->Add(settings_sizer, 0, wxTOP | wxRIGHT | wxLEFT | wxEXPAND, FromDIP(10));
|
||||
@@ -475,6 +497,7 @@ void Temp_Calibration_Dlg::on_start(wxCommandEvent& event) {
|
||||
}
|
||||
m_params.start = start;
|
||||
m_params.end = end;
|
||||
m_params.nozzle_based_resize = m_cbResize->GetValue();
|
||||
m_params.mode = CalibMode::Calib_Temp_Tower;
|
||||
m_plater->calib_temp(m_params);
|
||||
EndModal(wxID_OK);
|
||||
@@ -691,6 +714,22 @@ VFA_Test_Dlg::VFA_Test_Dlg(wxWindow* parent, wxWindowID id, Plater* plater)
|
||||
vol_step_sizer->Add(m_tiStep , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
settings_sizer->Add(vol_step_sizer, 0, wxLEFT, FromDIP(3));
|
||||
|
||||
// Resize the model to the nozzle diameter (recommended)
|
||||
m_cbResize = add_scale_checkbox(this, settings_sizer);
|
||||
|
||||
// Auto-adjust parameters to the filament's max volumetric speed
|
||||
auto auto_adjust_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_cbAutoAdjust = new CheckBox(this);
|
||||
m_cbAutoAdjust->SetValue(true);
|
||||
auto auto_adjust_text = new wxStaticText(this, wxID_ANY, _L("Auto-adjust to max volumetric speed"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
|
||||
m_cbAutoAdjust->SetToolTip(_L("If the end speed would exceed the filament's maximum volumetric speed, automatically lower the layer "
|
||||
"height (keeping standard values and staying within the machine's limits) to reach it. If even the "
|
||||
"minimum layer height is not enough, lower the end speed instead."));
|
||||
auto_adjust_text->SetToolTip(m_cbAutoAdjust->GetToolTipText());
|
||||
auto_adjust_sizer->Add(m_cbAutoAdjust , 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
auto_adjust_sizer->Add(auto_adjust_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(2));
|
||||
settings_sizer->Add(auto_adjust_sizer, 0, wxLEFT | wxTOP, FromDIP(3));
|
||||
|
||||
settings_sizer->AddSpacer(FromDIP(5));
|
||||
|
||||
v_sizer->Add(settings_sizer, 0, wxTOP | wxRIGHT | wxLEFT | wxEXPAND, FromDIP(10));
|
||||
@@ -732,6 +771,136 @@ void VFA_Test_Dlg::on_start(wxCommandEvent& event)
|
||||
return;
|
||||
}
|
||||
|
||||
// If the requested end speed would exceed the filament's maximum volumetric speed, the slicer clamps the
|
||||
// outer wall speed, so the upper blocks of the tower would all print at the same (clamped) speed instead of
|
||||
// the requested one. Depending on the "Auto-adjust" option, either fix it automatically or just warn.
|
||||
m_params.vfa_layer_height = 0.0; // 0 = auto (nozzle/2); overridden below when auto-adjusting
|
||||
m_params.nozzle_based_resize = m_cbResize->GetValue();
|
||||
if (const auto* preset_bundle = wxGetApp().preset_bundle) {
|
||||
const auto& printer_config = preset_bundle->printers.get_edited_preset().config;
|
||||
const auto& print_config = preset_bundle->prints.get_edited_preset().config;
|
||||
const auto& filament_config = preset_bundle->filaments.get_edited_preset().config;
|
||||
|
||||
const int extruder_id = std::max(m_params.extruder_id, 0);
|
||||
auto get_at = [extruder_id](const ConfigOptionFloats* opt, double fallback) {
|
||||
if (opt == nullptr || opt->values.empty())
|
||||
return fallback;
|
||||
return opt->values[std::min(static_cast<size_t>(extruder_id), opt->values.size() - 1)];
|
||||
};
|
||||
|
||||
const double nozzle_diameter = get_at(printer_config.option<ConfigOptionFloats>("nozzle_diameter"), vfa_base_nozzle_diameter);
|
||||
double preset_lh = nozzle_diameter / 2.0;
|
||||
if (const auto* lh_opt = print_config.option<ConfigOptionFloat>("layer_height"))
|
||||
if (lh_opt->value > 0.0)
|
||||
preset_lh = lh_opt->value;
|
||||
// Layer height the tower will actually print at: nozzle/2 when resizing, else the preset value.
|
||||
const double default_lh = m_params.nozzle_based_resize ? nozzle_diameter / 2.0 : preset_lh;
|
||||
const double max_vol_speed = get_at(filament_config.option<ConfigOptionFloats>("filament_max_volumetric_speed"), 0.0);
|
||||
const double machine_min_lh = get_at(printer_config.option<ConfigOptionFloats>("min_layer_height"), 0.0);
|
||||
const double machine_max_lh = get_at(printer_config.option<ConfigOptionFloats>("max_layer_height"), 0.0);
|
||||
|
||||
double line_width = print_config.get_abs_value("outer_wall_line_width", nozzle_diameter);
|
||||
if (line_width <= 0.0)
|
||||
line_width = print_config.get_abs_value("line_width", nozzle_diameter);
|
||||
if (line_width <= 0.0)
|
||||
line_width = nozzle_diameter;
|
||||
|
||||
// Max outer-wall speed printable at a given layer height without exceeding the volumetric limit.
|
||||
auto speed_limit_for_lh = [&](double lh) -> double {
|
||||
const double mm3_per_mm = Flow(line_width, lh, nozzle_diameter).mm3_per_mm();
|
||||
return mm3_per_mm > 0.0 ? max_vol_speed / mm3_per_mm : 1e9;
|
||||
};
|
||||
|
||||
auto confirm_clamp = [&](const wxString& question) -> bool {
|
||||
MessageDialog msg_dlg(nullptr,
|
||||
wxString::Format(_L("The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed "
|
||||
"(%.1f mm³/s), which limits the outer wall to about %.0f mm/s at this line width and "
|
||||
"layer height.\n Speeds above this will be clamped, so the upper blocks of the tower "
|
||||
"will not print at the requested speed.\n\n%s"),
|
||||
m_params.end, max_vol_speed, speed_limit_for_lh(default_lh), question),
|
||||
_L("VFA test"), wxICON_WARNING | wxYES_NO | wxNO_DEFAULT);
|
||||
return msg_dlg.ShowModal() == wxID_YES;
|
||||
};
|
||||
|
||||
if (max_vol_speed > 0.0 && nozzle_diameter > 0.0 && m_params.end > speed_limit_for_lh(default_lh)) {
|
||||
// The layer-height auto-adjust only applies when resizing is enabled (it changes the layer height).
|
||||
if (m_cbAutoAdjust->GetValue() && m_params.nozzle_based_resize) {
|
||||
// Candidate layer heights are the ones actually used by the process profiles compatible with the
|
||||
// current printer (clamped to the machine's layer-height limits, when set). A smaller layer height
|
||||
// means a smaller cross-section, hence a higher printable speed under the volumetric limit; pick the
|
||||
// largest candidate that still reaches the end speed to keep the change from the default minimal.
|
||||
std::vector<double> candidates;
|
||||
for (const auto& preset : preset_bundle->prints.get_presets()) {
|
||||
if (!preset.is_compatible || preset.is_default)
|
||||
continue;
|
||||
const auto* lh_opt = preset.config.option<ConfigOptionFloat>("layer_height");
|
||||
if (lh_opt == nullptr || lh_opt->value <= 0.0)
|
||||
continue;
|
||||
const double lh = lh_opt->value;
|
||||
if ((machine_min_lh > 0.0 && lh < machine_min_lh - 1e-6) ||
|
||||
(machine_max_lh > 0.0 && lh > machine_max_lh + 1e-6))
|
||||
continue;
|
||||
candidates.push_back(lh);
|
||||
}
|
||||
std::sort(candidates.begin(), candidates.end());
|
||||
candidates.erase(std::unique(candidates.begin(), candidates.end(),
|
||||
[](double a, double b) { return std::abs(a - b) < 1e-6; }),
|
||||
candidates.end());
|
||||
|
||||
// Largest candidate <= the default layer height that still reaches the end speed (smallest change).
|
||||
double chosen_lh = 0.0;
|
||||
for (auto it = candidates.rbegin(); it != candidates.rend(); ++it) {
|
||||
if (*it > default_lh + 1e-6)
|
||||
continue; // never increase the layer height above the default
|
||||
if (speed_limit_for_lh(*it) >= m_params.end) { chosen_lh = *it; break; }
|
||||
}
|
||||
|
||||
if (chosen_lh > 0.0) {
|
||||
// Reducing the layer height is enough to reach the requested end speed.
|
||||
m_params.vfa_layer_height = chosen_lh;
|
||||
MessageDialog msg_dlg(nullptr,
|
||||
wxString::Format(_L("The end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed "
|
||||
"(%.1f mm³/s) at the default layer height (%.2f mm).\n\n"
|
||||
"The layer height has been reduced to %.2f mm (a value used by this printer's "
|
||||
"profiles) so the tower can reach the requested speed."),
|
||||
m_params.end, max_vol_speed, default_lh, chosen_lh),
|
||||
_L("VFA test"), wxICON_INFORMATION | wxOK);
|
||||
msg_dlg.ShowModal();
|
||||
} else if (!candidates.empty()) {
|
||||
// Even the smallest available layer height cannot reach the end speed; propose a lower end speed
|
||||
// based on that layer height, the line width and the maximum volumetric speed.
|
||||
const double min_lh = candidates.front();
|
||||
const double reachable = speed_limit_for_lh(min_lh);
|
||||
double new_end = std::floor(reachable / m_params.step) * m_params.step; // snap down to a step multiple
|
||||
if (new_end < m_params.start + m_params.step)
|
||||
new_end = m_params.start + m_params.step;
|
||||
MessageDialog msg_dlg(nullptr,
|
||||
wxString::Format(_L("Even at the smallest layer height used by this printer's profiles (%.2f mm) the "
|
||||
"end speed (%.0f mm/s) exceeds the filament's maximum volumetric speed "
|
||||
"(%.1f mm³/s).\n\n"
|
||||
"The layer height will be set to %.2f mm and the end speed lowered to %.0f mm/s.\n\n"
|
||||
"Continue?"),
|
||||
min_lh, m_params.end, max_vol_speed, min_lh, new_end),
|
||||
_L("VFA test"), wxICON_WARNING | wxYES_NO | wxNO_DEFAULT);
|
||||
if (msg_dlg.ShowModal() != wxID_YES)
|
||||
return;
|
||||
m_params.end = new_end;
|
||||
m_params.vfa_layer_height = min_lh;
|
||||
} else {
|
||||
// No compatible process profiles to draw layer heights from: warn and let the user decide.
|
||||
if (!confirm_clamp(_L("Continue anyway?")))
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Auto-adjust off, or resizing disabled (which forbids changing the layer height): just warn.
|
||||
if (!confirm_clamp(m_params.nozzle_based_resize
|
||||
? _L("Enable \"Auto-adjust\" to fix this automatically, or continue anyway?")
|
||||
: _L("Enable \"Auto-scale for nozzle\" and \"Auto-adjust\" to fix this automatically, or continue anyway?")))
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_params.mode = CalibMode::Calib_VFA_Tower;
|
||||
m_plater->calib_VFA(m_params);
|
||||
EndModal(wxID_OK);
|
||||
|
||||
@@ -65,6 +65,7 @@ protected:
|
||||
TextInput* m_tiStart;
|
||||
TextInput* m_tiEnd;
|
||||
TextInput* m_tiStep;
|
||||
CheckBox* m_cbResize;
|
||||
Plater* m_plater;
|
||||
};
|
||||
|
||||
@@ -99,6 +100,8 @@ protected:
|
||||
TextInput* m_tiStart;
|
||||
TextInput* m_tiEnd;
|
||||
TextInput* m_tiStep;
|
||||
CheckBox* m_cbAutoAdjust;
|
||||
CheckBox* m_cbResize;
|
||||
Plater* m_plater;
|
||||
};
|
||||
|
||||
|
||||
@@ -1265,6 +1265,19 @@ void CalibUtils::calib_VFA(const CalibInfo &calib_info, wxString &error_message)
|
||||
DynamicPrintConfig filament_config = calib_info.filament_prest->config;
|
||||
DynamicPrintConfig printer_config = calib_info.printer_prest->config;
|
||||
|
||||
const ConfigOptionFloats* nozzle_diameter_config = printer_config.option<ConfigOptionFloats>("nozzle_diameter");
|
||||
size_t nozzle_id = static_cast<size_t>(std::max(params.extruder_id, 0));
|
||||
double nozzle_diameter = vfa_base_nozzle_diameter;
|
||||
if (nozzle_diameter_config && !nozzle_diameter_config->values.empty()) {
|
||||
nozzle_id = std::min(nozzle_id, nozzle_diameter_config->values.size() - 1);
|
||||
nozzle_diameter = nozzle_diameter_config->values[nozzle_id];
|
||||
}
|
||||
if (nozzle_diameter <= 0.0)
|
||||
nozzle_diameter = vfa_base_nozzle_diameter;
|
||||
|
||||
// Resolved layer height: use the (possibly auto-adjusted) value if provided, else default to nozzle/2.
|
||||
double layer_height = params.vfa_layer_height > 0.0 ? params.vfa_layer_height : nozzle_diameter / 2.0;
|
||||
|
||||
filament_config.set_key_value("slow_down_layer_time", new ConfigOptionInts{0});
|
||||
filament_config.set_key_value("filament_max_volumetric_speed", new ConfigOptionFloats{200});
|
||||
filament_config.set_key_value("curr_bed_type", new ConfigOptionEnum<BedType>(calib_info.bed_type));
|
||||
@@ -1280,13 +1293,18 @@ void CalibUtils::calib_VFA(const CalibInfo &calib_info, wxString &error_message)
|
||||
print_config.set_key_value("sparse_infill_density", new ConfigOptionPercent(0));
|
||||
print_config.set_key_value("overhang_reverse", new ConfigOptionBool(false));
|
||||
print_config.set_key_value("spiral_mode", new ConfigOptionBool(true));
|
||||
print_config.set_key_value("initial_layer_print_height", new ConfigOptionFloat(layer_height));
|
||||
model.objects[0]->config.set_key_value("layer_height", new ConfigOptionFloat(layer_height));
|
||||
model.objects[0]->config.set_key_value("brim_type", new ConfigOptionEnum<BrimType>(btOuterOnly));
|
||||
model.objects[0]->config.set_key_value("brim_width", new ConfigOptionFloat(3.0));
|
||||
model.objects[0]->config.set_key_value("brim_object_gap", new ConfigOptionFloat(0.0));
|
||||
|
||||
// cut upper
|
||||
// cut upper (on the unscaled model, using the base block height); the scaling below keeps the physical
|
||||
// block height (vfa_layers_per_block * layer_height) in sync with the speed stepping in GCode::process_layer.
|
||||
// Subtract EPSILON (as the temperature tower does) so the cut lands just below the flat block surface instead
|
||||
// of exactly on it, which would otherwise add a degenerate extra layer.
|
||||
auto obj_bb = model.objects[0]->bounding_box_exact();
|
||||
auto height = 5 * ((params.end - params.start) / params.step + 1);
|
||||
auto height = vfa_base_block_height * ((params.end - params.start) / params.step + 1) - EPSILON;
|
||||
if (height < obj_bb.size().z()) {
|
||||
cut_model(model, height, ModelObjectCutAttribute::KeepLower);
|
||||
}
|
||||
@@ -1295,6 +1313,13 @@ void CalibUtils::calib_VFA(const CalibInfo &calib_info, wxString &error_message)
|
||||
return;
|
||||
}
|
||||
|
||||
// XY scales with the nozzle; Z scales so each base block becomes vfa_layers_per_block layers of layer_height.
|
||||
const double xy_scale = nozzle_diameter / vfa_base_nozzle_diameter;
|
||||
const double z_scale = (vfa_layers_per_block * layer_height) / vfa_base_block_height;
|
||||
if (std::abs(xy_scale - 1.0) > EPSILON || std::abs(z_scale - 1.0) > EPSILON)
|
||||
model.objects[0]->scale(xy_scale, xy_scale, z_scale);
|
||||
model.objects[0]->ensure_on_bed();
|
||||
|
||||
DynamicPrintConfig full_config;
|
||||
full_config.apply(FullPrintConfig::defaults());
|
||||
full_config.apply(print_config);
|
||||
@@ -1303,7 +1328,10 @@ void CalibUtils::calib_VFA(const CalibInfo &calib_info, wxString &error_message)
|
||||
|
||||
init_multi_extruder_params_for_cali(full_config, calib_info);
|
||||
|
||||
process_and_store_3mf(&model, full_config, params, error_message);
|
||||
// Pass the resolved layer height on so the GCode speed stepping matches the geometry.
|
||||
Calib_Params store_params = params;
|
||||
store_params.vfa_layer_height = layer_height;
|
||||
process_and_store_3mf(&model, full_config, store_params, error_message);
|
||||
if (!error_message.empty())
|
||||
return;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user