diff --git a/CMakeLists.txt b/CMakeLists.txt index 68d4e4d1e..85b054bf0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -514,8 +514,10 @@ elseif (SLIC3R_FHS) set(SLIC3R_FHS_RESOURCES "${CMAKE_INSTALL_FULL_DATAROOTDIR}/PrusaSlicer") install(DIRECTORY "${SLIC3R_RESOURCES_DIR}/" DESTINATION "${SLIC3R_FHS_RESOURCES}") install(FILES src/platform/unix/PrusaSlicer.desktop DESTINATION ${SLIC3R_FHS_RESOURCES}/applications) + install(FILES src/platform/unix/PrusaGcodeviewer.desktop DESTINATION ${SLIC3R_FHS_RESOURCES}/applications) else () install(FILES src/platform/unix/PrusaSlicer.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/resources/applications) + install(FILES src/platform/unix/PrusaGcodeviewer.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/resources/applications) install(DIRECTORY "${SLIC3R_RESOURCES_DIR}/" DESTINATION "${CMAKE_INSTALL_PREFIX}/resources") endif () diff --git a/deps/CGAL/CGAL.cmake b/deps/CGAL/CGAL.cmake index f0584c5b5..fa88fc29e 100644 --- a/deps/CGAL/CGAL.cmake +++ b/deps/CGAL/CGAL.cmake @@ -15,6 +15,17 @@ include(GNUInstallDirs) # 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/${CMAKE_INSTALL_LIBDIR}/cmake/CGAL" ) + +# Again, for whatever reason, CGAL thinks that its version is not relevant if +# configured as a header only library. Fixing it by placing a cmake version file +# besides the installed config file. +ExternalProject_Add_Step(dep_CGAL dep_CGAL_version_fix + DEPENDEES install + + COMMAND ${CMAKE_COMMAND} -E copy cgal/CGALConfigVersion.cmake "${DESTDIR}/usr/local/${CMAKE_INSTALL_LIBDIR}/cmake/CGAL/CGALConfigVersion.cmake" + WORKING_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}" +) diff --git a/deps/CGAL/cgal/CGALConfigVersion.cmake b/deps/CGAL/cgal/CGALConfigVersion.cmake new file mode 100644 index 000000000..f688824e8 --- /dev/null +++ b/deps/CGAL/cgal/CGALConfigVersion.cmake @@ -0,0 +1,37 @@ +# This is a basic version file for the Config-mode of find_package(). +# It is used by write_basic_package_version_file() as input file for configure_file() +# to create a version-file which can be installed along a config.cmake file. +# +# The created file sets PACKAGE_VERSION_EXACT if the current version string and +# the requested version string are exactly the same and it sets +# PACKAGE_VERSION_COMPATIBLE if the current version is >= requested version. +# The variable CVF_VERSION must be set before calling configure_file(). + +set(PACKAGE_VERSION "5.0.0") + +if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION) + set(PACKAGE_VERSION_COMPATIBLE FALSE) +else() + set(PACKAGE_VERSION_COMPATIBLE TRUE) + if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION) + set(PACKAGE_VERSION_EXACT TRUE) + endif() +endif() + + +# if the installed project requested no architecture check, don't perform the check +if("FALSE") + return() +endif() + +# if the installed or the using project don't have CMAKE_SIZEOF_VOID_P set, ignore it: +if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "" OR "8" STREQUAL "") + return() +endif() + +# check that the installed version has the same 32/64bit-ness as the one which is currently searching: +if(NOT CMAKE_SIZEOF_VOID_P STREQUAL "8") + math(EXPR installedBits "8 * 8") + set(PACKAGE_VERSION "${PACKAGE_VERSION} (${installedBits}bit)") + set(PACKAGE_VERSION_UNSUITABLE TRUE) +endif() diff --git a/resources/data/flatpak/com.prusa3d.PrusaSlicer.desktop b/resources/data/flatpak/com.prusa3d.PrusaSlicer.desktop new file mode 100755 index 000000000..898722298 --- /dev/null +++ b/resources/data/flatpak/com.prusa3d.PrusaSlicer.desktop @@ -0,0 +1,12 @@ +[Desktop Entry] +Name=PrusaSlicer +GenericName=3D Printing Software +Icon=com.prusa3d.PrusaSlicer +Exec=prusa-slicer %F +Terminal=false +Type=Application +MimeType=model/stl;model/x-wavefront-obj;model/3mf;model/x-geomview-off;application/x-amf; +Categories=Graphics;3DGraphics;Engineering; +Keywords=3D;Printing;Slicer;slice;3D;printer;convert;gcode;stl;obj;amf;SLA +StartupNotify=false +StartupWMClass=prusa-slicer \ No newline at end of file diff --git a/resources/data/flatpak/com.prusa3d.PrusaSlicer.metainfo.xml b/resources/data/flatpak/com.prusa3d.PrusaSlicer.metainfo.xml new file mode 100755 index 000000000..b62a57e48 --- /dev/null +++ b/resources/data/flatpak/com.prusa3d.PrusaSlicer.metainfo.xml @@ -0,0 +1,62 @@ + + + com.prusa3d.PrusaSlicer + com.prusa3d.PrusaSlicer.desktop + + prusa-slicer.desktop + + PrusaSlicer + Powerful 3D printing slicer optimized for Prusa printers + 0BSD + AGPL-3.0-only + +

