mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-27 10:51:22 +00:00
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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user