Add mixed filament support: Implement MixedFilament and MixedFilamentManager classes for layer-based color mixing. Update CLI to handle downward_check option. Enhance GUI to display mixed filaments and integrate with existing filament management.

This commit is contained in:
Rad
2026-02-09 22:35:18 +01:00
parent 706508c792
commit e529461234
21 changed files with 669 additions and 39 deletions
+15 -1
View File
@@ -1120,9 +1120,23 @@ int CLI::run(int argc, char **argv)
sliced_info_t sliced_info;
std::map<std::string, std::string> record_key_values;
// Only honor downward_check when explicitly requested on CLI.
// This prevents accidental activation during normal GUI startup.
bool downward_check_requested = false;
for (int i = 1; i < argc; ++i) {
std::string arg = argv[i] ? argv[i] : "";
if (arg == "--downward_check" || boost::algorithm::starts_with(arg, "--downward_check=") ||
arg == "--downward-check" || boost::algorithm::starts_with(arg, "--downward-check=")) {
downward_check_requested = true;
break;
}
}
ConfigOptionBool* downward_check_option = m_config.option<ConfigOptionBool>("downward_check");
if (downward_check_option)
if (downward_check_option && downward_check_requested)
downward_check = downward_check_option->value;
else
downward_check = false;
bool start_gui = m_actions.empty() && !downward_check;
if (start_gui) {
+2
View File
@@ -283,6 +283,8 @@ set(lisbslic3r_sources
MinAreaBoundingBox.hpp
MinimumSpanningTree.cpp
MinimumSpanningTree.hpp
MixedFilament.cpp
MixedFilament.hpp
miniz_extension.cpp
miniz_extension.hpp
ModelArrange.cpp
+41 -10
View File
@@ -146,23 +146,34 @@ bool LayerTools::is_extruder_order(unsigned int a, unsigned int b) const
return false;
}
// Resolve a 1-based filament ID through the mixed-filament manager for this layer.
unsigned int LayerTools::resolve_mixed_1based(unsigned int filament_id) const
{
if (mixed_mgr && mixed_mgr->is_mixed(filament_id, num_physical))
return mixed_mgr->resolve(filament_id, num_physical, this->layer_index);
return filament_id;
}
// Return a zero based extruder from the region, or extruder_override if overriden.
unsigned int LayerTools::wall_filament(const PrintRegion &region) const
{
assert(region.config().wall_filament.value > 0);
return ((this->extruder_override == 0) ? region.config().wall_filament.value : this->extruder_override) - 1;
unsigned int id = (this->extruder_override == 0) ? region.config().wall_filament.value : this->extruder_override;
return resolve_mixed_1based(id) - 1;
}
unsigned int LayerTools::sparse_infill_filament(const PrintRegion &region) const
{
assert(region.config().sparse_infill_filament.value > 0);
return ((this->extruder_override == 0) ? region.config().sparse_infill_filament.value : this->extruder_override) - 1;
unsigned int id = (this->extruder_override == 0) ? region.config().sparse_infill_filament.value : this->extruder_override;
return resolve_mixed_1based(id) - 1;
}
unsigned int LayerTools::solid_infill_filament(const PrintRegion &region) const
{
assert(region.config().solid_infill_filament.value > 0);
return ((this->extruder_override == 0) ? region.config().solid_infill_filament.value : this->extruder_override) - 1;
unsigned int id = (this->extruder_override == 0) ? region.config().solid_infill_filament.value : this->extruder_override;
return resolve_mixed_1based(id) - 1;
}
// Returns a zero based extruder this eec should be printed with, according to PrintRegion config or extruder_override if overriden.
@@ -184,6 +195,7 @@ unsigned int LayerTools::extruder(const ExtrusionEntityCollection &extrusions, c
} else
extruder = this->extruder_override;
extruder = resolve_mixed_1based(extruder);
return (extruder == 0) ? 0 : extruder - 1;
}
@@ -208,6 +220,9 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude
m_is_BBL_printer = object.print()->is_BBL_printer();
m_print_full_config = &object.print()->full_print_config();
m_print_object_ptr = &object;
// Mixed filament support.
m_mixed_mgr = &object.print()->mixed_filament_manager();
m_num_physical = object.print()->config().filament_diameter.size();
if (object.layers().empty())
return;
@@ -271,6 +286,9 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
m_is_BBL_printer = print.is_BBL_printer();
m_print_full_config = &print.full_print_config();
m_print_config_ptr = &print.config();
// Mixed filament support.
m_mixed_mgr = &print.mixed_filament_manager();
m_num_physical = print.config().filament_diameter.size();
// Initialize the print layers for all objects and all layers.
coordf_t object_bottom_z = 0.;
@@ -480,8 +498,8 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
ExtrusionRole role = support_layer->support_fills.role();
bool has_support = role == erMixed || role == erSupportMaterial || role == erSupportTransition;
bool has_interface = role == erMixed || role == erSupportMaterialInterface;
unsigned int extruder_support = object.config().support_filament.value;
unsigned int extruder_interface = object.config().support_interface_filament.value;
unsigned int extruder_support = resolve_mixed(object.config().support_filament.value, layer_tools.layer_index);
unsigned int extruder_interface = resolve_mixed(object.config().support_interface_filament.value, layer_tools.layer_index);
if (has_support)
layer_tools.extruders.push_back(extruder_support);
if (has_interface)
@@ -505,6 +523,10 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
// Collect the object extruders.
for (auto layer : object.layers()) {
LayerTools &layer_tools = this->tools_for_layer(layer->print_z);
// Store the sequential layer index and mixed-filament context for resolution.
layer_tools.layer_index = layerCount;
layer_tools.mixed_mgr = m_mixed_mgr;
layer_tools.num_physical = m_num_physical;
// Override extruder with the next
for (; it_per_layer_extruder_override != per_layer_extruder_switches.end() && it_per_layer_extruder_override->first < layer->print_z + EPSILON; ++ it_per_layer_extruder_override)
@@ -528,9 +550,11 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
}
if (something_nonoverriddable){
layer_tools.extruders.emplace_back((extruder_override == 0) ? region.config().wall_filament.value : extruder_override);
unsigned int wall_ext = (extruder_override == 0) ? region.config().wall_filament.value : extruder_override;
wall_ext = resolve_mixed(wall_ext, layerCount);
layer_tools.extruders.emplace_back(wall_ext);
if (layerCount == 0) {
firstLayerExtruders.emplace_back((extruder_override == 0) ? region.config().wall_filament.value : extruder_override);
firstLayerExtruders.emplace_back(wall_ext);
}
}
@@ -558,11 +582,11 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
if (something_nonoverriddable || !m_print_config_ptr) {
if (extruder_override == 0) {
if (has_solid_infill)
layer_tools.extruders.emplace_back(region.config().solid_infill_filament);
layer_tools.extruders.emplace_back(resolve_mixed(region.config().solid_infill_filament, layerCount));
if (has_infill)
layer_tools.extruders.emplace_back(region.config().sparse_infill_filament);
layer_tools.extruders.emplace_back(resolve_mixed(region.config().sparse_infill_filament, layerCount));
} else if (has_solid_infill || has_infill)
layer_tools.extruders.emplace_back(extruder_override);
layer_tools.extruders.emplace_back(resolve_mixed(extruder_override, layerCount));
}
if (has_solid_infill || has_infill)
layer_tools.has_object = true;
@@ -1414,5 +1438,12 @@ int WipingExtrusions::get_support_interface_extruder_overrides(const PrintObject
return -1;
}
// Resolve a 1-based filament ID through the mixed-filament manager.
unsigned int ToolOrdering::resolve_mixed(unsigned int filament_id_1based, int layer_index) const
{
if (m_mixed_mgr && m_mixed_mgr->is_mixed(filament_id_1based, m_num_physical))
return m_mixed_mgr->resolve(filament_id_1based, m_num_physical, layer_index);
return filament_id_1based;
}
} // namespace Slic3r
+18
View File
@@ -4,6 +4,7 @@
#define slic3r_ToolOrdering_hpp_
#include "../libslic3r.h"
#include "../MixedFilament.hpp"
#include <utility>
@@ -119,6 +120,8 @@ public:
// If per layer extruder switches are inserted by the G-code preview slider, this value contains the new (1 based) extruder, with which the whole object layer is being printed with.
// If not overriden, it is set to 0.
unsigned int extruder_override = 0;
// Sequential layer index (0-based), used by mixed-filament resolution.
int layer_index = 0;
// Should a skirt be printed at this layer?
// Layers are marked for infinite skirt aka draft shield. Not all the layers have to be printed.
bool has_skirt = false;
@@ -138,7 +141,13 @@ public:
return m_wiping_extrusions;
}
// Mixed-filament resolution context (set by ToolOrdering during collect_extruders).
const MixedFilamentManager *mixed_mgr = nullptr;
size_t num_physical = 0;
private:
// Resolve a 1-based filament ID through the mixed-filament manager for this layer.
unsigned int resolve_mixed_1based(unsigned int filament_id) const;
// This object holds list of extrusion that will be used for extruder wiping
WipingExtrusions m_wiping_extrusions;
};
@@ -203,6 +212,11 @@ private:
std::vector<unsigned int> generate_first_layer_tool_order(const Print& print);
std::vector<unsigned int> generate_first_layer_tool_order(const PrintObject& object);
// Resolve a 1-based filament ID through the mixed-filament manager.
// Returns the resolved physical extruder (1-based). If the ID is not a
// mixed filament or no manager is set, returns the input unchanged.
unsigned int resolve_mixed(unsigned int filament_id_1based, int layer_index) const;
std::vector<LayerTools> m_layer_tools;
// First printing extruder, including the multi-material priming sequence.
unsigned int m_first_printing_extruder = (unsigned int)-1;
@@ -215,6 +229,10 @@ private:
const PrintConfig* m_print_config_ptr = nullptr;
const PrintObject* m_print_object_ptr = nullptr;
bool m_is_BBL_printer = false;
// Mixed filament support: pointer to manager (owned by Print) and
// number of physical extruders.
const MixedFilamentManager* m_mixed_mgr = nullptr;
size_t m_num_physical = 0;
};
} // namespace SLic3r
+158
View File
@@ -0,0 +1,158 @@
#include "MixedFilament.hpp"
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <sstream>
#include <iomanip>
namespace Slic3r {
// ---------------------------------------------------------------------------
// Colour helpers (internal)
// ---------------------------------------------------------------------------
struct RGB {
int r = 0, g = 0, b = 0;
};
// Parse "#RRGGBB" to RGB. Returns black on failure.
static RGB parse_hex_color(const std::string &hex)
{
RGB c;
if (hex.size() >= 7 && hex[0] == '#') {
c.r = std::stoi(hex.substr(1, 2), nullptr, 16);
c.g = std::stoi(hex.substr(3, 2), nullptr, 16);
c.b = std::stoi(hex.substr(5, 2), nullptr, 16);
}
return c;
}
static std::string rgb_to_hex(const RGB &c)
{
char buf[8];
std::snprintf(buf, sizeof(buf), "#%02X%02X%02X", c.r, c.g, c.b);
return std::string(buf);
}
// ---------------------------------------------------------------------------
// MixedFilamentManager
// ---------------------------------------------------------------------------
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
// enabled flags.
std::vector<MixedFilament> old = std::move(m_mixed);
m_mixed.clear();
const size_t n = filament_colours.size();
if (n < 2)
return;
// Generate all C(N,2) pairwise combinations.
for (size_t i = 0; i < n; ++i) {
for (size_t j = i + 1; j < n; ++j) {
MixedFilament mf;
mf.component_a = static_cast<unsigned int>(i + 1); // 1-based
mf.component_b = static_cast<unsigned int>(j + 1);
mf.ratio_a = 1;
mf.ratio_b = 1;
mf.enabled = true;
// Try to preserve previous settings.
for (const auto &prev : old) {
if (prev.component_a == mf.component_a &&
prev.component_b == mf.component_b) {
mf.ratio_a = prev.ratio_a;
mf.ratio_b = prev.ratio_b;
mf.enabled = prev.enabled;
break;
}
}
mf.display_color = blend_color(filament_colours[i],
filament_colours[j],
mf.ratio_a, mf.ratio_b);
m_mixed.push_back(mf);
}
}
}
unsigned int MixedFilamentManager::resolve(unsigned int filament_id,
size_t num_physical,
int layer_index) const
{
if (!is_mixed(filament_id, num_physical))
return filament_id;
const size_t idx = index_of(filament_id, num_physical);
if (idx >= m_mixed.size())
return 1; // fallback to first extruder
const MixedFilament &mf = m_mixed[idx];
const int cycle = mf.ratio_a + mf.ratio_b;
if (cycle <= 0)
return mf.component_a;
const int pos = ((layer_index % cycle) + cycle) % cycle; // safe modulo for negatives
return (pos < mf.ratio_a) ? mf.component_a : mf.component_b;
}
std::string MixedFilamentManager::blend_color(const std::string &color_a,
const std::string &color_b,
int ratio_a, int ratio_b)
{
RGB a = parse_hex_color(color_a);
RGB b = parse_hex_color(color_b);
// Additive blend: min(a + b, 255) per channel.
// For unequal ratios, weight accordingly.
const float total = static_cast<float>(ratio_a + ratio_b);
const float wa = (total > 0.f) ? static_cast<float>(ratio_a) / total : 0.5f;
const float wb = 1.f - wa;
// Use screen blending which is additive-like without oversaturation:
// screen(A, B) = A + B - A*B/255
// Weighted variant: blend each channel independently.
auto screen_ch = [](int ca, int cb, float wa, float wb) -> int {
// Weighted additive with clamping matches user expectation:
// Red(255,0,0) + Green(0,255,0) = Yellow(255,255,0)
float v = static_cast<float>(ca) * wa + static_cast<float>(cb) * wb;
// Boost towards additive: add the minimum so pure colours combine fully.
float additive = std::min(static_cast<float>(ca + cb), 255.f);
// Blend between weighted-average and full-additive based on colour distance.
float result = wa * static_cast<float>(ca) + wb * static_cast<float>(cb);
// For the 1:1 case, use pure additive (clamped) to get R+G=Y.
if (std::abs(wa - wb) < 0.01f)
result = additive;
return std::min(static_cast<int>(std::round(result)), 255);
};
RGB out;
out.r = screen_ch(a.r, b.r, wa, wb);
out.g = screen_ch(a.g, b.g, wa, wb);
out.b = screen_ch(a.b, b.b, wa, wb);
return rgb_to_hex(out);
}
size_t MixedFilamentManager::enabled_count() const
{
size_t count = 0;
for (const auto &mf : m_mixed)
if (mf.enabled)
++count;
return count;
}
std::vector<std::string> MixedFilamentManager::display_colors() const
{
std::vector<std::string> colors;
for (const auto &mf : m_mixed)
if (mf.enabled)
colors.push_back(mf.display_color);
return colors;
}
} // namespace Slic3r
+108
View File
@@ -0,0 +1,108 @@
#ifndef slic3r_MixedFilament_hpp_
#define slic3r_MixedFilament_hpp_
#include <string>
#include <vector>
#include <algorithm>
#include <cstdint>
namespace Slic3r {
// Represents a virtual "mixed" filament created by alternating layers of two
// physical filaments. The display colour is an additive RGB blend so that,
// for example, Red + Green previews as Yellow.
struct MixedFilament
{
// 1-based physical filament IDs that are combined.
unsigned int component_a = 1;
unsigned int component_b = 2;
// Layer-alternation ratio. With ratio_a = 2, ratio_b = 1 the cycle is
// A, A, B, A, A, B, ...
int ratio_a = 1;
int ratio_b = 1;
// Whether this mixed filament is enabled (available for assignment).
bool enabled = true;
// Computed display colour as "#RRGGBB".
std::string display_color;
bool operator==(const MixedFilament &rhs) const
{
return component_a == rhs.component_a &&
component_b == rhs.component_b &&
ratio_a == rhs.ratio_a &&
ratio_b == rhs.ratio_b &&
enabled == rhs.enabled;
}
bool operator!=(const MixedFilament &rhs) const { return !(*this == rhs); }
};
// ---------------------------------------------------------------------------
// MixedFilamentManager
//
// Owns the list of mixed filaments and provides helpers used by the slicing
// pipeline to resolve virtual IDs back to physical extruders.
//
// Virtual filament IDs are numbered starting at (num_physical + 1). For a
// 4-extruder printer the first mixed filament has ID 5, the second 6, etc.
// ---------------------------------------------------------------------------
class MixedFilamentManager
{
public:
MixedFilamentManager() = default;
// ---- Auto-generation ------------------------------------------------
// Rebuild the mixed-filament list from the current set of physical
// filament colours. Generates all C(N,2) pairwise combinations.
// Previous ratio/enabled state is preserved when a combination still
// exists.
void auto_generate(const std::vector<std::string> &filament_colours);
// ---- Queries --------------------------------------------------------
// True when `filament_id` (1-based) refers to a mixed filament.
bool is_mixed(unsigned int filament_id, size_t num_physical) const
{
return filament_id > num_physical && index_of(filament_id, num_physical) < m_mixed.size();
}
// Resolve a mixed filament ID to a physical extruder (1-based) for the
// given `layer_index`. Returns `filament_id` unchanged when it is not a
// mixed filament.
unsigned int resolve(unsigned int filament_id, size_t num_physical, int layer_index) const;
// Compute a display colour by additively blending the two component
// colours. `filament_colours` contains the physical colours only.
static std::string blend_color(const std::string &color_a,
const std::string &color_b,
int ratio_a, int ratio_b);
// ---- Accessors ------------------------------------------------------
const std::vector<MixedFilament> &mixed_filaments() const { return m_mixed; }
std::vector<MixedFilament> &mixed_filaments() { return m_mixed; }
size_t enabled_count() const;
// Total filament count = num_physical + number of *enabled* mixed filaments.
size_t total_filaments(size_t num_physical) const { return num_physical + enabled_count(); }
// Return the display colours of all enabled mixed filaments (in order).
std::vector<std::string> display_colors() const;
private:
// Convert a 1-based virtual ID to a 0-based index into m_mixed.
size_t index_of(unsigned int filament_id, size_t num_physical) const
{
return static_cast<size_t>(filament_id - num_physical - 1);
}
std::vector<MixedFilament> m_mixed;
};
} // namespace Slic3r
#endif /* slic3r_MixedFilament_hpp_ */
+11
View File
@@ -2477,6 +2477,17 @@ void ModelVolume::update_extruder_count(size_t extruder_count)
std::vector<int> used_extruders = get_extruders();
for (int extruder_id : used_extruders) {
if (extruder_id > extruder_count) {
std::string used_extruders_str;
for (size_t i = 0; i < used_extruders.size(); ++i) {
if (i > 0)
used_extruders_str += ",";
used_extruders_str += std::to_string(used_extruders[i]);
}
BOOST_LOG_TRIVIAL(warning) << "ModelVolume::update_extruder_count clipping painted extruders to limit"
<< " volume_name=" << this->name
<< " volume_id=" << this->id().id
<< " requested_limit=" << extruder_count
<< " used_extruders=[" << used_extruders_str << "]";
mmu_segmentation_facets.set_enforcer_block_type_limit(*this, (EnforcerBlockerType)extruder_count);
break;
}
+28 -1
View File
@@ -8,6 +8,7 @@
#include "MutablePolygon.hpp"
#include "format.hpp"
#include <algorithm>
#include <utility>
#include <unordered_set>
@@ -2195,11 +2196,37 @@ std::vector<std::vector<ExPolygons>> segmentation_by_painting(const PrintObject
// Returns multi-material segmentation based on painting in multi-material segmentation gizmo
std::vector<std::vector<ExPolygons>> multi_material_segmentation_by_painting(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback) {
const size_t num_facets_states = print_object.print()->config().filament_colour.size() + 1;
const size_t num_physical_filaments = print_object.print()->config().filament_colour.size();
const size_t num_total_filaments = print_object.print()->mixed_filament_manager().total_filaments(num_physical_filaments);
const size_t num_facets_states = num_total_filaments + 1;
const float max_width = float(print_object.config().mmu_segmented_region_max_width.value);
const float interlocking_depth = float(print_object.config().mmu_segmented_region_interlocking_depth.value);
const bool interlocking_beam = print_object.config().interlocking_beam.value;
size_t max_painted_state = 0;
for (const ModelVolume *mv : print_object.model_object()->volumes) {
if (!mv->is_model_part())
continue;
const auto &used_states = mv->mmu_segmentation_facets.get_data().used_states;
for (size_t state_idx = static_cast<size_t>(EnforcerBlockerType::Extruder1); state_idx < used_states.size(); ++state_idx) {
if (used_states[state_idx])
max_painted_state = std::max(max_painted_state, state_idx);
}
}
if (max_painted_state >= num_facets_states) {
BOOST_LOG_TRIVIAL(warning) << "multi_material_segmentation_by_painting dropping painted states above segmentation range"
<< " max_painted_state=" << max_painted_state
<< " num_facets_states=" << num_facets_states
<< " physical_filaments=" << num_physical_filaments
<< " total_filaments=" << num_total_filaments;
} else {
BOOST_LOG_TRIVIAL(debug) << "multi_material_segmentation_by_painting state range check"
<< " max_painted_state=" << max_painted_state
<< " num_facets_states=" << num_facets_states
<< " physical_filaments=" << num_physical_filaments
<< " total_filaments=" << num_total_filaments;
}
const auto extract_facets_info = [](const ModelVolume &mv) -> ModelVolumeFacetsInfo {
return {mv.mmu_segmentation_facets, mv.is_mm_painted(), false};
};
+13 -1
View File
@@ -1811,7 +1811,9 @@ void PresetBundle::export_selections(AppConfig &config)
config.set_printer_setting(printer_name, name, filament_presets[i]);
}
CNumericLocalesSetter locales_setter;
std::string filament_colors = boost::algorithm::join(project_config.option<ConfigOptionStrings>("filament_colour")->values, ",");
std::vector<std::string> physical_filament_colors = project_config.option<ConfigOptionStrings>("filament_colour")->values;
physical_filament_colors.resize(filament_presets.size(), "#26A69A");
std::string filament_colors = boost::algorithm::join(physical_filament_colors, ",");
config.set_printer_setting(printer_name, "filament_colors", filament_colors);
std::string flush_volumes_matrix = boost::algorithm::join(project_config.option<ConfigOptionFloats>("flush_volumes_matrix")->values |
boost::adaptors::transformed(static_cast<std::string (*)(double)>(std::to_string)),
@@ -3256,6 +3258,16 @@ void PresetBundle::update_multi_material_filament_presets(size_t to_delete_filam
}
this->project_config.option<ConfigOptionFloats>("flush_volumes_matrix")->values = new_matrix;
}
// Keep project colours aligned to physical filaments, then regenerate mixed
// (virtual) entries from the physical set only.
{
ConfigOptionStrings *color_opt = this->project_config.option<ConfigOptionStrings>("filament_colour");
if (color_opt) {
color_opt->values.resize(num_filaments, "#26A69A");
this->mixed_filaments.auto_generate(color_opt->values);
}
}
}
void PresetBundle::update_compatible(PresetSelectCompatibleType select_other_print_if_incompatible, PresetSelectCompatibleType select_other_filament_if_incompatible)
+4
View File
@@ -4,6 +4,7 @@
#include "Preset.hpp"
#include "AppConfig.hpp"
#include "enum_bitmask.hpp"
#include "MixedFilament.hpp"
#include <memory>
#include <unordered_map>
@@ -153,6 +154,9 @@ public:
std::map<int, DynamicPrintConfig> filament_ams_list;
std::vector<std::vector<std::string>> ams_multi_color_filment;
// Mixed (virtual) filaments for layer-based colour mixing.
MixedFilamentManager mixed_filaments;
// Snapmaker
std::map<int, std::pair<std::string, std::string>> machine_filaments;
+4
View File
@@ -17,6 +17,7 @@
#include "GCode/ThumbnailData.hpp"
#include "GCode/GCodeProcessor.hpp"
#include "MultiMaterialSegmentation.hpp"
#include "MixedFilament.hpp"
#include "libslic3r.h"
#include <Eigen/Geometry>
@@ -896,6 +897,8 @@ public:
const PrintConfig& config() const { return m_config; }
const PrintObjectConfig& default_object_config() const { return m_default_object_config; }
const PrintRegionConfig& default_region_config() const { return m_default_region_config; }
const MixedFilamentManager& mixed_filament_manager() const { return m_mixed_filament_mgr; }
MixedFilamentManager& mixed_filament_manager() { return m_mixed_filament_mgr; }
ConstPrintObjectPtrsAdaptor objects() const { return ConstPrintObjectPtrsAdaptor(&m_objects); }
PrintObject* get_object(size_t idx) { return const_cast<PrintObject*>(m_objects[idx]); }
const PrintObject* get_object(size_t idx) const { return m_objects[idx]; }
@@ -1028,6 +1031,7 @@ private:
PrintConfig m_config;
PrintObjectConfig m_default_object_config;
PrintRegionConfig m_default_region_config;
MixedFilamentManager m_mixed_filament_mgr;
PrintObjectPtrs m_objects;
PrintRegionPtrs m_print_regions;
+50 -7
View File
@@ -2,6 +2,7 @@
#include "Print.hpp"
#include <boost/log/trivial.hpp>
#include <algorithm>
#include <cfloat>
namespace Slic3r {
@@ -1188,6 +1189,14 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
}
}
// Regenerate mixed (virtual) filaments from physical filament colours only.
std::vector<std::string> physical_filament_colors = m_config.filament_colour.values;
physical_filament_colors.resize(num_extruders, "#26A69A");
m_mixed_filament_mgr.auto_generate(physical_filament_colors);
// Total filaments = physical extruders + enabled mixed (virtual) filaments.
// Used for extruder ID clamping so that virtual IDs are accepted.
size_t num_total_filaments = m_mixed_filament_mgr.total_filaments(num_extruders);
ModelObjectStatusDB model_object_status_db;
// 1) Synchronize model objects.
@@ -1375,7 +1384,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
if (object_config_changed)
model_object.config.assign_config(model_object_new.config);
if (! object_diff.empty() || object_config_changed || num_extruders_changed ) {
PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_extruders );
PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_total_filaments );
for (const PrintObjectStatus &print_object_status : print_object_status_db.get_range(model_object)) {
t_config_option_keys diff = print_object_status.print_object->config().diff(new_config);
if (! diff.empty()) {
@@ -1438,10 +1447,10 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
// Generate a list of trafos and XY offsets for instances of a ModelObject
// Producing the config for PrintObject on demand, caching it at print_object_last.
const PrintObject *print_object_last = nullptr;
auto print_object_apply_config = [this, &print_object_last, model_object, num_extruders ](PrintObject *print_object) {
auto print_object_apply_config = [this, &print_object_last, model_object, num_total_filaments ](PrintObject *print_object) {
print_object->config_apply(print_object_last ?
print_object_last->config() :
PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_extruders ));
PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_total_filaments ));
print_object_last = print_object;
};
if (old.empty()) {
@@ -1581,9 +1590,43 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
used_facet_states[state_idx] |= volume_used_facet_states[state_idx];
}
size_t dropped_painted_states = 0;
for (size_t state_idx = static_cast<size_t>(EnforcerBlockerType::Extruder1); state_idx < used_facet_states.size(); ++state_idx) {
if (used_facet_states[state_idx])
painting_extruders.emplace_back(state_idx);
if (!used_facet_states[state_idx])
continue;
if (state_idx <= num_total_filaments)
painting_extruders.emplace_back(static_cast<unsigned int>(state_idx));
else
++dropped_painted_states;
}
if (dropped_painted_states > 0) {
BOOST_LOG_TRIVIAL(warning) << "Print::apply dropping painted extruder IDs above available filament range"
<< " dropped_states=" << dropped_painted_states
<< " physical_filaments=" << num_extruders
<< " total_filaments=" << num_total_filaments;
}
if (!painting_extruders.empty()) {
std::string painting_ids;
for (size_t i = 0; i < painting_extruders.size(); ++i) {
if (i > 0)
painting_ids += ",";
painting_ids += std::to_string(painting_extruders[i]);
}
const unsigned int max_painted_extruder = *std::max_element(painting_extruders.begin(), painting_extruders.end());
if (max_painted_extruder > num_total_filaments) {
BOOST_LOG_TRIVIAL(warning) << "Print::apply detected painted extruder IDs above available filament range"
<< " painted_extruders=[" << painting_ids << "]"
<< " physical_filaments=" << num_extruders
<< " total_filaments=" << num_total_filaments;
} else {
BOOST_LOG_TRIVIAL(debug) << "Print::apply collected painted extruders"
<< " painted_extruders=[" << painting_ids << "]"
<< " physical_filaments=" << num_extruders
<< " total_filaments=" << num_total_filaments;
}
}
}
if (model_object_status.print_object_regions_status == ModelObjectStatus::PrintObjectRegionsStatus::Valid) {
@@ -1602,7 +1645,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
verify_update_print_object_regions(
print_object.model_object()->volumes,
m_default_region_config,
num_extruders,
num_total_filaments,
*print_object_regions,
[it_print_object, it_print_object_end, &update_apply_status](const PrintRegionConfig &old_config, const PrintRegionConfig &new_config, const t_config_option_keys &diff_keys) {
for (auto it = it_print_object; it != it_print_object_end; ++it)
@@ -1627,7 +1670,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
LayerRanges(print_object.model_object()->layer_config_ranges),
m_default_region_config,
model_object_status.print_instances.front().trafo,
num_extruders ,
num_total_filaments ,
print_object.is_mm_painted() ? 0.f : float(print_object.config().xy_contour_compensation.value),
painting_extruders,
print_object.is_fuzzy_skin_painted());
+4 -2
View File
@@ -3157,9 +3157,11 @@ void PrintObject::bridge_over_infill()
} // void PrintObject::bridge_over_infill()
static void clamp_exturder_to_default(ConfigOptionInt &opt, size_t num_extruders)
// Clamp extruder IDs that exceed the total number of available filaments
// (physical + virtual/mixed) back to the default extruder.
static void clamp_exturder_to_default(ConfigOptionInt &opt, size_t num_total_filaments)
{
if (opt.value > (int)num_extruders)
if (opt.value > (int)num_total_filaments)
// assign the default extruder
opt.value = 1;
}
+4 -2
View File
@@ -854,8 +854,9 @@ static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCance
const auto &layer_ranges = print_object.shared_regions()->layer_ranges;
double z = print_object.get_layer(int(range.begin()))->slice_z;
auto it_layer_range = layer_range_first(layer_ranges, z);
// BBS
const size_t num_extruders = print_object.print()->config().filament_diameter.size();
// MM segmentation channels correspond to filament IDs (1-based), which now
// include enabled mixed / virtual filaments.
const size_t num_extruders = segmentation.empty() ? 0 : segmentation.front().size();
struct ByExtruder {
ExPolygons expolygons;
@@ -875,6 +876,7 @@ static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCance
it_layer_range = layer_range_next(layer_ranges, it_layer_range, layer.slice_z);
const PrintObjectRegions::LayerRangeRegions &layer_range = *it_layer_range;
// Gather per extruder expolygons.
assert(segmentation[layer_id].size() == num_extruders);
by_extruder.assign(num_extruders, ByExtruder());
by_region.assign(layer.region_count(), ByRegion());
bool layer_split = false;
+7 -2
View File
@@ -2432,7 +2432,8 @@ 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(aux_volume_state.begin(), aux_volume_state.end(), model_volume_state_lower);
// BBS: normalize painting data with current filament count
// BBS: normalize painting data with current available filament count
// (physical + enabled mixed/virtual filaments).
for (unsigned int obj_idx = 0; obj_idx < (unsigned int)m_model->objects.size(); ++obj_idx) {
const ModelObject& model_object = *m_model->objects[obj_idx];
for (int volume_idx = 0; volume_idx < (int)model_object.volumes.size(); ++volume_idx) {
@@ -2441,7 +2442,11 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
continue;
unsigned int filaments_count = (unsigned int)dynamic_cast<const ConfigOptionStrings*>(m_config->option("filament_colour"))->values.size();
model_volume.update_extruder_count(filaments_count);
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);
}
}
+8 -4
View File
@@ -1488,14 +1488,18 @@ void MenuFactory::create_filament_action_menu(bool init, int active_filament_men
if (item_id != wxNOT_FOUND)
menu->Destroy(item_id);
wxMenu* sub_menu = new wxMenu();
std::vector<wxBitmap*> icons = get_extruder_color_icons(true);
int filaments_cnt = icons.size();
wxMenu * sub_menu = new wxMenu();
std::vector<wxBitmap *> icons = get_extruder_color_icons(true);
const std::vector<std::string> &filament_presets = wxGetApp().preset_bundle->filament_presets;
int filaments_cnt = std::max(wxGetApp().filaments_cnt(), 0);
filaments_cnt = std::min(filaments_cnt, static_cast<int>(icons.size()));
filaments_cnt = std::min(filaments_cnt, static_cast<int>(filament_presets.size()));
for (int i = 0; i < filaments_cnt; i++) {
if (i == active_filament_menu_id)
continue;
auto preset = wxGetApp().preset_bundle->filaments.find_preset(wxGetApp().preset_bundle->filament_presets[i]);
auto preset = wxGetApp().preset_bundle->filaments.find_preset(filament_presets[i]);
wxString item_name = preset ? from_u8(preset->label(false)) : wxString::Format(_L("Filament %d"), i + 1);
append_menu_item(
+7 -1
View File
@@ -70,7 +70,13 @@ static DynamicPrintConfig& printer_config()
static int filaments_count()
{
return wxGetApp().filaments_cnt();
if (wxGetApp().preset_bundle == nullptr)
return 0;
int physical = wxGetApp().filaments_cnt();
// Include enabled mixed (virtual) filaments in the count.
const auto &mixed_mgr = wxGetApp().preset_bundle->mixed_filaments;
return static_cast<int>(mixed_mgr.total_filaments(physical));
}
static void take_snapshot(const std::string& snapshot_name)
@@ -16,6 +16,8 @@
#include <GL/glew.h>
#include <algorithm>
#include <boost/log/trivial.hpp>
namespace Slic3r::GUI {
@@ -770,6 +772,29 @@ void GLGizmoMmuSegmentation::update_model_object()
}
if (updated) {
const size_t num_physical = static_cast<size_t>(std::max(wxGetApp().filaments_cnt(), 0));
size_t num_total = num_physical;
if (wxGetApp().preset_bundle != nullptr)
num_total = wxGetApp().preset_bundle->mixed_filaments.total_filaments(num_physical);
size_t max_used_state = 0;
for (const ModelVolume *mv : mo->volumes) {
if (!mv->is_model_part())
continue;
const auto &used_states = mv->mmu_segmentation_facets.get_data().used_states;
for (size_t state_idx = static_cast<size_t>(EnforcerBlockerType::Extruder1); state_idx < used_states.size(); ++state_idx) {
if (used_states[state_idx])
max_used_state = std::max(max_used_state, state_idx);
}
}
if (max_used_state > num_physical) {
BOOST_LOG_TRIVIAL(warning) << "GLGizmoMmuSegmentation::update_model_object painted virtual extruder state detected"
<< " max_used_state=" << max_used_state
<< " physical_filaments=" << num_physical
<< " total_filaments=" << num_total;
}
const ModelObjectPtrs &mos = wxGetApp().model().objects;
size_t obj_idx = std::find(mos.begin(), mos.end(), mo) - mos.begin();
wxGetApp().obj_list()->update_info_items(obj_idx);
+158 -4
View File
@@ -1,5 +1,6 @@
#include "Plater.hpp"
#include "libslic3r/Config.hpp"
#include "libslic3r/MixedFilament.hpp"
#include "common_func/common_func.hpp"
#include <cstddef>
@@ -42,6 +43,7 @@
#include <wx/popupwin.h>
#endif
#include <wx/clrpicker.h>
#include <wx/spinctrl.h>
#include <wx/tokenzr.h>
#include <wx/aui/aui.h>
@@ -634,6 +636,10 @@ struct Sidebar::priv
int m_menu_filament_id = -1;
wxPanel* m_panel_filament_content;
wxScrolledWindow* m_scrolledWindow_filament_content;
// Mixed (virtual) filaments panel
wxPanel* m_panel_mixed_filaments = nullptr;
wxBoxSizer* m_sizer_mixed_filaments = nullptr;
wxStaticLine* m_staticline2;
wxPanel* m_panel_project_title;
ScalableButton* m_filament_icon = nullptr;
@@ -1266,7 +1272,7 @@ Sidebar::Sidebar(Plater *parent)
ConfigOptionFloat* flush_multi_opt = project_config.option<ConfigOptionFloat>("flush_multiplier");
float flush_multiplier = flush_multi_opt ? flush_multi_opt->getFloat() : 1.f;
const std::vector<std::string> extruder_colours = wxGetApp().plater()->get_extruder_colors_from_plater_config();
const std::vector<std::string> extruder_colours = wxGetApp().plater()->get_extruder_colors_from_plater_config(nullptr, false);
const auto& full_config = wxGetApp().preset_bundle->full_config();
const auto& extra_flush_volumes = get_min_flush_volumes(full_config);
WipingDialog dlg(parent, cast<float>(init_matrix), cast<float>(init_extruders), extruder_colours, extra_flush_volumes, flush_multiplier);
@@ -1423,6 +1429,23 @@ Sidebar::Sidebar(Plater *parent)
scrolled_sizer->Add(p->m_panel_filament_content, 0, wxEXPAND, 0);
}
// --- Mixed Colours Panel ---
{
p->m_panel_mixed_filaments = new wxPanel(p->scrolled, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
p->m_panel_mixed_filaments->SetBackgroundColour(wxColour(255, 255, 255));
p->m_sizer_mixed_filaments = new wxBoxSizer(wxVERTICAL);
p->m_sizer_mixed_filaments->AddSpacer(FromDIP(4));
// Title
auto *mixed_title = new wxStaticText(p->m_panel_mixed_filaments, wxID_ANY, _L("Mixed Colors"));
mixed_title->SetFont(Label::Head_14);
p->m_sizer_mixed_filaments->Add(mixed_title, 0, wxLEFT | wxRIGHT, FromDIP(16));
p->m_sizer_mixed_filaments->AddSpacer(FromDIP(4));
p->m_panel_mixed_filaments->SetSizer(p->m_sizer_mixed_filaments);
p->m_panel_mixed_filaments->Layout();
p->m_panel_mixed_filaments->Hide(); // Hidden until 2+ filaments
scrolled_sizer->Add(p->m_panel_mixed_filaments, 0, wxEXPAND, 0);
}
{
//add project title
auto params_panel = ((MainFrame*)parent->GetParent())->m_param_panel;
@@ -2161,6 +2184,116 @@ void Sidebar::on_filaments_change(size_t num_filaments)
p->m_panel_filament_title->Refresh();
update_ui_from_settings();
update_dynamic_filament_list();
update_mixed_filament_panel();
}
void Sidebar::update_mixed_filament_panel()
{
if (!p->m_panel_mixed_filaments || !p->m_sizer_mixed_filaments)
return;
auto *preset_bundle = wxGetApp().preset_bundle;
if (!preset_bundle)
return;
const auto &mixed_mgr = preset_bundle->mixed_filaments;
const auto &mixed = mixed_mgr.mixed_filaments();
// Clear old entries safely. Using Clear(true) avoids manual child-window
// destruction loops with deferred wxWindow::Destroy().
p->m_sizer_mixed_filaments->Clear(true);
// Recreate header.
p->m_sizer_mixed_filaments->AddSpacer(FromDIP(4));
auto *mixed_title = new wxStaticText(p->m_panel_mixed_filaments, wxID_ANY, _L("Mixed Colors"));
mixed_title->SetFont(Label::Head_14);
p->m_sizer_mixed_filaments->Add(mixed_title, 0, wxLEFT | wxRIGHT, FromDIP(16));
p->m_sizer_mixed_filaments->AddSpacer(FromDIP(4));
if (mixed.empty()) {
p->m_panel_mixed_filaments->Hide();
Layout();
return;
}
int mixed_id = 0;
for (const auto &mf : mixed) {
// Row panel
auto *row = new wxPanel(p->m_panel_mixed_filaments, wxID_ANY);
row->SetBackgroundColour(wxColour(255, 255, 255));
auto *row_sizer = new wxBoxSizer(wxHORIZONTAL);
// Colour swatch
wxColour swatch_color(mf.display_color);
auto *swatch = new wxPanel(row, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(16), FromDIP(16)));
swatch->SetBackgroundColour(swatch_color);
swatch->SetMinSize(wxSize(FromDIP(16), FromDIP(16)));
row_sizer->Add(swatch, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(8));
// Label: "Filament 1 + Filament 2"
wxString label_str = wxString::Format("Filament %u + Filament %u",
(unsigned)mf.component_a, (unsigned)mf.component_b);
auto *label = new wxStaticText(row, wxID_ANY, label_str);
row_sizer->Add(label, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(8));
// Ratio display
wxString ratio_str = wxString::Format("%d:%d", mf.ratio_a, mf.ratio_b);
auto *ratio_label = new wxStaticText(row, wxID_ANY, ratio_str);
row_sizer->Add(ratio_label, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(4));
// Ratio spin controls
auto *spin_a = new wxSpinCtrl(row, wxID_ANY, wxEmptyString, wxDefaultPosition,
wxSize(FromDIP(45), -1), wxSP_ARROW_KEYS, 1, 10, mf.ratio_a);
auto *spin_b = new wxSpinCtrl(row, wxID_ANY, wxEmptyString, wxDefaultPosition,
wxSize(FromDIP(45), -1), wxSP_ARROW_KEYS, 1, 10, mf.ratio_b);
row_sizer->Add(spin_a, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(2));
auto *colon = new wxStaticText(row, wxID_ANY, ":");
row_sizer->Add(colon, 0, wxALIGN_CENTER_VERTICAL);
row_sizer->Add(spin_b, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(2));
// Enable checkbox
auto *chk = new wxCheckBox(row, wxID_ANY, wxEmptyString);
chk->SetValue(mf.enabled);
row_sizer->Add(chk, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, FromDIP(8));
// Bind events to update the MixedFilamentManager when ratio/enabled changes.
int capturedIdx = mixed_id;
auto *capturedSpinA = spin_a;
auto *capturedSpinB = spin_b;
auto *capturedChk = chk;
auto update_lambda = [capturedIdx, capturedSpinA, capturedSpinB, capturedChk](wxCommandEvent &) {
auto &mgr = wxGetApp().preset_bundle->mixed_filaments;
auto &mfs = mgr.mixed_filaments();
if (capturedIdx < (int)mfs.size()) {
mfs[capturedIdx].ratio_a = capturedSpinA->GetValue();
mfs[capturedIdx].ratio_b = capturedSpinB->GetValue();
mfs[capturedIdx].enabled = capturedChk->GetValue();
// Recompute display color.
ConfigOptionStrings *co = wxGetApp().preset_bundle->project_config.option<ConfigOptionStrings>("filament_colour");
if (co && mfs[capturedIdx].component_a <= co->values.size() && mfs[capturedIdx].component_b <= co->values.size()) {
mfs[capturedIdx].display_color = Slic3r::MixedFilamentManager::blend_color(
co->values[mfs[capturedIdx].component_a - 1],
co->values[mfs[capturedIdx].component_b - 1],
mfs[capturedIdx].ratio_a, mfs[capturedIdx].ratio_b);
}
}
};
spin_a->Bind(wxEVT_SPINCTRL, update_lambda);
spin_b->Bind(wxEVT_SPINCTRL, update_lambda);
chk->Bind(wxEVT_CHECKBOX, update_lambda);
row->SetSizer(row_sizer);
p->m_sizer_mixed_filaments->Add(row, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(8));
p->m_sizer_mixed_filaments->AddSpacer(FromDIP(2));
++mixed_id;
}
p->m_sizer_mixed_filaments->AddSpacer(FromDIP(8));
p->m_panel_mixed_filaments->Show();
p->m_panel_mixed_filaments->Layout();
Layout();
}
void Sidebar::add_filament() {
@@ -2845,7 +2978,7 @@ void Sidebar::auto_calc_flushing_volumes(const int modify_id)
int m_max_flush_volume = Slic3r::g_max_flush_volume;
unsigned int m_number_of_extruders = (int)(sqrt(init_matrix.size()) + 0.001);
const std::vector<std::string> extruder_colours = wxGetApp().plater()->get_extruder_colors_from_plater_config();
const std::vector<std::string> extruder_colours = wxGetApp().plater()->get_extruder_colors_from_plater_config(nullptr, false);
std::vector<std::vector<wxColour>> multi_colours;
// Support for multi-color filament
@@ -8377,6 +8510,10 @@ void Plater::priv::on_filament_color_changed(wxCommandEvent &event)
if (wxGetApp().app_config->get("auto_calculate") == "true") {
sidebar->auto_calc_flushing_volumes(modify_id);
}
// Regenerate mixed filaments and update the sidebar panel.
wxGetApp().preset_bundle->update_multi_material_filament_presets();
sidebar->update_mixed_filament_panel();
}
void Plater::priv::install_network_plugin(wxCommandEvent &event)
@@ -14238,9 +14375,13 @@ void Plater::on_filaments_change(size_t 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(num_filaments);
mv->update_extruder_count(total_filaments);
}
}
}
@@ -14442,17 +14583,30 @@ void Plater::on_activate()
}
// Get vector of extruder colors considering filament color, if extruder color is undefined.
std::vector<std::string> Plater::get_extruder_colors_from_plater_config(const GCodeProcessorResult* const result) const
std::vector<std::string> Plater::get_extruder_colors_from_plater_config(const GCodeProcessorResult* const result, bool include_mixed) const
{
if (wxGetApp().is_gcode_viewer() && result != nullptr)
return result->extruder_colors;
else {
if (wxGetApp().preset_bundle == nullptr)
return {};
const Slic3r::DynamicPrintConfig* config = &wxGetApp().preset_bundle->project_config;
std::vector<std::string> filament_colors;
if (!config->has("filament_colour")) // in case of a SLA print
return filament_colors;
filament_colors = (config->option<ConfigOptionStrings>("filament_colour"))->values;
const size_t num_physical = static_cast<size_t>(std::max(wxGetApp().filaments_cnt(), 0));
filament_colors.resize(num_physical, "#26A69A");
if (include_mixed) {
// Append display colours for enabled mixed (virtual) filaments.
const auto &mixed_mgr = wxGetApp().preset_bundle->mixed_filaments;
for (const auto &dc : mixed_mgr.display_colors())
filament_colors.push_back(dc);
}
return filament_colors;
}
}
+2 -1
View File
@@ -156,6 +156,7 @@ public:
void edit_filament();
void on_filaments_delete(size_t filament_id);
void update_mixed_filament_panel();
// BBS
void on_bed_type_change(BedType bed_type);
void load_ams_list(std::string const & device, MachineObject* obj);
@@ -499,7 +500,7 @@ public:
void force_print_bed_update();
// On activating the parent window.
void on_activate();
std::vector<std::string> get_extruder_colors_from_plater_config(const GCodeProcessorResult* const result = nullptr) const;
std::vector<std::string> get_extruder_colors_from_plater_config(const GCodeProcessorResult* const result = nullptr, bool include_mixed = true) const;
std::vector<std::string> get_colors_for_color_print(const GCodeProcessorResult* const result = nullptr) const;
void update_menus();
+2 -3
View File
@@ -1702,7 +1702,7 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
// -1 means caculate all
auto update_flush_volume = [](int idx = -1) {
if (idx < 0) {
size_t filament_size = wxGetApp().plater()->get_extruder_colors_from_plater_config().size();
size_t filament_size = wxGetApp().plater()->get_extruder_colors_from_plater_config(nullptr, false).size();
for (size_t i = 0; i < filament_size; ++i)
wxGetApp().plater()->sidebar().auto_calc_flushing_volumes(i);
}
@@ -5347,8 +5347,7 @@ bool Tab::select_preset(std::string preset_name, bool delete_current /*=false*/,
if (m_type == Preset::TYPE_PRINTER && wxGetApp().app_config->get_bool("remember_printer_config")) {
if (preset_name.find("Snapmaker U1") != std::string::npos) {
// 在 update_selections() 改变耗材数量之前先保存旧数量和颜色
size_t old_filament_count = m_preset_bundle->filament_presets.size();
std::vector<std::string> old_filament_colors = wxGetApp().plater()->get_extruder_colors_from_plater_config();
std::vector<std::string> old_filament_colors = wxGetApp().plater()->get_extruder_colors_from_plater_config(nullptr, false);
std::vector<std::string> old_filament_presets = m_preset_bundle->filament_presets;
m_preset_bundle->update_selections(*wxGetApp().app_config);