mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-08-01 15:22:21 +00:00
feat(gui): per-filament nozzle volume selection and persistence
- FilamentMapDialog's manual page understands volume types: a mixed (Hybrid) extruder shows separate Standard / High Flow drop zones with live sub-nozzle counts, a validation timer with an inline error + "set nozzle count" suggestion, and composes a per-filament volume map on OK (persisted to the plate or globally) - switching an extruder's Flow type rewrites the affected plate map entries; plate maps stay sized across filament add/delete/count changes (values keep their filament, no index shift) - CLI: manual mapping on multi-nozzle printers synthesizes the volume map from extruder flow types when absent; nozzle-manual mode requires explicit maps; computed maps land on the plate so exported projects carry filament_volume_maps - filament_nozzle_map joins the project options (selection seeding + filament-count resizing) - pot entries for the new dialog strings All 19 reference fixtures byte-identical; slicing an exported Hybrid project reproduces its g-code byte-for-byte with the volume map round-tripped through model_settings.config.
This commit is contained in:
@@ -1,9 +1,14 @@
|
||||
#include "DragDropPanel.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "Widgets/Label.hpp"
|
||||
#include "Widgets/StateColor.hpp"
|
||||
#include <slic3r/GUI/wxExtensions.hpp>
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
wxDEFINE_EVENT(wxEVT_DRAG_DROP_COMPLETED, wxCommandEvent);
|
||||
|
||||
struct CustomData
|
||||
{
|
||||
int filament_id;
|
||||
@@ -201,7 +206,7 @@ wxDragResult ColorDropTarget::OnData(wxCoord x, wxCoord y, wxDragResult def)
|
||||
/////////////// ColorDropTarget end ////////////////////////
|
||||
|
||||
|
||||
DragDropPanel::DragDropPanel(wxWindow *parent, const wxString &label, bool is_auto)
|
||||
DragDropPanel::DragDropPanel(wxWindow *parent, const wxString &label, bool is_auto, bool has_title, bool is_sub)
|
||||
: wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE)
|
||||
, m_is_auto(is_auto)
|
||||
{
|
||||
@@ -209,22 +214,31 @@ DragDropPanel::DragDropPanel(wxWindow *parent, const wxString &label, bool is_au
|
||||
|
||||
m_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto title_panel = new wxPanel(this);
|
||||
title_panel->SetBackgroundColour(0xEEEEEE);
|
||||
auto title_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
title_panel->SetSizer(title_sizer);
|
||||
if (has_title) {
|
||||
auto title_panel = new wxPanel(this);
|
||||
title_panel->SetBackgroundColour(is_sub ? wxColour(0xF8F8F8) : wxColour(0xEEEEEE));
|
||||
auto title_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
title_panel->SetSizer(title_sizer);
|
||||
|
||||
Label* static_text = new Label(this, label);
|
||||
static_text->SetFont(Label::Head_13);
|
||||
static_text->SetBackgroundColour(0xEEEEEE);
|
||||
m_title_label = new Label(this, label);
|
||||
m_title_label->SetFont(is_sub ? Label::Body_12 : Label::Head_13);
|
||||
if (is_sub)
|
||||
m_title_label->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#6B6B6B")));
|
||||
m_title_label->SetBackgroundColour(is_sub ? wxColour(0xF8F8F8) : wxColour(0xEEEEEE));
|
||||
|
||||
title_sizer->Add(static_text, 0, wxALIGN_CENTER | wxALL, FromDIP(5));
|
||||
title_sizer->Add(m_title_label, 0, wxALIGN_CENTER | wxALL, FromDIP(5));
|
||||
|
||||
m_sizer->Add(title_panel, 0, wxEXPAND);
|
||||
m_sizer->AddSpacer(10);
|
||||
m_sizer->Add(title_panel, 0, wxEXPAND);
|
||||
m_sizer->AddSpacer(10);
|
||||
}
|
||||
|
||||
m_grid_item_sizer = new wxGridSizer(0, 6, FromDIP(8), FromDIP(8)); // row = 0, col = 3, 10 10 is space
|
||||
m_sizer->Add(m_grid_item_sizer, 1, wxEXPAND | wxALL, FromDIP(8));
|
||||
if (is_sub) {
|
||||
m_grid_item_sizer = new wxGridSizer(0, 3, FromDIP(6), FromDIP(6));
|
||||
m_sizer->Add(m_grid_item_sizer, 0, wxEXPAND);
|
||||
} else {
|
||||
m_grid_item_sizer = new wxGridSizer(0, 6, FromDIP(8), FromDIP(8)); // row = 0, col = 3, 10 10 is space
|
||||
m_sizer->Add(m_grid_item_sizer, 1, wxEXPAND | wxALL, FromDIP(8));
|
||||
}
|
||||
|
||||
// set droptarget
|
||||
auto drop_target = new ColorDropTarget(this);
|
||||
@@ -242,20 +256,35 @@ void DragDropPanel::AddColorBlock(const wxColour &color, const std::string &type
|
||||
m_grid_item_sizer->Add(panel, 0);
|
||||
m_filament_blocks.push_back(panel);
|
||||
if (update_ui) {
|
||||
m_filament_blocks.front()->Refresh(); // FIX BUG: STUDIO-8467
|
||||
Freeze();
|
||||
|
||||
m_grid_item_sizer->Layout();
|
||||
Layout();
|
||||
Fit();
|
||||
GetParent()->GetParent()->Layout();
|
||||
GetParent()->GetParent()->Fit();
|
||||
m_filament_blocks.front()->Refresh(); // FIX BUG: STUDIO-8467
|
||||
|
||||
Thaw();
|
||||
NotifyDragDropCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
void DragDropPanel::RemoveColorBlock(ColorPanel *panel, bool update_ui)
|
||||
{
|
||||
m_sizer->Detach(panel);
|
||||
m_grid_item_sizer->Detach(panel);
|
||||
panel->Destroy();
|
||||
m_filament_blocks.erase(std::remove(m_filament_blocks.begin(), m_filament_blocks.end(), panel), m_filament_blocks.end());
|
||||
if (update_ui) {
|
||||
Freeze();
|
||||
|
||||
Layout();
|
||||
Fit();
|
||||
GetParent()->GetParent()->Layout();
|
||||
GetParent()->GetParent()->Fit();
|
||||
|
||||
Thaw();
|
||||
NotifyDragDropCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,6 +299,15 @@ void DragDropPanel::DoDragDrop(ColorPanel *panel, const wxColour &color, const s
|
||||
}
|
||||
}
|
||||
|
||||
void DragDropPanel::UpdateLabel(const wxString &label)
|
||||
{
|
||||
if (m_title_label) {
|
||||
m_title_label->SetLabel(label);
|
||||
m_title_label->Refresh();
|
||||
Layout();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> DragDropPanel::GetAllFilaments() const
|
||||
{
|
||||
std::vector<int> filaments;
|
||||
@@ -287,4 +325,327 @@ std::vector<int> DragDropPanel::GetAllFilaments() const
|
||||
return filaments;
|
||||
}
|
||||
|
||||
void DragDropPanel::NotifyDragDropCompleted()
|
||||
{
|
||||
wxCommandEvent event(wxEVT_DRAG_DROP_COMPLETED);
|
||||
event.SetEventObject(this);
|
||||
|
||||
wxWindow *parent = GetParent();
|
||||
while (parent) {
|
||||
auto name = parent->GetName();
|
||||
if (name == wxT("FilamentMapManualPanel")) {
|
||||
parent->GetEventHandler()->ProcessEvent(event);
|
||||
break;
|
||||
}
|
||||
parent = parent->GetParent();
|
||||
}
|
||||
}
|
||||
|
||||
class SeparatedColorDropTarget : public wxDropTarget
|
||||
{
|
||||
public:
|
||||
SeparatedColorDropTarget(SeparatedDragDropPanel *panel) : wxDropTarget(), m_panel(panel)
|
||||
{
|
||||
m_data = new ColorDataObject();
|
||||
SetDataObject(m_data);
|
||||
}
|
||||
|
||||
virtual wxDragResult OnData(wxCoord x, wxCoord y, wxDragResult def) override;
|
||||
virtual bool OnDrop(wxCoord x, wxCoord y) override { return true; }
|
||||
|
||||
private:
|
||||
SeparatedDragDropPanel *m_panel;
|
||||
ColorDataObject *m_data;
|
||||
};
|
||||
|
||||
wxDragResult SeparatedColorDropTarget::OnData(wxCoord x, wxCoord y, wxDragResult def)
|
||||
{
|
||||
if (!GetData()) return wxDragNone;
|
||||
|
||||
m_panel->AddColorBlock(m_data->GetColor(), m_data->GetType(), m_data->GetFilament(), false);
|
||||
return wxDragCopy;
|
||||
}
|
||||
|
||||
SeparatedDragDropPanel::SeparatedDragDropPanel(wxWindow *parent, const wxString &label, bool use_separation) : wxPanel(parent), m_use_separation(use_separation)
|
||||
{
|
||||
SetBackgroundColour(0xF8F8F8);
|
||||
|
||||
m_main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto title_panel = new wxPanel(this);
|
||||
title_panel->SetBackgroundColour(0xEEEEEE);
|
||||
auto title_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
title_panel->SetSizer(title_sizer);
|
||||
|
||||
m_label = new Label(title_panel, label);
|
||||
m_label->SetFont(Label::Head_13);
|
||||
m_label->SetBackgroundColour(0xEEEEEE);
|
||||
|
||||
title_sizer->Add(m_label, 0, wxALIGN_CENTER | wxALL, FromDIP(5));
|
||||
|
||||
m_main_sizer->Add(title_panel, 0, wxEXPAND);
|
||||
m_main_sizer->AddSpacer(10);
|
||||
|
||||
m_content_panel = new wxPanel(this);
|
||||
m_content_panel->SetBackgroundColour(0xF8F8F8);
|
||||
m_content_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_content_panel->SetSizer(m_content_sizer);
|
||||
|
||||
m_high_flow_panel = new DragDropPanel(m_content_panel, _L("High Flow"), false, true, true);
|
||||
m_standard_panel = new DragDropPanel(m_content_panel, _L("Standard"), false, true, true);
|
||||
m_unified_panel = new DragDropPanel(m_content_panel, wxEmptyString, false, false);
|
||||
|
||||
m_high_flow_panel->SetBackgroundColour(0xF8F8F8);
|
||||
m_standard_panel->SetBackgroundColour(0xF8F8F8);
|
||||
m_unified_panel->SetBackgroundColour(0xF8F8F8);
|
||||
m_unified_panel->SetMinSize({FromDIP(260), -1});
|
||||
|
||||
m_main_sizer->Add(m_content_panel, 1, wxEXPAND);
|
||||
|
||||
UpdateLayout();
|
||||
|
||||
auto drop_target = new SeparatedColorDropTarget(this);
|
||||
SetDropTarget(drop_target);
|
||||
|
||||
SetSizer(m_main_sizer);
|
||||
Layout();
|
||||
wxGetApp().UpdateDarkUIWin(this);
|
||||
}
|
||||
|
||||
void SeparatedDragDropPanel::UpdateLayout()
|
||||
{
|
||||
m_content_sizer->Clear(false);
|
||||
|
||||
if (m_use_separation) {
|
||||
m_unified_panel->Hide();
|
||||
m_high_flow_panel->Show();
|
||||
m_standard_panel->Show();
|
||||
|
||||
wxSize content_size = m_content_panel->GetSize();
|
||||
int panel_width = (content_size.GetWidth() - FromDIP(1) - FromDIP(8)) / 2;
|
||||
if (panel_width > 0) {
|
||||
m_high_flow_panel->SetMinSize(wxSize(panel_width, -1));
|
||||
m_standard_panel->SetMinSize(wxSize(panel_width, -1));
|
||||
}
|
||||
|
||||
m_content_sizer->Add(m_high_flow_panel, 1, wxEXPAND | wxLEFT, FromDIP(8));
|
||||
|
||||
// Create the separator once and re-attach it on relayout; Clear(false) above only
|
||||
// detaches, so allocating here would leak a window per relayout.
|
||||
if (!m_separator) {
|
||||
m_separator = new wxPanel(m_content_panel, wxID_ANY);
|
||||
m_separator->SetBackgroundColour(StateColor::darkModeColorFor(wxColour(0xCE, 0xCE, 0xCE)));
|
||||
m_separator->SetMinSize(wxSize(FromDIP(1), -1));
|
||||
}
|
||||
m_separator->Show();
|
||||
m_content_sizer->Add(m_separator, 0, wxEXPAND | wxALL, FromDIP(4));
|
||||
|
||||
m_content_sizer->Add(m_standard_panel, 1, wxEXPAND | wxRIGHT, FromDIP(8));
|
||||
} else {
|
||||
m_high_flow_panel->Hide();
|
||||
m_standard_panel->Hide();
|
||||
if (m_separator)
|
||||
m_separator->Hide();
|
||||
m_unified_panel->Show();
|
||||
|
||||
m_content_sizer->Add(m_unified_panel, 1, wxEXPAND);
|
||||
}
|
||||
m_content_sizer->Layout();
|
||||
Layout();
|
||||
Fit();
|
||||
if (GetParent()) {
|
||||
GetParent()->Layout();
|
||||
if (GetParent()->GetParent()) {
|
||||
GetParent()->GetParent()->Layout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SeparatedDragDropPanel::UpdateLabel(const wxString &label)
|
||||
{
|
||||
if (m_label) {
|
||||
m_label->SetLabel(label);
|
||||
m_label->Refresh();
|
||||
Layout();
|
||||
}
|
||||
}
|
||||
|
||||
void SeparatedDragDropPanel::SetUseSeparation(bool use_separation)
|
||||
{
|
||||
if (m_use_separation != use_separation) {
|
||||
m_use_separation = use_separation;
|
||||
|
||||
if (use_separation) {
|
||||
auto blocks = m_unified_panel->get_filament_blocks();
|
||||
for (auto &block : blocks) {
|
||||
m_standard_panel->AddColorBlock(block->GetColor(), block->GetType(), block->GetFilamentId(), false);
|
||||
m_unified_panel->RemoveColorBlock(block, false);
|
||||
}
|
||||
} else {
|
||||
auto high_flow_blocks = m_high_flow_panel->get_filament_blocks();
|
||||
auto standard_blocks = m_standard_panel->get_filament_blocks();
|
||||
|
||||
for (auto &block : high_flow_blocks) {
|
||||
m_unified_panel->AddColorBlock(block->GetColor(), block->GetType(), block->GetFilamentId(), false);
|
||||
m_high_flow_panel->RemoveColorBlock(block, false);
|
||||
}
|
||||
|
||||
for (auto &block : standard_blocks) {
|
||||
m_unified_panel->AddColorBlock(block->GetColor(), block->GetType(), block->GetFilamentId(), false);
|
||||
m_standard_panel->RemoveColorBlock(block, false);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateLayout();
|
||||
}
|
||||
}
|
||||
|
||||
void SeparatedDragDropPanel::AddColorBlock(const wxColour &color, const std::string &type, int filament_id, bool is_high_flow, bool update_ui)
|
||||
{
|
||||
if (m_use_separation) {
|
||||
if (is_high_flow) {
|
||||
m_high_flow_panel->AddColorBlock(color, type, filament_id, update_ui);
|
||||
} else {
|
||||
m_standard_panel->AddColorBlock(color, type, filament_id, update_ui);
|
||||
}
|
||||
|
||||
if (update_ui) {
|
||||
CallAfter([this]() {
|
||||
Layout();
|
||||
GetParent()->Layout();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
m_unified_panel->AddColorBlock(color, type, filament_id, update_ui);
|
||||
}
|
||||
}
|
||||
|
||||
void SeparatedDragDropPanel::RemoveColorBlock(ColorPanel *panel, bool update_ui)
|
||||
{
|
||||
auto high_flow_blocks = m_high_flow_panel->get_filament_blocks();
|
||||
auto standard_blocks = m_standard_panel->get_filament_blocks();
|
||||
auto unified_blocks = m_unified_panel->get_filament_blocks();
|
||||
|
||||
if (std::find(high_flow_blocks.begin(), high_flow_blocks.end(), panel) != high_flow_blocks.end()) {
|
||||
m_high_flow_panel->RemoveColorBlock(panel, update_ui);
|
||||
} else if (std::find(standard_blocks.begin(), standard_blocks.end(), panel) != standard_blocks.end()) {
|
||||
m_standard_panel->RemoveColorBlock(panel, update_ui);
|
||||
} else if (std::find(unified_blocks.begin(), unified_blocks.end(), panel) != unified_blocks.end()) {
|
||||
m_unified_panel->RemoveColorBlock(panel, update_ui);
|
||||
}
|
||||
|
||||
if (update_ui && m_use_separation) {
|
||||
CallAfter([this]() {
|
||||
Layout();
|
||||
GetParent()->Layout();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> SeparatedDragDropPanel::GetAllFilaments() const
|
||||
{
|
||||
if (m_use_separation) {
|
||||
auto high_flow = m_high_flow_panel->GetAllFilaments();
|
||||
auto standard = m_standard_panel->GetAllFilaments();
|
||||
|
||||
std::vector<int> result;
|
||||
result.insert(result.end(), high_flow.begin(), high_flow.end());
|
||||
result.insert(result.end(), standard.begin(), standard.end());
|
||||
return result;
|
||||
} else {
|
||||
return m_unified_panel->GetAllFilaments();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> SeparatedDragDropPanel::GetHighFlowFilaments() const
|
||||
{
|
||||
if (m_use_separation) {
|
||||
return m_high_flow_panel->GetAllFilaments();
|
||||
}
|
||||
auto nozzle_volumes = wxGetApp().preset_bundle->project_config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type");
|
||||
const int right_eid = 1;
|
||||
if (nozzle_volumes->values.size() > right_eid) {
|
||||
int volume_type = nozzle_volumes->values[right_eid];
|
||||
if (volume_type == static_cast<int>(NozzleVolumeType::nvtHighFlow)) {
|
||||
return m_unified_panel->GetAllFilaments();
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<int> SeparatedDragDropPanel::GetStandardFilaments() const
|
||||
{
|
||||
if (m_use_separation) {
|
||||
return m_standard_panel->GetAllFilaments();
|
||||
}
|
||||
auto nozzle_volumes = wxGetApp().preset_bundle->project_config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type");
|
||||
const int right_eid = 1;
|
||||
if (nozzle_volumes->values.size() > right_eid) {
|
||||
int volume_type = nozzle_volumes->values[right_eid];
|
||||
if (volume_type == static_cast<int>(NozzleVolumeType::nvtStandard)) {
|
||||
return m_unified_panel->GetAllFilaments();
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<int> SeparatedDragDropPanel::GetTPUHighFlowFilaments() const
|
||||
{
|
||||
if (m_use_separation) {
|
||||
return {};
|
||||
}
|
||||
auto nozzle_volumes = wxGetApp().preset_bundle->project_config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type");
|
||||
const int right_eid = 1;
|
||||
if (nozzle_volumes->values.size() > right_eid) {
|
||||
int volume_type = nozzle_volumes->values[right_eid];
|
||||
if (volume_type == static_cast<int>(NozzleVolumeType::nvtTPUHighFlow)) {
|
||||
return m_unified_panel->GetAllFilaments();
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<ColorPanel *> SeparatedDragDropPanel::get_filament_blocks() const
|
||||
{
|
||||
if (m_use_separation) {
|
||||
auto high_flow = m_high_flow_panel->get_filament_blocks();
|
||||
auto standard = m_standard_panel->get_filament_blocks();
|
||||
|
||||
std::vector<ColorPanel *> result;
|
||||
result.insert(result.end(), high_flow.begin(), high_flow.end());
|
||||
result.insert(result.end(), standard.begin(), standard.end());
|
||||
return result;
|
||||
} else {
|
||||
return m_unified_panel->get_filament_blocks();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ColorPanel *> SeparatedDragDropPanel::get_high_flow_blocks() const
|
||||
{
|
||||
return m_high_flow_panel->get_filament_blocks();
|
||||
}
|
||||
|
||||
std::vector<ColorPanel *> SeparatedDragDropPanel::get_standard_blocks() const
|
||||
{
|
||||
return m_standard_panel->get_filament_blocks();
|
||||
}
|
||||
|
||||
void SeparatedDragDropPanel::ClearAllBlocks()
|
||||
{
|
||||
auto high_flow_blocks = m_high_flow_panel->get_filament_blocks();
|
||||
for (auto &block : high_flow_blocks) {
|
||||
m_high_flow_panel->RemoveColorBlock(block, false);
|
||||
}
|
||||
|
||||
auto standard_blocks = m_standard_panel->get_filament_blocks();
|
||||
for (auto &block : standard_blocks) {
|
||||
m_standard_panel->RemoveColorBlock(block, false);
|
||||
}
|
||||
|
||||
auto unified_blocks = m_unified_panel->get_filament_blocks();
|
||||
for (auto &block : unified_blocks) {
|
||||
m_unified_panel->RemoveColorBlock(block, false);
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "Widgets/Label.hpp"
|
||||
|
||||
#include <wx/simplebook.h>
|
||||
#include <wx/dialog.h>
|
||||
@@ -13,17 +14,20 @@
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
wxDECLARE_EVENT(wxEVT_DRAG_DROP_COMPLETED, wxCommandEvent);
|
||||
|
||||
wxColor Hex2Color(const std::string& str);
|
||||
|
||||
class ColorPanel;
|
||||
class DragDropPanel : public wxPanel
|
||||
{
|
||||
public:
|
||||
DragDropPanel(wxWindow *parent, const wxString &label, bool is_auto);
|
||||
DragDropPanel(wxWindow *parent, const wxString &label, bool is_auto, bool has_title = true, bool is_sub = false);
|
||||
|
||||
void AddColorBlock(const wxColour &color, const std::string &type, int filament_id, bool update_ui = true);
|
||||
void RemoveColorBlock(ColorPanel *panel, bool update_ui = true);
|
||||
void DoDragDrop(ColorPanel *panel, const wxColour &color, const std::string &type, int filament_id);
|
||||
void UpdateLabel(const wxString &label);
|
||||
|
||||
std::vector<int> GetAllFilaments() const;
|
||||
|
||||
@@ -35,9 +39,12 @@ public:
|
||||
private:
|
||||
wxBoxSizer *m_sizer;
|
||||
wxGridSizer *m_grid_item_sizer;
|
||||
Label *m_title_label = nullptr;
|
||||
bool m_is_auto;
|
||||
|
||||
std::vector<ColorPanel *> m_filament_blocks;
|
||||
|
||||
void NotifyDragDropCompleted();
|
||||
private:
|
||||
bool m_is_draging = false;
|
||||
};
|
||||
@@ -64,6 +71,49 @@ private:
|
||||
int m_filament_id;
|
||||
|
||||
};
|
||||
|
||||
// Drop zone for one extruder that can split into two sub-zones (High Flow / Standard)
|
||||
// when the extruder mixes nozzle volume types (Hybrid flow), and collapses back to a
|
||||
// single unified zone otherwise.
|
||||
class SeparatedDragDropPanel : public wxPanel
|
||||
{
|
||||
public:
|
||||
SeparatedDragDropPanel(wxWindow *parent, const wxString &label, bool use_separation = false);
|
||||
|
||||
void AddColorBlock(const wxColour &color, const std::string &type, int filament_id, bool is_high_flow = false, bool update_ui = true);
|
||||
void RemoveColorBlock(ColorPanel *panel, bool update_ui = true);
|
||||
|
||||
std::vector<int> GetAllFilaments() const;
|
||||
std::vector<int> GetHighFlowFilaments() const;
|
||||
std::vector<int> GetStandardFilaments() const;
|
||||
std::vector<int> GetTPUHighFlowFilaments() const;
|
||||
|
||||
std::vector<ColorPanel *> get_filament_blocks() const;
|
||||
std::vector<ColorPanel *> get_high_flow_blocks() const;
|
||||
std::vector<ColorPanel *> get_standard_blocks() const;
|
||||
|
||||
void SetUseSeparation(bool use_separation);
|
||||
bool IsUseSeparation() const { return m_use_separation; }
|
||||
void ClearAllBlocks();
|
||||
void UpdateLabel(const wxString &label);
|
||||
|
||||
private:
|
||||
void UpdateLayout();
|
||||
|
||||
wxBoxSizer *m_main_sizer;
|
||||
wxPanel *m_content_panel;
|
||||
wxBoxSizer *m_content_sizer;
|
||||
wxPanel *m_separator = nullptr;
|
||||
Label *m_label;
|
||||
|
||||
DragDropPanel *m_high_flow_panel;
|
||||
DragDropPanel *m_standard_panel;
|
||||
|
||||
DragDropPanel *m_unified_panel;
|
||||
|
||||
bool m_use_separation;
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
||||
#endif /* slic3r_DragDropPanel_hpp_ */
|
||||
|
||||
@@ -36,6 +36,13 @@ static std::vector<int> get_applied_map(DynamicConfig& proj_config, const Plater
|
||||
return plater_ref->get_global_filament_map();
|
||||
}
|
||||
|
||||
static std::vector<int> get_applied_volume_map(DynamicConfig& proj_config, const Plater* plater_ref, const PartPlate* partplate_ref, const bool sync_plate)
|
||||
{
|
||||
if (sync_plate)
|
||||
return partplate_ref->get_real_filament_volume_maps(proj_config);
|
||||
return plater_ref->get_global_filament_volume_map();
|
||||
}
|
||||
|
||||
extern std::string& get_left_extruder_unprintable_text();
|
||||
extern std::string& get_right_extruder_unprintable_text();
|
||||
|
||||
@@ -123,7 +130,9 @@ bool try_pop_up_before_slice(bool is_slice_all, Plater* plater_ref, PartPlate* p
|
||||
std::vector<std::string> filament_types = full_config.option<ConfigOptionStrings>("filament_type")->values;
|
||||
FilamentMapMode applied_mode = get_applied_map_mode(full_config, plater_ref,partplate_ref, sync_plate);
|
||||
std::vector<int> applied_maps = get_applied_map(full_config, plater_ref, partplate_ref, sync_plate);
|
||||
std::vector<int> applied_volume_maps = get_applied_volume_map(full_config, plater_ref, partplate_ref, sync_plate);
|
||||
applied_maps.resize(filament_colors.size(), 1);
|
||||
applied_volume_maps.resize(filament_colors.size(), 0);
|
||||
|
||||
if (!force_pop_up && applied_mode != fmmManual)
|
||||
return true;
|
||||
@@ -141,6 +150,7 @@ bool try_pop_up_before_slice(bool is_slice_all, Plater* plater_ref, PartPlate* p
|
||||
filament_colors,
|
||||
filament_types,
|
||||
applied_maps,
|
||||
applied_volume_maps,
|
||||
filament_lists,
|
||||
applied_mode,
|
||||
plater_ref->get_machine_sync_status(),
|
||||
@@ -152,25 +162,32 @@ bool try_pop_up_before_slice(bool is_slice_all, Plater* plater_ref, PartPlate* p
|
||||
if (ret == wxID_OK) {
|
||||
FilamentMapMode new_mode = map_dlg.get_mode();
|
||||
std::vector<int> new_maps = map_dlg.get_filament_maps();
|
||||
std::vector<int> new_volume_maps = map_dlg.get_filament_volume_maps();
|
||||
if (sync_plate) {
|
||||
if (is_slice_all) {
|
||||
auto plate_list = plater_ref->get_partplate_list().get_plate_list();
|
||||
for (int i = 0; i < plate_list.size(); ++i) {
|
||||
plate_list[i]->set_filament_map_mode(new_mode);
|
||||
if(new_mode == fmmManual)
|
||||
if (new_mode == fmmManual) {
|
||||
plate_list[i]->set_filament_maps(new_maps);
|
||||
plate_list[i]->set_filament_volume_maps(new_volume_maps);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
partplate_ref->set_filament_map_mode(new_mode);
|
||||
if (new_mode == fmmManual)
|
||||
if (new_mode == fmmManual) {
|
||||
partplate_ref->set_filament_maps(new_maps);
|
||||
partplate_ref->set_filament_volume_maps(new_volume_maps);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
plater_ref->set_global_filament_map_mode(new_mode);
|
||||
if (new_mode == fmmManual)
|
||||
if (new_mode == fmmManual) {
|
||||
plater_ref->set_global_filament_map(new_maps);
|
||||
plater_ref->set_global_filament_volume_map(new_volume_maps);
|
||||
}
|
||||
}
|
||||
plater_ref->update();
|
||||
// check whether able to slice, if not, return false
|
||||
@@ -186,12 +203,17 @@ FilamentMapDialog::FilamentMapDialog(wxWindow *parent,
|
||||
const std::vector<std::string> &filament_color,
|
||||
const std::vector<std::string> &filament_type,
|
||||
const std::vector<int> &filament_map,
|
||||
const std::vector<int> &filament_volume_map,
|
||||
const std::vector<int> &filaments,
|
||||
const FilamentMapMode mode,
|
||||
bool machine_synced,
|
||||
bool show_default,
|
||||
bool with_checkbox)
|
||||
: wxDialog(parent, wxID_ANY, _L("Filament grouping"), wxDefaultPosition, wxDefaultSize,wxDEFAULT_DIALOG_STYLE), m_filament_color(filament_color), m_filament_type(filament_type), m_filament_map(filament_map)
|
||||
: wxDialog(parent, wxID_ANY, _L("Filament grouping"), wxDefaultPosition, wxDefaultSize,wxDEFAULT_DIALOG_STYLE)
|
||||
, m_filament_color(filament_color)
|
||||
, m_filament_type(filament_type)
|
||||
, m_filament_map(filament_map)
|
||||
, m_filament_volume_map(filament_volume_map)
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
|
||||
@@ -244,7 +266,20 @@ FilamentMapDialog::FilamentMapDialog(wxWindow *parent,
|
||||
mode == fmmAutoForMatch && !auto_match_available ? fmmAutoForFlush :
|
||||
mode;
|
||||
|
||||
m_manual_map_panel = new FilamentMapManualPanel(this, m_filament_color, m_filament_type, filaments, filament_map);
|
||||
m_manual_map_panel = new FilamentMapManualPanel(this, m_filament_color, m_filament_type, filaments, filament_map, filament_volume_map);
|
||||
// A manual grouping can point filaments at a nozzle volume the extruder does not physically
|
||||
// carry; the panel's validation timer reports that here so OK is gated on a printable map.
|
||||
m_manual_map_panel->Bind(wxEVT_INVALID_MANUAL_MAP, [this](wxCommandEvent &event) {
|
||||
if (m_page_type != PageType::ptManual) {
|
||||
if (!m_ok_btn->IsEnabled()) { m_ok_btn->Enable(); }
|
||||
return;
|
||||
}
|
||||
if (event.GetInt()) {
|
||||
if (!m_ok_btn->IsEnabled()) { m_ok_btn->Enable(); }
|
||||
} else {
|
||||
if (m_ok_btn->IsEnabled()) { m_ok_btn->Disable(); }
|
||||
}
|
||||
});
|
||||
m_auto_map_panel = new FilamentMapAutoPanel(this, default_auto_mode, auto_match_available);
|
||||
if (show_default)
|
||||
m_default_map_panel = new FilamentMapDefaultPanel(this);
|
||||
@@ -351,16 +386,8 @@ void FilamentMapDialog::on_checkbox(wxCommandEvent &event)
|
||||
void FilamentMapDialog::on_ok(wxCommandEvent &event)
|
||||
{
|
||||
if (m_page_type == PageType::ptManual) {
|
||||
std::vector<int> left_filaments = m_manual_map_panel->GetLeftFilaments();
|
||||
std::vector<int> right_filaments = m_manual_map_panel->GetRightFilaments();
|
||||
|
||||
for (int i = 0; i < m_filament_map.size(); ++i) {
|
||||
if (std::find(left_filaments.begin(), left_filaments.end(), i + 1) != left_filaments.end()) {
|
||||
m_filament_map[i] = 1;
|
||||
} else if (std::find(right_filaments.begin(), right_filaments.end(), i + 1) != right_filaments.end()) {
|
||||
m_filament_map[i] = 2;
|
||||
}
|
||||
}
|
||||
m_filament_map = m_manual_map_panel->GetFilamentMaps();
|
||||
m_filament_volume_map = m_manual_map_panel->GetFilamentVolumeMaps();
|
||||
}
|
||||
|
||||
EndModal(wxID_OK);
|
||||
@@ -398,6 +425,11 @@ void FilamentMapDialog::update_panel_status(PageType page)
|
||||
m_auto_map_panel->Show();
|
||||
}
|
||||
|
||||
// The nozzle-availability gate only constrains manual grouping; every other page must
|
||||
// leave OK usable even if the manual page had disabled it.
|
||||
if (page != PageType::ptManual && m_ok_btn && !m_ok_btn->IsEnabled())
|
||||
m_ok_btn->Enable();
|
||||
|
||||
if (m_smart_filament)
|
||||
m_smart_filament->Show(get_mode() == fmmAutoForFlush);
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ public:
|
||||
const std::vector<std::string>& filament_color,
|
||||
const std::vector<std::string>& filament_type,
|
||||
const std::vector<int> &filament_map,
|
||||
const std::vector<int> &filament_volume_map,
|
||||
const std::vector<int> &filaments,
|
||||
const FilamentMapMode mode,
|
||||
bool machine_synced,
|
||||
@@ -57,6 +58,12 @@ public:
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<int> get_filament_volume_maps() const {
|
||||
if (m_page_type == PageType::ptManual)
|
||||
return m_filament_volume_map;
|
||||
return {};
|
||||
}
|
||||
|
||||
int ShowModal();
|
||||
void set_modal_btn_labels(const wxString& left_label, const wxString& right_label);
|
||||
private:
|
||||
@@ -87,6 +94,7 @@ private:
|
||||
|
||||
private:
|
||||
std::vector<int> m_filament_map;
|
||||
std::vector<int> m_filament_volume_map;
|
||||
std::vector<std::string> m_filament_color;
|
||||
std::vector<std::string> m_filament_type;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "FilamentMapPanel.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "Widgets/MultiNozzleSync.hpp" // manuallySetNozzleCount producer for extruder_nozzle_stats
|
||||
#include <algorithm>
|
||||
#include <wx/dcbuffer.h>
|
||||
@@ -19,28 +20,278 @@ static const wxColour BorderDisableColor = wxColour("#EEEEEE");
|
||||
static const wxColour TextNormalBlackColor = wxColour("#262E30");
|
||||
static const wxColour TextNormalGreyColor = wxColour("#6B6B6B");
|
||||
static const wxColour TextDisableColor = wxColour("#CECECE");
|
||||
static const wxColour TextErrorColor = wxColour("#E14747");
|
||||
|
||||
wxDEFINE_EVENT(wxEVT_INVALID_MANUAL_MAP, wxCommandEvent);
|
||||
|
||||
// Orca: whether the edited printer has an extruder that can physically carry several nozzles
|
||||
// (only such extruders track a per-volume-type nozzle inventory worth validating against).
|
||||
static bool printer_has_multi_nozzle_extruder()
|
||||
{
|
||||
auto *max_nozzle_counts_opt = wxGetApp().preset_bundle->printers.get_edited_preset().config.option<ConfigOptionIntsNullable>("extruder_max_nozzle_count");
|
||||
// Skip nil entries: a nullable-int nil is INT_MAX (> 1) and would otherwise falsely pass the gate.
|
||||
return max_nozzle_counts_opt &&
|
||||
std::any_of(max_nozzle_counts_opt->values.begin(), max_nozzle_counts_opt->values.end(),
|
||||
[](int v) { return v > 1 && v != ConfigOptionIntsNullable::nil_value(); });
|
||||
}
|
||||
|
||||
void FilamentMapManualPanel::OnTimer(wxTimerEvent &)
|
||||
{
|
||||
bool valid = true;
|
||||
int invalid_eid = -1;
|
||||
NozzleVolumeType invalid_nozzle = NozzleVolumeType::nvtStandard;
|
||||
auto preset_bundle = wxGetApp().preset_bundle;
|
||||
auto proj_config = preset_bundle->project_config;
|
||||
auto nozzle_volume_values = proj_config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type")->values;
|
||||
std::vector<int> filament_map = GetFilamentMaps();
|
||||
std::vector<int> filament_volume_map = GetFilamentVolumeMaps();
|
||||
// Orca: only multi-nozzle extruders carry a meaningful nozzle inventory; validating a plain
|
||||
// dual-extruder printer against it would flag every grouping whenever the stats are stale.
|
||||
if (printer_has_multi_nozzle_extruder()) {
|
||||
for (size_t eid = 0; eid < nozzle_volume_values.size(); ++eid) {
|
||||
NozzleVolumeType extruder_volume_type = NozzleVolumeType(nozzle_volume_values[eid]);
|
||||
bool extruder_used = std::find_if(m_filament_list.begin(), m_filament_list.end(),
|
||||
[&filament_map, eid](int fid) {
|
||||
return fid - 1 < (int) filament_map.size() && (filament_map[fid - 1] - 1) == (int) eid;
|
||||
}) != m_filament_list.end();
|
||||
|
||||
if (!extruder_used) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (extruder_volume_type == nvtHybrid) {
|
||||
int standard_count = getExtruderNozzleCount(preset_bundle, eid, NozzleVolumeType::nvtStandard);
|
||||
int highflow_count = getExtruderNozzleCount(preset_bundle, eid, NozzleVolumeType::nvtHighFlow);
|
||||
|
||||
auto has_material_of_type = [this, eid, &filament_map, &filament_volume_map](NozzleVolumeType volume_type) {
|
||||
return std::find_if(m_filament_list.begin(), m_filament_list.end(),
|
||||
[eid, &filament_map, &filament_volume_map, volume_type](int fid) {
|
||||
return fid - 1 < (int) filament_map.size() && (filament_map[fid - 1] - 1) == (int) eid &&
|
||||
fid - 1 < (int) filament_volume_map.size() &&
|
||||
filament_volume_map[fid - 1] == static_cast<int>(volume_type);
|
||||
}) != m_filament_list.end();
|
||||
};
|
||||
|
||||
bool has_standard = has_material_of_type(NozzleVolumeType::nvtStandard);
|
||||
bool has_highflow = has_material_of_type(NozzleVolumeType::nvtHighFlow);
|
||||
|
||||
if ((has_standard && standard_count == 0) ||
|
||||
(has_highflow && highflow_count == 0)) {
|
||||
valid = false;
|
||||
invalid_eid = eid;
|
||||
invalid_nozzle = (has_standard && standard_count == 0) ? NozzleVolumeType::nvtStandard : NozzleVolumeType::nvtHighFlow;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
int count = getExtruderNozzleCount(preset_bundle, eid, extruder_volume_type);
|
||||
if (count == 0) {
|
||||
valid = false;
|
||||
invalid_eid = eid;
|
||||
invalid_nozzle = extruder_volume_type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool update_ui = m_invalid_id != invalid_eid;
|
||||
bool send_event = update_ui || m_force_validation;
|
||||
|
||||
m_invalid_id = invalid_eid;
|
||||
|
||||
if (update_ui) {
|
||||
if (valid) {
|
||||
m_errors->Hide();
|
||||
m_suggestion_panel->Hide();
|
||||
} else {
|
||||
m_errors->SetLabel(wxString::Format(_L("Error: %s extruder has no available %s nozzle, current group result is invalid."),
|
||||
invalid_eid == 0 ? _L("Left") : _L("Right"),
|
||||
invalid_nozzle == NozzleVolumeType::nvtStandard ? _L("Standard") : _L("High Flow")));
|
||||
// Re-wrap: wrapping only applies to the label text present when Wrap is called,
|
||||
// and the label was empty at construction time.
|
||||
m_errors->Wrap(FromDIP(520));
|
||||
m_errors->Show();
|
||||
m_suggestion_panel->Show();
|
||||
}
|
||||
m_left_panel->Freeze();
|
||||
m_right_panel->Freeze();
|
||||
m_tips->Freeze();
|
||||
m_description->Freeze();
|
||||
Layout();
|
||||
Fit();
|
||||
this->GetParent()->Layout();
|
||||
this->GetParent()->Fit();
|
||||
m_left_panel->Thaw();
|
||||
m_right_panel->Thaw();
|
||||
m_tips->Thaw();
|
||||
m_description->Thaw();
|
||||
}
|
||||
|
||||
if (send_event) {
|
||||
wxCommandEvent event(wxEVT_INVALID_MANUAL_MAP);
|
||||
event.SetInt(valid);
|
||||
ProcessEvent(event);
|
||||
m_force_validation = false;
|
||||
}
|
||||
}
|
||||
|
||||
void FilamentMapManualPanel::OnSuggestionClicked(wxCommandEvent &event)
|
||||
{
|
||||
wxWindow *current = this;
|
||||
while (current && !wxDynamicCast(current, wxDialog)) {
|
||||
current = current->GetParent();
|
||||
}
|
||||
|
||||
if (current) {
|
||||
wxDialog *dlg = wxDynamicCast(current, wxDialog);
|
||||
if (dlg) {
|
||||
int invalid_eid = m_invalid_id;
|
||||
dlg->EndModal(wxID_CANCEL);
|
||||
|
||||
if (invalid_eid >= 0) {
|
||||
manuallySetNozzleCount(invalid_eid);
|
||||
}
|
||||
wxGetApp().plater()->update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> FilamentMapManualPanel::GetFilamentMaps() const
|
||||
{
|
||||
std::vector<int> new_filament_map = m_filament_map;
|
||||
std::vector<int> left_filaments = this->GetLeftFilaments();
|
||||
std::vector<int> right_filaments = this->GetRightFilaments();
|
||||
|
||||
for (int i = 0; i < (int) new_filament_map.size(); ++i) {
|
||||
if (std::find(left_filaments.begin(), left_filaments.end(), i + 1) != left_filaments.end()) {
|
||||
new_filament_map[i] = 1;
|
||||
} else if (std::find(right_filaments.begin(), right_filaments.end(), i + 1) != right_filaments.end()) {
|
||||
new_filament_map[i] = 2;
|
||||
}
|
||||
}
|
||||
return new_filament_map;
|
||||
}
|
||||
|
||||
std::vector<int> FilamentMapManualPanel::GetFilamentVolumeMaps() const
|
||||
{
|
||||
std::vector<int> volume_map(m_filament_map.size(), 0);
|
||||
|
||||
std::vector<int> left_filaments = this->GetLeftFilaments();
|
||||
std::vector<int> right_high_flow_filaments = this->GetRightHighFlowFilaments();
|
||||
std::vector<int> right_standard_filaments = this->GetRightStandardFilaments();
|
||||
std::vector<int> right_tpu_high_flow_filaments = this->GetRightTPUHighFlowFilaments();
|
||||
|
||||
auto preset_bundle = wxGetApp().preset_bundle;
|
||||
auto proj_config = preset_bundle->project_config;
|
||||
auto nozzle_volume_values = proj_config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type")->values;
|
||||
|
||||
for (int i = 0; i < (int) volume_map.size(); ++i) {
|
||||
int filament_id = i + 1;
|
||||
|
||||
if (std::find(left_filaments.begin(), left_filaments.end(), filament_id) != left_filaments.end()) {
|
||||
if (nozzle_volume_values.size() > 0) {
|
||||
// Orca: never emit the Hybrid marker as a per-filament value; on a Hybrid
|
||||
// extruder each filament prints with a concrete flow, defaulting to Standard.
|
||||
volume_map[i] = nozzle_volume_values[0] == static_cast<int>(NozzleVolumeType::nvtHybrid) ?
|
||||
static_cast<int>(NozzleVolumeType::nvtStandard) :
|
||||
nozzle_volume_values[0];
|
||||
}
|
||||
}
|
||||
else if (std::find(right_high_flow_filaments.begin(), right_high_flow_filaments.end(), filament_id) != right_high_flow_filaments.end()) {
|
||||
volume_map[i] = static_cast<int>(NozzleVolumeType::nvtHighFlow);
|
||||
}
|
||||
else if (std::find(right_standard_filaments.begin(), right_standard_filaments.end(), filament_id) != right_standard_filaments.end()) {
|
||||
volume_map[i] = static_cast<int>(NozzleVolumeType::nvtStandard);
|
||||
}
|
||||
else if (std::find(right_tpu_high_flow_filaments.begin(), right_tpu_high_flow_filaments.end(), filament_id) != right_tpu_high_flow_filaments.end()) {
|
||||
volume_map[i] = static_cast<int>(NozzleVolumeType::nvtTPUHighFlow);
|
||||
}
|
||||
}
|
||||
|
||||
return volume_map;
|
||||
}
|
||||
|
||||
void FilamentMapManualPanel::SyncPanelHeights()
|
||||
{
|
||||
if (!m_left_panel || !m_right_panel) return;
|
||||
|
||||
auto curr_left = m_left_panel->GetMinSize();
|
||||
auto curr_right = m_right_panel->GetMinSize();
|
||||
|
||||
m_left_panel->SetMinSize(wxSize(FromDIP(260), FromDIP(110)));
|
||||
m_right_panel->SetMinSize(wxSize(FromDIP(260), FromDIP(110)));
|
||||
|
||||
m_left_panel->Layout();
|
||||
m_left_panel->Fit();
|
||||
m_right_panel->Layout();
|
||||
m_right_panel->Fit();
|
||||
|
||||
wxSize left_best_size = m_left_panel->GetBestSize();
|
||||
wxSize right_best_size = m_right_panel->GetBestSize();
|
||||
|
||||
int max_height = std::max(left_best_size.GetHeight(), right_best_size.GetHeight());
|
||||
bool height_changed = curr_left.GetHeight() != max_height || curr_right.GetHeight() != max_height;
|
||||
if (!height_changed) {
|
||||
if (curr_left.GetHeight() > 0)
|
||||
m_left_panel->SetMinSize(curr_left);
|
||||
if (curr_right.GetHeight() > 0)
|
||||
m_right_panel->SetMinSize(curr_right);
|
||||
if (GetParent()) {
|
||||
GetParent()->Layout();
|
||||
GetParent()->Fit();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
m_left_panel->SetMinSize(wxSize(FromDIP(260), max_height));
|
||||
m_right_panel->SetMinSize(wxSize(FromDIP(260), max_height));
|
||||
|
||||
Layout();
|
||||
Fit();
|
||||
|
||||
if (GetParent()) {
|
||||
GetParent()->Layout();
|
||||
GetParent()->Fit();
|
||||
}
|
||||
}
|
||||
|
||||
void FilamentMapManualPanel::OnDragDropCompleted(wxCommandEvent &event)
|
||||
{
|
||||
SyncPanelHeights();
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
FilamentMapManualPanel::FilamentMapManualPanel(wxWindow *parent,
|
||||
const std::vector<std::string> &color,
|
||||
const std::vector<std::string> &type,
|
||||
const std::vector<int> &filament_list,
|
||||
const std::vector<int> &filament_map)
|
||||
: wxPanel(parent), m_filament_map(filament_map), m_filament_color(color), m_filament_type(type), m_filament_list(filament_list)
|
||||
const std::vector<int> &filament_map,
|
||||
const std::vector<int> &filament_volume_map)
|
||||
: wxPanel(parent)
|
||||
, m_filament_map(filament_map)
|
||||
, m_filament_volume_map(filament_volume_map)
|
||||
, m_filament_list(filament_list)
|
||||
, m_filament_color(color)
|
||||
, m_filament_type(type)
|
||||
{
|
||||
SetName(wxT("FilamentMapManualPanel"));
|
||||
SetBackgroundColour(BgNormalColor);
|
||||
|
||||
auto top_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
m_description = new Label(this, _L("We will slice according to this grouping method:"));
|
||||
top_sizer->Add(m_description, 0, wxALIGN_LEFT | wxLEFT, FromDIP(15));
|
||||
m_description->Wrap(FromDIP(520));
|
||||
top_sizer->AddSpacer(FromDIP(8));
|
||||
|
||||
auto drag_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
m_left_panel = new DragDropPanel(this, _L("Left Nozzle"), false);
|
||||
m_right_panel = new DragDropPanel(this, _L("Right Nozzle"), false);
|
||||
m_right_panel = new SeparatedDragDropPanel(this, _L("Right Nozzle"), false);
|
||||
m_switch_btn = new ScalableButton(this, wxID_ANY, "switch_filament_maps");
|
||||
|
||||
UpdateNozzleVolumeType();
|
||||
|
||||
for (size_t idx = 0; idx < m_filament_map.size(); ++idx) {
|
||||
auto iter = std::find(m_filament_list.begin(), m_filament_list.end(), idx + 1);
|
||||
if (iter == m_filament_list.end()) continue;
|
||||
@@ -50,29 +301,55 @@ FilamentMapManualPanel::FilamentMapManualPanel(wxWindow *p
|
||||
m_left_panel->AddColorBlock(color, type, idx + 1);
|
||||
} else {
|
||||
assert(m_filament_map[idx] == 2);
|
||||
m_right_panel->AddColorBlock(color, type, idx + 1);
|
||||
bool is_high_flow = (idx < m_filament_volume_map.size()) && (m_filament_volume_map[idx] == static_cast<int>(NozzleVolumeType::nvtHighFlow));
|
||||
m_right_panel->AddColorBlock(color, type, idx + 1, is_high_flow);
|
||||
}
|
||||
}
|
||||
m_left_panel->SetMinSize({ FromDIP(260),-1 });
|
||||
m_right_panel->SetMinSize({ FromDIP(260),-1 });
|
||||
m_left_panel->SetMinSize({FromDIP(260), FromDIP(110)});
|
||||
m_right_panel->SetMinSize({FromDIP(260), FromDIP(110)});
|
||||
|
||||
drag_sizer->AddStretchSpacer();
|
||||
drag_sizer->Add(m_left_panel, 1, wxEXPAND);
|
||||
drag_sizer->AddSpacer(FromDIP(7));
|
||||
drag_sizer->Add(m_switch_btn, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, FromDIP(1));
|
||||
drag_sizer->AddSpacer(FromDIP(7));
|
||||
drag_sizer->Add(m_switch_btn, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, FromDIP(8));
|
||||
drag_sizer->Add(m_right_panel, 1, wxEXPAND);
|
||||
drag_sizer->AddStretchSpacer();
|
||||
|
||||
top_sizer->Add(drag_sizer, 0, wxEXPAND);
|
||||
|
||||
m_tips = new Label(this, _L("Tip: You can drag the filaments to reassign them to different nozzles."));
|
||||
m_tips->SetFont(Label::Body_14);
|
||||
m_tips->SetForegroundColour(TextNormalGreyColor);
|
||||
m_tips->Wrap(FromDIP(520));
|
||||
top_sizer->AddSpacer(FromDIP(20));
|
||||
top_sizer->Add(m_tips, 0, wxALIGN_LEFT | wxLEFT, FromDIP(15));
|
||||
|
||||
m_switch_btn->Bind(wxEVT_BUTTON, &FilamentMapManualPanel::OnSwitchFilament, this);
|
||||
m_errors = new Label(this, "");
|
||||
m_errors->SetFont(Label::Body_13);
|
||||
m_errors->SetForegroundColour(TextErrorColor);
|
||||
m_errors->Wrap(FromDIP(520));
|
||||
top_sizer->AddSpacer(FromDIP(10));
|
||||
top_sizer->Add(m_errors, 0, wxALIGN_LEFT | wxLEFT, FromDIP(15));
|
||||
|
||||
m_errors->Hide();
|
||||
|
||||
m_suggestion_panel = new wxPanel(this, wxID_ANY);
|
||||
m_suggestion_panel->SetBackgroundColour(*wxWHITE);
|
||||
auto suggestion_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto suggestion_text = new Label(m_suggestion_panel, _L("Please adjust your grouping or click "));
|
||||
suggestion_text->SetFont(Label::Body_13);
|
||||
suggestion_text->SetForegroundColour(TextErrorColor);
|
||||
suggestion_text->SetBackgroundColour(*wxWHITE);
|
||||
auto suggestion_btn = new ScalableButton(m_suggestion_panel, wxID_ANY, "edit", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true, 14);
|
||||
suggestion_btn->SetBackgroundColour(*wxWHITE);
|
||||
auto suggestion_text2 = new Label(m_suggestion_panel, _L(" to set nozzle count"));
|
||||
suggestion_text2->SetFont(Label::Body_13);
|
||||
suggestion_text2->SetForegroundColour(TextErrorColor);
|
||||
suggestion_text2->SetBackgroundColour(*wxWHITE);
|
||||
suggestion_sizer->Add(suggestion_text, 0, wxALIGN_CENTER_VERTICAL);
|
||||
suggestion_sizer->Add(suggestion_btn, 0, wxALIGN_CENTER_VERTICAL);
|
||||
suggestion_sizer->Add(suggestion_text2, 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_suggestion_panel->SetSizer(suggestion_sizer);
|
||||
top_sizer->Add(m_suggestion_panel, 0, wxALIGN_LEFT | wxLEFT, FromDIP(15));
|
||||
m_suggestion_panel->Hide();
|
||||
suggestion_btn->Bind(wxEVT_BUTTON, &FilamentMapManualPanel::OnSuggestionClicked, this);
|
||||
|
||||
// Multi-nozzle: give the user a reachable way to declare, per extruder, how many
|
||||
// physical nozzles of each volume type a multi-nozzle extruder carries. This is the
|
||||
@@ -80,11 +357,8 @@ FilamentMapManualPanel::FilamentMapManualPanel(wxWindow *p
|
||||
// auto-sync is deferred). Gated on the edited printer preset having an extruder with
|
||||
// extruder_max_nozzle_count > 1, so the trigger is not even created for any single-nozzle or
|
||||
// dual-extruder ({1,1}, H2D) printer - zero UI change for every existing profile.
|
||||
auto *max_nozzle_counts_opt = wxGetApp().preset_bundle->printers.get_edited_preset().config.option<ConfigOptionIntsNullable>("extruder_max_nozzle_count");
|
||||
// Skip nil entries: a nullable-int nil is INT_MAX (> 1) and would otherwise falsely pass the gate.
|
||||
if (max_nozzle_counts_opt &&
|
||||
std::any_of(max_nozzle_counts_opt->values.begin(), max_nozzle_counts_opt->values.end(),
|
||||
[](int v) { return v > 1 && v != ConfigOptionIntsNullable::nil_value(); })) {
|
||||
if (printer_has_multi_nozzle_extruder()) {
|
||||
auto *max_nozzle_counts_opt = wxGetApp().preset_bundle->printers.get_edited_preset().config.option<ConfigOptionIntsNullable>("extruder_max_nozzle_count");
|
||||
auto *set_count_link = new Label(this, _L("Set the physical nozzle count..."));
|
||||
set_count_link->SetFont(Label::Body_14);
|
||||
set_count_link->SetForegroundColour(BorderSelectedColor);
|
||||
@@ -94,26 +368,88 @@ FilamentMapManualPanel::FilamentMapManualPanel(wxWindow *p
|
||||
const std::vector<int> max_counts = max_nozzle_counts_opt->values;
|
||||
set_count_link->Bind(wxEVT_LEFT_DOWN, [max_counts](wxMouseEvent &evt) {
|
||||
for (int extruder_id = 0; extruder_id < (int) max_counts.size(); ++extruder_id) {
|
||||
if (max_counts[extruder_id] > 1)
|
||||
if (max_counts[extruder_id] > 1 && max_counts[extruder_id] != ConfigOptionIntsNullable::nil_value())
|
||||
GUI::manuallySetNozzleCount(extruder_id);
|
||||
}
|
||||
evt.Skip();
|
||||
});
|
||||
}
|
||||
|
||||
m_timer = new wxTimer(this);
|
||||
Bind(wxEVT_TIMER, &FilamentMapManualPanel::OnTimer, this);
|
||||
Bind(wxEVT_DRAG_DROP_COMPLETED, &FilamentMapManualPanel::OnDragDropCompleted, this);
|
||||
|
||||
m_switch_btn->Bind(wxEVT_BUTTON, &FilamentMapManualPanel::OnSwitchFilament, this);
|
||||
|
||||
SetSizer(top_sizer);
|
||||
SetMinSize(wxSize(FromDIP(580), -1));
|
||||
Layout();
|
||||
Fit();
|
||||
GUI::wxGetApp().UpdateDarkUIWin(this);
|
||||
}
|
||||
|
||||
void FilamentMapManualPanel::UpdateNozzleVolumeType()
|
||||
{
|
||||
auto check_separation = []() {
|
||||
auto preset_bundle = wxGetApp().preset_bundle;
|
||||
auto nozzle_volume_values = preset_bundle->project_config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type")->values;
|
||||
if (nozzle_volume_values.size() <= 1)
|
||||
return false;
|
||||
|
||||
return nozzle_volume_values[1] == static_cast<int>(NozzleVolumeType::nvtHybrid);
|
||||
};
|
||||
bool should_separate = check_separation();
|
||||
m_right_panel->SetUseSeparation(should_separate);
|
||||
|
||||
UpdateNozzleCountDisplay();
|
||||
|
||||
Layout();
|
||||
Fit();
|
||||
}
|
||||
|
||||
void FilamentMapManualPanel::UpdateNozzleCountDisplay()
|
||||
{
|
||||
auto preset_bundle = wxGetApp().preset_bundle;
|
||||
|
||||
// Orca: nozzle counts are only tracked (and only meaningful) for multi-nozzle extruders;
|
||||
// plain dual-extruder printers keep their unadorned zone titles.
|
||||
if (!printer_has_multi_nozzle_extruder()) {
|
||||
m_left_panel->UpdateLabel(_L("Left Nozzle"));
|
||||
m_right_panel->UpdateLabel(_L("Right Nozzle"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Format the count suffix separately so a translation containing '%' cannot
|
||||
// corrupt the wxString::Format output.
|
||||
int left_count = getExtruderNozzleCountTotal(preset_bundle, 0);
|
||||
wxString left_title = _L("Left Nozzle") + wxString::Format("(%d)", left_count);
|
||||
m_left_panel->UpdateLabel(left_title);
|
||||
|
||||
if (m_right_panel->IsUseSeparation()) {
|
||||
int standard_count = getExtruderNozzleCount(preset_bundle, 1, NozzleVolumeType::nvtStandard);
|
||||
int highflow_count = getExtruderNozzleCount(preset_bundle, 1, NozzleVolumeType::nvtHighFlow);
|
||||
wxString right_title = _L("Right Nozzle") + wxString::Format("(Std: %d, HF: %d)", standard_count, highflow_count);
|
||||
m_right_panel->UpdateLabel(right_title);
|
||||
} else {
|
||||
int right_count = getExtruderNozzleCountTotal(preset_bundle, 1);
|
||||
wxString right_title = _L("Right Nozzle") + wxString::Format("(%d)", right_count);
|
||||
m_right_panel->UpdateLabel(right_title);
|
||||
}
|
||||
}
|
||||
|
||||
FilamentMapManualPanel::~FilamentMapManualPanel()
|
||||
{
|
||||
m_timer->Stop();
|
||||
delete m_timer;
|
||||
}
|
||||
|
||||
void FilamentMapManualPanel::OnSwitchFilament(wxCommandEvent &)
|
||||
{
|
||||
auto left_blocks = m_left_panel->get_filament_blocks();
|
||||
auto right_blocks = m_right_panel->get_filament_blocks();
|
||||
|
||||
for (auto &block : left_blocks) {
|
||||
m_right_panel->AddColorBlock(block->GetColor(), block->GetType(), block->GetFilamentId(), false);
|
||||
m_right_panel->AddColorBlock(block->GetColor(), block->GetType(), block->GetFilamentId(), false, false);
|
||||
m_left_panel->RemoveColorBlock(block, false);
|
||||
}
|
||||
|
||||
@@ -123,22 +459,27 @@ void FilamentMapManualPanel::OnSwitchFilament(wxCommandEvent &)
|
||||
}
|
||||
this->GetParent()->Layout();
|
||||
this->GetParent()->Fit();
|
||||
|
||||
if (m_right_panel->IsUseSeparation()) {
|
||||
m_left_panel->Layout();
|
||||
m_left_panel->Fit();
|
||||
m_right_panel->Layout();
|
||||
m_right_panel->Fit();
|
||||
SyncPanelHeights();
|
||||
}
|
||||
}
|
||||
|
||||
void FilamentMapManualPanel::Hide()
|
||||
bool FilamentMapManualPanel::Show(bool show)
|
||||
{
|
||||
m_left_panel->Hide();
|
||||
m_right_panel->Hide();
|
||||
m_switch_btn->Hide();
|
||||
wxPanel::Hide();
|
||||
}
|
||||
m_force_validation = show;
|
||||
if (show) {
|
||||
m_timer->Start(500);
|
||||
SyncPanelHeights();
|
||||
} else {
|
||||
m_timer->Stop();
|
||||
}
|
||||
|
||||
void FilamentMapManualPanel::Show()
|
||||
{
|
||||
m_left_panel->Show();
|
||||
m_right_panel->Show();
|
||||
m_switch_btn->Show();
|
||||
wxPanel::Show();
|
||||
return wxPanel::Show(show);
|
||||
}
|
||||
|
||||
GUI::FilamentMapBtnPanel::FilamentMapBtnPanel(wxWindow *parent, const wxString &label, const wxString &detail, const std::string &icon) : wxPanel(parent)
|
||||
|
||||
@@ -9,32 +9,60 @@
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
// Fired by the manual panel's validation timer: the event int is 1 when the current manual
|
||||
// grouping is printable with the installed nozzles, 0 when some zone has no matching nozzle.
|
||||
wxDECLARE_EVENT(wxEVT_INVALID_MANUAL_MAP, wxCommandEvent);
|
||||
|
||||
class FilamentMapManualPanel : public wxPanel
|
||||
{
|
||||
public:
|
||||
FilamentMapManualPanel(wxWindow *parent, const std::vector<std::string> &color, const std::vector<std::string> &type, const std::vector<int> &filament_list, const std::vector<int> &filament_map);
|
||||
FilamentMapManualPanel(wxWindow *parent,
|
||||
const std::vector<std::string> &color,
|
||||
const std::vector<std::string> &type,
|
||||
const std::vector<int> &filament_list,
|
||||
const std::vector<int> &filament_map,
|
||||
const std::vector<int> &filament_volume_map);
|
||||
~FilamentMapManualPanel();
|
||||
|
||||
std::vector<int> GetFilamentMaps() const { return m_filament_map; }
|
||||
std::vector<int> GetFilamentMaps() const;
|
||||
std::vector<int> GetFilamentVolumeMaps() const;
|
||||
std::vector<int> GetLeftFilaments() const { return m_left_panel->GetAllFilaments(); }
|
||||
std::vector<int> GetRightFilaments() const { return m_right_panel->GetAllFilaments(); }
|
||||
|
||||
void Hide();
|
||||
void Show();
|
||||
std::vector<int> GetRightHighFlowFilaments() const { return m_right_panel->GetHighFlowFilaments(); }
|
||||
std::vector<int> GetRightStandardFilaments() const { return m_right_panel->GetStandardFilaments(); }
|
||||
std::vector<int> GetRightTPUHighFlowFilaments() const { return m_right_panel->GetTPUHighFlowFilaments(); }
|
||||
void UpdateNozzleVolumeType();
|
||||
void UpdateNozzleCountDisplay();
|
||||
|
||||
bool Show(bool show = true) override;
|
||||
|
||||
private:
|
||||
void OnSwitchFilament(wxCommandEvent &);
|
||||
DragDropPanel *m_left_panel;
|
||||
DragDropPanel *m_right_panel;
|
||||
void OnTimer(wxTimerEvent &evt);
|
||||
void OnSwitchFilament(wxCommandEvent &);
|
||||
void SyncPanelHeights();
|
||||
void OnDragDropCompleted(wxCommandEvent &evt);
|
||||
void OnSuggestionClicked(wxCommandEvent &event);
|
||||
|
||||
Label *m_description;
|
||||
Label *m_tips;
|
||||
DragDropPanel *m_left_panel;
|
||||
SeparatedDragDropPanel *m_right_panel;
|
||||
|
||||
Label *m_description;
|
||||
Label *m_tips;
|
||||
Label *m_errors;
|
||||
wxPanel *m_suggestion_panel;
|
||||
|
||||
ScalableButton *m_switch_btn;
|
||||
|
||||
std::vector<int> m_filament_map;
|
||||
std::vector<int> m_filament_volume_map;
|
||||
std::vector<int> m_filament_list;
|
||||
std::vector<std::string> m_filament_color;
|
||||
std::vector<std::string> m_filament_type;
|
||||
|
||||
wxTimer *m_timer;
|
||||
int m_invalid_id{-1};
|
||||
bool m_force_validation{false};
|
||||
};
|
||||
|
||||
class FilamentMapBtnPanel : public wxPanel
|
||||
|
||||
@@ -3894,6 +3894,16 @@ void PartPlate::set_filament_count(int filament_count)
|
||||
std::vector<int>& filament_maps = m_config.option<ConfigOptionInts>("filament_map")->values;
|
||||
filament_maps.resize(filament_count, 1);
|
||||
}
|
||||
|
||||
if (m_config.has("filament_nozzle_map")) {
|
||||
std::vector<int>& filament_nozzle_map = m_config.option<ConfigOptionInts>("filament_nozzle_map")->values;
|
||||
filament_nozzle_map.resize(filament_count, 0);
|
||||
}
|
||||
|
||||
if (m_config.has("filament_volume_map")) {
|
||||
std::vector<int>& filament_volume_map = m_config.option<ConfigOptionInts>("filament_volume_map")->values;
|
||||
filament_volume_map.resize(filament_count, static_cast<int>(NozzleVolumeType::nvtStandard));
|
||||
}
|
||||
}
|
||||
|
||||
void PartPlate::on_filament_added()
|
||||
@@ -3902,6 +3912,27 @@ void PartPlate::on_filament_added()
|
||||
std::vector<int>& filament_maps = m_config.option<ConfigOptionInts>("filament_map")->values;
|
||||
filament_maps.push_back(1);
|
||||
}
|
||||
|
||||
if (m_config.has("filament_nozzle_map")) {
|
||||
std::vector<int>& filament_nozzle_map = m_config.option<ConfigOptionInts>("filament_nozzle_map")->values;
|
||||
filament_nozzle_map.push_back(0);
|
||||
}
|
||||
|
||||
if (m_config.has("filament_volume_map")) {
|
||||
std::vector<int>& filament_volume_map = m_config.option<ConfigOptionInts>("filament_volume_map")->values;
|
||||
// A new filament defaults onto the first extruder, so seed its volume value from
|
||||
// that extruder's flow type.
|
||||
int volume_type = static_cast<int>(NozzleVolumeType::nvtStandard);
|
||||
auto nozzle_volumes = wxGetApp().preset_bundle->project_config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type");
|
||||
if (nozzle_volumes && !nozzle_volumes->values.empty())
|
||||
volume_type = nozzle_volumes->values[0];
|
||||
// Orca: never store the Hybrid marker as a per-filament value; on a Hybrid extruder
|
||||
// each filament still prints with a concrete flow, defaulting to Standard.
|
||||
if (volume_type == static_cast<int>(NozzleVolumeType::nvtHybrid))
|
||||
volume_type = static_cast<int>(NozzleVolumeType::nvtStandard);
|
||||
|
||||
filament_volume_map.push_back(volume_type);
|
||||
}
|
||||
}
|
||||
|
||||
void PartPlate::on_filament_deleted(int filament_count, int filament_id)
|
||||
@@ -3914,6 +3945,19 @@ void PartPlate::on_filament_deleted(int filament_count, int filament_id)
|
||||
if (filament_id >= 0 && filament_id < (int) filament_maps.size())
|
||||
filament_maps.erase(filament_maps.begin() + filament_id);
|
||||
}
|
||||
|
||||
if (m_config.has("filament_nozzle_map")) {
|
||||
std::vector<int>& filament_nozzle_map = m_config.option<ConfigOptionInts>("filament_nozzle_map")->values;
|
||||
if (filament_id >= 0 && filament_id < (int) filament_nozzle_map.size())
|
||||
filament_nozzle_map.erase(filament_nozzle_map.begin() + filament_id);
|
||||
}
|
||||
|
||||
if (m_config.has("filament_volume_map")) {
|
||||
std::vector<int>& filament_volume_map = m_config.option<ConfigOptionInts>("filament_volume_map")->values;
|
||||
if (filament_id >= 0 && filament_id < (int) filament_volume_map.size())
|
||||
filament_volume_map.erase(filament_volume_map.begin() + filament_id);
|
||||
}
|
||||
|
||||
update_first_layer_print_sequence_when_delete_filament(filament_id);
|
||||
}
|
||||
|
||||
|
||||
@@ -1213,9 +1213,16 @@ ExtruderGroup::ExtruderGroup(wxWindow * parent, int index, wxString const &title
|
||||
combo_flow->GetDropDown().SetUseContentWidth(true);
|
||||
combo_flow->Bind(wxEVT_COMBOBOX, [this, index, combo_flow](wxCommandEvent &evt) {
|
||||
auto printer_tab = dynamic_cast<TabPrinter *>(wxGetApp().get_tab(Preset::TYPE_PRINTER));
|
||||
printer_tab->set_extruder_volume_type(index, NozzleVolumeType(intptr_t(combo_flow->GetClientData(evt.GetInt()))));
|
||||
if (GUI::wxGetApp().plater())
|
||||
GUI::wxGetApp().plater()->update_machine_sync_status();
|
||||
NozzleVolumeType volume_type = NozzleVolumeType(intptr_t(combo_flow->GetClientData(evt.GetInt())));
|
||||
printer_tab->set_extruder_volume_type(index, volume_type);
|
||||
auto plater = GUI::wxGetApp().plater();
|
||||
if (plater) {
|
||||
// A new Flow type invalidates the per-filament volume choices stored on the
|
||||
// plates for this extruder; rewrite them so the next apply/grouping sees the
|
||||
// selected volume instead of a stale one.
|
||||
plater->update_filament_volume_map(index, static_cast<int>(volume_type));
|
||||
plater->update_machine_sync_status();
|
||||
}
|
||||
});
|
||||
this->combo_flow = combo_flow;
|
||||
|
||||
@@ -7328,6 +7335,16 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||
filament_map->values.resize(filament_count, 1);
|
||||
}
|
||||
|
||||
ConfigOptionInts* filament_nozzle_map = proj_cfg.opt<ConfigOptionInts>("filament_nozzle_map", true);
|
||||
if (filament_nozzle_map->size() != filament_count) {
|
||||
filament_nozzle_map->values.resize(filament_count, 0);
|
||||
}
|
||||
|
||||
ConfigOptionInts* filament_volume_map = proj_cfg.opt<ConfigOptionInts>("filament_volume_map", true);
|
||||
if (filament_volume_map->size() != filament_count) {
|
||||
filament_volume_map->values.resize(filament_count, static_cast<int>(NozzleVolumeType::nvtStandard));
|
||||
}
|
||||
|
||||
// Sync filament multi colour
|
||||
ConfigOptionStrings* filament_multi_color = proj_cfg.opt<ConfigOptionStrings>("filament_multi_colour", true);
|
||||
if (filament_multi_color->size() != filament_count) {
|
||||
@@ -17799,12 +17816,24 @@ void Plater::set_global_filament_map(const std::vector<int>& filament_map)
|
||||
project_config.option<ConfigOptionInts>("filament_map")->values = filament_map;
|
||||
}
|
||||
|
||||
void Plater::set_global_filament_volume_map(const std::vector<int>& filament_volume_map)
|
||||
{
|
||||
auto& project_config = wxGetApp().preset_bundle->project_config;
|
||||
project_config.option<ConfigOptionInts>("filament_volume_map")->values = filament_volume_map;
|
||||
}
|
||||
|
||||
std::vector<int> Plater::get_global_filament_map() const
|
||||
{
|
||||
auto& project_config = wxGetApp().preset_bundle->project_config;
|
||||
return project_config.option<ConfigOptionInts>("filament_map")->values;
|
||||
}
|
||||
|
||||
std::vector<int> Plater::get_global_filament_volume_map() const
|
||||
{
|
||||
auto& project_config = wxGetApp().preset_bundle->project_config;
|
||||
return project_config.option<ConfigOptionInts>("filament_volume_map")->values;
|
||||
}
|
||||
|
||||
|
||||
FilamentMapMode Plater::get_global_filament_map_mode() const
|
||||
{
|
||||
@@ -18721,13 +18750,17 @@ void Plater::open_filament_map_setting_dialog(wxCommandEvent &evt)
|
||||
|
||||
auto plate_filament_maps = curr_plate->get_real_filament_maps(project_config);
|
||||
auto plate_filament_map_mode = curr_plate->get_filament_map_mode();
|
||||
auto plate_filament_volume_maps = curr_plate->get_real_filament_volume_maps(project_config);
|
||||
if (plate_filament_maps.size() != filament_colors.size()) // refine it later, save filament map to app config
|
||||
plate_filament_maps.resize(filament_colors.size(), 1);
|
||||
if (plate_filament_volume_maps.size() != filament_colors.size())
|
||||
plate_filament_volume_maps.resize(filament_colors.size(), 0);
|
||||
|
||||
FilamentMapDialog filament_dlg(this,
|
||||
filament_colors,
|
||||
filament_types,
|
||||
plate_filament_maps,
|
||||
plate_filament_volume_maps,
|
||||
curr_plate->get_extruders(true),
|
||||
plate_filament_map_mode,
|
||||
this->get_machine_sync_status(),
|
||||
@@ -18741,16 +18774,21 @@ void Plater::open_filament_map_setting_dialog(wxCommandEvent &evt)
|
||||
FilamentMapMode old_map_mode = curr_plate->get_filament_map_mode();
|
||||
FilamentMapMode new_map_mode = filament_dlg.get_mode();
|
||||
|
||||
std::vector<int> new_filament_volume_maps = filament_dlg.get_filament_volume_maps();
|
||||
std::vector<int> old_filament_volume_maps = curr_plate->get_real_filament_volume_maps(project_config);
|
||||
|
||||
if (new_map_mode != old_map_mode) {
|
||||
curr_plate->set_filament_map_mode(new_map_mode);
|
||||
}
|
||||
|
||||
if (new_map_mode == fmmManual){
|
||||
curr_plate->set_filament_maps(new_filament_maps);
|
||||
curr_plate->set_filament_volume_maps(new_filament_volume_maps);
|
||||
}
|
||||
|
||||
bool need_invalidate = (old_map_mode != new_map_mode ||
|
||||
old_filament_maps != new_filament_maps);
|
||||
old_filament_maps != new_filament_maps ||
|
||||
old_filament_volume_maps != new_filament_volume_maps);
|
||||
|
||||
if (need_invalidate) {
|
||||
if (need_slice) {
|
||||
@@ -19216,6 +19254,30 @@ bool Plater::get_machine_sync_status()
|
||||
return p->get_machine_sync_status();
|
||||
}
|
||||
|
||||
void Plater::update_filament_volume_map(int extruder_id, int volume_type)
|
||||
{
|
||||
// Hybrid is a per-extruder mix, not a per-filament volume; reset the affected filaments
|
||||
// to Standard so the manual grouping dialog starts from a concrete assignment.
|
||||
int selected_volume_type = volume_type == static_cast<int>(NozzleVolumeType::nvtHybrid) ? static_cast<int>(NozzleVolumeType::nvtStandard) : volume_type;
|
||||
auto& partplate_list = get_partplate_list();
|
||||
for (int idx = 0; idx < partplate_list.get_plate_count(); ++idx) {
|
||||
auto plate = partplate_list.get_plate(idx);
|
||||
if (!plate) continue;
|
||||
auto filament_map = plate->get_filament_maps();
|
||||
auto filament_volume_map = plate->get_filament_volume_maps();
|
||||
if (filament_map.empty() || filament_volume_map.empty()) continue;
|
||||
if (filament_volume_map.size() < filament_map.size()) {
|
||||
filament_volume_map.resize(filament_map.size(), static_cast<int>(NozzleVolumeType::nvtStandard));
|
||||
}
|
||||
for (size_t i = 0; i < filament_map.size(); ++i) {
|
||||
if (filament_map[i] == extruder_id + 1) {
|
||||
filament_volume_map[i] = selected_volume_type;
|
||||
}
|
||||
}
|
||||
plate->set_filament_volume_maps(filament_volume_map);
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_ENVIRONMENT_MAP
|
||||
void Plater::init_environment_texture()
|
||||
{
|
||||
|
||||
@@ -586,7 +586,9 @@ public:
|
||||
|
||||
void set_global_filament_map_mode(FilamentMapMode mode);
|
||||
void set_global_filament_map(const std::vector<int>& filament_map);
|
||||
void set_global_filament_volume_map(const std::vector<int>& filament_volume_map);
|
||||
std::vector<int> get_global_filament_map() const;
|
||||
std::vector<int> get_global_filament_volume_map() const;
|
||||
FilamentMapMode get_global_filament_map_mode() const;
|
||||
|
||||
void update_menus();
|
||||
@@ -748,6 +750,11 @@ public:
|
||||
|
||||
void update_machine_sync_status();
|
||||
|
||||
// Rewrite every plate's per-filament volume choice for the filaments grouped onto this
|
||||
// extruder after its Flow type changed (Hybrid resets them to Standard so the user
|
||||
// re-assigns concrete volumes in the grouping dialog).
|
||||
void update_filament_volume_map(int extruder_id, int volume_type);
|
||||
|
||||
#if ENABLE_ENVIRONMENT_MAP
|
||||
void init_environment_texture();
|
||||
unsigned int get_environment_texture_id() const;
|
||||
|
||||
@@ -35,7 +35,7 @@ static const int RightExtruderIdx = 1;
|
||||
// config key (ConfigOptionStrings, one encoded map per extruder) via get_extruder_nozzle_stats /
|
||||
// save_extruder_nozzle_stats_to_string. These helpers read and write that config.
|
||||
|
||||
static int extruder_nozzle_count(PresetBundle *preset_bundle, int extruder_id, NozzleVolumeType volume_type)
|
||||
int getExtruderNozzleCount(PresetBundle *preset_bundle, int extruder_id, NozzleVolumeType volume_type)
|
||||
{
|
||||
if (!preset_bundle)
|
||||
return 0;
|
||||
@@ -49,7 +49,7 @@ static int extruder_nozzle_count(PresetBundle *preset_bundle, int extruder_id, N
|
||||
return it == stats[extruder_id].end() ? 0 : it->second;
|
||||
}
|
||||
|
||||
static int extruder_nozzle_count_total(PresetBundle *preset_bundle, int extruder_id)
|
||||
int getExtruderNozzleCountTotal(PresetBundle *preset_bundle, int extruder_id)
|
||||
{
|
||||
if (!preset_bundle)
|
||||
return 0;
|
||||
@@ -230,8 +230,8 @@ void updateNozzleCountDisplay(PresetBundle *preset_bundle, int extruder_id, Nozz
|
||||
|
||||
int display_count = -1; // hides the badge
|
||||
if (support_multi_nozzle)
|
||||
display_count = volume_type == nvtHybrid ? extruder_nozzle_count_total(preset_bundle, extruder_id)
|
||||
: extruder_nozzle_count(preset_bundle, extruder_id, volume_type);
|
||||
display_count = volume_type == nvtHybrid ? getExtruderNozzleCountTotal(preset_bundle, extruder_id)
|
||||
: getExtruderNozzleCount(preset_bundle, extruder_id, volume_type);
|
||||
plater->sidebar().set_extruder_nozzle_count(extruder_id, display_count);
|
||||
}
|
||||
|
||||
@@ -269,7 +269,7 @@ void onNozzleVolumeTypeSwitch(PresetBundle *preset_bundle, int extruder_id, Nozz
|
||||
// Hybrid is a mix, not a type every nozzle shares, so there is nothing to carry over.
|
||||
if (type == nvtHybrid)
|
||||
return;
|
||||
const int total = extruder_nozzle_count_total(preset_bundle, extruder_id);
|
||||
const int total = getExtruderNozzleCountTotal(preset_bundle, extruder_id);
|
||||
setExtruderNozzleCount(preset_bundle, extruder_id, type, total, true);
|
||||
}
|
||||
|
||||
@@ -294,14 +294,14 @@ void manuallySetNozzleCount(int extruder_id)
|
||||
return;
|
||||
|
||||
const NozzleVolumeType volume_type = NozzleVolumeType(nozzle_volume_type_opt->values[extruder_id]);
|
||||
const int standard_count = extruder_nozzle_count(preset_bundle, extruder_id, nvtStandard);
|
||||
const int highflow_count = extruder_nozzle_count(preset_bundle, extruder_id, nvtHighFlow);
|
||||
const int standard_count = getExtruderNozzleCount(preset_bundle, extruder_id, nvtStandard);
|
||||
const int highflow_count = getExtruderNozzleCount(preset_bundle, extruder_id, nvtHighFlow);
|
||||
|
||||
// Require at least one nozzle for a Hybrid extruder (an empty mix is meaningless) and when the other
|
||||
// extruder currently has none.
|
||||
bool force_no_zero = volume_type == nvtHybrid;
|
||||
if (nozzle_volume_type_opt->values.size() > 1)
|
||||
force_no_zero |= extruder_nozzle_count_total(preset_bundle, 1 - extruder_id) == 0;
|
||||
force_no_zero |= getExtruderNozzleCountTotal(preset_bundle, 1 - extruder_id) == 0;
|
||||
|
||||
ManualNozzleCountDialog dialog(wxGetApp().plater(), volume_type, standard_count, highflow_count, max_nozzle_count->values[extruder_id], force_no_zero);
|
||||
if (dialog.ShowModal() == wxID_OK) {
|
||||
|
||||
@@ -219,6 +219,11 @@ std::optional<NozzleOption> tryPopUpMultiNozzleDialog(MachineObject* obj);
|
||||
// counts are reset first.
|
||||
void setExtruderNozzleCount(PresetBundle *preset_bundle, int extruder_id, NozzleVolumeType type, int nozzle_count, bool clear_all);
|
||||
|
||||
// Read one extruder's nozzle count for a volume type (or its total) from the edited printer preset's
|
||||
// `extruder_nozzle_stats` config key. Returns 0 when the stats are absent or the extruder is unknown.
|
||||
int getExtruderNozzleCount(PresetBundle *preset_bundle, int extruder_id, NozzleVolumeType volume_type);
|
||||
int getExtruderNozzleCountTotal(PresetBundle *preset_bundle, int extruder_id);
|
||||
|
||||
// Refresh the sidebar nozzle-count badge for one extruder: shows the extruder's physical nozzle count on
|
||||
// multi-nozzle printers, hides it (count -1) everywhere else.
|
||||
void updateNozzleCountDisplay(PresetBundle *preset_bundle, int extruder_id, NozzleVolumeType volume_type);
|
||||
|
||||
Reference in New Issue
Block a user