+ PrusaSlicer takes 3D models (STL, OBJ, AMF) and converts them into G-code + instructions for FFF printers or PNG layers for mSLA 3D printers. It's + compatible with any modern printer based on the RepRap toolchain, including all + those based on the Marlin, Prusa, Sprinter and Repetier firmware. It also works + with Mach3, LinuxCNC and Machinekit controllers. +

+

+ PrusaSlicer is based on Slic3r by Alessandro Ranelucci and the RepRap community. +

+

+ What are some of PrusaSlicer's main features? +

+ +
+ https://www.prusa3d.com/prusaslicer/ + https://help.prusa3d.com + https://github.com/prusa3d/PrusaSlicer/issues + + + https://user-images.githubusercontent.com/590307/78981854-24d07580-7b21-11ea-9441-77923534a659.png + + + https://user-images.githubusercontent.com/590307/78981860-2863fc80-7b21-11ea-8c2d-8ff79ced2578.png + + + https://user-images.githubusercontent.com/590307/78981862-28fc9300-7b21-11ea-9b0d-d03e16b709d3.png + + + + + + +

This is final release of PrusaSlicer 2.2.0 introducing SLA hollowing and hole drilling, support for 3rd party printer vendors, 3Dconnexion support, + automatic variable layer height, macOS dark mode support, greatly improved ColorPrint feature and much, much more. + Several bugs found in the previous release candidate are fixed in this final release. See the respective change logs of the previous releases for all the + new features, improvements and bugfixes in the 2.2.0 series.

