Make filament compatibility temperature-aware (#13522)

* Make filament compatibility temperature-aware

Extend filament compatibility checks to consider actual nozzle temperatures and temperature ranges.
Print::check_multi_filaments_compatibility now accepts nozzle temperatures and range lows/highs; it resolves missing ranges from material defaults, computes per-filament effective temperatures, and checks pairwise compatibility (including high/low/mid mixed cases).
Updated callers in Print::check_multi_filament_valid and CalibrationWizardPresetPage to pass nozzle settings, consolidated user-facing warning strings, and fixed extruder index handling and minor logic/path improvements.

Clarify incompatible nozzle temperature warnings

* Update Print.cpp

* Remove json usage

* Reduce messages
This commit is contained in:
Ian Bassi
2026-05-09 03:30:24 -03:00
committed by GitHub
parent 16fc3c1b14
commit 635d96183d
3 changed files with 150 additions and 92 deletions

View File

@@ -1044,28 +1044,60 @@ static StringObjectException layered_print_cleareance_valid(const Print &print,
return {};
}
FilamentCompatibilityType Print::check_multi_filaments_compatibility(const std::vector<std::string>& filament_types)
FilamentCompatibilityType Print::check_multi_filaments_compatibility(
const std::vector<std::string>& filament_types,
const std::vector<int>& nozzle_temperatures,
const std::vector<int>& nozzle_temperature_range_lows,
const std::vector<int>& nozzle_temperature_range_highs)
{
bool has_high_temperature_filament = false;
bool has_low_temperature_filament = false;
bool has_mid_temperature_filament = false;
const size_t filament_count = filament_types.size();
if (filament_count < 2)
return FilamentCompatibilityType::Compatible;
for (const auto& type : filament_types) {
if (get_filament_temp_type(type) ==FilamentTempType::HighTemp)
has_high_temperature_filament = true;
else if (get_filament_temp_type(type) == FilamentTempType::LowTemp)
has_low_temperature_filament = true;
else if (get_filament_temp_type(type) == FilamentTempType::HighLowCompatible)
has_mid_temperature_filament = true;
std::vector<int> resolved_temperatures(filament_count, 0);
std::vector<int> resolved_range_lows(filament_count, 0);
std::vector<int> resolved_range_highs(filament_count, 0);
for (size_t i = 0; i < filament_count; ++i) {
int range_low = (i < nozzle_temperature_range_lows.size()) ? nozzle_temperature_range_lows[i] : 0;
int range_high = (i < nozzle_temperature_range_highs.size()) ? nozzle_temperature_range_highs[i] : 0;
if (range_low == 0 || range_high == 0) {
int default_low = range_low;
int default_high = range_high;
MaterialType::get_temperature_range(filament_types[i], default_low, default_high);
if (range_low == 0)
range_low = default_low;
if (range_high == 0)
range_high = default_high;
}
if (has_high_temperature_filament && has_low_temperature_filament)
if (range_low >= range_high)
return FilamentCompatibilityType::InvalidTemperatureRange;
int print_temperature = (i < nozzle_temperatures.size()) ? nozzle_temperatures[i] : 0;
resolved_temperatures[i] = print_temperature;
resolved_range_lows[i] = range_low;
resolved_range_highs[i] = range_high;
}
for (size_t i = 0; i < filament_count; ++i) {
for (size_t j = i + 1; j < filament_count; ++j) {
const bool i_temp_is_compatible_with_j =
resolved_temperatures[i] >= resolved_range_lows[j] &&
resolved_temperatures[i] <= resolved_range_highs[j];
const bool j_temp_is_compatible_with_i =
resolved_temperatures[j] >= resolved_range_lows[i] &&
resolved_temperatures[j] <= resolved_range_highs[i];
if (i_temp_is_compatible_with_j && j_temp_is_compatible_with_i)
continue;
// Range-only rule: any pair outside mutual recommended ranges is incompatible.
return FilamentCompatibilityType::HighLowMixed;
else if (has_high_temperature_filament && has_mid_temperature_filament)
return FilamentCompatibilityType::HighMidMixed;
else if (has_low_temperature_filament && has_mid_temperature_filament)
return FilamentCompatibilityType::LowMidMixed;
else
}
}
return FilamentCompatibilityType::Compatible;
}
@@ -1111,18 +1143,21 @@ int Print::get_compatible_filament_type(const std::set<int>& filament_types)
StringObjectException Print::check_multi_filament_valid(const Print& print)
{
auto print_config = print.config();
const std::string incompatible_temp_msg = L("Selected nozzle temperatures are incompatible. Each filament's nozzle temperature must fall within the recommended nozzle temperature range of the other filaments. Otherwise, nozzle clogging or printer damage may occur.");
const std::string invalid_temp_range_msg = L("Invalid recommended nozzle temperature range. The lower bound must be lower than the upper bound.");
const std::string incompatible_temp_msg_preferences_enable = L("If you still want to print, you can enable the option in Preferences / Control / Slicing / Remove mixed temperature restriction.");
if(print_config.print_sequence == PrintSequence::ByObject) {// use ByObject valid under ByObject print sequence
std::set<FilamentCompatibilityType> Compatibility_each_obj;
bool has_incompatible_object = false;
bool enable_mix_printing = !print.need_check_multi_filaments_compatibility();
StringObjectException ret;
for (const auto &objectID_t : print.print_object_ids()) {
std::set<int> obj_used_extruder_ids;
auto print_object = print.get_object(objectID_t);// current object
if (print_object){
auto object_extruders_t = print_object->object_extruders(); // object used extruder
for (int extruder : object_extruders_t) {
assert(extruder > 0);
obj_used_extruder_ids.insert(extruder);
for (unsigned int extruder : object_extruders_t) {
obj_used_extruder_ids.insert(static_cast<int>(extruder));
}
}
@@ -1136,57 +1171,83 @@ StringObjectException Print::check_multi_filament_valid(const Print& print)
obj_used_extruder_ids.insert((unsigned int) print_object->config().support_interface_filament - 1);
}
std::vector<std::string> filament_types;
std::vector<int> nozzle_temperatures;
std::vector<int> nozzle_temperature_range_lows;
std::vector<int> nozzle_temperature_range_highs;
filament_types.reserve(obj_used_extruder_ids.size());
for (const auto &extruder_idx : obj_used_extruder_ids) filament_types.push_back(print_config.filament_type.get_at(extruder_idx));
nozzle_temperatures.reserve(obj_used_extruder_ids.size());
nozzle_temperature_range_lows.reserve(obj_used_extruder_ids.size());
nozzle_temperature_range_highs.reserve(obj_used_extruder_ids.size());
auto compatibility = check_multi_filaments_compatibility(filament_types);// check for each object
Compatibility_each_obj.insert(compatibility);
for (const auto &extruder_idx : obj_used_extruder_ids) {
filament_types.push_back(print_config.filament_type.get_at(extruder_idx));
nozzle_temperatures.push_back(print_config.nozzle_temperature.get_at(extruder_idx));
nozzle_temperature_range_lows.push_back(print_config.nozzle_temperature_range_low.get_at(extruder_idx));
nozzle_temperature_range_highs.push_back(print_config.nozzle_temperature_range_high.get_at(extruder_idx));
}
StringObjectException ret;
std::string hypertext = "filament_mix_print";
if (Compatibility_each_obj.count(FilamentCompatibilityType::HighLowMixed)){// at least one object has HighLowMixed
auto compatibility = check_multi_filaments_compatibility(
filament_types,
nozzle_temperatures,
nozzle_temperature_range_lows,
nozzle_temperature_range_highs); // check for each object
if (compatibility == FilamentCompatibilityType::InvalidTemperatureRange) {
ret.string = invalid_temp_range_msg;
return ret;
}
if (compatibility != FilamentCompatibilityType::Compatible) {
has_incompatible_object = true;
break;
}
}
if (has_incompatible_object){
if (enable_mix_printing) {
ret.string = L("Printing high-temp and low-temp filaments together may cause nozzle clogging or printer damage.");
ret.string = incompatible_temp_msg;
ret.is_warning = true;
// ret.hypetext = hypertext;
} else
ret.string = L("Printing high-temp and low-temp filaments together may cause nozzle clogging or printer damage. If you still want to print, you can enable the option in Preferences.");
}else if (Compatibility_each_obj.count(FilamentCompatibilityType::LowMidMixed) || Compatibility_each_obj.count(FilamentCompatibilityType::HighMidMixed)){// at least one object has other Mixed
ret.is_warning = true;
// ret.hypetext = hypertext;
ret.string = L("Printing different-temp filaments together may cause nozzle clogging or printer damage.");
ret.string = incompatible_temp_msg + " " + incompatible_temp_msg_preferences_enable;
}
return ret;
}
std::vector<unsigned int> extruders = print.extruders();
std::vector<std::string> filament_types;
std::vector<int> nozzle_temperatures;
std::vector<int> nozzle_temperature_range_lows;
std::vector<int> nozzle_temperature_range_highs;
filament_types.reserve(extruders.size());
for (const auto& extruder_idx : extruders)
nozzle_temperatures.reserve(extruders.size());
nozzle_temperature_range_lows.reserve(extruders.size());
nozzle_temperature_range_highs.reserve(extruders.size());
for (const auto& extruder_idx : extruders) {
filament_types.push_back(print_config.filament_type.get_at(extruder_idx));
nozzle_temperatures.push_back(print_config.nozzle_temperature.get_at(extruder_idx));
nozzle_temperature_range_lows.push_back(print_config.nozzle_temperature_range_low.get_at(extruder_idx));
nozzle_temperature_range_highs.push_back(print_config.nozzle_temperature_range_high.get_at(extruder_idx));
}
auto compatibility = check_multi_filaments_compatibility(filament_types);
auto compatibility = check_multi_filaments_compatibility(
filament_types,
nozzle_temperatures,
nozzle_temperature_range_lows,
nozzle_temperature_range_highs);
bool enable_mix_printing = !print.need_check_multi_filaments_compatibility();
StringObjectException ret;
if(compatibility == FilamentCompatibilityType::HighLowMixed){
if (compatibility == FilamentCompatibilityType::InvalidTemperatureRange) {
ret.string = invalid_temp_range_msg;
return ret;
}
if(compatibility != FilamentCompatibilityType::Compatible){
if(enable_mix_printing){
ret.string =L("Printing high-temp and low-temp filaments together may cause nozzle clogging or printer damage.");
ret.string = incompatible_temp_msg;
ret.is_warning = true;
}
else{
ret.string =L("Printing high-temp and low-temp filaments together may cause nozzle clogging or printer damage. If you still want to print, you can enable the option in Preferences.");
ret.string = incompatible_temp_msg + " " + incompatible_temp_msg_preferences_enable;
}
}
else if (compatibility == FilamentCompatibilityType::HighMidMixed) {
ret.is_warning = true;
ret.string =L("Printing high-temp and mid-temp filaments together may cause nozzle clogging or printer damage.");
}
else if (compatibility == FilamentCompatibilityType::LowMidMixed) {
ret.is_warning = true;
ret.string = L("Printing mid-temp and low-temp filaments together may cause nozzle clogging or printer damage.");
}
return ret;
}
@@ -2798,43 +2859,7 @@ Vec2d Print::translate_to_print_space(const Point &point) const {
FilamentTempType Print::get_filament_temp_type(const std::string& filament_type)
{
const static std::string HighTempFilamentStr = "high_temp_filament";
const static std::string LowTempFilamentStr = "low_temp_filament";
const static std::string HighLowCompatibleFilamentStr = "high_low_compatible_filament";
static std::unordered_map<std::string, std::unordered_set<std::string>>filament_temp_type_map;
if (filament_temp_type_map.empty()) {
fs::path file_path = fs::path(resources_dir()) / "info" / "filament_info.json";
std::ifstream in(file_path.string());
json j;
try{
j = json::parse(in);
in.close();
auto&&high_temp_filament_arr =j[HighTempFilamentStr].get < std::vector<std::string>>();
filament_temp_type_map[HighTempFilamentStr] = std::unordered_set<std::string>(high_temp_filament_arr.begin(), high_temp_filament_arr.end());
auto&& low_temp_filament_arr = j[LowTempFilamentStr].get < std::vector<std::string>>();
filament_temp_type_map[LowTempFilamentStr] = std::unordered_set<std::string>(low_temp_filament_arr.begin(), low_temp_filament_arr.end());
auto&& high_low_compatible_filament_arr = j[HighLowCompatibleFilamentStr].get < std::vector<std::string>>();
filament_temp_type_map[HighLowCompatibleFilamentStr] = std::unordered_set<std::string>(high_low_compatible_filament_arr.begin(), high_low_compatible_filament_arr.end());
}
catch (const json::parse_error& err){
in.close();
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": parse " << file_path.string() << " got a nlohmann::detail::parse_error, reason = " << err.what();
filament_temp_type_map[HighTempFilamentStr] = {"ABS","ASA","PC","PA","PA-CF","PA-GF","PA6-CF","PET-CF", "PETG-GF","PPS","PPS-CF","PPA-GF","PPA-CF","ABS-Aero","ABS-GF"};
filament_temp_type_map[LowTempFilamentStr] = {"PLA","TPU","PLA-CF","PLA-AERO","PVA","BVOH","SBS"};
filament_temp_type_map[HighLowCompatibleFilamentStr] = { "HIPS","PETG","PCTG","PE","PP","EVA","PE-CF","PP-CF","PP-GF","PHA"};
}
}
if (filament_temp_type_map[HighLowCompatibleFilamentStr].find(filament_type) != filament_temp_type_map[HighLowCompatibleFilamentStr].end())
return HighLowCompatible;
if (filament_temp_type_map[HighTempFilamentStr].find(filament_type) != filament_temp_type_map[HighTempFilamentStr].end())
return HighTemp;
if (filament_temp_type_map[LowTempFilamentStr].find(filament_type) != filament_temp_type_map[LowTempFilamentStr].end())
return LowTemp;
// Orca: prefer explicit definition from JSON, if the filament type is not defined in json, fallback to temperature-based logic to determine the filament temp type.
// FilamentTempType Temperature-based logic
// Range-based classification only: do not use filament_info.json.
int min_temp, max_temp;
if (MaterialType::get_temperature_range(filament_type, min_temp, max_temp)) {
if (max_temp <= 250)

View File

@@ -877,8 +877,9 @@ enum FilamentTempType {
enum FilamentCompatibilityType {
Compatible,
HighLowMixed,
HighMidMixed,
LowMidMixed
//HighLowMixed,
//HighMidMixed,
InvalidTemperatureRange
};
// The complete print tray with possibly multiple objects.
@@ -1087,7 +1088,11 @@ public:
static FilamentTempType get_filament_temp_type(const std::string& filament_type);
static int get_hrc_by_nozzle_type(const NozzleType& type);
static std::vector<std::string> get_incompatible_filaments_by_nozzle(const float nozzle_diameter, const std::optional<NozzleVolumeType> nozzle_volume_type = std::nullopt);
static FilamentCompatibilityType check_multi_filaments_compatibility(const std::vector<std::string>& filament_types);
static FilamentCompatibilityType check_multi_filaments_compatibility(
const std::vector<std::string>& filament_types,
const std::vector<int>& nozzle_temperatures,
const std::vector<int>& nozzle_temperature_range_lows,
const std::vector<int>& nozzle_temperature_range_highs);
// similar to check_multi_filaments_compatibility, but the input is int, and may be negative (means unset)
static bool is_filaments_compatible(const std::vector<int>& types);
// get the compatible filament type of a multi-material object

View File

@@ -1505,6 +1505,9 @@ bool CalibrationPresetPage::is_filaments_compatiable(const std::map<int, Preset*
bed_temp = 0;
std::vector<std::string> filament_types;
std::vector<int> nozzle_temperatures;
std::vector<int> nozzle_temperature_range_lows;
std::vector<int> nozzle_temperature_range_highs;
for (auto &item : prests) {
const auto& item_preset = item.second;
if (!item_preset)
@@ -1533,13 +1536,38 @@ bool CalibrationPresetPage::is_filaments_compatiable(const std::map<int, Preset*
std::string display_filament_type;
filament_types.push_back(item_preset->config.get_filament_type(display_filament_type, 0));
int nozzle_temperature = 0;
int nozzle_temperature_range_low = 0;
int nozzle_temperature_range_high = 0;
if (const auto* opt_nozzle_temp = item_preset->config.option<ConfigOptionInts>("nozzle_temperature"))
nozzle_temperature = opt_nozzle_temp->get_at(0);
if (const auto* opt_nozzle_temp_low = item_preset->config.option<ConfigOptionInts>("nozzle_temperature_range_low"))
nozzle_temperature_range_low = opt_nozzle_temp_low->get_at(0);
if (const auto* opt_nozzle_temp_high = item_preset->config.option<ConfigOptionInts>("nozzle_temperature_range_high"))
nozzle_temperature_range_high = opt_nozzle_temp_high->get_at(0);
nozzle_temperatures.push_back(nozzle_temperature);
nozzle_temperature_range_lows.push_back(nozzle_temperature_range_low);
nozzle_temperature_range_highs.push_back(nozzle_temperature_range_high);
// check is it in the filament blacklist
if (!is_filament_in_blacklist(item.first, item_preset, error_tips))
return false;
}
if (Print::check_multi_filaments_compatibility(filament_types) == FilamentCompatibilityType::HighLowMixed) {
error_tips = _u8L("Cannot print multiple filaments which have large difference of temperature together. Otherwise, the extruder and nozzle may be blocked or damaged during printing");
auto compatibility = Print::check_multi_filaments_compatibility(
filament_types,
nozzle_temperatures,
nozzle_temperature_range_lows,
nozzle_temperature_range_highs);
if (compatibility == FilamentCompatibilityType::InvalidTemperatureRange) {
error_tips = _u8L("Invalid recommended nozzle temperature range. The lower bound must be lower than the upper bound.");
return false;
}
if (compatibility == FilamentCompatibilityType::HighLowMixed) {
error_tips = _u8L("Selected nozzle temperatures are incompatible. For multi-material printing, each filament's nozzle temperature must be within the recommended nozzle temperature range of the other filaments. Otherwise, nozzle clogging or printer damage may occur.");
return false;
}