mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-28 05:12:27 +00:00
Refactor custom GCode handling and mixed filament management
- Updated `custom_tool_changes` function to use filament IDs instead of extruder IDs, ensuring better compatibility with mixed filament configurations. - Enhanced `MixedFilament` structure to include a `stable_id` for persistent identity across mixed filament entries, improving remapping during updates. - Introduced new methods for stable ID allocation and normalization in `MixedFilamentManager`. - Updated serialization and loading functions to maintain stable IDs, ensuring consistent behavior when mixed filament entries are modified. - Added tests to verify the correct handling of tool changes and stable ID remapping in mixed filament scenarios.
This commit is contained in:
@@ -57,16 +57,17 @@ extern void check_mode_for_custom_gcode_per_print_z(Info& info)
|
||||
info.mode = is_single_extruder ? SingleExtruder : MultiExtruder;
|
||||
}
|
||||
|
||||
// Return pairs of <print_z, 1-based extruder ID> sorted by increasing print_z from custom_gcode_per_print_z.
|
||||
// print_z corresponds to the first layer printed with the new extruder.
|
||||
std::vector<std::pair<double, unsigned int>> custom_tool_changes(const Info& custom_gcode_per_print_z, size_t num_extruders)
|
||||
// Return pairs of <print_z, 1-based filament ID> sorted by increasing print_z
|
||||
// from custom_gcode_per_print_z.
|
||||
std::vector<std::pair<double, unsigned int>> custom_tool_changes(const Info& custom_gcode_per_print_z, size_t num_filaments)
|
||||
{
|
||||
std::vector<std::pair<double, unsigned int>> custom_tool_changes;
|
||||
for (const Item& custom_gcode : custom_gcode_per_print_z.gcodes)
|
||||
if (custom_gcode.type == ToolChange) {
|
||||
// If extruder count in PrinterSettings was changed, use default (0) extruder for extruders, more than num_extruders
|
||||
// If available filament slots changed, fall back to filament 1 for
|
||||
// stale IDs beyond the current physical+mixed range.
|
||||
assert(custom_gcode.extruder >= 0);
|
||||
custom_tool_changes.emplace_back(custom_gcode.print_z, static_cast<unsigned int>(size_t(custom_gcode.extruder) > num_extruders ? 1 : custom_gcode.extruder));
|
||||
custom_tool_changes.emplace_back(custom_gcode.print_z, static_cast<unsigned int>(size_t(custom_gcode.extruder) > num_filaments ? 1 : custom_gcode.extruder));
|
||||
}
|
||||
return custom_tool_changes;
|
||||
}
|
||||
|
||||
@@ -120,9 +120,10 @@ struct Info
|
||||
// So, we should set CustomGCode::Info.mode should be updated considering code values from items.
|
||||
extern void check_mode_for_custom_gcode_per_print_z(Info& info);
|
||||
|
||||
// Return pairs of <print_z, 1-based extruder ID> sorted by increasing print_z from custom_gcode_per_print_z.
|
||||
// print_z corresponds to the first layer printed with the new extruder.
|
||||
std::vector<std::pair<double, unsigned int>> custom_tool_changes(const Info& custom_gcode_per_print_z, size_t num_extruders);
|
||||
// Return pairs of <print_z, 1-based filament ID> sorted by increasing print_z
|
||||
// from custom_gcode_per_print_z. The filament count may include mixed virtual
|
||||
// filaments in addition to physical ones.
|
||||
std::vector<std::pair<double, unsigned int>> custom_tool_changes(const Info& custom_gcode_per_print_z, size_t num_filaments);
|
||||
|
||||
} // namespace CustomGCode
|
||||
|
||||
|
||||
@@ -366,12 +366,13 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
|
||||
std::vector<std::pair<double, unsigned int>> per_layer_extruder_switches;
|
||||
|
||||
// BBS
|
||||
if (auto num_filaments = unsigned(print.config().filament_diameter.size());
|
||||
num_filaments > 1 && print.object_extruders().size() == 1 && // the current Print's configuration is CustomGCode::MultiAsSingle
|
||||
if (auto num_physical = unsigned(print.config().filament_diameter.size());
|
||||
num_physical > 1 && print.object_extruders().size() == 1 && // the current Print's configuration is CustomGCode::MultiAsSingle
|
||||
//BBS: replace model custom gcode with current plate custom gcode
|
||||
print.model().get_curr_plate_custom_gcodes().mode == CustomGCode::MultiAsSingle) {
|
||||
// Printing a single extruder platter on a printer with more than 1 extruder (or single-extruder multi-material).
|
||||
// There may be custom per-layer tool changes available at the model.
|
||||
const size_t num_filaments = (m_mixed_mgr == nullptr) ? num_physical : m_mixed_mgr->total_filaments(num_physical);
|
||||
per_layer_extruder_switches = custom_tool_changes(print.model().get_curr_plate_custom_gcodes(), num_filaments);
|
||||
}
|
||||
|
||||
|
||||
@@ -315,6 +315,7 @@ static bool use_component_b_advanced_dither(int layer_index, int ratio_a, int ra
|
||||
static bool parse_row_definition(const std::string &row,
|
||||
unsigned int &a,
|
||||
unsigned int &b,
|
||||
uint64_t &stable_id,
|
||||
bool &enabled,
|
||||
bool &custom,
|
||||
bool &origin_auto,
|
||||
@@ -352,13 +353,29 @@ static bool parse_row_definition(const std::string &row,
|
||||
}
|
||||
};
|
||||
|
||||
auto parse_uint64_token = [&trim_copy](const std::string &tok, uint64_t &out) {
|
||||
const std::string t = trim_copy(tok);
|
||||
if (t.empty())
|
||||
return false;
|
||||
try {
|
||||
size_t consumed = 0;
|
||||
const unsigned long long v = std::stoull(t, &consumed);
|
||||
if (consumed != t.size())
|
||||
return false;
|
||||
out = uint64_t(v);
|
||||
return true;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<std::string> tokens;
|
||||
std::stringstream ss(row);
|
||||
std::string token;
|
||||
while (std::getline(ss, token, ','))
|
||||
tokens.emplace_back(trim_copy(token));
|
||||
|
||||
if (tokens.size() < 4 || tokens.size() > 12)
|
||||
if (tokens.size() < 4 || tokens.size() > 13)
|
||||
return false;
|
||||
|
||||
int values[5] = { 0, 0, 1, 1, 50 };
|
||||
@@ -381,6 +398,7 @@ static bool parse_row_definition(const std::string &row,
|
||||
|
||||
a = unsigned(values[0]);
|
||||
b = unsigned(values[1]);
|
||||
stable_id = 0;
|
||||
enabled = (values[2] != 0);
|
||||
custom = (tokens.size() == 4) ? true : (values[3] != 0);
|
||||
origin_auto = !custom;
|
||||
@@ -440,6 +458,12 @@ static bool parse_row_definition(const std::string &row,
|
||||
origin_auto = parsed_origin_auto != 0;
|
||||
continue;
|
||||
}
|
||||
if (tok[0] == 'u' || tok[0] == 'U') {
|
||||
uint64_t parsed_stable_id = stable_id;
|
||||
if (parse_uint64_token(tok.substr(1), parsed_stable_id))
|
||||
stable_id = parsed_stable_id;
|
||||
continue;
|
||||
}
|
||||
manual_pattern = tok;
|
||||
}
|
||||
|
||||
@@ -685,6 +709,22 @@ static std::vector<unsigned int> build_weighted_gradient_sequence(const std::vec
|
||||
// MixedFilamentManager
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
uint64_t MixedFilamentManager::allocate_stable_id()
|
||||
{
|
||||
const uint64_t stable_id = std::max<uint64_t>(1, m_next_stable_id);
|
||||
m_next_stable_id = stable_id + 1;
|
||||
return stable_id;
|
||||
}
|
||||
|
||||
uint64_t MixedFilamentManager::normalize_stable_id(uint64_t stable_id)
|
||||
{
|
||||
if (stable_id == 0)
|
||||
return allocate_stable_id();
|
||||
if (stable_id >= m_next_stable_id)
|
||||
m_next_stable_id = stable_id + 1;
|
||||
return stable_id;
|
||||
}
|
||||
|
||||
void MixedFilamentManager::auto_generate(const std::vector<std::string> &filament_colours)
|
||||
{
|
||||
// Keep a copy of the old list so we can preserve user-modified ratios and
|
||||
@@ -703,7 +743,9 @@ void MixedFilamentManager::auto_generate(const std::vector<std::string> &filamen
|
||||
continue;
|
||||
if (prev.component_a == 0 || prev.component_b == 0 || prev.component_a > n || prev.component_b > n || prev.component_a == prev.component_b)
|
||||
continue;
|
||||
custom_rows.push_back(prev);
|
||||
MixedFilament custom = prev;
|
||||
custom.stable_id = normalize_stable_id(custom.stable_id);
|
||||
custom_rows.push_back(std::move(custom));
|
||||
}
|
||||
|
||||
// Generate all C(N,2) pairwise combinations.
|
||||
@@ -727,11 +769,13 @@ void MixedFilamentManager::auto_generate(const std::vector<std::string> &filamen
|
||||
prev.component_b == mf.component_b) {
|
||||
mf.enabled = prev.enabled;
|
||||
mf.deleted = prev.deleted;
|
||||
mf.stable_id = prev.stable_id;
|
||||
if (mf.deleted)
|
||||
mf.enabled = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mf.stable_id = normalize_stable_id(mf.stable_id);
|
||||
m_mixed.push_back(mf);
|
||||
}
|
||||
}
|
||||
@@ -781,6 +825,7 @@ void MixedFilamentManager::add_custom_filament(unsigned int component_a,
|
||||
MixedFilament mf;
|
||||
mf.component_a = component_a;
|
||||
mf.component_b = component_b;
|
||||
mf.stable_id = allocate_stable_id();
|
||||
mf.mix_b_percent = clamp_int(mix_b_percent, 0, 100);
|
||||
mf.ratio_a = 1;
|
||||
mf.ratio_b = 1;
|
||||
@@ -840,14 +885,15 @@ void MixedFilamentManager::apply_gradient_settings(int gradient_mode,
|
||||
}
|
||||
}
|
||||
|
||||
std::string MixedFilamentManager::serialize_custom_entries() const
|
||||
std::string MixedFilamentManager::serialize_custom_entries()
|
||||
{
|
||||
std::ostringstream ss;
|
||||
bool first = true;
|
||||
for (const MixedFilament &mf : m_mixed) {
|
||||
for (MixedFilament &mf : m_mixed) {
|
||||
if (!first)
|
||||
ss << ';';
|
||||
first = false;
|
||||
mf.stable_id = normalize_stable_id(mf.stable_id);
|
||||
const std::string normalized_ids = normalize_gradient_component_ids(mf.gradient_component_ids);
|
||||
const std::string normalized_weights = normalize_gradient_component_weights(mf.gradient_component_weights, normalized_ids.size());
|
||||
ss << mf.component_a << ','
|
||||
@@ -860,7 +906,8 @@ std::string MixedFilamentManager::serialize_custom_entries() const
|
||||
<< 'w' << normalized_weights << ','
|
||||
<< 'm' << clamp_int(mf.distribution_mode, int(MixedFilament::LayerCycle), int(MixedFilament::Simple)) << ','
|
||||
<< 'd' << (mf.deleted ? 1 : 0) << ','
|
||||
<< 'o' << (mf.origin_auto ? 1 : 0);
|
||||
<< 'o' << (mf.origin_auto ? 1 : 0) << ','
|
||||
<< 'u' << mf.stable_id;
|
||||
const std::string normalized_pattern = normalize_manual_pattern(mf.manual_pattern);
|
||||
if (!normalized_pattern.empty())
|
||||
ss << ',' << normalized_pattern;
|
||||
@@ -898,6 +945,15 @@ void MixedFilamentManager::load_custom_entries(const std::string &serialized, co
|
||||
std::vector<MixedFilament> rebuilt;
|
||||
rebuilt.reserve(m_mixed.size() + 8);
|
||||
std::set<std::pair<unsigned int, unsigned int>> consumed_auto_pairs;
|
||||
std::set<uint64_t> used_stable_ids;
|
||||
auto dedupe_stable_id = [this, &used_stable_ids](uint64_t stable_id) {
|
||||
stable_id = normalize_stable_id(stable_id);
|
||||
if (used_stable_ids.insert(stable_id).second)
|
||||
return stable_id;
|
||||
uint64_t replacement = allocate_stable_id();
|
||||
used_stable_ids.insert(replacement);
|
||||
return replacement;
|
||||
};
|
||||
|
||||
std::stringstream all(serialized);
|
||||
std::string row;
|
||||
@@ -907,6 +963,7 @@ void MixedFilamentManager::load_custom_entries(const std::string &serialized, co
|
||||
++parsed_rows;
|
||||
unsigned int a = 0;
|
||||
unsigned int b = 0;
|
||||
uint64_t stable_id = 0;
|
||||
bool enabled = true;
|
||||
bool custom = true;
|
||||
bool origin_auto = false;
|
||||
@@ -917,7 +974,7 @@ void MixedFilamentManager::load_custom_entries(const std::string &serialized, co
|
||||
std::string manual_pattern;
|
||||
int distribution_mode = int(MixedFilament::Simple);
|
||||
bool deleted = false;
|
||||
if (!parse_row_definition(row, a, b, enabled, custom, origin_auto, mix, pointillism_all_filaments,
|
||||
if (!parse_row_definition(row, a, b, stable_id, enabled, custom, origin_auto, mix, pointillism_all_filaments,
|
||||
gradient_component_ids, gradient_component_weights, manual_pattern, distribution_mode, deleted)) {
|
||||
++skipped_rows;
|
||||
BOOST_LOG_TRIVIAL(warning) << "MixedFilamentManager::load_custom_entries invalid row format: " << row;
|
||||
@@ -959,6 +1016,7 @@ void MixedFilamentManager::load_custom_entries(const std::string &serialized, co
|
||||
MixedFilament mf = *it_auto;
|
||||
mf.component_a = key.first;
|
||||
mf.component_b = key.second;
|
||||
mf.stable_id = dedupe_stable_id(stable_id != 0 ? stable_id : mf.stable_id);
|
||||
mf.enabled = enabled;
|
||||
mf.pointillism_all_filaments = pointillism_all_filaments;
|
||||
mf.gradient_component_ids = normalize_gradient_component_ids(gradient_component_ids);
|
||||
@@ -982,6 +1040,7 @@ void MixedFilamentManager::load_custom_entries(const std::string &serialized, co
|
||||
MixedFilament mf;
|
||||
mf.component_a = a;
|
||||
mf.component_b = b;
|
||||
mf.stable_id = dedupe_stable_id(stable_id);
|
||||
mf.mix_b_percent = mix;
|
||||
mf.ratio_a = 1;
|
||||
mf.ratio_b = 1;
|
||||
@@ -1012,6 +1071,7 @@ void MixedFilamentManager::load_custom_entries(const std::string &serialized, co
|
||||
MixedFilament mf = auto_mf;
|
||||
mf.component_a = key.first;
|
||||
mf.component_b = key.second;
|
||||
mf.stable_id = dedupe_stable_id(mf.stable_id);
|
||||
mf.custom = false;
|
||||
mf.origin_auto = true;
|
||||
rebuilt.push_back(std::move(mf));
|
||||
|
||||
@@ -27,6 +27,10 @@ struct MixedFilament
|
||||
unsigned int component_a = 1;
|
||||
unsigned int component_b = 2;
|
||||
|
||||
// Persistent row identity used to keep painted virtual-tool assignments
|
||||
// stable even when the visible mixed-filament list is rebuilt.
|
||||
uint64_t stable_id = 0;
|
||||
|
||||
// Layer-alternation ratio. With ratio_a = 2, ratio_b = 1 the cycle is
|
||||
// A, A, B, A, A, B, ...
|
||||
int ratio_a = 1;
|
||||
@@ -77,6 +81,7 @@ struct MixedFilament
|
||||
{
|
||||
return component_a == rhs.component_a &&
|
||||
component_b == rhs.component_b &&
|
||||
stable_id == rhs.stable_id &&
|
||||
ratio_a == rhs.ratio_a &&
|
||||
ratio_b == rhs.ratio_b &&
|
||||
mix_b_percent == rhs.mix_b_percent &&
|
||||
@@ -133,8 +138,9 @@ public:
|
||||
float upper_bound,
|
||||
bool advanced_dithering = false);
|
||||
|
||||
// Persist only custom rows.
|
||||
std::string serialize_custom_entries() const;
|
||||
// Persist mixed rows, including auto/deleted state, into the compact
|
||||
// project-settings string.
|
||||
std::string serialize_custom_entries();
|
||||
void load_custom_entries(const std::string &serialized, const std::vector<std::string> &filament_colours);
|
||||
|
||||
// Normalize a manual mixed-pattern string into compact token form.
|
||||
@@ -196,12 +202,15 @@ private:
|
||||
}
|
||||
|
||||
void refresh_display_colors(const std::vector<std::string> &filament_colours);
|
||||
uint64_t allocate_stable_id();
|
||||
uint64_t normalize_stable_id(uint64_t stable_id);
|
||||
|
||||
std::vector<MixedFilament> m_mixed;
|
||||
int m_gradient_mode = 0;
|
||||
float m_height_lower_bound = 0.04f;
|
||||
float m_height_upper_bound = 0.16f;
|
||||
bool m_advanced_dithering = false;
|
||||
uint64_t m_next_stable_id = 1;
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <fstream>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/algorithm/clamp.hpp>
|
||||
@@ -3365,51 +3366,84 @@ void PresetBundle::update_multi_material_filament_presets(size_t to_delete_filam
|
||||
// Build old->new filament ID remap for painted facet data normalization.
|
||||
// This is needed for both deletion and addition of physical filaments so
|
||||
// painted mixed states keep pointing at the same virtual mixed entries.
|
||||
if (old_num_filaments != num_filaments || deleting_filament) {
|
||||
const unsigned int deleted_1based = deleting_filament ? unsigned(to_delete_filament_id + 1) : 0u;
|
||||
size_t old_enabled_mixed = 0;
|
||||
for (const auto &mf : old_mixed)
|
||||
if (mf.enabled)
|
||||
++old_enabled_mixed;
|
||||
if (old_num_filaments != num_filaments || deleting_filament)
|
||||
build_filament_id_remap(old_mixed, old_num_filaments, num_filaments, deleting_filament,
|
||||
deleting_filament ? unsigned(to_delete_filament_id + 1) : 0u);
|
||||
}
|
||||
|
||||
const size_t old_total_filaments = old_num_filaments + old_enabled_mixed;
|
||||
m_last_filament_id_remap.assign(old_total_filaments + 1, 0);
|
||||
void PresetBundle::update_mixed_filament_id_remap(const std::vector<MixedFilament> &old_mixed,
|
||||
size_t old_num_filaments,
|
||||
size_t new_num_filaments)
|
||||
{
|
||||
build_filament_id_remap(old_mixed, old_num_filaments, new_num_filaments, false, 0u);
|
||||
}
|
||||
|
||||
for (unsigned int old_id = 1; old_id <= unsigned(old_num_filaments); ++old_id) {
|
||||
unsigned int mapped = 0;
|
||||
if (deleting_filament && old_id == deleted_1based) {
|
||||
mapped = 0;
|
||||
} else if (old_id <= unsigned(num_filaments)) {
|
||||
mapped = old_id;
|
||||
if (deleting_filament && old_id > deleted_1based)
|
||||
--mapped;
|
||||
void PresetBundle::build_filament_id_remap(const std::vector<MixedFilament> &old_mixed,
|
||||
size_t old_num_filaments,
|
||||
size_t new_num_filaments,
|
||||
bool deleting_filament,
|
||||
unsigned int deleted_1based)
|
||||
{
|
||||
size_t old_enabled_mixed = 0;
|
||||
for (const auto &mf : old_mixed)
|
||||
if (mf.enabled)
|
||||
++old_enabled_mixed;
|
||||
|
||||
const size_t old_total_filaments = old_num_filaments + old_enabled_mixed;
|
||||
m_last_filament_id_remap.assign(old_total_filaments + 1, 0);
|
||||
|
||||
for (unsigned int old_id = 1; old_id <= unsigned(old_num_filaments); ++old_id) {
|
||||
unsigned int mapped = 0;
|
||||
if (deleting_filament && old_id == deleted_1based) {
|
||||
mapped = 0;
|
||||
} else if (old_id <= unsigned(new_num_filaments)) {
|
||||
mapped = old_id;
|
||||
if (deleting_filament && old_id > deleted_1based)
|
||||
--mapped;
|
||||
}
|
||||
m_last_filament_id_remap[old_id] = mapped;
|
||||
}
|
||||
|
||||
auto canonical_pair = [](unsigned int a, unsigned int b) {
|
||||
return std::make_pair(std::min(a, b), std::max(a, b));
|
||||
};
|
||||
|
||||
std::unordered_map<uint64_t, unsigned int> new_stable_id_to_virtual_id;
|
||||
std::map<std::pair<unsigned int, unsigned int>, std::vector<unsigned int>> new_pair_to_ids;
|
||||
unsigned int next_virtual_id = unsigned(new_num_filaments + 1);
|
||||
for (const auto &mf : this->mixed_filaments.mixed_filaments()) {
|
||||
if (!mf.enabled)
|
||||
continue;
|
||||
if (mf.stable_id != 0)
|
||||
new_stable_id_to_virtual_id.emplace(mf.stable_id, next_virtual_id);
|
||||
new_pair_to_ids[canonical_pair(mf.component_a, mf.component_b)].push_back(next_virtual_id++);
|
||||
}
|
||||
|
||||
std::map<std::pair<unsigned int, unsigned int>, size_t> used_per_pair;
|
||||
size_t stable_id_hits = 0;
|
||||
size_t fallback_pair_hits = 0;
|
||||
size_t missing_hits = 0;
|
||||
unsigned int old_virtual_id = unsigned(old_num_filaments + 1);
|
||||
for (const auto &mf : old_mixed) {
|
||||
if (!mf.enabled)
|
||||
continue;
|
||||
|
||||
unsigned int a = mf.component_a;
|
||||
unsigned int b = mf.component_b;
|
||||
if (a == deleted_1based || b == deleted_1based) {
|
||||
m_last_filament_id_remap[old_virtual_id] = 0;
|
||||
++missing_hits;
|
||||
} else {
|
||||
bool mapped_by_stable_id = false;
|
||||
if (mf.stable_id != 0) {
|
||||
auto it_stable = new_stable_id_to_virtual_id.find(mf.stable_id);
|
||||
if (it_stable != new_stable_id_to_virtual_id.end()) {
|
||||
m_last_filament_id_remap[old_virtual_id] = it_stable->second;
|
||||
mapped_by_stable_id = true;
|
||||
++stable_id_hits;
|
||||
}
|
||||
}
|
||||
m_last_filament_id_remap[old_id] = mapped;
|
||||
}
|
||||
|
||||
auto canonical_pair = [](unsigned int a, unsigned int b) {
|
||||
return std::make_pair(std::min(a, b), std::max(a, b));
|
||||
};
|
||||
|
||||
std::map<std::pair<unsigned int, unsigned int>, std::vector<unsigned int>> new_pair_to_ids;
|
||||
unsigned int next_virtual_id = unsigned(num_filaments + 1);
|
||||
for (const auto &mf : this->mixed_filaments.mixed_filaments()) {
|
||||
if (!mf.enabled)
|
||||
continue;
|
||||
new_pair_to_ids[canonical_pair(mf.component_a, mf.component_b)].push_back(next_virtual_id++);
|
||||
}
|
||||
|
||||
std::map<std::pair<unsigned int, unsigned int>, size_t> used_per_pair;
|
||||
unsigned int old_virtual_id = unsigned(old_num_filaments + 1);
|
||||
for (const auto &mf : old_mixed) {
|
||||
if (!mf.enabled)
|
||||
continue;
|
||||
|
||||
unsigned int a = mf.component_a;
|
||||
unsigned int b = mf.component_b;
|
||||
if (a == deleted_1based || b == deleted_1based) {
|
||||
m_last_filament_id_remap[old_virtual_id] = 0;
|
||||
} else {
|
||||
if (!mapped_by_stable_id) {
|
||||
if (deleting_filament) {
|
||||
if (a > deleted_1based)
|
||||
--a;
|
||||
@@ -3420,42 +3454,48 @@ void PresetBundle::update_multi_material_filament_presets(size_t to_delete_filam
|
||||
auto it = new_pair_to_ids.find(key);
|
||||
if (it == new_pair_to_ids.end()) {
|
||||
m_last_filament_id_remap[old_virtual_id] = 0;
|
||||
++missing_hits;
|
||||
} else {
|
||||
size_t &used = used_per_pair[key];
|
||||
if (used >= it->second.size()) {
|
||||
m_last_filament_id_remap[old_virtual_id] = 0;
|
||||
++missing_hits;
|
||||
} else {
|
||||
m_last_filament_id_remap[old_virtual_id] = it->second[used++];
|
||||
++fallback_pair_hits;
|
||||
}
|
||||
}
|
||||
}
|
||||
++old_virtual_id;
|
||||
}
|
||||
|
||||
auto summarize_uint_vector = [](const std::vector<unsigned int> &values, size_t max_items = 24) {
|
||||
std::string out = "[";
|
||||
const size_t n = std::min(values.size(), max_items);
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
if (i > 0)
|
||||
out += ",";
|
||||
out += std::to_string(values[i]);
|
||||
}
|
||||
if (values.size() > n)
|
||||
out += ",...";
|
||||
out += "]";
|
||||
return out;
|
||||
};
|
||||
|
||||
BOOST_LOG_TRIVIAL(warning) << "MF_REMAP preset_bundle"
|
||||
<< " old_physical=" << old_num_filaments
|
||||
<< " new_physical=" << num_filaments
|
||||
<< " deleting=" << (deleting_filament ? 1 : 0)
|
||||
<< " deleted_id=" << deleted_1based
|
||||
<< " old_mixed_enabled=" << old_enabled_mixed
|
||||
<< " new_mixed_enabled=" << this->mixed_filaments.enabled_count()
|
||||
<< " remap_size=" << m_last_filament_id_remap.size()
|
||||
<< " remap=" << summarize_uint_vector(m_last_filament_id_remap);
|
||||
++old_virtual_id;
|
||||
}
|
||||
|
||||
auto summarize_uint_vector = [](const std::vector<unsigned int> &values, size_t max_items = 24) {
|
||||
std::string out = "[";
|
||||
const size_t n = std::min(values.size(), max_items);
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
if (i > 0)
|
||||
out += ",";
|
||||
out += std::to_string(values[i]);
|
||||
}
|
||||
if (values.size() > n)
|
||||
out += ",...";
|
||||
out += "]";
|
||||
return out;
|
||||
};
|
||||
|
||||
BOOST_LOG_TRIVIAL(warning) << "MF_REMAP preset_bundle"
|
||||
<< " old_physical=" << old_num_filaments
|
||||
<< " new_physical=" << new_num_filaments
|
||||
<< " deleting=" << (deleting_filament ? 1 : 0)
|
||||
<< " deleted_id=" << deleted_1based
|
||||
<< " old_mixed_enabled=" << old_enabled_mixed
|
||||
<< " new_mixed_enabled=" << this->mixed_filaments.enabled_count()
|
||||
<< " stable_id_hits=" << stable_id_hits
|
||||
<< " fallback_pair_hits=" << fallback_pair_hits
|
||||
<< " missing_hits=" << missing_hits
|
||||
<< " remap_size=" << m_last_filament_id_remap.size()
|
||||
<< " remap=" << summarize_uint_vector(m_last_filament_id_remap);
|
||||
}
|
||||
|
||||
void PresetBundle::update_compatible(PresetSelectCompatibleType select_other_print_if_incompatible, PresetSelectCompatibleType select_other_filament_if_incompatible)
|
||||
|
||||
@@ -254,6 +254,11 @@ public:
|
||||
// update size and content of filament_presets.
|
||||
void update_multi_material_filament_presets(size_t to_delete_filament_id = size_t(-1),
|
||||
size_t old_num_filaments = size_t(-1));
|
||||
// Rebuild old->new virtual filament mapping after mixed-row enable/delete
|
||||
// changes when the physical filament count itself did not change.
|
||||
void update_mixed_filament_id_remap(const std::vector<MixedFilament> &old_mixed,
|
||||
size_t old_num_filaments,
|
||||
size_t new_num_filaments);
|
||||
// Mapping generated during the latest filament count change.
|
||||
// Index is old 1-based filament ID, value is new 1-based filament ID (0 = removed).
|
||||
const std::vector<unsigned int>& last_filament_id_remap() const { return m_last_filament_id_remap; }
|
||||
@@ -312,6 +317,11 @@ private:
|
||||
std::pair<PresetsConfigSubstitutions, std::string> load_system_presets_from_json(ForwardCompatibilitySubstitutionRule compatibility_rule);
|
||||
// Merge one vendor's presets with the other vendor's presets, report duplicates.
|
||||
std::vector<std::string> merge_presets(PresetBundle &&other);
|
||||
void build_filament_id_remap(const std::vector<MixedFilament> &old_mixed,
|
||||
size_t old_num_filaments,
|
||||
size_t new_num_filaments,
|
||||
bool deleting_filament,
|
||||
unsigned int deleted_1based);
|
||||
// Update renamed_from and alias maps of system profiles.
|
||||
void update_system_maps();
|
||||
|
||||
|
||||
@@ -484,10 +484,11 @@ std::vector<unsigned int> Print::extruders(bool conside_custom_gcode) const
|
||||
|
||||
if (conside_custom_gcode) {
|
||||
//BBS
|
||||
int num_extruders = m_config.filament_colour.size();
|
||||
const size_t num_physical = m_config.filament_colour.size();
|
||||
const size_t num_filaments = m_mixed_filament_mgr.total_filaments(num_physical);
|
||||
if (m_model.plates_custom_gcodes.find(m_model.curr_plate_index) != m_model.plates_custom_gcodes.end()) {
|
||||
for (auto item : m_model.plates_custom_gcodes.at(m_model.curr_plate_index).gcodes) {
|
||||
if (item.type == CustomGCode::Type::ToolChange && item.extruder <= num_extruders)
|
||||
if (item.type == CustomGCode::Type::ToolChange && item.extruder <= int(num_filaments))
|
||||
extruders.push_back((unsigned int)(item.extruder - 1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1439,9 +1439,10 @@ std::vector<int> PartPlate::get_extruders(bool conside_custom_gcode) const
|
||||
int nums_extruders = 0;
|
||||
if (const ConfigOptionStrings *color_option = dynamic_cast<const ConfigOptionStrings *>(wxGetApp().preset_bundle->project_config.option("filament_colour"))) {
|
||||
nums_extruders = color_option->values.size();
|
||||
const size_t total_filaments = wxGetApp().preset_bundle->mixed_filaments.total_filaments(size_t(nums_extruders));
|
||||
if (m_model->plates_custom_gcodes.find(m_plate_index) != m_model->plates_custom_gcodes.end()) {
|
||||
for (auto item : m_model->plates_custom_gcodes.at(m_plate_index).gcodes) {
|
||||
if (item.type == CustomGCode::Type::ToolChange && item.extruder <= nums_extruders)
|
||||
if (item.type == CustomGCode::Type::ToolChange && item.extruder <= int(total_filaments))
|
||||
plate_extruders.push_back(item.extruder);
|
||||
}
|
||||
}
|
||||
@@ -1561,9 +1562,12 @@ std::vector<int> PartPlate::get_extruders_under_cli(bool conside_custom_gcode, D
|
||||
int nums_extruders = 0;
|
||||
if (const ConfigOptionStrings *color_option = dynamic_cast<const ConfigOptionStrings *>(full_config.option("filament_colour"))) {
|
||||
nums_extruders = color_option->values.size();
|
||||
size_t total_filaments = size_t(nums_extruders);
|
||||
if (wxGetApp().preset_bundle != nullptr)
|
||||
total_filaments = wxGetApp().preset_bundle->mixed_filaments.total_filaments(total_filaments);
|
||||
if (m_model->plates_custom_gcodes.find(m_plate_index) != m_model->plates_custom_gcodes.end()) {
|
||||
for (auto item : m_model->plates_custom_gcodes.at(m_plate_index).gcodes) {
|
||||
if (item.type == CustomGCode::Type::ToolChange && item.extruder <= nums_extruders)
|
||||
if (item.type == CustomGCode::Type::ToolChange && item.extruder <= int(total_filaments))
|
||||
plate_extruders.push_back(item.extruder);
|
||||
}
|
||||
}
|
||||
@@ -1606,9 +1610,10 @@ std::vector<int> PartPlate::get_extruders_without_support(bool conside_custom_gc
|
||||
int nums_extruders = 0;
|
||||
if (const ConfigOptionStrings* color_option = dynamic_cast<const ConfigOptionStrings*>(wxGetApp().preset_bundle->project_config.option("filament_colour"))) {
|
||||
nums_extruders = color_option->values.size();
|
||||
const size_t total_filaments = wxGetApp().preset_bundle->mixed_filaments.total_filaments(size_t(nums_extruders));
|
||||
if (m_model->plates_custom_gcodes.find(m_plate_index) != m_model->plates_custom_gcodes.end()) {
|
||||
for (auto item : m_model->plates_custom_gcodes.at(m_plate_index).gcodes) {
|
||||
if (item.type == CustomGCode::Type::ToolChange && item.extruder <= nums_extruders)
|
||||
if (item.type == CustomGCode::Type::ToolChange && item.extruder <= int(total_filaments))
|
||||
plate_extruders.push_back(item.extruder);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4400,7 +4400,10 @@ void Sidebar::update_mixed_filament_panel()
|
||||
return wxString::Format("(F%u + F%u)", unsigned(entry.component_a), unsigned(entry.component_b));
|
||||
};
|
||||
|
||||
auto apply_mixed_entry_changes = [this, preset_bundle, print_cfg](size_t mixed_id, const MixedFilament &updated_mf, bool preserve_enabled = false) {
|
||||
auto apply_mixed_entry_changes = [this, preset_bundle, print_cfg, num_physical](size_t mixed_id,
|
||||
const MixedFilament &updated_mf,
|
||||
bool preserve_enabled = false,
|
||||
bool rebuild_virtual_id_remap = false) {
|
||||
if (!preset_bundle)
|
||||
return;
|
||||
|
||||
@@ -4409,6 +4412,7 @@ void Sidebar::update_mixed_filament_panel()
|
||||
if (mixed_id >= mfs.size())
|
||||
return;
|
||||
|
||||
const std::vector<MixedFilament> old_mixed = rebuild_virtual_id_remap ? mfs : std::vector<MixedFilament>();
|
||||
MixedFilament merged = updated_mf;
|
||||
if (preserve_enabled)
|
||||
merged.enabled = mfs[mixed_id].enabled;
|
||||
@@ -4435,6 +4439,9 @@ void Sidebar::update_mixed_filament_panel()
|
||||
if (wxGetApp().plater())
|
||||
wxGetApp().plater()->update_project_dirty_from_presets();
|
||||
|
||||
if (rebuild_virtual_id_remap)
|
||||
preset_bundle->update_mixed_filament_id_remap(old_mixed, num_physical, num_physical);
|
||||
|
||||
int mode = 0;
|
||||
if (const ConfigOptionBool *opt = preset_bundle->project_config.option<ConfigOptionBool>("mixed_filament_gradient_mode"))
|
||||
mode = opt->value ? 1 : 0;
|
||||
@@ -4452,6 +4459,9 @@ void Sidebar::update_mixed_filament_panel()
|
||||
hi = std::max(lo, hi);
|
||||
mgr.apply_gradient_settings(mode, lo, hi, advanced);
|
||||
update_dynamic_filament_list();
|
||||
|
||||
if (rebuild_virtual_id_remap && wxGetApp().plater())
|
||||
wxGetApp().plater()->on_filaments_change(num_physical);
|
||||
};
|
||||
|
||||
size_t visible_mixed_idx = 0;
|
||||
@@ -4507,7 +4517,7 @@ void Sidebar::update_mixed_filament_panel()
|
||||
return;
|
||||
MixedFilament updated = mfs[mixed_id];
|
||||
updated.enabled = enabled_chk->GetValue();
|
||||
apply_mixed_entry_changes(mixed_id, updated, false);
|
||||
apply_mixed_entry_changes(mixed_id, updated, false, true);
|
||||
});
|
||||
|
||||
auto *del_btn = new ScalableButton(header_panel, wxID_ANY, "cross");
|
||||
@@ -4519,6 +4529,7 @@ void Sidebar::update_mixed_filament_panel()
|
||||
auto &mgr = wxGetApp().preset_bundle->mixed_filaments;
|
||||
auto &mfs = mgr.mixed_filaments();
|
||||
if (mixed_id < mfs.size()) {
|
||||
const std::vector<MixedFilament> old_mixed = mfs;
|
||||
auto canonical_pair = [](unsigned int a, unsigned int b) {
|
||||
return std::make_pair(std::min(a, b), std::max(a, b));
|
||||
};
|
||||
@@ -4571,10 +4582,12 @@ void Sidebar::update_mixed_filament_panel()
|
||||
}
|
||||
p->m_expanded_mixed_filament_rows.clear();
|
||||
set_mixed_string("mixed_filament_definitions", mgr.serialize_custom_entries());
|
||||
wxGetApp().preset_bundle->update_mixed_filament_id_remap(old_mixed, num_physical, num_physical);
|
||||
notify_mixed_change();
|
||||
if (wxGetApp().plater())
|
||||
wxGetApp().plater()->update_project_dirty_from_presets();
|
||||
update_mixed_filament_panel();
|
||||
if (wxGetApp().plater())
|
||||
wxGetApp().plater()->on_filaments_change(num_physical);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -16894,30 +16907,6 @@ void Plater::on_filaments_change(size_t num_filaments)
|
||||
}
|
||||
}
|
||||
|
||||
// Add-flow is deterministic: old virtual filament IDs must shift by
|
||||
// +delta_physical because physical IDs were prepended.
|
||||
// Keep this rule authoritative to avoid stale/partial remaps.
|
||||
if (num_filaments > old_num_filaments) {
|
||||
const size_t delta = num_filaments - old_num_filaments;
|
||||
const size_t old_first_virtual = old_num_filaments + 1;
|
||||
size_t old_total = 0;
|
||||
if (!id_remap.empty() && id_remap.size() > 1)
|
||||
old_total = std::min(id_remap.size() - 1, state_map.size() - 1);
|
||||
else
|
||||
old_total = std::min((total_filaments >= delta ? total_filaments - delta : 0), state_map.size() - 1);
|
||||
|
||||
if (old_total >= old_first_virtual) {
|
||||
should_remap_states = true;
|
||||
for (size_t i = old_first_virtual; i <= old_total; ++i) {
|
||||
const size_t mapped = i + delta;
|
||||
if (mapped >= state_map.size() || mapped > total_filaments)
|
||||
state_map[i] = EnforcerBlockerType::NONE;
|
||||
else
|
||||
state_map[i] = EnforcerBlockerType(mapped);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t changed_entries = 0;
|
||||
std::string changed_map_preview = "[";
|
||||
for (size_t i = 1; i < state_map.size(); ++i) {
|
||||
|
||||
Reference in New Issue
Block a user