Merge branch 'master' into lm_tm_hollowing

This commit is contained in:
Lukas Matena 2020-01-06 12:41:29 +01:00
commit 0551411c48
82 changed files with 2066 additions and 462 deletions

View File

@ -394,13 +394,27 @@ target_include_directories(cereal INTERFACE include)
# l10n # l10n
set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/localization") 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 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" -f "${L10N_DIR}/list.txt"
-o "${L10N_DIR}/PrusaSlicer.pot" -o "${L10N_DIR}/PrusaSlicer.pot"
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
COMMENT "Generate pot file from strings in the source tree" 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) find_package(NLopt 1.4 REQUIRED)

View File

@ -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 \*) 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` (?). 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Svg Vector Icons : http://www.onlinewebfonts.com/icon -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">
<metadata> Svg Vector Icons : http://www.onlinewebfonts.com/icon </metadata>
<g>
<path fill="#ED6B21" d="M59.7,265.7c0,0,89.2-138.9,230.3-213.4C431.1-22.2,605-0.7,719,71.5c114,72.3,152.4,133.2,152.4,133.2l98.2-56.5c0,0,20.3-10.2,20.3,13.5v354.5c0,0,0,31.6-23.7,20.3c-19.9-9.5-235.7-133.3-303.6-172.3c-37.3-16.8-4.5-30.4-4.5-30.4l94.8-54.7c0,0-54.1-68.3-133.2-104.5C535,130.3,455.7,125,358.6,162c-63.3,24.1-137.9,85.9-191.6,177.2L59.7,265.7L59.7,265.7z"/>
<path fill="#ED6B21" d="M940.3,734.3c0,0-89.2,138.9-230.3,213.4c-141.1,74.5-315,53.1-429-19.2c-114-72.3-152.4-133.2-152.4-133.2l-98.2,56.4c0,0-20.3,10.2-20.3-13.5V483.6c0,0,0-31.6,23.7-20.3c19.9,9.5,235.7,133.3,303.6,172.3c37.3,16.8,4.5,30.4,4.5,30.4l-94.8,54.7c0,0,54.1,68.3,133.2,104.5c84.7,44.5,164,49.8,261.1,12.8c63.3-24.1,137.9-85.9,191.6-177.2L940.3,734.3L940.3,734.3z"/></g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -6865,7 +6865,7 @@ msgstr "Textura"
#: src/slic3r/GUI/ConfigManipulation.cpp:200 #: src/slic3r/GUI/ConfigManipulation.cpp:200
msgid "The %1% infill pattern is not supposed to work at 100%% density." 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 #: src/slic3r/GUI/FirmwareDialog.cpp:530
#, possible-c-format #, possible-c-format

View File

@ -7013,7 +7013,7 @@ msgstr "Texture"
#: src/slic3r/GUI/ConfigManipulation.cpp:200 #: src/slic3r/GUI/ConfigManipulation.cpp:200
msgid "The %1% infill pattern is not supposed to work at 100%% density." 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 #: src/slic3r/GUI/FirmwareDialog.cpp:530
#, c-format #, c-format

View File

@ -6865,7 +6865,7 @@ msgstr "Texture"
#: src/slic3r/GUI/ConfigManipulation.cpp:200 #: src/slic3r/GUI/ConfigManipulation.cpp:200
msgid "The %1% infill pattern is not supposed to work at 100%% density." 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 #: src/slic3r/GUI/FirmwareDialog.cpp:530
#, possible-c-format #, possible-c-format

View File

@ -50,6 +50,7 @@ src/slic3r/GUI/ExtruderSequenceDialog.cpp
src/slic3r/Utils/Duet.cpp src/slic3r/Utils/Duet.cpp
src/slic3r/Utils/OctoPrint.cpp src/slic3r/Utils/OctoPrint.cpp
src/slic3r/Utils/FlashAir.cpp src/slic3r/Utils/FlashAir.cpp
src/slic3r/Utils/AstroBox.cpp
src/slic3r/Utils/PresetUpdater.cpp src/slic3r/Utils/PresetUpdater.cpp
src/slic3r/Utils/FixModelByWin10.cpp src/slic3r/Utils/FixModelByWin10.cpp
src/libslic3r/SLA/SLAPad.cpp src/libslic3r/SLA/SLAPad.cpp

View File

@ -3275,7 +3275,7 @@ msgstr "Шаблон наповнення "
#: src/slic3r/GUI/Tab.cpp:1309 #: src/slic3r/GUI/Tab.cpp:1309
#, no-c-format #, no-c-format
msgid "" msgid ""
" infill pattern is not supposed to work at 100% density.\n" " infill pattern is not supposed to work at 100%% density.\n"
"\n" "\n"
"Shall I switch to rectilinear fill pattern?" "Shall I switch to rectilinear fill pattern?"
msgstr "" msgstr ""

View File

@ -3292,7 +3292,7 @@ msgstr "這個 "
#: src/slic3r/GUI/Tab.cpp:1309 #: src/slic3r/GUI/Tab.cpp:1309
#, no-c-format #, no-c-format
msgid "" msgid ""
" infill pattern is not supposed to work at 100% density.\n" " infill pattern is not supposed to work at 100%% density.\n"
"\n" "\n"
"Shall I switch to rectilinear fill pattern?" "Shall I switch to rectilinear fill pattern?"
msgstr "" msgstr ""

View File

@ -1,4 +1,5 @@
min_slic3r_version = 2.2.0-alpha0 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 1.1.0 Filament aliases, Creality profiles and other goodies for PrusaSlicer 2.2.0-alpha0
min_slic3r_version = 2.1.1-beta0 min_slic3r_version = 2.1.1-beta0
1.0.6 Added Prusa MINI profiles 1.0.6 Added Prusa MINI profiles

View File

@ -5,7 +5,7 @@
name = Prusa Research name = Prusa Research
# Configuration version of this file. Config file will only be installed, if the config_version differs. # 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. # 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? # Where to get the updates from?
config_update_url = http://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/ 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% changelog_url = http://files.prusa3d.com/?latest=slicer-profiles&lng=%1%

View File

@ -691,7 +691,7 @@ extern "C" {
for (size_t i = 0; i < argc; ++ i) for (size_t i = 0; i < argc; ++ i)
argv_narrow.emplace_back(boost::nowide::narrow(argv[i])); argv_narrow.emplace_back(boost::nowide::narrow(argv[i]));
for (size_t i = 0; i < argc; ++ i) for (size_t i = 0; i < argc; ++ i)
argv_ptrs[i] = const_cast<char*>(argv_narrow[i].data()); argv_ptrs[i] = argv_narrow[i].data();
// Call the UTF8 main. // Call the UTF8 main.
return CLI().run(argc, argv_ptrs.data()); return CLI().run(argc, argv_ptrs.data());
} }

View File

@ -93,11 +93,11 @@ void AvrDude::priv::unset_handlers()
int AvrDude::priv::run_one(const std::vector<std::string> &args) { int AvrDude::priv::run_one(const std::vector<std::string> &args) {
std::vector<char*> c_args { const_cast<char*>(PACKAGE) }; std::vector<const char*> c_args { PACKAGE };
std::string command_line { PACKAGE }; std::string command_line { PACKAGE };
for (const auto &arg : args) { for (const auto &arg : args) {
c_args.push_back(const_cast<char*>(arg.data())); c_args.push_back(arg.c_str());
command_line.push_back(' '); command_line.push_back(' ');
command_line.append(arg); command_line.append(arg);
} }
@ -107,7 +107,7 @@ int AvrDude::priv::run_one(const std::vector<std::string> &args) {
message_fn(command_line.c_str(), (unsigned)command_line.size()); message_fn(command_line.c_str(), (unsigned)command_line.size());
const auto res = ::avrdude_main(static_cast<int>(c_args.size()), c_args.data()); const auto res = ::avrdude_main(static_cast<int>(c_args.size()), const_cast<char**>(c_args.data()));
return res; return res;
} }

View File

@ -630,39 +630,38 @@ size_t ConfigBase::load_from_gcode_string(const char* str)
return 0; return 0;
// Walk line by line in reverse until a non-configuration key appears. // Walk line by line in reverse until a non-configuration key appears.
char *data_start = const_cast<char*>(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. // 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; size_t num_key_value_pairs = 0;
for (;;) { for (;;) {
// Extract next line. // Extract next line.
for (--end; end > data_start && (*end == '\r' || *end == '\n'); --end); for (--end; end > data_start && (*end == '\r' || *end == '\n'); --end);
if (end == data_start) if (end == data_start)
break; break;
char *start = end; const char *start = end ++;
*(++end) = 0;
for (; start > data_start && *start != '\r' && *start != '\n'; --start); for (; start > data_start && *start != '\r' && *start != '\n'; --start);
if (start == data_start) if (start == data_start)
break; break;
// Extracted a line from start to end. Extract the key = value pair. // 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; break;
char *key = start + 2; const char *key = start + 2;
if (!(*key >= 'a' && *key <= 'z') || (*key >= 'A' && *key <= 'Z')) if (!(*key >= 'a' && *key <= 'z') || (*key >= 'A' && *key <= 'Z'))
// A key must start with a letter. // A key must start with a letter.
break; break;
char *sep = strchr(key, '='); const char *sep = key;
if (sep == nullptr || sep[-1] != ' ' || sep[1] != ' ') for (; sep != end && *sep != '='; ++ sep) ;
if (sep == end || sep[-1] != ' ' || sep[1] != ' ')
break; break;
char *value = sep + 2; const char *value = sep + 2;
if (value > end) if (value > end)
break; break;
char *key_end = sep - 1; const char *key_end = sep - 1;
if (key_end - key < 3) if (key_end - key < 3)
break; break;
*key_end = 0;
// The key may contain letters, digits and underscores. // 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 == '_')) { if (!((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <= 'Z') || (*c >= '0' && *c <= '9') || *c == '_')) {
key = nullptr; key = nullptr;
break; break;
@ -670,7 +669,7 @@ size_t ConfigBase::load_from_gcode_string(const char* str)
if (key == nullptr) if (key == nullptr)
break; break;
try { try {
this->set_deserialize(key, value); this->set_deserialize(std::string(key, key_end), std::string(value, end));
++num_key_value_pairs; ++num_key_value_pairs;
} }
catch (UnknownOptionException & /* e */) { 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<std::string> &tokens, t_config_option_keys* extra, t_config_option_keys* keys) void DynamicConfig::read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra, t_config_option_keys* keys)
{ {
std::vector<char*> args; std::vector<const char*> args;
// push a bogus executable name (argv[0]) // push a bogus executable name (argv[0])
args.emplace_back(const_cast<char*>("")); args.emplace_back("");
for (size_t i = 0; i < tokens.size(); ++ i) for (size_t i = 0; i < tokens.size(); ++ i)
args.emplace_back(const_cast<char *>(tokens[i].c_str())); args.emplace_back(tokens[i].c_str());
this->read_cli(int(args.size()), &args[0], extra, keys); 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 // cache the CLI option => opt_key mapping
std::map<std::string,std::string> opts; std::map<std::string,std::string> opts;

View File

@ -1779,7 +1779,7 @@ public:
// Command line processing // Command line processing
void read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra, t_config_option_keys* keys = nullptr); void read_cli(const std::vector<std::string> &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<t_config_option_key, std::unique_ptr<ConfigOption>>::const_iterator cbegin() const { return options.cbegin(); } std::map<t_config_option_key, std::unique_ptr<ConfigOption>>::const_iterator cbegin() const { return options.cbegin(); }
std::map<t_config_option_key, std::unique_ptr<ConfigOption>>::const_iterator cend() const { return options.cend(); } std::map<t_config_option_key, std::unique_ptr<ConfigOption>>::const_iterator cend() const { return options.cend(); }

View File

@ -88,6 +88,7 @@ const char* V3_ATTR = "v3";
const char* OBJECTID_ATTR = "objectid"; const char* OBJECTID_ATTR = "objectid";
const char* TRANSFORM_ATTR = "transform"; const char* TRANSFORM_ATTR = "transform";
const char* PRINTABLE_ATTR = "printable"; const char* PRINTABLE_ATTR = "printable";
const char* INSTANCESCOUNT_ATTR = "instances_count";
const char* KEY_ATTR = "key"; const char* KEY_ATTR = "key";
const char* VALUE_ATTR = "value"; const char* VALUE_ATTR = "value";
@ -729,8 +730,8 @@ namespace Slic3r {
return false; return false;
} }
// fixes the min z of the model if negative // // fixes the min z of the model if negative
model.adjust_min_z(); // model.adjust_min_z();
return true; return true;
} }
@ -1712,6 +1713,9 @@ namespace Slic3r {
return false; 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_objects_metadata.insert(IdToMetadataMap::value_type(object_id, ObjectMetadata()));
m_curr_config.object_id = object_id; m_curr_config.object_id = object_id;
return true; return true;
@ -1812,7 +1816,9 @@ namespace Slic3r {
break; break;
} }
} }
#if !ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
Transform3d inv_matrix = volume_matrix_to_object.inverse(); Transform3d inv_matrix = volume_matrix_to_object.inverse();
#endif // !ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
// splits volume out of imported geometry // splits volume out of imported geometry
TriangleMesh triangle_mesh; TriangleMesh triangle_mesh;
@ -1832,11 +1838,15 @@ namespace Slic3r {
for (unsigned int v = 0; v < 3; ++v) for (unsigned int v = 0; v < 3; ++v)
{ {
unsigned int tri_id = geometry.triangles[src_start_id + ii + v] * 3; 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]); Vec3f vertex(geometry.vertices[tri_id + 0], geometry.vertices[tri_id + 1], geometry.vertices[tri_id + 2]);
facet.vertex[v] = has_transform ? facet.vertex[v] = has_transform ?
// revert the vertices to the original mesh reference system // revert the vertices to the original mesh reference system
(inv_matrix * vertex.cast<double>()).cast<float>() : (inv_matrix * vertex.cast<double>()).cast<float>() :
vertex; vertex;
#endif // ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
} }
} }
@ -1844,9 +1854,15 @@ namespace Slic3r {
triangle_mesh.repair(); triangle_mesh.repair();
ModelVolume* volume = object.add_volume(std::move(triangle_mesh)); 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 // apply the volume matrix taken from the metadata, if present
if (has_transform) if (has_transform)
volume->set_transformation(Slic3r::Geometry::Transformation(volume_matrix_to_object)); volume->set_transformation(Slic3r::Geometry::Transformation(volume_matrix_to_object));
#endif //ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
volume->calculate_convex_hull(); volume->calculate_convex_hull();
// apply the remaining volume's metadata // apply the remaining volume's metadata
@ -2635,7 +2651,8 @@ namespace Slic3r {
const ModelObject* obj = obj_metadata.second.object; const ModelObject* obj = obj_metadata.second.object;
if (obj != nullptr) 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 // stores object's name
if (!obj->name.empty()) if (!obj->name.empty())
@ -2673,7 +2690,11 @@ namespace Slic3r {
// stores volume's local matrix // stores volume's local matrix
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << MATRIX_KEY << "\" " << VALUE_ATTR << "=\""; 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(); const Transform3d& matrix = volume->get_matrix();
#endif // ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
for (int r = 0; r < 4; ++r) for (int r = 0; r < 4; ++r)
{ {
for (int c = 0; c < 4; ++c) for (int c = 0; c < 4; ++c)

View File

@ -585,24 +585,36 @@ void AMFParserContext::endElement(const char * /* name */)
stl_allocate(&stl); stl_allocate(&stl);
bool has_transform = ! m_volume_transform.isApprox(Transform3d::Identity(), 1e-10); 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(); 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();) { for (size_t i = 0; i < m_volume_facets.size();) {
stl_facet &facet = stl.facet_start[i/3]; stl_facet &facet = stl.facet_start[i/3];
for (unsigned int v = 0; v < 3; ++v) for (unsigned int v = 0; v < 3; ++v)
{ {
unsigned int tri_id = m_volume_facets[i++] * 3; 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]); 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 ? facet.vertex[v] = has_transform ?
// revert the vertices to the original mesh reference system // revert the vertices to the original mesh reference system
(inv_matrix * vertex.cast<double>()).cast<float>() : (inv_matrix * vertex.cast<double>()).cast<float>() :
vertex; vertex;
#endif // ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
} }
} }
stl_get_size(&stl); stl_get_size(&stl);
mesh.repair(); mesh.repair();
m_volume->set_mesh(std::move(mesh)); m_volume->set_mesh(std::move(mesh));
#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) if (has_transform)
m_volume->set_transformation(m_volume_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)) if (m_volume->source.input_file.empty() && (m_volume->type() == ModelVolumeType::MODEL_PART))
{ {
m_volume->source.object_idx = (int)m_model.objects.size() - 1; 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]); 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) { } 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. // Parse object's layer height profile, a semicolon separated list of floats.
char *p = const_cast<char*>(m_value[1].c_str()); char *p = m_value[1].data();
for (;;) { for (;;) {
char *end = strchr(p, ';'); char *end = strchr(p, ';');
if (end != nullptr) 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. // Parse object's layer height profile, a semicolon separated list of floats.
unsigned char coord_idx = 0; unsigned char coord_idx = 0;
Eigen::Matrix<float, 5, 1, Eigen::DontAlign> point(Eigen::Matrix<float, 5, 1, Eigen::DontAlign>::Zero()); Eigen::Matrix<float, 5, 1, Eigen::DontAlign> point(Eigen::Matrix<float, 5, 1, Eigen::DontAlign>::Zero());
char *p = const_cast<char*>(m_value[1].c_str()); char *p = m_value[1].data();
for (;;) { for (;;) {
char *end = strchr(p, ';'); char *end = strchr(p, ';');
if (end != nullptr) 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 && 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) { m_object && strcmp(opt_key, "layer_height_range") == 0) {
// Parse object's layer_height_range, a semicolon separated doubles. // Parse object's layer_height_range, a semicolon separated doubles.
char* p = const_cast<char*>(m_value[1].c_str()); char* p = m_value[1].data();
char* end = strchr(p, ';'); char* end = strchr(p, ';');
*end = 0; *end = 0;
@ -998,7 +1010,7 @@ bool load_amf(const char* path, DynamicPrintConfig* config, Model* model, bool c
return false; return false;
std::string zip_mask(2, '\0'); std::string zip_mask(2, '\0');
file.read(const_cast<char*>(zip_mask.data()), 2); file.read(zip_mask.data(), 2);
file.close(); file.close();
return (zip_mask == "PK") ? load_amf_archive(path, config, model, check_version) : load_amf_file(path, config, model); return (zip_mask == "PK") ? load_amf_archive(path, config, model, check_version) : load_amf_file(path, config, model);
@ -1147,7 +1159,11 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
stream << " <metadata type=\"slic3r.modifier\">1</metadata>\n"; stream << " <metadata type=\"slic3r.modifier\">1</metadata>\n";
stream << " <metadata type=\"slic3r.volume_type\">" << ModelVolume::type_to_string(volume->type()) << "</metadata>\n"; stream << " <metadata type=\"slic3r.volume_type\">" << ModelVolume::type_to_string(volume->type()) << "</metadata>\n";
stream << " <metadata type=\"slic3r.matrix\">"; stream << " <metadata type=\"slic3r.matrix\">";
#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(); const Transform3d& matrix = volume->get_matrix();
#endif // ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
stream << std::setprecision(std::numeric_limits<double>::max_digits10); stream << std::setprecision(std::numeric_limits<double>::max_digits10);
for (int r = 0; r < 4; ++r) for (int r = 0; r < 4; ++r)
{ {
@ -1248,7 +1264,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
pt::write_xml(oss, tree); pt::write_xml(oss, tree);
out = oss.str(); out = oss.str();
int del_header_pos = out.find("<custom_gcodes_per_height"); size_t del_header_pos = out.find("<custom_gcodes_per_height");
if (del_header_pos != std::string::npos) if (del_header_pos != std::string::npos)
out.erase(out.begin(), out.begin() + del_header_pos); out.erase(out.begin(), out.begin() + del_header_pos);

View File

@ -449,7 +449,7 @@ bool loadvector(FILE *pFile, std::vector<std::string> &v)
if (::fread(&len, sizeof(len), 1, pFile) != 1) if (::fread(&len, sizeof(len), 1, pFile) != 1)
return false; return false;
std::string s(" ", len); std::string s(" ", len);
if (::fread(const_cast<char*>(s.c_str()), 1, len, pFile) != len) if (::fread(s.data(), 1, len, pFile) != len)
return false; return false;
v.push_back(std::move(s)); v.push_back(std::move(s));
} }
@ -471,7 +471,7 @@ bool loadvectornameidx(FILE *pFile, std::vector<T> &v)
if (::fread(&len, sizeof(len), 1, pFile) != 1) if (::fread(&len, sizeof(len), 1, pFile) != 1)
return false; return false;
v[i].name.assign(" ", len); v[i].name.assign(" ", len);
if (::fread(const_cast<char*>(v[i].name.c_str()), 1, len, pFile) != len) if (::fread(v[i].name.data(), 1, len, pFile) != len)
return false; return false;
} }
return true; return true;

View File

@ -122,11 +122,11 @@ Polygons AvoidCrossingPerimeters::collect_contours_all_layers(const PrintObjectP
const Layer* layer1 = object->layers()[i * 2]; const Layer* layer1 = object->layers()[i * 2];
const Layer* layer2 = object->layers()[i * 2 + 1]; const Layer* layer2 = object->layers()[i * 2 + 1];
Polygons polys; Polygons polys;
polys.reserve(layer1->slices.size() + layer2->slices.size()); polys.reserve(layer1->lslices.size() + layer2->lslices.size());
for (const ExPolygon &expoly : layer1->slices) for (const ExPolygon &expoly : layer1->lslices)
//FIXME no holes? //FIXME no holes?
polys.emplace_back(expoly.contour); polys.emplace_back(expoly.contour);
for (const ExPolygon &expoly : layer2->slices) for (const ExPolygon &expoly : layer2->lslices)
//FIXME no holes? //FIXME no holes?
polys.emplace_back(expoly.contour); polys.emplace_back(expoly.contour);
polygons_per_layer[i] = union_(polys); polygons_per_layer[i] = union_(polys);
@ -135,8 +135,8 @@ Polygons AvoidCrossingPerimeters::collect_contours_all_layers(const PrintObjectP
if (object->layers().size() & 1) { if (object->layers().size() & 1) {
const Layer *layer = object->layers().back(); const Layer *layer = object->layers().back();
Polygons polys; Polygons polys;
polys.reserve(layer->slices.size()); polys.reserve(layer->lslices.size());
for (const ExPolygon &expoly : layer->slices) for (const ExPolygon &expoly : layer->lslices)
//FIXME no holes? //FIXME no holes?
polys.emplace_back(expoly.contour); polys.emplace_back(expoly.contour);
polygons_per_layer.back() = union_(polys); 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 // - for each island, we extrude perimeters first, unless user set the infill_first
// option // option
// (Still, we have to keep track of regions because we need to apply their config) // (Still, we have to keep track of regions because we need to apply their config)
size_t n_slices = layer.slices.size(); size_t n_slices = layer.lslices.size();
const std::vector<BoundingBox> &layer_surface_bboxes = layer.slices_bboxes; const std::vector<BoundingBox> &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, // 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. // so we can just test a point inside ExPolygon::contour and we may skip testing the holes.
std::vector<size_t> slices_test_order; std::vector<size_t> slices_test_order;
@ -2023,7 +2023,7 @@ void GCode::process_layer(
const BoundingBox &bbox = layer_surface_bboxes[i]; const BoundingBox &bbox = layer_surface_bboxes[i];
return point(0) >= bbox.min(0) && point(0) < bbox.max(0) && return point(0) >= bbox.min(0) && point(0) < bbox.max(0) &&
point(1) >= bbox.min(1) && point(1) < bbox.max(1) && 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) { 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_config.apply(instance_to_print.print_object.config(), true);
m_layer = layers[instance_to_print.layer_id].layer(); m_layer = layers[instance_to_print.layer_id].layer();
if (m_config.avoid_crossing_perimeters) 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) 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"; 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. // Create the distance field for a layer below.
const coord_t distance_field_resolution = coord_t(scale_(1.) + 0.5); const coord_t distance_field_resolution = coord_t(scale_(1.) + 0.5);
*lower_layer_edge_grid = make_unique<EdgeGrid::Grid>(); *lower_layer_edge_grid = make_unique<EdgeGrid::Grid>();
(*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(); (*lower_layer_edge_grid)->calculate_sdf();
#if 0 #if 0
{ {

View File

@ -362,11 +362,10 @@ protected:
bool m_second_layer_things_done; bool m_second_layer_things_done;
// Index of a last object copy extruded. // Index of a last object copy extruded.
std::pair<const PrintObject*, Point> m_last_obj_copy; std::pair<const PrintObject*, Point> m_last_obj_copy;
/* Extensions for colorprint - now it's not a just color_print_heights, // Extensions for colorprint - now it's not a just color_print_heights,
* there can be some custom gcode. // there can be some custom gcode.
* Updated before the export and erased during the process, // Updated before the export and erased during the process,
* so no toolchange occurs twice. // so no toolchange occurs twice.
* */
std::vector<Model::CustomGCode> m_custom_gcode_per_print_z; std::vector<Model::CustomGCode> m_custom_gcode_per_print_z;
// Time estimators // Time estimators

View File

@ -47,8 +47,8 @@ void Layer::make_slices()
slices = union_ex(slices_p); slices = union_ex(slices_p);
} }
this->slices.clear(); this->lslices.clear();
this->slices.reserve(slices.size()); this->lslices.reserve(slices.size());
// prepare ordering points // prepare ordering points
Points ordering_points; Points ordering_points;
@ -61,19 +61,21 @@ void Layer::make_slices()
// populate slices vector // populate slices vector
for (size_t i : order) 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. // 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() 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, // 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. // 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 { } else {
for (LayerRegion *layerm : m_regions) 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); layerm->slices.set(union_ex(to_polygons(std::move(layerm->slices.surfaces)), true), stInternal);
} }
} }

View File

@ -106,12 +106,16 @@ public:
coordf_t print_z; // Z used for printing in unscaled coordinates coordf_t print_z; // Z used for printing in unscaled coordinates
coordf_t height; // layer height in unscaled coordinates coordf_t height; // layer height in unscaled coordinates
// collection of expolygons generated by slicing the original geometry; // Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry
// also known as 'islands' (all regions and surface types are merged here) // (with possibly differing extruder ID and slicing parameters) and merged.
// The slices are chained by the shortest traverse distance and this traversal // For the first layer, if the ELephant foot compensation is applied, this lslice is uncompensated, therefore
// order will be recovered by the G-code generator. // it includes the Elephant foot effect, thus it corresponds to the shape of the printed 1st layer.
ExPolygons slices; // These lslices aka islands are chained by the shortest traverse distance and this traversal
std::vector<BoundingBox> slices_bboxes; // 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<BoundingBox> lslices_bboxes;
size_t region_count() const { return m_regions.size(); } size_t region_count() const { return m_regions.size(); }
const LayerRegion* get_region(int idx) const { return m_regions.at(idx); } const LayerRegion* get_region(int idx) const { return m_regions.at(idx); }

View File

@ -72,7 +72,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
if (this->layer()->lower_layer != nullptr) if (this->layer()->lower_layer != nullptr)
// Cummulative sum of polygons over all the regions. // 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.layer_id = (int)this->layer()->id();
g.ext_perimeter_flow = this->flow(frExternalPerimeter); 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. // Remove voids from fill_boundaries, that are not supported by the layer below.
if (lower_layer_covered == nullptr) { if (lower_layer_covered == nullptr) {
lower_layer_covered = &lower_layer_covered_tmp; 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()) if (! lower_layer_covered->empty())
voids = diff(voids, *lower_layer_covered); 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 // of very thin (but still working) anchors, the grown expolygon would go beyond them
BridgeDetector bd( BridgeDetector bd(
initial, initial,
lower_layer->slices, lower_layer->lslices,
this->flow(frInfill, true).scaled_width() this->flow(frInfill, true).scaled_width()
); );
#ifdef SLIC3R_DEBUG #ifdef SLIC3R_DEBUG

View File

@ -66,7 +66,7 @@ Model& Model::assign_copy(Model &&rhs)
rhs.objects.clear(); rhs.objects.clear();
// copy custom code per height // 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; return *this;
} }
@ -1953,15 +1953,14 @@ extern bool model_has_advanced_features(const Model &model)
extern void update_custom_gcode_per_print_z_from_config(std::vector<Model::CustomGCode>& custom_gcode_per_print_z, DynamicPrintConfig* config) extern void update_custom_gcode_per_print_z_from_config(std::vector<Model::CustomGCode>& custom_gcode_per_print_z, DynamicPrintConfig* config)
{ {
if (!config->has("colorprint_heights")) auto *colorprint_heights = config->option<ConfigOptionFloats>("colorprint_heights");
if (colorprint_heights == nullptr)
return; return;
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<std::string>& colors = GCodePreviewData::ColorPrintColors(); const std::vector<std::string>& colors = GCodePreviewData::ColorPrintColors();
const auto& colorprint_values = colorprint_heights->values;
const auto& colorprint_values = config->option<ConfigOptionFloats>("colorprint_heights")->values;
if (!colorprint_values.empty())
{
custom_gcode_per_print_z.clear(); custom_gcode_per_print_z.clear();
custom_gcode_per_print_z.reserve(colorprint_values.size()); custom_gcode_per_print_z.reserve(colorprint_values.size());
int i = 0; int i = 0;
@ -1969,9 +1968,8 @@ extern void update_custom_gcode_per_print_z_from_config(std::vector<Model::Custo
custom_gcode_per_print_z.emplace_back(Model::CustomGCode{ val, ColorChangeCode, 1, colors[(++i)%7] }); 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. // The "colorprint_heights" config value has been deprecated. At this point of time it has been converted
* It wouldn't be used in the future, so erase it. // to a new format and therefore it shall be erased.
* */
config->erase("colorprint_heights"); config->erase("colorprint_heights");
} }

View File

@ -403,8 +403,13 @@ public:
int object_idx{ -1 }; int object_idx{ -1 };
int volume_idx{ -1 }; int volume_idx{ -1 };
Vec3d mesh_offset{ Vec3d::Zero() }; Vec3d mesh_offset{ Vec3d::Zero() };
#if ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
Geometry::Transformation transform;
template<class Archive> void serialize(Archive& ar) { ar(input_file, object_idx, volume_idx, mesh_offset, transform); }
#else
template<class Archive> void serialize(Archive& ar) { ar(input_file, object_idx, volume_idx, mesh_offset); } template<class Archive> void serialize(Archive& ar) { ar(input_file, object_idx, volume_idx, mesh_offset); }
#endif // ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
}; };
Source source; Source source;
@ -754,21 +759,15 @@ public:
// Extensions for color print // Extensions for color print
struct CustomGCode struct CustomGCode
{ {
bool operator<(const CustomGCode& other) const { return other.print_z > this->print_z; } bool operator<(const CustomGCode& rhs) const { return this->print_z < rhs.print_z; }
bool operator==(const CustomGCode& other) const bool operator==(const CustomGCode& rhs) const
{ {
return (other.print_z == this->print_z ) && return (rhs.print_z == this->print_z ) &&
(other.gcode == this->gcode ) && (rhs.gcode == this->gcode ) &&
(other.extruder == this->extruder ) && (rhs.extruder == this->extruder ) &&
(other.color == this->color ); (rhs.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 );
} }
bool operator!=(const CustomGCode& rhs) const { return ! (*this == rhs); }
double print_z; double print_z;
std::string gcode; 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); extern bool model_has_multi_part_objects(const Model &model);
// If the model has advanced features, then it cannot be processed in simple mode. // If the model has advanced features, then it cannot be processed in simple mode.
extern bool model_has_advanced_features(const Model &model); extern bool model_has_advanced_features(const Model &model);
/* If loaded configuration has a "colorprint_heights" option (if it was imported from older Slicer), // 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 // 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<Model::CustomGCode>& custom_gcode_per_print_z, DynamicPrintConfig* config); extern void update_custom_gcode_per_print_z_from_config(std::vector<Model::CustomGCode>& custom_gcode_per_print_z, DynamicPrintConfig* config);
#ifndef NDEBUG #ifndef NDEBUG

View File

@ -1658,7 +1658,7 @@ void Print::_make_skirt()
for (const Layer *layer : object->m_layers) { for (const Layer *layer : object->m_layers) {
if (layer->print_z > skirt_height_z) if (layer->print_z > skirt_height_z)
break; 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. // Collect the outer contour points only, ignore holes for the calculation of the convex hull.
append(object_points, expoly.contour.points); append(object_points, expoly.contour.points);
} }
@ -1787,7 +1787,7 @@ void Print::_make_brim()
Polygons islands; Polygons islands;
for (PrintObject *object : m_objects) { for (PrintObject *object : m_objects) {
Polygons object_islands; 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); object_islands.push_back(expoly.contour);
if (! object->support_layers().empty()) if (! object->support_layers().empty())
object->support_layers().front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON)); object->support_layers().front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON));

View File

@ -182,7 +182,7 @@ private:
void _slice(const std::vector<coordf_t> &layer_height_profile); void _slice(const std::vector<coordf_t> &layer_height_profile);
std::string _fix_slicing_errors(); std::string _fix_slicing_errors();
void _simplify_slices(double distance); void simplify_slices(double distance);
bool has_support_material() const; bool has_support_material() const;
void detect_surfaces_type(); void detect_surfaces_type();
void process_external_surfaces(); void process_external_surfaces();

View File

@ -1333,9 +1333,11 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("octoprint"); def->enum_values.push_back("octoprint");
def->enum_values.push_back("duet"); def->enum_values.push_back("duet");
def->enum_values.push_back("flashair"); def->enum_values.push_back("flashair");
def->enum_values.push_back("astrobox");
def->enum_labels.push_back("OctoPrint"); def->enum_labels.push_back("OctoPrint");
def->enum_labels.push_back("Duet"); def->enum_labels.push_back("Duet");
def->enum_labels.push_back("FlashAir"); def->enum_labels.push_back("FlashAir");
def->enum_values.push_back("AstroBox");
def->mode = comAdvanced; def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<PrintHostType>(htOctoPrint)); def->set_default_value(new ConfigOptionEnum<PrintHostType>(htOctoPrint));
@ -3466,7 +3468,8 @@ CLIMiscConfigDef::CLIMiscConfigDef()
def = this->add("loglevel", coInt); def = this->add("loglevel", coInt);
def->label = L("Logging level"); 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; def->min = 0;
#if (defined(_MSC_VER) || defined(__MINGW32__)) && defined(SLIC3R_GUI) #if (defined(_MSC_VER) || defined(__MINGW32__)) && defined(SLIC3R_GUI)

View File

@ -30,7 +30,7 @@ enum GCodeFlavor : unsigned char {
}; };
enum PrintHostType { enum PrintHostType {
htOctoPrint, htDuet, htFlashAir htOctoPrint, htDuet, htFlashAir, htAstroBox
}; };
enum InfillPattern { enum InfillPattern {
@ -103,6 +103,7 @@ template<> inline const t_config_enum_values& ConfigOptionEnum<PrintHostType>::g
keys_map["octoprint"] = htOctoPrint; keys_map["octoprint"] = htOctoPrint;
keys_map["duet"] = htDuet; keys_map["duet"] = htDuet;
keys_map["flashair"] = htFlashAir; keys_map["flashair"] = htFlashAir;
keys_map["astrobox"] = htAstroBox;
} }
return keys_map; return keys_map;
} }

View File

@ -117,7 +117,7 @@ void PrintObject::slice()
BOOST_LOG_TRIVIAL(info) << warning; BOOST_LOG_TRIVIAL(info) << warning;
// Simplify slices if required. // Simplify slices if required.
if (m_print->config().resolution) if (m_print->config().resolution)
this->_simplify_slices(scale_(this->print()->config().resolution)); this->simplify_slices(scale_(this->print()->config().resolution));
// Update bounding boxes // Update bounding boxes
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()), tbb::blocked_range<size_t>(0, m_layers.size()),
@ -125,10 +125,10 @@ void PrintObject::slice()
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
m_print->throw_if_canceled(); m_print->throw_if_canceled();
Layer &layer = *m_layers[layer_idx]; Layer &layer = *m_layers[layer_idx];
layer.slices_bboxes.clear(); layer.lslices_bboxes.clear();
layer.slices_bboxes.reserve(layer.slices.size()); layer.lslices_bboxes.reserve(layer.lslices.size());
for (const ExPolygon &expoly : layer.slices) for (const ExPolygon &expoly : layer.lslices)
layer.slices_bboxes.emplace_back(get_extents(expoly)); layer.lslices_bboxes.emplace_back(get_extents(expoly));
} }
}); });
if (m_layers.empty()) if (m_layers.empty())
@ -242,13 +242,6 @@ void PrintObject::make_perimeters()
m_print->throw_if_canceled(); m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - end"; 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); this->set_done(posPerimeters);
} }
@ -692,7 +685,7 @@ void PrintObject::detect_surfaces_type()
if (upper_layer) { if (upper_layer) {
Polygons upper_slices = interface_shells ? Polygons upper_slices = interface_shells ?
to_polygons(upper_layer->get_region(idx_region)->slices.surfaces) : to_polygons(upper_layer->get_region(idx_region)->slices.surfaces) :
to_polygons(upper_layer->slices); to_polygons(upper_layer->lslices);
surfaces_append(top, surfaces_append(top,
//FIXME implement offset2_ex working over ExPolygons, that should be a bit more efficient than calling offset_ex twice. //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), 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( surfaces_append(
bottom, bottom,
offset2_ex( offset2_ex(
diff(layerm_slices_surfaces, to_polygons(lower_layer->slices), true), diff(layerm_slices_surfaces, to_polygons(lower_layer->lslices), true),
-offset, offset), -offset, offset),
surface_type_bottom_other); surface_type_bottom_other);
// if user requested internal shells, we need to identify surfaces // if user requested internal shells, we need to identify surfaces
@ -733,7 +726,7 @@ void PrintObject::detect_surfaces_type()
bottom, bottom,
offset2_ex( offset2_ex(
diff( 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), to_polygons(lower_layer->get_region(idx_region)->slices.surfaces),
true), true),
-offset, offset), -offset, offset),
@ -879,7 +872,7 @@ void PrintObject::process_external_surfaces()
// Shrink the holes, let the layer above expand slightly inside the unsupported areas. // Shrink the holes, let the layer above expand slightly inside the unsupported areas.
polygons_append(voids, offset(surface.expolygon, unsupported_width)); 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); 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. // 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.) { if (perimeter_offset > 0.) {
// The layer.slices are forced to merge by expanding them first. // The layer.lslices 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)); 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 #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{ {
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-extra-holes-%d.svg", debug_idx), get_extents(layer.slices)); Slic3r::SVG svg(debug_out_path("discover_vertical_shells-extra-holes-%d.svg", debug_idx), get_extents(layer.slices));
@ -1762,42 +1755,55 @@ end:
; ;
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - make_slices in parallel - begin"; BOOST_LOG_TRIVIAL(debug) << "Slicing objects - make_slices in parallel - begin";
{
// 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::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()), tbb::blocked_range<size_t>(0, m_layers.size()),
[this, upscaled, clipped](const tbb::blocked_range<size_t>& range) { [this, upscaled, clipped, xy_compensation_scaled, elephant_foot_compensation_scaled, &lslices_1st_layer]
(const tbb::blocked_range<size_t>& range) {
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
m_print->throw_if_canceled(); m_print->throw_if_canceled();
Layer *layer = m_layers[layer_id]; Layer *layer = m_layers[layer_id];
// Apply size compensation and perform clipping of multi-part objects. // Apply size compensation and perform clipping of multi-part objects.
float delta = float(scale_(m_config.xy_size_compensation.value)); float elfoot = (layer_id == 0) ? elephant_foot_compensation_scaled : 0.f;
//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) { if (layer->m_regions.size() == 1) {
assert(! upscaled);
assert(! clipped);
// Optimized version for a single region layer. // 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. // Single region, growing or shrinking.
LayerRegion *layerm = layer->m_regions.front(); 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<double>(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. // Apply the XY compensation.
ExPolygons expolygons = (delta == 0.f) ? layerm->slices.set(
to_expolygons(std::move(layerm->slices.surfaces)) : offset_ex(to_expolygons(std::move(layerm->slices.surfaces)), xy_compensation_scaled),
offset_ex(to_expolygons(std::move(layerm->slices.surfaces)), delta); stInternal);
// Apply the elephant foot compensation.
if (elephant_foot_compensation > 0)
expolygons = union_ex(Slic3r::elephant_foot_compensation(expolygons, layerm->flow(frExternalPerimeter), unscale<double>(elephant_foot_compensation)));
layerm->slices.set(std::move(expolygons), stInternal);
} }
} else { } else {
bool upscale = ! upscaled && delta > 0.f; bool upscale = ! upscaled && xy_compensation_scaled > 0.f;
bool clip = ! clipped && m_config.clip_multipart_objects.value; bool clip = ! clipped && m_config.clip_multipart_objects.value;
if (upscale || clip) { if (upscale || clip) {
// Multiple regions, growing or just clipping one region by the other. // Multiple regions, growing or just clipping one region by the other.
@ -1807,7 +1813,7 @@ end:
LayerRegion *layerm = layer->m_regions[region_id]; LayerRegion *layerm = layer->m_regions[region_id];
ExPolygons slices = to_expolygons(std::move(layerm->slices.surfaces)); ExPolygons slices = to_expolygons(std::move(layerm->slices.surfaces));
if (upscale) if (upscale)
slices = offset_ex(std::move(slices), delta); slices = offset_ex(std::move(slices), xy_compensation_scaled);
if (region_id > 0 && clip) if (region_id > 0 && clip)
// Trim by the slices of already processed regions. // Trim by the slices of already processed regions.
slices = diff_ex(to_polygons(std::move(slices)), processed); slices = diff_ex(to_polygons(std::move(slices)), processed);
@ -1817,15 +1823,16 @@ end:
layerm->slices.set(std::move(slices), stInternal); layerm->slices.set(std::move(slices), stInternal);
} }
} }
if (delta < 0.f || elephant_foot_compensation > 0.f) { if (xy_compensation_scaled < 0.f || elfoot > 0.f) {
// Apply the negative XY compensation. // Apply the negative XY compensation.
Polygons trimming; Polygons trimming;
static const float eps = float(scale_(m_config.slice_closing_radius.value) * 1.5); static const float eps = float(scale_(m_config.slice_closing_radius.value) * 1.5);
if (elephant_foot_compensation > 0.f) { if (elfoot > 0.f) {
trimming = to_polygons(Slic3r::elephant_foot_compensation(offset_ex(layer->merged(eps), std::min(delta, 0.f) - eps), lslices_1st_layer = offset_ex(layer->merged(eps), std::min(xy_compensation_scaled, 0.f) - eps);
layer->m_regions.front()->flow(frExternalPerimeter), unscale<double>(elephant_foot_compensation))); trimming = to_polygons(Slic3r::elephant_foot_compensation(lslices_1st_layer,
layer->m_regions.front()->flow(frExternalPerimeter), unscale<double>(elfoot)));
} else } else
trimming = offset(layer->merged(float(SCALED_EPSILON)), delta - float(SCALED_EPSILON)); 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) for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id)
layer->m_regions[region_id]->trim_surfaces(trimming); layer->m_regions[region_id]->trim_surfaces(trimming);
} }
@ -1834,6 +1841,15 @@ end:
layer->make_slices(); 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(); m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - make_slices in parallel - end"; 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"; BOOST_LOG_TRIVIAL(debug) << "Slicing objects - fixing slicing errors in parallel - end";
// remove empty layers from bottom // 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(); delete m_layers.front();
m_layers.erase(m_layers.begin()); m_layers.erase(m_layers.begin());
m_layers.front()->lower_layer = nullptr; 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. // Simplify the sliced model, if "resolution" configuration parameter > 0.
// The simplification is problematic, because it simplifies the slices independent from each other, // The simplification is problematic, because it simplifies the slices independent from each other,
// which makes the simplified discretization visible on the object surface. // 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"; BOOST_LOG_TRIVIAL(debug) << "Slicing objects - siplifying slices in parallel - begin";
tbb::parallel_for( tbb::parallel_for(
@ -2160,9 +2176,9 @@ void PrintObject::_simplify_slices(double distance)
layer->m_regions[region_idx]->slices.simplify(distance); layer->m_regions[region_idx]->slices.simplify(distance);
{ {
ExPolygons simplified; ExPolygons simplified;
for (const ExPolygon& expoly : layer->slices) for (const ExPolygon &expoly : layer->lslices)
expoly.simplify(distance, &simplified); 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. // Detect things that we need to support.
// Cummulative slices. // Cummulative slices.
Polygons slices; Polygons slices;
polygons_append(slices, layer->slices); polygons_append(slices, layer->lslices);
// Cummulative fill surfaces. // Cummulative fill surfaces.
Polygons fill_surfaces; Polygons fill_surfaces;
// Solid surfaces to be supported. // Solid surfaces to be supported.

View File

@ -149,7 +149,7 @@ private:
Semver(semver_t ver) : ver(ver) {} Semver(semver_t ver) : ver(ver) {}
static semver_t semver_zero() { return { 0, 0, 0, nullptr, nullptr }; } static semver_t semver_zero() { return { 0, 0, 0, nullptr, nullptr }; }
static char * strdup(const std::string &str) { return ::semver_strdup(const_cast<char*>(str.c_str())); } static char * strdup(const std::string &str) { return ::semver_strdup(str.data()); }
}; };

View File

@ -1,6 +1,5 @@
#include "ClipperUtils.hpp" #include "ClipperUtils.hpp"
#include "ExtrusionEntityCollection.hpp" #include "ExtrusionEntityCollection.hpp"
#include "PerimeterGenerator.hpp"
#include "Layer.hpp" #include "Layer.hpp"
#include "Print.hpp" #include "Print.hpp"
#include "SupportMaterial.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 collect_slices_outer(const Layer &layer)
{ {
Polygons out; Polygons out;
out.reserve(out.size() + layer.slices.size()); out.reserve(out.size() + layer.lslices.size());
for (const ExPolygon &expoly : layer.slices) for (const ExPolygon &expoly : layer.lslices)
out.emplace_back(expoly.contour); out.emplace_back(expoly.contour);
return out; return out;
} }
@ -907,9 +906,9 @@ namespace SupportMaterialInternal {
polyline.extend_start(fw); polyline.extend_start(fw);
polyline.extend_end(fw); polyline.extend_end(fw);
// Is the straight perimeter segment supported at both sides? // Is the straight perimeter segment supported at both sides?
for (size_t i = 0; i < lower_layer.slices.size(); ++ i) for (size_t i = 0; i < lower_layer.lslices.size(); ++ i)
if (lower_layer.slices_bboxes[i].contains(polyline.first_point()) && lower_layer.slices_bboxes[i].contains(polyline.last_point()) && if (lower_layer.lslices_bboxes[i].contains(polyline.first_point()) && lower_layer.lslices_bboxes[i].contains(polyline.last_point()) &&
lower_layer.slices[i].contains(polyline.first_point()) && lower_layer.slices[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. // Offset a polyline into a thick line.
polygons_append(bridges, offset(polyline, 0.5f * w + 10.f)); polygons_append(bridges, offset(polyline, 0.5f * w + 10.f));
break; break;
@ -998,7 +997,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// inflate the polygons over and over. // inflate the polygons over and over.
Polygons &covered = buildplate_covered[layer_id]; Polygons &covered = buildplate_covered[layer_id];
covered = buildplate_covered[layer_id - 1]; 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. covered = union_(covered, false); // don't apply the safety offset.
} }
} }
@ -1027,7 +1026,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
Polygons contact_polygons; Polygons contact_polygons;
Polygons slices_margin_cached; Polygons slices_margin_cached;
float slices_margin_cached_offset = -1.; 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. // Offset of the lower layer, to trim the support polygons with to calculate dense supports.
float no_interface_offset = 0.f; float no_interface_offset = 0.f;
if (layer_id == 0) { if (layer_id == 0) {
@ -1166,7 +1165,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
slices_margin_cached_offset = slices_margin_offset; slices_margin_cached_offset = slices_margin_offset;
slices_margin_cached = (slices_margin_offset == 0.f) ? slices_margin_cached = (slices_margin_offset == 0.f) ?
lower_layer_polygons : 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()) { if (! buildplate_covered.empty()) {
// Trim the inflated contact surfaces by the top surfaces as well. // Trim the inflated contact surfaces by the top surfaces as well.
polygons_append(slices_margin_cached, buildplate_covered[layer_id]); 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] { 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. // 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 = 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); projection = diff(projection_raw, trimming, false);
#ifdef SLIC3R_DEBUG #ifdef SLIC3R_DEBUG
{ {
@ -2105,7 +2104,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
const Layer &object_layer = *object.layers()[i]; const Layer &object_layer = *object.layers()[i];
if (object_layer.print_z - object_layer.height > support_layer.print_z + gap_extra_above - EPSILON) if (object_layer.print_z - object_layer.height > support_layer.print_z + gap_extra_above - EPSILON)
break; 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) { if (! m_slicing_params.soluble_interface) {
// Collect all bottom surfaces, which will be extruded with a bridging flow. // 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. // Expand the bases of the support columns in the 1st layer.
columns_base->polygons = diff( columns_base->polygons = diff(
offset(columns_base->polygons, inflate_factor_1st_layer), 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) if (contacts != nullptr)
columns_base->polygons = diff(columns_base->polygons, interface_polygons); columns_base->polygons = diff(columns_base->polygons, interface_polygons);
} }

View File

@ -56,4 +56,16 @@
// Enable closing 3Dconnextion imgui settings dialog by clicking on [X] and [Close] buttons // 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) #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_ #endif // _technologies_h_

View File

@ -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); 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. // 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. // Ignore system and hidden files, which may be created by the DropBox synchronisation process.
// https://github.com/prusa3d/PrusaSlicer/issues/1298 // https://github.com/prusa3d/PrusaSlicer/issues/1298

View File

@ -417,7 +417,7 @@ std::error_code rename_file(const std::string &from, const std::string &to)
#endif #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 source(from);
const boost::filesystem::path target(to); const boost::filesystem::path target(to);
@ -436,10 +436,59 @@ int copy_file(const std::string &from, const std::string &to)
return -1; return -1;
} }
boost::filesystem::permissions(target, perms, ec); 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 &copy)
{
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<char> buffer_origin(buffer_size, 0);
std::vector<char> 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. // Ignore system and hidden files, which may be created by the DropBox synchronisation process.
// https://github.com/prusa3d/PrusaSlicer/issues/1298 // https://github.com/prusa3d/PrusaSlicer/issues/1298
bool is_plain_file(const boost::filesystem::directory_entry &dir_entry) bool is_plain_file(const boost::filesystem::directory_entry &dir_entry)
@ -486,7 +535,7 @@ std::string encode_path(const char *src)
// Convert a wide string to a local code page. // 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); int size_needed = ::WideCharToMultiByte(0, 0, wstr_src.data(), (int)wstr_src.size(), nullptr, 0, nullptr, nullptr);
std::string str_dst(size_needed, 0); std::string str_dst(size_needed, 0);
::WideCharToMultiByte(0, 0, wstr_src.data(), (int)wstr_src.size(), const_cast<char*>(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; return str_dst;
#else /* WIN32 */ #else /* WIN32 */
return src; 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. // Convert the string encoded using the local code page to a wide string.
int size_needed = ::MultiByteToWideChar(0, 0, src, len, nullptr, 0); int size_needed = ::MultiByteToWideChar(0, 0, src, len, nullptr, 0);
std::wstring wstr_dst(size_needed, 0); std::wstring wstr_dst(size_needed, 0);
::MultiByteToWideChar(0, 0, src, len, const_cast<wchar_t*>(wstr_dst.data()), size_needed); ::MultiByteToWideChar(0, 0, src, len, wstr_dst.data(), size_needed);
// Convert a wide string to utf8. // Convert a wide string to utf8.
return boost::nowide::narrow(wstr_dst.c_str()); return boost::nowide::narrow(wstr_dst.c_str());
#else /* WIN32 */ #else /* WIN32 */

View File

@ -113,6 +113,8 @@ set(SLIC3R_GUI_SOURCES
GUI/WipeTowerDialog.hpp GUI/WipeTowerDialog.hpp
GUI/RammingChart.cpp GUI/RammingChart.cpp
GUI/RammingChart.hpp GUI/RammingChart.hpp
GUI/RemovableDriveManager.cpp
GUI/RemovableDriveManager.hpp
GUI/BonjourDialog.cpp GUI/BonjourDialog.cpp
GUI/BonjourDialog.hpp GUI/BonjourDialog.hpp
GUI/ButtonsDescription.cpp GUI/ButtonsDescription.cpp
@ -153,6 +155,8 @@ set(SLIC3R_GUI_SOURCES
Utils/Duet.hpp Utils/Duet.hpp
Utils/FlashAir.cpp Utils/FlashAir.cpp
Utils/FlashAir.hpp Utils/FlashAir.hpp
Utils/AstroBox.cpp
Utils/AstroBox.hpp
Utils/PrintHost.cpp Utils/PrintHost.cpp
Utils/PrintHost.hpp Utils/PrintHost.hpp
Utils/Bonjour.cpp Utils/Bonjour.cpp
@ -170,7 +174,12 @@ if (APPLE)
list(APPEND SLIC3R_GUI_SOURCES list(APPEND SLIC3R_GUI_SOURCES
Utils/RetinaHelperImpl.mm Utils/RetinaHelperImpl.mm
Utils/MacDarkMode.mm Utils/MacDarkMode.mm
GUI/RemovableDriveManagerMM.mm
GUI/RemovableDriveManagerMM.h
) )
#DK
FIND_LIBRARY(DISKARBITRATION_LIBRARY DiskArbitration)
endif () endif ()
add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES}) add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES})
@ -178,6 +187,11 @@ add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES})
encoding_check(libslic3r_gui) encoding_check(libslic3r_gui)
target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi) 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) if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE) add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE)
endif () endif ()

View File

@ -205,7 +205,7 @@ size_t Index::load(const boost::filesystem::path &path)
#endif #endif
++ idx_line; ++ idx_line;
// Skip the initial white spaces. // Skip the initial white spaces.
char *key = left_trim(const_cast<char*>(line.data())); char *key = left_trim(line.data());
if (*key == '#') if (*key == '#')
// Skip a comment line. // Skip a comment line.
continue; continue;

View File

@ -271,7 +271,11 @@ void AppConfig::set_recent_projects(const std::vector<std::string>& 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) 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; std::string key = std::string("mouse_device:") + name;
auto it = m_storage.find(key); 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["translation_deadzone"] = std::to_string(translation_deadzone);
it->second["rotation_speed"] = std::to_string(rotation_speed); it->second["rotation_speed"] = std::to_string(rotation_speed);
it->second["rotation_deadzone"] = std::to_string(rotation_deadzone); 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) 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; 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) void AppConfig::update_config_dir(const std::string &dir)
{ {
this->set("recent", "config_directory", 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 std::string AppConfig::get_last_output_dir(const std::string &alt) const
{ {
const auto it = m_storage.find(""); const auto it = m_storage.find("");
if (it != m_storage.end()) { if (it != m_storage.end()) {
const auto it2 = it->second.find("last_output_path"); const auto it2 = it->second.find("last_output_path");

View File

@ -131,11 +131,18 @@ public:
std::vector<std::string> get_recent_projects() const; std::vector<std::string> get_recent_projects() const;
void set_recent_projects(const std::vector<std::string>& recent_projects); void set_recent_projects(const std::vector<std::string>& recent_projects);
#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); 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_speed(const std::string& name, double& speed);
bool get_mouse_device_translation_deadzone(const std::string& name, double& deadzone); 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_speed(const std::string& name, float& speed);
bool get_mouse_device_rotation_deadzone(const std::string& name, float& deadzone); 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_FILAMENTS;
static const std::string SECTION_MATERIALS; static const std::string SECTION_MATERIALS;

View File

@ -34,6 +34,7 @@
#include <boost/nowide/cstdio.hpp> #include <boost/nowide/cstdio.hpp>
#include "I18N.hpp" #include "I18N.hpp"
#include "GUI.hpp" #include "GUI.hpp"
#include "RemovableDriveManager.hpp"
namespace Slic3r { namespace Slic3r {
@ -107,7 +108,7 @@ void BackgroundSlicingProcess::process_fff()
//FIXME localize the messages //FIXME localize the messages
// Perform the final post-processing of the export path by applying the print statistics over the file name. // 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); 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?"))); 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"))); m_print->set_status(95, _utf8(L("Running post-processing scripts")));
run_post_process_scripts(export_path, m_fff_print->config()); run_post_process_scripts(export_path, m_fff_print->config());

View File

@ -338,7 +338,7 @@ void Camera::debug_render() const
float fov = (float)get_fov(); float fov = (float)get_fov();
float gui_scale = (float)get_gui_scale(); float gui_scale = (float)get_gui_scale();
ImGui::InputText("Type", const_cast<char*>(type.data()), type.length(), ImGuiInputTextFlags_ReadOnly); ImGui::InputText("Type", type.data(), type.length(), ImGuiInputTextFlags_ReadOnly);
ImGui::Separator(); ImGui::Separator();
ImGui::InputFloat3("Position", position.data(), "%.6f", ImGuiInputTextFlags_ReadOnly); ImGui::InputFloat3("Position", position.data(), "%.6f", ImGuiInputTextFlags_ReadOnly);
ImGui::InputFloat3("Target", target.data(), "%.6f", ImGuiInputTextFlags_ReadOnly); ImGui::InputFloat3("Target", target.data(), "%.6f", ImGuiInputTextFlags_ReadOnly);

View File

@ -1605,7 +1605,27 @@ void ConfigWizard::priv::on_3rdparty_install(const VendorProfile *vendor, bool i
load_pages(); 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) const auto exist_preset = [this](const std::string& section, const Materials& materials)
{ {
@ -1899,15 +1919,14 @@ ConfigWizard::ConfigWizard(wxWindow *parent)
// check, that there is selected at least one filament/material // check, that there is selected at least one filament/material
ConfigWizardPage* active_page = this->p->index->active_page(); ConfigWizardPage* active_page = this->p->index->active_page();
if ( (active_page == p->page_filaments || active_page == p->page_sla_materials) if ( (active_page == p->page_filaments || active_page == p->page_sla_materials)
&& !p->check_material_config(dynamic_cast<PageMaterials*>(active_page)->materials->technology)) && !p->check_materials_in_config(dynamic_cast<PageMaterials*>(active_page)->materials->technology))
return; return;
this->p->index->go_next(); this->p->index->go_next();
}); });
p->btn_finish->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &) p->btn_finish->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &)
{ {
if (!p->check_material_config(T_ANY)) if (p->on_bnt_finish())
return;
this->EndModal(wxID_OK); this->EndModal(wxID_OK);
}); });

View File

@ -489,7 +489,8 @@ struct ConfigWizard::priv
void on_printer_pick(PagePrinters *page, const PrinterPickerEvent &evt); void on_printer_pick(PagePrinters *page, const PrinterPickerEvent &evt);
void on_3rdparty_install(const VendorProfile *vendor, bool install); 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); void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater);
// #ys_FIXME_alise // #ys_FIXME_alise
void update_presets_in_config(const std::string& section, const std::string& alias_key, bool add); void update_presets_in_config(const std::string& section, const std::string& alias_key, bool add);

View File

@ -142,7 +142,7 @@ bool GLShader::load_from_file(const char* fragment_shader_filename, const char*
int file_length = (int)vs.tellg(); int file_length = (int)vs.tellg();
vs.seekg(0, vs.beg); vs.seekg(0, vs.beg);
std::string vertex_shader(file_length, '\0'); std::string vertex_shader(file_length, '\0');
vs.read(const_cast<char*>(vertex_shader.data()), file_length); vs.read(vertex_shader.data(), file_length);
if (!vs.good()) if (!vs.good())
return false; return false;
@ -156,7 +156,7 @@ bool GLShader::load_from_file(const char* fragment_shader_filename, const char*
file_length = (int)fs.tellg(); file_length = (int)fs.tellg();
fs.seekg(0, fs.beg); fs.seekg(0, fs.beg);
std::string fragment_shader(file_length, '\0'); std::string fragment_shader(file_length, '\0');
fs.read(const_cast<char*>(fragment_shader.data()), file_length); fs.read(fragment_shader.data(), file_length);
if (!fs.good()) if (!fs.good())
return false; return false;

View File

@ -46,6 +46,7 @@
#include "SysInfoDialog.hpp" #include "SysInfoDialog.hpp"
#include "KBShortcutsDialog.hpp" #include "KBShortcutsDialog.hpp"
#include "UpdateDialogs.hpp" #include "UpdateDialogs.hpp"
#include "RemovableDriveManager.hpp"
#ifdef __WXMSW__ #ifdef __WXMSW__
#include <Shlobj.h> #include <Shlobj.h>
@ -261,6 +262,8 @@ bool GUI_App::on_init_inner()
m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg())); m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg()));
RemovableDriveManager::get_instance().init();
Bind(wxEVT_IDLE, [this](wxIdleEvent& event) Bind(wxEVT_IDLE, [this](wxIdleEvent& event)
{ {
if (! plater_) if (! plater_)
@ -271,6 +274,10 @@ bool GUI_App::on_init_inner()
this->obj_manipul()->update_if_dirty(); this->obj_manipul()->update_if_dirty();
#if !__APPLE__
RemovableDriveManager::get_instance().update(wxGetLocalTime(), true);
#endif
// Preset updating & Configwizard are done after the above initializations, // Preset updating & Configwizard are done after the above initializations,
// and after MainFrame is created & shown. // and after MainFrame is created & shown.
// The extra CallAfter() is needed because of Mac, where this is the only way // 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->slic3r_update_notify();
preset_updater->sync(preset_bundle); preset_updater->sync(preset_bundle);
}); });
} }
}); });

View File

@ -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)) layer_height <= get_max_layer_height(extruder_idx))
{ {
config->set_key_value("layer_height", new ConfigOptionFloat(layer_height)); config->set_key_value("layer_height", new ConfigOptionFloat(layer_height));
changed_object(obj_idx);
return true; return true;
} }
@ -2952,6 +2953,7 @@ bool ObjectList::edit_layer_range(const t_layer_height_range& range, const t_lay
ranges.erase(range); ranges.erase(range);
ranges[new_range] = config; ranges[new_range] = config;
changed_object(obj_idx);
wxDataViewItem root_item = m_objects_model->GetLayerRootItem(m_objects_model->GetItemById(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) // To avoid update selection after deleting of a selected item (under GTK)

View File

@ -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) void Preview::set_enabled(bool enabled)
{ {
m_enabled = enabled; m_enabled = enabled;

View File

@ -116,7 +116,6 @@ public:
void set_as_dirty(); void set_as_dirty();
void set_number_extruders(unsigned int number_extruders); void set_number_extruders(unsigned int number_extruders);
void set_canvas_as_dirty();
void set_enabled(bool enabled); void set_enabled(bool enabled);
void bed_shape_changed(); void bed_shape_changed();
void select_view(const std::string& direction); void select_view(const std::string& direction);

View File

@ -57,13 +57,19 @@ const double Mouse3DController::State::DefaultTranslationScale = 2.5;
const double Mouse3DController::State::MaxTranslationDeadzone = 0.2; const double Mouse3DController::State::MaxTranslationDeadzone = 0.2;
const double Mouse3DController::State::DefaultTranslationDeadzone = 0.5 * Mouse3DController::State::MaxTranslationDeadzone; const double Mouse3DController::State::DefaultTranslationDeadzone = 0.5 * Mouse3DController::State::MaxTranslationDeadzone;
const float Mouse3DController::State::DefaultRotationScale = 1.0f; 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; 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() Mouse3DController::State::State()
: m_buttons_enabled(false) : m_buttons_enabled(false)
, m_translation_params(DefaultTranslationScale, DefaultTranslationDeadzone) , m_translation_params(DefaultTranslationScale, DefaultTranslationDeadzone)
, m_rotation_params(DefaultRotationScale, DefaultRotationDeadzone) , 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) , m_mouse_wheel_counter(0)
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
, m_translation_queue_max_size(0) , m_translation_queue_max_size(0)
@ -112,7 +118,7 @@ void Mouse3DController::State::append_button(unsigned int id)
bool Mouse3DController::State::process_mouse_wheel() bool Mouse3DController::State::process_mouse_wheel()
{ {
if (m_mouse_wheel_counter == 0) if (m_mouse_wheel_counter.load() == 0)
return false; return false;
else if (!m_rotation.queue.empty()) else if (!m_rotation.queue.empty())
{ {
@ -120,7 +126,7 @@ bool Mouse3DController::State::process_mouse_wheel()
return true; return true;
} }
m_mouse_wheel_counter = 0; m_mouse_wheel_counter.store(0);
return true; return true;
} }
@ -149,7 +155,13 @@ bool Mouse3DController::State::apply(Camera& camera)
if (has_translation()) if (has_translation())
{ {
const Vec3d& translation = m_translation.queue.front(); 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())); 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(); m_translation.queue.pop();
ret = true; ret = true;
} }
@ -229,8 +241,6 @@ bool Mouse3DController::apply(Camera& camera)
if (!m_initialized) if (!m_initialized)
return false; return false;
std::lock_guard<std::mutex> lock(m_mutex);
// check if the user unplugged the device // check if the user unplugged the device
if (!m_running && is_device_connected()) if (!m_running && is_device_connected())
{ {
@ -311,20 +321,30 @@ void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsign
ImGui::PopStyleColor(); ImGui::PopStyleColor();
float translation_scale = (float)m_state.get_translation_scale() / State::DefaultTranslationScale; 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); m_state.set_translation_scale(State::DefaultTranslationScale * (double)translation_scale);
float rotation_scale = m_state.get_rotation_scale() / State::DefaultRotationScale; 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); 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::Separator();
ImGui::PushStyleColor(ImGuiCol_Text, color); ImGui::PushStyleColor(ImGuiCol_Text, color);
imgui.text(_(L("Deadzone:"))); imgui.text(_(L("Deadzone:")));
ImGui::PopStyleColor(); ImGui::PopStyleColor();
float translation_deadzone = (float)m_state.get_translation_deadzone(); 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")) 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); m_state.set_translation_deadzone((double)translation_deadzone);
float rotation_deadzone = m_state.get_rotation_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() 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()) if (is_device_connected())
return false; return false;
@ -619,30 +639,36 @@ bool Mouse3DController::connect_device()
hid_get_product_string(m_device, product.data(), 1024); hid_get_product_string(m_device, product.data(), 1024);
m_device_str += "/" + boost::nowide::narrow(product.data()); m_device_str += "/" + boost::nowide::narrow(product.data());
BOOST_LOG_TRIVIAL(info) << "Connected device: " << m_device_str; BOOST_LOG_TRIVIAL(info) << "Connected 3DConnexion device:";
BOOST_LOG_TRIVIAL(info) << "Manufacturer/product: " << m_device_str;
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT BOOST_LOG_TRIVIAL(info) << "Manufacturer id.....: " << vendor_id << " (" << std::hex << vendor_id << std::dec << ")";
std::cout << std::endl << "Connected device:" << std::endl; BOOST_LOG_TRIVIAL(info) << "Product id..........: " << product_id << " (" << std::hex << product_id << std::dec << ")";
std::cout << "Manufacturer/product: " << m_device_str << std::endl; if (!path.empty())
std::cout << "Manufacturer id.....: " << vendor_id << " (" << std::hex << vendor_id << std::dec << ")" << std::endl; BOOST_LOG_TRIVIAL(info) << "Path................: '" << path << "'";
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
// get device parameters from the config, if present // get device parameters from the config, if present
double translation_speed = 1.0; double translation_speed = 1.0;
float rotation_speed = 1.0; float rotation_speed = 1.0;
double translation_deadzone = State::DefaultTranslationDeadzone; double translation_deadzone = State::DefaultTranslationDeadzone;
float rotation_deadzone = State::DefaultRotationDeadzone; 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_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_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_speed(m_device_str, rotation_speed);
wxGetApp().app_config->get_mouse_device_rotation_deadzone(m_device_str, rotation_deadzone); 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 // 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_scale(State::DefaultTranslationScale * std::clamp(translation_speed, 0.2, 5.0));
m_state.set_translation_deadzone(std::max(0.0, std::min(State::MaxTranslationDeadzone, translation_deadzone))); m_state.set_translation_deadzone(std::clamp(translation_deadzone, 0.0, State::MaxTranslationDeadzone));
m_state.set_rotation_scale(State::DefaultRotationScale * std::max(0.5f, std::min(2.0f, rotation_speed))); m_state.set_rotation_scale(State::DefaultRotationScale * std::clamp(rotation_speed, 0.2f, 5.0f));
m_state.set_rotation_deadzone(std::max(0.0f, std::min(State::MaxRotationDeadzone, rotation_deadzone))); 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 #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
else else
@ -668,8 +694,13 @@ void Mouse3DController::disconnect_device()
m_thread.join(); m_thread.join();
// Store current device parameters into the config // 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(), 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_rotation_scale() / State::DefaultRotationScale, m_state.get_rotation_deadzone());
#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
wxGetApp().app_config->save(); wxGetApp().app_config->save();
// Close the 3Dconnexion device // Close the 3Dconnexion device
@ -697,7 +728,6 @@ void Mouse3DController::run()
collect_input(); collect_input();
} }
} }
void Mouse3DController::collect_input() void Mouse3DController::collect_input()
{ {
DataPacket packet = { 0 }; DataPacket packet = { 0 };
@ -712,8 +742,6 @@ void Mouse3DController::collect_input()
if (!wxGetApp().IsActive()) if (!wxGetApp().IsActive())
return; return;
std::lock_guard<std::mutex> lock(m_mutex);
bool updated = false; bool updated = false;
if (res == 7) if (res == 7)
@ -729,9 +757,12 @@ void Mouse3DController::collect_input()
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
if (updated) if (updated)
{
wxGetApp().plater()->set_current_canvas_as_dirty();
// ask for an idle event to update 3D scene // ask for an idle event to update 3D scene
wxWakeUpIdle(); wxWakeUpIdle();
} }
}
bool Mouse3DController::handle_packet(const DataPacket& packet) bool Mouse3DController::handle_packet(const DataPacket& packet)
{ {

View File

@ -33,6 +33,9 @@ class Mouse3DController
static const float DefaultRotationScale; static const float DefaultRotationScale;
static const float MaxRotationDeadzone; static const float MaxRotationDeadzone;
static const float DefaultRotationDeadzone; static const float DefaultRotationDeadzone;
#if ENABLE_3DCONNEXION_Y_AS_ZOOM
static const double DefaultZoomScale;
#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
private: private:
template <typename Number> template <typename Number>
@ -64,6 +67,9 @@ class Mouse3DController
CustomParameters<double> m_translation_params; CustomParameters<double> m_translation_params;
CustomParameters<float> m_rotation_params; CustomParameters<float> m_rotation_params;
#if ENABLE_3DCONNEXION_Y_AS_ZOOM
CustomParameters<float> 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. // 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 // 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 // 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_mouse_wheel() through the call to the process_mouse_wheel() method
// GLCanvas3D::on_idle() through the call to the apply() method // GLCanvas3D::on_idle() through the call to the apply() method
unsigned int m_mouse_wheel_counter; std::atomic<unsigned int> m_mouse_wheel_counter;
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT #if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
size_t m_translation_queue_max_size; size_t m_translation_queue_max_size;
@ -99,6 +105,11 @@ class Mouse3DController
float get_rotation_scale() const { return m_rotation_params.scale; } float get_rotation_scale() const { return m_rotation_params.scale; }
void set_rotation_scale(float scale) { m_rotation_params.scale = 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; } double get_translation_deadzone() const { return m_translation_params.deadzone; }
void set_translation_deadzone(double deadzone) { m_translation_params.deadzone = deadzone; } void set_translation_deadzone(double deadzone) { m_translation_params.deadzone = deadzone; }
@ -128,7 +139,6 @@ class Mouse3DController
bool m_initialized; bool m_initialized;
mutable State m_state; mutable State m_state;
std::mutex m_mutex;
std::thread m_thread; std::thread m_thread;
hid_device* m_device; hid_device* m_device;
std::string m_device_str; std::string m_device_str;
@ -151,7 +161,7 @@ public:
bool is_device_connected() const { return m_device != nullptr; } bool is_device_connected() const { return m_device != nullptr; }
bool is_running() const { return m_running; } bool is_running() const { return m_running; }
bool process_mouse_wheel() { std::lock_guard<std::mutex> lock(m_mutex); return m_state.process_mouse_wheel(); } bool process_mouse_wheel() { return m_state.process_mouse_wheel(); }
bool apply(Camera& camera); bool apply(Camera& camera);

View File

@ -80,6 +80,7 @@
#include "../Utils/FixModelByWin10.hpp" #include "../Utils/FixModelByWin10.hpp"
#include "../Utils/UndoRedo.hpp" #include "../Utils/UndoRedo.hpp"
#include "../Utils/Thread.hpp" #include "../Utils/Thread.hpp"
#include "RemovableDriveManager.hpp"
#include <wx/glcanvas.h> // Needs to be last because reasons :-/ #include <wx/glcanvas.h> // Needs to be last because reasons :-/
#include "WipeTowerDialog.hpp" #include "WipeTowerDialog.hpp"
@ -701,7 +702,8 @@ struct Sidebar::priv
wxButton *btn_export_gcode; wxButton *btn_export_gcode;
wxButton *btn_reslice; wxButton *btn_reslice;
wxButton *btn_send_gcode; ScalableButton *btn_send_gcode;
ScalableButton *btn_remove_device;
priv(Plater *plater) : plater(plater) {} priv(Plater *plater) : plater(plater) {}
~priv(); ~priv();
@ -850,22 +852,47 @@ Sidebar::Sidebar(Plater *parent)
// Buttons underneath the scrolled area // 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, *btn = new wxButton(this, wxID_ANY, label, wxDefaultPosition,
wxDefaultSize, wxBU_EXACTFIT); wxSize(-1, button_height), wxBU_EXACTFIT);
(*btn)->SetFont(wxGetApp().bold_font()); (*btn)->SetFont(wxGetApp().bold_font());
}; };
init_btn(&p->btn_send_gcode, _(L("Send to printer"))); init_btn(&p->btn_export_gcode, _(L("Export G-code")) + dots , scaled_height);
p->btn_send_gcode->Hide(); init_btn(&p->btn_reslice , _(L("Slice now")) , scaled_height);
init_btn(&p->btn_export_gcode, _(L("Export G-code")) + dots);
init_btn(&p->btn_reslice, _(L("Slice now")));
enable_buttons(false); enable_buttons(false);
auto *btns_sizer = new wxBoxSizer(wxVERTICAL); 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_reslice, 0, wxEXPAND | wxTOP, margin_5);
btns_sizer->Add(p->btn_send_gcode, 0, wxEXPAND | wxTOP, margin_5); btns_sizer->Add(complect_btns_sizer, 0, wxEXPAND | wxTOP, margin_5);
btns_sizer->Add(p->btn_export_gcode, 0, wxEXPAND | wxTOP, margin_5);
auto *sizer = new wxBoxSizer(wxVERTICAL); auto *sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(p->scrolled, 1, wxEXPAND); sizer->Add(p->scrolled, 1, wxEXPAND);
@ -884,6 +911,7 @@ Sidebar::Sidebar(Plater *parent)
p->plater->select_view_3D("Preview"); p->plater->select_view_3D("Preview");
}); });
p->btn_send_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->send_gcode(); }); 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() {} Sidebar::~Sidebar() {}
@ -1029,6 +1057,12 @@ void Sidebar::msw_rescale()
p->object_info->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(); p->scrolled->Layout();
} }
@ -1257,11 +1291,13 @@ void Sidebar::enable_buttons(bool enable)
p->btn_reslice->Enable(enable); p->btn_reslice->Enable(enable);
p->btn_export_gcode->Enable(enable); p->btn_export_gcode->Enable(enable);
p->btn_send_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_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_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_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() bool Sidebar::is_multifilament()
{ {
@ -1736,6 +1772,8 @@ struct Plater::priv
bool is_preview_loaded() const { return preview->is_loaded(); } bool is_preview_loaded() const { return preview->is_loaded(); }
bool is_view3D_shown() const { return current_panel == view3D; } bool is_view3D_shown() const { return current_panel == view3D; }
void set_current_canvas_as_dirty();
#if ENABLE_VIEW_TOOLBAR_BACKGROUND_FIX #if ENABLE_VIEW_TOOLBAR_BACKGROUND_FIX
bool init_view_toolbar(); bool init_view_toolbar();
#endif // ENABLE_VIEW_TOOLBAR_BACKGROUND_FIX #endif // ENABLE_VIEW_TOOLBAR_BACKGROUND_FIX
@ -3140,6 +3178,7 @@ void Plater::priv::update_fff_scene()
this->preview->reload_print(); this->preview->reload_print();
// In case this was MM print, wipe tower bounding box on 3D tab might need redrawing with exact depth: // In case this was MM print, wipe tower bounding box on 3D tab might need redrawing with exact depth:
view3D->reload_scene(true); view3D->reload_scene(true);
} }
void Plater::priv::update_sla_scene() 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->config.apply(old_volume->config);
new_volume->set_type(old_volume->type()); new_volume->set_type(old_volume->type());
new_volume->set_material_id(old_volume->material_id()); 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()); 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->translate(new_volume->get_transformation().get_matrix(true) * (new_volume->source.mesh_offset - old_volume->source.mesh_offset));
new_volume->source.input_file = path; new_volume->source.input_file = path;
std::swap(old_model_object->volumes[old_v.volume_idx], old_model_object->volumes.back()); 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); 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(); model.adjust_min_z();
#endif // !ENABLE_KEEP_LOADED_VOLUME_TRANSFORM_AS_STAND_ALONE
// update 3D scene // update 3D scene
update(); update();
@ -3412,7 +3460,7 @@ void Plater::priv::set_current_panel(wxPanel* panel)
// keeps current gcode preview, if any // keeps current gcode preview, if any
preview->reload_print(true); preview->reload_print(true);
preview->set_canvas_as_dirty(); preview->set_as_dirty();
view_toolbar.select_item("Preview"); view_toolbar.select_item("Preview");
} }
@ -3554,6 +3602,7 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt)
default: break; default: break;
} }
if (canceled) { if (canceled) {
if (wxGetApp().get_mode() == comSimple) if (wxGetApp().get_mode() == comSimple)
sidebar->set_btn_label(ActionButtonType::abReslice, "Slice now"); sidebar->set_btn_label(ActionButtonType::abReslice, "Slice now");
@ -3561,6 +3610,16 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt)
} }
else if (wxGetApp().get_mode() == comSimple) else if (wxGetApp().get_mode() == comSimple)
show_action_buttons(false); 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) void Plater::priv::on_layer_editing_toggled(bool enable)
@ -3914,6 +3973,14 @@ bool Plater::priv::complit_init_part_menu()
return true; 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 #if ENABLE_VIEW_TOOLBAR_BACKGROUND_FIX
bool Plater::priv::init_view_toolbar() bool Plater::priv::init_view_toolbar()
#else #else
@ -4027,7 +4094,11 @@ bool Plater::priv::can_reload_from_disk() const
const GLVolume* v = selection.get_volume(idx); const GLVolume* v = selection.get_volume(idx);
int v_idx = v->volume_idx(); int v_idx = v->volume_idx();
if (v_idx >= 0) 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()); std::sort(selected_volumes.begin(), selected_volumes.end());
selected_volumes.erase(std::unique(selected_volumes.begin(), selected_volumes.end()), selected_volumes.end()); selected_volumes.erase(std::unique(selected_volumes.begin(), selected_volumes.end()), selected_volumes.end());
@ -4130,19 +4201,22 @@ void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const
const auto prin_host_opt = config->option<ConfigOptionString>("print_host"); const auto prin_host_opt = config->option<ConfigOptionString>("print_host");
const bool send_gcode_shown = prin_host_opt != nullptr && !prin_host_opt->value.empty(); 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 // when a background processing is ON, export_btn and/or send_btn are showing
if (wxGetApp().app_config->get("background_processing") == "1") if (wxGetApp().app_config->get("background_processing") == "1")
{ {
if (sidebar->show_reslice(false) | if (sidebar->show_reslice(false) |
sidebar->show_export(true) | sidebar->show_export(true) |
sidebar->show_send(send_gcode_shown)) sidebar->show_send(send_gcode_shown) |
sidebar->show_disconnect(disconnect_shown))
sidebar->Layout(); sidebar->Layout();
} }
else else
{ {
if (sidebar->show_reslice(is_ready_to_slice) | if (sidebar->show_reslice(is_ready_to_slice) |
sidebar->show_export(!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(); 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::abReslice: p->btn_reslice->SetLabelText(label); break;
case ActionButtonType::abExport: p->btn_export_gcode->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())); 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()); 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:")), wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _(L("Save G-code file as:")) : _(L("Save SL1 file as:")),
start_dir, start_dir,
from_path(default_output_file.filename()), from_path(default_output_file.filename()),
@ -4688,7 +4768,22 @@ void Plater::export_gcode()
output_path = std::move(path); output_path = std::move(path);
} }
if (! output_path.empty()) if (! output_path.empty())
{
std::string path = output_path.string();
p->export_gcode(std::move(output_path), PrintHostJob()); 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) 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 std::string &snapshot_name) { p->take_snapshot(snapshot_name); }
void Plater::take_snapshot(const wxString &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(); } void Plater::suppress_snapshots() { p->suppress_snapshots(); }
@ -5253,6 +5369,11 @@ BoundingBoxf Plater::bed_shape_bb() const
return p->bed_shape_bb(); return p->bed_shape_bb();
} }
void Plater::set_current_canvas_as_dirty()
{
p->set_current_canvas_as_dirty();
}
PrinterTechnology Plater::printer_technology() const PrinterTechnology Plater::printer_technology() const
{ {
return p->printer_technology; return p->printer_technology;

View File

@ -119,6 +119,7 @@ public:
bool show_reslice(bool show) const; bool show_reslice(bool show) const;
bool show_export(bool show) const; bool show_export(bool show) const;
bool show_send(bool show) const; bool show_send(bool show) const;
bool show_disconnect(bool show)const;
bool is_multifilament(); bool is_multifilament();
void update_mode(); void update_mode();
@ -202,6 +203,8 @@ public:
void suppress_background_process(const bool stop_background_process) ; void suppress_background_process(const bool stop_background_process) ;
void fix_through_netfabb(const int obj_idx, const int vol_idx = -1); void fix_through_netfabb(const int obj_idx, const int vol_idx = -1);
void send_gcode(); void send_gcode();
void eject_drive();
void drive_ejected_callback();
void take_snapshot(const std::string &snapshot_name); void take_snapshot(const std::string &snapshot_name);
void take_snapshot(const wxString &snapshot_name); void take_snapshot(const wxString &snapshot_name);
@ -238,6 +241,8 @@ public:
GLCanvas3D* canvas3D(); GLCanvas3D* canvas3D();
BoundingBoxf bed_shape_bb() const; BoundingBoxf bed_shape_bb() const;
void set_current_canvas_as_dirty();
PrinterTechnology printer_technology() const; PrinterTechnology printer_technology() const;
void set_printer_technology(PrinterTechnology printer_technology); void set_printer_technology(PrinterTechnology printer_technology);

View File

@ -594,6 +594,7 @@ void PresetCollection::reset(bool delete_files)
m_presets.erase(m_presets.begin() + m_num_default_presets, m_presets.end()); m_presets.erase(m_presets.begin() + m_num_default_presets, m_presets.end());
this->select_preset(0); this->select_preset(0);
} }
m_map_system_profile_renamed.clear();
} }
void PresetCollection::add_default_preset(const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &preset_name) void PresetCollection::add_default_preset(const std::vector<std::string> &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? // Is there a preset already loaded with the name stored inside the config?
std::deque<Preset>::iterator it = this->find_preset_internal(original_name); std::deque<Preset>::iterator it = this->find_preset_internal(original_name);
bool found = it != m_presets.end() && it->name == 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)) { if (found && profile_print_params_same(it->config, cfg)) {
// The preset exists and it matches the values stored inside config. // The preset exists and it matches the values stored inside config.
if (select) if (select)
@ -876,24 +882,27 @@ const Preset* PresetCollection::get_selected_preset_parent() const
if (this->get_selected_idx() == -1) if (this->get_selected_idx() == -1)
// This preset collection has no preset activated yet. Only the get_edited_preset() is valid. // This preset collection has no preset activated yet. Only the get_edited_preset() is valid.
return nullptr; 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(); const Preset &selected_preset = this->get_selected_preset();
if (inherits.empty()) if (selected_preset.is_system || selected_preset.is_default)
{ return &selected_preset;
if (this->get_selected_preset().is_system || this->get_selected_preset().is_default)
return &this->get_selected_preset(); const Preset &edited_preset = this->get_edited_preset();
if (this->get_selected_preset().is_external) const std::string &inherits = edited_preset.inherits();
const Preset *preset = nullptr;
if (inherits.empty()) {
if (selected_preset.is_external)
return nullptr; return nullptr;
preset = &this->default_preset(m_type == Preset::Type::TYPE_PRINTER && edited_preset.printer_technology() == ptSLA ? 1 : 0);
inherits = m_type != Preset::Type::TYPE_PRINTER ? "- default -" : } else
this->get_edited_preset().printer_technology() == ptFFF ? preset = this->find_preset(inherits, false);
"- default FFF -" : "- default SLA -" ; 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; 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 this->get_selected_preset().is_system ? &this->get_selected_preset() : nullptr;
return nullptr; return nullptr;
const Preset* preset = this->find_preset(inherits, false); 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; return (preset == nullptr/* || preset->is_default */|| preset->is_external) ? nullptr : preset;
} }
@ -1406,6 +1420,17 @@ std::vector<std::string> PresetCollection::merge_presets(PresetCollection &&othe
return duplicates; 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<std::string, std::string>(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 std::string PresetCollection::name() const
{ {
switch (this->type()) { switch (this->type()) {

View File

@ -163,6 +163,10 @@ public:
// Alias of the preset // Alias of the preset
std::string alias = ""; 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<std::string> renamed_from;
void save(); void save();
@ -333,7 +337,8 @@ public:
// The parent preset may be a system preset or a user preset, which will be // The parent preset may be a system preset or a user preset, which will be
// reflected by the UI. // reflected by the UI.
const Preset* get_selected_preset_parent() const; 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; const Preset* get_preset_parent(const Preset& child) const;
// Return the selected preset including the user modifications. // Return the selected preset including the user modifications.
Preset& get_edited_preset() { return m_edited_preset; } 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. // Merge one vendor's presets with the other vendor's presets, report duplicates.
std::vector<std::string> merge_presets(PresetCollection &&other, const VendorMap &new_vendors); std::vector<std::string> 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: private:
PresetCollection(); PresetCollection();
PresetCollection(const PresetCollection &other); PresetCollection(const PresetCollection &other);
@ -490,6 +498,14 @@ private:
} }
std::deque<Preset>::const_iterator find_preset_internal(const std::string &name) const std::deque<Preset>::const_iterator find_preset_internal(const std::string &name) const
{ return const_cast<PresetCollection*>(this)->find_preset_internal(name); } { return const_cast<PresetCollection*>(this)->find_preset_internal(name); }
std::deque<Preset>::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<Preset>::const_iterator find_preset_renamed(const std::string &name) const
{ return const_cast<PresetCollection*>(this)->find_preset_renamed(name); }
size_t update_compatible_internal(const PresetWithVendorProfile &active_printer, const PresetWithVendorProfile *active_print, bool unselect_if_incompatible); 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, // 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. // so that the addresses of the presets don't change during resizing of the container.
std::deque<Preset> m_presets; std::deque<Preset> m_presets;
// Map from old system profile name to a current system profile name.
std::map<std::string, std::string> 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. // Initially this preset contains a copy of the selected preset. Later on, this copy may be modified by the user.
Preset m_edited_preset; Preset m_edited_preset;
// Selected preset. // Selected preset.

View File

@ -288,6 +288,11 @@ std::string PresetBundle::load_system_presets()
// No config bundle loaded, reset. // No config bundle loaded, reset.
this->reset(false); 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; return errors_cummulative;
} }
@ -555,9 +560,11 @@ DynamicPrintConfig PresetBundle::full_fff_config() const
while (filament_configs.size() < num_extruders) while (filament_configs.size() < num_extruders)
filament_configs.emplace_back(&this->filaments.first_visible().config); filament_configs.emplace_back(&this->filaments.first_visible().config);
for (const DynamicPrintConfig *cfg : filament_configs) { for (const DynamicPrintConfig *cfg : filament_configs) {
compatible_printers_condition.emplace_back(Preset::compatible_printers_condition(*const_cast<DynamicPrintConfig*>(cfg))); // The compatible_prints/printers_condition() returns a reference to configuration key, which may not yet exist.
compatible_prints_condition .emplace_back(Preset::compatible_prints_condition(*const_cast<DynamicPrintConfig*>(cfg))); DynamicPrintConfig &cfg_rw = *const_cast<DynamicPrintConfig*>(cfg);
inherits .emplace_back(Preset::inherits(*const_cast<DynamicPrintConfig*>(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. // Option values to set a ConfigOptionVector from.
std::vector<const ConfigOption*> filament_opts(num_extruders, nullptr); std::vector<const ConfigOption*> filament_opts(num_extruders, nullptr);
@ -1132,7 +1139,6 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
PresetCollection *presets = nullptr; PresetCollection *presets = nullptr;
std::vector<std::string> *loaded = nullptr; std::vector<std::string> *loaded = nullptr;
std::string preset_name; std::string preset_name;
std::string alias_name;
if (boost::starts_with(section.first, "print:")) { if (boost::starts_with(section.first, "print:")) {
presets = &this->prints; presets = &this->prints;
loaded = &loaded_prints; loaded = &loaded_prints;
@ -1141,12 +1147,6 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
presets = &this->filaments; presets = &this->filaments;
loaded = &loaded_filaments; loaded = &loaded_filaments;
preset_name = section.first.substr(9); 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:")) { } else if (boost::starts_with(section.first, "sla_print:")) {
presets = &this->sla_prints; presets = &this->sla_prints;
loaded = &loaded_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; presets = &this->sla_materials;
loaded = &loaded_sla_materials; loaded = &loaded_sla_materials;
preset_name = section.first.substr(13); 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:")) { } else if (boost::starts_with(section.first, "printer:")) {
presets = &this->printers; presets = &this->printers;
loaded = &loaded_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. // Load the print, filament or printer preset.
const DynamicPrintConfig *default_config = nullptr; const DynamicPrintConfig *default_config = nullptr;
DynamicPrintConfig config; DynamicPrintConfig config;
std::string alias_name;
std::vector<std::string> renamed_from;
auto parse_config_section = [&section, &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) { if (presets == &this->printers) {
// Select the default config based on the printer_technology field extracted from kvp. // Select the default config based on the printer_technology field extracted from kvp.
DynamicPrintConfig config_src; DynamicPrintConfig config_src;
for (auto &kvp : section.second) parse_config_section(config_src);
config_src.set_deserialize(kvp.first, kvp.second.data());
default_config = &presets->default_preset_for(config_src).config; default_config = &presets->default_preset_for(config_src).config;
config = *default_config; config = *default_config;
config.apply(config_src); config.apply(config_src);
} else { } else {
default_config = &presets->default_preset().config; default_config = &presets->default_preset().config;
config = *default_config; config = *default_config;
for (auto &kvp : section.second) parse_config_section(config);
config.set_deserialize(kvp.first, kvp.second.data());
} }
Preset::normalize(config); Preset::normalize(config);
// Report configuration fields, which are misplaced into a wrong group. // 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; loaded.vendor = vendor_profile;
} }
// next step of an preset name aliasing // 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("@"); int end_pos = preset_name.find_first_of("@");
if (end_pos != std::string::npos) if (end_pos != std::string::npos) {
alias_name = preset_name.substr(0, end_pos - 1); alias_name = preset_name.substr(0, end_pos);
if (renamed_from.empty())
loaded.alias = alias_name.empty() ? preset_name : alias_name; // 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; ++ presets_loaded;
} }

View File

@ -0,0 +1,590 @@
#include "RemovableDriveManager.hpp"
#include <iostream>
#include "boost/nowide/convert.hpp"
#if _WIN32
#include <windows.h>
#include <tchar.h>
#include <winioctl.h>
#include <shlwapi.h>
#include <Dbt.h>
GUID WceusbshGUID = { 0x25dbce51, 0x6c8f, 0x4a72,
0x8a,0x6d,0xb5,0x4c,0x2b,0x4f,0xc8,0x35 };
#else
//linux includes
#include <errno.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <glob.h>
#include <pwd.h>
#include <boost/filesystem.hpp>
#include <boost/filesystem/convenience.hpp>
#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<HINSTANCE>(GetModuleHandle(0));
wndClass.lpfnWndProc = reinterpret_cast<WNDPROC>(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<DriveData> 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<void()> 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: "<<m_last_save_path;
//m_drives_count = m_current_drives.size();
if(!m_last_save_path_verified)
{
//std::cout<<"\n";
return true;
}
bool r = !is_drive_mounted(m_last_save_path);
if (r) reset_last_save_path();
//std::cout<<" "<< r <<"\n";
return r;
}
bool RemovableDriveManager::is_last_drive_removed_with_update(const long time)
{
update(time, false);
return is_last_drive_removed();
}
void RemovableDriveManager::reset_last_save_path()
{
m_last_save_path_verified = false;
m_last_save_path = "";
m_last_save_name = "";
}
void RemovableDriveManager::set_is_writing(const bool b)
{
m_is_writing = b;
if (b)
{
m_did_eject = false;
}
}
bool RemovableDriveManager::get_is_writing()
{
return m_is_writing;
}
bool RemovableDriveManager::get_did_eject()
{
return m_did_eject;
}
void RemovableDriveManager::set_did_eject(const bool b)
{
m_did_eject = b;
}
}}//namespace Slicer::Gui

View File

@ -0,0 +1,110 @@
#ifndef slic3r_GUI_RemovableDriveManager_hpp_
#define slic3r_GUI_RemovableDriveManager_hpp_
#include <vector>
#include <string>
#include <functional>
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<DriveData> 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<void()> 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<DriveData> m_current_drives;
std::vector<std::function<void()>> 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

View File

@ -0,0 +1,10 @@
#import <Cocoa/Cocoa.h>
@interface RemovableDriveManagerMM : NSObject
-(instancetype) init;
-(void) add_unmount_observer;
-(void) on_device_unmount: (NSNotification*) notification;
-(NSArray*) list_dev;
-(void)eject_drive:(NSString *)path;
@end

View File

@ -0,0 +1,157 @@
#import "RemovableDriveManager.hpp"
#import "RemovableDriveManagerMM.h"
#import <AppKit/AppKit.h>
#import <DiskArbitration/DiskArbitration.h>
@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

View File

@ -13,6 +13,7 @@
#include <wx/numformatter.h> #include <wx/numformatter.h>
#include <wx/colordlg.h> #include <wx/colordlg.h>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/replace.hpp>
#include <boost/nowide/cstdio.hpp> #include <boost/nowide/cstdio.hpp>
@ -433,17 +434,17 @@ static std::string icon_name_respected_to_mode(const std::string& bmp_name_in)
#else #else
const std::string folder = "white/"; const std::string folder = "white/";
#endif #endif
std::string bmp_name = Slic3r::GUI::wxGetApp().dark_mode() ? folder + bmp_name_in : bmp_name_in; std::string bmp_name;
if (Slic3r::GUI::wxGetApp().dark_mode()) {
bmp_name = folder + bmp_name_in;
boost::replace_last(bmp_name, ".png", ""); boost::replace_last(bmp_name, ".png", "");
FILE* fp = NULL; if (! boost::filesystem::exists(Slic3r::var(bmp_name + ".svg")))
fp = boost::nowide::fopen(Slic3r::var(bmp_name + ".svg").c_str(), "rb"); bmp_name.clear();
if (!fp) }
{ if (bmp_name.empty()) {
bmp_name = bmp_name_in; bmp_name = bmp_name_in;
boost::replace_last(bmp_name, ".png", ""); boost::replace_last(bmp_name, ".png", "");
if (fp) fclose(fp);
} }
return bmp_name; return bmp_name;
} }
@ -3960,8 +3961,10 @@ ScalableButton::ScalableButton( wxWindow * parent,
const ScalableBitmap& bitmap, const ScalableBitmap& bitmap,
const wxString& label /*= wxEmptyString*/, const wxString& label /*= wxEmptyString*/,
long style /*= wxBU_EXACTFIT | wxNO_BORDER*/) : long style /*= wxBU_EXACTFIT | wxNO_BORDER*/) :
m_parent(parent),
m_current_icon_name(bitmap.name()), 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); Create(parent, id, label, wxDefaultPosition, wxDefaultSize, style);
#ifdef __WXMSW__ #ifdef __WXMSW__
@ -3984,11 +3987,17 @@ void ScalableButton::SetBitmapDisabled_(const ScalableBitmap& bmp)
m_disabled_icon_name = bmp.name(); 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() 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()) 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) if (m_width > 0 || m_height>0)
{ {

View File

@ -730,6 +730,9 @@ public:
wxBitmap& bmp() { return m_bmp; } wxBitmap& bmp() { return m_bmp; }
const std::string& name() const{ return m_icon_name; } 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: private:
wxWindow* m_parent{ nullptr }; wxWindow* m_parent{ nullptr };
wxBitmap m_bmp = wxBitmap(); wxBitmap m_bmp = wxBitmap();
@ -1096,6 +1099,7 @@ public:
void SetBitmap_(const ScalableBitmap& bmp); void SetBitmap_(const ScalableBitmap& bmp);
void SetBitmapDisabled_(const ScalableBitmap &bmp); void SetBitmapDisabled_(const ScalableBitmap &bmp);
int GetBitmapHeight();
void msw_rescale(); void msw_rescale();
@ -1105,6 +1109,10 @@ private:
std::string m_disabled_icon_name = ""; std::string m_disabled_icon_name = "";
int m_width {-1}; // should be multiplied to em_unit int m_width {-1}; // should be multiplied to em_unit
int m_height{-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 };
}; };

View File

@ -0,0 +1,170 @@
#include "AstroBox.hpp"
#include <algorithm>
#include <sstream>
#include <exception>
#include <boost/format.hpp>
#include <boost/log/trivial.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <wx/progdlg.h>
#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<std::string>("api")) {
res = false;
return;
}
const auto text = ptree.get_optional<std::string>("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<std::string> &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();
}
}
}

View File

@ -0,0 +1,46 @@
#ifndef slic3r_AstroBox_hpp_
#define slic3r_AstroBox_hpp_
#include <string>
#include <wx/string.h>
#include <boost/optional.hpp>
#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<std::string> &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

View File

@ -32,8 +32,6 @@ Duet::Duet(DynamicPrintConfig *config) :
password(config->opt_string("printhost_apikey")) password(config->opt_string("printhost_apikey"))
{} {}
Duet::~Duet() {}
const char* Duet::get_name() const { return "Duet"; } const char* Duet::get_name() const { return "Duet"; }
bool Duet::test(wxString &msg) const bool Duet::test(wxString &msg) const
@ -111,21 +109,6 @@ bool Duet::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn e
return res; 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 Duet::connect(wxString &msg) const
{ {
bool res = false; bool res = false;

View File

@ -6,10 +6,8 @@
#include "PrintHost.hpp" #include "PrintHost.hpp"
namespace Slic3r { namespace Slic3r {
class DynamicPrintConfig; class DynamicPrintConfig;
class Http; class Http;
@ -17,18 +15,18 @@ class Duet : public PrintHost
{ {
public: public:
Duet(DynamicPrintConfig *config); 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; bool test(wxString &curl_msg) const override;
virtual wxString get_test_ok_msg () const; wxString get_test_ok_msg() const override;
virtual wxString get_test_failed_msg (wxString &msg) const; wxString get_test_failed_msg(wxString &msg) const override;
virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const; bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override;
virtual bool has_auto_discovery() const; bool has_auto_discovery() const override { return false; }
virtual bool can_test() const; bool can_test() const override { return true; }
virtual bool can_start_print() const; bool can_start_print() const override { return true; }
virtual std::string get_host() const { return host; } std::string get_host() const override { return host; }
private: private:
std::string host; std::string host;
@ -44,7 +42,6 @@ private:
int get_err_code_from_body(const std::string &body) const; int get_err_code_from_body(const std::string &body) const;
}; };
} }
#endif #endif

View File

@ -30,8 +30,6 @@ FlashAir::FlashAir(DynamicPrintConfig *config) :
host(config->opt_string("print_host")) host(config->opt_string("print_host"))
{} {}
FlashAir::~FlashAir() {}
const char* FlashAir::get_name() const { return "FlashAir"; } const char* FlashAir::get_name() const { return "FlashAir"; }
bool FlashAir::test(wxString &msg) const bool FlashAir::test(wxString &msg) const
@ -150,21 +148,6 @@ bool FlashAir::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Error
return res; 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 std::string FlashAir::timestamp_str() const
{ {
auto t = std::time(nullptr); auto t = std::time(nullptr);

View File

@ -9,7 +9,6 @@
namespace Slic3r { namespace Slic3r {
class DynamicPrintConfig; class DynamicPrintConfig;
class Http; class Http;
@ -17,18 +16,18 @@ class FlashAir : public PrintHost
{ {
public: public:
FlashAir(DynamicPrintConfig *config); 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; bool test(wxString &curl_msg) const override;
virtual wxString get_test_ok_msg () const; wxString get_test_ok_msg() const override;
virtual wxString get_test_failed_msg (wxString &msg) const; wxString get_test_failed_msg(wxString &msg) const override;
virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const; bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override;
virtual bool has_auto_discovery() const; bool has_auto_discovery() const override { return false; }
virtual bool can_test() const; bool can_test() const override { return true; }
virtual bool can_start_print() const; bool can_start_print() const override { return false; }
virtual std::string get_host() const { return host; } std::string get_host() const override { return host; }
private: private:
std::string host; 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; std::string make_url(const std::string &path, const std::string &arg, const std::string &val) const;
}; };
} }
#endif #endif

View File

@ -28,8 +28,6 @@ OctoPrint::OctoPrint(DynamicPrintConfig *config) :
cafile(config->opt_string("printhost_cafile")) cafile(config->opt_string("printhost_cafile"))
{} {}
OctoPrint::~OctoPrint() {}
const char* OctoPrint::get_name() const { return "OctoPrint"; } const char* OctoPrint::get_name() const { return "OctoPrint"; }
bool OctoPrint::test(wxString &msg) const bool OctoPrint::test(wxString &msg) const
@ -142,21 +140,6 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro
return res; 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<std::string> &version_text) const bool OctoPrint::validate_version_text(const boost::optional<std::string> &version_text) const
{ {
return version_text ? boost::starts_with(*version_text, "OctoPrint") : true; 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::~SL1Host() {}
const char* SL1Host::get_name() const { return "SL1Host"; } const char* SL1Host::get_name() const { return "SL1Host"; }
wxString SL1Host::get_test_ok_msg () const 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); 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<std::string> &version_text) const bool SL1Host::validate_version_text(const boost::optional<std::string> &version_text) const
{ {
return version_text ? boost::starts_with(*version_text, "Prusa SLA") : false; return version_text ? boost::starts_with(*version_text, "Prusa SLA") : false;
} }
} }

View File

@ -10,7 +10,6 @@
namespace Slic3r { namespace Slic3r {
class DynamicPrintConfig; class DynamicPrintConfig;
class Http; class Http;
@ -18,18 +17,18 @@ class OctoPrint : public PrintHost
{ {
public: public:
OctoPrint(DynamicPrintConfig *config); 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; bool test(wxString &curl_msg) const override;
virtual wxString get_test_ok_msg () const; wxString get_test_ok_msg () const override;
virtual wxString get_test_failed_msg (wxString &msg) const; wxString get_test_failed_msg (wxString &msg) const override;
virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const; bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override;
virtual bool has_auto_discovery() const; bool has_auto_discovery() const override { return true; }
virtual bool can_test() const; bool can_test() const override { return true; }
virtual bool can_start_print() const; bool can_start_print() const override { return true; }
virtual std::string get_host() const { return host; } std::string get_host() const override { return host; }
protected: protected:
virtual bool validate_version_text(const boost::optional<std::string> &version_text) const; virtual bool validate_version_text(const boost::optional<std::string> &version_text) const;
@ -43,23 +42,22 @@ private:
std::string make_url(const std::string &path) const; std::string make_url(const std::string &path) const;
}; };
class SL1Host: public OctoPrint class SL1Host: public OctoPrint
{ {
public: public:
SL1Host(DynamicPrintConfig *config) : OctoPrint(config) {} 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: protected:
virtual bool validate_version_text(const boost::optional<std::string> &version_text) const; bool validate_version_text(const boost::optional<std::string> &version_text) const override;
}; };
} }
#endif #endif

View File

@ -15,6 +15,7 @@
#include "OctoPrint.hpp" #include "OctoPrint.hpp"
#include "Duet.hpp" #include "Duet.hpp"
#include "FlashAir.hpp" #include "FlashAir.hpp"
#include "AstroBox.hpp"
#include "../GUI/PrintHostDialogs.hpp" #include "../GUI/PrintHostDialogs.hpp"
namespace fs = boost::filesystem; namespace fs = boost::filesystem;
@ -45,6 +46,7 @@ PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config)
case htOctoPrint: return new OctoPrint(config); case htOctoPrint: return new OctoPrint(config);
case htDuet: return new Duet(config); case htDuet: return new Duet(config);
case htFlashAir: return new FlashAir(config); case htFlashAir: return new FlashAir(config);
case htAstroBox: return new AstroBox(config);
default: return nullptr; default: return nullptr;
} }
} else { } else {

View File

@ -8,6 +8,11 @@ add_library(Catch2 INTERFACE)
list (APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules/Catch2) list (APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules/Catch2)
target_include_directories(Catch2 INTERFACE ${CMAKE_CURRENT_LIST_DIR}) target_include_directories(Catch2 INTERFACE ${CMAKE_CURRENT_LIST_DIR})
add_library(Catch2::Catch2 ALIAS Catch2) 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) include(Catch)
set(CATCH_EXTRA_ARGS "" CACHE STRING "Extra arguments for catch2 test suites.") set(CATCH_EXTRA_ARGS "" CACHE STRING "Extra arguments for catch2 test suites.")

Binary file not shown.

View File

@ -2,19 +2,86 @@
#include "libslic3r/Model.hpp" #include "libslic3r/Model.hpp"
#include "libslic3r/Format/3mf.hpp" #include "libslic3r/Format/3mf.hpp"
#include "libslic3r/Format/STL.hpp"
#include <boost/filesystem/operations.hpp>
using namespace Slic3r; using namespace Slic3r;
SCENARIO("Reading 3mf file", "[3mf]") { SCENARIO("Reading 3mf file", "[3mf]") {
GIVEN("umlauts in the path of the file") { GIVEN("umlauts in the path of the file") {
Slic3r::Model model; Model model;
WHEN("3mf model is read") { WHEN("3mf model is read") {
std::string path = std::string(TEST_DATA_DIR) + "/test_3mf/Geräte/Büchse.3mf"; std::string path = std::string(TEST_DATA_DIR) + "/test_3mf/Geräte/Büchse.3mf";
DynamicPrintConfig config; 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") { THEN("load should succeed") {
REQUIRE(ret); 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<double>()) << "\n";
}
}
}
THEN("world vertices coordinates after load match") {
REQUIRE(res);
}
}
}
}

View File

@ -3,7 +3,7 @@
set(SLIC3R_APP_NAME "PrusaSlicer") set(SLIC3R_APP_NAME "PrusaSlicer")
set(SLIC3R_APP_KEY "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_BUILD_ID "PrusaSlicer-${SLIC3R_VERSION}+UNKNOWN")
set(SLIC3R_RC_VERSION "2,2,0,0") set(SLIC3R_RC_VERSION "2,2,0,0")
set(SLIC3R_RC_VERSION_DOTS "2.2.0.0") set(SLIC3R_RC_VERSION_DOTS "2.2.0.0")

View File

@ -61,7 +61,7 @@
Ref<LayerRegion> add_region(PrintRegion* print_region); Ref<LayerRegion> add_region(PrintRegion* print_region);
ExPolygonCollection* slices() ExPolygonCollection* slices()
%code%{ RETVAL = new ExPolygonCollection(THIS->slices); %}; %code%{ RETVAL = new ExPolygonCollection(THIS->lslices); %};
int ptr() int ptr()
%code%{ RETVAL = (int)(intptr_t)THIS; %}; %code%{ RETVAL = (int)(intptr_t)THIS; %};
@ -110,7 +110,7 @@
Ref<LayerRegion> add_region(PrintRegion* print_region); Ref<LayerRegion> add_region(PrintRegion* print_region);
ExPolygonCollection* slices() 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_slices_to_svg(const char *path);
void export_region_fill_surfaces_to_svg(const char *path); void export_region_fill_surfaces_to_svg(const char *path);