diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6b379c580f..218e53add6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -394,13 +394,27 @@ target_include_directories(cereal INTERFACE include)
# l10n
set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/localization")
-add_custom_target(pot
+add_custom_target(gettext_make_pot
COMMAND xgettext --keyword=L --keyword=L_CONTEXT:1,2c --keyword=_L_PLURAL:1,2 --add-comments=TRN --from-code=UTF-8 --debug
-f "${L10N_DIR}/list.txt"
-o "${L10N_DIR}/PrusaSlicer.pot"
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
COMMENT "Generate pot file from strings in the source tree"
)
+add_custom_target(gettext_po_to_mo
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+ COMMENT "Generate localization po files (binary) from mo files (texts)"
+)
+file(GLOB L10N_PO_FILES "${L10N_DIR}/*/PrusaSlicer*.po")
+foreach(po_file ${L10N_PO_FILES})
+ GET_FILENAME_COMPONENT(po_dir "${po_file}" DIRECTORY)
+ SET(mo_file "${po_dir}/PrusaSlicer.mo")
+ add_custom_command(
+ TARGET gettext_po_to_mo PRE_BUILD
+ COMMAND msgfmt ARGS -o ${mo_file} ${po_file}
+ DEPENDS ${po_file}
+ )
+endforeach()
find_package(NLopt 1.4 REQUIRED)
diff --git a/doc/How to build - Windows.md b/doc/How to build - Windows.md
index b8829047bf..c2dc31ce31 100644
--- a/doc/How to build - Windows.md
+++ b/doc/How to build - Windows.md
@@ -120,3 +120,49 @@ Refer to the CMake scripts inside the `deps` directory to see which dependencies
\*) Specifically, the problem arises when building boost. Boost build tool appends all build options into paths of
intermediate files, which are not handled correctly by either `b2.exe` or possibly `ninja` (?).
+
+
+# Noob guide (step by step)
+
+Install Visual Studio Community 2019 from
+visualstudio.microsoft.com/vs/
+Select all workload options for C++
+
+Install git for Windows from
+gitforwindows.org
+download and run the exe accepting all defaults
+
+download PrusaSlicer-master.zip from github
+I downloaded this to c:\PrusaSlicer and unzipped to c:\PrusaSlicer\PrusaSlicer-master\ so this will be my prefix for all my steps. Substitute your prefix.
+
+Go to the Windows Start Menu and Click on "Visual Studio 2019" folder, then select the ->"x64 Native Tools Command Prompt" to open a command window
+
+cd c:\PrusaSlicer\PrusaSlicer-master\deps
+
+mkdir build
+
+cd build
+
+cmake .. -G "Visual Studio 16 2019" -DDESTDIR="c:\PrusaSlicer\PrusaSlicer-master"
+
+msbuild /m ALL_BUILD.vcxproj // This took 13.5 minutes on my machine: core I7-7700K @ 4.2Ghz with 32GB main memory and 20min on a average laptop
+
+cd c:\PrusaSlicer\PrusaSlicer-master\
+
+mkdir build
+
+cd build
+
+cmake .. -G "Visual Studio 16 2019" -DCMAKE_PREFIX_PATH="c:\PrusaSlicer\PrusaSlicer-master\usr\local"
+
+open Visual Studio for c++ development (VS asks this the first time you start it)
+
+Open->Project/Solution or File->Open->Project/Solution (depending on which dialog comes up first)
+
+click on c:\PrusaSlicer\PrusaSlicer-master\build\PrusaSlicer.sln
+
+Debug->Start Debugging or Debug->Start Without debugging
+PrusaSlicer should start. You're up and running!
+
+
+note: Thanks to @douggorgen for the original guide, as an answer for a issue
diff --git a/resources/icons/printer_placeholder.png b/resources/icons/printer_placeholder.png
new file mode 100644
index 0000000000..8274019d83
Binary files /dev/null and b/resources/icons/printer_placeholder.png differ
diff --git a/resources/icons/printers/Creality_Ender3.png b/resources/icons/printers/Creality_ENDER3.png
similarity index 100%
rename from resources/icons/printers/Creality_Ender3.png
rename to resources/icons/printers/Creality_ENDER3.png
diff --git a/resources/icons/revert_all_.svg b/resources/icons/revert_all_.svg
new file mode 100644
index 0000000000..fe8de635db
--- /dev/null
+++ b/resources/icons/revert_all_.svg
@@ -0,0 +1,9 @@
+
+
+
+
\ No newline at end of file
diff --git a/resources/localization/es/PrusaSlicer.mo b/resources/localization/es/PrusaSlicer.mo
index 17ba9216d8..3f02722948 100644
Binary files a/resources/localization/es/PrusaSlicer.mo and b/resources/localization/es/PrusaSlicer.mo differ
diff --git a/resources/localization/es/PrusaSlicer_es.po b/resources/localization/es/PrusaSlicer_es.po
index ef2db8b67e..088ff479bd 100644
--- a/resources/localization/es/PrusaSlicer_es.po
+++ b/resources/localization/es/PrusaSlicer_es.po
@@ -6865,7 +6865,7 @@ msgstr "Textura"
#: src/slic3r/GUI/ConfigManipulation.cpp:200
msgid "The %1% infill pattern is not supposed to work at 100%% density."
-msgstr "Se supone que el patrón de relleno %1% no funciona a una densidad del 100%."
+msgstr "Se supone que el patrón de relleno %1% no funciona a una densidad del 100%%."
#: src/slic3r/GUI/FirmwareDialog.cpp:530
#, possible-c-format
diff --git a/resources/localization/fr/PrusaSlicer.mo b/resources/localization/fr/PrusaSlicer.mo
index 130bda3343..f43e20ddbf 100644
Binary files a/resources/localization/fr/PrusaSlicer.mo and b/resources/localization/fr/PrusaSlicer.mo differ
diff --git a/resources/localization/fr/PrusaSlicer_fr.po b/resources/localization/fr/PrusaSlicer_fr.po
index cc796ba743..243fb44e9e 100644
--- a/resources/localization/fr/PrusaSlicer_fr.po
+++ b/resources/localization/fr/PrusaSlicer_fr.po
@@ -7013,7 +7013,7 @@ msgstr "Texture"
#: src/slic3r/GUI/ConfigManipulation.cpp:200
msgid "The %1% infill pattern is not supposed to work at 100%% density."
-msgstr "Le modèle de remplissage %1% n'est pas censé fonctionner avec une densité de 100%."
+msgstr "Le modèle de remplissage %1% n'est pas censé fonctionner avec une densité de 100%%."
#: src/slic3r/GUI/FirmwareDialog.cpp:530
#, c-format
diff --git a/resources/localization/it/PrusaSlicer.mo b/resources/localization/it/PrusaSlicer.mo
index 2f17472a6c..be6f9fd448 100644
Binary files a/resources/localization/it/PrusaSlicer.mo and b/resources/localization/it/PrusaSlicer.mo differ
diff --git a/resources/localization/it/PrusaSlicer_it.po b/resources/localization/it/PrusaSlicer_it.po
index 8cba947681..5d1a965bbd 100644
--- a/resources/localization/it/PrusaSlicer_it.po
+++ b/resources/localization/it/PrusaSlicer_it.po
@@ -6865,7 +6865,7 @@ msgstr "Texture"
#: src/slic3r/GUI/ConfigManipulation.cpp:200
msgid "The %1% infill pattern is not supposed to work at 100%% density."
-msgstr "La trama di riempimento %1% non è fatta per lavorare con densità al 100%."
+msgstr "La trama di riempimento %1% non è fatta per lavorare con densità al 100%%."
#: src/slic3r/GUI/FirmwareDialog.cpp:530
#, possible-c-format
diff --git a/resources/localization/list.txt b/resources/localization/list.txt
index 7f0b672526..93ee2f441d 100644
--- a/resources/localization/list.txt
+++ b/resources/localization/list.txt
@@ -50,6 +50,7 @@ src/slic3r/GUI/ExtruderSequenceDialog.cpp
src/slic3r/Utils/Duet.cpp
src/slic3r/Utils/OctoPrint.cpp
src/slic3r/Utils/FlashAir.cpp
+src/slic3r/Utils/AstroBox.cpp
src/slic3r/Utils/PresetUpdater.cpp
src/slic3r/Utils/FixModelByWin10.cpp
src/libslic3r/SLA/SLAPad.cpp
diff --git a/resources/localization/uk/PrusaSlicer.mo b/resources/localization/uk/PrusaSlicer.mo
index 9063835fef..aae82889f3 100644
Binary files a/resources/localization/uk/PrusaSlicer.mo and b/resources/localization/uk/PrusaSlicer.mo differ
diff --git a/resources/localization/uk/PrusaSlicer_uk.po b/resources/localization/uk/PrusaSlicer_uk.po
index 86e6da1dc7..42b067361e 100644
--- a/resources/localization/uk/PrusaSlicer_uk.po
+++ b/resources/localization/uk/PrusaSlicer_uk.po
@@ -3275,7 +3275,7 @@ msgstr "Шаблон наповнення "
#: src/slic3r/GUI/Tab.cpp:1309
#, no-c-format
msgid ""
-" infill pattern is not supposed to work at 100% density.\n"
+" infill pattern is not supposed to work at 100%% density.\n"
"\n"
"Shall I switch to rectilinear fill pattern?"
msgstr ""
diff --git a/resources/localization/zh_tw/PrusaSlicer.mo b/resources/localization/zh_tw/PrusaSlicer.mo
index 8f51236f78..12b1a17eb0 100644
Binary files a/resources/localization/zh_tw/PrusaSlicer.mo and b/resources/localization/zh_tw/PrusaSlicer.mo differ
diff --git a/resources/localization/zh_tw/PrusaSlicer_zh_TW.po b/resources/localization/zh_tw/PrusaSlicer_zh_TW.po
index 1e7bc4488f..ce35bc342a 100644
--- a/resources/localization/zh_tw/PrusaSlicer_zh_TW.po
+++ b/resources/localization/zh_tw/PrusaSlicer_zh_TW.po
@@ -3292,7 +3292,7 @@ msgstr "這個 "
#: src/slic3r/GUI/Tab.cpp:1309
#, no-c-format
msgid ""
-" infill pattern is not supposed to work at 100% density.\n"
+" infill pattern is not supposed to work at 100%% density.\n"
"\n"
"Shall I switch to rectilinear fill pattern?"
msgstr ""
diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx
index 3b2720300e..c451bb8bf4 100644
--- a/resources/profiles/PrusaResearch.idx
+++ b/resources/profiles/PrusaResearch.idx
@@ -1,4 +1,5 @@
min_slic3r_version = 2.2.0-alpha0
+1.1.1-alpha2 Bumped up config version, so our in house customer will get updated profiles.
1.1.0 Filament aliases, Creality profiles and other goodies for PrusaSlicer 2.2.0-alpha0
min_slic3r_version = 2.1.1-beta0
1.0.6 Added Prusa MINI profiles
diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini
index 74bc529aee..746aaf9668 100644
--- a/resources/profiles/PrusaResearch.ini
+++ b/resources/profiles/PrusaResearch.ini
@@ -5,7 +5,7 @@
name = Prusa Research
# Configuration version of this file. Config file will only be installed, if the config_version differs.
# This means, the server may force the PrusaSlicer configuration to be downgraded.
-config_version = 1.1.0
+config_version = 1.1.1-alpha2
# Where to get the updates from?
config_update_url = http://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/
changelog_url = http://files.prusa3d.com/?latest=slicer-profiles&lng=%1%
diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp
index f708679c18..d755f0b2e7 100644
--- a/src/PrusaSlicer.cpp
+++ b/src/PrusaSlicer.cpp
@@ -691,7 +691,7 @@ extern "C" {
for (size_t i = 0; i < argc; ++ i)
argv_narrow.emplace_back(boost::nowide::narrow(argv[i]));
for (size_t i = 0; i < argc; ++ i)
- argv_ptrs[i] = const_cast(argv_narrow[i].data());
+ argv_ptrs[i] = argv_narrow[i].data();
// Call the UTF8 main.
return CLI().run(argc, argv_ptrs.data());
}
diff --git a/src/avrdude/avrdude-slic3r.cpp b/src/avrdude/avrdude-slic3r.cpp
index b561cd8433..6dbb842265 100644
--- a/src/avrdude/avrdude-slic3r.cpp
+++ b/src/avrdude/avrdude-slic3r.cpp
@@ -93,11 +93,11 @@ void AvrDude::priv::unset_handlers()
int AvrDude::priv::run_one(const std::vector &args) {
- std::vector c_args { const_cast(PACKAGE) };
+ std::vector c_args { PACKAGE };
std::string command_line { PACKAGE };
for (const auto &arg : args) {
- c_args.push_back(const_cast(arg.data()));
+ c_args.push_back(arg.c_str());
command_line.push_back(' ');
command_line.append(arg);
}
@@ -107,7 +107,7 @@ int AvrDude::priv::run_one(const std::vector &args) {
message_fn(command_line.c_str(), (unsigned)command_line.size());
- const auto res = ::avrdude_main(static_cast(c_args.size()), c_args.data());
+ const auto res = ::avrdude_main(static_cast(c_args.size()), const_cast(c_args.data()));
return res;
}
diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp
index a81e9521af..bcd0172780 100644
--- a/src/libslic3r/Config.cpp
+++ b/src/libslic3r/Config.cpp
@@ -630,39 +630,38 @@ size_t ConfigBase::load_from_gcode_string(const char* str)
return 0;
// Walk line by line in reverse until a non-configuration key appears.
- char *data_start = const_cast(str);
+ const char *data_start = str;
// boost::nowide::ifstream seems to cook the text data somehow, so less then the 64k of characters may be retrieved.
- char *end = data_start + strlen(str);
+ const char *end = data_start + strlen(str);
size_t num_key_value_pairs = 0;
for (;;) {
// Extract next line.
for (--end; end > data_start && (*end == '\r' || *end == '\n'); --end);
if (end == data_start)
break;
- char *start = end;
- *(++end) = 0;
+ const char *start = end ++;
for (; start > data_start && *start != '\r' && *start != '\n'; --start);
if (start == data_start)
break;
// Extracted a line from start to end. Extract the key = value pair.
- if (end - (++start) < 10 || start[0] != ';' || start[1] != ' ')
+ if (end - (++ start) < 10 || start[0] != ';' || start[1] != ' ')
break;
- char *key = start + 2;
+ const char *key = start + 2;
if (!(*key >= 'a' && *key <= 'z') || (*key >= 'A' && *key <= 'Z'))
// A key must start with a letter.
break;
- char *sep = strchr(key, '=');
- if (sep == nullptr || sep[-1] != ' ' || sep[1] != ' ')
+ const char *sep = key;
+ for (; sep != end && *sep != '='; ++ sep) ;
+ if (sep == end || sep[-1] != ' ' || sep[1] != ' ')
break;
- char *value = sep + 2;
+ const char *value = sep + 2;
if (value > end)
break;
- char *key_end = sep - 1;
+ const char *key_end = sep - 1;
if (key_end - key < 3)
break;
- *key_end = 0;
// The key may contain letters, digits and underscores.
- for (char *c = key; c != key_end; ++c)
+ for (const char *c = key; c != key_end; ++ c)
if (!((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <= 'Z') || (*c >= '0' && *c <= '9') || *c == '_')) {
key = nullptr;
break;
@@ -670,7 +669,7 @@ size_t ConfigBase::load_from_gcode_string(const char* str)
if (key == nullptr)
break;
try {
- this->set_deserialize(key, value);
+ this->set_deserialize(std::string(key, key_end), std::string(value, end));
++num_key_value_pairs;
}
catch (UnknownOptionException & /* e */) {
@@ -760,15 +759,15 @@ ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool cre
void DynamicConfig::read_cli(const std::vector &tokens, t_config_option_keys* extra, t_config_option_keys* keys)
{
- std::vector args;
+ std::vector args;
// push a bogus executable name (argv[0])
- args.emplace_back(const_cast(""));
+ args.emplace_back("");
for (size_t i = 0; i < tokens.size(); ++ i)
- args.emplace_back(const_cast(tokens[i].c_str()));
- this->read_cli(int(args.size()), &args[0], extra, keys);
+ args.emplace_back(tokens[i].c_str());
+ this->read_cli(int(args.size()), args.data(), extra, keys);
}
-bool DynamicConfig::read_cli(int argc, char** argv, t_config_option_keys* extra, t_config_option_keys* keys)
+bool DynamicConfig::read_cli(int argc, const char* const argv[], t_config_option_keys* extra, t_config_option_keys* keys)
{
// cache the CLI option => opt_key mapping
std::map opts;
diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp
index c49dd134ed..87a79d93f5 100644
--- a/src/libslic3r/Config.hpp
+++ b/src/libslic3r/Config.hpp
@@ -1779,7 +1779,7 @@ public:
// Command line processing
void read_cli(const std::vector &tokens, t_config_option_keys* extra, t_config_option_keys* keys = nullptr);
- bool read_cli(int argc, char** argv, t_config_option_keys* extra, t_config_option_keys* keys = nullptr);
+ bool read_cli(int argc, const char* const argv[], t_config_option_keys* extra, t_config_option_keys* keys = nullptr);
std::map>::const_iterator cbegin() const { return options.cbegin(); }
std::map>::const_iterator cend() const { return options.cend(); }
diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp
index d5c8a3a6c8..b3ccc44b93 100644
--- a/src/libslic3r/Format/3mf.cpp
+++ b/src/libslic3r/Format/3mf.cpp
@@ -88,6 +88,7 @@ const char* V3_ATTR = "v3";
const char* OBJECTID_ATTR = "objectid";
const char* TRANSFORM_ATTR = "transform";
const char* PRINTABLE_ATTR = "printable";
+const char* INSTANCESCOUNT_ATTR = "instances_count";
const char* KEY_ATTR = "key";
const char* VALUE_ATTR = "value";
@@ -729,8 +730,8 @@ namespace Slic3r {
return false;
}
- // fixes the min z of the model if negative
- model.adjust_min_z();
+// // fixes the min z of the model if negative
+// model.adjust_min_z();
return true;
}
@@ -1712,6 +1713,9 @@ namespace Slic3r {
return false;
}
+ // Added because of github #3435, currently not used by PrusaSlicer
+ int instances_count_id = get_attribute_value_int(attributes, num_attributes, INSTANCESCOUNT_ATTR);
+
m_objects_metadata.insert(IdToMetadataMap::value_type(object_id, ObjectMetadata()));
m_curr_config.object_id = object_id;
return true;
@@ -1812,7 +1816,9 @@ namespace Slic3r {
break;
}
}
+#if !ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
Transform3d inv_matrix = volume_matrix_to_object.inverse();
+#endif // !ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
// splits volume out of imported geometry
TriangleMesh triangle_mesh;
@@ -1832,11 +1838,15 @@ namespace Slic3r {
for (unsigned int v = 0; v < 3; ++v)
{
unsigned int tri_id = geometry.triangles[src_start_id + ii + v] * 3;
+#if ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
+ facet.vertex[v] = Vec3f(geometry.vertices[tri_id + 0], geometry.vertices[tri_id + 1], geometry.vertices[tri_id + 2]);
+#else
Vec3f vertex(geometry.vertices[tri_id + 0], geometry.vertices[tri_id + 1], geometry.vertices[tri_id + 2]);
facet.vertex[v] = has_transform ?
// revert the vertices to the original mesh reference system
(inv_matrix * vertex.cast()).cast() :
vertex;
+#endif // ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
}
}
@@ -1844,9 +1854,15 @@ namespace Slic3r {
triangle_mesh.repair();
ModelVolume* volume = object.add_volume(std::move(triangle_mesh));
+#if ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
+ // stores the volume matrix taken from the metadata, if present
+ if (has_transform)
+ volume->source.transform = Slic3r::Geometry::Transformation(volume_matrix_to_object);
+#else
// apply the volume matrix taken from the metadata, if present
if (has_transform)
volume->set_transformation(Slic3r::Geometry::Transformation(volume_matrix_to_object));
+#endif //ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
volume->calculate_convex_hull();
// apply the remaining volume's metadata
@@ -2635,7 +2651,8 @@ namespace Slic3r {
const ModelObject* obj = obj_metadata.second.object;
if (obj != nullptr)
{
- stream << " <" << OBJECT_TAG << " id=\"" << obj_metadata.first << "\">\n";
+ // Output of instances count added because of github #3435, currently not used by PrusaSlicer
+ stream << " <" << OBJECT_TAG << " " << ID_ATTR << "=\"" << obj_metadata.first << "\" " << INSTANCESCOUNT_ATTR << "=\"" << obj->instances.size() << "\">\n";
// stores object's name
if (!obj->name.empty())
@@ -2673,7 +2690,11 @@ namespace Slic3r {
// stores volume's local matrix
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << MATRIX_KEY << "\" " << VALUE_ATTR << "=\"";
+#if ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
+ Transform3d matrix = volume->get_matrix() * volume->source.transform.get_matrix();
+#else
const Transform3d& matrix = volume->get_matrix();
+#endif // ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
for (int r = 0; r < 4; ++r)
{
for (int c = 0; c < 4; ++c)
diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp
index 4b19b8060a..5b65d8fa2d 100644
--- a/src/libslic3r/Format/AMF.cpp
+++ b/src/libslic3r/Format/AMF.cpp
@@ -585,24 +585,36 @@ void AMFParserContext::endElement(const char * /* name */)
stl_allocate(&stl);
bool has_transform = ! m_volume_transform.isApprox(Transform3d::Identity(), 1e-10);
+#if !ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
Transform3d inv_matrix = m_volume_transform.inverse();
+#endif // !ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
for (size_t i = 0; i < m_volume_facets.size();) {
stl_facet &facet = stl.facet_start[i/3];
for (unsigned int v = 0; v < 3; ++v)
{
unsigned int tri_id = m_volume_facets[i++] * 3;
+#if ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
+ facet.vertex[v] = Vec3f(m_object_vertices[tri_id + 0], m_object_vertices[tri_id + 1], m_object_vertices[tri_id + 2]);
+#else
Vec3f vertex(m_object_vertices[tri_id + 0], m_object_vertices[tri_id + 1], m_object_vertices[tri_id + 2]);
facet.vertex[v] = has_transform ?
// revert the vertices to the original mesh reference system
(inv_matrix * vertex.cast()).cast() :
vertex;
+#endif // ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
}
}
stl_get_size(&stl);
mesh.repair();
m_volume->set_mesh(std::move(mesh));
- if (has_transform)
- m_volume->set_transformation(m_volume_transform);
+#if ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
+ // stores the volume matrix taken from the metadata, if present
+ if (has_transform)
+ m_volume->source.transform = Slic3r::Geometry::Transformation(m_volume_transform);
+#else
+ if (has_transform)
+ m_volume->set_transformation(m_volume_transform);
+#endif // ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
if (m_volume->source.input_file.empty() && (m_volume->type() == ModelVolumeType::MODEL_PART))
{
m_volume->source.object_idx = (int)m_model.objects.size() - 1;
@@ -671,7 +683,7 @@ void AMFParserContext::endElement(const char * /* name */)
config->set_deserialize(opt_key, m_value[1]);
} else if (m_path.size() == 3 && m_path[1] == NODE_TYPE_OBJECT && m_object && strcmp(opt_key, "layer_height_profile") == 0) {
// Parse object's layer height profile, a semicolon separated list of floats.
- char *p = const_cast(m_value[1].c_str());
+ char *p = m_value[1].data();
for (;;) {
char *end = strchr(p, ';');
if (end != nullptr)
@@ -686,7 +698,7 @@ void AMFParserContext::endElement(const char * /* name */)
// Parse object's layer height profile, a semicolon separated list of floats.
unsigned char coord_idx = 0;
Eigen::Matrix point(Eigen::Matrix::Zero());
- char *p = const_cast(m_value[1].c_str());
+ char *p = m_value[1].data();
for (;;) {
char *end = strchr(p, ';');
if (end != nullptr)
@@ -706,7 +718,7 @@ void AMFParserContext::endElement(const char * /* name */)
else if (m_path.size() == 5 && m_path[1] == NODE_TYPE_OBJECT && m_path[3] == NODE_TYPE_RANGE &&
m_object && strcmp(opt_key, "layer_height_range") == 0) {
// Parse object's layer_height_range, a semicolon separated doubles.
- char* p = const_cast(m_value[1].c_str());
+ char* p = m_value[1].data();
char* end = strchr(p, ';');
*end = 0;
@@ -998,7 +1010,7 @@ bool load_amf(const char* path, DynamicPrintConfig* config, Model* model, bool c
return false;
std::string zip_mask(2, '\0');
- file.read(const_cast(zip_mask.data()), 2);
+ file.read(zip_mask.data(), 2);
file.close();
return (zip_mask == "PK") ? load_amf_archive(path, config, model, check_version) : load_amf_file(path, config, model);
@@ -1147,8 +1159,12 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
stream << " 1\n";
stream << " " << ModelVolume::type_to_string(volume->type()) << "\n";
stream << " ";
+#if ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
+ const Transform3d& matrix = volume->get_matrix() * volume->source.transform.get_matrix();
+#else
const Transform3d& matrix = volume->get_matrix();
- stream << std::setprecision(std::numeric_limits::max_digits10);
+#endif // ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
+ stream << std::setprecision(std::numeric_limits::max_digits10);
for (int r = 0; r < 4; ++r)
{
for (int c = 0; c < 4; ++c)
@@ -1248,7 +1264,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
pt::write_xml(oss, tree);
out = oss.str();
- int del_header_pos = out.find(" &v)
if (::fread(&len, sizeof(len), 1, pFile) != 1)
return false;
std::string s(" ", len);
- if (::fread(const_cast(s.c_str()), 1, len, pFile) != len)
+ if (::fread(s.data(), 1, len, pFile) != len)
return false;
v.push_back(std::move(s));
}
@@ -471,7 +471,7 @@ bool loadvectornameidx(FILE *pFile, std::vector &v)
if (::fread(&len, sizeof(len), 1, pFile) != 1)
return false;
v[i].name.assign(" ", len);
- if (::fread(const_cast(v[i].name.c_str()), 1, len, pFile) != len)
+ if (::fread(v[i].name.data(), 1, len, pFile) != len)
return false;
}
return true;
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 433422d997..40ca7b0748 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -122,21 +122,21 @@ Polygons AvoidCrossingPerimeters::collect_contours_all_layers(const PrintObjectP
const Layer* layer1 = object->layers()[i * 2];
const Layer* layer2 = object->layers()[i * 2 + 1];
Polygons polys;
- polys.reserve(layer1->slices.size() + layer2->slices.size());
- for (const ExPolygon &expoly : layer1->slices)
+ polys.reserve(layer1->lslices.size() + layer2->lslices.size());
+ for (const ExPolygon &expoly : layer1->lslices)
//FIXME no holes?
polys.emplace_back(expoly.contour);
- for (const ExPolygon &expoly : layer2->slices)
+ for (const ExPolygon &expoly : layer2->lslices)
//FIXME no holes?
polys.emplace_back(expoly.contour);
polygons_per_layer[i] = union_(polys);
- }
- });
+ }
+ });
if (object->layers().size() & 1) {
const Layer *layer = object->layers().back();
Polygons polys;
- polys.reserve(layer->slices.size());
- for (const ExPolygon &expoly : layer->slices)
+ polys.reserve(layer->lslices.size());
+ for (const ExPolygon &expoly : layer->lslices)
//FIXME no holes?
polys.emplace_back(expoly.contour);
polygons_per_layer.back() = union_(polys);
@@ -2006,8 +2006,8 @@ void GCode::process_layer(
// - for each island, we extrude perimeters first, unless user set the infill_first
// option
// (Still, we have to keep track of regions because we need to apply their config)
- size_t n_slices = layer.slices.size();
- const std::vector &layer_surface_bboxes = layer.slices_bboxes;
+ size_t n_slices = layer.lslices.size();
+ const std::vector &layer_surface_bboxes = layer.lslices_bboxes;
// Traverse the slices in an increasing order of bounding box size, so that the islands inside another islands are tested first,
// so we can just test a point inside ExPolygon::contour and we may skip testing the holes.
std::vector slices_test_order;
@@ -2023,7 +2023,7 @@ void GCode::process_layer(
const BoundingBox &bbox = layer_surface_bboxes[i];
return point(0) >= bbox.min(0) && point(0) < bbox.max(0) &&
point(1) >= bbox.min(1) && point(1) < bbox.max(1) &&
- layer.slices[i].contour.contains(point);
+ layer.lslices[i].contour.contains(point);
};
for (size_t region_id = 0; region_id < print.regions().size(); ++ region_id) {
@@ -2155,7 +2155,7 @@ void GCode::process_layer(
m_config.apply(instance_to_print.print_object.config(), true);
m_layer = layers[instance_to_print.layer_id].layer();
if (m_config.avoid_crossing_perimeters)
- m_avoid_crossing_perimeters.init_layer_mp(union_ex(m_layer->slices, true));
+ m_avoid_crossing_perimeters.init_layer_mp(union_ex(m_layer->lslices, true));
if (this->config().gcode_label_objects)
gcode += std::string("; printing object ") + instance_to_print.print_object.model_object()->name + " id:" + std::to_string(instance_to_print.layer_id) + " copy " + std::to_string(instance_to_print.instance_id) + "\n";
@@ -2476,7 +2476,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
// Create the distance field for a layer below.
const coord_t distance_field_resolution = coord_t(scale_(1.) + 0.5);
*lower_layer_edge_grid = make_unique();
- (*lower_layer_edge_grid)->create(m_layer->lower_layer->slices, distance_field_resolution);
+ (*lower_layer_edge_grid)->create(m_layer->lower_layer->lslices, distance_field_resolution);
(*lower_layer_edge_grid)->calculate_sdf();
#if 0
{
diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp
index 68bb85a988..497dc8c718 100644
--- a/src/libslic3r/GCode.hpp
+++ b/src/libslic3r/GCode.hpp
@@ -362,12 +362,11 @@ protected:
bool m_second_layer_things_done;
// Index of a last object copy extruded.
std::pair m_last_obj_copy;
- /* 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_gcode_per_print_z;
+ // 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_gcode_per_print_z;
// Time estimators
GCodeTimeEstimator m_normal_time_estimator;
diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp
index 53a7f2fc45..505b01705e 100644
--- a/src/libslic3r/Layer.cpp
+++ b/src/libslic3r/Layer.cpp
@@ -47,8 +47,8 @@ void Layer::make_slices()
slices = union_ex(slices_p);
}
- this->slices.clear();
- this->slices.reserve(slices.size());
+ this->lslices.clear();
+ this->lslices.reserve(slices.size());
// prepare ordering points
Points ordering_points;
@@ -61,19 +61,21 @@ void Layer::make_slices()
// populate slices vector
for (size_t i : order)
- this->slices.push_back(std::move(slices[i]));
+ this->lslices.emplace_back(std::move(slices[i]));
}
// Merge typed slices into untyped slices. This method is used to revert the effects of detect_surfaces_type() called for posPrepareInfill.
void Layer::merge_slices()
{
- if (m_regions.size() == 1) {
+ if (m_regions.size() == 1 && (this->id() > 0 || this->object()->config().elefant_foot_compensation.value == 0)) {
// Optimization, also more robust. Don't merge classified pieces of layerm->slices,
// but use the non-split islands of a layer. For a single region print, these shall be equal.
- m_regions.front()->slices.set(this->slices, stInternal);
+ // Don't use this optimization on 1st layer with Elephant foot compensation applied, as this->lslices are uncompensated,
+ // while regions are compensated.
+ m_regions.front()->slices.set(this->lslices, stInternal);
} else {
for (LayerRegion *layerm : m_regions)
- // without safety offset, artifacts are generated (GH #2494)
+ // without safety offset, artifacts are generated (upstream Slic3r GH #2494)
layerm->slices.set(union_ex(to_polygons(std::move(layerm->slices.surfaces)), true), stInternal);
}
}
diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp
index 9a4297ce55..b7725d11da 100644
--- a/src/libslic3r/Layer.hpp
+++ b/src/libslic3r/Layer.hpp
@@ -106,12 +106,16 @@ public:
coordf_t print_z; // Z used for printing in unscaled coordinates
coordf_t height; // layer height in unscaled coordinates
- // collection of expolygons generated by slicing the original geometry;
- // also known as 'islands' (all regions and surface types are merged here)
- // The slices are chained by the shortest traverse distance and this traversal
- // order will be recovered by the G-code generator.
- ExPolygons slices;
- std::vector slices_bboxes;
+ // Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry
+ // (with possibly differing extruder ID and slicing parameters) and merged.
+ // For the first layer, if the ELephant foot compensation is applied, this lslice is uncompensated, therefore
+ // it includes the Elephant foot effect, thus it corresponds to the shape of the printed 1st layer.
+ // These lslices aka islands are chained by the shortest traverse distance and this traversal
+ // order will be applied by the G-code generator to the extrusions fitting into these lslices.
+ // These lslices are also used to detect overhangs and overlaps between successive layers, therefore it is important
+ // that the 1st lslice is not compensated by the Elephant foot compensation algorithm.
+ ExPolygons lslices;
+ std::vector lslices_bboxes;
size_t region_count() const { return m_regions.size(); }
const LayerRegion* get_region(int idx) const { return m_regions.at(idx); }
diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp
index 35acaf9983..f4f0d6a5de 100644
--- a/src/libslic3r/LayerRegion.cpp
+++ b/src/libslic3r/LayerRegion.cpp
@@ -72,7 +72,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
if (this->layer()->lower_layer != nullptr)
// Cummulative sum of polygons over all the regions.
- g.lower_slices = &this->layer()->lower_layer->slices;
+ g.lower_slices = &this->layer()->lower_layer->lslices;
g.layer_id = (int)this->layer()->id();
g.ext_perimeter_flow = this->flow(frExternalPerimeter);
@@ -139,7 +139,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
// Remove voids from fill_boundaries, that are not supported by the layer below.
if (lower_layer_covered == nullptr) {
lower_layer_covered = &lower_layer_covered_tmp;
- lower_layer_covered_tmp = to_polygons(lower_layer->slices);
+ lower_layer_covered_tmp = to_polygons(lower_layer->lslices);
}
if (! lower_layer_covered->empty())
voids = diff(voids, *lower_layer_covered);
@@ -260,7 +260,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
// of very thin (but still working) anchors, the grown expolygon would go beyond them
BridgeDetector bd(
initial,
- lower_layer->slices,
+ lower_layer->lslices,
this->flow(frInfill, true).scaled_width()
);
#ifdef SLIC3R_DEBUG
diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp
index f0d8e4b06e..5f5199efb3 100644
--- a/src/libslic3r/Model.cpp
+++ b/src/libslic3r/Model.cpp
@@ -66,7 +66,7 @@ Model& Model::assign_copy(Model &&rhs)
rhs.objects.clear();
// copy custom code per height
- this->custom_gcode_per_print_z = rhs.custom_gcode_per_print_z;
+ this->custom_gcode_per_print_z = std::move(rhs.custom_gcode_per_print_z);
return *this;
}
@@ -1953,25 +1953,23 @@ extern bool model_has_advanced_features(const Model &model)
extern void update_custom_gcode_per_print_z_from_config(std::vector& custom_gcode_per_print_z, DynamicPrintConfig* config)
{
- if (!config->has("colorprint_heights"))
+ auto *colorprint_heights = config->option("colorprint_heights");
+ if (colorprint_heights == nullptr)
return;
- const std::vector& colors = GCodePreviewData::ColorPrintColors();
-
- const auto& colorprint_values = config->option("colorprint_heights")->values;
-
- if (!colorprint_values.empty())
- {
+ if (custom_gcode_per_print_z.empty() && ! colorprint_heights->values.empty()) {
+ // Convert the old colorprint_heighs only if there is no equivalent data in a new format.
+ const std::vector& colors = GCodePreviewData::ColorPrintColors();
+ const auto& colorprint_values = colorprint_heights->values;
custom_gcode_per_print_z.clear();
custom_gcode_per_print_z.reserve(colorprint_values.size());
int i = 0;
for (auto val : colorprint_values)
custom_gcode_per_print_z.emplace_back(Model::CustomGCode{ val, ColorChangeCode, 1, colors[(++i)%7] });
- }
+ }
- /* There is one and only place this configuration option is used now.
- * It wouldn't be used in the future, so erase it.
- * */
+ // The "colorprint_heights" config value has been deprecated. At this point of time it has been converted
+ // to a new format and therefore it shall be erased.
config->erase("colorprint_heights");
}
diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp
index ed99afaf74..934eea94f3 100644
--- a/src/libslic3r/Model.hpp
+++ b/src/libslic3r/Model.hpp
@@ -403,8 +403,13 @@ public:
int object_idx{ -1 };
int volume_idx{ -1 };
Vec3d mesh_offset{ Vec3d::Zero() };
+#if ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
+ Geometry::Transformation transform;
+ template void serialize(Archive& ar) { ar(input_file, object_idx, volume_idx, mesh_offset, transform); }
+#else
template void serialize(Archive& ar) { ar(input_file, object_idx, volume_idx, mesh_offset); }
+#endif // ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
};
Source source;
@@ -754,21 +759,15 @@ public:
// Extensions for color print
struct CustomGCode
{
- bool operator<(const CustomGCode& other) const { return other.print_z > this->print_z; }
- bool operator==(const CustomGCode& other) const
+ bool operator<(const CustomGCode& rhs) const { return this->print_z < rhs.print_z; }
+ bool operator==(const CustomGCode& rhs) const
{
- return (other.print_z == this->print_z ) &&
- (other.gcode == this->gcode ) &&
- (other.extruder == this->extruder ) &&
- (other.color == this->color );
- }
- bool operator!=(const CustomGCode& other) const
- {
- return (other.print_z != this->print_z ) ||
- (other.gcode != this->gcode ) ||
- (other.extruder != this->extruder ) ||
- (other.color != this->color );
+ return (rhs.print_z == this->print_z ) &&
+ (rhs.gcode == this->gcode ) &&
+ (rhs.extruder == this->extruder ) &&
+ (rhs.color == this->color );
}
+ bool operator!=(const CustomGCode& rhs) const { return ! (*this == rhs); }
double print_z;
std::string gcode;
@@ -879,9 +878,9 @@ extern bool model_volume_list_changed(const ModelObject &model_object_old, const
extern bool model_has_multi_part_objects(const Model &model);
// If the model has advanced features, then it cannot be processed in simple mode.
extern bool model_has_advanced_features(const Model &model);
-/* If loaded configuration has a "colorprint_heights" option (if it was imported from older Slicer),
- * then model.custom_gcode_per_print_z should be updated considering this option
- * */
+// If loaded configuration has a "colorprint_heights" option (if it was imported from older Slicer),
+// and if model.custom_gcode_per_print_z is empty (there is no color print data available in a new format
+// then model.custom_gcode_per_print_z should be updated considering this option.
extern void update_custom_gcode_per_print_z_from_config(std::vector& custom_gcode_per_print_z, DynamicPrintConfig* config);
#ifndef NDEBUG
diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp
index 1d4e69763e..02d9da7848 100644
--- a/src/libslic3r/Print.cpp
+++ b/src/libslic3r/Print.cpp
@@ -1658,7 +1658,7 @@ void Print::_make_skirt()
for (const Layer *layer : object->m_layers) {
if (layer->print_z > skirt_height_z)
break;
- for (const ExPolygon &expoly : layer->slices)
+ for (const ExPolygon &expoly : layer->lslices)
// Collect the outer contour points only, ignore holes for the calculation of the convex hull.
append(object_points, expoly.contour.points);
}
@@ -1787,7 +1787,7 @@ void Print::_make_brim()
Polygons islands;
for (PrintObject *object : m_objects) {
Polygons object_islands;
- for (ExPolygon &expoly : object->m_layers.front()->slices)
+ for (ExPolygon &expoly : object->m_layers.front()->lslices)
object_islands.push_back(expoly.contour);
if (! object->support_layers().empty())
object->support_layers().front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON));
diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp
index 098049f1db..95c7b656a4 100644
--- a/src/libslic3r/Print.hpp
+++ b/src/libslic3r/Print.hpp
@@ -182,7 +182,7 @@ private:
void _slice(const std::vector &layer_height_profile);
std::string _fix_slicing_errors();
- void _simplify_slices(double distance);
+ void simplify_slices(double distance);
bool has_support_material() const;
void detect_surfaces_type();
void process_external_surfaces();
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index 285e28b319..89d256a90a 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -1333,9 +1333,11 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("octoprint");
def->enum_values.push_back("duet");
def->enum_values.push_back("flashair");
+ def->enum_values.push_back("astrobox");
def->enum_labels.push_back("OctoPrint");
def->enum_labels.push_back("Duet");
def->enum_labels.push_back("FlashAir");
+ def->enum_values.push_back("AstroBox");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum(htOctoPrint));
@@ -3466,7 +3468,8 @@ CLIMiscConfigDef::CLIMiscConfigDef()
def = this->add("loglevel", coInt);
def->label = L("Logging level");
- def->tooltip = L("Messages with severity lower or eqal to the loglevel will be printed out. 0:trace, 1:debug, 2:info, 3:warning, 4:error, 5:fatal");
+ def->tooltip = L("Sets logging sensitivity. 0:fatal, 1:error, 2:warning, 3:info, 4:debug, 5:trace\n"
+ "For example. loglevel=2 logs fatal, error and warning level messages.");
def->min = 0;
#if (defined(_MSC_VER) || defined(__MINGW32__)) && defined(SLIC3R_GUI)
diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index ac822b5252..8d14969c9b 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -30,7 +30,7 @@ enum GCodeFlavor : unsigned char {
};
enum PrintHostType {
- htOctoPrint, htDuet, htFlashAir
+ htOctoPrint, htDuet, htFlashAir, htAstroBox
};
enum InfillPattern {
@@ -103,6 +103,7 @@ template<> inline const t_config_enum_values& ConfigOptionEnum::g
keys_map["octoprint"] = htOctoPrint;
keys_map["duet"] = htDuet;
keys_map["flashair"] = htFlashAir;
+ keys_map["astrobox"] = htAstroBox;
}
return keys_map;
}
diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp
index bf97baaf6f..8a59b6c3bf 100644
--- a/src/libslic3r/PrintObject.cpp
+++ b/src/libslic3r/PrintObject.cpp
@@ -117,7 +117,7 @@ void PrintObject::slice()
BOOST_LOG_TRIVIAL(info) << warning;
// Simplify slices if required.
if (m_print->config().resolution)
- this->_simplify_slices(scale_(this->print()->config().resolution));
+ this->simplify_slices(scale_(this->print()->config().resolution));
// Update bounding boxes
tbb::parallel_for(
tbb::blocked_range(0, m_layers.size()),
@@ -125,10 +125,10 @@ void PrintObject::slice()
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
m_print->throw_if_canceled();
Layer &layer = *m_layers[layer_idx];
- layer.slices_bboxes.clear();
- layer.slices_bboxes.reserve(layer.slices.size());
- for (const ExPolygon &expoly : layer.slices)
- layer.slices_bboxes.emplace_back(get_extents(expoly));
+ layer.lslices_bboxes.clear();
+ layer.lslices_bboxes.reserve(layer.lslices.size());
+ for (const ExPolygon &expoly : layer.lslices)
+ layer.lslices_bboxes.emplace_back(get_extents(expoly));
}
});
if (m_layers.empty())
@@ -242,13 +242,6 @@ void PrintObject::make_perimeters()
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - end";
- /*
- simplify slices (both layer and region slices),
- we only need the max resolution for perimeters
- ### This makes this method not-idempotent, so we keep it disabled for now.
- ###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION);
- */
-
this->set_done(posPerimeters);
}
@@ -692,7 +685,7 @@ void PrintObject::detect_surfaces_type()
if (upper_layer) {
Polygons upper_slices = interface_shells ?
to_polygons(upper_layer->get_region(idx_region)->slices.surfaces) :
- to_polygons(upper_layer->slices);
+ to_polygons(upper_layer->lslices);
surfaces_append(top,
//FIXME implement offset2_ex working over ExPolygons, that should be a bit more efficient than calling offset_ex twice.
offset_ex(offset_ex(diff_ex(layerm_slices_surfaces, upper_slices, true), -offset), offset),
@@ -721,7 +714,7 @@ void PrintObject::detect_surfaces_type()
surfaces_append(
bottom,
offset2_ex(
- diff(layerm_slices_surfaces, to_polygons(lower_layer->slices), true),
+ diff(layerm_slices_surfaces, to_polygons(lower_layer->lslices), true),
-offset, offset),
surface_type_bottom_other);
// if user requested internal shells, we need to identify surfaces
@@ -733,7 +726,7 @@ void PrintObject::detect_surfaces_type()
bottom,
offset2_ex(
diff(
- intersection(layerm_slices_surfaces, to_polygons(lower_layer->slices)), // supported
+ intersection(layerm_slices_surfaces, to_polygons(lower_layer->lslices)), // supported
to_polygons(lower_layer->get_region(idx_region)->slices.surfaces),
true),
-offset, offset),
@@ -879,7 +872,7 @@ void PrintObject::process_external_surfaces()
// Shrink the holes, let the layer above expand slightly inside the unsupported areas.
polygons_append(voids, offset(surface.expolygon, unsupported_width));
}
- surfaces_covered[layer_idx] = diff(to_polygons(this->m_layers[layer_idx]->slices), voids);
+ surfaces_covered[layer_idx] = diff(to_polygons(this->m_layers[layer_idx]->lslices), voids);
}
}
);
@@ -985,8 +978,8 @@ void PrintObject::discover_vertical_shells()
cache.bottom_surfaces = union_(cache.bottom_surfaces, false);
// For a multi-material print, simulate perimeter / infill split as if only a single extruder has been used for the whole print.
if (perimeter_offset > 0.) {
- // The layer.slices are forced to merge by expanding them first.
- polygons_append(cache.holes, offset(offset_ex(layer.slices, 0.3f * perimeter_min_spacing), - perimeter_offset - 0.3f * perimeter_min_spacing));
+ // The layer.lslices are forced to merge by expanding them first.
+ polygons_append(cache.holes, offset(offset_ex(layer.lslices, 0.3f * perimeter_min_spacing), - perimeter_offset - 0.3f * perimeter_min_spacing));
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-extra-holes-%d.svg", debug_idx), get_extents(layer.slices));
@@ -1762,78 +1755,101 @@ end:
;
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - make_slices in parallel - begin";
- tbb::parallel_for(
- tbb::blocked_range(0, m_layers.size()),
- [this, upscaled, clipped](const tbb::blocked_range& range) {
- for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
- m_print->throw_if_canceled();
- Layer *layer = m_layers[layer_id];
- // Apply size compensation and perform clipping of multi-part objects.
- float delta = float(scale_(m_config.xy_size_compensation.value));
- //FIXME only apply the compensation if no raft is enabled.
- float elephant_foot_compensation = 0.f;
- if (layer_id == 0 && m_config.raft_layers == 0)
- // Only enable Elephant foot compensation if printing directly on the print bed.
- elephant_foot_compensation = float(scale_(m_config.elefant_foot_compensation.value));
- if (layer->m_regions.size() == 1) {
- // Optimized version for a single region layer.
- if (layer_id == 0) {
- if (delta > elephant_foot_compensation) {
- delta -= elephant_foot_compensation;
- elephant_foot_compensation = 0.f;
- } else if (delta > 0)
- elephant_foot_compensation -= delta;
- }
- if (delta != 0.f || elephant_foot_compensation > 0.f) {
- // Single region, growing or shrinking.
- LayerRegion *layerm = layer->m_regions.front();
- // Apply the XY compensation.
- ExPolygons expolygons = (delta == 0.f) ?
- to_expolygons(std::move(layerm->slices.surfaces)) :
- offset_ex(to_expolygons(std::move(layerm->slices.surfaces)), delta);
- // Apply the elephant foot compensation.
- if (elephant_foot_compensation > 0)
- expolygons = union_ex(Slic3r::elephant_foot_compensation(expolygons, layerm->flow(frExternalPerimeter), unscale(elephant_foot_compensation)));
- layerm->slices.set(std::move(expolygons), stInternal);
- }
- } else {
- bool upscale = ! upscaled && delta > 0.f;
- bool clip = ! clipped && m_config.clip_multipart_objects.value;
- if (upscale || clip) {
- // Multiple regions, growing or just clipping one region by the other.
- // When clipping the regions, priority is given to the first regions.
- Polygons processed;
- for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) {
- LayerRegion *layerm = layer->m_regions[region_id];
- ExPolygons slices = to_expolygons(std::move(layerm->slices.surfaces));
- if (upscale)
- slices = offset_ex(std::move(slices), delta);
- if (region_id > 0 && clip)
- // Trim by the slices of already processed regions.
- slices = diff_ex(to_polygons(std::move(slices)), processed);
- if (clip && (region_id + 1 < layer->m_regions.size()))
- // Collect the already processed regions to trim the to be processed regions.
- polygons_append(processed, slices);
- layerm->slices.set(std::move(slices), stInternal);
- }
- }
- if (delta < 0.f || elephant_foot_compensation > 0.f) {
- // Apply the negative XY compensation.
- Polygons trimming;
- static const float eps = float(scale_(m_config.slice_closing_radius.value) * 1.5);
- if (elephant_foot_compensation > 0.f) {
- trimming = to_polygons(Slic3r::elephant_foot_compensation(offset_ex(layer->merged(eps), std::min(delta, 0.f) - eps),
- layer->m_regions.front()->flow(frExternalPerimeter), unscale(elephant_foot_compensation)));
- } else
- trimming = offset(layer->merged(float(SCALED_EPSILON)), delta - float(SCALED_EPSILON));
- for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id)
- layer->m_regions[region_id]->trim_surfaces(trimming);
- }
- }
- // Merge all regions' slices to get islands, chain them by a shortest path.
- layer->make_slices();
- }
- });
+ {
+ // Compensation value, scaled.
+ const float xy_compensation_scaled = float(scale_(m_config.xy_size_compensation.value));
+ const float elephant_foot_compensation_scaled = (m_config.raft_layers == 0) ?
+ // Only enable Elephant foot compensation if printing directly on the print bed.
+ float(scale_(m_config.elefant_foot_compensation.value)) :
+ 0.f;
+ // Uncompensated slices for the first layer in case the Elephant foot compensation is applied.
+ ExPolygons lslices_1st_layer;
+ tbb::parallel_for(
+ tbb::blocked_range(0, m_layers.size()),
+ [this, upscaled, clipped, xy_compensation_scaled, elephant_foot_compensation_scaled, &lslices_1st_layer]
+ (const tbb::blocked_range& range) {
+ for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
+ m_print->throw_if_canceled();
+ Layer *layer = m_layers[layer_id];
+ // Apply size compensation and perform clipping of multi-part objects.
+ float elfoot = (layer_id == 0) ? elephant_foot_compensation_scaled : 0.f;
+ if (layer->m_regions.size() == 1) {
+ assert(! upscaled);
+ assert(! clipped);
+ // Optimized version for a single region layer.
+ // Single region, growing or shrinking.
+ LayerRegion *layerm = layer->m_regions.front();
+ if (elfoot > 0) {
+ // Apply the elephant foot compensation and store the 1st layer slices without the Elephant foot compensation applied.
+ lslices_1st_layer = to_expolygons(std::move(layerm->slices.surfaces));
+ float delta = xy_compensation_scaled;
+ if (delta > elfoot) {
+ delta -= elfoot;
+ elfoot = 0.f;
+ } else if (delta > 0)
+ elfoot -= delta;
+ layerm->slices.set(
+ union_ex(
+ Slic3r::elephant_foot_compensation(
+ (delta == 0.f) ? lslices_1st_layer : offset_ex(lslices_1st_layer, delta),
+ layerm->flow(frExternalPerimeter), unscale(elfoot))),
+ stInternal);
+ if (xy_compensation_scaled != 0.f)
+ lslices_1st_layer = offset_ex(std::move(lslices_1st_layer), xy_compensation_scaled);
+ } else if (xy_compensation_scaled != 0.f) {
+ // Apply the XY compensation.
+ layerm->slices.set(
+ offset_ex(to_expolygons(std::move(layerm->slices.surfaces)), xy_compensation_scaled),
+ stInternal);
+ }
+ } else {
+ bool upscale = ! upscaled && xy_compensation_scaled > 0.f;
+ bool clip = ! clipped && m_config.clip_multipart_objects.value;
+ if (upscale || clip) {
+ // Multiple regions, growing or just clipping one region by the other.
+ // When clipping the regions, priority is given to the first regions.
+ Polygons processed;
+ for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) {
+ LayerRegion *layerm = layer->m_regions[region_id];
+ ExPolygons slices = to_expolygons(std::move(layerm->slices.surfaces));
+ if (upscale)
+ slices = offset_ex(std::move(slices), xy_compensation_scaled);
+ if (region_id > 0 && clip)
+ // Trim by the slices of already processed regions.
+ slices = diff_ex(to_polygons(std::move(slices)), processed);
+ if (clip && (region_id + 1 < layer->m_regions.size()))
+ // Collect the already processed regions to trim the to be processed regions.
+ polygons_append(processed, slices);
+ layerm->slices.set(std::move(slices), stInternal);
+ }
+ }
+ if (xy_compensation_scaled < 0.f || elfoot > 0.f) {
+ // Apply the negative XY compensation.
+ Polygons trimming;
+ static const float eps = float(scale_(m_config.slice_closing_radius.value) * 1.5);
+ if (elfoot > 0.f) {
+ lslices_1st_layer = offset_ex(layer->merged(eps), std::min(xy_compensation_scaled, 0.f) - eps);
+ trimming = to_polygons(Slic3r::elephant_foot_compensation(lslices_1st_layer,
+ layer->m_regions.front()->flow(frExternalPerimeter), unscale(elfoot)));
+ } else
+ trimming = offset(layer->merged(float(SCALED_EPSILON)), xy_compensation_scaled - float(SCALED_EPSILON));
+ for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id)
+ layer->m_regions[region_id]->trim_surfaces(trimming);
+ }
+ }
+ // Merge all regions' slices to get islands, chain them by a shortest path.
+ layer->make_slices();
+ }
+ });
+ if (elephant_foot_compensation_scaled > 0.f) {
+ // The Elephant foot has been compensated, therefore the 1st layer's lslices are shrank with the Elephant foot compensation value.
+ // Store the uncompensated value there.
+ assert(! m_layers.empty());
+ assert(m_layers.front()->id() == 0);
+ m_layers.front()->lslices = std::move(lslices_1st_layer);
+ }
+ }
+
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - make_slices in parallel - end";
}
@@ -2131,7 +2147,7 @@ std::string PrintObject::_fix_slicing_errors()
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - fixing slicing errors in parallel - end";
// remove empty layers from bottom
- while (! m_layers.empty() && m_layers.front()->slices.empty()) {
+ while (! m_layers.empty() && (m_layers.front()->lslices.empty() || m_layers.front()->empty())) {
delete m_layers.front();
m_layers.erase(m_layers.begin());
m_layers.front()->lower_layer = nullptr;
@@ -2147,7 +2163,7 @@ std::string PrintObject::_fix_slicing_errors()
// Simplify the sliced model, if "resolution" configuration parameter > 0.
// The simplification is problematic, because it simplifies the slices independent from each other,
// which makes the simplified discretization visible on the object surface.
-void PrintObject::_simplify_slices(double distance)
+void PrintObject::simplify_slices(double distance)
{
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - siplifying slices in parallel - begin";
tbb::parallel_for(
@@ -2160,9 +2176,9 @@ void PrintObject::_simplify_slices(double distance)
layer->m_regions[region_idx]->slices.simplify(distance);
{
ExPolygons simplified;
- for (const ExPolygon& expoly : layer->slices)
+ for (const ExPolygon &expoly : layer->lslices)
expoly.simplify(distance, &simplified);
- layer->slices = std::move(simplified);
+ layer->lslices = std::move(simplified);
}
}
});
@@ -2194,7 +2210,7 @@ void PrintObject::clip_fill_surfaces()
// Detect things that we need to support.
// Cummulative slices.
Polygons slices;
- polygons_append(slices, layer->slices);
+ polygons_append(slices, layer->lslices);
// Cummulative fill surfaces.
Polygons fill_surfaces;
// Solid surfaces to be supported.
diff --git a/src/libslic3r/Semver.hpp b/src/libslic3r/Semver.hpp
index 1723910773..24ca74f837 100644
--- a/src/libslic3r/Semver.hpp
+++ b/src/libslic3r/Semver.hpp
@@ -149,7 +149,7 @@ private:
Semver(semver_t ver) : ver(ver) {}
static semver_t semver_zero() { return { 0, 0, 0, nullptr, nullptr }; }
- static char * strdup(const std::string &str) { return ::semver_strdup(const_cast(str.c_str())); }
+ static char * strdup(const std::string &str) { return ::semver_strdup(str.data()); }
};
diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp
index 179b35f59e..1d98bb0e62 100644
--- a/src/libslic3r/SupportMaterial.cpp
+++ b/src/libslic3r/SupportMaterial.cpp
@@ -1,6 +1,5 @@
#include "ClipperUtils.hpp"
#include "ExtrusionEntityCollection.hpp"
-#include "PerimeterGenerator.hpp"
#include "Layer.hpp"
#include "Print.hpp"
#include "SupportMaterial.hpp"
@@ -445,8 +444,8 @@ Polygons collect_region_slices_by_type(const Layer &layer, SurfaceType surface_t
Polygons collect_slices_outer(const Layer &layer)
{
Polygons out;
- out.reserve(out.size() + layer.slices.size());
- for (const ExPolygon &expoly : layer.slices)
+ out.reserve(out.size() + layer.lslices.size());
+ for (const ExPolygon &expoly : layer.lslices)
out.emplace_back(expoly.contour);
return out;
}
@@ -907,9 +906,9 @@ namespace SupportMaterialInternal {
polyline.extend_start(fw);
polyline.extend_end(fw);
// Is the straight perimeter segment supported at both sides?
- for (size_t i = 0; i < lower_layer.slices.size(); ++ i)
- if (lower_layer.slices_bboxes[i].contains(polyline.first_point()) && lower_layer.slices_bboxes[i].contains(polyline.last_point()) &&
- lower_layer.slices[i].contains(polyline.first_point()) && lower_layer.slices[i].contains(polyline.last_point())) {
+ for (size_t i = 0; i < lower_layer.lslices.size(); ++ i)
+ if (lower_layer.lslices_bboxes[i].contains(polyline.first_point()) && lower_layer.lslices_bboxes[i].contains(polyline.last_point()) &&
+ lower_layer.lslices[i].contains(polyline.first_point()) && lower_layer.lslices[i].contains(polyline.last_point())) {
// Offset a polyline into a thick line.
polygons_append(bridges, offset(polyline, 0.5f * w + 10.f));
break;
@@ -998,7 +997,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// inflate the polygons over and over.
Polygons &covered = buildplate_covered[layer_id];
covered = buildplate_covered[layer_id - 1];
- polygons_append(covered, offset(lower_layer.slices, scale_(0.01)));
+ polygons_append(covered, offset(lower_layer.lslices, scale_(0.01)));
covered = union_(covered, false); // don't apply the safety offset.
}
}
@@ -1027,7 +1026,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
Polygons contact_polygons;
Polygons slices_margin_cached;
float slices_margin_cached_offset = -1.;
- Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers()[layer_id-1]->slices);
+ Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers()[layer_id-1]->lslices);
// Offset of the lower layer, to trim the support polygons with to calculate dense supports.
float no_interface_offset = 0.f;
if (layer_id == 0) {
@@ -1166,7 +1165,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
slices_margin_cached_offset = slices_margin_offset;
slices_margin_cached = (slices_margin_offset == 0.f) ?
lower_layer_polygons :
- offset2(to_polygons(lower_layer.slices), - no_interface_offset * 0.5f, slices_margin_offset + no_interface_offset * 0.5f, SUPPORT_SURFACES_OFFSET_PARAMETERS);
+ offset2(to_polygons(lower_layer.lslices), - no_interface_offset * 0.5f, slices_margin_offset + no_interface_offset * 0.5f, SUPPORT_SURFACES_OFFSET_PARAMETERS);
if (! buildplate_covered.empty()) {
// Trim the inflated contact surfaces by the top surfaces as well.
polygons_append(slices_margin_cached, buildplate_covered[layer_id]);
@@ -1573,7 +1572,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
task_group.run([this, &projection, &projection_raw, &layer, &layer_support_area, layer_id] {
// Remove the areas that touched from the projection that will continue on next, lower, top surfaces.
// Polygons trimming = union_(to_polygons(layer.slices), touching, true);
- Polygons trimming = offset(layer.slices, float(SCALED_EPSILON));
+ Polygons trimming = offset(layer.lslices, float(SCALED_EPSILON));
projection = diff(projection_raw, trimming, false);
#ifdef SLIC3R_DEBUG
{
@@ -2105,7 +2104,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
const Layer &object_layer = *object.layers()[i];
if (object_layer.print_z - object_layer.height > support_layer.print_z + gap_extra_above - EPSILON)
break;
- polygons_append(polygons_trimming, offset(object_layer.slices, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+ polygons_append(polygons_trimming, offset(object_layer.lslices, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
}
if (! m_slicing_params.soluble_interface) {
// Collect all bottom surfaces, which will be extruded with a bridging flow.
@@ -2218,7 +2217,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf
// Expand the bases of the support columns in the 1st layer.
columns_base->polygons = diff(
offset(columns_base->polygons, inflate_factor_1st_layer),
- offset(m_object->layers().front()->slices, (float)scale_(m_gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS));
+ offset(m_object->layers().front()->lslices, (float)scale_(m_gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS));
if (contacts != nullptr)
columns_base->polygons = diff(columns_base->polygons, interface_polygons);
}
diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp
index d503f0c64a..64c5ce953d 100644
--- a/src/libslic3r/Technologies.hpp
+++ b/src/libslic3r/Technologies.hpp
@@ -56,4 +56,16 @@
// Enable closing 3Dconnextion imgui settings dialog by clicking on [X] and [Close] buttons
#define ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG (1 && ENABLE_2_2_0_ALPHA1)
+// Enable not applying volume transformation during 3mf and amf loading, but keeping it as a ModelVolume member
+#define ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE (1 && ENABLE_2_2_0_ALPHA1)
+
+
+//==================
+// 2.2.0.beta1 techs
+//==================
+#define ENABLE_2_2_0_BETA1 1
+
+// Enable using Y axis of 3Dconnexion devices as zoom
+#define ENABLE_3DCONNEXION_Y_AS_ZOOM (1 && ENABLE_2_2_0_BETA1)
+
#endif // _technologies_h_
diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp
index f825f65e5d..06c4358099 100644
--- a/src/libslic3r/Utils.hpp
+++ b/src/libslic3r/Utils.hpp
@@ -65,7 +65,14 @@ extern std::string normalize_utf8_nfc(const char *src);
extern std::error_code rename_file(const std::string &from, const std::string &to);
// Copy a file, adjust the access attributes, so that the target is writable.
-extern int copy_file(const std::string &from, const std::string &to);
+int copy_file_inner(const std::string &from, const std::string &to);
+// Copy file to a temp file first, then rename it to the final file name.
+// If with_check is true, then the content of the copied file is compared to the content
+// of the source file before renaming.
+extern int copy_file(const std::string &from, const std::string &to, const bool with_check = false);
+
+// Compares two files, returns 0 if identical, -1 if different.
+extern int check_copy(const std::string& origin, const std::string& copy);
// Ignore system and hidden files, which may be created by the DropBox synchronisation process.
// https://github.com/prusa3d/PrusaSlicer/issues/1298
diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp
index 678ad9ed28..36653cdaff 100644
--- a/src/libslic3r/utils.cpp
+++ b/src/libslic3r/utils.cpp
@@ -417,27 +417,76 @@ std::error_code rename_file(const std::string &from, const std::string &to)
#endif
}
-int copy_file(const std::string &from, const std::string &to)
+int copy_file_inner(const std::string& from, const std::string& to)
{
- const boost::filesystem::path source(from);
- const boost::filesystem::path target(to);
- static const auto perms = boost::filesystem::owner_read | boost::filesystem::owner_write | boost::filesystem::group_read | boost::filesystem::others_read; // aka 644
+ const boost::filesystem::path source(from);
+ const boost::filesystem::path target(to);
+ static const auto perms = boost::filesystem::owner_read | boost::filesystem::owner_write | boost::filesystem::group_read | boost::filesystem::others_read; // aka 644
- // Make sure the file has correct permission both before and after we copy over it.
- // NOTE: error_code variants are used here to supress expception throwing.
- // Error code of permission() calls is ignored on purpose - if they fail,
- // the copy_file() function will fail appropriately and we don't want the permission()
- // calls to cause needless failures on permissionless filesystems (ie. FATs on SD cards etc.)
- // or when the target file doesn't exist.
- boost::system::error_code ec;
- boost::filesystem::permissions(target, perms, ec);
- boost::filesystem::copy_file(source, target, boost::filesystem::copy_option::overwrite_if_exists, ec);
- if (ec) {
- return -1;
- }
- boost::filesystem::permissions(target, perms, ec);
+ // Make sure the file has correct permission both before and after we copy over it.
+ // NOTE: error_code variants are used here to supress expception throwing.
+ // Error code of permission() calls is ignored on purpose - if they fail,
+ // the copy_file() function will fail appropriately and we don't want the permission()
+ // calls to cause needless failures on permissionless filesystems (ie. FATs on SD cards etc.)
+ // or when the target file doesn't exist.
+ boost::system::error_code ec;
+ boost::filesystem::permissions(target, perms, ec);
+ boost::filesystem::copy_file(source, target, boost::filesystem::copy_option::overwrite_if_exists, ec);
+ if (ec) {
+ return -1;
+ }
+ boost::filesystem::permissions(target, perms, ec);
+ return 0;
+}
- return 0;
+int copy_file(const std::string &from, const std::string &to, const bool with_check)
+{
+ std::string to_temp = to + ".tmp";
+ int ret_val = copy_file_inner(from,to_temp);
+ if(ret_val == 0)
+ {
+ if (with_check)
+ ret_val = check_copy(from, to_temp);
+
+ if (ret_val == 0 && rename_file(to_temp, to))
+ ret_val = -1;
+ }
+ return ret_val;
+}
+
+int check_copy(const std::string &origin, const std::string ©)
+{
+ std::ifstream f1(origin, std::ifstream::in | std::ifstream::binary | std::ifstream::ate);
+ std::ifstream f2(copy, std::ifstream::in | std::ifstream::binary | std::ifstream::ate);
+
+ if (f1.fail() || f2.fail())
+ return -1;
+
+ std::streampos fsize = f1.tellg();
+ if (fsize != f2.tellg())
+ return -1;
+
+ f1.seekg(0, std::ifstream::beg);
+ f2.seekg(0, std::ifstream::beg);
+
+ // Compare by reading 8 MiB buffers one at a time.
+ size_t buffer_size = 8 * 1024 * 1024;
+ std::vector buffer_origin(buffer_size, 0);
+ std::vector buffer_copy(buffer_size, 0);
+ do {
+ f1.read(buffer_origin.data(), buffer_size);
+ f2.read(buffer_copy.data(), buffer_size);
+ std::streampos origin_cnt = f1.gcount();
+ std::streampos copy_cnt = f2.gcount();
+ if (origin_cnt != copy_cnt ||
+ (origin_cnt > 0 && std::memcmp(buffer_origin.data(), buffer_copy.data(), origin_cnt) != 0))
+ // Files are different.
+ return -1;
+ fsize -= origin_cnt;
+ } while (f1.good() && f2.good());
+
+ // All data has been read and compared equal.
+ return (f1.eof() && f2.eof() && fsize == 0) ? 0 : -1;
}
// Ignore system and hidden files, which may be created by the DropBox synchronisation process.
@@ -486,7 +535,7 @@ std::string encode_path(const char *src)
// Convert a wide string to a local code page.
int size_needed = ::WideCharToMultiByte(0, 0, wstr_src.data(), (int)wstr_src.size(), nullptr, 0, nullptr, nullptr);
std::string str_dst(size_needed, 0);
- ::WideCharToMultiByte(0, 0, wstr_src.data(), (int)wstr_src.size(), const_cast(str_dst.data()), size_needed, nullptr, nullptr);
+ ::WideCharToMultiByte(0, 0, wstr_src.data(), (int)wstr_src.size(), str_dst.data(), size_needed, nullptr, nullptr);
return str_dst;
#else /* WIN32 */
return src;
@@ -503,7 +552,7 @@ std::string decode_path(const char *src)
// Convert the string encoded using the local code page to a wide string.
int size_needed = ::MultiByteToWideChar(0, 0, src, len, nullptr, 0);
std::wstring wstr_dst(size_needed, 0);
- ::MultiByteToWideChar(0, 0, src, len, const_cast(wstr_dst.data()), size_needed);
+ ::MultiByteToWideChar(0, 0, src, len, wstr_dst.data(), size_needed);
// Convert a wide string to utf8.
return boost::nowide::narrow(wstr_dst.c_str());
#else /* WIN32 */
diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt
index 4156481b0d..c489a0f8a6 100644
--- a/src/slic3r/CMakeLists.txt
+++ b/src/slic3r/CMakeLists.txt
@@ -113,6 +113,8 @@ set(SLIC3R_GUI_SOURCES
GUI/WipeTowerDialog.hpp
GUI/RammingChart.cpp
GUI/RammingChart.hpp
+ GUI/RemovableDriveManager.cpp
+ GUI/RemovableDriveManager.hpp
GUI/BonjourDialog.cpp
GUI/BonjourDialog.hpp
GUI/ButtonsDescription.cpp
@@ -153,6 +155,8 @@ set(SLIC3R_GUI_SOURCES
Utils/Duet.hpp
Utils/FlashAir.cpp
Utils/FlashAir.hpp
+ Utils/AstroBox.cpp
+ Utils/AstroBox.hpp
Utils/PrintHost.cpp
Utils/PrintHost.hpp
Utils/Bonjour.cpp
@@ -170,7 +174,12 @@ if (APPLE)
list(APPEND SLIC3R_GUI_SOURCES
Utils/RetinaHelperImpl.mm
Utils/MacDarkMode.mm
+ GUI/RemovableDriveManagerMM.mm
+ GUI/RemovableDriveManagerMM.h
)
+ #DK
+ FIND_LIBRARY(DISKARBITRATION_LIBRARY DiskArbitration)
+
endif ()
add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES})
@@ -178,6 +187,11 @@ add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES})
encoding_check(libslic3r_gui)
target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi)
+#DK
+if(APPLE)
+ target_link_libraries(libslic3r_gui ${DISKARBITRATION_LIBRARY})
+endif()
+
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE)
endif ()
diff --git a/src/slic3r/Config/Version.cpp b/src/slic3r/Config/Version.cpp
index fcebf88d14..2104a6eeab 100644
--- a/src/slic3r/Config/Version.cpp
+++ b/src/slic3r/Config/Version.cpp
@@ -205,7 +205,7 @@ size_t Index::load(const boost::filesystem::path &path)
#endif
++ idx_line;
// Skip the initial white spaces.
- char *key = left_trim(const_cast(line.data()));
+ char *key = left_trim(line.data());
if (*key == '#')
// Skip a comment line.
continue;
diff --git a/src/slic3r/GUI/AppConfig.cpp b/src/slic3r/GUI/AppConfig.cpp
index d33d945efb..eb0a7fca69 100644
--- a/src/slic3r/GUI/AppConfig.cpp
+++ b/src/slic3r/GUI/AppConfig.cpp
@@ -271,7 +271,11 @@ void AppConfig::set_recent_projects(const std::vector& recent_proje
}
}
+#if ENABLE_3DCONNEXION_Y_AS_ZOOM
+void AppConfig::set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone, double zoom_speed)
+#else
void AppConfig::set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone)
+#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
{
std::string key = std::string("mouse_device:") + name;
auto it = m_storage.find(key);
@@ -283,6 +287,9 @@ void AppConfig::set_mouse_device(const std::string& name, double translation_spe
it->second["translation_deadzone"] = std::to_string(translation_deadzone);
it->second["rotation_speed"] = std::to_string(rotation_speed);
it->second["rotation_deadzone"] = std::to_string(rotation_deadzone);
+#if ENABLE_3DCONNEXION_Y_AS_ZOOM
+ it->second["zoom_speed"] = std::to_string(zoom_speed);
+#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
}
bool AppConfig::get_mouse_device_translation_speed(const std::string& name, double& speed)
@@ -345,6 +352,23 @@ bool AppConfig::get_mouse_device_rotation_deadzone(const std::string& name, floa
return true;
}
+#if ENABLE_3DCONNEXION_Y_AS_ZOOM
+bool AppConfig::get_mouse_device_zoom_speed(const std::string& name, double& speed)
+{
+ std::string key = std::string("mouse_device:") + name;
+ auto it = m_storage.find(key);
+ if (it == m_storage.end())
+ return false;
+
+ auto it_val = it->second.find("zoom_speed");
+ if (it_val == it->second.end())
+ return false;
+
+ speed = (float)::atof(it_val->second.c_str());
+ return true;
+}
+#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
+
void AppConfig::update_config_dir(const std::string &dir)
{
this->set("recent", "config_directory", dir);
@@ -357,6 +381,7 @@ void AppConfig::update_skein_dir(const std::string &dir)
std::string AppConfig::get_last_output_dir(const std::string &alt) const
{
+
const auto it = m_storage.find("");
if (it != m_storage.end()) {
const auto it2 = it->second.find("last_output_path");
diff --git a/src/slic3r/GUI/AppConfig.hpp b/src/slic3r/GUI/AppConfig.hpp
index 3553704506..b432367b66 100644
--- a/src/slic3r/GUI/AppConfig.hpp
+++ b/src/slic3r/GUI/AppConfig.hpp
@@ -131,11 +131,18 @@ public:
std::vector get_recent_projects() const;
void set_recent_projects(const std::vector& recent_projects);
- void set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone);
- bool get_mouse_device_translation_speed(const std::string& name, double& speed);
+#if ENABLE_3DCONNEXION_Y_AS_ZOOM
+ void set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone, double zoom_speed);
+#else
+ void set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone);
+#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
+ bool get_mouse_device_translation_speed(const std::string& name, double& speed);
bool get_mouse_device_translation_deadzone(const std::string& name, double& deadzone);
bool get_mouse_device_rotation_speed(const std::string& name, float& speed);
bool get_mouse_device_rotation_deadzone(const std::string& name, float& deadzone);
+#if ENABLE_3DCONNEXION_Y_AS_ZOOM
+ bool get_mouse_device_zoom_speed(const std::string& name, double& speed);
+#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
static const std::string SECTION_FILAMENTS;
static const std::string SECTION_MATERIALS;
diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp
index a8b88dd037..9d6a2a5ec7 100644
--- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp
+++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp
@@ -34,6 +34,7 @@
#include
#include "I18N.hpp"
#include "GUI.hpp"
+#include "RemovableDriveManager.hpp"
namespace Slic3r {
@@ -107,7 +108,7 @@ void BackgroundSlicingProcess::process_fff()
//FIXME localize the messages
// Perform the final post-processing of the export path by applying the print statistics over the file name.
std::string export_path = m_fff_print->print_statistics().finalize_output_path(m_export_path);
- if (copy_file(m_temp_output_path, export_path) != 0)
+ if (copy_file(m_temp_output_path, export_path, GUI::RemovableDriveManager::get_instance().is_path_on_removable_drive(export_path)) != 0)
throw std::runtime_error(_utf8(L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?")));
m_print->set_status(95, _utf8(L("Running post-processing scripts")));
run_post_process_scripts(export_path, m_fff_print->config());
diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp
index bb514e8888..fa7a3be21e 100644
--- a/src/slic3r/GUI/Camera.cpp
+++ b/src/slic3r/GUI/Camera.cpp
@@ -338,7 +338,7 @@ void Camera::debug_render() const
float fov = (float)get_fov();
float gui_scale = (float)get_gui_scale();
- ImGui::InputText("Type", const_cast(type.data()), type.length(), ImGuiInputTextFlags_ReadOnly);
+ ImGui::InputText("Type", type.data(), type.length(), ImGuiInputTextFlags_ReadOnly);
ImGui::Separator();
ImGui::InputFloat3("Position", position.data(), "%.6f", ImGuiInputTextFlags_ReadOnly);
ImGui::InputFloat3("Target", target.data(), "%.6f", ImGuiInputTextFlags_ReadOnly);
diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp
index 971bd1f1d3..265e7f203a 100644
--- a/src/slic3r/GUI/ConfigWizard.cpp
+++ b/src/slic3r/GUI/ConfigWizard.cpp
@@ -1605,7 +1605,27 @@ void ConfigWizard::priv::on_3rdparty_install(const VendorProfile *vendor, bool i
load_pages();
}
-bool ConfigWizard::priv::check_material_config(Technology technology)
+bool ConfigWizard::priv::on_bnt_finish()
+{
+ /* When Filaments or Sla Materials pages are activated,
+ * materials for this pages are automaticaly updated and presets are reloaded.
+ *
+ * But, if _Finish_ button was clicked without activation of those pages
+ * (for example, just some printers were added/deleted),
+ * than last changes wouldn't be updated for filaments/materials.
+ * SO, do that before close of Wizard
+ */
+ update_materials(T_ANY);
+ if (any_fff_selected)
+ page_filaments->reload_presets();
+ if (any_sla_selected)
+ page_sla_materials->reload_presets();
+
+ // check, that there is selected at least one filament/material
+ return check_materials_in_config(T_ANY);
+}
+
+bool ConfigWizard::priv::check_materials_in_config(Technology technology)
{
const auto exist_preset = [this](const std::string& section, const Materials& materials)
{
@@ -1899,16 +1919,15 @@ ConfigWizard::ConfigWizard(wxWindow *parent)
// check, that there is selected at least one filament/material
ConfigWizardPage* active_page = this->p->index->active_page();
if ( (active_page == p->page_filaments || active_page == p->page_sla_materials)
- && !p->check_material_config(dynamic_cast(active_page)->materials->technology))
+ && !p->check_materials_in_config(dynamic_cast(active_page)->materials->technology))
return;
this->p->index->go_next();
});
p->btn_finish->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &)
{
- if (!p->check_material_config(T_ANY))
- return;
- this->EndModal(wxID_OK);
+ if (p->on_bnt_finish())
+ this->EndModal(wxID_OK);
});
p->btn_sel_all->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &) {
diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp
index fc91909e71..a90832a3fc 100644
--- a/src/slic3r/GUI/ConfigWizard_private.hpp
+++ b/src/slic3r/GUI/ConfigWizard_private.hpp
@@ -489,7 +489,8 @@ struct ConfigWizard::priv
void on_printer_pick(PagePrinters *page, const PrinterPickerEvent &evt);
void on_3rdparty_install(const VendorProfile *vendor, bool install);
- bool check_material_config(Technology technology);
+ bool on_bnt_finish();
+ bool check_materials_in_config(Technology technology);
void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater);
// #ys_FIXME_alise
void update_presets_in_config(const std::string& section, const std::string& alias_key, bool add);
diff --git a/src/slic3r/GUI/GLShader.cpp b/src/slic3r/GUI/GLShader.cpp
index 11f109fd5f..c310760603 100644
--- a/src/slic3r/GUI/GLShader.cpp
+++ b/src/slic3r/GUI/GLShader.cpp
@@ -142,7 +142,7 @@ bool GLShader::load_from_file(const char* fragment_shader_filename, const char*
int file_length = (int)vs.tellg();
vs.seekg(0, vs.beg);
std::string vertex_shader(file_length, '\0');
- vs.read(const_cast(vertex_shader.data()), file_length);
+ vs.read(vertex_shader.data(), file_length);
if (!vs.good())
return false;
@@ -156,7 +156,7 @@ bool GLShader::load_from_file(const char* fragment_shader_filename, const char*
file_length = (int)fs.tellg();
fs.seekg(0, fs.beg);
std::string fragment_shader(file_length, '\0');
- fs.read(const_cast(fragment_shader.data()), file_length);
+ fs.read(fragment_shader.data(), file_length);
if (!fs.good())
return false;
diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp
index 6f9e7c0deb..9574cdb8e1 100644
--- a/src/slic3r/GUI/GUI_App.cpp
+++ b/src/slic3r/GUI/GUI_App.cpp
@@ -46,6 +46,7 @@
#include "SysInfoDialog.hpp"
#include "KBShortcutsDialog.hpp"
#include "UpdateDialogs.hpp"
+#include "RemovableDriveManager.hpp"
#ifdef __WXMSW__
#include
@@ -261,6 +262,8 @@ bool GUI_App::on_init_inner()
m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg()));
+ RemovableDriveManager::get_instance().init();
+
Bind(wxEVT_IDLE, [this](wxIdleEvent& event)
{
if (! plater_)
@@ -271,6 +274,10 @@ bool GUI_App::on_init_inner()
this->obj_manipul()->update_if_dirty();
+#if !__APPLE__
+ RemovableDriveManager::get_instance().update(wxGetLocalTime(), true);
+#endif
+
// Preset updating & Configwizard are done after the above initializations,
// and after MainFrame is created & shown.
// The extra CallAfter() is needed because of Mac, where this is the only way
@@ -298,6 +305,7 @@ bool GUI_App::on_init_inner()
preset_updater->slic3r_update_notify();
preset_updater->sync(preset_bundle);
});
+
}
});
diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp
index abedc138bd..8aef0098c6 100644
--- a/src/slic3r/GUI/GUI_ObjectList.cpp
+++ b/src/slic3r/GUI/GUI_ObjectList.cpp
@@ -2931,6 +2931,7 @@ bool ObjectList::edit_layer_range(const t_layer_height_range& range, coordf_t la
layer_height <= get_max_layer_height(extruder_idx))
{
config->set_key_value("layer_height", new ConfigOptionFloat(layer_height));
+ changed_object(obj_idx);
return true;
}
@@ -2952,6 +2953,7 @@ bool ObjectList::edit_layer_range(const t_layer_height_range& range, const t_lay
ranges.erase(range);
ranges[new_range] = config;
+ changed_object(obj_idx);
wxDataViewItem root_item = m_objects_model->GetLayerRootItem(m_objects_model->GetItemById(obj_idx));
// To avoid update selection after deleting of a selected item (under GTK)
diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp
index 2eb316be0f..1a4d12d316 100644
--- a/src/slic3r/GUI/GUI_Preview.cpp
+++ b/src/slic3r/GUI/GUI_Preview.cpp
@@ -341,11 +341,6 @@ void Preview::set_number_extruders(unsigned int number_extruders)
}
}
-void Preview::set_canvas_as_dirty()
-{
- m_canvas->set_as_dirty();
-}
-
void Preview::set_enabled(bool enabled)
{
m_enabled = enabled;
diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp
index b0dac4223d..92ec15b22b 100644
--- a/src/slic3r/GUI/GUI_Preview.hpp
+++ b/src/slic3r/GUI/GUI_Preview.hpp
@@ -116,7 +116,6 @@ public:
void set_as_dirty();
void set_number_extruders(unsigned int number_extruders);
- void set_canvas_as_dirty();
void set_enabled(bool enabled);
void bed_shape_changed();
void select_view(const std::string& direction);
diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp
index ad5be88d8c..707a1d143a 100644
--- a/src/slic3r/GUI/Mouse3DController.cpp
+++ b/src/slic3r/GUI/Mouse3DController.cpp
@@ -57,13 +57,19 @@ const double Mouse3DController::State::DefaultTranslationScale = 2.5;
const double Mouse3DController::State::MaxTranslationDeadzone = 0.2;
const double Mouse3DController::State::DefaultTranslationDeadzone = 0.5 * Mouse3DController::State::MaxTranslationDeadzone;
const float Mouse3DController::State::DefaultRotationScale = 1.0f;
-const float Mouse3DController::State::MaxRotationDeadzone = (float)Mouse3DController::State::MaxTranslationDeadzone;
+const float Mouse3DController::State::MaxRotationDeadzone = 0.2f;
const float Mouse3DController::State::DefaultRotationDeadzone = 0.5f * Mouse3DController::State::MaxRotationDeadzone;
+#if ENABLE_3DCONNEXION_Y_AS_ZOOM
+const double Mouse3DController::State::DefaultZoomScale = 0.1;
+#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
Mouse3DController::State::State()
: m_buttons_enabled(false)
, m_translation_params(DefaultTranslationScale, DefaultTranslationDeadzone)
, m_rotation_params(DefaultRotationScale, DefaultRotationDeadzone)
+#if ENABLE_3DCONNEXION_Y_AS_ZOOM
+ , m_zoom_params(DefaultZoomScale, 0.0)
+#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
, m_mouse_wheel_counter(0)
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
, m_translation_queue_max_size(0)
@@ -112,7 +118,7 @@ void Mouse3DController::State::append_button(unsigned int id)
bool Mouse3DController::State::process_mouse_wheel()
{
- if (m_mouse_wheel_counter == 0)
+ if (m_mouse_wheel_counter.load() == 0)
return false;
else if (!m_rotation.queue.empty())
{
@@ -120,7 +126,7 @@ bool Mouse3DController::State::process_mouse_wheel()
return true;
}
- m_mouse_wheel_counter = 0;
+ m_mouse_wheel_counter.store(0);
return true;
}
@@ -149,7 +155,13 @@ bool Mouse3DController::State::apply(Camera& camera)
if (has_translation())
{
const Vec3d& translation = m_translation.queue.front();
+#if ENABLE_3DCONNEXION_Y_AS_ZOOM
+ camera.set_target(camera.get_target() + m_translation_params.scale * (translation(0) * camera.get_dir_right() + translation(2) * camera.get_dir_up()));
+ if (translation(1) != 0.0)
+ camera.update_zoom(m_zoom_params.scale * translation(1) / std::abs(translation(1)));
+#else
camera.set_target(camera.get_target() + m_translation_params.scale * (translation(0) * camera.get_dir_right() + translation(1) * camera.get_dir_forward() + translation(2) * camera.get_dir_up()));
+#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
m_translation.queue.pop();
ret = true;
}
@@ -229,8 +241,6 @@ bool Mouse3DController::apply(Camera& camera)
if (!m_initialized)
return false;
- std::lock_guard lock(m_mutex);
-
// check if the user unplugged the device
if (!m_running && is_device_connected())
{
@@ -311,20 +321,30 @@ void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsign
ImGui::PopStyleColor();
float translation_scale = (float)m_state.get_translation_scale() / State::DefaultTranslationScale;
- if (imgui.slider_float(_(L("Translation")) + "##1", &translation_scale, 0.5f, 2.0f, "%.1f"))
+ if (imgui.slider_float(_(L("Translation")) + "##1", &translation_scale, 0.2f, 5.0f, "%.1f"))
m_state.set_translation_scale(State::DefaultTranslationScale * (double)translation_scale);
float rotation_scale = m_state.get_rotation_scale() / State::DefaultRotationScale;
- if (imgui.slider_float(_(L("Rotation")) + "##1", &rotation_scale, 0.5f, 2.0f, "%.1f"))
+ if (imgui.slider_float(_(L("Rotation")) + "##1", &rotation_scale, 0.2f, 5.0f, "%.1f"))
m_state.set_rotation_scale(State::DefaultRotationScale * rotation_scale);
+#if ENABLE_3DCONNEXION_Y_AS_ZOOM
+ float zoom_scale = m_state.get_zoom_scale() / State::DefaultZoomScale;
+ if (imgui.slider_float(_(L("Zoom")), &zoom_scale, 0.2f, 5.0f, "%.1f"))
+ m_state.set_zoom_scale(State::DefaultZoomScale * zoom_scale);
+#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
+
ImGui::Separator();
ImGui::PushStyleColor(ImGuiCol_Text, color);
imgui.text(_(L("Deadzone:")));
ImGui::PopStyleColor();
float translation_deadzone = (float)m_state.get_translation_deadzone();
+#if ENABLE_3DCONNEXION_Y_AS_ZOOM
+ if (imgui.slider_float(_(L("Translation")) + "/" + _(L("Zoom")), &translation_deadzone, 0.0f, (float)State::MaxTranslationDeadzone, "%.2f"))
+#else
if (imgui.slider_float(_(L("Translation")) + "##2", &translation_deadzone, 0.0f, (float)State::MaxTranslationDeadzone, "%.2f"))
+#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
m_state.set_translation_deadzone((double)translation_deadzone);
float rotation_deadzone = m_state.get_rotation_deadzone();
@@ -393,7 +413,7 @@ void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsign
bool Mouse3DController::connect_device()
{
- static const long long DETECTION_TIME_MS = 2000; // seconds
+ static const long long DETECTION_TIME_MS = 2000; // two seconds
if (is_device_connected())
return false;
@@ -619,30 +639,36 @@ bool Mouse3DController::connect_device()
hid_get_product_string(m_device, product.data(), 1024);
m_device_str += "/" + boost::nowide::narrow(product.data());
- BOOST_LOG_TRIVIAL(info) << "Connected device: " << m_device_str;
-
-#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
- std::cout << std::endl << "Connected device:" << std::endl;
- std::cout << "Manufacturer/product: " << m_device_str << std::endl;
- std::cout << "Manufacturer id.....: " << vendor_id << " (" << std::hex << vendor_id << std::dec << ")" << std::endl;
- std::cout << "Product id..........: " << product_id << " (" << std::hex << product_id << std::dec << ")" << std::endl;
- std::cout << "Path................: '" << path << "'" << std::endl;
-#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
+ BOOST_LOG_TRIVIAL(info) << "Connected 3DConnexion device:";
+ BOOST_LOG_TRIVIAL(info) << "Manufacturer/product: " << m_device_str;
+ BOOST_LOG_TRIVIAL(info) << "Manufacturer id.....: " << vendor_id << " (" << std::hex << vendor_id << std::dec << ")";
+ BOOST_LOG_TRIVIAL(info) << "Product id..........: " << product_id << " (" << std::hex << product_id << std::dec << ")";
+ if (!path.empty())
+ BOOST_LOG_TRIVIAL(info) << "Path................: '" << path << "'";
// get device parameters from the config, if present
double translation_speed = 1.0;
float rotation_speed = 1.0;
double translation_deadzone = State::DefaultTranslationDeadzone;
float rotation_deadzone = State::DefaultRotationDeadzone;
+#if ENABLE_3DCONNEXION_Y_AS_ZOOM
+ double zoom_speed = 1.0;
+#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
wxGetApp().app_config->get_mouse_device_translation_speed(m_device_str, translation_speed);
wxGetApp().app_config->get_mouse_device_translation_deadzone(m_device_str, translation_deadzone);
wxGetApp().app_config->get_mouse_device_rotation_speed(m_device_str, rotation_speed);
wxGetApp().app_config->get_mouse_device_rotation_deadzone(m_device_str, rotation_deadzone);
+#if ENABLE_3DCONNEXION_Y_AS_ZOOM
+ wxGetApp().app_config->get_mouse_device_zoom_speed(m_device_str, zoom_speed);
+#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
// clamp to valid values
- m_state.set_translation_scale(State::DefaultTranslationScale * std::max(0.5, std::min(2.0, translation_speed)));
- m_state.set_translation_deadzone(std::max(0.0, std::min(State::MaxTranslationDeadzone, translation_deadzone)));
- m_state.set_rotation_scale(State::DefaultRotationScale * std::max(0.5f, std::min(2.0f, rotation_speed)));
- m_state.set_rotation_deadzone(std::max(0.0f, std::min(State::MaxRotationDeadzone, rotation_deadzone)));
+ m_state.set_translation_scale(State::DefaultTranslationScale * std::clamp(translation_speed, 0.2, 5.0));
+ m_state.set_translation_deadzone(std::clamp(translation_deadzone, 0.0, State::MaxTranslationDeadzone));
+ m_state.set_rotation_scale(State::DefaultRotationScale * std::clamp(rotation_speed, 0.2f, 5.0f));
+ m_state.set_rotation_deadzone(std::clamp(rotation_deadzone, 0.0f, State::MaxRotationDeadzone));
+#if ENABLE_3DCONNEXION_Y_AS_ZOOM
+ m_state.set_zoom_scale(State::DefaultZoomScale * std::clamp(zoom_speed, 0.2, 5.0));
+#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
}
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
else
@@ -668,8 +694,13 @@ void Mouse3DController::disconnect_device()
m_thread.join();
// Store current device parameters into the config
+#if ENABLE_3DCONNEXION_Y_AS_ZOOM
+ wxGetApp().app_config->set_mouse_device(m_device_str, m_state.get_translation_scale() / State::DefaultTranslationScale, m_state.get_translation_deadzone(),
+ m_state.get_rotation_scale() / State::DefaultRotationScale, m_state.get_rotation_deadzone(), m_state.get_zoom_scale() / State::DefaultZoomScale);
+#else
wxGetApp().app_config->set_mouse_device(m_device_str, m_state.get_translation_scale() / State::DefaultTranslationScale, m_state.get_translation_deadzone(),
m_state.get_rotation_scale() / State::DefaultRotationScale, m_state.get_rotation_deadzone());
+#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
wxGetApp().app_config->save();
// Close the 3Dconnexion device
@@ -697,7 +728,6 @@ void Mouse3DController::run()
collect_input();
}
}
-
void Mouse3DController::collect_input()
{
DataPacket packet = { 0 };
@@ -712,8 +742,6 @@ void Mouse3DController::collect_input()
if (!wxGetApp().IsActive())
return;
- std::lock_guard lock(m_mutex);
-
bool updated = false;
if (res == 7)
@@ -729,8 +757,11 @@ void Mouse3DController::collect_input()
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
if (updated)
+ {
+ wxGetApp().plater()->set_current_canvas_as_dirty();
// ask for an idle event to update 3D scene
wxWakeUpIdle();
+ }
}
bool Mouse3DController::handle_packet(const DataPacket& packet)
diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp
index 543c44e775..3598d7df7a 100644
--- a/src/slic3r/GUI/Mouse3DController.hpp
+++ b/src/slic3r/GUI/Mouse3DController.hpp
@@ -33,6 +33,9 @@ class Mouse3DController
static const float DefaultRotationScale;
static const float MaxRotationDeadzone;
static const float DefaultRotationDeadzone;
+#if ENABLE_3DCONNEXION_Y_AS_ZOOM
+ static const double DefaultZoomScale;
+#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
private:
template
@@ -64,6 +67,9 @@ class Mouse3DController
CustomParameters m_translation_params;
CustomParameters m_rotation_params;
+#if ENABLE_3DCONNEXION_Y_AS_ZOOM
+ CustomParameters m_zoom_params;
+#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
// When the 3Dconnexion driver is running the system gets, by default, mouse wheel events when rotations around the X axis are detected.
// We want to filter these out because we are getting the data directly from the device, bypassing the driver, and those mouse wheel events interfere
@@ -72,7 +78,7 @@ class Mouse3DController
// Mouse3DController::collect_input() through the call to the append_rotation() method
// GLCanvas3D::on_mouse_wheel() through the call to the process_mouse_wheel() method
// GLCanvas3D::on_idle() through the call to the apply() method
- unsigned int m_mouse_wheel_counter;
+ std::atomic m_mouse_wheel_counter;
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
size_t m_translation_queue_max_size;
@@ -99,6 +105,11 @@ class Mouse3DController
float get_rotation_scale() const { return m_rotation_params.scale; }
void set_rotation_scale(float scale) { m_rotation_params.scale = scale; }
+#if ENABLE_3DCONNEXION_Y_AS_ZOOM
+ double get_zoom_scale() const { return m_zoom_params.scale; }
+ void set_zoom_scale(double scale) { m_zoom_params.scale = scale; }
+#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
+
double get_translation_deadzone() const { return m_translation_params.deadzone; }
void set_translation_deadzone(double deadzone) { m_translation_params.deadzone = deadzone; }
@@ -128,7 +139,6 @@ class Mouse3DController
bool m_initialized;
mutable State m_state;
- std::mutex m_mutex;
std::thread m_thread;
hid_device* m_device;
std::string m_device_str;
@@ -151,7 +161,7 @@ public:
bool is_device_connected() const { return m_device != nullptr; }
bool is_running() const { return m_running; }
- bool process_mouse_wheel() { std::lock_guard lock(m_mutex); return m_state.process_mouse_wheel(); }
+ bool process_mouse_wheel() { return m_state.process_mouse_wheel(); }
bool apply(Camera& camera);
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index b4c5e4bd3c..f1a9a10f23 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -80,6 +80,7 @@
#include "../Utils/FixModelByWin10.hpp"
#include "../Utils/UndoRedo.hpp"
#include "../Utils/Thread.hpp"
+#include "RemovableDriveManager.hpp"
#include // Needs to be last because reasons :-/
#include "WipeTowerDialog.hpp"
@@ -701,7 +702,8 @@ struct Sidebar::priv
wxButton *btn_export_gcode;
wxButton *btn_reslice;
- wxButton *btn_send_gcode;
+ ScalableButton *btn_send_gcode;
+ ScalableButton *btn_remove_device;
priv(Plater *plater) : plater(plater) {}
~priv();
@@ -850,22 +852,47 @@ Sidebar::Sidebar(Plater *parent)
// Buttons underneath the scrolled area
- auto init_btn = [this](wxButton **btn, wxString label) {
+ // rescalable bitmap buttons "Send to printer" and "Remove device"
+
+ auto init_scalable_btn = [this](ScalableButton** btn, const std::string& icon_name, wxString tooltip = wxEmptyString)
+ {
+#ifdef __APPLE__
+ int bmp_px_cnt = 16;
+#else
+ int bmp_px_cnt = 32;
+#endif //__APPLE__
+ ScalableBitmap bmp = ScalableBitmap(this, icon_name, bmp_px_cnt);
+ *btn = new ScalableButton(this, wxID_ANY, bmp, "", wxBU_EXACTFIT);
+ (*btn)->SetToolTip(tooltip);
+ (*btn)->Hide();
+ };
+
+ init_scalable_btn(&p->btn_send_gcode , "export_gcode", _(L("Send to printer")));
+ init_scalable_btn(&p->btn_remove_device, "cross" , _(L("Remove device")));
+
+ // regular buttons "Slice now" and "Export G-code"
+
+ const int scaled_height = p->btn_remove_device->GetBitmapHeight() + 4;
+ auto init_btn = [this](wxButton **btn, wxString label, const int button_height) {
*btn = new wxButton(this, wxID_ANY, label, wxDefaultPosition,
- wxDefaultSize, wxBU_EXACTFIT);
+ wxSize(-1, button_height), wxBU_EXACTFIT);
(*btn)->SetFont(wxGetApp().bold_font());
};
- init_btn(&p->btn_send_gcode, _(L("Send to printer")));
- p->btn_send_gcode->Hide();
- init_btn(&p->btn_export_gcode, _(L("Export G-code")) + dots);
- init_btn(&p->btn_reslice, _(L("Slice now")));
+ init_btn(&p->btn_export_gcode, _(L("Export G-code")) + dots , scaled_height);
+ init_btn(&p->btn_reslice , _(L("Slice now")) , scaled_height);
+
enable_buttons(false);
auto *btns_sizer = new wxBoxSizer(wxVERTICAL);
+
+ auto* complect_btns_sizer = new wxBoxSizer(wxHORIZONTAL);
+ complect_btns_sizer->Add(p->btn_export_gcode, 1, wxEXPAND);
+ complect_btns_sizer->Add(p->btn_send_gcode);
+ complect_btns_sizer->Add(p->btn_remove_device);
+
btns_sizer->Add(p->btn_reslice, 0, wxEXPAND | wxTOP, margin_5);
- btns_sizer->Add(p->btn_send_gcode, 0, wxEXPAND | wxTOP, margin_5);
- btns_sizer->Add(p->btn_export_gcode, 0, wxEXPAND | wxTOP, margin_5);
+ btns_sizer->Add(complect_btns_sizer, 0, wxEXPAND | wxTOP, margin_5);
auto *sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(p->scrolled, 1, wxEXPAND);
@@ -884,6 +911,7 @@ Sidebar::Sidebar(Plater *parent)
p->plater->select_view_3D("Preview");
});
p->btn_send_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->send_gcode(); });
+ p->btn_remove_device->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->eject_drive(); });
}
Sidebar::~Sidebar() {}
@@ -1029,6 +1057,12 @@ void Sidebar::msw_rescale()
p->object_info->msw_rescale();
+ p->btn_send_gcode->msw_rescale();
+ p->btn_remove_device->msw_rescale();
+ const int scaled_height = p->btn_remove_device->GetBitmap().GetHeight() + 4;
+ p->btn_export_gcode->SetMinSize(wxSize(-1, scaled_height));
+ p->btn_reslice ->SetMinSize(wxSize(-1, scaled_height));
+
p->scrolled->Layout();
}
@@ -1257,11 +1291,13 @@ void Sidebar::enable_buttons(bool enable)
p->btn_reslice->Enable(enable);
p->btn_export_gcode->Enable(enable);
p->btn_send_gcode->Enable(enable);
+ p->btn_remove_device->Enable(enable);
}
bool Sidebar::show_reslice(bool show) const { return p->btn_reslice->Show(show); }
bool Sidebar::show_export(bool show) const { return p->btn_export_gcode->Show(show); }
bool Sidebar::show_send(bool show) const { return p->btn_send_gcode->Show(show); }
+bool Sidebar::show_disconnect(bool show)const { return p->btn_remove_device->Show(show); }
bool Sidebar::is_multifilament()
{
@@ -1736,6 +1772,8 @@ struct Plater::priv
bool is_preview_loaded() const { return preview->is_loaded(); }
bool is_view3D_shown() const { return current_panel == view3D; }
+ void set_current_canvas_as_dirty();
+
#if ENABLE_VIEW_TOOLBAR_BACKGROUND_FIX
bool init_view_toolbar();
#endif // ENABLE_VIEW_TOOLBAR_BACKGROUND_FIX
@@ -3140,6 +3178,7 @@ void Plater::priv::update_fff_scene()
this->preview->reload_print();
// In case this was MM print, wipe tower bounding box on 3D tab might need redrawing with exact depth:
view3D->reload_scene(true);
+
}
void Plater::priv::update_sla_scene()
@@ -3308,17 +3347,26 @@ void Plater::priv::reload_from_disk()
new_volume->config.apply(old_volume->config);
new_volume->set_type(old_volume->type());
new_volume->set_material_id(old_volume->material_id());
+#if ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
+ new_volume->set_transformation(old_volume->get_transformation() * old_volume->source.transform);
+#else
new_volume->set_transformation(old_volume->get_transformation());
+#endif // ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
new_volume->translate(new_volume->get_transformation().get_matrix(true) * (new_volume->source.mesh_offset - old_volume->source.mesh_offset));
new_volume->source.input_file = path;
std::swap(old_model_object->volumes[old_v.volume_idx], old_model_object->volumes.back());
old_model_object->delete_volume(old_model_object->volumes.size() - 1);
+#if ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
+ old_model_object->ensure_on_bed();
+#endif // ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
}
}
}
}
+#if !ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
model.adjust_min_z();
+#endif // !ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
// update 3D scene
update();
@@ -3412,7 +3460,7 @@ void Plater::priv::set_current_panel(wxPanel* panel)
// keeps current gcode preview, if any
preview->reload_print(true);
- preview->set_canvas_as_dirty();
+ preview->set_as_dirty();
view_toolbar.select_item("Preview");
}
@@ -3553,6 +3601,7 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt)
break;
default: break;
}
+
if (canceled) {
if (wxGetApp().get_mode() == comSimple)
@@ -3561,6 +3610,16 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt)
}
else if (wxGetApp().get_mode() == comSimple)
show_action_buttons(false);
+
+ if(!canceled && RemovableDriveManager::get_instance().get_is_writing())
+ {
+ //if (!RemovableDriveManager::get_instance().is_last_drive_removed())
+ //{
+ RemovableDriveManager::get_instance().set_is_writing(false);
+ show_action_buttons(false);
+ //}
+
+ }
}
void Plater::priv::on_layer_editing_toggled(bool enable)
@@ -3914,6 +3973,14 @@ bool Plater::priv::complit_init_part_menu()
return true;
}
+void Plater::priv::set_current_canvas_as_dirty()
+{
+ if (current_panel == view3D)
+ view3D->set_as_dirty();
+ else if (current_panel == preview)
+ preview->set_as_dirty();
+}
+
#if ENABLE_VIEW_TOOLBAR_BACKGROUND_FIX
bool Plater::priv::init_view_toolbar()
#else
@@ -4027,7 +4094,11 @@ bool Plater::priv::can_reload_from_disk() const
const GLVolume* v = selection.get_volume(idx);
int v_idx = v->volume_idx();
if (v_idx >= 0)
- selected_volumes.push_back({ v->object_idx(), v_idx });
+ {
+ 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());
@@ -4129,20 +4200,23 @@ void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const
wxWindowUpdateLocker noUpdater(sidebar);
const auto prin_host_opt = config->option("print_host");
const bool send_gcode_shown = prin_host_opt != nullptr && !prin_host_opt->value.empty();
-
+
+ bool disconnect_shown = !RemovableDriveManager::get_instance().is_last_drive_removed() ; // #dk_FIXME
// when a background processing is ON, export_btn and/or send_btn are showing
if (wxGetApp().app_config->get("background_processing") == "1")
{
if (sidebar->show_reslice(false) |
sidebar->show_export(true) |
- sidebar->show_send(send_gcode_shown))
+ sidebar->show_send(send_gcode_shown) |
+ sidebar->show_disconnect(disconnect_shown))
sidebar->Layout();
}
else
{
if (sidebar->show_reslice(is_ready_to_slice) |
sidebar->show_export(!is_ready_to_slice) |
- sidebar->show_send(send_gcode_shown && !is_ready_to_slice))
+ sidebar->show_send(send_gcode_shown && !is_ready_to_slice) |
+ sidebar->show_disconnect(disconnect_shown && !is_ready_to_slice))
sidebar->Layout();
}
}
@@ -4383,7 +4457,7 @@ void Sidebar::set_btn_label(const ActionButtonType btn_type, const wxString& lab
{
case ActionButtonType::abReslice: p->btn_reslice->SetLabelText(label); break;
case ActionButtonType::abExport: p->btn_export_gcode->SetLabelText(label); break;
- case ActionButtonType::abSendGCode: p->btn_send_gcode->SetLabelText(label); break;
+ case ActionButtonType::abSendGCode: /*p->btn_send_gcode->SetLabelText(label);*/ break;
}
}
@@ -4673,7 +4747,13 @@ void Plater::export_gcode()
}
default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string()));
auto start_dir = wxGetApp().app_config->get_last_output_dir(default_output_file.parent_path().string());
-
+ if (GUI::RemovableDriveManager::get_instance().update())
+ {
+ if (!RemovableDriveManager::get_instance().is_path_on_removable_drive(start_dir))
+ {
+ start_dir = RemovableDriveManager::get_instance().get_drive_path();
+ }
+ }
wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _(L("Save G-code file as:")) : _(L("Save SL1 file as:")),
start_dir,
from_path(default_output_file.filename()),
@@ -4688,7 +4768,22 @@ void Plater::export_gcode()
output_path = std::move(path);
}
if (! output_path.empty())
+ {
+ std::string path = output_path.string();
p->export_gcode(std::move(output_path), PrintHostJob());
+
+ RemovableDriveManager::get_instance().update(0, false);
+ RemovableDriveManager::get_instance().set_last_save_path(path);
+ RemovableDriveManager::get_instance().verify_last_save_path();
+
+ if(!RemovableDriveManager::get_instance().is_last_drive_removed())
+ {
+ RemovableDriveManager::get_instance().set_is_writing(true);
+ RemovableDriveManager::get_instance().erase_callbacks();
+ RemovableDriveManager::get_instance().add_callback(std::bind(&Plater::drive_ejected_callback, this));
+ }
+
+ }
}
void Plater::export_stl(bool extended, bool selection_only)
@@ -4973,6 +5068,27 @@ void Plater::send_gcode()
}
}
+void Plater::eject_drive()
+{
+ RemovableDriveManager::get_instance().update(0, true);
+ //RemovableDriveManager::get_instance().erase_callbacks();
+ //RemovableDriveManager::get_instance().add_callback(std::bind(&Plater::drive_ejected_callback, this));
+ RemovableDriveManager::get_instance().eject_drive(RemovableDriveManager::get_instance().get_last_save_path());
+
+}
+void Plater::drive_ejected_callback()
+{
+ if (RemovableDriveManager::get_instance().get_did_eject())
+ {
+ RemovableDriveManager::get_instance().set_did_eject(false);
+ wxString message = "Unmounting succesesful. The device " + RemovableDriveManager::get_instance().get_last_save_name() + "(" + RemovableDriveManager::get_instance().get_last_save_path() + ")" + " can now be safely removed from the computer.";
+ wxMessageBox(message);
+ }
+ p->show_action_buttons(false);
+}
+
+
+
void Plater::take_snapshot(const std::string &snapshot_name) { p->take_snapshot(snapshot_name); }
void Plater::take_snapshot(const wxString &snapshot_name) { p->take_snapshot(snapshot_name); }
void Plater::suppress_snapshots() { p->suppress_snapshots(); }
@@ -5253,6 +5369,11 @@ BoundingBoxf Plater::bed_shape_bb() const
return p->bed_shape_bb();
}
+void Plater::set_current_canvas_as_dirty()
+{
+ p->set_current_canvas_as_dirty();
+}
+
PrinterTechnology Plater::printer_technology() const
{
return p->printer_technology;
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index 730ba1097e..774875cd7c 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -119,6 +119,7 @@ public:
bool show_reslice(bool show) const;
bool show_export(bool show) const;
bool show_send(bool show) const;
+ bool show_disconnect(bool show)const;
bool is_multifilament();
void update_mode();
@@ -202,6 +203,8 @@ public:
void suppress_background_process(const bool stop_background_process) ;
void fix_through_netfabb(const int obj_idx, const int vol_idx = -1);
void send_gcode();
+ void eject_drive();
+ void drive_ejected_callback();
void take_snapshot(const std::string &snapshot_name);
void take_snapshot(const wxString &snapshot_name);
@@ -238,6 +241,8 @@ public:
GLCanvas3D* canvas3D();
BoundingBoxf bed_shape_bb() const;
+ void set_current_canvas_as_dirty();
+
PrinterTechnology printer_technology() const;
void set_printer_technology(PrinterTechnology printer_technology);
diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp
index 4f07a7cf0f..9f64c67ac2 100644
--- a/src/slic3r/GUI/Preset.cpp
+++ b/src/slic3r/GUI/Preset.cpp
@@ -594,6 +594,7 @@ void PresetCollection::reset(bool delete_files)
m_presets.erase(m_presets.begin() + m_num_default_presets, m_presets.end());
this->select_preset(0);
}
+ m_map_system_profile_renamed.clear();
}
void PresetCollection::add_default_preset(const std::vector &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &preset_name)
@@ -707,6 +708,11 @@ Preset& PresetCollection::load_external_preset(
// Is there a preset already loaded with the name stored inside the config?
std::deque::iterator it = this->find_preset_internal(original_name);
bool found = it != m_presets.end() && it->name == original_name;
+ if (! found) {
+ // Try to match the original_name against the "renamed_from" profile names of loaded system profiles.
+ it = this->find_preset_renamed(original_name);
+ found = it != m_presets.end();
+ }
if (found && profile_print_params_same(it->config, cfg)) {
// The preset exists and it matches the values stored inside config.
if (select)
@@ -876,24 +882,27 @@ const Preset* PresetCollection::get_selected_preset_parent() const
if (this->get_selected_idx() == -1)
// This preset collection has no preset activated yet. Only the get_edited_preset() is valid.
return nullptr;
-// const std::string &inherits = this->get_edited_preset().inherits();
-// if (inherits.empty())
-// return this->get_selected_preset().is_system ? &this->get_selected_preset() : nullptr;
- std::string inherits = this->get_edited_preset().inherits();
- if (inherits.empty())
- {
- if (this->get_selected_preset().is_system || this->get_selected_preset().is_default)
- return &this->get_selected_preset();
- if (this->get_selected_preset().is_external)
+ const Preset &selected_preset = this->get_selected_preset();
+ if (selected_preset.is_system || selected_preset.is_default)
+ return &selected_preset;
+
+ const Preset &edited_preset = this->get_edited_preset();
+ const std::string &inherits = edited_preset.inherits();
+ const Preset *preset = nullptr;
+ if (inherits.empty()) {
+ if (selected_preset.is_external)
return nullptr;
-
- inherits = m_type != Preset::Type::TYPE_PRINTER ? "- default -" :
- this->get_edited_preset().printer_technology() == ptFFF ?
- "- default FFF -" : "- default SLA -" ;
+ preset = &this->default_preset(m_type == Preset::Type::TYPE_PRINTER && edited_preset.printer_technology() == ptSLA ? 1 : 0);
+ } else
+ preset = this->find_preset(inherits, false);
+ if (preset == nullptr) {
+ // Resolve the "renamed_from" field.
+ assert(! inherits.empty());
+ auto it = this->find_preset_renamed(inherits);
+ if (it != m_presets.end())
+ preset = &(*it);
}
-
- const Preset* preset = this->find_preset(inherits, false);
return (preset == nullptr/* || preset->is_default*/ || preset->is_external) ? nullptr : preset;
}
@@ -904,6 +913,11 @@ const Preset* PresetCollection::get_preset_parent(const Preset& child) const
// return this->get_selected_preset().is_system ? &this->get_selected_preset() : nullptr;
return nullptr;
const Preset* preset = this->find_preset(inherits, false);
+ if (preset == nullptr) {
+ auto it = this->find_preset_renamed(inherits);
+ if (it != m_presets.end())
+ preset = &(*it);
+ }
return (preset == nullptr/* || preset->is_default */|| preset->is_external) ? nullptr : preset;
}
@@ -1406,6 +1420,17 @@ std::vector PresetCollection::merge_presets(PresetCollection &&othe
return duplicates;
}
+void PresetCollection::update_map_system_profile_renamed()
+{
+ m_map_system_profile_renamed.clear();
+ for (Preset &preset : m_presets)
+ for (const std::string &renamed_from : preset.renamed_from) {
+ const auto [it, success] = m_map_system_profile_renamed.insert(std::pair(renamed_from, preset.name));
+ if (! success)
+ BOOST_LOG_TRIVIAL(error) << boost::format("Preset name \"%1%\" was marked as renamed from \"%2%\", though preset name \"%3%\" was marked as renamed from \"%2%\" as well.") % preset.name % renamed_from % it->second;
+ }
+}
+
std::string PresetCollection::name() const
{
switch (this->type()) {
diff --git a/src/slic3r/GUI/Preset.hpp b/src/slic3r/GUI/Preset.hpp
index 785f52c429..547814559c 100644
--- a/src/slic3r/GUI/Preset.hpp
+++ b/src/slic3r/GUI/Preset.hpp
@@ -163,6 +163,10 @@ public:
// Alias of the preset
std::string alias = "";
+ // List of profile names, from which this profile was renamed at some point of time.
+ // This list is then used to match profiles by their names when loaded from .gcode, .3mf, .amf,
+ // and to match the "inherits" field of user profiles with updated system profiles.
+ std::vector renamed_from;
void save();
@@ -333,7 +337,8 @@ public:
// The parent preset may be a system preset or a user preset, which will be
// reflected by the UI.
const Preset* get_selected_preset_parent() const;
- // get parent preset for some child preset
+ // Get parent preset for a child preset, based on the "inherits" field of a child,
+ // where the "inherits" profile name is searched for in both m_presets and m_map_system_profile_renamed.
const Preset* get_preset_parent(const Preset& child) const;
// Return the selected preset including the user modifications.
Preset& get_edited_preset() { return m_edited_preset; }
@@ -465,6 +470,9 @@ protected:
// Merge one vendor's presets with the other vendor's presets, report duplicates.
std::vector merge_presets(PresetCollection &&other, const VendorMap &new_vendors);
+ // Update m_map_system_profile_renamed from loaded system profiles.
+ void update_map_system_profile_renamed();
+
private:
PresetCollection();
PresetCollection(const PresetCollection &other);
@@ -490,6 +498,14 @@ private:
}
std::deque::const_iterator find_preset_internal(const std::string &name) const
{ return const_cast(this)->find_preset_internal(name); }
+ std::deque::iterator find_preset_renamed(const std::string &name) {
+ auto it_renamed = m_map_system_profile_renamed.find(name);
+ auto it = (it_renamed == m_map_system_profile_renamed.end()) ? m_presets.end() : this->find_preset_internal(it_renamed->second);
+ assert((it_renamed == m_map_system_profile_renamed.end()) || (it != m_presets.end() && it->name == it_renamed->second));
+ return it;
+ }
+ std::deque::const_iterator find_preset_renamed(const std::string &name) const
+ { return const_cast(this)->find_preset_renamed(name); }
size_t update_compatible_internal(const PresetWithVendorProfile &active_printer, const PresetWithVendorProfile *active_print, bool unselect_if_incompatible);
@@ -501,6 +517,8 @@ private:
// Use deque to force the container to allocate an object per each entry,
// so that the addresses of the presets don't change during resizing of the container.
std::deque m_presets;
+ // Map from old system profile name to a current system profile name.
+ std::map m_map_system_profile_renamed;
// Initially this preset contains a copy of the selected preset. Later on, this copy may be modified by the user.
Preset m_edited_preset;
// Selected preset.
diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp
index dbfd446b11..219d7b89da 100644
--- a/src/slic3r/GUI/PresetBundle.cpp
+++ b/src/slic3r/GUI/PresetBundle.cpp
@@ -288,6 +288,11 @@ std::string PresetBundle::load_system_presets()
// No config bundle loaded, reset.
this->reset(false);
}
+ this->prints .update_map_system_profile_renamed();
+ this->sla_prints .update_map_system_profile_renamed();
+ this->filaments .update_map_system_profile_renamed();
+ this->sla_materials.update_map_system_profile_renamed();
+ this->printers .update_map_system_profile_renamed();
return errors_cummulative;
}
@@ -555,9 +560,11 @@ DynamicPrintConfig PresetBundle::full_fff_config() const
while (filament_configs.size() < num_extruders)
filament_configs.emplace_back(&this->filaments.first_visible().config);
for (const DynamicPrintConfig *cfg : filament_configs) {
- compatible_printers_condition.emplace_back(Preset::compatible_printers_condition(*const_cast(cfg)));
- compatible_prints_condition .emplace_back(Preset::compatible_prints_condition(*const_cast(cfg)));
- inherits .emplace_back(Preset::inherits(*const_cast(cfg)));
+ // The compatible_prints/printers_condition() returns a reference to configuration key, which may not yet exist.
+ DynamicPrintConfig &cfg_rw = *const_cast(cfg);
+ compatible_printers_condition.emplace_back(Preset::compatible_printers_condition(cfg_rw));
+ compatible_prints_condition .emplace_back(Preset::compatible_prints_condition(cfg_rw));
+ inherits .emplace_back(Preset::inherits(cfg_rw));
}
// Option values to set a ConfigOptionVector from.
std::vector filament_opts(num_extruders, nullptr);
@@ -1132,7 +1139,6 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
PresetCollection *presets = nullptr;
std::vector *loaded = nullptr;
std::string preset_name;
- std::string alias_name;
if (boost::starts_with(section.first, "print:")) {
presets = &this->prints;
loaded = &loaded_prints;
@@ -1141,12 +1147,6 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
presets = &this->filaments;
loaded = &loaded_filaments;
preset_name = section.first.substr(9);
-
- for (const auto& item : section.second)
- if (boost::starts_with(item.first, "alias")) {
- alias_name = item.second.data();
- break;
- }
} else if (boost::starts_with(section.first, "sla_print:")) {
presets = &this->sla_prints;
loaded = &loaded_sla_prints;
@@ -1155,9 +1155,6 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
presets = &this->sla_materials;
loaded = &loaded_sla_materials;
preset_name = section.first.substr(13);
-
- int end_pos = preset_name.find_first_of("0.");
- alias_name = preset_name.substr(0, end_pos-1);
} else if (boost::starts_with(section.first, "printer:")) {
presets = &this->printers;
loaded = &loaded_printers;
@@ -1213,19 +1210,32 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
// Load the print, filament or printer preset.
const DynamicPrintConfig *default_config = nullptr;
DynamicPrintConfig config;
+ std::string alias_name;
+ std::vector renamed_from;
+ auto parse_config_section = [§ion, &alias_name, &renamed_from, &path](DynamicPrintConfig &config) {
+ for (auto &kvp : section.second) {
+ if (kvp.first == "alias")
+ alias_name = kvp.second.data();
+ else if (kvp.first == "renamed_from") {
+ if (! unescape_strings_cstyle(kvp.second.data(), renamed_from)) {
+ BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The preset \"" <<
+ section.first << "\" contains invalid \"renamed_from\" key, which is being ignored.";
+ }
+ }
+ config.set_deserialize(kvp.first, kvp.second.data());
+ }
+ };
if (presets == &this->printers) {
// Select the default config based on the printer_technology field extracted from kvp.
DynamicPrintConfig config_src;
- for (auto &kvp : section.second)
- config_src.set_deserialize(kvp.first, kvp.second.data());
+ parse_config_section(config_src);
default_config = &presets->default_preset_for(config_src).config;
config = *default_config;
config.apply(config_src);
} else {
default_config = &presets->default_preset().config;
config = *default_config;
- for (auto &kvp : section.second)
- config.set_deserialize(kvp.first, kvp.second.data());
+ parse_config_section(config);
}
Preset::normalize(config);
// Report configuration fields, which are misplaced into a wrong group.
@@ -1304,12 +1314,22 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
loaded.vendor = vendor_profile;
}
- // next step of an preset name aliasing
- int end_pos = preset_name.find_first_of("@");
- if (end_pos != std::string::npos)
- alias_name = preset_name.substr(0, end_pos - 1);
-
- loaded.alias = alias_name.empty() ? preset_name : alias_name;
+ // Derive the profile logical name aka alias from the preset name if the alias was not stated explicitely.
+ if (alias_name.empty()) {
+ int end_pos = preset_name.find_first_of("@");
+ if (end_pos != std::string::npos) {
+ alias_name = preset_name.substr(0, end_pos);
+ if (renamed_from.empty())
+ // Add the preset name with the '@' character removed into the "renamed_from" list.
+ renamed_from.emplace_back(alias_name + preset_name.substr(end_pos + 1));
+ boost::trim_right(alias_name);
+ }
+ }
+ if (alias_name.empty())
+ loaded.alias = preset_name;
+ else
+ loaded.alias = std::move(alias_name);
+ loaded.renamed_from = std::move(renamed_from);
++ presets_loaded;
}
diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp
new file mode 100644
index 0000000000..5041e21205
--- /dev/null
+++ b/src/slic3r/GUI/RemovableDriveManager.cpp
@@ -0,0 +1,590 @@
+#include "RemovableDriveManager.hpp"
+#include
+#include "boost/nowide/convert.hpp"
+
+#if _WIN32
+#include
+#include
+#include
+#include
+
+#include
+GUID WceusbshGUID = { 0x25dbce51, 0x6c8f, 0x4a72,
+ 0x8a,0x6d,0xb5,0x4c,0x2b,0x4f,0xc8,0x35 };
+
+#else
+//linux includes
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#endif
+
+namespace Slic3r {
+namespace GUI {
+
+#if _WIN32
+INT_PTR WINAPI WinProcCallback(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
+void RemovableDriveManager::search_for_drives()
+{
+ m_current_drives.clear();
+ //get logical drives flags by letter in alphabetical order
+ DWORD drives_mask = GetLogicalDrives();
+ for (size_t i = 0; i < 26; i++)
+ {
+ if(drives_mask & (1 << i))
+ {
+ std::string path (1,(char)('A' + i));
+ path+=":";
+ UINT drive_type = GetDriveTypeA(path.c_str());
+ // DRIVE_REMOVABLE on W are sd cards and usb thumbnails (not usb harddrives)
+ if (drive_type == DRIVE_REMOVABLE)
+ {
+ // get name of drive
+ std::wstring wpath = boost::nowide::widen(path);//std::wstring(path.begin(), path.end());
+ std::wstring volume_name;
+ volume_name.resize(1024);
+ std::wstring file_system_name;
+ file_system_name.resize(1024);
+ LPWSTR lp_volume_name_buffer = new wchar_t;
+ BOOL error = GetVolumeInformationW(wpath.c_str(), &volume_name[0], sizeof(volume_name), NULL, NULL, NULL, &file_system_name[0], sizeof(file_system_name));
+ if(error != 0)
+ {
+ volume_name.erase(std::find(volume_name.begin(), volume_name.end(), '\0'), volume_name.end());
+ /*
+ if (volume_name == L"")
+ {
+ volume_name = L"REMOVABLE DRIVE";
+ }
+ */
+ if (file_system_name != L"")
+ {
+ ULARGE_INTEGER free_space;
+ GetDiskFreeSpaceExA(path.c_str(), &free_space, NULL, NULL);
+ if (free_space.QuadPart > 0)
+ {
+ path += "\\";
+ m_current_drives.push_back(DriveData(boost::nowide::narrow(volume_name), path));
+ }
+ }
+ }
+ }
+ }
+ }
+}
+void RemovableDriveManager::eject_drive(const std::string &path)
+{
+ if(m_current_drives.empty())
+ return;
+ for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
+ {
+ if ((*it).path == path)
+ {
+ // get handle to device
+ std::string mpath = "\\\\.\\" + path;
+ mpath = mpath.substr(0, mpath.size() - 1);
+ HANDLE handle = CreateFileA(mpath.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
+ if (handle == INVALID_HANDLE_VALUE)
+ {
+ std::cerr << "Ejecting " << mpath << " failed " << GetLastError() << " \n";
+ return;
+ }
+ DWORD deviceControlRetVal(0);
+ //these 3 commands should eject device safely but they dont, the device does disappear from file explorer but the "device was safely remove" notification doesnt trigger.
+ //sd cards does trigger WM_DEVICECHANGE messege, usb drives dont
+
+ DeviceIoControl(handle, FSCTL_LOCK_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
+ DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
+ // some implemenatations also calls IOCTL_STORAGE_MEDIA_REMOVAL here but it returns error to me
+ BOOL error = DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
+ if (error == 0)
+ {
+ CloseHandle(handle);
+ std::cerr << "Ejecting " << mpath << " failed " << deviceControlRetVal << " " << GetLastError() << " \n";
+ return;
+ }
+ CloseHandle(handle);
+ m_did_eject = true;
+ m_current_drives.erase(it);
+ break;
+ }
+ }
+}
+bool RemovableDriveManager::is_path_on_removable_drive(const std::string &path)
+{
+ if (m_current_drives.empty())
+ return false;
+ std::size_t found = path.find_last_of("\\");
+ std::string new_path = path.substr(0, found);
+ int letter = PathGetDriveNumberA(new_path.c_str());
+ for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
+ {
+ char drive = (*it).path[0];
+ if (drive == ('A' + letter))
+ return true;
+ }
+ return false;
+}
+std::string RemovableDriveManager::get_drive_from_path(const std::string& path)
+{
+ std::size_t found = path.find_last_of("\\");
+ std::string new_path = path.substr(0, found);
+ int letter = PathGetDriveNumberA(new_path.c_str());
+ for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
+ {
+ char drive = (*it).path[0];
+ if (drive == ('A' + letter))
+ return (*it).path;
+ }
+ return "";
+}
+void RemovableDriveManager::register_window()
+{
+ //creates new unvisible window that is recieving callbacks from system
+ // structure to register
+ WNDCLASSEX wndClass;
+ wndClass.cbSize = sizeof(WNDCLASSEX);
+ wndClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
+ wndClass.hInstance = reinterpret_cast(GetModuleHandle(0));
+ wndClass.lpfnWndProc = reinterpret_cast(WinProcCallback);//this is callback
+ wndClass.cbClsExtra = 0;
+ wndClass.cbWndExtra = 0;
+ wndClass.hIcon = LoadIcon(0, IDI_APPLICATION);
+ wndClass.hbrBackground = CreateSolidBrush(RGB(192, 192, 192));
+ wndClass.hCursor = LoadCursor(0, IDC_ARROW);
+ wndClass.lpszClassName = L"PrusaSlicer_aux_class";
+ wndClass.lpszMenuName = NULL;
+ wndClass.hIconSm = wndClass.hIcon;
+ if(!RegisterClassEx(&wndClass))
+ {
+ DWORD err = GetLastError();
+ return;
+ }
+
+ HWND hWnd = CreateWindowEx(
+ WS_EX_NOACTIVATE,
+ L"PrusaSlicer_aux_class",
+ L"PrusaSlicer_aux_wnd",
+ WS_DISABLED, // style
+ CW_USEDEFAULT, 0,
+ 640, 480,
+ NULL, NULL,
+ GetModuleHandle(NULL),
+ NULL);
+ if(hWnd == NULL)
+ {
+ DWORD err = GetLastError();
+ }
+ //ShowWindow(hWnd, SW_SHOWNORMAL);
+ UpdateWindow(hWnd);
+}
+
+INT_PTR WINAPI WinProcCallback(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ // here we need to catch messeges about device removal
+ // problem is that when ejecting usb (how is it implemented above) there is no messege dispached. Only after physical removal of the device.
+ //uncomment register_window() in init() to register and comment update() in GUI_App.cpp (only for windows!) to stop recieving periodical updates
+ LRESULT lRet = 1;
+ static HDEVNOTIFY hDeviceNotify;
+
+ switch (message)
+ {
+ case WM_CREATE:
+ DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
+
+ ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
+ NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
+ NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
+ NotificationFilter.dbcc_classguid = WceusbshGUID;
+
+ hDeviceNotify = RegisterDeviceNotification(hWnd, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
+ break;
+
+ case WM_DEVICECHANGE:
+ {
+ // here is the important
+ if(wParam == DBT_DEVICEREMOVECOMPLETE)
+ {
+- RemovableDriveManager::get_instance().update(0, true);
+ }
+ }
+ break;
+
+ default:
+ // Send all other messages on to the default windows handler.
+ lRet = DefWindowProc(hWnd, message, wParam, lParam);
+ break;
+ }
+ return lRet;
+}
+
+#else
+void RemovableDriveManager::search_for_drives()
+{
+
+ m_current_drives.clear();
+
+#if __APPLE__
+ // if on macos obj-c class will enumerate
+ if(m_rdmmm)
+ {
+ m_rdmmm->list_devices();
+ }
+#else
+
+
+ //search /media/* folder
+ search_path("/media/*", "/media");
+
+ //search /Volumes/* folder (OSX)
+ //search_path("/Volumes/*", "/Volumes");
+ std::string path(std::getenv("USER"));
+ std::string pp(path);
+ //std::cout << "user: "<< path << "\n";
+ //if program is run with sudo, we have to search for all users
+ // but do we want that?
+ /*
+ if(path == "root"){
+ while (true) {
+ passwd* entry = getpwent();
+ if (!entry) {
+ break;
+ }
+ path = entry->pw_name;
+ pp = path;
+ //search /media/USERNAME/* folder
+ pp = "/media/"+pp;
+ path = "/media/" + path + "/*";
+ search_path(path, pp);
+
+ //search /run/media/USERNAME/* folder
+ path = "/run" + path;
+ pp = "/run"+pp;
+ search_path(path, pp);
+ }
+ endpwent();
+ }else
+ */
+ {
+ //search /media/USERNAME/* folder
+ pp = "/media/"+pp;
+ path = "/media/" + path + "/*";
+ search_path(path, pp);
+
+ //search /run/media/USERNAME/* folder
+ path = "/run" + path;
+ pp = "/run"+pp;
+ search_path(path, pp);
+
+ }
+#endif
+}
+void RemovableDriveManager::search_path(const std::string &path,const std::string &parent_path)
+{
+ glob_t globbuf;
+ globbuf.gl_offs = 2;
+ int error = glob(path.c_str(), GLOB_TILDE, NULL, &globbuf);
+ if(error == 0)
+ {
+ for(size_t i = 0; i < globbuf.gl_pathc; i++)
+ {
+ inspect_file(globbuf.gl_pathv[i], parent_path);
+ }
+ }else
+ {
+ //if error - path probably doesnt exists so function just exits
+ //std::cout<<"glob error "<< error<< "\n";
+ }
+
+ globfree(&globbuf);
+}
+void RemovableDriveManager::inspect_file(const std::string &path, const std::string &parent_path)
+{
+ //confirms if the file is removable drive and adds it to vector
+
+ //if not same file system - could be removable drive
+ if(!compare_filesystem_id(path, parent_path))
+ {
+ //free space
+ boost::filesystem::space_info si = boost::filesystem::space(path);
+ //std::cout << "Free space: " << fs_si.free << "Available space: " << fs_si.available << " " << path << '\n';
+ if(si.available != 0)
+ {
+ //user id
+ struct stat buf;
+ stat(path.c_str(), &buf);
+ uid_t uid = buf.st_uid;
+ std::string username(std::getenv("USER"));
+ struct passwd *pw = getpwuid(uid);
+ if (pw != 0 && pw->pw_name == username)
+ m_current_drives.push_back(DriveData(boost::filesystem::basename(boost::filesystem::path(path)), path));
+ }
+
+ }
+}
+bool RemovableDriveManager::compare_filesystem_id(const std::string &path_a, const std::string &path_b)
+{
+ struct stat buf;
+ stat(path_a.c_str() ,&buf);
+ dev_t id_a = buf.st_dev;
+ stat(path_b.c_str() ,&buf);
+ dev_t id_b = buf.st_dev;
+ return id_a == id_b;
+}
+void RemovableDriveManager::eject_drive(const std::string &path)
+{
+ if (m_current_drives.empty())
+ return;
+
+ for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
+ {
+ if((*it).path == path)
+ {
+
+ std::string correct_path(path);
+ for (size_t i = 0; i < correct_path.size(); ++i)
+ {
+ if(correct_path[i]==' ')
+ {
+ correct_path = correct_path.insert(i,1,'\\');
+ i++;
+ }
+ }
+ std::cout<<"Ejecting "<<(*it).name<<" from "<< correct_path<<"\n";
+// there is no usable command in c++ so terminal command is used instead
+// but neither triggers "succesful safe removal messege"
+ std::string command = "";
+#if __APPLE__
+ //m_rdmmm->eject_device(path);
+ command = "diskutil unmount ";
+#else
+ command = "umount ";
+#endif
+ command += correct_path;
+ int err = system(command.c_str());
+ if(err)
+ {
+ std::cerr<<"Ejecting failed\n";
+ return;
+ }
+
+ m_did_eject = true;
+ m_current_drives.erase(it);
+
+ break;
+ }
+
+ }
+
+}
+bool RemovableDriveManager::is_path_on_removable_drive(const std::string &path)
+{
+ if (m_current_drives.empty())
+ return false;
+ std::size_t found = path.find_last_of("/");
+ std::string new_path = path.substr(0,found);
+ for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
+ {
+ if(compare_filesystem_id(new_path, (*it).path))
+ return true;
+ }
+ return false;
+}
+std::string RemovableDriveManager::get_drive_from_path(const std::string& path)
+{
+ std::size_t found = path.find_last_of("/");
+ std::string new_path = path.substr(0, found);
+ //check if same filesystem
+ for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
+ {
+ if (compare_filesystem_id(new_path, (*it).path))
+ return (*it).path;
+ }
+ return "";
+}
+#endif
+
+RemovableDriveManager::RemovableDriveManager():
+ m_drives_count(0),
+ m_last_update(0),
+ m_last_save_path(""),
+ m_last_save_name(""),
+ m_last_save_path_verified(false),
+ m_is_writing(false),
+ m_did_eject(false)
+#if __APPLE__
+ , m_rdmmm(new RDMMMWrapper())
+#endif
+{}
+RemovableDriveManager::~RemovableDriveManager()
+{
+#if __APPLE__
+ delete m_rdmmm;
+#endif
+}
+void RemovableDriveManager::init()
+{
+ //add_callback([](void) { RemovableDriveManager::get_instance().print(); });
+#if _WIN32
+ //register_window();
+#elif __APPLE__
+ m_rdmmm->register_window();
+#endif
+ update(0, true);
+}
+bool RemovableDriveManager::update(const long time,const bool check)
+{
+ if(time != 0) //time = 0 is forced update
+ {
+ long diff = m_last_update - time;
+ if(diff <= -2)
+ {
+ m_last_update = time;
+ }else
+ {
+ return false; // return value shouldnt matter if update didnt run
+ }
+ }
+ search_for_drives();
+ if (m_drives_count != m_current_drives.size())
+ {
+ if (check)check_and_notify();
+ m_drives_count = m_current_drives.size();
+ }
+ return !m_current_drives.empty();
+}
+
+bool RemovableDriveManager::is_drive_mounted(const std::string &path)
+{
+ for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
+ {
+ if ((*it).path == path)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+std::string RemovableDriveManager::get_drive_path()
+{
+ if (m_current_drives.size() == 0)
+ {
+ reset_last_save_path();
+ return "";
+ }
+ if (m_last_save_path_verified)
+ return m_last_save_path;
+ return m_current_drives.back().path;
+}
+std::string RemovableDriveManager::get_last_save_path()
+{
+ if (!m_last_save_path_verified)
+ return "";
+ return m_last_save_path;
+}
+std::string RemovableDriveManager::get_last_save_name()
+{
+ return m_last_save_name;
+}
+std::vector RemovableDriveManager::get_all_drives()
+{
+ return m_current_drives;
+}
+void RemovableDriveManager::check_and_notify()
+{
+ if(m_callbacks.size() != 0 && m_drives_count > m_current_drives.size() && m_last_save_path_verified && !is_drive_mounted(m_last_save_path))
+ {
+ for (auto it = m_callbacks.begin(); it != m_callbacks.end(); ++it)
+ {
+ (*it)();
+ }
+ }
+}
+void RemovableDriveManager::add_callback(std::function callback)
+{
+ m_callbacks.push_back(callback);
+}
+void RemovableDriveManager::erase_callbacks()
+{
+ m_callbacks.clear();
+}
+void RemovableDriveManager::set_last_save_path(const std::string& path)
+{
+ m_last_save_path_verified = false;
+ m_last_save_path = path;
+}
+void RemovableDriveManager::verify_last_save_path()
+{
+ std::string last_drive = get_drive_from_path(m_last_save_path);
+ if (last_drive != "")
+ {
+ m_last_save_path_verified = true;
+ m_last_save_path = last_drive;
+ m_last_save_name = get_drive_name(last_drive);
+ }else
+ {
+ reset_last_save_path();
+ }
+}
+std::string RemovableDriveManager::get_drive_name(const std::string& path)
+{
+ if (m_current_drives.size() == 0)
+ return "";
+ for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
+ {
+ if ((*it).path == path)
+ {
+ return (*it).name;
+ }
+ }
+ return "";
+}
+bool RemovableDriveManager::is_last_drive_removed()
+{
+ //std::cout<<"is last: "<
+#include
+#include
+
+namespace Slic3r {
+namespace GUI {
+#if __APPLE__
+class RDMMMWrapper;
+#endif
+
+struct DriveData
+{
+ std::string name;
+ std::string path;
+ DriveData(std::string n, std::string p):name(n),path(p){}
+};
+class RemovableDriveManager
+{
+#if __APPLE__
+friend class RDMMMWrapper;
+#endif
+public:
+ static RemovableDriveManager& get_instance()
+ {
+ static RemovableDriveManager instance;
+ return instance;
+ }
+ RemovableDriveManager(RemovableDriveManager const&) = delete;
+ void operator=(RemovableDriveManager const&) = delete;
+ ~RemovableDriveManager();
+ //call only once. on apple register for unmnount callbacks. on windows register for device notification is prepared but not called (eject usb drive on widnows doesnt trigger the callback, sdc ard does), also enumerates devices for first time so init shoud be called on linux too.
+ void init();
+ //update() searches for removable devices, returns false if empty. /time = 0 is forced update, time expects wxGetLocalTime()
+ bool update(const long time = 0,const bool check = false);
+ bool is_drive_mounted(const std::string &path);
+ void eject_drive(const std::string &path);
+ //returns path to last drive which was used, if none was used, returns device that was enumerated last
+ std::string get_last_save_path();
+ std::string get_last_save_name();
+ //returns path to last drive which was used, if none was used, returns empty string
+ std::string get_drive_path();
+ std::vector get_all_drives();
+ bool is_path_on_removable_drive(const std::string &path);
+ // callback will notify only if device with last save path was removed
+ void add_callback(std::function callback);
+ // erases all callbacks added by add_callback()
+ void erase_callbacks();
+ // marks one of the eveices in vector as last used
+ void set_last_save_path(const std::string &path);
+ void verify_last_save_path();
+ bool is_last_drive_removed();
+ // param as update()
+ bool is_last_drive_removed_with_update(const long time = 0);
+ void set_is_writing(const bool b);
+ bool get_is_writing();
+ bool get_did_eject();
+ void set_did_eject(const bool b);
+ std::string get_drive_name(const std::string& path);
+private:
+ RemovableDriveManager();
+ void search_for_drives();
+ //triggers callbacks if last used drive was removed
+ void check_and_notify();
+ //returns drive path (same as path in DriveData) if exists otherwise empty string ""
+ std::string get_drive_from_path(const std::string& path);
+ void reset_last_save_path();
+
+ std::vector m_current_drives;
+ std::vector> m_callbacks;
+ size_t m_drives_count;
+ long m_last_update;
+ std::string m_last_save_path;
+ bool m_last_save_path_verified;
+ std::string m_last_save_name;
+ bool m_is_writing;//on device
+ bool m_did_eject;
+#if _WIN32
+ //registers for notifications by creating invisible window
+ void register_window();
+#else
+#if __APPLE__
+ RDMMMWrapper * m_rdmmm;
+ #endif
+ void search_path(const std::string &path, const std::string &parent_path);
+ bool compare_filesystem_id(const std::string &path_a, const std::string &path_b);
+ void inspect_file(const std::string &path, const std::string &parent_path);
+#endif
+};
+// apple wrapper for RemovableDriveManagerMM which searches for drives and/or ejects them
+#if __APPLE__
+class RDMMMWrapper
+{
+public:
+ RDMMMWrapper();
+ ~RDMMMWrapper();
+ void register_window();
+ void list_devices();
+ void eject_device(const std::string &path);
+ void log(const std::string &msg);
+protected:
+ void *m_imp;
+ //friend void RemovableDriveManager::inspect_file(const std::string &path, const std::string &parent_path);
+};
+#endif
+}}
+#endif
+
diff --git a/src/slic3r/GUI/RemovableDriveManagerMM.h b/src/slic3r/GUI/RemovableDriveManagerMM.h
new file mode 100644
index 0000000000..71239dba32
--- /dev/null
+++ b/src/slic3r/GUI/RemovableDriveManagerMM.h
@@ -0,0 +1,10 @@
+#import
+
+@interface RemovableDriveManagerMM : NSObject
+
+-(instancetype) init;
+-(void) add_unmount_observer;
+-(void) on_device_unmount: (NSNotification*) notification;
+-(NSArray*) list_dev;
+-(void)eject_drive:(NSString *)path;
+@end
diff --git a/src/slic3r/GUI/RemovableDriveManagerMM.mm b/src/slic3r/GUI/RemovableDriveManagerMM.mm
new file mode 100644
index 0000000000..01d38b1855
--- /dev/null
+++ b/src/slic3r/GUI/RemovableDriveManagerMM.mm
@@ -0,0 +1,157 @@
+#import "RemovableDriveManager.hpp"
+#import "RemovableDriveManagerMM.h"
+#import
+#import
+
+@implementation RemovableDriveManagerMM
+
+
+
+-(instancetype) init
+{
+ self = [super init];
+ if(self)
+ {
+ }
+ return self;
+}
+-(void) on_device_unmount: (NSNotification*) notification
+{
+ NSLog(@"on device change");
+ Slic3r::GUI::RemovableDriveManager::get_instance().update(0,true);
+}
+-(void) add_unmount_observer
+{
+ NSLog(@"add unmount observer");
+ [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector: @selector(on_device_unmount:) name:NSWorkspaceDidUnmountNotification object:nil];
+ [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector: @selector(on_device_unmount:) name:NSWorkspaceDidMountNotification object:nil];
+}
+-(NSArray*) list_dev
+{
+ // DEPRICATED:
+ //NSArray* devices = [[NSWorkspace sharedWorkspace] mountedRemovableMedia];
+ //return devices;
+
+ NSArray *mountedRemovableMedia = [[NSFileManager defaultManager] mountedVolumeURLsIncludingResourceValuesForKeys:nil options:NSVolumeEnumerationSkipHiddenVolumes];
+ NSMutableArray *result = [NSMutableArray array];
+ for(NSURL *volURL in mountedRemovableMedia)
+ {
+ int err = 0;
+ DADiskRef disk;
+ DASessionRef session;
+ CFDictionaryRef descDict;
+ session = DASessionCreate(NULL);
+ if (session == NULL) {
+ err = EINVAL;
+ }
+ if (err == 0) {
+ disk = DADiskCreateFromVolumePath(NULL,session,(CFURLRef)volURL);
+ if (session == NULL) {
+ err = EINVAL;
+ }
+ }
+ if (err == 0) {
+ descDict = DADiskCopyDescription(disk);
+ if (descDict == NULL) {
+ err = EINVAL;
+ }
+ }
+ if (err == 0) {
+ CFTypeRef mediaEjectableKey = CFDictionaryGetValue(descDict,kDADiskDescriptionMediaEjectableKey);
+ BOOL ejectable = [mediaEjectableKey boolValue];
+ CFTypeRef deviceProtocolName = CFDictionaryGetValue(descDict,kDADiskDescriptionDeviceProtocolKey);
+ CFTypeRef deviceModelKey = CFDictionaryGetValue(descDict, kDADiskDescriptionDeviceModelKey);
+ if (mediaEjectableKey != NULL)
+ {
+ BOOL op = ejectable && (CFEqual(deviceProtocolName, CFSTR("USB")) || CFEqual(deviceModelKey, CFSTR("SD Card Reader")));
+ //!CFEqual(deviceModelKey, CFSTR("Disk Image"));
+ //
+ if (op) {
+ [result addObject:volURL.path];
+ }
+ }
+ }
+ if (descDict != NULL) {
+ CFRelease(descDict);
+ }
+
+
+ }
+ return result;
+}
+-(void)eject_drive:(NSString *)path
+{
+ DADiskRef disk;
+ DASessionRef session;
+ NSURL *url = [[NSURL alloc] initFileURLWithPath:path];
+ int err = 0;
+ session = DASessionCreate(NULL);
+ if (session == NULL) {
+ err = EINVAL;
+ }
+ if (err == 0) {
+ disk = DADiskCreateFromVolumePath(NULL,session,(CFURLRef)url);
+ }
+ if( err == 0)
+ {
+ DADiskUnmount(disk, kDADiskUnmountOptionDefault,
+ NULL, NULL);
+ }
+ if (disk != NULL) {
+ CFRelease(disk);
+ }
+ if (session != NULL) {
+ CFRelease(session);
+ }
+}
+namespace Slic3r {
+namespace GUI {
+RDMMMWrapper::RDMMMWrapper():m_imp(nullptr){
+ m_imp = [[RemovableDriveManagerMM alloc] init];
+}
+RDMMMWrapper::~RDMMMWrapper()
+{
+ if(m_imp)
+ {
+ [m_imp release];
+ }
+}
+void RDMMMWrapper::register_window()
+{
+ if(m_imp)
+ {
+ [m_imp add_unmount_observer];
+ }
+}
+void RDMMMWrapper::list_devices()
+{
+ if(m_imp)
+ {
+ NSArray* devices = [m_imp list_dev];
+ for (NSString* volumePath in devices)
+ {
+ NSLog(@"%@", volumePath);
+ Slic3r::GUI::RemovableDriveManager::get_instance().inspect_file(std::string([volumePath UTF8String]), "/Volumes");
+ }
+ }
+}
+void RDMMMWrapper::log(const std::string &msg)
+{
+ NSLog(@"%s", msg.c_str());
+}
+void RDMMMWrapper::eject_device(const std::string &path)
+{
+ if(m_imp)
+ {
+ NSString * pth = [NSString stringWithCString:path.c_str()
+ encoding:[NSString defaultCStringEncoding]];
+ [m_imp eject_drive:pth];
+ }
+}
+}}//namespace Slicer::GUI
+
+/*
+
+*/
+
+@end
diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp
index 8d9f2fd0be..7b36207f52 100644
--- a/src/slic3r/GUI/wxExtensions.cpp
+++ b/src/slic3r/GUI/wxExtensions.cpp
@@ -13,6 +13,7 @@
#include
#include
+#include
#include
#include
@@ -433,17 +434,17 @@ static std::string icon_name_respected_to_mode(const std::string& bmp_name_in)
#else
const std::string folder = "white/";
#endif
- std::string bmp_name = Slic3r::GUI::wxGetApp().dark_mode() ? folder + bmp_name_in : bmp_name_in;
- boost::replace_last(bmp_name, ".png", "");
- FILE* fp = NULL;
- fp = boost::nowide::fopen(Slic3r::var(bmp_name + ".svg").c_str(), "rb");
- if (!fp)
- {
- bmp_name = bmp_name_in;
- boost::replace_last(bmp_name, ".png", "");
- if (fp) fclose(fp);
- }
-
+ std::string bmp_name;
+ if (Slic3r::GUI::wxGetApp().dark_mode()) {
+ bmp_name = folder + bmp_name_in;
+ boost::replace_last(bmp_name, ".png", "");
+ if (! boost::filesystem::exists(Slic3r::var(bmp_name + ".svg")))
+ bmp_name.clear();
+ }
+ if (bmp_name.empty()) {
+ bmp_name = bmp_name_in;
+ boost::replace_last(bmp_name, ".png", "");
+ }
return bmp_name;
}
@@ -3960,8 +3961,10 @@ ScalableButton::ScalableButton( wxWindow * parent,
const ScalableBitmap& bitmap,
const wxString& label /*= wxEmptyString*/,
long style /*= wxBU_EXACTFIT | wxNO_BORDER*/) :
+ m_parent(parent),
m_current_icon_name(bitmap.name()),
- m_parent(parent)
+ m_px_cnt(bitmap.px_cnt()),
+ m_is_horizontal(bitmap.is_horizontal())
{
Create(parent, id, label, wxDefaultPosition, wxDefaultSize, style);
#ifdef __WXMSW__
@@ -3984,11 +3987,17 @@ void ScalableButton::SetBitmapDisabled_(const ScalableBitmap& bmp)
m_disabled_icon_name = bmp.name();
}
+int ScalableButton::GetBitmapHeight()
+{
+ const float scale_factor = get_svg_scale_factor(m_parent);
+ return int((float)GetBitmap().GetHeight() / scale_factor);
+}
+
void ScalableButton::msw_rescale()
{
- SetBitmap(create_scaled_bitmap(m_parent, m_current_icon_name));
+ SetBitmap(create_scaled_bitmap(m_parent, m_current_icon_name, m_px_cnt, m_is_horizontal));
if (!m_disabled_icon_name.empty())
- SetBitmapDisabled(create_scaled_bitmap(m_parent, m_disabled_icon_name));
+ SetBitmapDisabled(create_scaled_bitmap(m_parent, m_disabled_icon_name, m_px_cnt, m_is_horizontal));
if (m_width > 0 || m_height>0)
{
diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp
index 0c43be3a08..390dafab9e 100644
--- a/src/slic3r/GUI/wxExtensions.hpp
+++ b/src/slic3r/GUI/wxExtensions.hpp
@@ -730,6 +730,9 @@ public:
wxBitmap& bmp() { return m_bmp; }
const std::string& name() const{ return m_icon_name; }
+ int px_cnt()const {return m_px_cnt;}
+ bool is_horizontal()const {return m_is_horizontal;}
+
private:
wxWindow* m_parent{ nullptr };
wxBitmap m_bmp = wxBitmap();
@@ -1096,6 +1099,7 @@ public:
void SetBitmap_(const ScalableBitmap& bmp);
void SetBitmapDisabled_(const ScalableBitmap &bmp);
+ int GetBitmapHeight();
void msw_rescale();
@@ -1105,6 +1109,10 @@ private:
std::string m_disabled_icon_name = "";
int m_width {-1}; // should be multiplied to em_unit
int m_height{-1}; // should be multiplied to em_unit
+
+ // bitmap dimensions
+ int m_px_cnt{ 16 };
+ bool m_is_horizontal{ false };
};
diff --git a/src/slic3r/Utils/AstroBox.cpp b/src/slic3r/Utils/AstroBox.cpp
new file mode 100644
index 0000000000..83a72e64a0
--- /dev/null
+++ b/src/slic3r/Utils/AstroBox.cpp
@@ -0,0 +1,170 @@
+#include "AstroBox.hpp"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "libslic3r/PrintConfig.hpp"
+#include "slic3r/GUI/I18N.hpp"
+#include "Http.hpp"
+
+
+namespace fs = boost::filesystem;
+namespace pt = boost::property_tree;
+
+
+namespace Slic3r {
+
+AstroBox::AstroBox(DynamicPrintConfig *config) :
+ host(config->opt_string("print_host")),
+ apikey(config->opt_string("printhost_apikey")),
+ cafile(config->opt_string("printhost_cafile"))
+{}
+
+const char* AstroBox::get_name() const { return "AstroBox"; }
+
+bool AstroBox::test(wxString &msg) const
+{
+ // Since the request is performed synchronously here,
+ // it is ok to refer to `msg` from within the closure
+
+ const char *name = get_name();
+
+ bool res = true;
+ auto url = make_url("api/version");
+
+ BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url;
+
+ auto http = Http::get(std::move(url));
+ set_auth(http);
+ http.on_error([&](std::string body, std::string error, unsigned status) {
+ BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting version: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
+ res = false;
+ msg = format_error(body, error, status);
+ })
+ .on_complete([&, this](std::string body, unsigned) {
+ BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got version: %2%") % name % body;
+
+ try {
+ std::stringstream ss(body);
+ pt::ptree ptree;
+ pt::read_json(ss, ptree);
+
+ if (! ptree.get_optional("api")) {
+ res = false;
+ return;
+ }
+
+ const auto text = ptree.get_optional("text");
+ res = validate_version_text(text);
+ if (! res) {
+ msg = wxString::Format(_(L("Mismatched type of print host: %s")), text ? *text : "AstroBox");
+ }
+ }
+ catch (const std::exception &) {
+ res = false;
+ msg = "Could not parse server response";
+ }
+ })
+ .perform_sync();
+
+ return res;
+}
+
+wxString AstroBox::get_test_ok_msg () const
+{
+ return _(L("Connection to AstroBox works correctly."));
+}
+
+wxString AstroBox::get_test_failed_msg (wxString &msg) const
+{
+ return wxString::Format("%s: %s\n\n%s",
+ _(L("Could not connect to AstroBox")), msg, _(L("Note: AstroBox version at least 1.1.0 is required.")));
+}
+
+bool AstroBox::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const
+{
+ const char *name = get_name();
+
+ const auto upload_filename = upload_data.upload_path.filename();
+ const auto upload_parent_path = upload_data.upload_path.parent_path();
+
+ wxString test_msg;
+ if (! test(test_msg)) {
+ error_fn(std::move(test_msg));
+ return false;
+ }
+
+ bool res = true;
+
+ auto url = make_url("api/files/local");
+
+ BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Uploading file %2% at %3%, filename: %4%, path: %5%, print: %6%")
+ % name
+ % upload_data.source_path
+ % url
+ % upload_filename.string()
+ % upload_parent_path.string()
+ % upload_data.start_print;
+
+ auto http = Http::post(std::move(url));
+ set_auth(http);
+ http.form_add("print", upload_data.start_print ? "true" : "false")
+ .form_add("path", upload_parent_path.string()) // XXX: slashes on windows ???
+ .form_add_file("file", upload_data.source_path.string(), upload_filename.string())
+ .on_complete([&](std::string body, unsigned status) {
+ BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: File uploaded: HTTP %2%: %3%") % name % status % body;
+ })
+ .on_error([&](std::string body, std::string error, unsigned status) {
+ BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error uploading file: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
+ error_fn(format_error(body, error, status));
+ res = false;
+ })
+ .on_progress([&](Http::Progress progress, bool &cancel) {
+ prorgess_fn(std::move(progress), cancel);
+ if (cancel) {
+ // Upload was canceled
+ BOOST_LOG_TRIVIAL(info) << "AstroBox: Upload canceled";
+ res = false;
+ }
+ })
+ .perform_sync();
+
+ return res;
+}
+
+bool AstroBox::validate_version_text(const boost::optional &version_text) const
+{
+ return version_text ? boost::starts_with(*version_text, "AstroBox") : true;
+}
+
+void AstroBox::set_auth(Http &http) const
+{
+ http.header("X-Api-Key", apikey);
+
+ if (! cafile.empty()) {
+ http.ca_file(cafile);
+ }
+}
+
+std::string AstroBox::make_url(const std::string &path) const
+{
+ if (host.find("http://") == 0 || host.find("https://") == 0) {
+ if (host.back() == '/') {
+ return (boost::format("%1%%2%") % host % path).str();
+ } else {
+ return (boost::format("%1%/%2%") % host % path).str();
+ }
+ } else {
+ return (boost::format("http://%1%/%2%") % host % path).str();
+ }
+}
+
+}
diff --git a/src/slic3r/Utils/AstroBox.hpp b/src/slic3r/Utils/AstroBox.hpp
new file mode 100644
index 0000000000..38542275c5
--- /dev/null
+++ b/src/slic3r/Utils/AstroBox.hpp
@@ -0,0 +1,46 @@
+#ifndef slic3r_AstroBox_hpp_
+#define slic3r_AstroBox_hpp_
+
+#include
+#include
+#include
+
+#include "PrintHost.hpp"
+
+namespace Slic3r {
+
+class DynamicPrintConfig;
+class Http;
+
+class AstroBox : public PrintHost
+{
+public:
+ AstroBox(DynamicPrintConfig *config);
+ ~AstroBox() override = default;
+
+ const char* get_name() const override;
+
+ bool test(wxString &curl_msg) const override;
+ wxString get_test_ok_msg () const override;
+ wxString get_test_failed_msg (wxString &msg) const override;
+ bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override;
+ bool has_auto_discovery() const override { return true; }
+ bool can_test() const override { return true; }
+ bool can_start_print() const override { return true; }
+ std::string get_host() const override { return host; }
+
+protected:
+ bool validate_version_text(const boost::optional &version_text) const;
+
+private:
+ std::string host;
+ std::string apikey;
+ std::string cafile;
+
+ void set_auth(Http &http) const;
+ std::string make_url(const std::string &path) const;
+};
+
+}
+
+#endif
diff --git a/src/slic3r/Utils/Duet.cpp b/src/slic3r/Utils/Duet.cpp
index b0d8c0b8d7..554d3f5f55 100644
--- a/src/slic3r/Utils/Duet.cpp
+++ b/src/slic3r/Utils/Duet.cpp
@@ -32,8 +32,6 @@ Duet::Duet(DynamicPrintConfig *config) :
password(config->opt_string("printhost_apikey"))
{}
-Duet::~Duet() {}
-
const char* Duet::get_name() const { return "Duet"; }
bool Duet::test(wxString &msg) const
@@ -111,21 +109,6 @@ bool Duet::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn e
return res;
}
-bool Duet::has_auto_discovery() const
-{
- return false;
-}
-
-bool Duet::can_test() const
-{
- return true;
-}
-
-bool Duet::can_start_print() const
-{
- return true;
-}
-
bool Duet::connect(wxString &msg) const
{
bool res = false;
diff --git a/src/slic3r/Utils/Duet.hpp b/src/slic3r/Utils/Duet.hpp
index 04ceec36fd..702efbddb6 100644
--- a/src/slic3r/Utils/Duet.hpp
+++ b/src/slic3r/Utils/Duet.hpp
@@ -6,10 +6,8 @@
#include "PrintHost.hpp"
-
namespace Slic3r {
-
class DynamicPrintConfig;
class Http;
@@ -17,18 +15,18 @@ class Duet : public PrintHost
{
public:
Duet(DynamicPrintConfig *config);
- virtual ~Duet();
+ ~Duet() override = default;
- virtual const char* get_name() const;
+ const char* get_name() const override;
- virtual bool test(wxString &curl_msg) const;
- virtual wxString get_test_ok_msg () const;
- virtual wxString get_test_failed_msg (wxString &msg) const;
- virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const;
- virtual bool has_auto_discovery() const;
- virtual bool can_test() const;
- virtual bool can_start_print() const;
- virtual std::string get_host() const { return host; }
+ bool test(wxString &curl_msg) const override;
+ wxString get_test_ok_msg() const override;
+ wxString get_test_failed_msg(wxString &msg) const override;
+ bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override;
+ bool has_auto_discovery() const override { return false; }
+ bool can_test() const override { return true; }
+ bool can_start_print() const override { return true; }
+ std::string get_host() const override { return host; }
private:
std::string host;
@@ -44,7 +42,6 @@ private:
int get_err_code_from_body(const std::string &body) const;
};
-
}
#endif
diff --git a/src/slic3r/Utils/FlashAir.cpp b/src/slic3r/Utils/FlashAir.cpp
index 3fc913c994..79f3f2c2bd 100644
--- a/src/slic3r/Utils/FlashAir.cpp
+++ b/src/slic3r/Utils/FlashAir.cpp
@@ -30,8 +30,6 @@ FlashAir::FlashAir(DynamicPrintConfig *config) :
host(config->opt_string("print_host"))
{}
-FlashAir::~FlashAir() {}
-
const char* FlashAir::get_name() const { return "FlashAir"; }
bool FlashAir::test(wxString &msg) const
@@ -150,21 +148,6 @@ bool FlashAir::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Error
return res;
}
-bool FlashAir::has_auto_discovery() const
-{
- return false;
-}
-
-bool FlashAir::can_test() const
-{
- return true;
-}
-
-bool FlashAir::can_start_print() const
-{
- return false;
-}
-
std::string FlashAir::timestamp_str() const
{
auto t = std::time(nullptr);
diff --git a/src/slic3r/Utils/FlashAir.hpp b/src/slic3r/Utils/FlashAir.hpp
index 1499eee5d8..40af48da1b 100644
--- a/src/slic3r/Utils/FlashAir.hpp
+++ b/src/slic3r/Utils/FlashAir.hpp
@@ -9,7 +9,6 @@
namespace Slic3r {
-
class DynamicPrintConfig;
class Http;
@@ -17,18 +16,18 @@ class FlashAir : public PrintHost
{
public:
FlashAir(DynamicPrintConfig *config);
- virtual ~FlashAir();
+ ~FlashAir() override = default;
- virtual const char* get_name() const;
+ const char* get_name() const override;
- virtual bool test(wxString &curl_msg) const;
- virtual wxString get_test_ok_msg () const;
- virtual wxString get_test_failed_msg (wxString &msg) const;
- virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const;
- virtual bool has_auto_discovery() const;
- virtual bool can_test() const;
- virtual bool can_start_print() const;
- virtual std::string get_host() const { return host; }
+ bool test(wxString &curl_msg) const override;
+ wxString get_test_ok_msg() const override;
+ wxString get_test_failed_msg(wxString &msg) const override;
+ bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override;
+ bool has_auto_discovery() const override { return false; }
+ bool can_test() const override { return true; }
+ bool can_start_print() const override { return false; }
+ std::string get_host() const override { return host; }
private:
std::string host;
@@ -38,7 +37,6 @@ private:
std::string make_url(const std::string &path, const std::string &arg, const std::string &val) const;
};
-
}
#endif
diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp
index 09ca02071a..48f8ba0b9a 100644
--- a/src/slic3r/Utils/OctoPrint.cpp
+++ b/src/slic3r/Utils/OctoPrint.cpp
@@ -28,8 +28,6 @@ OctoPrint::OctoPrint(DynamicPrintConfig *config) :
cafile(config->opt_string("printhost_cafile"))
{}
-OctoPrint::~OctoPrint() {}
-
const char* OctoPrint::get_name() const { return "OctoPrint"; }
bool OctoPrint::test(wxString &msg) const
@@ -142,21 +140,6 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro
return res;
}
-bool OctoPrint::has_auto_discovery() const
-{
- return true;
-}
-
-bool OctoPrint::can_test() const
-{
- return true;
-}
-
-bool OctoPrint::can_start_print() const
-{
- return true;
-}
-
bool OctoPrint::validate_version_text(const boost::optional &version_text) const
{
return version_text ? boost::starts_with(*version_text, "OctoPrint") : true;
@@ -186,9 +169,6 @@ std::string OctoPrint::make_url(const std::string &path) const
// SL1Host
-
-SL1Host::~SL1Host() {}
-
const char* SL1Host::get_name() const { return "SL1Host"; }
wxString SL1Host::get_test_ok_msg () const
@@ -201,15 +181,9 @@ wxString SL1Host::get_test_failed_msg (wxString &msg) const
return wxString::Format("%s: %s", _(L("Could not connect to Prusa SLA")), msg);
}
-bool SL1Host::can_start_print() const
-{
- return false;
-}
-
bool SL1Host::validate_version_text(const boost::optional &version_text) const
{
return version_text ? boost::starts_with(*version_text, "Prusa SLA") : false;
}
-
}
diff --git a/src/slic3r/Utils/OctoPrint.hpp b/src/slic3r/Utils/OctoPrint.hpp
index 0e372c7c95..965019d859 100644
--- a/src/slic3r/Utils/OctoPrint.hpp
+++ b/src/slic3r/Utils/OctoPrint.hpp
@@ -10,7 +10,6 @@
namespace Slic3r {
-
class DynamicPrintConfig;
class Http;
@@ -18,18 +17,18 @@ class OctoPrint : public PrintHost
{
public:
OctoPrint(DynamicPrintConfig *config);
- virtual ~OctoPrint();
+ ~OctoPrint() override = default;
- virtual const char* get_name() const;
+ const char* get_name() const;
- virtual bool test(wxString &curl_msg) const;
- virtual wxString get_test_ok_msg () const;
- virtual wxString get_test_failed_msg (wxString &msg) const;
- virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const;
- virtual bool has_auto_discovery() const;
- virtual bool can_test() const;
- virtual bool can_start_print() const;
- virtual std::string get_host() const { return host; }
+ bool test(wxString &curl_msg) const override;
+ wxString get_test_ok_msg () const override;
+ wxString get_test_failed_msg (wxString &msg) const override;
+ bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override;
+ bool has_auto_discovery() const override { return true; }
+ bool can_test() const override { return true; }
+ bool can_start_print() const override { return true; }
+ std::string get_host() const override { return host; }
protected:
virtual bool validate_version_text(const boost::optional &version_text) const;
@@ -43,23 +42,22 @@ private:
std::string make_url(const std::string &path) const;
};
-
class SL1Host: public OctoPrint
{
public:
SL1Host(DynamicPrintConfig *config) : OctoPrint(config) {}
- virtual ~SL1Host();
+ ~SL1Host() override = default;
- virtual const char* get_name() const;
+ const char* get_name() const override;
+
+ wxString get_test_ok_msg() const override;
+ wxString get_test_failed_msg(wxString &msg) const override;
+ bool can_start_print() const override { return false; }
- virtual wxString get_test_ok_msg () const;
- virtual wxString get_test_failed_msg (wxString &msg) const;
- virtual bool can_start_print() const ;
protected:
- virtual bool validate_version_text(const boost::optional &version_text) const;
+ bool validate_version_text(const boost::optional &version_text) const override;
};
-
}
#endif
diff --git a/src/slic3r/Utils/PrintHost.cpp b/src/slic3r/Utils/PrintHost.cpp
index 59a929ecca..0a49b78153 100644
--- a/src/slic3r/Utils/PrintHost.cpp
+++ b/src/slic3r/Utils/PrintHost.cpp
@@ -15,6 +15,7 @@
#include "OctoPrint.hpp"
#include "Duet.hpp"
#include "FlashAir.hpp"
+#include "AstroBox.hpp"
#include "../GUI/PrintHostDialogs.hpp"
namespace fs = boost::filesystem;
@@ -45,6 +46,7 @@ PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config)
case htOctoPrint: return new OctoPrint(config);
case htDuet: return new Duet(config);
case htFlashAir: return new FlashAir(config);
+ case htAstroBox: return new AstroBox(config);
default: return nullptr;
}
} else {
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 458d398602..61fe972772 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -8,6 +8,11 @@ add_library(Catch2 INTERFACE)
list (APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules/Catch2)
target_include_directories(Catch2 INTERFACE ${CMAKE_CURRENT_LIST_DIR})
add_library(Catch2::Catch2 ALIAS Catch2)
+if (APPLE)
+ # OSX builds targeting OSX 10.9 do not support new std::uncought_exception()
+ # see https://github.com/catchorg/Catch2/issues/1218
+ target_compile_definitions(Catch2 INTERFACE -DCATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS)
+endif()
include(Catch)
set(CATCH_EXTRA_ARGS "" CACHE STRING "Extra arguments for catch2 test suites.")
diff --git a/tests/data/test_3mf/Prusa.stl b/tests/data/test_3mf/Prusa.stl
new file mode 100644
index 0000000000..049cbaceea
Binary files /dev/null and b/tests/data/test_3mf/Prusa.stl differ
diff --git a/tests/libslic3r/test_3mf.cpp b/tests/libslic3r/test_3mf.cpp
index b413cbe0bc..5215e2ebd2 100644
--- a/tests/libslic3r/test_3mf.cpp
+++ b/tests/libslic3r/test_3mf.cpp
@@ -2,19 +2,86 @@
#include "libslic3r/Model.hpp"
#include "libslic3r/Format/3mf.hpp"
+#include "libslic3r/Format/STL.hpp"
+
+#include
using namespace Slic3r;
SCENARIO("Reading 3mf file", "[3mf]") {
GIVEN("umlauts in the path of the file") {
- Slic3r::Model model;
+ Model model;
WHEN("3mf model is read") {
std::string path = std::string(TEST_DATA_DIR) + "/test_3mf/Geräte/Büchse.3mf";
DynamicPrintConfig config;
- bool ret = Slic3r::load_3mf(path.c_str(), &config, &model, false);
+ bool ret = load_3mf(path.c_str(), &config, &model, false);
THEN("load should succeed") {
REQUIRE(ret);
}
}
}
}
+
+SCENARIO("Export+Import geometry to/from 3mf file cycle", "[3mf]") {
+ GIVEN("world vertices coordinates before save") {
+ // load a model from stl file
+ Model src_model;
+ std::string src_file = std::string(TEST_DATA_DIR) + "/test_3mf/prusa.stl";
+ load_stl(src_file.c_str(), &src_model);
+ src_model.add_default_instances();
+
+ ModelObject* src_object = src_model.objects[0];
+
+ // apply generic transformation to the 1st volume
+ Geometry::Transformation src_volume_transform;
+ src_volume_transform.set_offset(Vec3d(10.0, 20.0, 0.0));
+ src_volume_transform.set_rotation(Vec3d(Geometry::deg2rad(25.0), Geometry::deg2rad(35.0), Geometry::deg2rad(45.0)));
+ src_volume_transform.set_scaling_factor(Vec3d(1.1, 1.2, 1.3));
+ src_volume_transform.set_mirror(Vec3d(-1.0, 1.0, -1.0));
+ src_object->volumes[0]->set_transformation(src_volume_transform);
+
+ // apply generic transformation to the 1st instance
+ Geometry::Transformation src_instance_transform;
+ src_instance_transform.set_offset(Vec3d(5.0, 10.0, 0.0));
+ src_instance_transform.set_rotation(Vec3d(Geometry::deg2rad(12.0), Geometry::deg2rad(13.0), Geometry::deg2rad(14.0)));
+ src_instance_transform.set_scaling_factor(Vec3d(0.9, 0.8, 0.7));
+ src_instance_transform.set_mirror(Vec3d(1.0, -1.0, -1.0));
+ src_object->instances[0]->set_transformation(src_instance_transform);
+
+ WHEN("model is saved+loaded to/from 3mf file") {
+ // save the model to 3mf file
+ std::string test_file = std::string(TEST_DATA_DIR) + "/test_3mf/prusa.3mf";
+ store_3mf(test_file.c_str(), &src_model, nullptr);
+
+ // load back the model from the 3mf file
+ Model dst_model;
+ DynamicPrintConfig dst_config;
+ load_3mf(test_file.c_str(), &dst_config, &dst_model, false);
+ boost::filesystem::remove(test_file);
+
+ // compare meshes
+ TriangleMesh src_mesh = src_model.mesh();
+ src_mesh.repair();
+
+ TriangleMesh dst_mesh = dst_model.mesh();
+ dst_mesh.repair();
+
+ bool res = src_mesh.its.vertices.size() == dst_mesh.its.vertices.size();
+ if (res)
+ {
+ for (size_t i = 0; i < dst_mesh.its.vertices.size(); ++i)
+ {
+ res &= dst_mesh.its.vertices[i].isApprox(src_mesh.its.vertices[i]);
+ if (!res)
+ {
+ Vec3f diff = dst_mesh.its.vertices[i] - src_mesh.its.vertices[i];
+ std::cout << i << ": diff " << to_string((Vec3d)diff.cast()) << "\n";
+ }
+ }
+ }
+ THEN("world vertices coordinates after load match") {
+ REQUIRE(res);
+ }
+ }
+ }
+}
diff --git a/version.inc b/version.inc
index c96a304ab3..dbd97b6554 100644
--- a/version.inc
+++ b/version.inc
@@ -3,7 +3,7 @@
set(SLIC3R_APP_NAME "PrusaSlicer")
set(SLIC3R_APP_KEY "PrusaSlicer")
-set(SLIC3R_VERSION "2.2.0-alpha0")
+set(SLIC3R_VERSION "2.2.0-alpha2")
set(SLIC3R_BUILD_ID "PrusaSlicer-${SLIC3R_VERSION}+UNKNOWN")
set(SLIC3R_RC_VERSION "2,2,0,0")
set(SLIC3R_RC_VERSION_DOTS "2.2.0.0")
diff --git a/xs/xsp/Layer.xsp b/xs/xsp/Layer.xsp
index 6f3164707a..6ca953f82c 100644
--- a/xs/xsp/Layer.xsp
+++ b/xs/xsp/Layer.xsp
@@ -61,7 +61,7 @@
Ref add_region(PrintRegion* print_region);
ExPolygonCollection* slices()
- %code%{ RETVAL = new ExPolygonCollection(THIS->slices); %};
+ %code%{ RETVAL = new ExPolygonCollection(THIS->lslices); %};
int ptr()
%code%{ RETVAL = (int)(intptr_t)THIS; %};
@@ -110,7 +110,7 @@
Ref add_region(PrintRegion* print_region);
ExPolygonCollection* slices()
- %code%{ RETVAL = new ExPolygonCollection(THIS->slices); %};
+ %code%{ RETVAL = new ExPolygonCollection(THIS->lslices); %};
void export_region_slices_to_svg(const char *path);
void export_region_fill_surfaces_to_svg(const char *path);