diff --git a/CMakeLists.txt b/CMakeLists.txt index 26619e459..6b379c580 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,8 +101,8 @@ list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules/) enable_testing () -# Enable C++11 language standard. -set(CMAKE_CXX_STANDARD 14) +# Enable C++17 language standard. +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) if(NOT WIN32) diff --git a/deps/CGAL/CGAL.cmake b/deps/CGAL/CGAL.cmake index 96a629258..f0584c5b5 100644 --- a/deps/CGAL/CGAL.cmake +++ b/deps/CGAL/CGAL.cmake @@ -8,8 +8,13 @@ prusaslicer_add_cmake_project( DEPENDS dep_boost dep_GMP dep_MPFR ) +include(GNUInstallDirs) + +# CGAL, for whatever reason, makes itself non-relocatable by writing the build directory into +# CGALConfig-installation-dirs.cmake and including it in configure time. +# If this file is not present, it will not consider the stored absolute path ExternalProject_Add_Step(dep_CGAL dep_CGAL_relocation_fix DEPENDEES install COMMAND ${CMAKE_COMMAND} -E remove CGALConfig-installation-dirs.cmake - WORKING_DIRECTORY "${DESTDIR}/usr/local/lib/cmake/CGAL" -) \ No newline at end of file + WORKING_DIRECTORY "${DESTDIR}/usr/local/${CMAKE_INSTALL_LIBDIR}/cmake/CGAL" +) diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 3e6f70e42..3935e38c3 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -60,7 +60,7 @@ function(prusaslicer_add_cmake_project projectname) set(_gen "") set(_build_j "-j${NPROC}") if (MSVC) - set(_gen CMAKE_GENERATOR "${DEP_MSVC_GEN}") + set(_gen CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}") set(_build_j "/m") endif () diff --git a/deps/MPFR/MPFR.cmake b/deps/MPFR/MPFR.cmake index 7873bc0d6..17ac283ea 100644 --- a/deps/MPFR/MPFR.cmake +++ b/deps/MPFR/MPFR.cmake @@ -19,7 +19,7 @@ if (MSVC) else () ExternalProject_Add(dep_MPFR - URL https://www.mpfr.org/mpfr-3.1.6/mpfr-3.1.6.tar.bz2 + URL http://ftp.vim.org/ftp/gnu/mpfr/mpfr-3.1.6.tar.bz2 https://www.mpfr.org/mpfr-3.1.6/mpfr-3.1.6.tar.bz2 # mirrors are allowed BUILD_IN_SOURCE ON CONFIGURE_COMMAND ./configure --prefix=${DESTDIR}/usr/local --with-gmp=${DESTDIR}/usr/local --with-pic BUILD_COMMAND make -j diff --git a/resources/icons/thumb_down.svg b/resources/icons/thumb_down.svg index 0499cea41..f789b7317 100644 --- a/resources/icons/thumb_down.svg +++ b/resources/icons/thumb_down.svg @@ -4,7 +4,7 @@ viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve"> - + diff --git a/resources/icons/thumb_up.svg b/resources/icons/thumb_up.svg index c9045929b..1a0c6f1b7 100644 --- a/resources/icons/thumb_up.svg +++ b/resources/icons/thumb_up.svg @@ -4,7 +4,7 @@ viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve"> - + diff --git a/resources/icons/white/edit_layers_all.svg b/resources/icons/white/edit_layers_all.svg new file mode 100644 index 000000000..efb7d609f --- /dev/null +++ b/resources/icons/white/edit_layers_all.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + diff --git a/resources/icons/white/edit_layers_some.svg b/resources/icons/white/edit_layers_some.svg new file mode 100644 index 000000000..585a8df2f --- /dev/null +++ b/resources/icons/white/edit_layers_some.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/resources/icons/white/funnel.svg b/resources/icons/white/funnel.svg new file mode 100644 index 000000000..b8a3408a7 --- /dev/null +++ b/resources/icons/white/funnel.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/resources/icons/white/mirroring_off.svg b/resources/icons/white/mirroring_off.svg new file mode 100644 index 000000000..2011d8f1b --- /dev/null +++ b/resources/icons/white/mirroring_off.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/white/mirroring_on.svg b/resources/icons/white/mirroring_on.svg new file mode 100644 index 000000000..1773b78ad --- /dev/null +++ b/resources/icons/white/mirroring_on.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/white/note.svg b/resources/icons/white/note.svg new file mode 100644 index 000000000..07c2a14cf --- /dev/null +++ b/resources/icons/white/note.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/white/redo_menu.svg b/resources/icons/white/redo_menu.svg new file mode 100644 index 000000000..749f49a42 --- /dev/null +++ b/resources/icons/white/redo_menu.svg @@ -0,0 +1,13 @@ + + + + + diff --git a/resources/icons/white/undo_menu.svg b/resources/icons/white/undo_menu.svg new file mode 100644 index 000000000..4e2b69a60 --- /dev/null +++ b/resources/icons/white/undo_menu.svg @@ -0,0 +1,13 @@ + + + + + diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index aefff5c85..74bc529ae 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -106,7 +106,7 @@ compatible_printers = complete_objects = 0 default_acceleration = 1000 dont_support_bridges = 1 -elefant_foot_compensation = 0 +elefant_foot_compensation = 0.2 ensure_vertical_shell_thickness = 1 external_fill_pattern = rectilinear external_perimeters_first = 0 @@ -223,6 +223,7 @@ extruder_clearance_radius = 35 # Print parameters common to a 0.25mm diameter nozzle. [print:*0.25nozzle*] +elefant_foot_compensation = 0 external_perimeter_extrusion_width = 0.25 extrusion_width = 0.25 first_layer_extrusion_width = 0.3 @@ -238,6 +239,7 @@ support_material_xy_spacing = 150% output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode [print:*0.25nozzleMK3*] +elefant_foot_compensation = 0 external_perimeter_extrusion_width = 0.25 extrusion_width = 0.25 first_layer_extrusion_width = 0.3 @@ -271,6 +273,7 @@ fill_density = 20% output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode [print:*0.25nozzleMINI*] +elefant_foot_compensation = 0 external_perimeter_extrusion_width = 0.25 extrusion_width = 0.25 first_layer_extrusion_width = 0.3 @@ -2993,21 +2996,21 @@ inherits = *common 0.025* exposure_time = 6 initial_exposure_time = 35 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Grey Tough @0.025] inherits = *common 0.025* exposure_time = 7 initial_exposure_time = 35 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Azure Blue Tough @0.025] inherits = *common 0.025* exposure_time = 7 initial_exposure_time = 35 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Maroon Tough @0.025] @@ -3015,56 +3018,56 @@ inherits = *common 0.025* exposure_time = 6 initial_exposure_time = 35 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Beige Tough @0.025] inherits = *common 0.025* exposure_time = 6 initial_exposure_time = 35 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Pink Tough @0.025] inherits = *common 0.025* exposure_time = 7 initial_exposure_time = 35 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa White Tough @0.025] inherits = *common 0.025* exposure_time = 6.5 initial_exposure_time = 35 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Transparent Tough @0.025] inherits = *common 0.025* exposure_time = 6 initial_exposure_time = 15 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Green Dental Casting @0.025] inherits = *common 0.025* exposure_time = 12 initial_exposure_time = 40 material_type = Casting -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Transparent Green Tough @0.025] inherits = *common 0.025* exposure_time = 5 initial_exposure_time = 35 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Clear ABS like @0.025] inherits = *common 0.025* exposure_time = 6 initial_exposure_time = 30 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa ## [sla_material:Prusa ABS like White @0.025] ## inherits = *common 0.025* @@ -3076,35 +3079,35 @@ inherits = *common 0.025* exposure_time = 5 initial_exposure_time = 30 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Super Low Odor Cyan Tough @0.025] inherits = *common 0.025* exposure_time = 5 initial_exposure_time = 35 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Super Low Odor Magenta Tough @0.025] inherits = *common 0.025* exposure_time = 5 initial_exposure_time = 35 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Super Low Odor Yellow Tough @0.025] inherits = *common 0.025* exposure_time = 5 initial_exposure_time = 35 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Orange-Yellow Teeth Model @0.025] inherits = *common 0.025* exposure_time = 5 initial_exposure_time = 30 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa ########### Materials 0.05 @@ -3333,91 +3336,91 @@ inherits = *common 0.05* exposure_time = 7 initial_exposure_time = 35 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Orange Tough @0.05] inherits = *common 0.05* exposure_time = 7.5 initial_exposure_time = 35 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Grey Tough @0.05] inherits = *common 0.05* exposure_time = 8.5 initial_exposure_time = 35 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Black Tough @0.05] inherits = *common 0.05* exposure_time = 6 initial_exposure_time = 35 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa ## [sla_material:Prusa Super Low Odor Beige Tough @0.05] ## inherits = *common 0.05* ## exposure_time = 7.5 ## initial_exposure_time = 35 ## material_type = Tough -## material_vendor = Prusa +## material_vendor = Made for Prusa ## [sla_material:Prusa Super Low Odor White Tough @0.05] ## inherits = *common 0.05* ## exposure_time = 6.5 ## initial_exposure_time = 35 ## material_type = Tough -## material_vendor = Prusa +## material_vendor = Made for Prusa ## [sla_material:Prusa Super Low Odor Grey Tough @0.05] ## inherits = *common 0.05* ## exposure_time = 6.5 ## initial_exposure_time = 35 ## material_type = Tough -## material_vendor = Prusa +## material_vendor = Made for Prusa [sla_material:Prusa Super Low Odor Cyan Tough @0.05] inherits = *common 0.05* exposure_time = 6 initial_exposure_time = 35 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Super Low Odor Magenta Tough @0.05] inherits = *common 0.05* exposure_time = 6 initial_exposure_time = 35 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Super Low Odor Yellow Tough @0.05] inherits = *common 0.05* exposure_time = 6 initial_exposure_time = 35 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa ## [sla_material:Prusa Black High Tenacity @0.05] ## inherits = *common 0.05* ## exposure_time = 7 ## initial_exposure_time = 35 ## material_type = Tough -## material_vendor = Prusa +## material_vendor = Made for Prusa [sla_material:Prusa Orange-Yellow Teeth Model @0.05] inherits = *common 0.05* exposure_time = 7 initial_exposure_time = 30 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Green Dental Casting @0.05] inherits = *common 0.05* exposure_time = 13 initial_exposure_time = 50 material_type = Casting -material_vendor = Prusa +material_vendor = Made for Prusa ## [sla_material:Prusa Yellow Solid @0.05] ## inherits = *common 0.05* @@ -3429,49 +3432,49 @@ inherits = *common 0.05* exposure_time = 7.5 initial_exposure_time = 35 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Transparent Green Tough @0.05] inherits = *common 0.05* exposure_time = 6 initial_exposure_time = 35 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Transparent Red Tough @0.05] inherits = *common 0.05* exposure_time = 6 initial_exposure_time = 35 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Maroon Tough @0.05] inherits = *common 0.05* exposure_time = 7.5 initial_exposure_time = 35 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Pink Tough @0.05] inherits = *common 0.05* exposure_time = 8 initial_exposure_time = 35 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Azure Blue Tough @0.05] inherits = *common 0.05* exposure_time = 8 initial_exposure_time = 35 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Transparent Tough @0.05] inherits = *common 0.05* exposure_time = 7 initial_exposure_time = 15 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa ## [sla_material:Prusa Yellow Flexible @0.05] ## inherits = *common 0.05* @@ -3483,7 +3486,7 @@ inherits = *common 0.05* exposure_time = 5 initial_exposure_time = 15 material_type = Flexible -material_vendor = Prusa +material_vendor = Made for Prusa ## [sla_material:Prusa White Flexible @0.05] ## inherits = *common 0.05* @@ -3495,7 +3498,7 @@ inherits = *common 0.05* exposure_time = 5 initial_exposure_time = 15 material_type = Flexible -material_vendor = Prusa +material_vendor = Made for Prusa ## [sla_material:Prusa Black Flexible @0.05] ## inherits = *common 0.05* @@ -3512,7 +3515,7 @@ inherits = *common 0.05* exposure_time = 8 initial_exposure_time = 30 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa ## [sla_material:Prusa ABS like White @0.05] ## inherits = *common 0.05* @@ -3524,14 +3527,14 @@ inherits = *common 0.05* exposure_time = 13 initial_exposure_time = 45 material_type = Casting -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Grey High Tenacity @0.05] inherits = *common 0.05* exposure_time = 7 initial_exposure_time = 30 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa ########### Materials 0.035 @@ -3540,7 +3543,7 @@ inherits = *common 0.035* exposure_time = 6 initial_exposure_time = 35 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa ########### Materials 0.1 @@ -3565,70 +3568,70 @@ inherits = *common 0.1* exposure_time = 13 initial_exposure_time = 45 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Beige Tough @0.1] inherits = *common 0.1* exposure_time = 13 initial_exposure_time = 45 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Pink Tough @0.1] inherits = *common 0.1* exposure_time = 13 initial_exposure_time = 45 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Azure Blue Tough @0.1] inherits = *common 0.1* exposure_time = 13 initial_exposure_time = 45 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Maroon Tough @0.1] inherits = *common 0.1* exposure_time = 13 initial_exposure_time = 45 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa White Tough @0.1] inherits = *common 0.1* exposure_time = 13 initial_exposure_time = 45 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Black Tough @0.1] inherits = *common 0.1* exposure_time = 13 initial_exposure_time = 55 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Transparent Tough @0.1] inherits = *common 0.1* exposure_time = 8 initial_exposure_time = 35 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Green Dental Casting @0.1] inherits = *common 0.1* exposure_time = 15 initial_exposure_time = 50 material_type = Casting -material_vendor = Prusa +material_vendor = Made for Prusa [sla_material:Prusa Transparent Green Tough @0.1] inherits = *common 0.1* exposure_time = 7 initial_exposure_time = 35 material_type = Tough -material_vendor = Prusa +material_vendor = Made for Prusa [printer:*common*] printer_technology = FFF diff --git a/src/avrdude/CMakeLists.txt b/src/avrdude/CMakeLists.txt index 889720021..fc01b7d8d 100644 --- a/src/avrdude/CMakeLists.txt +++ b/src/avrdude/CMakeLists.txt @@ -1,8 +1,5 @@ cmake_minimum_required(VERSION 3.0) -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - add_definitions(-D_BSD_SOURCE -D_DEFAULT_SOURCE) # To enable various useful macros and functions on Unices remove_definitions(-D_UNICODE -DUNICODE) set(CMAKE_POSITION_INDEPENDENT_CODE ON) diff --git a/src/hidapi/CMakeLists.txt b/src/hidapi/CMakeLists.txt index 1f53c9b69..f3045466e 100644 --- a/src/hidapi/CMakeLists.txt +++ b/src/hidapi/CMakeLists.txt @@ -15,5 +15,5 @@ add_library(hidapi STATIC ${HIDAPI_IMPL}) if (CMAKE_SYSTEM_NAME STREQUAL "Linux") # Don't link the udev library, as there are two versions out there (libudev.so.0, libudev.so.1), so they are linked explicitely. # target_link_libraries(hidapi udev) - target_link_libraries(hidapi) + target_link_libraries(hidapi dl) endif() diff --git a/src/hidapi/README.md b/src/hidapi/README.md new file mode 100644 index 000000000..4f66b3274 --- /dev/null +++ b/src/hidapi/README.md @@ -0,0 +1,5 @@ +** hidapi is a c++ library for communicating with USB and Bluetooth HID devices on Linux, Mac and Windows.** + +For more information go to https://github.com/libusb/hidapi + +THIS DIRECTORY CONTAINS THE hidapi-0.9.0 7da5cc9 SOURCE DISTRIBUTION. diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index f053aea29..d4c2f05e1 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -679,133 +679,73 @@ ClipperLib::PolyTree union_pt(ExPolygons &&subject, bool safety_offset_) return _clipper_do(ClipperLib::ctUnion, std::move(subject), Polygons(), ClipperLib::pftEvenOdd, safety_offset_); } -Polygons -union_pt_chained(const Polygons &subject, bool safety_offset_) -{ - ClipperLib::PolyTree polytree = union_pt(subject, safety_offset_); - - Polygons retval; - traverse_pt(polytree.Childs, &retval); - return retval; -} - -static ClipperLib::PolyNodes order_nodes(const ClipperLib::PolyNodes &nodes) +// Simple spatial ordering of Polynodes +ClipperLib::PolyNodes order_nodes(const ClipperLib::PolyNodes &nodes) { // collect ordering points Points ordering_points; ordering_points.reserve(nodes.size()); + for (const ClipperLib::PolyNode *node : nodes) - ordering_points.emplace_back(Point(node->Contour.front().X, node->Contour.front().Y)); - + ordering_points.emplace_back( + Point(node->Contour.front().X, node->Contour.front().Y)); + // perform the ordering - ClipperLib::PolyNodes ordered_nodes = chain_clipper_polynodes(ordering_points, nodes); - + ClipperLib::PolyNodes ordered_nodes = + chain_clipper_polynodes(ordering_points, nodes); + return ordered_nodes; } -enum class e_ordering { - ORDER_POLYNODES, - DONT_ORDER_POLYNODES -}; - -template -void foreach_node(const ClipperLib::PolyNodes &nodes, - std::function fn); - -template<> void foreach_node( - const ClipperLib::PolyNodes & nodes, - std::function fn) +static void traverse_pt_noholes(const ClipperLib::PolyNodes &nodes, Polygons *out) { - for (auto &n : nodes) fn(n); + foreach_node(nodes, [&out](const ClipperLib::PolyNode *node) + { + traverse_pt_noholes(node->Childs, out); + out->emplace_back(ClipperPath_to_Slic3rPolygon(node->Contour)); + if (node->IsHole()) out->back().reverse(); // ccw + }); } -template<> void foreach_node( - const ClipperLib::PolyNodes & nodes, - std::function fn) -{ - auto ordered_nodes = order_nodes(nodes); - for (auto &n : ordered_nodes) fn(n); -} - -template -void _traverse_pt(const ClipperLib::PolyNodes &nodes, Polygons *retval) +static void traverse_pt_old(ClipperLib::PolyNodes &nodes, Polygons* retval) { /* use a nearest neighbor search to order these children TODO: supply start_near to chained_path() too? */ + // collect ordering points + Points ordering_points; + ordering_points.reserve(nodes.size()); + for (ClipperLib::PolyNodes::const_iterator it = nodes.begin(); it != nodes.end(); ++it) { + Point p((*it)->Contour.front().X, (*it)->Contour.front().Y); + ordering_points.push_back(p); + } + + // perform the ordering + ClipperLib::PolyNodes ordered_nodes = chain_clipper_polynodes(ordering_points, nodes); + // push results recursively - foreach_node(nodes, [&retval](const ClipperLib::PolyNode *node) { + for (ClipperLib::PolyNodes::iterator it = ordered_nodes.begin(); it != ordered_nodes.end(); ++it) { // traverse the next depth - _traverse_pt(node->Childs, retval); - retval->emplace_back(ClipperPath_to_Slic3rPolygon(node->Contour)); - if (node->IsHole()) retval->back().reverse(); // ccw - }); + traverse_pt_old((*it)->Childs, retval); + retval->push_back(ClipperPath_to_Slic3rPolygon((*it)->Contour)); + if ((*it)->IsHole()) retval->back().reverse(); // ccw + } } -template -void _traverse_pt(const ClipperLib::PolyNode *tree, ExPolygons *retval) +Polygons union_pt_chained(const Polygons &subject, bool safety_offset_) { - if (!retval || !tree) return; + ClipperLib::PolyTree polytree = union_pt(subject, safety_offset_); - ExPolygons &retv = *retval; + Polygons retval; + traverse_pt_old(polytree.Childs, &retval); + return retval; - std::function hole_fn; +// TODO: This needs to be tested: +// ClipperLib::PolyTree polytree = union_pt(subject, safety_offset_); - auto contour_fn = [&retv, &hole_fn](const ClipperLib::PolyNode *pptr) { - ExPolygon poly; - poly.contour.points = ClipperPath_to_Slic3rPolygon(pptr->Contour); - auto fn = std::bind(hole_fn, std::placeholders::_1, poly); - foreach_node(pptr->Childs, fn); - retv.push_back(poly); - }; - - hole_fn = [&contour_fn](const ClipperLib::PolyNode *pptr, ExPolygon& poly) - { - poly.holes.emplace_back(); - poly.holes.back().points = ClipperPath_to_Slic3rPolygon(pptr->Contour); - foreach_node(pptr->Childs, contour_fn); - }; - - contour_fn(tree); -} - -template -void _traverse_pt(const ClipperLib::PolyNodes &nodes, ExPolygons *retval) -{ - // Here is the actual traverse - foreach_node(nodes, [&retval](const ClipperLib::PolyNode *node) { - _traverse_pt(node, retval); - }); -} - -void traverse_pt(const ClipperLib::PolyNode *tree, ExPolygons *retval) -{ - _traverse_pt(tree, retval); -} - -void traverse_pt_unordered(const ClipperLib::PolyNode *tree, ExPolygons *retval) -{ - _traverse_pt(tree, retval); -} - -void traverse_pt(const ClipperLib::PolyNodes &nodes, Polygons *retval) -{ - _traverse_pt(nodes, retval); -} - -void traverse_pt(const ClipperLib::PolyNodes &nodes, ExPolygons *retval) -{ - _traverse_pt(nodes, retval); -} - -void traverse_pt_unordered(const ClipperLib::PolyNodes &nodes, Polygons *retval) -{ - _traverse_pt(nodes, retval); -} - -void traverse_pt_unordered(const ClipperLib::PolyNodes &nodes, ExPolygons *retval) -{ - _traverse_pt(nodes, retval); +// Polygons retval; +// traverse_pt_noholes(polytree.Childs, &retval); +// return retval; } Polygons simplify_polygons(const Polygons &subject, bool preserve_collinear) diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index 5a41a6a90..0828ec21f 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -214,7 +214,6 @@ inline Slic3r::ExPolygons union_ex(const Slic3r::Surfaces &subject, bool safety_ return _clipper_ex(ClipperLib::ctUnion, to_polygons(subject), Slic3r::Polygons(), safety_offset_); } - ClipperLib::PolyTree union_pt(const Slic3r::Polygons &subject, bool safety_offset_ = false); ClipperLib::PolyTree union_pt(const Slic3r::ExPolygons &subject, bool safety_offset_ = false); ClipperLib::PolyTree union_pt(Slic3r::Polygons &&subject, bool safety_offset_ = false); @@ -222,13 +221,95 @@ ClipperLib::PolyTree union_pt(Slic3r::ExPolygons &&subject, bool safety_offset_ Slic3r::Polygons union_pt_chained(const Slic3r::Polygons &subject, bool safety_offset_ = false); -void traverse_pt(const ClipperLib::PolyNodes &nodes, Slic3r::Polygons *retval); -void traverse_pt(const ClipperLib::PolyNodes &nodes, Slic3r::ExPolygons *retval); -void traverse_pt(const ClipperLib::PolyNode *tree, Slic3r::ExPolygons *retval); +ClipperLib::PolyNodes order_nodes(const ClipperLib::PolyNodes &nodes); + +// Implementing generalized loop (foreach) over a list of nodes which can be +// ordered or unordered (performance gain) based on template parameter +enum class e_ordering { + ON, + OFF +}; + +// Create a template struct, template functions can not be partially specialized +template struct _foreach_node { + void operator()(const ClipperLib::PolyNodes &nodes, Fn &&fn); +}; + +// Specialization with NO ordering +template struct _foreach_node { + void operator()(const ClipperLib::PolyNodes &nodes, Fn &&fn) + { + for (auto &n : nodes) fn(n); + } +}; + +// Specialization with ordering +template struct _foreach_node { + void operator()(const ClipperLib::PolyNodes &nodes, Fn &&fn) + { + auto ordered_nodes = order_nodes(nodes); + for (auto &n : nodes) fn(n); + } +}; + +// Wrapper function for the foreach_node which can deduce arguments automatically +template +void foreach_node(const ClipperLib::PolyNodes &nodes, Fn &&fn) +{ + _foreach_node()(nodes, std::forward(fn)); +} + +// Collecting polygons of the tree into a list of Polygons, holes have clockwise +// orientation. +template +void traverse_pt(const ClipperLib::PolyNode *tree, Polygons *out) +{ + if (!tree) return; // terminates recursion + + // Push the contour of the current level + out->emplace_back(ClipperPath_to_Slic3rPolygon(tree->Contour)); + + // Do the recursion for all the children. + traverse_pt(tree->Childs, out); +} + +// Collecting polygons of the tree into a list of ExPolygons. +template +void traverse_pt(const ClipperLib::PolyNode *tree, ExPolygons *out) +{ + if (!tree) return; + else if(tree->IsHole()) { + // Levels of holes are skipped and handled together with the + // contour levels. + traverse_pt(tree->Childs, out); + return; + } + + ExPolygon level; + level.contour = ClipperPath_to_Slic3rPolygon(tree->Contour); + + foreach_node(tree->Childs, + [out, &level] (const ClipperLib::PolyNode *node) { + + // Holes are collected here. + level.holes.emplace_back(ClipperPath_to_Slic3rPolygon(node->Contour)); + + // By doing a recursion, a new level expoly is created with the contour + // and holes of the lower level. Doing this for all the childs. + traverse_pt(node->Childs, out); + }); + + out->emplace_back(level); +} + +template +void traverse_pt(const ClipperLib::PolyNodes &nodes, ExOrJustPolygons *retval) +{ + foreach_node(nodes, [&retval](const ClipperLib::PolyNode *node) { + traverse_pt(node, retval); + }); +} -void traverse_pt_unordered(const ClipperLib::PolyNodes &nodes, Slic3r::Polygons *retval); -void traverse_pt_unordered(const ClipperLib::PolyNodes &nodes, Slic3r::ExPolygons *retval); -void traverse_pt_unordered(const ClipperLib::PolyNode *tree, Slic3r::ExPolygons *retval); /* OTHER */ Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject, bool preserve_collinear = false); diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 9d24d8cb7..a81e9521a 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -612,7 +612,7 @@ void ConfigBase::load_from_gcode_file(const std::string &file) } ifs.seekg(0, ifs.end); auto file_length = ifs.tellg(); - auto data_length = std::min(65535, file_length); + auto data_length = std::min(65535, file_length); ifs.seekg(file_length - data_length, ifs.beg); std::vector data(size_t(data_length) + 1, 0); ifs.read(data.data(), data_length); diff --git a/src/libslic3r/ElephantFootCompensation.cpp b/src/libslic3r/ElephantFootCompensation.cpp index 69a20b5ec..2694b5e8a 100644 --- a/src/libslic3r/ElephantFootCompensation.cpp +++ b/src/libslic3r/ElephantFootCompensation.cpp @@ -472,7 +472,7 @@ static inline void smooth_compensation_banded(const Points &contour, float band, float l2 = (pthis - pprev).squaredNorm(); if (l2 < dist_min2) { float l = sqrt(l2); - int jprev = exchange(j, prev_idx_modulo(j, contour)); + int jprev = std::exchange(j, prev_idx_modulo(j, contour)); while (j != i) { const Vec2f pp = contour[j].cast(); const float lthis = (pp - pprev).norm(); @@ -487,7 +487,7 @@ static inline void smooth_compensation_banded(const Points &contour, float band, prev = use_min ? std::min(prev, compensation[j]) : compensation[j]; pprev = pp; l = lnext; - jprev = exchange(j, prev_idx_modulo(j, contour)); + jprev = std::exchange(j, prev_idx_modulo(j, contour)); } } @@ -497,7 +497,7 @@ static inline void smooth_compensation_banded(const Points &contour, float band, l2 = (pprev - pthis).squaredNorm(); if (l2 < dist_min2) { float l = sqrt(l2); - int jprev = exchange(j, next_idx_modulo(j, contour)); + int jprev = std::exchange(j, next_idx_modulo(j, contour)); while (j != i) { const Vec2f pp = contour[j].cast(); const float lthis = (pp - pprev).norm(); @@ -512,7 +512,7 @@ static inline void smooth_compensation_banded(const Points &contour, float band, next = use_min ? std::min(next, compensation[j]) : compensation[j]; pprev = pp; l = lnext; - jprev = exchange(j, next_idx_modulo(j, contour)); + jprev = std::exchange(j, next_idx_modulo(j, contour)); } } diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 3cb89bedf..d5c8a3a6c 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -34,8 +34,12 @@ namespace pt = boost::property_tree; // VERSION NUMBERS // 0 : .3mf, files saved by older slic3r or other applications. No version definition in them. // 1 : Introduction of 3mf versioning. No other change in data saved into 3mf files. -// 2 : Meshes saved in their local system; Volumes' matrices and source data added to Metadata/Slic3r_PE_model.config file. -const unsigned int VERSION_3MF = 2; +// 2 : Volumes' matrices and source data added to Metadata/Slic3r_PE_model.config file, meshes transformed back to their coordinate system on loading. +// WARNING !! -> the version number has been rolled back to 1 +// the next change should use 3 +const unsigned int VERSION_3MF = 1; +// Allow loading version 2 file as well. +const unsigned int VERSION_3MF_COMPATIBLE = 2; const char* SLIC3RPE_3MF_VERSION = "slic3rpe:Version3mf"; // definition of the metadata name saved into .model file const std::string MODEL_FOLDER = "3D/"; @@ -52,7 +56,7 @@ const std::string LAYER_HEIGHTS_PROFILE_FILE = "Metadata/Slic3r_PE_layer_heights const std::string LAYER_CONFIG_RANGES_FILE = "Metadata/Prusa_Slicer_layer_config_ranges.xml"; const std::string SLA_SUPPORT_POINTS_FILE = "Metadata/Slic3r_PE_sla_support_points.txt"; const std::string SLA_DRAIN_HOLES_FILE = "Metadata/Slic3r_PE_sla_drain_holes.txt"; -const std::string CUSTOM_GCODE_PER_HEIGHT_FILE = "Metadata/Prusa_Slicer_custom_gcode_per_height.xml"; +const std::string CUSTOM_GCODE_PER_PRINT_Z_FILE = "Metadata/Prusa_Slicer_custom_gcode_per_print_z.xml"; const char* MODEL_TAG = "model"; const char* RESOURCES_TAG = "resources"; @@ -422,7 +426,7 @@ namespace Slic3r { void _extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); void _extract_sla_drain_holes_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); - void _extract_custom_gcode_per_height_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); + void _extract_custom_gcode_per_print_z_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); void _extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig& config, const std::string& archive_filename); bool _extract_model_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model); @@ -638,10 +642,10 @@ namespace Slic3r { // extract slic3r print config file _extract_print_config_from_archive(archive, stat, config, filename); } - if (boost::algorithm::iequals(name, CUSTOM_GCODE_PER_HEIGHT_FILE)) + if (boost::algorithm::iequals(name, CUSTOM_GCODE_PER_PRINT_Z_FILE)) { // extract slic3r layer config ranges file - _extract_custom_gcode_per_height_from_archive(archive, stat); + _extract_custom_gcode_per_print_z_from_archive(archive, stat); } else if (boost::algorithm::iequals(name, MODEL_CONFIG_FILE)) { @@ -1163,7 +1167,7 @@ namespace Slic3r { return true; } - void _3MF_Importer::_extract_custom_gcode_per_height_from_archive(::mz_zip_archive &archive, const mz_zip_archive_file_stat &stat) + void _3MF_Importer::_extract_custom_gcode_per_print_z_from_archive(::mz_zip_archive &archive, const mz_zip_archive_file_stat &stat) { if (stat.m_uncomp_size > 0) { @@ -1178,24 +1182,23 @@ namespace Slic3r { pt::ptree main_tree; pt::read_xml(iss, main_tree); - if (main_tree.front().first != "custom_gcodes_per_height") + if (main_tree.front().first != "custom_gcodes_per_print_z") return; pt::ptree code_tree = main_tree.front().second; - if (!m_model->custom_gcode_per_height.empty()) - m_model->custom_gcode_per_height.clear(); + m_model->custom_gcode_per_print_z.clear(); for (const auto& code : code_tree) { if (code.first != "code") continue; pt::ptree tree = code.second; - double height = tree.get(".height"); - std::string gcode = tree.get(".gcode"); - int extruder = tree.get(".extruder"); - std::string color = tree.get(".color"); + double print_z = tree.get (".print_z" ); + std::string gcode = tree.get (".gcode" ); + int extruder = tree.get (".extruder" ); + std::string color = tree.get (".color" ); - m_model->custom_gcode_per_height.push_back(Model::CustomGCode(height, gcode, extruder, color)) ; + m_model->custom_gcode_per_print_z.push_back(Model::CustomGCode{print_z, gcode, extruder, color}) ; } } } @@ -1611,7 +1614,7 @@ namespace Slic3r { { m_version = (unsigned int)atoi(m_curr_characters.c_str()); - if (m_check_version && (m_version > VERSION_3MF)) + if (m_check_version && (m_version > VERSION_3MF_COMPATIBLE)) { // std::string msg = _(L("The selected 3mf file has been saved with a newer version of " + std::string(SLIC3R_APP_NAME) + " and is not compatible.")); // throw version_error(msg.c_str()); @@ -1797,20 +1800,19 @@ namespace Slic3r { return false; } - Slic3r::Geometry::Transformation transform; - if (m_version > 1) + Transform3d volume_matrix_to_object = Transform3d::Identity(); + bool has_transform = false; + // extract the volume transformation from the volume's metadata, if present + for (const Metadata& metadata : volume_data.metadata) { - // extract the volume transformation from the volume's metadata, if present - for (const Metadata& metadata : volume_data.metadata) + if (metadata.key == MATRIX_KEY) { - if (metadata.key == MATRIX_KEY) - { - transform.set_from_string(metadata.value); - break; - } + volume_matrix_to_object = Slic3r::Geometry::transform3d_from_string(metadata.value); + has_transform = ! volume_matrix_to_object.isApprox(Transform3d::Identity(), 1e-10); + break; } } - Transform3d inv_matrix = transform.get_matrix().inverse(); + Transform3d inv_matrix = volume_matrix_to_object.inverse(); // splits volume out of imported geometry TriangleMesh triangle_mesh; @@ -1831,10 +1833,10 @@ namespace Slic3r { { unsigned int tri_id = geometry.triangles[src_start_id + ii + v] * 3; Vec3f vertex(geometry.vertices[tri_id + 0], geometry.vertices[tri_id + 1], geometry.vertices[tri_id + 2]); - if (m_version > 1) + facet.vertex[v] = has_transform ? // revert the vertices to the original mesh reference system - vertex = (inv_matrix * vertex.cast()).cast(); - ::memcpy(facet.vertex[v].data(), (const void*)vertex.data(), 3 * sizeof(float)); + (inv_matrix * vertex.cast()).cast() : + vertex; } } @@ -1843,8 +1845,8 @@ namespace Slic3r { ModelVolume* volume = object.add_volume(std::move(triangle_mesh)); // apply the volume matrix taken from the metadata, if present - if (m_version > 1) - volume->set_transformation(transform); + if (has_transform) + volume->set_transformation(Slic3r::Geometry::Transformation(volume_matrix_to_object)); volume->calculate_convex_hull(); // apply the remaining volume's metadata @@ -1985,7 +1987,7 @@ namespace Slic3r { bool _add_sla_drain_holes_file_to_archive(mz_zip_archive& archive, Model& model); bool _add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config); bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, const IdToObjectDataMap &objects_data); - bool _add_custom_gcode_per_height_file_to_archive(mz_zip_archive& archive, Model& model); + bool _add_custom_gcode_per_print_z_file_to_archive(mz_zip_archive& archive, Model& model); }; #if ENABLE_THUMBNAIL_GENERATOR @@ -2096,9 +2098,9 @@ namespace Slic3r { } - // Adds custom gcode per height file ("Metadata/Prusa_Slicer_custom_gcode_per_height.xml"). + // Adds custom gcode per height file ("Metadata/Prusa_Slicer_custom_gcode_per_print_z.xml"). // All custom gcode per height of whole Model are stored here - if (!_add_custom_gcode_per_height_file_to_archive(archive, model)) + if (!_add_custom_gcode_per_print_z_file_to_archive(archive, model)) { close_zip_writer(&archive); boost::filesystem::remove(filename); @@ -2622,6 +2624,9 @@ namespace Slic3r { bool _3MF_Exporter::_add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, const IdToObjectDataMap &objects_data) { std::stringstream stream; + // Store mesh transformation in full precision, as the volumes are stored transformed and they need to be transformed back + // when loaded as accurately as possible. + stream << std::setprecision(std::numeric_limits::max_digits10); stream << "\n"; stream << "<" << CONFIG_TAG << ">\n"; @@ -2719,20 +2724,20 @@ namespace Slic3r { return true; } -bool _3MF_Exporter::_add_custom_gcode_per_height_file_to_archive( mz_zip_archive& archive, Model& model) +bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archive& archive, Model& model) { std::string out = ""; - if (!model.custom_gcode_per_height.empty()) + if (!model.custom_gcode_per_print_z.empty()) { pt::ptree tree; - pt::ptree& main_tree = tree.add("custom_gcodes_per_height", ""); + pt::ptree& main_tree = tree.add("custom_gcodes_per_print_z", ""); - for (const Model::CustomGCode& code : model.custom_gcode_per_height) + for (const Model::CustomGCode& code : model.custom_gcode_per_print_z) { pt::ptree& code_tree = main_tree.add("code", ""); // store minX and maxZ - code_tree.put(".height" , code.height ); + code_tree.put(".print_z" , code.print_z ); code_tree.put(".gcode" , code.gcode ); code_tree.put(".extruder" , code.extruder ); code_tree.put(".color" , code.color ); @@ -2751,9 +2756,9 @@ bool _3MF_Exporter::_add_custom_gcode_per_height_file_to_archive( mz_zip_archive if (!out.empty()) { - if (!mz_zip_writer_add_mem(&archive, CUSTOM_GCODE_PER_HEIGHT_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) + if (!mz_zip_writer_add_mem(&archive, CUSTOM_GCODE_PER_PRINT_Z_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) { - add_error("Unable to add custom Gcodes per height file to archive"); + add_error("Unable to add custom Gcodes per print_z file to archive"); return false; } } diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index d50f6e395..4b19b8060 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -41,8 +41,11 @@ namespace pt = boost::property_tree; // Added x and y components of rotation // Added x, y and z components of scale // Added x, y and z components of mirror -// 3 : Meshes saved in their local system; Added volumes' matrices and source data -const unsigned int VERSION_AMF = 3; +// 3 : Added volumes' matrices and source data, meshes transformed back to their coordinate system on loading. +// WARNING !! -> the version number has been rolled back to 2 +// the next change should use 4 +const unsigned int VERSION_AMF = 2; +const unsigned int VERSION_AMF_COMPATIBLE = 3; const char* SLIC3RPE_AMF_VERSION = "slic3rpe_amf_version"; const char* SLIC3R_CONFIG_TYPE = "slic3rpe_config"; @@ -228,6 +231,8 @@ struct AMFParserContext ModelVolume *m_volume; // Faces collected for the current m_volume. std::vector m_volume_facets; + // Transformation matrix of a volume mesh from its coordinate system to Object's coordinate system. + Transform3d m_volume_transform; // Current material allocated for an amf/metadata subtree. ModelMaterial *m_material; // Current instance allocated for an amf/constellation/instance subtree. @@ -319,6 +324,7 @@ void AMFParserContext::startElement(const char *name, const char **atts) else if (strcmp(name, "volume") == 0) { assert(! m_volume); m_volume = m_object->add_volume(TriangleMesh()); + m_volume_transform = Transform3d::Identity(); node_type_new = NODE_TYPE_VOLUME; } } else if (m_path[2] == NODE_TYPE_INSTANCE) { @@ -578,27 +584,25 @@ void AMFParserContext::endElement(const char * /* name */) stl.stats.original_num_facets = stl.stats.number_of_facets; stl_allocate(&stl); - Slic3r::Geometry::Transformation transform; - if (m_version > 2) - transform = m_volume->get_transformation(); - - Transform3d inv_matrix = transform.get_matrix().inverse(); - + bool has_transform = ! m_volume_transform.isApprox(Transform3d::Identity(), 1e-10); + Transform3d inv_matrix = m_volume_transform.inverse(); for (size_t i = 0; i < m_volume_facets.size();) { stl_facet &facet = stl.facet_start[i/3]; for (unsigned int v = 0; v < 3; ++v) { unsigned int tri_id = m_volume_facets[i++] * 3; Vec3f vertex(m_object_vertices[tri_id + 0], m_object_vertices[tri_id + 1], m_object_vertices[tri_id + 2]); - if (m_version > 2) + facet.vertex[v] = has_transform ? // revert the vertices to the original mesh reference system - vertex = (inv_matrix * vertex.cast()).cast(); - ::memcpy((void*)facet.vertex[v].data(), (const void*)vertex.data(), 3 * sizeof(float)); + (inv_matrix * vertex.cast()).cast() : + vertex; } - } + } stl_get_size(&stl); mesh.repair(); m_volume->set_mesh(std::move(mesh)); + if (has_transform) + m_volume->set_transformation(m_volume_transform); if (m_volume->source.input_file.empty() && (m_volume->type() == ModelVolumeType::MODEL_PART)) { m_volume->source.object_idx = (int)m_model.objects.size() - 1; @@ -637,7 +641,7 @@ void AMFParserContext::endElement(const char * /* name */) int extruder = atoi(m_value[2].c_str()); const std::string& color = m_value[3]; - m_model.custom_gcode_per_height.push_back(Model::CustomGCode(height, gcode, extruder, color)); + m_model.custom_gcode_per_print_z.push_back(Model::CustomGCode{height, gcode, extruder, color}); for (std::string& val: m_value) val.clear(); @@ -718,9 +722,7 @@ void AMFParserContext::endElement(const char * /* name */) m_volume->set_type(ModelVolume::type_from_string(m_value[1])); } else if (strcmp(opt_key, "matrix") == 0) { - Geometry::Transformation transform; - transform.set_from_string(m_value[1]); - m_volume->set_transformation(transform); + m_volume_transform = Slic3r::Geometry::transform3d_from_string(m_value[1]); } else if (strcmp(opt_key, "source_file") == 0) { m_volume->source.input_file = m_value[1]; @@ -910,7 +912,7 @@ bool extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_fi ctx.endDocument(); - if (check_version && (ctx.m_version > VERSION_AMF)) + if (check_version && (ctx.m_version > VERSION_AMF_COMPATIBLE)) { // std::string msg = _(L("The selected amf file has been saved with a newer version of " + std::string(SLIC3R_APP_NAME) + " and is not compatible.")); // throw std::runtime_error(msg.c_str()); @@ -1146,6 +1148,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) stream << " " << ModelVolume::type_to_string(volume->type()) << "\n"; stream << " "; const Transform3d& matrix = volume->get_matrix(); + stream << std::setprecision(std::numeric_limits::max_digits10); for (int r = 0; r < 4; ++r) { for (int c = 0; c < 4; ++c) @@ -1165,6 +1168,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) stream << " " << volume->source.mesh_offset(1) << "\n"; stream << " " << volume->source.mesh_offset(2) << "\n"; } + stream << std::setprecision(std::numeric_limits::max_digits10); const indexed_triangle_set &its = volume->mesh().its; for (size_t i = 0; i < its.indices.size(); ++i) { stream << " \n"; @@ -1221,21 +1225,21 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) stream << " \n"; } - if (!model->custom_gcode_per_height.empty()) + if (!model->custom_gcode_per_print_z.empty()) { std::string out = ""; pt::ptree tree; pt::ptree& main_tree = tree.add("custom_gcodes_per_height", ""); - for (const Model::CustomGCode& code : model->custom_gcode_per_height) + for (const Model::CustomGCode& code : model->custom_gcode_per_print_z) { pt::ptree& code_tree = main_tree.add("code", ""); // store minX and maxZ - code_tree.put(".height", code.height); - code_tree.put(".gcode", code.gcode); - code_tree.put(".extruder", code.extruder); - code_tree.put(".color", code.color); + code_tree.put(".print_z" , code.print_z ); + code_tree.put(".gcode" , code.gcode ); + code_tree.put(".extruder" , code.extruder ); + code_tree.put(".color" , code.color ); } if (!tree.empty()) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 91627631f..433422d99 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -936,7 +936,7 @@ void GCode::_do_export(Print& print, FILE* file) // Initialize custom gcode Model* model = print.get_object(0)->model_object()->get_model(); - m_custom_g_code_heights = model->custom_gcode_per_height; + m_custom_gcode_per_print_z = model->custom_gcode_per_print_z; // Initialize autospeed. { @@ -1124,20 +1124,22 @@ void GCode::_do_export(Print& print, FILE* file) } print.throw_if_canceled(); +// #ys_FIXME_no_exported_codes + /* /* To avoid change filament for non-used extruder for Multi-material, - * check model->custom_gcode_per_height using tool_ordering values - * */ - if (!m_custom_g_code_heights. empty()) + * check model->custom_gcode_per_print_z using tool_ordering values + * / + if (!m_custom_gcode_per_print_z. empty()) { bool delete_executed = false; - auto it = m_custom_g_code_heights.end(); - while (it != m_custom_g_code_heights.begin()) + auto it = m_custom_gcode_per_print_z.end(); + while (it != m_custom_gcode_per_print_z.begin()) { --it; if (it->gcode != ColorChangeCode) continue; - auto it_layer_tools = std::lower_bound(tool_ordering.begin(), tool_ordering.end(), LayerTools(it->height)); + auto it_layer_tools = std::lower_bound(tool_ordering.begin(), tool_ordering.end(), LayerTools(it->print_z)); bool used_extruder = false; for (; it_layer_tools != tool_ordering.end(); it_layer_tools++) @@ -1154,16 +1156,16 @@ void GCode::_do_export(Print& print, FILE* file) /* If we are there, current extruder wouldn't be used, * so this color change is a redundant move. - * Delete this item from m_custom_g_code_heights - * */ - it = m_custom_g_code_heights.erase(it); + * Delete this item from m_custom_gcode_per_print_z + * / + it = m_custom_gcode_per_print_z.erase(it); delete_executed = true; } if (delete_executed) - model->custom_gcode_per_height = m_custom_g_code_heights; + model->custom_gcode_per_print_z = m_custom_gcode_per_print_z; } - +*/ m_cooling_buffer->set_current_extruder(initial_extruder_id); @@ -1461,7 +1463,7 @@ void GCode::_do_export(Print& print, FILE* file) dst.first += buf; ++ dst.second; }; - print.m_print_statistics.filament_stats.insert(std::pair(extruder.id(), (float)used_filament)); + print.m_print_statistics.filament_stats.insert(std::pair{extruder.id(), (float)used_filament}); append(out_filament_used_mm, "%.1lf", used_filament); append(out_filament_used_cm3, "%.1lf", extruded_volume * 0.001); if (filament_weight > 0.) { @@ -1835,15 +1837,15 @@ void GCode::process_layer( std::string custom_code = ""; std::string pause_print_msg = ""; int m600_before_extruder = -1; - while (!m_custom_g_code_heights.empty() && m_custom_g_code_heights.front().height-EPSILON < layer.print_z) { - custom_code = m_custom_g_code_heights.front().gcode; + while (!m_custom_gcode_per_print_z.empty() && m_custom_gcode_per_print_z.front().print_z - EPSILON < layer.print_z) { + custom_code = m_custom_gcode_per_print_z.front().gcode; - if (custom_code == ColorChangeCode && m_custom_g_code_heights.front().extruder > 0) - m600_before_extruder = m_custom_g_code_heights.front().extruder - 1; + if (custom_code == ColorChangeCode && m_custom_gcode_per_print_z.front().extruder > 0) + m600_before_extruder = m_custom_gcode_per_print_z.front().extruder - 1; if (custom_code == PausePrintCode) - pause_print_msg = m_custom_g_code_heights.front().color; + pause_print_msg = m_custom_gcode_per_print_z.front().color; - m_custom_g_code_heights.erase(m_custom_g_code_heights.begin()); + m_custom_gcode_per_print_z.erase(m_custom_gcode_per_print_z.begin()); colorprint_change = true; } diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 7f009b814..68bb85a98 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -367,7 +367,7 @@ protected: * Updated before the export and erased during the process, * so no toolchange occurs twice. * */ - std::vector m_custom_g_code_heights; + std::vector m_custom_gcode_per_print_z; // Time estimators GCodeTimeEstimator m_normal_time_estimator; diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp index 664b0e3a1..98abdda28 100644 --- a/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCodeWriter.hpp @@ -10,6 +10,11 @@ namespace Slic3r { +// Additional Codes which can be set by user using DoubleSlider +static constexpr char ColorChangeCode[] = "M600"; +static constexpr char PausePrintCode[] = "M601"; +static constexpr char ExtruderChangeCode[] = "tool_change"; + class GCodeWriter { public: GCodeConfig config; diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index 46d7ef154..b9e4d6e78 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -1187,14 +1187,12 @@ MedialAxis::validate_edge(const VD::edge_type* edge) return true; } -const Line& -MedialAxis::retrieve_segment(const VD::cell_type* cell) const +const Line& MedialAxis::retrieve_segment(const VD::cell_type* cell) const { return this->lines[cell->source_index()]; } -const Point& -MedialAxis::retrieve_endpoint(const VD::cell_type* cell) const +const Point& MedialAxis::retrieve_endpoint(const VD::cell_type* cell) const { const Line& line = this->retrieve_segment(cell); if (cell->source_category() == SOURCE_CATEGORY_SEGMENT_START_POINT) { @@ -1208,11 +1206,8 @@ void assemble_transform(Transform3d& transform, const Vec3d& translation, const { transform = Transform3d::Identity(); transform.translate(translation); - transform.rotate(Eigen::AngleAxisd(rotation(2), Vec3d::UnitZ())); - transform.rotate(Eigen::AngleAxisd(rotation(1), Vec3d::UnitY())); - transform.rotate(Eigen::AngleAxisd(rotation(0), Vec3d::UnitX())); - transform.scale(scale); - transform.scale(mirror); + transform.rotate(Eigen::AngleAxisd(rotation(2), Vec3d::UnitZ()) * Eigen::AngleAxisd(rotation(1), Vec3d::UnitY()) * Eigen::AngleAxisd(rotation(0), Vec3d::UnitX())); + transform.scale(scale.cwiseProduct(mirror)); } Transform3d assemble_transform(const Vec3d& translation, const Vec3d& rotation, const Vec3d& scale, const Vec3d& mirror) @@ -1420,32 +1415,6 @@ void Transformation::set_from_transform(const Transform3d& transform) // std::cout << "something went wrong in extracting data from matrix" << std::endl; } -void Transformation::set_from_string(const std::string& transform_str) -{ - Transform3d transform = Transform3d::Identity(); - - if (!transform_str.empty()) - { - std::vector mat_elements_str; - boost::split(mat_elements_str, transform_str, boost::is_any_of(" "), boost::token_compress_on); - - unsigned int size = (unsigned int)mat_elements_str.size(); - if (size == 16) - { - unsigned int i = 0; - for (unsigned int r = 0; r < 4; ++r) - { - for (unsigned int c = 0; c < 4; ++c) - { - transform(r, c) = ::atof(mat_elements_str[i++].c_str()); - } - } - } - } - - set_from_transform(transform); -} - void Transformation::reset() { m_offset = Vec3d::Zero(); @@ -1536,6 +1505,33 @@ Transformation Transformation::volume_to_bed_transformation(const Transformation return out; } +// For parsing a transformation matrix from 3MF / AMF. +Transform3d transform3d_from_string(const std::string& transform_str) +{ + Transform3d transform = Transform3d::Identity(); + + if (!transform_str.empty()) + { + std::vector mat_elements_str; + boost::split(mat_elements_str, transform_str, boost::is_any_of(" "), boost::token_compress_on); + + unsigned int size = (unsigned int)mat_elements_str.size(); + if (size == 16) + { + unsigned int i = 0; + for (unsigned int r = 0; r < 4; ++r) + { + for (unsigned int c = 0; c < 4; ++c) + { + transform(r, c) = ::atof(mat_elements_str[i++].c_str()); + } + } + } + } + + return transform; +} + Eigen::Quaterniond rotation_xyz_diff(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to) { return diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index 283d1edb0..1fa15ba63 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -360,7 +360,6 @@ public: void set_mirror(Axis axis, double mirror); void set_from_transform(const Transform3d& transform); - void set_from_string(const std::string& transform_str); void reset(); @@ -385,6 +384,9 @@ private: } }; +// For parsing a transformation matrix from 3MF / AMF. +extern Transform3d transform3d_from_string(const std::string& transform_str); + // Rotation when going from the first coordinate system with rotation rot_xyz_from applied // to a coordinate system with rot_xyz_to applied. extern Eigen::Quaterniond rotation_xyz_diff(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to); diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index f218bfa6c..f0d8e4b06 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -18,6 +18,8 @@ #include "SVG.hpp" #include +#include "GCodeWriter.hpp" +#include "GCode/PreviewData.hpp" namespace Slic3r { @@ -43,7 +45,7 @@ Model& Model::assign_copy(const Model &rhs) } // copy custom code per height - this->custom_gcode_per_height = rhs.custom_gcode_per_height; + this->custom_gcode_per_print_z = rhs.custom_gcode_per_print_z; return *this; } @@ -64,7 +66,7 @@ Model& Model::assign_copy(Model &&rhs) rhs.objects.clear(); // copy custom code per height - this->custom_gcode_per_height = rhs.custom_gcode_per_height; + this->custom_gcode_per_print_z = rhs.custom_gcode_per_print_z; return *this; } @@ -124,6 +126,8 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c if (add_default_instances) model.add_default_instances(); + update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, config); + return model; } @@ -159,6 +163,8 @@ Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig if (add_default_instances) model.add_default_instances(); + update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, config); + return model; } @@ -595,16 +601,15 @@ std::string Model::propose_export_file_name_and_path(const std::string &new_exte std::vector> Model::get_custom_tool_changes(double default_layer_height, size_t num_extruders) const { std::vector> custom_tool_changes; - if (!custom_gcode_per_height.empty()) { - for (const CustomGCode& custom_gcode : custom_gcode_per_height) - if (custom_gcode.gcode == ExtruderChangeCode) { - DynamicPrintConfig config; - // If extruder count in PrinterSettings was changed, use default (0) extruder for extruders, more than num_extruders - config.set_key_value("extruder", new ConfigOptionInt(custom_gcode.extruder > num_extruders ? 0 : custom_gcode.extruder)); - // For correct extruders(tools) changing, we should decrease custom_gcode.height value by one default layer height - custom_tool_changes.push_back({ custom_gcode.height - default_layer_height, config }); - } - } + for (const CustomGCode& custom_gcode : custom_gcode_per_print_z) + if (custom_gcode.gcode == ExtruderChangeCode) { + DynamicPrintConfig config; + // If extruder count in PrinterSettings was changed, use default (0) extruder for extruders, more than num_extruders + config.set_key_value("extruder", new ConfigOptionInt(custom_gcode.extruder > num_extruders ? 0 : custom_gcode.extruder)); + // For correct extruders(tools) changing, we should decrease custom_gcode.height value by one default layer height + custom_tool_changes.push_back({ custom_gcode.print_z - default_layer_height, config }); + } + return custom_tool_changes; } @@ -1304,6 +1309,8 @@ void ModelObject::split(ModelObjectPtrs* new_objects) } new_vol->set_offset(Vec3d::Zero()); + // reset the source to disable reload from disk + new_vol->source = ModelVolume::Source(); new_objects->emplace_back(new_object); delete mesh; } @@ -1359,6 +1366,8 @@ void ModelObject::bake_xy_rotation_into_meshes(size_t instance_idx) model_volume->set_mirror(Vec3d(1., 1., 1.)); // Move the reference point of the volume to compensate for the change of the instance trafo. model_volume->set_offset(volume_offset_correction * volume_trafo.get_offset()); + // reset the source to disable reload from disk + model_volume->source = ModelVolume::Source(); } this->invalidate_bounding_box(); @@ -1670,6 +1679,8 @@ size_t ModelVolume::split(unsigned int max_extruders) this->calculate_convex_hull(); // Assign a new unique ID, so that a new GLVolume will be generated. this->set_new_unique_id(); + // reset the source to disable reload from disk + this->source = ModelVolume::Source(); } else this->object->volumes.insert(this->object->volumes.begin() + (++ivolume), new ModelVolume(object, *this, std::move(*mesh))); @@ -1940,6 +1951,30 @@ extern bool model_has_advanced_features(const Model &model) return false; } +extern void update_custom_gcode_per_print_z_from_config(std::vector& custom_gcode_per_print_z, DynamicPrintConfig* config) +{ + if (!config->has("colorprint_heights")) + return; + + const std::vector& colors = GCodePreviewData::ColorPrintColors(); + + const auto& colorprint_values = config->option("colorprint_heights")->values; + + if (!colorprint_values.empty()) + { + custom_gcode_per_print_z.clear(); + custom_gcode_per_print_z.reserve(colorprint_values.size()); + int i = 0; + for (auto val : colorprint_values) + custom_gcode_per_print_z.emplace_back(Model::CustomGCode{ val, ColorChangeCode, 1, colors[(++i)%7] }); + } + + /* There is one and only place this configuration option is used now. + * It wouldn't be used in the future, so erase it. + * */ + config->erase("colorprint_heights"); +} + #ifndef NDEBUG // Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique. void check_model_ids_validity(const Model &model) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index a9adbe0dc..ed99afaf7 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -470,6 +470,7 @@ public: const Geometry::Transformation& get_transformation() const { return m_transformation; } void set_transformation(const Geometry::Transformation& transformation) { m_transformation = transformation; } + void set_transformation(const Transform3d &trafo) { m_transformation.set_from_transform(trafo); } const Vec3d& get_offset() const { return m_transformation.get_offset(); } double get_offset(Axis axis) const { return m_transformation.get_offset(axis); } @@ -753,33 +754,30 @@ public: // Extensions for color print struct CustomGCode { - CustomGCode(double height, const std::string& code, int extruder, const std::string& color) : - height(height), gcode(code), extruder(extruder), color(color) {} - - bool operator<(const CustomGCode& other) const { return other.height > this->height; } + bool operator<(const CustomGCode& other) const { return other.print_z > this->print_z; } bool operator==(const CustomGCode& other) const { - return (other.height == this->height) && - (other.gcode == this->gcode) && - (other.extruder == this->extruder )&& - (other.color == this->color ); + return (other.print_z == this->print_z ) && + (other.gcode == this->gcode ) && + (other.extruder == this->extruder ) && + (other.color == this->color ); } bool operator!=(const CustomGCode& other) const { - return (other.height != this->height) || - (other.gcode != this->gcode) || - (other.extruder != this->extruder )|| - (other.color != this->color ); + return (other.print_z != this->print_z ) || + (other.gcode != this->gcode ) || + (other.extruder != this->extruder ) || + (other.color != this->color ); } - double height; + double print_z; std::string gcode; int extruder; // 0 - "gcode" will be applied for whole print // else - "gcode" will be applied only for "extruder" print std::string color; // if gcode is equal to PausePrintCode, // this field is used for save a short message shown on Printer display }; - std::vector custom_gcode_per_height; + std::vector custom_gcode_per_print_z; // Default constructor assigns a new ID to the model. Model() { assert(this->id().valid()); } @@ -845,7 +843,7 @@ public: // Propose an output path, replace extension. The new_extension shall contain the initial dot. std::string propose_export_file_name_and_path(const std::string &new_extension) const; - // from custom_gcode_per_height get just tool_change codes + // from custom_gcode_per_print_z get just tool_change codes std::vector> get_custom_tool_changes(double default_layer_height, size_t num_extruders) const; private: @@ -881,6 +879,10 @@ extern bool model_volume_list_changed(const ModelObject &model_object_old, const extern bool model_has_multi_part_objects(const Model &model); // If the model has advanced features, then it cannot be processed in simple mode. extern bool model_has_advanced_features(const Model &model); +/* If loaded configuration has a "colorprint_heights" option (if it was imported from older Slicer), + * then model.custom_gcode_per_print_z should be updated considering this option + * */ +extern void update_custom_gcode_per_print_z_from_config(std::vector& custom_gcode_per_print_z, DynamicPrintConfig* config); #ifndef NDEBUG // Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique. diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index ae32bd2d3..1d4e69763 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -739,10 +739,10 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ model_object_status.emplace(model_object->id(), ModelObjectStatus::Old); // But if custom gcode per layer height was changed - if (m_model.custom_gcode_per_height != model.custom_gcode_per_height) { + if (m_model.custom_gcode_per_print_z != model.custom_gcode_per_print_z) { // we should stop background processing update_apply_status(this->invalidate_step(psGCodeExport)); - m_model.custom_gcode_per_height = model.custom_gcode_per_height; + m_model.custom_gcode_per_print_z = model.custom_gcode_per_print_z; } } else if (model_object_list_extended(m_model, model)) { // Add new objects. Their volumes and configs will be synchronized later. diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 20fb5d08a..ac822b525 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -71,12 +71,6 @@ enum SLAPillarConnectionMode { slapcmDynamic }; -// ys_FIXME ! may be, it's not a best place -// Additional Codes which can be set by user using DoubleSlider -static const std::string ColorChangeCode = "M600"; -static const std::string PausePrintCode = "M601"; -static const std::string ExtruderChangeCode = "tool_change"; - template<> inline const t_config_enum_values& ConfigOptionEnum::get_enum_values() { static t_config_enum_values keys_map; if (keys_map.empty()) { diff --git a/src/libslic3r/SLA/Pad.cpp b/src/libslic3r/SLA/Pad.cpp index 4233bece4..742f0db1b 100644 --- a/src/libslic3r/SLA/Pad.cpp +++ b/src/libslic3r/SLA/Pad.cpp @@ -339,18 +339,15 @@ PadSkeleton divide_blueprint(const ExPolygons &bp) for (ClipperLib::PolyTree::PolyNode *node : ptree.Childs) { ExPolygon poly(ClipperPath_to_Slic3rPolygon(node->Contour)); for (ClipperLib::PolyTree::PolyNode *child : node->Childs) { - if (child->IsHole()) { - poly.holes.emplace_back( - ClipperPath_to_Slic3rPolygon(child->Contour)); + poly.holes.emplace_back( + ClipperPath_to_Slic3rPolygon(child->Contour)); - traverse_pt_unordered(child->Childs, &ret.inner); - } - else traverse_pt_unordered(child, &ret.inner); + traverse_pt(child->Childs, &ret.inner); } ret.outer.emplace_back(poly); } - + return ret; } @@ -432,9 +429,11 @@ public: ExPolygons fullpad = diff_ex(fullcvh, model_bp_sticks); - remove_redundant_parts(fullpad); - PadSkeleton divided = divide_blueprint(fullpad); + + remove_redundant_parts(divided.outer); + remove_redundant_parts(divided.inner); + outer = std::move(divided.outer); inner = std::move(divided.inner); } diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp index 8b5cbf483..e9df4c5b5 100644 --- a/src/libslic3r/ShortestPath.cpp +++ b/src/libslic3r/ShortestPath.cpp @@ -43,6 +43,7 @@ std::vector> chain_segments_closest_point(std::vector> chain_segments_greedy_constrained_reversals else if (num_segments == 1) { // Just sort the end points so that the first point visited is closest to start_near. - out.emplace_back(0, start_near != nullptr && + out.emplace_back(0, could_reverse_func(0) && start_near != nullptr && (end_point_func(0, true) - *start_near).template cast().squaredNorm() < (end_point_func(0, false) - *start_near).template cast().squaredNorm()); } else @@ -999,13 +1000,13 @@ std::vector> chain_extrusion_entities(std::vector const Point& { return first_point ? entities[idx]->first_point() : entities[idx]->last_point(); }; auto could_reverse = [&entities](size_t idx) { const ExtrusionEntity *ee = entities[idx]; return ee->is_loop() || ee->can_reverse(); }; std::vector> out = chain_segments_greedy_constrained_reversals(segment_end_point, could_reverse, entities.size(), start_near); - for (size_t i = 0; i < entities.size(); ++ i) { - ExtrusionEntity *ee = entities[i]; + for (std::pair &segment : out) { + ExtrusionEntity *ee = entities[segment.first]; if (ee->is_loop()) // Ignore reversals for loops, as the start point equals the end point. - out[i].second = false; + segment.second = false; // Is can_reverse() respected by the reversals? - assert(entities[i]->can_reverse() || ! out[i].second); + assert(ee->can_reverse() || ! segment.second); } return out; } diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index 9af6048ab..8199bde03 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -224,38 +224,14 @@ std::vector layer_height_profile_from_ranges( // Based on the work of @platsch // Fill layer_height_profile by heights ensuring a prescribed maximum cusp height. -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -std::vector layer_height_profile_adaptive(const SlicingParameters& slicing_params, - const ModelObject& object, float cusp_value) -#else -std::vector layer_height_profile_adaptive( - const SlicingParameters &slicing_params, - const t_layer_config_ranges & /* layer_config_ranges */, - const ModelVolumePtrs &volumes) -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE +std::vector layer_height_profile_adaptive(const SlicingParameters& slicing_params, const ModelObject& object, float quality_factor) { -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // 1) Initialize the SlicingAdaptive class with the object meshes. SlicingAdaptive as; as.set_slicing_parameters(slicing_params); - as.set_object(object); -#else - // 1) Initialize the SlicingAdaptive class with the object meshes. - SlicingAdaptive as; - as.set_slicing_parameters(slicing_params); - for (const ModelVolume* volume : volumes) - if (volume->is_model_part()) - as.add_mesh(&volume->mesh()); -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE - - as.prepare(); + as.prepare(object); // 2) Generate layers using the algorithm of @platsch - // loop until we have at least one layer and the max slice_z reaches the object height -#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE - double cusp_value = 0.2; -#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE - std::vector layer_height_profile; layer_height_profile.push_back(0.0); layer_height_profile.push_back(slicing_params.first_object_layer_height); @@ -263,39 +239,41 @@ std::vector layer_height_profile_adaptive( layer_height_profile.push_back(slicing_params.first_object_layer_height); layer_height_profile.push_back(slicing_params.first_object_layer_height); } - double slice_z = slicing_params.first_object_layer_height; - int current_facet = 0; -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE - while (slice_z <= slicing_params.object_print_z_height()) { - double height = slicing_params.max_layer_height; -#else - double height = slicing_params.first_object_layer_height; - while ((slice_z - height) <= slicing_params.object_print_z_height()) { - height = 999.0; -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + double print_z = slicing_params.first_object_layer_height; + // last facet visited by the as.next_layer_height() function, where the facets are sorted by their increasing Z span. + size_t current_facet = 0; + // loop until we have at least one layer and the max slice_z reaches the object height + while (print_z + EPSILON < slicing_params.object_print_z_height()) { + float height = slicing_params.max_layer_height; // Slic3r::debugf "\n Slice layer: %d\n", $id; // determine next layer height - double cusp_height = as.cusp_height((float)slice_z, cusp_value, current_facet); + float cusp_height = as.next_layer_height(float(print_z), quality_factor, current_facet); +#if 0 // check for horizontal features and object size - /* - if($self->config->get_value('match_horizontal_surfaces')) { - my $horizontal_dist = $adaptive_slicing[$region_id]->horizontal_facet_distance(scale $slice_z+$cusp_height, $min_height); - if(($horizontal_dist < $min_height) && ($horizontal_dist > 0)) { - Slic3r::debugf "Horizontal feature ahead, distance: %f\n", $horizontal_dist; - # can we shrink the current layer a bit? - if($cusp_height-($min_height-$horizontal_dist) > $min_height) { - # yes we can - $cusp_height = $cusp_height-($min_height-$horizontal_dist); - Slic3r::debugf "Shrink layer height to %f\n", $cusp_height; - }else{ - # no, current layer would become too thin - $cusp_height = $cusp_height+$horizontal_dist; - Slic3r::debugf "Widen layer height to %f\n", $cusp_height; + if (this->config.match_horizontal_surfaces.value) { + coordf_t horizontal_dist = as.horizontal_facet_distance(print_z + height, min_layer_height); + if ((horizontal_dist < min_layer_height) && (horizontal_dist > 0)) { + #ifdef SLIC3R_DEBUG + std::cout << "Horizontal feature ahead, distance: " << horizontal_dist << std::endl; + #endif + // can we shrink the current layer a bit? + if (height-(min_layer_height - horizontal_dist) > min_layer_height) { + // yes we can + height -= (min_layer_height - horizontal_dist); + #ifdef SLIC3R_DEBUG + std::cout << "Shrink layer height to " << height << std::endl; + #endif + } else { + // no, current layer would become too thin + height += horizontal_dist; + #ifdef SLIC3R_DEBUG + std::cout << "Widen layer height to " << height << std::endl; + #endif } } } - */ +#endif height = std::min(cusp_height, height); // apply z-gradation @@ -308,22 +286,22 @@ std::vector layer_height_profile_adaptive( // look for an applicable custom range /* - if (my $range = first { $_->[0] <= $slice_z && $_->[1] > $slice_z } @{$self->layer_height_ranges}) { + if (my $range = first { $_->[0] <= $print_z && $_->[1] > $print_z } @{$self->layer_height_ranges}) { $height = $range->[2]; # if user set custom height to zero we should just skip the range and resume slicing over it if ($height == 0) { - $slice_z += $range->[1] - $range->[0]; + $print_z += $range->[1] - $range->[0]; next; } } */ - layer_height_profile.push_back(slice_z); + layer_height_profile.push_back(print_z); layer_height_profile.push_back(height); - slice_z += height; + print_z += height; #if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE - layer_height_profile.push_back(slice_z); + layer_height_profile.push_back(print_z); layer_height_profile.push_back(height); #endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE } @@ -722,11 +700,7 @@ int generate_layer_height_texture( const Vec3crd &color1 = palette_raw[idx1]; const Vec3crd &color2 = palette_raw[idx2]; coordf_t z = cell_to_z * coordf_t(cell); -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE - assert((lo - EPSILON <= z) && (z <= hi + EPSILON)); -#else - assert(z >= lo && z <= hi); -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + assert(lo - EPSILON <= z && z <= hi + EPSILON); // Intensity profile to visualize the layers. coordf_t intensity = cos(M_PI * 0.7 * (mid - z) / h); // Color mapping from layer height to RGB. diff --git a/src/libslic3r/Slicing.hpp b/src/libslic3r/Slicing.hpp index 03ef7e67d..036344b22 100644 --- a/src/libslic3r/Slicing.hpp +++ b/src/libslic3r/Slicing.hpp @@ -18,12 +18,7 @@ namespace Slic3r class PrintConfig; class PrintObjectConfig; -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE class ModelObject; -#else -class ModelVolume; -typedef std::vector ModelVolumePtrs; -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // Parameters to guide object slicing and support generation. // The slicing parameters account for a raft and whether the 1st object layer is printed with a normal or a bridging flow @@ -142,10 +137,9 @@ extern std::vector layer_height_profile_from_ranges( const SlicingParameters &slicing_params, const t_layer_config_ranges &layer_config_ranges); -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE extern std::vector layer_height_profile_adaptive( const SlicingParameters& slicing_params, - const ModelObject& object, float cusp_value); + const ModelObject& object, float quality_factor); struct HeightProfileSmoothingParams { @@ -159,12 +153,6 @@ struct HeightProfileSmoothingParams extern std::vector smooth_height_profile( const std::vector& profile, const SlicingParameters& slicing_params, const HeightProfileSmoothingParams& smoothing_params); -#else -extern std::vector layer_height_profile_adaptive( - const SlicingParameters &slicing_params, - const t_layer_config_ranges &layer_config_ranges, - const ModelVolumePtrs &volumes); -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE enum LayerHeightEditActionType : unsigned int { LAYER_HEIGHT_EDIT_ACTION_INCREASE = 0, diff --git a/src/libslic3r/SlicingAdaptive.cpp b/src/libslic3r/SlicingAdaptive.cpp index b6ebf1ac0..7ab0c47b2 100644 --- a/src/libslic3r/SlicingAdaptive.cpp +++ b/src/libslic3r/SlicingAdaptive.cpp @@ -1,156 +1,211 @@ #include "libslic3r.h" -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE #include "Model.hpp" -#else #include "TriangleMesh.hpp" -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE #include "SlicingAdaptive.hpp" +#include + +// Based on the work of Florens Waserfall (@platch on github) +// and his paper +// Florens Wasserfall, Norman Hendrich, Jianwei Zhang: +// Adaptive Slicing for the FDM Process Revisited +// 13th IEEE Conference on Automation Science and Engineering (CASE-2017), August 20-23, Xi'an, China. DOI: 10.1109/COASE.2017.8256074 +// https://tams.informatik.uni-hamburg.de/publications/2017/Adaptive%20Slicing%20for%20the%20FDM%20Process%20Revisited.pdf + +// Vojtech believes that there is a bug in @platch's derivation of the triangle area error metric. +// Following Octave code paints graphs of recommended layer height versus surface slope angle. +#if 0 +adeg=0:1:85; +a=adeg*pi/180; +t=tan(a); +tsqr=sqrt(tan(a)); +lerr=1./cos(a); +lerr2=1./(0.3+cos(a)); +plot(adeg, t, 'b', adeg, sqrt(t), 'g', adeg, 0.5 * lerr, 'm', adeg, 0.5 * lerr2, 'r') +xlabel("angle(deg), 0 - horizontal wall, 90 - vertical wall"); +ylabel("layer height"); +legend("tan(a) as cura - topographic lines distance limit", "sqrt(tan(a)) as PrusaSlicer - error triangle area limit", "old slic3r - max distance metric", "new slic3r - Waserfall paper"); +#endif + +#ifndef NDEBUG + #define ADAPTIVE_LAYER_HEIGHT_DEBUG +#endif /* NDEBUG */ + namespace Slic3r { -#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -void SlicingAdaptive::clear() -{ - m_meshes.clear(); - m_faces.clear(); - m_face_normal_z.clear(); -} -#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE - -std::pair face_z_span(const stl_facet *f) +static inline std::pair face_z_span(const stl_facet &f) { return std::pair( - std::min(std::min(f->vertex[0](2), f->vertex[1](2)), f->vertex[2](2)), - std::max(std::max(f->vertex[0](2), f->vertex[1](2)), f->vertex[2](2))); + std::min(std::min(f.vertex[0](2), f.vertex[1](2)), f.vertex[2](2)), + std::max(std::max(f.vertex[0](2), f.vertex[1](2)), f.vertex[2](2))); } -void SlicingAdaptive::prepare() +// By Florens Waserfall aka @platch: +// This constant essentially describes the volumetric error at the surface which is induced +// by stacking "elliptic" extrusion threads. It is empirically determined by +// 1. measuring the surface profile of printed parts to find +// the ratio between layer height and profile height and then +// 2. computing the geometric difference between the model-surface and the elliptic profile. +// +// The definition of the roughness formula is in +// https://tams.informatik.uni-hamburg.de/publications/2017/Adaptive%20Slicing%20for%20the%20FDM%20Process%20Revisited.pdf +// (page 51, formula (8)) +// Currenty @platch's error metric formula is not used. +static constexpr double SURFACE_CONST = 0.18403; + +// for a given facet, compute maximum height within the allowed surface roughness / stairstepping deviation +static inline float layer_height_from_slope(const SlicingAdaptive::FaceZ &face, float max_surface_deviation) { -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE - if (m_object == nullptr) - return; +// @platch's formula, see his paper "Adaptive Slicing for the FDM Process Revisited". +// return float(max_surface_deviation / (SURFACE_CONST + 0.5 * std::abs(normal_z))); + +// Constant stepping in horizontal direction, as used by Cura. +// return (face.n_cos > 1e-5) ? float(max_surface_deviation * face.n_sin / face.n_cos) : FLT_MAX; - m_faces.clear(); - m_face_normal_z.clear(); +// Constant error measured as an area of the surface error triangle, Vojtech's formula. +// return (face.n_cos > 1e-5) ? float(1.44 * max_surface_deviation * sqrt(face.n_sin / face.n_cos)) : FLT_MAX; - m_mesh = m_object->raw_mesh(); - const ModelInstance* first_instance = m_object->instances.front(); - m_mesh.transform(first_instance->get_matrix(), first_instance->is_left_handed()); +// Constant error measured as an area of the surface error triangle, Vojtech's formula with clamping to roughness at 90 degrees. + return std::min(max_surface_deviation / 0.184f, (face.n_cos > 1e-5) ? float(1.44 * max_surface_deviation * sqrt(face.n_sin / face.n_cos)) : FLT_MAX); + +// Constant stepping along the surface, equivalent to the "surface roughness" metric by Perez and later Pandey et all, see @platch's paper for references. +// return float(max_surface_deviation * face.n_sin); +} + +void SlicingAdaptive::clear() +{ + m_faces.clear(); +} + +void SlicingAdaptive::prepare(const ModelObject &object) +{ + this->clear(); + + TriangleMesh mesh = object.raw_mesh(); + const ModelInstance &first_instance = *object.instances.front(); + mesh.transform(first_instance.get_matrix(), first_instance.is_left_handed()); // 1) Collect faces from mesh. - m_faces.reserve(m_mesh.stl.stats.number_of_facets); - for (stl_facet& face : m_mesh.stl.facet_start) - { - face.normal.normalize(); - m_faces.emplace_back(&face); + m_faces.reserve(mesh.stl.stats.number_of_facets); + for (const stl_facet &face : mesh.stl.facet_start) { + Vec3f n = face.normal.normalized(); + m_faces.emplace_back(FaceZ({ face_z_span(face), std::abs(n.z()), std::sqrt(n.x() * n.x() + n.y() * n.y()) })); } -#else - // 1) Collect faces of all meshes. - int nfaces_total = 0; - for (std::vector::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) - nfaces_total += (*it_mesh)->stl.stats.number_of_facets; - m_faces.reserve(nfaces_total); - for (std::vector::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) - for (const stl_facet& face : (*it_mesh)->stl.facet_start) - m_faces.emplace_back(&face); -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // 2) Sort faces lexicographically by their Z span. - std::sort(m_faces.begin(), m_faces.end(), [](const stl_facet *f1, const stl_facet *f2) { return face_z_span(f1) < face_z_span(f2); }); - - // 3) Generate Z components of the facet normals. - m_face_normal_z.assign(m_faces.size(), 0.0f); - for (size_t iface = 0; iface < m_faces.size(); ++ iface) - m_face_normal_z[iface] = m_faces[iface]->normal(2); + std::sort(m_faces.begin(), m_faces.end(), [](const FaceZ &f1, const FaceZ &f2) { return f1.z_span < f2.z_span; }); } -float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet) +// current_facet is in/out parameter, rememebers the index of the last face of m_faces visited, +// where this function will start from. +// print_z - the top print surface of the previous layer. +// returns height of the next layer. +float SlicingAdaptive::next_layer_height(const float print_z, float quality_factor, size_t ¤t_facet) { - float height = (float)m_slicing_params.max_layer_height; - bool first_hit = false; + float height = (float)m_slicing_params.max_layer_height; + + float max_surface_deviation; + + { +#if 0 +// @platch's formula for quality: + double delta_min = SURFACE_CONST * m_slicing_params.min_layer_height; + double delta_mid = (SURFACE_CONST + 0.5) * m_slicing_params.layer_height; + double delta_max = (SURFACE_CONST + 0.5) * m_slicing_params.max_layer_height; +#else +// Vojtech's formula for triangle area error metric. + double delta_min = m_slicing_params.min_layer_height; + double delta_mid = m_slicing_params.layer_height; + double delta_max = m_slicing_params.max_layer_height; +#endif + max_surface_deviation = (quality_factor < 0.5f) ? + lerp(delta_min, delta_mid, 2. * quality_factor) : + lerp(delta_max, delta_mid, 2. * (1. - quality_factor)); + } // find all facets intersecting the slice-layer - int ordered_id = current_facet; - for (; ordered_id < int(m_faces.size()); ++ ordered_id) { - std::pair zspan = face_z_span(m_faces[ordered_id]); - // facet's minimum is higher than slice_z -> end loop - if (zspan.first >= z) - break; - // facet's maximum is higher than slice_z -> store the first event for next cusp_height call to begin at this point - if (zspan.second > z) { - // first event? - if (! first_hit) { - first_hit = true; - current_facet = ordered_id; - } - // skip touching facets which could otherwise cause small cusp values - if (zspan.second <= z + EPSILON) - continue; - // compute cusp-height for this facet and store minimum of all heights - float normal_z = m_face_normal_z[ordered_id]; - height = std::min(height, (normal_z == 0.0f) ? (float)m_slicing_params.max_layer_height : std::abs(cusp_value / normal_z)); - } + size_t ordered_id = current_facet; + { + bool first_hit = false; + for (; ordered_id < m_faces.size(); ++ ordered_id) { + const std::pair &zspan = m_faces[ordered_id].z_span; + // facet's minimum is higher than slice_z -> end loop + if (zspan.first >= print_z) + break; + // facet's maximum is higher than slice_z -> store the first event for next cusp_height call to begin at this point + if (zspan.second > print_z) { + // first event? + if (! first_hit) { + first_hit = true; + current_facet = ordered_id; + } + // skip touching facets which could otherwise cause small cusp values + if (zspan.second < print_z + EPSILON) + continue; + // compute cusp-height for this facet and store minimum of all heights + height = std::min(height, layer_height_from_slope(m_faces[ordered_id], max_surface_deviation)); + } + } } // lower height limit due to printer capabilities height = std::max(height, float(m_slicing_params.min_layer_height)); // check for sloped facets inside the determined layer and correct height if necessary - if (height > m_slicing_params.min_layer_height) { - for (; ordered_id < int(m_faces.size()); ++ ordered_id) { - std::pair zspan = face_z_span(m_faces[ordered_id]); + if (height > float(m_slicing_params.min_layer_height)) { + for (; ordered_id < m_faces.size(); ++ ordered_id) { + const std::pair &zspan = m_faces[ordered_id].z_span; // facet's minimum is higher than slice_z + height -> end loop - if (zspan.first >= z + height) + if (zspan.first >= print_z + height) break; // skip touching facets which could otherwise cause small cusp values - if (zspan.second <= z + EPSILON) + if (zspan.second < print_z + EPSILON) continue; // Compute cusp-height for this facet and check against height. - float normal_z = m_face_normal_z[ordered_id]; - float cusp = (normal_z == 0.0f) ? (float)m_slicing_params.max_layer_height : std::abs(cusp_value / normal_z); + float reduced_height = layer_height_from_slope(m_faces[ordered_id], max_surface_deviation); - float z_diff = zspan.first - z; - - // handle horizontal facets - if (normal_z > 0.999f) { - // Slic3r::debugf "cusp computation, height is reduced from %f", $height; + float z_diff = zspan.first - print_z; + if (reduced_height < z_diff) { + assert(z_diff < height + EPSILON); + // The currently visited triangle's slope limits the next layer height so much, that + // the lowest point of the currently visible triangle is already above the newly proposed layer height. + // This means, that we need to limit the layer height so that the offending newly visited triangle + // is just above of the new layer. +#ifdef ADAPTIVE_LAYER_HEIGHT_DEBUG + BOOST_LOG_TRIVIAL(trace) << "cusp computation, height is reduced from " << height << "to " << z_diff << " due to z-diff"; +#endif /* ADAPTIVE_LAYER_HEIGHT_DEBUG */ height = z_diff; - // Slic3r::debugf "to %f due to near horizontal facet\n", $height; - } else if (cusp > z_diff) { - if (cusp < height) { - // Slic3r::debugf "cusp computation, height is reduced from %f", $height; - height = cusp; - // Slic3r::debugf "to %f due to new cusp height\n", $height; - } - } else { - // Slic3r::debugf "cusp computation, height is reduced from %f", $height; - height = z_diff; - // Slic3r::debugf "to z-diff: %f\n", $height; + } else if (reduced_height < height) { +#ifdef ADAPTIVE_LAYER_HEIGHT_DEBUG + BOOST_LOG_TRIVIAL(trace) << "adaptive layer computation: height is reduced from " << height << "to " << reduced_height << " due to higher facet"; +#endif /* ADAPTIVE_LAYER_HEIGHT_DEBUG */ + height = reduced_height; } } // lower height limit due to printer capabilities again height = std::max(height, float(m_slicing_params.min_layer_height)); } -// Slic3r::debugf "cusp computation, layer-bottom at z:%f, cusp_value:%f, resulting layer height:%f\n", unscale $z, $cusp_value, $height; +#ifdef ADAPTIVE_LAYER_HEIGHT_DEBUG + BOOST_LOG_TRIVIAL(trace) << "adaptive layer computation, layer-bottom at z:" << print_z << ", quality_factor:" << quality_factor << ", resulting layer height:" << height; +#endif /* ADAPTIVE_LAYER_HEIGHT_DEBUG */ return height; } -#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE // Returns the distance to the next horizontal facet in Z-dir // to consider horizontal object features in slice thickness float SlicingAdaptive::horizontal_facet_distance(float z) { for (size_t i = 0; i < m_faces.size(); ++ i) { - std::pair zspan = face_z_span(m_faces[i]); + std::pair zspan = m_faces[i].z_span; // facet's minimum is higher than max forward distance -> end loop if (zspan.first > z + m_slicing_params.max_layer_height) break; // min_z == max_z -> horizontal facet - if ((zspan.first > z) && (zspan.first == zspan.second)) + if (zspan.first > z && zspan.first == zspan.second) return zspan.first - z; } @@ -158,6 +213,5 @@ float SlicingAdaptive::horizontal_facet_distance(float z) return (z + (float)m_slicing_params.max_layer_height > (float)m_slicing_params.object_print_z_height()) ? std::max((float)m_slicing_params.object_print_z_height() - z, 0.f) : (float)m_slicing_params.max_layer_height; } -#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE }; // namespace Slic3r diff --git a/src/libslic3r/SlicingAdaptive.hpp b/src/libslic3r/SlicingAdaptive.hpp index 1d2996986..a296553d6 100644 --- a/src/libslic3r/SlicingAdaptive.hpp +++ b/src/libslic3r/SlicingAdaptive.hpp @@ -5,50 +5,36 @@ #include "Slicing.hpp" #include "admesh/stl.h" -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -#include "TriangleMesh.hpp" -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE namespace Slic3r { -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE class ModelVolume; -#else -class TriangleMesh; -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE class SlicingAdaptive { public: -#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE - void clear(); -#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE - void set_slicing_parameters(SlicingParameters params) { m_slicing_params = params; } -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE - void set_object(const ModelObject& object) { m_object = &object; } -#else - void add_mesh(const TriangleMesh* mesh) { m_meshes.push_back(mesh); } -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE - void prepare(); - float cusp_height(float z, float cusp_value, int ¤t_facet); -#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + void clear(); + void set_slicing_parameters(SlicingParameters params) { m_slicing_params = params; } + void prepare(const ModelObject &object); + // Return next layer height starting from the last print_z, using a quality measure + // (quality in range from 0 to 1, 0 - highest quality at low layer heights, 1 - lowest print quality at high layer heights). + // The layer height curve shall be centered roughly around the default profile's layer height for quality 0.5. + float next_layer_height(const float print_z, float quality, size_t ¤t_facet); float horizontal_facet_distance(float z); -#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE + + struct FaceZ { + std::pair z_span; + // Cosine of the normal vector towards the Z axis. + float n_cos; + // Sine of the normal vector towards the Z axis. + float n_sin; + }; protected: - SlicingParameters m_slicing_params; + SlicingParameters m_slicing_params; -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE - const ModelObject* m_object; - TriangleMesh m_mesh; -#else - std::vector m_meshes; -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE - // Collected faces of all meshes, sorted by raising Z of the bottom most face. - std::vector m_faces; - // Z component of face normals, normalized. - std::vector m_face_normal_z; + std::vector m_faces; }; }; // namespace Slic3r diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 0ec8b36ee..d503f0c64 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -53,4 +53,7 @@ // Enable selection for missing files in reload from disk command #define ENABLE_RELOAD_FROM_DISK_MISSING_SELECTION (1 && ENABLE_2_2_0_ALPHA1) +// Enable closing 3Dconnextion imgui settings dialog by clicking on [X] and [Close] buttons +#define ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG (1 && ENABLE_2_2_0_ALPHA1) + #endif // _technologies_h_ diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index e5fae485a..f825f65e5 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -217,14 +217,6 @@ inline typename CONTAINER_TYPE::value_type& next_value_modulo(typename CONTAINER return container[next_idx_modulo(idx, container.size())]; } -template -inline T exchange(T& obj, U&& new_value) -{ - T old_value = std::move(obj); - obj = std::forward(new_value); - return old_value; -} - extern std::string xml_escape(std::string text); diff --git a/src/platform/osx/Info.plist.in b/src/platform/osx/Info.plist.in index d09f015b9..f4e298180 100644 --- a/src/platform/osx/Info.plist.in +++ b/src/platform/osx/Info.plist.in @@ -116,7 +116,5 @@ NSApplication NSHighResolutionCapable - NSRequiresAquaSystemAppearance - diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index dce7c4c99..f6f80d822 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -191,7 +191,6 @@ Bed3D::Bed3D() : m_type(Custom) , m_custom_texture("") , m_custom_model("") - , m_requires_canvas_update(false) , m_vbo_id(0) , m_scale_factor(1.0f) { @@ -205,7 +204,6 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c std::string cst_texture(custom_texture); if (!cst_texture.empty()) { - std::replace(cst_texture.begin(), cst_texture.end(), '\\', '/'); if ((!boost::algorithm::iends_with(custom_texture, ".png") && !boost::algorithm::iends_with(custom_texture, ".svg")) || !boost::filesystem::exists(custom_texture)) cst_texture = ""; } @@ -214,7 +212,6 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c std::string cst_model(custom_model); if (!cst_model.empty()) { - std::replace(cst_model.begin(), cst_model.end(), '\\', '/'); if (!boost::algorithm::iends_with(custom_model, ".stl") || !boost::filesystem::exists(custom_model)) cst_model = ""; } @@ -438,6 +435,7 @@ void Bed3D::render_texture(const std::string& filename, bool bottom, GLCanvas3D& render_default(bottom); return; } + canvas.request_extra_frame(); } // starts generating the main texture, compression will run asynchronously @@ -457,6 +455,7 @@ void Bed3D::render_texture(const std::string& filename, bool bottom, GLCanvas3D& render_default(bottom); return; } + canvas.request_extra_frame(); } // starts generating the main texture, compression will run asynchronously @@ -481,13 +480,9 @@ void Bed3D::render_texture(const std::string& filename, bool bottom, GLCanvas3D& if (m_temp_texture.get_id() != 0) m_temp_texture.reset(); - m_requires_canvas_update = true; - } - else if (m_requires_canvas_update && m_texture.all_compressed_data_sent_to_gpu()) - m_requires_canvas_update = false; + canvas.request_extra_frame(); - if (m_texture.all_compressed_data_sent_to_gpu() && canvas.is_keeping_dirty()) - canvas.stop_keeping_dirty(); + } if (m_triangles.get_vertices_count() > 0) { diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp index 19ecddc0a..cd2de862e 100644 --- a/src/slic3r/GUI/3DBed.hpp +++ b/src/slic3r/GUI/3DBed.hpp @@ -86,8 +86,6 @@ private: mutable GLTexture m_texture; // temporary texture shown until the main texture has still no levels compressed mutable GLTexture m_temp_texture; - // used to trigger 3D scene update once all compressed textures have been sent to GPU - mutable bool m_requires_canvas_update; mutable Shader m_shader; mutable unsigned int m_vbo_id; mutable GLBed m_model; diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index e587509ac..a8b88dd03 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -94,12 +94,13 @@ void BackgroundSlicingProcess::process_fff() m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data); #endif // ENABLE_THUMBNAIL_GENERATOR - if (m_fff_print->model().custom_gcode_per_height != GUI::wxGetApp().model().custom_gcode_per_height) { - GUI::wxGetApp().model().custom_gcode_per_height = m_fff_print->model().custom_gcode_per_height; - // #ys_FIXME : controll text + /* #ys_FIXME_no_exported_codes + if (m_fff_print->model().custom_gcode_per_print_z != GUI::wxGetApp().model().custom_gcode_per_print_z) { + GUI::wxGetApp().model().custom_gcode_per_print_z = m_fff_print->model().custom_gcode_per_print_z; GUI::show_info(nullptr, _(L("To except of redundant tool manipulation, \n" "Color change(s) for unused extruder(s) was(were) deleted")), _(L("Info"))); } + */ if (this->set_step_started(bspsGCodeFinalize)) { if (! m_export_path.empty()) { diff --git a/src/slic3r/GUI/BedShapeDialog.cpp b/src/slic3r/GUI/BedShapeDialog.cpp index 96a0a59cd..44f5e6021 100644 --- a/src/slic3r/GUI/BedShapeDialog.cpp +++ b/src/slic3r/GUI/BedShapeDialog.cpp @@ -61,9 +61,7 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const Conf { m_shape = default_pt.values; m_custom_texture = custom_texture.value.empty() ? NONE : custom_texture.value; - std::replace(m_custom_texture.begin(), m_custom_texture.end(), '\\', '/'); m_custom_model = custom_model.value.empty() ? NONE : custom_model.value; - std::replace(m_custom_model.begin(), m_custom_model.end(), '\\', '/'); auto sbsizer = new wxStaticBoxSizer(wxVERTICAL, this, _(L("Shape"))); sbsizer->GetStaticBox()->SetFont(wxGetApp().bold_font()); @@ -546,8 +544,6 @@ void BedShapePanel::load_texture() return; } - std::replace(file_name.begin(), file_name.end(), '\\', '/'); - wxBusyCursor wait; m_custom_texture = file_name; @@ -571,8 +567,6 @@ void BedShapePanel::load_model() return; } - std::replace(file_name.begin(), file_name.end(), '\\', '/'); - wxBusyCursor wait; m_custom_model = file_name; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index c8f660e14..ce5609eca 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -133,7 +133,7 @@ GLCanvas3D::LayersEditing::LayersEditing() , m_slicing_parameters(nullptr) , m_layer_height_profile_modified(false) #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE - , m_adaptive_cusp(0.0f) + , m_adaptive_quality(0.5f) #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE , state(Unknown) , band_width(2.0f) @@ -268,24 +268,24 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const ImGui::Separator(); if (imgui.button(_(L("Adaptive")))) - wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), Event(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, m_adaptive_cusp)); + wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), Event(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, m_adaptive_quality)); ImGui::SameLine(); float text_align = ImGui::GetCursorPosX(); ImGui::AlignTextToFramePadding(); - imgui.text(_(L("Cusp (mm)"))); + imgui.text(_(L("Quality / Speed"))); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); - ImGui::TextUnformatted(_(L("I am a tooltip"))); + ImGui::TextUnformatted(_(L("Higher print quality versus higher print speed."))); ImGui::EndTooltip(); } ImGui::SameLine(); float widget_align = ImGui::GetCursorPosX(); ImGui::PushItemWidth(imgui.get_style_scaling() * 120.0f); - m_adaptive_cusp = clamp(0.0f, 0.5f * (float)m_slicing_parameters->layer_height, m_adaptive_cusp); - ImGui::SliderFloat("", &m_adaptive_cusp, 0.0f, 0.5f * (float)m_slicing_parameters->layer_height, "%.3f"); + m_adaptive_quality = clamp(0.0f, 1.f, m_adaptive_quality); + ImGui::SliderFloat("", &m_adaptive_quality, 0.0f, 1.f, "%.2f"); ImGui::Separator(); if (imgui.button(_(L("Smooth")))) @@ -645,10 +645,10 @@ void GLCanvas3D::LayersEditing::reset_layer_height_profile(GLCanvas3D& canvas) } #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE -void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas, float cusp) +void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas, float quality_factor) { this->update_slicing_parameters(); - m_layer_height_profile = layer_height_profile_adaptive(*m_slicing_parameters, *m_model_object, cusp); + m_layer_height_profile = layer_height_profile_adaptive(*m_slicing_parameters, *m_model_object, quality_factor); const_cast(m_model_object)->layer_height_profile = m_layer_height_profile; m_layers_texture.valid = false; canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); @@ -712,11 +712,6 @@ void GLCanvas3D::LayersEditing::update_slicing_parameters() m_slicing_parameters = new SlicingParameters(); *m_slicing_parameters = PrintObject::slicing_parameters(*m_config, *m_model_object, m_object_max_z); } - -#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE - if (m_adaptive_cusp == 0.0f) - m_adaptive_cusp = 0.25f * m_slicing_parameters->layer_height; -#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE } float GLCanvas3D::LayersEditing::thickness_bar_width(const GLCanvas3D &canvas) @@ -1016,24 +1011,25 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_items( const GLCanvas3D std::vector& colors, std::vector& cp_legend_items) { - std::vector custom_gcode_per_height = wxGetApp().plater()->model().custom_gcode_per_height; + std::vector custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z; const int extruders_cnt = wxGetApp().extruders_edited_cnt(); if (extruders_cnt == 1) { - if (custom_gcode_per_height.empty()) { - cp_legend_items.push_back(I18N::translate_utf8(L("Default print color"))); + if (custom_gcode_per_print_z.empty()) { + cp_legend_items.emplace_back(I18N::translate_utf8(L("Default print color"))); colors = colors_in; return; } std::vector> cp_values; + cp_values.reserve(custom_gcode_per_print_z.size()); std::vector print_zs = canvas.get_current_print_zs(true); - for (auto custom_code : custom_gcode_per_height) + for (auto custom_code : custom_gcode_per_print_z) { if (custom_code.gcode != ColorChangeCode) continue; - auto lower_b = std::lower_bound(print_zs.begin(), print_zs.end(), custom_code.height - DoubleSlider::epsilon()); + auto lower_b = std::lower_bound(print_zs.begin(), print_zs.end(), custom_code.print_z - DoubleSlider::epsilon()); if (lower_b == print_zs.end()) continue; @@ -1044,14 +1040,14 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_items( const GLCanvas3D // to avoid duplicate values, check adding values if (cp_values.empty() || !(cp_values.back().first == previous_z && cp_values.back().second == current_z)) - cp_values.push_back(std::pair(previous_z, current_z)); + cp_values.emplace_back(std::pair(previous_z, current_z)); } const auto items_cnt = (int)cp_values.size(); if (items_cnt == 0) // There is no one color change, but there is/are some pause print or custom Gcode { - cp_legend_items.push_back(I18N::translate_utf8(L("Default print color"))); - cp_legend_items.push_back(I18N::translate_utf8(L("Pause print or custom G-code"))); + cp_legend_items.emplace_back(I18N::translate_utf8(L("Default print color"))); + cp_legend_items.emplace_back(I18N::translate_utf8(L("Pause print or custom G-code"))); colors = colors_in; return; } @@ -1060,7 +1056,7 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_items( const GLCanvas3D colors.resize(colors_in.size(), 0.0); ::memcpy((void*)(colors.data()), (const void*)(colors_in.data() + (color_cnt - 1) * 4), 4 * sizeof(float)); - cp_legend_items.push_back(I18N::translate_utf8(L("Pause print or custom G-code"))); + cp_legend_items.emplace_back(I18N::translate_utf8(L("Pause print or custom G-code"))); size_t color_pos = 4; for (int i = items_cnt; i >= 0; --i, color_pos+=4) @@ -1072,15 +1068,15 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_items( const GLCanvas3D std::string id_str = std::to_string(i + 1) + ": "; if (i == 0) { - cp_legend_items.push_back(id_str + (boost::format(I18N::translate_utf8(L("up to %.2f mm"))) % cp_values[0].first).str()); + cp_legend_items.emplace_back(id_str + (boost::format(I18N::translate_utf8(L("up to %.2f mm"))) % cp_values[0].first).str()); break; } if (i == items_cnt) { - cp_legend_items.push_back(id_str + (boost::format(I18N::translate_utf8(L("above %.2f mm"))) % cp_values[i - 1].second).str()); + cp_legend_items.emplace_back(id_str + (boost::format(I18N::translate_utf8(L("above %.2f mm"))) % cp_values[i - 1].second).str()); continue; } - cp_legend_items.push_back(id_str + (boost::format(I18N::translate_utf8(L("%.2f - %.2f mm"))) % cp_values[i - 1].second % cp_values[i].first).str()); + cp_legend_items.emplace_back(id_str + (boost::format(I18N::translate_utf8(L("%.2f - %.2f mm"))) % cp_values[i - 1].second % cp_values[i].first).str()); } } else @@ -1094,20 +1090,20 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_items( const GLCanvas3D size_t color_in_pos = 4 * (color_cnt - 1); for (unsigned int i = 0; i < (unsigned int)extruders_cnt; ++i) - cp_legend_items.push_back((boost::format(I18N::translate_utf8(L("Extruder %d"))) % (i + 1)).str()); + cp_legend_items.emplace_back((boost::format(I18N::translate_utf8(L("Extruder %d"))) % (i + 1)).str()); ::memcpy((void*)(colors.data() + color_pos), (const void*)(colors_in.data() + color_in_pos), 4 * sizeof(float)); color_pos += 4; color_in_pos -= 4; - cp_legend_items.push_back(I18N::translate_utf8(L("Pause print or custom G-code"))); + cp_legend_items.emplace_back(I18N::translate_utf8(L("Pause print or custom G-code"))); - int cnt = custom_gcode_per_height.size(); + int cnt = custom_gcode_per_print_z.size(); for (int i = cnt-1; i >= 0; --i) - if (custom_gcode_per_height[i].gcode == ColorChangeCode) { + if (custom_gcode_per_print_z[i].gcode == ColorChangeCode) { ::memcpy((void*)(colors.data() + color_pos), (const void*)(colors_in.data() + color_in_pos), 4 * sizeof(float)); color_pos += 4; color_in_pos -= 4; - cp_legend_items.push_back((boost::format(I18N::translate_utf8(L("Color change for Extruder %d at %.2f mm"))) % custom_gcode_per_height[i].extruder % custom_gcode_per_height[i].height).str()); + cp_legend_items.emplace_back((boost::format(I18N::translate_utf8(L("Color change for Extruder %d at %.2f mm"))) % custom_gcode_per_print_z[i].extruder % custom_gcode_per_print_z[i].print_z).str()); } } } @@ -1398,7 +1394,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar , m_gizmos(*this) , m_use_clipping_planes(false) , m_sidebar_field("") - , m_keep_dirty(false) + , m_extra_frame_requested(false) , m_config(nullptr) , m_process(nullptr) , m_model(nullptr) @@ -1640,8 +1636,6 @@ void GLCanvas3D::bed_shape_changed() refresh_camera_scene_box(); m_camera.requires_zoom_to_bed = true; m_dirty = true; - if (m_bed.is_prusa()) - start_keeping_dirty(); } void GLCanvas3D::set_color_by(const std::string& value) @@ -1694,10 +1688,10 @@ void GLCanvas3D::reset_layer_height_profile() m_dirty = true; } -void GLCanvas3D::adaptive_layer_height_profile(float cusp) +void GLCanvas3D::adaptive_layer_height_profile(float quality_factor) { wxGetApp().plater()->take_snapshot(_(L("Variable layer height - Adaptive"))); - m_layers_editing.adaptive_layer_height_profile(*this, cusp); + m_layers_editing.adaptive_layer_height_profile(*this, quality_factor); m_layers_editing.state = LayersEditing::Completed; m_dirty = true; } @@ -1931,7 +1925,11 @@ void GLCanvas3D::render() m_camera.debug_render(); #endif // ENABLE_CAMERA_STATISTICS +#if ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG + wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog(*this); +#else wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); +#endif // ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG wxGetApp().imgui()->render(); @@ -2636,9 +2634,10 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) _refresh_if_shown_on_screen(); - if (m_keep_dirty || mouse3d_controller_applied) + if (m_extra_frame_requested || mouse3d_controller_applied) { m_dirty = true; + m_extra_frame_requested = false; evt.RequestMore(); } else @@ -5395,7 +5394,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c // For coloring by a color_print(M600), return a parsed color. bool color_by_color_print() const { return color_print_values!=nullptr; } const size_t color_print_color_idx_by_layer_idx(const size_t layer_idx) const { - const Model::CustomGCode value(layers[layer_idx]->print_z + EPSILON, "", 0, ""); + const Model::CustomGCode value{layers[layer_idx]->print_z + EPSILON, "", 0, ""}; auto it = std::lower_bound(color_print_values->begin(), color_print_values->end(), value); return (it - color_print_values->begin()) % number_tools(); } @@ -5406,7 +5405,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c auto it = std::find_if(color_print_values->begin(), color_print_values->end(), [print_z](const Model::CustomGCode& code) - { return fabs(code.height - print_z) < EPSILON; }); + { return fabs(code.print_z - print_z) < EPSILON; }); if (it != color_print_values->end()) { const std::string& code = it->gcode; @@ -5426,7 +5425,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c } } - const Model::CustomGCode value(print_z + EPSILON, "", 0, ""); + const Model::CustomGCode value{print_z + EPSILON, "", 0, ""}; it = std::lower_bound(color_print_values->begin(), color_print_values->end(), value); while (it != color_print_values->begin()) { diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 8df5fb03b..d68d5bb72 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -185,7 +185,7 @@ private: bool m_layer_height_profile_modified; #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE - mutable float m_adaptive_cusp; + mutable float m_adaptive_quality; mutable HeightProfileSmoothingParams m_smooth_params; #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE @@ -236,8 +236,8 @@ private: void accept_changes(GLCanvas3D& canvas); void reset_layer_height_profile(GLCanvas3D& canvas); #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE - void adaptive_layer_height_profile(GLCanvas3D& canvas, float cusp); - void smooth_layer_height_profile(GLCanvas3D& canvas, const HeightProfileSmoothingParams& smoothing_paramsn); + void adaptive_layer_height_profile(GLCanvas3D& canvas, float quality_factor); + void smooth_layer_height_profile(GLCanvas3D& canvas, const HeightProfileSmoothingParams& smoothing_params); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE static float get_cursor_z_relative(const GLCanvas3D& canvas); @@ -436,7 +436,9 @@ private: bool m_use_clipping_planes; mutable SlaCap m_sla_caps[2]; std::string m_sidebar_field; - bool m_keep_dirty; + // when true renders an extra frame by not resetting m_dirty to false + // see request_extra_frame() + bool m_extra_frame_requested; mutable GLVolumeCollection m_volumes; Selection m_selection; @@ -540,7 +542,7 @@ public: #if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE void reset_layer_height_profile(); - void adaptive_layer_height_profile(float cusp); + void adaptive_layer_height_profile(float quality_factor); void smooth_layer_height_profile(const HeightProfileSmoothingParams& smoothing_params); #endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE @@ -665,9 +667,7 @@ public: void set_cursor(ECursorType type); void msw_rescale(); - bool is_keeping_dirty() const { return m_keep_dirty; } - void start_keeping_dirty() { m_keep_dirty = true; } - void stop_keeping_dirty() { m_keep_dirty = false; } + void request_extra_frame() { m_extra_frame_requested = true; } int get_main_toolbar_item_id(const std::string& name) const { return m_main_toolbar.get_item_id(name); } void force_main_toolbar_left_action(int item_id) { m_main_toolbar.force_left_action(item_id, *this); } diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index e02dd5f6e..6f9e7c0de 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -333,17 +333,12 @@ unsigned GUI_App::get_colour_approx_luma(const wxColour &colour) } bool GUI_App::dark_mode() -{ - const unsigned luma = get_colour_approx_luma(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - return luma < 128; -} - -bool GUI_App::dark_mode_menus() { #if __APPLE__ return mac_dark_mode(); #else - return dark_mode(); + const unsigned luma = get_colour_approx_luma(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + return luma < 128; #endif } diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index a730ff95f..250d8122a 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -109,7 +109,6 @@ public: static unsigned get_colour_approx_luma(const wxColour &colour); static bool dark_mode(); - static bool dark_mode_menus(); void init_label_colours(); void update_label_colours_from_appconfig(); void init_fonts(); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 025262a23..abedc138b 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1193,7 +1193,13 @@ void ObjectList::get_settings_choice(const wxString& category_name) { wxArrayString names; wxArrayInt selections; - wxDataViewItem item = GetSelection(); + + /* If we try to add settings for object/part from 3Dscene, + * for the second try there is selected ItemSettings in ObjectList. + * So, check if selected item isn't SettingsItem. And get a SettingsItem's parent item, if yes + */ + const wxDataViewItem selected_item = GetSelection(); + wxDataViewItem item = m_objects_model->GetItemType(selected_item) & itSettings ? m_objects_model->GetParent(selected_item) : selected_item; const ItemType item_type = m_objects_model->GetItemType(item); @@ -1319,11 +1325,17 @@ void ObjectList::get_freq_settings_choice(const wxString& bundle_name) { std::vector options = get_options_for_bundle(bundle_name); const Selection& selection = scene_selection(); - wxDataViewItem item = GetSelectedItemsCount() > 1 && selection.is_single_full_object() ? - m_objects_model->GetItemById(selection.get_object_idx()) : - GetSelection(); + const wxDataViewItem sel_item = // when all instances in object are selected + GetSelectedItemsCount() > 1 && selection.is_single_full_object() ? + m_objects_model->GetItemById(selection.get_object_idx()) : + GetSelection(); - ItemType item_type = m_objects_model->GetItemType(item); + /* If we try to add settings for object/part from 3Dscene, + * for the second try there is selected ItemSettings in ObjectList. + * So, check if selected item isn't SettingsItem. And get a SettingsItem's parent item, if yes + */ + wxDataViewItem item = m_objects_model->GetItemType(sel_item) & itSettings ? m_objects_model->GetParent(sel_item) : sel_item; + const ItemType item_type = m_objects_model->GetItemType(item); /* Because of we couldn't edited layer_height for ItVolume from settings list, * correct options according to the selected item type : @@ -1547,17 +1559,21 @@ wxMenuItem* ObjectList::append_menu_item_change_type(wxMenu* menu) wxMenuItem* ObjectList::append_menu_item_instance_to_object(wxMenu* menu, wxWindow* parent) { wxMenuItem* menu_item = append_menu_item(menu, wxID_ANY, _(L("Set as a Separated Object")), "", - [this](wxCommandEvent&) { split_instances(); }, "", menu, [](){return wxGetApp().plater()->can_set_instance_to_object(); }, parent); + [this](wxCommandEvent&) { split_instances(); }, "", menu); /* New behavior logic: * 1. Split Object to several separated object, if ALL instances are selected * 2. Separate selected instances from the initial object to the separated object, * if some (not all) instances are selected */ - parent->Bind(wxEVT_UPDATE_UI, [](wxUpdateUIEvent& evt) { - evt.SetText(wxGetApp().plater()->canvas3D()->get_selection().is_single_full_object() ? - _(L("Set as a Separated Objects")) : _(L("Set as a Separated Object"))); - }, menu_item->GetId()); + parent->Bind(wxEVT_UPDATE_UI, [](wxUpdateUIEvent& evt) + { + const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); + evt.SetText(selection.is_single_full_object() ? + _(L("Set as a Separated Objects")) : _(L("Set as a Separated Object"))); + + evt.Enable(wxGetApp().plater()->can_set_instance_to_object()); + }, menu_item->GetId()); return menu_item; } @@ -1608,7 +1624,8 @@ void ObjectList::append_menu_item_export_stl(wxMenu* menu) const void ObjectList::append_menu_item_reload_from_disk(wxMenu* menu) const { append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")), - [this](wxCommandEvent&) { wxGetApp().plater()->reload_from_disk(); }, "", menu, []() { return wxGetApp().plater()->can_reload_from_disk(); }, wxGetApp().plater()); + [this](wxCommandEvent&) { wxGetApp().plater()->reload_from_disk(); }, "", menu, + []() { return wxGetApp().plater()->can_reload_from_disk(); }, wxGetApp().plater()); } void ObjectList::append_menu_item_change_extruder(wxMenu* menu) const @@ -1727,13 +1744,22 @@ wxMenu* ObjectList::create_settings_popupmenu(wxMenu *parent_menu) wxMenu *menu = new wxMenu; settings_menu_hierarchy settings_menu; - const bool is_part = !(m_objects_model->GetItemType(GetSelection()) == itObject || scene_selection().is_single_full_object()); + + /* If we try to add settings for object/part from 3Dscene, + * for the second try there is selected ItemSettings in ObjectList. + * So, check if selected item isn't SettingsItem. And get a SettingsItem's parent item, if yes + */ + const wxDataViewItem selected_item = GetSelection(); + wxDataViewItem item = m_objects_model->GetItemType(selected_item) & itSettings ? m_objects_model->GetParent(selected_item) : selected_item; + + const bool is_part = !(m_objects_model->GetItemType(item) == itObject || scene_selection().is_single_full_object()); get_options_menu(settings_menu, is_part); for (auto cat : settings_menu) { append_menu_item(menu, wxID_ANY, _(cat.first), "", [menu, this](wxCommandEvent& event) { get_settings_choice(menu->GetLabel(event.GetId())); }, - CATEGORY_ICON.find(cat.first) == CATEGORY_ICON.end() ? wxNullBitmap : CATEGORY_ICON.at(cat.first), parent_menu); + CATEGORY_ICON.find(cat.first) == CATEGORY_ICON.end() ? wxNullBitmap : CATEGORY_ICON.at(cat.first), parent_menu, + [this]() { return true; }, wxGetApp().plater()); } return menu; @@ -1753,7 +1779,8 @@ void ObjectList::create_freq_settings_popupmenu(wxMenu *menu, const bool is_obje append_menu_item(menu, wxID_ANY, _(it.first), "", [menu, this](wxCommandEvent& event) { get_freq_settings_choice(menu->GetLabel(event.GetId())); }, - CATEGORY_ICON.find(it.first) == CATEGORY_ICON.end() ? wxNullBitmap : CATEGORY_ICON.at(it.first), menu); + CATEGORY_ICON.find(it.first) == CATEGORY_ICON.end() ? wxNullBitmap : CATEGORY_ICON.at(it.first), menu, + [this]() { return true; }, wxGetApp().plater()); } #if 0 // Add "Quick" settings bundles @@ -1766,7 +1793,8 @@ void ObjectList::create_freq_settings_popupmenu(wxMenu *menu, const bool is_obje append_menu_item(menu, wxID_ANY, wxString::Format(_(L("Quick Add Settings (%s)")), _(it.first)), "", [menu, this](wxCommandEvent& event) { get_freq_settings_choice(menu->GetLabel(event.GetId())); }, - CATEGORY_ICON.find(it.first) == CATEGORY_ICON.end() ? wxNullBitmap : CATEGORY_ICON.at(it.first), menu); + CATEGORY_ICON.find(it.first) == CATEGORY_ICON.end() ? wxNullBitmap : CATEGORY_ICON.at(it.first), menu, + [this]() { return true; }, wxGetApp().plater()); } #endif } @@ -3006,7 +3034,8 @@ void ObjectList::update_selections() else if (selection.is_single_full_object() || selection.is_multiple_full_object()) { const Selection::ObjectIdxsToInstanceIdxsMap& objects_content = selection.get_content(); - if (m_selection_mode & (smSettings | smLayer | smLayerRoot)) + // it's impossible to select Settings, Layer or LayerRoot for several objects + if (!selection.is_multiple_full_object() && (m_selection_mode & (smSettings | smLayer | smLayerRoot))) { auto obj_idx = objects_content.begin()->first; wxDataViewItem obj_item = m_objects_model->GetItemById(obj_idx); @@ -3850,8 +3879,8 @@ void ObjectList::show_multi_selection_menu() GetSelections(sels); for (const wxDataViewItem& item : sels) - if (!(m_objects_model->GetItemType(item) & (itVolume | itObject))) - // show this menu only for Object(s)/Volume(s) selection + if (!(m_objects_model->GetItemType(item) & (itVolume | itObject | itInstance))) + // show this menu only for Objects(Instances mixed with Objects)/Volumes selection return; wxMenu* menu = new wxMenu(); @@ -3861,7 +3890,12 @@ void ObjectList::show_multi_selection_menu() _(L("Select extruder number for selected objects and/or parts")), [this](wxCommandEvent&) { extruder_selection(); }, "", menu); - PopupMenu(menu); + append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")), + [this](wxCommandEvent&) { wxGetApp().plater()->reload_from_disk(); }, "", menu, []() { + return wxGetApp().plater()->can_reload_from_disk(); + }, wxGetApp().plater()); + + wxGetApp().plater()->PopupMenu(menu); } void ObjectList::extruder_selection() @@ -3939,8 +3973,15 @@ void ObjectList::update_after_undo_redo() Plater::SuppressSnapshots suppress(wxGetApp().plater()); // Unselect all objects before deleting them, so that no change of selection is emitted during deletion. - unselect_objects();//this->UnselectAll(); + + /* To avoid execution of selection_changed() + * from wxEVT_DATAVIEW_SELECTION_CHANGED emitted from DeleteAll(), + * wrap this two functions into m_prevent_list_events * + * */ + m_prevent_list_events = true; + this->UnselectAll(); m_objects_model->DeleteAll(); + m_prevent_list_events = false; size_t obj_idx = 0; std::vector obj_idxs; diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 73b23453b..a5a72ad8c 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -366,6 +366,8 @@ public: void update_printable_state(int obj_idx, int instance_idx); void toggle_printable_state(wxDataViewItem item); + void show_multi_selection_menu(); + private: #ifdef __WXOSX__ // void OnChar(wxKeyEvent& event); @@ -384,8 +386,6 @@ private: void OnEditingStarted(wxDataViewEvent &event); #endif /* __WXMSW__ */ void OnEditingDone(wxDataViewEvent &event); - - void show_multi_selection_menu(); void extruder_selection(); void set_extruder_for_selected_items(const int extruder) const ; diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index edb244b34..2eb316be0 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -569,7 +569,7 @@ void Preview::update_view_type(bool slice_completed) { const DynamicPrintConfig& config = wxGetApp().preset_bundle->project_config; - const wxString& choice = !wxGetApp().plater()->model().custom_gcode_per_height.empty() /*&& + const wxString& choice = !wxGetApp().plater()->model().custom_gcode_per_print_z.empty() /*&& (wxGetApp().extruders_edited_cnt()==1 || !slice_completed) */? _(L("Color Print")) : config.option("wiping_volumes_matrix")->values.size() > 1 ? @@ -600,7 +600,7 @@ void Preview::create_double_slider() Bind(wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) { Model& model = wxGetApp().plater()->model(); - model.custom_gcode_per_height = m_slider->GetTicksValues(); + model.custom_gcode_per_print_z = m_slider->GetTicksValues(); m_schedule_background_process(); update_view_type(false); @@ -646,7 +646,7 @@ void Preview::check_slider_values(std::vector& ticks_from_mo ticks_from_model.erase(std::remove_if(ticks_from_model.begin(), ticks_from_model.end(), [layers_z](Model::CustomGCode val) { - auto it = std::lower_bound(layers_z.begin(), layers_z.end(), val.height - DoubleSlider::epsilon()); + auto it = std::lower_bound(layers_z.begin(), layers_z.end(), val.print_z - DoubleSlider::epsilon()); return it == layers_z.end(); }), ticks_from_model.end()); @@ -669,7 +669,7 @@ void Preview::update_double_slider(const std::vector& layers_z, bool kee bool snap_to_min = force_sliders_full_range || m_slider->is_lower_at_min(); bool snap_to_max = force_sliders_full_range || m_slider->is_higher_at_max(); - std::vector &ticks_from_model = wxGetApp().plater()->model().custom_gcode_per_height; + std::vector &ticks_from_model = wxGetApp().plater()->model().custom_gcode_per_print_z; check_slider_values(ticks_from_model, layers_z); m_slider->SetSliderValues(layers_z); @@ -789,7 +789,7 @@ void Preview::load_print_as_fff(bool keep_z_range) colors.push_back("#808080"); // gray color for pause print or custom G-code if (!gcode_preview_data_valid) - color_print_values = wxGetApp().plater()->model().custom_gcode_per_height; + color_print_values = wxGetApp().plater()->model().custom_gcode_per_print_z; } else if (gcode_preview_data_valid || (m_gcode_preview_data->extrusion.view_type == GCodePreviewData::Extrusion::Tool) ) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 4406b1bf5..e98446749 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -136,12 +136,25 @@ void GLGizmoCut::on_render_for_picking() const void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit) { - const float approx_height = m_imgui->scaled(11.0f); - y = std::min(y, bottom_limit - approx_height); - m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); + static float last_y = 0.0f; + static float last_h = 0.0f; m_imgui->begin(_(L("Cut")), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + // adjust window position to avoid overlap the view toolbar + float win_h = ImGui::GetWindowHeight(); + y = std::min(y, bottom_limit - win_h); + ImGui::SetWindowPos(ImVec2(x, y), ImGuiCond_Always); + if ((last_h != win_h) || (last_y != y)) + { + // ask canvas for another frame to render the window in the correct position + m_parent.request_extra_frame(); + if (last_h != win_h) + last_h = win_h; + if (last_y != y) + last_y = y; + } + ImGui::AlignTextToFramePadding(); m_imgui->text("Z"); ImGui::SameLine(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index ddde79c52..350bc8c9e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -749,17 +749,36 @@ void GLGizmoSlaSupports::make_line_segments() const void GLGizmoSlaSupports::on_render_input_window(float x, float y, float bottom_limit) { - if (!m_c->m_model_object) + static float last_y = 0.0f; + static float last_h = 0.0f; + + if (! m_c->m_model_object) return; bool first_run = true; // This is a hack to redraw the button when all points are removed, // so it is not delayed until the background process finishes. RENDER_AGAIN: - const float approx_height = m_imgui->scaled(18.0f); - y = std::min(y, bottom_limit - approx_height); - m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); + //m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); + //const ImVec2 window_size(m_imgui->scaled(18.f, 16.f)); + //ImGui::SetNextWindowPos(ImVec2(x, y - std::max(0.f, y+window_size.y-bottom_limit) )); + //ImGui::SetNextWindowSize(ImVec2(window_size)); + m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); + // adjust window position to avoid overlap the view toolbar + float win_h = ImGui::GetWindowHeight(); + y = std::min(y, bottom_limit - win_h); + ImGui::SetWindowPos(ImVec2(x, y), ImGuiCond_Always); + if ((last_h != win_h) || (last_y != y)) + { + // ask canvas for another frame to render the window in the correct position + m_parent.request_extra_frame(); + if (last_h != win_h) + last_h = win_h; + if (last_y != y) + last_y = y; + } + // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that: const float settings_sliders_left = std::max(m_imgui->calc_text_size(m_desc.at("minimal_distance")).x, m_imgui->calc_text_size(m_desc.at("points_density")).x) + m_imgui->scaled(1.f); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index e089de3fc..2501a9633 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -20,10 +20,6 @@ GLGizmosManager::GLGizmosManager(GLCanvas3D& parent) , m_enabled(false) , m_icons_texture_dirty(true) , m_current(Undefined) - , m_overlay_icons_size(Default_Icons_Size) - , m_overlay_scale(1.0f) - , m_overlay_border(5.0f) - , m_overlay_gap_y(5.0f) , m_tooltip("") , m_serializing(false) { @@ -53,19 +49,18 @@ size_t GLGizmosManager::get_gizmo_idx_from_mouse(const Vec2d& mouse_pos) const return Undefined; float cnv_h = (float)m_parent.get_canvas_size().get_height(); - float height = get_total_overlay_height(); - float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; - float scaled_border = m_overlay_border * m_overlay_scale; - float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; - float scaled_stride_y = scaled_icons_size + scaled_gap_y; - float top_y = 0.5f * (cnv_h - height) + scaled_border; + float height = get_scaled_total_height(); + float icons_size = m_layout.scaled_icons_size(); + float border = m_layout.scaled_border(); + float stride_y = m_layout.scaled_stride_y(); + float top_y = 0.5f * (cnv_h - height) + border; // is mouse horizontally in the area? - if ((scaled_border <= (float)mouse_pos(0) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size))) { + if ((border <= (float)mouse_pos(0) && ((float)mouse_pos(0) <= border + icons_size))) { // which icon is it on? - size_t from_top = (size_t)((float)mouse_pos(1) - top_y)/scaled_stride_y; + size_t from_top = (size_t)((float)mouse_pos(1) - top_y) / stride_y; // is it really on the icon or already past the border? - if ((float)mouse_pos(1) <= top_y + from_top*scaled_stride_y + scaled_icons_size) { + if ((float)mouse_pos(1) <= top_y + from_top * stride_y + icons_size) { std::vector selectable = get_selectable_idxs(); if (from_top < selectable.size()) return selectable[from_top]; @@ -113,18 +108,18 @@ bool GLGizmosManager::init() void GLGizmosManager::set_overlay_icon_size(float size) { - if (m_overlay_icons_size != size) + if (m_layout.icons_size != size) { - m_overlay_icons_size = size; + m_layout.icons_size = size; m_icons_texture_dirty = true; } } void GLGizmosManager::set_overlay_scale(float scale) { - if (m_overlay_scale != scale) + if (m_layout.scale != scale) { - m_overlay_scale = scale; + m_layout.scale = scale; m_icons_texture_dirty = true; } } @@ -873,26 +868,27 @@ void GLGizmosManager::do_render_overlay() const float zoom = (float)m_parent.get_camera().get_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - float height = get_total_overlay_height(); - float width = get_total_overlay_width(); - float scaled_border = m_overlay_border * m_overlay_scale * inv_zoom; + float height = get_scaled_total_height(); + float width = get_scaled_total_width(); + float zoomed_border = m_layout.scaled_border() * inv_zoom; - float top_x = (-0.5f * cnv_w) * inv_zoom; - float top_y = (0.5f * height) * inv_zoom; + float zoomed_top_x = (-0.5f * cnv_w) * inv_zoom; + float zoomed_top_y = (0.5f * height) * inv_zoom; - float left = top_x; - float top = top_y; - float right = left + width * inv_zoom; - float bottom = top - height * inv_zoom; + float zoomed_left = zoomed_top_x; + float zoomed_top = zoomed_top_y; + float zoomed_right = zoomed_left + width * inv_zoom; + float zoomed_bottom = zoomed_top - height * inv_zoom; - render_background(left, top, right, bottom, scaled_border); + render_background(zoomed_left, zoomed_top, zoomed_right, zoomed_bottom, zoomed_border); - top_x += scaled_border; - top_y -= scaled_border; - float scaled_gap_y = m_overlay_gap_y * m_overlay_scale * inv_zoom; + zoomed_top_x += zoomed_border; + zoomed_top_y -= zoomed_border; + + float icons_size = m_layout.scaled_icons_size(); + float zoomed_icons_size = icons_size * inv_zoom; + float zoomed_stride_y = m_layout.scaled_stride_y() * inv_zoom; - float scaled_icons_size = m_overlay_icons_size * m_overlay_scale * inv_zoom; - float scaled_stride_y = scaled_icons_size + scaled_gap_y; unsigned int icons_texture_id = m_icons_texture.get_id(); int tex_width = m_icons_texture.get_width(); int tex_height = m_icons_texture.get_height(); @@ -913,53 +909,36 @@ void GLGizmosManager::do_render_overlay() const int icon_idx = m_current == idx ? 2 : (m_hover == idx ? 1 : 0); #endif // ENABLE_GIZMO_ICONS_NON_ACTIVABLE_STATE - float u_icon_size = m_overlay_icons_size * m_overlay_scale * inv_tex_width; - float v_icon_size = m_overlay_icons_size * m_overlay_scale * inv_tex_height; + float u_icon_size = icons_size * inv_tex_width; + float v_icon_size = icons_size * inv_tex_height; + float v_top = sprite_id * v_icon_size; float u_left = icon_idx * u_icon_size; float v_bottom = v_top + v_icon_size; float u_right = u_left + u_icon_size; - GLTexture::render_sub_texture(icons_texture_id, top_x, top_x + scaled_icons_size, top_y - scaled_icons_size, top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } }); + GLTexture::render_sub_texture(icons_texture_id, zoomed_top_x, zoomed_top_x + zoomed_icons_size, zoomed_top_y - zoomed_icons_size, zoomed_top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } }); if (idx == m_current) { float toolbar_top = cnv_h - m_parent.get_view_toolbar_height(); - gizmo->render_input_window(width, 0.5f * cnv_h - top_y * zoom, toolbar_top); + gizmo->render_input_window(width, 0.5f * cnv_h - zoomed_top_y * zoom, toolbar_top); } - top_y -= scaled_stride_y; + zoomed_top_y -= zoomed_stride_y; } } -float GLGizmosManager::get_total_overlay_height() const +float GLGizmosManager::get_scaled_total_height() const { - float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; - float scaled_border = m_overlay_border * m_overlay_scale; - float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; - float scaled_stride_y = scaled_icons_size + scaled_gap_y; - float height = 2.0f * scaled_border; - - /*for (size_t idx=0; idxis_selectable()) - continue; - - height += scaled_stride_y; - }*/ - height += get_selectable_idxs().size() * scaled_stride_y; - - return height - scaled_gap_y; + return m_layout.scale * (2.0f * m_layout.border + (float)get_selectable_idxs().size() * m_layout.stride_y() - m_layout.gap_y); } -float GLGizmosManager::get_total_overlay_width() const +float GLGizmosManager::get_scaled_total_width() const { - return (2.0f * m_overlay_border + m_overlay_icons_size) * m_overlay_scale; + return 2.0f * m_layout.scaled_border() + m_layout.scaled_icons_size(); } GLGizmoBase* GLGizmosManager::get_current() const { - if (m_current==Undefined || m_gizmos.empty()) - return nullptr; - else - return m_gizmos[m_current].get(); + return ((m_current == Undefined) || m_gizmos.empty()) ? nullptr : m_gizmos[m_current].get(); } bool GLGizmosManager::generate_icons_texture() const @@ -984,7 +963,7 @@ bool GLGizmosManager::generate_icons_texture() const states.push_back(std::make_pair(2, false)); // Disabled #endif // ENABLE_GIZMO_ICONS_NON_ACTIVABLE_STATE - unsigned int sprite_size_px = (unsigned int)(m_overlay_icons_size * m_overlay_scale); + unsigned int sprite_size_px = (unsigned int)m_layout.scaled_icons_size(); // // force even size // if (sprite_size_px % 2 != 0) // sprite_size_px += 1; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index f816056a0..bab084cd2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -65,12 +65,28 @@ public: }; private: + struct Layout + { + float scale{ 1.0f }; + float icons_size{ Default_Icons_Size }; + float border{ 5.0f }; + float gap_y{ 5.0f }; + + float stride_y() const { return icons_size + gap_y;} + + float scaled_icons_size() const { return scale * icons_size; } + float scaled_border() const { return scale * border; } + float scaled_gap_y() const { return scale * gap_y; } + float scaled_stride_y() const { return scale * stride_y(); } + }; + GLCanvas3D& m_parent; bool m_enabled; std::vector> m_gizmos; mutable GLTexture m_icons_texture; mutable bool m_icons_texture_dirty; BackgroundTexture m_background_texture; + Layout m_layout; EType m_current; EType m_hover; @@ -80,11 +96,6 @@ private: void activate_gizmo(EType type); - float m_overlay_icons_size; - float m_overlay_scale; - float m_overlay_border; - float m_overlay_gap_y; - struct MouseCapture { bool left; @@ -205,8 +216,8 @@ private: void render_background(float left, float top, float right, float bottom, float border) const; void do_render_overlay() const; - float get_total_overlay_height() const; - float get_total_overlay_width() const; + float get_scaled_total_height() const; + float get_scaled_total_width() const; bool generate_icons_texture() const; diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 33e526083..90ef017fc 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -254,6 +254,16 @@ bool ImGuiWrapper::begin(const wxString &name, int flags) return begin(into_u8(name), flags); } +bool ImGuiWrapper::begin(const std::string& name, bool* close, int flags) +{ + return ImGui::Begin(name.c_str(), close, (ImGuiWindowFlags)flags); +} + +bool ImGuiWrapper::begin(const wxString& name, bool* close, int flags) +{ + return begin(into_u8(name), close, flags); +} + void ImGuiWrapper::end() { ImGui::End(); diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 7cce60367..5118af036 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -56,6 +56,8 @@ public: bool begin(const std::string &name, int flags = 0); bool begin(const wxString &name, int flags = 0); + bool begin(const std::string& name, bool* close, int flags = 0); + bool begin(const wxString& name, bool* close, int flags = 0); void end(); bool button(const wxString &label); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 9e70fbaa2..4ae03792b 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -380,16 +380,6 @@ void MainFrame::on_dpi_changed(const wxRect &suggested_rect) this->Maximize(is_maximized); } -static std::string menu_icon(const std::string& icon_name) -{ -#ifdef __WXMSW__ - const std::string folder = "white\\"; -#else - const std::string folder = "white/"; -#endif - return wxGetApp().dark_mode_menus() ? folder+icon_name : icon_name; -} - void MainFrame::init_menubar() { #ifdef __APPLE__ @@ -403,7 +393,7 @@ void MainFrame::init_menubar() [this](wxCommandEvent&) { if (m_plater) m_plater->new_project(); }, "", nullptr, [this](){return m_plater != nullptr && can_start_new_project(); }, this); append_menu_item(fileMenu, wxID_ANY, _(L("&Open Project")) + dots + "\tCtrl+O", _(L("Open a project file")), - [this](wxCommandEvent&) { if (m_plater) m_plater->load_project(); }, menu_icon("open"), nullptr, + [this](wxCommandEvent&) { if (m_plater) m_plater->load_project(); }, "open", nullptr, [this](){return m_plater != nullptr; }, this); wxMenu* recent_projects_menu = new wxMenu(); @@ -441,60 +431,65 @@ void MainFrame::init_menubar() Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(m_recent_projects.GetCount() > 0); }, recent_projects_submenu->GetId()); append_menu_item(fileMenu, wxID_ANY, _(L("&Save Project")) + "\tCtrl+S", _(L("Save current project file")), - [this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(into_path(m_plater->get_project_filename(".3mf"))); }, menu_icon("save"), nullptr, + [this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(into_path(m_plater->get_project_filename(".3mf"))); }, "save", nullptr, [this](){return m_plater != nullptr && can_save(); }, this); #ifdef __APPLE__ append_menu_item(fileMenu, wxID_ANY, _(L("Save Project &as")) + dots + "\tCtrl+Shift+S", _(L("Save current project file as")), #else append_menu_item(fileMenu, wxID_ANY, _(L("Save Project &as")) + dots + "\tCtrl+Alt+S", _(L("Save current project file as")), #endif // __APPLE__ - [this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(); }, menu_icon("save"), nullptr, + [this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(); }, "save", nullptr, [this](){return m_plater != nullptr && can_save(); }, this); fileMenu->AppendSeparator(); wxMenu* import_menu = new wxMenu(); append_menu_item(import_menu, wxID_ANY, _(L("Import STL/OBJ/AM&F/3MF")) + dots + "\tCtrl+I", _(L("Load a model")), - [this](wxCommandEvent&) { if (m_plater) m_plater->add_model(); }, menu_icon("import_plater"), nullptr, + [this](wxCommandEvent&) { if (m_plater) m_plater->add_model(); }, "import_plater", nullptr, [this](){return m_plater != nullptr; }, this); import_menu->AppendSeparator(); append_menu_item(import_menu, wxID_ANY, _(L("Import &Config")) + dots + "\tCtrl+L", _(L("Load exported configuration file")), - [this](wxCommandEvent&) { load_config_file(); }, menu_icon("import_config")); + [this](wxCommandEvent&) { load_config_file(); }, "import_config", nullptr, + [this]() {return true; }, this); append_menu_item(import_menu, wxID_ANY, _(L("Import Config from &project")) + dots +"\tCtrl+Alt+L", _(L("Load configuration from project file")), - [this](wxCommandEvent&) { if (m_plater) m_plater->extract_config_from_project(); }, menu_icon("import_config")); + [this](wxCommandEvent&) { if (m_plater) m_plater->extract_config_from_project(); }, "import_config", nullptr, + [this]() {return true; }, this); import_menu->AppendSeparator(); append_menu_item(import_menu, wxID_ANY, _(L("Import Config &Bundle")) + dots, _(L("Load presets from a bundle")), - [this](wxCommandEvent&) { load_configbundle(); }, menu_icon("import_config_bundle")); + [this](wxCommandEvent&) { load_configbundle(); }, "import_config_bundle", nullptr, + [this]() {return true; }, this); append_submenu(fileMenu, import_menu, wxID_ANY, _(L("&Import")), ""); wxMenu* export_menu = new wxMenu(); wxMenuItem* item_export_gcode = append_menu_item(export_menu, wxID_ANY, _(L("Export &G-code")) + dots +"\tCtrl+G", _(L("Export current plate as G-code")), - [this](wxCommandEvent&) { if (m_plater) m_plater->export_gcode(); }, menu_icon("export_gcode"), nullptr, + [this](wxCommandEvent&) { if (m_plater) m_plater->export_gcode(); }, "export_gcode", nullptr, [this](){return can_export_gcode(); }, this); m_changeable_menu_items.push_back(item_export_gcode); wxMenuItem* item_send_gcode = append_menu_item(export_menu, wxID_ANY, _(L("S&end G-code")) + dots +"\tCtrl+Shift+G", _(L("Send to print current plate as G-code")), - [this](wxCommandEvent&) { if (m_plater) m_plater->send_gcode(); }, menu_icon("export_gcode"), nullptr, + [this](wxCommandEvent&) { if (m_plater) m_plater->send_gcode(); }, "export_gcode", nullptr, [this](){return can_send_gcode(); }, this); m_changeable_menu_items.push_back(item_send_gcode); export_menu->AppendSeparator(); append_menu_item(export_menu, wxID_ANY, _(L("Export plate as &STL")) + dots, _(L("Export current plate as STL")), - [this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(); }, menu_icon("export_plater"), nullptr, + [this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(); }, "export_plater", nullptr, [this](){return can_export_model(); }, this); append_menu_item(export_menu, wxID_ANY, _(L("Export plate as STL &including supports")) + dots, _(L("Export current plate as STL including supports")), - [this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(true); }, menu_icon("export_plater"), nullptr, + [this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(true); }, "export_plater", nullptr, [this](){return can_export_supports(); }, this); append_menu_item(export_menu, wxID_ANY, _(L("Export plate as &AMF")) + dots, _(L("Export current plate as AMF")), - [this](wxCommandEvent&) { if (m_plater) m_plater->export_amf(); }, menu_icon("export_plater"), nullptr, + [this](wxCommandEvent&) { if (m_plater) m_plater->export_amf(); }, "export_plater", nullptr, [this](){return can_export_model(); }, this); export_menu->AppendSeparator(); append_menu_item(export_menu, wxID_ANY, _(L("Export &toolpaths as OBJ")) + dots, _(L("Export toolpaths as OBJ")), - [this](wxCommandEvent&) { if (m_plater) m_plater->export_toolpaths_to_obj(); }, menu_icon("export_plater"), nullptr, + [this](wxCommandEvent&) { if (m_plater) m_plater->export_toolpaths_to_obj(); }, "export_plater", nullptr, [this]() {return can_export_toolpaths(); }, this); export_menu->AppendSeparator(); append_menu_item(export_menu, wxID_ANY, _(L("Export &Config")) +dots +"\tCtrl+E", _(L("Export current configuration to file")), - [this](wxCommandEvent&) { export_config(); }, menu_icon("export_config")); + [this](wxCommandEvent&) { export_config(); }, "export_config", nullptr, + [this]() {return true; }, this); append_menu_item(export_menu, wxID_ANY, _(L("Export Config &Bundle")) + dots, _(L("Export all presets to file")), - [this](wxCommandEvent&) { export_configbundle(); }, menu_icon("export_config_bundle")); + [this](wxCommandEvent&) { export_configbundle(); }, "export_config_bundle", nullptr, + [this]() {return true; }, this); append_submenu(fileMenu, export_menu, wxID_ANY, _(L("&Export")), ""); fileMenu->AppendSeparator(); @@ -522,11 +517,12 @@ void MainFrame::init_menubar() fileMenu->AppendSeparator(); #endif m_menu_item_reslice_now = append_menu_item(fileMenu, wxID_ANY, _(L("(Re)Slice No&w")) + "\tCtrl+R", _(L("Start new slicing process")), - [this](wxCommandEvent&) { reslice_now(); }, menu_icon("re_slice"), nullptr, + [this](wxCommandEvent&) { reslice_now(); }, "re_slice", nullptr, [this](){return m_plater != nullptr && can_reslice(); }, this); fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_ANY, _(L("&Repair STL file")) + dots, _(L("Automatically repair an STL file")), - [this](wxCommandEvent&) { repair_stl(); }, menu_icon("wrench")); + [this](wxCommandEvent&) { repair_stl(); }, "wrench", nullptr, + [this]() {return true; }, this); fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_EXIT, _(L("&Quit")), wxString::Format(_(L("Quit %s")), SLIC3R_APP_NAME), [this](wxCommandEvent&) { Close(false); }); @@ -562,10 +558,10 @@ void MainFrame::init_menubar() editMenu->AppendSeparator(); append_menu_item(editMenu, wxID_ANY, _(L("&Delete selected")) + sep + hotkey_delete, _(L("Deletes the current selection")),[this](wxCommandEvent&) { m_plater->remove_selected(); }, - menu_icon("remove_menu"), nullptr, [this](){return can_delete(); }, this); + "remove_menu", nullptr, [this](){return can_delete(); }, this); append_menu_item(editMenu, wxID_ANY, _(L("Delete &all")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + hotkey_delete, _(L("Deletes all objects")), [this](wxCommandEvent&) { m_plater->reset_with_confirm(); }, - menu_icon("delete_all_menu"), nullptr, [this](){return can_delete_all(); }, this); + "delete_all_menu", nullptr, [this](){return can_delete_all(); }, this); editMenu->AppendSeparator(); append_menu_item(editMenu, wxID_ANY, _(L("&Undo")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "Z", @@ -578,10 +574,10 @@ void MainFrame::init_menubar() editMenu->AppendSeparator(); append_menu_item(editMenu, wxID_ANY, _(L("&Copy")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "C", _(L("Copy selection to clipboard")), [this](wxCommandEvent&) { m_plater->copy_selection_to_clipboard(); }, - menu_icon("copy_menu"), nullptr, [this](){return m_plater->can_copy_to_clipboard(); }, this); + "copy_menu", nullptr, [this](){return m_plater->can_copy_to_clipboard(); }, this); append_menu_item(editMenu, wxID_ANY, _(L("&Paste")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "V", _(L("Paste clipboard")), [this](wxCommandEvent&) { m_plater->paste_from_clipboard(); }, - menu_icon("paste_menu"), nullptr, [this](){return m_plater->can_paste_from_clipboard(); }, this); + "paste_menu", nullptr, [this](){return m_plater->can_paste_from_clipboard(); }, this); } // Window menu @@ -590,26 +586,30 @@ void MainFrame::init_menubar() size_t tab_offset = 0; if (m_plater) { append_menu_item(windowMenu, wxID_HIGHEST + 1, _(L("&Plater Tab")) + "\tCtrl+1", _(L("Show the plater")), - [this](wxCommandEvent&) { select_tab(0); }, menu_icon("plater")); + [this](wxCommandEvent&) { select_tab(0); }, "plater", nullptr, + [this]() {return true; }, this); tab_offset += 1; } if (tab_offset > 0) { windowMenu->AppendSeparator(); } append_menu_item(windowMenu, wxID_HIGHEST + 2, _(L("P&rint Settings Tab")) + "\tCtrl+2", _(L("Show the print settings")), - [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 0); }, menu_icon("cog")); + [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 0); }, "cog", nullptr, + [this]() {return true; }, this); wxMenuItem* item_material_tab = append_menu_item(windowMenu, wxID_HIGHEST + 3, _(L("&Filament Settings Tab")) + "\tCtrl+3", _(L("Show the filament settings")), - [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 1); }, menu_icon("spool")); + [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 1); }, "spool", nullptr, + [this]() {return true; }, this); m_changeable_menu_items.push_back(item_material_tab); append_menu_item(windowMenu, wxID_HIGHEST + 4, _(L("Print&er Settings Tab")) + "\tCtrl+4", _(L("Show the printer settings")), - [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 2); }, menu_icon("printer")); + [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 2); }, "printer", nullptr, + [this]() {return true; }, this); if (m_plater) { windowMenu->AppendSeparator(); append_menu_item(windowMenu, wxID_HIGHEST + 5, _(L("3&D")) + "\tCtrl+5", _(L("Show the 3D editing view")), - [this](wxCommandEvent&) { m_plater->select_view_3D("3D"); }, menu_icon("editor_menu"), nullptr, + [this](wxCommandEvent&) { m_plater->select_view_3D("3D"); }, "editor_menu", nullptr, [this](){return can_change_view(); }, this); append_menu_item(windowMenu, wxID_HIGHEST + 6, _(L("Pre&view")) + "\tCtrl+6", _(L("Show the 3D slices preview")), - [this](wxCommandEvent&) { m_plater->select_view_3D("Preview"); }, menu_icon("preview_menu"), nullptr, + [this](wxCommandEvent&) { m_plater->select_view_3D("Preview"); }, "preview_menu", nullptr, [this](){return can_change_view(); }, this); } @@ -628,7 +628,8 @@ void MainFrame::init_menubar() windowMenu->AppendSeparator(); append_menu_item(windowMenu, wxID_ANY, _(L("Print &Host Upload Queue")) + "\tCtrl+J", _(L("Display the Print Host Upload Queue window")), - [this](wxCommandEvent&) { m_printhost_queue_dlg->Show(); }, menu_icon("upload_queue")); + [this](wxCommandEvent&) { m_printhost_queue_dlg->Show(); }, "upload_queue", nullptr, + [this]() {return true; }, this); } // View menu @@ -727,7 +728,7 @@ void MainFrame::update_menubar() m_changeable_menu_items[miSend] ->SetItemLabel((is_fff ? _(L("S&end G-code")) : _(L("S&end to print"))) + dots + "\tCtrl+Shift+G"); m_changeable_menu_items[miMaterialTab] ->SetItemLabel((is_fff ? _(L("&Filament Settings Tab")) : _(L("Mate&rial Settings Tab"))) + "\tCtrl+3"); - m_changeable_menu_items[miMaterialTab] ->SetBitmap(create_scaled_bitmap(this, menu_icon(is_fff ? "spool": "resin"))); + m_changeable_menu_items[miMaterialTab] ->SetBitmap(create_scaled_bitmap(this, is_fff ? "spool": "resin")); } // To perform the "Quck Slice", "Quick Slice and Save As", "Repeat last Quick Slice" and "Slice to SVG". diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index b3d7c0e4f..ad5be88d8 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -5,6 +5,9 @@ #include "GUI_App.hpp" #include "PresetBundle.hpp" #include "AppConfig.hpp" +#if ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG +#include "GLCanvas3D.hpp" +#endif // ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG #include @@ -184,7 +187,10 @@ Mouse3DController::Mouse3DController() , m_device(nullptr) , m_device_str("") , m_running(false) - , m_settings_dialog(false) + , m_show_settings_dialog(false) +#if ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG + , m_settings_dialog_closed_by_user(false) +#endif // ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG { m_last_time = std::chrono::high_resolution_clock::now(); } @@ -229,8 +235,11 @@ bool Mouse3DController::apply(Camera& camera) if (!m_running && is_device_connected()) { disconnect_device(); - // hides the settings dialog if the user re-plug the device - m_settings_dialog = false; + // hides the settings dialog if the user un-plug the device + m_show_settings_dialog = false; +#if ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG + m_settings_dialog_closed_by_user = false; +#endif // ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG } // check if the user plugged the device @@ -240,88 +249,144 @@ bool Mouse3DController::apply(Camera& camera) return is_device_connected() ? m_state.apply(camera) : false; } +#if ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG +void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const +#else void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsigned int canvas_height) const +#endif // ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG { - if (!m_running || !m_settings_dialog) + if (!m_running || !m_show_settings_dialog) return; - ImGuiWrapper& imgui = *wxGetApp().imgui(); - - imgui.set_next_window_pos(0.5f * (float)canvas_width, 0.5f * (float)canvas_height, ImGuiCond_Always, 0.5f, 0.5f); - - imgui.begin(_(L("3Dconnexion settings")), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse); - - const ImVec4& color = ImGui::GetStyleColorVec4(ImGuiCol_Separator); - ImGui::PushStyleColor(ImGuiCol_Text, color); - imgui.text(_(L("Device:"))); - ImGui::PopStyleColor(); - ImGui::SameLine(); - imgui.text(m_device_str); - - ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, color); - imgui.text(_(L("Speed:"))); - ImGui::PopStyleColor(); - - float translation_scale = (float)m_state.get_translation_scale() / State::DefaultTranslationScale; - if (imgui.slider_float(_(L("Translation")) + "##1", &translation_scale, 0.5f, 2.0f, "%.1f")) - m_state.set_translation_scale(State::DefaultTranslationScale * (double)translation_scale); - - float rotation_scale = m_state.get_rotation_scale() / State::DefaultRotationScale; - if (imgui.slider_float(_(L("Rotation")) + "##1", &rotation_scale, 0.5f, 2.0f, "%.1f")) - m_state.set_rotation_scale(State::DefaultRotationScale * rotation_scale); - - ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, color); - imgui.text(_(L("Deadzone:"))); - ImGui::PopStyleColor(); - - float translation_deadzone = (float)m_state.get_translation_deadzone(); - if (imgui.slider_float(_(L("Translation")) + "##2", &translation_deadzone, 0.0f, (float)State::MaxTranslationDeadzone, "%.2f")) - m_state.set_translation_deadzone((double)translation_deadzone); - - float rotation_deadzone = m_state.get_rotation_deadzone(); - if (imgui.slider_float(_(L("Rotation")) + "##2", &rotation_deadzone, 0.0f, State::MaxRotationDeadzone, "%.2f")) - m_state.set_rotation_deadzone(rotation_deadzone); - -#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT - ImGui::Separator(); - ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, color); - imgui.text("DEBUG:"); - imgui.text("Vectors:"); - ImGui::PopStyleColor(); - Vec3f translation = m_state.get_translation().cast(); - Vec3f rotation = m_state.get_rotation(); - ImGui::InputFloat3("Translation##3", translation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); - ImGui::InputFloat3("Rotation##3", rotation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); - - ImGui::PushStyleColor(ImGuiCol_Text, color); - imgui.text("Queue size:"); - ImGui::PopStyleColor(); - - int translation_size[2] = { (int)m_state.get_translation_queue_size(), (int)m_state.get_translation_queue_max_size() }; - int rotation_size[2] = { (int)m_state.get_rotation_queue_size(), (int)m_state.get_rotation_queue_max_size() }; - int buttons_size[2] = { (int)m_state.get_buttons_queue_size(), (int)m_state.get_buttons_queue_max_size() }; - - ImGui::InputInt2("Translation##4", translation_size, ImGuiInputTextFlags_ReadOnly); - ImGui::InputInt2("Rotation##4", rotation_size, ImGuiInputTextFlags_ReadOnly); - ImGui::InputInt2("Buttons", buttons_size, ImGuiInputTextFlags_ReadOnly); - - int queue_size = (int)m_state.get_queues_max_size(); - if (ImGui::InputInt("Max size", &queue_size, 1, 1, ImGuiInputTextFlags_ReadOnly)) +#if ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG + // when the user clicks on [X] or [Close] button we need to trigger + // an extra frame to let the dialog disappear + if (m_settings_dialog_closed_by_user) { - if (queue_size > 0) - m_state.set_queues_max_size(queue_size); + m_show_settings_dialog = false; + m_settings_dialog_closed_by_user = false; + canvas.request_extra_frame(); + return; } - ImGui::Separator(); - ImGui::PushStyleColor(ImGuiCol_Text, color); - imgui.text("Camera:"); - ImGui::PopStyleColor(); - Vec3f target = wxGetApp().plater()->get_camera().get_target().cast(); - ImGui::InputFloat3("Target", target.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); + Size cnv_size = canvas.get_canvas_size(); +#endif // ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG + + ImGuiWrapper& imgui = *wxGetApp().imgui(); +#if ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG + imgui.set_next_window_pos(0.5f * (float)cnv_size.get_width(), 0.5f * (float)cnv_size.get_height(), ImGuiCond_Always, 0.5f, 0.5f); +#else + imgui.set_next_window_pos(0.5f * (float)canvas_width, 0.5f * (float)canvas_height, ImGuiCond_Always, 0.5f, 0.5f); +#endif // ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG + +#if ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG + static ImVec2 last_win_size(0.0f, 0.0f); + bool shown = true; + if (imgui.begin(_(L("3Dconnexion settings")), &shown, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse)) + { + if (shown) + { + ImVec2 win_size = ImGui::GetWindowSize(); + if ((last_win_size.x != win_size.x) || (last_win_size.y != win_size.y)) + { + // when the user clicks on [X] button, the next time the dialog is shown + // has a dummy size, so we trigger an extra frame to let it have the correct size + last_win_size = win_size; + canvas.request_extra_frame(); + } +#else + imgui.begin(_(L("3Dconnexion settings")), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse); +#endif // ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG + + const ImVec4& color = ImGui::GetStyleColorVec4(ImGuiCol_Separator); + ImGui::PushStyleColor(ImGuiCol_Text, color); + imgui.text(_(L("Device:"))); + ImGui::PopStyleColor(); + ImGui::SameLine(); + imgui.text(m_device_str); + + ImGui::Separator(); + ImGui::PushStyleColor(ImGuiCol_Text, color); + imgui.text(_(L("Speed:"))); + ImGui::PopStyleColor(); + + float translation_scale = (float)m_state.get_translation_scale() / State::DefaultTranslationScale; + if (imgui.slider_float(_(L("Translation")) + "##1", &translation_scale, 0.5f, 2.0f, "%.1f")) + m_state.set_translation_scale(State::DefaultTranslationScale * (double)translation_scale); + + float rotation_scale = m_state.get_rotation_scale() / State::DefaultRotationScale; + if (imgui.slider_float(_(L("Rotation")) + "##1", &rotation_scale, 0.5f, 2.0f, "%.1f")) + m_state.set_rotation_scale(State::DefaultRotationScale * rotation_scale); + + ImGui::Separator(); + ImGui::PushStyleColor(ImGuiCol_Text, color); + imgui.text(_(L("Deadzone:"))); + ImGui::PopStyleColor(); + + float translation_deadzone = (float)m_state.get_translation_deadzone(); + if (imgui.slider_float(_(L("Translation")) + "##2", &translation_deadzone, 0.0f, (float)State::MaxTranslationDeadzone, "%.2f")) + m_state.set_translation_deadzone((double)translation_deadzone); + + float rotation_deadzone = m_state.get_rotation_deadzone(); + if (imgui.slider_float(_(L("Rotation")) + "##2", &rotation_deadzone, 0.0f, State::MaxRotationDeadzone, "%.2f")) + m_state.set_rotation_deadzone(rotation_deadzone); + +#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT + ImGui::Separator(); + ImGui::Separator(); + ImGui::PushStyleColor(ImGuiCol_Text, color); + imgui.text("DEBUG:"); + imgui.text("Vectors:"); + ImGui::PopStyleColor(); + Vec3f translation = m_state.get_translation().cast(); + Vec3f rotation = m_state.get_rotation(); + ImGui::InputFloat3("Translation##3", translation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); + ImGui::InputFloat3("Rotation##3", rotation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); + + ImGui::PushStyleColor(ImGuiCol_Text, color); + imgui.text("Queue size:"); + ImGui::PopStyleColor(); + + int translation_size[2] = { (int)m_state.get_translation_queue_size(), (int)m_state.get_translation_queue_max_size() }; + int rotation_size[2] = { (int)m_state.get_rotation_queue_size(), (int)m_state.get_rotation_queue_max_size() }; + int buttons_size[2] = { (int)m_state.get_buttons_queue_size(), (int)m_state.get_buttons_queue_max_size() }; + + ImGui::InputInt2("Translation##4", translation_size, ImGuiInputTextFlags_ReadOnly); + ImGui::InputInt2("Rotation##4", rotation_size, ImGuiInputTextFlags_ReadOnly); + ImGui::InputInt2("Buttons", buttons_size, ImGuiInputTextFlags_ReadOnly); + + int queue_size = (int)m_state.get_queues_max_size(); + if (ImGui::InputInt("Max size", &queue_size, 1, 1, ImGuiInputTextFlags_ReadOnly)) + { + if (queue_size > 0) + m_state.set_queues_max_size(queue_size); + } + + ImGui::Separator(); + ImGui::PushStyleColor(ImGuiCol_Text, color); + imgui.text("Camera:"); + ImGui::PopStyleColor(); + Vec3f target = wxGetApp().plater()->get_camera().get_target().cast(); + ImGui::InputFloat3("Target", target.data(), "%.3f", ImGuiInputTextFlags_ReadOnly); #endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT +#if ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG + + ImGui::Separator(); + if (imgui.button(_(L("Close")))) + { + // the user clicked on the [Close] button + m_settings_dialog_closed_by_user = true; + canvas.set_as_dirty(); + } + } + else + { + // the user clicked on the [X] button + m_settings_dialog_closed_by_user = true; + canvas.set_as_dirty(); + } + } +#endif // ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG imgui.end(); } diff --git a/src/slic3r/GUI/Mouse3DController.hpp b/src/slic3r/GUI/Mouse3DController.hpp index cc03d4a24..543c44e77 100644 --- a/src/slic3r/GUI/Mouse3DController.hpp +++ b/src/slic3r/GUI/Mouse3DController.hpp @@ -18,6 +18,9 @@ namespace Slic3r { namespace GUI { struct Camera; +#if ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG +class GLCanvas3D; +#endif // ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG class Mouse3DController { @@ -130,7 +133,13 @@ class Mouse3DController hid_device* m_device; std::string m_device_str; bool m_running; - bool m_settings_dialog; +#if ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG + mutable bool m_show_settings_dialog; + // set to true when ther user closes the dialog by clicking on [X] or [Close] buttons + mutable bool m_settings_dialog_closed_by_user; +#else + bool m_show_settings_dialog; +#endif // ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG std::chrono::time_point m_last_time; public: @@ -146,9 +155,13 @@ public: bool apply(Camera& camera); - bool is_settings_dialog_shown() const { return m_settings_dialog; } - void show_settings_dialog(bool show) { m_settings_dialog = show && is_running(); } + bool is_settings_dialog_shown() const { return m_show_settings_dialog; } + void show_settings_dialog(bool show) { m_show_settings_dialog = show && is_running(); } +#if ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG + void render_settings_dialog(GLCanvas3D& canvas) const; +#else void render_settings_dialog(unsigned int canvas_width, unsigned int canvas_height) const; +#endif // ENABLE_3DCONNEXION_DEVICES_CLOSE_SETTING_DIALOG private: bool connect_device(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 43d2f06a5..b4c5e4bd3 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2252,7 +2252,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ config += std::move(config_loaded); } - this->model.custom_gcode_per_height = model.custom_gcode_per_height; + this->model.custom_gcode_per_print_z = model.custom_gcode_per_print_z; } if (load_config) @@ -2671,7 +2671,7 @@ void Plater::priv::reset() // The hiding of the slicing results, if shown, is not taken care by the background process, so we do it here this->sidebar->show_sliced_info_sizer(false); - model.custom_gcode_per_height.clear(); + model.custom_gcode_per_print_z.clear(); } void Plater::priv::mirror(Axis axis) @@ -3219,34 +3219,48 @@ void Plater::priv::reload_from_disk() while (!missing_input_paths.empty()) { // ask user to select the missing file - std::string search = missing_input_paths.back().string(); - wxFileDialog dialog(q, _(L("Please select the file to reload:")), "", search, file_wildcards(FT_MODEL), wxFD_OPEN | wxFD_FILE_MUST_EXIST); + fs::path search = missing_input_paths.back(); + wxString title = _(L("Please select the file to reload")); +#if defined(__APPLE__) + title += " (" + from_u8(search.filename().string()) + "):"; +#else + title += ":"; +#endif // __APPLE__ + wxFileDialog dialog(q, title, "", from_u8(search.filename().string()), file_wildcards(FT_MODEL), wxFD_OPEN | wxFD_FILE_MUST_EXIST); if (dialog.ShowModal() != wxID_OK) return; std::string sel_filename_path = dialog.GetPath().ToUTF8().data(); std::string sel_filename = fs::path(sel_filename_path).filename().string(); - if (boost::algorithm::iends_with(search, sel_filename)) + if (boost::algorithm::iequals(search.filename().string(), sel_filename)) { input_paths.push_back(sel_filename_path); missing_input_paths.pop_back(); - std::string sel_path = fs::path(sel_filename_path).remove_filename().string(); + fs::path sel_path = fs::path(sel_filename_path).remove_filename().string(); std::vector::iterator it = missing_input_paths.begin(); while (it != missing_input_paths.end()) { // try to use the path of the selected file with all remaining missing files - std::string repathed_filename = sel_path + "/" + it->filename().string(); + fs::path repathed_filename = sel_path; + repathed_filename /= it->filename(); if (fs::exists(repathed_filename)) { - input_paths.push_back(repathed_filename); + input_paths.push_back(repathed_filename.string()); it = missing_input_paths.erase(it); } else ++it; } } + else + { + wxString message = _(L("It is not allowed to change the file to reload")) + " (" + from_u8(search.filename().string()) + ").\n" + _(L("Do you want to retry")) + " ?"; + wxMessageDialog dlg(q, message, wxMessageBoxCaptionStr, wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION); + if (dlg.ShowModal() != wxID_YES) + return; + } } #endif // ENABLE_RELOAD_FROM_DISK_MISSING_SELECTION @@ -3281,7 +3295,8 @@ void Plater::priv::reload_from_disk() int new_volume_idx = old_volume->source.volume_idx; int new_object_idx = old_volume->source.object_idx; - if (old_volume->source.input_file == path) + if (boost::algorithm::iequals(fs::path(old_volume->source.input_file).filename().string(), + fs::path(path).filename().string())) { assert(new_object_idx < (int)new_model.objects.size()); ModelObject* new_model_object = new_model.objects[new_object_idx]; @@ -3295,6 +3310,7 @@ void Plater::priv::reload_from_disk() new_volume->set_material_id(old_volume->material_id()); new_volume->set_transformation(old_volume->get_transformation()); new_volume->translate(new_volume->get_transformation().get_matrix(true) * (new_volume->source.mesh_offset - old_volume->source.mesh_offset)); + new_volume->source.input_file = path; std::swap(old_model_object->volumes[old_v.volume_idx], old_model_object->volumes.back()); old_model_object->delete_volume(old_model_object->volumes.size() - 1); } @@ -3593,7 +3609,10 @@ void Plater::priv::on_right_click(RBtnEvent& evt) if (evt.data.second) // right button was clicked on empty space menu = &default_menu; else + { + sidebar->obj_list()->show_multi_selection_menu(); return; + } } else { @@ -4623,6 +4642,13 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_uppe remove(obj_idx); p->load_model_objects(new_objects); + + Selection& selection = p->get_selection(); + size_t last_id = p->model.objects.size() - 1; + for (size_t i = 0; i < new_objects.size(); ++i) + { + selection.add_object((unsigned int)(last_id - i), i == 0); + } } void Plater::export_gcode() @@ -5157,6 +5183,7 @@ const DynamicPrintConfig* Plater::get_plater_config() const return p->config; } +// Get vector of extruder colors considering filament color, if extruder color is undefined. std::vector Plater::get_extruder_colors_from_plater_config() const { const Slic3r::DynamicPrintConfig* config = &wxGetApp().preset_bundle->printers.get_edited_preset().config; @@ -5176,13 +5203,17 @@ std::vector Plater::get_extruder_colors_from_plater_config() const return extruder_colors; } +/* Get vector of colors used for rendering of a Preview scene in "Color print" mode + * It consists of extruder colors and colors, saved in model.custom_gcode_per_print_z + */ std::vector Plater::get_colors_for_color_print() const { std::vector colors = get_extruder_colors_from_plater_config(); + colors.reserve(colors.size() + p->model.custom_gcode_per_print_z.size()); - for (const Model::CustomGCode& code : p->model.custom_gcode_per_height) + for (const Model::CustomGCode& code : p->model.custom_gcode_per_print_z) if (code.gcode == ColorChangeCode) - colors.push_back(code.color); + colors.emplace_back(code.color); return colors; } diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp index 9b8eaa8ec..dbfd446b1 100644 --- a/src/slic3r/GUI/PresetBundle.cpp +++ b/src/slic3r/GUI/PresetBundle.cpp @@ -29,6 +29,7 @@ #include "libslic3r/libslic3r.h" #include "libslic3r/Utils.hpp" +#include "GUI_App.hpp" // Store the print/filament/printer presets into a "presets" subdirectory of the Slic3rPE config dir. // This breaks compatibility with the upstream Slic3r if the --datadir is used to switch between the two versions. @@ -868,6 +869,9 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool } // 4) Load the project config values (the per extruder wipe matrix etc). this->project_config.apply_only(config, s_project_options); + + update_custom_gcode_per_print_z_from_config(GUI::wxGetApp().plater()->model().custom_gcode_per_print_z, &this->project_config); + break; } case ptSLA: diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 302a5e521..8d9f2fd0b 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -14,6 +14,7 @@ #include #include +#include #include "BitmapCache.hpp" #include "GUI.hpp" @@ -57,7 +58,7 @@ void msw_rescale_menu(wxMenu* menu) #endif /* __WXMSW__ */ #endif /* no __WXGTK__ */ -void enable_menu_item(wxUpdateUIEvent& evt, std::function const cb_condition, wxMenuItem* item) +void enable_menu_item(wxUpdateUIEvent& evt, std::function const cb_condition, wxMenuItem* item, wxWindow* win) { const bool enable = cb_condition(); evt.Enable(enable); @@ -66,7 +67,7 @@ void enable_menu_item(wxUpdateUIEvent& evt, std::function const cb_condi const auto it = msw_menuitem_bitmaps.find(item->GetId()); if (it != msw_menuitem_bitmaps.end()) { - const wxBitmap& item_icon = create_scaled_bitmap(nullptr, it->second, 16, false, !enable); + const wxBitmap& item_icon = create_scaled_bitmap(win, it->second, 16, false, !enable); if (item_icon.IsOk()) item->SetBitmap(item_icon); } @@ -94,8 +95,8 @@ wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const menu->Bind(wxEVT_MENU, cb, id); if (parent) { - parent->Bind(wxEVT_UPDATE_UI, [cb_condition, item](wxUpdateUIEvent& evt) { - enable_menu_item(evt, cb_condition, item); }, id); + parent->Bind(wxEVT_UPDATE_UI, [cb_condition, item, parent](wxUpdateUIEvent& evt) { + enable_menu_item(evt, cb_condition, item, parent); }, id); } return item; @@ -108,7 +109,7 @@ wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const if (id == wxID_ANY) id = wxNewId(); - const wxBitmap& bmp = !icon.empty() ? create_scaled_bitmap(nullptr, icon) : wxNullBitmap; // FIXME: pass window ptr + const wxBitmap& bmp = !icon.empty() ? create_scaled_bitmap(parent, icon) : wxNullBitmap; // FIXME: pass window ptr //#ifdef __WXMSW__ #ifndef __WXGTK__ if (bmp.IsOk()) @@ -126,7 +127,7 @@ wxMenuItem* append_submenu(wxMenu* menu, wxMenu* sub_menu, int id, const wxStrin wxMenuItem* item = new wxMenuItem(menu, id, string, description); if (!icon.empty()) { - item->SetBitmap(create_scaled_bitmap(nullptr, icon)); // FIXME: pass window ptr + item->SetBitmap(create_scaled_bitmap(parent, icon)); // FIXME: pass window ptr //#ifdef __WXMSW__ #ifndef __WXGTK__ msw_menuitem_bitmaps[id] = icon; @@ -137,8 +138,8 @@ wxMenuItem* append_submenu(wxMenu* menu, wxMenu* sub_menu, int id, const wxStrin menu->Append(item); if (parent) { - parent->Bind(wxEVT_UPDATE_UI, [cb_condition, item](wxUpdateUIEvent& evt) { - enable_menu_item(evt, cb_condition, item); }, id); + parent->Bind(wxEVT_UPDATE_UI, [cb_condition, item, parent](wxUpdateUIEvent& evt) { + enable_menu_item(evt, cb_condition, item, parent); }, id); } return item; @@ -424,6 +425,28 @@ static float get_svg_scale_factor(wxWindow *win) #endif } +// in the Dark mode of any platform, we should draw icons in respect to OS background +static std::string icon_name_respected_to_mode(const std::string& bmp_name_in) +{ +#ifdef __WXMSW__ + const std::string folder = "white\\"; +#else + const std::string folder = "white/"; +#endif + std::string bmp_name = Slic3r::GUI::wxGetApp().dark_mode() ? folder + bmp_name_in : bmp_name_in; + boost::replace_last(bmp_name, ".png", ""); + FILE* fp = NULL; + fp = boost::nowide::fopen(Slic3r::var(bmp_name + ".svg").c_str(), "rb"); + if (!fp) + { + bmp_name = bmp_name_in; + boost::replace_last(bmp_name, ".png", ""); + if (fp) fclose(fp); + } + + return bmp_name; +} + // If an icon has horizontal orientation (width > height) call this function with is_horizontal = true wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name_in, const int px_cnt/* = 16*/, const bool is_horizontal /* = false*/, const bool grayscale/* = false*/) @@ -450,8 +473,10 @@ wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name_in, scale_base = (unsigned int)(em_unit(win) * px_cnt * 0.1f + 0.5f); - std::string bmp_name = bmp_name_in; - boost::replace_last(bmp_name, ".png", ""); +// std::string bmp_name = bmp_name_in; +// boost::replace_last(bmp_name, ".png", ""); + + std::string bmp_name = icon_name_respected_to_mode(bmp_name_in); // Try loading an SVG first, then PNG if SVG is not found: wxBitmap *bmp = cache.load_svg(bmp_name, width, height, scale_factor, grayscale); @@ -2538,7 +2563,7 @@ std::vector DoubleSlider::GetTicksValues() const for (const TICK_CODE& tick : m_ticks_) { if (tick.tick > val_size) break; - values.push_back(t_custom_code(m_values[tick.tick], tick.gcode, tick.extruder, tick.color)); + values.emplace_back(t_custom_code{m_values[tick.tick], tick.gcode, tick.extruder, tick.color}); } return values; @@ -2553,12 +2578,12 @@ void DoubleSlider::SetTicksValues(const std::vector& heights) m_ticks_.clear(); for (auto h : heights) { - auto it = std::lower_bound(m_values.begin(), m_values.end(), h.height - epsilon()); + auto it = std::lower_bound(m_values.begin(), m_values.end(), h.print_z - epsilon()); if (it == m_values.end()) continue; - m_ticks_.insert(TICK_CODE(it-m_values.begin(), h.gcode, h.extruder, h.color)); + m_ticks_.emplace(TICK_CODE{int(it-m_values.begin()), h.gcode, h.extruder, h.color}); } if (!was_empty && m_ticks_.empty()) @@ -2642,7 +2667,7 @@ void DoubleSlider::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoin return; wxBitmap* icon = m_is_action_icon_focesed ? &m_bmp_add_tick_off.bmp() : &m_bmp_add_tick_on.bmp(); - if (m_ticks_.find(tick) != m_ticks_.end()) + if (m_ticks_.find(TICK_CODE{tick}) != m_ticks_.end()) icon = m_is_action_icon_focesed ? &m_bmp_del_tick_off.bmp() : &m_bmp_del_tick_on.bmp(); wxCoord x_draw, y_draw; @@ -3081,7 +3106,7 @@ wxString DoubleSlider::get_tooltip(IconFocus icon_focus) else if (m_is_action_icon_focesed) { const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - const auto tick_code_it = m_ticks_.find(tick); + const auto tick_code_it = m_ticks_.find(TICK_CODE{tick}); tooltip = tick_code_it == m_ticks_.end() ? (m_state == msSingleExtruder ? _(L("For add color change use left mouse button click")) : _(L("For add change extruder use left mouse button click"))) + "\n" + @@ -3240,13 +3265,13 @@ void DoubleSlider::action_tick(const TicksAction action) const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - const auto it = m_ticks_.find(tick); + const auto it = m_ticks_.find(TICK_CODE{tick}); if (it != m_ticks_.end()) // erase this tick { if (action == taAdd) return; - m_ticks_.erase(TICK_CODE(tick)); + m_ticks_.erase(TICK_CODE{tick}); wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); Refresh(); @@ -3350,7 +3375,7 @@ void DoubleSlider::OnRightDown(wxMouseEvent& event) { const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; // if on this Z doesn't exist tick - auto it = m_ticks_.find(tick); + auto it = m_ticks_.find(TICK_CODE{ tick }); if (it == m_ticks_.end()) { // show context menu on OnRightUp() @@ -3387,7 +3412,7 @@ int DoubleSlider::get_extruder_for_tick(int tick) if (m_ticks_.empty()) return 0; - auto it = m_ticks_.lower_bound(tick); + auto it = m_ticks_.lower_bound(TICK_CODE{tick}); while (it != m_ticks_.begin()) { --it; if(it->gcode == Slic3r::ExtruderChangeCode) @@ -3454,7 +3479,7 @@ void DoubleSlider::OnRightUp(wxMouseEvent& event) else if (m_show_edit_menu) { wxMenu menu; - std::set::iterator it = m_ticks_.find(m_selection == ssLower ? m_lower_value : m_higher_value); + std::set::iterator it = m_ticks_.find(TICK_CODE{ m_selection == ssLower ? m_lower_value : m_higher_value }); const bool is_color_change = it->gcode == Slic3r::ColorChangeCode; append_menu_item(&menu, wxID_ANY, it->gcode == Slic3r::ColorChangeCode ? _(L("Edit color")) : @@ -3526,7 +3551,7 @@ void DoubleSlider::add_code(std::string code, int selected_extruder/* = -1*/) { const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; // if on this Z doesn't exist tick - auto it = m_ticks_.find(tick); + auto it = m_ticks_.find(TICK_CODE{ tick }); if (it == m_ticks_.end()) { std::string color = ""; @@ -3535,7 +3560,7 @@ void DoubleSlider::add_code(std::string code, int selected_extruder/* = -1*/) std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); if (m_state == msSingleExtruder && !m_ticks_.empty()) { - auto before_tick_it = std::lower_bound(m_ticks_.begin(), m_ticks_.end(), tick); + auto before_tick_it = std::lower_bound(m_ticks_.begin(), m_ticks_.end(), TICK_CODE{ tick }); while (before_tick_it != m_ticks_.begin()) { --before_tick_it; if (before_tick_it->gcode == Slic3r::ColorChangeCode) { @@ -3580,7 +3605,7 @@ void DoubleSlider::add_code(std::string code, int selected_extruder/* = -1*/) extruder = get_extruder_for_tick(m_selection == ssLower ? m_lower_value : m_higher_value); } - m_ticks_.insert(TICK_CODE(tick, code, extruder, color)); + m_ticks_.emplace(TICK_CODE{tick, code, extruder, color}); wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); Refresh(); @@ -3592,7 +3617,7 @@ void DoubleSlider::edit_tick() { const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; // if on this Z exists tick - std::set::iterator it = m_ticks_.find(tick); + std::set::iterator it = m_ticks_.find(TICK_CODE{ tick }); if (it != m_ticks_.end()) { std::string edited_value; @@ -3619,7 +3644,7 @@ void DoubleSlider::edit_tick() } m_ticks_.erase(it); - m_ticks_.insert(changed_tick); + m_ticks_.emplace(changed_tick); wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); } @@ -3632,9 +3657,9 @@ void DoubleSlider::change_extruder(int extruder) std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); // if on this Y doesn't exist tick - if (m_ticks_.find(tick) == m_ticks_.end()) + if (m_ticks_.find(TICK_CODE{tick}) == m_ticks_.end()) { - m_ticks_.insert(TICK_CODE(tick, Slic3r::ExtruderChangeCode, extruder, extruder == 0 ? "" : colors[extruder-1])); + m_ticks_.emplace(TICK_CODE{tick, Slic3r::ExtruderChangeCode, extruder, extruder == 0 ? "" : colors[extruder-1]}); wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED)); Refresh(); @@ -3672,7 +3697,7 @@ void DoubleSlider::edit_extruder_sequence() while (tick <= m_max_value) { int cur_extruder = m_extruders_sequence.extruders[extruder]; - m_ticks_.insert(TICK_CODE(tick, Slic3r::ExtruderChangeCode, cur_extruder + 1, colors[cur_extruder])); + m_ticks_.emplace(TICK_CODE{tick, Slic3r::ExtruderChangeCode, cur_extruder + 1, colors[cur_extruder]}); extruder++; if (extruder == extr_cnt) @@ -3680,12 +3705,12 @@ void DoubleSlider::edit_extruder_sequence() if (m_extruders_sequence.is_mm_intervals) { value += m_extruders_sequence.interval_by_mm; - auto it = std::lower_bound(m_values.begin(), m_values.end(), value - epsilon()); + auto val_it = std::lower_bound(m_values.begin(), m_values.end(), value - epsilon()); - if (it == m_values.end()) + if (val_it == m_values.end()) break; - tick = it - m_values.begin(); + tick = val_it - m_values.begin(); } else tick += m_extruders_sequence.interval_by_layers; diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index 7841b62fe..0c43be3a0 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -17,6 +17,7 @@ #include #include #include "libslic3r/Model.hpp" +#include "libslic3r/GCodeWriter.hpp" namespace Slic3r { enum class ModelVolumeType : int; @@ -958,24 +959,12 @@ private: struct TICK_CODE { - TICK_CODE(int tick):tick(tick), gcode(Slic3r::ColorChangeCode), extruder(0), color("") {} - TICK_CODE(int tick, const std::string& code) : - tick(tick), gcode(code), extruder(0) {} - TICK_CODE(int tick, int extruder) : - tick(tick), gcode(Slic3r::ColorChangeCode), extruder(extruder) {} - TICK_CODE(int tick, const std::string& code, int extruder, const std::string& color) : - tick(tick), gcode(code), extruder(extruder), color(color) {} - bool operator<(const TICK_CODE& other) const { return other.tick > this->tick; } bool operator>(const TICK_CODE& other) const { return other.tick < this->tick; } - TICK_CODE operator=(const TICK_CODE& other) const { - TICK_CODE ret_val(other.tick, other.gcode, other.extruder, other.color); - return ret_val; - } - int tick; - std::string gcode; - int extruder; + int tick = 0; + std::string gcode = Slic3r::ColorChangeCode; + int extruder = 0; std::string color; }; diff --git a/tests/libslic3r/test_clipper_utils.cpp b/tests/libslic3r/test_clipper_utils.cpp index 21d2c2cd4..69e9d0efb 100644 --- a/tests/libslic3r/test_clipper_utils.cpp +++ b/tests/libslic3r/test_clipper_utils.cpp @@ -1,5 +1,6 @@ #include +#include #include #include @@ -223,3 +224,78 @@ SCENARIO("Various Clipper operations - t/clipper.t", "[ClipperUtils]") { } } } + +template +double polytree_area(const Tree &tree, std::vector

