From a4ea6c6fde08af81d4ae909394541a1219501a3a Mon Sep 17 00:00:00 2001 From: grant0013 Date: Tue, 19 May 2026 09:30:37 +0000 Subject: [PATCH] CrealityPrintAgent: match Orca filament presets by vendor + brand name Score visible compatible filament presets against the CFS spool (vendor, brand_name, type) tuple to pick the right preset: +20 preset name contains the brand_name as a substring (eg Hyper PLA in Hyper PLA @Creality K2 0.4 nozzle) +10 preset name contains the vendor substring (eg Creality) Requires preset.filament_type to equal the spool base type so a PETG preset is never auto-picked for a PLA spool. Falls back to filaments.filament_id_by_type(base_type) when nothing scores. Considers both base/system presets and user-derived copies -- K2 owners frequently keep tweaked copies of system presets (per-spool PA, temps), so filtering to bases-only would skip exactly the presets users care about most. --- src/slic3r/Utils/CrealityPrintAgent.cpp | 92 +++++++++++++++++++++++-- 1 file changed, 87 insertions(+), 5 deletions(-) diff --git a/src/slic3r/Utils/CrealityPrintAgent.cpp b/src/slic3r/Utils/CrealityPrintAgent.cpp index 903418667d..39f83f73d0 100644 --- a/src/slic3r/Utils/CrealityPrintAgent.cpp +++ b/src/slic3r/Utils/CrealityPrintAgent.cpp @@ -27,6 +27,91 @@ bool has_visible_base_preset(const PresetCollection& filaments, const std::strin return false; } +// Score visible compatible filament presets against the CFS spool metadata and +// return the best-matching filament_id. Scoring: +// +20 preset name contains brand_name as a substring +// (e.g. "Hyper PLA" in "Creality Hyper PLA @K2 (Harky)") +// +10 preset name contains the vendor substring (e.g. "Creality") +// Tiebreak: prefer user-edited presets over system presets — the K2 owner +// typically copies the system base and tweaks PA / temps for their box, +// so their copy is the better fit than the pristine system entry. +// Requires the preset's declared filament_type to equal the spool's base type +// (PLA/PETG/ABS/...) so we never auto-pick a PETG preset for a PLA spool. +// Falls back to filaments.filament_id_by_type(base_type) when nothing scores. +std::string match_filament_preset(const PresetCollection& filaments, + const std::string& vendor, + const std::string& brand_name, + const std::string& base_type) +{ + auto to_lower = [](std::string s) { + for (auto& c : s) c = static_cast(std::tolower(static_cast(c))); + return s; + }; + + const std::string vendor_lower = to_lower(vendor); + const std::string brand_lower = to_lower(brand_name); + const std::string type_lower = to_lower(base_type); + + struct Match { + const Preset* preset; + int score; + bool is_user; + }; + std::vector matches; + + int considered = 0; + for (const auto& p : filaments.get_presets()) { + if (!p.is_visible || !p.is_compatible) continue; + // Note: we deliberately do NOT filter on get_preset_base(p) == &p. + // K2 owners frequently keep tweaked copies of system presets + // (e.g. "Creality Hyper PLA @K2 (Harky)" with their per-spool PA), + // which are derived presets — filtering to bases-only would skip + // exactly the presets users care about most. + ++considered; + + std::string preset_type; + if (const auto* ft = p.config.option("filament_type")) + if (!ft->values.empty()) preset_type = ft->values.front(); + if (to_lower(preset_type) != type_lower) continue; + + const std::string name_lower = to_lower(p.name); + int score = 0; + if (!brand_lower.empty() && name_lower.find(brand_lower) != std::string::npos) + score += 20; + if (!vendor_lower.empty() && name_lower.find(vendor_lower) != std::string::npos) + score += 10; + + if (score > 0) + matches.push_back({&p, score, !p.is_system && !p.is_default}); + } + + if (matches.empty()) { + const std::string fallback = filaments.filament_id_by_type(base_type); + const bool fallback_ok = has_visible_base_preset(filaments, fallback); + BOOST_LOG_TRIVIAL(info) + << "CrealityPrintAgent: no preset scored for spool {" << vendor << " " + << brand_name << " (" << base_type << ")} after considering " << considered + << " presets; falling back to generic preset id \"" << fallback << "\"" + << (fallback_ok ? "" : " (NOT visible — returning empty)"); + return fallback_ok ? fallback : std::string(); + } + + std::sort(matches.begin(), matches.end(), + [](const Match& a, const Match& b) { + if (a.score != b.score) return a.score > b.score; + if (a.is_user != b.is_user) return a.is_user; + return false; + }); + + BOOST_LOG_TRIVIAL(info) + << "CrealityPrintAgent: matched spool {" << vendor << " " << brand_name + << " (" << base_type << ")} -> preset \"" << matches.front().preset->name + << "\" (score=" << matches.front().score + << ", " << matches.size() << " candidate(s) of " << considered << " considered)"; + + return matches.front().preset->filament_id; +} + } // namespace CrealityPrintAgent::CrealityPrintAgent(std::string log_dir) @@ -220,11 +305,8 @@ bool CrealityPrintAgent::fetch_filament_info(std::string dev_id) tray.tray_color = s.color_hex; if (bundle) { - // Fall back to the visible preset that matches by base type. A - // proper vendor+brand-aware match can be layered on later. - std::string setting_id = bundle->filaments.filament_id_by_type(tray.tray_type); - if (!setting_id.empty() && has_visible_base_preset(bundle->filaments, setting_id)) - tray.tray_info_idx = setting_id; + tray.tray_info_idx = match_filament_preset( + bundle->filaments, s.vendor, s.brand_name, tray.tray_type); } trays.push_back(std::move(tray));