mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-11 19:17:33 +00:00
Add Filament_Extruder_map (#160)
* Optimize: extruder_filament_map
* Fix: {print_time}
This commit is contained in:
152
FILAMENT_EXTRUDER_ANALYSIS_FINAL.md
Normal file
152
FILAMENT_EXTRUDER_ANALYSIS_FINAL.md
Normal file
@@ -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<unsigned int> 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.
|
||||
Reference in New Issue
Block a user