diff --git a/.clang-format b/.clang-format
index 6b68254ec..440c89ec5 100644
--- a/.clang-format
+++ b/.clang-format
@@ -77,7 +77,7 @@ IndentWidth: 4
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
-KeepLineBreaksForNonEmptyLines: false
+#KeepLineBreaksForNonEmptyLines: false
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
diff --git a/.gitignore b/.gitignore
index efeaa91b5..63004bab2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,9 @@ MANIFEST.bak
xs/MANIFEST.bak
xs/assertlib*
.init_bundle.ini
+.vs/*
local-lib
/src/TAGS
/.vscode/
+build-linux/*
+deps/build-linux/*
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0f6b09983..d771a730a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -34,7 +34,7 @@ option(SLIC3R_MSVC_PDB "Generate PDB files on MSVC in Release mode" 1)
option(SLIC3R_PERL_XS "Compile XS Perl module and enable Perl unit and integration tests" 0)
option(SLIC3R_ASAN "Enable ASan on Clang and GCC" 0)
# If SLIC3R_FHS is 1 -> SLIC3R_DESKTOP_INTEGRATION is always 0, othrewise variable.
-CMAKE_DEPENDENT_OPTION(SLIC3R_DESKTOP_INTEGRATION "Allow perfoming desktop integration during runtime" 0 "NOT SLIC3R_FHS" 0)
+CMAKE_DEPENDENT_OPTION(SLIC3R_DESKTOP_INTEGRATION "Allow perfoming desktop integration during runtime" 1 "NOT SLIC3R_FHS" 0)
set(OPENVDB_FIND_MODULE_PATH "" CACHE PATH "Path to OpenVDB installation's find modules.")
@@ -481,6 +481,7 @@ add_custom_target(gettext_make_pot
COMMAND xgettext --keyword=L --keyword=_L --keyword=_u8L --keyword=L_CONTEXT:1,2c --keyword=_L_PLURAL:1,2 --add-comments=TRN --from-code=UTF-8 --debug
-f "${L10N_DIR}/list.txt"
-o "${L10N_DIR}/PrusaSlicer.pot"
+ COMMAND hintsToPot ${SLIC3R_RESOURCES_DIR} ${L10N_DIR}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
COMMENT "Generate pot file from strings in the source tree"
)
@@ -553,6 +554,8 @@ endfunction()
add_subdirectory(src)
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT PrusaSlicer_app_console)
+add_dependencies(gettext_make_pot hintsToPot)
+
# Perl bindings, currently only used for the unit / integration tests of libslic3r.
# Also runs the unit / integration tests.
#FIXME Port the tests into C++ to finally get rid of the Perl!
diff --git a/cmake/modules/FindDBus.cmake b/cmake/modules/FindDBus.cmake
index 1d0f29dd7..d54d4e516 100644
--- a/cmake/modules/FindDBus.cmake
+++ b/cmake/modules/FindDBus.cmake
@@ -56,4 +56,4 @@ FIND_PATH(DBUS_ARCH_INCLUDE_DIR
SET(DBUS_INCLUDE_DIRS ${DBUS_INCLUDE_DIR} ${DBUS_ARCH_INCLUDE_DIR})
INCLUDE(FindPackageHandleStandardArgs)
-FIND_PACKAGE_HANDLE_STANDARD_ARGS(DBUS REQUIRED_VARS DBUS_INCLUDE_DIRS DBUS_LIBRARIES)
\ No newline at end of file
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(DBus REQUIRED_VARS DBUS_INCLUDE_DIRS DBUS_LIBRARIES)
\ No newline at end of file
diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt
index 3ce6b88a9..bc98e0b83 100644
--- a/deps/CMakeLists.txt
+++ b/deps/CMakeLists.txt
@@ -57,6 +57,11 @@ set(PATCH_CMD ${GIT_EXECUTABLE} apply --verbose --ignore-space-change --whitespa
get_property(_is_multi GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+if (NOT _is_multi AND NOT CMAKE_BUILD_TYPE)
+ set(CMAKE_BUILD_TYPE Release)
+ message(STATUS "Forcing CMAKE_BUILD_TYPE to Release as it was not specified.")
+endif ()
+
function(prusaslicer_add_cmake_project projectname)
cmake_parse_arguments(P_ARGS "" "INSTALL_DIR;BUILD_COMMAND;INSTALL_COMMAND" "CMAKE_ARGS" ${ARGN})
diff --git a/resources/data/hints.ini b/resources/data/hints.ini
index 28a13236d..95060f9ec 100644
--- a/resources/data/hints.ini
+++ b/resources/data/hints.ini
@@ -49,7 +49,13 @@
# Algorithm shows hint only if ALL enabled tags are affirmative. (so never do enabled_tags = FFF; SLA;)
# Algorithm shows hint only if not in all disabled tags.
# if there are both disabled and preferred, only preferred that are not in disabled are valid.
-
+#
+#
+# Notifications shows in random order, already shown notifications are saved at cache/hints.cereal (as binary - human non-readable)
+# You can affect random ordering by seting weigh
+# weight = 5
+# Weight must be larger or equal to 1. Default weight is 1.
+# Weight defines probability as weight : sum_of_all_weights.
[hint:Fuzzy skin]
text = Fuzzy skin\nDid you know that you can create rough fibre-like texture on the sides of your models using theFuzzy skinfeature? You can also use modifiers to apply fuzzy-skin only to a portion of your model.
diff --git a/resources/icons/toolbar_arrow.png b/resources/icons/toolbar_arrow.png
deleted file mode 100644
index a370442ff..000000000
Binary files a/resources/icons/toolbar_arrow.png and /dev/null differ
diff --git a/resources/icons/toolbar_arrow.svg b/resources/icons/toolbar_arrow.svg
index 998646a24..b49645190 100644
--- a/resources/icons/toolbar_arrow.svg
+++ b/resources/icons/toolbar_arrow.svg
@@ -1,76 +1,22 @@
-
-
+
+
\ No newline at end of file
diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx
index dcb843a4a..37d144ff0 100644
--- a/resources/profiles/PrusaResearch.idx
+++ b/resources/profiles/PrusaResearch.idx
@@ -1,4 +1,5 @@
min_slic3r_version = 2.4.0-alpha0
+1.4.0-alpha7 Updated brim_offset value. Updated Prusa MINI end g-code. Added Filamentworld filament profiles.
1.4.0-alpha6 Added nozzle priming after M600. Added nozzle diameter checks for 0.8 nozzle printer profiles. Updated FW version. Increased number of top solid infill layers (0.2 layer height).
1.4.0-alpha5 Added multiple add:north and Extrudr filament profiles. Updated support head settings (SL1S).
1.4.0-alpha4 Decreased Area Fill (SL1S).
diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini
index 80095f4ab..0ed5b59a6 100644
--- a/resources/profiles/PrusaResearch.ini
+++ b/resources/profiles/PrusaResearch.ini
@@ -5,7 +5,7 @@
name = Prusa Research
# Configuration version of this file. Config file will only be installed, if the config_version differs.
# This means, the server may force the PrusaSlicer configuration to be downgraded.
-config_version = 1.4.0-alpha6
+config_version = 1.4.0-alpha7
# Where to get the updates from?
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/
changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1%
@@ -144,6 +144,7 @@ bridge_angle = 0
bridge_flow_ratio = 1
bridge_speed = 25
brim_width = 0
+brim_offset = 0.1
clip_multipart_objects = 1
compatible_printers =
complete_objects = 0
@@ -2464,6 +2465,76 @@ inherits = addnorth Textura
filament_retract_length = nil
compatible_printers_condition = printer_model=="MK2SMM"
+[filament:Filamentworld ABS]
+inherits = *ABSC*
+filament_vendor = Filamentworld
+filament_cost = 24.9
+filament_density = 1.04
+temperature = 230
+bed_temperature = 95
+first_layer_temperature = 240
+first_layer_bed_temperature = 105
+max_fan_speed = 20
+min_fan_speed = 10
+min_print_speed = 20
+disable_fan_first_layers = 3
+fan_below_layer_time = 60
+slowdown_below_layer_time = 15
+bridge_fan_speed = 20
+compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material)
+
+[filament:Filamentworld ABS @MINI]
+inherits = Filamentworld ABS
+first_layer_bed_temperature = 100
+min_fan_speed = 15
+fan_below_layer_time = 60
+compatible_printers_condition = printer_model=="MINI"
+
+[filament:Filamentworld PETG]
+inherits = *PET*
+filament_vendor = Filamentworld
+filament_cost = 34.9
+filament_density = 1.27
+bed_temperature = 70
+first_layer_bed_temperature = 85
+first_layer_temperature = 240
+temperature = 235
+fan_always_on = 1
+min_fan_speed = 25
+max_fan_speed = 55
+bridge_fan_speed = 55
+slowdown_below_layer_time = 20
+min_print_speed = 20
+fan_below_layer_time = 35
+disable_fan_first_layers = 2
+full_fan_speed_layer = 0
+filament_retract_length = 1.4
+filament_max_volumetric_speed = 8
+filament_spool_weight = 0
+
+[filament:Filamentworld PETG @MINI]
+inherits = Filamentworld PETG
+filament_retract_length = nil
+filament_retract_lift = nil
+filament_retract_speed = 40
+filament_deretract_speed = 25
+filament_max_volumetric_speed = 7
+compatible_printers_condition = printer_model=="MINI"
+
+[filament:Filamentworld PLA]
+inherits = *PLA*
+filament_vendor = Filamentworld
+filament_cost = 24.9
+filament_density = 1.24
+temperature = 205
+bed_temperature = 55
+first_layer_temperature = 215
+first_layer_bed_temperature = 60
+full_fan_speed_layer = 3
+slowdown_below_layer_time = 10
+filament_spool_weight = 0
+min_print_speed = 20
+
[filament:Filament PM PETG]
inherits = *PET*
renamed_from = "Plasty Mladec PETG"
@@ -3097,6 +3168,7 @@ filament_loading_speed_start = 19
filament_minimal_purge_on_wipe_tower = 15
filament_unloading_speed_start = 100
full_fan_speed_layer = 4
+filament_max_volumetric_speed = 13
[filament:Generic PLA @MMU2]
inherits = *PLA MMU2*
@@ -6447,7 +6519,7 @@ retract_layer_change = 1
silent_mode = 0
remaining_times = 1
start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S170 ; set extruder temp for bed leveling\nM140 S[first_layer_bed_temperature] ; set bed temp\nM109 R170 ; wait for bed leveling temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM204 T1250 ; set travel acceleration\nG28 ; home all without mesh bed level\nG29 ; mesh bed leveling \nM204 T[machine_max_acceleration_travel] ; restore travel acceleration\nM104 S[first_layer_temperature] ; set extruder temp\nG92 E0\nG1 Y-2 X179 F2400\nG1 Z3 F720\nM109 S[first_layer_temperature] ; wait for extruder temp\n\n; intro line\nG1 X170 F1000\nG1 Z0.2 F720\nG1 X110 E8 F900\nG1 X40 E10 F700\nG92 E0\n\nM221 S95 ; set flow
-end_gcode = G1 E-1 F2100 ; retract\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+2, max_print_height)}{endif} F720 ; Move print head up\nG1 X178 Y178 F4200 ; park print head\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)}{endif} F720 ; Move print head further up\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM221 S100 ; reset flow\nM900 K0 ; reset LA\nM84 ; disable motors
+end_gcode = G1 E-1 F2100 ; retract\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+2, max_print_height)} F720 ; Move print head up{endif}\nG1 X178 Y178 F4200 ; park print head\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)} F720 ; Move print head further up{endif}\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM221 S100 ; reset flow\nM900 K0 ; reset LA\nM84 ; disable motors
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MINI\n
extruder_colour =
color_change_gcode = M600\nG1 E0.8 F1500 ; prime after color change
diff --git a/resources/shaders/mm_contour.fs b/resources/shaders/mm_contour.fs
index 14c18dcf1..8ccf5b832 100644
--- a/resources/shaders/mm_contour.fs
+++ b/resources/shaders/mm_contour.fs
@@ -1,6 +1,11 @@
#version 110
+const float EPSILON = 0.0001;
+
void main()
{
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
+ // Values inside depth buffer for fragments of the contour of a selected area are offset
+ // by small epsilon to solve z-fighting between painted triangles and contour lines.
+ gl_FragDepth = gl_FragCoord.z - EPSILON;
}
diff --git a/resources/shaders/printbed.vs b/resources/shaders/printbed.vs
index ee19098c1..7633017f1 100644
--- a/resources/shaders/printbed.vs
+++ b/resources/shaders/printbed.vs
@@ -7,6 +7,8 @@ varying vec2 tex_coords;
void main()
{
- gl_Position = gl_ModelViewProjectionMatrix * vec4(v_position, 1.0);
+ gl_Position = gl_ModelViewProjectionMatrix * vec4(v_position.x, v_position.y, v_position.z, 1.0);
+ // the following line leads to crash on some Intel graphics card
+ //gl_Position = gl_ModelViewProjectionMatrix * vec4(v_position, 1.0);
tex_coords = v_tex_coords;
}
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index bbade8a97..9e89e82f6 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -13,6 +13,7 @@ add_subdirectory(qhull)
add_subdirectory(Shiny)
add_subdirectory(semver)
add_subdirectory(libigl)
+add_subdirectory(hints)
# Adding libnest2d project for bin packing...
add_subdirectory(libnest2d)
diff --git a/src/Shiny/CMakeLists.txt b/src/Shiny/CMakeLists.txt
index 8be7592ae..abdb96a72 100644
--- a/src/Shiny/CMakeLists.txt
+++ b/src/Shiny/CMakeLists.txt
@@ -1,5 +1,5 @@
+cmake_minimum_required(VERSION 2.8.12)
project(Shiny)
-cmake_minimum_required(VERSION 2.6)
add_library(Shiny STATIC
Shiny.h
diff --git a/src/admesh/CMakeLists.txt b/src/admesh/CMakeLists.txt
index 7d0177782..217976318 100644
--- a/src/admesh/CMakeLists.txt
+++ b/src/admesh/CMakeLists.txt
@@ -1,5 +1,5 @@
+cmake_minimum_required(VERSION 2.8.12)
project(admesh)
-cmake_minimum_required(VERSION 2.6)
add_library(admesh STATIC
connect.cpp
diff --git a/src/boost/CMakeLists.txt b/src/boost/CMakeLists.txt
index 12fe6b4e5..e8c9e11ce 100644
--- a/src/boost/CMakeLists.txt
+++ b/src/boost/CMakeLists.txt
@@ -1,5 +1,5 @@
+cmake_minimum_required(VERSION 2.8.12)
project(nowide)
-cmake_minimum_required(VERSION 2.6)
add_library(nowide STATIC
nowide/args.hpp
diff --git a/src/clipper/CMakeLists.txt b/src/clipper/CMakeLists.txt
index 0362a4d84..f62508820 100644
--- a/src/clipper/CMakeLists.txt
+++ b/src/clipper/CMakeLists.txt
@@ -1,5 +1,5 @@
+cmake_minimum_required(VERSION 2.8.12)
project(clipper)
-cmake_minimum_required(VERSION 2.6)
add_library(clipper STATIC
# We are using ClipperLib compiled as part of the libslic3r project using Slic3r::Point as its base type.
diff --git a/src/glu-libtess/CMakeLists.txt b/src/glu-libtess/CMakeLists.txt
index f3f8d024a..8fca992a3 100644
--- a/src/glu-libtess/CMakeLists.txt
+++ b/src/glu-libtess/CMakeLists.txt
@@ -1,5 +1,5 @@
+cmake_minimum_required(VERSION 2.8.12)
project(glu-libtess)
-cmake_minimum_required(VERSION 2.6)
add_library(glu-libtess STATIC
src/dict-list.h
diff --git a/src/hints/CMakeLists.txt b/src/hints/CMakeLists.txt
new file mode 100644
index 000000000..66550c786
--- /dev/null
+++ b/src/hints/CMakeLists.txt
@@ -0,0 +1,12 @@
+cmake_minimum_required(VERSION 3.13)
+project(HintsToPot)
+
+add_executable(hintsToPot
+ HintsToPot.cpp)
+
+target_link_libraries(hintsToPot PRIVATE boost_libs)
+
+#encoding_check(HintsToPot)
+
+
+
diff --git a/src/hints/HintsToPot.cpp b/src/hints/HintsToPot.cpp
new file mode 100644
index 000000000..7c8029cde
--- /dev/null
+++ b/src/hints/HintsToPot.cpp
@@ -0,0 +1,84 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+bool write_to_pot(boost::filesystem::path path, const std::vector>& data)
+{
+ boost::filesystem::ofstream file(std::move(path), std::ios_base::app);
+ for (const auto& element : data)
+ {
+ //Example of .pot element
+ //#: src/slic3r/GUI/GUI_App.cpp:1647 src/slic3r/GUI/wxExtensions.cpp:687
+ //msgctxt "Mode"
+ //msgid "Advanced"
+ //msgstr ""
+ file << "\n#: resources/data/hints.ini: ["<< element.first << "]\nmsgid \"" << element.second << "\"\nmsgstr \"\"\n";
+ }
+ file.close();
+ return true;
+}
+bool read_hints_ini(boost::filesystem::path path, std::vector>& pot_elements)
+{
+ namespace pt = boost::property_tree;
+ pt::ptree tree;
+ boost::nowide::ifstream ifs(path.string());
+ try {
+ pt::read_ini(ifs, tree);
+ }
+ catch (const boost::property_tree::ini_parser::ini_parser_error& err) {
+ std::cout << err.what() << std::endl;
+ return false;
+ }
+ for (const auto& section : tree) {
+ if (boost::starts_with(section.first, "hint:")) {
+ for (const auto& data : section.second) {
+ if (data.first == "text")
+ {
+ pot_elements.emplace_back(section.first, data.second.data());
+ break;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+int main(int argc, char* argv[])
+{
+ std::vector> data;
+ boost::filesystem::path path_to_ini;
+ boost::filesystem::path path_to_pot;
+ if (argc != 3)
+ {
+ std::cout << "HINTS_TO_POT FAILED: WRONG NUM OF ARGS" << std::endl;
+ return -1;
+ }
+ try {
+ path_to_ini = boost::filesystem::canonical(boost::filesystem::path(argv[1])).parent_path() / "resources" / "data" / "hints.ini";
+ path_to_pot = boost::filesystem::canonical(boost::filesystem::path(argv[2])).parent_path() / "localization" /"PrusaSlicer.pot";
+ } catch (std::exception&) {
+ std::cout << "HINTS_TO_POT FAILED: BOOST CANNONICAL" << std::endl;
+ return -1;
+ }
+
+ if (!boost::filesystem::exists(path_to_ini)){
+ std::cout << "HINTS_TO_POT FAILED: PATH TO INI DOES NOT EXISTS" << std::endl;
+ std::cout << path_to_ini.string() << std::endl;
+ return -1;
+ }
+ if (!read_hints_ini(std::move(path_to_ini), data)) {
+ std::cout << "HINTS_TO_POT FAILED TO READ HINTS INI" << std::endl;
+ return -1;
+ }
+ if (!write_to_pot(std::move(path_to_pot), data)) {
+ std::cout << "HINTS_TO_POT FAILED TO WRITE POT FILE" << std::endl;
+ return -1;
+ }
+ std::cout << "HINTS_TO_POT SUCCESS" << std::endl;
+ return 0;
+}
diff --git a/src/imgui/CMakeLists.txt b/src/imgui/CMakeLists.txt
index 50575308d..235afe110 100644
--- a/src/imgui/CMakeLists.txt
+++ b/src/imgui/CMakeLists.txt
@@ -1,5 +1,5 @@
+cmake_minimum_required(VERSION 2.8.12)
project(imgui)
-cmake_minimum_required(VERSION 2.6)
add_library(imgui STATIC
imconfig.h
diff --git a/src/libigl/CMakeLists.txt b/src/libigl/CMakeLists.txt
index 3daac757b..f023826a5 100644
--- a/src/libigl/CMakeLists.txt
+++ b/src/libigl/CMakeLists.txt
@@ -1,5 +1,5 @@
-project(libigl)
cmake_minimum_required(VERSION 3.0)
+project(libigl)
add_library(libigl INTERFACE)
diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp
index 4166658f5..177d8d708 100644
--- a/src/libslic3r/AppConfig.cpp
+++ b/src/libslic3r/AppConfig.cpp
@@ -167,9 +167,6 @@ void AppConfig::set_defaults()
if (get("show_splash_screen").empty())
set("show_splash_screen", "1");
- if (get("last_hint").empty())
- set("last_hint", "0");
-
if (get("show_hints").empty())
set("show_hints", "1");
diff --git a/src/libslic3r/Brim.cpp b/src/libslic3r/Brim.cpp
index e73bed2c9..f4455fdd5 100644
--- a/src/libslic3r/Brim.cpp
+++ b/src/libslic3r/Brim.cpp
@@ -71,12 +71,16 @@ static ConstPrintObjectPtrs get_top_level_objects_with_brim(const Print &print,
Polygons islands;
ConstPrintObjectPtrs island_to_object;
for(size_t print_object_idx = 0; print_object_idx < print.objects().size(); ++print_object_idx) {
+ const PrintObject *object = print.objects()[print_object_idx];
+
+ if (! object->has_brim())
+ continue;
+
Polygons islands_object;
islands_object.reserve(bottom_layers_expolygons[print_object_idx].size());
for (const ExPolygon &ex_poly : bottom_layers_expolygons[print_object_idx])
islands_object.emplace_back(ex_poly.contour);
- const PrintObject *object = print.objects()[print_object_idx];
islands.reserve(islands.size() + object->instances().size() * islands_object.size());
for (const PrintInstance &instance : object->instances())
for (Polygon &poly : islands_object) {
diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp
index 1fe4816ad..c8a3835dd 100644
--- a/src/libslic3r/Config.cpp
+++ b/src/libslic3r/Config.cpp
@@ -426,7 +426,19 @@ void ConfigBase::apply_only(const ConfigBase &other, const t_config_option_keys
}
}
-// this will *ignore* options not present in both configs
+// Are the two configs equal? Ignoring options not present in both configs.
+bool ConfigBase::equals(const ConfigBase &other) const
+{
+ for (const t_config_option_key &opt_key : this->keys()) {
+ const ConfigOption *this_opt = this->option(opt_key);
+ const ConfigOption *other_opt = other.option(opt_key);
+ if (this_opt != nullptr && other_opt != nullptr && *this_opt != *other_opt)
+ return false;
+ }
+ return true;
+}
+
+// Returns options differing in the two configs, ignoring options not present in both configs.
t_config_option_keys ConfigBase::diff(const ConfigBase &other) const
{
t_config_option_keys diff;
@@ -439,6 +451,7 @@ t_config_option_keys ConfigBase::diff(const ConfigBase &other) const
return diff;
}
+// Returns options being equal in the two configs, ignoring options not present in both configs.
t_config_option_keys ConfigBase::equal(const ConfigBase &other) const
{
t_config_option_keys equal;
@@ -1190,6 +1203,65 @@ t_config_option_keys StaticConfig::keys() const
return keys;
}
+// Iterate over the pairs of options with equal keys, call the fn.
+// Returns true on early exit by fn().
+template
+static inline bool dynamic_config_iterate(const DynamicConfig &lhs, const DynamicConfig &rhs, Fn fn)
+{
+ std::map>::const_iterator i = lhs.cbegin();
+ std::map>::const_iterator j = rhs.cbegin();
+ while (i != lhs.cend() && j != rhs.cend())
+ if (i->first < j->first)
+ ++ i;
+ else if (i->first > j->first)
+ ++ j;
+ else {
+ assert(i->first == j->first);
+ if (fn(i->first, i->second.get(), j->second.get()))
+ // Early exit by fn.
+ return true;
+ ++ i;
+ ++ j;
+ }
+ // Finished to the end.
+ return false;
+}
+
+// Are the two configs equal? Ignoring options not present in both configs.
+bool DynamicConfig::equals(const DynamicConfig &other) const
+{
+ return ! dynamic_config_iterate(*this, other,
+ [](const t_config_option_key & /* key */, const ConfigOption *l, const ConfigOption *r) { return *l != *r; });
+}
+
+// Returns options differing in the two configs, ignoring options not present in both configs.
+t_config_option_keys DynamicConfig::diff(const DynamicConfig &other) const
+{
+ t_config_option_keys diff;
+ dynamic_config_iterate(*this, other,
+ [&diff](const t_config_option_key &key, const ConfigOption *l, const ConfigOption *r) {
+ if (*l != *r)
+ diff.emplace_back(key);
+ // Continue iterating.
+ return false;
+ });
+ return diff;
+}
+
+// Returns options being equal in the two configs, ignoring options not present in both configs.
+t_config_option_keys DynamicConfig::equal(const DynamicConfig &other) const
+{
+ t_config_option_keys equal;
+ dynamic_config_iterate(*this, other,
+ [&equal](const t_config_option_key &key, const ConfigOption *l, const ConfigOption *r) {
+ if (*l == *r)
+ equal.emplace_back(key);
+ // Continue iterating.
+ return false;
+ });
+ return equal;
+}
+
}
#include
diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp
index 750b9411c..6439e4632 100644
--- a/src/libslic3r/Config.hpp
+++ b/src/libslic3r/Config.hpp
@@ -1893,8 +1893,8 @@ public:
// The configuration definition is static: It does not carry the actual configuration values,
// but it carries the defaults of the configuration values.
- ConfigBase() {}
- ~ConfigBase() override {}
+ ConfigBase() = default;
+ ~ConfigBase() override = default;
// Virtual overridables:
public:
@@ -1953,8 +1953,11 @@ public:
// An UnknownOptionException is thrown in case some option keys are not defined by this->def(),
// or this ConfigBase is of a StaticConfig type and it does not support some of the keys, and ignore_nonexistent is not set.
void apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false);
- bool equals(const ConfigBase &other) const { return this->diff(other).empty(); }
+ // Are the two configs equal? Ignoring options not present in both configs.
+ bool equals(const ConfigBase &other) const;
+ // Returns options differing in the two configs, ignoring options not present in both configs.
t_config_option_keys diff(const ConfigBase &other) const;
+ // Returns options being equal in the two configs, ignoring options not present in both configs.
t_config_option_keys equal(const ConfigBase &other) const;
std::string opt_serialize(const t_config_option_key &opt_key) const;
@@ -2022,12 +2025,12 @@ private:
class DynamicConfig : public virtual ConfigBase
{
public:
- DynamicConfig() {}
+ DynamicConfig() = default;
DynamicConfig(const DynamicConfig &rhs) { *this = rhs; }
DynamicConfig(DynamicConfig &&rhs) noexcept : options(std::move(rhs.options)) { rhs.options.clear(); }
explicit DynamicConfig(const ConfigBase &rhs, const t_config_option_keys &keys);
explicit DynamicConfig(const ConfigBase& rhs) : DynamicConfig(rhs, rhs.keys()) {}
- virtual ~DynamicConfig() override { clear(); }
+ virtual ~DynamicConfig() override = default;
// Copy a content of one DynamicConfig to another DynamicConfig.
// If rhs.def() is not null, then it has to be equal to this->def().
@@ -2144,6 +2147,13 @@ public:
}
}
+ // Are the two configs equal? Ignoring options not present in both configs.
+ bool equals(const DynamicConfig &other) const;
+ // Returns options differing in the two configs, ignoring options not present in both configs.
+ t_config_option_keys diff(const DynamicConfig &other) const;
+ // Returns options being equal in the two configs, ignoring options not present in both configs.
+ t_config_option_keys equal(const DynamicConfig &other) const;
+
std::string& opt_string(const t_config_option_key &opt_key, bool create = false) { return this->option(opt_key, create)->value; }
const std::string& opt_string(const t_config_option_key &opt_key) const { return const_cast(this)->opt_string(opt_key); }
std::string& opt_string(const t_config_option_key &opt_key, unsigned int idx) { return this->option(opt_key)->get_at(idx); }
diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp
index febeacdbe..e5007a3f4 100644
--- a/src/libslic3r/Format/3mf.cpp
+++ b/src/libslic3r/Format/3mf.cpp
@@ -595,7 +595,7 @@ namespace Slic3r {
mz_zip_archive_file_stat stat;
- m_name = boost::filesystem::path(filename).filename().stem().string();
+ m_name = boost::filesystem::path(filename).stem().string();
// we first loop the entries to read from the archive the .model file only, in order to extract the version from it
for (mz_uint i = 0; i < num_entries; ++i) {
@@ -1408,6 +1408,13 @@ namespace Slic3r {
m_model->delete_object(model_object);
}
+ if (m_version == 0) {
+ // if the 3mf was not produced by PrusaSlicer and there is only one object,
+ // set the object name to match the filename
+ if (m_model->objects.size() == 1)
+ m_model->objects.front()->name = m_name;
+ }
+
// applies instances' matrices
for (Instance& instance : m_instances) {
if (instance.instance != nullptr && instance.instance->get_object() != nullptr)
diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp
index b5c10823e..0d71a19f5 100644
--- a/src/libslic3r/GCode/GCodeProcessor.cpp
+++ b/src/libslic3r/GCode/GCodeProcessor.cpp
@@ -24,6 +24,9 @@
static const float INCHES_TO_MM = 25.4f;
static const float MMMIN_TO_MMSEC = 1.0f / 60.0f;
static const float DEFAULT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2
+#if ENABLE_RETRACT_ACCELERATION
+static const float DEFAULT_RETRACT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2
+#endif // ENABLE_RETRACT_ACCELERATION
static const float DEFAULT_TRAVEL_ACCELERATION = 1250.0f;
static const size_t MIN_EXTRUDERS_COUNT = 5;
@@ -178,6 +181,10 @@ void GCodeProcessor::TimeMachine::reset()
enabled = false;
acceleration = 0.0f;
max_acceleration = 0.0f;
+#if ENABLE_RETRACT_ACCELERATION
+ retract_acceleration = 0.0f;
+ max_retract_acceleration = 0.0f;
+#endif // ENABLE_RETRACT_ACCELERATION
travel_acceleration = 0.0f;
max_travel_acceleration = 0.0f;
extrude_factor_override_percentage = 1.0f;
@@ -834,6 +841,11 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i);
m_time_processor.machines[i].max_acceleration = max_acceleration;
m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION;
+#if ENABLE_RETRACT_ACCELERATION
+ float max_retract_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i);
+ m_time_processor.machines[i].max_retract_acceleration = max_retract_acceleration;
+ m_time_processor.machines[i].retract_acceleration = (max_retract_acceleration > 0.0f) ? max_retract_acceleration : DEFAULT_RETRACT_ACCELERATION;
+#endif // ENABLE_RETRACT_ACCELERATION
float max_travel_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_travel, i);
m_time_processor.machines[i].max_travel_acceleration = max_travel_acceleration;
m_time_processor.machines[i].travel_acceleration = (max_travel_acceleration > 0.0f) ? max_travel_acceleration : DEFAULT_TRAVEL_ACCELERATION;
@@ -1052,6 +1064,11 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i);
m_time_processor.machines[i].max_acceleration = max_acceleration;
m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION;
+#if ENABLE_RETRACT_ACCELERATION
+ float max_retract_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i);
+ m_time_processor.machines[i].max_retract_acceleration = max_retract_acceleration;
+ m_time_processor.machines[i].retract_acceleration = (max_retract_acceleration > 0.0f) ? max_retract_acceleration : DEFAULT_RETRACT_ACCELERATION;
+#endif // ENABLE_RETRACT_ACCELERATION
float max_travel_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_travel, i);
m_time_processor.machines[i].max_travel_acceleration = max_travel_acceleration;
m_time_processor.machines[i].travel_acceleration = (max_travel_acceleration > 0.0f) ? max_travel_acceleration : DEFAULT_TRAVEL_ACCELERATION;
@@ -2713,14 +2730,22 @@ void GCodeProcessor::process_M204(const GCodeReader::GCodeLine& line)
set_acceleration(static_cast(i), value);
set_travel_acceleration(static_cast(i), value);
if (line.has_value('T', value))
+#if ENABLE_RETRACT_ACCELERATION
+ set_retract_acceleration(static_cast(i), value);
+#else
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value);
+#endif // ENABLE_RETRACT_ACCELERATION
}
else {
// New acceleration format, compatible with the upstream Marlin.
if (line.has_value('P', value))
set_acceleration(static_cast(i), value);
if (line.has_value('R', value))
+#if ENABLE_RETRACT_ACCELERATION
+ set_retract_acceleration(static_cast(i), value);
+#else
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value);
+#endif // ENABLE_RETRACT_ACCELERATION
if (line.has_value('T', value))
// Interpret the T value as the travel acceleration in the new Marlin format.
set_travel_acceleration(static_cast(i), value);
@@ -2979,10 +3004,30 @@ float GCodeProcessor::get_axis_max_jerk(PrintEstimatedStatistics::ETimeMode mode
}
}
+#if ENABLE_RETRACT_ACCELERATION
+float GCodeProcessor::get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const
+{
+ size_t id = static_cast(mode);
+ return (id < m_time_processor.machines.size()) ? m_time_processor.machines[id].retract_acceleration : DEFAULT_RETRACT_ACCELERATION;
+}
+#else
float GCodeProcessor::get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const
{
return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, static_cast(mode));
}
+#endif // ENABLE_RETRACT_ACCELERATION
+
+#if ENABLE_RETRACT_ACCELERATION
+void GCodeProcessor::set_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value)
+{
+ size_t id = static_cast(mode);
+ if (id < m_time_processor.machines.size()) {
+ m_time_processor.machines[id].retract_acceleration = (m_time_processor.machines[id].max_retract_acceleration == 0.0f) ? value :
+ // Clamp the acceleration with the maximum.
+ std::min(value, m_time_processor.machines[id].max_retract_acceleration);
+ }
+}
+#endif // ENABLE_RETRACT_ACCELERATION
float GCodeProcessor::get_acceleration(PrintEstimatedStatistics::ETimeMode mode) const
{
diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp
index 999af481d..4fcdd8df3 100644
--- a/src/libslic3r/GCode/GCodeProcessor.hpp
+++ b/src/libslic3r/GCode/GCodeProcessor.hpp
@@ -242,6 +242,11 @@ namespace Slic3r {
float acceleration; // mm/s^2
// hard limit for the acceleration, to which the firmware will clamp.
float max_acceleration; // mm/s^2
+#if ENABLE_RETRACT_ACCELERATION
+ float retract_acceleration; // mm/s^2
+ // hard limit for the acceleration, to which the firmware will clamp.
+ float max_retract_acceleration; // mm/s^2
+#endif // ENABLE_RETRACT_ACCELERATION
float travel_acceleration; // mm/s^2
// hard limit for the travel acceleration, to which the firmware will clamp.
float max_travel_acceleration; // mm/s^2
@@ -669,6 +674,9 @@ namespace Slic3r {
float get_axis_max_acceleration(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
float get_axis_max_jerk(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
float get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;
+#if ENABLE_RETRACT_ACCELERATION
+ void set_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value);
+#endif // ENABLE_RETRACT_ACCELERATION
float get_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;
void set_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value);
float get_travel_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;
diff --git a/src/libslic3r/MeshBoolean.cpp b/src/libslic3r/MeshBoolean.cpp
index 3dd5da307..a59165946 100644
--- a/src/libslic3r/MeshBoolean.cpp
+++ b/src/libslic3r/MeshBoolean.cpp
@@ -118,6 +118,11 @@ void triangle_mesh_to_cgal(const std::vector & V,
{
if (F.empty()) return;
+ size_t vertices_count = V.size();
+ size_t edges_count = (F.size()* 3) / 2;
+ size_t faces_count = F.size();
+ out.reserve(vertices_count, edges_count, faces_count);
+
for (auto &v : V)
out.add_vertex(typename _Mesh::Point{v.x(), v.y(), v.z()});
diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp
index 538358177..6654d3a13 100644
--- a/src/libslic3r/Model.cpp
+++ b/src/libslic3r/Model.cpp
@@ -424,7 +424,7 @@ void Model::convert_multipart_object(unsigned int max_extruders)
ModelObject* object = new ModelObject(this);
object->input_file = this->objects.front()->input_file;
- object->name = this->objects.front()->name;
+ object->name = boost::filesystem::path(this->objects.front()->input_file).stem().string();
//FIXME copy the config etc?
unsigned int extruder_counter = 0;
@@ -439,7 +439,7 @@ void Model::convert_multipart_object(unsigned int max_extruders)
int counter = 1;
auto copy_volume = [o, max_extruders, &counter, &extruder_counter](ModelVolume *new_v) {
assert(new_v != nullptr);
- new_v->name = o->name + "_" + std::to_string(counter++);
+ new_v->name = (counter > 1) ? o->name + "_" + std::to_string(counter++) : o->name;
new_v->config.set("extruder", auto_extruder_id(max_extruders, extruder_counter));
return new_v;
};
@@ -956,9 +956,26 @@ void ModelObject::center_around_origin(bool include_modifiers)
void ModelObject::ensure_on_bed(bool allow_negative_z)
{
- const double min_z = get_min_z();
- if (!allow_negative_z || min_z > SINKING_Z_THRESHOLD)
- translate_instances({ 0.0, 0.0, -min_z });
+ double z_offset = 0.0;
+
+ if (allow_negative_z) {
+ if (parts_count() == 1) {
+ const double min_z = get_min_z();
+ const double max_z = get_max_z();
+ if (min_z >= SINKING_Z_THRESHOLD || max_z < 0.0)
+ z_offset = -min_z;
+ }
+ else {
+ const double max_z = get_max_z();
+ if (max_z < SINKING_MIN_Z_THRESHOLD)
+ z_offset = SINKING_MIN_Z_THRESHOLD - max_z;
+ }
+ }
+ else
+ z_offset = -get_min_z();
+
+ if (z_offset != 0.0)
+ translate_instances(z_offset * Vec3d::UnitZ());
}
void ModelObject::translate_instances(const Vec3d& vector)
@@ -1114,6 +1131,15 @@ size_t ModelObject::facets_count() const
return num;
}
+size_t ModelObject::parts_count() const
+{
+ size_t num = 0;
+ for (const ModelVolume* v : this->volumes)
+ if (v->is_model_part())
+ ++num;
+ return num;
+}
+
bool ModelObject::needed_repair() const
{
for (const ModelVolume *v : this->volumes)
@@ -1429,6 +1455,19 @@ double ModelObject::get_min_z() const
}
}
+double ModelObject::get_max_z() const
+{
+ if (instances.empty())
+ return 0.0;
+ else {
+ double max_z = -DBL_MAX;
+ for (size_t i = 0; i < instances.size(); ++i) {
+ max_z = std::max(max_z, get_instance_max_z(i));
+ }
+ return max_z;
+ }
+}
+
double ModelObject::get_instance_min_z(size_t instance_idx) const
{
double min_z = DBL_MAX;
@@ -1450,6 +1489,27 @@ double ModelObject::get_instance_min_z(size_t instance_idx) const
return min_z + inst->get_offset(Z);
}
+double ModelObject::get_instance_max_z(size_t instance_idx) const
+{
+ double max_z = -DBL_MAX;
+
+ const ModelInstance* inst = instances[instance_idx];
+ const Transform3d& mi = inst->get_matrix(true);
+
+ for (const ModelVolume* v : volumes) {
+ if (!v->is_model_part())
+ continue;
+
+ const Transform3d mv = mi * v->get_matrix();
+ const TriangleMesh& hull = v->get_convex_hull();
+ for (const stl_facet& facet : hull.stl.facet_start)
+ for (int i = 0; i < 3; ++i)
+ max_z = std::max(max_z, (mv * facet.vertex[i].cast()).z());
+ }
+
+ return max_z + inst->get_offset(Z);
+}
+
unsigned int ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume)
{
unsigned int num_printable = 0;
diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp
index 11dcfa775..b89dd5aa1 100644
--- a/src/libslic3r/Model.hpp
+++ b/src/libslic3r/Model.hpp
@@ -347,6 +347,7 @@ public:
size_t materials_count() const;
size_t facets_count() const;
+ size_t parts_count() const;
bool needed_repair() const;
ModelObjectPtrs cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes);
void split(ModelObjectPtrs* new_objects);
@@ -358,7 +359,9 @@ public:
void bake_xy_rotation_into_meshes(size_t instance_idx);
double get_min_z() const;
+ double get_max_z() const;
double get_instance_min_z(size_t instance_idx) const;
+ double get_instance_max_z(size_t instance_idx) const;
// Called by Print::validate() from the UI thread.
unsigned int check_instances_print_volume_state(const BoundingBoxf3& print_volume);
@@ -1177,6 +1180,7 @@ void check_model_ids_equal(const Model &model1, const Model &model2);
#endif /* NDEBUG */
static const float SINKING_Z_THRESHOLD = -0.001f;
+static const double SINKING_MIN_Z_THRESHOLD = 0.05;
} // namespace Slic3r
diff --git a/src/libslic3r/PlaceholderParser.hpp b/src/libslic3r/PlaceholderParser.hpp
index ae6ef8f02..6157ffe3c 100644
--- a/src/libslic3r/PlaceholderParser.hpp
+++ b/src/libslic3r/PlaceholderParser.hpp
@@ -24,6 +24,7 @@ public:
PlaceholderParser(const DynamicConfig *external_config = nullptr);
+ void clear_config() { m_config.clear(); }
// Return a list of keys, which should be changed in m_config from rhs.
// This contains keys, which are found in rhs, but not in m_config.
std::vector config_diff(const DynamicPrintConfig &rhs);
diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp
index 61ae78b5e..3883c4980 100644
--- a/src/libslic3r/Preset.cpp
+++ b/src/libslic3r/Preset.cpp
@@ -413,212 +413,181 @@ void Preset::set_visible_from_appconfig(const AppConfig &app_config)
}
}
-const std::vector& Preset::print_options()
-{
- static std::vector s_opts {
- "layer_height", "first_layer_height", "perimeters", "spiral_vase", "slice_closing_radius", "slicing_mode",
- "top_solid_layers", "top_solid_min_thickness", "bottom_solid_layers", "bottom_solid_min_thickness",
- "extra_perimeters", "ensure_vertical_shell_thickness", "avoid_crossing_perimeters", "thin_walls", "overhangs",
- "seam_position", "external_perimeters_first", "fill_density", "fill_pattern", "top_fill_pattern", "bottom_fill_pattern",
- "infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle",
- "solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first",
- "ironing", "ironing_type", "ironing_flowrate", "ironing_speed", "ironing_spacing",
- "max_print_speed", "max_volumetric_speed", "avoid_crossing_perimeters_max_detour",
- "fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_dist",
+static std::vector s_Preset_print_options {
+ "layer_height", "first_layer_height", "perimeters", "spiral_vase", "slice_closing_radius", "slicing_mode",
+ "top_solid_layers", "top_solid_min_thickness", "bottom_solid_layers", "bottom_solid_min_thickness",
+ "extra_perimeters", "ensure_vertical_shell_thickness", "avoid_crossing_perimeters", "thin_walls", "overhangs",
+ "seam_position", "external_perimeters_first", "fill_density", "fill_pattern", "top_fill_pattern", "bottom_fill_pattern",
+ "infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle",
+ "solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first",
+ "ironing", "ironing_type", "ironing_flowrate", "ironing_speed", "ironing_spacing",
+ "max_print_speed", "max_volumetric_speed", "avoid_crossing_perimeters_max_detour",
+ "fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_dist",
#ifdef HAS_PRESSURE_EQUALIZER
- "max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative",
+ "max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative",
#endif /* HAS_PRESSURE_EQUALIZER */
- "perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "infill_speed", "solid_infill_speed",
- "top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed",
- "bridge_speed", "gap_fill_speed", "gap_fill_enabled", "travel_speed", "travel_speed_z", "first_layer_speed", "perimeter_acceleration", "infill_acceleration",
- "bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", "draft_shield",
- "min_skirt_length", "brim_width", "brim_offset", "brim_type", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers",
- "raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion",
- "support_material_pattern", "support_material_with_sheath", "support_material_spacing", "support_material_closing_radius", "support_material_style",
- "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", "support_material_bottom_interface_layers",
- "support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops",
- "support_material_contact_distance", "support_material_bottom_contact_distance",
- "support_material_buildplate_only", "dont_support_bridges", "thick_bridges", "notes", "complete_objects", "extruder_clearance_radius",
- "extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "perimeter_extruder",
- "infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder",
- "ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width",
- "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width",
- "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "infill_anchor", "infill_anchor_max", "bridge_flow_ratio", "clip_multipart_objects",
- "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y",
- "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width",
- "wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits"
- };
- return s_opts;
-}
+ "perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "infill_speed", "solid_infill_speed",
+ "top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed",
+ "bridge_speed", "gap_fill_speed", "gap_fill_enabled", "travel_speed", "travel_speed_z", "first_layer_speed", "perimeter_acceleration", "infill_acceleration",
+ "bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", "draft_shield",
+ "min_skirt_length", "brim_width", "brim_offset", "brim_type", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers",
+ "raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion",
+ "support_material_pattern", "support_material_with_sheath", "support_material_spacing", "support_material_closing_radius", "support_material_style",
+ "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", "support_material_bottom_interface_layers",
+ "support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops",
+ "support_material_contact_distance", "support_material_bottom_contact_distance",
+ "support_material_buildplate_only", "dont_support_bridges", "thick_bridges", "notes", "complete_objects", "extruder_clearance_radius",
+ "extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "perimeter_extruder",
+ "infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder",
+ "ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width",
+ "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width",
+ "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "infill_anchor", "infill_anchor_max", "bridge_flow_ratio", "clip_multipart_objects",
+ "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y",
+ "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width",
+ "wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits"
+};
-const std::vector& Preset::filament_options()
-{
- static std::vector s_opts {
- "filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed",
- "extrusion_multiplier", "filament_density", "filament_cost", "filament_spool_weight", "filament_loading_speed", "filament_loading_speed_start", "filament_load_time",
- "filament_unloading_speed", "filament_unloading_speed_start", "filament_unload_time", "filament_toolchange_delay", "filament_cooling_moves",
- "filament_cooling_initial_speed", "filament_cooling_final_speed", "filament_ramming_parameters", "filament_minimal_purge_on_wipe_tower",
- "temperature", "first_layer_temperature", "bed_temperature", "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed",
- "max_fan_speed", "bridge_fan_speed", "disable_fan_first_layers", "full_fan_speed_layer", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed",
- "start_filament_gcode", "end_filament_gcode",
- // Retract overrides
- "filament_retract_length", "filament_retract_lift", "filament_retract_lift_above", "filament_retract_lift_below", "filament_retract_speed", "filament_deretract_speed", "filament_retract_restart_extra", "filament_retract_before_travel",
- "filament_retract_layer_change", "filament_wipe", "filament_retract_before_wipe",
- // Profile compatibility
- "filament_vendor", "compatible_prints", "compatible_prints_condition", "compatible_printers", "compatible_printers_condition", "inherits"
- };
- return s_opts;
-}
+static std::vector s_Preset_filament_options {
+ "filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed",
+ "extrusion_multiplier", "filament_density", "filament_cost", "filament_spool_weight", "filament_loading_speed", "filament_loading_speed_start", "filament_load_time",
+ "filament_unloading_speed", "filament_unloading_speed_start", "filament_unload_time", "filament_toolchange_delay", "filament_cooling_moves",
+ "filament_cooling_initial_speed", "filament_cooling_final_speed", "filament_ramming_parameters", "filament_minimal_purge_on_wipe_tower",
+ "temperature", "first_layer_temperature", "bed_temperature", "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed",
+ "max_fan_speed", "bridge_fan_speed", "disable_fan_first_layers", "full_fan_speed_layer", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed",
+ "start_filament_gcode", "end_filament_gcode",
+ // Retract overrides
+ "filament_retract_length", "filament_retract_lift", "filament_retract_lift_above", "filament_retract_lift_below", "filament_retract_speed", "filament_deretract_speed", "filament_retract_restart_extra", "filament_retract_before_travel",
+ "filament_retract_layer_change", "filament_wipe", "filament_retract_before_wipe",
+ // Profile compatibility
+ "filament_vendor", "compatible_prints", "compatible_prints_condition", "compatible_printers", "compatible_printers_condition", "inherits"
+};
-const std::vector& Preset::machine_limits_options()
-{
- static std::vector s_opts;
- if (s_opts.empty()) {
- s_opts = {
- "machine_max_acceleration_extruding", "machine_max_acceleration_retracting", "machine_max_acceleration_travel",
- "machine_max_acceleration_x", "machine_max_acceleration_y", "machine_max_acceleration_z", "machine_max_acceleration_e",
- "machine_max_feedrate_x", "machine_max_feedrate_y", "machine_max_feedrate_z", "machine_max_feedrate_e",
- "machine_min_extruding_rate", "machine_min_travel_rate",
- "machine_max_jerk_x", "machine_max_jerk_y", "machine_max_jerk_z", "machine_max_jerk_e",
- };
- }
- return s_opts;
-}
+static std::vector s_Preset_machine_limits_options {
+ "machine_max_acceleration_extruding", "machine_max_acceleration_retracting", "machine_max_acceleration_travel",
+ "machine_max_acceleration_x", "machine_max_acceleration_y", "machine_max_acceleration_z", "machine_max_acceleration_e",
+ "machine_max_feedrate_x", "machine_max_feedrate_y", "machine_max_feedrate_z", "machine_max_feedrate_e",
+ "machine_min_extruding_rate", "machine_min_travel_rate",
+ "machine_max_jerk_x", "machine_max_jerk_y", "machine_max_jerk_z", "machine_max_jerk_e",
+};
+
+static std::vector s_Preset_printer_options {
+ "printer_technology",
+ "bed_shape", "bed_custom_texture", "bed_custom_model", "z_offset", "gcode_flavor", "use_relative_e_distances",
+ "use_firmware_retraction", "use_volumetric_e", "variable_layer_height",
+ //FIXME the print host keys are left here just for conversion from the Printer preset to Physical Printer preset.
+ "host_type", "print_host", "printhost_apikey", "printhost_cafile",
+ "single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode",
+ "color_change_gcode", "pause_print_gcode", "template_custom_gcode",
+ "between_objects_gcode", "printer_vendor", "printer_model", "printer_variant", "printer_notes", "cooling_tube_retraction",
+ "cooling_tube_length", "high_current_on_filament_swap", "parking_pos_retraction", "extra_loading_move", "max_print_height",
+ "default_print_profile", "inherits",
+ "remaining_times", "silent_mode",
+ "machine_limits_usage", "thumbnails"
+};
+
+static std::vector s_Preset_sla_print_options {
+ "layer_height",
+ "faded_layers",
+ "supports_enable",
+ "support_head_front_diameter",
+ "support_head_penetration",
+ "support_head_width",
+ "support_pillar_diameter",
+ "support_small_pillar_diameter_percent",
+ "support_max_bridges_on_pillar",
+ "support_pillar_connection_mode",
+ "support_buildplate_only",
+ "support_pillar_widening_factor",
+ "support_base_diameter",
+ "support_base_height",
+ "support_base_safety_distance",
+ "support_critical_angle",
+ "support_max_bridge_length",
+ "support_max_pillar_link_distance",
+ "support_object_elevation",
+ "support_points_density_relative",
+ "support_points_minimal_distance",
+ "slice_closing_radius",
+ "slicing_mode",
+ "pad_enable",
+ "pad_wall_thickness",
+ "pad_wall_height",
+ "pad_brim_size",
+ "pad_max_merge_distance",
+ // "pad_edge_radius",
+ "pad_wall_slope",
+ "pad_object_gap",
+ "pad_around_object",
+ "pad_around_object_everywhere",
+ "pad_object_connector_stride",
+ "pad_object_connector_width",
+ "pad_object_connector_penetration",
+ "hollowing_enable",
+ "hollowing_min_thickness",
+ "hollowing_quality",
+ "hollowing_closing_distance",
+ "output_filename_format",
+ "default_sla_print_profile",
+ "compatible_printers",
+ "compatible_printers_condition",
+ "inherits"
+};
+
+static std::vector s_Preset_sla_material_options {
+ "material_type",
+ "initial_layer_height",
+ "bottle_cost",
+ "bottle_volume",
+ "bottle_weight",
+ "material_density",
+ "exposure_time",
+ "initial_exposure_time",
+ "material_correction",
+ "material_notes",
+ "material_vendor",
+ "default_sla_material_profile",
+ "compatible_prints", "compatible_prints_condition",
+ "compatible_printers", "compatible_printers_condition", "inherits"
+};
+
+static std::vector s_Preset_sla_printer_options {
+ "printer_technology",
+ "bed_shape", "bed_custom_texture", "bed_custom_model", "max_print_height",
+ "display_width", "display_height", "display_pixels_x", "display_pixels_y",
+ "display_mirror_x", "display_mirror_y",
+ "display_orientation",
+ "fast_tilt_time", "slow_tilt_time", "area_fill",
+ "relative_correction",
+ "absolute_correction",
+ "elefant_foot_compensation",
+ "elefant_foot_min_width",
+ "gamma_correction",
+ "min_exposure_time", "max_exposure_time",
+ "min_initial_exposure_time", "max_initial_exposure_time",
+ //FIXME the print host keys are left here just for conversion from the Printer preset to Physical Printer preset.
+ "print_host", "printhost_apikey", "printhost_cafile",
+ "printer_notes",
+ "inherits"
+};
+
+const std::vector& Preset::print_options() { return s_Preset_print_options; }
+const std::vector& Preset::filament_options() { return s_Preset_filament_options; }
+const std::vector& Preset::machine_limits_options() { return s_Preset_machine_limits_options; }
+// The following nozzle options of a printer profile will be adjusted to match the size
+// of the nozzle_diameter vector.
+const std::vector& Preset::nozzle_options() { return print_config_def.extruder_option_keys(); }
+const std::vector& Preset::sla_print_options() { return s_Preset_sla_print_options; }
+const std::vector& Preset::sla_material_options() { return s_Preset_sla_material_options; }
+const std::vector& Preset::sla_printer_options() { return s_Preset_sla_printer_options; }
const std::vector& Preset::printer_options()
{
- static std::vector s_opts;
- if (s_opts.empty()) {
- s_opts = {
- "printer_technology",
- "bed_shape", "bed_custom_texture", "bed_custom_model", "z_offset", "gcode_flavor", "use_relative_e_distances",
- "use_firmware_retraction", "use_volumetric_e", "variable_layer_height",
- //FIXME the print host keys are left here just for conversion from the Printer preset to Physical Printer preset.
- "host_type", "print_host", "printhost_apikey", "printhost_cafile",
- "single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode",
- "color_change_gcode", "pause_print_gcode", "template_custom_gcode",
- "between_objects_gcode", "printer_vendor", "printer_model", "printer_variant", "printer_notes", "cooling_tube_retraction",
- "cooling_tube_length", "high_current_on_filament_swap", "parking_pos_retraction", "extra_loading_move", "max_print_height",
- "default_print_profile", "inherits",
- "remaining_times", "silent_mode",
- "machine_limits_usage", "thumbnails"
- };
- s_opts.insert(s_opts.end(), Preset::machine_limits_options().begin(), Preset::machine_limits_options().end());
- s_opts.insert(s_opts.end(), Preset::nozzle_options().begin(), Preset::nozzle_options().end());
- }
- return s_opts;
-}
-
-// The following nozzle options of a printer profile will be adjusted to match the size
-// of the nozzle_diameter vector.
-const std::vector& Preset::nozzle_options()
-{
- return print_config_def.extruder_option_keys();
-}
-
-const std::vector& Preset::sla_print_options()
-{
- static std::vector s_opts;
- if (s_opts.empty()) {
- s_opts = {
- "layer_height",
- "faded_layers",
- "supports_enable",
- "support_head_front_diameter",
- "support_head_penetration",
- "support_head_width",
- "support_pillar_diameter",
- "support_small_pillar_diameter_percent",
- "support_max_bridges_on_pillar",
- "support_pillar_connection_mode",
- "support_buildplate_only",
- "support_pillar_widening_factor",
- "support_base_diameter",
- "support_base_height",
- "support_base_safety_distance",
- "support_critical_angle",
- "support_max_bridge_length",
- "support_max_pillar_link_distance",
- "support_object_elevation",
- "support_points_density_relative",
- "support_points_minimal_distance",
- "slice_closing_radius",
- "slicing_mode",
- "pad_enable",
- "pad_wall_thickness",
- "pad_wall_height",
- "pad_brim_size",
- "pad_max_merge_distance",
- // "pad_edge_radius",
- "pad_wall_slope",
- "pad_object_gap",
- "pad_around_object",
- "pad_around_object_everywhere",
- "pad_object_connector_stride",
- "pad_object_connector_width",
- "pad_object_connector_penetration",
- "hollowing_enable",
- "hollowing_min_thickness",
- "hollowing_quality",
- "hollowing_closing_distance",
- "output_filename_format",
- "default_sla_print_profile",
- "compatible_printers",
- "compatible_printers_condition",
- "inherits"
- };
- }
- return s_opts;
-}
-
-const std::vector& Preset::sla_material_options()
-{
- static std::vector s_opts;
- if (s_opts.empty()) {
- s_opts = {
- "material_type",
- "initial_layer_height",
- "bottle_cost",
- "bottle_volume",
- "bottle_weight",
- "material_density",
- "exposure_time",
- "initial_exposure_time",
- "material_correction",
- "material_notes",
- "material_vendor",
- "default_sla_material_profile",
- "compatible_prints", "compatible_prints_condition",
- "compatible_printers", "compatible_printers_condition", "inherits"
- };
- }
- return s_opts;
-}
-
-const std::vector& Preset::sla_printer_options()
-{
- static std::vector s_opts;
- if (s_opts.empty()) {
- s_opts = {
- "printer_technology",
- "bed_shape", "bed_custom_texture", "bed_custom_model", "max_print_height",
- "display_width", "display_height", "display_pixels_x", "display_pixels_y",
- "display_mirror_x", "display_mirror_y",
- "display_orientation",
- "fast_tilt_time", "slow_tilt_time", "area_fill",
- "relative_correction",
- "absolute_correction",
- "elefant_foot_compensation",
- "elefant_foot_min_width",
- "gamma_correction",
- "min_exposure_time", "max_exposure_time",
- "min_initial_exposure_time", "max_initial_exposure_time",
- //FIXME the print host keys are left here just for conversion from the Printer preset to Physical Printer preset.
- "print_host", "printhost_apikey", "printhost_cafile",
- "printer_notes",
- "inherits"
- };
- }
+ static std::vector s_opts = [](){
+ std::vector opts = s_Preset_printer_options;
+ append(opts, s_Preset_machine_limits_options);
+ append(opts, Preset::nozzle_options());
+ return opts;
+ }();
return s_opts;
}
@@ -1194,21 +1163,38 @@ inline t_config_option_keys deep_diff(const ConfigBase &config_this, const Confi
return diff;
}
+static constexpr const std::initializer_list optional_keys { "compatible_prints", "compatible_printers" };
+
+bool PresetCollection::is_dirty(const Preset *edited, const Preset *reference)
+{
+ if (edited != nullptr && reference != nullptr) {
+ // Only compares options existing in both configs.
+ if (! reference->config.equals(edited->config))
+ return true;
+ // The "compatible_printers" option key is handled differently from the others:
+ // It is not mandatory. If the key is missing, it means it is compatible with any printer.
+ // If the key exists and it is empty, it means it is compatible with no printer.
+ for (auto &opt_key : optional_keys)
+ if (reference->config.has(opt_key) != edited->config.has(opt_key))
+ return true;
+ }
+ return false;
+}
+
std::vector PresetCollection::dirty_options(const Preset *edited, const Preset *reference, const bool deep_compare /*= false*/)
{
std::vector changed;
if (edited != nullptr && reference != nullptr) {
+ // Only compares options existing in both configs.
changed = deep_compare ?
deep_diff(edited->config, reference->config) :
reference->config.diff(edited->config);
// The "compatible_printers" option key is handled differently from the others:
// It is not mandatory. If the key is missing, it means it is compatible with any printer.
// If the key exists and it is empty, it means it is compatible with no printer.
- std::initializer_list optional_keys { "compatible_prints", "compatible_printers" };
- for (auto &opt_key : optional_keys) {
+ for (auto &opt_key : optional_keys)
if (reference->config.has(opt_key) != edited->config.has(opt_key))
changed.emplace_back(opt_key);
- }
}
return changed;
}
@@ -1409,26 +1395,25 @@ std::string PhysicalPrinter::separator()
return " * ";
}
+static std::vector s_PhysicalPrinter_opts {
+ "preset_name", // temporary option to compatibility with older Slicer
+ "preset_names",
+ "printer_technology",
+ "host_type",
+ "print_host",
+ "printhost_apikey",
+ "printhost_cafile",
+ "printhost_port",
+ "printhost_authorization_type",
+ // HTTP digest authentization (RFC 2617)
+ "printhost_user",
+ "printhost_password",
+ "printhost_ssl_ignore_revoke"
+};
+
const std::vector& PhysicalPrinter::printer_options()
{
- static std::vector s_opts;
- if (s_opts.empty()) {
- s_opts = {
- "preset_name", // temporary option to compatibility with older Slicer
- "preset_names",
- "printer_technology",
- "host_type",
- "print_host",
- "printhost_apikey",
- "printhost_cafile",
- "printhost_port",
- "printhost_authorization_type",
- // HTTP digest authentization (RFC 2617)
- "printhost_user",
- "printhost_password"
- };
- }
- return s_opts;
+ return s_PhysicalPrinter_opts;
}
static constexpr auto legacy_print_host_options = {
diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp
index 4897f504c..849dd7b80 100644
--- a/src/libslic3r/Preset.hpp
+++ b/src/libslic3r/Preset.hpp
@@ -371,7 +371,7 @@ public:
const Preset& get_edited_preset() const { return m_edited_preset; }
// Return the last saved preset.
- const Preset& get_saved_preset() const { return m_saved_preset; }
+// const Preset& get_saved_preset() const { return m_saved_preset; }
// Return vendor of the first parent profile, for which the vendor is defined, or null if such profile does not exist.
PresetWithVendorProfile get_preset_with_vendor_profile(const Preset &preset) const;
@@ -395,7 +395,7 @@ public:
void discard_current_changes() {
m_presets[m_idx_selected].reset_dirty();
m_edited_preset = m_presets[m_idx_selected];
- update_saved_preset_from_current_preset();
+// update_saved_preset_from_current_preset();
}
// Return a preset by its name. If the preset is active, a temporary copy is returned.
@@ -463,7 +463,8 @@ public:
size_t num_visible() const { return std::count_if(m_presets.begin(), m_presets.end(), [](const Preset &preset){return preset.is_visible;}); }
// Compare the content of get_selected_preset() with get_edited_preset() configs, return true if they differ.
- bool current_is_dirty() const { return ! this->current_dirty_options().empty(); }
+ bool current_is_dirty() const
+ { return is_dirty(&this->get_edited_preset(), &this->get_selected_preset()); }
// Compare the content of get_selected_preset() with get_edited_preset() configs, return the list of keys where they differ.
std::vector current_dirty_options(const bool deep_compare = false) const
{ return dirty_options(&this->get_edited_preset(), &this->get_selected_preset(), deep_compare); }
@@ -472,10 +473,11 @@ public:
{ return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent(), deep_compare); }
// Compare the content of get_saved_preset() with get_edited_preset() configs, return true if they differ.
- bool saved_is_dirty() const { return !this->saved_dirty_options().empty(); }
+ bool saved_is_dirty() const
+ { return is_dirty(&this->get_edited_preset(), &m_saved_preset); }
// Compare the content of get_saved_preset() with get_edited_preset() configs, return the list of keys where they differ.
- std::vector saved_dirty_options(const bool deep_compare = false) const
- { return dirty_options(&this->get_edited_preset(), &this->get_saved_preset(), deep_compare); }
+// std::vector saved_dirty_options() const
+// { return dirty_options(&this->get_edited_preset(), &this->get_saved_preset(), /* deep_compare */ false); }
// Copy edited preset into saved preset.
void update_saved_preset_from_current_preset() { m_saved_preset = m_edited_preset; }
@@ -552,7 +554,8 @@ private:
size_t update_compatible_internal(const PresetWithVendorProfile &active_printer, const PresetWithVendorProfile *active_print, PresetSelectCompatibleType unselect_if_incompatible);
public:
- static std::vector dirty_options(const Preset *edited, const Preset *reference, const bool is_printer_type = false);
+ static bool is_dirty(const Preset *edited, const Preset *reference);
+ static std::vector dirty_options(const Preset *edited, const Preset *reference, const bool deep_compare = false);
private:
// Type of this PresetCollection: TYPE_PRINT, TYPE_FILAMENT or TYPE_PRINTER.
Preset::Type m_type;
diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp
index 95d93b1c2..9ac8864f9 100644
--- a/src/libslic3r/PresetBundle.cpp
+++ b/src/libslic3r/PresetBundle.cpp
@@ -1477,7 +1477,7 @@ std::pair PresetBundle::load_configbundle(
if (! active_print.empty())
prints.select_preset_by_name(active_print, true);
if (! active_sla_print.empty())
- sla_materials.select_preset_by_name(active_sla_print, true);
+ sla_prints.select_preset_by_name(active_sla_print, true);
if (! active_sla_material.empty())
sla_materials.select_preset_by_name(active_sla_material, true);
if (! active_printer.empty())
diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp
index b3a1bc993..48737f830 100644
--- a/src/libslic3r/Print.cpp
+++ b/src/libslic3r/Print.cpp
@@ -159,7 +159,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|| opt_key == "wipe_tower_rotation_angle") {
steps.emplace_back(psSkirtBrim);
} else if (
- opt_key == "nozzle_diameter"
+ opt_key == "first_layer_height"
+ || opt_key == "nozzle_diameter"
|| opt_key == "resolution"
// Spiral Vase forces different kind of slicing than the normal model:
// In Spiral Vase mode, holes are closed and only the largest area contour is kept at each layer.
diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp
index 491960705..0056aee33 100644
--- a/src/libslic3r/Print.hpp
+++ b/src/libslic3r/Print.hpp
@@ -271,7 +271,11 @@ public:
// Centering offset of the sliced mesh from the scaled and rotated mesh of the model.
const Point& center_offset() const { return m_center_offset; }
- bool has_brim() const { return this->config().brim_type != btNoBrim && this->config().brim_width.value > 0.; }
+ bool has_brim() const {
+ return this->config().brim_type != btNoBrim
+ && this->config().brim_width.value > 0.
+ && ! this->has_raft();
+ }
// This is the *total* layer count (including support layers)
// this value is not supposed to be compared with Layer::id
@@ -321,7 +325,7 @@ public:
bool has_raft() const { return m_config.raft_layers > 0; }
bool has_support_material() const { return this->has_support() || this->has_raft(); }
// Checks if the model object is painted using the multi-material painting gizmo.
- bool is_mm_painted() const { return this->model_object()->is_mm_painted(); };
+ bool is_mm_painted() const { return this->model_object()->is_mm_painted(); }
// returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions)
std::vector object_extruders() const;
diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp
index 0e252ac6f..10960c535 100644
--- a/src/libslic3r/PrintApply.cpp
+++ b/src/libslic3r/PrintApply.cpp
@@ -216,22 +216,25 @@ static t_config_option_keys print_config_diffs(
const ConfigOption *opt_new_filament = std::binary_search(extruder_retract_keys.begin(), extruder_retract_keys.end(), opt_key) ? new_full_config.option(filament_prefix + opt_key) : nullptr;
if (opt_new_filament != nullptr && ! opt_new_filament->is_nil()) {
// An extruder retract override is available at some of the filament presets.
- if (*opt_old != *opt_new || opt_new->overriden_by(opt_new_filament)) {
+ bool overriden = opt_new->overriden_by(opt_new_filament);
+ if (overriden || *opt_old != *opt_new) {
auto opt_copy = opt_new->clone();
opt_copy->apply_override(opt_new_filament);
- if (*opt_old == *opt_copy)
- delete opt_copy;
- else {
- filament_overrides.set_key_value(opt_key, opt_copy);
+ bool changed = *opt_old != *opt_copy;
+ if (changed)
print_diff.emplace_back(opt_key);
- }
+ if (changed || overriden) {
+ // filament_overrides will be applied to the placeholder parser, which layers these parameters over full_print_config.
+ filament_overrides.set_key_value(opt_key, opt_copy);
+ } else
+ delete opt_copy;
}
} else if (*opt_new != *opt_old)
print_diff.emplace_back(opt_key);
}
return print_diff;
- }
+}
// Prepare for storing of the full print config into new_full_config to be exported into the G-code and to be used by the PlaceholderParser.
static t_config_option_keys full_print_config_diffs(const DynamicPrintConfig ¤t_full_config, const DynamicPrintConfig &new_full_config)
@@ -812,7 +815,7 @@ static PrintObjectRegions* generate_print_object_regions(
layer_ranges_regions.push_back({ range.layer_height_range, range.config });
}
- const bool is_mm_painted = std::any_of(model_volumes.cbegin(), model_volumes.cend(), [](const ModelVolume *mv) { return mv->is_mm_painted(); });
+ const bool is_mm_painted = num_extruders > 1 && std::any_of(model_volumes.cbegin(), model_volumes.cend(), [](const ModelVolume *mv) { return mv->is_mm_painted(); });
update_volume_bboxes(layer_ranges_regions, out->cached_volume_ids, model_volumes, out->trafo_bboxes, is_mm_painted ? 0.f : std::max(0.f, xy_size_compensation));
std::vector region_set;
@@ -928,6 +931,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
bool num_extruders_changed = false;
if (! full_config_diff.empty()) {
update_apply_status(this->invalidate_step(psGCodeExport));
+ m_placeholder_parser.clear_config();
// Set the profile aliases for the PrintBase::output_filename()
m_placeholder_parser.set("print_preset", new_full_config.option("print_settings_id")->clone());
m_placeholder_parser.set("filament_preset", new_full_config.option("filament_settings_id")->clone());
@@ -939,6 +943,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
// It is also safe to change m_config now after this->invalidate_state_by_config_options() call.
m_config.apply_only(new_full_config, print_diff, true);
//FIXME use move semantics once ConfigBase supports it.
+ // Some filament_overrides may contain values different from new_full_config, but equal to m_config.
+ // As long as these config options don't reallocate memory when copying, we are safe overriding a value, which is in use by a worker thread.
m_config.apply(filament_overrides);
// Handle changes to object config defaults
m_default_object_config.apply_only(new_full_config, object_diff, true);
@@ -946,8 +952,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
m_default_region_config.apply_only(new_full_config, region_diff, true);
m_full_print_config = std::move(new_full_config);
if (num_extruders != m_config.nozzle_diameter.size()) {
- num_extruders = m_config.nozzle_diameter.size();
- num_extruders_changed = true;
+ num_extruders = m_config.nozzle_diameter.size();
+ num_extruders_changed = true;
}
}
@@ -1065,7 +1071,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
// Check whether a model part volume was added or removed, their transformations or order changed.
// Only volume IDs, volume types, transformation matrices and their order are checked, configuration and other parameters are NOT checked.
bool solid_or_modifier_differ = model_volume_list_changed(model_object, model_object_new, solid_or_modifier_types) ||
- model_mmu_segmentation_data_changed(model_object, model_object_new);
+ model_mmu_segmentation_data_changed(model_object, model_object_new) ||
+ (model_object_new.is_mm_painted() && num_extruders_changed);
bool supports_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER) ||
model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER);
bool layer_height_ranges_differ = ! layer_height_ranges_equal(model_object.layer_config_ranges, model_object_new.layer_config_ranges, model_object_new.layer_height_profile.empty());
@@ -1267,7 +1274,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
print_object_regions->ref_cnt_inc();
}
std::vector painting_extruders;
- if (const auto &volumes = print_object.model_object()->volumes;
+ if (const auto &volumes = print_object.model_object()->volumes;
+ num_extruders > 1 &&
std::find_if(volumes.begin(), volumes.end(), [](const ModelVolume *v) { return ! v->mmu_segmentation_facets.empty(); }) != volumes.end()) {
//FIXME be more specific! Don't enumerate extruders that are not used for painting!
painting_extruders.assign(num_extruders, 0);
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index e64824d0d..f010bad39 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -232,6 +232,16 @@ void PrintConfigDef::init_common_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionString(""));
+ def = this->add("elefant_foot_compensation", coFloat);
+ def->label = L("Elephant foot compensation");
+ def->category = L("Advanced");
+ def->tooltip = L("The first layer will be shrunk in the XY plane by the configured value "
+ "to compensate for the 1st layer squish aka an Elephant Foot effect.");
+ def->sidetext = L("mm");
+ def->min = 0;
+ def->mode = comAdvanced;
+ def->set_default_value(new ConfigOptionFloat(0.));
+
def = this->add("thumbnails", coPoints);
def->label = L("G-code thumbnails");
def->tooltip = L("Picture sizes to be stored into a .gcode and .sl1 / .sl1s files, in the following format: \"XxY, XxY, ...\"");
@@ -264,6 +274,7 @@ void PrintConfigDef::init_common_params()
"Print host behind HAProxy with basic auth enabled can be accessed by putting the user name and password into the URL "
"in the following format: https://username:password@your-octopi-address/");
def->mode = comAdvanced;
+ def->cli = ConfigOptionDef::nocli;
def->set_default_value(new ConfigOptionString(""));
def = this->add("printhost_apikey", coString);
@@ -271,6 +282,7 @@ void PrintConfigDef::init_common_params()
def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain "
"the API Key or the password required for authentication.");
def->mode = comAdvanced;
+ def->cli = ConfigOptionDef::nocli;
def->set_default_value(new ConfigOptionString(""));
def = this->add("printhost_port", coString);
@@ -278,6 +290,7 @@ void PrintConfigDef::init_common_params()
def->tooltip = L("Name of the printer");
def->gui_type = ConfigOptionDef::GUIType::select_open;
def->mode = comAdvanced;
+ def->cli = ConfigOptionDef::nocli;
def->set_default_value(new ConfigOptionString(""));
def = this->add("printhost_cafile", coString);
@@ -285,31 +298,33 @@ void PrintConfigDef::init_common_params()
def->tooltip = L("Custom CA certificate file can be specified for HTTPS OctoPrint connections, in crt/pem format. "
"If left blank, the default OS CA certificate repository is used.");
def->mode = comAdvanced;
+ def->cli = ConfigOptionDef::nocli;
def->set_default_value(new ConfigOptionString(""));
- def = this->add("elefant_foot_compensation", coFloat);
- def->label = L("Elephant foot compensation");
- def->category = L("Advanced");
- def->tooltip = L("The first layer will be shrunk in the XY plane by the configured value "
- "to compensate for the 1st layer squish aka an Elephant Foot effect.");
- def->sidetext = L("mm");
- def->min = 0;
- def->mode = comAdvanced;
- def->set_default_value(new ConfigOptionFloat(0.));
-
// Options used by physical printers
def = this->add("printhost_user", coString);
def->label = L("User");
// def->tooltip = L("");
def->mode = comAdvanced;
+ def->cli = ConfigOptionDef::nocli;
def->set_default_value(new ConfigOptionString(""));
def = this->add("printhost_password", coString);
def->label = L("Password");
// def->tooltip = L("");
def->mode = comAdvanced;
+ def->cli = ConfigOptionDef::nocli;
def->set_default_value(new ConfigOptionString(""));
+
+ // Only available on Windows.
+ def = this->add("printhost_ssl_ignore_revoke", coBool);
+ def->label = L("Ignore HTTPS certificate revocation checks");
+ def->tooltip = L("Ignore HTTPS certificate revocation checks in case of missing or offline distribution points. "
+ "One may want to enable this option for self signed certificates if connection fails.");
+ def->mode = comAdvanced;
+ def->cli = ConfigOptionDef::nocli;
+ def->set_default_value(new ConfigOptionBool(false));
def = this->add("preset_names", coStrings);
def->label = L("Printer preset names");
@@ -317,12 +332,6 @@ void PrintConfigDef::init_common_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionStrings());
- // temporary workaround for compatibility with older Slicer
- {
- def = this->add("preset_name", coString);
- def->set_default_value(new ConfigOptionString());
- }
-
def = this->add("printhost_authorization_type", coEnum);
def->label = L("Authorization Type");
// def->tooltip = L("");
@@ -332,7 +341,14 @@ void PrintConfigDef::init_common_params()
def->enum_labels.push_back(L("API key"));
def->enum_labels.push_back(L("HTTP digest"));
def->mode = comAdvanced;
+ def->cli = ConfigOptionDef::nocli;
def->set_default_value(new ConfigOptionEnum(atKeyPassword));
+
+ // temporary workaround for compatibility with older Slicer
+ {
+ def = this->add("preset_name", coString);
+ def->set_default_value(new ConfigOptionString());
+ }
}
void PrintConfigDef::init_fff_params()
@@ -465,7 +481,8 @@ void PrintConfigDef::init_fff_params()
def = this->add("brim_width", coFloat);
def->label = L("Brim width");
def->category = L("Skirt and brim");
- def->tooltip = L("Horizontal width of the brim that will be printed around each object on the first layer.");
+ def->tooltip = L("Horizontal width of the brim that will be printed around each object on the first layer."
+ "When raft is used, no brim is generated (use raft_first_layer_expansion).");
def->sidetext = L("mm");
def->min = 0;
def->max = 200;
@@ -1809,6 +1826,7 @@ void PrintConfigDef::init_fff_params()
def->enum_labels.push_back("AstroBox");
def->enum_labels.push_back("Repetier");
def->mode = comAdvanced;
+ def->cli = ConfigOptionDef::nocli;
def->set_default_value(new ConfigOptionEnum(htOctoPrint));
def = this->add("only_retract_when_crossing_perimeters", coBool);
diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp
index ae9816531..ee09e0f5b 100644
--- a/src/libslic3r/PrintObject.cpp
+++ b/src/libslic3r/PrintObject.cpp
@@ -535,7 +535,6 @@ bool PrintObject::invalidate_state_by_config_options(
steps.emplace_back(posPerimeters);
} else if (
opt_key == "layer_height"
- || opt_key == "first_layer_height"
|| opt_key == "mmu_segmented_region_max_width"
|| opt_key == "raft_layers"
|| opt_key == "raft_contact_distance"
diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp
index ad6a23abc..1d61be85b 100644
--- a/src/libslic3r/PrintObjectSlice.cpp
+++ b/src/libslic3r/PrintObjectSlice.cpp
@@ -167,8 +167,9 @@ static std::vector slice_volumes_inner(
params_base.mode_below = params_base.mode;
- const bool is_mm_painted = std::any_of(model_volumes.cbegin(), model_volumes.cend(), [](const ModelVolume *mv) { return mv->is_mm_painted(); });
- const auto extra_offset = is_mm_painted ? 0.f : std::max(0.f, float(print_object_config.xy_size_compensation.value));
+ const size_t num_extruders = print_config.nozzle_diameter.size();
+ const bool is_mm_painted = num_extruders > 1 && std::any_of(model_volumes.cbegin(), model_volumes.cend(), [](const ModelVolume *mv) { return mv->is_mm_painted(); });
+ const auto extra_offset = is_mm_painted ? 0.f : std::max(0.f, float(print_object_config.xy_size_compensation.value));
for (const ModelVolume *model_volume : model_volumes)
if (model_volume_needs_slicing(*model_volume)) {
@@ -723,6 +724,7 @@ void PrintObject::slice_volumes()
// Is any ModelVolume MMU painted?
if (const auto& volumes = this->model_object()->volumes;
+ m_print->config().nozzle_diameter.size() > 1 &&
std::find_if(volumes.begin(), volumes.end(), [](const ModelVolume* v) { return !v->mmu_segmentation_facets.empty(); }) != volumes.end()) {
// If XY Size compensation is also enabled, notify the user that XY Size compensation
@@ -743,8 +745,9 @@ void PrintObject::slice_volumes()
BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - make_slices in parallel - begin";
{
// Compensation value, scaled. Only applying the negative scaling here, as the positive scaling has already been applied during slicing.
- const auto xy_compensation_scaled = this->is_mm_painted() ? scaled(0.f) : scaled(std::min(m_config.xy_size_compensation.value, 0.));
- const float elephant_foot_compensation_scaled = (m_config.raft_layers == 0) ?
+ const size_t num_extruders = print->config().nozzle_diameter.size();
+ const auto xy_compensation_scaled = (num_extruders > 1 && this->is_mm_painted()) ? scaled(0.f) : scaled(std::min(m_config.xy_size_compensation.value, 0.));
+ 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;
diff --git a/src/libslic3r/QuadricEdgeCollapse.cpp b/src/libslic3r/QuadricEdgeCollapse.cpp
index 95c7b40d8..6efc5f4a9 100644
--- a/src/libslic3r/QuadricEdgeCollapse.cpp
+++ b/src/libslic3r/QuadricEdgeCollapse.cpp
@@ -7,13 +7,14 @@
using namespace Slic3r;
-// only private namespace not neccessary be in hpp
+// only private namespace not neccessary be in .hpp
namespace QuadricEdgeCollapse {
using Vertices = std::vector;
using Triangle = stl_triangle_vertex_indices;
using Indices = std::vector;
using SymMat = SimplifyMesh::implementation::SymetricMatrix;
-
+ using ThrowOnCancel = std::function;
+ using StatusFn = std::function;
// smallest error caused by edges, identify smallest edge in triangle
struct Error
{
@@ -74,12 +75,14 @@ namespace QuadricEdgeCollapse {
// calculate error for vertex and quadrics, triangle quadrics and triangle vertex give zero, only pozitive number
double vertex_error(const SymMat &q, const Vec3d &vertex);
SymMat create_quadric(const Triangle &t, const Vec3d& n, const Vertices &vertices);
- std::tuple init(const indexed_triangle_set &its);
+ std::tuple
+ init(const indexed_triangle_set &its, ThrowOnCancel& throw_on_cancel, StatusFn& status_fn);
std::optional find_triangle_index1(uint32_t vi, const VertexInfo& v_info,
uint32_t ti, const EdgeInfos& e_infos, const Indices& indices);
bool is_flipped(const Vec3f &new_vertex, uint32_t ti0, uint32_t ti1, const VertexInfo& v_info,
const TriangleInfos &t_infos, const EdgeInfos &e_infos, const indexed_triangle_set &its);
-
+ bool degenerate(uint32_t vi, uint32_t ti0, uint32_t ti1, const VertexInfo &v_info,
+ const EdgeInfos &e_infos, const Indices &indices);
// find edge with smallest error in triangle
Vec3d calculate_3errors(const Triangle &t, const Vertices &vertices, const VertexInfos &v_infos);
Error calculate_error(uint32_t ti, const Triangle& t,const Vertices &vertices, const VertexInfos& v_infos, unsigned char& min_index);
@@ -88,6 +91,14 @@ namespace QuadricEdgeCollapse {
uint32_t vi0, uint32_t vi1, uint32_t vi_top0,
const Triangle &t1, CopyEdgeInfos& infos, EdgeInfos &e_infos1);
void compact(const VertexInfos &v_infos, const TriangleInfos &t_infos, const EdgeInfos &e_infos, indexed_triangle_set &its);
+
+#ifndef NDEBUG
+ void store_surround(const char *obj_filename, size_t triangle_index, int depth, const indexed_triangle_set &its,
+ const VertexInfos &v_infos, const EdgeInfos &e_infos);
+ bool check_neighbors(const indexed_triangle_set &its, const TriangleInfos &t_infos,
+ const VertexInfos &v_infos, const EdgeInfos &e_infos);
+#endif /* NDEBUG */
+
} // namespace QuadricEdgeCollapse
using namespace QuadricEdgeCollapse;
@@ -97,7 +108,7 @@ void Slic3r::its_quadric_edge_collapse(
uint32_t triangle_count,
float * max_error,
std::function throw_on_cancel,
- std::function statusfn)
+ std::function status_fn)
{
// constants --> may be move to config
const int status_init_size = 10; // in percents
@@ -108,15 +119,22 @@ void Slic3r::its_quadric_edge_collapse(
float maximal_error = (max_error == nullptr)? std::numeric_limits::max() : *max_error;
if (maximal_error <= 0.f) return;
if (throw_on_cancel == nullptr) throw_on_cancel = []() {};
- if (statusfn == nullptr) statusfn = [](int) {};
+ if (status_fn == nullptr) status_fn = [](int) {};
+
+ StatusFn init_status_fn = [&](int percent) {
+ status_fn(std::round((percent * status_init_size) / 100.));
+ };
TriangleInfos t_infos; // only normals with information about deleted triangle
VertexInfos v_infos;
EdgeInfos e_infos;
Errors errors;
- std::tie(t_infos, v_infos, e_infos, errors) = init(its);
+ std::tie(t_infos, v_infos, e_infos, errors) = init(its, throw_on_cancel, init_status_fn);
throw_on_cancel();
- statusfn(status_init_size);
+ status_fn(status_init_size);
+
+ //its_store_triangle(its, "triangle.obj", 1182);
+ //store_surround("triangle_surround1.obj", 1182, 1, its, v_infos, e_infos);
// convert from triangle index to mutable priority queue index
std::vector ti_2_mpqi(its.indices.size(), {0});
@@ -142,7 +160,7 @@ void Slic3r::its_quadric_edge_collapse(
(double) count_triangle_to_reduce;
double status = status_init_size + (100 - status_init_size) *
(1. - reduced);
- statusfn(static_cast(std::round(status)));
+ status_fn(static_cast(std::round(status)));
};
// modulo for update status
uint32_t status_mod = std::max(uint32_t(16), count_triangle_to_reduce / 100);
@@ -181,6 +199,8 @@ void Slic3r::its_quadric_edge_collapse(
find_triangle_index1(vi1, v_info0, ti0, e_infos, its.indices) :
find_triangle_index1(vi0, v_info1, ti0, e_infos, its.indices) ;
if (!ti1_opt.has_value() || // edge has only one triangle
+ degenerate(vi0, ti0, *ti1_opt, v_info1, e_infos, its.indices) ||
+ degenerate(vi1, ti0, *ti1_opt, v_info0, e_infos, its.indices) ||
is_flipped(new_vertex0, ti0, *ti1_opt, v_info0, t_infos, e_infos, its) ||
is_flipped(new_vertex0, ti0, *ti1_opt, v_info1, t_infos, e_infos, its)) {
// try other triangle's edge
@@ -236,8 +256,7 @@ void Slic3r::its_quadric_edge_collapse(
}
v_info0.q = q;
- // fix neighbors
-
+ // fix neighbors
// vertex index of triangle 0 which is not vi0 nor vi1
uint32_t vi_top0 = t0[(t_info0.min_index + 2) % 3];
const Triangle &t1 = its.indices[ti1];
@@ -263,6 +282,7 @@ void Slic3r::its_quadric_edge_collapse(
t_info1.set_deleted();
// triangle counter decrementation
actual_triangle_count-=2;
+ assert(check_neighbors(its, t_infos, v_infos, e_infos));
}
// compact triangle
@@ -362,8 +382,16 @@ SymMat QuadricEdgeCollapse::create_quadric(const Triangle &t,
}
std::tuple
-QuadricEdgeCollapse::init(const indexed_triangle_set &its)
+QuadricEdgeCollapse::init(const indexed_triangle_set &its, ThrowOnCancel& throw_on_cancel, StatusFn& status_fn)
{
+ // change speed of progress bargraph
+ const int status_normal_size = 25;
+ const int status_sum_quadric = 25;
+ const int status_set_offsets = 10;
+ const int status_calc_errors = 30;
+ const int status_create_refs = 10;
+
+ int status_offset = 0;
TriangleInfos t_infos(its.indices.size());
VertexInfos v_infos(its.vertices.size());
{
@@ -377,8 +405,13 @@ QuadricEdgeCollapse::init(const indexed_triangle_set &its)
Vec3d normal = create_normal(t, its.vertices);
t_info.n = normal.cast();
triangle_quadrics[i] = create_quadric(t, normal, its.vertices);
+ if (i % 1000000 == 0) {
+ throw_on_cancel();
+ status_fn(status_offset + (i * status_normal_size) / its.indices.size());
+ }
}
}); // END parallel for
+ status_offset += status_normal_size;
// sum quadrics
for (size_t i = 0; i < its.indices.size(); i++) {
@@ -389,7 +422,12 @@ QuadricEdgeCollapse::init(const indexed_triangle_set &its)
v_info.q += q;
++v_info.count; // triangle count
}
+ if (i % 1000000 == 0) {
+ throw_on_cancel();
+ status_fn(status_offset + (i * status_sum_quadric) / its.indices.size());
+ }
}
+ status_offset += status_sum_quadric;
} // remove triangle quadrics
// set offseted starts
@@ -402,6 +440,10 @@ QuadricEdgeCollapse::init(const indexed_triangle_set &its)
}
assert(its.indices.size() * 3 == triangle_start);
+ status_offset += status_set_offsets;
+ throw_on_cancel();
+ status_fn(status_offset);
+
// calc error
Errors errors(its.indices.size());
tbb::parallel_for(tbb::blocked_range(0, its.indices.size()),
@@ -410,8 +452,15 @@ QuadricEdgeCollapse::init(const indexed_triangle_set &its)
const Triangle &t = its.indices[i];
TriangleInfo & t_info = t_infos[i];
errors[i] = calculate_error(i, t, its.vertices, v_infos, t_info.min_index);
+ if (i % 1000000 == 0) {
+ throw_on_cancel();
+ status_fn(status_offset + (i * status_calc_errors) / its.indices.size());
+ }
+ if (i % 1000000 == 0) throw_on_cancel();
}
}); // END parallel for
+
+ status_offset += status_calc_errors;
// create reference
EdgeInfos e_infos(its.indices.size() * 3);
@@ -426,7 +475,14 @@ QuadricEdgeCollapse::init(const indexed_triangle_set &its)
e_info.edge = j;
++v_info.count;
}
+ if (i % 1000000 == 0) {
+ throw_on_cancel();
+ status_fn(status_offset + (i * status_create_refs) / its.indices.size());
+ }
}
+
+ throw_on_cancel();
+ status_fn(100);
return {t_infos, v_infos, e_infos, errors};
}
@@ -489,6 +545,28 @@ bool QuadricEdgeCollapse::is_flipped(const Vec3f & new_vertex,
return false;
}
+bool QuadricEdgeCollapse::degenerate(uint32_t vi,
+ uint32_t ti0,
+ uint32_t ti1,
+ const VertexInfo &v_info,
+ const EdgeInfos & e_infos,
+ const Indices & indices)
+{
+ // check surround triangle do not contain vertex index
+ // protect from creation of triangle with two same vertices inside
+ size_t v_info_end = v_info.start + v_info.count;
+ for (size_t ei = v_info.start; ei < v_info_end; ++ei) {
+ assert(ei < e_infos.size());
+ const EdgeInfo &e_info = e_infos[ei];
+ if (e_info.t_index == ti0) continue; // ti0 will be deleted
+ if (e_info.t_index == ti1) continue; // ti1 will be deleted
+ const Triangle &t = indices[e_info.t_index];
+ for (size_t i = 0; i < 3; ++i)
+ if (static_cast(t[i]) == vi) return true;
+ }
+ return false;
+}
+
Vec3d QuadricEdgeCollapse::calculate_3errors(const Triangle & t,
const Vertices & vertices,
const VertexInfos &v_infos)
@@ -653,3 +731,115 @@ void QuadricEdgeCollapse::compact(const VertexInfos & v_infos,
}
its.indices.erase(its.indices.begin() + ti_new, its.indices.end());
}
+
+#ifndef NDEBUG
+// store triangle surrounding to file
+void QuadricEdgeCollapse::store_surround(const char *obj_filename,
+ size_t triangle_index,
+ int depth,
+ const indexed_triangle_set &its,
+ const VertexInfos & v_infos,
+ const EdgeInfos & e_infos)
+{
+ std::set triangles;
+ // triangle index, depth
+ using Item = std::pair;
+ std::queue- process;
+ process.push({triangle_index, depth});
+
+ while (!process.empty()) {
+ Item item = process.front();
+ process.pop();
+ size_t ti = item.first;
+ auto it = triangles.find(ti);
+ if (it != triangles.end()) continue;
+ triangles.insert(ti);
+ if (item.second == 0) continue;
+
+ const Vec3i &t = its.indices[ti];
+ for (size_t i = 0; i < 3; ++i) {
+ const auto &v_info = v_infos[t[i]];
+ for (size_t d = 0; d < v_info.count; ++d) {
+ size_t ei = v_info.start + d;
+ const auto &e_info = e_infos[ei];
+ auto it = triangles.find(e_info.t_index);
+ if (it != triangles.end()) continue;
+ process.push({e_info.t_index, item.second - 1});
+ }
+ }
+ }
+
+ std::vector trs;
+ trs.reserve(triangles.size());
+ for (size_t ti : triangles) trs.push_back(ti);
+ its_store_triangles(its, obj_filename, trs);
+ // its_write_obj(its,"original.obj");
+}
+
+bool QuadricEdgeCollapse::check_neighbors(const indexed_triangle_set &its,
+ const TriangleInfos & t_infos,
+ const VertexInfos & v_infos,
+ const EdgeInfos & e_infos)
+{
+ VertexInfos v_infos2(v_infos.size());
+ size_t count_indices = 0;
+
+ for (size_t ti = 0; ti < its.indices.size(); ti++) {
+ if (t_infos[ti].is_deleted()) continue;
+ ++count_indices;
+ const Triangle &t = its.indices[ti];
+ for (size_t e = 0; e < 3; e++) {
+ VertexInfo &v_info = v_infos2[t[e]];
+ ++v_info.count; // triangle count
+ }
+ }
+
+ uint32_t triangle_start = 0;
+ for (VertexInfo &v_info : v_infos2) {
+ v_info.start = triangle_start;
+ triangle_start += v_info.count;
+ // set filled vertex to zero
+ v_info.count = 0;
+ }
+
+ // create reference
+ EdgeInfos e_infos2(count_indices * 3);
+ for (size_t ti = 0; ti < its.indices.size(); ti++) {
+ if (t_infos[ti].is_deleted()) continue;
+ const Triangle &t = its.indices[ti];
+ for (size_t j = 0; j < 3; ++j) {
+ VertexInfo &v_info = v_infos2[t[j]];
+ size_t ei = v_info.start + v_info.count;
+ assert(ei < e_infos2.size());
+ EdgeInfo &e_info = e_infos2[ei];
+ e_info.t_index = ti;
+ e_info.edge = j;
+ ++v_info.count;
+ }
+ }
+
+ for (size_t vi = 0; vi < its.vertices.size(); vi++) {
+ const VertexInfo &v_info = v_infos[vi];
+ if (v_info.is_deleted()) continue;
+ const VertexInfo &v_info2 = v_infos2[vi];
+ if (v_info.count != v_info2.count) { return false; }
+ EdgeInfos eis;
+ eis.reserve(v_info.count);
+ std::copy(e_infos.begin() + v_info.start,
+ e_infos.begin() + v_info.start + v_info.count,
+ std::back_inserter(eis));
+ auto compare = [](const EdgeInfo &ei1, const EdgeInfo &ei2) {
+ return ei1.t_index < ei2.t_index;
+ };
+ std::sort(eis.begin(), eis.end(), compare);
+ std::sort(e_infos2.begin() + v_info2.start,
+ e_infos2.begin() + v_info2.start + v_info2.count, compare);
+ for (size_t ei = 0; ei < v_info.count; ++ei) {
+ if (eis[ei].t_index != e_infos2[ei + v_info2.start].t_index) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+#endif /* NDEBUG */
diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp
index d337fdc2d..01e314dfe 100644
--- a/src/libslic3r/Technologies.hpp
+++ b/src/libslic3r/Technologies.hpp
@@ -47,6 +47,8 @@
#define ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER (1 && ENABLE_2_4_0_ALPHA0)
// Enable drawing contours, at cut level, for sinking volumes
#define ENABLE_SINKING_CONTOURS (1 && ENABLE_2_4_0_ALPHA0)
+// Enable implementation of retract acceleration in gcode processor
+#define ENABLE_RETRACT_ACCELERATION (1 && ENABLE_2_4_0_ALPHA0)
// Enable the fix for exporting and importing to/from 3mf file of mirrored volumes
#define ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT (1 && ENABLE_2_4_0_ALPHA0)
diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp
index 360a8b14e..d4baabc97 100644
--- a/src/libslic3r/TriangleMesh.cpp
+++ b/src/libslic3r/TriangleMesh.cpp
@@ -957,6 +957,48 @@ int its_compactify_vertices(indexed_triangle_set &its, bool shrink_to_fit)
return removed;
}
+bool its_store_triangle(const indexed_triangle_set &its,
+ const char * obj_filename,
+ size_t triangle_index)
+{
+ if (its.indices.size() <= triangle_index) return false;
+ Vec3i t = its.indices[triangle_index];
+ indexed_triangle_set its2;
+ its2.indices = {{0, 1, 2}};
+ its2.vertices = {its.vertices[t[0]], its.vertices[t[1]],
+ its.vertices[t[2]]};
+ return its_write_obj(its2, obj_filename);
+}
+
+bool its_store_triangles(const indexed_triangle_set &its,
+ const char * obj_filename,
+ const std::vector & triangles)
+{
+ indexed_triangle_set its2;
+ its2.vertices.reserve(triangles.size() * 3);
+ its2.indices.reserve(triangles.size());
+ std::map vertex_map;
+ for (auto ti : triangles) {
+ if (its.indices.size() <= ti) return false;
+ Vec3i t = its.indices[ti];
+ Vec3i new_t;
+ for (size_t i = 0; i < 3; ++i) {
+ size_t vi = t[i];
+ auto it = vertex_map.find(vi);
+ if (it != vertex_map.end()) {
+ new_t[i] = it->second;
+ continue;
+ }
+ size_t new_vi = its2.vertices.size();
+ its2.vertices.push_back(its.vertices[vi]);
+ vertex_map[vi] = new_vi;
+ new_t[i] = new_vi;
+ }
+ its2.indices.push_back(new_t);
+ }
+ return its_write_obj(its2, obj_filename);
+}
+
void its_shrink_to_fit(indexed_triangle_set &its)
{
its.indices.shrink_to_fit();
diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp
index c8c7e0dd7..b7a1bebb1 100644
--- a/src/libslic3r/TriangleMesh.hpp
+++ b/src/libslic3r/TriangleMesh.hpp
@@ -140,6 +140,10 @@ int its_remove_degenerate_faces(indexed_triangle_set &its, bool shrink_to_fit =
// Remove vertices, which none of the faces references. Return number of freed vertices.
int its_compactify_vertices(indexed_triangle_set &its, bool shrink_to_fit = true);
+// store part of index triangle set
+bool its_store_triangle(const indexed_triangle_set &its, const char *obj_filename, size_t triangle_index);
+bool its_store_triangles(const indexed_triangle_set &its, const char *obj_filename, const std::vector& triangles);
+
std::vector its_split(const indexed_triangle_set &its);
bool its_is_splittable(const indexed_triangle_set &its);
diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp
index 485a7fcf2..6178bcec8 100644
--- a/src/libslic3r/TriangleSelector.cpp
+++ b/src/libslic3r/TriangleSelector.cpp
@@ -4,7 +4,7 @@
#include
#ifndef NDEBUG
- #define EXPENSIVE_DEBUG_CHECKS
+// #define EXPENSIVE_DEBUG_CHECKS
#endif // NDEBUG
namespace Slic3r {
diff --git a/src/miniz/CMakeLists.txt b/src/miniz/CMakeLists.txt
index ab27067d0..a664f7460 100644
--- a/src/miniz/CMakeLists.txt
+++ b/src/miniz/CMakeLists.txt
@@ -1,5 +1,5 @@
+cmake_minimum_required(VERSION 2.8.12)
project(miniz)
-cmake_minimum_required(VERSION 2.6)
add_library(miniz INTERFACE)
diff --git a/src/semver/CMakeLists.txt b/src/semver/CMakeLists.txt
index c273121d4..4b61a7456 100644
--- a/src/semver/CMakeLists.txt
+++ b/src/semver/CMakeLists.txt
@@ -1,5 +1,5 @@
+cmake_minimum_required(VERSION 2.8.12)
project(semver)
-cmake_minimum_required(VERSION 2.6)
add_library(semver STATIC
semver.c
diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp
index 9c0341ff4..7a9fdc388 100644
--- a/src/slic3r/GUI/3DScene.cpp
+++ b/src/slic3r/GUI/3DScene.cpp
@@ -595,7 +595,7 @@ bool GLVolume::is_sinking() const
bool GLVolume::is_below_printbed() const
{
- return transformed_convex_hull_bounding_box().max(2) < 0.0;
+ return transformed_convex_hull_bounding_box().max.z() < 0.0;
}
#if ENABLE_SINKING_CONTOURS
diff --git a/src/slic3r/GUI/AboutDialog.cpp b/src/slic3r/GUI/AboutDialog.cpp
index fdbc1a6d3..72f1421f6 100644
--- a/src/slic3r/GUI/AboutDialog.cpp
+++ b/src/slic3r/GUI/AboutDialog.cpp
@@ -195,7 +195,7 @@ void CopyrightsDialog::on_dpi_changed(const wxRect &suggested_rect)
void CopyrightsDialog::onLinkClicked(wxHtmlLinkEvent &event)
{
- wxLaunchDefaultBrowser(event.GetLinkInfo().GetHref());
+ wxGetApp().open_browser_with_warning_dialog(event.GetLinkInfo().GetHref());
event.Skip(false);
}
@@ -344,7 +344,7 @@ void AboutDialog::on_dpi_changed(const wxRect &suggested_rect)
void AboutDialog::onLinkClicked(wxHtmlLinkEvent &event)
{
- wxLaunchDefaultBrowser(event.GetLinkInfo().GetHref());
+ wxGetApp().open_browser_with_warning_dialog(event.GetLinkInfo().GetHref());
event.Skip(false);
}
diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp
index 47d283177..54b87786a 100644
--- a/src/slic3r/GUI/ConfigManipulation.cpp
+++ b/src/slic3r/GUI/ConfigManipulation.cpp
@@ -268,7 +268,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
toggle_field("gap_fill_speed", have_perimeters);
for (auto el : { "top_infill_extrusion_width", "top_solid_infill_speed" })
- toggle_field(el, has_top_solid_infill);
+ toggle_field(el, has_top_solid_infill || (has_spiral_vase && has_bottom_solid_infill));
bool have_default_acceleration = config->opt_float("default_acceleration") > 0;
for (auto el : { "perimeter_acceleration", "infill_acceleration",
diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp
index 36a4f6e5c..7d7ed08a2 100644
--- a/src/slic3r/GUI/DoubleSlider.cpp
+++ b/src/slic3r/GUI/DoubleSlider.cpp
@@ -2028,7 +2028,7 @@ void Control::show_cog_icon_context_menu()
append_menu_item(&menu, wxID_ANY, _L("Set extruder sequence for the entire print"), "",
[this](wxCommandEvent&) { edit_extruder_sequence(); }, "", &menu);
- if (m_mode != MultiExtruder && m_draw_mode == dmRegular)
+ if (GUI::wxGetApp().is_editor() && m_mode != MultiExtruder && m_draw_mode == dmRegular)
append_menu_item(&menu, wxID_ANY, _L("Set auto color changes"), "",
[this](wxCommandEvent&) { auto_color_change(); }, "", &menu);
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 227e5ae4e..ee1d3b14a 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -2239,6 +2239,8 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(wxGetApp().plater()->get_camera());
m_dirty |= mouse3d_controller_applied;
m_dirty |= wxGetApp().plater()->get_notification_manager()->update_notifications(*this);
+ auto gizmo = wxGetApp().plater()->canvas3D()->get_gizmos_manager().get_current();
+ if (gizmo != nullptr) m_dirty |= gizmo->update_items_state();
if (!m_dirty)
return;
@@ -2780,11 +2782,10 @@ void GLCanvas3D::on_timer(wxTimerEvent& evt)
void GLCanvas3D::on_render_timer(wxTimerEvent& evt)
{
- // no need to do anything here
- // right after this event is recieved, idle event is fired
-
- //m_dirty = true;
- //wxWakeUpIdle();
+ // no need to wake up idle
+ // right after this event, idle event is fired
+ // m_dirty = true;
+ // wxWakeUpIdle();
}
@@ -2802,21 +2803,15 @@ void GLCanvas3D::schedule_extra_frame(int miliseconds)
return;
}
}
- // Start timer
- int64_t now = timestamp_now();
+ int remaining_time = m_render_timer.GetInterval();
// Timer is not running
- if (! m_render_timer.IsRunning()) {
- m_extra_frame_requested_delayed = miliseconds;
+ if (!m_render_timer.IsRunning()) {
m_render_timer.StartOnce(miliseconds);
- m_render_timer_start = now;
// Timer is running - restart only if new period is shorter than remaning period
} else {
- const int64_t remaining_time = (m_render_timer_start + m_extra_frame_requested_delayed) - now;
if (miliseconds + 20 < remaining_time) {
m_render_timer.Stop();
- m_extra_frame_requested_delayed = miliseconds;
m_render_timer.StartOnce(miliseconds);
- m_render_timer_start = now;
}
}
}
@@ -4438,13 +4433,7 @@ bool GLCanvas3D::_init_main_toolbar()
}
// init arrow
BackgroundTexture::Metadata arrow_data;
- arrow_data.filename = "toolbar_arrow.png";
-// arrow_data.filename = "toolbar_arrow.svg";
- //arrow_data.left = 16;
- //arrow_data.top = 16;
- //arrow_data.right = 16;
- //arrow_data.bottom = 16;
-
+ arrow_data.filename = "toolbar_arrow.svg";
arrow_data.left = 0;
arrow_data.top = 0;
arrow_data.right = 0;
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index d9cd55e35..ba8430c07 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -463,15 +463,13 @@ private:
std::string m_sidebar_field;
// when true renders an extra frame by not resetting m_dirty to false
// see request_extra_frame()
- bool m_extra_frame_requested;
- int m_extra_frame_requested_delayed { std::numeric_limits::max() };
+ bool m_extra_frame_requested;
bool m_event_handlers_bound{ false };
GLVolumeCollection m_volumes;
GCodeViewer m_gcode_viewer;
RenderTimer m_render_timer;
- int64_t m_render_timer_start;
Selection m_selection;
const DynamicPrintConfig* m_config;
diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp
index 243031847..515b6ed3a 100644
--- a/src/slic3r/GUI/GLToolbar.cpp
+++ b/src/slic3r/GUI/GLToolbar.cpp
@@ -193,10 +193,9 @@ bool GLToolbar::init_arrow(const BackgroundTexture::Metadata& arrow_texture)
std::string path = resources_dir() + "/icons/";
bool res = false;
- if (!arrow_texture.filename.empty())
- res = m_arrow_texture.texture.load_from_file(path + arrow_texture.filename, false, GLTexture::SingleThreaded, false);
-// res = m_arrow_texture.texture.load_from_svg_file(path + arrow_texture.filename, false, true, false, 100);
-
+ if (!arrow_texture.filename.empty()) {
+ res = m_arrow_texture.texture.load_from_svg_file(path + arrow_texture.filename, false, false, false, 1000);
+ }
if (res)
m_arrow_texture.metadata = arrow_texture;
@@ -1176,19 +1175,22 @@ void GLToolbar::render_arrow(const GLCanvas3D& parent, GLToolbarItem* highlighte
float right = left + scaled_icons_size;
unsigned int tex_id = m_arrow_texture.texture.get_id();
+ // width and height of icon arrow is pointing to
float tex_width = (float)m_icons_texture.get_width();
float tex_height = (float)m_icons_texture.get_height();
+ // arrow width and height
+ float arr_tex_width = (float)m_arrow_texture.texture.get_width();
+ float arr_tex_height = (float)m_arrow_texture.texture.get_height();
+ if ((tex_id != 0) && (arr_tex_width > 0) && (arr_tex_height > 0)) {
+ float inv_tex_width = (arr_tex_width != 0.0f) ? 1.0f / arr_tex_width : 0.0f;
+ float inv_tex_height = (arr_tex_height != 0.0f) ? 1.0f / arr_tex_height : 0.0f;
- if ((tex_id != 0) && (tex_width > 0) && (tex_height > 0)) {
- float inv_tex_width = (tex_width != 0.0f) ? 1.0f / tex_width : 0.0f;
- float inv_tex_height = (tex_height != 0.0f) ? 1.0f / tex_height : 0.0f;
-
- float internal_left = left + border - scaled_icons_size / 2; // add half scaled_icons_size for huge arrow
- float internal_right = right - border + scaled_icons_size / 2;
+ float internal_left = left + border - scaled_icons_size * 1.5f; // add scaled_icons_size for huge arrow
+ float internal_right = right - border + scaled_icons_size * 1.5f;
float internal_top = top - border;
// bottom is not moving and should be calculated from arrow texture sides ratio
float arrow_sides_ratio = (float)m_arrow_texture.texture.get_height() / (float)m_arrow_texture.texture.get_width();
- float internal_bottom = internal_top - (internal_right - internal_left) * arrow_sides_ratio;
+ float internal_bottom = internal_top - (internal_right - internal_left) * arrow_sides_ratio ;
float internal_left_uv = (float)m_arrow_texture.metadata.left * inv_tex_width;
float internal_right_uv = 1.0f - (float)m_arrow_texture.metadata.right * inv_tex_width;
diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp
index 06d805eeb..102026a0c 100644
--- a/src/slic3r/GUI/GUI_App.cpp
+++ b/src/slic3r/GUI/GUI_App.cpp
@@ -87,6 +87,11 @@
#include
#endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG
+// Needed for forcing menu icons back under gtk2 and gtk3
+#if defined(__WXGTK20__) || defined(__WXGTK3__)
+ #include
+#endif
+
namespace Slic3r {
namespace GUI {
@@ -799,6 +804,14 @@ bool GUI_App::OnInit()
bool GUI_App::on_init_inner()
{
+ // Forcing back menu icons under gtk2 and gtk3. Solution is based on:
+ // https://docs.gtk.org/gtk3/class.Settings.html
+ // see also https://docs.wxwidgets.org/3.0/classwx_menu_item.html#a2b5d6bcb820b992b1e4709facbf6d4fb
+ // TODO: Find workaround for GTK4
+#if defined(__WXGTK20__) || defined(__WXGTK3__)
+ g_object_set (gtk_settings_get_default (), "gtk-menu-images", TRUE, NULL);
+#endif
+
// Verify resources path
const wxString resources_dir = from_u8(Slic3r::resources_dir());
wxCHECK_MSG(wxDirExists(resources_dir), false,
@@ -2314,7 +2327,7 @@ wxString GUI_App::current_language_code_safe() const
void GUI_App::open_web_page_localized(const std::string &http_address)
{
- wxLaunchDefaultBrowser(http_address + "&lng=" + this->current_language_code_safe());
+ open_browser_with_warning_dialog(http_address + "&lng=" + this->current_language_code_safe());
}
bool GUI_App::run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage start_page)
@@ -2512,6 +2525,23 @@ void GUI_App::check_updates(const bool verbose)
}
}
+bool GUI_App::open_browser_with_warning_dialog(const wxString& url, int flags/* = 0*/)
+{
+ bool launch = true;
+
+ if (get_app_config()->get("suppress_hyperlinks").empty()) {
+ wxRichMessageDialog dialog(nullptr, _L("Should we open this hyperlink in your default browser?"), _L("PrusaSlicer: Open hyperlink"), wxICON_QUESTION | wxYES_NO);
+ dialog.ShowCheckBox(_L("Remember my choice"));
+ int answer = dialog.ShowModal();
+ launch = answer == wxID_YES;
+ get_app_config()->set("suppress_hyperlinks", dialog.IsCheckBoxChecked() ? (answer == wxID_NO ? "1" : "0") : "");
+ }
+ if (launch)
+ launch = get_app_config()->get("suppress_hyperlinks") != "1";
+
+ return launch && wxLaunchDefaultBrowser(url, flags);
+}
+
// static method accepting a wxWindow object as first parameter
// void warning_catcher{
// my($self, $message_dialog) = @_;
diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp
index a140247e0..1d8c2fcf7 100644
--- a/src/slic3r/GUI/GUI_App.hpp
+++ b/src/slic3r/GUI/GUI_App.hpp
@@ -261,7 +261,8 @@ public:
void open_preferences(size_t open_on_tab = 0);
virtual bool OnExceptionInMainLoop() override;
-
+ // Calls wxLaunchDefaultBrowser if user confirms in dialog.
+ bool open_browser_with_warning_dialog(const wxString& url, int flags = 0);
#ifdef __APPLE__
void OSXStoreOpenFiles(const wxArrayString &files) override;
// wxWidgets override to get an event on open files.
diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp
index 0c555454d..d4030d41f 100644
--- a/src/slic3r/GUI/GUI_ObjectList.cpp
+++ b/src/slic3r/GUI/GUI_ObjectList.cpp
@@ -1049,7 +1049,7 @@ void ObjectList::key_event(wxKeyEvent& event)
|| event.GetKeyCode() == WXK_BACK
#endif //__WXOSX__
) {
- remove();
+ wxGetApp().plater()->remove_selected();
}
else if (event.GetKeyCode() == WXK_F5)
wxGetApp().plater()->reload_all_from_disk();
@@ -1920,16 +1920,15 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con
if (vol->is_model_part())
++solid_cnt;
if (volume->is_model_part() && solid_cnt == 1) {
- Slic3r::GUI::show_error(nullptr, _(L("From Object List You can't delete the last solid part from object.")));
+ Slic3r::GUI::show_error(nullptr, _L("From Object List You can't delete the last solid part from object."));
return false;
}
- take_snapshot(_(L("Delete Subobject")));
+ take_snapshot(_L("Delete Subobject"));
object->delete_volume(idx);
- if (object->volumes.size() == 1)
- {
+ if (object->volumes.size() == 1) {
const auto last_volume = object->volumes[0];
if (!last_volume->config.empty()) {
object->config.apply(last_volume->config);
@@ -1948,11 +1947,11 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con
}
else if (type == itInstance) {
if (object->instances.size() == 1) {
- Slic3r::GUI::show_error(nullptr, _(L("Last instance of an object cannot be deleted.")));
+ Slic3r::GUI::show_error(nullptr, _L("Last instance of an object cannot be deleted."));
return false;
}
- take_snapshot(_(L("Delete Instance")));
+ take_snapshot(_L("Delete Instance"));
object->delete_instance(idx);
}
else
@@ -2731,8 +2730,9 @@ void ObjectList::delete_from_model_and_list(const std::vector& it
return;
m_prevent_list_events = true;
- for (std::vector::const_reverse_iterator item = items_for_delete.rbegin(); item != items_for_delete.rend(); ++item)
- {
+
+ std::set modified_objects_ids;
+ for (std::vector::const_reverse_iterator item = items_for_delete.rbegin(); item != items_for_delete.rend(); ++item) {
if (!(item->type&(itObject | itVolume | itInstance)))
continue;
if (item->type&itObject) {
@@ -2742,8 +2742,7 @@ void ObjectList::delete_from_model_and_list(const std::vector& it
else {
if (!del_subobject_from_object(item->obj_idx, item->sub_obj_idx, item->type))
continue;
- if (item->type&itVolume)
- {
+ if (item->type&itVolume) {
m_objects_model->Delete(m_objects_model->GetItemByVolumeId(item->obj_idx, item->sub_obj_idx));
ModelObject* obj = object(item->obj_idx);
if (obj->volumes.size() == 1) {
@@ -2761,7 +2760,14 @@ void ObjectList::delete_from_model_and_list(const std::vector& it
else
m_objects_model->Delete(m_objects_model->GetItemByInstanceId(item->obj_idx, item->sub_obj_idx));
}
+
+ modified_objects_ids.insert(static_cast(item->obj_idx));
}
+
+ for (size_t id : modified_objects_ids) {
+ update_info_items(id);
+ }
+
m_prevent_list_events = true;
part_selection_changed();
}
@@ -4017,7 +4023,23 @@ void ObjectList::fix_through_netfabb()
void ObjectList::simplify()
{
- GLGizmosManager& gizmos_mgr = wxGetApp().plater()->canvas3D()->get_gizmos_manager();
+ auto plater = wxGetApp().plater();
+ GLGizmosManager& gizmos_mgr = plater->canvas3D()->get_gizmos_manager();
+
+ // Do not simplify when a gizmo is open. There might be issues with updates
+ // and what is worse, the snapshot time would refer to the internal stack.
+ auto current_type = gizmos_mgr.get_current_type();
+ if (current_type == GLGizmosManager::Simplify) {
+ // close first
+ gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify);
+ }else if (current_type != GLGizmosManager::Undefined) {
+ plater->get_notification_manager()->push_notification(
+ NotificationType::CustomSupportsAndSeamRemovedAfterRepair,
+ NotificationManager::NotificationLevel::RegularNotification,
+ _u8L("ERROR: Please close all manipulators available from "
+ "the left toolbar before start simplify the mesh."));
+ return;
+ }
gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify);
}
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp
index 92c03cb75..9cda6d75b 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp
@@ -85,6 +85,7 @@ GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, u
, m_dragging(false)
, m_imgui(wxGetApp().imgui())
, m_first_input_window_render(true)
+ , m_dirty(false)
{
m_base_color = DEFAULT_BASE_COLOR;
m_drag_color = DEFAULT_DRAG_COLOR;
@@ -154,6 +155,13 @@ void GLGizmoBase::update(const UpdateData& data)
on_update(data);
}
+bool GLGizmoBase::update_items_state()
+{
+ bool res = m_dirty;
+ m_dirty = false;
+ return res;
+};
+
std::array GLGizmoBase::picking_color_component(unsigned int id) const
{
static const float INV_255 = 1.0f / 255.0f;
@@ -209,6 +217,10 @@ std::string GLGizmoBase::format(float value, unsigned int decimals) const
return Slic3r::string_printf("%.*f", decimals, value);
}
+void GLGizmoBase::set_dirty() {
+ m_dirty = true;
+}
+
void GLGizmoBase::render_input_window(float x, float y, float bottom_limit)
{
on_render_input_window(x, y, bottom_limit);
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp
index 2fb9c296a..8b033ce73 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp
@@ -154,6 +154,9 @@ public:
void update(const UpdateData& data);
+ // returns True when Gizmo changed its state
+ bool update_items_state();
+
void render() { m_tooltip.clear(); on_render(); }
void render_for_picking() { on_render_for_picking(); }
void render_input_window(float x, float y, float bottom_limit);
@@ -187,6 +190,13 @@ protected:
void render_grabbers_for_picking(const BoundingBoxf3& box) const;
std::string format(float value, unsigned int decimals) const;
+
+ // Mark gizmo as dirty to Re-Render when idle()
+ void set_dirty();
+private:
+ // Flag for dirty visible state of Gizmo
+ // When True then need new rendering
+ bool m_dirty;
};
// Produce an alpha channel checksum for the red green blue components. The alpha channel may then be used to verify, whether the rgb components
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp
index 3d8be9eff..a69319d3b 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp
@@ -630,7 +630,11 @@ void TriangleSelectorMmGui::render(ImGuiWrapper *imgui)
auto *contour_shader = wxGetApp().get_shader("mm_contour");
contour_shader->start_using();
+
+ glsafe(::glDepthFunc(GL_LEQUAL));
m_gizmo_scene.render_contour();
+ glsafe(::glDepthFunc(GL_LESS));
+
contour_shader->stop_using();
}
@@ -725,10 +729,6 @@ void GLMmSegmentationGizmo3DScene::render(size_t triangle_indices_idx) const
assert(this->vertices_VBO_id != 0);
assert(this->triangle_indices_VBO_ids[triangle_indices_idx] != 0);
- ScopeGuard offset_fill_guard([]() { glsafe(::glDisable(GL_POLYGON_OFFSET_FILL)); });
- glsafe(::glEnable(GL_POLYGON_OFFSET_FILL));
- glsafe(::glPolygonOffset(5.0, 5.0));
-
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_VBO_id));
glsafe(::glVertexPointer(3, GL_FLOAT, 3 * sizeof(float), (const void*)(0 * sizeof(float))));
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp
index 694eeadd4..5ce0064db 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp
@@ -1,34 +1,38 @@
-// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
#include "GLGizmoSimplify.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/GUI_App.hpp"
-#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/GUI_ObjectManipulation.hpp"
#include "slic3r/GUI/GUI_ObjectList.hpp"
+#include "slic3r/GUI/NotificationManager.hpp"
+#include "slic3r/GUI/Plater.hpp"
#include "libslic3r/AppConfig.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/QuadricEdgeCollapse.hpp"
-#include
-#include
-
namespace Slic3r::GUI {
GLGizmoSimplify::GLGizmoSimplify(GLCanvas3D & parent,
const std::string &icon_filename,
unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, -1)
- , state(State::settings)
- , is_valid_result(false)
- , progress(0)
- , volume(nullptr)
- , obj_index(0)
- , need_reload(false)
+ , m_state(State::settings)
+ , m_is_valid_result(false)
+ , m_exist_preview(false)
+ , m_progress(0)
+ , m_volume(nullptr)
+ , m_obj_index(0)
+ , m_need_reload(false)
+
+ , tr_mesh_name(_u8L("Mesh name"))
+ , tr_triangles(_u8L("Triangles"))
+ , tr_preview(_u8L("Preview"))
+ , tr_detail_level(_u8L("Detail level"))
+ , tr_decimate_ratio(_u8L("Decimate ratio"))
{}
GLGizmoSimplify::~GLGizmoSimplify() {
- state = State::canceling;
- if (worker.joinable()) worker.join();
+ m_state = State::canceling;
+ if (m_worker.joinable()) m_worker.join();
}
bool GLGizmoSimplify::on_init()
@@ -38,7 +42,6 @@ bool GLGizmoSimplify::on_init()
return true;
}
-
std::string GLGizmoSimplify::on_get_name() const
{
return (_L("Simplify")).ToUTF8().data();
@@ -49,14 +52,7 @@ void GLGizmoSimplify::on_render_for_picking() {}
void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limit)
{
- const int min_triangle_count = 4; // tetrahedron
- const int max_char_in_name = 25;
create_gui_cfg();
-
- int flag = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize |
- ImGuiWindowFlags_NoCollapse;
- m_imgui->begin(on_get_name(), flag);
-
const Selection &selection = m_parent.get_selection();
int object_idx = selection.get_object_idx();
ModelObject *obj = wxGetApp().plater()->model().objects[object_idx];
@@ -64,159 +60,196 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
// Check selection of new volume
// Do not reselect object when processing
- if (act_volume != volume && state == State::settings) {
- obj_index = object_idx; // to remember correct object
- volume = act_volume;
- original_its = {};
- const TriangleMesh &tm = volume->mesh();
- c.wanted_percent = 50.; // default value
- c.update_percent(tm.its.indices.size());
- is_valid_result = false;
- // set window position
- ImVec2 pos = ImGui::GetMousePos();
- pos.x -= gui_cfg->window_offset;
- pos.y -= gui_cfg->window_offset;
- ImGui::SetWindowPos(pos, ImGuiCond_Always);
+ if (act_volume != m_volume && m_state == State::settings) {
+ bool change_window_position = (m_volume == nullptr);
+ // select different model
+ if (m_volume != nullptr && m_original_its.has_value()) {
+ set_its(*m_original_its);
+ }
+
+ m_obj_index = object_idx; // to remember correct object
+ m_volume = act_volume;
+ m_original_its = {};
+ m_configuration.decimate_ratio = 50.; // default value
+ m_configuration.fix_count_by_ratio(m_volume->mesh().its.indices.size());
+ m_is_valid_result = false;
+ m_exist_preview = false;
+
+ if (change_window_position) {
+ ImVec2 pos = ImGui::GetMousePos();
+ pos.x -= m_gui_cfg->window_offset_x;
+ pos.y -= m_gui_cfg->window_offset_y;
+ // minimal top left value
+ ImVec2 tl(m_gui_cfg->window_padding, m_gui_cfg->window_padding);
+ if (pos.x < tl.x) pos.x = tl.x;
+ if (pos.y < tl.y) pos.y = tl.y;
+ // maximal bottom right value
+ auto parent_size = m_parent.get_canvas_size();
+ ImVec2 br(
+ parent_size.get_width() - (2 * m_gui_cfg->window_offset_x + m_gui_cfg->window_padding),
+ parent_size.get_height() - (2 * m_gui_cfg->window_offset_y + m_gui_cfg->window_padding));
+ if (pos.x > br.x) pos.x = br.x;
+ if (pos.y > br.y) pos.y = br.y;
+ ImGui::SetNextWindowPos(pos, ImGuiCond_Always);
+ }
}
- size_t triangle_count = volume->mesh().its.indices.size();
- // already reduced mesh
- if (original_its.has_value())
- triangle_count = original_its->indices.size();
+ int flag = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize |
+ ImGuiWindowFlags_NoCollapse;
+ m_imgui->begin(on_get_name(), flag);
- m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Mesh name") + ":");
- ImGui::SameLine(gui_cfg->top_left_width);
- std::string name = volume->name;
- if (name.length() > max_char_in_name)
- name = name.substr(0, max_char_in_name-3) + "...";
+ size_t triangle_count = m_volume->mesh().its.indices.size();
+ // already reduced mesh
+ if (m_original_its.has_value())
+ triangle_count = m_original_its->indices.size();
+
+ m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, tr_mesh_name + ":");
+ ImGui::SameLine(m_gui_cfg->top_left_width);
+ std::string name = m_volume->name;
+ if (name.length() > m_gui_cfg->max_char_in_name)
+ name = name.substr(0, m_gui_cfg->max_char_in_name - 3) + "...";
m_imgui->text(name);
- m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Triangles") + ":");
- ImGui::SameLine(gui_cfg->top_left_width);
+ m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, tr_triangles + ":");
+ ImGui::SameLine(m_gui_cfg->top_left_width);
m_imgui->text(std::to_string(triangle_count));
+ /*
+ m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, tr_preview + ":");
+ ImGui::SameLine(m_gui_cfg->top_left_width);
+ if (m_exist_preview) {
+ m_imgui->text(std::to_string(m_volume->mesh().its.indices.size()));
+ } else {
+ m_imgui->text("---");
+ }*/
ImGui::Separator();
- ImGui::Text(_L("Limit by triangles").c_str());
- ImGui::SameLine(gui_cfg->bottom_left_width);
- // First initialization + fix triangle count
- if (m_imgui->checkbox("##UseCount", c.use_count)) {
- if (!c.use_count) c.use_error = true;
- is_valid_result = false;
+ if(ImGui::RadioButton("##use_error", !m_configuration.use_count)) {
+ m_is_valid_result = false;
+ m_configuration.use_count = !m_configuration.use_count;
+ }
+ ImGui::SameLine();
+ m_imgui->disabled_begin(m_configuration.use_count);
+ ImGui::Text("%s", tr_detail_level.c_str());
+ std::vector reduce_captions = {
+ static_cast(_u8L("Extra high")),
+ static_cast(_u8L("High")),
+ static_cast(_u8L("Medium")),
+ static_cast(_u8L("Low")),
+ static_cast(_u8L("Extra low"))
+ };
+ ImGui::SameLine(m_gui_cfg->bottom_left_width);
+ ImGui::SetNextItemWidth(m_gui_cfg->input_width);
+ static int reduction = 2;
+ if(ImGui::SliderInt("##ReductionLevel", &reduction, 0, 4, reduce_captions[reduction].c_str())) {
+ m_is_valid_result = false;
+ if (reduction < 0) reduction = 0;
+ if (reduction > 4) reduction = 4;
+ switch (reduction) {
+ case 0: m_configuration.max_error = 1e-3f; break;
+ case 1: m_configuration.max_error = 1e-2f; break;
+ case 2: m_configuration.max_error = 0.1f; break;
+ case 3: m_configuration.max_error = 0.5f; break;
+ case 4: m_configuration.max_error = 1.f; break;
+ }
+ }
+ m_imgui->disabled_end(); // !use_count
+
+ if (ImGui::RadioButton("##use_count", m_configuration.use_count)) {
+ m_is_valid_result = false;
+ m_configuration.use_count = !m_configuration.use_count;
+ }
+ ImGui::SameLine();
+
+ // show preview result triangle count (percent)
+ if (m_need_reload && !m_configuration.use_count) {
+ m_configuration.wanted_count = static_cast(m_volume->mesh().its.indices.size());
+ m_configuration.decimate_ratio =
+ (1.0f - (m_configuration.wanted_count / (float) triangle_count)) * 100.f;
}
- m_imgui->disabled_begin(!c.use_count);
- ImGui::Text(_L("Triangle count").c_str());
- ImGui::SameLine(gui_cfg->bottom_left_width);
- int wanted_count = c.wanted_count;
- ImGui::SetNextItemWidth(gui_cfg->input_width);
- if (ImGui::SliderInt("##triangle_count", &wanted_count, min_triangle_count, triangle_count, "%d")) {
- c.wanted_count = static_cast(wanted_count);
- if (c.wanted_count < min_triangle_count)
- c.wanted_count = min_triangle_count;
- if (c.wanted_count > triangle_count)
- c.wanted_count = triangle_count;
- c.update_count(triangle_count);
- is_valid_result = false;
+ m_imgui->disabled_begin(!m_configuration.use_count);
+ ImGui::Text("%s", tr_decimate_ratio.c_str());
+ ImGui::SameLine(m_gui_cfg->bottom_left_width);
+ ImGui::SetNextItemWidth(m_gui_cfg->input_width);
+ const char * format = (m_configuration.decimate_ratio > 10)? "%.0f %%":
+ ((m_configuration.decimate_ratio > 1)? "%.1f %%":"%.2f %%");
+ if (ImGui::SliderFloat("##decimate_ratio", &m_configuration.decimate_ratio, 0.f, 100.f, format)) {
+ m_is_valid_result = false;
+ if (m_configuration.decimate_ratio < 0.f)
+ m_configuration.decimate_ratio = 0.01f;
+ if (m_configuration.decimate_ratio > 100.f)
+ m_configuration.decimate_ratio = 100.f;
+ m_configuration.fix_count_by_ratio(triangle_count);
}
- ImGui::Text(_L("Ratio").c_str());
- ImGui::SameLine(gui_cfg->bottom_left_width);
- ImGui::SetNextItemWidth(gui_cfg->input_small_width);
- const char * precision = (c.wanted_percent > 10)? "%.0f": ((c.wanted_percent > 1)? "%.1f":"%.2f");
- float step = (c.wanted_percent > 10)? 1.f: ((c.wanted_percent > 1)? 0.1f : 0.01f);
- if (ImGui::InputFloat("%", &c.wanted_percent, step, 10*step, precision)) {
- if (c.wanted_percent > 100.f) c.wanted_percent = 100.f;
- c.update_percent(triangle_count);
- if (c.wanted_count < min_triangle_count) {
- c.wanted_count = min_triangle_count;
- c.update_count(triangle_count);
- }
- is_valid_result = false;
- }
- m_imgui->disabled_end(); // use_count
ImGui::NewLine();
- ImGui::Text(_L("Limit by error").c_str());
- ImGui::SameLine(gui_cfg->bottom_left_width);
- if (m_imgui->checkbox("##UseError", c.use_error)) {
- if (!c.use_error) c.use_count = true;
- is_valid_result = false;
- }
+ ImGui::SameLine(m_gui_cfg->bottom_left_width);
+ ImGui::Text(_L("%d triangles").c_str(), m_configuration.wanted_count);
+ m_imgui->disabled_end(); // use_count
- m_imgui->disabled_begin(!c.use_error);
- ImGui::Text(_L("Max. error").c_str());
- ImGui::SameLine(gui_cfg->bottom_left_width);
- ImGui::SetNextItemWidth(gui_cfg->input_small_width);
- if (ImGui::InputFloat("##maxError", &c.max_error, 0.01f, .1f, "%.2f")) {
- if (c.max_error < 0.f) c.max_error = 0.f;
- is_valid_result = false;
- }
- m_imgui->disabled_end(); // use_error
-
- if (state == State::settings) {
+ if (m_state == State::settings) {
if (m_imgui->button(_L("Cancel"))) {
- if (original_its.has_value()) {
- set_its(*original_its);
- state = State::close_on_end;
+ if (m_original_its.has_value()) {
+ set_its(*m_original_its);
+ m_state = State::close_on_end;
} else {
close();
}
}
- ImGui::SameLine(gui_cfg->bottom_left_width);
+ ImGui::SameLine(m_gui_cfg->bottom_left_width);
if (m_imgui->button(_L("Preview"))) {
- state = State::simplifying;
+ m_state = State::preview;
// simplify but not aply on mesh
process();
}
ImGui::SameLine();
if (m_imgui->button(_L("Apply"))) {
- if (!is_valid_result) {
- state = State::close_on_end;
+ if (!m_is_valid_result) {
+ m_state = State::close_on_end;
process();
} else {
// use preview and close
+ if (m_exist_preview) {
+ // fix hollowing, sla support points, modifiers, ...
+ auto plater = wxGetApp().plater();
+ plater->changed_mesh(m_obj_index);
+ }
close();
}
}
} else {
- m_imgui->disabled_begin(state == State::canceling);
- if (m_imgui->button(_L("Cancel"))) state = State::canceling;
+ m_imgui->disabled_begin(m_state == State::canceling);
+ if (m_imgui->button(_L("Cancel"))) m_state = State::canceling;
m_imgui->disabled_end();
- ImGui::SameLine(gui_cfg->bottom_left_width);
+ ImGui::SameLine(m_gui_cfg->bottom_left_width);
// draw progress bar
char buf[32];
- sprintf(buf, L("Process %d / 100"), progress);
- ImGui::ProgressBar(progress / 100., ImVec2(gui_cfg->input_width, 0.f), buf);
+ sprintf(buf, L("Process %d / 100"), m_progress);
+ ImGui::ProgressBar(m_progress / 100., ImVec2(m_gui_cfg->input_width, 0.f), buf);
}
m_imgui->end();
- if (need_reload) {
- need_reload = false;
-
+ if (m_need_reload) {
+ m_need_reload = false;
+ bool close_on_end = (m_state == State::close_on_end);
// Reload visualization of mesh - change VBO, FBO on GPU
- m_parent.reload_scene(true); // deactivate gizmo??
- GLGizmosManager &gizmos_mgr = m_parent.get_gizmos_manager();
- gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify);
-
- if (state == State::close_on_end) {
+ m_parent.reload_scene(true);
+ // set m_state must be before close() !!!
+ m_state = State::settings;
+ if (close_on_end) {
// fix hollowing, sla support points, modifiers, ...
auto plater = wxGetApp().plater();
- plater->changed_mesh(obj_index); // deactivate gizmo??
- // changed_mesh cause close();
- //close();
+ plater->changed_mesh(m_obj_index);
+ close();
}
-
- // change from simplifying | aply
- state = State::settings;
// Fix warning icon in object list
- wxGetApp().obj_list()->update_item_error_icon(obj_index, -1);
+ wxGetApp().obj_list()->update_item_error_icon(m_obj_index, -1);
}
}
void GLGizmoSimplify::close() {
- volume = nullptr;
-
// close gizmo == open it again
GLGizmosManager &gizmos_mgr = m_parent.get_gizmos_manager();
gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify);
@@ -230,66 +263,60 @@ void GLGizmoSimplify::process()
const char* what() const throw() { return L("Model simplification has been canceled"); }
};
- if (!original_its.has_value())
- original_its = volume->mesh().its; // copy
+ if (!m_original_its.has_value())
+ m_original_its = m_volume->mesh().its; // copy
auto plater = wxGetApp().plater();
- plater->take_snapshot(_L("Simplify ") + volume->name);
- plater->clear_before_change_mesh(obj_index);
- progress = 0;
- if (worker.joinable()) worker.join();
- worker = std::thread([&]() {
+ plater->take_snapshot(_L("Simplify ") + m_volume->name);
+ plater->clear_before_change_mesh(m_obj_index);
+ m_progress = 0;
+ if (m_worker.joinable()) m_worker.join();
+ m_worker = std::thread([this]() {
// store original triangles
- uint32_t triangle_count = (c.use_count) ? c.wanted_count : 0;
- float max_error = (c.use_error) ? c.max_error : std::numeric_limits::max();
+ uint32_t triangle_count = (m_configuration.use_count) ? m_configuration.wanted_count : 0;
+ float max_error = (!m_configuration.use_count) ? m_configuration.max_error : std::numeric_limits::max();
std::function throw_on_cancel = [&]() {
- if (state == State::canceling) {
+ if (m_state == State::canceling) {
throw SimplifyCanceledException();
}
- };
- std::function statusfn = [&](int percent) {
- progress = percent;
- m_parent.schedule_extra_frame(0);
};
- indexed_triangle_set collapsed;
- if (last_error.has_value()) {
- // is chance to continue with last reduction
- const indexed_triangle_set &its = volume->mesh().its;
- uint32_t last_triangle_count = static_cast(its.indices.size());
- if ((!c.use_count || triangle_count <= last_triangle_count) &&
- (!c.use_error || c.max_error <= *last_error)) {
- collapsed = its; // small copy
- } else {
- collapsed = *original_its; // copy
- }
- } else {
- collapsed = *original_its; // copy
- }
+ std::function statusfn = [this](int percent) {
+ m_progress = percent;
+
+ // check max 4fps
+ static int64_t last = 0;
+ int64_t now = m_parent.timestamp_now();
+ if ((now - last) < 250) return;
+ last = now;
+
+ request_rerender();
+ };
+
+ indexed_triangle_set collapsed = *m_original_its; // copy
try {
its_quadric_edge_collapse(collapsed, triangle_count, &max_error, throw_on_cancel, statusfn);
set_its(collapsed);
- is_valid_result = true;
- last_error = max_error;
+ m_is_valid_result = true;
+ m_exist_preview = true;
} catch (SimplifyCanceledException &) {
- state = State::settings;
+ // set state out of main thread
+ m_state = State::settings;
}
- // need to render last status fn
- // without sleep it freezes until mouse move
- std::this_thread::sleep_for(std::chrono::milliseconds(50));
- m_parent.schedule_extra_frame(0);
+ // need to render last status fn to change bar graph to buttons
+ request_rerender();
});
}
void GLGizmoSimplify::set_its(indexed_triangle_set &its) {
auto tm = std::make_unique(its);
tm->repair();
- volume->set_mesh(std::move(tm));
- volume->set_new_unique_id();
- volume->get_object()->invalidate_bounding_box();
- need_reload = true;
+ m_volume->set_mesh(std::move(tm));
+ m_volume->set_new_unique_id();
+ m_volume->get_object()->invalidate_bounding_box();
+ m_need_reload = true;
}
bool GLGizmoSimplify::on_is_activable() const
@@ -297,26 +324,62 @@ bool GLGizmoSimplify::on_is_activable() const
return !m_parent.get_selection().is_empty();
}
-void GLGizmoSimplify::create_gui_cfg() {
- if (gui_cfg.has_value()) return;
+void GLGizmoSimplify::on_set_state()
+{
+ // Closing gizmo. e.g. selecting another one
+ if (GLGizmoBase::m_state == GLGizmoBase::Off) {
+ // refuse outgoing during simlification
+ if (m_state != State::settings) {
+ GLGizmoBase::m_state = GLGizmoBase::On;
+ auto notification_manager = wxGetApp().plater()->get_notification_manager();
+ notification_manager->push_notification(
+ NotificationType::CustomNotification,
+ NotificationManager::NotificationLevel::RegularNotification,
+ _u8L("ERROR: Wait until Simplification ends or Cancel process."));
+ return;
+ }
+
+ // revert preview
+ if (m_exist_preview) {
+ set_its(*m_original_its);
+ m_parent.reload_scene(true);
+ m_need_reload = false;
+ }
+
+ // invalidate selected model
+ m_volume = nullptr;
+ } else if (GLGizmoBase::m_state == GLGizmoBase::On) {
+ // when open by hyperlink it needs to show up
+ request_rerender();
+ }
+}
+
+void GLGizmoSimplify::create_gui_cfg() {
+ if (m_gui_cfg.has_value()) return;
int space_size = m_imgui->calc_text_size(":MM").x;
GuiCfg cfg;
- cfg.top_left_width = std::max(m_imgui->calc_text_size(_L("Mesh name")).x,
- m_imgui->calc_text_size(_L("Triangles")).x)
+ cfg.top_left_width = std::max(m_imgui->calc_text_size(tr_mesh_name).x,
+ m_imgui->calc_text_size(tr_triangles).x)
+ space_size;
+ const float radio_size = ImGui::GetFrameHeight();
cfg.bottom_left_width =
- std::max(
- std::max(m_imgui->calc_text_size(_L("Limit by triangles")).x,
- std::max(m_imgui->calc_text_size(_L("Triangle count")).x,
- m_imgui->calc_text_size(_L("Ratio")).x)),
- std::max(m_imgui->calc_text_size(_L("Limit by error")).x,
- m_imgui->calc_text_size(_L("Max. error")).x)) + space_size;
- cfg.input_width = cfg.bottom_left_width;
- cfg.input_small_width = cfg.input_width * 0.8;
- cfg.window_offset = cfg.input_width;
- gui_cfg = cfg;
+ std::max(m_imgui->calc_text_size(tr_detail_level).x,
+ m_imgui->calc_text_size(tr_decimate_ratio).x) +
+ space_size + radio_size;
+
+ cfg.input_width = cfg.bottom_left_width * 1.5;
+ cfg.window_offset_x = (cfg.bottom_left_width + cfg.input_width)/2;
+ cfg.window_offset_y = ImGui::GetTextLineHeightWithSpacing() * 5;
+ m_gui_cfg = cfg;
+}
+
+void GLGizmoSimplify::request_rerender() {
+ wxGetApp().plater()->CallAfter([this]() {
+ set_dirty();
+ m_parent.schedule_extra_frame(0);
+ });
}
} // namespace Slic3r::GUI
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp
index bd3637360..1b25c4ac9 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp
@@ -1,35 +1,23 @@
#ifndef slic3r_GLGizmoSimplify_hpp_
#define slic3r_GLGizmoSimplify_hpp_
+// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code,
+// which overrides our localization "L" macro.
#include "GLGizmoBase.hpp"
-#include "libslic3r/Model.hpp"
+#include "admesh/stl.h" // indexed_triangle_set
#include
#include
+#include
namespace Slic3r {
+
+class ModelVolume;
+
namespace GUI {
+
class GLGizmoSimplify : public GLGizmoBase
-{
- enum class State {
- settings,
- simplifying, // start processing
- canceling, // canceled
- successfull, // successful simplified
- close_on_end
- } state;
-
- bool is_valid_result; // differ what to do in apply
- int progress;
-
- ModelVolume *volume;
- size_t obj_index;
- std::optional original_its;
-
- std::optional last_error; // for use previous reduction
-
- bool need_reload; // after simplify, glReload must be on main thread
- std::thread worker;
+{
public:
GLGizmoSimplify(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
virtual ~GLGizmoSimplify();
@@ -41,32 +29,51 @@ protected:
virtual void on_render_input_window(float x, float y, float bottom_limit) override;
virtual bool on_is_activable() const override;
virtual bool on_is_selectable() const override { return false; }
+ virtual void on_set_state() override;
private:
void close();
void process();
void set_its(indexed_triangle_set &its);
+ void create_gui_cfg();
+ void request_rerender();
+
+ std::atomic_bool m_is_valid_result; // differ what to do in apply
+ std::atomic_bool m_exist_preview; // set when process end
+
+ volatile int m_progress; // percent of done work
+ ModelVolume *m_volume; //
+ size_t m_obj_index;
+
+ std::optional m_original_its;
+
+ volatile bool m_need_reload; // after simplify, glReload must be on main thread
+ std::thread m_worker;
+
+ enum class State {
+ settings,
+ preview, // simplify to show preview
+ close_on_end, // simplify with close on end
+ canceling // after button click, before canceled
+ };
+ volatile State m_state;
+
struct Configuration
{
- bool use_count = true;
+ bool use_count = false;
// minimal triangle count
- float wanted_percent = 50.f;
+ float decimate_ratio = 50.f; // in percent
uint32_t wanted_count = 0; // initialize by percents
- bool use_error = false;
// maximal quadric error
float max_error = 1.;
- void update_count(size_t triangle_count)
- {
- wanted_percent = (float) wanted_count / triangle_count * 100.f;
- }
- void update_percent(size_t triangle_count)
+ void fix_count_by_ratio(size_t triangle_count)
{
wanted_count = static_cast(
- std::round(triangle_count * wanted_percent / 100.f));
+ std::round(triangle_count * (100.f-decimate_ratio) / 100.f));
}
- } c;
+ } m_configuration;
// This configs holds GUI layout size given by translated texts.
// etc. When language changes, GUI is recreated and this class constructed again,
@@ -76,11 +83,21 @@ private:
int top_left_width = 100;
int bottom_left_width = 100;
int input_width = 100;
- int input_small_width = 80;
- int window_offset = 100;
+ int window_offset_x = 100;
+ int window_offset_y = 100;
+ int window_padding = 0;
+
+ // trunc model name when longer
+ size_t max_char_in_name = 30;
};
- std::optional gui_cfg;
- void create_gui_cfg();
+ std::optional m_gui_cfg;
+
+ // translations used for calc window size
+ const std::string tr_mesh_name;
+ const std::string tr_triangles;
+ const std::string tr_preview;
+ const std::string tr_detail_level;
+ const std::string tr_decimate_ratio;
};
} // namespace GUI
diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
index 5617b5ed8..6d9a03977 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
@@ -132,8 +132,7 @@ bool GLGizmosManager::init_arrow(const BackgroundTexture::Metadata& arrow_textur
bool res = false;
if (!arrow_texture.filename.empty())
- res = m_arrow_texture.texture.load_from_file(path + arrow_texture.filename, false, GLTexture::SingleThreaded, false);
-// res = m_arrow_texture.texture.load_from_svg_file(path + arrow_texture.filename, false, true, false, 100);
+ res = m_arrow_texture.texture.load_from_svg_file(path + arrow_texture.filename, false, false, false, 1000);
if (res)
m_arrow_texture.metadata = arrow_texture;
@@ -1019,7 +1018,7 @@ void GLGizmosManager::render_arrow(const GLCanvas3D& parent, EType highlighted_t
float arrow_sides_ratio = (float)m_arrow_texture.texture.get_height() / (float)m_arrow_texture.texture.get_width();
- GLTexture::render_sub_texture(tex_id, zoomed_top_x + zoomed_icons_size * 1.2f, zoomed_top_x + zoomed_icons_size * 1.2f + zoomed_icons_size * arrow_sides_ratio, zoomed_top_y - zoomed_icons_size, zoomed_top_y, { { internal_left_uv, internal_bottom_uv }, { internal_left_uv, internal_top_uv }, { internal_right_uv, internal_top_uv }, { internal_right_uv, internal_bottom_uv } });
+ GLTexture::render_sub_texture(tex_id, zoomed_top_x + zoomed_icons_size * 1.2f, zoomed_top_x + zoomed_icons_size * 1.2f + zoomed_icons_size * 2.2f * arrow_sides_ratio, zoomed_top_y - zoomed_icons_size * 1.6f , zoomed_top_y + zoomed_icons_size * 0.6f, { { internal_left_uv, internal_bottom_uv }, { internal_left_uv, internal_top_uv }, { internal_right_uv, internal_top_uv }, { internal_right_uv, internal_bottom_uv } });
break;
}
zoomed_top_y -= zoomed_stride_y;
diff --git a/src/slic3r/GUI/HintNotification.cpp b/src/slic3r/GUI/HintNotification.cpp
index dd215052d..ead7a8f29 100644
--- a/src/slic3r/GUI/HintNotification.cpp
+++ b/src/slic3r/GUI/HintNotification.cpp
@@ -3,6 +3,7 @@
#include "format.hpp"
#include "I18N.hpp"
#include "GUI_ObjectList.hpp"
+#include "GLCanvas3D.hpp"
#include "libslic3r/AppConfig.hpp"
#include "libslic3r/Utils.hpp"
#include "libslic3r/Config.hpp"
@@ -13,6 +14,31 @@
#include
#include
#include