mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-16 13:32:44 +00:00
Add release notes for v0.92 and refactor mixed filament gradient handling
- Introduced a new `RELEASE_NOTES_v0.92.md` file detailing highlights such as gradual gradient behavior, mixed filament indexing fixes, and editable automatic mixed filaments. - Refactored `compute_gradient_ratios` to remove the cycle layers parameter, implementing a new gradual integer cadence for gradient transitions. - Updated `MixedFilament` structure to include an `origin_auto` flag for better management of auto-generated entries. - Adjusted related parsing, serialization, and UI handling to reflect these changes, ensuring improved user experience and color fidelity in mixed filament rendering.
This commit is contained in:
@@ -0,0 +1,49 @@
|
|||||||
|
# Snapmaker Orca Full Spectrum v0.92 (Pre-release)
|
||||||
|
|
||||||
|
Mixed-filament stability and workflow release.
|
||||||
|
Based on Snapmaker Orca v2.2.4.
|
||||||
|
|
||||||
|
## Highlights
|
||||||
|
|
||||||
|
### Gradient Behavior Is Now Gradual
|
||||||
|
- Removed the `Mixed filament layer cycle` setting.
|
||||||
|
- Reworked non-height-weighted gradient math to use gradual integer cadence by ratio (minority side anchors to 1 layer, majority side scales progressively).
|
||||||
|
- This prevents abrupt jumps in layer cadence and makes transitions more predictable.
|
||||||
|
|
||||||
|
### Mixed Filament Indexing And Mapping Fixes
|
||||||
|
- Fixed mixed-filament row labeling so IDs stay compact after deletions in the Mixed Filaments panel.
|
||||||
|
- Fixed regeneration order so auto-generated/precomputed mixes are appended at the end of existing mixed rows instead of being inserted at the front.
|
||||||
|
- Fixed paint/index remapping when adding physical or virtual filaments so existing painted mixed regions keep their intended virtual filament mapping.
|
||||||
|
- Fixed edge case where the first mixed filament could be temporarily replaced by a newly added physical filament.
|
||||||
|
|
||||||
|
### Automatic Mixed Filaments Are Editable
|
||||||
|
- Automatic mixed rows can now be expanded and edited through the gradient controls.
|
||||||
|
- Edited auto-origin rows are treated as user-managed entries.
|
||||||
|
- Deleting an edited auto-origin row now keeps that auto pair tombstoned (it will not be regenerated immediately and overwrite user intent).
|
||||||
|
|
||||||
|
### Gradient Preview Color Fidelity
|
||||||
|
- Updated gradient preview blending to use the FilamentMixer library path for color reproduction.
|
||||||
|
- Preview swatches now better match expected mixed output.
|
||||||
|
|
||||||
|
### Selective Expansion/Contraction Fix
|
||||||
|
- Updated selective mixed-zone contraction so positive values trim only the outside-facing (visible) boundary band.
|
||||||
|
- Internal/shared boundaries are no longer symmetrically contracted.
|
||||||
|
- Outward expansion behavior remains unchanged.
|
||||||
|
|
||||||
|
## Also Included (v0.91 Carry-Forward Fixes)
|
||||||
|
- Windows startup/log path handling for non-ASCII usernames (UTF-8-safe path resolution).
|
||||||
|
- Mixed-filament swatch desync/gray-regression fixes in preview rendering.
|
||||||
|
|
||||||
|
## Internal/Config Changes
|
||||||
|
- Removed `mixed_filament_cycle_layers` from config/UI wiring.
|
||||||
|
- Extended mixed filament serialized metadata with auto-origin tracking to preserve delete/regenerate intent across reloads.
|
||||||
|
- Added diagnostic logging around mixed-filament remap and selective-indentation application paths.
|
||||||
|
|
||||||
|
## Important Notes
|
||||||
|
- Experimental build with limited testing.
|
||||||
|
- Some projects may show different gradient cadence behavior due to the removal of explicit layer-cycle control.
|
||||||
|
- Use at your own risk.
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
- FilamentMixer color blending integration is powered by FilamentMixer by [justinh-rahb](https://github.com/justinh-rahb).
|
||||||
|
- Library: [https://github.com/justinh-rahb/filament-mixer](https://github.com/justinh-rahb/filament-mixer)
|
||||||
+112
-28
@@ -10,6 +10,7 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
@@ -231,7 +232,7 @@ static void normalize_ratio_pair(int &a, int &b)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void compute_gradient_ratios(MixedFilament &mf, int gradient_mode, float lower_bound, float upper_bound, int cycle_layers)
|
static void compute_gradient_ratios(MixedFilament &mf, int gradient_mode, float lower_bound, float upper_bound)
|
||||||
{
|
{
|
||||||
if (gradient_mode == 1) {
|
if (gradient_mode == 1) {
|
||||||
// Height-weighted mode:
|
// Height-weighted mode:
|
||||||
@@ -245,12 +246,25 @@ static void compute_gradient_ratios(MixedFilament &mf, int gradient_mode, float
|
|||||||
mf.ratio_b = std::max(1, safe_ratio_from_height(h_b, unit));
|
mf.ratio_b = std::max(1, safe_ratio_from_height(h_b, unit));
|
||||||
} else {
|
} else {
|
||||||
// Layer-cycle mode:
|
// Layer-cycle mode:
|
||||||
// distribute an integer cycle directly by blend percentages.
|
// derive a gradual integer cadence directly from the blend ratio
|
||||||
|
// by fixing the minority side to one layer and scaling the majority.
|
||||||
const int mix_b = clamp_int(mf.mix_b_percent, 0, 100);
|
const int mix_b = clamp_int(mf.mix_b_percent, 0, 100);
|
||||||
const float pct_b = float(mix_b) / 100.f;
|
if (mix_b <= 0) {
|
||||||
const int cycle = std::max(2, cycle_layers);
|
mf.ratio_a = 1;
|
||||||
mf.ratio_b = clamp_int(int(std::lround(pct_b * cycle)), 0, cycle);
|
mf.ratio_b = 0;
|
||||||
mf.ratio_a = cycle - mf.ratio_b;
|
} else if (mix_b >= 100) {
|
||||||
|
mf.ratio_a = 0;
|
||||||
|
mf.ratio_b = 1;
|
||||||
|
} else {
|
||||||
|
const int pct_b = mix_b;
|
||||||
|
const int pct_a = 100 - pct_b;
|
||||||
|
const bool b_is_major = pct_b >= pct_a;
|
||||||
|
const int major_pct = b_is_major ? pct_b : pct_a;
|
||||||
|
const int minor_pct = b_is_major ? pct_a : pct_b;
|
||||||
|
const int major_layers = std::max(1, int(std::lround(double(major_pct) / double(std::max(1, minor_pct)))));
|
||||||
|
mf.ratio_a = b_is_major ? 1 : major_layers;
|
||||||
|
mf.ratio_b = b_is_major ? major_layers : 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
normalize_ratio_pair(mf.ratio_a, mf.ratio_b);
|
normalize_ratio_pair(mf.ratio_a, mf.ratio_b);
|
||||||
@@ -303,6 +317,7 @@ static bool parse_row_definition(const std::string &row,
|
|||||||
unsigned int &b,
|
unsigned int &b,
|
||||||
bool &enabled,
|
bool &enabled,
|
||||||
bool &custom,
|
bool &custom,
|
||||||
|
bool &origin_auto,
|
||||||
int &mix_b_percent,
|
int &mix_b_percent,
|
||||||
bool &pointillism_all_filaments,
|
bool &pointillism_all_filaments,
|
||||||
std::string &gradient_component_ids,
|
std::string &gradient_component_ids,
|
||||||
@@ -368,6 +383,7 @@ static bool parse_row_definition(const std::string &row,
|
|||||||
b = unsigned(values[1]);
|
b = unsigned(values[1]);
|
||||||
enabled = (values[2] != 0);
|
enabled = (values[2] != 0);
|
||||||
custom = (tokens.size() == 4) ? true : (values[3] != 0);
|
custom = (tokens.size() == 4) ? true : (values[3] != 0);
|
||||||
|
origin_auto = !custom;
|
||||||
mix_b_percent = clamp_int(values[4], 0, 100);
|
mix_b_percent = clamp_int(values[4], 0, 100);
|
||||||
pointillism_all_filaments = false;
|
pointillism_all_filaments = false;
|
||||||
gradient_component_ids.clear();
|
gradient_component_ids.clear();
|
||||||
@@ -418,6 +434,12 @@ static bool parse_row_definition(const std::string &row,
|
|||||||
deleted = parsed_deleted != 0;
|
deleted = parsed_deleted != 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (tok[0] == 'o' || tok[0] == 'O') {
|
||||||
|
int parsed_origin_auto = origin_auto ? 1 : 0;
|
||||||
|
if (parse_int_token(tok.substr(1), parsed_origin_auto))
|
||||||
|
origin_auto = parsed_origin_auto != 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
manual_pattern = tok;
|
manual_pattern = tok;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -696,6 +718,7 @@ void MixedFilamentManager::auto_generate(const std::vector<std::string> &filamen
|
|||||||
mf.enabled = true;
|
mf.enabled = true;
|
||||||
mf.deleted = false;
|
mf.deleted = false;
|
||||||
mf.custom = false;
|
mf.custom = false;
|
||||||
|
mf.origin_auto = true;
|
||||||
|
|
||||||
// Try to preserve previous settings.
|
// Try to preserve previous settings.
|
||||||
for (const auto &prev : old) {
|
for (const auto &prev : old) {
|
||||||
@@ -769,6 +792,7 @@ void MixedFilamentManager::add_custom_filament(unsigned int component_a,
|
|||||||
mf.enabled = true;
|
mf.enabled = true;
|
||||||
mf.deleted = false;
|
mf.deleted = false;
|
||||||
mf.custom = true;
|
mf.custom = true;
|
||||||
|
mf.origin_auto = false;
|
||||||
m_mixed.push_back(std::move(mf));
|
m_mixed.push_back(std::move(mf));
|
||||||
refresh_display_colors(filament_colours);
|
refresh_display_colors(filament_colours);
|
||||||
}
|
}
|
||||||
@@ -799,13 +823,11 @@ std::string MixedFilamentManager::normalize_manual_pattern(const std::string &pa
|
|||||||
void MixedFilamentManager::apply_gradient_settings(int gradient_mode,
|
void MixedFilamentManager::apply_gradient_settings(int gradient_mode,
|
||||||
float lower_bound,
|
float lower_bound,
|
||||||
float upper_bound,
|
float upper_bound,
|
||||||
int cycle_layers,
|
|
||||||
bool advanced_dithering)
|
bool advanced_dithering)
|
||||||
{
|
{
|
||||||
m_gradient_mode = (gradient_mode != 0) ? 1 : 0;
|
m_gradient_mode = (gradient_mode != 0) ? 1 : 0;
|
||||||
m_height_lower_bound = std::max(0.01f, lower_bound);
|
m_height_lower_bound = std::max(0.01f, lower_bound);
|
||||||
m_height_upper_bound = std::max(m_height_lower_bound, upper_bound);
|
m_height_upper_bound = std::max(m_height_lower_bound, upper_bound);
|
||||||
m_cycle_layers = std::max(2, cycle_layers);
|
|
||||||
m_advanced_dithering = advanced_dithering;
|
m_advanced_dithering = advanced_dithering;
|
||||||
|
|
||||||
for (MixedFilament &mf : m_mixed) {
|
for (MixedFilament &mf : m_mixed) {
|
||||||
@@ -814,7 +836,7 @@ void MixedFilamentManager::apply_gradient_settings(int gradient_mode,
|
|||||||
mf.ratio_b = 1;
|
mf.ratio_b = 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
compute_gradient_ratios(mf, m_gradient_mode, m_height_lower_bound, m_height_upper_bound, m_cycle_layers);
|
compute_gradient_ratios(mf, m_gradient_mode, m_height_lower_bound, m_height_upper_bound);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -837,7 +859,8 @@ std::string MixedFilamentManager::serialize_custom_entries() const
|
|||||||
<< 'g' << normalized_ids << ','
|
<< 'g' << normalized_ids << ','
|
||||||
<< 'w' << normalized_weights << ','
|
<< 'w' << normalized_weights << ','
|
||||||
<< 'm' << clamp_int(mf.distribution_mode, int(MixedFilament::LayerCycle), int(MixedFilament::Simple)) << ','
|
<< 'm' << clamp_int(mf.distribution_mode, int(MixedFilament::LayerCycle), int(MixedFilament::Simple)) << ','
|
||||||
<< 'd' << (mf.deleted ? 1 : 0);
|
<< 'd' << (mf.deleted ? 1 : 0) << ','
|
||||||
|
<< 'o' << (mf.origin_auto ? 1 : 0);
|
||||||
const std::string normalized_pattern = normalize_manual_pattern(mf.manual_pattern);
|
const std::string normalized_pattern = normalize_manual_pattern(mf.manual_pattern);
|
||||||
if (!normalized_pattern.empty())
|
if (!normalized_pattern.empty())
|
||||||
ss << ',' << normalized_pattern;
|
ss << ',' << normalized_pattern;
|
||||||
@@ -858,8 +881,24 @@ void MixedFilamentManager::load_custom_entries(const std::string &serialized, co
|
|||||||
size_t parsed_rows = 0;
|
size_t parsed_rows = 0;
|
||||||
size_t loaded_rows = 0;
|
size_t loaded_rows = 0;
|
||||||
size_t updated_auto = 0;
|
size_t updated_auto = 0;
|
||||||
|
size_t appended_auto = 0;
|
||||||
size_t skipped_rows = 0;
|
size_t skipped_rows = 0;
|
||||||
|
|
||||||
|
auto canonical_pair = [](unsigned int a, unsigned int b) {
|
||||||
|
return std::make_pair(std::min(a, b), std::max(a, b));
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<MixedFilament> auto_rows;
|
||||||
|
auto_rows.reserve(m_mixed.size());
|
||||||
|
for (const MixedFilament &mf : m_mixed) {
|
||||||
|
if (!mf.custom)
|
||||||
|
auto_rows.push_back(mf);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<MixedFilament> rebuilt;
|
||||||
|
rebuilt.reserve(m_mixed.size() + 8);
|
||||||
|
std::set<std::pair<unsigned int, unsigned int>> consumed_auto_pairs;
|
||||||
|
|
||||||
std::stringstream all(serialized);
|
std::stringstream all(serialized);
|
||||||
std::string row;
|
std::string row;
|
||||||
while (std::getline(all, row, ';')) {
|
while (std::getline(all, row, ';')) {
|
||||||
@@ -870,6 +909,7 @@ void MixedFilamentManager::load_custom_entries(const std::string &serialized, co
|
|||||||
unsigned int b = 0;
|
unsigned int b = 0;
|
||||||
bool enabled = true;
|
bool enabled = true;
|
||||||
bool custom = true;
|
bool custom = true;
|
||||||
|
bool origin_auto = false;
|
||||||
int mix = 50;
|
int mix = 50;
|
||||||
bool pointillism_all_filaments = false;
|
bool pointillism_all_filaments = false;
|
||||||
std::string gradient_component_ids;
|
std::string gradient_component_ids;
|
||||||
@@ -877,7 +917,7 @@ void MixedFilamentManager::load_custom_entries(const std::string &serialized, co
|
|||||||
std::string manual_pattern;
|
std::string manual_pattern;
|
||||||
int distribution_mode = int(MixedFilament::Simple);
|
int distribution_mode = int(MixedFilament::Simple);
|
||||||
bool deleted = false;
|
bool deleted = false;
|
||||||
if (!parse_row_definition(row, a, b, enabled, custom, mix, pointillism_all_filaments,
|
if (!parse_row_definition(row, a, b, enabled, custom, origin_auto, mix, pointillism_all_filaments,
|
||||||
gradient_component_ids, gradient_component_weights, manual_pattern, distribution_mode, deleted)) {
|
gradient_component_ids, gradient_component_weights, manual_pattern, distribution_mode, deleted)) {
|
||||||
++skipped_rows;
|
++skipped_rows;
|
||||||
BOOST_LOG_TRIVIAL(warning) << "MixedFilamentManager::load_custom_entries invalid row format: " << row;
|
BOOST_LOG_TRIVIAL(warning) << "MixedFilamentManager::load_custom_entries invalid row format: " << row;
|
||||||
@@ -894,24 +934,49 @@ void MixedFilamentManager::load_custom_entries(const std::string &serialized, co
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!custom) {
|
if (!custom) {
|
||||||
auto it_auto = std::find_if(m_mixed.begin(), m_mixed.end(), [a, b](const MixedFilament &mf) {
|
const auto key = canonical_pair(a, b);
|
||||||
return !mf.custom && mf.component_a == a && mf.component_b == b;
|
if (consumed_auto_pairs.count(key) != 0) {
|
||||||
});
|
++skipped_rows;
|
||||||
if (it_auto != m_mixed.end()) {
|
BOOST_LOG_TRIVIAL(warning) << "MixedFilamentManager::load_custom_entries duplicate auto row"
|
||||||
it_auto->enabled = enabled;
|
<< ", row=" << row
|
||||||
it_auto->pointillism_all_filaments = pointillism_all_filaments;
|
<< ", a=" << key.first
|
||||||
it_auto->gradient_component_ids = normalize_gradient_component_ids(gradient_component_ids);
|
<< ", b=" << key.second;
|
||||||
it_auto->gradient_component_weights =
|
|
||||||
normalize_gradient_component_weights(gradient_component_weights, it_auto->gradient_component_ids.size());
|
|
||||||
it_auto->manual_pattern = normalize_manual_pattern(manual_pattern);
|
|
||||||
it_auto->distribution_mode = clamp_int(distribution_mode, int(MixedFilament::LayerCycle), int(MixedFilament::Simple));
|
|
||||||
it_auto->mix_b_percent = it_auto->manual_pattern.empty() ? mix : mix_percent_from_normalized_pattern(it_auto->manual_pattern);
|
|
||||||
it_auto->deleted = deleted;
|
|
||||||
if (it_auto->deleted)
|
|
||||||
it_auto->enabled = false;
|
|
||||||
++updated_auto;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto it_auto = std::find_if(auto_rows.begin(), auto_rows.end(), [key, canonical_pair](const MixedFilament &mf) {
|
||||||
|
return canonical_pair(mf.component_a, mf.component_b) == key;
|
||||||
|
});
|
||||||
|
if (it_auto == auto_rows.end()) {
|
||||||
|
++skipped_rows;
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "MixedFilamentManager::load_custom_entries auto row missing after regenerate"
|
||||||
|
<< ", row=" << row
|
||||||
|
<< ", a=" << key.first
|
||||||
|
<< ", b=" << key.second;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
MixedFilament mf = *it_auto;
|
||||||
|
mf.component_a = key.first;
|
||||||
|
mf.component_b = key.second;
|
||||||
|
mf.enabled = enabled;
|
||||||
|
mf.pointillism_all_filaments = pointillism_all_filaments;
|
||||||
|
mf.gradient_component_ids = normalize_gradient_component_ids(gradient_component_ids);
|
||||||
|
mf.gradient_component_weights =
|
||||||
|
normalize_gradient_component_weights(gradient_component_weights, mf.gradient_component_ids.size());
|
||||||
|
mf.manual_pattern = normalize_manual_pattern(manual_pattern);
|
||||||
|
mf.distribution_mode = clamp_int(distribution_mode, int(MixedFilament::LayerCycle), int(MixedFilament::Simple));
|
||||||
|
mf.mix_b_percent = mf.manual_pattern.empty() ? mix : mix_percent_from_normalized_pattern(mf.manual_pattern);
|
||||||
|
mf.deleted = deleted;
|
||||||
|
if (mf.deleted)
|
||||||
|
mf.enabled = false;
|
||||||
|
mf.custom = false;
|
||||||
|
mf.origin_auto = true;
|
||||||
|
|
||||||
|
rebuilt.push_back(std::move(mf));
|
||||||
|
consumed_auto_pairs.insert(key);
|
||||||
|
++updated_auto;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
MixedFilament mf;
|
MixedFilament mf;
|
||||||
@@ -933,15 +998,34 @@ void MixedFilamentManager::load_custom_entries(const std::string &serialized, co
|
|||||||
if (mf.deleted)
|
if (mf.deleted)
|
||||||
mf.enabled = false;
|
mf.enabled = false;
|
||||||
mf.custom = custom;
|
mf.custom = custom;
|
||||||
m_mixed.push_back(std::move(mf));
|
mf.origin_auto = origin_auto;
|
||||||
|
rebuilt.push_back(std::move(mf));
|
||||||
++loaded_rows;
|
++loaded_rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep any newly generated auto rows that were not present in serialized
|
||||||
|
// definitions and append them at the end to preserve existing virtual IDs.
|
||||||
|
for (const MixedFilament &auto_mf : auto_rows) {
|
||||||
|
const auto key = canonical_pair(auto_mf.component_a, auto_mf.component_b);
|
||||||
|
if (consumed_auto_pairs.count(key) != 0)
|
||||||
|
continue;
|
||||||
|
MixedFilament mf = auto_mf;
|
||||||
|
mf.component_a = key.first;
|
||||||
|
mf.component_b = key.second;
|
||||||
|
mf.custom = false;
|
||||||
|
mf.origin_auto = true;
|
||||||
|
rebuilt.push_back(std::move(mf));
|
||||||
|
++appended_auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_mixed = std::move(rebuilt);
|
||||||
refresh_display_colors(filament_colours);
|
refresh_display_colors(filament_colours);
|
||||||
BOOST_LOG_TRIVIAL(info) << "MixedFilamentManager::load_custom_entries"
|
BOOST_LOG_TRIVIAL(info) << "MixedFilamentManager::load_custom_entries"
|
||||||
<< ", physical_count=" << n
|
<< ", physical_count=" << n
|
||||||
<< ", parsed_rows=" << parsed_rows
|
<< ", parsed_rows=" << parsed_rows
|
||||||
<< ", loaded_rows=" << loaded_rows
|
<< ", loaded_rows=" << loaded_rows
|
||||||
<< ", updated_auto_rows=" << updated_auto
|
<< ", updated_auto_rows=" << updated_auto
|
||||||
|
<< ", appended_auto_rows=" << appended_auto
|
||||||
<< ", skipped_rows=" << skipped_rows
|
<< ", skipped_rows=" << skipped_rows
|
||||||
<< ", mixed_total=" << m_mixed.size();
|
<< ", mixed_total=" << m_mixed.size();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,11 @@ struct MixedFilament
|
|||||||
// True when this row was user-created (custom) instead of auto-generated.
|
// True when this row was user-created (custom) instead of auto-generated.
|
||||||
bool custom = false;
|
bool custom = false;
|
||||||
|
|
||||||
|
// True when this row originated from an auto-generated pair. This remains
|
||||||
|
// true even after editing so delete logic can keep the base auto pair
|
||||||
|
// tombstoned instead of letting regeneration resurrect it.
|
||||||
|
bool origin_auto = false;
|
||||||
|
|
||||||
// Computed display colour as "#RRGGBB".
|
// Computed display colour as "#RRGGBB".
|
||||||
std::string display_color;
|
std::string display_color;
|
||||||
|
|
||||||
@@ -82,7 +87,8 @@ struct MixedFilament
|
|||||||
distribution_mode == rhs.distribution_mode &&
|
distribution_mode == rhs.distribution_mode &&
|
||||||
enabled == rhs.enabled &&
|
enabled == rhs.enabled &&
|
||||||
deleted == rhs.deleted &&
|
deleted == rhs.deleted &&
|
||||||
custom == rhs.custom;
|
custom == rhs.custom &&
|
||||||
|
origin_auto == rhs.origin_auto;
|
||||||
}
|
}
|
||||||
bool operator!=(const MixedFilament &rhs) const { return !(*this == rhs); }
|
bool operator!=(const MixedFilament &rhs) const { return !(*this == rhs); }
|
||||||
};
|
};
|
||||||
@@ -125,7 +131,6 @@ public:
|
|||||||
void apply_gradient_settings(int gradient_mode,
|
void apply_gradient_settings(int gradient_mode,
|
||||||
float lower_bound,
|
float lower_bound,
|
||||||
float upper_bound,
|
float upper_bound,
|
||||||
int cycle_layers,
|
|
||||||
bool advanced_dithering = false);
|
bool advanced_dithering = false);
|
||||||
|
|
||||||
// Persist only custom rows.
|
// Persist only custom rows.
|
||||||
@@ -196,7 +201,6 @@ private:
|
|||||||
int m_gradient_mode = 0;
|
int m_gradient_mode = 0;
|
||||||
float m_height_lower_bound = 0.04f;
|
float m_height_lower_bound = 0.04f;
|
||||||
float m_height_upper_bound = 0.16f;
|
float m_height_upper_bound = 0.16f;
|
||||||
int m_cycle_layers = 4;
|
|
||||||
bool m_advanced_dithering = false;
|
bool m_advanced_dithering = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ static std::vector<std::string> s_project_options {
|
|||||||
"mixed_filament_gradient_mode",
|
"mixed_filament_gradient_mode",
|
||||||
"mixed_filament_height_lower_bound",
|
"mixed_filament_height_lower_bound",
|
||||||
"mixed_filament_height_upper_bound",
|
"mixed_filament_height_upper_bound",
|
||||||
"mixed_filament_cycle_layers",
|
|
||||||
"mixed_filament_advanced_dithering",
|
"mixed_filament_advanced_dithering",
|
||||||
"mixed_filament_surface_indentation",
|
"mixed_filament_surface_indentation",
|
||||||
"mixed_filament_definitions",
|
"mixed_filament_definitions",
|
||||||
@@ -1869,7 +1868,7 @@ void PresetBundle::update_num_filaments(unsigned int to_del_filament_id)
|
|||||||
ams_multi_color_filment.resize(to_del_filament_id);
|
ams_multi_color_filment.resize(to_del_filament_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
update_multi_material_filament_presets(to_del_filament_id);
|
update_multi_material_filament_presets(to_del_filament_id, old_filament_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PresetBundle::set_num_filaments(unsigned int n, std::vector<std::string> new_colors) {
|
void PresetBundle::set_num_filaments(unsigned int n, std::vector<std::string> new_colors) {
|
||||||
@@ -1890,7 +1889,7 @@ void PresetBundle::set_num_filaments(unsigned int n, std::vector<std::string> ne
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
update_multi_material_filament_presets();
|
update_multi_material_filament_presets(size_t(-1), size_t(old_filament_count));
|
||||||
}
|
}
|
||||||
void PresetBundle::set_num_filaments(unsigned int n, std::string new_color)
|
void PresetBundle::set_num_filaments(unsigned int n, std::string new_color)
|
||||||
{
|
{
|
||||||
@@ -1914,7 +1913,7 @@ void PresetBundle::set_num_filaments(unsigned int n, std::string new_color)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
update_multi_material_filament_presets();
|
update_multi_material_filament_presets(size_t(-1), size_t(old_filament_count));
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int PresetBundle::sync_ams_list(unsigned int &unknowns)
|
unsigned int PresetBundle::sync_ams_list(unsigned int &unknowns)
|
||||||
@@ -3226,7 +3225,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
|
|||||||
return std::make_pair(std::move(substitutions), presets_loaded);
|
return std::make_pair(std::move(substitutions), presets_loaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PresetBundle::update_multi_material_filament_presets(size_t to_delete_filament_id)
|
void PresetBundle::update_multi_material_filament_presets(size_t to_delete_filament_id, size_t old_num_filaments_arg)
|
||||||
{
|
{
|
||||||
if (printers.get_edited_preset().printer_technology() != ptFFF)
|
if (printers.get_edited_preset().printer_technology() != ptFFF)
|
||||||
return;
|
return;
|
||||||
@@ -3245,7 +3244,9 @@ void PresetBundle::update_multi_material_filament_presets(size_t to_delete_filam
|
|||||||
size_t num_filaments = this->filament_presets.size();
|
size_t num_filaments = this->filament_presets.size();
|
||||||
#endif
|
#endif
|
||||||
const bool deleting_filament = (to_delete_filament_id != size_t(-1));
|
const bool deleting_filament = (to_delete_filament_id != size_t(-1));
|
||||||
const size_t old_num_filaments = deleting_filament ? (num_filaments + 1) : num_filaments;
|
const size_t old_num_filaments = (old_num_filaments_arg != size_t(-1))
|
||||||
|
? old_num_filaments_arg
|
||||||
|
: (deleting_filament ? (num_filaments + 1) : num_filaments);
|
||||||
const std::vector<MixedFilament> old_mixed = this->mixed_filaments.mixed_filaments();
|
const std::vector<MixedFilament> old_mixed = this->mixed_filaments.mixed_filaments();
|
||||||
m_last_filament_id_remap.clear();
|
m_last_filament_id_remap.clear();
|
||||||
|
|
||||||
@@ -3288,13 +3289,6 @@ void PresetBundle::update_multi_material_filament_presets(size_t to_delete_filam
|
|||||||
ConfigOptionStrings *color_opt = this->project_config.option<ConfigOptionStrings>("filament_colour");
|
ConfigOptionStrings *color_opt = this->project_config.option<ConfigOptionStrings>("filament_colour");
|
||||||
if (color_opt) {
|
if (color_opt) {
|
||||||
DynamicPrintConfig &print_cfg = this->prints.get_edited_preset().config;
|
DynamicPrintConfig &print_cfg = this->prints.get_edited_preset().config;
|
||||||
auto get_mixed_int = [this, &print_cfg](const std::string &key, int fallback) {
|
|
||||||
if (this->project_config.has(key))
|
|
||||||
return this->project_config.opt_int(key);
|
|
||||||
if (print_cfg.has(key))
|
|
||||||
return print_cfg.opt_int(key);
|
|
||||||
return fallback;
|
|
||||||
};
|
|
||||||
auto get_mixed_bool = [this, &print_cfg](const std::string &key, bool fallback) {
|
auto get_mixed_bool = [this, &print_cfg](const std::string &key, bool fallback) {
|
||||||
if (const ConfigOptionBool *opt = this->project_config.option<ConfigOptionBool>(key))
|
if (const ConfigOptionBool *opt = this->project_config.option<ConfigOptionBool>(key))
|
||||||
return opt->value;
|
return opt->value;
|
||||||
@@ -3354,16 +3348,14 @@ void PresetBundle::update_multi_material_filament_presets(size_t to_delete_filam
|
|||||||
int gradient_mode = get_mixed_mode(false) ? 1 : 0;
|
int gradient_mode = get_mixed_mode(false) ? 1 : 0;
|
||||||
float lower_bound = get_mixed_float("mixed_filament_height_lower_bound", 0.04f);
|
float lower_bound = get_mixed_float("mixed_filament_height_lower_bound", 0.04f);
|
||||||
float upper_bound = get_mixed_float("mixed_filament_height_upper_bound", 0.16f);
|
float upper_bound = get_mixed_float("mixed_filament_height_upper_bound", 0.16f);
|
||||||
int cycle_layers = get_mixed_int("mixed_filament_cycle_layers", 4);
|
|
||||||
bool advanced_dithering = get_mixed_bool("mixed_filament_advanced_dithering", false);
|
bool advanced_dithering = get_mixed_bool("mixed_filament_advanced_dithering", false);
|
||||||
gradient_mode = std::clamp(gradient_mode, 0, 1);
|
gradient_mode = std::clamp(gradient_mode, 0, 1);
|
||||||
lower_bound = std::max(0.01f, lower_bound);
|
lower_bound = std::max(0.01f, lower_bound);
|
||||||
upper_bound = std::max(lower_bound, upper_bound);
|
upper_bound = std::max(lower_bound, upper_bound);
|
||||||
cycle_layers = std::max(2, cycle_layers);
|
|
||||||
|
|
||||||
this->mixed_filaments.clear_custom_entries();
|
this->mixed_filaments.clear_custom_entries();
|
||||||
this->mixed_filaments.load_custom_entries(get_mixed_string("mixed_filament_definitions"), color_opt->values);
|
this->mixed_filaments.load_custom_entries(get_mixed_string("mixed_filament_definitions"), color_opt->values);
|
||||||
this->mixed_filaments.apply_gradient_settings(gradient_mode, lower_bound, upper_bound, cycle_layers, advanced_dithering);
|
this->mixed_filaments.apply_gradient_settings(gradient_mode, lower_bound, upper_bound, advanced_dithering);
|
||||||
|
|
||||||
const std::string serialized = this->mixed_filaments.serialize_custom_entries();
|
const std::string serialized = this->mixed_filaments.serialize_custom_entries();
|
||||||
set_mixed_string("mixed_filament_definitions", serialized);
|
set_mixed_string("mixed_filament_definitions", serialized);
|
||||||
@@ -3371,9 +3363,10 @@ void PresetBundle::update_multi_material_filament_presets(size_t to_delete_filam
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build old->new filament ID remap for painted facet data normalization.
|
// Build old->new filament ID remap for painted facet data normalization.
|
||||||
if (deleting_filament) {
|
// This is needed for both deletion and addition of physical filaments so
|
||||||
const unsigned int deleted_1based = unsigned(to_delete_filament_id + 1);
|
// 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;
|
size_t old_enabled_mixed = 0;
|
||||||
for (const auto &mf : old_mixed)
|
for (const auto &mf : old_mixed)
|
||||||
if (mf.enabled)
|
if (mf.enabled)
|
||||||
@@ -3383,20 +3376,30 @@ void PresetBundle::update_multi_material_filament_presets(size_t to_delete_filam
|
|||||||
m_last_filament_id_remap.assign(old_total_filaments + 1, 0);
|
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) {
|
for (unsigned int old_id = 1; old_id <= unsigned(old_num_filaments); ++old_id) {
|
||||||
if (old_id == deleted_1based)
|
unsigned int mapped = 0;
|
||||||
m_last_filament_id_remap[old_id] = 0;
|
if (deleting_filament && old_id == deleted_1based) {
|
||||||
else
|
mapped = 0;
|
||||||
m_last_filament_id_remap[old_id] = old_id > deleted_1based ? old_id - 1 : old_id;
|
} else if (old_id <= unsigned(num_filaments)) {
|
||||||
|
mapped = old_id;
|
||||||
|
if (deleting_filament && old_id > deleted_1based)
|
||||||
|
--mapped;
|
||||||
|
}
|
||||||
|
m_last_filament_id_remap[old_id] = mapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<std::pair<unsigned int, unsigned int>, unsigned int> new_pair_to_id;
|
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);
|
unsigned int next_virtual_id = unsigned(num_filaments + 1);
|
||||||
for (const auto &mf : this->mixed_filaments.mixed_filaments()) {
|
for (const auto &mf : this->mixed_filaments.mixed_filaments()) {
|
||||||
if (!mf.enabled)
|
if (!mf.enabled)
|
||||||
continue;
|
continue;
|
||||||
new_pair_to_id[{mf.component_a, mf.component_b}] = 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;
|
||||||
unsigned int old_virtual_id = unsigned(old_num_filaments + 1);
|
unsigned int old_virtual_id = unsigned(old_num_filaments + 1);
|
||||||
for (const auto &mf : old_mixed) {
|
for (const auto &mf : old_mixed) {
|
||||||
if (!mf.enabled)
|
if (!mf.enabled)
|
||||||
@@ -3407,15 +3410,51 @@ void PresetBundle::update_multi_material_filament_presets(size_t to_delete_filam
|
|||||||
if (a == deleted_1based || b == deleted_1based) {
|
if (a == deleted_1based || b == deleted_1based) {
|
||||||
m_last_filament_id_remap[old_virtual_id] = 0;
|
m_last_filament_id_remap[old_virtual_id] = 0;
|
||||||
} else {
|
} else {
|
||||||
|
if (deleting_filament) {
|
||||||
if (a > deleted_1based)
|
if (a > deleted_1based)
|
||||||
--a;
|
--a;
|
||||||
if (b > deleted_1based)
|
if (b > deleted_1based)
|
||||||
--b;
|
--b;
|
||||||
auto it = new_pair_to_id.find({a, b});
|
}
|
||||||
m_last_filament_id_remap[old_virtual_id] = (it == new_pair_to_id.end()) ? 0 : it->second;
|
const auto key = canonical_pair(a, b);
|
||||||
|
auto it = new_pair_to_ids.find(key);
|
||||||
|
if (it == new_pair_to_ids.end()) {
|
||||||
|
m_last_filament_id_remap[old_virtual_id] = 0;
|
||||||
|
} else {
|
||||||
|
size_t &used = used_per_pair[key];
|
||||||
|
if (used >= it->second.size()) {
|
||||||
|
m_last_filament_id_remap[old_virtual_id] = 0;
|
||||||
|
} else {
|
||||||
|
m_last_filament_id_remap[old_virtual_id] = it->second[used++];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
++old_virtual_id;
|
++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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -252,10 +252,17 @@ public:
|
|||||||
|
|
||||||
// Read out the number of extruders from an active printer preset,
|
// Read out the number of extruders from an active printer preset,
|
||||||
// update size and content of filament_presets.
|
// update size and content of filament_presets.
|
||||||
void update_multi_material_filament_presets(size_t to_delete_filament_id = size_t(-1));
|
void update_multi_material_filament_presets(size_t to_delete_filament_id = size_t(-1),
|
||||||
// Mapping generated during the latest filament deletion.
|
size_t old_num_filaments = size_t(-1));
|
||||||
|
// Mapping generated during the latest filament count change.
|
||||||
// Index is old 1-based filament ID, value is new 1-based filament ID (0 = removed).
|
// 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; }
|
const std::vector<unsigned int>& last_filament_id_remap() const { return m_last_filament_id_remap; }
|
||||||
|
std::vector<unsigned int> consume_last_filament_id_remap()
|
||||||
|
{
|
||||||
|
std::vector<unsigned int> out = std::move(m_last_filament_id_remap);
|
||||||
|
m_last_filament_id_remap.clear();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
// Update the is_compatible flag of all print and filament presets depending on whether they are marked
|
// Update the is_compatible flag of all print and filament presets depending on whether they are marked
|
||||||
// as compatible with the currently selected printer (and print in case of filament presets).
|
// as compatible with the currently selected printer (and print in case of filament presets).
|
||||||
|
|||||||
@@ -251,7 +251,6 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
|||||||
|| opt_key == "mixed_filament_gradient_mode"
|
|| opt_key == "mixed_filament_gradient_mode"
|
||||||
|| opt_key == "mixed_filament_height_lower_bound"
|
|| opt_key == "mixed_filament_height_lower_bound"
|
||||||
|| opt_key == "mixed_filament_height_upper_bound"
|
|| opt_key == "mixed_filament_height_upper_bound"
|
||||||
|| opt_key == "mixed_filament_cycle_layers"
|
|
||||||
|| opt_key == "mixed_filament_advanced_dithering"
|
|| opt_key == "mixed_filament_advanced_dithering"
|
||||||
|| opt_key == "mixed_filament_surface_indentation"
|
|| opt_key == "mixed_filament_surface_indentation"
|
||||||
|| opt_key == "mixed_filament_definitions"
|
|| opt_key == "mixed_filament_definitions"
|
||||||
|
|||||||
@@ -1173,7 +1173,6 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||||||
new_full_config.option("mixed_filament_gradient_mode", true);
|
new_full_config.option("mixed_filament_gradient_mode", true);
|
||||||
new_full_config.option("mixed_filament_height_lower_bound", true);
|
new_full_config.option("mixed_filament_height_lower_bound", true);
|
||||||
new_full_config.option("mixed_filament_height_upper_bound", true);
|
new_full_config.option("mixed_filament_height_upper_bound", true);
|
||||||
new_full_config.option("mixed_filament_cycle_layers", true);
|
|
||||||
new_full_config.option("mixed_filament_advanced_dithering", true);
|
new_full_config.option("mixed_filament_advanced_dithering", true);
|
||||||
new_full_config.option("mixed_filament_pointillism_pixel_size", true);
|
new_full_config.option("mixed_filament_pointillism_pixel_size", true);
|
||||||
new_full_config.option("mixed_filament_pointillism_line_gap", true);
|
new_full_config.option("mixed_filament_pointillism_line_gap", true);
|
||||||
@@ -1185,7 +1184,6 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||||||
m_config.option("mixed_filament_gradient_mode", true);
|
m_config.option("mixed_filament_gradient_mode", true);
|
||||||
m_config.option("mixed_filament_height_lower_bound", true);
|
m_config.option("mixed_filament_height_lower_bound", true);
|
||||||
m_config.option("mixed_filament_height_upper_bound", true);
|
m_config.option("mixed_filament_height_upper_bound", true);
|
||||||
m_config.option("mixed_filament_cycle_layers", true);
|
|
||||||
m_config.option("mixed_filament_advanced_dithering", true);
|
m_config.option("mixed_filament_advanced_dithering", true);
|
||||||
m_config.option("mixed_filament_pointillism_pixel_size", true);
|
m_config.option("mixed_filament_pointillism_pixel_size", true);
|
||||||
m_config.option("mixed_filament_pointillism_line_gap", true);
|
m_config.option("mixed_filament_pointillism_line_gap", true);
|
||||||
@@ -1197,7 +1195,6 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||||||
m_default_object_config.option("mixed_filament_gradient_mode", true);
|
m_default_object_config.option("mixed_filament_gradient_mode", true);
|
||||||
m_default_object_config.option("mixed_filament_height_lower_bound", true);
|
m_default_object_config.option("mixed_filament_height_lower_bound", true);
|
||||||
m_default_object_config.option("mixed_filament_height_upper_bound", true);
|
m_default_object_config.option("mixed_filament_height_upper_bound", true);
|
||||||
m_default_object_config.option("mixed_filament_cycle_layers", true);
|
|
||||||
m_default_object_config.option("mixed_filament_advanced_dithering", true);
|
m_default_object_config.option("mixed_filament_advanced_dithering", true);
|
||||||
m_default_object_config.option("mixed_filament_pointillism_pixel_size", true);
|
m_default_object_config.option("mixed_filament_pointillism_pixel_size", true);
|
||||||
m_default_object_config.option("mixed_filament_pointillism_line_gap", true);
|
m_default_object_config.option("mixed_filament_pointillism_line_gap", true);
|
||||||
@@ -1303,7 +1300,6 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||||||
int mixed_gradient_mode = 0;
|
int mixed_gradient_mode = 0;
|
||||||
float mixed_height_lower = 0.04f;
|
float mixed_height_lower = 0.04f;
|
||||||
float mixed_height_upper = 0.16f;
|
float mixed_height_upper = 0.16f;
|
||||||
int mixed_cycle_layers = 4;
|
|
||||||
bool mixed_advanced_dither = false;
|
bool mixed_advanced_dither = false;
|
||||||
float mixed_pointillism_pixel_size = 0.f;
|
float mixed_pointillism_pixel_size = 0.f;
|
||||||
float mixed_pointillism_line_gap = 0.f;
|
float mixed_pointillism_line_gap = 0.f;
|
||||||
@@ -1319,8 +1315,6 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||||||
mixed_height_lower = float(new_full_config.opt_float("mixed_filament_height_lower_bound"));
|
mixed_height_lower = float(new_full_config.opt_float("mixed_filament_height_lower_bound"));
|
||||||
if (new_full_config.has("mixed_filament_height_upper_bound"))
|
if (new_full_config.has("mixed_filament_height_upper_bound"))
|
||||||
mixed_height_upper = float(new_full_config.opt_float("mixed_filament_height_upper_bound"));
|
mixed_height_upper = float(new_full_config.opt_float("mixed_filament_height_upper_bound"));
|
||||||
if (new_full_config.has("mixed_filament_cycle_layers"))
|
|
||||||
mixed_cycle_layers = new_full_config.opt_int("mixed_filament_cycle_layers");
|
|
||||||
if (new_full_config.has("mixed_filament_advanced_dithering")) {
|
if (new_full_config.has("mixed_filament_advanced_dithering")) {
|
||||||
if (const ConfigOptionBool *opt = new_full_config.option<ConfigOptionBool>("mixed_filament_advanced_dithering"))
|
if (const ConfigOptionBool *opt = new_full_config.option<ConfigOptionBool>("mixed_filament_advanced_dithering"))
|
||||||
mixed_advanced_dither = opt->value;
|
mixed_advanced_dither = opt->value;
|
||||||
@@ -1339,7 +1333,6 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||||||
mixed_gradient_mode = std::clamp(mixed_gradient_mode, 0, 1);
|
mixed_gradient_mode = std::clamp(mixed_gradient_mode, 0, 1);
|
||||||
mixed_height_lower = std::max(0.01f, mixed_height_lower);
|
mixed_height_lower = std::max(0.01f, mixed_height_lower);
|
||||||
mixed_height_upper = std::max(mixed_height_lower, mixed_height_upper);
|
mixed_height_upper = std::max(mixed_height_lower, mixed_height_upper);
|
||||||
mixed_cycle_layers = std::max(2, mixed_cycle_layers);
|
|
||||||
mixed_pointillism_pixel_size = std::max(0.f, mixed_pointillism_pixel_size);
|
mixed_pointillism_pixel_size = std::max(0.f, mixed_pointillism_pixel_size);
|
||||||
mixed_pointillism_line_gap = std::max(0.f, mixed_pointillism_line_gap);
|
mixed_pointillism_line_gap = std::max(0.f, mixed_pointillism_line_gap);
|
||||||
mixed_surface_indentation = std::clamp(mixed_surface_indentation, -2.f, 2.f);
|
mixed_surface_indentation = std::clamp(mixed_surface_indentation, -2.f, 2.f);
|
||||||
@@ -1348,7 +1341,6 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||||||
<< ", gradient_mode=" << mixed_gradient_mode
|
<< ", gradient_mode=" << mixed_gradient_mode
|
||||||
<< ", lower=" << mixed_height_lower
|
<< ", lower=" << mixed_height_lower
|
||||||
<< ", upper=" << mixed_height_upper
|
<< ", upper=" << mixed_height_upper
|
||||||
<< ", cycle_layers=" << mixed_cycle_layers
|
|
||||||
<< ", advanced_dither=" << (mixed_advanced_dither ? 1 : 0)
|
<< ", advanced_dither=" << (mixed_advanced_dither ? 1 : 0)
|
||||||
<< ", pointillism_pixel_size=" << mixed_pointillism_pixel_size
|
<< ", pointillism_pixel_size=" << mixed_pointillism_pixel_size
|
||||||
<< ", pointillism_line_gap=" << mixed_pointillism_line_gap
|
<< ", pointillism_line_gap=" << mixed_pointillism_line_gap
|
||||||
@@ -1366,7 +1358,6 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||||||
m_mixed_filament_mgr.apply_gradient_settings(mixed_gradient_mode,
|
m_mixed_filament_mgr.apply_gradient_settings(mixed_gradient_mode,
|
||||||
mixed_height_lower,
|
mixed_height_lower,
|
||||||
mixed_height_upper,
|
mixed_height_upper,
|
||||||
mixed_cycle_layers,
|
|
||||||
mixed_advanced_dither);
|
mixed_advanced_dither);
|
||||||
size_t mixed_custom_count = 0;
|
size_t mixed_custom_count = 0;
|
||||||
for (const auto &mf : m_mixed_filament_mgr.mixed_filaments())
|
for (const auto &mf : m_mixed_filament_mgr.mixed_filaments())
|
||||||
|
|||||||
@@ -4174,15 +4174,6 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->mode = comAdvanced;
|
def->mode = comAdvanced;
|
||||||
def->set_default_value(new ConfigOptionFloat(0.16));
|
def->set_default_value(new ConfigOptionFloat(0.16));
|
||||||
|
|
||||||
def = this->add("mixed_filament_cycle_layers", coInt);
|
|
||||||
def->label = L("Mixed filament layer cycle");
|
|
||||||
def->category = L("Others");
|
|
||||||
def->tooltip = L("Number of layers in one alternation cycle for layer-cycle mixed filament mode.\n\n"
|
|
||||||
"Detailed mixed filament setting explanations will be published once the project wiki is available.");
|
|
||||||
def->min = 2;
|
|
||||||
def->mode = comAdvanced;
|
|
||||||
def->set_default_value(new ConfigOptionInt(4));
|
|
||||||
|
|
||||||
def = this->add("mixed_filament_advanced_dithering", coBool);
|
def = this->add("mixed_filament_advanced_dithering", coBool);
|
||||||
def->label = L("Advanced dithering");
|
def->label = L("Advanced dithering");
|
||||||
def->category = L("Others");
|
def->category = L("Others");
|
||||||
|
|||||||
@@ -1356,7 +1356,6 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
|
|||||||
((ConfigOptionBool, mixed_filament_gradient_mode))
|
((ConfigOptionBool, mixed_filament_gradient_mode))
|
||||||
((ConfigOptionFloat, mixed_filament_height_lower_bound))
|
((ConfigOptionFloat, mixed_filament_height_lower_bound))
|
||||||
((ConfigOptionFloat, mixed_filament_height_upper_bound))
|
((ConfigOptionFloat, mixed_filament_height_upper_bound))
|
||||||
((ConfigOptionInt, mixed_filament_cycle_layers))
|
|
||||||
((ConfigOptionBool, mixed_filament_advanced_dithering))
|
((ConfigOptionBool, mixed_filament_advanced_dithering))
|
||||||
((ConfigOptionFloat, mixed_filament_pointillism_pixel_size))
|
((ConfigOptionFloat, mixed_filament_pointillism_pixel_size))
|
||||||
((ConfigOptionFloat, mixed_filament_pointillism_line_gap))
|
((ConfigOptionFloat, mixed_filament_pointillism_line_gap))
|
||||||
|
|||||||
@@ -971,7 +971,6 @@ bool PrintObject::invalidate_state_by_config_options(
|
|||||||
opt_key == "mixed_filament_gradient_mode"
|
opt_key == "mixed_filament_gradient_mode"
|
||||||
|| opt_key == "mixed_filament_height_lower_bound"
|
|| opt_key == "mixed_filament_height_lower_bound"
|
||||||
|| opt_key == "mixed_filament_height_upper_bound"
|
|| opt_key == "mixed_filament_height_upper_bound"
|
||||||
|| opt_key == "mixed_filament_cycle_layers"
|
|
||||||
|| opt_key == "mixed_filament_advanced_dithering"
|
|| opt_key == "mixed_filament_advanced_dithering"
|
||||||
|| opt_key == "mixed_filament_surface_indentation"
|
|| opt_key == "mixed_filament_surface_indentation"
|
||||||
|| opt_key == "mixed_filament_definitions") {
|
|| opt_key == "mixed_filament_definitions") {
|
||||||
|
|||||||
@@ -900,12 +900,14 @@ static bool apply_mixed_surface_indentation(PrintObject &print_object, std::vect
|
|||||||
size_t changed_states = 0;
|
size_t changed_states = 0;
|
||||||
size_t emptied_states = 0;
|
size_t emptied_states = 0;
|
||||||
size_t overlap_clipped_states = 0;
|
size_t overlap_clipped_states = 0;
|
||||||
|
size_t outside_trimmed_states = 0;
|
||||||
|
|
||||||
for (size_t layer_id = 0; layer_id < segmentation.size(); ++layer_id) {
|
for (size_t layer_id = 0; layer_id < segmentation.size(); ++layer_id) {
|
||||||
if (segmentation[layer_id].size() != num_channels)
|
if (segmentation[layer_id].size() != num_channels)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
bool layer_changed = false;
|
bool layer_changed = false;
|
||||||
|
ExPolygons outside_trim_band;
|
||||||
ExPolygons occupied;
|
ExPolygons occupied;
|
||||||
if (expand_outward) {
|
if (expand_outward) {
|
||||||
for (size_t channel_idx = 0; channel_idx < num_channels; ++channel_idx) {
|
for (size_t channel_idx = 0; channel_idx < num_channels; ++channel_idx) {
|
||||||
@@ -919,6 +921,25 @@ static bool apply_mixed_surface_indentation(PrintObject &print_object, std::vect
|
|||||||
}
|
}
|
||||||
if (occupied.size() > 1)
|
if (occupied.size() > 1)
|
||||||
occupied = union_ex(occupied);
|
occupied = union_ex(occupied);
|
||||||
|
} else {
|
||||||
|
ExPolygons layer_masks;
|
||||||
|
for (size_t channel_idx = 0; channel_idx < num_channels; ++channel_idx) {
|
||||||
|
const ExPolygons &state_masks = segmentation[layer_id][channel_idx];
|
||||||
|
if (!state_masks.empty())
|
||||||
|
append(layer_masks, state_masks);
|
||||||
|
}
|
||||||
|
if (!layer_masks.empty()) {
|
||||||
|
if (layer_masks.size() > 1)
|
||||||
|
layer_masks = union_ex(layer_masks);
|
||||||
|
|
||||||
|
ExPolygons layer_inner = offset_ex(layer_masks, -delta_scaled);
|
||||||
|
if (!layer_inner.empty() && layer_inner.size() > 1)
|
||||||
|
layer_inner = union_ex(layer_inner);
|
||||||
|
|
||||||
|
outside_trim_band = layer_inner.empty() ? layer_masks : diff_ex(layer_masks, layer_inner, ApplySafetyOffset::Yes);
|
||||||
|
if (!outside_trim_band.empty() && outside_trim_band.size() > 1)
|
||||||
|
outside_trim_band = union_ex(outside_trim_band);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t channel_idx = num_physical; channel_idx < num_channels; ++channel_idx) {
|
for (size_t channel_idx = num_physical; channel_idx < num_channels; ++channel_idx) {
|
||||||
@@ -930,11 +951,13 @@ static bool apply_mixed_surface_indentation(PrintObject &print_object, std::vect
|
|||||||
if (!mixed_mgr.is_mixed(state_id, num_physical))
|
if (!mixed_mgr.is_mixed(state_id, num_physical))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ExPolygons adjusted = offset_ex(state_masks, expand_outward ? delta_scaled : -delta_scaled);
|
ExPolygons adjusted;
|
||||||
|
if (expand_outward) {
|
||||||
|
adjusted = offset_ex(state_masks, delta_scaled);
|
||||||
if (!adjusted.empty() && adjusted.size() > 1)
|
if (!adjusted.empty() && adjusted.size() > 1)
|
||||||
adjusted = union_ex(adjusted);
|
adjusted = union_ex(adjusted);
|
||||||
|
|
||||||
if (expand_outward && !adjusted.empty() && !occupied.empty()) {
|
if (!adjusted.empty() && !occupied.empty()) {
|
||||||
ExPolygons clipped = diff_ex(adjusted, occupied, ApplySafetyOffset::Yes);
|
ExPolygons clipped = diff_ex(adjusted, occupied, ApplySafetyOffset::Yes);
|
||||||
if (std::abs(area(clipped)) + EPSILON < std::abs(area(adjusted)))
|
if (std::abs(area(clipped)) + EPSILON < std::abs(area(adjusted)))
|
||||||
++overlap_clipped_states;
|
++overlap_clipped_states;
|
||||||
@@ -942,6 +965,13 @@ static bool apply_mixed_surface_indentation(PrintObject &print_object, std::vect
|
|||||||
if (!adjusted.empty() && adjusted.size() > 1)
|
if (!adjusted.empty() && adjusted.size() > 1)
|
||||||
adjusted = union_ex(adjusted);
|
adjusted = union_ex(adjusted);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
adjusted = outside_trim_band.empty() ? state_masks : diff_ex(state_masks, outside_trim_band, ApplySafetyOffset::Yes);
|
||||||
|
if (std::abs(area(adjusted)) + EPSILON < std::abs(area(state_masks)))
|
||||||
|
++outside_trimmed_states;
|
||||||
|
if (!adjusted.empty() && adjusted.size() > 1)
|
||||||
|
adjusted = union_ex(adjusted);
|
||||||
|
}
|
||||||
|
|
||||||
state_masks = std::move(adjusted);
|
state_masks = std::move(adjusted);
|
||||||
if (state_masks.empty())
|
if (state_masks.empty())
|
||||||
@@ -970,7 +1000,8 @@ static bool apply_mixed_surface_indentation(PrintObject &print_object, std::vect
|
|||||||
<< " changed_layers=" << changed_layers
|
<< " changed_layers=" << changed_layers
|
||||||
<< " changed_states=" << changed_states
|
<< " changed_states=" << changed_states
|
||||||
<< " emptied_states=" << emptied_states
|
<< " emptied_states=" << emptied_states
|
||||||
<< " overlap_clipped_states=" << overlap_clipped_states;
|
<< " overlap_clipped_states=" << overlap_clipped_states
|
||||||
|
<< " outside_trimmed_states=" << outside_trimmed_states;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2432,23 +2432,10 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||||||
std::sort(model_volume_state.begin(), model_volume_state.end(), model_volume_state_lower);
|
std::sort(model_volume_state.begin(), model_volume_state.end(), model_volume_state_lower);
|
||||||
std::sort(aux_volume_state.begin(), aux_volume_state.end(), model_volume_state_lower);
|
std::sort(aux_volume_state.begin(), aux_volume_state.end(), model_volume_state_lower);
|
||||||
|
|
||||||
// BBS: normalize painting data with current available filament count
|
// Mixed/physical filament ID normalization is handled in
|
||||||
// (physical + enabled mixed/virtual filaments).
|
// Plater::on_filaments_change() with explicit old->new remap.
|
||||||
for (unsigned int obj_idx = 0; obj_idx < (unsigned int)m_model->objects.size(); ++obj_idx) {
|
// Mutating MMU painted states here during scene refresh may run before
|
||||||
const ModelObject& model_object = *m_model->objects[obj_idx];
|
// remap and corrupt virtual mixed IDs.
|
||||||
for (int volume_idx = 0; volume_idx < (int)model_object.volumes.size(); ++volume_idx) {
|
|
||||||
ModelVolume& model_volume = *model_object.volumes[volume_idx];
|
|
||||||
if (!model_volume.is_model_part())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
unsigned int filaments_count = (unsigned int)dynamic_cast<const ConfigOptionStrings*>(m_config->option("filament_colour"))->values.size();
|
|
||||||
size_t available_filaments = filaments_count;
|
|
||||||
if (wxGetApp().preset_bundle != nullptr)
|
|
||||||
available_filaments = wxGetApp().preset_bundle->mixed_filaments.total_filaments(filaments_count);
|
|
||||||
|
|
||||||
model_volume.update_extruder_count(available_filaments);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Release all ModelVolume based GLVolumes not found in the current Model. Find the GLVolume of a hollowed mesh.
|
// Release all ModelVolume based GLVolumes not found in the current Model. Find the GLVolume of a hollowed mesh.
|
||||||
for (size_t volume_id = 0; volume_id < m_volumes.volumes.size(); ++volume_id) {
|
for (size_t volume_id = 0; volume_id < m_volumes.volumes.size(); ++volume_id) {
|
||||||
|
|||||||
+294
-67
@@ -1,6 +1,7 @@
|
|||||||
#include "Plater.hpp"
|
#include "Plater.hpp"
|
||||||
#include "libslic3r/Config.hpp"
|
#include "libslic3r/Config.hpp"
|
||||||
#include "libslic3r/MixedFilament.hpp"
|
#include "libslic3r/MixedFilament.hpp"
|
||||||
|
#include "libslic3r/filament_mixer.h"
|
||||||
#include "common_func/common_func.hpp"
|
#include "common_func/common_func.hpp"
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
@@ -2320,6 +2321,69 @@ wxColour parse_mixed_color(const std::string &value)
|
|||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wxColour blend_pair_filament_mixer(const wxColour &left, const wxColour &right, float t)
|
||||||
|
{
|
||||||
|
const wxColour safe_left = left.IsOk() ? left : wxColour("#26A69A");
|
||||||
|
const wxColour safe_right = right.IsOk() ? right : wxColour("#26A69A");
|
||||||
|
|
||||||
|
unsigned char out_r = static_cast<unsigned char>(safe_left.Red());
|
||||||
|
unsigned char out_g = static_cast<unsigned char>(safe_left.Green());
|
||||||
|
unsigned char out_b = static_cast<unsigned char>(safe_left.Blue());
|
||||||
|
::Slic3r::filament_mixer_lerp(static_cast<unsigned char>(safe_left.Red()),
|
||||||
|
static_cast<unsigned char>(safe_left.Green()),
|
||||||
|
static_cast<unsigned char>(safe_left.Blue()),
|
||||||
|
static_cast<unsigned char>(safe_right.Red()),
|
||||||
|
static_cast<unsigned char>(safe_right.Green()),
|
||||||
|
static_cast<unsigned char>(safe_right.Blue()),
|
||||||
|
std::clamp(t, 0.f, 1.f),
|
||||||
|
&out_r, &out_g, &out_b);
|
||||||
|
return wxColour(out_r, out_g, out_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
wxColour blend_multi_filament_mixer(const std::vector<wxColour> &colors, const std::vector<double> &weights)
|
||||||
|
{
|
||||||
|
if (colors.empty() || weights.empty())
|
||||||
|
return wxColour("#26A69A");
|
||||||
|
|
||||||
|
unsigned char out_r = 0;
|
||||||
|
unsigned char out_g = 0;
|
||||||
|
unsigned char out_b = 0;
|
||||||
|
double accumulated_weight = 0.0;
|
||||||
|
bool has_color = false;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < colors.size() && i < weights.size(); ++i) {
|
||||||
|
const double weight = std::max(0.0, weights[i]);
|
||||||
|
if (weight <= 0.0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const wxColour safe = colors[i].IsOk() ? colors[i] : wxColour("#26A69A");
|
||||||
|
const unsigned char r = static_cast<unsigned char>(safe.Red());
|
||||||
|
const unsigned char g = static_cast<unsigned char>(safe.Green());
|
||||||
|
const unsigned char b = static_cast<unsigned char>(safe.Blue());
|
||||||
|
|
||||||
|
if (!has_color) {
|
||||||
|
out_r = r;
|
||||||
|
out_g = g;
|
||||||
|
out_b = b;
|
||||||
|
accumulated_weight = weight;
|
||||||
|
has_color = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const double new_total = accumulated_weight + weight;
|
||||||
|
if (new_total <= 0.0)
|
||||||
|
continue;
|
||||||
|
const float t = float(weight / new_total);
|
||||||
|
::Slic3r::filament_mixer_lerp(out_r, out_g, out_b, r, g, b, t, &out_r, &out_g, &out_b);
|
||||||
|
accumulated_weight = new_total;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!has_color)
|
||||||
|
return wxColour("#26A69A");
|
||||||
|
|
||||||
|
return wxColour(out_r, out_g, out_b);
|
||||||
|
}
|
||||||
|
|
||||||
class MixedGradientSelector : public wxPanel
|
class MixedGradientSelector : public wxPanel
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -2445,9 +2509,30 @@ private:
|
|||||||
dc.DrawText(wxString::Format("%d%%", m_multi_weights[2]), rect.GetRight() - FromDIP(28), rect.GetBottom() - FromDIP(14));
|
dc.DrawText(wxString::Format("%d%%", m_multi_weights[2]), rect.GetRight() - FromDIP(28), rect.GetBottom() - FromDIP(14));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
const int w = rect.GetWidth();
|
||||||
|
const int h = rect.GetHeight();
|
||||||
|
wxImage img(w, h);
|
||||||
|
unsigned char *data = img.GetData();
|
||||||
|
if (data != nullptr) {
|
||||||
|
for (int x = 0; x < w; ++x) {
|
||||||
|
const float t = (w > 1) ? float(x) / float(w - 1) : 0.5f;
|
||||||
|
const wxColour col = blend_pair_filament_mixer(m_left, m_right, t);
|
||||||
|
const unsigned char r = static_cast<unsigned char>(col.Red());
|
||||||
|
const unsigned char g = static_cast<unsigned char>(col.Green());
|
||||||
|
const unsigned char b = static_cast<unsigned char>(col.Blue());
|
||||||
|
for (int y = 0; y < h; ++y) {
|
||||||
|
const int idx = (y * w + x) * 3;
|
||||||
|
data[idx + 0] = r;
|
||||||
|
data[idx + 1] = g;
|
||||||
|
data[idx + 2] = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dc.DrawBitmap(wxBitmap(img), rect.GetLeft(), rect.GetTop(), false);
|
||||||
} else {
|
} else {
|
||||||
dc.GradientFillLinear(rect, m_left, m_right, wxEAST);
|
dc.GradientFillLinear(rect, m_left, m_right, wxEAST);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
dc.SetPen(wxPen(is_dark ? wxColour(100, 100, 106) : wxColour(170, 170, 170), 1));
|
dc.SetPen(wxPen(is_dark ? wxColour(100, 100, 106) : wxColour(170, 170, 170), 1));
|
||||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||||
dc.DrawRectangle(rect);
|
dc.DrawRectangle(rect);
|
||||||
@@ -2717,19 +2802,7 @@ private:
|
|||||||
|
|
||||||
wxColour blended_color(const std::vector<double> &weights) const
|
wxColour blended_color(const std::vector<double> &weights) const
|
||||||
{
|
{
|
||||||
if (weights.empty() || m_colors.empty())
|
return blend_multi_filament_mixer(m_colors, weights);
|
||||||
return wxColour("#26A69A");
|
|
||||||
double r = 0.0;
|
|
||||||
double g = 0.0;
|
|
||||||
double b = 0.0;
|
|
||||||
for (size_t i = 0; i < weights.size() && i < m_colors.size(); ++i) {
|
|
||||||
r += weights[i] * double(m_colors[i].Red());
|
|
||||||
g += weights[i] * double(m_colors[i].Green());
|
|
||||||
b += weights[i] * double(m_colors[i].Blue());
|
|
||||||
}
|
|
||||||
return wxColour(std::clamp(int(std::lround(r)), 0, 255),
|
|
||||||
std::clamp(int(std::lround(g)), 0, 255),
|
|
||||||
std::clamp(int(std::lround(b)), 0, 255));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wxRect canvas_rect() const
|
wxRect canvas_rect() const
|
||||||
@@ -3819,13 +3892,6 @@ void Sidebar::update_mixed_filament_panel()
|
|||||||
std::vector<std::string> physical_colors = color_opt ? color_opt->values : std::vector<std::string>();
|
std::vector<std::string> physical_colors = color_opt ? color_opt->values : std::vector<std::string>();
|
||||||
physical_colors.resize(num_physical, "#26A69A");
|
physical_colors.resize(num_physical, "#26A69A");
|
||||||
|
|
||||||
auto get_mixed_int = [preset_bundle, print_cfg](const std::string &key, int fallback) {
|
|
||||||
if (preset_bundle->project_config.has(key))
|
|
||||||
return preset_bundle->project_config.opt_int(key);
|
|
||||||
if (print_cfg && print_cfg->has(key))
|
|
||||||
return print_cfg->opt_int(key);
|
|
||||||
return fallback;
|
|
||||||
};
|
|
||||||
auto get_mixed_bool = [preset_bundle, print_cfg](const std::string &key, bool fallback) {
|
auto get_mixed_bool = [preset_bundle, print_cfg](const std::string &key, bool fallback) {
|
||||||
if (const ConfigOptionBool *opt = preset_bundle->project_config.option<ConfigOptionBool>(key))
|
if (const ConfigOptionBool *opt = preset_bundle->project_config.option<ConfigOptionBool>(key))
|
||||||
return opt->value;
|
return opt->value;
|
||||||
@@ -3872,18 +3938,6 @@ void Sidebar::update_mixed_filament_panel()
|
|||||||
}
|
}
|
||||||
return project_value.empty() ? fallback : project_value;
|
return project_value.empty() ? fallback : project_value;
|
||||||
};
|
};
|
||||||
auto set_mixed_int = [preset_bundle, print_cfg](const std::string &key, int value) {
|
|
||||||
if (print_cfg) {
|
|
||||||
if (ConfigOptionInt *opt = print_cfg->option<ConfigOptionInt>(key))
|
|
||||||
opt->value = value;
|
|
||||||
else
|
|
||||||
print_cfg->set_key_value(key, new ConfigOptionInt(value));
|
|
||||||
}
|
|
||||||
if (ConfigOptionInt *opt = preset_bundle->project_config.option<ConfigOptionInt>(key))
|
|
||||||
opt->value = value;
|
|
||||||
else
|
|
||||||
preset_bundle->project_config.set_key_value(key, new ConfigOptionInt(value));
|
|
||||||
};
|
|
||||||
auto set_mixed_float = [preset_bundle, print_cfg](const std::string &key, float value) {
|
auto set_mixed_float = [preset_bundle, print_cfg](const std::string &key, float value) {
|
||||||
if (print_cfg) {
|
if (print_cfg) {
|
||||||
if (ConfigOptionFloat *opt = print_cfg->option<ConfigOptionFloat>(key))
|
if (ConfigOptionFloat *opt = print_cfg->option<ConfigOptionFloat>(key))
|
||||||
@@ -4215,7 +4269,6 @@ void Sidebar::update_mixed_filament_panel()
|
|||||||
int gradient_mode = height_weighted_mode ? 1 : 0;
|
int gradient_mode = height_weighted_mode ? 1 : 0;
|
||||||
float lower_bound = std::max(0.01f, get_mixed_float("mixed_filament_height_lower_bound", 0.04f));
|
float lower_bound = std::max(0.01f, get_mixed_float("mixed_filament_height_lower_bound", 0.04f));
|
||||||
float upper_bound = std::max(lower_bound, get_mixed_float("mixed_filament_height_upper_bound", 0.16f));
|
float upper_bound = std::max(lower_bound, get_mixed_float("mixed_filament_height_upper_bound", 0.16f));
|
||||||
int cycle_layers = std::max(2, get_mixed_int("mixed_filament_cycle_layers", 4));
|
|
||||||
float pointillism_pixel_size = std::max(0.f, get_mixed_float("mixed_filament_pointillism_pixel_size", 0.f));
|
float pointillism_pixel_size = std::max(0.f, get_mixed_float("mixed_filament_pointillism_pixel_size", 0.f));
|
||||||
float pointillism_line_gap = std::max(0.f, get_mixed_float("mixed_filament_pointillism_line_gap", 0.f));
|
float pointillism_line_gap = std::max(0.f, get_mixed_float("mixed_filament_pointillism_line_gap", 0.f));
|
||||||
float mixed_surface_indentation = std::clamp(get_mixed_float("mixed_filament_surface_indentation", 0.f), -2.f, 2.f);
|
float mixed_surface_indentation = std::clamp(get_mixed_float("mixed_filament_surface_indentation", 0.f), -2.f, 2.f);
|
||||||
@@ -4226,7 +4279,7 @@ void Sidebar::update_mixed_filament_panel()
|
|||||||
mixed_mgr.auto_generate(physical_colors);
|
mixed_mgr.auto_generate(physical_colors);
|
||||||
mixed_mgr.clear_custom_entries();
|
mixed_mgr.clear_custom_entries();
|
||||||
mixed_mgr.load_custom_entries(mixed_definitions, physical_colors);
|
mixed_mgr.load_custom_entries(mixed_definitions, physical_colors);
|
||||||
mixed_mgr.apply_gradient_settings(gradient_mode, lower_bound, upper_bound, cycle_layers, advanced_dithering);
|
mixed_mgr.apply_gradient_settings(gradient_mode, lower_bound, upper_bound, advanced_dithering);
|
||||||
|
|
||||||
// During project load, sidebar may refresh before physical filament combos
|
// During project load, sidebar may refresh before physical filament combos
|
||||||
// finish syncing. Avoid overwriting persisted mixed definitions while the
|
// finish syncing. Avoid overwriting persisted mixed definitions while the
|
||||||
@@ -4235,7 +4288,6 @@ void Sidebar::update_mixed_filament_panel()
|
|||||||
set_mixed_mode(height_weighted_mode);
|
set_mixed_mode(height_weighted_mode);
|
||||||
set_mixed_float("mixed_filament_height_lower_bound", lower_bound);
|
set_mixed_float("mixed_filament_height_lower_bound", lower_bound);
|
||||||
set_mixed_float("mixed_filament_height_upper_bound", upper_bound);
|
set_mixed_float("mixed_filament_height_upper_bound", upper_bound);
|
||||||
set_mixed_int("mixed_filament_cycle_layers", cycle_layers);
|
|
||||||
set_mixed_float("mixed_filament_pointillism_pixel_size", pointillism_pixel_size);
|
set_mixed_float("mixed_filament_pointillism_pixel_size", pointillism_pixel_size);
|
||||||
set_mixed_float("mixed_filament_pointillism_line_gap", pointillism_line_gap);
|
set_mixed_float("mixed_filament_pointillism_line_gap", pointillism_line_gap);
|
||||||
set_mixed_float("mixed_filament_surface_indentation", mixed_surface_indentation);
|
set_mixed_float("mixed_filament_surface_indentation", mixed_surface_indentation);
|
||||||
@@ -4392,24 +4444,23 @@ void Sidebar::update_mixed_filament_panel()
|
|||||||
float(preset_bundle->project_config.opt_float("mixed_filament_height_lower_bound")) : 0.04f;
|
float(preset_bundle->project_config.opt_float("mixed_filament_height_lower_bound")) : 0.04f;
|
||||||
float hi = preset_bundle->project_config.has("mixed_filament_height_upper_bound") ?
|
float hi = preset_bundle->project_config.has("mixed_filament_height_upper_bound") ?
|
||||||
float(preset_bundle->project_config.opt_float("mixed_filament_height_upper_bound")) : 0.16f;
|
float(preset_bundle->project_config.opt_float("mixed_filament_height_upper_bound")) : 0.16f;
|
||||||
int cycle = preset_bundle->project_config.has("mixed_filament_cycle_layers") ?
|
|
||||||
preset_bundle->project_config.opt_int("mixed_filament_cycle_layers") : 4;
|
|
||||||
bool advanced = false;
|
bool advanced = false;
|
||||||
if (const ConfigOptionBool *opt = preset_bundle->project_config.option<ConfigOptionBool>("mixed_filament_advanced_dithering"))
|
if (const ConfigOptionBool *opt = preset_bundle->project_config.option<ConfigOptionBool>("mixed_filament_advanced_dithering"))
|
||||||
advanced = opt->value;
|
advanced = opt->value;
|
||||||
mode = std::clamp(mode, 0, 1);
|
mode = std::clamp(mode, 0, 1);
|
||||||
lo = std::max(0.01f, lo);
|
lo = std::max(0.01f, lo);
|
||||||
hi = std::max(lo, hi);
|
hi = std::max(lo, hi);
|
||||||
cycle = std::max(2, cycle);
|
mgr.apply_gradient_settings(mode, lo, hi, advanced);
|
||||||
mgr.apply_gradient_settings(mode, lo, hi, cycle, advanced);
|
|
||||||
update_dynamic_filament_list();
|
update_dynamic_filament_list();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
size_t visible_mixed_idx = 0;
|
||||||
for (size_t mixed_id = 0; mixed_id < mixed.size(); ++mixed_id) {
|
for (size_t mixed_id = 0; mixed_id < mixed.size(); ++mixed_id) {
|
||||||
MixedFilament &mf = mixed[mixed_id];
|
MixedFilament &mf = mixed[mixed_id];
|
||||||
if (mf.deleted)
|
if (mf.deleted)
|
||||||
continue;
|
continue;
|
||||||
const bool editable_row = mf.custom;
|
const size_t display_mixed_idx = visible_mixed_idx++;
|
||||||
|
const bool auto_row = !mf.custom;
|
||||||
|
|
||||||
auto *row = new wxPanel(rows_scroller, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
|
auto *row = new wxPanel(rows_scroller, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
|
||||||
row->SetBackgroundColour(mixed_row_bg);
|
row->SetBackgroundColour(mixed_row_bg);
|
||||||
@@ -4428,7 +4479,7 @@ void Sidebar::update_mixed_filament_panel()
|
|||||||
swatch->SetMinSize(wxSize(FromDIP(12), FromDIP(12)));
|
swatch->SetMinSize(wxSize(FromDIP(12), FromDIP(12)));
|
||||||
header_sizer->Add(swatch, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, compact_gap_x);
|
header_sizer->Add(swatch, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, compact_gap_x);
|
||||||
|
|
||||||
const int virtual_filament_id = int(num_physical + mixed_id + 1);
|
const int virtual_filament_id = int(num_physical + display_mixed_idx + 1);
|
||||||
auto *name_label = new wxStaticText(header_panel, wxID_ANY, wxString::Format("Mixed Filament %d", virtual_filament_id));
|
auto *name_label = new wxStaticText(header_panel, wxID_ANY, wxString::Format("Mixed Filament %d", virtual_filament_id));
|
||||||
name_label->SetForegroundColour(mixed_text_fg);
|
name_label->SetForegroundColour(mixed_text_fg);
|
||||||
header_sizer->Add(name_label, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, compact_gap_x);
|
header_sizer->Add(name_label, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, compact_gap_x);
|
||||||
@@ -4463,16 +4514,60 @@ void Sidebar::update_mixed_filament_panel()
|
|||||||
del_btn->SetToolTip(_L("Delete mixed filament"));
|
del_btn->SetToolTip(_L("Delete mixed filament"));
|
||||||
header_sizer->Add(del_btn, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, compact_gap_x);
|
header_sizer->Add(del_btn, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, compact_gap_x);
|
||||||
|
|
||||||
del_btn->Bind(wxEVT_BUTTON, [this, mixed_id, set_mixed_string, notify_mixed_change](wxCommandEvent&) {
|
del_btn->Bind(wxEVT_BUTTON, [this, mixed_id, num_physical, set_mixed_string, notify_mixed_change](wxCommandEvent&) {
|
||||||
if (wxGetApp().preset_bundle) {
|
if (wxGetApp().preset_bundle) {
|
||||||
auto &mgr = wxGetApp().preset_bundle->mixed_filaments;
|
auto &mgr = wxGetApp().preset_bundle->mixed_filaments;
|
||||||
auto &mfs = mgr.mixed_filaments();
|
auto &mfs = mgr.mixed_filaments();
|
||||||
if (mixed_id < mfs.size()) {
|
if (mixed_id < mfs.size()) {
|
||||||
if (mfs[mixed_id].custom)
|
auto canonical_pair = [](unsigned int a, unsigned int b) {
|
||||||
|
return std::make_pair(std::min(a, b), std::max(a, b));
|
||||||
|
};
|
||||||
|
MixedFilament &target = mfs[mixed_id];
|
||||||
|
const auto target_pair = canonical_pair(target.component_a, target.component_b);
|
||||||
|
const bool valid_auto_pair = target_pair.first >= 1 &&
|
||||||
|
target_pair.second >= 1 &&
|
||||||
|
target_pair.first <= num_physical &&
|
||||||
|
target_pair.second <= num_physical &&
|
||||||
|
target_pair.first != target_pair.second;
|
||||||
|
if (target.custom && target.origin_auto && valid_auto_pair) {
|
||||||
|
bool tombstoned_existing_auto = false;
|
||||||
|
for (size_t idx = 0; idx < mfs.size(); ++idx) {
|
||||||
|
if (idx == mixed_id)
|
||||||
|
continue;
|
||||||
|
MixedFilament &candidate = mfs[idx];
|
||||||
|
if (candidate.custom)
|
||||||
|
continue;
|
||||||
|
if (canonical_pair(candidate.component_a, candidate.component_b) != target_pair)
|
||||||
|
continue;
|
||||||
|
candidate.deleted = true;
|
||||||
|
candidate.enabled = false;
|
||||||
|
tombstoned_existing_auto = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tombstoned_existing_auto) {
|
||||||
mfs.erase(mfs.begin() + mixed_id);
|
mfs.erase(mfs.begin() + mixed_id);
|
||||||
else {
|
} else {
|
||||||
mfs[mixed_id].deleted = true;
|
target.component_a = target_pair.first;
|
||||||
mfs[mixed_id].enabled = false;
|
target.component_b = target_pair.second;
|
||||||
|
target.mix_b_percent = 50;
|
||||||
|
target.ratio_a = 1;
|
||||||
|
target.ratio_b = 1;
|
||||||
|
target.manual_pattern.clear();
|
||||||
|
target.gradient_component_ids.clear();
|
||||||
|
target.gradient_component_weights.clear();
|
||||||
|
target.pointillism_all_filaments = false;
|
||||||
|
target.distribution_mode = int(MixedFilament::Simple);
|
||||||
|
target.custom = false;
|
||||||
|
target.origin_auto = true;
|
||||||
|
target.deleted = true;
|
||||||
|
target.enabled = false;
|
||||||
|
}
|
||||||
|
} else if (target.custom) {
|
||||||
|
mfs.erase(mfs.begin() + mixed_id);
|
||||||
|
} else {
|
||||||
|
target.deleted = true;
|
||||||
|
target.enabled = false;
|
||||||
}
|
}
|
||||||
p->m_expanded_mixed_filament_rows.clear();
|
p->m_expanded_mixed_filament_rows.clear();
|
||||||
set_mixed_string("mixed_filament_definitions", mgr.serialize_custom_entries());
|
set_mixed_string("mixed_filament_definitions", mgr.serialize_custom_entries());
|
||||||
@@ -4591,18 +4686,14 @@ void Sidebar::update_mixed_filament_panel()
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
header_panel->SetToolTip(editable_row ?
|
header_panel->SetToolTip(auto_row ?
|
||||||
_L("Click to expand/retract mixed filament settings") :
|
_L("Click to edit automatic mixed filament settings (saved as custom).") :
|
||||||
_L("Automatic mixed filament settings are read-only."));
|
_L("Click to expand/retract mixed filament settings"));
|
||||||
if (editable_row) {
|
|
||||||
bind_toggle_target(row);
|
bind_toggle_target(row);
|
||||||
bind_toggle_target(header_panel);
|
bind_toggle_target(header_panel);
|
||||||
bind_toggle_target(name_label);
|
bind_toggle_target(name_label);
|
||||||
bind_toggle_target(summary_label);
|
bind_toggle_target(summary_label);
|
||||||
bind_toggle_target(swatch);
|
bind_toggle_target(swatch);
|
||||||
} else {
|
|
||||||
p->m_expanded_mixed_filament_rows.erase(mixed_id);
|
|
||||||
}
|
|
||||||
bind_hover_target(row);
|
bind_hover_target(row);
|
||||||
bind_hover_target(header_panel);
|
bind_hover_target(header_panel);
|
||||||
bind_hover_target(name_label);
|
bind_hover_target(name_label);
|
||||||
@@ -4614,7 +4705,7 @@ void Sidebar::update_mixed_filament_panel()
|
|||||||
evt.Skip();
|
evt.Skip();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (editable_row && p->m_expanded_mixed_filament_rows.count(mixed_id) != 0) {
|
if (p->m_expanded_mixed_filament_rows.count(mixed_id) != 0) {
|
||||||
ensure_editor();
|
ensure_editor();
|
||||||
editor_host->Show();
|
editor_host->Show();
|
||||||
}
|
}
|
||||||
@@ -16673,9 +16764,9 @@ void Plater::on_filaments_delete(size_t num_filaments, size_t filament_id, int r
|
|||||||
state_map[i] = EnforcerBlockerType(i);
|
state_map[i] = EnforcerBlockerType(i);
|
||||||
|
|
||||||
const bool can_use_remap = replace_filament_id < 0;
|
const bool can_use_remap = replace_filament_id < 0;
|
||||||
static const std::vector<unsigned int> empty_remap;
|
std::vector<unsigned int> id_remap;
|
||||||
const std::vector<unsigned int> &id_remap =
|
if (preset_bundle != nullptr)
|
||||||
preset_bundle != nullptr ? preset_bundle->last_filament_id_remap() : empty_remap;
|
id_remap = preset_bundle->consume_last_filament_id_remap();
|
||||||
if (can_use_remap && !id_remap.empty()) {
|
if (can_use_remap && !id_remap.empty()) {
|
||||||
for (size_t i = 1; i < state_map.size(); ++i) {
|
for (size_t i = 1; i < state_map.size(); ++i) {
|
||||||
const unsigned int mapped = i < id_remap.size() ? id_remap[i] : 0;
|
const unsigned int mapped = i < id_remap.size() ? id_remap[i] : 0;
|
||||||
@@ -16739,6 +16830,152 @@ void Plater::on_filaments_change(size_t num_filaments)
|
|||||||
{
|
{
|
||||||
// only update elements in plater
|
// only update elements in plater
|
||||||
update_filament_colors_in_full_config();
|
update_filament_colors_in_full_config();
|
||||||
|
|
||||||
|
const size_t old_num_filaments = sidebar().combos_filament().size();
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
auto summarize_used_states = [](const std::vector<bool> &used, size_t max_items = 24) {
|
||||||
|
std::string out = "[";
|
||||||
|
size_t total = 0;
|
||||||
|
size_t emitted = 0;
|
||||||
|
for (size_t i = 1; i < used.size(); ++i) {
|
||||||
|
if (!used[i])
|
||||||
|
continue;
|
||||||
|
++total;
|
||||||
|
if (emitted < max_items) {
|
||||||
|
if (emitted > 0)
|
||||||
|
out += ",";
|
||||||
|
out += std::to_string(i);
|
||||||
|
++emitted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (total > emitted)
|
||||||
|
out += ",...";
|
||||||
|
out += "] total=" + std::to_string(total);
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
PresetBundle *preset_bundle = wxGetApp().preset_bundle;
|
||||||
|
// Consume remap before sidebar refresh, which may trigger config sync
|
||||||
|
// paths that regenerate mixed filaments and clear this remap buffer.
|
||||||
|
std::vector<unsigned int> id_remap;
|
||||||
|
if (preset_bundle != nullptr)
|
||||||
|
id_remap = preset_bundle->consume_last_filament_id_remap();
|
||||||
|
|
||||||
|
size_t total_filaments = num_filaments;
|
||||||
|
if (preset_bundle != nullptr)
|
||||||
|
total_filaments = preset_bundle->mixed_filaments.total_filaments(num_filaments);
|
||||||
|
|
||||||
|
EnforcerBlockerStateMap state_map;
|
||||||
|
for (size_t i = 0; i < state_map.size(); ++i)
|
||||||
|
state_map[i] = EnforcerBlockerType(i);
|
||||||
|
|
||||||
|
bool have_explicit_remap = false;
|
||||||
|
bool should_remap_states = false;
|
||||||
|
if (!id_remap.empty()) {
|
||||||
|
have_explicit_remap = true;
|
||||||
|
should_remap_states = true;
|
||||||
|
for (size_t i = 1; i < state_map.size(); ++i) {
|
||||||
|
const unsigned int mapped = i < id_remap.size() ? id_remap[i] : 0;
|
||||||
|
if (mapped == 0 || mapped >= state_map.size() || mapped > total_filaments)
|
||||||
|
state_map[i] = EnforcerBlockerType::NONE;
|
||||||
|
else
|
||||||
|
state_map[i] = EnforcerBlockerType(mapped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
const unsigned int mapped = unsigned(state_map[i]);
|
||||||
|
if (mapped == i)
|
||||||
|
continue;
|
||||||
|
++changed_entries;
|
||||||
|
if (changed_entries <= 24) {
|
||||||
|
if (changed_entries > 1)
|
||||||
|
changed_map_preview += ",";
|
||||||
|
changed_map_preview += std::to_string(i) + "->" + std::to_string(mapped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changed_entries > 24)
|
||||||
|
changed_map_preview += ",...";
|
||||||
|
changed_map_preview += "]";
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "MF_REMAP on_filaments_change"
|
||||||
|
<< " old_physical=" << old_num_filaments
|
||||||
|
<< " new_physical=" << num_filaments
|
||||||
|
<< " total_filaments=" << total_filaments
|
||||||
|
<< " id_remap_size=" << id_remap.size()
|
||||||
|
<< " id_remap=" << summarize_uint_vector(id_remap)
|
||||||
|
<< " explicit_remap=" << (have_explicit_remap ? 1 : 0)
|
||||||
|
<< " should_remap_states=" << (should_remap_states ? 1 : 0)
|
||||||
|
<< " changed_entries=" << changed_entries
|
||||||
|
<< " changed_map=" << changed_map_preview;
|
||||||
|
|
||||||
|
size_t obj_idx = 0;
|
||||||
|
for (ModelObject* mo : wxGetApp().model().objects) {
|
||||||
|
size_t vol_idx = 0;
|
||||||
|
for (ModelVolume* mv : mo->volumes) {
|
||||||
|
std::string used_before;
|
||||||
|
const bool has_mmu_paint = (mv != nullptr && !mv->mmu_segmentation_facets.empty());
|
||||||
|
if (has_mmu_paint)
|
||||||
|
used_before = summarize_used_states(mv->mmu_segmentation_facets.get_data().used_states);
|
||||||
|
|
||||||
|
if (should_remap_states)
|
||||||
|
mv->remap_extruder_ids(total_filaments, state_map);
|
||||||
|
else
|
||||||
|
mv->update_extruder_count(total_filaments);
|
||||||
|
|
||||||
|
if (has_mmu_paint) {
|
||||||
|
const std::string used_after = summarize_used_states(mv->mmu_segmentation_facets.get_data().used_states);
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "MF_REMAP volume"
|
||||||
|
<< " obj_idx=" << obj_idx
|
||||||
|
<< " vol_idx=" << vol_idx
|
||||||
|
<< " obj_name=" << mo->name
|
||||||
|
<< " vol_name=" << mv->name
|
||||||
|
<< " before=" << used_before
|
||||||
|
<< " after=" << used_after;
|
||||||
|
}
|
||||||
|
++vol_idx;
|
||||||
|
}
|
||||||
|
++obj_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep UI refresh after model remap. Some UI update paths may trigger
|
||||||
|
// scene/model sync that assumes already-remapped MMU state.
|
||||||
sidebar().on_filaments_change(num_filaments);
|
sidebar().on_filaments_change(num_filaments);
|
||||||
sidebar().obj_list()->update_objects_list_filament_column(num_filaments);
|
sidebar().obj_list()->update_objects_list_filament_column(num_filaments);
|
||||||
|
|
||||||
@@ -16747,16 +16984,6 @@ void Plater::on_filaments_change(size_t num_filaments)
|
|||||||
PartPlate* part_plate = plate_list.get_plate(i);
|
PartPlate* part_plate = plate_list.get_plate(i);
|
||||||
part_plate->update_first_layer_print_sequence(num_filaments);
|
part_plate->update_first_layer_print_sequence(num_filaments);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t total_filaments = num_filaments;
|
|
||||||
if (wxGetApp().preset_bundle != nullptr)
|
|
||||||
total_filaments = wxGetApp().preset_bundle->mixed_filaments.total_filaments(num_filaments);
|
|
||||||
|
|
||||||
for (ModelObject* mo : wxGetApp().model().objects) {
|
|
||||||
for (ModelVolume* mv : mo->volumes) {
|
|
||||||
mv->update_extruder_count(total_filaments);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Plater::on_bed_type_change(BedType bed_type)
|
void Plater::on_bed_type_change(BedType bed_type)
|
||||||
|
|||||||
@@ -1787,7 +1787,6 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
|
|||||||
opt_key == "mixed_filament_height_upper_bound" ||
|
opt_key == "mixed_filament_height_upper_bound" ||
|
||||||
opt_key == "mixed_color_layer_height_a" ||
|
opt_key == "mixed_color_layer_height_a" ||
|
||||||
opt_key == "mixed_color_layer_height_b" ||
|
opt_key == "mixed_color_layer_height_b" ||
|
||||||
opt_key == "mixed_filament_cycle_layers" ||
|
|
||||||
opt_key == "mixed_filament_advanced_dithering" ||
|
opt_key == "mixed_filament_advanced_dithering" ||
|
||||||
opt_key == "mixed_filament_pointillism_pixel_size" ||
|
opt_key == "mixed_filament_pointillism_pixel_size" ||
|
||||||
opt_key == "mixed_filament_pointillism_line_gap" ||
|
opt_key == "mixed_filament_pointillism_line_gap" ||
|
||||||
@@ -2519,7 +2518,6 @@ optgroup->append_single_option_line("skirt_loops", "others_settings_skirt#loops"
|
|||||||
optgroup->append_single_option_line("mixed_filament_gradient_mode");
|
optgroup->append_single_option_line("mixed_filament_gradient_mode");
|
||||||
optgroup->append_single_option_line("mixed_filament_height_lower_bound");
|
optgroup->append_single_option_line("mixed_filament_height_lower_bound");
|
||||||
optgroup->append_single_option_line("mixed_filament_height_upper_bound");
|
optgroup->append_single_option_line("mixed_filament_height_upper_bound");
|
||||||
optgroup->append_single_option_line("mixed_filament_cycle_layers");
|
|
||||||
optgroup->append_single_option_line("mixed_filament_advanced_dithering");
|
optgroup->append_single_option_line("mixed_filament_advanced_dithering");
|
||||||
optgroup->append_single_option_line("mixed_filament_pointillism_pixel_size");
|
optgroup->append_single_option_line("mixed_filament_pointillism_pixel_size");
|
||||||
optgroup->append_single_option_line("mixed_filament_pointillism_line_gap");
|
optgroup->append_single_option_line("mixed_filament_pointillism_line_gap");
|
||||||
|
|||||||
Reference in New Issue
Block a user