diff --git a/.gitignore b/.gitignore index 9ba2c76d61d..212fbd20a3e 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,5 @@ deps_src/build/ .claude/ .hive-mind/ nul +.omc/ +.claude-flow/ diff --git a/FILAMENT_EXTRUDER_ANALYSIS_FINAL.md b/FILAMENT_EXTRUDER_ANALYSIS_FINAL.md new file mode 100644 index 00000000000..5d827d64393 --- /dev/null +++ b/FILAMENT_EXTRUDER_ANALYSIS_FINAL.md @@ -0,0 +1,152 @@ +# Filament-Extruder Mapping Analysis - Final Findings + +## The Key Discovery + +After investigating the original implementation vs the optimized version, I found the **critical missing piece**: `initialize_filament_extruder_map()`. + +## How It Actually Works + +### The Initialization Process + +**Called in PrintApply.cpp during apply():** +```cpp +// PrintApply.cpp line 1276 +this->initialize_filament_extruder_map(); +``` + +**Implementation in Print.cpp (lines 497-544):** +```cpp +void Print::initialize_filament_extruder_map() +{ + m_filament_extruder_map.clear(); + + // Get the number of physical extruders + size_t physical_extruder_count = m_config.nozzle_diameter.values.size(); + + // Get ALL configured filaments (not just used ones) + std::vector filament_extruders = this->extruders(); + if (filament_extruders.empty()) { + size_t filament_count = m_config.filament_diameter.size(); + for (size_t i = 0; i < filament_count; ++i) { + filament_extruders.push_back((unsigned int)i); + } + } + + // Create mapping: filament_id -> physical_extruder_id + // Mapping formula: physical_extruder = filament_id % physical_extruder_count + for (unsigned int filament_idx : filament_extruders) { + int physical_extruder = filament_idx % physical_extruder_count; + m_filament_extruder_map[filament_idx] = physical_extruder; + } +} +``` + +### The Result + +After initialization: +- Filament 0 → Extruder 0 +- Filament 1 → Extruder 1 +- Filament 2 → Extruder 2 +- Filament 3 → Extruder 3 +- Filament 4 → Extruder 0 (modulo) +- Filament 5 → Extruder 1 (modulo) +- Filament 6 → Extruder 2 (modulo) +- Filament 7 → Extruder 3 (modulo) + +**The map is ALWAYS populated before slicing!** + +## Why Both Versions Work + +### Original Implementation (9d423d0714) + +**get_physical_extruder():** +```cpp +int get_physical_extruder(int filament_idx) const { + auto it = m_filament_extruder_map.find(filament_idx); + // Fallback: identity mapping (filament 5 → extruder 5) + int physical_extruder_id = (it != m_filament_extruder_map.end()) ? it->second : filament_idx; + return physical_extruder_id; +} +``` + +**Why it worked:** The fallback logic was **never reached** because `initialize_filament_extruder_map()` always populates the map with modulo mapping. + +### Optimized Implementation (989a53e124) + +**get_physical_extruder():** +```cpp +int get_physical_extruder(int filament_idx) const { + auto it = m_filament_extruder_map.find(filament_idx); + if (it != m_filament_extruder_map.end()) { + return it->second; + } else { + // Fallback: modulo mapping (filament 5 → extruder 1) + size_t physical_count = m_config.nozzle_diameter.values.size(); + return filament_idx % physical_count; + } +} +``` + +**Why it also works:** Same reason - the map is always populated. The fallback is just defensive programming. + +## What Actually Changed + +The key difference between the two versions is **NOT** the behavior during normal operation (both work the same), but: + +1. **Defensive programming:** The optimized version has a safer fallback (modulo instead of identity) +2. **Code simplification:** The optimized version removed some redundant physical_extruder calculations in GCode.cpp +3. **Better logging:** The optimized version has more detailed logging +4. **Validation:** The optimized version added bounds checking in PrintApply.cpp + +## The Real Issue: Why 3MF Slicing Might Fail + +If the user is experiencing issues with >4 filament 3MF slicing, it's **NOT** because of the identity vs modulo fallback difference (since that code path is never reached). + +Possible causes: + +1. **initialize_filament_extruder_map() not being called:** Check if there's a code path that bypasses Print::apply() + +2. **Config array access using filament_id instead of physical_extruder_id:** The optimized version removed some explicit physical_extruder calculations in GCode.cpp. If those changes introduced direct filament_id access to config arrays, that would cause crashes. + +3. **3MF file format issues:** The 3MF file might have inconsistent filament/extruder configurations + +4. **Placeholder replacement:** GCode placeholder macros might still be using filament_id instead of physical_extruder_id + +## Code Changes That Matter + +### Potentially Problematic Change in GCode.cpp + +**Optimized version removed explicit physical_extruder calculation:** +```cpp +// BEFORE (9d423d0714): +int previous_physical_extruder = (previous_extruder_id >= 0) ? + gcode_writer.get_physical_extruder(previous_extruder_id) : -1; +int new_physical_extruder = gcode_writer.get_physical_extruder(new_extruder_id); +float old_retract_length = (gcode_writer.extruder() != nullptr && previous_physical_extruder >= 0) ? + full_config.retraction_length.get_at(previous_physical_extruder) : 0; + +// AFTER (989a53e124): +float old_retract_length = (gcode_writer.extruder() != nullptr && previous_extruder_id >= 0) ? + full_config.retraction_length.get_at(previous_extruder_id) : 0; // Uses filament_id directly! +``` + +**This is a BUG!** The optimized version uses `previous_extruder_id` (filament_id) directly to access `retraction_length` array. This will cause array out-of-bounds access when filament_id >= physical_extruder_count. + +## Recommendation + +The optimized version (989a53e124) may have introduced regressions by removing explicit physical_extruder calculations. Need to: + +1. **Audit all config array access** in GCode.cpp to ensure physical_extruder_id is used +2. **Add bounds checking** or use the PHYSICAL_EXTRUDER_CONFIG macro consistently +3. **Test with 8-filament 3MF** on 4-extruder configuration + +## Summary + +| Aspect | Original (9d423d0714) | Optimized (989a53e124) | +|--------|----------------------|------------------------| +| **Modulo mapping** | ✓ Via initialize_filament_extruder_map() | ✓ Via initialize_filament_extruder_map() | +| **Fallback logic** | Identity (never used) | Modulo (never used) | +| **Config array access** | Explicit physical_extruder calculation | Some direct filament_id access (BUG?) | +| **Safety** | Good | Potentially introduced bugs | + +**Bottom line:** Both versions use the same modulo mapping via `initialize_filament_extruder_map()`, but the optimized version may have introduced bugs by simplifying config array access. diff --git a/FILAMENT_EXTRUDER_ORIGINAL_VS_OPTIMIZED.md b/FILAMENT_EXTRUDER_ORIGINAL_VS_OPTIMIZED.md new file mode 100644 index 00000000000..7a6cb2c6c29 --- /dev/null +++ b/FILAMENT_EXTRUDER_ORIGINAL_VS_OPTIMIZED.md @@ -0,0 +1,261 @@ +# Filament-Extruder Mapping: Original Implementation vs Optimized Version + +## Executive Summary + +This document analyzes the differences between the original working filament-extruder mapping implementation (commit `9d423d0714`) and the optimized version (commit `989a53e124`), focusing on why the original was working for >4 filament 3MF slicing. + +## Key Findings + +### The Critical Difference: Default Mapping Behavior + +**Original Implementation (9d423d0714):** +```cpp +int get_physical_extruder(int filament_idx) const { + auto it = m_filament_extruder_map.find(filament_idx); + // When no mapping exists, use IDENTITY mapping (filament 5 → extruder 5) + int physical_extruder_id = (it != m_filament_extruder_map.end()) ? it->second : filament_idx; + return physical_extruder_id; +} +``` + +**Optimized Implementation (989a53e124):** +```cpp +int get_physical_extruder(int filament_idx) const { + auto it = m_filament_extruder_map.find(filament_idx); + int physical_extruder_id; + if (it != m_filament_extruder_map.end()) { + physical_extruder_id = it->second; + } else { + // When no mapping exists, use MODULO mapping (filament 5 → extruder 1) + size_t physical_count = m_config.nozzle_diameter.values.size(); + if (physical_count == 0) { + physical_extruder_id = 0; + } else { + physical_extruder_id = filament_idx % physical_count; + } + } + return physical_extruder_id; +} +``` + +### Why the Original Worked for >4 Filaments + +When slicing a 3MF file with 5-8 filaments on a 4-extruder printer: + +**Original behavior (IDENTITY mapping):** +- Filament 0 → Extruder 0 +- Filament 1 → Extruder 1 +- Filament 2 → Extruder 2 +- Filament 3 → Extruder 3 +- Filament 4 → Extruder 4 (out of bounds!) +- Filament 5 → Extruder 5 (out of bounds!) +- etc. + +This would cause **array out-of-bounds errors** when accessing config arrays like `nozzle_diameter[5]`, `retraction_length[5]`, etc. + +**However**, the original implementation had a critical workaround: **GCode placeholder replacement still used the filament ID directly**, not the physical extruder ID. + +### Placeholder Replacement Behavior + +**Original (9d423d0714):** +```cpp +// GCode.cpp - toolchange_gcode placeholder replacement +// Used filament_id directly for placeholders like { filament_extruder } +std::string toolchange_gcode = this->config().toolchange_gcode; +toolchange_gcode = replace_tool_macros( + toolchange_gcode, + extruder_id, // filament_id + previous_extruder, + // ... +); +``` + +This meant: +- GCode T commands: T5, T6, T7, T8 (filament IDs) +- Placeholder {filament_extruder}: 5, 6, 7, 8 (filament IDs) +- **But config array access**: filament 5 → physical 5 → **CRASH** + +**Wait, this doesn't add up!** If the original was crashing on config access, how could it work? + +### The Real Solution: Config Array Access Pattern + +The original implementation must have had additional protection. Let me verify... + +Actually, looking at the original diff more carefully: + +**Original Extruder constructor (9d423d0714):** +```cpp +Extruder::Extruder(const PrintConfig& config, uint16_t extruder_id) + : m_id(extruder_id) + , m_technology(config.printer_technology) +{ + // CRITICAL FIX: Get physical extruder index + uint16_t physical_extruder = config.get_physical_extruder(m_id); + + // Use physical extruder index for parameter access + m_nozzle_diameter = float(config.nozzle_diameter.get_at(physical_extruder)); + // ... other parameters +} +``` + +So the original **DID** use `get_physical_extruder()` for config array access! + +This means the original implementation would crash with identity mapping when accessing `nozzle_diameter[5]` on a 4-extruder printer. + +### The Missing Piece: PrintApply.cpp Validation + +Let me check if there was validation that prevented this scenario... + +**Optimized version (989a53e124) added validation in PrintApply.cpp:** +```cpp +// Validate that all filament IDs map to valid physical extruders +for (const auto& [filament_id, physical_id] : filament_extruder_map) { + if (physical_id >= (int)nozzle_diameter.size()) { + throw ConfigurationError("Filament " + std::to_string(filament_id) + + " maps to physical extruder " + std::to_string(physical_id) + + " but only " + std::to_string(nozzle_diameter.size()) + + " extruders available"); + } +} +``` + +### The Hypothesis: What Actually Made It Work + +Given the evidence, there are two possibilities: + +**Hypothesis 1: The Original Never Actually Worked** +- The original implementation (9d423d0714) was added on Feb 4, 2026 +- The optimized version (989a53e124) was added on Feb 5, 2026 (only 1 day later!) +- The original may have had the identity mapping bug which was quickly fixed +- User's "working" version may have been a different branch or configuration + +**Hypothesis 2: 3MF Files Include Pre-built Maps** +- 3MF files can embed filament-to-extruder mappings +- If the 3MF file had `filament_extruder_map = [0,1,2,3,0,1,2,3]` pre-configured +- Then the explicit mapping path would be used, avoiding the identity mapping +- This would work correctly without modulo + +**Hypothesis 3: Empty Map = Different Behavior** +- When `m_filament_extruder_map` is empty (not set in 3MF) +- Original: Uses identity mapping → crashes +- Optimized: Uses modulo mapping → works +- User's 3MF files must have had explicit mappings + +## Detailed Comparison Table + +| Aspect | Original (9d423d0714) | Optimized (989a53e124) | +|--------|----------------------|------------------------| +| **Empty map behavior** | Identity: filament_id → filament_id | Modulo: filament_id % physical_count | +| **5 filaments on 4-extruder** | Filament 4 → Extruder 4 → **CRASH** | Filament 4 → Extruder 0 → ✅ Works | +| **8 filaments on 4-extruder** | Filaments 4-7 → Extruders 4-7 → **CRASH** | Filaments 4-7 → Extruders 0-3 → ✅ Works | +| **GCode T commands** | Used filament_id directly | Uses physical_extruder_id | +| **Config array access** | Used physical_extruder via get_physical_extruder() | Uses physical_extruder via get_physical_extruder() | +| **Logging** | Basic logging | Enhanced logging with map size | +| **Validation** | None | Bounds checking in PrintApply.cpp | +| **Comments** | Chinese comments | Chinese comments + English explanations | + +## Code Changes Summary + +### Print.hpp - get_physical_extruder() + +**Before (9d423d0714):** +```cpp +int get_physical_extruder(int filament_idx) const { + auto it = m_filament_extruder_map.find(filament_idx); + int physical_extruder_id = (it != m_filament_extruder_map.end()) ? it->second : filament_idx; + BOOST_LOG_TRIVIAL(info) << "Print::get_physical_extruder: filament_id=" << filament_idx + << " -> physical_extruder_id=" << physical_extruder_id + << " (map_size=" << m_filament_extruder_map.size() << ")" + << (it != m_filament_extruder_map.end() ? " [from_map]" : " [default_identity]"); + return physical_extruder_id; +} +``` + +**After (989a53e124):** +```cpp +int get_physical_extruder(int filament_idx) const { + auto it = m_filament_extruder_map.find(filament_idx); + int physical_extruder_id; + if (it != m_filament_extruder_map.end()) { + physical_extruder_id = it->second; + } else { + size_t physical_count = m_config.nozzle_diameter.values.size(); + if (physical_count == 0) { + physical_extruder_id = 0; + BOOST_LOG_TRIVIAL(warning) << "Print::get_physical_extruder: nozzle_diameter is empty! Using default physical_extruder=0"; + } else { + physical_extruder_id = filament_idx % physical_count; + } + } + BOOST_LOG_TRIVIAL(info) << "Print::get_physical_extruder: filament_id=" << filament_idx + << " -> physical_extruder_id=" << physical_extruder_id + << " (map_size=" << m_filament_extruder_map.size() << ")" + << (it != m_filament_extruder_map.end() ? " [from_map]" : " [default_mod]"); + return physical_extruder_id; +} +``` + +### GCode.cpp - Simplified Access Pattern + +**Before (9d423d0714):** +```cpp +// Complex physical extruder calculation everywhere +int previous_physical_extruder = (previous_extruder_id >= 0) ? + gcode_writer.get_physical_extruder(previous_extruder_id) : -1; +int new_physical_extruder = gcode_writer.get_physical_extruder(new_extruder_id); +float old_retract_length = (gcode_writer.extruder() != nullptr && previous_physical_extruder >= 0) ? + full_config.retraction_length.get_at(previous_physical_extruder) : 0; +float new_retract_length = full_config.retraction_length.get_at(new_physical_extruder); +``` + +**After (989a53e124):** +```cpp +// Direct filament_id access (simpler, relies on get_physical_extruder() internally) +float old_retract_length = (gcode_writer.extruder() != nullptr && previous_extruder_id >= 0) ? + full_config.retraction_length.get_at(previous_extruder_id) : 0; +float new_retract_length = full_config.retraction_length.get_at(new_extruder_id); +``` + +**Wait, this is WRONG!** The optimized version REMOVED the physical_extruder calculation in GCode.cpp and went back to using filament_id directly for config access. This would cause the same crash as before! + +### The Critical Insight: Config Wrapper + +The optimized version must have added a wrapper layer. Let me check... + +Actually, looking at the diff, the optimized version added: + +```cpp +#define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.extruder()->id()) +#define PHYSICAL_EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.get_physical_extruder(m_writer.extruder()->id())) +``` + +But I don't see widespread usage of PHYSICAL_EXTRUDER_CONFIG in the diff. + +### Conclusion: The Optimization May Have Introduced a Bug + +The evidence suggests: + +1. **Original (9d423d0714)**: Had identity mapping bug but also had explicit physical_extruder calculations in GCode.cpp that may have prevented crashes in some paths + +2. **Optimized (989a53e124)**: Fixed the identity mapping bug (good) but removed some of the explicit physical_extruder calculations (potentially bad) + +3. **User's Issue**: The user says the original was working for >4 filaments. This suggests either: + - Their 3MF files had explicit `filament_extruder_map` configurations + - They were using a different code path that didn't hit the bug + - There's additional context I'm missing + +## Recommendations + +To properly fix this for >4 filament 3MF slicing: + +1. **Keep the modulo mapping** from the optimized version - this is correct for the default case +2. **Add back physical_extruder calculations** in GCode.cpp for all config array access +3. **Add validation** to ensure filament 3MF files either have explicit mappings or work with modulo +4. **Test with actual >4 filament 3MF files** to verify the fix + +## Next Steps + +1. Check if user's 3MF files have explicit `filament_extruder_map` configurations +2. Verify which code paths are actually used during 3MF slicing +3. Add comprehensive bounds checking to prevent crashes +4. Test with real 8-filament 3MF files on 4-extruder configuration diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index a94a6df6804..842779f4279 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include "libslic3r.h" #include "clonable_ptr.hpp" @@ -39,6 +40,12 @@ namespace Slic3r { inline bool operator==(const FloatOrPercent& l, const FloatOrPercent& r) throw() { return l.value == r.value && l.percent == r.percent; } inline bool operator!=(const FloatOrPercent& l, const FloatOrPercent& r) throw() { return !(l == r); } inline bool operator< (const FloatOrPercent& l, const FloatOrPercent& r) throw() { return l.value < r.value || (l.value == r.value && int(l.percent) < int(r.percent)); } + inline std::ostream& operator<<(std::ostream& os, const FloatOrPercent& v) { + os << v.value; + if (v.percent) + os << "%"; + return os; + } } namespace std { @@ -415,22 +422,63 @@ class ConfigOptionVector : public ConfigOptionVectorBase // This function is useful to split values from multiple extrder / filament settings into separate configurations. void set_at(const ConfigOption *rhs, size_t i, size_t j) override { + // SM Orca: Debug logging + BOOST_LOG_TRIVIAL(error) << "ConfigOptionVector::set_at: START - this->values.size()=" << this->values.size() + << ", i=" << i << ", j=" << j; + // It is expected that the vector value has at least one value, which is the default, if not overwritten. assert(! this->values.empty()); if (this->values.size() <= i) { // Resize this vector, fill in the new vector fields with the copy of the first field. T v = this->values.front(); this->values.resize(i + 1, v); + BOOST_LOG_TRIVIAL(error) << "ConfigOptionVector::set_at: resized to " << this->values.size(); } + if (rhs->type() == this->type()) { // Assign the first value of the rhs vector. auto other = static_cast*>(rhs); if (other->values.empty()) throw ConfigurationError("ConfigOptionVector::set_at(): Assigning from an empty vector"); + + // Log before assignment + std::stringstream before_ss; + before_ss << "["; + for (size_t k = 0; k < this->values.size(); ++k) { + if (k > 0) before_ss << ", "; + before_ss << this->values[k]; + } + before_ss << "]"; + BOOST_LOG_TRIVIAL(error) << "ConfigOptionVector::set_at: before this->values=" << before_ss.str(); + + // Log other vector + std::stringstream other_ss; + other_ss << "["; + for (size_t k = 0; k < other->values.size(); ++k) { + if (k > 0) other_ss << ", "; + other_ss << other->values[k]; + } + other_ss << "]"; + BOOST_LOG_TRIVIAL(error) << "ConfigOptionVector::set_at: other->values=" << other_ss.str() + << ", other->get_at(" << j << ")=" << other->get_at(j); + this->values[i] = other->get_at(j); - } else if (rhs->type() == this->scalar_type()) + + // Log after assignment + std::stringstream after_ss; + after_ss << "["; + for (size_t k = 0; k < this->values.size(); ++k) { + if (k > 0) after_ss << ", "; + after_ss << this->values[k]; + } + after_ss << "]"; + BOOST_LOG_TRIVIAL(error) << "ConfigOptionVector::set_at: after this->values[" << i << "]=" << this->values[i] + << ", full=" << after_ss.str(); + + } else if (rhs->type() == this->scalar_type()) { this->values[i] = static_cast*>(rhs)->value; - else + BOOST_LOG_TRIVIAL(error) << "ConfigOptionVector::set_at: assigned scalar value=" << this->values[i]; + } else throw ConfigurationError("ConfigOptionVector::set_at(): Assigning an incompatible type"); } diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 6ac2dbe3d10..af4a2f45bb2 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -225,7 +225,6 @@ static std::vector get_path_of_change_filament(const Print& print) gcode += '\n'; } - // Return true if tch_prefix is found in custom_gcode static bool custom_gcode_changes_tool(const std::string& custom_gcode, const std::string& tch_prefix, unsigned next_extruder) { @@ -362,7 +361,6 @@ static std::vector get_path_of_change_filament(const Print& print) if(_wipe_speed < 10) _wipe_speed = 10; - //SoftFever: allow 100% retract before wipe if (length >= 0) { @@ -523,23 +521,19 @@ static std::vector get_path_of_change_filament(const Print& print) { GCodeWriter &gcode_writer = gcodegen.m_writer; FullPrintConfig &full_config = gcodegen.m_config; - // SM Orca: 计算物理挤出机ID用于参数查询 - int previous_physical_extruder = (previous_extruder_id >= 0) ? gcode_writer.get_physical_extruder(previous_extruder_id) : -1; - int new_physical_extruder = gcode_writer.get_physical_extruder(new_extruder_id); - // SM Orca: 回抽和温度是挤出机属性,使用 physical_extruder - float old_retract_length = (gcode_writer.extruder() != nullptr && previous_physical_extruder >= 0) ? - full_config.retraction_length.get_at(previous_physical_extruder) : 0; - float new_retract_length = full_config.retraction_length.get_at(new_physical_extruder); - float old_retract_length_toolchange = (gcode_writer.extruder() != nullptr && previous_physical_extruder >= 0) ? - full_config.retract_length_toolchange.get_at(previous_physical_extruder) : 0; - float new_retract_length_toolchange = full_config.retract_length_toolchange.get_at(new_physical_extruder); - int old_filament_temp = (gcode_writer.extruder() != nullptr && previous_physical_extruder >= 0) ? + float old_retract_length = (gcode_writer.extruder() != nullptr && previous_extruder_id >= 0) ? + full_config.retraction_length.get_at(previous_extruder_id) : 0; + float new_retract_length = full_config.retraction_length.get_at(new_extruder_id); + float old_retract_length_toolchange = (gcode_writer.extruder() != nullptr && previous_extruder_id >= 0) ? + full_config.retract_length_toolchange.get_at(previous_extruder_id) : 0; + float new_retract_length_toolchange = full_config.retract_length_toolchange.get_at(new_extruder_id); + int old_filament_temp = (gcode_writer.extruder() != nullptr && previous_extruder_id >= 0) ? (gcodegen.on_first_layer() ? - full_config.nozzle_temperature_initial_layer.get_at(previous_physical_extruder) : - full_config.nozzle_temperature.get_at(previous_physical_extruder)) : + full_config.nozzle_temperature_initial_layer.get_at(previous_extruder_id) : + full_config.nozzle_temperature.get_at(previous_extruder_id)) : 210; - int new_filament_temp = gcodegen.on_first_layer() ? full_config.nozzle_temperature_initial_layer.get_at(new_physical_extruder) : - full_config.nozzle_temperature.get_at(new_physical_extruder); + int new_filament_temp = gcodegen.on_first_layer() ? full_config.nozzle_temperature_initial_layer.get_at(new_extruder_id) : + full_config.nozzle_temperature.get_at(new_extruder_id); Vec3d nozzle_pos = gcode_writer.get_position(); float purge_volume = tcr.purge_volume < EPSILON ? 0 : std::max(tcr.purge_volume, g_min_purge_volume); @@ -740,7 +734,6 @@ static std::vector get_path_of_change_filament(const Print& print) double current_z = gcodegen.writer().get_position().z(); - if (z == -1.) // in case no specific z was provided, print at current_z pos z = current_z; @@ -847,7 +840,6 @@ static std::vector get_path_of_change_filament(const Print& print) if (m_single_extruder_multi_material) extruder_offset = m_extruder_offsets[0].cast(); else { - // SM Orca: 使用物理挤出机ID,不是耗材ID int physical_extruder = gcodegen.writer().get_physical_extruder(tcr.initial_tool); extruder_offset = m_extruder_offsets[physical_extruder].cast(); } @@ -910,7 +902,6 @@ static std::vector get_path_of_change_filament(const Print& print) if (line == "[change_filament_gcode]") { // BBS if (!m_single_extruder_multi_material) { - // SM Orca: 使用物理挤出机ID,不是耗材ID int physical_extruder_new = gcodegen.writer().get_physical_extruder(tcr.new_tool); int physical_extruder_initial = gcodegen.writer().get_physical_extruder(tcr.initial_tool); Vec2f new_extruder_offset = m_extruder_offsets[physical_extruder_new].cast(); @@ -1051,6 +1042,7 @@ static std::vector get_path_of_change_filament(const Print& print) const std::vector ColorPrintColors::Colors = { "#C0392B", "#E67E22", "#F1C40F", "#27AE60", "#1ABC9C", "#2980B9", "#9B59B6" }; #define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.extruder()->id()) +#define PHYSICAL_EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.get_physical_extruder(m_writer.extruder()->id())) void GCode::PlaceholderParserIntegration::reset() { @@ -1600,20 +1592,9 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu // DoExport::update_print_estimated_times_stats(m_processor, print->m_print_statistics); DoExport::update_print_estimated_stats(m_processor, m_writer.extruders(), print->m_print_statistics, print->config()); if (result != nullptr) { - // SM Orca: 记录移动赋值前的状态 - BOOST_LOG_TRIVIAL(info) << "SM Orca: Before move assignment - result=" << result - << ", result->extruders_count=" << result->extruders_count - << ", result->filament_diameters.size()=" << result->filament_diameters.size() - << ", processor.result().extruders_count=" << m_processor.result().extruders_count - << ", processor.result().filament_diameters.size()=" << m_processor.result().filament_diameters.size(); *result = std::move(m_processor.extract_result()); - // SM Orca: 记录移动赋值后的状态 - BOOST_LOG_TRIVIAL(info) << "SM Orca: After move assignment - result=" << result - << ", result->extruders_count=" << result->extruders_count - << ", result->filament_diameters.size()=" << result->filament_diameters.size(); - // set the filename to the correct value result->filename = path; } @@ -1870,21 +1851,15 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato { PROFILE_FUNC(); - // SM Orca: 确保GCodeWriter有正确的映射表(从Print获取并设置到writer) const auto& print_mapping = print.get_filament_extruder_map(); if (!print_mapping.empty()) { m_writer.set_filament_extruder_map(print_mapping); - BOOST_LOG_TRIVIAL(info) << "SM Orca: GCode::_do_export - Set filament_extruder_map from Print to writer, size: " << print_mapping.size(); for (const auto& pair : print_mapping) { - BOOST_LOG_TRIVIAL(info) << " SM Orca: Print mapping: filament " << pair.first << " -> extruder " << pair.second; } } else { - BOOST_LOG_TRIVIAL(info) << "SM Orca: GCode::_do_export - No filament_extruder_map from Print (using default 1:1 mapping)"; } - // SM Orca: 从GCodeWriter获取映射表并传递给GCodeProcessor const auto& writer_mapping = m_writer.get_filament_extruder_map(); - BOOST_LOG_TRIVIAL(info) << "SM Orca: GCode::_do_export - Setting filament_extruder_map to processor, mapping size: " << writer_mapping.size(); m_processor.set_filament_extruder_map(writer_mapping); // modifies m_silent_time_estimator_enabled @@ -2066,7 +2041,6 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato } } - // Write some terse information on the slicing parameters. const PrintObject *first_object = print.objects().front(); const double layer_height = first_object->config().layer_height.value; @@ -2249,7 +2223,6 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato this->placeholder_parser().set("long_retraction_when_cut", m_config.long_retractions_when_cut.get_at(initial_extruder_id)); this->placeholder_parser().set("temperature", new ConfigOptionInts(print.config().nozzle_temperature)); - this->placeholder_parser().set("retraction_distances_when_cut", new ConfigOptionFloats(m_config.retraction_distances_when_cut)); this->placeholder_parser().set("long_retractions_when_cut",new ConfigOptionBools(m_config.long_retractions_when_cut)); //Set variable for total layer count so it can be used in custom gcode. @@ -2403,10 +2376,8 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // calculate the volumetric speed of outer wall. Ignore per-object setting and multi-filament, and just use the default setting { - // SM Orca: 使用物理挤出机的喷嘴直径 int physical_extruder_id = m_writer.get_physical_extruder(initial_non_support_extruder_id); - // SM Orca: 日志 - 配置数组访问边界检查 size_t nozzle_array_size = m_config.nozzle_diameter.values.size(); BOOST_LOG_TRIVIAL(info) << "GCode::process_layer: volumetric_speed_calc - filament_id=" << initial_non_support_extruder_id << " physical_extruder_id=" << physical_extruder_id @@ -2853,12 +2824,12 @@ void GCode::process_layers( } }); if (m_spiral_vase) { - float nozzle_diameter = EXTRUDER_CONFIG(nozzle_diameter); + float nozzle_diameter = PHYSICAL_EXTRUDER_CONFIG(nozzle_diameter); float max_xy_smoothing = m_config.get_abs_value("spiral_mode_max_xy_smoothing", nozzle_diameter); this->m_spiral_vase->set_max_xy_smoothing(max_xy_smoothing); } const auto spiral_mode = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&spiral_mode = *this->m_spiral_vase.get(), &layers_to_print](LayerResult in) -> LayerResult { + [&spiral_mode = *this->m_spiral_vase.get(), &layers_to_print](LayerResult in)->LayerResult { if (in.nop_layer_result) return in; @@ -2953,7 +2924,7 @@ void GCode::process_layers( } }); if (m_spiral_vase) { - float nozzle_diameter = EXTRUDER_CONFIG(nozzle_diameter); + float nozzle_diameter = PHYSICAL_EXTRUDER_CONFIG(nozzle_diameter); float max_xy_smoothing = m_config.get_abs_value("spiral_mode_max_xy_smoothing", nozzle_diameter); this->m_spiral_vase->set_max_xy_smoothing(max_xy_smoothing); } @@ -3221,7 +3192,6 @@ int GCode::get_bed_temperature(const int extruder_id, const bool is_first_layer, return bed_temp_opt->get_at(extruder_id); } - // Write 1st layer bed temperatures into the G-code. // Only do that if the start G-code does not already contain any M-code controlling an extruder temperature. // M140 - Set Extruder Temperature @@ -4710,8 +4680,6 @@ std::string GCode::change_layer(coordf_t print_z) return gcode; } - - static std::unique_ptr calculate_layer_edge_grid(const Layer& layer) { auto out = make_unique(); @@ -4765,7 +4733,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou !m_config.spiral_mode && (loop.role() == erExternalPerimeter || (loop.role() == erPerimeter && m_config.seam_slope_inner_walls)) && layer_id() > 0; - const auto nozzle_diameter = EXTRUDER_CONFIG(nozzle_diameter); + const auto nozzle_diameter = PHYSICAL_EXTRUDER_CONFIG(nozzle_diameter); if (enable_seam_slope && m_config.seam_slope_conditional.value) { enable_seam_slope = loop.is_smooth(m_config.scarf_angle_threshold.value * M_PI / 180., nozzle_diameter); } @@ -4871,7 +4839,6 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou } } - const auto speed_for_path = [&speed, &small_peri_speed](const ExtrusionPath& path) { // don't apply small perimeter setting for overhangs/bridges/non-perimeters const bool is_small_peri = is_perimeter(path.role()) && !is_bridge(path.role()) && small_peri_speed > 0; @@ -4988,7 +4955,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou Vec2d p1 = paths.front().polyline.points.front().cast(); Vec2d p2 = paths.front().polyline.points[1].cast(); Vec2d v = p2 - p1; - double nd = scale_(EXTRUDER_CONFIG(nozzle_diameter)); + double nd = scale_(PHYSICAL_EXTRUDER_CONFIG(nozzle_diameter)); double l2 = v.squaredNorm(); // Shift by no more than a nozzle diameter. //FIXME Hiding the seams will not work nicely for very densely discretized contours! @@ -5309,7 +5276,6 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } } - // if needed, write the gcode_label_objects_end then gcode_label_objects_start // should be already done by travel_to, but just in case m_writer.add_object_change_labels(gcode); @@ -5392,8 +5358,6 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, double e_per_mm = m_writer.extruder()->e_per_mm3() * _mm3_per_mm; e_per_mm /= filament_flow_ratio; - - // set speed if (speed == -1) { if (path.role() == erPerimeter) { @@ -5577,10 +5541,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, // If adaptive PA is enabled, by default evaluate PA on all extrusion moves bool is_pa_calib = m_curr_print->calib_mode() == CalibMode::Calib_PA_Line || m_curr_print->calib_mode() == CalibMode::Calib_PA_Pattern || - m_curr_print->calib_mode() == CalibMode::Calib_PA_Tower; + m_curr_print->calib_mode() == CalibMode::Calib_PA_Tower; bool evaluate_adaptive_pa = false; bool role_change = (m_last_extrusion_role != path.role()); - if (!is_pa_calib && EXTRUDER_CONFIG(adaptive_pressure_advance) && EXTRUDER_CONFIG(enable_pressure_advance)) { + if (!is_pa_calib && PHYSICAL_EXTRUDER_CONFIG(adaptive_pressure_advance) && PHYSICAL_EXTRUDER_CONFIG(enable_pressure_advance)) { evaluate_adaptive_pa = true; // If we have already emmited a PA change because the m_multi_flow_segment_path_pa_set is set // skip re-issuing the PA change tag. @@ -5691,9 +5655,8 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } } - - auto overhang_fan_threshold = EXTRUDER_CONFIG(overhang_fan_threshold); - auto enable_overhang_bridge_fan = EXTRUDER_CONFIG(enable_overhang_bridge_fan); + auto overhang_fan_threshold = PHYSICAL_EXTRUDER_CONFIG(overhang_fan_threshold); + auto enable_overhang_bridge_fan = PHYSICAL_EXTRUDER_CONFIG(enable_overhang_bridge_fan); // { "0%", Overhang_threshold_none }, // { "10%", Overhang_threshold_1_4 }, @@ -5758,8 +5721,8 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, }; auto apply_role_based_fan_speed = [ &path, &append_role_based_fan_marker, - supp_interface_fan_speed = EXTRUDER_CONFIG(support_material_interface_fan_speed), - ironing_fan_speed = EXTRUDER_CONFIG(ironing_fan_speed) + supp_interface_fan_speed = PHYSICAL_EXTRUDER_CONFIG(support_material_interface_fan_speed), + ironing_fan_speed = PHYSICAL_EXTRUDER_CONFIG(ironing_fan_speed) ] { append_role_based_fan_marker(erSupportMaterialInterface, "_SUPP_INTERFACE"sv, supp_interface_fan_speed >= 0 && path.role() == erSupportMaterialInterface); @@ -5776,9 +5739,9 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, // Emit tag before new speed is set so the post processor reads the next speed immediately and uses it. // Dont emit tag if it has just already been emitted from a role change above if(_mm3_per_mm >0 && - EXTRUDER_CONFIG(adaptive_pressure_advance) && - EXTRUDER_CONFIG(enable_pressure_advance) && - EXTRUDER_CONFIG(adaptive_pressure_advance_overhangs) && + PHYSICAL_EXTRUDER_CONFIG(adaptive_pressure_advance) && + PHYSICAL_EXTRUDER_CONFIG(enable_pressure_advance) && + PHYSICAL_EXTRUDER_CONFIG(adaptive_pressure_advance_overhangs) && !evaluate_adaptive_pa){ if(writer().get_current_speed() > F){ // Ramping down speed - use overhang logic where the minimum speed is used between current and upcoming extrusion if(m_config.gcode_comments){ @@ -5985,9 +5948,9 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, // There is a speed change or flow change so emit the flag to evaluate PA for the upcomming extrusion // Emit tag before new speed is set so the post processor reads the next speed immediately and uses it. if(_mm3_per_mm >0 && - EXTRUDER_CONFIG(adaptive_pressure_advance) && - EXTRUDER_CONFIG(enable_pressure_advance) && - EXTRUDER_CONFIG(adaptive_pressure_advance_overhangs) ){ + PHYSICAL_EXTRUDER_CONFIG(adaptive_pressure_advance) && + PHYSICAL_EXTRUDER_CONFIG(enable_pressure_advance) && + PHYSICAL_EXTRUDER_CONFIG(adaptive_pressure_advance_overhangs) ){ if(last_set_speed > new_speed){ // Ramping down speed - use overhang logic where the minimum speed is used between current and upcoming extrusion if(m_config.gcode_comments) { sprintf(buf, "; Ramp up-variable\n"); @@ -6435,7 +6398,6 @@ std::string GCode::retract(bool toolchange, bool is_last_retraction, LiftType li gcode += toolchange ? m_writer.retract_for_toolchange() : m_writer.retract(); } - gcode += m_writer.reset_e(); // Orca: check if should + can lift (roughly from SuperSlicer) RetractLiftEnforceType retract_lift_type = RetractLiftEnforceType(EXTRUDER_CONFIG(retract_lift_enforce)); @@ -6495,11 +6457,12 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z, bool b gcode += this->placeholder_parser_process("filament_start_gcode", filament_start_gcode, extruder_id, &config); check_add_eol(gcode); } - if (m_config.enable_pressure_advance.get_at(extruder_id)) { - gcode += m_writer.set_pressure_advance(m_config.pressure_advance.get_at(extruder_id)); + int physical_extruder_id = m_writer.get_physical_extruder(extruder_id); + if (m_config.enable_pressure_advance.get_at(physical_extruder_id)) { + gcode += m_writer.set_pressure_advance(m_config.pressure_advance.get_at(physical_extruder_id)); // Orca: Adaptive PA // Reset Adaptive PA processor last PA value - m_pa_processor->resetPreviousPA(m_config.pressure_advance.get_at(extruder_id)); + m_pa_processor->resetPreviousPA(m_config.pressure_advance.get_at(physical_extruder_id)); } gcode += m_writer.toolchange(extruder_id); @@ -6535,17 +6498,14 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z, bool b } } - // If ooze prevention is enabled, park current extruder in the nearest // standby point and set it to the standby temperature. if (m_ooze_prevention.enable && m_writer.extruder() != nullptr) gcode += m_ooze_prevention.pre_toolchange(*this); - // SM Orca: 计算物理挤出机ID,用于耗材-挤出机映射 // 需要在参数查询之前计算,因为挤出机属性(温度、回抽等)应该从物理挤出机获取 int next_physical_extruder = m_writer.get_physical_extruder(extruder_id); - // SM Orca: 日志 - 工具切换开始 BOOST_LOG_TRIVIAL(info) << "GCode::set_extruder: Tool change START - filament_id=" << extruder_id << " -> physical_extruder_id=" << next_physical_extruder << " print_z=" << print_z @@ -6554,31 +6514,30 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z, bool b // BBS // 回抽和温度是挤出机属性,使用 physical_extruder - // SM Orca: 日志 - 配置数组访问边界检查 size_t retract_array_size = m_config.retraction_length.values.size(); size_t temp_array_size = m_config.nozzle_temperature.values.size(); BOOST_LOG_TRIVIAL(info) << "GCode::set_extruder: Config access check -" << " retraction_length array_size=" << retract_array_size << " nozzle_temperature array_size=" << temp_array_size - << " access_index=" << next_physical_extruder - << (next_physical_extruder >= (int)retract_array_size ? " [RETRACT_OUT_OF_BOUNDS!]" : "") - << (next_physical_extruder >= (int)temp_array_size ? " [TEMP_OUT_OF_BOUNDS!]" : ""); + << " extruder_id=" << extruder_id + << (extruder_id >= (int)retract_array_size ? " [RETRACT_OUT_OF_BOUNDS!]" : "") + << (extruder_id >= (int)temp_array_size ? " [TEMP_OUT_OF_BOUNDS!]" : ""); - float new_retract_length = m_config.retraction_length.get_at(next_physical_extruder); - float new_retract_length_toolchange = m_config.retract_length_toolchange.get_at(next_physical_extruder); - int new_filament_temp = this->on_first_layer() ? m_config.nozzle_temperature_initial_layer.get_at(next_physical_extruder): m_config.nozzle_temperature.get_at(next_physical_extruder); + float new_retract_length = m_config.retraction_length.get_at(extruder_id); + float new_retract_length_toolchange = m_config.retract_length_toolchange.get_at(extruder_id); + int new_filament_temp = this->on_first_layer() ? m_config.nozzle_temperature_initial_layer.get_at(extruder_id): m_config.nozzle_temperature.get_at(extruder_id); // BBS: if print_z == 0 use first layer temperature if (abs(print_z) < EPSILON) - new_filament_temp = m_config.nozzle_temperature_initial_layer.get_at(next_physical_extruder); + new_filament_temp = m_config.nozzle_temperature_initial_layer.get_at(extruder_id); Vec3d nozzle_pos = m_writer.get_position(); float old_retract_length, old_retract_length_toolchange, wipe_volume; int old_filament_temp, old_filament_e_feedrate; + int previous_physical_extruder = -1; float filament_area = float((M_PI / 4.f) * pow(m_config.filament_diameter.get_at(extruder_id), 2)); //BBS: add handling for filament change in start gcode int previous_extruder_id = -1; - int previous_physical_extruder = -1; // SM Orca: 前一个物理挤出机ID if (m_writer.extruder() != nullptr || m_start_gcode_filament != -1) { std::vector flush_matrix(cast(m_config.flush_volumes_matrix.values)); const unsigned int number_of_extruders = (unsigned int)(sqrt(flush_matrix.size()) + EPSILON); @@ -6588,18 +6547,16 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z, bool b assert(m_start_gcode_filament < number_of_extruders); previous_extruder_id = m_writer.extruder() != nullptr ? m_writer.extruder()->id() : m_start_gcode_filament; - previous_physical_extruder = m_writer.get_physical_extruder(previous_extruder_id); // SM Orca: 计算前一个物理挤出机 - // SM Orca: 日志 - 前一个挤出机配置访问 BOOST_LOG_TRIVIAL(info) << "GCode::set_extruder: Previous extruder config - filament_id=" << previous_extruder_id - << " physical_extruder_id=" << previous_physical_extruder << " (array_size=" << m_config.retraction_length.values.size() << ")" - << (previous_physical_extruder >= (int)m_config.retraction_length.values.size() ? " [PREV_OUT_OF_BOUNDS!]" : " [OK]"); + << (previous_extruder_id >= (int)m_config.retraction_length.values.size() ? " [PREV_OUT_OF_BOUNDS!]" : " [OK]"); + + old_retract_length = m_config.retraction_length.get_at(previous_extruder_id); + old_retract_length_toolchange = m_config.retract_length_toolchange.get_at(previous_extruder_id); + previous_physical_extruder = m_writer.get_physical_extruder(previous_extruder_id); - // SM Orca: 回抽和温度是挤出机属性,使用 physical_extruder - old_retract_length = m_config.retraction_length.get_at(previous_physical_extruder); - old_retract_length_toolchange = m_config.retract_length_toolchange.get_at(previous_physical_extruder); - old_filament_temp = this->on_first_layer()? m_config.nozzle_temperature_initial_layer.get_at(previous_physical_extruder) : m_config.nozzle_temperature.get_at(previous_physical_extruder); + old_filament_temp = this->on_first_layer()? m_config.nozzle_temperature_initial_layer.get_at(previous_extruder_id) : m_config.nozzle_temperature.get_at(previous_extruder_id); //Orca: always calculate wipe volume and hence provide correct flush_length, so that MMU devices with cutter and purge bin (e.g. ERCF_v2 with a filament cutter or Filametrix can take advantage of it) wipe_volume = flush_matrix[previous_extruder_id * number_of_extruders + extruder_id]; wipe_volume *= m_config.flush_multiplier; @@ -6622,7 +6579,6 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z, bool b DynamicConfig dyn_config; dyn_config.set_key_value("previous_extruder", new ConfigOptionInt(previous_extruder_id)); dyn_config.set_key_value("next_extruder", new ConfigOptionInt((int)extruder_id)); - // SM Orca: 物理挤出机ID在前面已经计算过了 dyn_config.set_key_value("next_physical_extruder", new ConfigOptionInt(next_physical_extruder)); dyn_config.set_key_value("previous_physical_extruder", new ConfigOptionInt(previous_physical_extruder)); dyn_config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); @@ -6743,8 +6699,9 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z, bool b if (m_ooze_prevention.enable) gcode += m_ooze_prevention.post_toolchange(*this); - if (m_config.enable_pressure_advance.get_at(extruder_id)) { - gcode += m_writer.set_pressure_advance(m_config.pressure_advance.get_at(extruder_id)); + int physical_extruder_id = m_writer.get_physical_extruder(extruder_id); + if (m_config.enable_pressure_advance.get_at(physical_extruder_id)) { + gcode += m_writer.set_pressure_advance(m_config.pressure_advance.get_at(physical_extruder_id)); } //Orca: tool changer or IDEX's firmware may change Z position, so we set it to unknown/undefined m_last_pos_defined = false; @@ -6819,16 +6776,9 @@ std::string GCode::set_object_info(Print *print) { // convert a model-space scaled point into G-code coordinates Vec2d GCode::point_to_gcode(const Point &point) const { - // SM Orca: 无条件诊断 - 检查是否被调用 static std::atomic call_count{0}; int this_call = ++call_count; - if (this_call <= 5 || (m_writer.extruder() && m_writer.extruder()->id() >= 12)) { - BOOST_LOG_TRIVIAL(info) << "SM Orca: point_to_gcode called #" << this_call - << " extruder=" << (m_writer.extruder() ? m_writer.extruder()->id() : -1) - << " point=(" << point.x() << ", " << point.y() << ")"; - } - // SM Orca: 使用物理挤出机ID获取offset,与gcode_to_point保持一致 Vec2d extruder_offset = Vec2d::Zero(); if (const Extruder *extruder = m_writer.extruder(); extruder) { extruder_offset = m_config.extruder_offset.get_at(m_writer.get_physical_extruder(extruder->id())); @@ -6836,7 +6786,6 @@ Vec2d GCode::point_to_gcode(const Point &point) const Vec2d result = unscale(point) + m_origin - extruder_offset; - // SM Orca: 诊断NaN问题 if (std::isnan(result.x()) || std::isinf(result.x()) || std::isnan(result.y()) || std::isinf(result.y())) { Vec2d unscaled_val = unscale(point); BOOST_LOG_TRIVIAL(error) << "SM Orca: point_to_gcode produced NaN/inf" @@ -6858,7 +6807,6 @@ Point GCode::gcode_to_point(const Vec2d &point) const Vec2d pt = point - m_origin; if (const Extruder *extruder = m_writer.extruder(); extruder) // This function may be called at the very start from toolchange G-code when the extruder is not assigned yet. - // SM Orca: 使用物理挤出机的偏移 pt += m_config.extruder_offset.get_at(m_writer.get_physical_extruder(extruder->id())); return scaled(pt); @@ -6870,7 +6818,6 @@ Vec2d GCode::point_to_gcode_quantized(const Point& point) const return { GCodeFormatter::quantize_xyzf(p.x()), GCodeFormatter::quantize_xyzf(p.y()) }; } - // Goes through by_region std::vector and returns reference to a subvector of entities, that are to be printed // during infill/perimeter wiping, or normally (depends on wiping_entities parameter) // Fills in by_region_per_copy_cache and returns its reference. @@ -6971,9 +6918,7 @@ void GCode::ObjectByExtruder::Island::Region::append(const Type type, const Extr } } - // Index into std::vector, which contains Object and Support layers for the current print_z, collected for // a single object, or for possibly multiple objects with multiple instances. - } // namespace Slic3r diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 64f2a8ad71f..b163a6e1349 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -198,7 +198,11 @@ class GCode { void set_gcode_offset(double x, double y) { m_writer.set_xy_offset(x, y); m_processor.set_xy_offset(x, y);} // SM Orca: Set filament-extruder mapping - void set_filament_extruder_map(const std::unordered_map& map) { m_writer.set_filament_extruder_map(map); m_processor.set_filament_extruder_map(map); } + void set_filament_extruder_map(const std::unordered_map& map) { + m_writer.set_filament_extruder_map(map); + m_processor.set_filament_extruder_map(map); + if (m_cooling_buffer) m_cooling_buffer->set_filament_extruder_map(map); + } // Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests. const Vec2d& origin() const { return m_origin; } diff --git a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp index e412cf3e67d..4c3d8ca80fe 100644 --- a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp +++ b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp @@ -482,8 +482,11 @@ static inline float get_default_perimeter_spacing(const PrintObject &print_objec std::vector printing_extruders = print_object.object_extruders(); assert(!printing_extruders.empty()); float avg_extruder = 0; - for(unsigned int extruder_id : printing_extruders) - avg_extruder += float(scale_(print_object.print()->config().nozzle_diameter.get_at(extruder_id))); + for(unsigned int extruder_id : printing_extruders) { + // SM Orca: nozzle_diameter是物理挤出机参数,使用physical_extruder_id访问 + int physical_extruder_id = print_object.print()->get_physical_extruder(extruder_id); + avg_extruder += float(scale_(print_object.print()->config().nozzle_diameter.get_at(physical_extruder_id))); + } avg_extruder /= printing_extruders.size(); return avg_extruder; } diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index 9541d680349..59b34d79384 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -340,12 +340,14 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: for (size_t i = 0; i < m_extruder_ids.size(); ++ i) { PerExtruderAdjustments &adj = per_extruder_adjustments[i]; unsigned int extruder_id = m_extruder_ids[i]; + // SM Orca: 冷却参数都是物理挤出机参数(无耗材覆盖),使用physical_extruder_id访问 + int physical_extruder_id = get_physical_extruder(extruder_id); adj.extruder_id = extruder_id; - adj.cooling_slow_down_enabled = m_config.slow_down_for_layer_cooling.get_at(extruder_id); - adj.slow_down_layer_time = float(m_config.slow_down_layer_time.get_at(extruder_id)); - adj.slow_down_min_speed = float(m_config.slow_down_min_speed.get_at(extruder_id)); + adj.cooling_slow_down_enabled = m_config.slow_down_for_layer_cooling.get_at(physical_extruder_id); + adj.slow_down_layer_time = float(m_config.slow_down_layer_time.get_at(physical_extruder_id)); + adj.slow_down_min_speed = float(m_config.slow_down_min_speed.get_at(physical_extruder_id)); // ORCA: To enable dont slow down external perimeters feature per filament (extruder) - adj.dont_slow_down_outer_wall = m_config.dont_slow_down_outer_wall.get_at(extruder_id); + adj.dont_slow_down_outer_wall = m_config.dont_slow_down_outer_wall.get_at(physical_extruder_id); map_extruder_to_per_extruder_adjustment[extruder_id] = i; } @@ -731,7 +733,8 @@ std::string CoolingBuffer::apply_layer_cooldown( &supp_interface_fan_control, &supp_interface_fan_speed, &ironing_fan_control, &ironing_fan_speed ](bool immediately_apply) { -#define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_current_extruder) + // SM Orca: 风扇参数都是物理挤出机参数(无耗材覆盖),使用physical_extruder_id访问 +#define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(get_physical_extruder(m_current_extruder)) float fan_min_speed = EXTRUDER_CONFIG(fan_min_speed); float fan_speed_new = EXTRUDER_CONFIG(reduce_fan_stop_start_freq) ? fan_min_speed : 0; //BBS diff --git a/src/libslic3r/GCode/CoolingBuffer.hpp b/src/libslic3r/GCode/CoolingBuffer.hpp index fba27b289b6..4c5222a972b 100644 --- a/src/libslic3r/GCode/CoolingBuffer.hpp +++ b/src/libslic3r/GCode/CoolingBuffer.hpp @@ -27,6 +27,13 @@ class CoolingBuffer { void reset(const Vec3d &position); void set_current_extruder(unsigned int extruder_id) { m_current_extruder = extruder_id; } std::string process_layer(std::string &&gcode, size_t layer_id, bool flush); + // SM Orca: Set filament to physical extruder mapping for correct parameter access + void set_filament_extruder_map(const std::unordered_map& map) { m_filament_extruder_map = map; } + // SM Orca: Get physical extruder ID from filament ID + int get_physical_extruder(int filament_idx) const { + auto it = m_filament_extruder_map.find(filament_idx); + return (it != m_filament_extruder_map.end()) ? it->second : filament_idx; + } private: CoolingBuffer& operator=(const CoolingBuffer&) = delete; @@ -55,6 +62,8 @@ class CoolingBuffer { // the PrintConfig slice of FullPrintConfig is constant, thus no thread synchronization is required. const PrintConfig &m_config; unsigned int m_current_extruder; + // SM Orca: Filament to physical extruder mapping for correct parameter access + std::unordered_map m_filament_extruder_map; //BBS: current fan speed int m_current_fan_speed; }; diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 28658493fef..b87e1f45186 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -94,7 +94,6 @@ const std::vector GCodeProcessor::Reserved_Tags_compatible = { " PA_CHANGE:" }; - const std::string GCodeProcessor::Flush_Start_Tag = " FLUSH_START"; const std::string GCodeProcessor::Flush_End_Tag = " FLUSH_END"; @@ -397,7 +396,6 @@ void GCodeProcessor::TimeProcessor::reset() filament_unload_times = 0.0f; machine_tool_change_time = 0.0f; - for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { machines[i].reset(); } @@ -457,7 +455,6 @@ void GCodeProcessor::UsedFilaments::process_color_change_cache() } } - void GCodeProcessor::UsedFilaments::process_total_volume_cache(GCodeProcessor* processor) { size_t active_extruder_id = processor->m_extruder_id; @@ -526,7 +523,6 @@ void GCodeProcessor::UsedFilaments::process_role_cache(GCodeProcessor* processor if (role_cache != 0.0f) { std::pair filament = { 0.0f, 0.0f }; - // SM Orca: 添加边界检查,与其他地方保持一致 float diameter = (static_cast(processor->m_extruder_id) < processor->m_result.filament_diameters.size()) ? processor->m_result.filament_diameters[processor->m_extruder_id] : processor->m_result.filament_diameters.back(); @@ -565,21 +561,16 @@ void GCodeProcessorResult::reset() { //BBS: add mutex for protection of gcode result lock(); - // SM Orca: 保存当前的extruders_count并尝试从已有数组推断 size_t saved_count = extruders_count; - // SM Orca: 如果extruders_count不合理,尝试从已有数组大小推断 if (saved_count == 0 || saved_count > 256) { // 尝试从已有数组大小推断(优先使用filament_diameters的大小) if (!filament_diameters.empty() && filament_diameters.size() <= 256) { saved_count = filament_diameters.size(); - BOOST_LOG_TRIVIAL(info) << "SM Orca: GCodeProcessorResult::reset() - Inferred extruders_count from existing array: " << saved_count; } else { - // SM Orca: 使用16作为默认值(覆盖U1等多耗材机型) // 对于只有少量耗材的用户,稍微多分配一些内存影响很小 saved_count = 16; - BOOST_LOG_TRIVIAL(info) << "SM Orca: GCodeProcessorResult::reset() - Using default extruders_count: " << saved_count << " (was " << extruders_count << ", array size was " << filament_diameters.size() << ")"; } } @@ -596,18 +587,15 @@ void GCodeProcessorResult::reset() { printable_height = 0.0f; settings_ids.reset(); - // SM Orca: 恢复extruders_count为合理的值 extruders_count = saved_count; extruder_colors = std::vector(); - // SM Orca: 使用推断的大小初始化所有耗材相关数组 filament_diameters = std::vector(saved_count, DEFAULT_FILAMENT_DIAMETER); filament_densities = std::vector(saved_count, DEFAULT_FILAMENT_DENSITY); filament_costs = std::vector(saved_count, DEFAULT_FILAMENT_COST); required_nozzle_HRC = std::vector(saved_count, DEFAULT_FILAMENT_HRC); filament_vitrification_temperature = std::vector(saved_count, DEFAULT_FILAMENT_VITRIFICATION_TEMPERATURE); - BOOST_LOG_TRIVIAL(info) << "SM Orca: GCodeProcessorResult::reset() - Resized arrays to " << saved_count << " (original extruders_count=" << (saved_count == extruders_count ? "preserved" : "inferred") << ", this=" << this << ")"; @@ -623,22 +611,15 @@ void GCodeProcessorResult::reset() { //BBS: add mutex for protection of gcode result lock(); - // SM Orca: 保存当前的extruders_count并尝试从已有数组推断 size_t saved_count = extruders_count; - // SM Orca: 如果extruders_count不合理,尝试从已有数组大小推断 if (saved_count == 0 || saved_count > 256) { // 尝试从已有数组大小推断(优先使用filament_diameters的大小) if (!filament_diameters.empty() && filament_diameters.size() <= 256) { saved_count = filament_diameters.size(); - BOOST_LOG_TRIVIAL(info) << "SM Orca: GCodeProcessorResult::reset() - Inferred extruders_count from existing array: " - << saved_count; } else { - // SM Orca: 使用16作为默认值(覆盖U1等多耗材机型) // 对于只有少量耗材的用户,稍微多分配一些内存影响很小 saved_count = 16; - BOOST_LOG_TRIVIAL(info) << "SM Orca: GCodeProcessorResult::reset() - Using default extruders_count: " - << saved_count << " (was " << extruders_count << ", array size was " << filament_diameters.size() << ")"; } } @@ -658,20 +639,14 @@ void GCodeProcessorResult::reset() { backtrace_enabled = false; extruder_colors = std::vector(); - // SM Orca: 恢复extruders_count为合理的值 extruders_count = saved_count; - // SM Orca: 使用推断的大小初始化所有耗材相关数组 filament_diameters = std::vector(saved_count, DEFAULT_FILAMENT_DIAMETER); required_nozzle_HRC = std::vector(saved_count, DEFAULT_FILAMENT_HRC); filament_densities = std::vector(saved_count, DEFAULT_FILAMENT_DENSITY); filament_costs = std::vector(saved_count, DEFAULT_FILAMENT_COST); filament_vitrification_temperature = std::vector(saved_count, DEFAULT_FILAMENT_VITRIFICATION_TEMPERATURE); - BOOST_LOG_TRIVIAL(info) << "SM Orca: GCodeProcessorResult::reset() - Resized arrays to " << saved_count - << " (original extruders_count=" << (saved_count == extruders_count ? "preserved" : "inferred") - << ", this=" << this << ")"; - custom_gcode_per_print_z = std::vector(); spiral_vase_layers = std::vector>>(); bed_match_result = BedMatchResult(true); @@ -788,12 +763,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config) m_preheat_steps = 1; m_result.backtrace_enabled = m_preheat_time > 0 && (m_is_XL_printer || (!m_single_extruder_multi_material && extruders_count > 1)); - // SM Orca: 配置验证 - 在resize之前检查配置完整性 size_t physical_extruder_count = config.extruder_offset.values.size(); - BOOST_LOG_TRIVIAL(info) << "SM Orca: Configuration validation:" - << " filaments=" << extruders_count - << ", physical_extruders=" << physical_extruder_count - << ", mappings=" << m_filament_extruder_map.size(); // 验证各配置数组大小 if (config.filament_density.values.size() < extruders_count) { @@ -840,7 +810,6 @@ void GCodeProcessor::apply_config(const PrintConfig& config) m_result.nozzle_hrc = static_cast(config.nozzle_hrc.getInt()); m_result.nozzle_type = config.nozzle_type; - // SM Orca: 获取各配置数组的大小,用于边界检查 size_t diameter_count = config.filament_diameter.values.size(); size_t density_count = config.filament_density.values.size(); size_t cost_count = config.filament_cost.values.size(); @@ -850,10 +819,8 @@ void GCodeProcessor::apply_config(const PrintConfig& config) size_t vitrification_count = config.temperature_vitrification.values.size(); for (size_t i = 0; i < extruders_count; ++ i) { - // SM Orca: 使用物理挤出机ID来访问offset,并确保不越界 int physical_extruder = get_physical_extruder(i); - // SM Orca: 边界检查 - 如果映射的物理挤出机超出范围,回退到1:1映射 if (physical_extruder < 0 || physical_extruder >= static_cast(physical_extruder_count)) { BOOST_LOG_TRIVIAL(error) << "SM Orca: Filament " << i << " maps to invalid physical extruder " << physical_extruder @@ -866,7 +833,6 @@ void GCodeProcessor::apply_config(const PrintConfig& config) m_extruder_offsets[i] = to_3d(config.extruder_offset.get_at(physical_extruder).cast().eval(), 0.f); m_extruder_colors[i] = static_cast(i); - // SM Orca: 安全读取温度配置(使用最后有效值作为回退) // 温度是挤出机属性,使用 physical_extruder 而不是 filament index if (physical_extruder < static_cast(temp_initial_count)) { m_extruder_temps_first_layer_config[i] = static_cast(config.nozzle_temperature_initial_layer.get_at(physical_extruder)); @@ -891,7 +857,6 @@ void GCodeProcessor::apply_config(const PrintConfig& config) m_extruder_temps_config[i] = m_extruder_temps_first_layer_config[i]; } - // SM Orca: 安全读取直径(使用最后有效值作为回退) if (i < diameter_count) { m_result.filament_diameters[i] = static_cast(config.filament_diameter.get_at(i)); } else { @@ -901,7 +866,6 @@ void GCodeProcessor::apply_config(const PrintConfig& config) BOOST_LOG_TRIVIAL(warning) << "SM Orca: Filament " << i << " diameter not configured, using " << fallback << "mm"; } - // SM Orca: 安全读取HRC if (i < hrc_count) { m_result.required_nozzle_HRC[i] = static_cast(config.required_nozzle_HRC.get_at(i)); } else { @@ -910,7 +874,6 @@ void GCodeProcessor::apply_config(const PrintConfig& config) m_result.required_nozzle_HRC[i] = fallback; } - // SM Orca: 安全读取密度(使用最后有效值作为回退) if (i < density_count) { m_result.filament_densities[i] = static_cast(config.filament_density.get_at(i)); } else { @@ -920,7 +883,6 @@ void GCodeProcessor::apply_config(const PrintConfig& config) BOOST_LOG_TRIVIAL(warning) << "SM Orca: Filament " << i << " density not configured, using " << fallback << " g/cm³"; } - // SM Orca: 安全读取玻璃化温度 if (i < vitrification_count) { m_result.filament_vitrification_temperature[i] = static_cast(config.temperature_vitrification.get_at(i)); } else { @@ -929,7 +891,6 @@ void GCodeProcessor::apply_config(const PrintConfig& config) m_result.filament_vitrification_temperature[i] = fallback; } - // SM Orca: 安全读取成本(使用0作为默认值) if (i < cost_count) { m_result.filament_costs[i] = static_cast(config.filament_cost.get_at(i)); } else { @@ -1051,17 +1012,14 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) if (filament_diameters != nullptr) { size_t config_size = filament_diameters->values.size(); - // SM Orca: 确保数组大小等于extruders_count,不要clear() if (m_result.filament_diameters.size() < m_result.extruders_count) { m_result.filament_diameters.resize(m_result.extruders_count, DEFAULT_FILAMENT_DIAMETER); } - // SM Orca: 只更新配置中有的值 for (size_t i = 0; i < config_size && i < m_result.extruders_count; ++i) { m_result.filament_diameters[i] = static_cast(filament_diameters->values[i]); } - // SM Orca: 对于配置中没有的值,使用最后一个有效值 if (config_size > 0 && config_size < m_result.extruders_count) { float last_value = static_cast(filament_diameters->values[config_size - 1]); for (size_t i = config_size; i < m_result.extruders_count; ++i) { @@ -1072,7 +1030,6 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) } } - // SM Orca: 确保数组大小正确(移除原有的回退逻辑,因为已经在上面处理了) if (m_result.filament_diameters.size() < m_result.extruders_count) { m_result.filament_diameters.resize(m_result.extruders_count, DEFAULT_FILAMENT_DIAMETER); } @@ -1093,17 +1050,14 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) if (filament_densities != nullptr) { size_t config_size = filament_densities->values.size(); - // SM Orca: 确保数组大小等于extruders_count,不要clear() if (m_result.filament_densities.size() < m_result.extruders_count) { m_result.filament_densities.resize(m_result.extruders_count, DEFAULT_FILAMENT_DENSITY); } - // SM Orca: 只更新配置中有的值 for (size_t i = 0; i < config_size && i < m_result.extruders_count; ++i) { m_result.filament_densities[i] = static_cast(filament_densities->values[i]); } - // SM Orca: 对于配置中没有的值,使用最后一个有效值 if (config_size > 0 && config_size < m_result.extruders_count) { float last_value = static_cast(filament_densities->values[config_size - 1]); for (size_t i = config_size; i < m_result.extruders_count; ++i) { @@ -1114,7 +1068,6 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) } } - // SM Orca: 确保数组大小正确 if (m_result.filament_densities.size() < m_result.extruders_count) { m_result.filament_densities.resize(m_result.extruders_count, DEFAULT_FILAMENT_DENSITY); } @@ -1124,16 +1077,13 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) if (filament_costs != nullptr) { size_t config_size = filament_costs->values.size(); - // SM Orca: 确保数组大小等于extruders_count,不要clear() if (m_result.filament_costs.size() < m_result.extruders_count) { m_result.filament_costs.resize(m_result.extruders_count, DEFAULT_FILAMENT_COST); } - // SM Orca: 只更新配置中有的值 for (size_t i = 0; i < config_size && i < m_result.extruders_count; ++i) m_result.filament_costs[i] = static_cast(filament_costs->values[i]); - // SM Orca: 对于配置中没有的值,使用0(成本默认值) if (config_size < m_result.extruders_count) { for (size_t i = config_size; i < m_result.extruders_count; ++i) { m_result.filament_costs[i] = DEFAULT_FILAMENT_COST; @@ -1143,7 +1093,6 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) } } - // SM Orca: 确保数组大小正确 if (m_result.filament_costs.size() < m_result.extruders_count) { m_result.filament_costs.resize(m_result.extruders_count, DEFAULT_FILAMENT_COST); } @@ -1153,17 +1102,14 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) if (filament_vitrification_temperature != nullptr) { size_t config_size = filament_vitrification_temperature->values.size(); - // SM Orca: 确保数组大小等于extruders_count,不要clear() if (m_result.filament_vitrification_temperature.size() < m_result.extruders_count) { m_result.filament_vitrification_temperature.resize(m_result.extruders_count, DEFAULT_FILAMENT_VITRIFICATION_TEMPERATURE); } - // SM Orca: 只更新配置中有的值 for (size_t i = 0; i < config_size && i < m_result.extruders_count; ++i) { m_result.filament_vitrification_temperature[i] = static_cast(filament_vitrification_temperature->values[i]); } - // SM Orca: 对于配置中没有的值,使用最后一个有效值 if (config_size > 0 && config_size < m_result.extruders_count) { int last_value = static_cast(filament_vitrification_temperature->values[config_size - 1]); for (size_t i = config_size; i < m_result.extruders_count; ++i) { @@ -1186,7 +1132,6 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) } } else { - // SM Orca: 先确保数组足够大,不要缩小已有的数组 size_t physical_count = extruder_offset->values.size(); if (m_extruder_offsets.size() < m_result.extruders_count) { m_extruder_offsets.resize(m_result.extruders_count, DEFAULT_EXTRUDER_OFFSET); @@ -1201,7 +1146,6 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) } if (m_extruder_offsets.size() < m_result.extruders_count) { - // SM Orca: 使用映射来填充剩余耗材的offset,而不是使用默认值 size_t physical_count = m_extruder_offsets.size(); for (size_t i = m_extruder_offsets.size(); i < m_result.extruders_count; ++i) { int physical_extruder = get_physical_extruder(i); @@ -1257,7 +1201,6 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) if (machine_tool_change_time != nullptr) m_time_processor.machine_tool_change_time = static_cast(machine_tool_change_time->value); - if (m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware || m_flavor == gcfKlipper) { const ConfigOptionFloats* machine_max_acceleration_x = config.option("machine_max_acceleration_x"); if (machine_max_acceleration_x != nullptr) @@ -1319,7 +1262,6 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) if (machine_max_acceleration_retracting != nullptr) m_time_processor.machine_limits.machine_max_acceleration_retracting.values = machine_max_acceleration_retracting->values; - // Legacy Marlin does not have separate travel acceleration, it uses the 'extruding' value instead. const ConfigOptionFloats* machine_max_acceleration_travel = config.option(m_flavor == gcfMarlinLegacy || m_flavor == gcfKlipper ? "machine_max_acceleration_extruding" @@ -1327,7 +1269,6 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) if (machine_max_acceleration_travel != nullptr) m_time_processor.machine_limits.machine_max_acceleration_travel.values = machine_max_acceleration_travel->values; - const ConfigOptionFloats* machine_min_extruding_rate = config.option("machine_min_extruding_rate"); if (machine_min_extruding_rate != nullptr) m_time_processor.machine_limits.machine_min_extruding_rate.values = machine_min_extruding_rate->values; @@ -1379,12 +1320,10 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) if (bed_type != nullptr) m_result.bed_type = (BedType)bed_type->value; - const ConfigOptionFloat* z_offset = config.option("z_offset"); if (z_offset != nullptr) m_z_offset = z_offset->value; - // SM Orca: 验证所有数组大小是否正确 bool arrays_valid = true; if (m_result.filament_diameters.size() != m_result.extruders_count) { BOOST_LOG_TRIVIAL(error) << "SM Orca: CRITICAL - filament_diameters size mismatch: " @@ -1407,10 +1346,6 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) arrays_valid = false; } - if (arrays_valid) { - BOOST_LOG_TRIVIAL(info) << "SM Orca: DynamicPrintConfig array validation passed - all arrays correctly sized to " - << m_result.extruders_count; - } } void GCodeProcessor::enable_stealth_time_estimator(bool enabled) @@ -1850,7 +1785,6 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line, bool // update start position m_start_position = m_end_position; - // SM Orca: 防止NaN/inf从m_end_position传播到m_start_position if (std::isnan(m_start_position[X]) || std::isinf(m_start_position[X]) || std::isnan(m_start_position[Y]) || std::isinf(m_start_position[Y]) || std::isnan(m_start_position[Z]) || std::isinf(m_start_position[Z])) { @@ -2946,7 +2880,6 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line, const std::o m_end_position[a] = absolute_position((Axis)a, line); } - // SM Orca: 验证position更新,捕获NaN/inf的源头 if (std::isnan(m_end_position[X]) || std::isinf(m_end_position[X]) || std::isnan(m_end_position[Y]) || std::isinf(m_end_position[Y]) || std::isnan(m_end_position[Z]) || std::isinf(m_end_position[Z])) { @@ -3026,12 +2959,10 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line, const std::o // cross section: rectangle m_width = delta_pos[E] * static_cast(M_PI * sqr(1.05f * filament_radius)) / (delta_xyz * m_height); else if (m_extrusion_role == erBridgeInfill || m_extrusion_role == erInternalBridgeInfill || m_extrusion_role == erNone) { - // SM Orca: 添加边界检查 float diameter = (static_cast(m_extruder_id) < m_result.filament_diameters.size()) ? m_result.filament_diameters[m_extruder_id] : m_result.filament_diameters.back(); - // SM Orca: 防止sqrt(负数)产生NaN float ratio = delta_pos[E] / delta_xyz; if (ratio < 0.0f) { BOOST_LOG_TRIVIAL(warning) << "SM Orca: Negative E/XYZ ratio (" << ratio @@ -3046,7 +2977,6 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line, const std::o // cross section: rectangle + 2 semicircles m_width = delta_pos[E] * static_cast(M_PI * sqr(filament_radius)) / (delta_xyz * m_height) + static_cast(1.0 - 0.25 * M_PI) * m_height; - // SM Orca: 验证宽度计算结果,防止NaN/inf传播 if (std::isnan(m_width) || std::isinf(m_width)) { BOOST_LOG_TRIVIAL(error) << "SM Orca: Invalid width calculated: " << m_width << " for extruder " << m_extruder_id @@ -3262,7 +3192,6 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line, const std::o // axis reversal std::max(-v_exit, v_entry)); - float axis_max_jerk = get_axis_max_jerk(static_cast(i), static_cast(a)); if (jerk > axis_max_jerk) { v_factor *= axis_max_jerk / jerk; @@ -3309,7 +3238,6 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line, const std::o // check for seam starting vertex if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) { //BBS: m_result.moves.back().position has plate offset, must minus plate offset before calculate the real seam position - // SM Orca: m_extruder_offsets[i] 已经在 apply_config 中映射过了,这里直接使用 m_extruder_id 作为索引 const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset; if (!m_seams_detector.has_first_vertex()) { m_seams_detector.set_first_vertex(new_pos); @@ -3329,7 +3257,6 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line, const std::o const Vec3f curr_pos(m_end_position[X], m_end_position[Y], m_end_position[Z]); //BBS: m_result.moves.back().position has plate offset, must minus plate offset before calculate the real seam position - // SM Orca: m_extruder_offsets[i] 已经在 apply_config 中映射过了,这里直接使用 m_extruder_id 作为索引 const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset; const std::optional first_vertex = m_seams_detector.get_first_vertex(); // the threshold value = 0.0625f == 0.25 * 0.25 is arbitrary, we may find some smarter condition later @@ -3345,7 +3272,6 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line, const std::o } else if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) { m_seams_detector.activate(true); - // SM Orca: m_extruder_offsets[i] 已经在 apply_config 中映射过了,这里直接使用 m_extruder_id 作为索引 m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset); } @@ -3439,7 +3365,6 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line) m_end_position[a] = absolute_position((Axis)a, line); } - // SM Orca: 验证position更新,捕获NaN/inf的源头 if (std::isnan(m_end_position[X]) || std::isinf(m_end_position[X]) || std::isnan(m_end_position[Y]) || std::isinf(m_end_position[Y]) || std::isnan(m_end_position[Z]) || std::isinf(m_end_position[Z])) { @@ -3499,7 +3424,6 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line) EMoveType type = move_type(delta_pos[E]); - const float delta_xyz = std::sqrt(sqr(arc_length) + sqr(delta_pos[Z])); m_travel_dist = delta_xyz; if (type == EMoveType::Extrude) { @@ -3548,12 +3472,10 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line) //BBS: cross section: rectangle m_width = delta_pos[E] * static_cast(M_PI * sqr(1.05f * filament_radius)) / (delta_xyz * m_height); else if (m_extrusion_role == erBridgeInfill || m_extrusion_role == erInternalBridgeInfill || m_extrusion_role == erNone) { - // SM Orca: 添加边界检查 float diameter = (static_cast(m_extruder_id) < m_result.filament_diameters.size()) ? m_result.filament_diameters[m_extruder_id] : m_result.filament_diameters.back(); - // SM Orca: 防止sqrt(负数)产生NaN float ratio = delta_pos[E] / delta_xyz; if (ratio < 0.0f) { BOOST_LOG_TRIVIAL(warning) << "SM Orca: Negative E/XYZ ratio (" << ratio @@ -3568,7 +3490,6 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line) //BBS: cross section: rectangle + 2 semicircles m_width = delta_pos[E] * static_cast(M_PI * sqr(filament_radius)) / (delta_xyz * m_height) + static_cast(1.0 - 0.25 * M_PI) * m_height; - // SM Orca: 验证宽度计算结果,防止NaN/inf传播 if (std::isnan(m_width) || std::isinf(m_width)) { BOOST_LOG_TRIVIAL(error) << "SM Orca: Invalid width calculated: " << m_width << " for extruder " << m_extruder_id @@ -3739,7 +3660,6 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line) //BBS: axis reversal std::max(-v_exit, v_entry)); - float axis_max_jerk = get_axis_max_jerk(static_cast(i), static_cast(a)); if (jerk > axis_max_jerk) { v_factor *= axis_max_jerk / jerk; @@ -3786,7 +3706,6 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line) if (m_seams_detector.is_active()) { //BBS: check for seam starting vertex if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) { - // SM Orca: m_extruder_offsets[i] 已经在 apply_config 中映射过了,这里直接使用 m_extruder_id 作为索引 const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset; if (!m_seams_detector.has_first_vertex()) { m_seams_detector.set_first_vertex(new_pos); @@ -3804,7 +3723,6 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line) m_end_position[X] = pos.x(); m_end_position[Y] = pos.y(); m_end_position[Z] = pos.z(); }; const Vec3f curr_pos(m_end_position[X], m_end_position[Y], m_end_position[Z]); - // SM Orca: m_extruder_offsets[i] 已经在 apply_config 中映射过了,这里直接使用 m_extruder_id 作为索引 const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset; const std::optional first_vertex = m_seams_detector.get_first_vertex(); //BBS: the threshold value = 0.0625f == 0.25 * 0.25 is arbitrary, we may find some smarter condition later @@ -3820,7 +3738,6 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line) } else if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) { m_seams_detector.activate(true); - // SM Orca: m_extruder_offsets[i] 已经在 apply_config 中映射过了,这里直接使用 m_extruder_id 作为索引 m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset); } @@ -3866,21 +3783,17 @@ void GCodeProcessor::process_G29(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_G10(const GCodeReader::GCodeLine& line) { - // SM Orca: 回抽参数是挤出机属性,使用 physical_extruder - int physical_extruder = get_physical_extruder(m_extruder_id); GCodeReader::GCodeLine g10; - g10.set(Axis::E, -this->m_parser.config().retraction_length.get_at(physical_extruder)); - g10.set(Axis::F, this->m_parser.config().retraction_speed.get_at(physical_extruder) * 60); + g10.set(Axis::E, -this->m_parser.config().retraction_length.get_at(m_extruder_id)); + g10.set(Axis::F, this->m_parser.config().retraction_speed.get_at(m_extruder_id) * 60); process_G1(g10); } void GCodeProcessor::process_G11(const GCodeReader::GCodeLine& line) { - // SM Orca: 回抽参数是挤出机属性,使用 physical_extruder - int physical_extruder = get_physical_extruder(m_extruder_id); GCodeReader::GCodeLine g11; - g11.set(Axis::E, this->m_parser.config().retraction_length.get_at(physical_extruder) + this->m_parser.config().retract_restart_extra.get_at(physical_extruder)); - g11.set(Axis::F, this->m_parser.config().deretraction_speed.get_at(physical_extruder) * 60); + g11.set(Axis::E, this->m_parser.config().retraction_length.get_at(m_extruder_id) + this->m_parser.config().retract_restart_extra.get_at(m_extruder_id)); + g11.set(Axis::F, this->m_parser.config().deretraction_speed.get_at(m_extruder_id) * 60); process_G1(g11); } @@ -3979,7 +3892,6 @@ void GCodeProcessor::process_G92(const GCodeReader::GCodeLine& line) } } - // SM Orca: 验证m_origin,防止NaN/inf传播 if (std::isnan(m_origin[X]) || std::isinf(m_origin[X]) || std::isnan(m_origin[Y]) || std::isinf(m_origin[Y]) || std::isnan(m_origin[Z]) || std::isinf(m_origin[Z])) { @@ -4121,7 +4033,6 @@ void GCodeProcessor::process_M191(const GCodeReader::GCodeLine& line) simulate_st_synchronize(wait_chamber_temp_time); } - void GCodeProcessor::process_M201(const GCodeReader::GCodeLine& line) { // see http://reprap.org/wiki/G-code#M201:_Set_max_printing_acceleration @@ -4457,7 +4368,6 @@ void GCodeProcessor::run_post_process() double filament_total_cost = 0.0; for (const auto& [id, volume] : m_result.print_statistics.total_volumes_per_extruder) { - // SM Orca: 边界检查 - 确保id在有效范围内 if (id >= m_result.filament_diameters.size() || id >= m_result.filament_densities.size() || id >= m_result.filament_costs.size()) { @@ -4468,12 +4378,10 @@ void GCodeProcessor::run_post_process() continue; } - // SM Orca: 读取并验证配置值 double diameter = m_result.filament_diameters[id]; double density = m_result.filament_densities[id]; double cost = m_result.filament_costs[id]; - // SM Orca: 防止除零和NaN传播 if (diameter <= 0.0 || std::isnan(diameter)) { BOOST_LOG_TRIVIAL(warning) << "SM Orca: Invalid filament diameter " << diameter << " for filament " << id << ", using default 1.75mm"; @@ -4492,7 +4400,6 @@ void GCodeProcessor::run_post_process() cost = 0.0; } - // SM Orca: 执行成本计算 double cross_section = M_PI * sqr(0.5 * diameter); filament_mm[id] = volume / cross_section; filament_cm3[id] = volume * 0.001; @@ -4509,7 +4416,6 @@ void GCodeProcessor::run_post_process() double total_g_wipe_tower = m_print->print_statistics().total_wipe_tower_filament; - auto time_in_minutes = [](float time_in_seconds) { assert(time_in_seconds >= 0.f); return int((time_in_seconds + 0.5f) / 60.0f); @@ -4637,7 +4543,6 @@ void GCodeProcessor::run_post_process() size_t m_times_cache_id{ 0 }; size_t m_out_file_pos{ 0 }; - public: ExportLines(EWriteType type, const std::array(PrintEstimatedStatistics::ETimeMode::Count)>& machines) @@ -5207,7 +5112,6 @@ void GCodeProcessor::run_post_process() export_lines.flush(out, m_result, out_path); - out.close(); in.close(); @@ -5225,7 +5129,6 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type) m_line_id + 1 : ((type == EMoveType::Seam) ? m_last_line_id : m_line_id); - // SM Orca: 添加边界检查,防止访问越界 Vec3f extruder_offset = (static_cast(m_extruder_id) < m_extruder_offsets.size()) ? m_extruder_offsets[m_extruder_id] : Vec3f(0.0f, 0.0f, 0.0f); @@ -5241,7 +5144,6 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type) extruder_offset; } - // SM Orca: 最后一道防线,防止无效值进入result if (std::isnan(m_width) || std::isinf(m_width)) { BOOST_LOG_TRIVIAL(error) << "SM Orca: Blocking invalid width: " << m_width << " (extruder " << m_extruder_id << ")"; @@ -5254,7 +5156,6 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type) m_height = DEFAULT_TOOLPATH_HEIGHT; } - // SM Orca: 验证position,防止NaN/inf进入moves Vec3f final_position = Vec3f(m_end_position[X] + m_x_offset, m_end_position[Y] + m_y_offset, m_processing_start_custom_gcode ? m_first_layer_height : m_end_position[Z] - m_z_offset) @@ -5281,7 +5182,6 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type) m_extruder_id, m_cp_color.current, //BBS: add plate's offset to the rendering vertices - // SM Orca: 使用验证过的final_position final_position, static_cast(m_end_position[E] - m_start_position[E]), m_feedrate, @@ -5295,7 +5195,6 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type) static_cast(m_layer_id), //layer_duration: set later //BBS: add arc move related data path_type, - // SM Orca: m_extruder_offsets[i] 已经在 apply_config 中映射过了,这里直接使用 m_extruder_id 作为索引 Vec3f(m_arc_center(0, 0) + m_x_offset, m_arc_center(1, 0) + m_y_offset, m_arc_center(2, 0)) + m_extruder_offsets[m_extruder_id], m_interpolation_points, }); diff --git a/src/libslic3r/GCode/WipeTower2.cpp b/src/libslic3r/GCode/WipeTower2.cpp index 2b86415d16b..86fc7056192 100644 --- a/src/libslic3r/GCode/WipeTower2.cpp +++ b/src/libslic3r/GCode/WipeTower2.cpp @@ -1410,9 +1410,9 @@ void WipeTower2::set_extruder(size_t idx, int physical_extruder, const PrintConf m_used_filament_length.resize(std::max(m_used_filament_length.size(), idx + 1)); // makes sure that the vector is big enough so we don't have to check later - // SM Orca: 回抽参数是挤出机属性,使用 physical_extruder - m_filpar[idx].retract_length = config.retraction_length.get_at(physical_extruder); - m_filpar[idx].retract_speed = config.retraction_speed.get_at(physical_extruder); + // SM Orca: 回抽参数支持耗材覆盖,继承后存储在耗材位置,使用 filament_id (idx) + m_filpar[idx].retract_length = config.retraction_length.get_at(idx); + m_filpar[idx].retract_speed = config.retraction_speed.get_at(idx); } diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index 4a89103d472..fa19ea31edb 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -27,6 +27,11 @@ void GCodeWriter::apply_print_config(const PrintConfig &print_config) { this->config.apply(print_config, true); m_single_extruder_multi_material = print_config.single_extruder_multi_material.value; + m_physical_extruder_count = print_config.nozzle_diameter.values.size(); + if (m_physical_extruder_count == 0) { + m_physical_extruder_count = 1; // 防止除零,默认为1 + BOOST_LOG_TRIVIAL(warning) << "GCodeWriter::apply_print_config: nozzle_diameter is empty, using default physical_extruder_count=1"; + } bool use_mach_limits = print_config.gcode_flavor.value == gcfMarlinLegacy || print_config.gcode_flavor.value == gcfMarlinFirmware || print_config.gcode_flavor.value == gcfKlipper || print_config.gcode_flavor.value == gcfRepRapFirmware; m_max_acceleration = std::lrint(use_mach_limits ? print_config.machine_max_acceleration_extruding.values.front() : 0); @@ -46,13 +51,11 @@ void GCodeWriter::apply_print_config(const PrintConfig &print_config) void GCodeWriter::set_extruders(std::vector extruder_ids) { BOOST_LOG_TRIVIAL(info) << "GCodeWriter::set_extruders: START - Creating Extruder objects for " << extruder_ids.size() << " extruders"; - BOOST_LOG_TRIVIAL(info) << "GCodeWriter::set_extruders: filament_extruder_map size=" << m_filament_extruder_map.size(); std::sort(extruder_ids.begin(), extruder_ids.end()); m_extruder = nullptr; // this points to object inside `m_extruders`, so should be cleared too m_extruders.clear(); m_extruders.reserve(extruder_ids.size()); - // SM Orca: 创建 Extruder 对象时传递物理挤出机ID for (unsigned int extruder_id : extruder_ids) { int physical_extruder_id = get_physical_extruder(extruder_id); BOOST_LOG_TRIVIAL(info) << "GCodeWriter::set_extruders: Creating Extruder - filament_id=" << extruder_id @@ -408,7 +411,6 @@ std::string GCodeWriter::set_input_shaping(char axis, float damp, float freq) co return gcode.str(); } - std::string GCodeWriter::reset_e(bool force) { if (FLAVOR_IS(gcfMach3) @@ -463,11 +465,8 @@ std::string GCodeWriter::toolchange_prefix() const std::string GCodeWriter::toolchange(unsigned int extruder_id) { - BOOST_LOG_TRIVIAL(info) << "SM Orca: GCodeWriter::toolchange(" << extruder_id << ") called"; - // SM Orca: 记录映射信息 int physical_extruder = get_physical_extruder(extruder_id); - BOOST_LOG_TRIVIAL(info) << "SM Orca: Toolchange - filament " << extruder_id << " maps to physical extruder " << physical_extruder << " (mapping table size: " << m_filament_extruder_map.size() << ")"; // set the new extruder auto it_extruder = Slic3r::lower_bound_by_predicate(m_extruders.begin(), m_extruders.end(), [extruder_id](const Extruder &e) { return e.id() < extruder_id; }); @@ -478,8 +477,6 @@ std::string GCodeWriter::toolchange(unsigned int extruder_id) // if we are running a single-extruder setup, just set the extruder and return nothing std::ostringstream gcode; if (this->multiple_extruders || (this->config.filament_diameter.values.size() > 1 && !is_bbl_printers())) { - // SM Orca: T命令使用耗材序号(extruder_id),物理换头由固件处理 - BOOST_LOG_TRIVIAL(info) << "SM Orca: Generating T command: T" << extruder_id; gcode << this->toolchange_prefix() << extruder_id; //BBS if (GCodeWriter::full_gcode_comment) @@ -487,7 +484,6 @@ std::string GCodeWriter::toolchange(unsigned int extruder_id) gcode << "\n"; gcode << this->reset_e(true); } else { - BOOST_LOG_TRIVIAL(info) << "SM Orca: Toolchange - Single extruder mode, no T command generated"; } return gcode.str(); } @@ -508,7 +504,6 @@ std::string GCodeWriter::set_speed(double F, const std::string &comment, const s std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &comment) { - // SM Orca: 诊断NaN输入 if (std::isnan(point(0)) || std::isinf(point(0)) || std::isnan(point(1)) || std::isinf(point(1))) { BOOST_LOG_TRIVIAL(error) << "SM Orca: travel_to_xy received NaN/inf point" << " extruder=" << (m_extruder ? m_extruder->id() : -1) @@ -736,7 +731,6 @@ bool GCodeWriter::will_move_z(double z) const std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std::string &comment, bool force_no_extrusion) { - // SM Orca: 诊断NaN输入 if (std::isnan(point(0)) || std::isinf(point(0)) || std::isnan(point(1)) || std::isinf(point(1))) { BOOST_LOG_TRIVIAL(error) << "SM Orca: extrude_to_xy received NaN/inf point" << " extruder=" << (m_extruder ? m_extruder->id() : -1) diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp index c34fde6112a..cdb5070e402 100644 --- a/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCodeWriter.hpp @@ -123,10 +123,24 @@ class GCodeWriter { // SM Orca: 设置耗材-挤出机映射 void set_filament_extruder_map(const std::unordered_map& map) { m_filament_extruder_map = map; } const std::unordered_map& get_filament_extruder_map() const { return m_filament_extruder_map; } - // SM Orca: 获取物理挤出机ID(如果没有映射,返回耗材ID本身) + // SM Orca: 获取物理挤出机ID + // 关键修复:当映射表为空时,使用模运算而不是直接返回耗材ID,避免越界 + // 例如:4个物理挤出机时,耗材0-7分别映射到0,1,2,3,0,1,2,3 int get_physical_extruder(int filament_idx) const { auto it = m_filament_extruder_map.find(filament_idx); - int physical_extruder_id = (it != m_filament_extruder_map.end()) ? it->second : filament_idx; + int physical_extruder_id; + if (it != m_filament_extruder_map.end()) { + // 从映射表获取 + physical_extruder_id = it->second; + } else { + // 映射表为空或没有该耗材的映射,使用默认模运算映射 + physical_extruder_id = filament_idx % m_physical_extruder_count; + } + // SM Orca: 日志 - 映射查询 + BOOST_LOG_TRIVIAL(info) << "GCodeWriter::get_physical_extruder: filament_id=" << filament_idx + << " -> physical_extruder_id=" << physical_extruder_id + << " (map_size=" << m_filament_extruder_map.size() << ", physical_count=" << m_physical_extruder_count << ")" + << (it != m_filament_extruder_map.end() ? " [from_map]" : " [default_mod]"); return physical_extruder_id; } @@ -183,6 +197,8 @@ class GCodeWriter { // SM Orca: 耗材到物理挤出机的映射表(filament_idx -> physical_extruder_id) std::unordered_map m_filament_extruder_map; + // SM Orca: 物理挤出机数量(用于默认模运算映射) + size_t m_physical_extruder_count = 1; // 默认为1,防止除零 enum class Acceleration { Travel, diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index b2e9d6065a3..b20f22a1333 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -493,7 +493,6 @@ std::vector Print::extruders(bool conside_custom_gcode) const return extruders; } -// SM Orca: Initialize filament-to-physical-extruder mapping // This must be called before the mapping is used (e.g., before export_gcode) void Print::initialize_filament_extruder_map() { @@ -503,16 +502,35 @@ void Print::initialize_filament_extruder_map() size_t physical_extruder_count = m_config.nozzle_diameter.values.size(); if (physical_extruder_count == 0) { - BOOST_LOG_TRIVIAL(warning) << "Print::initialize_filament_extruder_map: No physical extruders configured!"; + BOOST_LOG_TRIVIAL(error) << "Print::initialize_filament_extruder_map: ERROR - No physical extruders configured!"; return; } // Get all filament indices that will be used std::vector filament_extruders = this->extruders(); - BOOST_LOG_TRIVIAL(info) << "Print::initialize_filament_extruder_map: Initializing with " - << physical_extruder_count << " physical extruders and " - << filament_extruders.size() << " filaments to map"; + // IMPORTANT: Always create mappings for ALL configured filaments, not just those used by objects. + // This is critical because filament override parameters need to access the mapping for all filaments. + // For example, if a user has 8 filaments configured but only uses 4 in their model, + // the mapping table must still contain entries for all 8 filaments to correctly + // inherit parameters from the corresponding physical extruders. + // extruders() returns empty. In this case, use filament_diameter.size() to determine filament count. + // This ensures the mapping is created for all configured filaments, not just those used by objects. + if (filament_extruders.empty()) { + size_t filament_count = m_config.filament_diameter.size(); + for (size_t i = 0; i < filament_count; ++i) { + filament_extruders.push_back((unsigned int)i); + } + } else { + // Even if extruders() returns some values, we need to ensure ALL configured filaments are in the map. + // Add any missing filament indices that are configured but not used by objects. + size_t configured_filament_count = m_config.filament_diameter.size(); + for (size_t i = 0; i < configured_filament_count; ++i) { + if (std::find(filament_extruders.begin(), filament_extruders.end(), (unsigned int)i) == filament_extruders.end()) { + filament_extruders.push_back((unsigned int)i); + } + } + } // Create mapping: filament_id -> physical_extruder_id // Mapping formula: physical_extruder = filament_id % physical_extruder_count @@ -522,13 +540,7 @@ void Print::initialize_filament_extruder_map() for (unsigned int filament_idx : filament_extruders) { int physical_extruder = filament_idx % physical_extruder_count; m_filament_extruder_map[filament_idx] = physical_extruder; - - BOOST_LOG_TRIVIAL(info) << "Print::initialize_filament_extruder_map: filament " - << filament_idx << " -> physical_extruder " << physical_extruder; } - - BOOST_LOG_TRIVIAL(info) << "Print::initialize_filament_extruder_map: Map initialized with " - << m_filament_extruder_map.size() << " entries"; } unsigned int Print::num_object_instances() const @@ -543,7 +555,6 @@ double Print::max_allowed_layer_height() const { double nozzle_diameter_max = 0.; for (unsigned int extruder_id : this->extruders()) { - // SM Orca: 使用物理挤出机的喷嘴直径 int physical_extruder = get_physical_extruder(extruder_id); nozzle_diameter_max = std::max(nozzle_diameter_max, m_config.nozzle_diameter.get_at(physical_extruder)); } @@ -1224,7 +1235,6 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons* if (this->has_wipe_tower() && ! m_objects.empty()) { // Make sure all extruders use same diameter filament and have the same nozzle diameter // EPSILON comparison is used for nozzles and 10 % tolerance is used for filaments - // SM Orca: 使用物理挤出机的喷嘴直径 int first_physical = get_physical_extruder(extruders.front()); double first_nozzle_diam = m_config.nozzle_diameter.get_at(first_physical); double first_filament_diam = m_config.filament_diameter.get_at(extruders.front()); @@ -1336,7 +1346,6 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons* double min_nozzle_diameter = std::numeric_limits::max(); double max_nozzle_diameter = 0; for (unsigned int extruder_id : extruders) { - // SM Orca: 使用物理挤出机的喷嘴直径 int physical_extruder = get_physical_extruder(extruder_id); double dmr = m_config.nozzle_diameter.get_at(physical_extruder); min_nozzle_diameter = std::min(min_nozzle_diameter, dmr); @@ -1426,7 +1435,6 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons* size_t first_layer_extruder = object->config().raft_layers == 1 ? object->config().support_interface_filament-1 : object->config().support_filament-1; - // SM Orca: 使用物理挤出机的喷嘴直径 int physical_extruder = get_physical_extruder(first_layer_extruder); first_layer_min_nozzle_diameter = (first_layer_extruder == size_t(-1)) ? min_nozzle_diameter : @@ -1748,7 +1756,6 @@ Flow Print::brim_flow() const extruders and take the one with, say, the smallest index. The same logic should be applied to the code that selects the extruder during G-code generation as well. */ - // SM Orca: 使用物理挤出机的喷嘴直径 int filament_idx = m_print_regions.front()->config().wall_filament - 1; int physical_extruder = get_physical_extruder(filament_idx); return Flow::new_from_config_width( @@ -1770,7 +1777,6 @@ Flow Print::skirt_flow() const extruders and take the one with, say, the smallest index; The same logic should be applied to the code that selects the extruder during G-code generation as well. */ - // SM Orca: 使用物理挤出机的喷嘴直径 int filament_idx = m_objects.front()->config().support_filament - 1; int physical_extruder = get_physical_extruder(filament_idx); return Flow::new_from_config_width( @@ -1856,7 +1862,6 @@ void PrintObject::copy_layers_overhang_from_shared_object() } } - // BBS BoundingBox PrintObject::get_first_layer_bbox(float& a, float& layer_height, std::string& name) { @@ -2207,7 +2212,6 @@ void Print::process(long long *time_cost_with_cache, bool use_cache) append(m_first_layer_convex_hull.points, std::move(poly.points)); } - if (has_skirt() && ! draft_shield) { // In case that draft shield is NOT active, generate skirt now. // It will be placed around the brim, so brim has to be ready. @@ -2296,21 +2300,41 @@ std::string Print::export_gcode(const std::string& path_template, GCodeProcessor const Vec3d origin = this->get_plate_origin(); gcode.set_gcode_offset(origin(0), origin(1)); - // SM Orca: Initialize filament-to-extruder mapping before it's used this->initialize_filament_extruder_map(); - // SM Orca: 设置耗材-挤出机映射 - BOOST_LOG_TRIVIAL(info) << "SM Orca: Print::export_gcode - Setting filament_extruder_map to GCode, mapping size: " << m_filament_extruder_map.size(); for (const auto& pair : m_filament_extruder_map) { - BOOST_LOG_TRIVIAL(info) << " SM Orca: Print mapping: filament " << pair.first << " -> extruder " << pair.second; } gcode.set_filament_extruder_map(m_filament_extruder_map); - BOOST_LOG_TRIVIAL(info) << "SM Orca: Print::export_gcode - Mapping set to GCode object, calling do_export"; gcode.do_export(this, path.c_str(), result, thumbnail_cb); //BBS result->conflict_result = m_conflict_result; - return path.c_str(); + + // After G-code export is complete, finalize the output path by replacing placeholders with actual values + // This ensures that placeholders like {print_time} are replaced with calculated values + // Note: This is needed for direct export (not through BackgroundSlicingProcess) where + // finalize_output_path() might not be called automatically + std::string final_path = this->print_statistics().finalize_output_path(path); + + // Rename the file from the placeholder path to the finalized path + if (final_path != path) { + std::error_code ret = rename_file(path, final_path); + if (ret) { + BOOST_LOG_TRIVIAL(warning) << "Failed to rename G-code file from '" << path + << "' to '" << final_path << "': " << ret.message(); + // If rename fails, return the original path + return path; + } else { + BOOST_LOG_TRIVIAL(info) << "Renamed G-code file from '" << path + << "' to '" << final_path << "'"; + // Update result filename to reflect the new path + if (result) { + result->filename = final_path; + } + } + } + + return final_path; } void Print::_make_skirt() @@ -2396,7 +2420,6 @@ void Print::_make_skirt() extruders_e_per_mm.reserve(set_extruders.size()); for (auto &extruder_id : set_extruders) { extruders.push_back(extruder_id); - // SM Orca: 创建 Extruder 对象时传递物理挤出机ID int physical_extruder_id = get_physical_extruder(extruder_id); extruders_e_per_mm.push_back(Extruder((unsigned int)extruder_id, physical_extruder_id, &m_config, m_config.single_extruder_multi_material).e_per_mm(mm3_per_mm)); } @@ -2798,7 +2821,6 @@ void Print::_make_wipe_tower() // wipe_tower.set_zhop(); // Set the extruder & material properties at the wipe tower object. - // SM Orca: 传递物理挤出机ID以支持耗材-挤出机映射 for (size_t i = 0; i < number_of_extruders; ++i) { int physical_extruder = get_physical_extruder(i); wipe_tower.set_extruder(i, physical_extruder, m_config); @@ -2897,7 +2919,6 @@ void Print::_make_wipe_tower() // wipe_tower.set_zhop(); // Set the extruder & material properties at the wipe tower object. - // SM Orca: 传递物理挤出机ID以支持耗材-挤出机映射 for (size_t i = 0; i < number_of_extruders; ++i) { int physical_extruder = get_physical_extruder(i); wipe_tower.set_extruder(i, physical_extruder, m_config); @@ -2991,7 +3012,10 @@ std::string Print::output_filename(const std::string &filename_base) const { // Set the placeholders for the data know first after the G-code export is finished. // These values will be just propagated into the output file name. - DynamicConfig config = this->finished() ? this->print_statistics().config() : this->print_statistics().placeholders(); + // Use cached statistics if available (even if not finished) to avoid placeholders like {print_time} + const PrintStatistics& stats = this->print_statistics(); + bool has_valid_stats = stats.total_used_filament > 0 || !stats.estimated_normal_print_time.empty(); + DynamicConfig config = (this->finished() || has_valid_stats) ? stats.config() : stats.placeholders(); config.set_key_value("num_filaments", new ConfigOptionInt((int)m_config.nozzle_diameter.size())); config.set_key_value("num_extruders", new ConfigOptionInt((int) m_config.nozzle_diameter.size())); config.set_key_value("plate_name", new ConfigOptionString(get_plate_name())); @@ -3041,7 +3065,6 @@ void Print::export_gcode_from_previous_file(const std::string& file, GCodeProces GCodeProcessor::s_IsBBLPrinter = is_BBL_printer(); const Vec3d origin = this->get_plate_origin(); processor.set_xy_offset(origin(0), origin(1)); - // SM Orca: 设置耗材到物理挤出机的映射 processor.set_filament_extruder_map(m_filament_extruder_map); //processor.enable_producers(true); processor.process_file(file); @@ -3186,7 +3209,6 @@ const std::string PrintStatistics::TotalFilamentCostValueMask = "; total filamen const std::string PrintStatistics::TotalFilamentUsedWipeTower = "total filament used for wipe tower [g]"; const std::string PrintStatistics::TotalFilamentUsedWipeTowerValueMask = "; total filament used for wipe tower [g] = %.2lf\n"; - /*add json export/import related functions */ #define JSON_POLYGON_CONTOUR "contour" #define JSON_POLYGON_HOLES "holes" @@ -3196,7 +3218,6 @@ const std::string PrintStatistics::TotalFilamentUsedWipeTowerValueMask = "; tota #define JSON_OBJECT_NAME "name" #define JSON_IDENTIFY_ID "identify_id" - #define JSON_LAYERS "layers" #define JSON_SUPPORT_LAYERS "support_layers" #define JSON_TREE_SUPPORT_LAYERS "tree_support_layers" @@ -3233,8 +3254,6 @@ const std::string PrintStatistics::TotalFilamentUsedWipeTowerValueMask = "; tota #define JSON_LAYER_REGION_PERIMETERS "perimeters" #define JSON_LAYER_REGION_FILLS "fills" - - #define JSON_SURF_TYPE "surface_type" #define JSON_SURF_THICKNESS "thickness" #define JSON_SURF_THICKNESS_LAYER "thickness_layers" @@ -3274,7 +3293,6 @@ const std::string PrintStatistics::TotalFilamentUsedWipeTowerValueMask = "; tota #define JSON_EXTRUSION_NO_EXTRUSION "no_extrusion" #define JSON_EXTRUSION_LOOP_ROLE "loop_role" - static void to_json(json& j, const Points& p_s) { for (const Point& p : p_s) { @@ -3338,7 +3356,6 @@ static void to_json(json& j, const ArcSegment& arc_seg) { j[JSON_ARC_CENTER] = std::move(center_point_json); } - static void to_json(json& j, const Polyline& poly_line) { json points_json = json::array(), fittings_json = json::array(); points_json = poly_line.points; @@ -3612,7 +3629,6 @@ static void from_json(const json& j, ArcSegment& arc_seg) { return; } - static void from_json(const json& j, Polyline& poly_line) { poly_line.points = j[JSON_POINTS]; @@ -3823,7 +3839,6 @@ static void convert_layer_region_from_json(const json& j, LayerRegion& layer_reg return; } - void extract_layer(const json& layer_json, Layer& layer) { //slice_polygons int slice_polygons_count = layer_json[JSON_LAYER_SLICED_POLYGONS].size(); @@ -4194,7 +4209,6 @@ int Print::export_cached_data(const std::string& directory, bool with_space) return ret; } - int Print::load_cached_data(const std::string& directory) { int ret = 0; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 12e59ca5280..bb394865f76 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -894,10 +894,29 @@ class Print : public PrintBaseWithState // SM Orca: 获取耗材-挤出机映射表 const std::unordered_map& get_filament_extruder_map() const { return m_filament_extruder_map; } // SM Orca: 获取物理挤出机ID(根据耗材索引) + // 关键修复:当映射表为空时,使用模运算而不是直接返回耗材ID,避免越界 int get_physical_extruder(int filament_idx) const { auto it = m_filament_extruder_map.find(filament_idx); - int physical_extruder_id = (it != m_filament_extruder_map.end()) ? it->second : filament_idx; - + int physical_extruder_id; + if (it != m_filament_extruder_map.end()) { + // 从映射表获取 + physical_extruder_id = it->second; + } else { + // 映射表为空或没有该耗材的映射,使用默认模运算映射 + size_t physical_count = m_config.nozzle_diameter.values.size(); + if (physical_count == 0) { + // 防止除零,使用安全的默认值 + physical_extruder_id = 0; + BOOST_LOG_TRIVIAL(warning) << "Print::get_physical_extruder: nozzle_diameter is empty! Using default physical_extruder=0"; + } else { + physical_extruder_id = filament_idx % physical_count; + } + } + // SM Orca: 日志 - 映射查询 + BOOST_LOG_TRIVIAL(info) << "Print::get_physical_extruder: filament_id=" << filament_idx + << " -> physical_extruder_id=" << physical_extruder_id + << " (map_size=" << m_filament_extruder_map.size() << ")" + << (it != m_filament_extruder_map.end() ? " [from_map]" : " [default_mod]"); return physical_extruder_id; } // SM Orca: Initialize filament-to-physical-extruder mapping table diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index 41012785deb..6976a718cf7 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -3,6 +3,7 @@ #include #include +#include namespace Slic3r { @@ -216,50 +217,127 @@ static bool custom_per_printz_gcodes_tool_changes_differ(const std::vector& filament_extruder_map) { - if (!target->is_vector() || !extruder_defaults->is_vector()) - return; + if (!extruder_defaults->is_vector()) + return nullptr; - auto* target_vec = dynamic_cast(target); auto* extruder_vec = dynamic_cast(extruder_defaults); const ConfigOptionVectorBase* override_vec = filament_overrides ? dynamic_cast(filament_overrides) : nullptr; - if (!target_vec || !extruder_vec) - return; + if (!extruder_vec) + return nullptr; + + // Clone the extruder defaults to create the target + auto* target = extruder_defaults->clone(); + auto* target_vec = dynamic_cast(target); + if (!target_vec) { + delete target; + return nullptr; + } - size_t num_filaments = target_vec->size(); + // Resize target to filament_count + target_vec->resize(filament_count); - for (size_t filament_idx = 0; filament_idx < num_filaments; ++filament_idx) { - // Check if this filament has an override + for (size_t filament_idx = 0; filament_idx < filament_count; ++filament_idx) { bool has_override = false; if (override_vec && filament_idx < override_vec->size()) { - has_override = override_vec->nullable() ? !override_vec->is_nil(filament_idx) : true; + if (override_vec->nullable()) { + // Nullable type: use override only if not nil (checkbox is checked) + has_override = !override_vec->is_nil(filament_idx); + } else { + // Non-nullable type: check if the value differs from the default (printer config) + // Only if it's different do we consider it a user override + // Get the default value from the mapped physical extruder + auto map_it = filament_extruder_map.find(filament_idx); + int physical_extruder_idx; + if (map_it != filament_extruder_map.end()) { + physical_extruder_idx = map_it->second; + } else { + // Fallback: use modulo to map filament to physical extruder + // This handles edge cases where the map is incomplete or filament_idx is out of range + size_t physical_extruder_count = extruder_vec->size(); + if (physical_extruder_count == 0) { + // Should not happen, but safety check + physical_extruder_idx = 0; + } else { + physical_extruder_idx = (int)filament_idx % (int)physical_extruder_count; + } + } + + if (physical_extruder_idx < extruder_vec->size()) { + // Try different types: double, int, bool + auto* override_dbl = dynamic_cast*>(override_vec); + auto* extruder_dbl = dynamic_cast*>(extruder_vec); + if (override_dbl && extruder_dbl) { + // Compare with the value from the mapped physical extruder + double override_value = override_dbl->get_at(filament_idx); + double default_value = extruder_dbl->get_at(physical_extruder_idx); + // Use override only if value differs from default + has_override = (override_value != default_value); + + } else { + // Try int type + auto* override_int = dynamic_cast*>(override_vec); + auto* extruder_int = dynamic_cast*>(extruder_vec); + if (override_int && extruder_int) { + int override_value = override_int->get_at(filament_idx); + int default_value = extruder_int->get_at(physical_extruder_idx); + has_override = (override_value != default_value); + } else { + // Try bool type + auto* override_bool = dynamic_cast*>(override_vec); + auto* extruder_bool = dynamic_cast*>(extruder_vec); + if (override_bool && extruder_bool) { + unsigned char override_value = override_bool->get_at(filament_idx); + unsigned char default_value = extruder_bool->get_at(physical_extruder_idx); + has_override = (override_value != default_value); + } + } + } + } + } } - // If no override, inherit from the mapped physical extruder if (!has_override) { + // No override: inherit from the mapped physical extruder auto map_it = filament_extruder_map.find(filament_idx); - int physical_extruder_idx = (map_it != filament_extruder_map.end()) ? - map_it->second : filament_idx; + int physical_extruder_idx; + if (map_it != filament_extruder_map.end()) { + physical_extruder_idx = map_it->second; + } else { + // Fallback: use modulo to map filament to physical extruder + // This handles edge cases where the map is incomplete or filament_idx is out of range + size_t physical_extruder_count = extruder_vec->size(); + if (physical_extruder_count == 0) { + // Should not happen, but safety check + physical_extruder_idx = 0; + } else { + physical_extruder_idx = (int)filament_idx % (int)physical_extruder_count; + } + } - if (physical_extruder_idx < extruder_vec->size()) { + if (physical_extruder_idx < extruder_vec->size() && filament_idx < target_vec->size()) { target_vec->set_at(extruder_vec, filament_idx, physical_extruder_idx); } + } else if (override_vec && filament_idx < override_vec->size()) { + // Has override: use the value from filament config + target_vec->set_at(override_vec, filament_idx, filament_idx); } } + + return target; } // Collect changes to print config, account for overrides of extruder retract values by filament presets. //BBS: add plate index -// SM Orca: add filament_extruder_map for physical extruder mapping static t_config_option_keys print_config_diffs( const PrintConfig ¤t_config, const DynamicPrintConfig &new_full_config, @@ -284,45 +362,81 @@ static t_config_option_keys print_config_diffs( // const ConfigOption *opt_new_filament = std::binary_search(extruder_retract_keys.begin(), extruder_retract_keys.end(), opt_key) ? new_full_config.option(filament_prefix + opt_key) : nullptr; const ConfigOption* opt_new_filament = (iter == extruder_retract_keys.end()) ? nullptr : new_full_config.option(filament_prefix + opt_key); - if (opt_new_filament != nullptr && ! opt_new_filament->is_nil()) { + + bool is_extruder_retract_param = (iter != extruder_retract_keys.end()); + + // 1. This is an extruder retract parameter AND + // 2. Filament overrides exist AND + // 3. Filament-extruder map is not empty (meaning objects are loaded and mapping is initialized) + // When user edits printer config directly (without objects loaded or without filament overrides), + // we should treat it as a regular config change to ensure UI updates work correctly. + bool has_filament_overrides = (opt_new_filament != nullptr && !opt_new_filament->is_nil()); + bool needs_physical_mapping = is_extruder_retract_param && has_filament_overrides && !filament_extruder_map.empty(); + + if (needs_physical_mapping) { + + // This is safe because both opt_old and opt_new should have the same number of physical extruders + bool printer_config_changed = (*opt_old != *opt_new); + + auto* override_vec = dynamic_cast(opt_new_filament); + if (override_vec) { + BOOST_LOG_TRIVIAL(info) << "print_config_diffs: " << opt_key + << " - filament_override size=" << override_vec->size() + << ", nullable=" << override_vec->nullable(); + } + + // since we know has_filament_overrides is true at this point + if ((opt_key == "long_retractions_when_cut" || opt_key == "retraction_distances_when_cut") + && new_full_config.option("enable_long_retraction_when_cut")->value != LongRectrationLevel::EnableFilament) + continue; + + // - Check if printer config or filament override changed the effective value + // - Only add to print_diff (not filament_overrides) to avoid array size mismatch + // The actual filament->extruder mapping is applied later during config usage + // + // 关键修复:只要打印机配置变化了,就应该添加到 print_diff + // 这确保了用户在UI中修改打印机配置时,修改能被正确保存 + auto opt_copy = opt_new->clone(); + opt_copy->apply_override(opt_new_filament); + if (printer_config_changed || *opt_old != *opt_copy) { + print_diff.emplace_back(opt_key); + BOOST_LOG_TRIVIAL(info) << "print_config_diffs: " << opt_key + << " - adding to print_diff (printer_changed=" << (printer_config_changed ? "Y" : "N") + << ", effective_changed=" << (*opt_old != *opt_copy ? "Y" : "N") << ")"; + } + delete opt_copy; + } else if (opt_new_filament != nullptr && ! opt_new_filament->is_nil()) { // An extruder retract override is available at some of the filament presets. bool overriden = opt_new->overriden_by(opt_new_filament); - if (overriden || *opt_old != *opt_new) { + + bool printer_config_changed = (*opt_old != *opt_new); + + if (overriden || printer_config_changed) { auto opt_copy = opt_new->clone(); if (!((opt_key == "long_retractions_when_cut" || opt_key == "retraction_distances_when_cut") && new_full_config.option("enable_long_retraction_when_cut")->value != LongRectrationLevel::EnableFilament)) // ugly code, remove it later if firmware supports opt_copy->apply_override(opt_new_filament); - // SM Orca: Apply physical extruder mapping for slots without overrides - bool is_extruder_retract_param = (iter != extruder_retract_keys.end()); - if (is_extruder_retract_param && !filament_extruder_map.empty()) { - apply_physical_extruder_defaults(opt_copy, opt_new_filament, opt_new, filament_extruder_map); - } - bool changed = *opt_old != *opt_copy; if (changed) print_diff.emplace_back(opt_key); - if (changed || overriden) { + + // If user directly edited printer config, don't override it with filament values + if ((changed || overriden) && !printer_config_changed) { if ((opt_key == "long_retractions_when_cut" || opt_key == "retraction_distances_when_cut") && new_full_config.option("enable_long_retraction_when_cut")->value != LongRectrationLevel::EnableFilament) continue; // filament_overrides will be applied to the placeholder parser, which layers these parameters over full_print_config. filament_overrides.set_key_value(opt_key, opt_copy); - } else + } else if (changed && printer_config_changed) { + // Only add to print_diff, not to filament_overrides + // This preserves user's printer config edit + BOOST_LOG_TRIVIAL(info) << "print_config_diffs: " << opt_key + << " - printer config changed, not adding to filament_overrides to preserve user edit"; delete opt_copy; - } - } else if (iter != extruder_retract_keys.end() && !filament_extruder_map.empty()) { - // SM Orca: No filament override exists, but this is an extruder retract parameter - // Apply physical extruder mapping to inherit from correct extruders - auto opt_copy = opt_new->clone(); - apply_physical_extruder_defaults(opt_copy, nullptr, opt_new, filament_extruder_map); - - bool changed = *opt_old != *opt_copy; - if (changed) { - print_diff.emplace_back(opt_key); - filament_overrides.set_key_value(opt_key, opt_copy); - } else { - delete opt_copy; + } else { + delete opt_copy; + } } } else if (*opt_new != *opt_old) { //BBS: add plate_index logic for wipe_tower_x/wipe_tower_y @@ -1157,6 +1271,10 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ // BBS int used_filaments = this->extruders(true).size(); + // 此时 m_objects 和 m_config 是稳定的,可以安全地计算映射 + // 这确保了 print_config_diffs 中的参数继承机制能正常工作 + this->initialize_filament_extruder_map(); + //new_full_config.normalize_fdm(used_filaments); new_full_config.normalize_fdm_1(); t_config_option_keys changed_keys = new_full_config.normalize_fdm_2(objects().size(), used_filaments); @@ -1194,13 +1312,36 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ // Find modified keys of the various configs. Resolve overrides extruder retract values by filament profiles. DynamicPrintConfig filament_overrides; //BBS: add plate index - // SM Orca: Pass filament_extruder_map to apply physical extruder mapping t_config_option_keys print_diff = print_config_diffs(m_config, new_full_config, filament_overrides, this->m_plate_index, m_filament_extruder_map); t_config_option_keys full_config_diff = full_print_config_diffs(m_full_print_config, new_full_config, this->m_plate_index); // Collect changes to object and region configs. t_config_option_keys object_diff = m_default_object_config.diff(new_full_config); t_config_option_keys region_diff = m_default_region_config.diff(new_full_config); + // + // 问题根源: + // 1. 回抽参数(如retraction_length)既存在于打印机配置,也可能被耗材覆盖 + // 2. 当耗材-挤出机映射激活时(m_filament_extruder_map不为空), + // filament_overrides中的回抽值会覆盖用户对打印机配置的直接修改 + // + // 修复策略: + // - 当存在耗材-挤出机映射时(说明已加载项目),移除filament_overrides中的所有回抽参数 + // - 这样用户的打印机配置修改就不会被耗材覆盖值覆盖 + // - 保留filament_overrides中其他参数的功能不受影响 + // + // 注意:此修复确保用户对打印机挤出机回抽参数的直接编辑拥有最高优先级 + const std::vector &extruder_retract_keys = print_config_def.extruder_retract_keys(); + bool has_mapping = !m_filament_extruder_map.empty(); + if (has_mapping) { + // 当有映射时,移除所有回抽参数的耗材覆盖,让打印机配置生效 + for (const std::string &key : extruder_retract_keys) { + if (filament_overrides.erase(key)) { + BOOST_LOG_TRIVIAL(info) << "Print::apply - Clearing filament override for '" << key + << "' to allow printer config to take effect (filament-extruder mapping active)"; + } + } + } + // Do not use the ApplyStatus as we will use the max function when updating apply_status. unsigned int apply_status = APPLY_STATUS_UNCHANGED; auto update_apply_status = [&apply_status](bool invalidated) @@ -1614,6 +1755,10 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ m_full_print_config = std::move(new_full_config); } + // 在对象同步后,m_objects 可能已经被重建,需要重新计算映射以反映最新状态 + // 这确保了后续使用映射表时(如 export_gcode)能获得正确的映射关系 + this->initialize_filament_extruder_map(); + // All regions now have distinct settings. // Check whether applying the new region config defaults we would get different regions, // update regions or create regions from scratch. diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index 5b6b619e86e..0292e4524cc 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -136,12 +136,12 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt } case coPercents:{ ConfigOptionPercents* vec_new = new ConfigOptionPercents{ boost::any_cast(value) }; - config.option(opt_key)->set_at(vec_new, opt_index, opt_index); + config.option(opt_key)->set_at(vec_new, opt_index, 0); // SM Orca: Fix - use src_idx=0 for single-element vectors break; } case coFloats:{ ConfigOptionFloats* vec_new = new ConfigOptionFloats{ boost::any_cast(value) }; - config.option(opt_key)->set_at(vec_new, opt_index, opt_index); + config.option(opt_key)->set_at(vec_new, opt_index, 0); // SM Orca: Fix - use src_idx=0 for single-element vectors break; } case coString: diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index ba6ff14f378..76f021619a8 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -645,6 +645,10 @@ void ConfigOptionsGroup::on_change_OG(const t_config_option_key& opt_id, const b const std::string &opt_key = itOption.first; int opt_index = itOption.second; + // SM Orca: Debug logging - track parameter changes from UI + BOOST_LOG_TRIVIAL(error) << "ConfigOptionsGroup::on_change_OG: opt_id=" << opt_id + << ", opt_key=" << opt_key << ", opt_index=" << opt_index; + this->change_opt_value(opt_key, value, opt_index == -1 ? 0 : opt_index); } @@ -1227,18 +1231,12 @@ void ExtruderOptionsGroup::on_change_OG(const t_config_option_key& opt_id, const auto itOption = it->second; const std::string& opt_key = itOption.first; + int opt_index = itOption.second; - auto opt = m_config->option(opt_key); - const ConfigOptionVectorBase* opt_vec = dynamic_cast(opt); - if (opt_vec != nullptr) { - for (int opt_index = 0; opt_index < opt_vec->size(); opt_index++) { - this->change_opt_value(opt_key, value, opt_index); - } - } - else { - int opt_index = itOption.second; - this->change_opt_value(opt_key, value, opt_index == -1 ? 0 : opt_index); - } + // SM Orca: FIX - Only modify the specific extruder's value, not all extruders + // The original code iterated through all indices and set them to the same value, + // which caused all extruders to have identical values when editing one extruder + this->change_opt_value(opt_key, value, opt_index == -1 ? 0 : opt_index); } OptionsGroup::on_change_OG(opt_id, value); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 4fd9e4ce451..de4e163ff72 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3286,7 +3286,24 @@ void TabFilament::add_filament_overrides_page() else { const std::string printer_opt_key = opt_key.substr(strlen("filament_")); const auto printer_config = m_preset_bundle->printers.get_edited_preset().config; - const boost::any printer_config_value = optgroup_sh->get_config_value(printer_config, printer_opt_key, opt_index); + // SM Orca: Map filament slot to physical extruder index for inheritance + auto& filament_extruder_map = wxGetApp().app_config->get_filament_extruder_map_ref(); + // SM Orca: First calculate num_extruders to use modulo for default mapping + const ConfigOptionFloats* nozzle_diameter = printer_config.option("nozzle_diameter"); + int num_extruders = nozzle_diameter ? (int)nozzle_diameter->values.size() : 1; + // SM Orca: Use modulo arithmetic for default mapping when no explicit mapping exists + int physical_extruder_idx = opt_index % num_extruders; // default: filament N maps to extruder N % num_extruders + auto map_it = filament_extruder_map.find(opt_index); + if (map_it != filament_extruder_map.end()) { + physical_extruder_idx = map_it->second; + } + // SM Orca: Bounds check to prevent crash from misconfigured map + if (physical_extruder_idx < 0 || physical_extruder_idx >= num_extruders) { + BOOST_LOG_TRIVIAL(warning) << "Invalid physical_extruder_idx " << physical_extruder_idx + << " for filament slot " << opt_index << ", using default"; + physical_extruder_idx = std::clamp(physical_extruder_idx, 0, num_extruders - 1); + } + const boost::any printer_config_value = optgroup_sh->get_config_value(printer_config, printer_opt_key, physical_extruder_idx); field->update_na_value(printer_config_value); field->set_na_value(); } @@ -3301,7 +3318,7 @@ void TabFilament::add_filament_overrides_page() optgroup->append_line(line); }; - const int extruder_idx = 0; // #ys_FIXME + const int extruder_idx = (m_presets_choice && m_presets_choice->get_filament_idx() >= 0) ? m_presets_choice->get_filament_idx() : 0; // SM Orca: Get actual filament slot index for (const std::string opt_key : { "filament_retraction_length", "filament_z_hop", @@ -3367,7 +3384,7 @@ void TabFilament::update_filament_overrides_page(const DynamicPrintConfig* print // "filament_seam_gap" }; - const int extruder_idx = 0; // #ys_FIXME + const int extruder_idx = (m_presets_choice && m_presets_choice->get_filament_idx() >= 0) ? m_presets_choice->get_filament_idx() : 0; // SM Orca: Get actual filament slot index const bool have_retract_length = m_config->option("filament_retraction_length")->is_nil() || m_config->opt_float("filament_retraction_length", extruder_idx) > 0; @@ -3399,7 +3416,26 @@ void TabFilament::update_filament_overrides_page(const DynamicPrintConfig* print } else { if (!is_checked) { const std::string printer_opt_key = opt_key.substr(strlen("filament_")); - boost::any printer_config_value = optgroup->get_config_value(*printers_config, printer_opt_key, extruder_idx); + // SM Orca: Map filament slot to physical extruder index for inheritance + auto& filament_extruder_map = wxGetApp().app_config->get_filament_extruder_map_ref(); + // SM Orca: Determine extruder count first for proper modulo calculation + const ConfigOptionFloats* nozzle_diameter = printers_config->option("nozzle_diameter"); + int num_extruders = nozzle_diameter ? (int)nozzle_diameter->values.size() : 1; + int physical_extruder_idx = extruder_idx; // default: filament N uses extruder N + auto map_it = filament_extruder_map.find(extruder_idx); + if (map_it != filament_extruder_map.end()) { + physical_extruder_idx = map_it->second; + } else { + // SM Orca: Use modulo arithmetic when map entry doesn't exist + physical_extruder_idx = extruder_idx % num_extruders; + } + // SM Orca: Bounds check to prevent crash from misconfigured map + if (physical_extruder_idx < 0 || physical_extruder_idx >= num_extruders) { + BOOST_LOG_TRIVIAL(warning) << "Invalid physical_extruder_idx " << physical_extruder_idx + << " for filament slot " << extruder_idx << ", using default"; + physical_extruder_idx = std::clamp(physical_extruder_idx, 0, num_extruders - 1); + } + boost::any printer_config_value = optgroup->get_config_value(*printers_config, printer_opt_key, physical_extruder_idx); field->update_na_value(printer_config_value); field->set_value(printer_config_value, false); } diff --git "a/\345\217\202\346\225\260\350\256\277\351\227\256\344\277\256\345\244\215\350\241\250.md" "b/\345\217\202\346\225\260\350\256\277\351\227\256\344\277\256\345\244\215\350\241\250.md" new file mode 100644 index 00000000000..ee0e273b384 --- /dev/null +++ "b/\345\217\202\346\225\260\350\256\277\351\227\256\344\277\256\345\244\215\350\241\250.md" @@ -0,0 +1,200 @@ +# 参数访问修复表 + +## 修复概述 + +**问题**: 在8耗材4挤出机的配置下,使用耗材ID(0-7)访问只有4个元素的物理挤出机参数数组,导致数组越界。 + +**解决**: 通过 `get_physical_extruder(filament_id)` 获取正确的物理挤出机ID进行访问。 + +**修复统计**: +- 修改文件: 9个 +- 修改代码行: 约40处 +- 修复参数: 22个 + +--- + +## GCode.cpp 中的修复 + +| 代码位置 | 参数名 | 原来索引 | 现在索引 | 有无改动 | 改动原因 | +|---------|-------|---------|---------|---------|---------| +| 1051-1054 | EXTRUDER_CONFIG宏定义 | `m_writer.extruder()->id()` (filament_id) | `m_writer.extruder()->id()` (filament_id) | ❌ 无 | 用于耗材参数,保持不变 | +| 1053-1054 | **PHYSICAL_EXTRUDER_CONFIG宏定义** | - | `m_writer.get_physical_extruder(m_writer.extruder()->id())` | ✅ **新增** | **新增宏用于访问物理挤出机参数** | +| 2857-2858 | nozzle_diameter (spiral_vase) | `extruder()->id()` (filament_id) | `get_physical_extruder(extruder()->id())` | ✅ **已改** | nozzle_diameter是物理挤出机参数,只有4个元素,filament 5-7会越界 | +| 2958-2959 | nozzle_diameter (spiral_vase) | `extruder()->id()` (filament_id) | `get_physical_extruder(extruder()->id())` | ✅ **已改** | 同上 | +| 4771-4772 | nozzle_diameter (seam_slope) | `extruder()->id()` (filament_id) | `get_physical_extruder(extruder()->id())` | ✅ **已改** | 同上 | +| 4995-4996 | nozzle_diameter (hide_seam) | `extruder()->id()` (filament_id) | `get_physical_extruder(extruder()->id())` | ✅ **已改** | 同上 | +| 5589-5590 | enable_pressure_advance | `extruder()->id()` (filament_id) | `get_physical_extruder(extruder()->id())` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 5589-5590 | adaptive_pressure_advance | `extruder()->id()` (filament_id) | `get_physical_extruder(extruder()->id())` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 5785-5789 | enable_pressure_advance | `extruder()->id()` (filament_id) | `get_physical_extruder(extruder()->id())` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 5785-5789 | adaptive_pressure_advance | `extruder()->id()` (filament_id) | `get_physical_extruder(extruder()->id())` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 5785-5789 | adaptive_pressure_advance_overhangs | `extruder()->id()` (filament_id) | `get_physical_extruder(extruder()->id())` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 5995-5999 | enable_pressure_advance | `extruder()->id()` (filament_id) | `get_physical_extruder(extruder()->id())` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 5995-5999 | adaptive_pressure_advance | `extruder()->id()` (filament_id) | `get_physical_extruder(extruder()->id())` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 5995-5999 | adaptive_pressure_advance_overhangs | `extruder()->id()` (filament_id) | `get_physical_extruder(extruder()->id())` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 5702-5703 | overhang_fan_threshold | `extruder()->id()` (filament_id) | `get_physical_extruder(extruder()->id())` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 5702-5703 | enable_overhang_bridge_fan | `extruder()->id()` (filament_id) | `get_physical_extruder(extruder()->id())` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 5769-5771 | support_material_interface_fan_speed | `extruder()->id()` (filament_id) | `get_physical_extruder(extruder()->id())` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 5769-5771 | ironing_fan_speed | `extruder()->id()` (filament_id) | `get_physical_extruder(extruder()->id())` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 6510-6515 | enable_pressure_advance (set_extruder) | `extruder_id` (filament_id) | `physical_extruder_id` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 6510-6515 | pressure_advance (set_extruder) | `extruder_id` (filament_id) | `physical_extruder_id` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 6760-6763 | enable_pressure_advance (set_extruder) | `extruder_id` (filament_id) | `physical_extruder_id` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 6760-6763 | pressure_advance (set_extruder) | `extruder_id` (filament_id) | `physical_extruder_id` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 5437, 5475, 5491, 5517, 5519 | filament_max_volumetric_speed | `extruder()->id()` (filament_id) | `extruder()->id()` (filament_id) | ❌ 无 | 耗材参数(名称带filament_),使用耗材索引正确 | +| 4685-4688 | retract_when_changing_layer | `extruder()->id()` (filament_id) | `extruder()->id()` (filament_id) | ❌ 无 | 有耗材覆盖(filament_retract_when_changing_layer) | +| 4685-4688 | z_hop_types | `extruder()->id()` (filament_id) | `extruder()->id()` (filament_id) | ❌ 无 | 有耗材覆盖(filament_z_hop_types) | +| 6286 | retraction_minimum_travel | `extruder()->id()` (filament_id) | `extruder()->id()` (filament_id) | ❌ 无 | 有耗材覆盖 | +| 6365-6369 | z_hop_types | `extruder()->id()` (filament_id) | `extruder()->id()` (filament_id) | ❌ 无 | 有耗材覆盖 | +| 6399-6403 | z_hop_types | `extruder()->id()` (filament_id) | `extruder()->id()` (filament_id) | ❌ 无 | 有耗材覆盖 | +| 6416 | wipe | `extruder()->id()` (filament_id) | `extruder()->id()` (filament_id) | ❌ 无 | 有耗材覆盖 | +| 6416 | wipe_distance | `extruder()->id()` (filament_id) | `extruder()->id()` (filament_id) | ❌ 无 | 有耗材覆盖 | +| 6440 | retract_lift_enforce | `extruder()->id()` (filament_id) | `extruder()->id()` (filament_id) | ❌ 无 | 有耗材覆盖 | + +--- + +## CoolingBuffer.cpp 中的修复 + +| 代码位置 | 参数名 | 原来索引 | 现在索引 | 有无改动 | 改动原因 | +|---------|-------|---------|---------|---------|---------| +| 734-735 | EXTRUDER_CONFIG宏定义 | `m_current_extruder` (filament_id) | `get_physical_extruder(m_current_extruder)` | ✅ **已改** | **所有风扇参数都是物理挤出机参数** | +| 736 | fan_min_speed | `m_current_extruder` | `get_physical_extruder(m_current_extruder)` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 737 | reduce_fan_stop_start_freq | `m_current_extruder` | `get_physical_extruder(m_current_extruder)` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 739 | additional_cooling_fan_speed | `m_current_extruder` | `get_physical_extruder(m_current_extruder)` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 740 | close_fan_the_first_x_layers | `m_current_extruder` | `get_physical_extruder(m_current_extruder)` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 742 | full_fan_speed_layer | `m_current_extruder` | `get_physical_extruder(m_current_extruder)` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 743 | support_material_interface_fan_speed | `m_current_extruder` | `get_physical_extruder(m_current_extruder)` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 751 | fan_max_speed | `m_current_extruder` | `get_physical_extruder(m_current_extruder)` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 752 | slow_down_layer_time | `m_current_extruder` | `get_physical_extruder(m_current_extruder)` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 753 | fan_cooling_layer_time | `m_current_extruder` | `get_physical_extruder(m_current_extruder)` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 766 | overhang_fan_speed | `m_current_extruder` | `get_physical_extruder(m_current_extruder)` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 773 | support_material_interface_fan_speed | `m_current_extruder` | `get_physical_extruder(m_current_extruder)` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 779 | internal_bridge_fan_speed | `m_current_extruder` | `get_physical_extruder(m_current_extruder)` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 788 | ironing_fan_speed | `m_current_extruder` | `get_physical_extruder(m_current_extruder)` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 344-350 | slow_down_for_layer_cooling | `extruder_id` | `get_physical_extruder(extruder_id)` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 347 | slow_down_layer_time | `extruder_id` | `get_physical_extruder(extruder_id)` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 348 | slow_down_min_speed | `extruder_id` | `get_physical_extruder(extruder_id)` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | +| 350 | dont_slow_down_outer_wall | `extruder_id` | `get_physical_extruder(extruder_id)` | ✅ **已改** | 无耗材覆盖,物理挤出机参数 | + +--- + +## AvoidCrossingPerimeters.cpp 中的修复 + +| 代码位置 | 参数名 | 原来索引 | 现在索引 | 有无改动 | 改动原因 | +|---------|-------|---------|---------|---------|---------| +| 486-488 | nozzle_diameter | `extruder_id` (filament_id) | `get_physical_extruder(extruder_id)` | ✅ **已改** | nozzle_diameter是物理挤出机参数,只有4个元素 | + +--- + +## Extruder.cpp 中的修复(之前已完成) + +| 代码位置 | 参数名 | 原来索引 | 现在索引 | 有无改动 | 改动原因 | +|---------|-------|---------|---------|---------|---------| +| 163 | retract_before_wipe | `m_physical_extruder_id` | `m_id` | ✅ **已改** | 有耗材覆盖,继承后存储在耗材位置 | +| 168 | retraction_length | `m_physical_extruder_id` | `m_id` | ✅ **已改** | 有耗材覆盖 | +| 173 | z_hop | `m_physical_extruder_id` | `m_id` | ✅ **已改** | 有耗材覆盖 | +| 178 | retraction_speed | `m_physical_extruder_id` | `m_id` | ✅ **已改** | 有耗材覆盖 | +| 188 | deretraction_speed | `m_physical_extruder_id` | `m_id` | ✅ **已改** | 有耗材覆盖 | +| 194 | retract_restart_extra | `m_physical_extruder_id` | `m_id` | ✅ **已改** | 有耗材覆盖 | +| 199 | retract_length_toolchange | `m_physical_extruder_id` | `m_id` | ✅ **已改** | 有耗材覆盖 | +| 204 | retract_restart_extra_toolchange | `m_physical_extruder_id` | `m_id` | ✅ **已改** | 有耗材覆盖 | +| 209 | travel_slope | `m_physical_extruder_id` | `m_id` | ✅ **已改** | 有耗材覆盖 | + +--- + +## GCode.cpp 中未改动(正确的耗材参数) + +| 代码位置 | 参数名 | 使用索引 | 是否改动 | 原因 | +|---------|-------|---------|---------|------| +| 262, 274 | filament_idle_temp | `extruder_id` | ❌ 无 | 耗材参数(名称带filament_) | +| 333 | wipe_distance | `extruder_id` | ❌ 无 | 有耗材覆盖 | +| 339 | retraction_speed | `extruder_id` | ❌ 无 | 有耗材覆盖 | +| 2731 | filament_end_gcode | `extruder_id` | ❌ 无 | 耗材参数 | +| 3979, 4044, 4074 | filament_soluble | `extruder_id` | ❌ 无 | 耗材参数 | +| 6494 | filament_start_gcode | `extruder_id` | ❌ 无 | 耗材参数 | +| 6503 | retraction_distances_when_cut | `extruder_id` | ❌ 无 | 有耗材覆盖 | +| 6504 | long_retractions_when_cut | `extruder_id` | ❌ 无 | 有耗材覆盖 | +| 6581 | retraction_length | `extruder_id` | ❌ 无 | 有耗材覆盖 | +| 6582 | retract_length_toolchange | `extruder_id` | ❌ 无 | 有耗材覆盖 | +| 6584, 6587, 6734-6735 | nozzle_temperature | `extruder_id` | ❌ 无 | 耗材参数(在耗材配置文件中定义) | +| 6593 | filament_diameter | `extruder_id` | ❌ 无 | 耗材参数 | +| 6632 | filament_max_volumetric_speed | `extruder_id` | ❌ 无 | 耗材参数 | +| 6741-6742 | retraction_distances_when_cut | `extruder_id` | ❌ 无 | 有耗材覆盖 | + +--- + +## 修复的物理挤出机参数列表(共22个) + +| 参数 | 说明 | 数组大小 | 修复文件 | +|------|------|----------|----------| +| nozzle_diameter | 喷嘴直径 | physical_extruder_count (4) | GCode.cpp, AvoidCrossingPerimeters.cpp | +| enable_pressure_advance | 启用压力提前 | physical_extruder_count (4) | GCode.cpp | +| adaptive_pressure_advance | 自适应压力提前 | physical_extruder_count (4) | GCode.cpp | +| adaptive_pressure_advance_overhangs | 悬空自适应PA | physical_extruder_count (4) | GCode.cpp | +| pressure_advance | 压力提前值 | physical_extruder_count (4) | GCode.cpp | +| overhang_fan_threshold | 悬空风扇阈值 | physical_extruder_count (4) | GCode.cpp | +| enable_overhang_bridge_fan | 悬空桥风扇开关 | physical_extruder_count (4) | GCode.cpp | +| overhang_fan_speed | 悬空风扇速度 | physical_extruder_count (4) | CoolingBuffer.cpp | +| support_material_interface_fan_speed | 支持界面风扇速度 | physical_extruder_count (4) | GCode.cpp, CoolingBuffer.cpp | +| ironing_fan_speed | 熨烫风扇速度 | physical_extruder_count (4) | GCode.cpp, CoolingBuffer.cpp | +| internal_bridge_fan_speed | 内部桥风扇速度 | physical_extruder_count (4) | CoolingBuffer.cpp | +| fan_min_speed | 最小风扇速度 | physical_extruder_count (4) | CoolingBuffer.cpp | +| fan_max_speed | 最大风扇速度 | physical_extruder_count (4) | CoolingBuffer.cpp | +| slow_down_layer_time | 减速层时间 | physical_extruder_count (4) | CoolingBuffer.cpp | +| slow_down_for_layer_cooling | 层冷却减速开关 | physical_extruder_count (4) | CoolingBuffer.cpp | +| slow_down_min_speed | 最小减速速度 | physical_extruder_count (4) | CoolingBuffer.cpp | +| fan_cooling_layer_time | 风扇冷却层时间 | physical_extruder_count (4) | CoolingBuffer.cpp | +| close_fan_the_first_x_layers | 前N层关闭风扇 | physical_extruder_count (4) | CoolingBuffer.cpp | +| full_fan_speed_layer | 全速风扇层 | physical_extruder_count (4) | CoolingBuffer.cpp | +| additional_cooling_fan_speed | 额外冷却风扇速度 | physical_extruder_count (4) | CoolingBuffer.cpp | +| reduce_fan_stop_start_frequency | 减少风扇启停频率 | physical_extruder_count (4) | CoolingBuffer.cpp | +| dont_slow_down_outer_wall | 外墙不减速 | physical_extruder_count (4) | CoolingBuffer.cpp | + +--- + +## 修复统计汇总 + +| 类别 | 数量 | +|------|------| +| **需要改动的参数** | **22个** | +| **修改的代码行数** | **约40处** | +| **新增的宏** | **1个 (PHYSICAL_EXTRUDER_CONFIG)** | +| **新增的方法** | **2个 (CoolingBuffer)** | + +--- + +## 核心问题与解决方案 + +### 问题根源 +``` +8耗材 (索引0-7) → 访问4元素数组 → 索引5-7时数组越界 ❌ +``` + +### 解决方案 +``` +耗材ID → 映射表 → 物理挤出机ID (0-3) → 访问4元素数组 ✅ +``` + +### 关键改动 +1. **新增 `PHYSICAL_EXTRUDER_CONFIG` 宏**:专门用于访问物理挤出机参数 + ```cpp + #define PHYSICAL_EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.get_physical_extruder(m_writer.extruder()->id())) + ``` + +2. **CoolingBuffer 添加映射表和方法**: + - `m_filament_extruder_map`:存储耗材到物理挤出机的映射 + - `get_physical_extruder(filament_idx)`:获取物理挤出机ID + - `set_filament_extruder_map(map)`:设置映射表 + +3. **所有物理挤出机参数改用 `physical_extruder_id`**: + - 原来使用 `filament_id` (0-7) + - 现在使用 `physical_extruder_id` (0-3) + - 确保不会数组越界 + +--- + +## 验证检查点 + +- ✅ GCode.cpp 中的 PHYSICAL_EXTRUDER_CONFIG 宏正确使用 +- ✅ CoolingBuffer 中的映射表正确设置 +- ✅ 所有物理挤出机参数使用 physical_extruder_id 访问 +- ✅ 所有耗材参数使用 filament_id 访问 +- ✅ 大于4号的耗材能够正确切片,不再越界 diff --git "a/\350\200\227\346\235\220\347\273\247\346\211\277\344\277\256\345\244\215\346\200\273\347\273\223.md" "b/\350\200\227\346\235\220\347\273\247\346\211\277\344\277\256\345\244\215\346\200\273\347\273\223.md" new file mode 100644 index 00000000000..ce85da6e13c --- /dev/null +++ "b/\350\200\227\346\235\220\347\273\247\346\211\277\344\277\256\345\244\215\346\200\273\347\273\223.md" @@ -0,0 +1,127 @@ +# 耗材继承修复总结 + +**状态**: ✅ 已完成并验证工作 + +## 问题描述 + +### 问题1: 打印机配置参数无法修改 +修改挤出机2的回抽长度从1.5到1.6后,值会闪回1.5 + +### 问题2: 所有耗材都从挤出机1继承参数 +期望: +- 耗材1 → 继承挤出机1 +- 耗材2 → 继承挤出机2 +- 耗材3 → 继承挤出机3 +- 耗材4 → 继承挤出机4 +- 耗材5 → 继承挤出机1(通过 filament_extruder_map 映射) +- 耗材6 → 继承挤出机2(通过 filament_extruder_map 映射) + +实际:所有耗材都继承挤出机1的参数 + +## 修复方案 + +### 修复1: GUI.cpp (打印机配置修改) + +**文件**: `src/slic3r/GUI/GUI.cpp` +**位置**: 第139行、第144行 + +```cpp +// 之前(错误): +config.option(opt_key)->set_at(vec_new, opt_index, opt_index); +config.option(opt_key)->set_at(vec_new, opt_index, opt_index); + +// 之后(正确): +config.option(opt_key)->set_at(vec_new, opt_index, 0); // SM Orca: Fix +config.option(opt_key)->set_at(vec_new, opt_index, 0); // SM Orca: Fix +``` + +**原因**: 用户输入单个值时创建的是1元素向量 `{value}`,原代码试图访问 `vec_new[opt_index]` 会越界 + +### 修复2: Tab.cpp (耗材继承) + +**文件**: `src/slic3r/GUI/Tab.cpp` + +#### 更改2.1: 获取实际耗材槽索引 +**位置**: 第3317行、第3383行 + +```cpp +// 之前(硬编码): +const int extruder_idx = 0; // #ys_FIXME + +// 之后(动态获取): +const int extruder_idx = (m_presets_choice && m_presets_choice->get_filament_idx() >= 0) + ? m_presets_choice->get_filament_idx() : 0; +``` + +#### 更改2.2: 添加耗材→挤出机映射逻辑 +**位置**: 第3289-3303行、第3418-3435行 + +```cpp +// SM Orca: Map filament slot to physical extruder index for inheritance +auto& filament_extruder_map = wxGetApp().app_config->get_filament_extruder_map_ref(); +int physical_extruder_idx = opt_index; // default: filament N uses extruder N +auto map_it = filament_extruder_map.find(opt_index); +if (map_it != filament_extruder_map.end()) { + physical_extruder_idx = map_it->second; +} + +// SM Orca: Bounds check to prevent crash from misconfigured map +const ConfigOptionFloats* nozzle_diameter = printer_config.option("nozzle_diameter"); +int num_extruders = nozzle_diameter ? (int)nozzle_diameter->values.size() : 1; +if (physical_extruder_idx < 0 || physical_extruder_idx >= num_extruders) { + BOOST_LOG_TRIVIAL(warning) << "Invalid physical_extruder_idx " << physical_extruder_idx + << " for filament slot " << opt_index << ", using default"; + physical_extruder_idx = std::clamp(physical_extruder_idx, 0, num_extruders - 1); +} + +const boost::any printer_config_value = optgroup_sh->get_config_value(printer_config, printer_opt_key, physical_extruder_idx); +``` + +## 架构验证 + +由 Architect (Opus) 验证: +- ✅ 架构正确 +- ✅ 使用现有 filament_extruder_map 机制(只读,不修改) +- ✅ 逻辑流程正确 +- ✅ 边界检查已添加 + +## 风险审视 + +### 低风险 +1. **只读映射**: 代码只读取 `filament_extruder_map`,不修改映射机制本身 +2. **边界检查**: 添加了边界检查,防止配置错误导致崩溃 +3. **默认回退**: 如果映射不存在或索引无效,回退到 1:1 映射 + +### 需要注意的点 +1. **filament_extruder_map 同步**: + - GUI 层使用 `AppConfig::filament_extruder_map` + - Print 层使用 `Print::m_filament_extruder_map` + - 需确保这两个映射保持同步 + +2. **多线程访问**: + - `get_filament_extruder_map_ref()` 返回非const引用 + - 如果GUI和Print线程同时访问可能存在竞态 + - 当前 AppConfig 似乎是主线程专用 + +## 保存当前状态 + +创建备份命令: +```bash +# 保存当前更改到stash +git stash save "耗材继承修复 - 工作版本" + +# 或创建补丁文件 +git diff src/slic3r/GUI/Tab.cpp > filament_inheritance_fix.patch +git diff src/slic3r/GUI/GUI.cpp > printer_config_fix.patch +``` + +## 修改的文件列表 + +1. `src/slic3r/GUI/GUI.cpp` - 打印机配置修改修复 +2. `src/slic3r/GUI/Tab.cpp` - 耗材继承修复(4处更改) +3. `src/libslic3r/Config.hpp` - 添加了 set_at 重载方法 +4. `src/libslic3r/PrintApply.cpp` - 相关调整 +5. 其他GCode相关文件的适配性修改 + +**总计**: 17个文件,479行新增,135行删除 +