diff --git a/CMakeLists.txt b/CMakeLists.txt
index 633ab3f19..a41229987 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,5 +1,5 @@
-project(PrusaSlicer)
cmake_minimum_required(VERSION 3.2)
+project(PrusaSlicer)
include("version.inc")
include(GNUInstallDirs)
diff --git a/deps/deps-linux.cmake b/deps/deps-linux.cmake
index f5571d470..209cc3fd4 100644
--- a/deps/deps-linux.cmake
+++ b/deps/deps-linux.cmake
@@ -26,8 +26,8 @@ ExternalProject_Add(dep_boost
ExternalProject_Add(dep_libopenssl
EXCLUDE_FROM_ALL 1
- URL "https://github.com/openssl/openssl/archive/OpenSSL_1_1_0g.tar.gz"
- URL_HASH SHA256=8e9516b8635bb9113c51a7b5b27f9027692a56b104e75b709e588c3ffd6a0422
+ URL "https://github.com/openssl/openssl/archive/OpenSSL_1_1_0l.tar.gz"
+ URL_HASH SHA256=e2acf0cf58d9bff2b42f2dc0aee79340c8ffe2c5e45d3ca4533dd5d4f5775b1d
BUILD_IN_SOURCE 1
CONFIGURE_COMMAND ./config
"--prefix=${DESTDIR}/usr/local"
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 @@
+
+
+
diff --git a/resources/icons/change_extruder.svg b/resources/icons/change_extruder.svg
new file mode 100644
index 000000000..fe8de635d
--- /dev/null
+++ b/resources/icons/change_extruder.svg
@@ -0,0 +1,9 @@
+
+
+
+
\ No newline at end of file
diff --git a/resources/icons/edit_gcode.svg b/resources/icons/edit_gcode.svg
new file mode 100644
index 000000000..694e106cc
--- /dev/null
+++ b/resources/icons/edit_gcode.svg
@@ -0,0 +1,15 @@
+
+
+
diff --git a/resources/icons/edit_uni.svg b/resources/icons/edit_uni.svg
new file mode 100644
index 000000000..f7b1673a6
--- /dev/null
+++ b/resources/icons/edit_uni.svg
@@ -0,0 +1,20 @@
+
+
+
diff --git a/resources/icons/pause_add.png b/resources/icons/pause_add.png
new file mode 100644
index 000000000..afe881de8
Binary files /dev/null and b/resources/icons/pause_add.png differ
diff --git a/resources/icons/pause_print.svg b/resources/icons/pause_print.svg
new file mode 100644
index 000000000..a905b1ea1
--- /dev/null
+++ b/resources/icons/pause_print.svg
@@ -0,0 +1,18 @@
+
+
+
diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp
index ff3cf777d..62e964a7a 100644
--- a/src/libslic3r/Format/3mf.cpp
+++ b/src/libslic3r/Format/3mf.cpp
@@ -51,6 +51,7 @@ const std::string MODEL_CONFIG_FILE = "Metadata/Slic3r_PE_model.config";
const std::string LAYER_HEIGHTS_PROFILE_FILE = "Metadata/Slic3r_PE_layer_heights_profile.txt";
const std::string LAYER_CONFIG_RANGES_FILE = "Metadata/Prusa_Slicer_layer_config_ranges.xml";
const std::string SLA_SUPPORT_POINTS_FILE = "Metadata/Slic3r_PE_sla_support_points.txt";
+const std::string CUSTOM_GCODE_PER_HEIGHT_FILE = "Metadata/Prusa_Slicer_custom_gcode_per_height.xml";
const char* MODEL_TAG = "model";
const char* RESOURCES_TAG = "resources";
@@ -417,6 +418,8 @@ namespace Slic3r {
void _extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
void _extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
+ void _extract_custom_gcode_per_height_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
+
void _extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig& config, const std::string& archive_filename);
bool _extract_model_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model);
@@ -626,6 +629,11 @@ namespace Slic3r {
// extract slic3r print config file
_extract_print_config_from_archive(archive, stat, config, filename);
}
+ if (boost::algorithm::iequals(name, CUSTOM_GCODE_PER_HEIGHT_FILE))
+ {
+ // extract slic3r layer config ranges file
+ _extract_custom_gcode_per_height_from_archive(archive, stat);
+ }
else if (boost::algorithm::iequals(name, MODEL_CONFIG_FILE))
{
// extract slic3r model config file
@@ -1056,6 +1064,43 @@ namespace Slic3r {
return true;
}
+ void _3MF_Importer::_extract_custom_gcode_per_height_from_archive(::mz_zip_archive &archive, const mz_zip_archive_file_stat &stat)
+ {
+ if (stat.m_uncomp_size > 0)
+ {
+ std::string buffer((size_t)stat.m_uncomp_size, 0);
+ mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
+ if (res == 0) {
+ add_error("Error while reading custom Gcodes per height data to buffer");
+ return;
+ }
+
+ std::istringstream iss(buffer); // wrap returned xml to istringstream
+ pt::ptree main_tree;
+ pt::read_xml(iss, main_tree);
+
+ if (main_tree.front().first != "custom_gcodes_per_height")
+ return;
+ pt::ptree code_tree = main_tree.front().second;
+
+ if (!m_model->custom_gcode_per_height.empty())
+ m_model->custom_gcode_per_height.clear();
+
+ for (const auto& code : code_tree)
+ {
+ if (code.first != "code")
+ continue;
+ pt::ptree tree = code.second;
+ double height = tree.get(".height");
+ std::string gcode = tree.get(".gcode");
+ int extruder = tree.get(".extruder");
+ std::string color = tree.get(".color");
+
+ m_model->custom_gcode_per_height.push_back(Model::CustomGCode(height, gcode, extruder, color)) ;
+ }
+ }
+ }
+
void _3MF_Importer::_handle_start_model_xml_element(const char* name, const char** attributes)
{
if (m_xml_parser == nullptr)
@@ -1838,6 +1883,7 @@ namespace Slic3r {
bool _add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model);
bool _add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config);
bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, const IdToObjectDataMap &objects_data);
+ bool _add_custom_gcode_per_height_file_to_archive(mz_zip_archive& archive, Model& model);
};
#if ENABLE_THUMBNAIL_GENERATOR
@@ -1940,6 +1986,15 @@ namespace Slic3r {
return false;
}
+ // Adds custom gcode per height file ("Metadata/Prusa_Slicer_custom_gcode_per_height.xml").
+ // All custom gcode per height of whole Model are stored here
+ if (!_add_custom_gcode_per_height_file_to_archive(archive, model))
+ {
+ close_zip_writer(&archive);
+ boost::filesystem::remove(filename);
+ return false;
+ }
+
// Adds slic3r print config file ("Metadata/Slic3r_PE.config").
// This file contains the content of FullPrintConfing / SLAFullPrintConfig.
if (config != nullptr)
@@ -2324,7 +2379,7 @@ namespace Slic3r {
if (!tree.empty())
{
std::ostringstream oss;
- boost::property_tree::write_xml(oss, tree);
+ pt::write_xml(oss, tree);
out = oss.str();
// Post processing("beautification") of the output string for a better preview
@@ -2510,7 +2565,49 @@ namespace Slic3r {
return true;
}
- bool load_3mf(const char* path, DynamicPrintConfig* config, Model* model, bool check_version)
+bool _3MF_Exporter::_add_custom_gcode_per_height_file_to_archive( mz_zip_archive& archive, Model& model)
+{
+ std::string out = "";
+
+ if (!model.custom_gcode_per_height.empty())
+ {
+ pt::ptree tree;
+ pt::ptree& main_tree = tree.add("custom_gcodes_per_height", "");
+
+ for (const Model::CustomGCode& code : model.custom_gcode_per_height)
+ {
+ pt::ptree& code_tree = main_tree.add("code", "");
+ // store minX and maxZ
+ code_tree.put(".height" , code.height );
+ code_tree.put(".gcode" , code.gcode );
+ code_tree.put(".extruder" , code.extruder );
+ code_tree.put(".color" , code.color );
+ }
+
+ if (!tree.empty())
+ {
+ std::ostringstream oss;
+ boost::property_tree::write_xml(oss, tree);
+ out = oss.str();
+
+ // Post processing("beautification") of the output string
+ boost::replace_all(out, "><", ">\n<");
+ }
+ }
+
+ if (!out.empty())
+ {
+ if (!mz_zip_writer_add_mem(&archive, CUSTOM_GCODE_PER_HEIGHT_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION))
+ {
+ add_error("Unable to add custom Gcodes per height file to archive");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool load_3mf(const char* path, DynamicPrintConfig* config, Model* model, bool check_version)
{
if ((path == nullptr) || (config == nullptr) || (model == nullptr))
return false;
diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp
index 181d6cb99..213952df3 100644
--- a/src/libslic3r/Format/AMF.cpp
+++ b/src/libslic3r/Format/AMF.cpp
@@ -16,6 +16,10 @@
#include "AMF.hpp"
+#include
+#include
+namespace pt = boost::property_tree;
+
#include
#include
#include
@@ -147,6 +151,8 @@ struct AMFParserContext
NODE_TYPE_MIRRORY, // amf/constellation/instance/mirrory
NODE_TYPE_MIRRORZ, // amf/constellation/instance/mirrorz
NODE_TYPE_PRINTABLE, // amf/constellation/instance/mirrorz
+ NODE_TYPE_CUSTOM_GCODE, // amf/custom_code_per_height
+ NODE_TYPE_GCODE_PER_HEIGHT, // amf/custom_code_per_height/code
NODE_TYPE_METADATA, // anywhere under amf/*/metadata
};
@@ -227,7 +233,7 @@ struct AMFParserContext
// Current instance allocated for an amf/constellation/instance subtree.
Instance *m_instance;
// Generic string buffer for vertices, face indices, metadata etc.
- std::string m_value[3];
+ std::string m_value[4];
// Pointer to config to update if config data are stored inside the amf file
DynamicPrintConfig *m_config;
@@ -268,6 +274,8 @@ void AMFParserContext::startElement(const char *name, const char **atts)
}
} else if (strcmp(name, "constellation") == 0) {
node_type_new = NODE_TYPE_CONSTELLATION;
+ } else if (strcmp(name, "custom_gcodes_per_height") == 0) {
+ node_type_new = NODE_TYPE_CUSTOM_GCODE;
}
break;
case 2:
@@ -294,6 +302,13 @@ void AMFParserContext::startElement(const char *name, const char **atts)
}
else
this->stop();
+ }
+ else if (strcmp(name, "code") == 0 && m_path[1] == NODE_TYPE_CUSTOM_GCODE) {
+ node_type_new = NODE_TYPE_GCODE_PER_HEIGHT;
+ m_value[0] = get_attribute(atts, "height");
+ m_value[1] = get_attribute(atts, "gcode");
+ m_value[2] = get_attribute(atts, "extruder");
+ m_value[3] = get_attribute(atts, "color");
}
break;
case 3:
@@ -616,6 +631,19 @@ void AMFParserContext::endElement(const char * /* name */)
m_instance = nullptr;
break;
+ case NODE_TYPE_GCODE_PER_HEIGHT: {
+ double height = double(atof(m_value[0].c_str()));
+ const std::string& gcode = m_value[1];
+ int extruder = atoi(m_value[2].c_str());
+ const std::string& color = m_value[3];
+
+ m_model.custom_gcode_per_height.push_back(Model::CustomGCode(height, gcode, extruder, color));
+
+ for (std::string& val: m_value)
+ val.clear();
+ break;
+ }
+
case NODE_TYPE_METADATA:
if ((m_config != nullptr) && strncmp(m_value[0].c_str(), SLIC3R_CONFIG_TYPE, strlen(SLIC3R_CONFIG_TYPE)) == 0)
m_config->load_from_gcode_string(m_value[1].c_str());
@@ -1190,6 +1218,42 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
stream << instances;
stream << " \n";
}
+
+ if (!model->custom_gcode_per_height.empty())
+ {
+ std::string out = "";
+ pt::ptree tree;
+
+ pt::ptree& main_tree = tree.add("custom_gcodes_per_height", "");
+
+ for (const Model::CustomGCode& code : model->custom_gcode_per_height)
+ {
+ pt::ptree& code_tree = main_tree.add("code", "");
+ // store minX and maxZ
+ code_tree.put(".height", code.height);
+ code_tree.put(".gcode", code.gcode);
+ code_tree.put(".extruder", code.extruder);
+ code_tree.put(".color", code.color);
+ }
+
+ if (!tree.empty())
+ {
+ std::ostringstream oss;
+ pt::write_xml(oss, tree);
+ out = oss.str();
+
+ int del_header_pos = out.find("\n <", ">\n<");
+
+ stream << out << "\n";
+ }
+ }
+
stream << "\n";
std::string internal_amf_filename = boost::ireplace_last_copy(boost::filesystem::path(export_path).filename().string(), ".zip.amf", ".amf");
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 014deb79c..358c77f3f 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -924,8 +924,9 @@ void GCode::_do_export(Print& print, FILE* file)
this->apply_print_config(print.config());
this->set_extruders(print.extruders());
- // Initialize colorprint.
- m_colorprint_heights = cast(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.
{
@@ -991,7 +992,7 @@ void GCode::_do_export(Print& print, FILE* file)
{
const size_t max_row_length = 78;
ThumbnailsList thumbnails;
- thumbnail_cb(thumbnails, print.full_print_config().option("thumbnails")->values, true, true, false);
+ thumbnail_cb(thumbnails, print.full_print_config().option("thumbnails")->values, true, true, true);
for (const ThumbnailData& data : thumbnails)
{
if (data.is_valid())
@@ -1113,6 +1114,47 @@ void GCode::_do_export(Print& print, FILE* file)
}
print.throw_if_canceled();
+ /* To avoid change filament for non-used extruder for Multi-material,
+ * check model->custom_gcode_per_height using tool_ordering values
+ * */
+ if (!m_custom_g_code_heights. empty())
+ {
+ bool delete_executed = false;
+ auto it = m_custom_g_code_heights.end();
+ while (it != m_custom_g_code_heights.begin())
+ {
+ --it;
+ if (it->gcode != ColorChangeCode)
+ continue;
+
+ auto it_layer_tools = std::lower_bound(tool_ordering.begin(), tool_ordering.end(), LayerTools(it->height));
+
+ bool used_extruder = false;
+ for (; it_layer_tools != tool_ordering.end(); it_layer_tools++)
+ {
+ const std::vector& extruders = it_layer_tools->extruders;
+ if (std::find(extruders.begin(), extruders.end(), (unsigned)(it->extruder-1)) != extruders.end())
+ {
+ used_extruder = true;
+ break;
+ }
+ }
+ if (used_extruder)
+ continue;
+
+ /* If we are there, current extruder wouldn't be used,
+ * so this color change is a redundant move.
+ * Delete this item from m_custom_g_code_heights
+ * */
+ it = m_custom_g_code_heights.erase(it);
+ delete_executed = true;
+ }
+
+ if (delete_executed)
+ model->custom_gcode_per_height = m_custom_g_code_heights;
+ }
+
+
m_cooling_buffer->set_current_extruder(initial_extruder_id);
// Emit machine envelope limits for the Marlin firmware.
@@ -1779,19 +1821,66 @@ 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());
+
+ std::string custom_code = "";
+ std::string pause_print_msg = "";
+ 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 == ColorChangeCode && m_custom_g_code_heights.front().extruder > 0)
+ m600_before_extruder = m_custom_g_code_heights.front().extruder - 1;
+ if (custom_code == PausePrintCode)
+ pause_print_msg = m_custom_g_code_heights.front().color;
+
+ 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";
+
+ // don't save "tool_change"(ExtruderChangeCode) code to GCode
+ if (colorprint_change && custom_code != ExtruderChangeCode) {
+ const bool single_material_print = print.config().nozzle_diameter.size() == 1;
+
+ if (custom_code == ColorChangeCode) // color change
+ {
+ // add tag for analyzer
+ gcode += "; " + GCodeAnalyzer::Color_Change_Tag + ",T" + std::to_string(m600_before_extruder) + "\n";
+ // add tag for time estimator
+ gcode += "; " + GCodeTimeEstimator::Color_Change_Tag + "\n";
+
+ if (!single_material_print && m600_before_extruder >= 0 && first_extruder_id != m600_before_extruder
+ // && !MMU1
+ ) {
+ //! FIXME_in_fw show message during print pause
+ gcode += "M601\n"; // pause print
+ gcode += "M117 Change filament for Extruder " + std::to_string(m600_before_extruder) + "\n";
+ }
+ else
+ gcode += custom_code + "\n";
+ }
+ else
+ {
+ if (custom_code == PausePrintCode) // Pause print
+ {
+ // add tag for analyzer
+ gcode += "; " + GCodeAnalyzer::Pause_Print_Tag + "\n";
+ //! FIXME_in_fw show message during print pause
+ if (!pause_print_msg.empty())
+ gcode += "M117 " + pause_print_msg + "\n";
+ // add tag for time estimator
+ //gcode += "; " + GCodeTimeEstimator::Pause_Print_Tag + "\n";
+ }
+ else // custom Gcode
+ {
+ // add tag for analyzer
+ gcode += "; " + GCodeAnalyzer::Custom_Code_Tag + "\n";
+ // add tag for time estimator
+ //gcode += "; " + GCodeTimeEstimator::Custom_Code_Tag + "\n";
+ }
+ gcode += custom_code + "\n";
+ }
}
@@ -2101,6 +2190,12 @@ void GCode::process_layer(
if (m_cooling_buffer)
gcode = m_cooling_buffer->process_layer(gcode, layer.id());
+ // add tag for analyzer
+ if (gcode.find(GCodeAnalyzer::Pause_Print_Tag) != gcode.npos)
+ gcode += "\n; " + GCodeAnalyzer::End_Pause_Print_Or_Custom_Code_Tag + "\n";
+ else if (gcode.find(GCodeAnalyzer::Custom_Code_Tag) != gcode.npos)
+ gcode += "\n; " + GCodeAnalyzer::End_Pause_Print_Or_Custom_Code_Tag + "\n";
+
#ifdef HAS_PRESSURE_EQUALIZER
// Apply pressure equalization if enabled;
// printf("G-code before filter:\n%s\n", gcode.c_str());
diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp
index 40794986a..7f009b814 100644
--- a/src/libslic3r/GCode.hpp
+++ b/src/libslic3r/GCode.hpp
@@ -362,9 +362,12 @@ protected:
bool m_second_layer_things_done;
// Index of a last object copy extruded.
std::pair m_last_obj_copy;
- // Layer heights for colorprint - updated before the export and erased during the process
- // so no toolchange occurs twice.
- std::vector m_colorprint_heights;
+ /* Extensions for colorprint - now it's not a just color_print_heights,
+ * there can be some custom gcode.
+ * Updated before the export and erased during the process,
+ * so no toolchange occurs twice.
+ * */
+ std::vector m_custom_g_code_heights;
// Time estimators
GCodeTimeEstimator m_normal_time_estimator;
diff --git a/src/libslic3r/GCode/Analyzer.cpp b/src/libslic3r/GCode/Analyzer.cpp
index 3f0b8735f..f73f4b4ba 100644
--- a/src/libslic3r/GCode/Analyzer.cpp
+++ b/src/libslic3r/GCode/Analyzer.cpp
@@ -29,6 +29,9 @@ const std::string GCodeAnalyzer::Mm3_Per_Mm_Tag = "_ANALYZER_MM3_PER_MM:";
const std::string GCodeAnalyzer::Width_Tag = "_ANALYZER_WIDTH:";
const std::string GCodeAnalyzer::Height_Tag = "_ANALYZER_HEIGHT:";
const std::string GCodeAnalyzer::Color_Change_Tag = "_ANALYZER_COLOR_CHANGE";
+const std::string GCodeAnalyzer::Pause_Print_Tag = "_ANALYZER_PAUSE_PRINT";
+const std::string GCodeAnalyzer::Custom_Code_Tag = "_ANALYZER_CUSTOM_CODE";
+const std::string GCodeAnalyzer::End_Pause_Print_Or_Custom_Code_Tag = "_ANALYZER_END_PAUSE_PRINT_OR_CUSTOM_CODE";
const double GCodeAnalyzer::Default_mm3_per_mm = 0.0;
const float GCodeAnalyzer::Default_Width = 0.0f;
@@ -118,6 +121,8 @@ void GCodeAnalyzer::set_extruder_offsets(const GCodeAnalyzer::ExtruderOffsetsMap
void GCodeAnalyzer::set_extruders_count(unsigned int count)
{
m_extruders_count = count;
+ for (unsigned int i=0; i GCodeMovesList;
typedef std::map TypeToMovesMap;
typedef std::map ExtruderOffsetsMap;
+ typedef std::map ExtruderToColorMap;
private:
struct State
@@ -102,7 +106,7 @@ private:
float start_extrusion;
float position[Num_Axis];
float origin[Num_Axis];
- unsigned int cur_cp_color_id = 0;
+ unsigned int cp_color_counter = 0;
};
private:
@@ -113,6 +117,8 @@ private:
unsigned int m_extruders_count;
GCodeFlavor m_gcode_flavor;
+ ExtruderToColorMap m_extruder_color;
+
// The output of process_layer()
std::string m_process_output;
@@ -212,7 +218,13 @@ private:
void _process_height_tag(const std::string& comment, size_t pos);
// Processes color change tag
- void _process_color_change_tag();
+ void _process_color_change_tag(int extruder);
+
+ // Processes pause print and custom gcode tag
+ void _process_pause_print_or_custom_code_tag();
+
+ // Processes new layer tag
+ void _process_end_pause_print_or_custom_code_tag();
void _set_units(EUnits units);
EUnits _get_units() const;
diff --git a/src/libslic3r/GCode/PreviewData.cpp b/src/libslic3r/GCode/PreviewData.cpp
index 53c13a2f2..e7475089a 100644
--- a/src/libslic3r/GCode/PreviewData.cpp
+++ b/src/libslic3r/GCode/PreviewData.cpp
@@ -379,7 +379,8 @@ std::string GCodePreviewData::get_legend_title() const
return "";
}
-GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::vector& tool_colors, const std::vector*double*/std::pair>& cp_values) const
+GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::vector& tool_colors,
+ const std::vector& cp_items) const
{
struct Helper
{
@@ -455,31 +456,25 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
case Extrusion::ColorPrint:
{
const int color_cnt = (int)tool_colors.size()/4;
-
- const auto color_print_cnt = (int)cp_values.size();
- for (int i = color_print_cnt; i >= 0 ; --i)
+ const auto color_print_cnt = (int)cp_items.size();
+ if (color_print_cnt == 1) // means "Default print color"
{
- GCodePreviewData::Color color;
- ::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + (i % color_cnt) * 4), 4 * sizeof(float));
+ Color color;
+ ::memcpy((void*)color.rgba, (const void*)(tool_colors.data()), 4 * sizeof(float));
+
+ items.emplace_back(cp_items[0], color);
+ break;
+ }
+
+ if (color_cnt != color_print_cnt)
+ break;
+
+ for (int i = 0 ; i < color_print_cnt; ++i)
+ {
+ Color color;
+ ::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + i * 4), 4 * sizeof(float));
- if (color_print_cnt == 0) {
- items.emplace_back(Slic3r::I18N::translate(L("Default print color")), color);
- break;
- }
-
- std::string id_str = std::to_string(i + 1) + ": ";
-
- if (i == 0) {
- items.emplace_back(id_str + (boost::format(Slic3r::I18N::translate(L("up to %.2f mm"))) % cp_values[0].first).str(), color);
- break;
- }
- if (i == color_print_cnt) {
- items.emplace_back(id_str + (boost::format(Slic3r::I18N::translate(L("above %.2f mm"))) % cp_values[i - 1].second).str(), color);
- continue;
- }
-
-// items.emplace_back((boost::format(Slic3r::I18N::translate(L("%.2f - %.2f mm"))) % cp_values[i-1] % cp_values[i]).str(), color);
- items.emplace_back(id_str + (boost::format(Slic3r::I18N::translate(L("%.2f - %.2f mm"))) % cp_values[i - 1].second% cp_values[i].first).str(), color);
+ items.emplace_back(cp_items[i], color);
}
break;
}
diff --git a/src/libslic3r/GCode/PreviewData.hpp b/src/libslic3r/GCode/PreviewData.hpp
index 70b6edffd..725c0258d 100644
--- a/src/libslic3r/GCode/PreviewData.hpp
+++ b/src/libslic3r/GCode/PreviewData.hpp
@@ -237,7 +237,7 @@ public:
void set_extrusion_paths_colors(const std::vector& colors);
std::string get_legend_title() const;
- LegendItemsList get_legend_items(const std::vector& tool_colors, const std::vector*double*/std::pair>& cp_values) const;
+ LegendItemsList get_legend_items(const std::vector& tool_colors, const std::vector& cp_items) const;
// Return an estimate of the memory consumed by the time estimator.
size_t memory_used() const;
diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp
index 061c5bd50..ce3debfb5 100644
--- a/src/libslic3r/Model.cpp
+++ b/src/libslic3r/Model.cpp
@@ -41,6 +41,9 @@ Model& Model::assign_copy(const Model &rhs)
mo->set_model(this);
this->objects.emplace_back(mo);
}
+
+ // copy custom code per height
+ this->custom_gcode_per_height = rhs.custom_gcode_per_height;
return *this;
}
@@ -59,6 +62,9 @@ Model& Model::assign_copy(Model &&rhs)
for (ModelObject *model_object : this->objects)
model_object->set_model(this);
rhs.objects.clear();
+
+ // copy custom code per height
+ this->custom_gcode_per_height = rhs.custom_gcode_per_height;
return *this;
}
@@ -586,6 +592,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> Model::get_custom_tool_changes(double default_layer_height, size_t num_extruders) const
+{
+ std::vector> custom_tool_changes;
+ if (!custom_gcode_per_height.empty()) {
+ for (const CustomGCode& custom_gcode : custom_gcode_per_height)
+ if (custom_gcode.gcode == ExtruderChangeCode) {
+ 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..c2942a4ea 100644
--- a/src/libslic3r/Model.hpp
+++ b/src/libslic3r/Model.hpp
@@ -745,6 +745,37 @@ public:
ModelObjectPtrs objects;
// Wipe tower object.
ModelWipeTower wipe_tower;
+
+ // Extensions for color print
+ struct CustomGCode
+ {
+ CustomGCode(double height, const std::string& code, int extruder, const std::string& color) :
+ height(height), gcode(code), extruder(extruder), color(color) {}
+
+ 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 )&&
+ (other.color == this->color );
+ }
+ bool operator!=(const CustomGCode& other) const
+ {
+ return (other.height != this->height) ||
+ (other.gcode != this->gcode) ||
+ (other.extruder != this->extruder )||
+ (other.color != this->color );
+ }
+
+ 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::string color; // if gcode is equal to PausePrintCode,
+ // this field is used for save a short message shown on Printer display
+ };
+ std::vector custom_gcode_per_height;
// Default constructor assigns a new ID to the model.
Model() { assert(this->id().valid()); }
@@ -810,6 +841,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> 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 8b79f5367..0d2f65076 100644
--- a/src/libslic3r/Print.cpp
+++ b/src/libslic3r/Print.cpp
@@ -637,11 +637,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> &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 &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>::const_iterator begin() const { return m_ranges.cbegin(); }
@@ -689,6 +737,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));
@@ -780,6 +835,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> 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];
@@ -787,7 +845,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(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges);
+ // ys_FIXME_COLOR
+ // const_cast(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges);
+ const_cast(*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;
@@ -955,6 +1015,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 1649f3c04..098049f1d 100644
--- a/src/libslic3r/Print.hpp
+++ b/src/libslic3r/Print.hpp
@@ -370,6 +370,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]; }
@@ -412,6 +415,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/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index 5f5a861da..9f3bc0539 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -71,6 +71,12 @@ enum SLAPillarConnectionMode {
slapcmDynamic
};
+// ys_FIXME ! may be, it's not a best place
+// Additional Codes which can be set by user using DoubleSlider
+static const std::string ColorChangeCode = "M600";
+static const std::string PausePrintCode = "M601";
+static const std::string ExtruderChangeCode = "tool_change";
+
template<> inline const t_config_enum_values& ConfigOptionEnum::get_enum_values() {
static t_config_enum_values keys_map;
if (keys_map.empty()) {
diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp
index ae6023e7a..9362e6043 100644
--- a/src/libslic3r/ShortestPath.cpp
+++ b/src/libslic3r/ShortestPath.cpp
@@ -1333,6 +1333,82 @@ static inline std::pair minimum_crossover_cost(
return std::make_pair(cost_min, flip_min);
}
+static inline std::pair minimum_crossover_cost(
+ const std::vector &edges,
+ const std::pair &span1, const ConnectionCost &cost1,
+ const std::pair &span2, const ConnectionCost &cost2,
+ const std::pair &span3, const ConnectionCost &cost3,
+ const std::pair &span4, const ConnectionCost &cost4,
+ const double cost_current)
+{
+ auto connection_cost = [&edges](
+ const std::pair &span1, const ConnectionCost &cost1, bool reversed1, bool flipped1,
+ const std::pair &span2, const ConnectionCost &cost2, bool reversed2, bool flipped2,
+ const std::pair &span3, const ConnectionCost &cost3, bool reversed3, bool flipped3,
+ const std::pair &span4, const ConnectionCost &cost4, bool reversed4, bool flipped4) {
+ auto first_point = [&edges](const std::pair &span, bool flipped) { return flipped ? edges[span.first].p2 : edges[span.first].p1; };
+ auto last_point = [&edges](const std::pair &span, bool flipped) { return flipped ? edges[span.second - 1].p1 : edges[span.second - 1].p2; };
+ auto point = [first_point, last_point](const std::pair &span, bool start, bool flipped) { return start ? first_point(span, flipped) : last_point(span, flipped); };
+ auto cost = [](const ConnectionCost &acost, bool flipped) {
+ assert(acost.cost >= 0. && acost.cost_flipped >= 0.);
+ return flipped ? acost.cost_flipped : acost.cost;
+ };
+ // Ignore reversed single segment spans.
+ auto simple_span_ignore = [](const std::pair& span, bool reversed) {
+ return span.first + 1 == span.second && reversed;
+ };
+ assert(span1.first < span1.second);
+ assert(span2.first < span2.second);
+ assert(span3.first < span3.second);
+ assert(span4.first < span4.second);
+ return
+ simple_span_ignore(span1, reversed1) || simple_span_ignore(span2, reversed2) || simple_span_ignore(span3, reversed3) || simple_span_ignore(span4, reversed4) ?
+ // Don't perform unnecessary calculations simulating reversion of single segment spans.
+ std::numeric_limits::max() :
+ // Calculate the cost of reverting chains and / or flipping segment orientations.
+ cost(cost1, flipped1) + cost(cost2, flipped2) + cost(cost3, flipped3) + cost(cost4, flipped4) +
+ (point(span2, ! reversed2, flipped2) - point(span1, reversed1, flipped1)).norm() +
+ (point(span3, ! reversed3, flipped3) - point(span2, reversed2, flipped2)).norm() +
+ (point(span4, ! reversed4, flipped4) - point(span3, reversed3, flipped3)).norm();
+ };
+
+#ifndef NDEBUG
+ {
+ double c = connection_cost(span1, cost1, false, false, span2, cost2, false, false, span3, cost3, false, false, span4, cost4, false, false);
+ assert(std::abs(c - cost_current) < SCALED_EPSILON);
+ }
+#endif /* NDEBUG */
+
+ double cost_min = cost_current;
+ size_t flip_min = 0; // no flip, no improvement
+ for (size_t i = 0; i < (1 << 8); ++ i) {
+ // From the three combinations of 1,2,3 ordering, the other three are reversals of the first three.
+ size_t permutation = 0;
+ for (double c : {
+ (i == 0) ? cost_current :
+ connection_cost(span1, cost1, (i & 1) != 0, (i & (1 << 1)) != 0, span2, cost2, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span3, cost3, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span4, cost4, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0),
+ connection_cost(span1, cost1, (i & 1) != 0, (i & (1 << 1)) != 0, span2, cost2, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span4, cost4, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span3, cost3, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0),
+ connection_cost(span1, cost1, (i & 1) != 0, (i & (1 << 1)) != 0, span3, cost3, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span2, cost2, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span4, cost4, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0),
+ connection_cost(span1, cost1, (i & 1) != 0, (i & (1 << 1)) != 0, span3, cost3, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span4, cost4, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span2, cost2, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0),
+ connection_cost(span1, cost1, (i & 1) != 0, (i & (1 << 1)) != 0, span4, cost4, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span2, cost2, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span3, cost3, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0),
+ connection_cost(span1, cost1, (i & 1) != 0, (i & (1 << 1)) != 0, span4, cost4, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span3, cost3, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span2, cost2, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0),
+ connection_cost(span2, cost2, (i & 1) != 0, (i & (1 << 1)) != 0, span1, cost1, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span3, cost3, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span4, cost4, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0),
+ connection_cost(span2, cost2, (i & 1) != 0, (i & (1 << 1)) != 0, span1, cost1, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span4, cost4, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span3, cost3, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0),
+ connection_cost(span2, cost2, (i & 1) != 0, (i & (1 << 1)) != 0, span3, cost3, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span1, cost1, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span4, cost4, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0),
+ connection_cost(span2, cost2, (i & 1) != 0, (i & (1 << 1)) != 0, span4, cost4, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span1, cost1, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span3, cost3, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0),
+ connection_cost(span3, cost3, (i & 1) != 0, (i & (1 << 1)) != 0, span1, cost1, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span2, cost2, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span4, cost4, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0),
+ connection_cost(span3, cost3, (i & 1) != 0, (i & (1 << 1)) != 0, span2, cost2, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span1, cost1, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span4, cost4, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0)
+ }) {
+ if (c < cost_min) {
+ cost_min = c;
+ flip_min = i + (permutation << 8);
+ }
+ ++ permutation;
+ }
+ }
+ return std::make_pair(cost_min, flip_min);
+}
+
static inline void do_crossover(const std::vector &edges_in, std::vector &edges_out,
const std::pair &span1, const std::pair &span2, const std::pair &span3,
size_t i)
@@ -1374,6 +1450,79 @@ static inline void do_crossover(const std::vector &edges_in, std::vect
assert(edges_in.size() == edges_out.size());
}
+
+static inline void do_crossover(const std::vector &edges_in, std::vector &edges_out,
+ const std::pair &span1, const std::pair &span2, const std::pair &span3, const std::pair &span4,
+ size_t i)
+{
+ assert(edges_in.size() == edges_out.size());
+ auto do_it = [&edges_in, &edges_out](
+ const std::pair &span1, bool reversed1, bool flipped1,
+ const std::pair &span2, bool reversed2, bool flipped2,
+ const std::pair &span3, bool reversed3, bool flipped3,
+ const std::pair &span4, bool reversed4, bool flipped4) {
+ auto it_edges_out = edges_out.begin();
+ auto copy_span = [&edges_in, &edges_out, &it_edges_out](std::pair span, bool reversed, bool flipped) {
+ assert(span.first < span.second);
+ auto it = it_edges_out;
+ if (reversed)
+ std::reverse_copy(edges_in.begin() + span.first, edges_in.begin() + span.second, it_edges_out);
+ else
+ std::copy (edges_in.begin() + span.first, edges_in.begin() + span.second, it_edges_out);
+ it_edges_out += span.second - span.first;
+ if (reversed != flipped) {
+ for (; it != it_edges_out; ++ it)
+ it->flip();
+ }
+ };
+ copy_span(span1, reversed1, flipped1);
+ copy_span(span2, reversed2, flipped2);
+ copy_span(span3, reversed3, flipped3);
+ copy_span(span4, reversed4, flipped4);
+ };
+ switch (i >> 8) {
+ case 0:
+ assert(i != 0); // otherwise it would be a no-op
+ do_it(span1, (i & 1) != 0, (i & (1 << 1)) != 0, span2, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span3, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span4, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0);
+ break;
+ case 1:
+ do_it(span1, (i & 1) != 0, (i & (1 << 1)) != 0, span2, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span4, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span3, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0);
+ break;
+ case 2:
+ do_it(span1, (i & 1) != 0, (i & (1 << 1)) != 0, span3, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span2, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span4, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0);
+ break;
+ case 3:
+ do_it(span1, (i & 1) != 0, (i & (1 << 1)) != 0, span3, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span4, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span2, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0);
+ break;
+ case 4:
+ do_it(span1, (i & 1) != 0, (i & (1 << 1)) != 0, span4, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span2, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span3, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0);
+ break;
+ case 5:
+ do_it(span1, (i & 1) != 0, (i & (1 << 1)) != 0, span4, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span3, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span2, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0);
+ break;
+ case 6:
+ do_it(span2, (i & 1) != 0, (i & (1 << 1)) != 0, span1, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span3, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span4, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0);
+ break;
+ case 7:
+ do_it(span2, (i & 1) != 0, (i & (1 << 1)) != 0, span1, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span4, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span3, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0);
+ break;
+ case 8:
+ do_it(span2, (i & 1) != 0, (i & (1 << 1)) != 0, span3, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span1, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span4, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0);
+ break;
+ case 9:
+ do_it(span2, (i & 1) != 0, (i & (1 << 1)) != 0, span4, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span1, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span3, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0);
+ break;
+ case 10:
+ do_it(span3, (i & 1) != 0, (i & (1 << 1)) != 0, span1, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span2, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span4, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0);
+ break;
+ default:
+ assert((i >> 8) == 11);
+ do_it(span3, (i & 1) != 0, (i & (1 << 1)) != 0, span2, (i & (1 << 2)) != 0, (i & (1 << 3)) != 0, span1, (i & (1 << 4)) != 0, (i & (1 << 5)) != 0, span4, (i & (1 << 6)) != 0, (i & (1 << 7)) != 0);
+ break;
+ }
+ assert(edges_in.size() == edges_out.size());
+}
+
static inline void reorder_by_two_exchanges_with_segment_flipping(std::vector &edges)
{
if (edges.size() < 2)
@@ -1448,6 +1597,90 @@ static inline void reorder_by_two_exchanges_with_segment_flipping(std::vector &edges)
+{
+ if (edges.size() < 3) {
+ reorder_by_two_exchanges_with_segment_flipping(edges);
+ return;
+ }
+
+ std::vector connections(edges.size());
+ std::vector edges_tmp(edges);
+ std::vector> connection_lengths(edges.size() - 1, std::pair(0., 0));
+ std::vector connection_tried(edges.size(), false);
+ for (size_t iter = 0; iter < edges.size(); ++ iter) {
+ // Initialize connection costs and connection lengths.
+ for (size_t i = 1; i < edges.size(); ++ i) {
+ const FlipEdge &e1 = edges[i - 1];
+ const FlipEdge &e2 = edges[i];
+ ConnectionCost &c = connections[i];
+ c = connections[i - 1];
+ double l = (e2.p1 - e1.p2).norm();
+ c.cost += l;
+ c.cost_flipped += (e2.p2 - e1.p1).norm();
+ connection_lengths[i - 1] = std::make_pair(l, i);
+ }
+ std::sort(connection_lengths.begin(), connection_lengths.end(), [](const std::pair &l, const std::pair &r) { return l.first > r.first; });
+ std::fill(connection_tried.begin(), connection_tried.end(), false);
+ size_t crossover1_pos_final = std::numeric_limits::max();
+ size_t crossover2_pos_final = std::numeric_limits::max();
+ size_t crossover3_pos_final = std::numeric_limits::max();
+ size_t crossover_flip_final = 0;
+ for (const std::pair &first_crossover_candidate : connection_lengths) {
+ double longest_connection_length = first_crossover_candidate.first;
+ size_t longest_connection_idx = first_crossover_candidate.second;
+ connection_tried[longest_connection_idx] = true;
+ // Find the second crossover connection with the lowest total chain cost.
+ size_t crossover_pos_min = std::numeric_limits::max();
+ double crossover_cost_min = connections.back().cost;
+ for (size_t j = 1; j < connections.size(); ++ j)
+ if (! connection_tried[j]) {
+ for (size_t k = j + 1; k < connections.size(); ++ k)
+ if (! connection_tried[k]) {
+ size_t a = longest_connection_idx;
+ size_t b = j;
+ size_t c = k;
+ if (a > c)
+ std::swap(a, c);
+ if (a > b)
+ std::swap(a, b);
+ if (b > c)
+ std::swap(b, c);
+ std::pair cost_and_flip = minimum_crossover_cost(edges,
+ std::make_pair(size_t(0), a), connections[a - 1], std::make_pair(a, b), connections[b - 1] - connections[a],
+ std::make_pair(b, c), connections[c - 1] - connections[b], std::make_pair(c, edges.size()), connections.back() - connections[c],
+ connections.back().cost);
+ if (cost_and_flip.second > 0 && cost_and_flip.first < crossover_cost_min) {
+ crossover_cost_min = cost_and_flip.first;
+ crossover1_pos_final = a;
+ crossover2_pos_final = b;
+ crossover3_pos_final = c;
+ crossover_flip_final = cost_and_flip.second;
+ assert(crossover_cost_min < connections.back().cost + EPSILON);
+ }
+ }
+ }
+ if (crossover_flip_final > 0) {
+ // The cost of the chain with the proposed two crossovers has a lower total cost than the current chain. Apply the crossover.
+ break;
+ } else {
+ // Continue with another long candidate edge.
+ }
+ }
+ if (crossover_flip_final > 0) {
+ // Pair of cross over positions and flip / reverse constellation has been found, which improves the total cost of the connection.
+ // Perform a crossover.
+ do_crossover(edges, edges_tmp, std::make_pair(size_t(0), crossover1_pos_final), std::make_pair(crossover1_pos_final, crossover2_pos_final),
+ std::make_pair(crossover2_pos_final, crossover3_pos_final), std::make_pair(crossover3_pos_final, edges.size()), crossover_flip_final);
+ edges.swap(edges_tmp);
+ } else {
+ // No valid pair of cross over positions was found improving the total cost. Giving up.
+ break;
+ }
+ }
+}
+
// Flip the sequences of polylines to lower the total length of connecting lines.
static inline void improve_ordering_by_two_exchanges_with_segment_flipping(Polylines &polylines, bool fixed_start)
{
@@ -1471,7 +1704,11 @@ static inline void improve_ordering_by_two_exchanges_with_segment_flipping(Polyl
edges.reserve(polylines.size());
std::transform(polylines.begin(), polylines.end(), std::back_inserter(edges),
[&polylines](const Polyline &pl){ return FlipEdge(pl.first_point().cast(), pl.last_point().cast(), &pl - polylines.data()); });
+#if 1
reorder_by_two_exchanges_with_segment_flipping(edges);
+#else
+ reorder_by_three_exchanges_with_segment_flipping(edges);
+#endif
Polylines out;
out.reserve(polylines.size());
for (const FlipEdge &edge : edges) {
diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp
index 81e9bf0c5..9af6048ab 100644
--- a/src/libslic3r/Slicing.cpp
+++ b/src/libslic3r/Slicing.cpp
@@ -267,7 +267,7 @@ std::vector layer_height_profile_adaptive(
int current_facet = 0;
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
while (slice_z <= slicing_params.object_print_z_height()) {
- double height = 999.0;
+ double height = slicing_params.max_layer_height;
#else
double height = slicing_params.first_object_layer_height;
while ((slice_z - height) <= slicing_params.object_print_z_height()) {
diff --git a/src/libslic3r/SlicingAdaptive.cpp b/src/libslic3r/SlicingAdaptive.cpp
index bc02a89c1..6776d8d71 100644
--- a/src/libslic3r/SlicingAdaptive.cpp
+++ b/src/libslic3r/SlicingAdaptive.cpp
@@ -94,8 +94,8 @@ float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet
continue;
// compute cusp-height for this facet and store minimum of all heights
float normal_z = m_face_normal_z[ordered_id];
- height = std::min(height, (normal_z == 0.f) ? 9999.f : std::abs(cusp_value / normal_z));
- }
+ height = std::min(height, (normal_z == 0.0f) ? (float)m_slicing_params.max_layer_height : std::abs(cusp_value / normal_z));
+ }
}
// lower height limit due to printer capabilities
@@ -115,13 +115,13 @@ float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet
// Compute cusp-height for this facet and check against height.
float normal_z = m_face_normal_z[ordered_id];
- float cusp = (normal_z == 0) ? 9999 : abs(cusp_value / normal_z);
-
+ float cusp = (normal_z == 0.0f) ? (float)m_slicing_params.max_layer_height : abs(cusp_value / normal_z);
+
float z_diff = zspan.first - z;
// handle horizontal facets
- if (m_face_normal_z[ordered_id] > 0.999) {
- // Slic3r::debugf "cusp computation, height is reduced from %f", $height;
+ if (normal_z > 0.999f) {
+ // Slic3r::debugf "cusp computation, height is reduced from %f", $height;
height = z_diff;
// Slic3r::debugf "to %f due to near horizontal facet\n", $height;
} else if (cusp > z_diff) {
@@ -139,7 +139,7 @@ float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet
// lower height limit due to printer capabilities again
height = std::max(height, float(m_slicing_params.min_layer_height));
}
-
+
// Slic3r::debugf "cusp computation, layer-bottom at z:%f, cusp_value:%f, resulting layer height:%f\n", unscale $z, $cusp_value, $height;
return height;
}
diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt
index 6dcbc60b7..7f06cac29 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.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp
index 570841a45..9108fd196 100644
--- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp
+++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp
@@ -33,6 +33,7 @@
#include
#include
#include "I18N.hpp"
+#include "GUI.hpp"
namespace Slic3r {
@@ -92,7 +93,15 @@ void BackgroundSlicingProcess::process_fff()
#else
m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data);
#endif // ENABLE_THUMBNAIL_GENERATOR
- if (this->set_step_started(bspsGCodeFinalize)) {
+
+ if (m_fff_print->model().custom_gcode_per_height != GUI::wxGetApp().model().custom_gcode_per_height) {
+ GUI::wxGetApp().model().custom_gcode_per_height = m_fff_print->model().custom_gcode_per_height;
+ // #ys_FIXME : controll text
+ GUI::show_info(nullptr, _(L("To except of redundant tool manipulation, \n"
+ "Color change(s) for unused extruder(s) was(were) deleted")), _(L("Info")));
+ }
+
+ if (this->set_step_started(bspsGCodeFinalize)) {
if (! m_export_path.empty()) {
//FIXME localize the messages
// Perform the final post-processing of the export path by applying the print statistics over the file name.
@@ -108,7 +117,7 @@ void BackgroundSlicingProcess::process_fff()
m_print->set_status(100, _utf8(L("Slicing complete")));
}
this->set_step_done(bspsGCodeFinalize);
- }
+ }
}
#if ENABLE_THUMBNAIL_GENERATOR
@@ -139,8 +148,8 @@ void BackgroundSlicingProcess::process_sla()
if (m_thumbnail_cb != nullptr)
{
ThumbnailsList thumbnails;
- m_thumbnail_cb(thumbnails, current_print()->full_print_config().option("thumbnails")->values, true, true, false);
-// m_thumbnail_cb(thumbnails, current_print()->full_print_config().option("thumbnails")->values, true, false, false); // renders also supports and pad
+ m_thumbnail_cb(thumbnails, current_print()->full_print_config().option("thumbnails")->values, true, true, true);
+// m_thumbnail_cb(thumbnails, current_print()->full_print_config().option("thumbnails")->values, true, false, true); // renders also supports and pad
for (const ThumbnailData& data : thumbnails)
{
if (data.is_valid())
@@ -464,8 +473,8 @@ void BackgroundSlicingProcess::prepare_upload()
if (m_thumbnail_cb != nullptr)
{
ThumbnailsList thumbnails;
- m_thumbnail_cb(thumbnails, current_print()->full_print_config().option("thumbnails")->values, true, true, false);
-// m_thumbnail_cb(thumbnails, current_print()->full_print_config().option("thumbnails")->values, true, false, false); // renders also supports and pad
+ m_thumbnail_cb(thumbnails, current_print()->full_print_config().option("thumbnails")->values, true, true, true);
+// m_thumbnail_cb(thumbnails, current_print()->full_print_config().option("thumbnails")->values, true, false, true); // renders also supports and pad
for (const ThumbnailData& data : thumbnails)
{
if (data.is_valid())
diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp
index 984686e35..a66dcf39c 100644
--- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp
+++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp
@@ -132,6 +132,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..a850ac192
--- /dev/null
+++ b/src/slic3r/GUI/ExtruderSequenceDialog.cpp
@@ -0,0 +1,235 @@
+#include "ExtruderSequenceDialog.hpp"
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#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);
+
+ auto ID_RADIO_BUTTON = wxWindow::NewControlId(1);
+
+ wxRadioButton* rb_by_layers = new wxRadioButton(this, ID_RADIO_BUTTON, "", wxDefaultPosition, wxDefaultSize, wxRB_GROUP);
+ rb_by_layers->Bind(wxEVT_RADIOBUTTON, [this](wxCommandEvent& event) { m_sequence.is_mm_intervals = false; });
+ rb_by_layers->SetValue(!m_sequence.is_mm_intervals);
+
+ 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;
+ }
+
+ int val = wxAtoi(str);
+ if (val < 1) {
+ m_interval_by_layers->SetValue("1");
+ val = 1;
+ }
+
+ if (m_sequence.interval_by_layers == val)
+ return;
+
+ m_sequence.interval_by_layers = val;
+
+ 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, ID_RADIO_BUTTON, "");
+ 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, wxTE_PROCESS_ENTER);
+
+ auto change_value = [this]()
+ {
+ 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)
+ val = 3.0; // default value
+
+ if (fabs(m_sequence.interval_by_layers - val) < 0.001)
+ return;
+
+ m_sequence.interval_by_mm = val;
+ };
+
+ m_interval_by_mm->Bind(wxEVT_TEXT, [this, rb_by_mm](wxEvent&)
+ {
+ m_sequence.is_mm_intervals = true;
+ rb_by_mm->SetValue(true);
+ });
+
+ m_interval_by_mm->Bind(wxEVT_KILL_FOCUS, [this, change_value](wxFocusEvent& event)
+ {
+ change_value();
+ event.Skip();
+ });
+
+ m_interval_by_mm->Bind(wxEVT_TEXT_ENTER, [this, change_value](wxEvent&)
+ {
+ change_value();
+ });
+
+ 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);
+
+ /* For this moment min sizes for dialog and its sizer are calculated.
+ * If we left them, it can cause a problem with layouts during deleting of extruders
+ */
+ if (m_sequence.extruders.size()>1)
+ {
+ wxSize sz = wxSize(-1, 10 * em);
+ SetMinSize(sz);
+ GetSizer()->SetMinSize(sz);
+ }
+}
+
+void ExtruderSequenceDialog::apply_extruder_sequence()
+{
+ 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(15*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, 0, wxALIGN_CENTER_VERTICAL);
+ m_extruders_grid_sizer->Add(del_btn, 0, wxALIGN_CENTER_VERTICAL);
+ m_extruders_grid_sizer->Add(add_btn, 0, wxALIGN_CENTER_VERTICAL);
+ }
+ m_extruders_grid_sizer->ShowItems(true); // show items hidden in apply_extruder_selector()
+
+ Fit();
+ Refresh();
+}
+
+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 693204435..475a81ad8 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -22,6 +22,7 @@
#include "slic3r/GUI/PresetBundle.hpp"
#include "slic3r/GUI/Tab.hpp"
#include "slic3r/GUI/GUI_Preview.hpp"
+
#include "GUI_App.hpp"
#include "GUI_ObjectList.hpp"
#include "GUI_ObjectManipulation.hpp"
@@ -277,9 +278,9 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const
imgui.text(_(L("Cusp (mm)")));
ImGui::SameLine();
float widget_align = ImGui::GetCursorPosX();
- ImGui::PushItemWidth(120.0f);
- m_adaptive_cusp = std::min(m_adaptive_cusp, (float)m_slicing_parameters->max_layer_height);
- ImGui::SliderFloat("", &m_adaptive_cusp, 0.0f, (float)m_slicing_parameters->max_layer_height, "%.2f");
+ ImGui::PushItemWidth(imgui.get_style_scaling() * 120.0f);
+ m_adaptive_cusp = clamp((float)m_slicing_parameters->min_layer_height, (float)m_slicing_parameters->max_layer_height, m_adaptive_cusp);
+ ImGui::SliderFloat("", &m_adaptive_cusp, (float)m_slicing_parameters->min_layer_height, (float)m_slicing_parameters->max_layer_height, "%.2f");
ImGui::Separator();
if (imgui.button(_(L("Smooth"))))
@@ -289,8 +290,8 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const
ImGui::SetCursorPosX(text_align);
imgui.text(_(L("Radius")));
ImGui::SameLine();
- ImGui::PushItemWidth(120.0f);
ImGui::SetCursorPosX(widget_align);
+ ImGui::PushItemWidth(imgui.get_style_scaling() * 120.0f);
int radius = (int)m_smooth_params.radius;
if (ImGui::SliderInt("##1", &radius, 1, 10))
m_smooth_params.radius = (unsigned int)radius;
@@ -298,8 +299,8 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const
ImGui::SetCursorPosX(text_align);
imgui.text(_(L("Keep min")));
ImGui::SameLine();
- ImGui::PushItemWidth(120.0f);
ImGui::SetCursorPosX(widget_align);
+ ImGui::PushItemWidth(imgui.get_style_scaling() * 120.0f);
imgui.checkbox("##2", m_smooth_params.keep_min);
ImGui::Separator();
@@ -411,6 +412,35 @@ bool GLCanvas3D::LayersEditing::is_initialized() const
return m_shader.is_initialized();
}
+std::string GLCanvas3D::LayersEditing::get_tooltip(const GLCanvas3D& canvas) const
+{
+ std::string ret;
+ if (m_enabled && (m_layer_height_profile.size() >= 4))
+ {
+ float z = get_cursor_z_relative(canvas);
+ if (z != -1000.0f)
+ {
+ z *= m_object_max_z;
+
+ float h = 0.0f;
+ for (size_t i = m_layer_height_profile.size() - 2; i >= 2; i -= 2)
+ {
+ float zi = m_layer_height_profile[i];
+ float zi_1 = m_layer_height_profile[i - 2];
+ if ((zi_1 <= z) && (z <= zi))
+ {
+ float dz = zi - zi_1;
+ h = (dz != 0.0f) ? lerp(m_layer_height_profile[i - 1], m_layer_height_profile[i + 1], (z - zi_1) / dz) : m_layer_height_profile[i + 1];
+ break;
+ }
+ }
+ if (h > 0.0f)
+ ret = std::to_string(h);
+ }
+ }
+ return ret;
+}
+
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const
{
@@ -659,7 +689,7 @@ void GLCanvas3D::LayersEditing::accept_changes(GLCanvas3D& canvas)
{
if (last_object_id >= 0) {
if (m_layer_height_profile_modified) {
- wxGetApp().plater()->take_snapshot(_(L("Layers heights")));
+ wxGetApp().plater()->take_snapshot(_(L("Layer height profile-Manual edit")));
const_cast(m_model_object)->layer_height_profile = m_layer_height_profile;
canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
}
@@ -967,47 +997,125 @@ GLCanvas3D::LegendTexture::LegendTexture()
{
}
-void GLCanvas3D::LegendTexture::fill_color_print_legend_values(const GCodePreviewData& preview_data, const GLCanvas3D& canvas,
- std::vector>& cp_legend_values)
+void GLCanvas3D::LegendTexture::fill_color_print_legend_items( const GLCanvas3D& canvas,
+ const std::vector& colors_in,
+ std::vector& colors,
+ std::vector& cp_legend_items)
{
- if (preview_data.extrusion.view_type == GCodePreviewData::Extrusion::ColorPrint &&
- wxGetApp().extruders_edited_cnt() == 1) // show color change legend only for single-material presets
+ std::vector custom_gcode_per_height = wxGetApp().plater()->model().custom_gcode_per_height;
+
+ const int extruders_cnt = wxGetApp().extruders_edited_cnt();
+ if (extruders_cnt == 1)
{
- auto& config = wxGetApp().preset_bundle->project_config;
- const std::vector& color_print_values = config.option("colorprint_heights")->values;
-
- if (!color_print_values.empty()) {
- std::vector print_zs = canvas.get_current_print_zs(true);
- for (auto cp_value : color_print_values)
- {
- auto lower_b = std::lower_bound(print_zs.begin(), print_zs.end(), cp_value - 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(previous_z, current_z));
- }
+ if (custom_gcode_per_height.empty()) {
+ cp_legend_items.push_back(I18N::translate_utf8(L("Default print color")));
+ colors = colors_in;
+ return;
}
+ std::vector> cp_values;
+
+ std::vector print_zs = canvas.get_current_print_zs(true);
+ for (auto custom_code : custom_gcode_per_height)
+ {
+ if (custom_code.gcode != ColorChangeCode)
+ continue;
+ 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_values.empty() ||
+ !(cp_values.back().first == previous_z && cp_values.back().second == current_z))
+ cp_values.push_back(std::pair(previous_z, current_z));
+ }
+
+ const auto items_cnt = (int)cp_values.size();
+ if (items_cnt == 0) // There is no one color change, but there is/are some pause print or custom Gcode
+ {
+ cp_legend_items.push_back(I18N::translate_utf8(L("Default print color")));
+ cp_legend_items.push_back(I18N::translate_utf8(L("Pause print or custom G-code")));
+ colors = colors_in;
+ return;
+ }
+
+ const int color_cnt = (int)colors_in.size() / 4;
+ colors.resize(colors_in.size(), 0.0);
+
+ ::memcpy((void*)(colors.data()), (const void*)(colors_in.data() + (color_cnt - 1) * 4), 4 * sizeof(float));
+ cp_legend_items.push_back(I18N::translate_utf8(L("Pause print or custom G-code")));
+ size_t color_pos = 4;
+
+ for (int i = items_cnt; i >= 0; --i, color_pos+=4)
+ {
+ // update colors for color print item
+ ::memcpy((void*)(colors.data() + color_pos), (const void*)(colors_in.data() + i * 4), 4 * sizeof(float));
+
+ // create label for color print item
+ std::string id_str = std::to_string(i + 1) + ": ";
+
+ if (i == 0) {
+ cp_legend_items.push_back(id_str + (boost::format(I18N::translate_utf8(L("up to %.2f mm"))) % cp_values[0].first).str());
+ break;
+ }
+ if (i == items_cnt) {
+ cp_legend_items.push_back(id_str + (boost::format(I18N::translate_utf8(L("above %.2f mm"))) % cp_values[i - 1].second).str());
+ continue;
+ }
+
+ cp_legend_items.push_back(id_str + (boost::format(I18N::translate_utf8(L("%.2f - %.2f mm"))) % cp_values[i - 1].second % cp_values[i].first).str());
+ }
+ }
+ else
+ {
+ // colors = colors_in;
+ const int color_cnt = (int)colors_in.size() / 4;
+ colors.resize(colors_in.size(), 0.0);
+
+ ::memcpy((void*)(colors.data()), (const void*)(colors_in.data()), 4 * extruders_cnt * sizeof(float));
+ size_t color_pos = 4 * extruders_cnt;
+ size_t color_in_pos = 4 * (color_cnt - 1);
+
+ for (unsigned int i = 0; i < extruders_cnt; ++i)
+ cp_legend_items.push_back((boost::format(I18N::translate_utf8(L("Extruder %d"))) % (i + 1)).str());
+
+ ::memcpy((void*)(colors.data() + color_pos), (const void*)(colors_in.data() + color_in_pos), 4 * sizeof(float));
+ color_pos += 4;
+ color_in_pos -= 4;
+ cp_legend_items.push_back(I18N::translate_utf8(L("Pause print or custom G-code")));
+
+ int cnt = custom_gcode_per_height.size();
+ for (int i = cnt-1; i >= 0; --i)
+ if (custom_gcode_per_height[i].gcode == ColorChangeCode) {
+ ::memcpy((void*)(colors.data() + color_pos), (const void*)(colors_in.data() + color_in_pos), 4 * sizeof(float));
+ color_pos += 4;
+ color_in_pos -= 4;
+ cp_legend_items.push_back((boost::format(I18N::translate_utf8(L("Color change for Extruder %d at %.2f mm"))) % custom_gcode_per_height[i].extruder % custom_gcode_per_height[i].height).str());
+ }
}
}
-bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, const std::vector& tool_colors, const GLCanvas3D& canvas, bool compress)
+bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, const std::vector& tool_colors_in, const GLCanvas3D& canvas, bool compress)
{
reset();
// collects items to render
auto title = _(preview_data.get_legend_title());
- std::vector> cp_legend_values;
- fill_color_print_legend_values(preview_data, canvas, cp_legend_values);
+ std::vector cp_legend_items;
+ std::vector cp_colors;
- const GCodePreviewData::LegendItemsList& items = preview_data.get_legend_items(tool_colors, cp_legend_values);
+ if (preview_data.extrusion.view_type == GCodePreviewData::Extrusion::ColorPrint)
+ {
+ cp_legend_items.reserve(cp_colors.size());
+ fill_color_print_legend_items(canvas, tool_colors_in, cp_colors, cp_legend_items);
+ }
+
+ const std::vector& tool_colors = preview_data.extrusion.view_type == GCodePreviewData::Extrusion::ColorPrint ? cp_colors : tool_colors_in;
+ const GCodePreviewData::LegendItemsList& items = preview_data.get_legend_items(tool_colors, cp_legend_items);
unsigned int items_count = (unsigned int)items.size();
if (items_count == 0)
@@ -1559,6 +1667,7 @@ bool GLCanvas3D::is_layers_editing_allowed() const
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
void GLCanvas3D::reset_layer_height_profile()
{
+ wxGetApp().plater()->take_snapshot(_(L("Layer height profile-Reset")));
m_layers_editing.reset_layer_height_profile(*this);
m_layers_editing.state = LayersEditing::Completed;
m_dirty = true;
@@ -1566,6 +1675,7 @@ void GLCanvas3D::reset_layer_height_profile()
void GLCanvas3D::adaptive_layer_height_profile(float cusp)
{
+ wxGetApp().plater()->take_snapshot(_(L("Layer height profile-Adaptive")));
m_layers_editing.adaptive_layer_height_profile(*this, cusp);
m_layers_editing.state = LayersEditing::Completed;
m_dirty = true;
@@ -1573,6 +1683,7 @@ void GLCanvas3D::adaptive_layer_height_profile(float cusp)
void GLCanvas3D::smooth_layer_height_profile(const HeightProfileSmoothingParams& smoothing_params)
{
+ wxGetApp().plater()->take_snapshot(_(L("Layer height profile-Smooth all")));
m_layers_editing.smooth_layer_height_profile(*this, smoothing_params);
m_layers_editing.state = LayersEditing::Completed;
m_dirty = true;
@@ -2408,7 +2519,7 @@ void GLCanvas3D::load_sla_preview()
}
}
-void GLCanvas3D::load_preview(const std::vector& str_tool_colors, const std::vector& color_print_values)
+void GLCanvas3D::load_preview(const std::vector& str_tool_colors, const std::vector& color_print_values)
{
const Print *print = this->fff_print();
if (print == nullptr)
@@ -3136,6 +3247,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
if ((m_layers_editing.state != LayersEditing::Unknown) && (layer_editing_object_idx != -1))
{
+ set_tooltip("");
if (m_layers_editing.state == LayersEditing::Editing)
_perform_layer_editing_action(&evt);
}
@@ -3245,6 +3357,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_mouse.position = pos.cast();
std::string tooltip = "";
+ if (tooltip.empty())
+ tooltip = m_layers_editing.get_tooltip(*this);
+
if (tooltip.empty())
tooltip = m_gizmos.get_tooltip();
@@ -3810,7 +3925,7 @@ static void render_volumes_in_thumbnail(Shader& shader, const GLVolumePtrs& volu
camera.apply_projection(box);
if (transparent_background)
- glsafe(::glClearColor(1.0f, 1.0f, 1.0f, 0.0f));
+ glsafe(::glClearColor(0.0f, 0.0f, 0.0f, 0.0f));
glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
glsafe(::glEnable(GL_DEPTH_TEST));
@@ -5210,7 +5325,7 @@ 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& str_tool_colors, const std::vector& color_print_values)
+void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const std::vector& str_tool_colors, const std::vector& color_print_values)
{
std::vector tool_colors = _parse_colors(str_tool_colors);
@@ -5222,11 +5337,14 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
bool has_infill;
bool has_support;
const std::vector* tool_colors;
- const std::vector* color_print_values;
+ bool is_single_material_print;
+ int extruders_cnt;
+ const std::vector* 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; }
@@ -5236,9 +5354,106 @@ 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);
+ 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 size_t color_print_color_idx_by_layer_idx_and_extruder(const size_t layer_idx, const int extruder) 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())
+ {
+ const std::string& code = it->gcode;
+ // pause print or custom Gcode
+ if (code == PausePrintCode ||
+ (code != ColorChangeCode && code != ExtruderChangeCode))
+ return number_tools()-1; // last color item is a gray color for pause print or custom G-code
+
+ // change tool (extruder)
+ if (code == ExtruderChangeCode)
+ return get_color_idx_for_tool_change(it, extruder);
+ // change color for current extruder
+ if (code == ColorChangeCode) {
+ int color_idx = get_color_idx_for_color_change(it, extruder);
+ if (color_idx >= 0)
+ return color_idx;
+ }
+ }
+
+ const Model::CustomGCode value(print_z + EPSILON, "", 0, "");
+ it = std::lower_bound(color_print_values->begin(), color_print_values->end(), value);
+ while (it != color_print_values->begin())
+ {
+ --it;
+ // change color for current extruder
+ if (it->gcode == ColorChangeCode) {
+ int color_idx = get_color_idx_for_color_change(it, extruder);
+ if (color_idx >= 0)
+ return color_idx;
+ }
+ // change tool (extruder)
+ if (it->gcode == ExtruderChangeCode)
+ return get_color_idx_for_tool_change(it, extruder);
+ }
+
+ return std::min(extruders_cnt - 1, std::max(extruder - 1, 0));;
+ }
+
+ private:
+ int get_m600_color_idx(std::vector::const_iterator it) const
+ {
+ int shift = 0;
+ while (it != color_print_values->begin()) {
+ --it;
+ if (it->gcode == ColorChangeCode)
+ shift++;
+ }
+ return extruders_cnt + shift;
+ }
+
+ int get_color_idx_for_tool_change(std::vector::const_iterator it, const int extruder) const
+ {
+ const int current_extruder = it->extruder == 0 ? extruder : it->extruder;
+ if (number_tools() == extruders_cnt + 1) // there is no one "M600"
+ return std::min(extruders_cnt - 1, std::max(current_extruder - 1, 0));
+
+ auto it_n = it;
+ while (it_n != color_print_values->begin()) {
+ --it_n;
+ if (it_n->gcode == ColorChangeCode && it_n->extruder == current_extruder)
+ return get_m600_color_idx(it_n);
+ }
+
+ return std::min(extruders_cnt - 1, std::max(current_extruder - 1, 0));
+ }
+
+ int get_color_idx_for_color_change(std::vector::const_iterator it, const int extruder) const
+ {
+ if (extruders_cnt == 1)
+ return get_m600_color_idx(it);
+
+ auto it_n = it;
+ bool is_tool_change = false;
+ while (it_n != color_print_values->begin()) {
+ --it_n;
+ if (it_n->gcode == ExtruderChangeCode) {
+ is_tool_change = true;
+ if (it_n->extruder == it->extruder || (it_n->extruder == 0 && it->extruder == extruder))
+ return get_m600_color_idx(it);
+ break;
+ }
+ }
+ if (!is_tool_change && it->extruder == extruder)
+ return get_m600_color_idx(it);
+
+ return -1;
+ }
+
} ctxt;
ctxt.has_perimeters = print_object.is_step_done(posPerimeters);
@@ -5246,6 +5461,8 @@ 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.extruders_cnt = wxGetApp().extruders_edited_cnt();
ctxt.shifted_copies = &print_object.copies();
@@ -5269,6 +5486,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;
@@ -5286,32 +5505,18 @@ 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(0, ctxt.layers.size(), grain_size),
- [&ctxt, &new_volume](const tbb::blocked_range& range) {
+ [&ctxt, &new_volume, is_selected_separate_extruder, this](const tbb::blocked_range& range) {
GLVolumePtrs vols;
std::vector 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() ?
- color_print_layer_to_glvolume[layer_idx - range.begin()] :
+ 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()?
+ ctxt.color_print_color_idx_by_layer_idx_and_extruder(layer_idx, extruder) :
ctxt.color_by_tool() ?
std::min(ctxt.number_tools() - 1, std::max(extruder - 1, 0)) :
feature
];
};
- if (ctxt.color_by_color_print()) {
- // Create a map from the layer index to a GLVolume, which is initialized with the correct layer span color.
- std::vector color_print_tool_to_glvolume(ctxt.number_tools(), -1);
- color_print_layer_to_glvolume.reserve(range.end() - range.begin());
- vols.reserve(ctxt.number_tools());
- for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
- int idx_tool = (int)ctxt.color_print_color_idx_by_layer_idx(idx_layer);
- if (color_print_tool_to_glvolume[idx_tool] == -1) {
- color_print_tool_to_glvolume[idx_tool] = (int)vols.size();
- vols.emplace_back(new_volume(ctxt.color_tool(idx_tool)));
- }
- color_print_layer_to_glvolume.emplace_back(color_print_tool_to_glvolume[idx_tool]);
- }
- }
- else if (ctxt.color_by_tool()) {
+ if (ctxt.color_by_color_print() || ctxt.color_by_tool()) {
for (size_t i = 0; i < ctxt.number_tools(); ++i)
vols.emplace_back(new_volume(ctxt.color_tool(i)));
}
@@ -5322,6 +5527,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);
@@ -5330,6 +5555,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));
@@ -5613,11 +5846,8 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
case GCodePreviewData::Extrusion::ColorPrint:
{
int color_cnt = (int)tool_colors.size() / 4;
+ int val = value > color_cnt ? color_cnt - 1 : value;
- int val = int(value);
- while (val >= color_cnt)
- val -= color_cnt;
-
GCodePreviewData::Color color;
::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + val * 4), 4 * sizeof(float));
@@ -5678,10 +5908,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> &filters = roles_filters[size_t(path.extrusion_role)];
auto key = std::make_pair(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 c673bc39b..ef91f8265 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -179,7 +179,7 @@ private:
float m_object_max_z;
// Owned by LayersEditing.
SlicingParameters *m_slicing_parameters;
- std::vector m_layer_height_profile;
+ std::vector m_layer_height_profile;
bool m_layer_height_profile_modified;
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
@@ -254,6 +254,8 @@ private:
float object_max_z() const { return m_object_max_z; }
+ std::string get_tooltip(const GLCanvas3D& canvas) const;
+
private:
bool is_initialized() const;
void generate_layer_height_texture();
@@ -382,8 +384,10 @@ private:
public:
LegendTexture();
- void fill_color_print_legend_values(const GCodePreviewData& preview_data, const GLCanvas3D& canvas,
- std::vector>& cp_legend_values);
+ void fill_color_print_legend_items(const GLCanvas3D& canvas,
+ const std::vector& colors_in,
+ std::vector& colors,
+ std::vector& cp_legend_items);
bool generate(const GCodePreviewData& preview_data, const std::vector& tool_colors, const GLCanvas3D& canvas, bool compress);
@@ -473,6 +477,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);
@@ -586,7 +591,7 @@ public:
void load_gcode_preview(const GCodePreviewData& preview_data, const std::vector& str_tool_colors);
void load_sla_preview();
- void load_preview(const std::vector& str_tool_colors, const std::vector& color_print_values);
+ void load_preview(const std::vector& str_tool_colors, const std::vector& color_print_values);
void bind_event_handlers();
void unbind_event_handlers();
@@ -625,6 +630,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:
@@ -748,7 +754,7 @@ 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& str_tool_colors,
- const std::vector& color_print_values);
+ const std::vector& color_print_values);
// Create 3D thick extrusion lines for wipe tower extrusions
void _load_wipe_tower_toolpaths(const std::vector& str_tool_colors);
diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp
index a88980a8d..aa10aca52 100644
--- a/src/slic3r/GUI/GUI_ObjectList.cpp
+++ b/src/slic3r/GUI/GUI_ObjectList.cpp
@@ -895,10 +895,6 @@ void ObjectList::extruder_editing()
if (!item || !(m_objects_model->GetItemType(item) & (itVolume | itObject)))
return;
- std::vector 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,29 +902,10 @@ void ObjectList::extruder_editing()
pos.x = GetColumn(colName)->GetWidth() + GetColumn(colPrint)->GetWidth() + 5;
pos.y -= GetTextExtent("m").y;
- if (!m_extruder_editor)
- m_extruder_editor = new wxBitmapComboBox(this, wxID_ANY, wxEmptyString, pos, size,
- 0, nullptr, wxCB_READONLY);
- else
- {
- m_extruder_editor->SetPosition(pos);
- m_extruder_editor->SetMinSize(size);
- m_extruder_editor->SetSize(size);
- m_extruder_editor->Clear();
- m_extruder_editor->Show();
- }
+ apply_extruder_selector(&m_extruder_editor, this, L("default"), pos, size);
- int i = 0;
- for (wxBitmap* bmp : icons) {
- if (i == 0) {
- m_extruder_editor->Append(_(L("default")), *bmp);
- ++i;
- }
-
- m_extruder_editor->Append(wxString::Format("%d", i), *bmp);
- ++i;
- }
m_extruder_editor->SetSelection(m_objects_model->GetExtruderNumber(item));
+ m_extruder_editor->Show();
auto set_extruder = [this]()
{
@@ -940,6 +917,7 @@ void ObjectList::extruder_editing()
m_objects_model->SetExtruder(m_extruder_editor->GetString(selection), item);
m_extruder_editor->Hide();
+ update_extruder_in_config(item);
};
// to avoid event propagation to other sidebar items
@@ -948,13 +926,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 937e3dbdc..36293525a 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 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..edb244b34 100644
--- a/src/slic3r/GUI/GUI_Preview.cpp
+++ b/src/slic3r/GUI/GUI_Preview.cpp
@@ -492,18 +492,23 @@ 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& layers_z, bool keep_z_range)
{
m_enabled = true;
+
update_double_slider(layers_z, keep_z_range);
m_double_slider_sizer->Show((size_t)0);
+
Layout();
}
@@ -560,12 +565,12 @@ 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;
- const wxString& choice = !config.option("colorprint_heights")->values.empty() &&
- wxGetApp().extruders_edited_cnt()==1 ?
+ const wxString& choice = !wxGetApp().plater()->model().custom_gcode_per_height.empty() /*&&
+ (wxGetApp().extruders_edited_cnt()==1 || !slice_completed) */?
_(L("Color Print")) :
config.option("wiping_volumes_matrix")->values.size() > 1 ?
_(L("Tool")) :
@@ -583,6 +588,8 @@ void Preview::update_view_type()
void Preview::create_double_slider()
{
m_slider = new DoubleSlider(this, wxID_ANY, 0, 0, 0, 100);
+ m_slider->EnableTickManipulation(wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptFFF);
+
m_double_slider_sizer->Add(m_slider, 0, wxEXPAND, 0);
// sizer, m_canvas_widget
@@ -592,10 +599,11 @@ void Preview::create_double_slider()
Bind(wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) {
- wxGetApp().preset_bundle->project_config.option("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 +636,24 @@ static int find_close_layer_idx(const std::vector& zs, double &z, double
return -1;
}
+void Preview::check_slider_values(std::vector& ticks_from_model,
+ const std::vector& 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& layers_z, bool keep_z_range)
{
// Save the initial slider span.
@@ -643,8 +669,8 @@ void Preview::update_double_slider(const std::vector& 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 &ticks_from_config = (wxGetApp().preset_bundle->project_config.option("colorprint_heights"))->values;
- check_slider_values(ticks_from_config, layers_z);
+ std::vector &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,33 +692,12 @@ void Preview::update_double_slider(const std::vector& layers_z, bool kee
}
m_slider->SetSelectionSpan(idx_low, idx_high);
- 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("nozzle_diameter")->values.size() > 1)
- color_print_enable = false;
- }
- m_slider->EnableTickManipulation(color_print_enable);
-}
-void Preview::check_slider_values(std::vector& ticks_from_config,
- const std::vector &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_config.size();
- ticks_from_config.erase(std::remove_if(ticks_from_config.begin(), ticks_from_config.end(),
- [layers_z](double val)
- {
- auto it = std::lower_bound(layers_z.begin(), layers_z.end(), val - DoubleSlider::epsilon());
- return it == layers_z.end();
- }),
- ticks_from_config.end());
- if (ticks_from_config.size() != old_size)
- m_schedule_background_process();
+ m_slider->EnableTickManipulation(color_print_enable);
+ m_slider->SetManipulationState(wxGetApp().extruders_edited_cnt());
}
void Preview::reset_double_slider()
@@ -753,7 +758,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,51 +781,30 @@ 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 colors;
- std::vector color_print_values = {};
+ std::vector 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();
- 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("colorprint_heights")->values;
- }
+ colors = wxGetApp().plater()->get_colors_for_color_print();
+ colors.push_back("#808080"); // gray color for pause print or custom G-code
+
+ if (!gcode_preview_data_valid)
+ 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) )
{
- const ConfigOptionStrings* extruders_opt = dynamic_cast(m_config->option("extruder_colour"));
- const ConfigOptionStrings* filamemts_opt = dynamic_cast(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);
- }
+ colors = wxGetApp().plater()->get_extruder_colors_from_plater_config();
color_print_values.clear();
}
if (IsShown())
{
+ m_canvas->set_selected_extruder(0);
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();
-
// Load the initial preview based on slices, not the final G-code.
m_canvas->load_preview(colors, color_print_values);
}
@@ -829,7 +813,7 @@ void Preview::load_print_as_fff(bool keep_z_range)
std::vector zs = m_canvas->get_current_print_zs(true);
if (zs.empty()) {
// all layers filtered out
- reset_sliders();
+ reset_sliders(true);
m_canvas_widget->Refresh();
} else
update_sliders(zs, keep_z_range);
@@ -860,7 +844,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..b0dac4223 100644
--- a/src/slic3r/GUI/GUI_Preview.hpp
+++ b/src/slic3r/GUI/GUI_Preview.hpp
@@ -5,6 +5,7 @@
#include "libslic3r/Point.hpp"
#include
+#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,7 @@ class Preview : public wxPanel
bool m_loaded;
bool m_enabled;
- DoubleSlider* m_slider {nullptr};
+ DoubleSlider* m_slider {nullptr};
public:
Preview(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config,
@@ -128,7 +130,7 @@ public:
void move_double_slider(wxKeyEvent& evt);
void edit_double_slider(wxKeyEvent& evt);
- void update_view_type();
+ void update_view_type(bool slice_completed);
bool is_loaded() const { return m_loaded; }
@@ -140,7 +142,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& layers_z, bool keep_z_range = false);
void on_size(wxSizeEvent& evt);
@@ -154,9 +156,9 @@ private:
// Create/Update/Reset double slider on 3dPreview
void create_double_slider();
+ void check_slider_values(std::vector &ticks_from_model,
+ const std::vector &layers_z);
void update_double_slider(const std::vector& layers_z, bool keep_z_range = false);
- void check_slider_values(std::vector &ticks_from_config,
- const std::vector &layers_z);
void reset_double_slider();
// update DoubleSlider after keyDown in canvas
void update_double_slider_from_canvas(wxKeyEvent& event);
diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp
index 39542e652..e69ef4857 100644
--- a/src/slic3r/GUI/Mouse3DController.cpp
+++ b/src/slic3r/GUI/Mouse3DController.cpp
@@ -333,17 +333,16 @@ void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsign
bool Mouse3DController::connect_device()
{
- static const long long DETECTION_TIME = 2; // seconds
+ static const long long DETECTION_TIME_MS = 2000; // seconds
if (is_device_connected())
return false;
// check time since last detection took place
- auto now = std::chrono::high_resolution_clock::now();
- if (std::chrono::duration_cast(now - m_last_time).count() < DETECTION_TIME)
+ if (std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - m_last_time).count() < DETECTION_TIME_MS)
return false;
- m_last_time = now;
+ m_last_time = std::chrono::high_resolution_clock::now();
// Enumerates devices
hid_device_info* devices = hid_enumerate(0, 0);
diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp
index 40c489853..cc03d4a24 100644
--- a/src/slic3r/GUI/Mouse3DController.hpp
+++ b/src/slic3r/GUI/Mouse3DController.hpp
@@ -131,7 +131,7 @@ class Mouse3DController
std::string m_device_str;
bool m_running;
bool m_settings_dialog;
- std::chrono::time_point m_last_time;
+ std::chrono::time_point m_last_time;
public:
Mouse3DController();
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 9155c3692..acfd5d322 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -2325,6 +2325,8 @@ std::vector Plater::priv::load_files(const std::vector& input_
// and place the loaded config over the base.
config += std::move(config_loaded);
}
+
+ this->model.custom_gcode_per_height = model.custom_gcode_per_height;
}
if (load_config)
@@ -2743,8 +2745,7 @@ 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("colorprint_heights")->values.clear();
+ model.custom_gcode_per_height.clear();
}
void Plater::priv::mirror(Axis axis)
@@ -2975,6 +2976,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.
@@ -3180,9 +3182,13 @@ void Plater::priv::reload_from_disk()
for (unsigned int idx : selected_volumes_idxs)
{
const GLVolume* v = selection.get_volume(idx);
- int o_idx = v->object_idx();
int v_idx = v->volume_idx();
- selected_volumes.push_back({ o_idx, v_idx });
+ if (v_idx >= 0)
+ {
+ int o_idx = v->object_idx();
+ if ((0 <= o_idx) && (o_idx < (int)model.objects.size()))
+ selected_volumes.push_back({ o_idx, v_idx });
+ }
}
std::sort(selected_volumes.begin(), selected_volumes.end());
selected_volumes.erase(std::unique(selected_volumes.begin(), selected_volumes.end()), selected_volumes.end());
@@ -4176,6 +4182,7 @@ void Plater::priv::undo_redo_to(std::vector::const_iterator
// Disable layer editing before the Undo / Redo jump.
if (!new_variable_layer_editing_active && view3D->is_layers_editing_enabled())
view3D->get_canvas3d()->force_main_toolbar_left_action(view3D->get_canvas3d()->get_main_toolbar_item_id("layersediting"));
+
// Make a copy of the snapshot, undo/redo could invalidate the iterator
const UndoRedo::Snapshot snapshot_copy = *it_snapshot;
// Do the jump in time.
@@ -4782,7 +4789,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)
@@ -5061,6 +5068,9 @@ std::vector Plater::get_extruder_colors_from_plater_config() const
return extruder_colors;
extruder_colors = (config->option("extruder_colour"))->values;
+ if (!wxGetApp().plater())
+ return extruder_colors;
+
const std::vector& filament_colours = (p->config->option("filament_colour"))->values;
for (size_t i = 0; i < extruder_colors.size(); ++i)
if (extruder_colors[i] == "" && i < filament_colours.size())
@@ -5069,6 +5079,17 @@ std::vector Plater::get_extruder_colors_from_plater_config() const
return extruder_colors;
}
+std::vector Plater::get_colors_for_color_print() const
+{
+ std::vector colors = get_extruder_colors_from_plater_config();
+
+ for (const Model::CustomGCode& code : p->model.custom_gcode_per_height)
+ if (code.gcode == ColorChangeCode)
+ colors.push_back(code.color);
+
+ return colors;
+}
+
wxString Plater::get_project_filename(const wxString& extension) const
{
return p->get_project_filename(extension);
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index 2f7ff3002..5c36dbf5e 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -223,6 +223,7 @@ public:
void on_activate();
const DynamicPrintConfig* get_plater_config() const;
std::vector get_extruder_colors_from_plater_config() const;
+ std::vector get_colors_for_color_print() const;
void update_object_menu();
diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp
index eb47fd208..90cfe6824 100644
--- a/src/slic3r/GUI/wxExtensions.cpp
+++ b/src/slic3r/GUI/wxExtensions.cpp
@@ -11,6 +11,7 @@
#include
#include
#include
+#include
#include
@@ -22,9 +23,11 @@
#include "I18N.hpp"
#include "GUI_Utils.hpp"
#include "PresetBundle.hpp"
+#include "ExtruderSequenceDialog.hpp"
#include "../Utils/MacDarkMode.hpp"
using Slic3r::GUI::from_u8;
+using Slic3r::GUI::into_u8;
wxDEFINE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent);
wxDEFINE_EVENT(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED, wxCommandEvent);
@@ -404,6 +407,23 @@ int em_unit(wxWindow* win)
return Slic3r::GUI::wxGetApp().em_unit();
}
+static float get_svg_scale_factor(wxWindow *win)
+{
+#ifdef __APPLE__
+ // Note: win->GetContentScaleFactor() is not used anymore here because it tends to
+ // return bogus results quite often (such as 1.0 on Retina or even 0.0).
+ // We're using the max scaling factor across all screens because it's very likely to be good enough.
+
+ static float max_scaling_factor = NAN;
+ if (std::isnan(max_scaling_factor)) {
+ max_scaling_factor = Slic3r::GUI::mac_max_scaling_factor();
+ }
+ return win != nullptr ? max_scaling_factor : 1.0f;
+#else
+ return 1.0f;
+#endif
+}
+
// If an icon has horizontal orientation (width > height) call this function with is_horizontal = true
wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name_in,
const int px_cnt/* = 16*/, const bool is_horizontal /* = false*/, const bool grayscale/* = false*/)
@@ -449,7 +469,7 @@ wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name_in,
Slic3r::GUI::BitmapCache* m_bitmap_cache = nullptr;
-/*static*/ std::vector get_extruder_color_icons()
+std::vector get_extruder_color_icons(bool thin_icon/* = false*/)
{
// Create the bitmap with color bars.
std::vector bmps;
@@ -465,16 +485,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 +505,62 @@ 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 bmps = get_extruder_color_icons();
+ std::vector 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 icons = get_extruder_color_icons(use_thin_icon);
+
+ 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();
+ }
+ if (first_item.empty())
+ (*ctrl)->Hide(); // to avoid unwanted rendering before layout (ExtruderSequenceDialog)
+
+ if (icons.empty() && !first_item.empty()) {
+ (*ctrl)->Append(_(first_item), wxNullBitmap);
+ return;
+ }
+
+ // For ObjectList we use short extruder name (just a number)
+ const bool use_full_item_name = dynamic_cast(parent) == nullptr;
+
+ 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(use_full_item_name ? wxString::Format("%s %d", str, i) : std::to_string(i), *bmp);
+ ++i;
+ }
+ (*ctrl)->SetSelection(0);
+}
+
+
// *****************************************************************************
// ----------------------------------------------------------------------------
// ObjectDataViewModelNode
@@ -2230,6 +2298,8 @@ DoubleSlider::DoubleSlider( wxWindow *parent,
if (!is_osx)
SetDoubleBuffered(true);// SetDoubleBuffered exists on Win and Linux/GTK, but is missing on OSX
+ const float scale_factor = get_svg_scale_factor(this);
+
m_bmp_thumb_higher = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "right_half_circle.png") : ScalableBitmap(this, "up_half_circle.png", 16, true));
m_bmp_thumb_lower = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "left_half_circle.png" ) : ScalableBitmap(this, "down_half_circle.png", 16, true));
m_thumb_size = m_bmp_thumb_lower.bmp().GetSize();
@@ -2240,16 +2310,19 @@ DoubleSlider::DoubleSlider( wxWindow *parent,
m_bmp_del_tick_off = ScalableBitmap(this, "colorchange_delete_off.png");
m_tick_icon_dim = m_bmp_add_tick_on.bmp().GetSize().x;
- m_bmp_one_layer_lock_on = ScalableBitmap(this, "one_layer_lock_on.png");
- m_bmp_one_layer_lock_off = ScalableBitmap(this, "one_layer_lock_off.png");
- m_bmp_one_layer_unlock_on = ScalableBitmap(this, "one_layer_unlock_on.png");
- m_bmp_one_layer_unlock_off = ScalableBitmap(this, "one_layer_unlock_off.png");
- m_lock_icon_dim = m_bmp_one_layer_lock_on.bmp().GetSize().x;
+ m_bmp_one_layer_lock_on = ScalableBitmap(this, "lock_closed");
+ m_bmp_one_layer_lock_off = ScalableBitmap(this, "lock_closed_f");
+ m_bmp_one_layer_unlock_on = ScalableBitmap(this, "lock_open");
+ m_bmp_one_layer_unlock_off = ScalableBitmap(this, "lock_open_f");
+ m_lock_icon_dim = int((float)m_bmp_one_layer_lock_on.bmp().GetSize().x / scale_factor);
m_bmp_revert = ScalableBitmap(this, "undo");
- m_revert_icon_dim = m_bmp_revert.bmp().GetSize().x;
+ m_revert_icon_dim = int((float)m_bmp_revert.bmp().GetSize().x / scale_factor);
+ m_bmp_cog = ScalableBitmap(this, "cog");
+ m_cog_icon_dim = int((float)m_bmp_cog.bmp().GetSize().x / scale_factor);
m_selection = ssUndef;
+ m_pause_print_msg = _utf8(L("Place bearings in slots and resume"));
// slider events
Bind(wxEVT_PAINT, &DoubleSlider::OnPaint, this);
@@ -2306,6 +2379,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();
@@ -2453,41 +2528,45 @@ double DoubleSlider::get_double_value(const SelectedSlider& selection)
return m_values[selection == ssLower ? m_lower_value : m_higher_value];
}
-std::vector DoubleSlider::GetTicksValues() const
+using t_custom_code = Slic3r::Model::CustomGCode;
+std::vector DoubleSlider::GetTicksValues() const
{
- std::vector