diff options
-rw-r--r-- | resources/icons/add_gcode.svg | 12 | ||||
-rw-r--r-- | resources/icons/pause_add.png | bin | 0 -> 5883 bytes | |||
-rw-r--r-- | src/libslic3r/GCode.cpp | 57 | ||||
-rw-r--r-- | src/libslic3r/GCode.hpp | 2 | ||||
-rw-r--r-- | src/libslic3r/Model.cpp | 16 | ||||
-rw-r--r-- | src/libslic3r/Model.hpp | 30 | ||||
-rw-r--r-- | src/libslic3r/Print.cpp | 70 | ||||
-rw-r--r-- | src/libslic3r/Print.hpp | 6 | ||||
-rw-r--r-- | src/slic3r/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/slic3r/GUI/BackgroundSlicingProcess.hpp | 5 | ||||
-rw-r--r-- | src/slic3r/GUI/ExtruderSequenceDialog.cpp | 197 | ||||
-rw-r--r-- | src/slic3r/GUI/ExtruderSequenceDialog.hpp | 45 | ||||
-rw-r--r-- | src/slic3r/GUI/GLCanvas3D.cpp | 184 | ||||
-rw-r--r-- | src/slic3r/GUI/GLCanvas3D.hpp | 11 | ||||
-rw-r--r-- | src/slic3r/GUI/GUI_ObjectList.cpp | 16 | ||||
-rw-r--r-- | src/slic3r/GUI/GUI_ObjectManipulation.cpp | 2 | ||||
-rw-r--r-- | src/slic3r/GUI/GUI_Preview.cpp | 187 | ||||
-rw-r--r-- | src/slic3r/GUI/GUI_Preview.hpp | 15 | ||||
-rw-r--r-- | src/slic3r/GUI/Plater.cpp | 15 | ||||
-rw-r--r-- | src/slic3r/GUI/wxExtensions.cpp | 484 | ||||
-rw-r--r-- | src/slic3r/GUI/wxExtensions.hpp | 118 |
21 files changed, 1352 insertions, 122 deletions
diff --git a/resources/icons/add_gcode.svg b/resources/icons/add_gcode.svg new file mode 100644 index 000000000..e2aa21adf --- /dev/null +++ b/resources/icons/add_gcode.svg @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve"> +<g id="export_x5F_gcode"> + <g> + <path fill="#808080" d="M5.02,7.17H9v3.08c0,2.6-1.23,3.72-4.05,3.72S1,12.85,1,10.29V5.54C1,3.12,2.09,2,4.95,2S9,3,9,5.54H6.88 + c0-1.11-0.28-1.66-1.92-1.66c-1.54,0-1.83,0.69-1.83,1.77v4.65c0,1.12,0.29,1.77,1.83,1.77c1.54,0,2.08-0.65,2.08-1.82V9.09H5.02 + V7.17z"/> + </g> +</g> +</svg> diff --git a/resources/icons/pause_add.png b/resources/icons/pause_add.png Binary files differnew file mode 100644 index 000000000..afe881de8 --- /dev/null +++ b/resources/icons/pause_add.png diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 26d2fc649..2a74de41d 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -874,8 +874,11 @@ void GCode::_do_export(Print &print, FILE *file) this->apply_print_config(print.config()); this->set_extruders(print.extruders()); - // Initialize colorprint. + // #ys_FIXME_COLOR // Initialize colorprint. m_colorprint_heights = cast<float>(print.config().colorprint_heights.values); + // Initialize custom gcode + Model* model = print.get_object(0)->model_object()->get_model(); + m_custom_g_code_heights = model->custom_gcode_per_height; // Initialize autospeed. { @@ -1680,19 +1683,53 @@ void GCode::process_layer( // In case there are more toolchange requests that weren't done yet and should happen simultaneously, erase them all. // (Layers can be close to each other, model could have been resliced with bigger layer height, ...). bool colorprint_change = false; - while (!m_colorprint_heights.empty() && m_colorprint_heights.front()-EPSILON < layer.print_z) { - m_colorprint_heights.erase(m_colorprint_heights.begin()); + // #ys_FIXME_COLOR + // while (!m_colorprint_heights.empty() && m_colorprint_heights.front()-EPSILON < layer.print_z) { + // m_colorprint_heights.erase(m_colorprint_heights.begin()); + // colorprint_change = true; + // } + std::string custom_code = ""; + int m600_before_extruder = -1; + while (!m_custom_g_code_heights.empty() && m_custom_g_code_heights.front().height-EPSILON < layer.print_z) { + custom_code = m_custom_g_code_heights.front().gcode; + if (custom_code == "M600" && m_custom_g_code_heights.front().extruder > 0) + m600_before_extruder = m_custom_g_code_heights.front().extruder - 1; + m_custom_g_code_heights.erase(m_custom_g_code_heights.begin()); colorprint_change = true; } // we should add or not colorprint_change in respect to nozzle_diameter count instead of really used extruders count - if (colorprint_change && print./*extruders()*/config().nozzle_diameter.size()==1) - { - // add tag for analyzer - gcode += "; " + GCodeAnalyzer::Color_Change_Tag + "\n"; - // add tag for time estimator - gcode += "; " + GCodeTimeEstimator::Color_Change_Tag + "\n"; - gcode += "M600\n"; + // #ys_FIXME_COLOR + // if (colorprint_change && print./*extruders()*/config().nozzle_diameter.size()==1) + // { + // // add tag for analyzer + // gcode += "; " + GCodeAnalyzer::Color_Change_Tag + "\n"; + // // add tag for time estimator + // gcode += "; " + GCodeTimeEstimator::Color_Change_Tag + "\n"; + // + // gcode += "M600\n"; + // } + if (colorprint_change) { + const bool single_material_print = print.config().nozzle_diameter.size() == 1; + if (single_material_print || custom_code != "tool_change") + { + // add tag for analyzer + gcode += "; " + GCodeAnalyzer::Color_Change_Tag + "\n"; + // add tag for time estimator + gcode += "; " + GCodeTimeEstimator::Color_Change_Tag + "\n"; + if (single_material_print && custom_code == "tool_change") + custom_code = "M600"; + + if (!single_material_print && custom_code == "M600" && + m600_before_extruder >= 0 && first_extruder_id != m600_before_extruder + // && !MMU1 + ) { + gcode += "M601\n"; // pause print + gcode += "M117 Change filament for Extruder " + std::to_string(m600_before_extruder) + "\n"; + } + else + gcode += custom_code + "\n"; + } } diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 45ff7eda6..a1a83983e 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -353,6 +353,8 @@ protected: // Layer heights for colorprint - updated before the export and erased during the process // so no toolchange occurs twice. std::vector<float> m_colorprint_heights; + // extensions for colorprint - now it's not a just color_print, there can be some custom gcode + std::vector<Model::CustomGCode> m_custom_g_code_heights; // Time estimators GCodeTimeEstimator m_normal_time_estimator; diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 061c5bd50..0bb2edfc4 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -586,6 +586,22 @@ std::string Model::propose_export_file_name_and_path(const std::string &new_exte return boost::filesystem::path(this->propose_export_file_name_and_path()).replace_extension(new_extension).string(); } +std::vector<std::pair<double, DynamicPrintConfig>> Model::get_custom_tool_changes(double default_layer_height, size_t num_extruders) const +{ + std::vector<std::pair<double, DynamicPrintConfig>> custom_tool_changes; + if (!custom_gcode_per_height.empty()) { + for (const CustomGCode& custom_gcode : custom_gcode_per_height) + if (custom_gcode.gcode == "tool_change") { + DynamicPrintConfig config; + // If extruder count in PrinterSettings was changed, use default (0) extruder for extruders, more than num_extruders + config.set_key_value("extruder", new ConfigOptionInt(custom_gcode.extruder > num_extruders ? 0 : custom_gcode.extruder)); + // For correct extruders(tools) changing, we should decrease custom_gcode.height value by one default layer height + custom_tool_changes.push_back({ custom_gcode.height - default_layer_height, config }); + } + } + return custom_tool_changes; +} + ModelObject::~ModelObject() { this->clear_volumes(); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 410c2d3ef..47f735508 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -745,6 +745,33 @@ public: ModelObjectPtrs objects; // Wipe tower object. ModelWipeTower wipe_tower; + + // Extensions for color print + struct CustomGCode + { + CustomGCode(double height, const std::string& code, int extruder) : + height(height), gcode(code), extruder(extruder) {} + + bool operator<(const CustomGCode& other) const { return other.height > this->height; } + bool operator==(const CustomGCode& other) const + { + return (other.height == this->height) && + (other.gcode == this->gcode) && + (other.extruder == this->extruder ); + } + bool operator!=(const CustomGCode& other) const + { + return (other.height != this->height) || + (other.gcode != this->gcode) || + (other.extruder != this->extruder ); + } + + double height; + std::string gcode; + int extruder; // 0 - "gcode" will be applied for whole print + // else - "gcode" will be applied only for "extruder" print + }; + std::vector<CustomGCode> custom_gcode_per_height; // Default constructor assigns a new ID to the model. Model() { assert(this->id().valid()); } @@ -810,6 +837,9 @@ public: // Propose an output path, replace extension. The new_extension shall contain the initial dot. std::string propose_export_file_name_and_path(const std::string &new_extension) const; + // from custom_gcode_per_height get just tool_change codes + std::vector<std::pair<double, DynamicPrintConfig>> get_custom_tool_changes(double default_layer_height, size_t num_extruders) const; + private: explicit Model(int) : ObjectBase(-1) { assert(this->id().invalid()); }; void assign_new_unique_ids_recursive(); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 88645df15..a8d905efe 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -636,11 +636,59 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ else m_ranges.emplace_back(t_layer_height_range(m_ranges.back().first.second, DBL_MAX), nullptr); } + + // Convert input config ranges into continuous non-overlapping sorted vector of intervals and their configs, + // considering custom_tool_change values + void assign(const t_layer_config_ranges &in, const std::vector<std::pair<double, DynamicPrintConfig>> &custom_tool_changes) { + m_ranges.clear(); + m_ranges.reserve(in.size()); + // Input ranges are sorted lexicographically. First range trims the other ranges. + coordf_t last_z = 0; + for (const std::pair<const t_layer_height_range, DynamicPrintConfig> &range : in) + if (range.first.second > last_z) { + coordf_t min_z = std::max(range.first.first, 0.); + if (min_z > last_z + EPSILON) { + m_ranges.emplace_back(t_layer_height_range(last_z, min_z), nullptr); + last_z = min_z; + } + if (range.first.second > last_z + EPSILON) { + const DynamicPrintConfig* cfg = &range.second; + m_ranges.emplace_back(t_layer_height_range(last_z, range.first.second), cfg); + last_z = range.first.second; + } + } + + // add ranges for extruder changes from custom_tool_changes + for (size_t i = 0; i < custom_tool_changes.size(); i++) { + const DynamicPrintConfig* cfg = &custom_tool_changes[i].second; + coordf_t cur_Z = custom_tool_changes[i].first; + coordf_t next_Z = i == custom_tool_changes.size()-1 ? DBL_MAX : custom_tool_changes[i+1].first; + if (cur_Z > last_z + EPSILON) { + if (i==0) + m_ranges.emplace_back(t_layer_height_range(last_z, cur_Z), nullptr); + m_ranges.emplace_back(t_layer_height_range(cur_Z, next_Z), cfg); + } + else if (next_Z > last_z + EPSILON) + m_ranges.emplace_back(t_layer_height_range(last_z, next_Z), cfg); + } + + if (m_ranges.empty()) + m_ranges.emplace_back(t_layer_height_range(0, DBL_MAX), nullptr); + else if (m_ranges.back().second == nullptr) + m_ranges.back().first.second = DBL_MAX; + else if (m_ranges.back().first.second != DBL_MAX) + m_ranges.emplace_back(t_layer_height_range(m_ranges.back().first.second, DBL_MAX), nullptr); + } const DynamicPrintConfig* config(const t_layer_height_range &range) const { auto it = std::lower_bound(m_ranges.begin(), m_ranges.end(), std::make_pair< t_layer_height_range, const DynamicPrintConfig*>(t_layer_height_range(range.first - EPSILON, range.second - EPSILON), nullptr)); - assert(it != m_ranges.end()); - assert(it == m_ranges.end() || std::abs(it->first.first - range.first ) < EPSILON); - assert(it == m_ranges.end() || std::abs(it->first.second - range.second) < EPSILON); + // #ys_FIXME_COLOR + // assert(it != m_ranges.end()); + // assert(it == m_ranges.end() || std::abs(it->first.first - range.first ) < EPSILON); + // assert(it == m_ranges.end() || std::abs(it->first.second - range.second) < EPSILON); + if (it == m_ranges.end() || + std::abs(it->first.first - range.first) > EPSILON || + std::abs(it->first.second - range.second) > EPSILON ) + return nullptr; // desired range doesn't found return (it == m_ranges.end()) ? nullptr : it->second; } std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>>::const_iterator begin() const { return m_ranges.cbegin(); } @@ -688,6 +736,13 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ // The object list did not change. for (const ModelObject *model_object : m_model.objects) model_object_status.emplace(model_object->id(), ModelObjectStatus::Old); + + // But if custom gcode per layer height was changed + if (m_model.custom_gcode_per_height != model.custom_gcode_per_height) { + // we should stop background processing + update_apply_status(this->invalidate_step(psGCodeExport)); + m_model.custom_gcode_per_height = model.custom_gcode_per_height; + } } else if (model_object_list_extended(m_model, model)) { // Add new objects. Their volumes and configs will be synchronized later. update_apply_status(this->invalidate_step(psGCodeExport)); @@ -779,6 +834,9 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ for (PrintObject *print_object : m_objects) print_object_status.emplace(PrintObjectStatus(print_object)); + std::vector<std::pair<double, DynamicPrintConfig>> custom_tool_changes = + m_model.get_custom_tool_changes(m_default_object_config.layer_height, num_extruders); + // 3) Synchronize ModelObjects & PrintObjects. for (size_t idx_model_object = 0; idx_model_object < model.objects.size(); ++ idx_model_object) { ModelObject &model_object = *m_model.objects[idx_model_object]; @@ -786,7 +844,9 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ assert(it_status != model_object_status.end()); assert(it_status->status != ModelObjectStatus::Deleted); const ModelObject& model_object_new = *model.objects[idx_model_object]; - const_cast<ModelObjectStatus&>(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges); + // ys_FIXME_COLOR + // const_cast<ModelObjectStatus&>(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges); + const_cast<ModelObjectStatus&>(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges, custom_tool_changes); if (it_status->status == ModelObjectStatus::New) // PrintObject instances will be added in the next loop. continue; @@ -954,6 +1014,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ PrintRegionConfig this_region_config; bool this_region_config_set = false; for (PrintObject *print_object : m_objects) { + if(m_force_update_print_regions && !custom_tool_changes.empty()) + goto print_object_end; const LayerRanges *layer_ranges; { auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id())); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 6d94a515f..d5810f057 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -361,6 +361,9 @@ public: // Accessed by SupportMaterial const PrintRegion* get_region(size_t idx) const { return m_regions[idx]; } + // force update of PrintRegions, when custom_tool_change is not empty and (Re)Slicing is started + void set_force_update_print_regions(bool force_update_print_regions) { m_force_update_print_regions = force_update_print_regions; } + protected: // methods for handling regions PrintRegion* get_region(size_t idx) { return m_regions[idx]; } @@ -403,6 +406,9 @@ private: // Estimated print time, filament consumed. PrintStatistics m_print_statistics; + // flag used + bool m_force_update_print_regions = false; + // To allow GCode to set the Print's GCodeExport step status. friend class GCode; // Allow PrintObject to access m_mutex and m_cancel_callback. diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 17b76e629..253488cd4 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -105,6 +105,8 @@ set(SLIC3R_GUI_SOURCES GUI/Camera.hpp GUI/wxExtensions.cpp GUI/wxExtensions.hpp + GUI/ExtruderSequenceDialog.cpp + GUI/ExtruderSequenceDialog.hpp GUI/WipeTowerDialog.cpp GUI/WipeTowerDialog.hpp GUI/RammingChart.cpp diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index cf5edd55f..e389c1e86 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -128,6 +128,11 @@ public: // This "finished" flag does not account for the final export of the output file (.gcode or zipped PNGs), // and it does not account for the OctoPrint scheduling. bool finished() const { return m_print->finished(); } + + void set_force_update_print_regions(bool force_update_print_regions) { + if (m_fff_print) + m_fff_print->set_force_update_print_regions(force_update_print_regions); + } private: void thread_proc(); diff --git a/src/slic3r/GUI/ExtruderSequenceDialog.cpp b/src/slic3r/GUI/ExtruderSequenceDialog.cpp new file mode 100644 index 000000000..ef60a041b --- /dev/null +++ b/src/slic3r/GUI/ExtruderSequenceDialog.cpp @@ -0,0 +1,197 @@ +#include "ExtruderSequenceDialog.hpp" + +#include <wx/wx.h> +#include <wx/stattext.h> +#include <wx/dialog.h> +#include <wx/sizer.h> +#include <wx/bmpcbox.h> + +#include <vector> +#include <set> +#include <functional> + +#include "GUI.hpp" +#include "GUI_App.hpp" +#include "I18N.hpp" +#include "OptionsGroup.hpp" + + +namespace Slic3r { +namespace GUI { + +ExtruderSequenceDialog::ExtruderSequenceDialog(const DoubleSlider::ExtrudersSequence& sequence) + : DPIDialog(NULL, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _(L("Set extruder sequence")), + wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER), + m_sequence(sequence) +{ + SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + SetDoubleBuffered(true); + SetFont(wxGetApp().normal_font()); + + auto main_sizer = new wxBoxSizer(wxVERTICAL); + const int em = wxGetApp().em_unit(); + + m_bmp_del = ScalableBitmap(this, "remove_copies"); + m_bmp_add = ScalableBitmap(this, "add_copies"); + + auto option_sizer = new wxBoxSizer(wxVERTICAL); + + auto intervals_box = new wxStaticBox(this, wxID_ANY, _(L("Set extruder change for every"))+ " : "); + auto intervals_box_sizer = new wxStaticBoxSizer(intervals_box, wxVERTICAL); + + m_intervals_grid_sizer = new wxFlexGridSizer(3, 5, em); + + auto editor_sz = wxSize(4*em, wxDefaultCoord); + + wxRadioButton* rb_by_layers = new wxRadioButton(this, wxID_ANY, ""); + rb_by_layers->Bind(wxEVT_RADIOBUTTON, [this](wxEvent&) { m_sequence.is_mm_intervals = false; }); + + wxStaticText* st_by_layers = new wxStaticText(this, wxID_ANY, _(L("layers"))); + m_interval_by_layers = new wxTextCtrl(this, wxID_ANY, + wxString::Format("%d", m_sequence.interval_by_layers), + wxDefaultPosition, editor_sz); + m_interval_by_layers->Bind(wxEVT_TEXT, [this, rb_by_layers](wxEvent&) + { + wxString str = m_interval_by_layers->GetValue(); + if (str.IsEmpty()) { + m_interval_by_layers->SetValue(wxString::Format("%d", m_sequence.interval_by_layers)); + return; + } + + m_sequence.interval_by_layers = wxAtoi(str); + + m_sequence.is_mm_intervals = false; + rb_by_layers->SetValue(true); + }); + + m_intervals_grid_sizer->Add(rb_by_layers, 0, wxALIGN_CENTER_VERTICAL); + m_intervals_grid_sizer->Add(m_interval_by_layers,0, wxALIGN_CENTER_VERTICAL); + m_intervals_grid_sizer->Add(st_by_layers,0, wxALIGN_CENTER_VERTICAL); + + wxRadioButton* rb_by_mm = new wxRadioButton(this, wxID_ANY, ""); + rb_by_mm->Bind(wxEVT_RADIOBUTTON, [this](wxEvent&) { m_sequence.is_mm_intervals = true; }); + rb_by_mm->SetValue(m_sequence.is_mm_intervals); + + wxStaticText* st_by_mm = new wxStaticText(this, wxID_ANY, _(L("mm"))); + m_interval_by_mm = new wxTextCtrl(this, wxID_ANY, + double_to_string(sequence.interval_by_mm), + wxDefaultPosition, editor_sz); + m_interval_by_mm->Bind(wxEVT_TEXT, [this, rb_by_mm](wxEvent&) + { + wxString str = m_interval_by_mm->GetValue(); + if (str.IsEmpty()) { + m_interval_by_mm->SetValue(wxString::Format("%d", m_sequence.interval_by_mm)); + return; + } + + str.Replace(",", ".", false); + double val; + if (str == "." || !str.ToCDouble(&val)) + val = 0.0; + + m_sequence.interval_by_mm = val; + + m_sequence.is_mm_intervals = true; + rb_by_mm->SetValue(true); + }); + + m_intervals_grid_sizer->Add(rb_by_mm, 0, wxALIGN_CENTER_VERTICAL); + m_intervals_grid_sizer->Add(m_interval_by_mm,0, wxALIGN_CENTER_VERTICAL); + m_intervals_grid_sizer->Add(st_by_mm,0, wxALIGN_CENTER_VERTICAL); + + intervals_box_sizer->Add(m_intervals_grid_sizer, 0, wxLEFT, em); + option_sizer->Add(intervals_box_sizer, 0, wxEXPAND); + + + auto extruders_box = new wxStaticBox(this, wxID_ANY, _(L("Set extruder(tool) sequence"))+ " : "); + auto extruders_box_sizer = new wxStaticBoxSizer(extruders_box, wxVERTICAL); + + m_extruders_grid_sizer = new wxFlexGridSizer(3, 5, em); + + apply_extruder_sequence(); + + extruders_box_sizer->Add(m_extruders_grid_sizer, 0, wxALL, em); + option_sizer->Add(extruders_box_sizer, 0, wxEXPAND | wxTOP, em); + + main_sizer->Add(option_sizer, 0, wxEXPAND | wxALL, em); + + wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL); + main_sizer->Add(buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, em); + + SetSizer(main_sizer); + main_sizer->SetSizeHints(this); +} + +void ExtruderSequenceDialog::apply_extruder_sequence() +{ + Freeze(); + m_extruders_grid_sizer->Clear(true); + + for (size_t extruder=0; extruder < m_sequence.extruders.size(); ++extruder) + { + wxBitmapComboBox* extruder_selector = nullptr; + apply_extruder_selector(&extruder_selector, this, "", wxDefaultPosition, wxSize(12*wxGetApp().em_unit(), -1)); + extruder_selector->SetSelection(m_sequence.extruders[extruder]); + + extruder_selector->Bind(wxEVT_COMBOBOX, [this, extruder_selector, extruder](wxCommandEvent& evt) + { + m_sequence.extruders[extruder] = extruder_selector->GetSelection(); + evt.StopPropagation(); + }); + + auto del_btn = new ScalableButton(this, wxID_ANY, m_bmp_del); + del_btn->SetToolTip(_(L("Remove extruder from sequence"))); + if (m_sequence.extruders.size()==1) + del_btn->Disable(); + + del_btn->Bind(wxEVT_BUTTON, [this, extruder](wxEvent&) { + m_sequence.delete_extruder(extruder); + apply_extruder_sequence(); + }); + + auto add_btn = new ScalableButton(this, wxID_ANY, m_bmp_add); + add_btn->SetToolTip(_(L("Add extruder to sequence"))); + + add_btn->Bind(wxEVT_BUTTON, [this, extruder](wxEvent&) { + m_sequence.add_extruder(extruder); + apply_extruder_sequence(); + }); + + m_extruders_grid_sizer->Add(extruder_selector); + m_extruders_grid_sizer->Add(del_btn); + m_extruders_grid_sizer->Add(add_btn); + } + + Fit(); + Refresh(); + + Thaw(); +} + +void ExtruderSequenceDialog::on_dpi_changed(const wxRect& suggested_rect) +{ + SetFont(wxGetApp().normal_font()); + + m_bmp_add.msw_rescale(); + m_bmp_del.msw_rescale(); + + const int em = em_unit(); + + m_intervals_grid_sizer->SetHGap(em); + m_intervals_grid_sizer->SetVGap(em); + m_extruders_grid_sizer->SetHGap(em); + m_extruders_grid_sizer->SetVGap(em); + + msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL }); + + // wxSize size = get_size(); + // SetMinSize(size); + + Fit(); + Refresh(); +} + +} +} + + diff --git a/src/slic3r/GUI/ExtruderSequenceDialog.hpp b/src/slic3r/GUI/ExtruderSequenceDialog.hpp new file mode 100644 index 000000000..3efd9e3a2 --- /dev/null +++ b/src/slic3r/GUI/ExtruderSequenceDialog.hpp @@ -0,0 +1,45 @@ +#ifndef slic3r_GUI_ExtruderSequenceDialog_hpp_ +#define slic3r_GUI_ExtruderSequenceDialog_hpp_ + +#include "GUI_Utils.hpp" +#include "wxExtensions.hpp" + +class wxTextCtrl; +class wxFlexGridSizer; + +namespace Slic3r { +namespace GUI { + +// ---------------------------------------------------------------------------- +// ExtruderSequenceDialog: a node inside ObjectDataViewModel +// ---------------------------------------------------------------------------- + +class ExtruderSequenceDialog: public DPIDialog +{ + ScalableBitmap m_bmp_del; + ScalableBitmap m_bmp_add; + DoubleSlider::ExtrudersSequence m_sequence; + + wxTextCtrl* m_interval_by_layers {nullptr}; + wxTextCtrl* m_interval_by_mm {nullptr}; + + wxFlexGridSizer* m_intervals_grid_sizer {nullptr}; + wxFlexGridSizer* m_extruders_grid_sizer {nullptr}; +public: + ExtruderSequenceDialog(const DoubleSlider::ExtrudersSequence& sequence); + + ~ExtruderSequenceDialog() {} + + DoubleSlider::ExtrudersSequence GetValue() { return m_sequence; } + +protected: + void apply_extruder_sequence(); + void on_dpi_changed(const wxRect& suggested_rect) override; + +}; + +} +} + + +#endif // slic3r_GUI_ExtruderSequenceDialog_hpp_ diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 268b4dc30..7e782dee3 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -836,9 +836,11 @@ GLCanvas3D::LegendTexture::LegendTexture() void GLCanvas3D::LegendTexture::fill_color_print_legend_values(const GCodePreviewData& preview_data, const GLCanvas3D& canvas, std::vector<std::pair<double, double>>& cp_legend_values) { - if (preview_data.extrusion.view_type == GCodePreviewData::Extrusion::ColorPrint && - wxGetApp().extruders_edited_cnt() == 1) // show color change legend only for single-material presets + if (preview_data.extrusion.view_type == GCodePreviewData::Extrusion::ColorPrint /*&& + wxGetApp().extruders_edited_cnt() == 1*/) // show color change legend only for single-material presets { + /* + // #ys_FIXME_COLOR auto& config = wxGetApp().preset_bundle->project_config; const std::vector<double>& color_print_values = config.option<ConfigOptionFloats>("colorprint_heights")->values; @@ -860,6 +862,27 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_values(const GCodePrevie cp_legend_values.push_back(std::pair<double, double>(previous_z, current_z)); } } + */ + std::vector<Model::CustomGCode> custom_gcode_per_height = wxGetApp().plater()->model().custom_gcode_per_height; + + if (!custom_gcode_per_height.empty()) { + std::vector<double> print_zs = canvas.get_current_print_zs(true); + for (auto custom_code : custom_gcode_per_height) + { + auto lower_b = std::lower_bound(print_zs.begin(), print_zs.end(), custom_code.height - DoubleSlider::epsilon()); + + if (lower_b == print_zs.end()) + continue; + + double current_z = *lower_b; + double previous_z = lower_b == print_zs.begin() ? 0.0 : *(--lower_b); + + // to avoid duplicate values, check adding values + if (cp_legend_values.empty() || + !(cp_legend_values.back().first == previous_z && cp_legend_values.back().second == current_z) ) + cp_legend_values.push_back(std::pair<double, double>(previous_z, current_z)); + } + } } } @@ -2071,20 +2094,6 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, contained_min_one && !m_model->objects.empty() && state != ModelInstance::PVS_Partly_Outside)); - -// #ys_FIXME_delete_after_testing -// bool contained = m_volumes.check_outside_state(m_config, &state); -// if (!contained) -// { -// _set_warning_texture(WarningTexture::ObjectOutside, true); -// post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, state == ModelInstance::PVS_Fully_Outside)); -// } -// else -// { -// m_volumes.reset_outside_state(); -// _set_warning_texture(WarningTexture::ObjectOutside, false); -// post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, !m_model->objects.empty())); -// } } else { @@ -2238,7 +2247,9 @@ void GLCanvas3D::load_sla_preview() } } -void GLCanvas3D::load_preview(const std::vector<std::string>& str_tool_colors, const std::vector<double>& color_print_values) +// #ys_FIXME_COLOR +// void GLCanvas3D::load_preview(const std::vector<std::string>& str_tool_colors, const std::vector<double>& color_print_values) +void GLCanvas3D::load_preview(const std::vector<std::string>& str_tool_colors, const std::vector<Model::CustomGCode>& color_print_values) { const Print *print = this->fff_print(); if (print == nullptr) @@ -4666,7 +4677,9 @@ void GLCanvas3D::_load_print_toolpaths() volume->indexed_vertex_array.finalize_geometry(m_initialized); } -void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors, const std::vector<double>& color_print_values) +// #ys_FIXME_COLOR +// void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors, const std::vector<double>& color_print_values) +void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors, const std::vector<Model::CustomGCode>& color_print_values) { std::vector<float> tool_colors = _parse_colors(str_tool_colors); @@ -4678,11 +4691,15 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c bool has_infill; bool has_support; const std::vector<float>* tool_colors; - const std::vector<double>* color_print_values; + // #ys_FIXME_COLOR + // const std::vector<double>* color_print_values; + bool is_single_material_print; + const std::vector<Model::CustomGCode>* color_print_values; static const float* color_perimeters() { static float color[4] = { 1.0f, 1.0f, 0.0f, 1.f }; return color; } // yellow static const float* color_infill() { static float color[4] = { 1.0f, 0.5f, 0.5f, 1.f }; return color; } // redish static const float* color_support() { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish + static const float* color_pause_or_custom_code() { static float color[4] = { 0.5f, 0.5f, 0.5f, 1.f }; return color; } // gray // For cloring by a tool, return a parsed color. bool color_by_tool() const { return tool_colors != nullptr; } @@ -4692,9 +4709,26 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c // For coloring by a color_print(M600), return a parsed color. bool color_by_color_print() const { return color_print_values!=nullptr; } const size_t color_print_color_idx_by_layer_idx(const size_t layer_idx) const { - auto it = std::lower_bound(color_print_values->begin(), color_print_values->end(), layers[layer_idx]->print_z + EPSILON); + // #ys_FIXME_COLOR + // auto it = std::lower_bound(color_print_values->begin(), color_print_values->end(), layers[layer_idx]->print_z + EPSILON); + const Model::CustomGCode value(layers[layer_idx]->print_z + EPSILON, "", 0); + auto it = std::lower_bound(color_print_values->begin(), color_print_values->end(), value); return (it - color_print_values->begin()) % number_tools(); } + + const bool pause_or_custom_code_layer(const size_t layer_idx) const + { + const coordf_t print_z = layers[layer_idx]->print_z; + auto it = std::find_if(color_print_values->begin(), color_print_values->end(), + [print_z](const Model::CustomGCode& code) + { return fabs(code.height - print_z) < EPSILON; }); + if (it == color_print_values->end()) + return false; + + const std::string& code = (*it).gcode; + return code == "M601" || (code != "M600" && code != "tool_change"); + } + } ctxt; ctxt.has_perimeters = print_object.is_step_done(posPerimeters); @@ -4702,6 +4736,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c ctxt.has_support = print_object.is_step_done(posSupportMaterial); ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors; ctxt.color_print_values = color_print_values.empty() ? nullptr : &color_print_values; + ctxt.is_single_material_print = this->fff_print()->extruders().size()==1; ctxt.shifted_copies = &print_object.copies(); @@ -4725,6 +4760,8 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c // Maximum size of an allocation block: 32MB / sizeof(float) BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - start" << m_volumes.log_memory_info() << log_memory_info(); + const bool is_selected_separate_extruder = m_selected_extruder > 0 && ctxt.color_by_color_print(); + //FIXME Improve the heuristics for a grain size. size_t grain_size = std::max(ctxt.layers.size() / 16, size_t(1)); tbb::spin_mutex new_volume_mutex; @@ -4742,18 +4779,69 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c const size_t volumes_cnt_initial = m_volumes.volumes.size(); tbb::parallel_for( tbb::blocked_range<size_t>(0, ctxt.layers.size(), grain_size), - [&ctxt, &new_volume](const tbb::blocked_range<size_t>& range) { + [&ctxt, &new_volume, is_selected_separate_extruder, this](const tbb::blocked_range<size_t>& range) { GLVolumePtrs vols; std::vector<size_t> color_print_layer_to_glvolume; auto volume = [&ctxt, &vols, &color_print_layer_to_glvolume, &range](size_t layer_idx, int extruder, int feature) -> GLVolume& { - return *vols[ctxt.color_by_color_print() ? + if (ctxt.color_by_color_print() && !ctxt.is_single_material_print) + { + const coordf_t print_z = ctxt.layers[layer_idx]->print_z; + const std::vector<Model::CustomGCode>* cp_values = ctxt.color_print_values; + + // pause print or custom Gcode + auto it = std::find_if(cp_values->begin(), cp_values->end(), + [print_z](const Model::CustomGCode& code) + { return fabs(code.height - print_z) < EPSILON; }); + if (it != cp_values->end()) + { + const std::string& code = (*it).gcode; + if (code == "M601" || (code != "M600" && code != "tool_change")) + return *vols[ctxt.number_tools()];//*vols.back(); + // change tool (extruder) + if (code == "tool_change") + return *vols[std::min<int>(ctxt.number_tools() - 1, std::max<int>(it->extruder - 1, 0))]; + if (code == "M600" && it->extruder == extruder) { + int shift = 1; + while (it != cp_values->begin()) { + --it; + if (it->gcode == "M600") + shift++; + } + return *vols[ctxt.number_tools()+shift]; + } + } + + const Model::CustomGCode value(print_z + EPSILON, "", 0); + it = std::lower_bound(cp_values->begin(), cp_values->end(), value); + while (it != cp_values->begin()) + { + --it; + const std::string& code = (*it).gcode; + if (code == "M600" && it->extruder == extruder) { + auto it_n = it; + int shift = 1; + while (it_n != cp_values->begin()) { + --it_n; + if (it_n->gcode == "M600") + shift++; + } + return *vols[ctxt.number_tools() + shift]; + } + if (code == "tool_change") + return *vols[std::min<int>(ctxt.number_tools() - 1, std::max<int>((*it).extruder - 1, 0))]; + } + + return *vols[std::min<int>(ctxt.number_tools() - 1, std::max<int>(extruder - 1, 0))]; + } + + return *vols[ctxt.color_by_color_print() && ctxt.is_single_material_print ? color_print_layer_to_glvolume[layer_idx - range.begin()] : ctxt.color_by_tool() ? std::min<int>(ctxt.number_tools() - 1, std::max<int>(extruder - 1, 0)) : feature ]; }; - if (ctxt.color_by_color_print()) { + if (ctxt.color_by_color_print() && ctxt.is_single_material_print) { // Create a map from the layer index to a GLVolume, which is initialized with the correct layer span color. std::vector<int> color_print_tool_to_glvolume(ctxt.number_tools(), -1); color_print_layer_to_glvolume.reserve(range.end() - range.begin()); @@ -4767,6 +4855,25 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c color_print_layer_to_glvolume.emplace_back(color_print_tool_to_glvolume[idx_tool]); } } + else if (ctxt.color_by_color_print() && !ctxt.is_single_material_print) { + for (size_t i = 0; i < ctxt.number_tools(); ++i) + vols.emplace_back(new_volume(ctxt.color_tool(i))); + vols.emplace_back(new_volume(ctxt.color_pause_or_custom_code())); + + for ( auto it = ctxt.color_print_values->begin(); it < ctxt.color_print_values->end(); it++) { + if (it->gcode == "M600" && it->extruder != 0) + { + int cp_id = it - ctxt.color_print_values->begin() + 1; + float koef = fabs(1- cp_id * 0.1); + float color_f[4]; + memcpy(color_f, ctxt.color_tool(it->extruder - 1), sizeof(float) * 4); + for (int i=0; i<3; i++) + color_f[i] = clamp(0.0f, 1.0f, koef * color_f[i]); + + vols.emplace_back(new_volume(color_f)); + } + } + } else if (ctxt.color_by_tool()) { for (size_t i = 0; i < ctxt.number_tools(); ++i) vols.emplace_back(new_volume(ctxt.color_tool(i))); @@ -4778,6 +4885,26 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c vol->indexed_vertex_array.reserve(VERTEX_BUFFER_RESERVE_SIZE / 6); for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { const Layer *layer = ctxt.layers[idx_layer]; + + if (is_selected_separate_extruder) + { + bool at_least_one_has_correct_extruder = false; + for (const LayerRegion* layerm : layer->regions()) + { + if (layerm->slices.surfaces.empty()) + continue; + const PrintRegionConfig& cfg = layerm->region()->config(); + if (cfg.perimeter_extruder.value == m_selected_extruder || + cfg.infill_extruder.value == m_selected_extruder || + cfg.solid_infill_extruder.value == m_selected_extruder ) { + at_least_one_has_correct_extruder = true; + break; + } + } + if (!at_least_one_has_correct_extruder) + continue; + } + for (GLVolume *vol : vols) if (vol->print_zs.empty() || vol->print_zs.back() != layer->print_z) { vol->print_zs.push_back(layer->print_z); @@ -4786,6 +4913,14 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c } for (const Point © : *ctxt.shifted_copies) { for (const LayerRegion *layerm : layer->regions()) { + if (is_selected_separate_extruder) + { + const PrintRegionConfig& cfg = layerm->region()->config(); + if (cfg.perimeter_extruder.value != m_selected_extruder || + cfg.infill_extruder.value != m_selected_extruder || + cfg.solid_infill_extruder.value != m_selected_extruder) + continue; + } if (ctxt.has_perimeters) _3DScene::extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy, volume(idx_layer, layerm->region()->config().perimeter_extruder.value, 0)); @@ -5134,10 +5269,13 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat BOOST_LOG_TRIVIAL(debug) << "Loading G-code extrusion paths - populate volumes" << m_volumes.log_memory_info() << log_memory_info(); // populates volumes + const bool is_selected_separate_extruder = m_selected_extruder > 0 && preview_data.extrusion.view_type == GCodePreviewData::Extrusion::ColorPrint; for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers) { for (const GCodePreviewData::Extrusion::Path& path : layer.paths) { + if (is_selected_separate_extruder && path.extruder_id != m_selected_extruder - 1) + continue; std::vector<std::pair<float, GLVolume*>> &filters = roles_filters[size_t(path.extrusion_role)]; auto key = std::make_pair<float, GLVolume*>(Helper::path_filter(preview_data.extrusion.view_type, path), nullptr); auto it_filter = std::lower_bound(filters.begin(), filters.end(), key); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 2c2676ae7..9a0582fee 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -436,6 +436,7 @@ private: #endif // ENABLE_RENDER_STATISTICS int m_imgui_undo_redo_hovered_pos{ -1 }; + int m_selected_extruder; public: GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar); @@ -537,7 +538,9 @@ public: void load_gcode_preview(const GCodePreviewData& preview_data, const std::vector<std::string>& str_tool_colors); void load_sla_preview(); - void load_preview(const std::vector<std::string>& str_tool_colors, const std::vector<double>& color_print_values); + // #ys_FIXME_COLOR + // void load_preview(const std::vector<std::string>& str_tool_colors, const std::vector<double>& color_print_values); + void load_preview(const std::vector<std::string>& str_tool_colors, const std::vector<Model::CustomGCode>& color_print_values); void bind_event_handlers(); void unbind_event_handlers(); @@ -578,6 +581,7 @@ public: int get_move_volume_id() const { return m_mouse.drag.move_volume_idx; } int get_first_hover_volume_idx() const { return m_hover_volume_idxs.empty() ? -1 : m_hover_volume_idxs.front(); } + void set_selected_extruder(int extruder) { m_selected_extruder = extruder;} class WipeTowerInfo { protected: @@ -688,7 +692,10 @@ private: // Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes, // one for perimeters, one for infill and one for supports. void _load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors, - const std::vector<double>& color_print_values); + const std::vector<Model::CustomGCode>& color_print_values); + // #ys_FIXME_COLOR + // void _load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors, + // const std::vector<double>& color_print_values); // Create 3D thick extrusion lines for wipe tower extrusions void _load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index a88980a8d..1cbfc2bdf 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -895,10 +895,12 @@ void ObjectList::extruder_editing() if (!item || !(m_objects_model->GetItemType(item) & (itVolume | itObject))) return; + // ! #ys Use ApplyExtruderSelector instead this code + /* std::vector<wxBitmap*> icons = get_extruder_color_icons(); if (icons.empty()) return; - + */ const int column_width = GetColumn(colExtruder)->GetWidth() + wxSystemSettings::GetMetric(wxSYS_VSCROLL_X) + 5; wxPoint pos = get_mouse_position_in_control(); @@ -906,6 +908,10 @@ void ObjectList::extruder_editing() pos.x = GetColumn(colName)->GetWidth() + GetColumn(colPrint)->GetWidth() + 5; pos.y -= GetTextExtent("m").y; + apply_extruder_selector(&m_extruder_editor, this, L("default"), pos, size); + + // ! #ys Use ApplyExtruderSelector instead this code + /* if (!m_extruder_editor) m_extruder_editor = new wxBitmapComboBox(this, wxID_ANY, wxEmptyString, pos, size, 0, nullptr, wxCB_READONLY); @@ -928,6 +934,7 @@ void ObjectList::extruder_editing() m_extruder_editor->Append(wxString::Format("%d", i), *bmp); ++i; } + */ m_extruder_editor->SetSelection(m_objects_model->GetExtruderNumber(item)); auto set_extruder = [this]() @@ -948,13 +955,6 @@ void ObjectList::extruder_editing() set_extruder(); evt.StopPropagation(); }); - /* - m_extruder_editor->Bind(wxEVT_KILL_FOCUS, [set_extruder](wxFocusEvent& evt) - { - set_extruder(); - evt.Skip(); - });*/ - } void ObjectList::copy() diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 4ecab8a0f..5e939cb71 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -243,11 +243,13 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : // Add Axes labels with icons static const char axes[] = { 'X', 'Y', 'Z' }; +// std::vector<wxString> axes_color = {"#990000", "#009900","#000099"}; for (size_t axis_idx = 0; axis_idx < sizeof(axes); axis_idx++) { const char label = axes[axis_idx]; wxStaticText* axis_name = new wxStaticText(m_parent, wxID_ANY, wxString(label)); set_font_and_background_style(axis_name, wxGetApp().bold_font()); +// axis_name->SetForegroundColour(wxColour(axes_color[axis_idx])); sizer = new wxBoxSizer(wxHORIZONTAL); // Under OSX we use font, smaller than default font, so diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index d89ac1bcb..81367081f 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -492,18 +492,33 @@ void Preview::show_hide_ui_elements(const std::string& what) m_choice_view_type->Show(visible); } -void Preview::reset_sliders() +void Preview::reset_sliders(bool reset_all) { m_enabled = false; // reset_double_slider(); - m_double_slider_sizer->Hide((size_t)0); + if (reset_all) + m_double_slider_sizer->Hide((size_t)0); + else + m_double_slider_sizer->GetItem(size_t(0))->GetSizer()->Hide(1); } void Preview::update_sliders(const std::vector<double>& layers_z, bool keep_z_range) { m_enabled = true; + // update extruder selector + if (wxGetApp().extruders_edited_cnt() != m_extruder_selector->GetCount()-1) + { + m_selected_extruder = m_extruder_selector->GetSelection(); + update_extruder_selector(); + if (m_selected_extruder >= m_extruder_selector->GetCount()) + m_selected_extruder = 0; + m_extruder_selector->SetSelection(m_selected_extruder); + } + update_double_slider(layers_z, keep_z_range); m_double_slider_sizer->Show((size_t)0); + if (m_slider->GetManipulationState() == DoubleSlider::msSingleExtruder) + m_double_slider_sizer->GetItem(size_t(0))->GetSizer()->Hide((size_t)0); Layout(); } @@ -520,6 +535,9 @@ void Preview::on_choice_view_type(wxCommandEvent& evt) if ((0 <= selection) && (selection < (int)GCodePreviewData::Extrusion::Num_View_Types)) m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)selection; + if (m_gcode_preview_data->extrusion.view_type != GCodePreviewData::Extrusion::ColorPrint) + m_extruder_selector->SetSelection(0); + reload_print(); } @@ -560,16 +578,26 @@ void Preview::on_checkbox_legend(wxCommandEvent& evt) m_canvas_widget->Refresh(); } -void Preview::update_view_type() +void Preview::update_view_type(bool slice_completed) { const DynamicPrintConfig& config = wxGetApp().preset_bundle->project_config; + /* + // #ys_FIXME_COLOR const wxString& choice = !config.option<ConfigOptionFloats>("colorprint_heights")->values.empty() && wxGetApp().extruders_edited_cnt()==1 ? _(L("Color Print")) : config.option<ConfigOptionFloats>("wiping_volumes_matrix")->values.size() > 1 ? _(L("Tool")) : _(L("Feature type")); + */ + + const wxString& choice = !wxGetApp().plater()->model().custom_gcode_per_height.empty() && + (wxGetApp().extruders_edited_cnt()==1 || !slice_completed) ? + _(L("Color Print")) : + config.option<ConfigOptionFloats>("wiping_volumes_matrix")->values.size() > 1 ? + _(L("Tool")) : + _(L("Feature type")); int type = m_choice_view_type->FindString(choice); if (m_choice_view_type->GetSelection() != type) { @@ -578,12 +606,47 @@ void Preview::update_view_type() m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)type; m_preferred_color_mode = "feature"; } + + if (type != GCodePreviewData::Extrusion::EViewType::ColorPrint) + m_extruder_selector->SetSelection(0); +} + +void Preview::update_extruder_selector() +{ + apply_extruder_selector(&m_extruder_selector, this, L("Whole print"), wxDefaultPosition, wxDefaultSize, true); } void Preview::create_double_slider() { m_slider = new DoubleSlider(this, wxID_ANY, 0, 0, 0, 100); - m_double_slider_sizer->Add(m_slider, 0, wxEXPAND, 0); + // #ys_FIXME_COLOR + // m_double_slider_sizer->Add(m_slider, 0, wxEXPAND, 0); + + update_extruder_selector(); + m_extruder_selector->SetSelection(0); + m_extruder_selector->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& evt) + { + m_selected_extruder = m_extruder_selector->GetSelection(); + m_slider->SetExtruderID(m_selected_extruder); + + int type = m_choice_view_type->FindString(_(L("Color Print"))); + + if (m_choice_view_type->GetSelection() != type) { + m_choice_view_type->SetSelection(type); + if (0 <= type && type < (int)GCodePreviewData::Extrusion::Num_View_Types) + m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)type; + m_preferred_color_mode = "feature"; + } + reload_print(); + + evt.StopPropagation(); + }); + + auto sizer = new wxBoxSizer(wxVERTICAL); + sizer->Add(m_extruder_selector, 0, wxEXPAND, 0); + sizer->Add(m_slider, 1, wxEXPAND, 0); + + m_double_slider_sizer->Add(sizer, 0, wxEXPAND, 0); // sizer, m_canvas_widget m_canvas_widget->Bind(wxEVT_KEY_DOWN, &Preview::update_double_slider_from_canvas, this); @@ -592,10 +655,14 @@ void Preview::create_double_slider() Bind(wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) { - wxGetApp().preset_bundle->project_config.option<ConfigOptionFloats>("colorprint_heights")->values = m_slider->GetTicksValues(); + // #ys_FIXME_COLOR + // wxGetApp().preset_bundle->project_config.option<ConfigOptionFloats>("colorprint_heights")->values = m_slider->GetTicksValues(); + + Model& model = wxGetApp().plater()->model(); + model.custom_gcode_per_height = m_slider->GetTicksValues_(); m_schedule_background_process(); - update_view_type(); + update_view_type(false); reload_print(); }); @@ -628,6 +695,24 @@ static int find_close_layer_idx(const std::vector<double>& zs, double &z, double return -1; } +void Preview::check_slider_values(std::vector<Model::CustomGCode>& ticks_from_model, + const std::vector<double>& layers_z) +{ + // All ticks that would end up outside the slider range should be erased. + // TODO: this should be placed into more appropriate part of code, + // this function is e.g. not called when the last object is deleted + unsigned int old_size = ticks_from_model.size(); + ticks_from_model.erase(std::remove_if(ticks_from_model.begin(), ticks_from_model.end(), + [layers_z](Model::CustomGCode val) + { + auto it = std::lower_bound(layers_z.begin(), layers_z.end(), val.height - DoubleSlider::epsilon()); + return it == layers_z.end(); + }), + ticks_from_model.end()); + if (ticks_from_model.size() != old_size) + m_schedule_background_process(); +} + void Preview::update_double_slider(const std::vector<double>& layers_z, bool keep_z_range) { // Save the initial slider span. @@ -643,8 +728,15 @@ void Preview::update_double_slider(const std::vector<double>& layers_z, bool kee bool snap_to_min = force_sliders_full_range || m_slider->is_lower_at_min(); bool snap_to_max = force_sliders_full_range || m_slider->is_higher_at_max(); - std::vector<double> &ticks_from_config = (wxGetApp().preset_bundle->project_config.option<ConfigOptionFloats>("colorprint_heights"))->values; - check_slider_values(ticks_from_config, layers_z); + // #ys_FIXME_COLOR + // std::vector<double> &ticks_from_config = (wxGetApp().preset_bundle->project_config.option<ConfigOptionFloats>("colorprint_heights"))->values; + // check_slider_values(ticks_from_config, layers_z); + std::vector<Model::CustomGCode> tmp_ticks_from_model; + if (m_selected_extruder != 0) + tmp_ticks_from_model = wxGetApp().plater()->model().custom_gcode_per_height; + std::vector<Model::CustomGCode> &ticks_from_model = m_selected_extruder != 0 ? tmp_ticks_from_model : + wxGetApp().plater()->model().custom_gcode_per_height; + check_slider_values(ticks_from_model, layers_z); m_slider->SetSliderValues(layers_z); assert(m_slider->GetMinValue() == 0); @@ -666,17 +758,29 @@ void Preview::update_double_slider(const std::vector<double>& layers_z, bool kee } m_slider->SetSelectionSpan(idx_low, idx_high); - m_slider->SetTicksValues(ticks_from_config); + // #ys_FIXME_COLOR + // m_slider->SetTicksValues(ticks_from_config); + m_slider->SetTicksValues_(ticks_from_model); bool color_print_enable = (wxGetApp().plater()->printer_technology() == ptFFF); - if (color_print_enable) { - const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->printers.get_edited_preset().config; - if (cfg.opt<ConfigOptionFloats>("nozzle_diameter")->values.size() > 1) - color_print_enable = false; - } + // #ys_FIXME_COLOR + // if (color_print_enable) { + // const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->printers.get_edited_preset().config; + // if (cfg.opt<ConfigOptionFloats>("nozzle_diameter")->values.size() > 1) + // color_print_enable = false; + // } + // m_slider->EnableTickManipulation(color_print_enable); + m_slider->EnableTickManipulation(color_print_enable); -} + if (color_print_enable && wxGetApp().extruders_edited_cnt() > 1) { + //bool is_detected_full_print = //wxGetApp().plater()->fff_print().extruders().size() == 1; + m_slider->SetExtruderID(m_extruder_selector->GetSelection()); + } + else + m_slider->SetExtruderID(-1); +} +// #ys_FIXME_COLOR void Preview::check_slider_values(std::vector<double>& ticks_from_config, const std::vector<double> &layers_z) { @@ -753,7 +857,7 @@ void Preview::load_print_as_fff(bool keep_z_range) if (! has_layers) { - reset_sliders(); + reset_sliders(true); m_canvas->reset_legend_texture(); m_canvas_widget->Refresh(); return; @@ -776,16 +880,46 @@ void Preview::load_print_as_fff(bool keep_z_range) bool gcode_preview_data_valid = print->is_step_done(psGCodeExport) && ! m_gcode_preview_data->empty(); // Collect colors per extruder. std::vector<std::string> colors; - std::vector<double> color_print_values = {}; + // #ys_FIXME_COLOR + // std::vector<double> color_print_values = {}; + std::vector<Model::CustomGCode> color_print_values = {}; // set color print values, if it si selected "ColorPrint" view type if (m_gcode_preview_data->extrusion.view_type == GCodePreviewData::Extrusion::ColorPrint) { - colors = GCodePreviewData::ColorPrintColors(); + unsigned int number_extruders = (unsigned int)print->extruders().size(); + if (number_extruders == 1) // use GCodePreviewData::ColorPrintColors() just for Single-extruder printing + colors = GCodePreviewData::ColorPrintColors(); + else + { + const ConfigOptionStrings* extruders_opt = dynamic_cast<const ConfigOptionStrings*>(m_config->option("extruder_colour")); + const ConfigOptionStrings* filamemts_opt = dynamic_cast<const ConfigOptionStrings*>(m_config->option("filament_colour")); + unsigned int colors_count = std::max((unsigned int)extruders_opt->values.size(), (unsigned int)filamemts_opt->values.size()); + + unsigned char rgb[3]; + for (unsigned int i = 0; i < colors_count; ++i) + { + std::string color = m_config->opt_string("extruder_colour", i); + if (!PresetBundle::parse_color(color, rgb)) + { + color = m_config->opt_string("filament_colour", i); + if (!PresetBundle::parse_color(color, rgb)) + color = "#FFFFFF"; + } + + colors.emplace_back(color); + } + } if (! gcode_preview_data_valid) { - //FIXME accessing full_config() is pretty expensive. - // Only initialize color_print_values for the initial preview, not for the full preview where the color_print_values is extracted from the G-code. - const auto& config = wxGetApp().preset_bundle->project_config; - color_print_values = config.option<ConfigOptionFloats>("colorprint_heights")->values; + // #ys_FIXME_COLOR + // const auto& config = wxGetApp().preset_bundle->project_config; + // color_print_values = config.option<ConfigOptionFloats>("colorprint_heights")->values; + /* + const std::vector<Model::CustomGCode>& custom_codes = wxGetApp().plater()->model().custom_gcode_per_height; + color_print_values.reserve(custom_codes.size()); + for (const Model::CustomGCode& code : custom_codes) + color_print_values.push_back(code.height); + */ + color_print_values = wxGetApp().plater()->model().custom_gcode_per_height; } } else if (gcode_preview_data_valid || (m_gcode_preview_data->extrusion.view_type == GCodePreviewData::Extrusion::Tool) ) @@ -812,14 +946,15 @@ void Preview::load_print_as_fff(bool keep_z_range) if (IsShown()) { + m_canvas->set_selected_extruder(m_selected_extruder); if (gcode_preview_data_valid) { // Load the real G-code preview. m_canvas->load_gcode_preview(*m_gcode_preview_data, colors); m_loaded = true; } else { // disable color change information for multi-material presets - if (wxGetApp().extruders_edited_cnt() > 1) - color_print_values.clear(); + // if (wxGetApp().extruders_edited_cnt() > 1) // #ys_FIXME_COLOR + // color_print_values.clear(); // Load the initial preview based on slices, not the final G-code. m_canvas->load_preview(colors, color_print_values); @@ -829,7 +964,7 @@ void Preview::load_print_as_fff(bool keep_z_range) std::vector<double> zs = m_canvas->get_current_print_zs(true); if (zs.empty()) { // all layers filtered out - reset_sliders(); + reset_sliders(m_selected_extruder==0); m_canvas_widget->Refresh(); } else update_sliders(zs, keep_z_range); @@ -860,7 +995,7 @@ void Preview::load_print_as_sla() n_layers = (unsigned int)zs.size(); if (n_layers == 0) { - reset_sliders(); + reset_sliders(true); m_canvas_widget->Refresh(); } diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index 08d5991b4..7a11e334c 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -5,6 +5,7 @@ #include "libslic3r/Point.hpp" #include <string> +#include "libslic3r/Model.hpp" class wxNotebook; class wxGLCanvas; @@ -12,6 +13,7 @@ class wxBoxSizer; class wxStaticText; class wxChoice; class wxComboCtrl; +class wxBitmapComboBox; class wxCheckBox; class DoubleSlider; @@ -101,7 +103,9 @@ class Preview : public wxPanel bool m_loaded; bool m_enabled; - DoubleSlider* m_slider {nullptr}; + DoubleSlider* m_slider {nullptr}; + wxBitmapComboBox* m_extruder_selector {nullptr}; + int m_selected_extruder {0}; // 0 means "Whole print" public: Preview(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, @@ -128,7 +132,8 @@ public: void move_double_slider(wxKeyEvent& evt); void edit_double_slider(wxKeyEvent& evt); - void update_view_type(); + void update_view_type(bool slice_completed); + void update_extruder_selector(); bool is_loaded() const { return m_loaded; } @@ -140,7 +145,7 @@ private: void show_hide_ui_elements(const std::string& what); - void reset_sliders(); + void reset_sliders(bool reset_all); void update_sliders(const std::vector<double>& layers_z, bool keep_z_range = false); void on_size(wxSizeEvent& evt); @@ -154,9 +159,11 @@ private: // Create/Update/Reset double slider on 3dPreview void create_double_slider(); + void check_slider_values(std::vector<Model::CustomGCode> &ticks_from_model, + const std::vector<double> &layers_z); void update_double_slider(const std::vector<double>& layers_z, bool keep_z_range = false); void check_slider_values(std::vector<double> &ticks_from_config, - const std::vector<double> &layers_z); + const std::vector<double> &layers_z); // #ys_FIXME_COLOR void reset_double_slider(); // update DoubleSlider after keyDown in canvas void update_double_slider_from_canvas(wxKeyEvent& event); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 90529f88c..14fbdf72c 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2688,8 +2688,10 @@ void Plater::priv::reset() // The hiding of the slicing results, if shown, is not taken care by the background process, so we do it here this->sidebar->show_sliced_info_sizer(false); - auto& config = wxGetApp().preset_bundle->project_config; - config.option<ConfigOptionFloats>("colorprint_heights")->values.clear(); + // #ys_FIXME_COLOR + // auto& config = wxGetApp().preset_bundle->project_config; + // config.option<ConfigOptionFloats>("colorprint_heights")->values.clear(); + model.custom_gcode_per_height.clear(); } void Plater::priv::mirror(Axis axis) @@ -2920,6 +2922,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool this->update_print_volume_state(); // Apply new config to the possibly running background task. bool was_running = this->background_process.running(); + this->background_process.set_force_update_print_regions(force_validation); Print::ApplyStatus invalidated = this->background_process.apply(this->q->model(), wxGetApp().preset_bundle->full_config()); // Just redraw the 3D canvas without reloading the scene to consume the update of the layer height profile. @@ -4699,7 +4702,7 @@ void Plater::reslice() p->show_action_buttons(true); // update type of preview - p->preview->update_view_type(); + p->preview->update_view_type(true); } void Plater::reslice_SLA_supports(const ModelObject &object, bool postpone_error_messages) @@ -4865,6 +4868,7 @@ void Plater::on_config_change(const DynamicPrintConfig &config) filament_colors.push_back(filaments.find_preset(filament_preset, true)->config.opt_string("filament_colour", (unsigned)0)); p->config->option<ConfigOptionStrings>(opt_key)->values = filament_colors; + p->preview->update_extruder_selector(); p->sidebar->obj_list()->update_extruder_colors(); continue; } @@ -4891,6 +4895,7 @@ void Plater::on_config_change(const DynamicPrintConfig &config) else if(opt_key == "extruder_colour") { update_scheduled = true; p->preview->set_number_extruders(p->config->option<ConfigOptionStrings>(opt_key)->values.size()); + p->preview->update_extruder_selector(); p->sidebar->obj_list()->update_extruder_colors(); } else if(opt_key == "max_print_height") { update_scheduled = true; @@ -4942,6 +4947,7 @@ void Plater::force_filament_colors_update() if (update_scheduled) { update(); + p->preview->update_extruder_selector(); p->sidebar->obj_list()->update_extruder_colors(); } @@ -4978,6 +4984,9 @@ std::vector<std::string> Plater::get_extruder_colors_from_plater_config() const return extruder_colors; extruder_colors = (config->option<ConfigOptionStrings>("extruder_colour"))->values; + if (!wxGetApp().plater()) + return extruder_colors; + const std::vector<std::string>& filament_colours = (p->config->option<ConfigOptionStrings>("filament_colour"))->values; for (size_t i = 0; i < extruder_colors.size(); ++i) if (extruder_colors[i] == "" && i < filament_colours.size()) diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index eb47fd208..40f509ad7 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -22,6 +22,7 @@ #include "I18N.hpp" #include "GUI_Utils.hpp" #include "PresetBundle.hpp" +#include "ExtruderSequenceDialog.hpp" #include "../Utils/MacDarkMode.hpp" using Slic3r::GUI::from_u8; @@ -449,7 +450,7 @@ wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name_in, Slic3r::GUI::BitmapCache* m_bitmap_cache = nullptr; -/*static*/ std::vector<wxBitmap*> get_extruder_color_icons() +std::vector<wxBitmap*> get_extruder_color_icons(bool thin_icon/* = false*/) { // Create the bitmap with color bars. std::vector<wxBitmap*> bmps; @@ -465,16 +466,18 @@ Slic3r::GUI::BitmapCache* m_bitmap_cache = nullptr; * and scale them in respect to em_unit value */ const double em = Slic3r::GUI::wxGetApp().em_unit(); - const int icon_width = lround(3.2 * em); + const int icon_width = lround((thin_icon ? 1 : 3.2) * em); const int icon_height = lround(1.6 * em); for (const std::string& color : colors) { - wxBitmap* bitmap = m_bitmap_cache->find(color); + std::string bitmap_key = color + "-h" + std::to_string(icon_height) + "-w" + std::to_string(icon_width); + + wxBitmap* bitmap = m_bitmap_cache->find(bitmap_key); if (bitmap == nullptr) { // Paint the color icon. Slic3r::PresetBundle::parse_color(color, rgb); - bitmap = m_bitmap_cache->insert(color, m_bitmap_cache->mksolid(icon_width, icon_height, rgb)); + bitmap = m_bitmap_cache->insert(bitmap_key, m_bitmap_cache->mksolid(icon_width, icon_height, rgb)); } bmps.emplace_back(bitmap); } @@ -483,16 +486,54 @@ Slic3r::GUI::BitmapCache* m_bitmap_cache = nullptr; } -static wxBitmap get_extruder_color_icon(size_t extruder_idx) +static wxBitmap get_extruder_color_icon(size_t extruder_idx, bool thin_icon = false) { // Create the bitmap with color bars. - std::vector<wxBitmap*> bmps = get_extruder_color_icons(); + std::vector<wxBitmap*> bmps = get_extruder_color_icons(thin_icon); if (bmps.empty()) return wxNullBitmap; return *bmps[extruder_idx >= bmps.size() ? 0 : extruder_idx]; } +void apply_extruder_selector(wxBitmapComboBox** ctrl, + wxWindow* parent, + const std::string& first_item/* = ""*/, + wxPoint pos/* = wxDefaultPosition*/, + wxSize size/* = wxDefaultSize*/, + bool use_thin_icon/* = false*/) +{ + std::vector<wxBitmap*> icons = get_extruder_color_icons(use_thin_icon); + if (icons.empty()) + return; + + if (!*ctrl) + *ctrl = new wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, pos, size, + 0, nullptr, wxCB_READONLY); + else + { + (*ctrl)->SetPosition(pos); + (*ctrl)->SetMinSize(size); + (*ctrl)->SetSize(size); + (*ctrl)->Clear(); + } + + int i = 0; + wxString str = _(L("Extruder")); + for (wxBitmap* bmp : icons) { + if (i == 0) { + if (!first_item.empty()) + (*ctrl)->Append(_(first_item), *bmp); + ++i; + } + + (*ctrl)->Append(wxString::Format("%s %d", str, i), *bmp); + ++i; + } + (*ctrl)->SetSelection(0); +} + + // ***************************************************************************** // ---------------------------------------------------------------------------- // ObjectDataViewModelNode @@ -2248,6 +2289,8 @@ DoubleSlider::DoubleSlider( wxWindow *parent, m_bmp_revert = ScalableBitmap(this, "undo"); m_revert_icon_dim = m_bmp_revert.bmp().GetSize().x; + m_bmp_cog = ScalableBitmap(this, "cog"); + m_cog_icon_dim = m_bmp_cog.bmp().GetSize().x; m_selection = ssUndef; @@ -2306,6 +2349,8 @@ void DoubleSlider::msw_rescale() m_bmp_revert.msw_rescale(); m_revert_icon_dim = m_bmp_revert.bmp().GetSize().x; + m_bmp_cog.msw_rescale(); + m_cog_icon_dim = m_bmp_cog.bmp().GetSize().x; SLIDER_MARGIN = 4 + Slic3r::GUI::wxGetApp().em_unit(); @@ -2459,33 +2504,94 @@ std::vector<double> DoubleSlider::GetTicksValues() const const int val_size = m_values.size(); if (!m_values.empty()) - for (int tick : m_ticks) { - if (tick > val_size) + // #ys_FIXME_COLOR + // for (int tick : m_ticks) { + // if (tick > val_size) + // break; + // values.push_back(m_values[tick]); + // } + for (const TICK_CODE& tick : m_ticks_) { + if (tick.tick > val_size) break; - values.push_back(m_values[tick]); + values.push_back(m_values[tick.tick]); } return values; } -void DoubleSlider::SetTicksValues(const std::vector<double>& heights) +using t_custom_code = Slic3r::Model::CustomGCode; +std::vector<t_custom_code> DoubleSlider::GetTicksValues_() const +{ + std::vector<t_custom_code> values; + + const int val_size = m_values.size(); + if (!m_values.empty()) + for (const TICK_CODE& tick : m_ticks_) { + if (tick.tick > val_size) + break; + values.push_back(t_custom_code(m_values[tick.tick], tick.gcode, tick.extruder)); + } + + return values; +} + +void DoubleSlider::SetTicksValues_(const std::vector<t_custom_code>& heights) { if (m_values.empty()) return; - const bool was_empty = m_ticks.empty(); + const bool was_empty = m_ticks_.empty(); - m_ticks.clear(); + m_ticks_.clear(); + for (auto h : heights) { + auto it = std::lower_bound(m_values.begin(), m_values.end(), h.height - epsilon()); + + if (it == m_values.end()) + continue; + + m_ticks_.insert(TICK_CODE(it-m_values.begin(), h.gcode, h.extruder)); + } + + if (!was_empty && m_ticks_.empty() && m_state != msMultiExtruder) + // Switch to the "Feature type"/"Tool" from the very beginning of a new object slicing after deleting of the old one + wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); +} + +void DoubleSlider::SetTicksValues(const std::vector<double>& heights) +{ + if (m_values.empty()) + return; + + // #ys_FIXME_COLOR + // const bool was_empty = m_ticks.empty(); + // + // m_ticks.clear(); + // for (auto h : heights) { + // auto it = std::lower_bound(m_values.begin(), m_values.end(), h - epsilon()); + // + // if (it == m_values.end()) + // continue; + // + // m_ticks.insert(it-m_values.begin()); + // } + // + // if (!was_empty && m_ticks.empty()) + // // Switch to the "Feature type"/"Tool" from the very beginning of a new object slicing after deleting of the old one + // wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); + + const bool was_empty = m_ticks_.empty(); + + m_ticks_.clear(); for (auto h : heights) { auto it = std::lower_bound(m_values.begin(), m_values.end(), h - epsilon()); if (it == m_values.end()) continue; - m_ticks.insert(it-m_values.begin()); + m_ticks_.insert(it-m_values.begin()); } - if (!was_empty && m_ticks.empty()) + if (!was_empty && m_ticks_.empty()) // Switch to the "Feature type"/"Tool" from the very beginning of a new object slicing after deleting of the old one wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); } @@ -2549,6 +2655,9 @@ void DoubleSlider::render() //draw revert bitmap (if it's shown) draw_revert_icon(dc); + + //draw cog bitmap (if it's shown) + draw_cog_icon(dc); } void DoubleSlider::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end) @@ -2560,7 +2669,10 @@ void DoubleSlider::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoin return; wxBitmap* icon = m_is_action_icon_focesed ? &m_bmp_add_tick_off.bmp() : &m_bmp_add_tick_on.bmp(); - if (m_ticks.find(tick) != m_ticks.end()) + // #ys_FIXME_COLOR + // if (m_ticks.find(tick) != m_ticks.end()) + // icon = m_is_action_icon_focesed ? &m_bmp_del_tick_off.bmp() : &m_bmp_del_tick_on.bmp(); + if (m_ticks_.find(tick) != m_ticks_.end()) icon = m_is_action_icon_focesed ? &m_bmp_del_tick_off.bmp() : &m_bmp_del_tick_on.bmp(); wxCoord x_draw, y_draw; @@ -2703,9 +2815,13 @@ void DoubleSlider::draw_ticks(wxDC& dc) int height, width; get_size(&width, &height); const wxCoord mid = is_horizontal() ? 0.5*height : 0.5*width; - for (auto tick : m_ticks) + // #ys_FIXME_COLOR + // for (auto tick : m_ticks) + for (auto tick : m_ticks_) { - const wxCoord pos = get_position_from_value(tick); + // #ys_FIXME_COLOR + // const wxCoord pos = get_position_from_value(tick); + const wxCoord pos = get_position_from_value(tick.tick); is_horizontal() ? dc.DrawLine(pos, mid-14, pos, mid-9) : dc.DrawLine(mid - 14, pos/* - 1*/, mid - 9, pos/* - 1*/); @@ -2735,7 +2851,9 @@ void DoubleSlider::draw_colored_band(wxDC& dc) main_band.SetBottom(height - SLIDER_MARGIN + 1); } - if (m_ticks.empty()) { + // #ys_FIXME_COLOR + // if (m_ticks.empty()) { + if (m_ticks_.empty()) { dc.SetPen(GetParent()->GetBackgroundColour()); dc.SetBrush(GetParent()->GetBackgroundColour()); dc.DrawRectangle(main_band); @@ -2751,11 +2869,15 @@ void DoubleSlider::draw_colored_band(wxDC& dc) dc.DrawRectangle(main_band); size_t i = 1; - for (auto tick : m_ticks) + // #ys_FIXME_COLOR + // for (auto tick : m_ticks) + for (auto tick : m_ticks_) { if (i == colors_cnt) i = 0; - const wxCoord pos = get_position_from_value(tick); + // #ys_FIXME_COLOR + //const wxCoord pos = get_position_from_value(tick); + const wxCoord pos = get_position_from_value(tick.tick); is_horizontal() ? main_band.SetLeft(SLIDER_MARGIN + pos) : main_band.SetBottom(pos-1); @@ -2788,7 +2910,9 @@ void DoubleSlider::draw_one_layer_icon(wxDC& dc) void DoubleSlider::draw_revert_icon(wxDC& dc) { - if (m_ticks.empty() || !m_is_enabled_tick_manipulation) + // #ys_FIXME_COLOR + // if (m_ticks.empty() || !m_is_enabled_tick_manipulation) + if (m_ticks_.empty() || !m_is_enabled_tick_manipulation) return; int width, height; @@ -2804,6 +2928,24 @@ void DoubleSlider::draw_revert_icon(wxDC& dc) m_rect_revert_icon = wxRect(x_draw, y_draw, m_revert_icon_dim, m_revert_icon_dim); } +void DoubleSlider::draw_cog_icon(wxDC& dc) +{ + if (m_state != msMultiExtruderWholePrint) + return; + + int width, height; + get_size(&width, &height); + + wxCoord x_draw, y_draw; + is_horizontal() ? x_draw = width-2 : x_draw = width - m_cog_icon_dim - 2; + is_horizontal() ? y_draw = height - m_cog_icon_dim - 2 : y_draw = height-2; + + dc.DrawBitmap(m_bmp_cog.bmp(), x_draw, y_draw); + + //update rect of the lock/unlock icon + m_rect_cog_icon = wxRect(x_draw, y_draw, m_cog_icon_dim, m_cog_icon_dim); +} + void DoubleSlider::update_thumb_rect(const wxCoord& begin_x, const wxCoord& begin_y, const SelectedSlider& selection) { const wxRect& rect = wxRect(begin_x, begin_y, m_thumb_size.x, m_thumb_size.y); @@ -2840,16 +2982,24 @@ bool DoubleSlider::is_point_in_rect(const wxPoint& pt, const wxRect& rect) int DoubleSlider::is_point_near_tick(const wxPoint& pt) { - for (auto tick : m_ticks) { - const wxCoord pos = get_position_from_value(tick); + // #ys_FIXME_COLOR + // for (auto tick : m_ticks) { + for (auto tick : m_ticks_) { + // #ys_FIXME_COLOR + // const wxCoord pos = get_position_from_value(tick); + const wxCoord pos = get_position_from_value(tick.tick); if (is_horizontal()) { if (pos - 4 <= pt.x && pt.x <= pos + 4) - return tick; + // #ys_FIXME_COLOR + // return tick; + return tick.tick; } else { if (pos - 4 <= pt.y && pt.y <= pos + 4) - return tick; + // #ys_FIXME_COLOR + // return tick; + return tick.tick; } } return -1; @@ -2901,7 +3051,49 @@ void DoubleSlider::OnLeftDown(wxMouseEvent& event) m_selection == ssLower ? correct_lower_value() : correct_higher_value(); if (!m_selection) m_selection = ssHigher; - m_ticks.clear(); + // #ys_FIXME_COLOR + // m_ticks.clear(); + m_ticks_.clear(); + wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); + } + else if (is_point_in_rect(pos, m_rect_cog_icon) && m_state == msMultiExtruderWholePrint) { + // show dialog for set extruder sequence + Slic3r::GUI::ExtruderSequenceDialog dlg(m_extruders_sequence); + if (dlg.ShowModal() != wxID_OK) + return; + + m_extruders_sequence = dlg.GetValue(); + + m_ticks_.erase(std::remove_if(m_ticks_.begin(), m_ticks_.end(), + [](TICK_CODE tick) { return tick.gcode == "tool_change"; }), m_ticks_.end()); + + int tick = 0; + double value = 0.0; + int extruder = 0; + const int extr_cnt = m_extruders_sequence.extruders.size(); + + while (tick <= m_max_value) + { + m_ticks_.insert(TICK_CODE(tick, "tool_change", m_extruders_sequence.extruders[extruder]+1)); + + extruder++; + if (extruder == extr_cnt) + extruder = 0; + if (m_extruders_sequence.is_mm_intervals) + { + value += m_extruders_sequence.interval_by_mm; + auto it = std::lower_bound(m_values.begin(), m_values.end(), value - epsilon()); + + if (it == m_values.end()) + break; + + tick = it - m_values.begin(); + } + else + tick += m_extruders_sequence.interval_by_layers; + } + + // m_ticks_.clear(); wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); } else @@ -2956,6 +3148,31 @@ void DoubleSlider::correct_higher_value() m_lower_value = m_higher_value; } +wxString DoubleSlider::get_tooltip(IconFocus icon_focus) +{ + wxString tooltip(wxEmptyString); + if (m_is_one_layer_icon_focesed) + tooltip = _(L("One layer mode")); + + if (icon_focus == ifRevert) + tooltip = _(L("Discard all custom changes")); + if (icon_focus == ifCog) + tooltip = _(L("Set extruder sequence for whole print")); + else if (m_is_action_icon_focesed) + { + const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + const auto tick_code_it = m_ticks_.find(tick); + tooltip = tick_code_it == m_ticks_.end() ? _(L("Add color change")) : + tick_code_it->gcode == "M600" ? _(L("Delete color change")) : + tick_code_it->gcode == "M601" ? _(L("Delete pause")) : + tick_code_it->gcode == "tool_change" ? ( m_state == msSingleExtruder ? _(L("Delete color change")) : + from_u8((boost::format(_utf8(L("Delete extruder change to \"%1%\""))) % tick_code_it->extruder).str()) ) : + from_u8((boost::format(_utf8(L("Delete \"%1%\" code"))) % tick_code_it->gcode).str()); + } + + return tooltip; +} + void DoubleSlider::OnMotion(wxMouseEvent& event) { bool action = false; @@ -2964,11 +3181,16 @@ void DoubleSlider::OnMotion(wxMouseEvent& event) const wxPoint pos = event.GetLogicalPosition(dc); m_is_one_layer_icon_focesed = is_point_in_rect(pos, m_rect_one_layer_icon); - bool is_revert_icon_focused = false; + IconFocus icon_focus = ifNone; if (!m_is_left_down && !m_is_one_layer) { m_is_action_icon_focesed = is_point_in_rect(pos, m_rect_tick_action); - is_revert_icon_focused = !m_ticks.empty() && is_point_in_rect(pos, m_rect_revert_icon); + // #ys_FIXME_COLOR + // is_revert_icon_focused = !m_ticks.empty() && is_point_in_rect(pos, m_rect_revert_icon); + if (!m_ticks_.empty() && is_point_in_rect(pos, m_rect_revert_icon)) + icon_focus = ifRevert; + else if (is_point_in_rect(pos, m_rect_cog_icon)) + icon_focus = ifCog; } else if (m_is_left_down || m_is_right_down) { if (m_selection == ssLower) { @@ -2989,10 +3211,11 @@ void DoubleSlider::OnMotion(wxMouseEvent& event) event.Skip(); // Set tooltips with information for each icon - const wxString tooltip = m_is_one_layer_icon_focesed ? _(L("One layer mode")) : - m_is_action_icon_focesed ? _(L("Add/Del color change")) : - is_revert_icon_focused ? _(L("Discard all color changes")) : ""; - this->SetToolTip(tooltip); + // #ys_FIXME_COLOR + // const wxString tooltip = m_is_one_layer_icon_focesed ? _(L("One layer mode")) : + // m_is_action_icon_focesed ? _(L("Add/Del color change")) : + // is_revert_icon_focused ? _(L("Discard all color changes")) : ""; + this->SetToolTip(get_tooltip(icon_focus)); if (action) { @@ -3009,6 +3232,31 @@ void DoubleSlider::OnLeftUp(wxMouseEvent& event) return; this->ReleaseMouse(); m_is_left_down = false; + + if (m_show_context_menu) + { + if (m_state == msMultiExtruderWholePrint) + { + wxMenu menu; + const int extruders_cnt = Slic3r::GUI::wxGetApp().extruders_edited_cnt(); + if (extruders_cnt > 1) + { + wxMenu* add_color_change_menu = new wxMenu(); + + for (int i = 1; i <= extruders_cnt; i++) + append_menu_item(add_color_change_menu, wxID_ANY, wxString::Format(_(L("Extruder %d")), i), "", + [this, i](wxCommandEvent&) { add_code("M600", i); }, "", &menu); + + const wxString menu_name = from_u8((boost::format(_utf8(L("Add color change (%1%) for:"))) % "M600").str()); + wxMenuItem* add_color_change_menu_item = menu.AppendSubMenu(add_color_change_menu, menu_name, ""); + add_color_change_menu_item->SetBitmap(create_scaled_bitmap(nullptr, "colorchange_add_off.png")); + } + + Slic3r::GUI::wxGetApp().plater()->PopupMenu(&menu); + m_show_context_menu = false; + } + } + Refresh(); Update(); event.Skip(); @@ -3059,17 +3307,38 @@ void DoubleSlider::action_tick(const TicksAction action) const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - if (action == taOnIcon) { - if (!m_ticks.insert(tick).second) - m_ticks.erase(tick); + // #ys_FIXME_COLOR + // if (action == taOnIcon) { + // if (!m_ticks.insert(tick).second) + // m_ticks.erase(tick); + // } + // else { + // const auto it = m_ticks.find(tick); + // if (it == m_ticks.end() && action == taAdd) + // m_ticks.insert(tick); + // else if (it != m_ticks.end() && action == taDel) + // m_ticks.erase(tick); + // } + + const auto it = m_ticks_.find(tick); + + if (it != m_ticks_.end()) + { + if (action == taAdd) + return; + m_ticks_.erase(TICK_CODE(tick)); } - else { - const auto it = m_ticks.find(tick); - if (it == m_ticks.end() && action == taAdd) - m_ticks.insert(tick); - else if (it != m_ticks.end() && action == taDel) - m_ticks.erase(tick); + else if (action == taDel) + return; + else if (m_state == msMultiExtruderWholePrint) + { + if (action == taAdd) + return; + m_show_context_menu = true; + return; } + else + m_ticks_.insert(TICK_CODE(tick, "M600", m_state == msSingleExtruder ? 0 : m_current_extruder)); wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); Refresh(); @@ -3148,6 +3417,22 @@ void DoubleSlider::OnRightDown(wxMouseEvent& event) this->CaptureMouse(); const wxClientDC dc(this); + + wxPoint pos = event.GetLogicalPosition(dc); + if (is_point_in_rect(pos, m_rect_tick_action) && m_is_enabled_tick_manipulation) + { + const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + // if on this Y doesn't exist tick + // #ys_FIXME_COLOR + // if (m_ticks.find(tick) == m_ticks.end()) + if (m_ticks_.find(tick) == m_ticks_.end()) + { + // show context menu on OnRightUp() + m_show_context_menu = true; + return; + } + } + detect_selected_slider(event.GetLogicalPosition(dc)); if (!m_selection) return; @@ -3157,6 +3442,7 @@ void DoubleSlider::OnRightDown(wxMouseEvent& event) else m_lower_value = m_higher_value; + // set slider to "one layer" mode m_is_right_down = m_is_one_layer = true; Refresh(); @@ -3164,6 +3450,20 @@ void DoubleSlider::OnRightDown(wxMouseEvent& event) event.Skip(); } +int DoubleSlider::get_extruder_for_tick(int tick) +{ + int extruder = 0; + if (!m_ticks_.empty()) { + auto tick_code_it = m_ticks_.lower_bound(tick); + if (tick_code_it != m_ticks_.begin()) { + --tick_code_it; + extruder = tick_code_it->extruder; + } + } + + return extruder; +} + void DoubleSlider::OnRightUp(wxMouseEvent& event) { if (!HasCapture()) @@ -3171,11 +3471,113 @@ void DoubleSlider::OnRightUp(wxMouseEvent& event) this->ReleaseMouse(); m_is_right_down = m_is_one_layer = false; + if (m_show_context_menu) { + wxMenu menu; + + if (m_state == msMultiExtruderWholePrint) + { + const int extruders_cnt = Slic3r::GUI::wxGetApp().extruders_edited_cnt(); + if (extruders_cnt > 1) + { + const int initial_extruder = get_extruder_for_tick(m_selection == ssLower ? m_lower_value : m_higher_value); + + wxMenu* change_extruder_menu = new wxMenu(); + wxMenu* add_color_change_menu = new wxMenu(); + + for (int i = 0; i <= extruders_cnt; i++) { + const wxString item_name = i == 0 ? _(L("Default")) : wxString::Format(_(L("Extruder %d")), i); + + append_menu_radio_item(change_extruder_menu, wxID_ANY, item_name, "", + [this, i](wxCommandEvent&) { change_extruder(i); }, &menu)->Check(i == initial_extruder); + + if (i==0) // don't use M600 for default extruder, if multimaterial print is selected + continue; + append_menu_item(add_color_change_menu, wxID_ANY, item_name, "", + [this, i](wxCommandEvent&) { add_code("M600", i); }, "", &menu); + } + + wxMenuItem* change_extruder_menu_item = menu.AppendSubMenu(change_extruder_menu, _(L("Change extruder")), _(L("Use another extruder"))); + change_extruder_menu_item->SetBitmap(create_scaled_bitmap(nullptr, "change_extruder")); + + const wxString menu_name = from_u8((boost::format(_utf8(L("Add color change (%1%) for:"))) % "M600").str()); + wxMenuItem* add_color_change_menu_item = menu.AppendSubMenu(add_color_change_menu, menu_name, ""); + add_color_change_menu_item->SetBitmap(create_scaled_bitmap(nullptr, "colorchange_add_off.png")); + } + } + else + append_menu_item(&menu, wxID_ANY, _(L("Add color change")) + " (M600)", "", + [this](wxCommandEvent&) { add_code("M600"); }, "colorchange_add_off.png", &menu); + + if (m_state != msMultiExtruder) + append_menu_item(&menu, wxID_ANY, _(L("Add pause print")) + " (M601)", "", + [this](wxCommandEvent&) { add_code("M601"); }, "pause_add.png", &menu); + + append_menu_item(&menu, wxID_ANY, _(L("Add custom G-code")), "", + [this](wxCommandEvent&) { add_code(""); }, "add_gcode", &menu); + + Slic3r::GUI::wxGetApp().plater()->PopupMenu(&menu); + + m_show_context_menu = false; + } + Refresh(); Update(); event.Skip(); } +void DoubleSlider::add_code(std::string code, int selected_extruder/* = -1*/) +{ + const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + // if on this Y doesn't exist tick + if (m_ticks_.find(tick) == m_ticks_.end()) + { + if (code.empty()) + { + wxString msg_text = from_u8(_utf8(L("Enter custom G-code used on current layer"))) + " :"; + wxString msg_header = from_u8((boost::format(_utf8(L("Custom Gcode on current layer (%1% mm)."))) % m_values[tick]).str()); + + // get custom gcode + wxTextEntryDialog dlg(nullptr, msg_text, msg_header, m_custom_gcode, + wxTextEntryDialogStyle | wxTE_MULTILINE); + if (dlg.ShowModal() != wxID_OK || dlg.GetValue().IsEmpty()) + return; + + m_custom_gcode = dlg.GetValue(); + code = m_custom_gcode.c_str(); + } + + int extruder = 0; + if (m_state == msMultiExtruderWholePrint) { + if (code == "M600" && selected_extruder >= 0) + extruder = selected_extruder; + else + extruder = get_extruder_for_tick(m_selection == ssLower ? m_lower_value : m_higher_value); + } + else if (m_state == msMultiExtruder && m_current_extruder > 0) + extruder = m_current_extruder; + + m_ticks_.insert(TICK_CODE(tick, code, extruder)); + + wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); + Refresh(); + Update(); + } +} + +void DoubleSlider::change_extruder(int extruder) +{ + const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + // if on this Y doesn't exist tick + if (m_ticks_.find(tick) == m_ticks_.end()) + { + m_ticks_.insert(TICK_CODE(tick, "tool_change", extruder)); + + wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); + Refresh(); + Update(); + } +} + // ---------------------------------------------------------------------------- // LockButton diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index d4d5e7998..1b8dc2fa5 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -16,6 +16,7 @@ #include <vector> #include <set> #include <functional> +#include "libslic3r/Model.hpp" namespace Slic3r { enum class ModelVolumeType : int; @@ -48,6 +49,8 @@ wxMenuItem* append_menu_check_item(wxMenu* menu, int id, const wxString& string, std::function<void(wxCommandEvent& event)> cb, wxEvtHandler* event_handler); class wxDialog; +class wxBitmapComboBox; + void edit_tooltip(wxString& tooltip); void msw_buttons_rescale(wxDialog* dlg, const int em_unit, const std::vector<int>& btn_ids); int em_unit(wxWindow* win); @@ -55,7 +58,13 @@ int em_unit(wxWindow* win); wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name, const int px_cnt = 16, const bool is_horizontal = false, const bool grayscale = false); -std::vector<wxBitmap*> get_extruder_color_icons(); +std::vector<wxBitmap*> get_extruder_color_icons(bool thin_icon = false); +void apply_extruder_selector(wxBitmapComboBox** ctrl, + wxWindow* parent, + const std::string& first_item = "", + wxPoint pos = wxDefaultPosition, + wxSize size = wxDefaultSize, + bool use_thin_icon = false); class wxCheckListBoxComboPopup : public wxCheckListBox, public wxComboPopup { @@ -749,6 +758,11 @@ enum TicksAction{ class DoubleSlider : public wxControl { + enum IconFocus { + ifNone, + ifRevert, + ifCog + }; public: DoubleSlider( wxWindow *parent, @@ -795,6 +809,8 @@ public: } void ChangeOneLayerLock(); std::vector<double> GetTicksValues() const; + std::vector<Slic3r::Model::CustomGCode> GetTicksValues_() const; + void SetTicksValues_(const std::vector<Slic3r::Model::CustomGCode> &heights); void SetTicksValues(const std::vector<double>& heights); void EnableTickManipulation(bool enable = true) { m_is_enabled_tick_manipulation = enable; @@ -803,6 +819,21 @@ public: EnableTickManipulation(false); } + enum ManipulationState { + msSingleExtruder, // single extruder printer preset is selected + msMultiExtruder, // multiple extruder printer preset is selected + msMultiExtruderWholePrint // multiple extruder printer preset is selected, and "Whole print" is selected + }; + void SetManipulationState(ManipulationState state) { + m_state = state; + } + ManipulationState GetManipulationState() const { return m_state; } + void SetExtruderID(int extruder) { + m_current_extruder = extruder; + m_state = extruder < 0 ? msSingleExtruder : + extruder > 0 ? msMultiExtruder : msMultiExtruderWholePrint; + } + bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; } bool is_one_layer() const { return m_is_one_layer; } bool is_lower_at_min() const { return m_lower_value == m_min_value; } @@ -820,7 +851,10 @@ public: void OnKeyUp(wxKeyEvent &event); void OnChar(wxKeyEvent &event); void OnRightDown(wxMouseEvent& event); + int get_extruder_for_tick(int tick); void OnRightUp(wxMouseEvent& event); + void add_code(std::string code, int selected_extruder = -1); + void change_extruder(int extruder); protected: @@ -834,6 +868,7 @@ protected: void draw_colored_band(wxDC& dc); void draw_one_layer_icon(wxDC& dc); void draw_revert_icon(wxDC& dc); + void draw_cog_icon(wxDC &dc); void draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection); void draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, SelectedSlider selection); void draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const; @@ -842,6 +877,7 @@ protected: void detect_selected_slider(const wxPoint& pt); void correct_lower_value(); void correct_higher_value(); + wxString get_tooltip(IconFocus icon_focus); void move_current_thumb(const bool condition); void action_tick(const TicksAction action); void enter_window(wxMouseEvent& event, const bool enter); @@ -876,6 +912,7 @@ private: ScalableBitmap m_bmp_one_layer_unlock_on; ScalableBitmap m_bmp_one_layer_unlock_off; ScalableBitmap m_bmp_revert; + ScalableBitmap m_bmp_cog; SelectedSlider m_selection; bool m_is_left_down = false; bool m_is_right_down = false; @@ -884,16 +921,22 @@ private: bool m_is_action_icon_focesed = false; bool m_is_one_layer_icon_focesed = false; bool m_is_enabled_tick_manipulation = true; + bool m_show_context_menu = false; + ManipulationState m_state = msSingleExtruder; + wxString m_custom_gcode = wxEmptyString; + int m_current_extruder = -1; wxRect m_rect_lower_thumb; wxRect m_rect_higher_thumb; wxRect m_rect_tick_action; wxRect m_rect_one_layer_icon; wxRect m_rect_revert_icon; + wxRect m_rect_cog_icon; wxSize m_thumb_size; int m_tick_icon_dim; int m_lock_icon_dim; int m_revert_icon_dim; + int m_cog_icon_dim; long m_style; float m_label_koef = 1.0; @@ -912,6 +955,79 @@ private: std::vector<wxPen*> m_segm_pens; std::set<int> m_ticks; std::vector<double> m_values; + + struct TICK_CODE + { + TICK_CODE(int tick):tick(tick), gcode("M600"), extruder(0) {} + TICK_CODE(int tick, const std::string& code) : + tick(tick), gcode(code), extruder(0) {} + TICK_CODE(int tick, int extruder) : + tick(tick), gcode("M600"), extruder(extruder) {} + TICK_CODE(int tick, const std::string& code, int extruder) : + tick(tick), gcode(code), extruder(extruder) {} + + bool operator<(const TICK_CODE& other) const { return other.tick > this->tick; } + bool operator>(const TICK_CODE& other) const { return other.tick < this->tick; } + TICK_CODE operator=(const TICK_CODE& other) const { + TICK_CODE ret_val(other.tick, other.gcode, other.extruder); + return ret_val; + }/* + TICK_CODE& operator=(const TICK_CODE& other) { + this->tick = other.tick; + this->gcode = other.gcode; + this->extruder = other.extruder; + return *this; + }*/ + + int tick; + std::string gcode; + int extruder; + }; + + std::set<TICK_CODE> m_ticks_; + +public: + struct ExtrudersSequence + { + bool is_mm_intervals; + double interval_by_mm; + int interval_by_layers; + std::vector<size_t> extruders; + + ExtrudersSequence() : + is_mm_intervals(true), + interval_by_mm(3.0), + interval_by_layers(10), + extruders({ 0 }) {} + + ExtrudersSequence(const ExtrudersSequence& other) : + is_mm_intervals(other.is_mm_intervals), + interval_by_mm(other.interval_by_mm), + interval_by_layers(other.interval_by_layers), + extruders(other.extruders) {} + + ExtrudersSequence& operator=(const ExtrudersSequence& other) { + this->is_mm_intervals = other.is_mm_intervals; + this->interval_by_mm = other.interval_by_mm; + this->interval_by_layers= other.interval_by_layers; + this->extruders = other.extruders; + + return *this; + } + + void add_extruder(size_t pos) + { + extruders.insert(extruders.begin() + pos+1, size_t(0)); + } + + void delete_extruder(size_t pos) + { + if (extruders.size() == 1) + return;// last item can't be deleted + extruders.erase(extruders.begin() + pos); + } + } + m_extruders_sequence; }; |