diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt
index 74bf6eb53eb..71894582864 100644
--- a/deps/CMakeLists.txt
+++ b/deps/CMakeLists.txt
@@ -152,53 +152,54 @@ include(MPFR/MPFR.cmake)
include(CGAL/CGAL.cmake)
include(wxWidgets/wxWidgets.cmake)
-if (ZLIB_PKG)
+if (NOT "${ZLIB_PKG}" STREQUAL "")
add_dependencies(dep_blosc ${ZLIB_PKG})
add_dependencies(dep_openexr ${ZLIB_PKG})
endif ()
-if (MSVC)
-
- add_custom_target(deps ALL
- DEPENDS
- dep_boost
- dep_tbb
- dep_libcurl
- dep_wxWidgets
- dep_gtest
- dep_cereal
- dep_nlopt
- # dep_qhull # Experimental
- dep_openvdb
- dep_OpenCSG
- dep_CGAL
- ${PNG_PKG}
- ${ZLIB_PKG}
- ${EXPAT_PKG}
+set(_dep_list
+ dep_boost
+ dep_tbb
+ dep_libcurl
+ dep_wxWidgets
+ dep_gtest
+ dep_cereal
+ dep_nlopt
+ dep_openvdb
+ dep_OpenCSG
+ dep_CGAL
+ ${PNG_PKG}
+ ${ZLIB_PKG}
+ ${EXPAT_PKG}
)
-else()
-
- add_custom_target(deps ALL
- DEPENDS
- dep_boost
- dep_tbb
- dep_libcurl
- dep_wxWidgets
- dep_gtest
- dep_cereal
- dep_nlopt
- dep_qhull
- dep_openvdb
- dep_OpenCSG
- dep_CGAL
- ${PNG_PKG}
- ${ZLIB_PKG}
- ${EXPAT_PKG}
- # dep_libigl # Not working, static build has different Eigen
+if ("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8")
+ # Patch the boost::polygon library with a custom one.
+ ExternalProject_Add(dep_boost_polygon
+ EXCLUDE_FROM_ALL ON
+ GIT_REPOSITORY "https://github.com/prusa3d/polygon"
+ GIT_TAG prusaslicer_gmp
+ DEPENDS dep_boost
+ CONFIGURE_COMMAND ""
+ BUILD_COMMAND ""
+ INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory
+ "${CMAKE_CURRENT_BINARY_DIR}/dep_boost_polygon-prefix/src/dep_boost_polygon/include/boost/polygon"
+ "${DESTDIR}/usr/local/include/boost/polygon"
)
+ # Only override boost::Polygon Voronoi implementation with Vojtech's GMP hacks on 64bit platforms.
+ list(APPEND _dep_list "dep_boost_polygon")
+endif ()
+if (MSVC)
+ # Experimental
+ #list(APPEND _dep_list "dep_qhull")
+else()
+ list(APPEND _dep_list "dep_qhull")
+ # Not working, static build has different Eigen
+ #list(APPEND _dep_list "dep_libigl")
endif()
+add_custom_target(deps ALL DEPENDS ${_dep_list})
+
# Note: I'm not using any of the LOG_xxx options in ExternalProject_Add() commands
# because they seem to generate bogus build files (possibly a bug in ExternalProject).
diff --git a/deps/deps-macos.cmake b/deps/deps-macos.cmake
index ef00b80d50d..a71a0ebfc0f 100644
--- a/deps/deps-macos.cmake
+++ b/deps/deps-macos.cmake
@@ -9,6 +9,8 @@ set(DEP_CMAKE_OPTS
"-DCMAKE_OSX_DEPLOYMENT_TARGET=${DEP_OSX_TARGET}"
"-DCMAKE_CXX_FLAGS=${DEP_WERRORS_SDK}"
"-DCMAKE_C_FLAGS=${DEP_WERRORS_SDK}"
+ "-DCMAKE_FIND_FRAMEWORK=LAST"
+ "-DCMAKE_FIND_APPBUNDLE=LAST"
)
include("deps-unix-common.cmake")
diff --git a/resources/icons/Pmetal_001.png b/resources/icons/Pmetal_001.png
new file mode 100644
index 00000000000..c848f839c50
Binary files /dev/null and b/resources/icons/Pmetal_001.png differ
diff --git a/resources/icons/cog_.svg b/resources/icons/cog_.svg
new file mode 100644
index 00000000000..94cab0a8eba
--- /dev/null
+++ b/resources/icons/cog_.svg
@@ -0,0 +1,17 @@
+
+
+
diff --git a/resources/icons/white/dot.svg b/resources/icons/white/dot.svg
new file mode 100644
index 00000000000..90fbaf7fb10
--- /dev/null
+++ b/resources/icons/white/dot.svg
@@ -0,0 +1,8 @@
+
+
+
diff --git a/resources/icons/white/drop_to_bed.svg b/resources/icons/white/drop_to_bed.svg
new file mode 100644
index 00000000000..76243f89768
--- /dev/null
+++ b/resources/icons/white/drop_to_bed.svg
@@ -0,0 +1,15 @@
+
+
+
diff --git a/resources/icons/white/edit_uni.svg b/resources/icons/white/edit_uni.svg
new file mode 100644
index 00000000000..661924763ca
--- /dev/null
+++ b/resources/icons/white/edit_uni.svg
@@ -0,0 +1,20 @@
+
+
+
diff --git a/resources/icons/white/eye_closed.svg b/resources/icons/white/eye_closed.svg
new file mode 100644
index 00000000000..0cdd16ae006
--- /dev/null
+++ b/resources/icons/white/eye_closed.svg
@@ -0,0 +1,13 @@
+
+
+
diff --git a/resources/icons/white/eye_open.svg b/resources/icons/white/eye_open.svg
new file mode 100644
index 00000000000..1b320da079c
--- /dev/null
+++ b/resources/icons/white/eye_open.svg
@@ -0,0 +1,11 @@
+
+
+
diff --git a/resources/icons/white/lock_closed_f.svg b/resources/icons/white/lock_closed_f.svg
new file mode 100644
index 00000000000..412c93c1647
--- /dev/null
+++ b/resources/icons/white/lock_closed_f.svg
@@ -0,0 +1,10 @@
+
+
+
diff --git a/resources/icons/white/search.svg b/resources/icons/white/search.svg
new file mode 100644
index 00000000000..679bb30f71c
--- /dev/null
+++ b/resources/icons/white/search.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/resources/shaders/gouraud.fs b/resources/shaders/gouraud.fs
index 853a5512c1e..45175acc21f 100644
--- a/resources/shaders/gouraud.fs
+++ b/resources/shaders/gouraud.fs
@@ -17,6 +17,9 @@ struct SlopeDetection
uniform vec4 uniform_color;
uniform SlopeDetection slope;
+uniform sampler2D environment_tex;
+uniform bool use_environment_tex;
+
varying vec3 clipping_planes_dots;
// x = tainted, y = specular;
@@ -26,6 +29,7 @@ varying vec3 delta_box_min;
varying vec3 delta_box_max;
varying float world_normal_z;
+varying vec3 eye_normal;
vec3 slope_color()
{
@@ -40,5 +44,8 @@ void main()
vec3 color = slope.actived ? slope_color() : uniform_color.rgb;
// if the fragment is outside the print volume -> use darker color
color = (any(lessThan(delta_box_min, ZERO)) || any(greaterThan(delta_box_max, ZERO))) ? mix(color, ZERO, 0.3333) : color;
- gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + color * intensity.x, uniform_color.a);
+ if (use_environment_tex)
+ gl_FragColor = vec4(0.45 * texture2D(environment_tex, normalize(eye_normal).xy * 0.5 + 0.5).xyz + 0.8 * color * intensity.x, uniform_color.a);
+ else
+ gl_FragColor = vec4(vec3(intensity.y) + color * intensity.x, uniform_color.a);
}
diff --git a/resources/shaders/gouraud.vs b/resources/shaders/gouraud.vs
index 2644d48e4eb..d60f6eae8a4 100644
--- a/resources/shaders/gouraud.vs
+++ b/resources/shaders/gouraud.vs
@@ -51,22 +51,23 @@ varying vec3 delta_box_max;
varying vec3 clipping_planes_dots;
varying float world_normal_z;
+varying vec3 eye_normal;
void main()
{
// First transform the normal into camera space and normalize the result.
- vec3 normal = normalize(gl_NormalMatrix * gl_Normal);
+ eye_normal = normalize(gl_NormalMatrix * gl_Normal);
// Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex.
// Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range.
- float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0);
+ float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0);
intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
vec3 position = (gl_ModelViewMatrix * gl_Vertex).xyz;
- intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS);
+ intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS);
// Perform the same lighting calculation for the 2nd light source (no specular applied).
- NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0);
+ NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0);
intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
// compute deltas for out of print volume detection (world coordinates)
diff --git a/sandboxes/CMakeLists.txt b/sandboxes/CMakeLists.txt
index a2bd13bb0b3..23c15f0899f 100644
--- a/sandboxes/CMakeLists.txt
+++ b/sandboxes/CMakeLists.txt
@@ -2,3 +2,4 @@
#add_subdirectory(openvdb)
add_subdirectory(meshboolean)
add_subdirectory(opencsg)
+#add_subdirectory(aabb-evaluation)
\ No newline at end of file
diff --git a/sandboxes/aabb-evaluation/CMakeLists.txt b/sandboxes/aabb-evaluation/CMakeLists.txt
new file mode 100644
index 00000000000..20011e34509
--- /dev/null
+++ b/sandboxes/aabb-evaluation/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_executable(aabb-evaluation aabb-evaluation.cpp)
+target_link_libraries(aabb-evaluation libslic3r ${Boost_LIBRARIES} ${TBB_LIBRARIES} ${Boost_LIBRARIES} ${CMAKE_DL_LIBS})
diff --git a/sandboxes/aabb-evaluation/aabb-evaluation.cpp b/sandboxes/aabb-evaluation/aabb-evaluation.cpp
new file mode 100644
index 00000000000..9ec7451e50e
--- /dev/null
+++ b/sandboxes/aabb-evaluation/aabb-evaluation.cpp
@@ -0,0 +1,224 @@
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 4244)
+#pragma warning(disable: 4267)
+#endif
+#include
+#include
+#include
+#include
+#include
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+const std::string USAGE_STR = {
+ "Usage: aabb-evaluation stlfilename.stl"
+};
+
+using namespace Slic3r;
+
+void profile(const TriangleMesh &mesh)
+{
+ Eigen::MatrixXd V;
+ Eigen::MatrixXi F;
+ Eigen::MatrixXd vertex_normals;
+ sla::to_eigen_mesh(mesh, V, F);
+ igl::per_vertex_normals(V, F, vertex_normals);
+
+ static constexpr int num_samples = 100;
+ const int num_vertices = std::min(10000, int(mesh.its.vertices.size()));
+ const Eigen::MatrixXd dirs = igl::random_dir_stratified(num_samples).cast();
+
+ Eigen::MatrixXd occlusion_output0;
+ {
+ AABBTreeIndirect::Tree3f tree;
+ {
+ PROFILE_BLOCK(AABBIndirect_Init);
+ tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(mesh.its.vertices, mesh.its.indices);
+ }
+ {
+ PROFILE_BLOCK(EigenMesh3D_AABBIndirectF_AmbientOcclusion);
+ occlusion_output0.resize(num_vertices, 1);
+ for (int ivertex = 0; ivertex < num_vertices; ++ ivertex) {
+ const Eigen::Vector3d origin = mesh.its.vertices[ivertex].template cast();
+ const Eigen::Vector3d normal = vertex_normals.row(ivertex).template cast();
+ int num_hits = 0;
+ for (int s = 0; s < num_samples; s++) {
+ Eigen::Vector3d d = dirs.row(s);
+ if(d.dot(normal) < 0) {
+ // reverse ray
+ d *= -1;
+ }
+ igl::Hit hit;
+ if (AABBTreeIndirect::intersect_ray_first_hit(mesh.its.vertices, mesh.its.indices, tree, (origin + 1e-4 * d).eval(), d, hit))
+ ++ num_hits;
+ }
+ occlusion_output0(ivertex) = (double)num_hits/(double)num_samples;
+ }
+ }
+
+ {
+ PROFILE_BLOCK(EigenMesh3D_AABBIndirectFF_AmbientOcclusion);
+ occlusion_output0.resize(num_vertices, 1);
+ for (int ivertex = 0; ivertex < num_vertices; ++ ivertex) {
+ const Eigen::Vector3d origin = mesh.its.vertices[ivertex].template cast();
+ const Eigen::Vector3d normal = vertex_normals.row(ivertex).template cast();
+ int num_hits = 0;
+ for (int s = 0; s < num_samples; s++) {
+ Eigen::Vector3d d = dirs.row(s);
+ if(d.dot(normal) < 0) {
+ // reverse ray
+ d *= -1;
+ }
+ igl::Hit hit;
+ if (AABBTreeIndirect::intersect_ray_first_hit(mesh.its.vertices, mesh.its.indices, tree,
+ Eigen::Vector3f((origin + 1e-4 * d).template cast()),
+ Eigen::Vector3f(d.template cast()), hit))
+ ++ num_hits;
+ }
+ occlusion_output0(ivertex) = (double)num_hits/(double)num_samples;
+ }
+ }
+ }
+
+ Eigen::MatrixXd occlusion_output1;
+ {
+ std::vector vertices;
+ std::vector triangles;
+ for (int i = 0; i < V.rows(); ++ i)
+ vertices.emplace_back(V.row(i).transpose());
+ for (int i = 0; i < F.rows(); ++ i)
+ triangles.emplace_back(F.row(i).transpose());
+ AABBTreeIndirect::Tree3d tree;
+ {
+ PROFILE_BLOCK(AABBIndirectD_Init);
+ tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(vertices, triangles);
+ }
+
+ {
+ PROFILE_BLOCK(EigenMesh3D_AABBIndirectD_AmbientOcclusion);
+ occlusion_output1.resize(num_vertices, 1);
+ for (int ivertex = 0; ivertex < num_vertices; ++ ivertex) {
+ const Eigen::Vector3d origin = V.row(ivertex).template cast();
+ const Eigen::Vector3d normal = vertex_normals.row(ivertex).template cast();
+ int num_hits = 0;
+ for (int s = 0; s < num_samples; s++) {
+ Eigen::Vector3d d = dirs.row(s);
+ if(d.dot(normal) < 0) {
+ // reverse ray
+ d *= -1;
+ }
+ igl::Hit hit;
+ if (AABBTreeIndirect::intersect_ray_first_hit(vertices, triangles, tree, Eigen::Vector3d(origin + 1e-4 * d), d, hit))
+ ++ num_hits;
+ }
+ occlusion_output1(ivertex) = (double)num_hits/(double)num_samples;
+ }
+ }
+ }
+
+ // Build the AABB accelaration tree
+
+ Eigen::MatrixXd occlusion_output2;
+ {
+ igl::AABB AABB;
+ {
+ PROFILE_BLOCK(EigenMesh3D_AABB_Init);
+ AABB.init(V, F);
+ }
+ {
+ PROFILE_BLOCK(EigenMesh3D_AABB_AmbientOcclusion);
+ occlusion_output2.resize(num_vertices, 1);
+ for (int ivertex = 0; ivertex < num_vertices; ++ ivertex) {
+ const Eigen::Vector3d origin = V.row(ivertex).template cast();
+ const Eigen::Vector3d normal = vertex_normals.row(ivertex).template cast();
+ int num_hits = 0;
+ for (int s = 0; s < num_samples; s++) {
+ Eigen::Vector3d d = dirs.row(s);
+ if(d.dot(normal) < 0) {
+ // reverse ray
+ d *= -1;
+ }
+ igl::Hit hit;
+ if (AABB.intersect_ray(V, F, origin + 1e-4 * d, d, hit))
+ ++ num_hits;
+ }
+ occlusion_output2(ivertex) = (double)num_hits/(double)num_samples;
+ }
+ }
+ }
+
+ Eigen::MatrixXd occlusion_output3;
+ {
+ typedef Eigen::Map> MapMatrixXfUnaligned;
+ typedef Eigen::Map> MapMatrixXiUnaligned;
+ igl::AABB AABB;
+ auto vertices = MapMatrixXfUnaligned(mesh.its.vertices.front().data(), mesh.its.vertices.size(), 3);
+ auto faces = MapMatrixXiUnaligned(mesh.its.indices.front().data(), mesh.its.indices.size(), 3);
+ {
+ PROFILE_BLOCK(EigenMesh3D_AABBf_Init);
+ AABB.init(
+ vertices,
+ faces);
+ }
+
+ {
+ PROFILE_BLOCK(EigenMesh3D_AABBf_AmbientOcclusion);
+ occlusion_output3.resize(num_vertices, 1);
+ for (int ivertex = 0; ivertex < num_vertices; ++ ivertex) {
+ const Eigen::Vector3d origin = mesh.its.vertices[ivertex].template cast();
+ const Eigen::Vector3d normal = vertex_normals.row(ivertex).template cast();
+ int num_hits = 0;
+ for (int s = 0; s < num_samples; s++) {
+ Eigen::Vector3d d = dirs.row(s);
+ if(d.dot(normal) < 0) {
+ // reverse ray
+ d *= -1;
+ }
+ igl::Hit hit;
+ if (AABB.intersect_ray(vertices, faces, (origin + 1e-4 * d).eval().template cast(), d.template cast(), hit))
+ ++ num_hits;
+ }
+ occlusion_output3(ivertex) = (double)num_hits/(double)num_samples;
+ }
+ }
+ }
+
+ PROFILE_UPDATE();
+ PROFILE_OUTPUT(nullptr);
+}
+
+int main(const int argc, const char *argv[])
+{
+ if(argc < 2) {
+ std::cout << USAGE_STR << std::endl;
+ return EXIT_SUCCESS;
+ }
+
+ TriangleMesh mesh;
+ if (! mesh.ReadSTLFile(argv[1])) {
+ std::cerr << "Error loading " << argv[1] << std::endl;
+ return -1;
+ }
+
+ mesh.repair();
+ if (mesh.facets_count() == 0) {
+ std::cerr << "Error loading " << argv[1] << " . It is empty." << std::endl;
+ return -1;
+ }
+
+ profile(mesh);
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp
index 6ef58799410..a0422f5fa0d 100644
--- a/src/PrusaSlicer.cpp
+++ b/src/PrusaSlicer.cpp
@@ -53,6 +53,8 @@
#include "slic3r/GUI/3DScene.hpp"
#include "slic3r/GUI/InstanceCheck.hpp"
#include "slic3r/GUI/AppConfig.hpp"
+ #include "slic3r/GUI/MainFrame.hpp"
+ #include "slic3r/GUI/Plater.hpp"
#endif /* SLIC3R_GUI */
using namespace Slic3r;
@@ -532,9 +534,6 @@ int CLI::run(int argc, char **argv)
return -1;
}
- //gui->app_config = app_config;
- //app_config = nullptr;
-
// gui->autosave = m_config.opt_string("autosave");
GUI::GUI_App::SetInstance(gui);
gui->CallAfter([gui, this, &load_configs] {
diff --git a/src/Shiny/ShinyOutput.c b/src/Shiny/ShinyOutput.c
index ad02ea003c6..c2c624d589b 100644
--- a/src/Shiny/ShinyOutput.c
+++ b/src/Shiny/ShinyOutput.c
@@ -40,8 +40,8 @@ THE SOFTWARE.
/*---------------------------------------------------------------------------*/
#define OUTPUT_WIDTH_CALL 6
-#define OUTPUT_WIDTH_TIME 6
-#define OUTPUT_WIDTH_PERC 4
+#define OUTPUT_WIDTH_TIME (6+3)
+#define OUTPUT_WIDTH_PERC (4+3)
#define OUTPUT_WIDTH_SUM 120
#define OUTPUT_WIDTH_DATA (1+OUTPUT_WIDTH_CALL + 1 + 2*(OUTPUT_WIDTH_TIME+4+OUTPUT_WIDTH_PERC+1) + 1)
@@ -70,7 +70,7 @@ SHINY_INLINE char* printData(char *output, const ShinyData *a_data, float a_tope
const ShinyTimeUnit *totalUnit = ShinyGetTimeUnit(totalTicksAvg);
snprintf(output, OUTPUT_WIDTH_DATA + TRAILING,
- " %*.1f %*.0f %-2s %*.0f%% %*.0f %-2s %*.0f%%",
+ " %*.1f %*.2f %-2s %*.2f%% %*.2f %-2s %*.2f%%",
OUTPUT_WIDTH_CALL, a_data->entryCount.avg,
OUTPUT_WIDTH_TIME, a_data->selfTicks.avg * selfUnit->invTickFreq, selfUnit->suffix,
OUTPUT_WIDTH_PERC, a_data->selfTicks.avg * a_topercent,
diff --git a/src/clipper/CMakeLists.txt b/src/clipper/CMakeLists.txt
index 412ab53c7d4..8a4e9285268 100644
--- a/src/clipper/CMakeLists.txt
+++ b/src/clipper/CMakeLists.txt
@@ -7,3 +7,7 @@ add_library(clipper STATIC
clipper_z.cpp
clipper_z.hpp
)
+
+if(SLIC3R_PROFILE)
+ target_link_libraries(clipper Shiny)
+endif()
diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h
index eb47459eabf..d32f64aa4bd 100644
--- a/src/imgui/imconfig.h
+++ b/src/imgui/imconfig.h
@@ -107,7 +107,7 @@ namespace ImGui
const char ColorMarkerStart = 0x2; // STX
const char ColorMarkerEnd = 0x3; // ETX
- // Special ASCII characters are used here as a ikons markers
+ // Special ASCII characters are used here as an ikons markers
const char PrintIconMarker = 0x4;
const char PrinterIconMarker = 0x5;
const char PrinterSlaIconMarker = 0x6;
diff --git a/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp
index 57da6ec12eb..dd80322bcc5 100644
--- a/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp
+++ b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp
@@ -77,7 +77,7 @@ inline void offset(PolygonImpl& sh, TCoord distance, const PolygonTag
#define DISABLE_BOOST_OFFSET
using ClipperLib::ClipperOffset;
- using ClipperLib::jtMiter;
+ using ClipperLib::jtSquare;
using ClipperLib::etClosedPolygon;
using ClipperLib::Paths;
@@ -85,8 +85,8 @@ inline void offset(PolygonImpl& sh, TCoord distance, const PolygonTag
try {
ClipperOffset offs;
- offs.AddPath(sh.Contour, jtMiter, etClosedPolygon);
- offs.AddPaths(sh.Holes, jtMiter, etClosedPolygon);
+ offs.AddPath(sh.Contour, jtSquare, etClosedPolygon);
+ offs.AddPaths(sh.Holes, jtSquare, etClosedPolygon);
offs.Execute(result, static_cast(distance));
} catch (ClipperLib::clipperException &) {
throw GeometryException(GeomErr::OFFSET);
diff --git a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp
index 91f04cb9330..6cdaadd2570 100644
--- a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp
+++ b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp
@@ -528,15 +528,12 @@ class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer 0) diff += wdiff;
- if(hdiff > 0) diff += hdiff;
+ auto wdiff = TCompute(bb.width()) - bin.width();
+ auto hdiff = TCompute(bb.height()) - bin.height();
+ double diff = .0;
+ if(wdiff > 0) diff += double(wdiff);
+ if(hdiff > 0) diff += double(hdiff);
+
return diff;
}
diff --git a/src/libslic3r/AABBTreeIndirect.hpp b/src/libslic3r/AABBTreeIndirect.hpp
new file mode 100644
index 00000000000..ec9b14a7ae8
--- /dev/null
+++ b/src/libslic3r/AABBTreeIndirect.hpp
@@ -0,0 +1,698 @@
+// AABB tree built upon external data set, referencing the external data by integer indices.
+// The AABB tree balancing and traversal (ray casting, closest triangle of an indexed triangle mesh)
+// were adapted from libigl AABB.{cpp,hpp} Copyright (C) 2015 Alec Jacobson
+// while the implicit balanced tree representation and memory optimizations are Vojtech's.
+
+#ifndef slic3r_AABBTreeIndirect_hpp_
+#define slic3r_AABBTreeIndirect_hpp_
+
+#include
+#include
+#include
+#include
+
+#include "Utils.hpp" // for next_highest_power_of_2()
+
+extern "C"
+{
+// Ray-Triangle Intersection Test Routines by Tomas Moller, May 2000
+#include
+}
+// Definition of the ray intersection hit structure.
+#include
+
+namespace Slic3r {
+namespace AABBTreeIndirect {
+
+// Static balanced AABB tree for raycasting and closest triangle search.
+// The balanced tree is built over a single large std::vector of nodes, where the children of nodes
+// are addressed implicitely using a power of two indexing rule.
+// Memory for a full balanced tree is allocated, but not all nodes at the last level are used.
+// This may seem like a waste of memory, but one saves memory for the node links and there is zero
+// overhead of a memory allocator management (usually the memory allocator adds at least one pointer
+// before the memory returned). However, allocating memory in a single vector is very fast even
+// in multi-threaded environment and it is cache friendly.
+//
+// A balanced tree is built upon a vector of bounding boxes and their centroids, storing the reference
+// to the source entity (a 3D triangle, a 2D segment etc, a 3D or 2D point etc).
+// The source bounding boxes may have an epsilon applied to fight numeric rounding errors when
+// traversing the AABB tree.
+template
+class Tree
+{
+public:
+ static constexpr int NumDimensions = ANumDimensions;
+ using CoordType = ACoordType;
+ using VectorType = Eigen::Matrix;
+ using BoundingBox = Eigen::AlignedBox;
+ // Following could be static constexpr size_t, but that would not link in C++11
+ enum : size_t {
+ // Node is not used.
+ npos = size_t(-1),
+ // Inner node (not leaf).
+ inner = size_t(-2)
+ };
+
+ // Single node of the implicit balanced AABB tree. There are no links to the children nodes,
+ // as these links are calculated implicitely using a power of two rule.
+ struct Node {
+ // Index of the external source entity, for which this AABB tree was built, npos for internal nodes.
+ size_t idx = npos;
+ // Bounding box around this entity, possibly with epsilons applied to fight numeric rounding errors
+ // when traversing the AABB tree.
+ BoundingBox bbox;
+
+ bool is_valid() const { return this->idx != npos; }
+ bool is_inner() const { return this->idx == inner; }
+ bool is_leaf() const { return ! this->is_inner(); }
+
+ template
+ void set(const SourceNode &rhs) {
+ this->idx = rhs.idx();
+ this->bbox = rhs.bbox();
+ }
+ };
+
+ void clear() { m_nodes.clear(); }
+
+ // SourceNode shall implement
+ // size_t SourceNode::idx() const
+ // - Index to the outside entity (triangle, edge, point etc).
+ // const VectorType& SourceNode::centroid() const
+ // - Centroid of this node. The centroid is used for balancing the tree.
+ // const BoundingBox& SourceNode::bbox() const
+ // - Bounding box of this node, likely expanded with epsilon to account for numeric rounding during tree traversal.
+ // Union of bounding boxes at a single level of the AABB tree is used for deciding the longest axis aligned dimension
+ // to split around.
+ template
+ void build(std::vector &&input)
+ {
+ if (input.empty())
+ clear();
+ else {
+ // Allocate enough memory for a full binary tree.
+ m_nodes.assign(next_highest_power_of_2(input.size()) * 2 - 1, Node());
+ build_recursive(input, 0, 0, input.size() - 1);
+ }
+ input.clear();
+ }
+
+ const std::vector& nodes() const { return m_nodes; }
+ const Node& node(size_t idx) const { return m_nodes[idx]; }
+ bool empty() const { return m_nodes.empty(); }
+
+ // Addressing the child nodes using the power of two rule.
+ static size_t left_child_idx(size_t idx) { return idx * 2 + 1; }
+ static size_t right_child_idx(size_t idx) { return left_child_idx(idx) + 1; }
+ const Node& left_child(size_t idx) const { return m_nodes[left_child_idx(idx)]; }
+ const Node& right_child(size_t idx) const { return m_nodes[right_child_idx(idx)]; }
+
+ template
+ void build(const std::vector &input)
+ {
+ std::vector copy(input);
+ this->build(std::move(copy));
+ }
+
+private:
+ // Build a balanced tree by splitting the input sequence by an axis aligned plane at a dimension.
+ template
+ void build_recursive(std::vector &input, size_t node, const size_t left, const size_t right)
+ {
+ assert(node < m_nodes.size());
+ assert(left <= right);
+
+ if (left == right) {
+ // Insert a node into the balanced tree.
+ m_nodes[node].set(input[left]);
+ return;
+ }
+
+ // Calculate bounding box of the input.
+ BoundingBox bbox(input[left].bbox());
+ for (size_t i = left + 1; i <= right; ++ i)
+ bbox.extend(input[i].bbox());
+ int dimension = -1;
+ bbox.diagonal().maxCoeff(&dimension);
+
+ // Partition the input to left / right pieces of the same length to produce a balanced tree.
+ size_t center = (left + right) / 2;
+ partition_input(input, size_t(dimension), left, right, center);
+ // Insert an inner node into the tree. Inner node does not reference any input entity (triangle, line segment etc).
+ m_nodes[node].idx = inner;
+ m_nodes[node].bbox = bbox;
+ build_recursive(input, node * 2 + 1, left, center);
+ build_recursive(input, node * 2 + 2, center + 1, right);
+ }
+
+ // Partition the input m_nodes at "k" and "dimension" using the QuickSelect method:
+ // https://en.wikipedia.org/wiki/Quickselect
+ // Items left of the k'th item are lower than the k'th item in the "dimension",
+ // items right of the k'th item are higher than the k'th item in the "dimension",
+ template
+ void partition_input(std::vector &input, const size_t dimension, size_t left, size_t right, const size_t k) const
+ {
+ while (left < right) {
+ size_t center = (left + right) / 2;
+ CoordType pivot;
+ {
+ // Bubble sort the input[left], input[center], input[right], so that a median of the three values
+ // will end up in input[center].
+ CoordType left_value = input[left ].centroid()(dimension);
+ CoordType center_value = input[center].centroid()(dimension);
+ CoordType right_value = input[right ].centroid()(dimension);
+ if (left_value > center_value) {
+ std::swap(input[left], input[center]);
+ std::swap(left_value, center_value);
+ }
+ if (left_value > right_value) {
+ std::swap(input[left], input[right]);
+ right_value = left_value;
+ }
+ if (center_value > right_value) {
+ std::swap(input[center], input[right]);
+ center_value = right_value;
+ }
+ pivot = center_value;
+ }
+ if (right <= left + 2)
+ // The interval is already sorted.
+ break;
+ size_t i = left;
+ size_t j = right - 1;
+ std::swap(input[center], input[j]);
+ // Partition the set based on the pivot.
+ for (;;) {
+ // Skip left points that are already at correct positions.
+ // Search will certainly stop at position (right - 1), which stores the pivot.
+ while (input[++ i].centroid()(dimension) < pivot) ;
+ // Skip right points that are already at correct positions.
+ while (input[-- j].centroid()(dimension) > pivot && i < j) ;
+ if (i >= j)
+ break;
+ std::swap(input[i], input[j]);
+ }
+ // Restore pivot to the center of the sequence.
+ std::swap(input[i], input[right - 1]);
+ // Which side the kth element is in?
+ if (k < i)
+ right = i - 1;
+ else if (k == i)
+ // Sequence is partitioned, kth element is at its place.
+ break;
+ else
+ left = i + 1;
+ }
+ }
+
+ // The balanced tree storage.
+ std::vector m_nodes;
+};
+
+using Tree2f = Tree<2, float>;
+using Tree3f = Tree<3, float>;
+using Tree2d = Tree<2, double>;
+using Tree3d = Tree<3, double>;
+
+namespace detail {
+ template
+ struct RayIntersector {
+ using VertexType = AVertexType;
+ using IndexedFaceType = AIndexedFaceType;
+ using TreeType = ATreeType;
+ using VectorType = AVectorType;
+
+ const std::vector &vertices;
+ const std::vector &faces;
+ const TreeType &tree;
+
+ const VectorType origin;
+ const VectorType dir;
+ const VectorType invdir;
+ };
+
+ template
+ struct RayIntersectorHits : RayIntersector {
+ std::vector hits;
+ };
+
+ //FIXME implement SSE for float AABB trees with float ray queries.
+ // SSE/SSE2 is supported by any Intel/AMD x64 processor.
+ // SSE support requires 16 byte alignment of the AABB nodes, representing the bounding boxes with 4+4 floats,
+ // storing the node index as the 4th element of the bounding box min value etc.
+ // https://www.flipcode.com/archives/SSE_RayBox_Intersection_Test.shtml
+ template
+ inline bool ray_box_intersect_invdir(
+ const Eigen::MatrixBase &origin,
+ const Eigen::MatrixBase &inv_dir,
+ Eigen::AlignedBox box,
+ const Scalar &t0,
+ const Scalar &t1) {
+ // http://people.csail.mit.edu/amy/papers/box-jgt.pdf
+ // "An Efficient and Robust Ray–Box Intersection Algorithm"
+ if (inv_dir.x() < 0)
+ std::swap(box.min().x(), box.max().x());
+ if (inv_dir.y() < 0)
+ std::swap(box.min().y(), box.max().y());
+ Scalar tmin = (box.min().x() - origin.x()) * inv_dir.x();
+ Scalar tymax = (box.max().y() - origin.y()) * inv_dir.y();
+ if (tmin > tymax)
+ return false;
+ Scalar tmax = (box.max().x() - origin.x()) * inv_dir.x();
+ Scalar tymin = (box.min().y() - origin.y()) * inv_dir.y();
+ if (tymin > tmax)
+ return false;
+ if (tymin > tmin)
+ tmin = tymin;
+ if (tymax < tmax)
+ tmax = tymax;
+ if (inv_dir.z() < 0)
+ std::swap(box.min().z(), box.max().z());
+ Scalar tzmin = (box.min().z() - origin.z()) * inv_dir.z();
+ if (tzmin > tmax)
+ return false;
+ Scalar tzmax = (box.max().z() - origin.z()) * inv_dir.z();
+ if (tmin > tzmax)
+ return false;
+ if (tzmin > tmin)
+ tmin = tzmin;
+ if (tzmax < tmax)
+ tmax = tzmax;
+ return tmin < t1 && tmax > t0;
+ }
+
+ template
+ std::enable_if_t::value && std::is_same::value, bool>
+ intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W v2, double &t, double &u, double &v) {
+ return intersect_triangle1(const_cast(origin.data()), const_cast(dir.data()),
+ const_cast(v0.data()), const_cast(v1.data()), const_cast(v2.data()),
+ &t, &u, &v);
+ }
+
+ template
+ std::enable_if_t::value && !std::is_same::value, bool>
+ intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W v2, double &t, double &u, double &v) {
+ using Vector = Eigen::Matrix;
+ Vector w0 = v0.template cast();
+ Vector w1 = v1.template cast();
+ Vector w2 = v2.template cast();
+ return intersect_triangle1(const_cast(origin.data()), const_cast(dir.data()),
+ w0.data(), w1.data(), w2.data(), &t, &u, &v);
+ }
+
+ template
+ std::enable_if_t::value && std::is_same::value, bool>
+ intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W v2, double &t, double &u, double &v) {
+ using Vector = Eigen::Matrix;
+ Vector o = origin.template cast();
+ Vector d = dir.template cast();
+ return intersect_triangle1(o.data(), d.data(), const_cast(v0.data()), const_cast(v1.data()), const_cast(v2.data()), &t, &u, &v);
+ }
+
+ template
+ std::enable_if_t::value && ! std::is_same::value, bool>
+ intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W v2, double &t, double &u, double &v) {
+ using Vector = Eigen::Matrix;
+ Vector o = origin.template cast();
+ Vector d = dir.template cast();
+ Vector w0 = v0.template cast();
+ Vector w1 = v1.template cast();
+ Vector w2 = v2.template cast();
+ return intersect_triangle1(o.data(), d.data(), w0.data(), w1.data(), w2.data(), &t, &u, &v);
+ }
+
+ template
+ static inline bool intersect_ray_recursive_first_hit(
+ RayIntersectorType &ray_intersector,
+ size_t node_idx,
+ Scalar min_t,
+ igl::Hit &hit)
+ {
+ const auto &node = ray_intersector.tree.node(node_idx);
+ assert(node.is_valid());
+
+ if (! ray_box_intersect_invdir(ray_intersector.origin, ray_intersector.invdir, node.bbox.template cast(), Scalar(0), min_t))
+ return false;
+
+ if (node.is_leaf()) {
+ // shoot ray, record hit
+ auto face = ray_intersector.faces[node.idx];
+ double t, u, v;
+ if (intersect_triangle(
+ ray_intersector.origin, ray_intersector.dir,
+ ray_intersector.vertices[face(0)], ray_intersector.vertices[face(1)], ray_intersector.vertices[face(2)],
+ t, u, v)
+ && t > 0.) {
+ hit = igl::Hit { int(node.idx), -1, float(u), float(v), float(t) };
+ return true;
+ } else
+ return false;
+ } else {
+ // Left / right child node index.
+ size_t left = node_idx * 2 + 1;
+ size_t right = left + 1;
+ igl::Hit left_hit;
+ igl::Hit right_hit;
+ bool left_ret = intersect_ray_recursive_first_hit(ray_intersector, left, min_t, left_hit);
+ if (left_ret && left_hit.t < min_t) {
+ min_t = left_hit.t;
+ hit = left_hit;
+ } else
+ left_ret = false;
+ bool right_ret = intersect_ray_recursive_first_hit(ray_intersector, right, min_t, right_hit);
+ if (right_ret && right_hit.t < min_t)
+ hit = right_hit;
+ else
+ right_ret = false;
+ return left_ret || right_ret;
+ }
+ }
+
+ template
+ static inline void intersect_ray_recursive_all_hits(RayIntersectorType &ray_intersector, size_t node_idx)
+ {
+ using Scalar = typename RayIntersectorType::VectorType::Scalar;
+
+ const auto &node = ray_intersector.tree.node(node_idx);
+ assert(node.is_valid());
+
+ if (! ray_box_intersect_invdir(ray_intersector.origin, ray_intersector.invdir, node.bbox.template cast(),
+ Scalar(0), std::numeric_limits::infinity()))
+ return;
+
+ if (node.is_leaf()) {
+ auto face = ray_intersector.faces[node.idx];
+ double t, u, v;
+ if (intersect_triangle(
+ ray_intersector.origin, ray_intersector.dir,
+ ray_intersector.vertices[face(0)], ray_intersector.vertices[face(1)], ray_intersector.vertices[face(2)],
+ t, u, v)
+ && t > 0.) {
+ ray_intersector.hits.emplace_back(igl::Hit{ int(node.idx), -1, float(u), float(v), float(t) });
+ }
+ } else {
+ // Left / right child node index.
+ size_t left = node_idx * 2 + 1;
+ size_t right = left + 1;
+ intersect_ray_recursive_all_hits(ray_intersector, left);
+ intersect_ray_recursive_all_hits(ray_intersector, right);
+ }
+ }
+
+ // Nothing to do with COVID-19 social distancing.
+ template
+ struct IndexedTriangleSetDistancer {
+ using VertexType = AVertexType;
+ using IndexedFaceType = AIndexedFaceType;
+ using TreeType = ATreeType;
+ using VectorType = AVectorType;
+
+ const std::vector &vertices;
+ const std::vector &faces;
+ const TreeType &tree;
+
+ const VectorType origin;
+ };
+
+ // Real-time collision detection, Ericson, Chapter 5
+ template
+ static inline Vector closest_point_to_triangle(const Vector &p, const Vector &a, const Vector &b, const Vector &c)
+ {
+ using Scalar = typename Vector::Scalar;
+ // Check if P in vertex region outside A
+ Vector ab = b - a;
+ Vector ac = c - a;
+ Vector ap = p - a;
+ Scalar d1 = ab.dot(ap);
+ Scalar d2 = ac.dot(ap);
+ if (d1 <= 0 && d2 <= 0)
+ return a;
+ // Check if P in vertex region outside B
+ Vector bp = p - b;
+ Scalar d3 = ab.dot(bp);
+ Scalar d4 = ac.dot(bp);
+ if (d3 >= 0 && d4 <= d3)
+ return b;
+ // Check if P in edge region of AB, if so return projection of P onto AB
+ Scalar vc = d1*d4 - d3*d2;
+ if (a != b && vc <= 0 && d1 >= 0 && d3 <= 0) {
+ Scalar v = d1 / (d1 - d3);
+ return a + v * ab;
+ }
+ // Check if P in vertex region outside C
+ Vector cp = p - c;
+ Scalar d5 = ab.dot(cp);
+ Scalar d6 = ac.dot(cp);
+ if (d6 >= 0 && d5 <= d6)
+ return c;
+ // Check if P in edge region of AC, if so return projection of P onto AC
+ Scalar vb = d5*d2 - d1*d6;
+ if (vb <= 0 && d2 >= 0 && d6 <= 0) {
+ Scalar w = d2 / (d2 - d6);
+ return a + w * ac;
+ }
+ // Check if P in edge region of BC, if so return projection of P onto BC
+ Scalar va = d3*d6 - d5*d4;
+ if (va <= 0 && (d4 - d3) >= 0 && (d5 - d6) >= 0) {
+ Scalar w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
+ return b + w * (c - b);
+ }
+ // P inside face region. Compute Q through its barycentric coordinates (u,v,w)
+ Scalar denom = Scalar(1.0) / (va + vb + vc);
+ Scalar v = vb * denom;
+ Scalar w = vc * denom;
+ return a + ab * v + ac * w; // = u*a + v*b + w*c, u = va * denom = 1.0-v-w
+ };
+
+ template
+ static inline Scalar squared_distance_to_indexed_triangle_set_recursive(
+ IndexedTriangleSetDistancerType &distancer,
+ size_t node_idx,
+ Scalar low_sqr_d,
+ Scalar up_sqr_d,
+ size_t &i,
+ Eigen::PlainObjectBase &c)
+ {
+ using Vector = typename IndexedTriangleSetDistancerType::VectorType;
+
+ if (low_sqr_d > up_sqr_d)
+ return low_sqr_d;
+
+ // Save the best achieved hit.
+ auto set_min = [&i, &c, &up_sqr_d](const Scalar sqr_d_candidate, const size_t i_candidate, const Vector &c_candidate) {
+ if (sqr_d_candidate < up_sqr_d) {
+ i = i_candidate;
+ c = c_candidate;
+ up_sqr_d = sqr_d_candidate;
+ }
+ };
+
+ const auto &node = distancer.tree.node(node_idx);
+ assert(node.is_valid());
+ if (node.is_leaf())
+ {
+ const auto &triangle = distancer.faces[node.idx];
+ Vector c_candidate = closest_point_to_triangle(
+ distancer.origin,
+ distancer.vertices[triangle(0)].template cast(),
+ distancer.vertices[triangle(1)].template cast(),
+ distancer.vertices[triangle(2)].template cast());
+ set_min((c_candidate - distancer.origin).squaredNorm(), node.idx, c_candidate);
+ }
+ else
+ {
+ size_t left_node_idx = node_idx * 2 + 1;
+ size_t right_node_idx = left_node_idx + 1;
+ const auto &node_left = distancer.tree.node(left_node_idx);
+ const auto &node_right = distancer.tree.node(right_node_idx);
+ assert(node_left.is_valid());
+ assert(node_right.is_valid());
+
+ bool looked_left = false;
+ bool looked_right = false;
+ const auto &look_left = [&]()
+ {
+ size_t i_left;
+ Vector c_left = c;
+ Scalar sqr_d_left = squared_distance_to_indexed_triangle_set_recursive(distancer, left_node_idx, low_sqr_d, up_sqr_d, i_left, c_left);
+ set_min(sqr_d_left, i_left, c_left);
+ looked_left = true;
+ };
+ const auto &look_right = [&]()
+ {
+ size_t i_right;
+ Vector c_right = c;
+ Scalar sqr_d_right = squared_distance_to_indexed_triangle_set_recursive(distancer, right_node_idx, low_sqr_d, up_sqr_d, i_right, c_right);
+ set_min(sqr_d_right, i_right, c_right);
+ looked_right = true;
+ };
+
+ // must look left or right if in box
+ using BBoxScalar = typename IndexedTriangleSetDistancerType::TreeType::BoundingBox::Scalar;
+ if (node_left.bbox.contains(distancer.origin.template cast()))
+ look_left();
+ if (node_right.bbox.contains(distancer.origin.template cast()))
+ look_right();
+ // if haven't looked left and could be less than current min, then look
+ Scalar left_up_sqr_d = node_left.bbox.squaredExteriorDistance(distancer.origin);
+ Scalar right_up_sqr_d = node_right.bbox.squaredExteriorDistance(distancer.origin);
+ if (left_up_sqr_d < right_up_sqr_d) {
+ if (! looked_left && left_up_sqr_d < up_sqr_d)
+ look_left();
+ if (! looked_right && right_up_sqr_d < up_sqr_d)
+ look_right();
+ } else {
+ if (! looked_right && right_up_sqr_d < up_sqr_d)
+ look_right();
+ if (! looked_left && left_up_sqr_d < up_sqr_d)
+ look_left();
+ }
+ }
+ return up_sqr_d;
+ }
+
+} // namespace detail
+
+// Build a balanced AABB Tree over an indexed triangles set, balancing the tree
+// on centroids of the triangles.
+// Epsilon is applied to the bounding boxes of the AABB Tree to cope with numeric inaccuracies
+// during tree traversal.
+template
+inline Tree<3, typename VertexType::Scalar> build_aabb_tree_over_indexed_triangle_set(
+ // Indexed triangle set - 3D vertices.
+ const std::vector &vertices,
+ // Indexed triangle set - triangular faces, references to vertices.
+ const std::vector &faces,
+ //FIXME do we want to apply an epsilon?
+ const typename VertexType::Scalar eps = 0)
+{
+ using TreeType = Tree<3, typename VertexType::Scalar>;
+// using CoordType = typename TreeType::CoordType;
+ using VectorType = typename TreeType::VectorType;
+ using BoundingBox = typename TreeType::BoundingBox;
+
+ struct InputType {
+ size_t idx() const { return m_idx; }
+ const BoundingBox& bbox() const { return m_bbox; }
+ const VectorType& centroid() const { return m_centroid; }
+
+ size_t m_idx;
+ BoundingBox m_bbox;
+ VectorType m_centroid;
+ };
+
+ std::vector input;
+ input.reserve(faces.size());
+ const VectorType veps(eps, eps, eps);
+ for (size_t i = 0; i < faces.size(); ++ i) {
+ const IndexedFaceType &face = faces[i];
+ const VertexType &v1 = vertices[face(0)];
+ const VertexType &v2 = vertices[face(1)];
+ const VertexType &v3 = vertices[face(2)];
+ InputType n;
+ n.m_idx = i;
+ n.m_centroid = (1./3.) * (v1 + v2 + v3);
+ n.m_bbox = BoundingBox(v1, v1);
+ n.m_bbox.extend(v2);
+ n.m_bbox.extend(v3);
+ n.m_bbox.min() -= veps;
+ n.m_bbox.max() += veps;
+ input.emplace_back(n);
+ }
+
+ TreeType out;
+ out.build(std::move(input));
+ return out;
+}
+
+// Find a first intersection of a ray with indexed triangle set.
+// Intersection test is calculated with the accuracy of VectorType::Scalar
+// even if the triangle mesh and the AABB Tree are built with floats.
+template
+inline bool intersect_ray_first_hit(
+ // Indexed triangle set - 3D vertices.
+ const std::vector &vertices,
+ // Indexed triangle set - triangular faces, references to vertices.
+ const std::vector &faces,
+ // AABBTreeIndirect::Tree over vertices & faces, bounding boxes built with the accuracy of vertices.
+ const TreeType &tree,
+ // Origin of the ray.
+ const VectorType &origin,
+ // Direction of the ray.
+ const VectorType &dir,
+ // First intersection of the ray with the indexed triangle set.
+ igl::Hit &hit)
+{
+ using Scalar = typename VectorType::Scalar;
+ auto ray_intersector = detail::RayIntersector {
+ vertices, faces, tree,
+ origin, dir, VectorType(dir.cwiseInverse())
+ };
+ return ! tree.empty() && detail::intersect_ray_recursive_first_hit(
+ ray_intersector, size_t(0), std::numeric_limits::infinity(), hit);
+}
+
+// Find all intersections of a ray with indexed triangle set.
+// Intersection test is calculated with the accuracy of VectorType::Scalar
+// even if the triangle mesh and the AABB Tree are built with floats.
+// The output hits are sorted by the ray parameter.
+// If the ray intersects a shared edge of two triangles, hits for both triangles are returned.
+template
+inline bool intersect_ray_all_hits(
+ // Indexed triangle set - 3D vertices.
+ const std::vector &vertices,
+ // Indexed triangle set - triangular faces, references to vertices.
+ const std::vector &faces,
+ // AABBTreeIndirect::Tree over vertices & faces, bounding boxes built with the accuracy of vertices.
+ const TreeType &tree,
+ // Origin of the ray.
+ const VectorType &origin,
+ // Direction of the ray.
+ const VectorType &dir,
+ // All intersections of the ray with the indexed triangle set, sorted by parameter t.
+ std::vector &hits)
+{
+ auto ray_intersector = detail::RayIntersectorHits {
+ vertices, faces, tree,
+ origin, dir, VectorType(dir.cwiseInverse())
+ };
+ if (! tree.empty()) {
+ ray_intersector.hits.reserve(8);
+ detail::intersect_ray_recursive_all_hits(ray_intersector, 0);
+ std::swap(hits, ray_intersector.hits);
+ std::sort(hits.begin(), hits.end(), [](const auto &l, const auto &r) { return l.t < r.t; });
+ }
+ return ! hits.empty();
+}
+
+// Finding a closest triangle, its closest point and squared distance to the closest point
+// on a 3D indexed triangle set using a pre-built AABBTreeIndirect::Tree.
+// Closest point to triangle test will be performed with the accuracy of VectorType::Scalar
+// even if the triangle mesh and the AABB Tree are built with floats.
+// Returns squared distance to the closest point or -1 if the input is empty.
+template
+inline typename VectorType::Scalar squared_distance_to_indexed_triangle_set(
+ // Indexed triangle set - 3D vertices.
+ const std::vector &vertices,
+ // Indexed triangle set - triangular faces, references to vertices.
+ const std::vector &faces,
+ // AABBTreeIndirect::Tree over vertices & faces, bounding boxes built with the accuracy of vertices.
+ const TreeType &tree,
+ // Point to which the closest point on the indexed triangle set is searched for.
+ const VectorType &point,
+ // Index of the closest triangle in faces.
+ size_t &hit_idx_out,
+ // Position of the closest point on the indexed triangle set.
+ Eigen::PlainObjectBase &hit_point_out)
+{
+ using Scalar = typename VectorType::Scalar;
+ auto distancer = detail::IndexedTriangleSetDistancer
+ { vertices, faces, tree, point };
+ return tree.empty() ? Scalar(-1) :
+ detail::squared_distance_to_indexed_triangle_set_recursive(distancer, size_t(0), Scalar(0), std::numeric_limits::infinity(), hit_idx_out, hit_point_out);
+}
+
+} // namespace AABBTreeIndirect
+} // namespace Slic3r
+
+#endif /* slic3r_AABBTreeIndirect_hpp_ */
diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp
index 5b048b0ffbd..6ae7dd6a2e1 100644
--- a/src/libslic3r/Arrange.cpp
+++ b/src/libslic3r/Arrange.cpp
@@ -1,7 +1,8 @@
#include "Arrange.hpp"
-//#include "Geometry.hpp"
#include "SVG.hpp"
+#include "BoundingBox.hpp"
+
#include
#include
#include
@@ -51,8 +52,8 @@ template struct NfpImpl
namespace Slic3r {
template, int...EigenArgs>
-inline SLIC3R_CONSTEXPR Eigen::Matrix unscaled(
- const ClipperLib::IntPoint &v) SLIC3R_NOEXCEPT
+inline constexpr Eigen::Matrix unscaled(
+ const ClipperLib::IntPoint &v) noexcept
{
return Eigen::Matrix{unscaled(v.X),
unscaled(v.Y)};
@@ -395,11 +396,14 @@ template<> std::function AutoArranger::get_objfn()
double score = std::get<0>(result);
auto& fullbb = std::get<1>(result);
- double miss = Placer::overfit(fullbb, m_bin);
+ auto bin = m_bin;
+ sl::offset(bin, -EPSILON * (m_bin.width() + m_bin.height()));
+
+ double miss = Placer::overfit(fullbb, bin);
miss = miss > 0? miss : 0;
score += miss*miss;
- return score;
+ return score;
};
}
diff --git a/src/libslic3r/Arrange.hpp b/src/libslic3r/Arrange.hpp
index 8ff1e37a9fa..7630ab3e8e5 100644
--- a/src/libslic3r/Arrange.hpp
+++ b/src/libslic3r/Arrange.hpp
@@ -2,9 +2,12 @@
#define ARRANGE_HPP
#include "ExPolygon.hpp"
-#include "BoundingBox.hpp"
-namespace Slic3r { namespace arrangement {
+namespace Slic3r {
+
+class BoundingBox;
+
+namespace arrangement {
/// A geometry abstraction for a circular print bed. Similarly to BoundingBox.
class CircleBed {
diff --git a/src/libslic3r/BoundingBox.cpp b/src/libslic3r/BoundingBox.cpp
index d3cca7ff243..e3f93950922 100644
--- a/src/libslic3r/BoundingBox.cpp
+++ b/src/libslic3r/BoundingBox.cpp
@@ -11,17 +11,6 @@ template BoundingBoxBase::BoundingBoxBase(const std::vector &point
template BoundingBox3Base::BoundingBox3Base(const std::vector &points);
-BoundingBox::BoundingBox(const Lines &lines)
-{
- Points points;
- points.reserve(lines.size());
- for (const Line &line : lines) {
- points.emplace_back(line.a);
- points.emplace_back(line.b);
- }
- *this = BoundingBox(points);
-}
-
void BoundingBox::polygon(Polygon* polygon) const
{
polygon->points.clear();
diff --git a/src/libslic3r/BoundingBox.hpp b/src/libslic3r/BoundingBox.hpp
index 2216d788822..f03733dd868 100644
--- a/src/libslic3r/BoundingBox.hpp
+++ b/src/libslic3r/BoundingBox.hpp
@@ -20,17 +20,19 @@ class BoundingBoxBase
min(pmin), max(pmax), defined(pmin(0) < pmax(0) && pmin(1) < pmax(1)) {}
BoundingBoxBase(const std::vector& points) : min(PointClass::Zero()), max(PointClass::Zero())
{
- if (points.empty())
- throw std::invalid_argument("Empty point set supplied to BoundingBoxBase constructor");
-
- typename std::vector::const_iterator it = points.begin();
- this->min = *it;
- this->max = *it;
- for (++ it; it != points.end(); ++ it) {
- this->min = this->min.cwiseMin(*it);
- this->max = this->max.cwiseMax(*it);
+ if (points.empty()) {
+ this->defined = false;
+ // throw std::invalid_argument("Empty point set supplied to BoundingBoxBase constructor");
+ } else {
+ typename std::vector::const_iterator it = points.begin();
+ this->min = *it;
+ this->max = *it;
+ for (++ it; it != points.end(); ++ it) {
+ this->min = this->min.cwiseMin(*it);
+ this->max = this->max.cwiseMax(*it);
+ }
+ this->defined = (this->min(0) < this->max(0)) && (this->min(1) < this->max(1));
}
- this->defined = (this->min(0) < this->max(0)) && (this->min(1) < this->max(1));
}
void reset() { this->defined = false; this->min = PointClass::Zero(); this->max = PointClass::Zero(); }
void merge(const PointClass &point);
@@ -143,7 +145,6 @@ class BoundingBox : public BoundingBoxBase
BoundingBox() : BoundingBoxBase() {}
BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase(pmin, pmax) {}
BoundingBox(const Points &points) : BoundingBoxBase(points) {}
- BoundingBox(const Lines &lines);
friend BoundingBox get_extents_rotated(const Points &points, double angle);
};
diff --git a/src/libslic3r/BridgeDetector.cpp b/src/libslic3r/BridgeDetector.cpp
index ce7c960fa3b..bf8907c3fe2 100644
--- a/src/libslic3r/BridgeDetector.cpp
+++ b/src/libslic3r/BridgeDetector.cpp
@@ -53,7 +53,7 @@ void BridgeDetector::initialize()
this->_edges = intersection_pl(to_polylines(grown), contours);
#ifdef SLIC3R_DEBUG
- printf(" bridge has " PRINTF_ZU " support(s)\n", this->_edges.size());
+ printf(" bridge has %zu support(s)\n", this->_edges.size());
#endif
// detect anchors as intersection between our bridge expolygon and the lower slices
diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index 12e61d7f3c0..9f566b4051b 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -187,7 +187,12 @@ add_library(libslic3r STATIC
Utils.hpp
Time.cpp
Time.hpp
+ TriangleSelector.cpp
+ TriangleSelector.hpp
MTUtils.hpp
+ VoronoiOffset.cpp
+ VoronoiOffset.hpp
+ VoronoiVisualUtils.hpp
Zipper.hpp
Zipper.cpp
MinAreaBoundingBox.hpp
@@ -304,7 +309,7 @@ if(WIN32)
endif()
if(SLIC3R_PROFILE)
- target_link_libraries(slic3r Shiny)
+ target_link_libraries(libslic3r Shiny)
endif()
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
diff --git a/src/libslic3r/CustomGCode.cpp b/src/libslic3r/CustomGCode.cpp
index 824bcdd93c7..ba1890a1ffe 100644
--- a/src/libslic3r/CustomGCode.cpp
+++ b/src/libslic3r/CustomGCode.cpp
@@ -23,7 +23,7 @@ extern void update_custom_gcode_per_print_z_from_config(Info& info, DynamicPrint
info.gcodes.reserve(colorprint_values.size());
int i = 0;
for (auto val : colorprint_values)
- info.gcodes.emplace_back(Item{ val, ColorChangeCode, 1, colors[(++i)%7] });
+ info.gcodes.emplace_back(Item{ val, ColorChange, 1, colors[(++i)%7] });
info.mode = SingleExtruder;
}
@@ -43,11 +43,11 @@ extern void check_mode_for_custom_gcode_per_print_z(Info& info)
bool is_single_extruder = true;
for (auto item : info.gcodes)
{
- if (item.gcode == ToolChangeCode) {
+ if (item.type == ToolChange) {
info.mode = MultiAsSingle;
return;
}
- if (item.gcode == ColorChangeCode && item.extruder > 1)
+ if (item.type == ColorChange && item.extruder > 1)
is_single_extruder = false;
}
@@ -60,7 +60,7 @@ std::vector> custom_tool_changes(const Info& cus
{
std::vector> custom_tool_changes;
for (const Item& custom_gcode : custom_gcode_per_print_z.gcodes)
- if (custom_gcode.gcode == ToolChangeCode) {
+ if (custom_gcode.type == ToolChange) {
// If extruder count in PrinterSettings was changed, use default (0) extruder for extruders, more than num_extruders
assert(custom_gcode.extruder >= 0);
custom_tool_changes.emplace_back(custom_gcode.print_z, static_cast(size_t(custom_gcode.extruder) > num_extruders ? 1 : custom_gcode.extruder));
diff --git a/src/libslic3r/CustomGCode.hpp b/src/libslic3r/CustomGCode.hpp
index a5ef1cc2ea0..334b4043130 100644
--- a/src/libslic3r/CustomGCode.hpp
+++ b/src/libslic3r/CustomGCode.hpp
@@ -8,38 +8,40 @@ namespace Slic3r {
class DynamicPrintConfig;
-// Additional Codes which can be set by user using DoubleSlider
-static constexpr char ColorChangeCode[] = "M600";
-static constexpr char PausePrintCode[] = "M601";
-static constexpr char ToolChangeCode[] = "tool_change";
+namespace CustomGCode {
-enum CustomGcodeType
+enum Type
{
- cgtColorChange,
- cgtPausePrint,
+ ColorChange,
+ PausePrint,
+ ToolChange,
+ Template,
+ Custom
};
-namespace CustomGCode {
-
struct Item
{
bool operator<(const Item& rhs) const { return this->print_z < rhs.print_z; }
bool operator==(const Item& rhs) const
{
return (rhs.print_z == this->print_z ) &&
- (rhs.gcode == this->gcode ) &&
+ (rhs.type == this->type ) &&
(rhs.extruder == this->extruder ) &&
- (rhs.color == this->color );
+ (rhs.color == this->color ) &&
+ (rhs.extra == this->extra );
}
bool operator!=(const Item& rhs) const { return ! (*this == rhs); }
double print_z;
- std::string gcode;
+ Type type;
int extruder; // Informative value for ColorChangeCode and ToolChangeCode
// "gcode" == ColorChangeCode => M600 will be applied for "extruder" extruder
// "gcode" == ToolChangeCode => for whole print tool will be switched to "extruder" extruder
std::string color; // if gcode is equal to PausePrintCode,
// this field is used for save a short message shown on Printer display
+ std::string extra; // this field is used for the extra data like :
+ // - G-code text for the Type::Custom
+ // - message text for the Type::PausePrint
};
enum Mode
diff --git a/src/libslic3r/EdgeGrid.cpp b/src/libslic3r/EdgeGrid.cpp
index 24095105382..486a7b1aa8a 100644
--- a/src/libslic3r/EdgeGrid.cpp
+++ b/src/libslic3r/EdgeGrid.cpp
@@ -1586,12 +1586,17 @@ std::vector>
++ cnt;
}
}
- len /= double(cnt);
- bbox.offset(20);
- EdgeGrid::Grid grid;
- grid.set_bbox(bbox);
- grid.create(polygons, len);
- return grid.intersecting_edges();
+
+ std::vector> out;
+ if (cnt > 0) {
+ len /= double(cnt);
+ bbox.offset(20);
+ EdgeGrid::Grid grid;
+ grid.set_bbox(bbox);
+ grid.create(polygons, len);
+ out = grid.intersecting_edges();
+ }
+ return out;
}
// Find all pairs of intersectiong edges from the set of polygons, highlight them in an SVG.
diff --git a/src/libslic3r/ExPolygon.cpp b/src/libslic3r/ExPolygon.cpp
index 4a896604471..daaab47555f 100644
--- a/src/libslic3r/ExPolygon.cpp
+++ b/src/libslic3r/ExPolygon.cpp
@@ -404,7 +404,7 @@ void ExPolygon::triangulate_pp(Polygons* polygons) const
{
TPPLPoly p;
p.Init(int(ex->contour.points.size()));
- //printf(PRINTF_ZU "\n0\n", ex->contour.points.size());
+ //printf("%zu\n0\n", ex->contour.points.size());
for (const Point &point : ex->contour.points) {
size_t i = &point - &ex->contour.points.front();
p[i].x = point(0);
@@ -419,7 +419,7 @@ void ExPolygon::triangulate_pp(Polygons* polygons) const
for (Polygons::const_iterator hole = ex->holes.begin(); hole != ex->holes.end(); ++hole) {
TPPLPoly p;
p.Init(hole->points.size());
- //printf(PRINTF_ZU "\n1\n", hole->points.size());
+ //printf("%zu\n1\n", hole->points.size());
for (const Point &point : hole->points) {
size_t i = &point - &hole->points.front();
p[i].x = point(0);
diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp
index c482f7edba7..69b3a6455d6 100644
--- a/src/libslic3r/ExtrusionEntity.cpp
+++ b/src/libslic3r/ExtrusionEntity.cpp
@@ -312,8 +312,8 @@ std::string ExtrusionEntity::role_to_string(ExtrusionRole role)
case erOverhangPerimeter : return L("Overhang perimeter");
case erInternalInfill : return L("Internal infill");
case erSolidInfill : return L("Solid infill");
- case erIroning : return L("Ironing");
case erTopSolidInfill : return L("Top solid infill");
+ case erIroning : return L("Ironing");
case erBridgeInfill : return L("Bridge infill");
case erGapFill : return L("Gap fill");
case erSkirt : return L("Skirt");
diff --git a/src/libslic3r/Fill/Fill.hpp b/src/libslic3r/Fill/Fill.hpp
index 9e354508494..64963495a31 100644
--- a/src/libslic3r/Fill/Fill.hpp
+++ b/src/libslic3r/Fill/Fill.hpp
@@ -6,7 +6,6 @@
#include
#include "../libslic3r.h"
-#include "../BoundingBox.hpp"
#include "../PrintConfig.hpp"
#include "FillBase.hpp"
diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp
index 083cb86cef8..2e9b6473542 100644
--- a/src/libslic3r/Fill/FillBase.hpp
+++ b/src/libslic3r/Fill/FillBase.hpp
@@ -11,13 +11,13 @@
#include "../libslic3r.h"
#include "../BoundingBox.hpp"
-#include "../PrintConfig.hpp"
#include "../Utils.hpp"
namespace Slic3r {
class ExPolygon;
class Surface;
+enum InfillPattern : int;
class InfillFailedException : public std::runtime_error {
public:
diff --git a/src/libslic3r/Fill/FillRectilinear2.cpp b/src/libslic3r/Fill/FillRectilinear2.cpp
index d8324993901..5b862e8cbdb 100644
--- a/src/libslic3r/Fill/FillRectilinear2.cpp
+++ b/src/libslic3r/Fill/FillRectilinear2.cpp
@@ -901,7 +901,7 @@ static void connect_segment_intersections_by_contours(
const SegmentedIntersectionLine *il_prev = i_vline > 0 ? &segs[i_vline - 1] : nullptr;
const SegmentedIntersectionLine *il_next = i_vline + 1 < segs.size() ? &segs[i_vline + 1] : nullptr;
- for (int i_intersection = 0; i_intersection < il.intersections.size(); ++ i_intersection) {
+ for (int i_intersection = 0; i_intersection < int(il.intersections.size()); ++ i_intersection) {
SegmentIntersection &itsct = il.intersections[i_intersection];
const Polygon &poly = poly_with_offset.contour(itsct.iContour);
const bool forward = itsct.is_low(); // == poly_with_offset.is_contour_ccw(intrsctn->iContour);
@@ -914,7 +914,7 @@ static void connect_segment_intersections_by_contours(
int iprev = -1;
int d_prev = std::numeric_limits::max();
if (il_prev) {
- for (int i = 0; i < il_prev->intersections.size(); ++ i) {
+ for (int i = 0; i < int(il_prev->intersections.size()); ++ i) {
const SegmentIntersection &itsct2 = il_prev->intersections[i];
if (itsct.iContour == itsct2.iContour && itsct.type == itsct2.type) {
// The intersection points lie on the same contour and have the same orientation.
@@ -932,7 +932,7 @@ static void connect_segment_intersections_by_contours(
int inext = -1;
int d_next = std::numeric_limits::max();
if (il_next) {
- for (int i = 0; i < il_next->intersections.size(); ++ i) {
+ for (int i = 0; i < int(il_next->intersections.size()); ++ i) {
const SegmentIntersection &itsct2 = il_next->intersections[i];
if (itsct.iContour == itsct2.iContour && itsct.type == itsct2.type) {
// The intersection points lie on the same contour and have the same orientation.
@@ -950,7 +950,7 @@ static void connect_segment_intersections_by_contours(
bool same_prev = false;
bool same_next = false;
// Does the perimeter intersect the current vertical line above intrsctn?
- for (int i = 0; i < il.intersections.size(); ++ i)
+ for (int i = 0; i < int(il.intersections.size()); ++ i)
if (const SegmentIntersection &it2 = il.intersections[i];
i != i_intersection && it2.iContour == itsct.iContour && it2.type != itsct.type) {
int d = distance_of_segmens(poly, it2.iSegment, itsct.iSegment, forward);
@@ -1040,7 +1040,7 @@ static void connect_segment_intersections_by_contours(
}
// Make the LinkQuality::Invalid symmetric on vertical connections.
- for (int i_intersection = 0; i_intersection < il.intersections.size(); ++ i_intersection) {
+ for (int i_intersection = 0; i_intersection < int(il.intersections.size()); ++ i_intersection) {
SegmentIntersection &it = il.intersections[i_intersection];
if (it.has_left_vertical() && it.prev_on_contour_quality == SegmentIntersection::LinkQuality::Invalid) {
SegmentIntersection &it2 = il.intersections[it.left_vertical()];
@@ -1157,9 +1157,9 @@ static void traverse_graph_generate_polylines(
{
// For each outer only chords, measure their maximum distance to the bow of the outer contour.
// Mark an outer only chord as consumed, if the distance is low.
- for (int i_vline = 0; i_vline < segs.size(); ++ i_vline) {
+ for (int i_vline = 0; i_vline < int(segs.size()); ++ i_vline) {
SegmentedIntersectionLine &vline = segs[i_vline];
- for (int i_intersection = 0; i_intersection + 1 < vline.intersections.size(); ++ i_intersection) {
+ for (int i_intersection = 0; i_intersection + 1 < int(vline.intersections.size()); ++ i_intersection) {
if (vline.intersections[i_intersection].type == SegmentIntersection::OUTER_LOW &&
vline.intersections[i_intersection + 1].type == SegmentIntersection::OUTER_HIGH) {
bool consumed = false;
@@ -1189,14 +1189,14 @@ static void traverse_graph_generate_polylines(
if (i_intersection == -1) {
// The path has been interrupted. Find a next starting point, closest to the previous extruder position.
coordf_t dist2min = std::numeric_limits().max();
- for (int i_vline2 = 0; i_vline2 < segs.size(); ++ i_vline2) {
+ for (int i_vline2 = 0; i_vline2 < int(segs.size()); ++ i_vline2) {
const SegmentedIntersectionLine &vline = segs[i_vline2];
if (! vline.intersections.empty()) {
assert(vline.intersections.size() > 1);
// Even number of intersections with the loops.
assert((vline.intersections.size() & 1) == 0);
assert(vline.intersections.front().type == SegmentIntersection::OUTER_LOW);
- for (int i = 0; i < vline.intersections.size(); ++ i) {
+ for (int i = 0; i < int(vline.intersections.size()); ++ i) {
const SegmentIntersection& intrsctn = vline.intersections[i];
if (intrsctn.is_outer()) {
assert(intrsctn.is_low() || i > 0);
@@ -1674,13 +1674,13 @@ static std::vector generate_montonous_regions(std::vector generate_montonous_regions(std::vectorconsumed_vertical_up = true;
int num_lines = 1;
- while (++ i_vline < segs.size()) {
+ while (++ i_vline < int(segs.size())) {
SegmentedIntersectionLine &vline_left = segs[i_vline - 1];
SegmentedIntersectionLine &vline_right = segs[i_vline];
std::pair right = right_overlap(left, vline_left, vline_right);
@@ -1860,7 +1860,7 @@ static void connect_monotonous_regions(std::vector ®ions, c
}
}
}
- if (region.right.vline + 1 < segs.size()) {
+ if (region.right.vline + 1 < int(segs.size())) {
auto &vline = segs[region.right.vline];
auto &vline_right = segs[region.right.vline + 1];
auto [rbegin, rend] = right_overlap(vline.intersections[region.right.low], vline.intersections[region.right.high], vline, vline_right);
@@ -2100,7 +2100,7 @@ static std::vector chain_monotonous_regions(
AntPath &path2 = path_matrix(region, dir, *next, true);
if (path1.visibility > next_candidate.probability)
next_candidate = { next, &path1, &path1, path1.visibility, false };
- if (path2.visibility > next_candidate.probability)
+ if (path2.visibility > next_candidate.probability)
next_candidate = { next, &path2, &path2, path2.visibility, true };
}
}
@@ -2111,7 +2111,7 @@ static std::vector chain_monotonous_regions(
AntPath &path2 = path_matrix(region, dir, *next, true);
if (path1.visibility > next_candidate.probability)
next_candidate = { next, &path1, &path1, path1.visibility, false };
- if (path2.visibility > next_candidate.probability)
+ if (path2.visibility > next_candidate.probability)
next_candidate = { next, &path2, &path2, path2.visibility, true };
}
}
diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp
index f355d400d90..3612e6898cc 100644
--- a/src/libslic3r/Format/3mf.cpp
+++ b/src/libslic3r/Format/3mf.cpp
@@ -4,6 +4,7 @@
#include "../GCode.hpp"
#include "../Geometry.hpp"
#include "../GCode/ThumbnailData.hpp"
+#include "../Time.hpp"
#include "../I18N.hpp"
@@ -85,6 +86,7 @@ const char* OBJECTID_ATTR = "objectid";
const char* TRANSFORM_ATTR = "transform";
const char* PRINTABLE_ATTR = "printable";
const char* INSTANCESCOUNT_ATTR = "instances_count";
+const char* CUSTOM_SUPPORTS_ATTR = "slic3rpe:custom_supports";
const char* KEY_ATTR = "key";
const char* VALUE_ATTR = "value";
@@ -282,6 +284,7 @@ namespace Slic3r {
{
std::vector vertices;
std::vector triangles;
+ std::vector custom_supports;
bool empty()
{
@@ -292,6 +295,7 @@ namespace Slic3r {
{
vertices.clear();
triangles.clear();
+ custom_supports.clear();
}
};
@@ -1197,13 +1201,32 @@ namespace Slic3r {
}
if (code.first != "code")
continue;
+
pt::ptree tree = code.second;
- double print_z = tree.get (".print_z" );
- std::string gcode = tree.get (".gcode" );
- int extruder = tree.get (".extruder" );
- std::string color = tree.get (".color" );
+ double print_z = tree.get (".print_z" );
+ int extruder = tree.get (".extruder");
+ std::string color = tree.get (".color" );
- m_model->custom_gcode_per_print_z.gcodes.push_back(CustomGCode::Item{print_z, gcode, extruder, color}) ;
+ CustomGCode::Type type;
+ std::string extra;
+ if (tree.find("type") == tree.not_found())
+ {
+ // It means that data was saved in old version (2.2.0 and older) of PrusaSlicer
+ // read old data ...
+ std::string gcode = tree.get (".gcode");
+ // ... and interpret them to the new data
+ type = gcode == "M600" ? CustomGCode::ColorChange :
+ gcode == "M601" ? CustomGCode::PausePrint :
+ gcode == "tool_change" ? CustomGCode::ToolChange : CustomGCode::Custom;
+ extra = type == CustomGCode::PausePrint ? color :
+ type == CustomGCode::Custom ? gcode : "";
+ }
+ else
+ {
+ type = static_cast(tree.get(".type"));
+ extra = tree.get(".extra");
+ }
+ m_model->custom_gcode_per_print_z.gcodes.push_back(CustomGCode::Item{print_z, type, extruder, color, extra}) ;
}
}
}
@@ -1519,6 +1542,8 @@ namespace Slic3r {
m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V1_ATTR));
m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V2_ATTR));
m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V3_ATTR));
+
+ m_curr_object.geometry.custom_supports.push_back(get_attribute_value_string(attributes, num_attributes, CUSTOM_SUPPORTS_ATTR));
return true;
}
@@ -1852,6 +1877,13 @@ namespace Slic3r {
volume->source.transform = Slic3r::Geometry::Transformation(volume_matrix_to_object);
volume->calculate_convex_hull();
+ // recreate custom supports from previously loaded attribute
+ assert(geometry.custom_supports.size() == triangles_count);
+ for (unsigned i=0; im_supported_facets.set_triangle_from_string(i, geometry.custom_supports[i]);
+ }
+
// apply the remaining volume's metadata
for (const Metadata& metadata : volume_data.metadata)
{
@@ -1972,7 +2004,7 @@ namespace Slic3r {
bool _add_content_types_file_to_archive(mz_zip_archive& archive);
bool _add_thumbnail_file_to_archive(mz_zip_archive& archive, const ThumbnailData& thumbnail_data);
bool _add_relationships_file_to_archive(mz_zip_archive& archive);
- bool _add_model_file_to_archive(mz_zip_archive& archive, const Model& model, IdToObjectDataMap &objects_data);
+ bool _add_model_file_to_archive(const std::string& filename, mz_zip_archive& archive, const Model& model, IdToObjectDataMap& objects_data);
bool _add_object_to_model_stream(std::stringstream& stream, unsigned int& object_id, ModelObject& object, BuildItemsList& build_items, VolumeToOffsetsMap& volumes_offsets);
bool _add_mesh_to_object_stream(std::stringstream& stream, ModelObject& object, VolumeToOffsetsMap& volumes_offsets);
bool _add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items);
@@ -1982,7 +2014,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_print_z_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, const DynamicPrintConfig* config);
};
bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data)
@@ -2035,7 +2067,7 @@ namespace Slic3r {
// Adds model file ("3D/3dmodel.model").
// This is the one and only file that contains all the geometry (vertices and triangles) of all ModelVolumes.
IdToObjectDataMap objects_data;
- if (!_add_model_file_to_archive(archive, model, objects_data))
+ if (!_add_model_file_to_archive(filename, archive, model, objects_data))
{
close_zip_writer(&archive);
boost::filesystem::remove(filename);
@@ -2082,7 +2114,7 @@ namespace Slic3r {
// 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_print_z_file_to_archive(archive, model))
+ if (!_add_custom_gcode_per_print_z_file_to_archive(archive, model, config))
{
close_zip_writer(&archive);
boost::filesystem::remove(filename);
@@ -2184,7 +2216,7 @@ namespace Slic3r {
return true;
}
- bool _3MF_Exporter::_add_model_file_to_archive(mz_zip_archive& archive, const Model& model, IdToObjectDataMap &objects_data)
+ bool _3MF_Exporter::_add_model_file_to_archive(const std::string& filename, mz_zip_archive& archive, const Model& model, IdToObjectDataMap& objects_data)
{
std::stringstream stream;
// https://en.cppreference.com/w/cpp/types/numeric_limits/max_digits10
@@ -2195,6 +2227,19 @@ namespace Slic3r {
stream << "\n";
stream << "<" << MODEL_TAG << " unit=\"millimeter\" xml:lang=\"en-US\" xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\" xmlns:slic3rpe=\"http://schemas.slic3r.org/3mf/2017/06\">\n";
stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_3MF_VERSION << "\">" << VERSION_3MF << "" << METADATA_TAG << ">\n";
+ std::string name = boost::filesystem::path(filename).stem().string();
+ stream << " <" << METADATA_TAG << " name=\"Title\">" << name << "" << METADATA_TAG << ">\n";
+ stream << " <" << METADATA_TAG << " name=\"Designer\">" << "" << METADATA_TAG << ">\n";
+ stream << " <" << METADATA_TAG << " name=\"Description\">" << name << "" << METADATA_TAG << ">\n";
+ stream << " <" << METADATA_TAG << " name=\"Copyright\">" << "" << METADATA_TAG << ">\n";
+ stream << " <" << METADATA_TAG << " name=\"LicenseTerms\">" << "" << METADATA_TAG << ">\n";
+ stream << " <" << METADATA_TAG << " name=\"Rating\">" << "" << METADATA_TAG << ">\n";
+ std::string date = Slic3r::Utils::utc_timestamp(Slic3r::Utils::get_current_time_utc());
+ // keep only the date part of the string
+ date = date.substr(0, 10);
+ stream << " <" << METADATA_TAG << " name=\"CreationDate\">" << date << "" << METADATA_TAG << ">\n";
+ stream << " <" << METADATA_TAG << " name=\"ModificationDate\">" << date << "" << METADATA_TAG << ">\n";
+ stream << " <" << METADATA_TAG << " name=\"Application\">" << SLIC3R_APP_KEY << "-" << SLIC3R_VERSION << "" << METADATA_TAG << ">\n";
stream << " <" << RESOURCES_TAG << ">\n";
// Instance transformations, indexed by the 3MF object ID (which is a linear serialization of all instances of all ModelObjects).
@@ -2350,6 +2395,11 @@ namespace Slic3r {
{
stream << "v" << j + 1 << "=\"" << its.indices[i][j] + volume_it->second.first_vertex_id << "\" ";
}
+
+ std::string custom_supports_data_string = volume->m_supported_facets.get_triangle_as_string(i);
+ if (! custom_supports_data_string.empty())
+ stream << CUSTOM_SUPPORTS_ATTR << "=\"" << custom_supports_data_string << "\" ";
+
stream << "/>\n";
}
}
@@ -2702,7 +2752,7 @@ namespace Slic3r {
return true;
}
-bool _3MF_Exporter::_add_custom_gcode_per_print_z_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, const DynamicPrintConfig* config)
{
std::string out = "";
@@ -2714,11 +2764,20 @@ bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archiv
for (const CustomGCode::Item& code : model.custom_gcode_per_print_z.gcodes)
{
pt::ptree& code_tree = main_tree.add("code", "");
- // store minX and maxZ
+
+ // store data of custom_gcode_per_print_z
code_tree.put(".print_z" , code.print_z );
- code_tree.put(".gcode" , code.gcode );
+ code_tree.put(".type" , static_cast(code.type));
code_tree.put(".extruder" , code.extruder );
code_tree.put(".color" , code.color );
+ code_tree.put(".extra" , code.extra );
+
+ // add gcode field data for the old version of the PrusaSlicer
+ std::string gcode = code.type == CustomGCode::ColorChange ? config->opt_string("color_change_gcode") :
+ code.type == CustomGCode::PausePrint ? config->opt_string("pause_print_gcode") :
+ code.type == CustomGCode::Template ? config->opt_string("template_custom_gcode") :
+ code.type == CustomGCode::ToolChange ? "tool_change" : code.extra;
+ code_tree.put(".gcode" , gcode );
}
pt::ptree& mode_tree = main_tree.add("mode", "");
diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp
index 2dc4ea0ba5b..af7b9b1b60a 100644
--- a/src/libslic3r/Format/AMF.cpp
+++ b/src/libslic3r/Format/AMF.cpp
@@ -240,7 +240,7 @@ struct AMFParserContext
// Current instance allocated for an amf/constellation/instance subtree.
Instance *m_instance;
// Generic string buffer for vertices, face indices, metadata etc.
- std::string m_value[4];
+ std::string m_value[5];
// Pointer to config to update if config data are stored inside the amf file
DynamicPrintConfig *m_config;
@@ -314,9 +314,26 @@ void AMFParserContext::startElement(const char *name, const char **atts)
if (strcmp(name, "code") == 0) {
node_type_new = NODE_TYPE_GCODE_PER_HEIGHT;
m_value[0] = get_attribute(atts, "print_z");
- m_value[1] = get_attribute(atts, "gcode");
- m_value[2] = get_attribute(atts, "extruder");
- m_value[3] = get_attribute(atts, "color");
+ m_value[1] = get_attribute(atts, "extruder");
+ m_value[2] = get_attribute(atts, "color");
+ if (get_attribute(atts, "type"))
+ {
+ m_value[3] = get_attribute(atts, "type");
+ m_value[4] = get_attribute(atts, "extra");
+ }
+ else
+ {
+ // It means that data was saved in old version (2.2.0 and older) of PrusaSlicer
+ // read old data ...
+ std::string gcode = get_attribute(atts, "gcode");
+ // ... and interpret them to the new data
+ CustomGCode::Type type= gcode == "M600" ? CustomGCode::ColorChange :
+ gcode == "M601" ? CustomGCode::PausePrint :
+ gcode == "tool_change" ? CustomGCode::ToolChange : CustomGCode::Custom;
+ m_value[3] = std::to_string(static_cast(type));
+ m_value[4] = type == CustomGCode::PausePrint ? m_value[2] :
+ type == CustomGCode::Custom ? gcode : "";
+ }
}
else if (strcmp(name, "mode") == 0) {
node_type_new = NODE_TYPE_CUSTOM_GCODE_MODE;
@@ -640,12 +657,13 @@ void AMFParserContext::endElement(const char * /* name */)
break;
case NODE_TYPE_GCODE_PER_HEIGHT: {
- double print_z = double(atof(m_value[0].c_str()));
- const std::string& gcode = m_value[1];
- int extruder = atoi(m_value[2].c_str());
- const std::string& color = m_value[3];
+ double print_z = double(atof(m_value[0].c_str()));
+ int extruder = atoi(m_value[1].c_str());
+ const std::string& color= m_value[2];
+ CustomGCode::Type type = static_cast(atoi(m_value[3].c_str()));
+ const std::string& extra= m_value[4];
- m_model.custom_gcode_per_print_z.gcodes.push_back(CustomGCode::Item{print_z, gcode, extruder, color});
+ m_model.custom_gcode_per_print_z.gcodes.push_back(CustomGCode::Item{print_z, type, extruder, color, extra});
for (std::string& val: m_value)
val.clear();
@@ -1200,7 +1218,7 @@ bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config,
for (ModelInstance *instance : object->instances) {
char buf[512];
sprintf(buf,
- " \n"
+ " \n"
" %lf\n"
" %lf\n"
" %lf\n"
@@ -1253,9 +1271,17 @@ bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config,
pt::ptree& code_tree = main_tree.add("code", "");
// store custom_gcode_per_print_z gcodes information
code_tree.put(".print_z" , code.print_z );
- code_tree.put(".gcode" , code.gcode );
+ code_tree.put(".type" , static_cast(code.type));
code_tree.put(".extruder" , code.extruder );
code_tree.put(".color" , code.color );
+ code_tree.put(".extra" , code.extra );
+
+ // add gcode field data for the old version of the PrusaSlicer
+ std::string gcode = code.type == CustomGCode::ColorChange ? config->opt_string("color_change_gcode") :
+ code.type == CustomGCode::PausePrint ? config->opt_string("pause_print_gcode") :
+ code.type == CustomGCode::Template ? config->opt_string("template_custom_gcode") :
+ code.type == CustomGCode::ToolChange ? "tool_change" : code.extra;
+ code_tree.put(".gcode" , gcode );
}
pt::ptree& mode_tree = main_tree.add("mode", "");
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index b5890f57843..35dc5a53bd6 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -7,6 +7,7 @@
#include "GCode/PrintExtents.hpp"
#include "GCode/WipeTower.hpp"
#include "ShortestPath.hpp"
+#include "Print.hpp"
#include "Utils.hpp"
#include "libslic3r.h"
@@ -616,6 +617,15 @@ std::vector GCode::collect_layers_to_print(const PrintObjec
layers_to_print.emplace_back(layer_to_print);
+ bool has_extrusions = (layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions())
+ || (layer_to_print.support_layer && layer_to_print.support_layer->has_extrusions());
+
+ // Check that there are extrusions on the very first layer.
+ if (layers_to_print.size() == 1u) {
+ if (! has_extrusions)
+ throw std::runtime_error(_(L("There is an object with no extrusions on the first layer.")));
+ }
+
// In case there are extrusions on this layer, check there is a layer to lay it on.
if ((layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions())
// Allow empty support layers, as the support generator may produce no extrusions for non-empty support regions.
@@ -629,18 +639,18 @@ std::vector GCode::collect_layers_to_print(const PrintObjec
// Negative support_contact_z is not taken into account, it can result in false positives in cases
// where previous layer has object extrusions too (https://github.com/prusa3d/PrusaSlicer/issues/2752)
- // Only check this layer in case it has some extrusions.
- bool has_extrusions = (layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions())
- || (layer_to_print.support_layer && layer_to_print.support_layer->has_extrusions());
-
- if (has_extrusions && layer_to_print.print_z() > maximal_print_z + 2. * EPSILON)
- throw std::runtime_error(_(L("Empty layers detected, the output would not be printable.")) + "\n\n" +
+ if (has_extrusions && layer_to_print.print_z() > maximal_print_z + 2. * EPSILON) {
+ const_cast(object.print())->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL,
+ _(L("Empty layers detected, the output would not be printable.")) + "\n\n" +
_(L("Object name")) + ": " + object.model_object()->name + "\n" + _(L("Print z")) + ": " +
std::to_string(layers_to_print.back().print_z()) + "\n\n" + _(L("This is "
"usually caused by negligibly small extrusions or by a faulty model. Try to repair "
"the model or change its orientation on the bed.")));
+ }
+
// Remember last layer with extrusions.
- last_extrusion_layer = &layers_to_print.back();
+ if (has_extrusions)
+ last_extrusion_layer = &layers_to_print.back();
}
}
@@ -1297,7 +1307,28 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
m_placeholder_parser.set("has_wipe_tower", has_wipe_tower);
m_placeholder_parser.set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config().single_extruder_multi_material_priming);
m_placeholder_parser.set("total_toolchanges", std::max(0, print.wipe_tower_data().number_of_toolchanges)); // Check for negative toolchanges (single extruder mode) and set to 0 (no tool change).
-
+ {
+ BoundingBoxf bbox(print.config().bed_shape.values);
+ m_placeholder_parser.set("print_bed_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() }));
+ m_placeholder_parser.set("print_bed_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() }));
+ m_placeholder_parser.set("print_bed_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() }));
+ }
+ {
+ // Convex hull of the 1st layer extrusions, for bed leveling and placing the initial purge line.
+ // It encompasses the object extrusions, support extrusions, skirt, brim, wipe tower.
+ // It does NOT encompass user extrusions generated by custom G-code,
+ // therefore it does NOT encompass the initial purge line.
+ // It does NOT encompass MMU/MMU2 starting (wipe) areas.
+ auto pts = std::make_unique();
+ pts->values.reserve(print.first_layer_convex_hull().size());
+ for (const Point &pt : print.first_layer_convex_hull().points)
+ pts->values.emplace_back(unscale(pt));
+ BoundingBoxf bbox(pts->values);
+ m_placeholder_parser.set("first_layer_print_convex_hull", pts.release());
+ m_placeholder_parser.set("first_layer_print_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() }));
+ m_placeholder_parser.set("first_layer_print_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() }));
+ m_placeholder_parser.set("first_layer_print_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() }));
+ }
std::string start_gcode = this->placeholder_parser_process("start_gcode", print.config().start_gcode.value, initial_extruder_id);
// Set bed temperature if the start G-code does not contain any bed temp control G-codes.
this->_print_first_layer_bed_temperature(file, print, start_gcode, initial_extruder_id, true);
@@ -1756,17 +1787,18 @@ namespace ProcessLayer
const CustomGCode::Item *custom_gcode,
// ID of the first extruder printing this layer.
unsigned int first_extruder_id,
- bool single_extruder_printer)
+ const PrintConfig &config)
{
std::string gcode;
+ bool single_extruder_printer = config.nozzle_diameter.size() == 1;
if (custom_gcode != nullptr) {
// Extruder switches are processed by LayerTools, they should be filtered out.
- assert(custom_gcode->gcode != ToolChangeCode);
+ assert(custom_gcode->type != CustomGCode::ToolChange);
- const std::string &custom_code = custom_gcode->gcode;
- bool color_change = custom_code == ColorChangeCode;
- bool tool_change = custom_code == ToolChangeCode;
+ CustomGCode::Type gcode_type = custom_gcode->type;
+ bool color_change = gcode_type == CustomGCode::ColorChange;
+ bool tool_change = gcode_type == CustomGCode::ToolChange;
// Tool Change is applied as Color Change for a single extruder printer only.
assert(! tool_change || single_extruder_printer);
@@ -1774,8 +1806,8 @@ namespace ProcessLayer
int m600_extruder_before_layer = -1;
if (color_change && custom_gcode->extruder > 0)
m600_extruder_before_layer = custom_gcode->extruder - 1;
- else if (custom_code == PausePrintCode)
- pause_print_msg = custom_gcode->color;
+ else if (gcode_type == CustomGCode::PausePrint)
+ pause_print_msg = custom_gcode->extra;
// we should add or not colorprint_change in respect to nozzle_diameter count instead of really used extruders count
if (color_change || tool_change)
@@ -1791,17 +1823,18 @@ namespace ProcessLayer
// && !MMU1
) {
//! FIXME_in_fw show message during print pause
- gcode += "M601\n"; // pause print
+ gcode += config.pause_print_gcode;// pause print
+ gcode += "\n";
gcode += "M117 Change filament for Extruder " + std::to_string(m600_extruder_before_layer) + "\n";
}
else {
- gcode += ColorChangeCode;
+ gcode += config.color_change_gcode;//ColorChangeCode;
gcode += "\n";
}
}
else
{
- if (custom_code == PausePrintCode) // Pause print
+ if (gcode_type == CustomGCode::PausePrint) // Pause print
{
// add tag for analyzer
gcode += "; " + GCodeAnalyzer::Pause_Print_Tag + "\n";
@@ -1810,15 +1843,21 @@ namespace ProcessLayer
gcode += "M117 " + pause_print_msg + "\n";
// add tag for time estimator
gcode += "; " + GCodeTimeEstimator::Pause_Print_Tag + "\n";
+ gcode += config.pause_print_gcode;
}
- else // custom Gcode
+ else
{
// add tag for analyzer
gcode += "; " + GCodeAnalyzer::Custom_Code_Tag + "\n";
// add tag for time estimator
//gcode += "; " + GCodeTimeEstimator::Custom_Code_Tag + "\n";
+ if (gcode_type == CustomGCode::Template) // Template Cistom Gcode
+ gcode += config.template_custom_gcode;
+ else // custom Gcode
+ gcode += custom_gcode->extra;
+
}
- gcode += custom_code + "\n";
+ gcode += "\n";
}
}
@@ -1909,7 +1948,6 @@ void GCode::process_layer(
const size_t single_object_instance_idx)
{
assert(! layers.empty());
-// assert(! layer_tools.extruders.empty());
// Either printing all copies of all objects, or just a single copy of a single object.
assert(single_object_instance_idx == size_t(-1) || layers.size() == 1);
@@ -1995,7 +2033,7 @@ void GCode::process_layer(
if (single_object_instance_idx == size_t(-1)) {
// Normal (non-sequential) print.
- gcode += ProcessLayer::emit_custom_gcode_per_print_z(layer_tools.custom_gcode, first_extruder_id, print.config().nozzle_diameter.size() == 1);
+ gcode += ProcessLayer::emit_custom_gcode_per_print_z(layer_tools.custom_gcode, first_extruder_id, print.config());
}
// Extrude skirt at the print_z of the raft layers and normal object layers
// not at the print_z of the interlaced support material layers.
diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp
index 2daf0fe16e3..8d473378320 100644
--- a/src/libslic3r/GCode.hpp
+++ b/src/libslic3r/GCode.hpp
@@ -8,7 +8,6 @@
#include "MotionPlanner.hpp"
#include "Point.hpp"
#include "PlaceholderParser.hpp"
-#include "Print.hpp"
#include "PrintConfig.hpp"
#include "GCode/CoolingBuffer.hpp"
#include "GCode/SpiralVase.hpp"
@@ -32,6 +31,10 @@ namespace Slic3r {
class GCode;
class GCodePreviewData;
+namespace { struct Item; }
+struct PrintInstance;
+using PrintObjectPtrs = std::vector;
+
class AvoidCrossingPerimeters {
public:
diff --git a/src/libslic3r/GCode/PrintExtents.cpp b/src/libslic3r/GCode/PrintExtents.cpp
index 7a8271e3009..4a6624531b2 100644
--- a/src/libslic3r/GCode/PrintExtents.cpp
+++ b/src/libslic3r/GCode/PrintExtents.cpp
@@ -6,6 +6,7 @@
#include "../BoundingBox.hpp"
#include "../ExtrusionEntity.hpp"
#include "../ExtrusionEntityCollection.hpp"
+#include "../Layer.hpp"
#include "../Print.hpp"
#include "PrintExtents.hpp"
diff --git a/src/libslic3r/GCode/ThumbnailData.cpp b/src/libslic3r/GCode/ThumbnailData.cpp
index 09d564e95df..a5941bff169 100644
--- a/src/libslic3r/GCode/ThumbnailData.cpp
+++ b/src/libslic3r/GCode/ThumbnailData.cpp
@@ -1,4 +1,3 @@
-#include "libslic3r/libslic3r.h"
#include "ThumbnailData.hpp"
namespace Slic3r {
diff --git a/src/libslic3r/GCode/ThumbnailData.hpp b/src/libslic3r/GCode/ThumbnailData.hpp
index 473ef274b91..2a302ed8553 100644
--- a/src/libslic3r/GCode/ThumbnailData.hpp
+++ b/src/libslic3r/GCode/ThumbnailData.hpp
@@ -24,4 +24,4 @@ typedef std::function
-#include "../GCodeWriter.hpp"
namespace Slic3r {
@@ -355,7 +355,7 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_
max_layer_height = std::min(max_layer_height, mlh);
}
// The Prusa3D Fast (0.35mm layer height) print profile sets a higher layer height than what is normally allowed
- // by the nozzle. This is a hack and it works by increasing extrusion width.
+ // by the nozzle. This is a hack and it works by increasing extrusion width. See GH #3919.
max_layer_height = std::max(max_layer_height, max_object_layer_height);
for (size_t i = 0; i + 1 < m_layer_tools.size(); ++ i) {
@@ -400,47 +400,21 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_
// and maybe other problems. We will therefore go through layer_tools and detect and fix this.
// So, if there is a non-object layer starting with different extruder than the last one ended with (or containing more than one extruder),
// we'll mark it with has_wipe tower.
- assert(! m_layer_tools.empty() && m_layer_tools.front().has_wipe_tower);
- if (! m_layer_tools.empty() && m_layer_tools.front().has_wipe_tower) {
- for (size_t i = 0; i + 1 < m_layer_tools.size();) {
- const LayerTools < = m_layer_tools[i];
- assert(lt.has_wipe_tower);
- assert(! lt.extruders.empty());
- // Find the next layer with wipe tower or mark a layer as such.
- size_t j = i + 1;
- for (; j < m_layer_tools.size() && ! m_layer_tools[j].has_wipe_tower; ++ j) {
- LayerTools <_next = m_layer_tools[j];
- if (lt_next.extruders.empty()) {
- //FIXME Vojtech: Lukasi, proc?
- j = m_layer_tools.size();
- break;
- }
- if (lt_next.extruders.front() != lt.extruders.back() || lt_next.extruders.size() > 1) {
- // Support only layer, soluble layers? Otherwise the layer should have been already marked as having wipe tower.
- assert(lt_next.has_support && ! lt_next.has_object);
- lt_next.has_wipe_tower = true;
- break;
- }
- }
- if (j == m_layer_tools.size())
- // No wipe tower above layer i, therefore no need to add any wipe tower layer above i.
- break;
- // We should also check that the next wipe tower layer is no further than max_layer_height.
- // This algorith may in theory create very thin wipe layer j if layer closely below j is marked as wipe tower.
- // This may happen if printing with non-soluble break away supports.
- // On the other side it should not hurt as there will be no wipe, just perimeter and sparse infill printed
- // at that particular wipe tower layer without extruder change.
- double last_wipe_tower_print_z = lt.print_z;
- assert(m_layer_tools[j].has_wipe_tower);
- for (size_t k = i + 1; k < j; ++k) {
- assert(! m_layer_tools[k].has_wipe_tower);
- if (m_layer_tools[k + 1].print_z - last_wipe_tower_print_z > max_layer_height + EPSILON) {
- m_layer_tools[k].has_wipe_tower = true;
- last_wipe_tower_print_z = m_layer_tools[k].print_z;
- }
+ for (unsigned int i=0; i+1 1))
+ lt_next.has_wipe_tower = true;
+ // We should also check that the next wipe tower layer is no further than max_layer_height:
+ unsigned int j = i+1;
+ double last_wipe_tower_print_z = lt_next.print_z;
+ while (++j < m_layer_tools.size()-1 && !m_layer_tools[j].has_wipe_tower)
+ if (m_layer_tools[j+1].print_z - last_wipe_tower_print_z > max_layer_height + EPSILON) {
+ m_layer_tools[j].has_wipe_tower = true;
+ last_wipe_tower_print_z = m_layer_tools[j].print_z;
}
- i = j;
- }
}
// Calculate the wipe_tower_layer_height values.
@@ -518,7 +492,7 @@ void ToolOrdering::assign_custom_gcodes(const Print &print)
for (unsigned int i : lt.extruders)
extruder_printing_above[i] = true;
// Skip all custom G-codes above this layer and skip all extruder switches.
- for (; custom_gcode_it != custom_gcode_per_print_z.gcodes.rend() && (custom_gcode_it->print_z > lt.print_z + EPSILON || custom_gcode_it->gcode == ToolChangeCode); ++ custom_gcode_it);
+ for (; custom_gcode_it != custom_gcode_per_print_z.gcodes.rend() && (custom_gcode_it->print_z > lt.print_z + EPSILON || custom_gcode_it->type == CustomGCode::ToolChange); ++ custom_gcode_it);
if (custom_gcode_it == custom_gcode_per_print_z.gcodes.rend())
// Custom G-codes were processed.
break;
@@ -530,8 +504,8 @@ void ToolOrdering::assign_custom_gcodes(const Print &print)
print_z_below = it_lt_below->print_z;
if (custom_gcode.print_z > print_z_below + 0.5 * EPSILON) {
// The custom G-code applies to the current layer.
- bool color_change = custom_gcode.gcode == ColorChangeCode;
- bool tool_change = custom_gcode.gcode == ToolChangeCode;
+ bool color_change = custom_gcode.type == CustomGCode::ColorChange;
+ bool tool_change = custom_gcode.type == CustomGCode::ToolChange;
bool pause_or_custom_gcode = ! color_change && ! tool_change;
bool apply_color_change = ! ignore_tool_and_color_changes &&
// If it is color change, it will actually be useful as the exturder above will print.
diff --git a/src/libslic3r/GCode/ToolOrdering.hpp b/src/libslic3r/GCode/ToolOrdering.hpp
index 5fe27516dc7..e2e07533faf 100644
--- a/src/libslic3r/GCode/ToolOrdering.hpp
+++ b/src/libslic3r/GCode/ToolOrdering.hpp
@@ -14,6 +14,8 @@ namespace Slic3r {
class Print;
class PrintObject;
class LayerTools;
+namespace CustomGCode { struct Item; }
+class PrintRegion;
diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp
index d31adbd8fcc..c0f778687c3 100644
--- a/src/libslic3r/GCode/WipeTower.cpp
+++ b/src/libslic3r/GCode/WipeTower.cpp
@@ -102,7 +102,9 @@ class WipeTowerWriter
}
WipeTowerWriter& disable_linear_advance() {
- m_gcode += (m_gcode_flavor == gcfRepRap ? std::string("M572 D0 S0\n") : std::string("M900 K0\n"));
+ m_gcode += (m_gcode_flavor == gcfRepRap
+ ? (std::string("M572 D") + std::to_string(m_current_tool) + " S0\n")
+ : std::string("M900 K0\n"));
return *this;
}
diff --git a/src/libslic3r/GCodeSender.cpp b/src/libslic3r/GCodeSender.cpp
index 0988091ce38..9567e07d284 100644
--- a/src/libslic3r/GCodeSender.cpp
+++ b/src/libslic3r/GCodeSender.cpp
@@ -393,7 +393,7 @@ GCodeSender::on_read(const boost::system::error_code& error,
}
this->send();
} else {
- printf("Cannot resend " PRINTF_ZU " (oldest we have is " PRINTF_ZU ")\n", toresend, this->sent - this->last_sent.size());
+ printf("Cannot resend %zu (oldest we have is %zu)\n", toresend, this->sent - this->last_sent.size());
}
} else if (boost::starts_with(line, "wait")) {
// ignore
diff --git a/src/libslic3r/GCodeTimeEstimator.cpp b/src/libslic3r/GCodeTimeEstimator.cpp
index 6d7f5f65992..9e8137ef0ed 100644
--- a/src/libslic3r/GCodeTimeEstimator.cpp
+++ b/src/libslic3r/GCodeTimeEstimator.cpp
@@ -188,7 +188,7 @@ namespace Slic3r {
_calculate_time(0);
if (m_needs_custom_gcode_times && (m_custom_gcode_time_cache != 0.0f))
- m_custom_gcode_times.push_back({ cgtColorChange, m_custom_gcode_time_cache });
+ m_custom_gcode_times.push_back({CustomGCode::ColorChange, m_custom_gcode_time_cache });
#if ENABLE_MOVE_STATS
_log_moves_stats();
@@ -282,7 +282,7 @@ namespace Slic3r {
};
GCodeReader parser;
- unsigned int g1_lines_count = 0;
+ int g1_lines_count = 0;
int normal_g1_line_id = 0;
float normal_last_recorded_time = 0.0f;
int silent_g1_line_id = 0;
@@ -678,7 +678,7 @@ namespace Slic3r {
return _get_time_minutes(get_time());
}
- std::vector> GCodeTimeEstimator::get_custom_gcode_times() const
+ std::vector> GCodeTimeEstimator::get_custom_gcode_times() const
{
return m_custom_gcode_times;
}
@@ -722,9 +722,9 @@ namespace Slic3r {
return ret;
}
- std::vector> GCodeTimeEstimator::get_custom_gcode_times_dhm(bool include_remaining) const
+ std::vector> GCodeTimeEstimator::get_custom_gcode_times_dhm(bool include_remaining) const
{
- std::vector> ret;
+ std::vector> ret;
float total_time = 0.0f;
for (auto t : m_custom_gcode_times)
@@ -1470,7 +1470,7 @@ namespace Slic3r {
size_t pos = comment.find(Color_Change_Tag);
if (pos != comment.npos)
{
- _process_custom_gcode_tag(cgtColorChange);
+ _process_custom_gcode_tag(CustomGCode::ColorChange);
return true;
}
@@ -1478,14 +1478,14 @@ namespace Slic3r {
pos = comment.find(Pause_Print_Tag);
if (pos != comment.npos)
{
- _process_custom_gcode_tag(cgtPausePrint);
+ _process_custom_gcode_tag(CustomGCode::PausePrint);
return true;
}
return false;
}
- void GCodeTimeEstimator::_process_custom_gcode_tag(CustomGcodeType code)
+ void GCodeTimeEstimator::_process_custom_gcode_tag(CustomGCode::Type code)
{
PROFILE_FUNC();
m_needs_custom_gcode_times = true;
diff --git a/src/libslic3r/GCodeTimeEstimator.hpp b/src/libslic3r/GCodeTimeEstimator.hpp
index 836d1ceb4a8..63e11c4faa4 100644
--- a/src/libslic3r/GCodeTimeEstimator.hpp
+++ b/src/libslic3r/GCodeTimeEstimator.hpp
@@ -234,7 +234,7 @@ namespace Slic3r {
// data to calculate custom code times
bool m_needs_custom_gcode_times;
- std::vector> m_custom_gcode_times;
+ std::vector> m_custom_gcode_times;
float m_custom_gcode_time_cache;
#if ENABLE_MOVE_STATS
@@ -358,7 +358,7 @@ namespace Slic3r {
std::string get_time_minutes() const;
// Returns the estimated time, in seconds, for each custom gcode
- std::vector> get_custom_gcode_times() const;
+ std::vector> get_custom_gcode_times() const;
// Returns the estimated time, in format DDd HHh MMm SSs, for each color
// If include_remaining==true the strings will be formatted as: "time for color (remaining time at color start)"
@@ -370,7 +370,7 @@ namespace Slic3r {
// Returns the estimated time, in format DDd HHh MMm, for each custom_gcode
// If include_remaining==true the strings will be formatted as: "time for custom_gcode (remaining time at color start)"
- std::vector> get_custom_gcode_times_dhm(bool include_remaining) const;
+ std::vector> get_custom_gcode_times_dhm(bool include_remaining) const;
// Return an estimate of the memory consumed by the time estimator.
size_t memory_used() const;
@@ -453,7 +453,7 @@ namespace Slic3r {
bool _process_tags(const GCodeReader::GCodeLine& line);
// Processes ColorChangeTag and PausePrintTag
- void _process_custom_gcode_tag(CustomGcodeType code);
+ void _process_custom_gcode_tag(CustomGCode::Type code);
// Simulates firmware st_synchronize() call
void _simulate_st_synchronize(float additional_time);
diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp
index b9e4d6e78ca..00a4ad47c3f 100644
--- a/src/libslic3r/Geometry.cpp
+++ b/src/libslic3r/Geometry.cpp
@@ -471,7 +471,7 @@ Pointfs arrange(size_t num_parts, const Vec2d &part_size, coordf_t gap, const Bo
size_t cellw = size_t(floor((bed_bbox.size()(0) + gap) / cell_size(0)));
size_t cellh = size_t(floor((bed_bbox.size()(1) + gap) / cell_size(1)));
if (num_parts > cellw * cellh)
- throw std::invalid_argument(PRINTF_ZU " parts won't fit in your print area!\n", num_parts);
+ throw std::invalid_argument("%zu parts won't fit in your print area!\n", num_parts);
// Get a bounding box of cellw x cellh cells, centered at the center of the bed.
Vec2d cells_size(cellw * cell_size(0) - gap, cellh * cell_size(1) - gap);
diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp
index 1fa15ba6350..75f3708d253 100644
--- a/src/libslic3r/Geometry.hpp
+++ b/src/libslic3r/Geometry.hpp
@@ -10,6 +10,7 @@
// Serialization through the Cereal library
#include
+#define BOOST_VORONOI_USE_GMP 1
#include "boost/polygon/voronoi.hpp"
namespace ClipperLib {
@@ -114,32 +115,94 @@ inline bool segment_segment_intersection(const Vec2d &p1, const Vec2d &v1, const
return true;
}
-
-inline int segments_could_intersect(
+inline bool segments_intersect(
const Slic3r::Point &ip1, const Slic3r::Point &ip2,
const Slic3r::Point &jp1, const Slic3r::Point &jp2)
+{
+ assert(ip1 != ip2);
+ assert(jp1 != jp2);
+
+ auto segments_could_intersect = [](
+ const Slic3r::Point &ip1, const Slic3r::Point &ip2,
+ const Slic3r::Point &jp1, const Slic3r::Point &jp2) -> std::pair
+ {
+ Vec2i64 iv = (ip2 - ip1).cast();
+ Vec2i64 vij1 = (jp1 - ip1).cast();
+ Vec2i64 vij2 = (jp2 - ip1).cast();
+ int64_t tij1 = cross2(iv, vij1);
+ int64_t tij2 = cross2(iv, vij2);
+ return std::make_pair(
+ // signum
+ (tij1 > 0) ? 1 : ((tij1 < 0) ? -1 : 0),
+ (tij2 > 0) ? 1 : ((tij2 < 0) ? -1 : 0));
+ };
+
+ std::pair sign1 = segments_could_intersect(ip1, ip2, jp1, jp2);
+ std::pair sign2 = segments_could_intersect(jp1, jp2, ip1, ip2);
+ int test1 = sign1.first * sign1.second;
+ int test2 = sign2.first * sign2.second;
+ if (test1 <= 0 && test2 <= 0) {
+ // The segments possibly intersect. They may also be collinear, but not intersect.
+ if (test1 != 0 || test2 != 0)
+ // Certainly not collinear, then the segments intersect.
+ return true;
+ // If the first segment is collinear with the other, the other is collinear with the first segment.
+ assert((sign1.first == 0 && sign1.second == 0) == (sign2.first == 0 && sign2.second == 0));
+ if (sign1.first == 0 && sign1.second == 0) {
+ // The segments are certainly collinear. Now verify whether they overlap.
+ Slic3r::Point vi = ip2 - ip1;
+ // Project both on the longer coordinate of vi.
+ int axis = std::abs(vi.x()) > std::abs(vi.y()) ? 0 : 1;
+ coord_t i = ip1(axis);
+ coord_t j = ip2(axis);
+ coord_t k = jp1(axis);
+ coord_t l = jp2(axis);
+ if (i > j)
+ std::swap(i, j);
+ if (k > l)
+ std::swap(k, l);
+ return (k >= i && k <= j) || (i >= k && i <= l);
+ }
+ }
+ return false;
+}
+
+template