Merge branch '2.3.0' into dev_2.3.0_alves

This commit is contained in:
alves
2026-02-26 16:44:09 +08:00
21 changed files with 1214 additions and 339 deletions
+2
View File
@@ -42,3 +42,5 @@ deps_src/build/
.claude/ .claude/
.hive-mind/ .hive-mind/
nul nul
.omc/
.claude-flow/
+152
View 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.
+261
View File
@@ -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
+50 -2
View File
@@ -10,6 +10,7 @@
#include <iostream> #include <iostream>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <sstream>
#include <vector> #include <vector>
#include "libslic3r.h" #include "libslic3r.h"
#include "clonable_ptr.hpp" #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.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 == 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 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 { namespace std {
@@ -415,22 +422,63 @@ public:
// This function is useful to split values from multiple extrder / filament settings into separate configurations. // 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 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. // It is expected that the vector value has at least one value, which is the default, if not overwritten.
assert(! this->values.empty()); assert(! this->values.empty());
if (this->values.size() <= i) { if (this->values.size() <= i) {
// Resize this vector, fill in the new vector fields with the copy of the first field. // Resize this vector, fill in the new vector fields with the copy of the first field.
T v = this->values.front(); T v = this->values.front();
this->values.resize(i + 1, v); this->values.resize(i + 1, v);
BOOST_LOG_TRIVIAL(error) << "ConfigOptionVector::set_at: resized to " << this->values.size();
} }
if (rhs->type() == this->type()) { if (rhs->type() == this->type()) {
// Assign the first value of the rhs vector. // Assign the first value of the rhs vector.
auto other = static_cast<const ConfigOptionVector<T>*>(rhs); auto other = static_cast<const ConfigOptionVector<T>*>(rhs);
if (other->values.empty()) if (other->values.empty())
throw ConfigurationError("ConfigOptionVector::set_at(): Assigning from an empty vector"); 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); 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<const ConfigOptionSingle<T>*>(rhs)->value; this->values[i] = static_cast<const ConfigOptionSingle<T>*>(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"); throw ConfigurationError("ConfigOptionVector::set_at(): Assigning an incompatible type");
} }
+49 -104
View File
@@ -225,7 +225,6 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
gcode += '\n'; gcode += '\n';
} }
// Return true if tch_prefix is found in custom_gcode // 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) 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<Vec2d> get_path_of_change_filament(const Print& print)
if(_wipe_speed < 10) if(_wipe_speed < 10)
_wipe_speed = 10; _wipe_speed = 10;
//SoftFever: allow 100% retract before wipe //SoftFever: allow 100% retract before wipe
if (length >= 0) if (length >= 0)
{ {
@@ -523,23 +521,19 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
{ {
GCodeWriter &gcode_writer = gcodegen.m_writer; GCodeWriter &gcode_writer = gcodegen.m_writer;
FullPrintConfig &full_config = gcodegen.m_config; FullPrintConfig &full_config = gcodegen.m_config;
// SM Orca: 计算物理挤出机ID用于参数查询 float old_retract_length = (gcode_writer.extruder() != nullptr && previous_extruder_id >= 0) ?
int previous_physical_extruder = (previous_extruder_id >= 0) ? gcode_writer.get_physical_extruder(previous_extruder_id) : -1; full_config.retraction_length.get_at(previous_extruder_id) : 0;
int new_physical_extruder = gcode_writer.get_physical_extruder(new_extruder_id); float new_retract_length = full_config.retraction_length.get_at(new_extruder_id);
// SM Orca: 回抽和温度是挤出机属性,使用 physical_extruder float old_retract_length_toolchange = (gcode_writer.extruder() != nullptr && previous_extruder_id >= 0) ?
float old_retract_length = (gcode_writer.extruder() != nullptr && previous_physical_extruder >= 0) ? full_config.retract_length_toolchange.get_at(previous_extruder_id) : 0;
full_config.retraction_length.get_at(previous_physical_extruder) : 0; float new_retract_length_toolchange = full_config.retract_length_toolchange.get_at(new_extruder_id);
float new_retract_length = full_config.retraction_length.get_at(new_physical_extruder); int old_filament_temp = (gcode_writer.extruder() != nullptr && previous_extruder_id >= 0) ?
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) ?
(gcodegen.on_first_layer() ? (gcodegen.on_first_layer() ?
full_config.nozzle_temperature_initial_layer.get_at(previous_physical_extruder) : full_config.nozzle_temperature_initial_layer.get_at(previous_extruder_id) :
full_config.nozzle_temperature.get_at(previous_physical_extruder)) : full_config.nozzle_temperature.get_at(previous_extruder_id)) :
210; 210;
int new_filament_temp = gcodegen.on_first_layer() ? full_config.nozzle_temperature_initial_layer.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_physical_extruder); full_config.nozzle_temperature.get_at(new_extruder_id);
Vec3d nozzle_pos = gcode_writer.get_position(); Vec3d nozzle_pos = gcode_writer.get_position();
float purge_volume = tcr.purge_volume < EPSILON ? 0 : std::max(tcr.purge_volume, g_min_purge_volume); float purge_volume = tcr.purge_volume < EPSILON ? 0 : std::max(tcr.purge_volume, g_min_purge_volume);
@@ -740,7 +734,6 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
double current_z = gcodegen.writer().get_position().z(); double current_z = gcodegen.writer().get_position().z();
if (z == -1.) // in case no specific z was provided, print at current_z pos if (z == -1.) // in case no specific z was provided, print at current_z pos
z = current_z; z = current_z;
@@ -847,7 +840,6 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
if (m_single_extruder_multi_material) if (m_single_extruder_multi_material)
extruder_offset = m_extruder_offsets[0].cast<float>(); extruder_offset = m_extruder_offsets[0].cast<float>();
else { else {
// SM Orca: 使用物理挤出机ID,不是耗材ID
int physical_extruder = gcodegen.writer().get_physical_extruder(tcr.initial_tool); int physical_extruder = gcodegen.writer().get_physical_extruder(tcr.initial_tool);
extruder_offset = m_extruder_offsets[physical_extruder].cast<float>(); extruder_offset = m_extruder_offsets[physical_extruder].cast<float>();
} }
@@ -910,7 +902,6 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
if (line == "[change_filament_gcode]") { if (line == "[change_filament_gcode]") {
// BBS // BBS
if (!m_single_extruder_multi_material) { 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_new = gcodegen.writer().get_physical_extruder(tcr.new_tool);
int physical_extruder_initial = gcodegen.writer().get_physical_extruder(tcr.initial_tool); int physical_extruder_initial = gcodegen.writer().get_physical_extruder(tcr.initial_tool);
Vec2f new_extruder_offset = m_extruder_offsets[physical_extruder_new].cast<float>(); Vec2f new_extruder_offset = m_extruder_offsets[physical_extruder_new].cast<float>();
@@ -1051,6 +1042,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
const std::vector<std::string> ColorPrintColors::Colors = { "#C0392B", "#E67E22", "#F1C40F", "#27AE60", "#1ABC9C", "#2980B9", "#9B59B6" }; const std::vector<std::string> ColorPrintColors::Colors = { "#C0392B", "#E67E22", "#F1C40F", "#27AE60", "#1ABC9C", "#2980B9", "#9B59B6" };
#define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.extruder()->id()) #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() 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_times_stats(m_processor, print->m_print_statistics);
DoExport::update_print_estimated_stats(m_processor, m_writer.extruders(), print->m_print_statistics, print->config()); DoExport::update_print_estimated_stats(m_processor, m_writer.extruders(), print->m_print_statistics, print->config());
if (result != nullptr) { 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()); *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 // set the filename to the correct value
result->filename = path; result->filename = path;
} }
@@ -1870,21 +1851,15 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
{ {
PROFILE_FUNC(); PROFILE_FUNC();
// SM Orca: 确保GCodeWriter有正确的映射表(从Print获取并设置到writer)
const auto& print_mapping = print.get_filament_extruder_map(); const auto& print_mapping = print.get_filament_extruder_map();
if (!print_mapping.empty()) { if (!print_mapping.empty()) {
m_writer.set_filament_extruder_map(print_mapping); 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) { for (const auto& pair : print_mapping) {
BOOST_LOG_TRIVIAL(info) << " SM Orca: Print mapping: filament " << pair.first << " -> extruder " << pair.second;
} }
} else { } 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(); 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); m_processor.set_filament_extruder_map(writer_mapping);
// modifies m_silent_time_estimator_enabled // 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. // Write some terse information on the slicing parameters.
const PrintObject *first_object = print.objects().front(); const PrintObject *first_object = print.objects().front();
const double layer_height = first_object->config().layer_height.value; 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("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("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("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)); 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. //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 // 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); 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(); 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 BOOST_LOG_TRIVIAL(info) << "GCode::process_layer: volumetric_speed_calc - filament_id=" << initial_non_support_extruder_id
<< " physical_extruder_id=" << physical_extruder_id << " physical_extruder_id=" << physical_extruder_id
@@ -2853,12 +2824,12 @@ void GCode::process_layers(
} }
}); });
if (m_spiral_vase) { 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); 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); this->m_spiral_vase->set_max_xy_smoothing(max_xy_smoothing);
} }
const auto spiral_mode = tbb::make_filter<LayerResult, LayerResult>(slic3r_tbb_filtermode::serial_in_order, const auto spiral_mode = tbb::make_filter<LayerResult, LayerResult>(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) if (in.nop_layer_result)
return in; return in;
@@ -2953,7 +2924,7 @@ void GCode::process_layers(
} }
}); });
if (m_spiral_vase) { 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); 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); 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); return bed_temp_opt->get_at(extruder_id);
} }
// Write 1st layer bed temperatures into the G-code. // 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. // Only do that if the start G-code does not already contain any M-code controlling an extruder temperature.
// M140 - Set Extruder Temperature // M140 - Set Extruder Temperature
@@ -4710,8 +4680,6 @@ std::string GCode::change_layer(coordf_t print_z)
return gcode; return gcode;
} }
static std::unique_ptr<EdgeGrid::Grid> calculate_layer_edge_grid(const Layer& layer) static std::unique_ptr<EdgeGrid::Grid> calculate_layer_edge_grid(const Layer& layer)
{ {
auto out = make_unique<EdgeGrid::Grid>(); auto out = make_unique<EdgeGrid::Grid>();
@@ -4765,7 +4733,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
!m_config.spiral_mode && !m_config.spiral_mode &&
(loop.role() == erExternalPerimeter || (loop.role() == erPerimeter && m_config.seam_slope_inner_walls)) && (loop.role() == erExternalPerimeter || (loop.role() == erPerimeter && m_config.seam_slope_inner_walls)) &&
layer_id() > 0; 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) { 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); 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) { const auto speed_for_path = [&speed, &small_peri_speed](const ExtrusionPath& path) {
// don't apply small perimeter setting for overhangs/bridges/non-perimeters // 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; 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<double>(); Vec2d p1 = paths.front().polyline.points.front().cast<double>();
Vec2d p2 = paths.front().polyline.points[1].cast<double>(); Vec2d p2 = paths.front().polyline.points[1].cast<double>();
Vec2d v = p2 - p1; Vec2d v = p2 - p1;
double nd = scale_(EXTRUDER_CONFIG(nozzle_diameter)); double nd = scale_(PHYSICAL_EXTRUDER_CONFIG(nozzle_diameter));
double l2 = v.squaredNorm(); double l2 = v.squaredNorm();
// Shift by no more than a nozzle diameter. // Shift by no more than a nozzle diameter.
//FIXME Hiding the seams will not work nicely for very densely discretized contours! //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 // if needed, write the gcode_label_objects_end then gcode_label_objects_start
// should be already done by travel_to, but just in case // should be already done by travel_to, but just in case
m_writer.add_object_change_labels(gcode); 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; double e_per_mm = m_writer.extruder()->e_per_mm3() * _mm3_per_mm;
e_per_mm /= filament_flow_ratio; e_per_mm /= filament_flow_ratio;
// set speed // set speed
if (speed == -1) { if (speed == -1) {
if (path.role() == erPerimeter) { if (path.role() == erPerimeter) {
@@ -5580,7 +5544,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
m_curr_print->calib_mode() == CalibMode::Calib_PA_Tower; m_curr_print->calib_mode() == CalibMode::Calib_PA_Tower;
bool evaluate_adaptive_pa = false; bool evaluate_adaptive_pa = false;
bool role_change = (m_last_extrusion_role != path.role()); 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; evaluate_adaptive_pa = true;
// If we have already emmited a PA change because the m_multi_flow_segment_path_pa_set is set // 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. // 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 = PHYSICAL_EXTRUDER_CONFIG(overhang_fan_threshold);
auto overhang_fan_threshold = EXTRUDER_CONFIG(overhang_fan_threshold); auto enable_overhang_bridge_fan = PHYSICAL_EXTRUDER_CONFIG(enable_overhang_bridge_fan);
auto enable_overhang_bridge_fan = EXTRUDER_CONFIG(enable_overhang_bridge_fan);
// { "0%", Overhang_threshold_none }, // { "0%", Overhang_threshold_none },
// { "10%", Overhang_threshold_1_4 }, // { "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 = [ auto apply_role_based_fan_speed = [
&path, &append_role_based_fan_marker, &path, &append_role_based_fan_marker,
supp_interface_fan_speed = EXTRUDER_CONFIG(support_material_interface_fan_speed), supp_interface_fan_speed = PHYSICAL_EXTRUDER_CONFIG(support_material_interface_fan_speed),
ironing_fan_speed = EXTRUDER_CONFIG(ironing_fan_speed) ironing_fan_speed = PHYSICAL_EXTRUDER_CONFIG(ironing_fan_speed)
] { ] {
append_role_based_fan_marker(erSupportMaterialInterface, "_SUPP_INTERFACE"sv, append_role_based_fan_marker(erSupportMaterialInterface, "_SUPP_INTERFACE"sv,
supp_interface_fan_speed >= 0 && path.role() == erSupportMaterialInterface); 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. // 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 // Dont emit tag if it has just already been emitted from a role change above
if(_mm3_per_mm >0 && if(_mm3_per_mm >0 &&
EXTRUDER_CONFIG(adaptive_pressure_advance) && PHYSICAL_EXTRUDER_CONFIG(adaptive_pressure_advance) &&
EXTRUDER_CONFIG(enable_pressure_advance) && PHYSICAL_EXTRUDER_CONFIG(enable_pressure_advance) &&
EXTRUDER_CONFIG(adaptive_pressure_advance_overhangs) && PHYSICAL_EXTRUDER_CONFIG(adaptive_pressure_advance_overhangs) &&
!evaluate_adaptive_pa){ !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(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){ 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 // 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. // Emit tag before new speed is set so the post processor reads the next speed immediately and uses it.
if(_mm3_per_mm >0 && if(_mm3_per_mm >0 &&
EXTRUDER_CONFIG(adaptive_pressure_advance) && PHYSICAL_EXTRUDER_CONFIG(adaptive_pressure_advance) &&
EXTRUDER_CONFIG(enable_pressure_advance) && PHYSICAL_EXTRUDER_CONFIG(enable_pressure_advance) &&
EXTRUDER_CONFIG(adaptive_pressure_advance_overhangs) ){ 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(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) { if(m_config.gcode_comments) {
sprintf(buf, "; Ramp up-variable\n"); 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 += toolchange ? m_writer.retract_for_toolchange() : m_writer.retract();
} }
gcode += m_writer.reset_e(); gcode += m_writer.reset_e();
// Orca: check if should + can lift (roughly from SuperSlicer) // Orca: check if should + can lift (roughly from SuperSlicer)
RetractLiftEnforceType retract_lift_type = RetractLiftEnforceType(EXTRUDER_CONFIG(retract_lift_enforce)); 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); gcode += this->placeholder_parser_process("filament_start_gcode", filament_start_gcode, extruder_id, &config);
check_add_eol(gcode); check_add_eol(gcode);
} }
if (m_config.enable_pressure_advance.get_at(extruder_id)) { int physical_extruder_id = m_writer.get_physical_extruder(extruder_id);
gcode += m_writer.set_pressure_advance(m_config.pressure_advance.get_at(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 // Orca: Adaptive PA
// Reset Adaptive PA processor last PA value // 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); 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 // If ooze prevention is enabled, park current extruder in the nearest
// standby point and set it to the standby temperature. // standby point and set it to the standby temperature.
if (m_ooze_prevention.enable && m_writer.extruder() != nullptr) if (m_ooze_prevention.enable && m_writer.extruder() != nullptr)
gcode += m_ooze_prevention.pre_toolchange(*this); gcode += m_ooze_prevention.pre_toolchange(*this);
// SM Orca: 计算物理挤出机ID,用于耗材-挤出机映射
// 需要在参数查询之前计算,因为挤出机属性(温度、回抽等)应该从物理挤出机获取 // 需要在参数查询之前计算,因为挤出机属性(温度、回抽等)应该从物理挤出机获取
int next_physical_extruder = m_writer.get_physical_extruder(extruder_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 BOOST_LOG_TRIVIAL(info) << "GCode::set_extruder: Tool change START - filament_id=" << extruder_id
<< " -> physical_extruder_id=" << next_physical_extruder << " -> physical_extruder_id=" << next_physical_extruder
<< " print_z=" << print_z << " print_z=" << print_z
@@ -6554,31 +6514,30 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z, bool b
// BBS // BBS
// 回抽和温度是挤出机属性,使用 physical_extruder // 回抽和温度是挤出机属性,使用 physical_extruder
// SM Orca: 日志 - 配置数组访问边界检查
size_t retract_array_size = m_config.retraction_length.values.size(); size_t retract_array_size = m_config.retraction_length.values.size();
size_t temp_array_size = m_config.nozzle_temperature.values.size(); size_t temp_array_size = m_config.nozzle_temperature.values.size();
BOOST_LOG_TRIVIAL(info) << "GCode::set_extruder: Config access check -" BOOST_LOG_TRIVIAL(info) << "GCode::set_extruder: Config access check -"
<< " retraction_length array_size=" << retract_array_size << " retraction_length array_size=" << retract_array_size
<< " nozzle_temperature array_size=" << temp_array_size << " nozzle_temperature array_size=" << temp_array_size
<< " access_index=" << next_physical_extruder << " extruder_id=" << extruder_id
<< (next_physical_extruder >= (int)retract_array_size ? " [RETRACT_OUT_OF_BOUNDS!]" : "") << (extruder_id >= (int)retract_array_size ? " [RETRACT_OUT_OF_BOUNDS!]" : "")
<< (next_physical_extruder >= (int)temp_array_size ? " [TEMP_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 = m_config.retraction_length.get_at(extruder_id);
float new_retract_length_toolchange = m_config.retract_length_toolchange.get_at(next_physical_extruder); 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(next_physical_extruder): m_config.nozzle_temperature.get_at(next_physical_extruder); 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 // BBS: if print_z == 0 use first layer temperature
if (abs(print_z) < EPSILON) 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(); Vec3d nozzle_pos = m_writer.get_position();
float old_retract_length, old_retract_length_toolchange, wipe_volume; float old_retract_length, old_retract_length_toolchange, wipe_volume;
int old_filament_temp, old_filament_e_feedrate; 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)); 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 //BBS: add handling for filament change in start gcode
int previous_extruder_id = -1; int previous_extruder_id = -1;
int previous_physical_extruder = -1; // SM Orca: 前一个物理挤出机ID
if (m_writer.extruder() != nullptr || m_start_gcode_filament != -1) { if (m_writer.extruder() != nullptr || m_start_gcode_filament != -1) {
std::vector<float> flush_matrix(cast<float>(m_config.flush_volumes_matrix.values)); std::vector<float> flush_matrix(cast<float>(m_config.flush_volumes_matrix.values));
const unsigned int number_of_extruders = (unsigned int)(sqrt(flush_matrix.size()) + EPSILON); 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); assert(m_start_gcode_filament < number_of_extruders);
previous_extruder_id = m_writer.extruder() != nullptr ? m_writer.extruder()->id() : m_start_gcode_filament; 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 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() << ")" << " (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]");
// SM Orca: 回抽和温度是挤出机属性,使用 physical_extruder old_retract_length = m_config.retraction_length.get_at(previous_extruder_id);
old_retract_length = m_config.retraction_length.get_at(previous_physical_extruder); old_retract_length_toolchange = m_config.retract_length_toolchange.get_at(previous_extruder_id);
old_retract_length_toolchange = m_config.retract_length_toolchange.get_at(previous_physical_extruder); previous_physical_extruder = m_writer.get_physical_extruder(previous_extruder_id);
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) //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 = flush_matrix[previous_extruder_id * number_of_extruders + extruder_id];
wipe_volume *= m_config.flush_multiplier; 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; DynamicConfig dyn_config;
dyn_config.set_key_value("previous_extruder", new ConfigOptionInt(previous_extruder_id)); dyn_config.set_key_value("previous_extruder", new ConfigOptionInt(previous_extruder_id));
dyn_config.set_key_value("next_extruder", new ConfigOptionInt((int)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("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("previous_physical_extruder", new ConfigOptionInt(previous_physical_extruder));
dyn_config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); 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) if (m_ooze_prevention.enable)
gcode += m_ooze_prevention.post_toolchange(*this); gcode += m_ooze_prevention.post_toolchange(*this);
if (m_config.enable_pressure_advance.get_at(extruder_id)) { int physical_extruder_id = m_writer.get_physical_extruder(extruder_id);
gcode += m_writer.set_pressure_advance(m_config.pressure_advance.get_at(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 //Orca: tool changer or IDEX's firmware may change Z position, so we set it to unknown/undefined
m_last_pos_defined = false; 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 // convert a model-space scaled point into G-code coordinates
Vec2d GCode::point_to_gcode(const Point &point) const Vec2d GCode::point_to_gcode(const Point &point) const
{ {
// SM Orca: 无条件诊断 - 检查是否被调用
static std::atomic<int> call_count{0}; static std::atomic<int> call_count{0};
int this_call = ++call_count; 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(); Vec2d extruder_offset = Vec2d::Zero();
if (const Extruder *extruder = m_writer.extruder(); extruder) { if (const Extruder *extruder = m_writer.extruder(); extruder) {
extruder_offset = m_config.extruder_offset.get_at(m_writer.get_physical_extruder(extruder->id())); 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; 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())) { if (std::isnan(result.x()) || std::isinf(result.x()) || std::isnan(result.y()) || std::isinf(result.y())) {
Vec2d unscaled_val = unscale(point); Vec2d unscaled_val = unscale(point);
BOOST_LOG_TRIVIAL(error) << "SM Orca: point_to_gcode produced NaN/inf" 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; Vec2d pt = point - m_origin;
if (const Extruder *extruder = m_writer.extruder(); extruder) 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. // 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())); pt += m_config.extruder_offset.get_at(m_writer.get_physical_extruder(extruder->id()));
return scaled<coord_t>(pt); return scaled<coord_t>(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()) }; 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 // 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) // during infill/perimeter wiping, or normally (depends on wiping_entities parameter)
// Fills in by_region_per_copy_cache and returns its reference. // 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<LayerToPrint>, which contains Object and Support layers for the current print_z, collected for // Index into std::vector<LayerToPrint>, which contains Object and Support layers for the current print_z, collected for
// a single object, or for possibly multiple objects with multiple instances. // a single object, or for possibly multiple objects with multiple instances.
} // namespace Slic3r } // namespace Slic3r
+5 -1
View File
@@ -198,7 +198,11 @@ public:
void set_gcode_offset(double x, double y) { m_writer.set_xy_offset(x, y); m_processor.set_xy_offset(x, y);} 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 // SM Orca: Set filament-extruder mapping
void set_filament_extruder_map(const std::unordered_map<int, int>& map) { m_writer.set_filament_extruder_map(map); m_processor.set_filament_extruder_map(map); } void set_filament_extruder_map(const std::unordered_map<int, int>& 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. // Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests.
const Vec2d& origin() const { return m_origin; } const Vec2d& origin() const { return m_origin; }
@@ -482,8 +482,11 @@ static inline float get_default_perimeter_spacing(const PrintObject &print_objec
std::vector<unsigned int> printing_extruders = print_object.object_extruders(); std::vector<unsigned int> printing_extruders = print_object.object_extruders();
assert(!printing_extruders.empty()); assert(!printing_extruders.empty());
float avg_extruder = 0; float avg_extruder = 0;
for(unsigned int extruder_id : printing_extruders) for(unsigned int extruder_id : printing_extruders) {
avg_extruder += float(scale_(print_object.print()->config().nozzle_diameter.get_at(extruder_id))); // 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(); avg_extruder /= printing_extruders.size();
return avg_extruder; return avg_extruder;
} }
+8 -5
View File
@@ -340,12 +340,14 @@ std::vector<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std::
for (size_t i = 0; i < m_extruder_ids.size(); ++ i) { for (size_t i = 0; i < m_extruder_ids.size(); ++ i) {
PerExtruderAdjustments &adj = per_extruder_adjustments[i]; PerExtruderAdjustments &adj = per_extruder_adjustments[i];
unsigned int extruder_id = m_extruder_ids[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.extruder_id = extruder_id;
adj.cooling_slow_down_enabled = m_config.slow_down_for_layer_cooling.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(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(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) // 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; 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, &supp_interface_fan_control, &supp_interface_fan_speed,
&ironing_fan_control, &ironing_fan_speed &ironing_fan_control, &ironing_fan_speed
](bool immediately_apply) { ](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_min_speed = EXTRUDER_CONFIG(fan_min_speed);
float fan_speed_new = EXTRUDER_CONFIG(reduce_fan_stop_start_freq) ? fan_min_speed : 0; float fan_speed_new = EXTRUDER_CONFIG(reduce_fan_stop_start_freq) ? fan_min_speed : 0;
//BBS //BBS
+9
View File
@@ -27,6 +27,13 @@ public:
void reset(const Vec3d &position); void reset(const Vec3d &position);
void set_current_extruder(unsigned int extruder_id) { m_current_extruder = extruder_id; } 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); 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<int, int>& 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: private:
CoolingBuffer& operator=(const CoolingBuffer&) = delete; CoolingBuffer& operator=(const CoolingBuffer&) = delete;
@@ -55,6 +62,8 @@ private:
// the PrintConfig slice of FullPrintConfig is constant, thus no thread synchronization is required. // the PrintConfig slice of FullPrintConfig is constant, thus no thread synchronization is required.
const PrintConfig &m_config; const PrintConfig &m_config;
unsigned int m_current_extruder; unsigned int m_current_extruder;
// SM Orca: Filament to physical extruder mapping for correct parameter access
std::unordered_map<int, int> m_filament_extruder_map;
//BBS: current fan speed //BBS: current fan speed
int m_current_fan_speed; int m_current_fan_speed;
}; };
+4 -105
View File
@@ -94,7 +94,6 @@ const std::vector<std::string> GCodeProcessor::Reserved_Tags_compatible = {
" PA_CHANGE:" " PA_CHANGE:"
}; };
const std::string GCodeProcessor::Flush_Start_Tag = " FLUSH_START"; const std::string GCodeProcessor::Flush_Start_Tag = " FLUSH_START";
const std::string GCodeProcessor::Flush_End_Tag = " FLUSH_END"; const std::string GCodeProcessor::Flush_End_Tag = " FLUSH_END";
@@ -397,7 +396,6 @@ void GCodeProcessor::TimeProcessor::reset()
filament_unload_times = 0.0f; filament_unload_times = 0.0f;
machine_tool_change_time = 0.0f; machine_tool_change_time = 0.0f;
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) { for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
machines[i].reset(); machines[i].reset();
} }
@@ -457,7 +455,6 @@ void GCodeProcessor::UsedFilaments::process_color_change_cache()
} }
} }
void GCodeProcessor::UsedFilaments::process_total_volume_cache(GCodeProcessor* processor) void GCodeProcessor::UsedFilaments::process_total_volume_cache(GCodeProcessor* processor)
{ {
size_t active_extruder_id = processor->m_extruder_id; 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) { if (role_cache != 0.0f) {
std::pair<double, double> filament = { 0.0f, 0.0f }; std::pair<double, double> filament = { 0.0f, 0.0f };
// SM Orca: 添加边界检查,与其他地方保持一致
float diameter = (static_cast<size_t>(processor->m_extruder_id) < processor->m_result.filament_diameters.size()) float diameter = (static_cast<size_t>(processor->m_extruder_id) < processor->m_result.filament_diameters.size())
? processor->m_result.filament_diameters[processor->m_extruder_id] ? processor->m_result.filament_diameters[processor->m_extruder_id]
: processor->m_result.filament_diameters.back(); : processor->m_result.filament_diameters.back();
@@ -565,21 +561,16 @@ void GCodeProcessorResult::reset() {
//BBS: add mutex for protection of gcode result //BBS: add mutex for protection of gcode result
lock(); lock();
// SM Orca: 保存当前的extruders_count并尝试从已有数组推断
size_t saved_count = extruders_count; size_t saved_count = extruders_count;
// SM Orca: 如果extruders_count不合理,尝试从已有数组大小推断
if (saved_count == 0 || saved_count > 256) { if (saved_count == 0 || saved_count > 256) {
// 尝试从已有数组大小推断(优先使用filament_diameters的大小) // 尝试从已有数组大小推断(优先使用filament_diameters的大小)
if (!filament_diameters.empty() && filament_diameters.size() <= 256) { if (!filament_diameters.empty() && filament_diameters.size() <= 256) {
saved_count = filament_diameters.size(); saved_count = filament_diameters.size();
BOOST_LOG_TRIVIAL(info) << "SM Orca: GCodeProcessorResult::reset() - Inferred extruders_count from existing array: "
<< saved_count; << saved_count;
} else { } else {
// SM Orca: 使用16作为默认值(覆盖U1等多耗材机型)
// 对于只有少量耗材的用户,稍微多分配一些内存影响很小 // 对于只有少量耗材的用户,稍微多分配一些内存影响很小
saved_count = 16; 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() << ")"; << saved_count << " (was " << extruders_count << ", array size was " << filament_diameters.size() << ")";
} }
} }
@@ -596,18 +587,15 @@ void GCodeProcessorResult::reset() {
printable_height = 0.0f; printable_height = 0.0f;
settings_ids.reset(); settings_ids.reset();
// SM Orca: 恢复extruders_count为合理的值
extruders_count = saved_count; extruders_count = saved_count;
extruder_colors = std::vector<std::string>(); extruder_colors = std::vector<std::string>();
// SM Orca: 使用推断的大小初始化所有耗材相关数组
filament_diameters = std::vector<float>(saved_count, DEFAULT_FILAMENT_DIAMETER); filament_diameters = std::vector<float>(saved_count, DEFAULT_FILAMENT_DIAMETER);
filament_densities = std::vector<float>(saved_count, DEFAULT_FILAMENT_DENSITY); filament_densities = std::vector<float>(saved_count, DEFAULT_FILAMENT_DENSITY);
filament_costs = std::vector<float>(saved_count, DEFAULT_FILAMENT_COST); filament_costs = std::vector<float>(saved_count, DEFAULT_FILAMENT_COST);
required_nozzle_HRC = std::vector<int>(saved_count, DEFAULT_FILAMENT_HRC); required_nozzle_HRC = std::vector<int>(saved_count, DEFAULT_FILAMENT_HRC);
filament_vitrification_temperature = std::vector<int>(saved_count, DEFAULT_FILAMENT_VITRIFICATION_TEMPERATURE); filament_vitrification_temperature = std::vector<int>(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") << " (original extruders_count=" << (saved_count == extruders_count ? "preserved" : "inferred")
<< ", this=" << this << ")"; << ", this=" << this << ")";
@@ -623,22 +611,15 @@ void GCodeProcessorResult::reset() {
//BBS: add mutex for protection of gcode result //BBS: add mutex for protection of gcode result
lock(); lock();
// SM Orca: 保存当前的extruders_count并尝试从已有数组推断
size_t saved_count = extruders_count; size_t saved_count = extruders_count;
// SM Orca: 如果extruders_count不合理,尝试从已有数组大小推断
if (saved_count == 0 || saved_count > 256) { if (saved_count == 0 || saved_count > 256) {
// 尝试从已有数组大小推断(优先使用filament_diameters的大小) // 尝试从已有数组大小推断(优先使用filament_diameters的大小)
if (!filament_diameters.empty() && filament_diameters.size() <= 256) { if (!filament_diameters.empty() && filament_diameters.size() <= 256) {
saved_count = filament_diameters.size(); saved_count = filament_diameters.size();
BOOST_LOG_TRIVIAL(info) << "SM Orca: GCodeProcessorResult::reset() - Inferred extruders_count from existing array: "
<< saved_count;
} else { } else {
// SM Orca: 使用16作为默认值(覆盖U1等多耗材机型)
// 对于只有少量耗材的用户,稍微多分配一些内存影响很小 // 对于只有少量耗材的用户,稍微多分配一些内存影响很小
saved_count = 16; 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; backtrace_enabled = false;
extruder_colors = std::vector<std::string>(); extruder_colors = std::vector<std::string>();
// SM Orca: 恢复extruders_count为合理的值
extruders_count = saved_count; extruders_count = saved_count;
// SM Orca: 使用推断的大小初始化所有耗材相关数组
filament_diameters = std::vector<float>(saved_count, DEFAULT_FILAMENT_DIAMETER); filament_diameters = std::vector<float>(saved_count, DEFAULT_FILAMENT_DIAMETER);
required_nozzle_HRC = std::vector<int>(saved_count, DEFAULT_FILAMENT_HRC); required_nozzle_HRC = std::vector<int>(saved_count, DEFAULT_FILAMENT_HRC);
filament_densities = std::vector<float>(saved_count, DEFAULT_FILAMENT_DENSITY); filament_densities = std::vector<float>(saved_count, DEFAULT_FILAMENT_DENSITY);
filament_costs = std::vector<float>(saved_count, DEFAULT_FILAMENT_COST); filament_costs = std::vector<float>(saved_count, DEFAULT_FILAMENT_COST);
filament_vitrification_temperature = std::vector<int>(saved_count, DEFAULT_FILAMENT_VITRIFICATION_TEMPERATURE); filament_vitrification_temperature = std::vector<int>(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<CustomGCode::Item>(); custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
spiral_vase_layers = std::vector<std::pair<float, std::pair<size_t, size_t>>>(); spiral_vase_layers = std::vector<std::pair<float, std::pair<size_t, size_t>>>();
bed_match_result = BedMatchResult(true); bed_match_result = BedMatchResult(true);
@@ -788,12 +763,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
m_preheat_steps = 1; m_preheat_steps = 1;
m_result.backtrace_enabled = m_preheat_time > 0 && (m_is_XL_printer || (!m_single_extruder_multi_material && extruders_count > 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(); 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) { 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<int>(config.nozzle_hrc.getInt()); m_result.nozzle_hrc = static_cast<int>(config.nozzle_hrc.getInt());
m_result.nozzle_type = config.nozzle_type; m_result.nozzle_type = config.nozzle_type;
// SM Orca: 获取各配置数组的大小,用于边界检查
size_t diameter_count = config.filament_diameter.values.size(); size_t diameter_count = config.filament_diameter.values.size();
size_t density_count = config.filament_density.values.size(); size_t density_count = config.filament_density.values.size();
size_t cost_count = config.filament_cost.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(); size_t vitrification_count = config.temperature_vitrification.values.size();
for (size_t i = 0; i < extruders_count; ++ i) { for (size_t i = 0; i < extruders_count; ++ i) {
// SM Orca: 使用物理挤出机ID来访问offset,并确保不越界
int physical_extruder = get_physical_extruder(i); int physical_extruder = get_physical_extruder(i);
// SM Orca: 边界检查 - 如果映射的物理挤出机超出范围,回退到1:1映射
if (physical_extruder < 0 || physical_extruder >= static_cast<int>(physical_extruder_count)) { if (physical_extruder < 0 || physical_extruder >= static_cast<int>(physical_extruder_count)) {
BOOST_LOG_TRIVIAL(error) << "SM Orca: Filament " << i BOOST_LOG_TRIVIAL(error) << "SM Orca: Filament " << i
<< " maps to invalid physical extruder " << physical_extruder << " 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<float>().eval(), 0.f); m_extruder_offsets[i] = to_3d(config.extruder_offset.get_at(physical_extruder).cast<float>().eval(), 0.f);
m_extruder_colors[i] = static_cast<unsigned char>(i); m_extruder_colors[i] = static_cast<unsigned char>(i);
// SM Orca: 安全读取温度配置(使用最后有效值作为回退)
// 温度是挤出机属性,使用 physical_extruder 而不是 filament index // 温度是挤出机属性,使用 physical_extruder 而不是 filament index
if (physical_extruder < static_cast<int>(temp_initial_count)) { if (physical_extruder < static_cast<int>(temp_initial_count)) {
m_extruder_temps_first_layer_config[i] = static_cast<int>(config.nozzle_temperature_initial_layer.get_at(physical_extruder)); m_extruder_temps_first_layer_config[i] = static_cast<int>(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]; m_extruder_temps_config[i] = m_extruder_temps_first_layer_config[i];
} }
// SM Orca: 安全读取直径(使用最后有效值作为回退)
if (i < diameter_count) { if (i < diameter_count) {
m_result.filament_diameters[i] = static_cast<float>(config.filament_diameter.get_at(i)); m_result.filament_diameters[i] = static_cast<float>(config.filament_diameter.get_at(i));
} else { } 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"; BOOST_LOG_TRIVIAL(warning) << "SM Orca: Filament " << i << " diameter not configured, using " << fallback << "mm";
} }
// SM Orca: 安全读取HRC
if (i < hrc_count) { if (i < hrc_count) {
m_result.required_nozzle_HRC[i] = static_cast<int>(config.required_nozzle_HRC.get_at(i)); m_result.required_nozzle_HRC[i] = static_cast<int>(config.required_nozzle_HRC.get_at(i));
} else { } else {
@@ -910,7 +874,6 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
m_result.required_nozzle_HRC[i] = fallback; m_result.required_nozzle_HRC[i] = fallback;
} }
// SM Orca: 安全读取密度(使用最后有效值作为回退)
if (i < density_count) { if (i < density_count) {
m_result.filament_densities[i] = static_cast<float>(config.filament_density.get_at(i)); m_result.filament_densities[i] = static_cast<float>(config.filament_density.get_at(i));
} else { } 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³"; BOOST_LOG_TRIVIAL(warning) << "SM Orca: Filament " << i << " density not configured, using " << fallback << " g/cm³";
} }
// SM Orca: 安全读取玻璃化温度
if (i < vitrification_count) { if (i < vitrification_count) {
m_result.filament_vitrification_temperature[i] = static_cast<int>(config.temperature_vitrification.get_at(i)); m_result.filament_vitrification_temperature[i] = static_cast<int>(config.temperature_vitrification.get_at(i));
} else { } else {
@@ -929,7 +891,6 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
m_result.filament_vitrification_temperature[i] = fallback; m_result.filament_vitrification_temperature[i] = fallback;
} }
// SM Orca: 安全读取成本(使用0作为默认值)
if (i < cost_count) { if (i < cost_count) {
m_result.filament_costs[i] = static_cast<float>(config.filament_cost.get_at(i)); m_result.filament_costs[i] = static_cast<float>(config.filament_cost.get_at(i));
} else { } else {
@@ -1051,17 +1012,14 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
if (filament_diameters != nullptr) { if (filament_diameters != nullptr) {
size_t config_size = filament_diameters->values.size(); size_t config_size = filament_diameters->values.size();
// SM Orca: 确保数组大小等于extruders_count,不要clear()
if (m_result.filament_diameters.size() < m_result.extruders_count) { if (m_result.filament_diameters.size() < m_result.extruders_count) {
m_result.filament_diameters.resize(m_result.extruders_count, DEFAULT_FILAMENT_DIAMETER); 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) { for (size_t i = 0; i < config_size && i < m_result.extruders_count; ++i) {
m_result.filament_diameters[i] = static_cast<float>(filament_diameters->values[i]); m_result.filament_diameters[i] = static_cast<float>(filament_diameters->values[i]);
} }
// SM Orca: 对于配置中没有的值,使用最后一个有效值
if (config_size > 0 && config_size < m_result.extruders_count) { if (config_size > 0 && config_size < m_result.extruders_count) {
float last_value = static_cast<float>(filament_diameters->values[config_size - 1]); float last_value = static_cast<float>(filament_diameters->values[config_size - 1]);
for (size_t i = config_size; i < m_result.extruders_count; ++i) { 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) { if (m_result.filament_diameters.size() < m_result.extruders_count) {
m_result.filament_diameters.resize(m_result.extruders_count, DEFAULT_FILAMENT_DIAMETER); 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) { if (filament_densities != nullptr) {
size_t config_size = filament_densities->values.size(); size_t config_size = filament_densities->values.size();
// SM Orca: 确保数组大小等于extruders_count,不要clear()
if (m_result.filament_densities.size() < m_result.extruders_count) { if (m_result.filament_densities.size() < m_result.extruders_count) {
m_result.filament_densities.resize(m_result.extruders_count, DEFAULT_FILAMENT_DENSITY); 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) { for (size_t i = 0; i < config_size && i < m_result.extruders_count; ++i) {
m_result.filament_densities[i] = static_cast<float>(filament_densities->values[i]); m_result.filament_densities[i] = static_cast<float>(filament_densities->values[i]);
} }
// SM Orca: 对于配置中没有的值,使用最后一个有效值
if (config_size > 0 && config_size < m_result.extruders_count) { if (config_size > 0 && config_size < m_result.extruders_count) {
float last_value = static_cast<float>(filament_densities->values[config_size - 1]); float last_value = static_cast<float>(filament_densities->values[config_size - 1]);
for (size_t i = config_size; i < m_result.extruders_count; ++i) { 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) { if (m_result.filament_densities.size() < m_result.extruders_count) {
m_result.filament_densities.resize(m_result.extruders_count, DEFAULT_FILAMENT_DENSITY); 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) { if (filament_costs != nullptr) {
size_t config_size = filament_costs->values.size(); size_t config_size = filament_costs->values.size();
// SM Orca: 确保数组大小等于extruders_count,不要clear()
if (m_result.filament_costs.size() < m_result.extruders_count) { if (m_result.filament_costs.size() < m_result.extruders_count) {
m_result.filament_costs.resize(m_result.extruders_count, DEFAULT_FILAMENT_COST); 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) for (size_t i = 0; i < config_size && i < m_result.extruders_count; ++i)
m_result.filament_costs[i] = static_cast<float>(filament_costs->values[i]); m_result.filament_costs[i] = static_cast<float>(filament_costs->values[i]);
// SM Orca: 对于配置中没有的值,使用0(成本默认值)
if (config_size < m_result.extruders_count) { if (config_size < m_result.extruders_count) {
for (size_t i = config_size; i < m_result.extruders_count; ++i) { for (size_t i = config_size; i < m_result.extruders_count; ++i) {
m_result.filament_costs[i] = DEFAULT_FILAMENT_COST; 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) { if (m_result.filament_costs.size() < m_result.extruders_count) {
m_result.filament_costs.resize(m_result.extruders_count, DEFAULT_FILAMENT_COST); 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) { if (filament_vitrification_temperature != nullptr) {
size_t config_size = filament_vitrification_temperature->values.size(); 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) { 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); 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) { for (size_t i = 0; i < config_size && i < m_result.extruders_count; ++i) {
m_result.filament_vitrification_temperature[i] = static_cast<int>(filament_vitrification_temperature->values[i]); m_result.filament_vitrification_temperature[i] = static_cast<int>(filament_vitrification_temperature->values[i]);
} }
// SM Orca: 对于配置中没有的值,使用最后一个有效值
if (config_size > 0 && config_size < m_result.extruders_count) { if (config_size > 0 && config_size < m_result.extruders_count) {
int last_value = static_cast<int>(filament_vitrification_temperature->values[config_size - 1]); int last_value = static_cast<int>(filament_vitrification_temperature->values[config_size - 1]);
for (size_t i = config_size; i < m_result.extruders_count; ++i) { for (size_t i = config_size; i < m_result.extruders_count; ++i) {
@@ -1186,7 +1132,6 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
} }
} }
else { else {
// SM Orca: 先确保数组足够大,不要缩小已有的数组
size_t physical_count = extruder_offset->values.size(); size_t physical_count = extruder_offset->values.size();
if (m_extruder_offsets.size() < m_result.extruders_count) { if (m_extruder_offsets.size() < m_result.extruders_count) {
m_extruder_offsets.resize(m_result.extruders_count, DEFAULT_EXTRUDER_OFFSET); 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) { if (m_extruder_offsets.size() < m_result.extruders_count) {
// SM Orca: 使用映射来填充剩余耗材的offset,而不是使用默认值
size_t physical_count = m_extruder_offsets.size(); size_t physical_count = m_extruder_offsets.size();
for (size_t i = m_extruder_offsets.size(); i < m_result.extruders_count; ++i) { for (size_t i = m_extruder_offsets.size(); i < m_result.extruders_count; ++i) {
int physical_extruder = get_physical_extruder(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) if (machine_tool_change_time != nullptr)
m_time_processor.machine_tool_change_time = static_cast<float>(machine_tool_change_time->value); m_time_processor.machine_tool_change_time = static_cast<float>(machine_tool_change_time->value);
if (m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware || m_flavor == gcfKlipper) { if (m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware || m_flavor == gcfKlipper) {
const ConfigOptionFloats* machine_max_acceleration_x = config.option<ConfigOptionFloats>("machine_max_acceleration_x"); const ConfigOptionFloats* machine_max_acceleration_x = config.option<ConfigOptionFloats>("machine_max_acceleration_x");
if (machine_max_acceleration_x != nullptr) if (machine_max_acceleration_x != nullptr)
@@ -1319,7 +1262,6 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
if (machine_max_acceleration_retracting != nullptr) if (machine_max_acceleration_retracting != nullptr)
m_time_processor.machine_limits.machine_max_acceleration_retracting.values = machine_max_acceleration_retracting->values; 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. // Legacy Marlin does not have separate travel acceleration, it uses the 'extruding' value instead.
const ConfigOptionFloats* machine_max_acceleration_travel = config.option<ConfigOptionFloats>(m_flavor == gcfMarlinLegacy || m_flavor == gcfKlipper const ConfigOptionFloats* machine_max_acceleration_travel = config.option<ConfigOptionFloats>(m_flavor == gcfMarlinLegacy || m_flavor == gcfKlipper
? "machine_max_acceleration_extruding" ? "machine_max_acceleration_extruding"
@@ -1327,7 +1269,6 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
if (machine_max_acceleration_travel != nullptr) if (machine_max_acceleration_travel != nullptr)
m_time_processor.machine_limits.machine_max_acceleration_travel.values = machine_max_acceleration_travel->values; m_time_processor.machine_limits.machine_max_acceleration_travel.values = machine_max_acceleration_travel->values;
const ConfigOptionFloats* machine_min_extruding_rate = config.option<ConfigOptionFloats>("machine_min_extruding_rate"); const ConfigOptionFloats* machine_min_extruding_rate = config.option<ConfigOptionFloats>("machine_min_extruding_rate");
if (machine_min_extruding_rate != nullptr) if (machine_min_extruding_rate != nullptr)
m_time_processor.machine_limits.machine_min_extruding_rate.values = machine_min_extruding_rate->values; 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) if (bed_type != nullptr)
m_result.bed_type = (BedType)bed_type->value; m_result.bed_type = (BedType)bed_type->value;
const ConfigOptionFloat* z_offset = config.option<ConfigOptionFloat>("z_offset"); const ConfigOptionFloat* z_offset = config.option<ConfigOptionFloat>("z_offset");
if (z_offset != nullptr) if (z_offset != nullptr)
m_z_offset = z_offset->value; m_z_offset = z_offset->value;
// SM Orca: 验证所有数组大小是否正确
bool arrays_valid = true; bool arrays_valid = true;
if (m_result.filament_diameters.size() != m_result.extruders_count) { if (m_result.filament_diameters.size() != m_result.extruders_count) {
BOOST_LOG_TRIVIAL(error) << "SM Orca: CRITICAL - filament_diameters size mismatch: " 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; 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) 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 // update start position
m_start_position = m_end_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]) || 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[Y]) || std::isinf(m_start_position[Y]) ||
std::isnan(m_start_position[Z]) || std::isinf(m_start_position[Z])) { 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); 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]) || 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[Y]) || std::isinf(m_end_position[Y]) ||
std::isnan(m_end_position[Z]) || std::isinf(m_end_position[Z])) { 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 // cross section: rectangle
m_width = delta_pos[E] * static_cast<float>(M_PI * sqr(1.05f * filament_radius)) / (delta_xyz * m_height); m_width = delta_pos[E] * static_cast<float>(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) { else if (m_extrusion_role == erBridgeInfill || m_extrusion_role == erInternalBridgeInfill || m_extrusion_role == erNone) {
// SM Orca: 添加边界检查
float diameter = (static_cast<size_t>(m_extruder_id) < m_result.filament_diameters.size()) float diameter = (static_cast<size_t>(m_extruder_id) < m_result.filament_diameters.size())
? m_result.filament_diameters[m_extruder_id] ? m_result.filament_diameters[m_extruder_id]
: m_result.filament_diameters.back(); : m_result.filament_diameters.back();
// SM Orca: 防止sqrt(负数)产生NaN
float ratio = delta_pos[E] / delta_xyz; float ratio = delta_pos[E] / delta_xyz;
if (ratio < 0.0f) { if (ratio < 0.0f) {
BOOST_LOG_TRIVIAL(warning) << "SM Orca: Negative E/XYZ ratio (" << ratio 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 // cross section: rectangle + 2 semicircles
m_width = delta_pos[E] * static_cast<float>(M_PI * sqr(filament_radius)) / (delta_xyz * m_height) + static_cast<float>(1.0 - 0.25 * M_PI) * m_height; m_width = delta_pos[E] * static_cast<float>(M_PI * sqr(filament_radius)) / (delta_xyz * m_height) + static_cast<float>(1.0 - 0.25 * M_PI) * m_height;
// SM Orca: 验证宽度计算结果,防止NaN/inf传播
if (std::isnan(m_width) || std::isinf(m_width)) { if (std::isnan(m_width) || std::isinf(m_width)) {
BOOST_LOG_TRIVIAL(error) << "SM Orca: Invalid width calculated: " << m_width BOOST_LOG_TRIVIAL(error) << "SM Orca: Invalid width calculated: " << m_width
<< " for extruder " << m_extruder_id << " for extruder " << m_extruder_id
@@ -3262,7 +3192,6 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line, const std::o
// axis reversal // axis reversal
std::max(-v_exit, v_entry)); std::max(-v_exit, v_entry));
float axis_max_jerk = get_axis_max_jerk(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a)); float axis_max_jerk = get_axis_max_jerk(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
if (jerk > axis_max_jerk) { if (jerk > axis_max_jerk) {
v_factor *= axis_max_jerk / 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 // check for seam starting vertex
if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) { 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 //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 Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset;
if (!m_seams_detector.has_first_vertex()) { if (!m_seams_detector.has_first_vertex()) {
m_seams_detector.set_first_vertex(new_pos); 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]); 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 //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 Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset;
const std::optional<Vec3f> first_vertex = m_seams_detector.get_first_vertex(); const std::optional<Vec3f> 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 // 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) { else if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) {
m_seams_detector.activate(true); 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); 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); 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]) || 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[Y]) || std::isinf(m_end_position[Y]) ||
std::isnan(m_end_position[Z]) || std::isinf(m_end_position[Z])) { 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]); EMoveType type = move_type(delta_pos[E]);
const float delta_xyz = std::sqrt(sqr(arc_length) + sqr(delta_pos[Z])); const float delta_xyz = std::sqrt(sqr(arc_length) + sqr(delta_pos[Z]));
m_travel_dist = delta_xyz; m_travel_dist = delta_xyz;
if (type == EMoveType::Extrude) { if (type == EMoveType::Extrude) {
@@ -3548,12 +3472,10 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line)
//BBS: cross section: rectangle //BBS: cross section: rectangle
m_width = delta_pos[E] * static_cast<float>(M_PI * sqr(1.05f * filament_radius)) / (delta_xyz * m_height); m_width = delta_pos[E] * static_cast<float>(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) { else if (m_extrusion_role == erBridgeInfill || m_extrusion_role == erInternalBridgeInfill || m_extrusion_role == erNone) {
// SM Orca: 添加边界检查
float diameter = (static_cast<size_t>(m_extruder_id) < m_result.filament_diameters.size()) float diameter = (static_cast<size_t>(m_extruder_id) < m_result.filament_diameters.size())
? m_result.filament_diameters[m_extruder_id] ? m_result.filament_diameters[m_extruder_id]
: m_result.filament_diameters.back(); : m_result.filament_diameters.back();
// SM Orca: 防止sqrt(负数)产生NaN
float ratio = delta_pos[E] / delta_xyz; float ratio = delta_pos[E] / delta_xyz;
if (ratio < 0.0f) { if (ratio < 0.0f) {
BOOST_LOG_TRIVIAL(warning) << "SM Orca: Negative E/XYZ ratio (" << ratio 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 //BBS: cross section: rectangle + 2 semicircles
m_width = delta_pos[E] * static_cast<float>(M_PI * sqr(filament_radius)) / (delta_xyz * m_height) + static_cast<float>(1.0 - 0.25 * M_PI) * m_height; m_width = delta_pos[E] * static_cast<float>(M_PI * sqr(filament_radius)) / (delta_xyz * m_height) + static_cast<float>(1.0 - 0.25 * M_PI) * m_height;
// SM Orca: 验证宽度计算结果,防止NaN/inf传播
if (std::isnan(m_width) || std::isinf(m_width)) { if (std::isnan(m_width) || std::isinf(m_width)) {
BOOST_LOG_TRIVIAL(error) << "SM Orca: Invalid width calculated: " << m_width BOOST_LOG_TRIVIAL(error) << "SM Orca: Invalid width calculated: " << m_width
<< " for extruder " << m_extruder_id << " for extruder " << m_extruder_id
@@ -3739,7 +3660,6 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line)
//BBS: axis reversal //BBS: axis reversal
std::max(-v_exit, v_entry)); std::max(-v_exit, v_entry));
float axis_max_jerk = get_axis_max_jerk(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a)); float axis_max_jerk = get_axis_max_jerk(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
if (jerk > axis_max_jerk) { if (jerk > axis_max_jerk) {
v_factor *= axis_max_jerk / 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()) { if (m_seams_detector.is_active()) {
//BBS: check for seam starting vertex //BBS: check for seam starting vertex
if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) { 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; const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset;
if (!m_seams_detector.has_first_vertex()) { if (!m_seams_detector.has_first_vertex()) {
m_seams_detector.set_first_vertex(new_pos); 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(); 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]); 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 Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset;
const std::optional<Vec3f> first_vertex = m_seams_detector.get_first_vertex(); const std::optional<Vec3f> 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 //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) { else if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) {
m_seams_detector.activate(true); 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); 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) void GCodeProcessor::process_G10(const GCodeReader::GCodeLine& line)
{ {
// SM Orca: 回抽参数是挤出机属性,使用 physical_extruder
int physical_extruder = get_physical_extruder(m_extruder_id);
GCodeReader::GCodeLine g10; GCodeReader::GCodeLine g10;
g10.set(Axis::E, -this->m_parser.config().retraction_length.get_at(physical_extruder)); 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(physical_extruder) * 60); g10.set(Axis::F, this->m_parser.config().retraction_speed.get_at(m_extruder_id) * 60);
process_G1(g10); process_G1(g10);
} }
void GCodeProcessor::process_G11(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_G11(const GCodeReader::GCodeLine& line)
{ {
// SM Orca: 回抽参数是挤出机属性,使用 physical_extruder
int physical_extruder = get_physical_extruder(m_extruder_id);
GCodeReader::GCodeLine g11; 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::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(physical_extruder) * 60); g11.set(Axis::F, this->m_parser.config().deretraction_speed.get_at(m_extruder_id) * 60);
process_G1(g11); 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]) || 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[Y]) || std::isinf(m_origin[Y]) ||
std::isnan(m_origin[Z]) || std::isinf(m_origin[Z])) { 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); simulate_st_synchronize(wait_chamber_temp_time);
} }
void GCodeProcessor::process_M201(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_M201(const GCodeReader::GCodeLine& line)
{ {
// see http://reprap.org/wiki/G-code#M201:_Set_max_printing_acceleration // 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; double filament_total_cost = 0.0;
for (const auto& [id, volume] : m_result.print_statistics.total_volumes_per_extruder) { for (const auto& [id, volume] : m_result.print_statistics.total_volumes_per_extruder) {
// SM Orca: 边界检查 - 确保id在有效范围内
if (id >= m_result.filament_diameters.size() || if (id >= m_result.filament_diameters.size() ||
id >= m_result.filament_densities.size() || id >= m_result.filament_densities.size() ||
id >= m_result.filament_costs.size()) { id >= m_result.filament_costs.size()) {
@@ -4468,12 +4378,10 @@ void GCodeProcessor::run_post_process()
continue; continue;
} }
// SM Orca: 读取并验证配置值
double diameter = m_result.filament_diameters[id]; double diameter = m_result.filament_diameters[id];
double density = m_result.filament_densities[id]; double density = m_result.filament_densities[id];
double cost = m_result.filament_costs[id]; double cost = m_result.filament_costs[id];
// SM Orca: 防止除零和NaN传播
if (diameter <= 0.0 || std::isnan(diameter)) { if (diameter <= 0.0 || std::isnan(diameter)) {
BOOST_LOG_TRIVIAL(warning) << "SM Orca: Invalid filament diameter " << diameter BOOST_LOG_TRIVIAL(warning) << "SM Orca: Invalid filament diameter " << diameter
<< " for filament " << id << ", using default 1.75mm"; << " for filament " << id << ", using default 1.75mm";
@@ -4492,7 +4400,6 @@ void GCodeProcessor::run_post_process()
cost = 0.0; cost = 0.0;
} }
// SM Orca: 执行成本计算
double cross_section = M_PI * sqr(0.5 * diameter); double cross_section = M_PI * sqr(0.5 * diameter);
filament_mm[id] = volume / cross_section; filament_mm[id] = volume / cross_section;
filament_cm3[id] = volume * 0.001; 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; double total_g_wipe_tower = m_print->print_statistics().total_wipe_tower_filament;
auto time_in_minutes = [](float time_in_seconds) { auto time_in_minutes = [](float time_in_seconds) {
assert(time_in_seconds >= 0.f); assert(time_in_seconds >= 0.f);
return int((time_in_seconds + 0.5f) / 60.0f); 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_times_cache_id{ 0 };
size_t m_out_file_pos{ 0 }; size_t m_out_file_pos{ 0 };
public: public:
ExportLines(EWriteType type, ExportLines(EWriteType type,
const std::array<TimeMachine, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)>& machines) const std::array<TimeMachine, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)>& machines)
@@ -5207,7 +5112,6 @@ void GCodeProcessor::run_post_process()
export_lines.flush(out, m_result, out_path); export_lines.flush(out, m_result, out_path);
out.close(); out.close();
in.close(); in.close();
@@ -5225,7 +5129,6 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type)
m_line_id + 1 : m_line_id + 1 :
((type == EMoveType::Seam) ? m_last_line_id : m_line_id); ((type == EMoveType::Seam) ? m_last_line_id : m_line_id);
// SM Orca: 添加边界检查,防止访问越界
Vec3f extruder_offset = (static_cast<size_t>(m_extruder_id) < m_extruder_offsets.size()) Vec3f extruder_offset = (static_cast<size_t>(m_extruder_id) < m_extruder_offsets.size())
? m_extruder_offsets[m_extruder_id] ? m_extruder_offsets[m_extruder_id]
: Vec3f(0.0f, 0.0f, 0.0f); : Vec3f(0.0f, 0.0f, 0.0f);
@@ -5241,7 +5144,6 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type)
extruder_offset; extruder_offset;
} }
// SM Orca: 最后一道防线,防止无效值进入result
if (std::isnan(m_width) || std::isinf(m_width)) { if (std::isnan(m_width) || std::isinf(m_width)) {
BOOST_LOG_TRIVIAL(error) << "SM Orca: Blocking invalid width: " << m_width BOOST_LOG_TRIVIAL(error) << "SM Orca: Blocking invalid width: " << m_width
<< " (extruder " << m_extruder_id << ")"; << " (extruder " << m_extruder_id << ")";
@@ -5254,7 +5156,6 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type)
m_height = DEFAULT_TOOLPATH_HEIGHT; m_height = DEFAULT_TOOLPATH_HEIGHT;
} }
// SM Orca: 验证position,防止NaN/inf进入moves
Vec3f final_position = Vec3f(m_end_position[X] + m_x_offset, Vec3f final_position = Vec3f(m_end_position[X] + m_x_offset,
m_end_position[Y] + m_y_offset, m_end_position[Y] + m_y_offset,
m_processing_start_custom_gcode ? m_first_layer_height : m_end_position[Z] - m_z_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_extruder_id,
m_cp_color.current, m_cp_color.current,
//BBS: add plate's offset to the rendering vertices //BBS: add plate's offset to the rendering vertices
// SM Orca: 使用验证过的final_position
final_position, final_position,
static_cast<float>(m_end_position[E] - m_start_position[E]), static_cast<float>(m_end_position[E] - m_start_position[E]),
m_feedrate, m_feedrate,
@@ -5295,7 +5195,6 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type)
static_cast<float>(m_layer_id), //layer_duration: set later static_cast<float>(m_layer_id), //layer_duration: set later
//BBS: add arc move related data //BBS: add arc move related data
path_type, 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], 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, m_interpolation_points,
}); });
+3 -3
View File
@@ -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 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 // SM Orca: 回抽参数支持耗材覆盖,继承后存储在耗材位置,使用 filament_id (idx)
m_filpar[idx].retract_length = config.retraction_length.get_at(physical_extruder); m_filpar[idx].retract_length = config.retraction_length.get_at(idx);
m_filpar[idx].retract_speed = config.retraction_speed.get_at(physical_extruder); m_filpar[idx].retract_speed = config.retraction_speed.get_at(idx);
} }
+5 -11
View File
@@ -27,6 +27,11 @@ void GCodeWriter::apply_print_config(const PrintConfig &print_config)
{ {
this->config.apply(print_config, true); this->config.apply(print_config, true);
m_single_extruder_multi_material = print_config.single_extruder_multi_material.value; 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 || 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; 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); 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<unsigned int> extruder_ids) void GCodeWriter::set_extruders(std::vector<unsigned int> 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: 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()); std::sort(extruder_ids.begin(), extruder_ids.end());
m_extruder = nullptr; // this points to object inside `m_extruders`, so should be cleared too m_extruder = nullptr; // this points to object inside `m_extruders`, so should be cleared too
m_extruders.clear(); m_extruders.clear();
m_extruders.reserve(extruder_ids.size()); m_extruders.reserve(extruder_ids.size());
// SM Orca: 创建 Extruder 对象时传递物理挤出机ID
for (unsigned int extruder_id : extruder_ids) { for (unsigned int extruder_id : extruder_ids) {
int physical_extruder_id = get_physical_extruder(extruder_id); int physical_extruder_id = get_physical_extruder(extruder_id);
BOOST_LOG_TRIVIAL(info) << "GCodeWriter::set_extruders: Creating Extruder - filament_id=" << 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(); return gcode.str();
} }
std::string GCodeWriter::reset_e(bool force) std::string GCodeWriter::reset_e(bool force)
{ {
if (FLAVOR_IS(gcfMach3) if (FLAVOR_IS(gcfMach3)
@@ -463,11 +465,8 @@ std::string GCodeWriter::toolchange_prefix() const
std::string GCodeWriter::toolchange(unsigned int extruder_id) 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); 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 // 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; }); 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 // if we are running a single-extruder setup, just set the extruder and return nothing
std::ostringstream gcode; std::ostringstream gcode;
if (this->multiple_extruders || (this->config.filament_diameter.values.size() > 1 && !is_bbl_printers())) { 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; gcode << this->toolchange_prefix() << extruder_id;
//BBS //BBS
if (GCodeWriter::full_gcode_comment) if (GCodeWriter::full_gcode_comment)
@@ -487,7 +484,6 @@ std::string GCodeWriter::toolchange(unsigned int extruder_id)
gcode << "\n"; gcode << "\n";
gcode << this->reset_e(true); gcode << this->reset_e(true);
} else { } else {
BOOST_LOG_TRIVIAL(info) << "SM Orca: Toolchange - Single extruder mode, no T command generated";
} }
return gcode.str(); 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) 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))) { 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" BOOST_LOG_TRIVIAL(error) << "SM Orca: travel_to_xy received NaN/inf point"
<< " extruder=" << (m_extruder ? m_extruder->id() : -1) << " 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) 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))) { 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" BOOST_LOG_TRIVIAL(error) << "SM Orca: extrude_to_xy received NaN/inf point"
<< " extruder=" << (m_extruder ? m_extruder->id() : -1) << " extruder=" << (m_extruder ? m_extruder->id() : -1)
+18 -2
View File
@@ -123,10 +123,24 @@ public:
// SM Orca: 设置耗材-挤出机映射 // SM Orca: 设置耗材-挤出机映射
void set_filament_extruder_map(const std::unordered_map<int, int>& map) { m_filament_extruder_map = map; } void set_filament_extruder_map(const std::unordered_map<int, int>& map) { m_filament_extruder_map = map; }
const std::unordered_map<int, int>& get_filament_extruder_map() const { return m_filament_extruder_map; } const std::unordered_map<int, int>& 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 { int get_physical_extruder(int filament_idx) const {
auto it = m_filament_extruder_map.find(filament_idx); 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; return physical_extruder_id;
} }
@@ -183,6 +197,8 @@ public:
// SM Orca: 耗材到物理挤出机的映射表(filament_idx -> physical_extruder_id) // SM Orca: 耗材到物理挤出机的映射表(filament_idx -> physical_extruder_id)
std::unordered_map<int, int> m_filament_extruder_map; std::unordered_map<int, int> m_filament_extruder_map;
// SM Orca: 物理挤出机数量(用于默认模运算映射)
size_t m_physical_extruder_count = 1; // 默认为1,防止除零
enum class Acceleration { enum class Acceleration {
Travel, Travel,
+53 -39
View File
@@ -493,7 +493,6 @@ std::vector<unsigned int> Print::extruders(bool conside_custom_gcode) const
return extruders; return extruders;
} }
// SM Orca: Initialize filament-to-physical-extruder mapping
// This must be called before the mapping is used (e.g., before export_gcode) // This must be called before the mapping is used (e.g., before export_gcode)
void Print::initialize_filament_extruder_map() 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(); size_t physical_extruder_count = m_config.nozzle_diameter.values.size();
if (physical_extruder_count == 0) { 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; return;
} }
// Get all filament indices that will be used // Get all filament indices that will be used
std::vector<unsigned int> filament_extruders = this->extruders(); std::vector<unsigned int> filament_extruders = this->extruders();
BOOST_LOG_TRIVIAL(info) << "Print::initialize_filament_extruder_map: Initializing with " // IMPORTANT: Always create mappings for ALL configured filaments, not just those used by objects.
<< physical_extruder_count << " physical extruders and " // This is critical because filament override parameters need to access the mapping for all filaments.
<< filament_extruders.size() << " filaments to map"; // 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 // Create mapping: filament_id -> physical_extruder_id
// Mapping formula: physical_extruder = filament_id % physical_extruder_count // 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) { for (unsigned int filament_idx : filament_extruders) {
int physical_extruder = filament_idx % physical_extruder_count; int physical_extruder = filament_idx % physical_extruder_count;
m_filament_extruder_map[filament_idx] = physical_extruder; 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 unsigned int Print::num_object_instances() const
@@ -543,7 +555,6 @@ double Print::max_allowed_layer_height() const
{ {
double nozzle_diameter_max = 0.; double nozzle_diameter_max = 0.;
for (unsigned int extruder_id : this->extruders()) { for (unsigned int extruder_id : this->extruders()) {
// SM Orca: 使用物理挤出机的喷嘴直径
int physical_extruder = get_physical_extruder(extruder_id); int physical_extruder = get_physical_extruder(extruder_id);
nozzle_diameter_max = std::max(nozzle_diameter_max, m_config.nozzle_diameter.get_at(physical_extruder)); 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()) { if (this->has_wipe_tower() && ! m_objects.empty()) {
// Make sure all extruders use same diameter filament and have the same nozzle diameter // 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 // EPSILON comparison is used for nozzles and 10 % tolerance is used for filaments
// SM Orca: 使用物理挤出机的喷嘴直径
int first_physical = get_physical_extruder(extruders.front()); int first_physical = get_physical_extruder(extruders.front());
double first_nozzle_diam = m_config.nozzle_diameter.get_at(first_physical); double first_nozzle_diam = m_config.nozzle_diameter.get_at(first_physical);
double first_filament_diam = m_config.filament_diameter.get_at(extruders.front()); 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<double>::max(); double min_nozzle_diameter = std::numeric_limits<double>::max();
double max_nozzle_diameter = 0; double max_nozzle_diameter = 0;
for (unsigned int extruder_id : extruders) { for (unsigned int extruder_id : extruders) {
// SM Orca: 使用物理挤出机的喷嘴直径
int physical_extruder = get_physical_extruder(extruder_id); int physical_extruder = get_physical_extruder(extruder_id);
double dmr = m_config.nozzle_diameter.get_at(physical_extruder); double dmr = m_config.nozzle_diameter.get_at(physical_extruder);
min_nozzle_diameter = std::min(min_nozzle_diameter, dmr); 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 size_t first_layer_extruder = object->config().raft_layers == 1
? object->config().support_interface_filament-1 ? object->config().support_interface_filament-1
: object->config().support_filament-1; : object->config().support_filament-1;
// SM Orca: 使用物理挤出机的喷嘴直径
int physical_extruder = get_physical_extruder(first_layer_extruder); int physical_extruder = get_physical_extruder(first_layer_extruder);
first_layer_min_nozzle_diameter = (first_layer_extruder == size_t(-1)) ? first_layer_min_nozzle_diameter = (first_layer_extruder == size_t(-1)) ?
min_nozzle_diameter : min_nozzle_diameter :
@@ -1748,7 +1756,6 @@ Flow Print::brim_flow() const
extruders and take the one with, say, the smallest index. 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 The same logic should be applied to the code that selects the extruder during G-code
generation as well. */ generation as well. */
// SM Orca: 使用物理挤出机的喷嘴直径
int filament_idx = m_print_regions.front()->config().wall_filament - 1; int filament_idx = m_print_regions.front()->config().wall_filament - 1;
int physical_extruder = get_physical_extruder(filament_idx); int physical_extruder = get_physical_extruder(filament_idx);
return Flow::new_from_config_width( 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; 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 The same logic should be applied to the code that selects the extruder during G-code
generation as well. */ generation as well. */
// SM Orca: 使用物理挤出机的喷嘴直径
int filament_idx = m_objects.front()->config().support_filament - 1; int filament_idx = m_objects.front()->config().support_filament - 1;
int physical_extruder = get_physical_extruder(filament_idx); int physical_extruder = get_physical_extruder(filament_idx);
return Flow::new_from_config_width( return Flow::new_from_config_width(
@@ -1856,7 +1862,6 @@ void PrintObject::copy_layers_overhang_from_shared_object()
} }
} }
// BBS // BBS
BoundingBox PrintObject::get_first_layer_bbox(float& a, float& layer_height, std::string& name) 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)); append(m_first_layer_convex_hull.points, std::move(poly.points));
} }
if (has_skirt() && ! draft_shield) { if (has_skirt() && ! draft_shield) {
// In case that draft shield is NOT active, generate skirt now. // In case that draft shield is NOT active, generate skirt now.
// It will be placed around the brim, so brim has to be ready. // 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(); const Vec3d origin = this->get_plate_origin();
gcode.set_gcode_offset(origin(0), origin(1)); gcode.set_gcode_offset(origin(0), origin(1));
// SM Orca: Initialize filament-to-extruder mapping before it's used
this->initialize_filament_extruder_map(); 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) { 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); 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); gcode.do_export(this, path.c_str(), result, thumbnail_cb);
//BBS //BBS
result->conflict_result = m_conflict_result; 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() void Print::_make_skirt()
@@ -2396,7 +2420,6 @@ void Print::_make_skirt()
extruders_e_per_mm.reserve(set_extruders.size()); extruders_e_per_mm.reserve(set_extruders.size());
for (auto &extruder_id : set_extruders) { for (auto &extruder_id : set_extruders) {
extruders.push_back(extruder_id); extruders.push_back(extruder_id);
// SM Orca: 创建 Extruder 对象时传递物理挤出机ID
int physical_extruder_id = get_physical_extruder(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)); 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(); // wipe_tower.set_zhop();
// Set the extruder & material properties at the wipe tower object. // Set the extruder & material properties at the wipe tower object.
// SM Orca: 传递物理挤出机ID以支持耗材-挤出机映射
for (size_t i = 0; i < number_of_extruders; ++i) { for (size_t i = 0; i < number_of_extruders; ++i) {
int physical_extruder = get_physical_extruder(i); int physical_extruder = get_physical_extruder(i);
wipe_tower.set_extruder(i, physical_extruder, m_config); wipe_tower.set_extruder(i, physical_extruder, m_config);
@@ -2897,7 +2919,6 @@ void Print::_make_wipe_tower()
// wipe_tower.set_zhop(); // wipe_tower.set_zhop();
// Set the extruder & material properties at the wipe tower object. // Set the extruder & material properties at the wipe tower object.
// SM Orca: 传递物理挤出机ID以支持耗材-挤出机映射
for (size_t i = 0; i < number_of_extruders; ++i) { for (size_t i = 0; i < number_of_extruders; ++i) {
int physical_extruder = get_physical_extruder(i); int physical_extruder = get_physical_extruder(i);
wipe_tower.set_extruder(i, physical_extruder, m_config); 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. // 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. // 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_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("num_extruders", new ConfigOptionInt((int) m_config.nozzle_diameter.size()));
config.set_key_value("plate_name", new ConfigOptionString(get_plate_name())); 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(); GCodeProcessor::s_IsBBLPrinter = is_BBL_printer();
const Vec3d origin = this->get_plate_origin(); const Vec3d origin = this->get_plate_origin();
processor.set_xy_offset(origin(0), origin(1)); processor.set_xy_offset(origin(0), origin(1));
// SM Orca: 设置耗材到物理挤出机的映射
processor.set_filament_extruder_map(m_filament_extruder_map); processor.set_filament_extruder_map(m_filament_extruder_map);
//processor.enable_producers(true); //processor.enable_producers(true);
processor.process_file(file); 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::TotalFilamentUsedWipeTower = "total filament used for wipe tower [g]";
const std::string PrintStatistics::TotalFilamentUsedWipeTowerValueMask = "; total filament used for wipe tower [g] = %.2lf\n"; const std::string PrintStatistics::TotalFilamentUsedWipeTowerValueMask = "; total filament used for wipe tower [g] = %.2lf\n";
/*add json export/import related functions */ /*add json export/import related functions */
#define JSON_POLYGON_CONTOUR "contour" #define JSON_POLYGON_CONTOUR "contour"
#define JSON_POLYGON_HOLES "holes" #define JSON_POLYGON_HOLES "holes"
@@ -3196,7 +3218,6 @@ const std::string PrintStatistics::TotalFilamentUsedWipeTowerValueMask = "; tota
#define JSON_OBJECT_NAME "name" #define JSON_OBJECT_NAME "name"
#define JSON_IDENTIFY_ID "identify_id" #define JSON_IDENTIFY_ID "identify_id"
#define JSON_LAYERS "layers" #define JSON_LAYERS "layers"
#define JSON_SUPPORT_LAYERS "support_layers" #define JSON_SUPPORT_LAYERS "support_layers"
#define JSON_TREE_SUPPORT_LAYERS "tree_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_PERIMETERS "perimeters"
#define JSON_LAYER_REGION_FILLS "fills" #define JSON_LAYER_REGION_FILLS "fills"
#define JSON_SURF_TYPE "surface_type" #define JSON_SURF_TYPE "surface_type"
#define JSON_SURF_THICKNESS "thickness" #define JSON_SURF_THICKNESS "thickness"
#define JSON_SURF_THICKNESS_LAYER "thickness_layers" #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_NO_EXTRUSION "no_extrusion"
#define JSON_EXTRUSION_LOOP_ROLE "loop_role" #define JSON_EXTRUSION_LOOP_ROLE "loop_role"
static void to_json(json& j, const Points& p_s) { static void to_json(json& j, const Points& p_s) {
for (const Point& p : 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); j[JSON_ARC_CENTER] = std::move(center_point_json);
} }
static void to_json(json& j, const Polyline& poly_line) { static void to_json(json& j, const Polyline& poly_line) {
json points_json = json::array(), fittings_json = json::array(); json points_json = json::array(), fittings_json = json::array();
points_json = poly_line.points; points_json = poly_line.points;
@@ -3612,7 +3629,6 @@ static void from_json(const json& j, ArcSegment& arc_seg) {
return; return;
} }
static void from_json(const json& j, Polyline& poly_line) { static void from_json(const json& j, Polyline& poly_line) {
poly_line.points = j[JSON_POINTS]; poly_line.points = j[JSON_POINTS];
@@ -3823,7 +3839,6 @@ static void convert_layer_region_from_json(const json& j, LayerRegion& layer_reg
return; return;
} }
void extract_layer(const json& layer_json, Layer& layer) { void extract_layer(const json& layer_json, Layer& layer) {
//slice_polygons //slice_polygons
int slice_polygons_count = layer_json[JSON_LAYER_SLICED_POLYGONS].size(); 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; return ret;
} }
int Print::load_cached_data(const std::string& directory) int Print::load_cached_data(const std::string& directory)
{ {
int ret = 0; int ret = 0;
+21 -2
View File
@@ -894,10 +894,29 @@ public:
// SM Orca: 获取耗材-挤出机映射表 // SM Orca: 获取耗材-挤出机映射表
const std::unordered_map<int, int>& get_filament_extruder_map() const { return m_filament_extruder_map; } const std::unordered_map<int, int>& get_filament_extruder_map() const { return m_filament_extruder_map; }
// SM Orca: 获取物理挤出机ID(根据耗材索引) // SM Orca: 获取物理挤出机ID(根据耗材索引)
// 关键修复:当映射表为空时,使用模运算而不是直接返回耗材ID,避免越界
int get_physical_extruder(int filament_idx) const { int get_physical_extruder(int filament_idx) const {
auto it = m_filament_extruder_map.find(filament_idx); 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; return physical_extruder_id;
} }
// SM Orca: Initialize filament-to-physical-extruder mapping table // SM Orca: Initialize filament-to-physical-extruder mapping table
+190 -45
View File
@@ -3,6 +3,7 @@
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
#include <cfloat> #include <cfloat>
#include <sstream>
namespace Slic3r { namespace Slic3r {
@@ -216,50 +217,127 @@ static bool custom_per_printz_gcodes_tool_changes_differ(const std::vector<Custo
return false; return false;
} }
// SM Orca: Apply physical extruder mapping to filament parameters without overrides
// For each filament slot, if no override is provided, inherit from the mapped physical extruder // For each filament slot, if no override is provided, inherit from the mapped physical extruder
static void apply_physical_extruder_defaults( // IMPORTANT: This function creates a NEW target array with filament_count elements
ConfigOption* target, static ConfigOption* apply_physical_extruder_defaults(
const ConfigOption* filament_overrides, const ConfigOption* filament_overrides,
const ConfigOption* extruder_defaults, const ConfigOption* extruder_defaults,
size_t filament_count,
const std::unordered_map<int, int>& filament_extruder_map) const std::unordered_map<int, int>& filament_extruder_map)
{ {
if (!target->is_vector() || !extruder_defaults->is_vector()) if (!extruder_defaults->is_vector())
return; return nullptr;
auto* target_vec = dynamic_cast<ConfigOptionVectorBase*>(target);
auto* extruder_vec = dynamic_cast<const ConfigOptionVectorBase*>(extruder_defaults); auto* extruder_vec = dynamic_cast<const ConfigOptionVectorBase*>(extruder_defaults);
const ConfigOptionVectorBase* override_vec = filament_overrides ? const ConfigOptionVectorBase* override_vec = filament_overrides ?
dynamic_cast<const ConfigOptionVectorBase*>(filament_overrides) : nullptr; dynamic_cast<const ConfigOptionVectorBase*>(filament_overrides) : nullptr;
if (!target_vec || !extruder_vec) if (!extruder_vec)
return; return nullptr;
size_t num_filaments = target_vec->size(); // Clone the extruder defaults to create the target
auto* target = extruder_defaults->clone();
auto* target_vec = dynamic_cast<ConfigOptionVectorBase*>(target);
if (!target_vec) {
delete target;
return nullptr;
}
for (size_t filament_idx = 0; filament_idx < num_filaments; ++filament_idx) { // Resize target to filament_count
// Check if this filament has an override target_vec->resize(filament_count);
for (size_t filament_idx = 0; filament_idx < filament_count; ++filament_idx) {
bool has_override = false; bool has_override = false;
if (override_vec && filament_idx < override_vec->size()) { 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 no override, inherit from the mapped physical extruder if (physical_extruder_idx < extruder_vec->size()) {
if (!has_override) { // Try different types: double, int, bool
auto map_it = filament_extruder_map.find(filament_idx); auto* override_dbl = dynamic_cast<const ConfigOptionVector<double>*>(override_vec);
int physical_extruder_idx = (map_it != filament_extruder_map.end()) ? auto* extruder_dbl = dynamic_cast<const ConfigOptionVector<double>*>(extruder_vec);
map_it->second : filament_idx; 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);
if (physical_extruder_idx < extruder_vec->size()) { } else {
target_vec->set_at(extruder_vec, filament_idx, physical_extruder_idx); // Try int type
auto* override_int = dynamic_cast<const ConfigOptionVector<int>*>(override_vec);
auto* extruder_int = dynamic_cast<const ConfigOptionVector<int>*>(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<const ConfigOptionVector<unsigned char>*>(override_vec);
auto* extruder_bool = dynamic_cast<const ConfigOptionVector<unsigned char>*>(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 (!has_override) {
// No override: inherit 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() && 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. // Collect changes to print config, account for overrides of extruder retract values by filament presets.
//BBS: add plate index //BBS: add plate index
// SM Orca: add filament_extruder_map for physical extruder mapping
static t_config_option_keys print_config_diffs( static t_config_option_keys print_config_diffs(
const PrintConfig &current_config, const PrintConfig &current_config,
const DynamicPrintConfig &new_full_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 = 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 : const ConfigOption* opt_new_filament = (iter == extruder_retract_keys.end()) ? nullptr :
new_full_config.option(filament_prefix + opt_key); 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<const ConfigOptionVectorBase*>(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<ConfigOptionInt>("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. // An extruder retract override is available at some of the filament presets.
bool overriden = opt_new->overriden_by(opt_new_filament); 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(); auto opt_copy = opt_new->clone();
if (!((opt_key == "long_retractions_when_cut" || opt_key == "retraction_distances_when_cut") if (!((opt_key == "long_retractions_when_cut" || opt_key == "retraction_distances_when_cut")
&& new_full_config.option<ConfigOptionInt>("enable_long_retraction_when_cut")->value != LongRectrationLevel::EnableFilament)) // ugly code, remove it later if firmware supports && new_full_config.option<ConfigOptionInt>("enable_long_retraction_when_cut")->value != LongRectrationLevel::EnableFilament)) // ugly code, remove it later if firmware supports
opt_copy->apply_override(opt_new_filament); 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; bool changed = *opt_old != *opt_copy;
if (changed) if (changed)
print_diff.emplace_back(opt_key); 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") if ((opt_key == "long_retractions_when_cut" || opt_key == "retraction_distances_when_cut")
&& new_full_config.option<ConfigOptionInt>("enable_long_retraction_when_cut")->value != LongRectrationLevel::EnableFilament) && new_full_config.option<ConfigOptionInt>("enable_long_retraction_when_cut")->value != LongRectrationLevel::EnableFilament)
continue; continue;
// filament_overrides will be applied to the placeholder parser, which layers these parameters over full_print_config. // 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); 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; delete opt_copy;
} } else {
} else if (iter != extruder_retract_keys.end() && !filament_extruder_map.empty()) { delete opt_copy;
// 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 if (*opt_new != *opt_old) { } else if (*opt_new != *opt_old) {
//BBS: add plate_index logic for wipe_tower_x/wipe_tower_y //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 // BBS
int used_filaments = this->extruders(true).size(); 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(used_filaments);
new_full_config.normalize_fdm_1(); new_full_config.normalize_fdm_1();
t_config_option_keys changed_keys = new_full_config.normalize_fdm_2(objects().size(), used_filaments); 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. // Find modified keys of the various configs. Resolve overrides extruder retract values by filament profiles.
DynamicPrintConfig filament_overrides; DynamicPrintConfig filament_overrides;
//BBS: add plate index //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 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); 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. // 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 object_diff = m_default_object_config.diff(new_full_config);
t_config_option_keys region_diff = m_default_region_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<std::string> &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. // Do not use the ApplyStatus as we will use the max function when updating apply_status.
unsigned int apply_status = APPLY_STATUS_UNCHANGED; unsigned int apply_status = APPLY_STATUS_UNCHANGED;
auto update_apply_status = [&apply_status](bool invalidated) 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_full_print_config = std::move(new_full_config);
} }
// 在对象同步后,m_objects 可能已经被重建,需要重新计算映射以反映最新状态
// 这确保了后续使用映射表时(如 export_gcode)能获得正确的映射关系
this->initialize_filament_extruder_map();
// All regions now have distinct settings. // All regions now have distinct settings.
// Check whether applying the new region config defaults we would get different regions, // Check whether applying the new region config defaults we would get different regions,
// update regions or create regions from scratch. // update regions or create regions from scratch.
+2 -2
View File
@@ -136,12 +136,12 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt
} }
case coPercents:{ case coPercents:{
ConfigOptionPercents* vec_new = new ConfigOptionPercents{ boost::any_cast<double>(value) }; ConfigOptionPercents* vec_new = new ConfigOptionPercents{ boost::any_cast<double>(value) };
config.option<ConfigOptionPercents>(opt_key)->set_at(vec_new, opt_index, opt_index); config.option<ConfigOptionPercents>(opt_key)->set_at(vec_new, opt_index, 0); // SM Orca: Fix - use src_idx=0 for single-element vectors
break; break;
} }
case coFloats:{ case coFloats:{
ConfigOptionFloats* vec_new = new ConfigOptionFloats{ boost::any_cast<double>(value) }; ConfigOptionFloats* vec_new = new ConfigOptionFloats{ boost::any_cast<double>(value) };
config.option<ConfigOptionFloats>(opt_key)->set_at(vec_new, opt_index, opt_index); config.option<ConfigOptionFloats>(opt_key)->set_at(vec_new, opt_index, 0); // SM Orca: Fix - use src_idx=0 for single-element vectors
break; break;
} }
case coString: case coString:
+9 -11
View File
@@ -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; const std::string &opt_key = itOption.first;
int opt_index = itOption.second; 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); 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; auto itOption = it->second;
const std::string& opt_key = itOption.first; const std::string& opt_key = itOption.first;
int opt_index = itOption.second;
auto opt = m_config->option(opt_key); // SM Orca: FIX - Only modify the specific extruder's value, not all extruders
const ConfigOptionVectorBase* opt_vec = dynamic_cast<const ConfigOptionVectorBase*>(opt); // The original code iterated through all indices and set them to the same value,
if (opt_vec != nullptr) { // which caused all extruders to have identical values when editing one extruder
for (int opt_index = 0; opt_index < opt_vec->size(); opt_index++) { this->change_opt_value(opt_key, value, opt_index == -1 ? 0 : 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);
}
} }
OptionsGroup::on_change_OG(opt_id, value); OptionsGroup::on_change_OG(opt_id, value);
+40 -4
View File
@@ -3286,7 +3286,24 @@ void TabFilament::add_filament_overrides_page()
else { else {
const std::string printer_opt_key = opt_key.substr(strlen("filament_")); const std::string printer_opt_key = opt_key.substr(strlen("filament_"));
const auto printer_config = m_preset_bundle->printers.get_edited_preset().config; 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<ConfigOptionFloats>("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->update_na_value(printer_config_value);
field->set_na_value(); field->set_na_value();
} }
@@ -3301,7 +3318,7 @@ void TabFilament::add_filament_overrides_page()
optgroup->append_line(line); 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", for (const std::string opt_key : { "filament_retraction_length",
"filament_z_hop", "filament_z_hop",
@@ -3367,7 +3384,7 @@ void TabFilament::update_filament_overrides_page(const DynamicPrintConfig* print
// "filament_seam_gap" // "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() || const bool have_retract_length = m_config->option("filament_retraction_length")->is_nil() ||
m_config->opt_float("filament_retraction_length", extruder_idx) > 0; m_config->opt_float("filament_retraction_length", extruder_idx) > 0;
@@ -3399,7 +3416,26 @@ void TabFilament::update_filament_overrides_page(const DynamicPrintConfig* print
} else { } else {
if (!is_checked) { if (!is_checked) {
const std::string printer_opt_key = opt_key.substr(strlen("filament_")); 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<ConfigOptionFloats>("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->update_na_value(printer_config_value);
field->set_value(printer_config_value, false); field->set_value(printer_config_value, false);
} }
+200
View File
@@ -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号的耗材能够正确切片,不再越界
+127
View File
@@ -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<ConfigOptionPercents>(opt_key)->set_at(vec_new, opt_index, opt_index);
config.option<ConfigOptionFloats>(opt_key)->set_at(vec_new, opt_index, opt_index);
// 之后(正确):
config.option<ConfigOptionPercents>(opt_key)->set_at(vec_new, opt_index, 0); // SM Orca: Fix
config.option<ConfigOptionFloats>(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<ConfigOptionFloats>("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行删除