Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/BambuStudio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7840,6 +7840,12 @@ bool CLI::setup(int argc, char **argv)

//set_data_dir(m_config.opt_string("datadir"));

// Development-only: Allow custom data directory via --dev-data-dir
std::string dev_data_dir = m_config.opt_string("dev-data-dir");
if (!dev_data_dir.empty()) {
set_data_dir(dev_data_dir);
}

//FIXME Validating at this stage most likely does not make sense, as the config is not fully initialized yet.
if (!validity.empty()) {
boost::nowide::cerr << "Params in command line error: "<< std::endl;
Expand Down
54 changes: 53 additions & 1 deletion src/libslic3r/GCode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6055,15 +6055,67 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
// m_config.max_volumetric_speed.value / path.mm3_per_mm
// );
//}

// Save volumetric speed limit for later re-clamping after resonance adjustments
double volumetric_speed_limit = std::numeric_limits<double>::max();

if (filament_max_volumetric_speed > 0) {
double extrude_speed = filament_max_volumetric_speed / path.mm3_per_mm;
if (_mm3_per_mm > 0)
if (_mm3_per_mm > 0) {
extrude_speed = filament_max_volumetric_speed / _mm3_per_mm;
}

volumetric_speed_limit = extrude_speed; // Save for resonance re-clamp

// cap speed with max_volumetric_speed anyway (even if user is not using autospeed)
speed = std::min(speed, extrude_speed);
}

// Multi-zone resonance avoidance for external perimeters
// Adjusts outer wall speeds to avoid printer resonance frequencies that cause ringing.
//
// Algorithm (bidirectional midpoint adjustment for each zone):
// 1. Check each zone pair (min, max) from the zones config
// 2. If speed falls within a zone (speed < max AND max > min):
// - Calculate midpoint of that zone
// - Speeds below midpoint: clamp DOWN to zone min (safe zone below resonance)
// - Speeds above midpoint: boost UP to zone max (safe zone above resonance)
// 3. Once a zone matches, adjustment is applied and we break (no cascading)
//
// Zones are stored as interleaved min-max pairs: [min1, max1, min2, max2, ...]
// This is stateless - no flags or state variables needed. Each path segment is
// evaluated independently based on its target speed.
if (path.role() == erExternalPerimeter && EXTRUDER_CONFIG(resonance_avoidance)) {
// Resonance avoidance: adjust speed to avoid problematic frequencies
const auto& zones_config = m_config.resonance_avoidance_zones;

// Convert config array to ResonanceZone objects and check each zone
for (size_t i = 0; i < zones_config.values.size(); i += 2) {
if (i + 1 >= zones_config.values.size()) {
break; // Incomplete pair
}

ResonanceZone zone(zones_config.values[i], zones_config.values[i + 1]);

// Skip invalid zones (defensive check against corrupted config data)
if (!zone.is_valid()) {
continue;
}

// Use the zone's adjust_speed method (bidirectional midpoint algorithm)
double adjusted = zone.adjust_speed(speed);
if (adjusted != speed) {
// Re-clamp to volumetric limit to prevent under-extrusion.
// Resonance avoidance can boost speed above the filament's flow capacity,
// which would cause under-extrusion on external perimeters
// The volumetric limit is a hard constraint - we can boost as high as possible
// while staying within the extruder's physical limits.
speed = std::min(adjusted, volumetric_speed_limit);
break; // Applied adjustment, exit loop
}
}
}

if (do_slowdown_by_height)
speed = std::min(speed, desiredMaxSpeed);
double F = speed * 60; // convert mm/sec to mm/min
Expand Down
2 changes: 2 additions & 0 deletions src/libslic3r/Preset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1024,6 +1024,8 @@ static std::vector<std::string> s_Preset_machine_limits_options {
"machine_max_speed_x", "machine_max_speed_y", "machine_max_speed_z", "machine_max_speed_e",
"machine_min_extruding_rate", "machine_min_travel_rate",
"machine_max_jerk_x", "machine_max_jerk_y", "machine_max_jerk_z", "machine_max_jerk_e",
// Resonance avoidance options
"resonance_avoidance", "resonance_avoidance_zones",
};

static std::vector<std::string> s_Preset_printer_options {
Expand Down
89 changes: 88 additions & 1 deletion src/libslic3r/PrintConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4197,6 +4197,32 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionInt(10));

// Resonance avoidance feature
// Adjusts outer wall print speeds to avoid printer resonance frequencies that cause ringing artifacts.
// Uses a bidirectional midpoint algorithm: speeds below the midpoint are clamped to min,
// speeds above midpoint are boosted to max, avoiding the resonance zone entirely.
def = this->add("resonance_avoidance", coBools);
def->label = L("Resonance avoidance");
def->category = L("Speed");
def->tooltip = L("Adjust outer wall speed to avoid printer resonance zones, reducing ringing or VFA.\n"
"Enable this option and configure zones to activate.\n"
"Disable when calibrating for ringing.");
def->mode = comSimple;
def->set_default_value(new ConfigOptionBools{ false });

// Multi-zone resonance avoidance
// Stores interleaved min-max pairs: [min1, max1, min2, max2, ...]
def = this->add("resonance_avoidance_zones", coFloats);
def->label = L("Resonance zones");
def->category = L("Speed");
def->tooltip = L("Speed ranges to avoid, in min-max pairs.\n"
"Example: 70-120 and 150-180 mm/s.\n"
"Use the dynamic UI in Printer Settings to manage zones.");
def->sidetext = L("mm/s");
def->min = 0;
def->mode = comSimple;
def->set_default_value(new ConfigOptionFloats{}); // Empty = no zones

def = this->add("seam_slope_inner_walls", coBool);
def->label = L("Scarf joint for inner walls");
def->category = L("Quality");
Expand Down Expand Up @@ -6433,7 +6459,8 @@ std::set<std::string> printer_extruder_options = {
"extruder_printable_height",
"min_layer_height",
"max_layer_height",
"extruder_max_nozzle_count"
"extruder_max_nozzle_count",
"resonance_avoidance"
};

std::set<std::string> printer_options_with_variant_1 = {
Expand Down Expand Up @@ -8377,6 +8404,52 @@ std::map<std::string, std::string> validate(const FullPrintConfig &cfg, bool und
}
}

// Validate multi-zone resonance avoidance
// Zones are stored as interleaved min-max pairs: [min1, max1, min2, max2, ...]
for (size_t i = 0; i < cfg.resonance_avoidance.values.size(); ++i) {
if (cfg.resonance_avoidance.get_at(i)) {
// Get zones for this extruder (per-extruder config)
const auto& zones = cfg.resonance_avoidance_zones.values;

if (!zones.empty()) {
// Must have even number of values (pairs)
if (zones.size() % 2 != 0) {
error_message.emplace("resonance_avoidance_zones",
L("Resonance zones must be in min-max pairs (even number of values)"));
break;
}

// Validate each zone pair
for (size_t j = 0; j < zones.size(); j += 2) {
if (j + 1 >= zones.size()) {
break;
}

double min_speed = zones[j];
double max_speed = zones[j + 1];

// Both must be positive
if (min_speed <= 0 || max_speed <= 0) {
error_message.emplace("resonance_avoidance_zones",
L("Zone speeds must be greater than 0 (zone ") +
std::to_string(j/2 + 1) + L(")"));
break;
}

// Min must be less than max
if (min_speed >= max_speed) {
error_message.emplace("resonance_avoidance_zones",
L("Zone min must be less than max (zone ") +
std::to_string(j/2 + 1) + L(": min=") +
std::to_string(min_speed) + L(", max=") +
std::to_string(max_speed) + L(")"));
break;
}
}
}
}
}

// The configuration is valid.
return error_message;
}
Expand Down Expand Up @@ -8682,6 +8755,20 @@ CLIMiscConfigDef::CLIMiscConfigDef()
{
ConfigOptionDef* def;

// Original datadir option (kept for compatibility, currently unused)
def = this->add("datadir", coString);
def->label = "Data directory";
def->tooltip = "Use custom data directory for configuration, profiles, and cache";
def->cli_params = "path";
def->set_default_value(new ConfigOptionString(""));

// Development-only: custom data directory (not for production use)
def = this->add("dev-data-dir", coString);
def->label = "Dev data directory";
def->tooltip = "Development only: Use custom data directory for configuration, profiles, and cache";
def->cli_params = "path";
def->set_default_value(new ConfigOptionString(""));

/*def = this->add("ignore_nonexistent_config", coBool);
def->label = L("Ignore non-existent config files");
def->tooltip = L("Do not fail if a file supplied to --load does not exist.");
Expand Down
76 changes: 76 additions & 0 deletions src/libslic3r/PrintConfig.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,78 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PerimeterGeneratorType)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(TopOneWallType)
#undef CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS

// Represents a single resonance avoidance zone with min/max speeds
struct ResonanceZone {
double min_speed;
double max_speed;

ResonanceZone() : min_speed(0), max_speed(0) {}
ResonanceZone(double min, double max) : min_speed(min), max_speed(max) {}

// Validation
bool is_valid(std::string* error_msg = nullptr) const {
// Min must be less than max
if (min_speed >= max_speed) {
if (error_msg)
*error_msg = "Minimum speed must be less than maximum speed";
return false;
}
// Both values must be positive
if (min_speed <= 0 || max_speed <= 0) {
if (error_msg)
*error_msg = "Speeds must be greater than 0";
return false;
}
return true;
}

// Speed checks
bool contains(double speed) const {
return speed >= min_speed && speed < max_speed;
}

double get_midpoint() const {
return min_speed + ((max_speed - min_speed) / 2.0);
}

// Overlap detection
bool overlaps_with(const ResonanceZone& other) const {
// Ranges overlap if A_min < B_max AND B_min < A_max
return min_speed < other.max_speed && other.min_speed < max_speed;
}

// Speed adjustment algorithm (bidirectional midpoint)
double adjust_speed(double speed) const {
// Only adjust if speed falls in this zone
if (!contains(speed)) {
return speed;
}

// Bidirectional midpoint algorithm
const double midpoint = get_midpoint();

if (speed < midpoint) {
// Below midpoint -> clamp down to min
return std::min(speed, min_speed);
} else {
// Above midpoint -> boost up to max
return max_speed;
}
}

// Comparison operators for tracking/searching
// Use epsilon for floating point comparison to avoid precision issues
bool operator==(const ResonanceZone& other) const {
const double EPSILON = 0.0001;
return std::abs(min_speed - other.min_speed) < EPSILON &&
std::abs(max_speed - other.max_speed) < EPSILON;
}

bool operator!=(const ResonanceZone& other) const {
return !(*this == other);
}
};

// Defines each and every confiuration option of Slic3r, including the properties of the GUI dialogs.
// Does not store the actual values, but defines default values.
class PrintConfigDef : public ConfigDef
Expand Down Expand Up @@ -1044,6 +1116,10 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloatsNullable, machine_min_travel_rate))
// M205 S... [mm/sec]
((ConfigOptionFloatsNullable, machine_min_extruding_rate))

// Resonance avoidance: adjusts outer wall speeds to avoid resonance frequencies
((ConfigOptionBools, resonance_avoidance))
((ConfigOptionFloats, resonance_avoidance_zones))
)

// This object is mapped to Perl as Slic3r::Config::GCode.
Expand Down
2 changes: 2 additions & 0 deletions src/slic3r/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ set(SLIC3R_GUI_SOURCES
GUI/GUI_ObjectList.hpp
GUI/GUI_ObjectLayers.cpp
GUI/GUI_ObjectLayers.hpp
GUI/GUI_ResonanceZones.cpp
GUI/GUI_ResonanceZones.hpp
GUI/GUI_AuxiliaryList.cpp
GUI/GUI_AuxiliaryList.hpp
GUI/GUI_ObjectSettings.cpp
Expand Down
Loading
Loading