+
+
+
+
diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index dfa49d9df..7a0dcb52b 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -205,6 +205,20 @@ std::string AppConfig::load() m_legacy_datadir = ini_ver < Semver(1, 40, 0); } + // Legacy conversion + if (m_mode == EAppMode::Editor) { + // Convert [extras] "physical_printer" to [presets] "physical_printer", + // remove the [extras] section if it becomes empty. + if (auto it_section = m_storage.find("extras"); it_section != m_storage.end()) { + if (auto it_physical_printer = it_section->second.find("physical_printer"); it_physical_printer != it_section->second.end()) { + m_storage["presets"]["physical_printer"] = it_physical_printer->second; + it_section->second.erase(it_physical_printer); + } + if (it_section->second.empty()) + m_storage.erase(it_section); + } + } + // Override missing or keys with their defaults. this->set_defaults(); m_dirty = false; @@ -428,6 +442,7 @@ void AppConfig::reset_selections() it->second.erase("sla_print"); it->second.erase("sla_material"); it->second.erase("printer"); + it->second.erase("physical_printer"); m_dirty = true; } } diff --git a/src/libslic3r/AppConfig.hpp b/src/libslic3r/AppConfig.hpp index c5b8ece2b..1ed7b28a5 100644 --- a/src/libslic3r/AppConfig.hpp +++ b/src/libslic3r/AppConfig.hpp @@ -142,20 +142,20 @@ public: #endif // ENABLE_GCODE_VIEWER // Returns true if the user's data directory comes from before Slic3r 1.40.0 (no updating) - bool legacy_datadir() const { return m_legacy_datadir; } - void set_legacy_datadir(bool value) { m_legacy_datadir = value; } + bool legacy_datadir() const { return m_legacy_datadir; } + void set_legacy_datadir(bool value) { m_legacy_datadir = value; } // Get the Slic3r version check url. // This returns a hardcoded string unless it is overriden by "version_check_url" in the ini file. - std::string version_check_url() const; + std::string version_check_url() const; // Returns the original Slic3r version found in the ini file before it was overwritten // by the current version - Semver orig_version() const { return m_orig_version; } + Semver orig_version() const { return m_orig_version; } // Does the config file exist? #if ENABLE_GCODE_VIEWER - bool exists(); + bool exists(); #else static bool exists(); #endif // ENABLE_GCODE_VIEWER diff --git a/src/libslic3r/Fill/FillRectilinear2.cpp b/src/libslic3r/Fill/FillRectilinear2.cpp index 0c4589200..493bb7c6f 100644 --- a/src/libslic3r/Fill/FillRectilinear2.cpp +++ b/src/libslic3r/Fill/FillRectilinear2.cpp @@ -154,7 +154,9 @@ struct SegmentIntersection // Vertical link, up. Up, // Vertical link, down. - Down + Down, + // Phony intersection point has no link. + Phony, }; enum class LinkQuality : uint8_t { @@ -353,6 +355,25 @@ struct SegmentedIntersectionLine std::vector intersections; }; +static SegmentIntersection phony_outer_intersection(SegmentIntersection::SegmentIntersectionType type, coord_t pos) +{ + assert(type == SegmentIntersection::OUTER_LOW || type == SegmentIntersection::OUTER_HIGH); + SegmentIntersection out; + // Invalid contour & segment. + out.iContour = std::numeric_limits::max(); + out.iSegment = std::numeric_limits::max(); + out.pos_p = pos; + out.type = type; + // Invalid prev / next. + out.prev_on_contour = -1; + out.next_on_contour = -1; + out.prev_on_contour_type = SegmentIntersection::LinkType::Phony; + out.next_on_contour_type = SegmentIntersection::LinkType::Phony; + out.prev_on_contour_quality = SegmentIntersection::LinkQuality::Invalid; + out.next_on_contour_quality = SegmentIntersection::LinkQuality::Invalid; + return out; +} + // A container maintaining an expolygon with its inner offsetted polygon. // The purpose of the inner offsetted polygon is to provide segments to connect the infill lines. struct ExPolygonWithOffset @@ -889,6 +910,60 @@ static std::vector slice_region_by_vertical_lines(con return segs; } +#ifndef NDEBUG +bool validate_segment_intersection_connectivity(const std::vector &segs) +{ + // Validate the connectivity. + for (size_t i_vline = 0; i_vline + 1 < segs.size(); ++ i_vline) { + const SegmentedIntersectionLine &il_left = segs[i_vline]; + const SegmentedIntersectionLine &il_right = segs[i_vline + 1]; + for (const SegmentIntersection &it : il_left.intersections) { + if (it.has_right_horizontal()) { + const SegmentIntersection &it_right = il_right.intersections[it.right_horizontal()]; + // For a right link there is a symmetric left link. + assert(it.iContour == it_right.iContour); + assert(it.type == it_right.type); + assert(it_right.has_left_horizontal()); + assert(it_right.left_horizontal() == int(&it - il_left.intersections.data())); + } + } + for (const SegmentIntersection &it : il_right.intersections) { + if (it.has_left_horizontal()) { + const SegmentIntersection &it_left = il_left.intersections[it.left_horizontal()]; + // For a right link there is a symmetric left link. + assert(it.iContour == it_left.iContour); + assert(it.type == it_left.type); + assert(it_left.has_right_horizontal()); + assert(it_left.right_horizontal() == int(&it - il_right.intersections.data())); + } + } + } + for (size_t i_vline = 0; i_vline < segs.size(); ++ i_vline) { + const SegmentedIntersectionLine &il = segs[i_vline]; + for (const SegmentIntersection &it : il.intersections) { + auto i_it = int(&it - il.intersections.data()); + if (it.has_left_vertical_up()) { + assert(il.intersections[it.left_vertical_up()].left_vertical_down() == i_it); + assert(il.intersections[it.left_vertical_up()].prev_on_contour_quality == it.prev_on_contour_quality); + } + if (it.has_left_vertical_down()) { + assert(il.intersections[it.left_vertical_down()].left_vertical_up() == i_it); + assert(il.intersections[it.left_vertical_down()].prev_on_contour_quality == it.prev_on_contour_quality); + } + if (it.has_right_vertical_up()) { + assert(il.intersections[it.right_vertical_up()].right_vertical_down() == i_it); + assert(il.intersections[it.right_vertical_up()].next_on_contour_quality == it.next_on_contour_quality); + } + if (it.has_right_vertical_down()) { + assert(il.intersections[it.right_vertical_down()].right_vertical_up() == i_it); + assert(il.intersections[it.right_vertical_down()].next_on_contour_quality == it.next_on_contour_quality); + } + } + } + return true; +} +#endif /* NDEBUG */ + // Connect each contour / vertical line intersection point with another two contour / vertical line intersection points. // (fill in SegmentIntersection::{prev_on_contour, prev_on_contour_vertical, next_on_contour, next_on_contour_vertical}. // These contour points are either on the same vertical line, or on the vertical line left / right to the current one. @@ -1055,55 +1130,104 @@ static void connect_segment_intersections_by_contours( } } -#ifndef NDEBUG - // Validate the connectivity. - for (size_t i_vline = 0; i_vline + 1 < segs.size(); ++ i_vline) { - const SegmentedIntersectionLine &il_left = segs[i_vline]; - const SegmentedIntersectionLine &il_right = segs[i_vline + 1]; - for (const SegmentIntersection &it : il_left.intersections) { - if (it.has_right_horizontal()) { - const SegmentIntersection &it_right = il_right.intersections[it.right_horizontal()]; - // For a right link there is a symmetric left link. - assert(it.iContour == it_right.iContour); - assert(it.type == it_right.type); - assert(it_right.has_left_horizontal()); - assert(it_right.left_horizontal() == int(&it - il_left.intersections.data())); + assert(validate_segment_intersection_connectivity(segs)); +} + +static void pinch_contours_insert_phony_outer_intersections(std::vector &segs) +{ + // Keep the vector outside the loops, so they will not be reallocated. + // Where to insert new outer points. + std::vector insert_after; + // Mapping of indices of current intersection line after inserting new outer points. + std::vector map; + std::vector temp_intersections; + + for (size_t i_vline = 1; i_vline < segs.size(); ++ i_vline) { + SegmentedIntersectionLine &il = segs[i_vline]; + assert(il.intersections.empty() || il.intersections.size() >= 2); + if (! il.intersections.empty()) { + assert(il.intersections.front().type == SegmentIntersection::OUTER_LOW); + assert(il.intersections.back().type == SegmentIntersection::OUTER_HIGH); + auto end = il.intersections.end() - 1; + insert_after.clear(); + for (auto it = il.intersections.begin() + 1; it != end;) { + if (it->type == SegmentIntersection::OUTER_HIGH) { + ++ it; + assert(it->type == SegmentIntersection::OUTER_LOW); + ++ it; + } else { + auto lo = it; + assert(lo->type == SegmentIntersection::INNER_LOW); + auto hi = ++ it; + assert(hi->type == SegmentIntersection::INNER_HIGH); + auto lo2 = ++ it; + if (lo2->type == SegmentIntersection::INNER_LOW) { + // INNER_HIGH followed by INNER_LOW. The outer contour may have squeezed the inner contour into two separate loops. + // In that case one shall insert a phony OUTER_HIGH / OUTER_LOW pair. + int up = hi->vertical_up(); + int dn = lo2->vertical_down(); +#ifndef _NDEBUG + assert(up == -1 || up > 0); + assert(dn == -1 || dn >= 0); + assert((up == -1 && dn == -1) || (dn + 1 == up)); +#endif // _NDEBUG + bool pinched = dn + 1 != up; + if (pinched) { + // hi is not connected with its inner contour to lo2. + // Insert a phony OUTER_HIGH / OUTER_LOW pair. +#if 0 + static int pinch_idx = 0; + printf("Pinched %d\n", pinch_idx++); +#endif + insert_after.emplace_back(hi - il.intersections.begin()); + } + } + } } - } - for (const SegmentIntersection &it : il_right.intersections) { - if (it.has_left_horizontal()) { - const SegmentIntersection &it_left = il_left.intersections[it.left_horizontal()]; - // For a right link there is a symmetric left link. - assert(it.iContour == it_left.iContour); - assert(it.type == it_left.type); - assert(it_left.has_right_horizontal()); - assert(it_left.right_horizontal() == int(&it - il_right.intersections.data())); + + if (! insert_after.empty()) { + // Insert phony OUTER_HIGH / OUTER_LOW pairs, adjust indices pointing to intersection points on this contour. + map.clear(); + { + size_t i = 0; + temp_intersections.clear(); + for (size_t idx_inset_after : insert_after) { + for (; i <= idx_inset_after; ++ i) { + map.emplace_back(temp_intersections.size()); + temp_intersections.emplace_back(il.intersections[i]); + } + coord_t pos = (temp_intersections.back().pos() + il.intersections[i].pos()) / 2; + temp_intersections.emplace_back(phony_outer_intersection(SegmentIntersection::OUTER_HIGH, pos)); + temp_intersections.emplace_back(phony_outer_intersection(SegmentIntersection::OUTER_LOW, pos)); + } + for (; i < il.intersections.size(); ++ i) { + map.emplace_back(temp_intersections.size()); + temp_intersections.emplace_back(il.intersections[i]); + } + temp_intersections.swap(il.intersections); + } + // Reindex references on current intersection line. + for (SegmentIntersection &ip : il.intersections) { + if (ip.has_left_vertical()) + ip.prev_on_contour = map[ip.prev_on_contour]; + if (ip.has_right_vertical()) + ip.next_on_contour = map[ip.next_on_contour]; + } + // Reindex references on previous intersection line. + for (SegmentIntersection &ip : segs[i_vline - 1].intersections) + if (ip.has_right_horizontal()) + ip.next_on_contour = map[ip.next_on_contour]; + if (i_vline < segs.size()) { + // Reindex references on next intersection line. + for (SegmentIntersection &ip : segs[i_vline + 1].intersections) + if (ip.has_left_horizontal()) + ip.prev_on_contour = map[ip.prev_on_contour]; + } } } } - for (size_t i_vline = 0; i_vline < segs.size(); ++ i_vline) { - const SegmentedIntersectionLine &il = segs[i_vline]; - for (const SegmentIntersection &it : il.intersections) { - auto i_it = int(&it - il.intersections.data()); - if (it.has_left_vertical_up()) { - assert(il.intersections[it.left_vertical_up()].left_vertical_down() == i_it); - assert(il.intersections[it.left_vertical_up()].prev_on_contour_quality == it.prev_on_contour_quality); - } - if (it.has_left_vertical_down()) { - assert(il.intersections[it.left_vertical_down()].left_vertical_up() == i_it); - assert(il.intersections[it.left_vertical_down()].prev_on_contour_quality == it.prev_on_contour_quality); - } - if (it.has_right_vertical_up()) { - assert(il.intersections[it.right_vertical_up()].right_vertical_down() == i_it); - assert(il.intersections[it.right_vertical_up()].next_on_contour_quality == it.next_on_contour_quality); - } - if (it.has_right_vertical_down()) { - assert(il.intersections[it.right_vertical_down()].right_vertical_up() == i_it); - assert(il.intersections[it.right_vertical_down()].next_on_contour_quality == it.next_on_contour_quality); - } - } - } -#endif /* NDEBUG */ + + assert(validate_segment_intersection_connectivity(segs)); } // Find the last INNER_HIGH intersection starting with INNER_LOW, that is followed by OUTER_HIGH intersection. @@ -1431,7 +1555,7 @@ struct AntPath struct MonotonicRegionLink { - MonotonicRegion *region; + MonotonicRegion *region; bool flipped; // Distance of right side of this region to left side of the next region, if the "flipped" flag of this region and the next region // is applied as defined. @@ -2058,7 +2182,7 @@ static std::vector chain_monotonic_regions( // After how many rounds without an improvement to exit? constexpr int num_rounds_no_change_exit = 8; // With how many ants each of the run will be performed? - const int num_ants = std::min(regions.size(), 10); + const int num_ants = std::min(int(regions.size()), 10); // Base (initial) pheromone level. This value will be adjusted based on the length of the first greedy path found. float pheromone_initial_deposit = 0.5f; // Evaporation rate of pheromones. @@ -2136,7 +2260,7 @@ static std::vector chain_monotonic_regions( } // Set an initial pheromone value to 10% of the greedy path's value. - pheromone_initial_deposit = 0.1 / total_length; + pheromone_initial_deposit = 0.1f / total_length; path_matrix.update_inital_pheromone(pheromone_initial_deposit); } @@ -2334,9 +2458,21 @@ static void polylines_from_paths(const std::vector &path, c // Handle nearly zero length edges. if (polyline->points.size() <= 1 || (polyline->points.size() == 2 && - std::abs(polyline->points.front()(0) - polyline->points.back()(0)) < SCALED_EPSILON && - std::abs(polyline->points.front()(1) - polyline->points.back()(1)) < SCALED_EPSILON)) + std::abs(polyline->points.front().x() - polyline->points.back().x()) < SCALED_EPSILON && + std::abs(polyline->points.front().y() - polyline->points.back().y()) < SCALED_EPSILON)) polylines_out.pop_back(); + else if (polylines_out.size() >= 2) { + assert(polyline->points.size() >= 2); + // Merge the two last polylines. An extrusion may have been split by an introduction of phony outer points on intersection lines + // to cope with pinching of inner offset contours. + Polyline &pl_prev = polylines_out[polylines_out.size() - 2]; + if (std::abs(polyline->points.front().x() - pl_prev.points.back().x()) < SCALED_EPSILON && + std::abs(polyline->points.front().y() - pl_prev.points.back().y()) < SCALED_EPSILON) { + pl_prev.points.back() = (pl_prev.points.back() + polyline->points.front()) / 2; + pl_prev.points.insert(pl_prev.points.end(), polyline->points.begin() + 1, polyline->points.end()); + polylines_out.pop_back(); + } + } polyline = nullptr; }; @@ -2489,7 +2625,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP surface->expolygon, - rotate_vector.first, float(scale_(this->overlap - (0.5 - INFILL_OVERLAP_OVER_SPACING) * this->spacing)), - float(scale_(this->overlap - 0.5 * this->spacing))); + float(scale_(this->overlap - 0.5f * this->spacing))); if (poly_with_offset.n_contours_inner == 0) { // Not a single infill line fits. //FIXME maybe one shall trigger the gap fill here? @@ -2561,6 +2697,10 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP //FIXME this is a hack to get the monotonic infill rolling. We likely want a smarter switch, likely based on user decison. bool monotonic_infill = params.monotonic; // || params.density > 0.99; if (monotonic_infill) { + // Sometimes the outer contour pinches the inner contour from both sides along a single vertical line. + // This situation is not handled correctly by generate_montonous_regions(). + // Insert phony OUTER_HIGH / OUTER_LOW pairs at the position where the contour is pinched. + pinch_contours_insert_phony_outer_intersections(segs); std::vector regions = generate_montonous_regions(segs); connect_monotonic_regions(regions, poly_with_offset, segs); if (! regions.empty()) { diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index dccd0f5cd..b1166d2a4 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -458,7 +458,7 @@ void PresetBundle::load_selections(AppConfig &config, const std::string &preferr this->update_multi_material_filament_presets(); // Parse the initial physical printer name. - std::string initial_physical_printer_name = remove_ini_suffix(config.get("extras", "physical_printer")); + std::string initial_physical_printer_name = remove_ini_suffix(config.get("presets", "physical_printer")); // Activate physical printer from the config if (!initial_physical_printer_name.empty()) @@ -482,8 +482,7 @@ void PresetBundle::export_selections(AppConfig &config) config.set("presets", "sla_print", sla_prints.get_selected_preset_name()); config.set("presets", "sla_material", sla_materials.get_selected_preset_name()); config.set("presets", "printer", printers.get_selected_preset_name()); - - config.set("extras", "physical_printer", physical_printers.get_selected_full_printer_name()); + config.set("presets", "physical_printer", physical_printers.get_selected_full_printer_name()); } DynamicPrintConfig PresetBundle::full_config() const diff --git a/src/platform/unix/PrusaGcodeviewer.desktop b/src/platform/unix/PrusaGcodeviewer.desktop new file mode 100644 index 000000000..b6c419b0f --- /dev/null +++ b/src/platform/unix/PrusaGcodeviewer.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Name=Prusa GCode viewer +Exec=prusa-slicer --gcodeviewer %F +Icon=PrusaSlicer # TODO: change when the new icon is ready +Terminal=false +Type=Application +MimeType=text/x.gcode; +Categories=Graphics;3DGraphics; +Keywords=3D;Printing;Slicer; \ No newline at end of file diff --git a/src/platform/unix/PrusaSlicer.desktop b/src/platform/unix/PrusaSlicer.desktop index 464873f45..dae507b54 100644 --- a/src/platform/unix/PrusaSlicer.desktop +++ b/src/platform/unix/PrusaSlicer.desktop @@ -1,9 +1,12 @@ [Desktop Entry] Name=PrusaSlicer -Exec=prusa-slicer %F +GenericName=3D Printing Software Icon=PrusaSlicer +Exec=prusa-slicer %F Terminal=false Type=Application MimeType=model/stl;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;application/x-amf; -Categories=Graphics;3DGraphics; -Keywords=3D;Printing;Slicer; \ No newline at end of file +Categories=Graphics;3DGraphics;Engineering; +Keywords=3D;Printing;Slicer;slice;3D;printer;convert;gcode;stl;obj;amf;SLA +StartupNotify=false +StartupWMClass=prusa-slicer \ No newline at end of file diff --git a/src/slic3r/Config/Snapshot.cpp b/src/slic3r/Config/Snapshot.cpp index 45dc99874..54d1dea57 100644 --- a/src/slic3r/Config/Snapshot.cpp +++ b/src/slic3r/Config/Snapshot.cpp @@ -31,8 +31,11 @@ void Snapshot::clear() this->comment.clear(); this->reason = SNAPSHOT_UNKNOWN; this->print.clear(); + this->sla_print.clear(); this->filaments.clear(); + this->sla_material.clear(); this->printer.clear(); + this->physical_printer.clear(); } void Snapshot::load_ini(const std::string &path) @@ -94,6 +97,8 @@ void Snapshot::load_ini(const std::string &path) for (auto &kvp : section.second) { if (kvp.first == "print") { this->print = kvp.second.data(); + } else if (kvp.first == "sla_print") { + this->sla_print = kvp.second.data(); } else if (boost::starts_with(kvp.first, "filament")) { int idx = 0; if (kvp.first == "filament" || sscanf(kvp.first.c_str(), "filament_%d", &idx) == 1) { @@ -101,8 +106,12 @@ void Snapshot::load_ini(const std::string &path) this->filaments.resize(idx + 1, std::string()); this->filaments[idx] = kvp.second.data(); } + } else if (kvp.first == "sla_material") { + this->sla_material = kvp.second.data(); } else if (kvp.first == "printer") { this->printer = kvp.second.data(); + } else if (kvp.first == "physical_printer") { + this->physical_printer = kvp.second.data(); } } } else if (boost::starts_with(section.first, group_name_vendor) && section.first.size() > group_name_vendor.size()) { @@ -172,10 +181,13 @@ void Snapshot::save_ini(const std::string &path) // Export the active presets at the time of the snapshot. c << std::endl << "[presets]" << std::endl; c << "print = " << this->print << std::endl; + c << "sla_print = " << this->sla_print << std::endl; c << "filament = " << this->filaments.front() << std::endl; for (size_t i = 1; i < this->filaments.size(); ++ i) c << "filament_" << std::to_string(i) << " = " << this->filaments[i] << std::endl; + c << "sla_material = " << this->sla_material << std::endl; c << "printer = " << this->printer << std::endl; + c << "physical_printer = " << this->physical_printer << std::endl; // Export the vendor configs. for (const VendorConfig &vc : this->vendor_configs) { @@ -199,14 +211,17 @@ void Snapshot::export_selections(AppConfig &config) const { assert(filaments.size() >= 1); config.clear_section("presets"); - config.set("presets", "print", print); - config.set("presets", "filament", filaments.front()); + config.set("presets", "print", print); + config.set("presets", "sla_print", sla_print); + config.set("presets", "filament", filaments.front()); for (unsigned i = 1; i < filaments.size(); ++i) { char name[64]; sprintf(name, "filament_%u", i); config.set("presets", name, filaments[i]); } - config.set("presets", "printer", printer); + config.set("presets", "sla_material", sla_material); + config.set("presets", "printer", printer); + config.set("presets", "physical_printer", physical_printer); } void Snapshot::export_vendor_configs(AppConfig &config) const @@ -217,8 +232,10 @@ void Snapshot::export_vendor_configs(AppConfig &config) const config.set_vendors(std::move(vendors)); } -// Perform a deep compare of the active print / filament / printer / vendor directories. -// Return true if the content of the current print / filament / printer / vendor directories +static constexpr auto snapshot_subdirs = { "print", "sla_print", "filament", "sla_material", "printer", "physical_printer", "vendor" }; + +// Perform a deep compare of the active print / sla_print / filament / sla_material / printer / physical_printer / vendor directories. +// Return true if the content of the current print / sla_print / filament / sla_material / printer / physical_printer / vendor directories // matches the state stored in this snapshot. bool Snapshot::equal_to_active(const AppConfig &app_config) const { @@ -243,7 +260,7 @@ bool Snapshot::equal_to_active(const AppConfig &app_config) const // 2) Check, whether this snapshot references the same set of ini files as the current state. boost::filesystem::path data_dir = boost::filesystem::path(Slic3r::data_dir()); boost::filesystem::path snapshot_dir = boost::filesystem::path(Slic3r::data_dir()) / SLIC3R_SNAPSHOTS_DIR / this->id; - for (const char *subdir : { "print", "filament", "printer", "vendor" }) { + for (const char *subdir : snapshot_subdirs) { boost::filesystem::path path1 = data_dir / subdir; boost::filesystem::path path2 = snapshot_dir / subdir; std::vector files1, files2; @@ -369,9 +386,12 @@ const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot: snapshot.comment = comment; snapshot.reason = reason; // Active presets at the time of the snapshot. - snapshot.print = app_config.get("presets", "print"); + snapshot.print = app_config.get("presets", "print"); + snapshot.sla_print = app_config.get("presets", "sla_print"); snapshot.filaments.emplace_back(app_config.get("presets", "filament")); - snapshot.printer = app_config.get("presets", "printer"); + snapshot.sla_material = app_config.get("presets", "sla_material"); + snapshot.printer = app_config.get("presets", "printer"); + snapshot.physical_printer = app_config.get("presets", "physical_printer"); for (unsigned i = 1; i < 1000; ++ i) { char name[64]; sprintf(name, "filament_%u", i); @@ -414,7 +434,7 @@ const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot: boost::filesystem::create_directory(snapshot_dir); // Backup the presets. - for (const char *subdir : { "print", "filament", "printer", "vendor" }) + for (const char *subdir : snapshot_subdirs) copy_config_dir_single_level(data_dir / subdir, snapshot_dir / subdir); snapshot.save_ini((snapshot_dir / "snapshot.ini").string()); assert(m_snapshots.empty() || m_snapshots.back().time_captured <= snapshot.time_captured); @@ -438,11 +458,11 @@ void SnapshotDB::restore_snapshot(const Snapshot &snapshot, AppConfig &app_confi boost::filesystem::path snapshot_db_dir = SnapshotDB::create_db_dir(); boost::filesystem::path snapshot_dir = snapshot_db_dir / snapshot.id; // Remove existing ini files and restore the ini files from the snapshot. - for (const char *subdir : { "print", "filament", "printer", "vendor" }) { + for (const char *subdir : snapshot_subdirs) { delete_existing_ini_files(data_dir / subdir); copy_config_dir_single_level(snapshot_dir / subdir, data_dir / subdir); } - // Update AppConfig with the selections of the print / filament / printer profiles + // Update AppConfig with the selections of the print / sla_print / filament / sla_material / printer profiles // and about the installed printer types and variants. snapshot.export_selections(app_config); snapshot.export_vendor_configs(app_config); diff --git a/src/slic3r/Config/Snapshot.hpp b/src/slic3r/Config/Snapshot.hpp index c6bd5c6e8..48add8a1a 100644 --- a/src/slic3r/Config/Snapshot.hpp +++ b/src/slic3r/Config/Snapshot.hpp @@ -23,8 +23,11 @@ namespace Config { // Slic3r.ini // vendor/ // print/ +// sla_print/ // filament/ +// sla_material // printer/ +// physical_printer/ class Snapshot { public: @@ -42,12 +45,12 @@ public: void load_ini(const std::string &path); void save_ini(const std::string &path); - // Export the print / filament / printer selections to be activated into the AppConfig. + // Export the print / sla_print / filament / sla_material / printer selections to be activated into the AppConfig. void export_selections(AppConfig &config) const; void export_vendor_configs(AppConfig &config) const; - // Perform a deep compare of the active print / filament / printer / vendor directories. - // Return true if the content of the current print / filament / printer / vendor directories + // Perform a deep compare of the active print / sla_print / filament / sla_material / printer / physical_printer / vendor directories. + // Return true if the content of the current print / sla_print / filament / sla_material / printer / physical_printer / vendor directories // matches the state stored in this snapshot. bool equal_to_active(const AppConfig &app_config) const; @@ -65,8 +68,11 @@ public: // Active presets at the time of the snapshot. std::string print; + std::string sla_print; std::vector filaments; + std::string sla_material; std::string printer; + std::string physical_printer; // Annotation of the vendor configuration stored in the snapshot. // This information is displayed to the user and used to decide compatibility @@ -97,7 +103,7 @@ public: size_t load_db(); void update_slic3r_versions(std::vector &index_db); - // Create a snapshot directory, copy the vendor config bundles, user print/filament/printer profiles, + // Create a snapshot directory, copy the vendor config bundles, user print / sla_print / filament / sla_material / printer / physical_printer profiles, // create an index. const Snapshot& take_snapshot(const AppConfig &app_config, Snapshot::Reason reason, const std::string &comment = ""); const Snapshot& restore_snapshot(const std::string &id, AppConfig &app_config); diff --git a/src/slic3r/GUI/ConfigSnapshotDialog.cpp b/src/slic3r/GUI/ConfigSnapshotDialog.cpp index 48b5a2b00..5a9a2306c 100644 --- a/src/slic3r/GUI/ConfigSnapshotDialog.cpp +++ b/src/slic3r/GUI/ConfigSnapshotDialog.cpp @@ -48,9 +48,17 @@ static wxString generate_html_row(const Config::Snapshot &snapshot, bool row_eve text += "
"; // End of row header. text += _(L("PrusaSlicer version")) + ": " + snapshot.slic3r_version_captured.to_string() + "
"; - text += _(L("print")) + ": " + snapshot.print + "
"; - text += _(L("filaments")) + ": " + snapshot.filaments.front() + "
"; - text += _(L("printer")) + ": " + snapshot.printer + "
"; + bool has_fff = ! snapshot.print.empty() || ! snapshot.filaments.empty(); + bool has_sla = ! snapshot.sla_print.empty() || ! snapshot.sla_material.empty(); + if (has_fff || ! has_sla) { + text += _(L("print")) + ": " + snapshot.print + "
"; + text += _(L("filaments")) + ": " + snapshot.filaments.front() + "
"; + } + if (has_sla) { + text += _(L("SLA print")) + ": " + snapshot.sla_print + "
"; + text += _(L("SLA material")) + ": " + snapshot.sla_material + "
"; + } + text += _(L("printer")) + ": " + (snapshot.physical_printer.empty() ? snapshot.printer : snapshot.physical_printer) + "
"; bool compatible = true; for (const Config::Snapshot::VendorConfig &vc : snapshot.vendor_configs) { diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index f37131570..44e0bcd42 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -12,9 +12,7 @@ #include "I18N.hpp" #include "ExtruderSequenceDialog.hpp" #include "libslic3r/Print.hpp" -#if ENABLE_GCODE_VIEWER #include "libslic3r/AppConfig.hpp" -#endif // ENABLE_GCODE_VIEWER #include #include @@ -477,8 +475,10 @@ void Control::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_ { const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; +#if ENABLE_GCODE_VIEWER if (!m_enable_action_icon) return; +#endif // ENABLE_GCODE_VIEWER // suppress add tick on first layer if (tick == 0) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index bde3f3f41..feef55476 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -717,6 +717,7 @@ void GUI_App::init_app_config() std::string error = app_config->load(); if (!error.empty()) { // Error while parsing config file. We'll customize the error message and rethrow to be displayed. +#if ENABLE_GCODE_VIEWER if (is_editor()) { throw Slic3r::RuntimeError( _u8L("Error parsing PrusaSlicer config file, it is probably corrupted. " @@ -724,11 +725,14 @@ void GUI_App::init_app_config() "\n\n" + app_config->config_path() + "\n\n" + error); } else { +#endif // ENABLE_GCODE_VIEWER throw Slic3r::RuntimeError( _u8L("Error parsing PrusaGCodeViewer config file, it is probably corrupted. " "Try to manually delete the file to recover from the error.") + "\n\n" + app_config->config_path() + "\n\n" + error); +#if ENABLE_GCODE_VIEWER } +#endif // ENABLE_GCODE_VIEWER } } } diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index cb499b3c6..8516a4419 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -9,6 +9,8 @@ #include #include +#include + #include #include @@ -639,7 +641,7 @@ NotificationManager::NotificationManager(wxEvtHandler* evt_handler) : void NotificationManager::push_notification(const NotificationType type, GLCanvas3D& canvas, int timestamp) { auto it = std::find_if(basic_notifications.begin(), basic_notifications.end(), - boost::bind(&NotificationData::type, _1) == type); + boost::bind(&NotificationData::type, boost::placeholders::_1) == type); assert(it != basic_notifications.end()); if (it != basic_notifications.end()) push_notification_data( *it, canvas, timestamp); @@ -825,7 +827,7 @@ void NotificationManager::render_notifications(GLCanvas3D& canvas, float overlay it = m_pop_notifications.erase(it); } else { (*it)->set_paused(m_hovered); - PopNotification::RenderResult res = (*it)->render(canvas, last_x, m_move_from_overlay, overlay_width); + PopNotification::RenderResult res = (*it)->render(canvas, last_x, m_move_from_overlay && !m_in_preview, overlay_width); if (res != PopNotification::RenderResult::Finished) { last_x = (*it)->get_top() + GAP_WIDTH; current_height = std::max(current_height, (*it)->get_current_top());