Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer
This commit is contained in:
commit
00ec9cc4ed
17 changed files with 415 additions and 85 deletions
|
@ -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 ()
|
||||
|
||||
|
|
11
deps/CGAL/CGAL.cmake
vendored
11
deps/CGAL/CGAL.cmake
vendored
|
@ -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}"
|
||||
)
|
||||
|
|
37
deps/CGAL/cgal/CGALConfigVersion.cmake
vendored
Normal file
37
deps/CGAL/cgal/CGALConfigVersion.cmake
vendored
Normal file
|
@ -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()
|
12
resources/data/flatpak/com.prusa3d.PrusaSlicer.desktop
Executable file
12
resources/data/flatpak/com.prusa3d.PrusaSlicer.desktop
Executable file
|
@ -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
|
62
resources/data/flatpak/com.prusa3d.PrusaSlicer.metainfo.xml
Executable file
62
resources/data/flatpak/com.prusa3d.PrusaSlicer.metainfo.xml
Executable file
|
@ -0,0 +1,62 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="desktop">
|
||||
<id>com.prusa3d.PrusaSlicer</id>
|
||||
<launchable type="desktop-id">com.prusa3d.PrusaSlicer.desktop</launchable>
|
||||
<provides>
|
||||
<id>prusa-slicer.desktop</id>
|
||||
</provides>
|
||||
<name>PrusaSlicer</name>
|
||||
<summary>Powerful 3D printing slicer optimized for Prusa printers</summary>
|
||||
<metadata_license>0BSD</metadata_license>
|
||||
<project_license>AGPL-3.0-only</project_license>
|
||||
<description>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
<p>
|
||||
PrusaSlicer is based on Slic3r by Alessandro Ranelucci and the RepRap community.
|
||||
</p>
|
||||
<p>
|
||||
What are some of PrusaSlicer's main features?
|
||||
</p>
|
||||
<ul>
|
||||
<li>multi-platform (Linux/Mac/Win) and packaged as standalone-app with no dependencies required</li>
|
||||
<li>complete command-line interface to use it with no GUI</li>
|
||||
<li>multi-material (multiple extruders) object printing</li>
|
||||
<li>multiple G-code flavors supported (RepRap, Makerbot, Mach3, Machinekit etc.)</li>
|
||||
<li>ability to plate multiple objects having distinct print settings</li>
|
||||
<li>multithread processing</li>
|
||||
<li>STL auto-repair (tolerance for broken models)</li>
|
||||
<li>wide automated unit testing</li>
|
||||
</ul>
|
||||
</description>
|
||||
<url type="homepage">https://www.prusa3d.com/prusaslicer/</url>
|
||||
<url type="help">https://help.prusa3d.com</url>
|
||||
<url type="bugtracker">https://github.com/prusa3d/PrusaSlicer/issues</url>
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<image>https://user-images.githubusercontent.com/590307/78981854-24d07580-7b21-11ea-9441-77923534a659.png</image>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<image>https://user-images.githubusercontent.com/590307/78981860-2863fc80-7b21-11ea-8c2d-8ff79ced2578.png</image>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<image>https://user-images.githubusercontent.com/590307/78981862-28fc9300-7b21-11ea-9b0d-d03e16b709d3.png</image>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
<content_rating type="oars-1.1" />
|
||||
<releases>
|
||||
<release version="2.2.0" date="2020-03-21">
|
||||
<description>
|
||||
<p>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.</p>
|
||||
</description>
|
||||
</release>
|
||||
</releases>
|
||||
</component>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<SegmentIntersection> 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<size_t>::max();
|
||||
out.iSegment = std::numeric_limits<size_t>::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<SegmentedIntersectionLine> slice_region_by_vertical_lines(con
|
|||
return segs;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool validate_segment_intersection_connectivity(const std::vector<SegmentedIntersectionLine> &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<SegmentedIntersectionLine> &segs)
|
||||
{
|
||||
// Keep the vector outside the loops, so they will not be reallocated.
|
||||
// Where to insert new outer points.
|
||||
std::vector<size_t> insert_after;
|
||||
// Mapping of indices of current intersection line after inserting new outer points.
|
||||
std::vector<int32_t> map;
|
||||
std::vector<SegmentIntersection> 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<MonotonicRegionLink> 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<int>(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<MonotonicRegionLink> 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<MonotonicRegionLink> &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<MonotonicRegion> regions = generate_montonous_regions(segs);
|
||||
connect_monotonic_regions(regions, poly_with_offset, segs);
|
||||
if (! regions.empty()) {
|
||||
|
|
|
@ -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
|
||||
|
|
9
src/platform/unix/PrusaGcodeviewer.desktop
Normal file
9
src/platform/unix/PrusaGcodeviewer.desktop
Normal file
|
@ -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;
|
|
@ -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;
|
||||
Categories=Graphics;3DGraphics;Engineering;
|
||||
Keywords=3D;Printing;Slicer;slice;3D;printer;convert;gcode;stl;obj;amf;SLA
|
||||
StartupNotify=false
|
||||
StartupWMClass=prusa-slicer
|
|
@ -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<std::string> 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);
|
||||
|
|
|
@ -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<std::string> 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> &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);
|
||||
|
|
|
@ -48,9 +48,17 @@ static wxString generate_html_row(const Config::Snapshot &snapshot, bool row_eve
|
|||
text += "</b></font><br>";
|
||||
// End of row header.
|
||||
text += _(L("PrusaSlicer version")) + ": " + snapshot.slic3r_version_captured.to_string() + "<br>";
|
||||
text += _(L("print")) + ": " + snapshot.print + "<br>";
|
||||
text += _(L("filaments")) + ": " + snapshot.filaments.front() + "<br>";
|
||||
text += _(L("printer")) + ": " + snapshot.printer + "<br>";
|
||||
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 + "<br>";
|
||||
text += _(L("filaments")) + ": " + snapshot.filaments.front() + "<br>";
|
||||
}
|
||||
if (has_sla) {
|
||||
text += _(L("SLA print")) + ": " + snapshot.sla_print + "<br>";
|
||||
text += _(L("SLA material")) + ": " + snapshot.sla_material + "<br>";
|
||||
}
|
||||
text += _(L("printer")) + ": " + (snapshot.physical_printer.empty() ? snapshot.printer : snapshot.physical_printer) + "<br>";
|
||||
|
||||
bool compatible = true;
|
||||
for (const Config::Snapshot::VendorConfig &vc : snapshot.vendor_configs) {
|
||||
|
|
|
@ -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 <wx/button.h>
|
||||
#include <wx/dialog.h>
|
||||
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/bind/placeholders.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <wx/glcanvas.h>
|
||||
|
@ -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());
|
||||
|
|
Loading…
Add table
Reference in a new issue