Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--resources/icons/add_gcode.svg12
-rw-r--r--resources/icons/pause_add.pngbin0 -> 5883 bytes
-rw-r--r--src/libslic3r/GCode.cpp57
-rw-r--r--src/libslic3r/GCode.hpp2
-rw-r--r--src/libslic3r/Model.cpp16
-rw-r--r--src/libslic3r/Model.hpp30
-rw-r--r--src/libslic3r/Print.cpp70
-rw-r--r--src/libslic3r/Print.hpp6
-rw-r--r--src/slic3r/CMakeLists.txt2
-rw-r--r--src/slic3r/GUI/BackgroundSlicingProcess.hpp5
-rw-r--r--src/slic3r/GUI/ExtruderSequenceDialog.cpp197
-rw-r--r--src/slic3r/GUI/ExtruderSequenceDialog.hpp45
-rw-r--r--src/slic3r/GUI/GLCanvas3D.cpp184
-rw-r--r--src/slic3r/GUI/GLCanvas3D.hpp11
-rw-r--r--src/slic3r/GUI/GUI_ObjectList.cpp16
-rw-r--r--src/slic3r/GUI/GUI_ObjectManipulation.cpp2
-rw-r--r--src/slic3r/GUI/GUI_Preview.cpp187
-rw-r--r--src/slic3r/GUI/GUI_Preview.hpp15
-rw-r--r--src/slic3r/GUI/Plater.cpp15
-rw-r--r--src/slic3r/GUI/wxExtensions.cpp484
-rw-r--r--src/slic3r/GUI/wxExtensions.hpp118
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
new file mode 100644
index 000000000..afe881de8
--- /dev/null
+++ b/resources/icons/pause_add.png
Binary files differ
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 &copy : *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;
};