Calibrations improvements (#14759)

This commit is contained in:
Ian Bassi
2026-07-27 20:11:44 -03:00
committed by GitHub
parent 33dfb66aa5
commit 6bcb809dd0
6 changed files with 284 additions and 32 deletions

View File

@@ -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)

View File

@@ -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,

View File

@@ -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)

View File

@@ -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);

View File

@@ -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;
};

View File

@@ -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;