*out) +{ + traverse_pt(tree, out); + + return std::accumulate(out->begin(), out->end(), 0.0, + [](double a, const P &p) { return a + p.area(); }); +} + +size_t count_polys(const ExPolygons& expolys) +{ + size_t c = 0; + for (auto &ep : expolys) c += ep.holes.size() + 1; + + return c; +} + +TEST_CASE("Traversing Clipper PolyTree", "[ClipperUtils]") { + // Create a polygon representing unit box + Polygon unitbox; + const auto UNIT = coord_t(1. / SCALING_FACTOR); + unitbox.points = {{0, 0}, {UNIT, 0}, {UNIT, UNIT}, {0, UNIT}}; + + Polygon box_frame = unitbox; + box_frame.scale(20, 10); + + Polygon hole_left = unitbox; + hole_left.scale(8); + hole_left.translate(UNIT, UNIT); + hole_left.reverse(); + + Polygon hole_right = hole_left; + hole_right.translate(UNIT * 10, 0); + + Polygon inner_left = unitbox; + inner_left.scale(4); + inner_left.translate(UNIT * 3, UNIT * 3); + + Polygon inner_right = inner_left; + inner_right.translate(UNIT * 10, 0); + + Polygons reference = union_({box_frame, hole_left, hole_right, inner_left, inner_right}); + + ClipperLib::PolyTree tree = union_pt(reference); + double area_sum = box_frame.area() + hole_left.area() + + hole_right.area() + inner_left.area() + + inner_right.area(); + + REQUIRE(area_sum > 0); + + SECTION("Traverse into Polygons WITHOUT spatial ordering") { + Polygons output; + REQUIRE(area_sum == Approx(polytree_area(tree.GetFirst(), &output))); + REQUIRE(output.size() == reference.size()); + } + + SECTION("Traverse into ExPolygons WITHOUT spatial ordering") { + ExPolygons output; + REQUIRE(area_sum == Approx(polytree_area(tree.GetFirst(), &output))); + REQUIRE(count_polys(output) == reference.size()); + } + + SECTION("Traverse into Polygons WITH spatial ordering") { + Polygons output; + REQUIRE(area_sum == Approx(polytree_area(tree.GetFirst(), &output))); + REQUIRE(output.size() == reference.size()); + } + + SECTION("Traverse into ExPolygons WITH spatial ordering") { + ExPolygons output; + REQUIRE(area_sum == Approx(polytree_area(tree.GetFirst(), &output))); + REQUIRE(count_polys(output) == reference.size()); + } +} diff --git a/version.inc b/version.inc index 0a62ff6f3..c96a304ab 100644 --- a/version.inc +++ b/version.inc @@ -3,7 +3,7 @@ set(SLIC3R_APP_NAME "PrusaSlicer") set(SLIC3R_APP_KEY "PrusaSlicer") -set(SLIC3R_VERSION "2.1.0") +set(SLIC3R_VERSION "2.2.0-alpha0") set(SLIC3R_BUILD_ID "PrusaSlicer-${SLIC3R_VERSION}+UNKNOWN") -set(SLIC3R_RC_VERSION "2,1,0,0") -set(SLIC3R_RC_VERSION_DOTS "2.1.0.0") +set(SLIC3R_RC_VERSION "2,2,0,0") +set(SLIC3R_RC_VERSION_DOTS "2.2.0.0")