diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1e6cf45bd4..8adea28c34 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -49,6 +49,10 @@ foreach (_cache_var ${_cache_vars})
endif ()
endforeach()
+if (SLIC3R_GUI)
+ add_definitions(-DSLIC3R_GUI)
+endif ()
+
if (MSVC)
if (SLIC3R_MSVC_COMPILE_PARALLEL)
add_compile_options(/MP)
diff --git a/resources/icons/add_copies.svg b/resources/icons/add_copies.svg
new file mode 100644
index 0000000000..45b1d27cf9
--- /dev/null
+++ b/resources/icons/add_copies.svg
@@ -0,0 +1,19 @@
+
+
+
diff --git a/resources/icons/add_modifier.svg b/resources/icons/add_modifier.svg
new file mode 100644
index 0000000000..c3cfaabbb2
--- /dev/null
+++ b/resources/icons/add_modifier.svg
@@ -0,0 +1,13 @@
+
+
+
diff --git a/resources/icons/add_part.svg b/resources/icons/add_part.svg
new file mode 100644
index 0000000000..5f0afdcc35
--- /dev/null
+++ b/resources/icons/add_part.svg
@@ -0,0 +1,19 @@
+
+
+
diff --git a/resources/icons/advanced_plus.svg b/resources/icons/advanced_plus.svg
new file mode 100644
index 0000000000..48c4e08db0
--- /dev/null
+++ b/resources/icons/advanced_plus.svg
@@ -0,0 +1,24 @@
+
+
+
diff --git a/resources/icons/browse.svg b/resources/icons/browse.svg
new file mode 100644
index 0000000000..c4297c41da
--- /dev/null
+++ b/resources/icons/browse.svg
@@ -0,0 +1,11 @@
+
+
+
diff --git a/resources/icons/copy.svg b/resources/icons/copy.svg
new file mode 100644
index 0000000000..9b8430dd79
--- /dev/null
+++ b/resources/icons/copy.svg
@@ -0,0 +1,37 @@
+
+
+
diff --git a/resources/icons/copy_menu.svg b/resources/icons/copy_menu.svg
new file mode 100644
index 0000000000..0d1af6a0a7
--- /dev/null
+++ b/resources/icons/copy_menu.svg
@@ -0,0 +1,37 @@
+
+
+
diff --git a/resources/icons/delete.svg b/resources/icons/delete.svg
new file mode 100644
index 0000000000..f0976a76bc
--- /dev/null
+++ b/resources/icons/delete.svg
@@ -0,0 +1,22 @@
+
+
+
diff --git a/resources/icons/delete_all_menu.svg b/resources/icons/delete_all_menu.svg
new file mode 100644
index 0000000000..5ee6d6ea6d
--- /dev/null
+++ b/resources/icons/delete_all_menu.svg
@@ -0,0 +1,31 @@
+
+
+
diff --git a/resources/icons/dot.svg b/resources/icons/dot.svg
new file mode 100644
index 0000000000..236db36786
--- /dev/null
+++ b/resources/icons/dot.svg
@@ -0,0 +1,8 @@
+
+
+
diff --git a/resources/icons/dot_white.svg b/resources/icons/dot_white.svg
new file mode 100644
index 0000000000..90fbaf7fb1
--- /dev/null
+++ b/resources/icons/dot_white.svg
@@ -0,0 +1,8 @@
+
+
+
diff --git a/resources/icons/editor_menu.svg b/resources/icons/editor_menu.svg
new file mode 100644
index 0000000000..223efda0f5
--- /dev/null
+++ b/resources/icons/editor_menu.svg
@@ -0,0 +1,20 @@
+
+
+
diff --git a/resources/icons/exclamation.svg b/resources/icons/exclamation.svg
new file mode 100644
index 0000000000..4568026529
--- /dev/null
+++ b/resources/icons/exclamation.svg
@@ -0,0 +1,17 @@
+
+
+
diff --git a/resources/icons/export_config.svg b/resources/icons/export_config.svg
new file mode 100644
index 0000000000..e70035dae5
--- /dev/null
+++ b/resources/icons/export_config.svg
@@ -0,0 +1,20 @@
+
+
+
diff --git a/resources/icons/export_config_bundle.svg b/resources/icons/export_config_bundle.svg
new file mode 100644
index 0000000000..1e587689d0
--- /dev/null
+++ b/resources/icons/export_config_bundle.svg
@@ -0,0 +1,51 @@
+
+
+
diff --git a/resources/icons/export_gcode.svg b/resources/icons/export_gcode.svg
new file mode 100644
index 0000000000..317e01f7df
--- /dev/null
+++ b/resources/icons/export_gcode.svg
@@ -0,0 +1,14 @@
+
+
+
diff --git a/resources/icons/export_plater.svg b/resources/icons/export_plater.svg
new file mode 100644
index 0000000000..641d952ad6
--- /dev/null
+++ b/resources/icons/export_plater.svg
@@ -0,0 +1,12 @@
+
+
+
diff --git a/resources/icons/import_config.svg b/resources/icons/import_config.svg
new file mode 100644
index 0000000000..636a311510
--- /dev/null
+++ b/resources/icons/import_config.svg
@@ -0,0 +1,25 @@
+
+
+
diff --git a/resources/icons/import_config_bundle.svg b/resources/icons/import_config_bundle.svg
new file mode 100644
index 0000000000..b8342760a8
--- /dev/null
+++ b/resources/icons/import_config_bundle.svg
@@ -0,0 +1,62 @@
+
+
+
diff --git a/resources/icons/import_plater.svg b/resources/icons/import_plater.svg
new file mode 100644
index 0000000000..a953122f26
--- /dev/null
+++ b/resources/icons/import_plater.svg
@@ -0,0 +1,17 @@
+
+
+
diff --git a/resources/icons/layers.svg b/resources/icons/layers.svg
index cd71fab3a3..da5dec21d5 100644
--- a/resources/icons/layers.svg
+++ b/resources/icons/layers.svg
@@ -5,13 +5,13 @@
-
+
-
+
-
+
@@ -20,7 +20,7 @@
-
+
diff --git a/resources/icons/mark_X.svg b/resources/icons/mark_X.svg
new file mode 100644
index 0000000000..1045debc51
--- /dev/null
+++ b/resources/icons/mark_X.svg
@@ -0,0 +1,9 @@
+
+
+
diff --git a/resources/icons/mark_Y.svg b/resources/icons/mark_Y.svg
new file mode 100644
index 0000000000..26e01b2952
--- /dev/null
+++ b/resources/icons/mark_Y.svg
@@ -0,0 +1,9 @@
+
+
+
diff --git a/resources/icons/mark_Z.svg b/resources/icons/mark_Z.svg
new file mode 100644
index 0000000000..cd2826ac00
--- /dev/null
+++ b/resources/icons/mark_Z.svg
@@ -0,0 +1,9 @@
+
+
+
diff --git a/resources/icons/number_of_copies.svg b/resources/icons/number_of_copies.svg
new file mode 100644
index 0000000000..a5c8affc7f
--- /dev/null
+++ b/resources/icons/number_of_copies.svg
@@ -0,0 +1,28 @@
+
+
+
diff --git a/resources/icons/open.svg b/resources/icons/open.svg
new file mode 100644
index 0000000000..8fcddc2a61
--- /dev/null
+++ b/resources/icons/open.svg
@@ -0,0 +1,11 @@
+
+
+
diff --git a/resources/icons/overlay/cut_hover.png b/resources/icons/overlay/cut_hover.png
deleted file mode 100644
index b026667c28..0000000000
Binary files a/resources/icons/overlay/cut_hover.png and /dev/null differ
diff --git a/resources/icons/overlay/cut_off.png b/resources/icons/overlay/cut_off.png
deleted file mode 100644
index c01f205546..0000000000
Binary files a/resources/icons/overlay/cut_off.png and /dev/null differ
diff --git a/resources/icons/overlay/cut_on.png b/resources/icons/overlay/cut_on.png
deleted file mode 100644
index fac6ecb45d..0000000000
Binary files a/resources/icons/overlay/cut_on.png and /dev/null differ
diff --git a/resources/icons/overlay/layflat_hover.png b/resources/icons/overlay/layflat_hover.png
deleted file mode 100644
index 508916e480..0000000000
Binary files a/resources/icons/overlay/layflat_hover.png and /dev/null differ
diff --git a/resources/icons/overlay/layflat_off.png b/resources/icons/overlay/layflat_off.png
deleted file mode 100644
index 6bfadd3165..0000000000
Binary files a/resources/icons/overlay/layflat_off.png and /dev/null differ
diff --git a/resources/icons/overlay/layflat_on.png b/resources/icons/overlay/layflat_on.png
deleted file mode 100644
index 4892b57fc7..0000000000
Binary files a/resources/icons/overlay/layflat_on.png and /dev/null differ
diff --git a/resources/icons/overlay/move_hover.png b/resources/icons/overlay/move_hover.png
deleted file mode 100644
index 933c34c245..0000000000
Binary files a/resources/icons/overlay/move_hover.png and /dev/null differ
diff --git a/resources/icons/overlay/move_off.png b/resources/icons/overlay/move_off.png
deleted file mode 100644
index 6c921a0421..0000000000
Binary files a/resources/icons/overlay/move_off.png and /dev/null differ
diff --git a/resources/icons/overlay/move_on.png b/resources/icons/overlay/move_on.png
deleted file mode 100644
index 80204b52ee..0000000000
Binary files a/resources/icons/overlay/move_on.png and /dev/null differ
diff --git a/resources/icons/overlay/rotate_hover.png b/resources/icons/overlay/rotate_hover.png
deleted file mode 100644
index 9df377fc7f..0000000000
Binary files a/resources/icons/overlay/rotate_hover.png and /dev/null differ
diff --git a/resources/icons/overlay/rotate_off.png b/resources/icons/overlay/rotate_off.png
deleted file mode 100644
index f5b6529795..0000000000
Binary files a/resources/icons/overlay/rotate_off.png and /dev/null differ
diff --git a/resources/icons/overlay/rotate_on.png b/resources/icons/overlay/rotate_on.png
deleted file mode 100644
index 6d4c5bf31e..0000000000
Binary files a/resources/icons/overlay/rotate_on.png and /dev/null differ
diff --git a/resources/icons/overlay/scale_hover.png b/resources/icons/overlay/scale_hover.png
deleted file mode 100644
index 40a05fffdd..0000000000
Binary files a/resources/icons/overlay/scale_hover.png and /dev/null differ
diff --git a/resources/icons/overlay/scale_off.png b/resources/icons/overlay/scale_off.png
deleted file mode 100644
index 351d5a0046..0000000000
Binary files a/resources/icons/overlay/scale_off.png and /dev/null differ
diff --git a/resources/icons/overlay/scale_on.png b/resources/icons/overlay/scale_on.png
deleted file mode 100644
index f8450e1058..0000000000
Binary files a/resources/icons/overlay/scale_on.png and /dev/null differ
diff --git a/resources/icons/overlay/sla_support_points_hover.png b/resources/icons/overlay/sla_support_points_hover.png
deleted file mode 100644
index 2b385c0e74..0000000000
Binary files a/resources/icons/overlay/sla_support_points_hover.png and /dev/null differ
diff --git a/resources/icons/overlay/sla_support_points_off.png b/resources/icons/overlay/sla_support_points_off.png
deleted file mode 100644
index 0a1e3f570f..0000000000
Binary files a/resources/icons/overlay/sla_support_points_off.png and /dev/null differ
diff --git a/resources/icons/overlay/sla_support_points_on.png b/resources/icons/overlay/sla_support_points_on.png
deleted file mode 100644
index e7f5d0a875..0000000000
Binary files a/resources/icons/overlay/sla_support_points_on.png and /dev/null differ
diff --git a/resources/icons/pad.svg b/resources/icons/pad.svg
new file mode 100644
index 0000000000..dc5907e37f
--- /dev/null
+++ b/resources/icons/pad.svg
@@ -0,0 +1,83 @@
+
+
+
diff --git a/resources/icons/paste.svg b/resources/icons/paste.svg
new file mode 100644
index 0000000000..028ffb8ea0
--- /dev/null
+++ b/resources/icons/paste.svg
@@ -0,0 +1,27 @@
+
+
+
diff --git a/resources/icons/paste_menu.svg b/resources/icons/paste_menu.svg
new file mode 100644
index 0000000000..74dbbf8eef
--- /dev/null
+++ b/resources/icons/paste_menu.svg
@@ -0,0 +1,27 @@
+
+
+
diff --git a/resources/icons/plater.svg b/resources/icons/plater.svg
new file mode 100644
index 0000000000..6fd21215b6
--- /dev/null
+++ b/resources/icons/plater.svg
@@ -0,0 +1,12 @@
+
+
+
diff --git a/resources/icons/preview_menu.svg b/resources/icons/preview_menu.svg
new file mode 100644
index 0000000000..725caf7b8a
--- /dev/null
+++ b/resources/icons/preview_menu.svg
@@ -0,0 +1,48 @@
+
+
+
diff --git a/resources/icons/printer_white.svg b/resources/icons/printer_white.svg
new file mode 100644
index 0000000000..d94f6fd5c4
--- /dev/null
+++ b/resources/icons/printer_white.svg
@@ -0,0 +1,14 @@
+
+
+
diff --git a/resources/icons/printers/PrusaResearch_SL1.png b/resources/icons/printers/PrusaResearch_SL1.png
index 3c15f95eae..b4425a705f 100644
Binary files a/resources/icons/printers/PrusaResearch_SL1.png and b/resources/icons/printers/PrusaResearch_SL1.png differ
diff --git a/resources/icons/re_slice.svg b/resources/icons/re_slice.svg
new file mode 100644
index 0000000000..54490ea428
--- /dev/null
+++ b/resources/icons/re_slice.svg
@@ -0,0 +1,19 @@
+
+
+
diff --git a/resources/icons/remove_copies.svg b/resources/icons/remove_copies.svg
new file mode 100644
index 0000000000..d249cb5bcc
--- /dev/null
+++ b/resources/icons/remove_copies.svg
@@ -0,0 +1,15 @@
+
+
+
diff --git a/resources/icons/remove_menu.svg b/resources/icons/remove_menu.svg
new file mode 100644
index 0000000000..a25ae965b4
--- /dev/null
+++ b/resources/icons/remove_menu.svg
@@ -0,0 +1,44 @@
+
+
+
diff --git a/resources/icons/set_separate_obj.svg b/resources/icons/set_separate_obj.svg
new file mode 100644
index 0000000000..c95149e2dd
--- /dev/null
+++ b/resources/icons/set_separate_obj.svg
@@ -0,0 +1,29 @@
+
+
+
diff --git a/resources/icons/split_object_SMALL.svg b/resources/icons/split_object_SMALL.svg
new file mode 100644
index 0000000000..7e362c2ba2
--- /dev/null
+++ b/resources/icons/split_object_SMALL.svg
@@ -0,0 +1,19 @@
+
+
+
diff --git a/resources/icons/split_parts_SMALL.svg b/resources/icons/split_parts_SMALL.svg
new file mode 100644
index 0000000000..45f90bee06
--- /dev/null
+++ b/resources/icons/split_parts_SMALL.svg
@@ -0,0 +1,18 @@
+
+
+
diff --git a/resources/icons/support_blocker.svg b/resources/icons/support_blocker.svg
new file mode 100644
index 0000000000..56086b2795
--- /dev/null
+++ b/resources/icons/support_blocker.svg
@@ -0,0 +1,48 @@
+
+
+
diff --git a/resources/icons/support_enforcer.svg b/resources/icons/support_enforcer.svg
new file mode 100644
index 0000000000..0f0eb4907f
--- /dev/null
+++ b/resources/icons/support_enforcer.svg
@@ -0,0 +1,19 @@
+
+
+
diff --git a/resources/icons/test.svg b/resources/icons/test.svg
new file mode 100644
index 0000000000..abf35d0ae7
--- /dev/null
+++ b/resources/icons/test.svg
@@ -0,0 +1,37 @@
+
+
+
diff --git a/resources/icons/upload_queue.svg b/resources/icons/upload_queue.svg
new file mode 100644
index 0000000000..7647178366
--- /dev/null
+++ b/resources/icons/upload_queue.svg
@@ -0,0 +1,29 @@
+
+
+
diff --git a/resources/icons/wrench.svg b/resources/icons/wrench.svg
index 7966da8d8f..5266ec1ebc 100644
--- a/resources/icons/wrench.svg
+++ b/resources/icons/wrench.svg
@@ -3,22 +3,41 @@
diff --git a/resources/shaders/gouraud.fs b/resources/shaders/gouraud.fs
index f6116eec70..09003f4079 100644
--- a/resources/shaders/gouraud.fs
+++ b/resources/shaders/gouraud.fs
@@ -2,24 +2,21 @@
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
+varying vec3 clipping_planes_dots;
+
// x = tainted, y = specular;
varying vec2 intensity;
varying vec3 delta_box_min;
varying vec3 delta_box_max;
-varying float world_z;
-
uniform vec4 uniform_color;
-// x = min z, y = max z;
-uniform vec2 z_range;
void main()
{
- if ((world_z < z_range.x) || (z_range.y < world_z))
+ if (any(lessThan(clipping_planes_dots, ZERO)))
discard;
-
// if the fragment is outside the print volume -> use darker color
vec3 color = (any(lessThan(delta_box_min, ZERO)) || any(greaterThan(delta_box_max, ZERO))) ? mix(uniform_color.rgb, ZERO, 0.3333) : uniform_color.rgb;
gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + color * intensity.x, uniform_color.a);
diff --git a/resources/shaders/gouraud.vs b/resources/shaders/gouraud.vs
index 84ae513913..cc54c1c449 100644
--- a/resources/shaders/gouraud.vs
+++ b/resources/shaders/gouraud.vs
@@ -28,13 +28,18 @@ struct PrintBoxDetection
uniform PrintBoxDetection print_box;
+// Clipping plane, x = min z, y = max z. Used by the FFF and SLA previews to clip with a top / bottom plane.
+uniform vec2 z_range;
+// Clipping plane - general orientation. Used by the SLA gizmo.
+uniform vec4 clipping_plane;
+
// x = tainted, y = specular;
varying vec2 intensity;
varying vec3 delta_box_min;
varying vec3 delta_box_max;
-varying float world_z;
+varying vec3 clipping_planes_dots;
void main()
{
@@ -66,8 +71,11 @@ void main()
{
delta_box_min = ZERO;
delta_box_max = ZERO;
- }
+ }
gl_Position = ftransform();
- world_z = vec3(print_box.volume_world_matrix * gl_Vertex).z;
-}
+ // Point in homogenous coordinates.
+ vec4 world_pos = print_box.volume_world_matrix * gl_Vertex;
+ // Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded.
+ clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z);
+}
diff --git a/src/admesh/stl.h b/src/admesh/stl.h
index 2c436b426b..d682b24347 100644
--- a/src/admesh/stl.h
+++ b/src/admesh/stl.h
@@ -43,11 +43,21 @@ typedef Eigen::Matrix stl_normal;
static_assert(sizeof(stl_vertex) == 12, "size of stl_vertex incorrect");
static_assert(sizeof(stl_normal) == 12, "size of stl_normal incorrect");
-typedef struct {
+struct stl_facet {
stl_normal normal;
stl_vertex vertex[3];
char extra[2];
-} stl_facet;
+
+ stl_facet rotated(const Eigen::Quaternion &rot) {
+ stl_facet out;
+ out.normal = rot * this->normal;
+ out.vertex[0] = rot * this->vertex[0];
+ out.vertex[1] = rot * this->vertex[1];
+ out.vertex[2] = rot * this->vertex[2];
+ return out;
+ }
+};
+
#define SIZEOF_STL_FACET 50
static_assert(offsetof(stl_facet, normal) == 0, "stl_facet.normal has correct offset");
diff --git a/src/admesh/stlinit.cpp b/src/admesh/stlinit.cpp
index e2939b8af8..911f4f5e82 100644
--- a/src/admesh/stlinit.cpp
+++ b/src/admesh/stlinit.cpp
@@ -41,10 +41,12 @@ stl_open(stl_file *stl, const char *file) {
stl_count_facets(stl, file);
stl_allocate(stl);
stl_read(stl, 0, true);
- if (!stl->error) fclose(stl->fp);
+ if (stl->fp != nullptr) {
+ fclose(stl->fp);
+ stl->fp = nullptr;
+ }
}
-
void
stl_initialize(stl_file *stl) {
memset(stl, 0, sizeof(stl_file));
@@ -118,7 +120,7 @@ stl_count_facets(stl_file *stl, const char *file) {
}
/* Read the int following the header. This should contain # of facets */
- bool header_num_faces_read = fread(&header_num_facets, sizeof(uint32_t), 1, stl->fp);
+ bool header_num_faces_read = fread(&header_num_facets, sizeof(uint32_t), 1, stl->fp) != 0;
#ifndef BOOST_LITTLE_ENDIAN
// Convert from little endian to big endian.
stl_internal_reverse_quads((char*)&header_num_facets, 4);
@@ -257,7 +259,6 @@ stl_reallocate(stl_file *stl) {
time running this for the stl and therefore we should reset our max and min stats. */
void stl_read(stl_file *stl, int first_facet, bool first) {
stl_facet facet;
- int i;
if (stl->error) return;
@@ -268,7 +269,7 @@ void stl_read(stl_file *stl, int first_facet, bool first) {
}
char normal_buf[3][32];
- for(i = first_facet; i < stl->stats.number_of_facets; i++) {
+ for(uint32_t i = first_facet; i < stl->stats.number_of_facets; i++) {
if(stl->stats.type == binary)
/* Read a single facet from a binary .STL file */
{
@@ -366,17 +367,19 @@ void stl_facet_stats(stl_file *stl, stl_facet facet, bool &first)
}
}
-void
-stl_close(stl_file *stl) {
- if (stl->error) return;
+void stl_close(stl_file *stl)
+{
+ assert(stl->fp == nullptr);
+ assert(stl->heads == nullptr);
+ assert(stl->tail == nullptr);
- if(stl->neighbors_start != NULL)
- free(stl->neighbors_start);
- if(stl->facet_start != NULL)
- free(stl->facet_start);
- if(stl->v_indices != NULL)
- free(stl->v_indices);
- if(stl->v_shared != NULL)
- free(stl->v_shared);
+ if (stl->facet_start != NULL)
+ free(stl->facet_start);
+ if (stl->neighbors_start != NULL)
+ free(stl->neighbors_start);
+ if (stl->v_indices != NULL)
+ free(stl->v_indices);
+ if (stl->v_shared != NULL)
+ free(stl->v_shared);
+ memset(stl, 0, sizeof(stl_file));
}
-
diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index 5bd4ea2b69..ce93d95fab 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -189,7 +189,6 @@ target_link_libraries(libslic3r
clipper
nowide
${EXPAT_LIBRARIES}
- ${GLEW_LIBRARIES}
glu-libtess
polypartition
poly2tri
diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp
index e634dd1383..6b16855e8e 100644
--- a/src/libslic3r/Model.cpp
+++ b/src/libslic3r/Model.cpp
@@ -593,6 +593,8 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
this->origin_translation = rhs.origin_translation;
m_bounding_box = rhs.m_bounding_box;
m_bounding_box_valid = rhs.m_bounding_box_valid;
+ m_raw_bounding_box = rhs.m_raw_bounding_box;
+ m_raw_bounding_box_valid = rhs.m_raw_bounding_box_valid;
m_raw_mesh_bounding_box = rhs.m_raw_mesh_bounding_box;
m_raw_mesh_bounding_box_valid = rhs.m_raw_mesh_bounding_box_valid;
@@ -627,6 +629,8 @@ ModelObject& ModelObject::assign_copy(ModelObject &&rhs)
this->origin_translation = std::move(rhs.origin_translation);
m_bounding_box = std::move(rhs.m_bounding_box);
m_bounding_box_valid = std::move(rhs.m_bounding_box_valid);
+ m_raw_bounding_box = rhs.m_raw_bounding_box;
+ m_raw_bounding_box_valid = rhs.m_raw_bounding_box_valid;
m_raw_mesh_bounding_box = rhs.m_raw_mesh_bounding_box;
m_raw_mesh_bounding_box_valid = rhs.m_raw_mesh_bounding_box_valid;
@@ -859,7 +863,7 @@ TriangleMesh ModelObject::full_raw_mesh() const
return mesh;
}
-BoundingBoxf3 ModelObject::raw_mesh_bounding_box() const
+const BoundingBoxf3& ModelObject::raw_mesh_bounding_box() const
{
if (! m_raw_mesh_bounding_box_valid) {
m_raw_mesh_bounding_box_valid = true;
@@ -880,33 +884,36 @@ BoundingBoxf3 ModelObject::full_raw_mesh_bounding_box() const
}
// A transformed snug bounding box around the non-modifier object volumes, without the translation applied.
-// This bounding box is only used for the actual slicing.
-BoundingBoxf3 ModelObject::raw_bounding_box() const
+// This bounding box is only used for the actual slicing and for layer editing UI to calculate the layers.
+const BoundingBoxf3& ModelObject::raw_bounding_box() const
{
- BoundingBoxf3 bb;
-#if ENABLE_GENERIC_SUBPARTS_PLACEMENT
- if (this->instances.empty())
- throw std::invalid_argument("Can't call raw_bounding_box() with no instances");
+ if (! m_raw_bounding_box_valid) {
+ m_raw_bounding_box_valid = true;
+ m_raw_bounding_box.reset();
+ #if ENABLE_GENERIC_SUBPARTS_PLACEMENT
+ if (this->instances.empty())
+ throw std::invalid_argument("Can't call raw_bounding_box() with no instances");
- const Transform3d& inst_matrix = this->instances.front()->get_transformation().get_matrix(true);
-#endif // ENABLE_GENERIC_SUBPARTS_PLACEMENT
- for (const ModelVolume *v : this->volumes)
- if (v->is_model_part()) {
-#if !ENABLE_GENERIC_SUBPARTS_PLACEMENT
- if (this->instances.empty())
- throw std::invalid_argument("Can't call raw_bounding_box() with no instances");
-#endif // !ENABLE_GENERIC_SUBPARTS_PLACEMENT
+ const Transform3d& inst_matrix = this->instances.front()->get_transformation().get_matrix(true);
+ #endif // ENABLE_GENERIC_SUBPARTS_PLACEMENT
+ for (const ModelVolume *v : this->volumes)
+ if (v->is_model_part()) {
+ #if !ENABLE_GENERIC_SUBPARTS_PLACEMENT
+ if (this->instances.empty())
+ throw std::invalid_argument("Can't call raw_bounding_box() with no instances");
+ #endif // !ENABLE_GENERIC_SUBPARTS_PLACEMENT
- TriangleMesh vol_mesh(v->mesh);
-#if ENABLE_GENERIC_SUBPARTS_PLACEMENT
- vol_mesh.transform(inst_matrix * v->get_matrix());
- bb.merge(vol_mesh.bounding_box());
-#else
- vol_mesh.transform(v->get_matrix());
- bb.merge(this->instances.front()->transform_mesh_bounding_box(vol_mesh, true));
-#endif // ENABLE_GENERIC_SUBPARTS_PLACEMENT
- }
- return bb;
+ #if ENABLE_GENERIC_SUBPARTS_PLACEMENT
+ m_raw_bounding_box.merge(v->mesh.transformed_bounding_box(inst_matrix * v->get_matrix()));
+ #else
+ // unmaintaned
+ assert(false);
+ // vol_mesh.transform(v->get_matrix());
+ // m_raw_bounding_box_valid.merge(this->instances.front()->transform_mesh_bounding_box(vol_mesh, true));
+ #endif // ENABLE_GENERIC_SUBPARTS_PLACEMENT
+ }
+ }
+ return m_raw_bounding_box;
}
// This returns an accurate snug bounding box of the transformed object instance, without the translation applied.
@@ -920,13 +927,13 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_
{
if (v->is_model_part())
{
- TriangleMesh mesh(v->mesh);
#if ENABLE_GENERIC_SUBPARTS_PLACEMENT
- mesh.transform(inst_matrix * v->get_matrix());
- bb.merge(mesh.bounding_box());
+ bb.merge(v->mesh.transformed_bounding_box(inst_matrix * v->get_matrix()));
#else
- mesh.transform(v->get_matrix());
- bb.merge(this->instances[instance_idx]->transform_mesh_bounding_box(mesh, dont_translate));
+ // not maintained
+ assert(false);
+ //mesh.transform(v->get_matrix());
+ //bb.merge(this->instances[instance_idx]->transform_mesh_bounding_box(mesh, dont_translate));
#endif // ENABLE_GENERIC_SUBPARTS_PLACEMENT
}
}
diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp
index 9514012434..1234102e0f 100644
--- a/src/libslic3r/Model.hpp
+++ b/src/libslic3r/Model.hpp
@@ -212,7 +212,7 @@ public:
// This bounding box is approximate and not snug.
// This bounding box is being cached.
const BoundingBoxf3& bounding_box() const;
- void invalidate_bounding_box() { m_bounding_box_valid = false; m_raw_mesh_bounding_box_valid = false; }
+ void invalidate_bounding_box() { m_bounding_box_valid = false; m_raw_bounding_box_valid = false; m_raw_mesh_bounding_box_valid = false; }
// A mesh containing all transformed instances of this object.
TriangleMesh mesh() const;
@@ -223,11 +223,11 @@ public:
TriangleMesh full_raw_mesh() const;
// A transformed snug bounding box around the non-modifier object volumes, without the translation applied.
// This bounding box is only used for the actual slicing.
- BoundingBoxf3 raw_bounding_box() const;
+ const BoundingBoxf3& raw_bounding_box() const;
// A snug bounding box around the transformed non-modifier object volumes.
BoundingBoxf3 instance_bounding_box(size_t instance_idx, bool dont_translate = false) const;
// A snug bounding box of non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes.
- BoundingBoxf3 raw_mesh_bounding_box() const;
+ const BoundingBoxf3& raw_mesh_bounding_box() const;
// A snug bounding box of non-transformed (non-rotated, non-scaled, non-translated) sum of all object volumes.
BoundingBoxf3 full_raw_mesh_bounding_box() const;
@@ -285,7 +285,7 @@ protected:
private:
ModelObject(Model *model) : m_model(model), origin_translation(Vec3d::Zero()),
- m_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) {}
+ m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) {}
~ModelObject();
/* To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" */
@@ -304,6 +304,8 @@ private:
// Bounding box, cached.
mutable BoundingBoxf3 m_bounding_box;
mutable bool m_bounding_box_valid;
+ mutable BoundingBoxf3 m_raw_bounding_box;
+ mutable bool m_raw_bounding_box_valid;
mutable BoundingBoxf3 m_raw_mesh_bounding_box;
mutable bool m_raw_mesh_bounding_box_valid;
};
diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp
index 9e97ab20cb..b566eadeda 100644
--- a/src/libslic3r/Print.hpp
+++ b/src/libslic3r/Print.hpp
@@ -132,7 +132,7 @@ public:
// The slicing parameters are dependent on various configuration values
// (layer height, first layer height, raft settings, print nozzle diameter etc).
const SlicingParameters& slicing_parameters() const { return m_slicing_params; }
- static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object);
+ static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z);
// returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions)
std::vector object_extruders() const;
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index 46efceb9a8..9fd7787b1b 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -2528,14 +2528,17 @@ void PrintConfigDef::init_sla_params()
def = this->add("pad_wall_height", coFloat);
def->label = L("Pad wall height");
- def->tooltip = L("Defines the cavity depth. Set to zero to disable the cavity.");
+ def->tooltip = L("Defines the pad cavity depth. Set to zero to disable the cavity. "
+ "Be careful when enabling this feature, as some resins may "
+ "produce an extreme suction effect inside the cavity, "
+ "which makes pealing the print off the vat foil difficult.");
def->category = L("Pad");
// def->tooltip = L("");
def->sidetext = L("mm");
def->min = 0;
def->max = 30;
- def->mode = comSimple;
- def->default_value = new ConfigOptionFloat(5.0);
+ def->mode = comExpert;
+ def->default_value = new ConfigOptionFloat(0.);
def = this->add("pad_max_merge_distance", coFloat);
def->label = L("Max merge distance");
@@ -3114,6 +3117,13 @@ CLIMiscConfigDef::CLIMiscConfigDef()
def->label = L("Logging level");
def->tooltip = L("Messages with severity lower or eqal to the loglevel will be printed out. 0:trace, 1:debug, 2:info, 3:warning, 4:error, 5:fatal");
def->min = 0;
+
+#if defined(_MSC_VER) && defined(SLIC3R_GUI)
+ def = this->add("sw_renderer", coBool);
+ def->label = L("Render with a software renderer");
+ def->tooltip = L("Render with a software renderer. The bundled MESA software renderer is loaded instead of the default OpenGL driver.");
+ def->min = 0;
+#endif /* _MSC_VER */
}
const CLIActionsConfigDef cli_actions_config_def;
diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp
index 954e583f7c..0b51f36ec0 100644
--- a/src/libslic3r/PrintObject.cpp
+++ b/src/libslic3r/PrintObject.cpp
@@ -1370,7 +1370,7 @@ void PrintObject::update_slicing_parameters()
this->print()->config(), m_config, unscale(this->size(2)), this->object_extruders());
}
-SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object)
+SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z)
{
PrintConfig print_config;
PrintObjectConfig object_config;
@@ -1390,7 +1390,9 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full
object_extruders);
sort_remove_duplicates(object_extruders);
- return SlicingParameters::create_from_config(print_config, object_config, model_object.bounding_box().max.z(), object_extruders);
+ if (object_max_z <= 0.f)
+ object_max_z = model_object.raw_bounding_box().size().z();
+ return SlicingParameters::create_from_config(print_config, object_config, object_max_z, object_extruders);
}
// returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions)
diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp
index 34dd80cee0..bb0e5e0071 100644
--- a/src/libslic3r/SLA/SLASupportTree.cpp
+++ b/src/libslic3r/SLA/SLASupportTree.cpp
@@ -1572,10 +1572,8 @@ public:
auto hit = bridge_mesh_intersect(headjp, n, r);
if(std::isinf(hit.distance())) ground_head_indices.emplace_back(i);
- else {
- if(m_cfg.ground_facing_only) head.invalidate();
- m_iheads_onmodel.emplace_back(std::make_pair(i, hit));
- }
+ else if(m_cfg.ground_facing_only) head.invalidate();
+ else m_iheads_onmodel.emplace_back(std::make_pair(i, hit));
}
// We want to search for clusters of points that are far enough
@@ -1872,7 +1870,7 @@ public:
}
}
- void cascade_pillars() {
+ void interconnect_pillars() {
// Now comes the algorithm that connects pillars with each other.
// Ideally every pillar should be connected with at least one of its
// neighbors if that neighbor is within max_pillar_link_distance
@@ -2121,7 +2119,7 @@ bool SLASupportTree::generate(const std::vector &support_points,
std::bind(&Algorithm::routing_to_model, &alg),
- std::bind(&Algorithm::cascade_pillars, &alg),
+ std::bind(&Algorithm::interconnect_pillars, &alg),
std::bind(&Algorithm::routing_headless, &alg),
@@ -2150,16 +2148,16 @@ bool SLASupportTree::generate(const std::vector &support_points,
// Let's define a simple automaton that will run our program.
auto progress = [&ctl, &pc] () {
static const std::array stepstr {
- L("Starting"),
- L("Filtering"),
- L("Generate pinheads"),
- L("Classification"),
- L("Routing to ground"),
- L("Routing supports to model surface"),
- L("Cascading pillars"),
- L("Processing small holes"),
- L("Done"),
- L("Abort")
+ "Starting",
+ "Filtering",
+ "Generate pinheads",
+ "Classification",
+ "Routing to ground",
+ "Routing supports to model surface",
+ "Interconnecting pillars",
+ "Processing small holes",
+ "Done",
+ "Abort"
};
static const std::array stepstate {
diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp
index bfba364af6..2d603661dc 100644
--- a/src/libslic3r/TriangleMesh.cpp
+++ b/src/libslic3r/TriangleMesh.cpp
@@ -693,6 +693,16 @@ void TriangleMeshSlicer::init(TriangleMesh *_mesh, throw_on_cancel_callback_type
}
}
+
+
+void TriangleMeshSlicer::set_up_direction(const Vec3f& up)
+{
+ m_quaternion.setFromTwoVectors(up, Vec3f::UnitZ());
+ m_use_quaternion = true;
+}
+
+
+
void TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const
{
BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::slice";
@@ -795,7 +805,7 @@ void TriangleMeshSlicer::slice(const std::vector &z, std::vector* lines, boost::mutex* lines_mutex,
const std::vector &z) const
{
- const stl_facet &facet = this->mesh->stl.facet_start[facet_idx];
+ const stl_facet &facet = m_use_quaternion ? this->mesh->stl.facet_start[facet_idx].rotated(m_quaternion) : this->mesh->stl.facet_start[facet_idx];
// find facet extents
const float min_z = fminf(facet.vertex[0](2), fminf(facet.vertex[1](2), facet.vertex[2](2)));
@@ -860,26 +870,43 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
IntersectionPoint points[3];
size_t num_points = 0;
size_t point_on_layer = size_t(-1);
-
+
// Reorder vertices so that the first one is the one with lowest Z.
// This is needed to get all intersection lines in a consistent order
// (external on the right of the line)
const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
int i = (facet.vertex[1].z() == min_z) ? 1 : ((facet.vertex[2].z() == min_z) ? 2 : 0);
+
+ // These are used only if the cut plane is tilted:
+ stl_vertex rotated_a;
+ stl_vertex rotated_b;
+
for (int j = i; j - i < 3; ++j) { // loop through facet edges
int edge_id = this->facets_edges[facet_idx * 3 + (j % 3)];
int a_id = vertices[j % 3];
int b_id = vertices[(j+1) % 3];
- const stl_vertex *a = &this->v_scaled_shared[a_id];
- const stl_vertex *b = &this->v_scaled_shared[b_id];
+
+ const stl_vertex *a;
+ const stl_vertex *b;
+ if (m_use_quaternion) {
+ rotated_a = m_quaternion * this->v_scaled_shared[a_id];
+ rotated_b = m_quaternion * this->v_scaled_shared[b_id];
+ a = &rotated_a;
+ b = &rotated_b;
+ }
+ else {
+ a = &this->v_scaled_shared[a_id];
+ b = &this->v_scaled_shared[b_id];
+ }
// Is edge or face aligned with the cutting plane?
if (a->z() == slice_z && b->z() == slice_z) {
// Edge is horizontal and belongs to the current layer.
- const stl_vertex &v0 = this->v_scaled_shared[vertices[0]];
- const stl_vertex &v1 = this->v_scaled_shared[vertices[1]];
- const stl_vertex &v2 = this->v_scaled_shared[vertices[2]];
- const stl_normal &normal = this->mesh->stl.facet_start[facet_idx].normal;
+ // The following rotation of the three vertices may not be efficient, but this branch happens rarely.
+ const stl_vertex &v0 = m_use_quaternion ? stl_vertex(m_quaternion * this->v_scaled_shared[vertices[0]]) : this->v_scaled_shared[vertices[0]];
+ const stl_vertex &v1 = m_use_quaternion ? stl_vertex(m_quaternion * this->v_scaled_shared[vertices[1]]) : this->v_scaled_shared[vertices[1]];
+ const stl_vertex &v2 = m_use_quaternion ? stl_vertex(m_quaternion * this->v_scaled_shared[vertices[2]]) : this->v_scaled_shared[vertices[2]];
+ const stl_normal &normal = facet.normal;
// We may ignore this edge for slicing purposes, but we may still use it for object cutting.
FacetSliceType result = Slicing;
if (min_z == max_z) {
@@ -995,7 +1022,9 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
if (i == line_out->a_id || i == line_out->b_id)
i = vertices[2];
assert(i != line_out->a_id && i != line_out->b_id);
- line_out->edge_type = (this->v_scaled_shared[i].z() < slice_z) ? feTop : feBottom;
+ line_out->edge_type = ((m_use_quaternion ?
+ (m_quaternion * this->v_scaled_shared[i]).z()
+ : this->v_scaled_shared[i].z()) < slice_z) ? feTop : feBottom;
}
#endif
return Slicing;
diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp
index d389500c69..4bf5ce039f 100644
--- a/src/libslic3r/TriangleMesh.hpp
+++ b/src/libslic3r/TriangleMesh.hpp
@@ -25,9 +25,10 @@ public:
TriangleMesh(const Pointf3s &points, const std::vector &facets);
TriangleMesh(const TriangleMesh &other) : repaired(false) { stl_initialize(&this->stl); *this = other; }
TriangleMesh(TriangleMesh &&other) : repaired(false) { stl_initialize(&this->stl); this->swap(other); }
- ~TriangleMesh() { stl_close(&this->stl); }
+ ~TriangleMesh() { clear(); }
TriangleMesh& operator=(const TriangleMesh &other);
TriangleMesh& operator=(TriangleMesh &&other) { this->swap(other); return *this; }
+ void clear() { stl_close(&this->stl); this->repaired = false; }
void swap(TriangleMesh &other) { std::swap(this->stl, other.stl); std::swap(this->repaired, other.repaired); }
void ReadSTLFile(const char* input_file) { stl_open(&stl, input_file); }
void write_ascii(const char* output_file) { stl_write_ascii(&this->stl, output_file, ""); }
@@ -171,6 +172,7 @@ public:
FacetSliceType slice_facet(float slice_z, const stl_facet &facet, const int facet_idx,
const float min_z, const float max_z, IntersectionLine *line_out) const;
void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const;
+ void set_up_direction(const Vec3f& up);
private:
const TriangleMesh *mesh;
@@ -178,6 +180,10 @@ private:
std::vector facets_edges;
// Scaled copy of this->mesh->stl.v_shared
std::vector v_scaled_shared;
+ // Quaternion that will be used to rotate every facet before the slicing
+ Eigen::Quaternion m_quaternion;
+ // Whether or not the above quaterion should be used
+ bool m_use_quaternion = false;
void _slice_do(size_t facet_idx, std::vector* lines, boost::mutex* lines_mutex, const std::vector &z) const;
void make_loops(std::vector &lines, Polygons* loops) const;
diff --git a/src/slic3r.cpp b/src/slic3r.cpp
index 780efea7b3..ff87b3f6d9 100644
--- a/src/slic3r.cpp
+++ b/src/slic3r.cpp
@@ -6,10 +6,12 @@
#define NOMINMAX
#include
#include
- // Let the NVIDIA and AMD know we want to use their graphics card
- // on a dual graphics card system.
- __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
- __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
+ #ifdef SLIC3R_GUI
+ // Let the NVIDIA and AMD know we want to use their graphics card
+ // on a dual graphics card system.
+ __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
+ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
+ #endif /* SLIC3R_GUI */
#endif /* WIN32 */
#include
@@ -38,8 +40,11 @@
#include "libslic3r/Utils.hpp"
#include "slic3r.hpp"
-#include "slic3r/GUI/GUI.hpp"
-#include "slic3r/GUI/GUI_App.hpp"
+
+#ifdef SLIC3R_GUI
+ #include "slic3r/GUI/GUI.hpp"
+ #include "slic3r/GUI/GUI_App.hpp"
+#endif /* SLIC3R_GUI */
using namespace Slic3r;
@@ -448,7 +453,7 @@ int CLI::run(int argc, char **argv)
}
if (start_gui) {
-#if 1
+#ifdef SLIC3R_GUI
// #ifdef USE_WX
GUI::GUI_App *gui = new GUI::GUI_App();
// gui->autosave = m_config.opt_string("autosave");
@@ -477,12 +482,12 @@ int CLI::run(int argc, char **argv)
gui->mainframe->load_config(m_extra_config);
});
return wxEntry(argc, argv);
-#else
+#else /* SLIC3R_GUI */
// No GUI support. Just print out a help.
this->print_help(false);
// If started without a parameter, consider it to be OK, otherwise report an error code (no action etc).
return (argc == 0) ? 0 : 1;
-#endif
+#endif /* SLIC3R_GUI */
}
return 0;
@@ -563,7 +568,13 @@ bool CLI::setup(int argc, char **argv)
void CLI::print_help(bool include_print_options, PrinterTechnology printer_technology) const
{
boost::nowide::cout
- << "Slic3r Prusa Edition " << SLIC3R_BUILD << std::endl
+ << "Slic3r Prusa Edition " << SLIC3R_BUILD
+#ifdef SLIC3R_GUI
+ << " (with GUI support)"
+#else /* SLIC3R_GUI */
+ << " (without GUI support)"
+#endif /* SLIC3R_GUI */
+ << std::endl
<< "https://github.com/prusa3d/Slic3r" << std::endl << std::endl
<< "Usage: slic3r [ ACTIONS ] [ TRANSFORM ] [ OPTIONS ] [ file.stl ... ]" << std::endl
<< std::endl
diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt
index 9d25318136..1d8a1e26e3 100644
--- a/src/slic3r/CMakeLists.txt
+++ b/src/slic3r/CMakeLists.txt
@@ -154,7 +154,7 @@ endif ()
add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES})
-target_link_libraries(libslic3r_gui libslic3r avrdude imgui)
+target_link_libraries(libslic3r_gui libslic3r avrdude imgui ${GLEW_LIBRARIES})
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE)
endif ()
diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp
index 3dd680820c..9038e388c8 100644
--- a/src/slic3r/GUI/3DScene.cpp
+++ b/src/slic3r/GUI/3DScene.cpp
@@ -721,32 +721,37 @@ int GLVolumeCollection::load_wipe_tower_preview(
return int(this->volumes.size() - 1);
}
-typedef std::pair GLVolumeWithZ;
-typedef std::vector GLVolumesWithZList;
-static GLVolumesWithZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type, const Transform3d& view_matrix, std::function filter_func)
+GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type, const Transform3d& view_matrix, std::function filter_func)
{
- GLVolumesWithZList list;
+ GLVolumeWithIdAndZList list;
list.reserve(volumes.size());
- for (GLVolume* volume : volumes)
+ for (unsigned int i = 0; i < (unsigned int)volumes.size(); ++i)
{
+ GLVolume* volume = volumes[i];
bool is_transparent = (volume->render_color[3] < 1.0f);
if ((((type == GLVolumeCollection::Opaque) && !is_transparent) ||
((type == GLVolumeCollection::Transparent) && is_transparent) ||
(type == GLVolumeCollection::All)) &&
(! filter_func || filter_func(*volume)))
- list.emplace_back(std::make_pair(volume, 0.0));
+ list.emplace_back(std::make_pair(volume, std::make_pair(i, 0.0)));
}
if ((type == GLVolumeCollection::Transparent) && (list.size() > 1))
{
- for (GLVolumeWithZ& volume : list)
+ for (GLVolumeWithIdAndZ& volume : list)
{
- volume.second = volume.first->bounding_box.transformed(view_matrix * volume.first->world_matrix()).max(2);
+ volume.second.second = volume.first->bounding_box.transformed(view_matrix * volume.first->world_matrix()).max(2);
}
std::sort(list.begin(), list.end(),
- [](const GLVolumeWithZ& v1, const GLVolumeWithZ& v2) -> bool { return v1.second < v2.second; }
+ [](const GLVolumeWithIdAndZ& v1, const GLVolumeWithIdAndZ& v2) -> bool { return v1.second.second < v2.second.second; }
+ );
+ }
+ else if ((type == GLVolumeCollection::Opaque) && (list.size() > 1))
+ {
+ std::sort(list.begin(), list.end(),
+ [](const GLVolumeWithIdAndZ& v1, const GLVolumeWithIdAndZ& v2) -> bool { return v1.first->selected && !v2.first->selected; }
);
}
@@ -769,6 +774,7 @@ void GLVolumeCollection::render_VBOs(GLVolumeCollection::ERenderType type, bool
glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id));
GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1;
GLint z_range_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "z_range") : -1;
+ GLint clipping_plane_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "clipping_plane") : -1;
GLint print_box_min_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.min") : -1;
GLint print_box_max_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.max") : -1;
GLint print_box_detection_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.volume_detection") : -1;
@@ -784,8 +790,11 @@ void GLVolumeCollection::render_VBOs(GLVolumeCollection::ERenderType type, bool
if (z_range_id != -1)
glsafe(::glUniform2fv(z_range_id, 1, (const GLfloat*)z_range));
- GLVolumesWithZList to_render = volumes_to_render(this->volumes, type, view_matrix, filter_func);
- for (GLVolumeWithZ& volume : to_render) {
+ if (clipping_plane_id != -1)
+ glsafe(::glUniform4fv(clipping_plane_id, 1, (const GLfloat*)clipping_plane));
+
+ GLVolumeWithIdAndZList to_render = volumes_to_render(this->volumes, type, view_matrix, filter_func);
+ for (GLVolumeWithIdAndZ& volume : to_render) {
volume.first->set_render_color();
volume.first->render_VBOs(color_id, print_box_detection_id, print_box_worldmatrix_id);
}
@@ -814,8 +823,8 @@ void GLVolumeCollection::render_legacy(ERenderType type, bool disable_cullface,
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
- GLVolumesWithZList to_render = volumes_to_render(this->volumes, type, view_matrix, filter_func);
- for (GLVolumeWithZ& volume : to_render)
+ GLVolumeWithIdAndZList to_render = volumes_to_render(this->volumes, type, view_matrix, filter_func);
+ for (GLVolumeWithIdAndZ& volume : to_render)
{
volume.first->set_render_color();
volume.first->render_legacy();
diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp
index ce7bf8e975..88547359ea 100644
--- a/src/slic3r/GUI/3DScene.hpp
+++ b/src/slic3r/GUI/3DScene.hpp
@@ -412,6 +412,8 @@ public:
};
typedef std::vector GLVolumePtrs;
+typedef std::pair> GLVolumeWithIdAndZ;
+typedef std::vector GLVolumeWithIdAndZList;
class GLVolumeCollection
{
@@ -431,6 +433,9 @@ private:
// z range for clipping in shaders
float z_range[2];
+ // plane coeffs for clipping in shaders
+ float clipping_plane[4];
+
public:
GLVolumePtrs volumes;
@@ -489,6 +494,7 @@ public:
}
void set_z_range(float min_z, float max_z) { z_range[0] = min_z; z_range[1] = max_z; }
+ void set_clipping_plane(const double* coeffs) { clipping_plane[0] = coeffs[0]; clipping_plane[1] = coeffs[1]; clipping_plane[2] = coeffs[2]; clipping_plane[3] = coeffs[3]; }
// returns true if all the volumes are completely contained in the print volume
// returns the containment state in the given out_state, if non-null
@@ -505,6 +511,8 @@ private:
GLVolumeCollection& operator=(const GLVolumeCollection &);
};
+GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type, const Transform3d& view_matrix, std::function filter_func = nullptr);
+
class GLModel
{
protected:
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 29c64b9a5c..0dc3ec83a8 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -255,16 +255,21 @@ void GLCanvas3D::LayersEditing::set_config(const DynamicPrintConfig* config)
void GLCanvas3D::LayersEditing::select_object(const Model &model, int object_id)
{
const ModelObject *model_object_new = (object_id >= 0) ? model.objects[object_id] : nullptr;
- if (model_object_new == nullptr || this->last_object_id != object_id || m_model_object != model_object_new || m_model_object->id() != model_object_new->id()) {
+ // Maximum height of an object changes when the object gets rotated or scaled.
+ // Changing maximum height of an object will invalidate the layer heigth editing profile.
+ // m_model_object->raw_bounding_box() is cached, therefore it is cheap even if this method is called frequently.
+ float new_max_z = (m_model_object == nullptr) ? 0.f : m_model_object->raw_bounding_box().size().z();
+ if (m_model_object != model_object_new || this->last_object_id != object_id || m_object_max_z != new_max_z ||
+ (model_object_new != nullptr && m_model_object->id() != model_object_new->id())) {
m_layer_height_profile.clear();
m_layer_height_profile_modified = false;
delete m_slicing_parameters;
- m_slicing_parameters = nullptr;
+ m_slicing_parameters = nullptr;
m_layers_texture.valid = false;
+ this->last_object_id = object_id;
+ m_model_object = model_object_new;
+ m_object_max_z = new_max_z;
}
- this->last_object_id = object_id;
- m_model_object = model_object_new;
- m_object_max_z = (m_model_object == nullptr) ? 0.f : m_model_object->bounding_box().max.z();
}
bool GLCanvas3D::LayersEditing::is_allowed() const
@@ -623,7 +628,7 @@ void GLCanvas3D::LayersEditing::update_slicing_parameters()
{
if (m_slicing_parameters == nullptr) {
m_slicing_parameters = new SlicingParameters();
- *m_slicing_parameters = PrintObject::slicing_parameters(*m_config, *m_model_object);
+ *m_slicing_parameters = PrintObject::slicing_parameters(*m_config, *m_model_object, m_object_max_z);
}
}
@@ -2204,6 +2209,9 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
void GLCanvas3D::on_char(wxKeyEvent& evt)
{
+ if (!m_initialized)
+ return;
+
// see include/wx/defs.h enum wxKeyCode
int keyCode = evt.GetKeyCode();
int ctrlMask = wxMOD_CONTROL;
@@ -2222,9 +2230,12 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
//#endif /* __APPLE__ */
if ((evt.GetModifiers() & ctrlMask) != 0) {
switch (keyCode) {
+#ifdef __APPLE__
case 'a':
case 'A':
+#else /* __APPLE__ */
case WXK_CONTROL_A:
+#endif /* __APPLE__ */
post_event(SimpleEvent(EVT_GLCANVAS_SELECT_ALL));
break;
#ifdef __APPLE__
@@ -3134,6 +3145,11 @@ Linef3 GLCanvas3D::mouse_ray(const Point& mouse_pos)
return Linef3(_mouse_to_3d(mouse_pos, &z0), _mouse_to_3d(mouse_pos, &z1));
}
+double GLCanvas3D::get_size_proportional_to_max_bed_size(double factor) const
+{
+ return factor * m_bed.get_bounding_box().max_size();
+}
+
bool GLCanvas3D::_is_shown_on_screen() const
{
return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false;
@@ -3226,12 +3242,37 @@ bool GLCanvas3D::_init_toolbar()
if (!m_toolbar.add_separator())
return false;
+ item.name = "copy";
+#if ENABLE_SVG_ICONS
+ item.icon_filename = "copy.svg";
+#endif // ENABLE_SVG_ICONS
+ item.tooltip = GUI::L_str("Copy") + " [" + GUI::shortkey_ctrl_prefix() + "C]";
+ item.sprite_id = 4;
+ item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_COPY)); };
+ item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_copy(); };
+ if (!m_toolbar.add_item(item))
+ return false;
+
+ item.name = "paste";
+#if ENABLE_SVG_ICONS
+ item.icon_filename = "paste.svg";
+#endif // ENABLE_SVG_ICONS
+ item.tooltip = GUI::L_str("Paste") + " [" + GUI::shortkey_ctrl_prefix() + "V]";
+ item.sprite_id = 5;
+ item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_PASTE)); };
+ item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_paste(); };
+ if (!m_toolbar.add_item(item))
+ return false;
+
+ if (!m_toolbar.add_separator())
+ return false;
+
item.name = "more";
#if ENABLE_SVG_ICONS
item.icon_filename = "instance_add.svg";
#endif // ENABLE_SVG_ICONS
item.tooltip = GUI::L_str("Add instance [+]");
- item.sprite_id = 4;
+ item.sprite_id = 6;
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_MORE)); };
item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; };
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_increase_instances(); };
@@ -3243,7 +3284,7 @@ bool GLCanvas3D::_init_toolbar()
item.icon_filename = "instance_remove.svg";
#endif // ENABLE_SVG_ICONS
item.tooltip = GUI::L_str("Remove instance [-]");
- item.sprite_id = 5;
+ item.sprite_id = 7;
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_FEWER)); };
item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; };
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_decrease_instances(); };
@@ -3258,7 +3299,7 @@ bool GLCanvas3D::_init_toolbar()
item.icon_filename = "split_objects.svg";
#endif // ENABLE_SVG_ICONS
item.tooltip = GUI::L_str("Split to objects");
- item.sprite_id = 6;
+ item.sprite_id = 8;
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_OBJECTS)); };
item.visibility_callback = GLToolbarItem::Default_Visibility_Callback;
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_split_to_objects(); };
@@ -3270,7 +3311,7 @@ bool GLCanvas3D::_init_toolbar()
item.icon_filename = "split_parts.svg";
#endif // ENABLE_SVG_ICONS
item.tooltip = GUI::L_str("Split to parts");
- item.sprite_id = 7;
+ item.sprite_id = 9;
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_VOLUMES)); };
item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; };
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_split_to_volumes(); };
@@ -3285,7 +3326,7 @@ bool GLCanvas3D::_init_toolbar()
item.icon_filename = "layers.svg";
#endif // ENABLE_SVG_ICONS
item.tooltip = GUI::L_str("Layers editing");
- item.sprite_id = 8;
+ item.sprite_id = 10;
item.is_toggable = true;
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); };
item.visibility_callback = [this]()->bool { return m_process->current_printer_technology() == ptFFF; };
@@ -3490,7 +3531,15 @@ void GLCanvas3D::_picking_pass() const
glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
- _render_volumes(true);
+ m_camera_clipping_plane = m_gizmos.get_sla_clipping_plane();
+ if (m_camera_clipping_plane.is_active()) {
+ ::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)m_camera_clipping_plane.get_data());
+ ::glEnable(GL_CLIP_PLANE0);
+ }
+ _render_volumes_for_picking();
+ if (m_camera_clipping_plane.is_active())
+ ::glDisable(GL_CLIP_PLANE0);
+
m_gizmos.render_current_gizmo_for_picking_pass(m_selection);
if (m_multisample_allowed)
@@ -3571,6 +3620,8 @@ void GLCanvas3D::_render_axes() const
m_bed.render_axes();
}
+
+
void GLCanvas3D::_render_objects() const
{
if (m_volumes.empty())
@@ -3579,6 +3630,8 @@ void GLCanvas3D::_render_objects() const
glsafe(::glEnable(GL_LIGHTING));
glsafe(::glEnable(GL_DEPTH_TEST));
+ m_camera_clipping_plane = m_gizmos.get_sla_clipping_plane();
+
if (m_use_VBOs)
{
if (m_picking_enabled)
@@ -3599,6 +3652,8 @@ void GLCanvas3D::_render_objects() const
else
m_volumes.set_z_range(-FLT_MAX, FLT_MAX);
+ m_volumes.set_clipping_plane(m_camera_clipping_plane.get_data());
+
m_shader.start_using();
if (m_picking_enabled && m_layers_editing.is_enabled() && m_layers_editing.last_object_id != -1) {
int object_id = m_layers_editing.last_object_id;
@@ -3619,13 +3674,17 @@ void GLCanvas3D::_render_objects() const
}
else
{
+ ::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)m_camera_clipping_plane.get_data());
+ ::glEnable(GL_CLIP_PLANE0);
+
if (m_use_clipping_planes)
{
- glsafe(::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)m_clipping_planes[0].get_data()));
- glsafe(::glEnable(GL_CLIP_PLANE0));
- glsafe(::glClipPlane(GL_CLIP_PLANE1, (GLdouble*)m_clipping_planes[1].get_data()));
+ glsafe(::glClipPlane(GL_CLIP_PLANE1, (GLdouble*)m_clipping_planes[0].get_data()));
glsafe(::glEnable(GL_CLIP_PLANE1));
+ glsafe(::glClipPlane(GL_CLIP_PLANE2, (GLdouble*)m_clipping_planes[1].get_data()));
+ glsafe(::glEnable(GL_CLIP_PLANE2));
}
+
// do not cull backfaces to show broken geometry, if any
m_volumes.render_legacy(GLVolumeCollection::Opaque, m_picking_enabled, m_camera.get_view_matrix(), [this](const GLVolume& volume) {
@@ -3633,13 +3692,16 @@ void GLCanvas3D::_render_objects() const
});
m_volumes.render_legacy(GLVolumeCollection::Transparent, false, m_camera.get_view_matrix());
+ ::glDisable(GL_CLIP_PLANE0);
+
if (m_use_clipping_planes)
{
- glsafe(::glDisable(GL_CLIP_PLANE0));
glsafe(::glDisable(GL_CLIP_PLANE1));
+ glsafe(::glDisable(GL_CLIP_PLANE2));
}
}
-
+
+ m_camera_clipping_plane = ClippingPlane::ClipsNothing();
glsafe(::glDisable(GL_LIGHTING));
}
@@ -3675,13 +3737,10 @@ void GLCanvas3D::_render_legend_texture() const
m_legend_texture.render(*this);
}
-void GLCanvas3D::_render_volumes(bool fake_colors) const
+void GLCanvas3D::_render_volumes_for_picking() const
{
static const GLfloat INV_255 = 1.0f / 255.0f;
- if (!fake_colors)
- glsafe(::glEnable(GL_LIGHTING));
-
// do not cull backfaces to show broken geometry, if any
glsafe(::glDisable(GL_CULL_FACE));
@@ -3691,27 +3750,31 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
- unsigned int volume_id = 0;
- for (GLVolume* vol : m_volumes.volumes)
+ const Transform3d& view_matrix = m_camera.get_view_matrix();
+ GLVolumeWithIdAndZList to_render = volumes_to_render(m_volumes.volumes, GLVolumeCollection::Opaque, view_matrix);
+ for (const GLVolumeWithIdAndZ& volume : to_render)
{
- if (fake_colors)
- {
- // Object picking mode. Render the object with a color encoding the object index.
- unsigned int r = (volume_id & 0x000000FF) >> 0;
- unsigned int g = (volume_id & 0x0000FF00) >> 8;
- unsigned int b = (volume_id & 0x00FF0000) >> 16;
- glsafe(::glColor3f((GLfloat)r * INV_255, (GLfloat)g * INV_255, (GLfloat)b * INV_255));
- }
- else
- {
- vol->set_render_color();
- glsafe(::glColor4fv(vol->render_color));
- }
+ // Object picking mode. Render the object with a color encoding the object index.
+ unsigned int r = (volume.second.first & 0x000000FF) >> 0;
+ unsigned int g = (volume.second.first & 0x0000FF00) >> 8;
+ unsigned int b = (volume.second.first & 0x00FF0000) >> 16;
+ glsafe(::glColor3f((GLfloat)r * INV_255, (GLfloat)g * INV_255, (GLfloat)b * INV_255));
- if ((!fake_colors || !vol->disabled) && (vol->composite_id.volume_id >= 0 || m_render_sla_auxiliaries))
- vol->render();
+ if (!volume.first->disabled && ((volume.first->composite_id.volume_id >= 0) || m_render_sla_auxiliaries))
+ volume.first->render();
+ }
- ++volume_id;
+ to_render = volumes_to_render(m_volumes.volumes, GLVolumeCollection::Transparent, view_matrix);
+ for (const GLVolumeWithIdAndZ& volume : to_render)
+ {
+ // Object picking mode. Render the object with a color encoding the object index.
+ unsigned int r = (volume.second.first & 0x000000FF) >> 0;
+ unsigned int g = (volume.second.first & 0x0000FF00) >> 8;
+ unsigned int b = (volume.second.first & 0x00FF0000) >> 16;
+ glsafe(::glColor3f((GLfloat)r * INV_255, (GLfloat)g * INV_255, (GLfloat)b * INV_255));
+
+ if (!volume.first->disabled && ((volume.first->composite_id.volume_id >= 0) || m_render_sla_auxiliaries))
+ volume.first->render();
}
glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
@@ -3719,9 +3782,6 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const
glsafe(::glDisable(GL_BLEND));
glsafe(::glEnable(GL_CULL_FACE));
-
- if (!fake_colors)
- glsafe(::glDisable(GL_LIGHTING));
}
void GLCanvas3D::_render_current_gizmo() const
@@ -3999,15 +4059,9 @@ void GLCanvas3D::_update_volumes_hover_state() const
return;
GLVolume* volume = m_volumes.volumes[m_hover_volume_id];
-
- switch (m_selection.get_mode())
- {
- case Selection::Volume:
- {
+ if (volume->is_modifier)
volume->hover = true;
- break;
- }
- case Selection::Instance:
+ else
{
int object_idx = volume->object_idx();
int instance_idx = volume->instance_idx();
@@ -4017,9 +4071,6 @@ void GLCanvas3D::_update_volumes_hover_state() const
if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx))
v->hover = true;
}
-
- break;
- }
}
}
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index 53551a4728..e81d46f117 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -65,6 +65,37 @@ public:
void set_scale_factor(int height);
};
+
+class ClippingPlane
+{
+ double m_data[4];
+
+public:
+ ClippingPlane()
+ {
+ m_data[0] = 0.0;
+ m_data[1] = 0.0;
+ m_data[2] = 1.0;
+ m_data[3] = 0.0;
+ }
+
+ ClippingPlane(const Vec3d& direction, double offset)
+ {
+ Vec3d norm_dir = direction.normalized();
+ m_data[0] = norm_dir(0);
+ m_data[1] = norm_dir(1);
+ m_data[2] = norm_dir(2);
+ m_data[3] = offset;
+ }
+
+ bool is_active() const { return m_data[3] != DBL_MAX; }
+
+ static ClippingPlane ClipsNothing() { return ClippingPlane(Vec3d(0., 0., 1.), DBL_MAX); }
+
+ const double* get_data() const { return m_data; }
+};
+
+
wxDECLARE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent);
using Vec2dEvent = Event;
@@ -288,32 +319,6 @@ class GLCanvas3D
}
};
-public:
- class ClippingPlane
- {
- double m_data[4];
-
- public:
- ClippingPlane()
- {
- m_data[0] = 0.0;
- m_data[1] = 0.0;
- m_data[2] = 1.0;
- m_data[3] = 0.0;
- }
-
- ClippingPlane(const Vec3d& direction, double offset)
- {
- Vec3d norm_dir = direction.normalized();
- m_data[0] = norm_dir(0);
- m_data[1] = norm_dir(1);
- m_data[2] = norm_dir(2);
- m_data[3] = offset;
- }
-
- const double* get_data() const { return m_data; }
- };
-
private:
struct SlaCap
{
@@ -405,6 +410,7 @@ private:
mutable GLGizmosManager m_gizmos;
mutable GLToolbar m_toolbar;
ClippingPlane m_clipping_planes[2];
+ mutable ClippingPlane m_camera_clipping_plane;
bool m_use_clipping_planes;
mutable SlaCap m_sla_caps[2];
std::string m_sidebar_field;
@@ -579,6 +585,8 @@ public:
void refresh_camera_scene_box() { m_camera.set_scene_box(scene_bounding_box()); }
bool is_mouse_dragging() const { return m_mouse.dragging; }
+ double get_size_proportional_to_max_bed_size(double factor) const;
+
private:
bool _is_shown_on_screen() const;
@@ -605,7 +613,7 @@ private:
#endif // ENABLE_RENDER_SELECTION_CENTER
void _render_warning_texture() const;
void _render_legend_texture() const;
- void _render_volumes(bool fake_colors) const;
+ void _render_volumes_for_picking() const;
void _render_current_gizmo() const;
void _render_gizmos_overlay() const;
void _render_toolbar() const;
diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp
index 144d02476e..842700aef8 100644
--- a/src/slic3r/GUI/GLToolbar.cpp
+++ b/src/slic3r/GUI/GLToolbar.cpp
@@ -21,6 +21,8 @@ wxDEFINE_EVENT(EVT_GLTOOLBAR_ADD, SimpleEvent);
wxDEFINE_EVENT(EVT_GLTOOLBAR_DELETE, SimpleEvent);
wxDEFINE_EVENT(EVT_GLTOOLBAR_DELETE_ALL, SimpleEvent);
wxDEFINE_EVENT(EVT_GLTOOLBAR_ARRANGE, SimpleEvent);
+wxDEFINE_EVENT(EVT_GLTOOLBAR_COPY, SimpleEvent);
+wxDEFINE_EVENT(EVT_GLTOOLBAR_PASTE, SimpleEvent);
wxDEFINE_EVENT(EVT_GLTOOLBAR_MORE, SimpleEvent);
wxDEFINE_EVENT(EVT_GLTOOLBAR_FEWER, SimpleEvent);
wxDEFINE_EVENT(EVT_GLTOOLBAR_SPLIT_OBJECTS, SimpleEvent);
diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp
index 0f8b17e04f..24314d60ff 100644
--- a/src/slic3r/GUI/GLToolbar.hpp
+++ b/src/slic3r/GUI/GLToolbar.hpp
@@ -20,6 +20,8 @@ wxDECLARE_EVENT(EVT_GLTOOLBAR_ADD, SimpleEvent);
wxDECLARE_EVENT(EVT_GLTOOLBAR_DELETE, SimpleEvent);
wxDECLARE_EVENT(EVT_GLTOOLBAR_DELETE_ALL, SimpleEvent);
wxDECLARE_EVENT(EVT_GLTOOLBAR_ARRANGE, SimpleEvent);
+wxDECLARE_EVENT(EVT_GLTOOLBAR_COPY, SimpleEvent);
+wxDECLARE_EVENT(EVT_GLTOOLBAR_PASTE, SimpleEvent);
wxDECLARE_EVENT(EVT_GLTOOLBAR_MORE, SimpleEvent);
wxDECLARE_EVENT(EVT_GLTOOLBAR_FEWER, SimpleEvent);
wxDECLARE_EVENT(EVT_GLTOOLBAR_SPLIT_OBJECTS, SimpleEvent);
diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp
index c3bc808f40..b9cd2fc98a 100644
--- a/src/slic3r/GUI/GUI_ObjectList.cpp
+++ b/src/slic3r/GUI/GUI_ObjectList.cpp
@@ -61,19 +61,22 @@ ObjectList::ObjectList(wxWindow* parent) :
{
// Fill CATEGORY_ICON
{
+ // Note: `this` isn't passed to create_scaled_bitmap() here because of bugs in the widget,
+ // see note in PresetBundle::load_compatible_bitmaps()
+
// ptFFF
- CATEGORY_ICON[L("Layers and Perimeters")] = create_scaled_bitmap(this, "layers");
- CATEGORY_ICON[L("Infill")] = create_scaled_bitmap(this, "infill");
- CATEGORY_ICON[L("Support material")] = create_scaled_bitmap(this, "support");
- CATEGORY_ICON[L("Speed")] = create_scaled_bitmap(this, "time");
- CATEGORY_ICON[L("Extruders")] = create_scaled_bitmap(this, "funnel");
- CATEGORY_ICON[L("Extrusion Width")] = create_scaled_bitmap(this, "funnel");
-// CATEGORY_ICON[L("Skirt and brim")] = create_scaled_bitmap(this, "skirt+brim");
-// CATEGORY_ICON[L("Speed > Acceleration")] = create_scaled_bitmap(this, "time");
- CATEGORY_ICON[L("Advanced")] = create_scaled_bitmap(this, "wrench");
- // ptSLA
- CATEGORY_ICON[L("Supports")] = create_scaled_bitmap(this, "sla_supports");
- CATEGORY_ICON[L("Pad")] = create_scaled_bitmap(this, "brick.png");
+ CATEGORY_ICON[L("Layers and Perimeters")] = create_scaled_bitmap(nullptr, "layers");
+ CATEGORY_ICON[L("Infill")] = create_scaled_bitmap(nullptr, "infill");
+ CATEGORY_ICON[L("Support material")] = create_scaled_bitmap(nullptr, "support");
+ CATEGORY_ICON[L("Speed")] = create_scaled_bitmap(nullptr, "time");
+ CATEGORY_ICON[L("Extruders")] = create_scaled_bitmap(nullptr, "funnel");
+ CATEGORY_ICON[L("Extrusion Width")] = create_scaled_bitmap(nullptr, "funnel");
+// CATEGORY_ICON[L("Skirt and brim")] = create_scaled_bitmap(nullptr, "skirt+brim");
+// CATEGORY_ICON[L("Speed > Acceleration")] = create_scaled_bitmap(nullptr, "time");
+ CATEGORY_ICON[L("Advanced")] = create_scaled_bitmap(nullptr, "wrench");
+ // ptSLA
+ CATEGORY_ICON[L("Supports")] = create_scaled_bitmap(nullptr, "support"/*"sla_supports"*/);
+ CATEGORY_ICON[L("Pad")] = create_scaled_bitmap(nullptr, "pad");
}
// create control
@@ -392,10 +395,10 @@ void ObjectList::update_name_in_model(const wxDataViewItem& item) const
void ObjectList::init_icons()
{
- m_bmp_modifiermesh = create_scaled_bitmap(this, "lambda.png");
- m_bmp_solidmesh = create_scaled_bitmap(this, "object.png");
- m_bmp_support_enforcer = create_scaled_bitmap(this, "support_enforcer_.png");
- m_bmp_support_blocker = create_scaled_bitmap(this, "support_blocker_.png");
+ m_bmp_modifiermesh = create_scaled_bitmap(nullptr, "add_modifier");
+ m_bmp_solidmesh = create_scaled_bitmap(nullptr, "add_part");
+ m_bmp_support_enforcer = create_scaled_bitmap(nullptr, "support_enforcer");
+ m_bmp_support_blocker = create_scaled_bitmap(nullptr, "support_blocker");
m_bmp_vector.reserve(4); // bitmaps for different types of parts
@@ -406,13 +409,13 @@ void ObjectList::init_icons()
m_objects_model->SetVolumeBitmaps(m_bmp_vector);
// init icon for manifold warning
- m_bmp_manifold_warning = create_scaled_bitmap(this, "exclamation_mark_.png");
+ m_bmp_manifold_warning = create_scaled_bitmap(nullptr, "exclamation");
// init bitmap for "Split to sub-objects" context menu
- m_bmp_split = create_scaled_bitmap(this, "split_parts");
+ m_bmp_split = create_scaled_bitmap(nullptr, "split_parts_SMALL");
// init bitmap for "Add Settings" context menu
- m_bmp_cog = create_scaled_bitmap(this, "cog");
+ m_bmp_cog = create_scaled_bitmap(nullptr, "cog");
}
@@ -436,6 +439,66 @@ void ObjectList::selection_changed()
part_selection_changed();
}
+void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& volumes)
+{
+ if ((obj_idx < 0) || ((int)m_objects->size() <= obj_idx))
+ return;
+
+ if (volumes.empty())
+ return;
+
+ ModelObject& model_object = *(*m_objects)[obj_idx];
+ const auto object_item = m_objects_model->GetItemById(obj_idx);
+
+ wxDataViewItemArray items;
+
+ for (const ModelVolume* volume : volumes)
+ {
+ auto vol_item = m_objects_model->AddVolumeChild(object_item, volume->name, volume->type(),
+ volume->config.has("extruder") ? volume->config.option("extruder")->value : 0);
+ auto opt_keys = volume->config.keys();
+ if (!opt_keys.empty() && !((opt_keys.size() == 1) && (opt_keys[0] == "extruder")))
+ select_item(m_objects_model->AddSettingsChild(vol_item));
+
+ items.Add(vol_item);
+ }
+
+ m_parts_changed = true;
+ parts_changed(obj_idx);
+
+ if (items.size() > 1)
+ {
+ m_selection_mode = smVolume;
+ m_last_selected_item = wxDataViewItem(0);
+ }
+
+ select_items(items);
+#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
+ selection_changed();
+#endif //no __WXOSX__ //__WXMSW__
+}
+
+void ObjectList::paste_objects_into_list(const std::vector& object_idxs)
+{
+ if (object_idxs.empty())
+ return;
+
+ wxDataViewItemArray items;
+ for (const size_t object : object_idxs)
+ {
+ add_object_to_list(object);
+ m_parts_changed = true;
+ parts_changed(object);
+
+ items.Add(m_objects_model->GetItemById(object));
+ }
+
+ select_items(items);
+#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
+ selection_changed();
+#endif //no __WXOSX__ //__WXMSW__
+}
+
void ObjectList::OnChar(wxKeyEvent& event)
{
if (event.GetKeyCode() == WXK_BACK){
@@ -1374,9 +1437,7 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
const wxString name = _(L("Generic")) + "-" + _(type_name);
TriangleMesh mesh;
- auto& bed_shape = printer_config().option("bed_shape")->values;
- const auto& sz = BoundingBoxf(bed_shape).size();
- const auto side = 0.1 * std::max(sz(0), sz(1));
+ double side = wxGetApp().plater()->canvas3D()->get_size_proportional_to_max_bed_size(0.1);
if (type_name == "Box")
// Sitting on the print bed, left front front corner at (0, 0).
@@ -1573,11 +1634,11 @@ void ObjectList::split()
for (auto id = 0; id < model_object->volumes.size(); id++) {
const auto vol_item = m_objects_model->AddVolumeChild(parent, from_u8(model_object->volumes[id]->name),
- model_object->volumes[id]->is_modifier() ?
- ModelVolumeType::PARAMETER_MODIFIER : ModelVolumeType::MODEL_PART,
- model_object->volumes[id]->config.has("extruder") ?
- model_object->volumes[id]->config.option("extruder")->value : 0,
- false);
+ model_object->volumes[id]->is_modifier() ?
+ ModelVolumeType::PARAMETER_MODIFIER : ModelVolumeType::MODEL_PART,
+ model_object->volumes[id]->config.has("extruder") ?
+ model_object->volumes[id]->config.option("extruder")->value : 0,
+ false);
// add settings to the part, if it has those
auto opt_keys = model_object->volumes[id]->config.keys();
if ( !(opt_keys.size() == 1 && opt_keys[0] == "extruder") ) {
@@ -2132,6 +2193,7 @@ void ObjectList::update_selections_on_canvas()
add_to_selection(item, selection, instance_idx, false);
wxGetApp().plater()->canvas3D()->update_gizmos_on_off_state();
+ wxGetApp().plater()->canvas3D()->render();
}
void ObjectList::select_item(const wxDataViewItem& item)
diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp
index c3d76f656a..d1651748e1 100644
--- a/src/slic3r/GUI/GUI_ObjectList.hpp
+++ b/src/slic3r/GUI/GUI_ObjectList.hpp
@@ -31,6 +31,8 @@ typedef std::map> FreqSettingsBundle;
// category -> vector ( option ; label )
typedef std::map< std::string, std::vector< std::pair > > settings_menu_hierarchy;
+typedef std::vector ModelVolumePtrs;
+
namespace GUI {
wxDECLARE_EVENT(EVT_OBJ_LIST_OBJECT_SELECT, SimpleEvent);
@@ -286,6 +288,9 @@ public:
void fix_through_netfabb() const;
void update_item_error_icon(const int obj_idx, int vol_idx) const ;
+ void paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& volumes);
+ void paste_objects_into_list(const std::vector& object_idxs);
+
void rescale();
private:
diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp
index 438e9d236a..206253451a 100644
--- a/src/slic3r/GUI/GUI_Preview.cpp
+++ b/src/slic3r/GUI/GUI_Preview.cpp
@@ -818,8 +818,8 @@ void Preview::on_sliders_scroll_changed(wxEvent& event)
}
else if (tech == ptSLA)
{
- m_canvas->set_clipping_plane(0, GLCanvas3D::ClippingPlane(Vec3d::UnitZ(), -m_slider->GetLowerValueD()));
- m_canvas->set_clipping_plane(1, GLCanvas3D::ClippingPlane(-Vec3d::UnitZ(), m_slider->GetHigherValueD()));
+ m_canvas->set_clipping_plane(0, ClippingPlane(Vec3d::UnitZ(), -m_slider->GetLowerValueD()));
+ m_canvas->set_clipping_plane(1, ClippingPlane(-Vec3d::UnitZ(), m_slider->GetHigherValueD()));
m_canvas->set_use_clipping_planes(m_slider->GetHigherValue() != 0);
m_canvas_widget->Refresh();
}
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
index 67abfdd35a..aa88f9dd59 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
@@ -10,6 +10,7 @@
#include "slic3r/GUI/GUI_ObjectSettings.hpp"
#include "slic3r/GUI/GUI_ObjectList.hpp"
#include "slic3r/GUI/PresetBundle.hpp"
+#include "libslic3r/Tesselate.hpp"
namespace Slic3r {
@@ -47,24 +48,24 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const S
{
if (selection.is_empty()) {
m_model_object = nullptr;
- m_old_model_object = nullptr;
return;
}
- m_old_model_object = m_model_object;
m_model_object = model_object;
m_active_instance = selection.get_instance_idx();
if (model_object && selection.is_from_single_instance())
{
+ // Cache the bb - it's needed for dealing with the clipping plane quite often
+ // It could be done inside update_mesh but one has to account for scaling of the instance.
+ //FIXME calling ModelObject::instance_bounding_box() is expensive!
+ m_active_instance_bb_radius = m_model_object->instance_bounding_box(m_active_instance).radius();
+
if (is_mesh_update_necessary()) {
update_mesh();
editing_mode_reload_cache();
}
- if (m_model_object != m_old_model_object)
- m_editing_mode = false;
-
if (m_editing_mode_cache.empty() && m_model_object->sla_points_status != sla::PointsStatus::UserModified)
get_data_from_backend();
@@ -88,12 +89,75 @@ void GLGizmoSlaSupports::on_render(const Selection& selection) const
glsafe(::glEnable(GL_BLEND));
glsafe(::glEnable(GL_DEPTH_TEST));
- render_points(selection, false);
+ // we'll recover current look direction from the modelview matrix (in world coords):
+ Eigen::Matrix modelview_matrix;
+ ::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix.data());
+ Vec3d direction_to_camera(modelview_matrix.data()[2], modelview_matrix.data()[6], modelview_matrix.data()[10]);
+ m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z();
+
+ if (m_quadric != nullptr && selection.is_from_single_instance())
+ render_points(selection, direction_to_camera, false);
+
render_selection_rectangle();
+ render_clipping_plane(selection, direction_to_camera);
glsafe(::glDisable(GL_BLEND));
}
+
+
+void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection, const Vec3d& direction_to_camera) const
+{
+ if (m_clipping_plane_distance == 0.f)
+ return;
+
+ const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
+ Transform3f instance_matrix = vol->get_instance_transformation().get_matrix().cast();
+ Transform3f instance_matrix_no_translation_no_scaling = vol->get_instance_transformation().get_matrix(true,false,true).cast();
+ Vec3f scaling = vol->get_instance_scaling_factor().cast();
+
+ Vec3f up_noscale = instance_matrix_no_translation_no_scaling.inverse() * direction_to_camera.cast();
+ Vec3f up = Vec3f(up_noscale(0)*scaling(0), up_noscale(1)*scaling(1), up_noscale(2)*scaling(2));
+ float height_mesh = (m_active_instance_bb_radius - m_clipping_plane_distance * 2*m_active_instance_bb_radius) * (up_noscale.norm()/up.norm());
+
+ if (m_clipping_plane_distance != m_old_clipping_plane_distance
+ || m_old_direction_to_camera != direction_to_camera) {
+
+ std::vector list_of_expolys;
+ if (! m_tms) {
+ m_tms.reset(new TriangleMeshSlicer);
+ m_tms->init(const_cast(&m_mesh), [](){});
+ }
+
+ m_tms->set_up_direction(up);
+ m_tms->slice(std::vector{height_mesh}, 0.f, &list_of_expolys, [](){});
+ m_triangles = triangulate_expolygons_2f(list_of_expolys[0]);
+
+ m_old_direction_to_camera = direction_to_camera;
+ m_old_clipping_plane_distance = m_clipping_plane_distance;
+ }
+
+ if (! m_triangles.empty()) {
+ ::glPushMatrix();
+ ::glTranslated(0.0, 0.0, m_z_shift);
+ ::glMultMatrixf(instance_matrix.data());
+ Eigen::Quaternionf q;
+ q.setFromTwoVectors(Vec3f::UnitZ(), up);
+ Eigen::AngleAxisf aa(q);
+ ::glRotatef(aa.angle() * (180./M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2));
+ ::glTranslatef(0.f, 0.f, -0.001f); // to make sure the cut is safely beyond the near clipping plane
+ ::glColor3f(1.0f, 0.37f, 0.0f);
+ ::glBegin(GL_TRIANGLES);
+ ::glColor3f(1.0f, 0.37f, 0.0f);
+ for (const Vec2f& point : m_triangles)
+ ::glVertex3f(point(0), point(1), height_mesh);
+ ::glEnd();
+ ::glPopMatrix();
+ }
+}
+
+
+
void GLGizmoSlaSupports::render_selection_rectangle() const
{
if (m_selection_rectangle_status == srOff)
@@ -143,24 +207,25 @@ void GLGizmoSlaSupports::on_render_for_picking(const Selection& selection) const
{
glsafe(::glEnable(GL_DEPTH_TEST));
- render_points(selection, true);
+ // we'll recover current look direction from the modelview matrix (in world coords):
+ Eigen::Matrix modelview_matrix;
+ ::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix.data());
+ Vec3d direction_to_camera(modelview_matrix.data()[2], modelview_matrix.data()[6], modelview_matrix.data()[10]);
+
+ render_points(selection, direction_to_camera, true);
}
-void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) const
+void GLGizmoSlaSupports::render_points(const Selection& selection, const Vec3d& direction_to_camera, bool picking) const
{
- if (m_quadric == nullptr || !selection.is_from_single_instance())
- return;
-
if (!picking)
glsafe(::glEnable(GL_LIGHTING));
const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
- double z_shift = vol->get_sla_shift_z();
const Transform3d& instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse();
const Transform3d& instance_matrix = vol->get_instance_transformation().get_matrix();
glsafe(::glPushMatrix());
- glsafe(::glTranslated(0.0, 0.0, z_shift));
+ glsafe(::glTranslated(0.0, 0.0, m_z_shift));
glsafe(::glMultMatrixd(instance_matrix.data()));
float render_color[3];
@@ -169,6 +234,9 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
const sla::SupportPoint& support_point = m_editing_mode_cache[i].support_point;
const bool& point_selected = m_editing_mode_cache[i].selected;
+ if (is_point_clipped(support_point.pos.cast(), direction_to_camera))
+ continue;
+
// First decide about the color of the point.
if (picking) {
std::array color = picking_color_component(i);
@@ -238,13 +306,25 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
glsafe(::glPopMatrix());
}
+
+
+bool GLGizmoSlaSupports::is_point_clipped(const Vec3d& point, const Vec3d& direction_to_camera) const
+{
+ if (m_clipping_plane_distance == 0.f)
+ return false;
+
+ Vec3d transformed_point = m_model_object->instances.front()->get_transformation().get_matrix() * point;
+ transformed_point(2) += m_z_shift;
+ return direction_to_camera.dot(m_model_object->instances[m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift)) + m_active_instance_bb_radius
+ - m_clipping_plane_distance * 2*m_active_instance_bb_radius < direction_to_camera.dot(transformed_point);
+}
+
+
+
bool GLGizmoSlaSupports::is_mesh_update_necessary() const
{
return ((m_state == On) && (m_model_object != nullptr) && !m_model_object->instances.empty())
- && ((m_model_object != m_old_model_object) || m_V.size()==0);
-
- //if (m_state != On || !m_model_object || m_model_object->instances.empty() || ! m_instance_matrix.isApprox(m_source_data.matrix))
- // return false;
+ && ((m_model_object->id() != m_current_mesh_model_id) || m_V.size()==0);
}
void GLGizmoSlaSupports::update_mesh()
@@ -252,10 +332,9 @@ void GLGizmoSlaSupports::update_mesh()
wxBusyCursor wait;
Eigen::MatrixXf& V = m_V;
Eigen::MatrixXi& F = m_F;
- // Composite mesh of all instances in the world coordinate system.
// This mesh does not account for the possible Z up SLA offset.
- TriangleMesh mesh = m_model_object->raw_mesh();
- const stl_file& stl = mesh.stl;
+ m_mesh = m_model_object->raw_mesh();
+ const stl_file& stl = m_mesh.stl;
V.resize(3 * stl.stats.number_of_facets, 3);
F.resize(stl.stats.number_of_facets, 3);
for (unsigned int i=0; iid();
+ m_editing_mode = false;
m_AABB = igl::AABB();
m_AABB.init(m_V, m_F);
}
+// Unprojects the mouse position on the mesh and return the hit point and normal of the facet.
+// The function throws if no intersection if found.
std::pair GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos)
{
// if the gizmo doesn't have the V, F structures for igl, calculate them first:
@@ -288,31 +371,53 @@ std::pair GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse
::gluUnProject(mouse_pos(0), viewport[3] - mouse_pos(1), 0.f, modelview_matrix.data(), projection_matrix.data(), viewport.data(), &point1(0), &point1(1), &point1(2));
::gluUnProject(mouse_pos(0), viewport[3] - mouse_pos(1), 1.f, modelview_matrix.data(), projection_matrix.data(), viewport.data(), &point2(0), &point2(1), &point2(2));
- igl::Hit hit;
+ std::vector hits;
const Selection& selection = m_parent.get_selection();
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
- double z_offset = volume->get_sla_shift_z();
- point1(2) -= z_offset;
- point2(2) -= z_offset;
+ // we'll recover current look direction from the modelview matrix (in world coords):
+ Vec3d direction_to_camera(modelview_matrix.data()[2], modelview_matrix.data()[6], modelview_matrix.data()[10]);
+
+ point1(2) -= m_z_shift;
+ point2(2) -= m_z_shift;
Transform3d inv = volume->get_instance_transformation().get_matrix().inverse();
point1 = inv * point1;
point2 = inv * point2;
- if (!m_AABB.intersect_ray(m_V, m_F, point1.cast(), (point2-point1).cast(), hit))
+ if (!m_AABB.intersect_ray(m_V, m_F, point1.cast(), (point2-point1).cast(), hits))
throw std::invalid_argument("unproject_on_mesh(): No intersection found.");
- int fid = hit.id; // facet id
- Vec3f bc(1-hit.u-hit.v, hit.u, hit.v); // barycentric coordinates of the hit
- Vec3f a = (m_V.row(m_F(fid, 1)) - m_V.row(m_F(fid, 0)));
- Vec3f b = (m_V.row(m_F(fid, 2)) - m_V.row(m_F(fid, 0)));
+ std::sort(hits.begin(), hits.end(), [](const igl::Hit& a, const igl::Hit& b) { return a.t < b.t; });
+
+ // Now let's iterate through the points and find the first that is not clipped:
+ unsigned int i=0;
+ Vec3f bc;
+ Vec3f a;
+ Vec3f b;
+ Vec3f result;
+ for (i=0; i(), direction_to_camera))
+ break;
+ }
+
+ if (i==hits.size() || (hits.size()-i) % 2 != 0) {
+ // All hits are either clipped, or there is an odd number of unclipped
+ // hits - meaning the nearest must be from inside the mesh.
+ throw std::invalid_argument("unproject_on_mesh(): No intersection found.");
+ }
// Calculate and return both the point and the facet normal.
return std::make_pair(
- bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2)),
+ result,
a.cross(b)
);
}
@@ -383,36 +488,64 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
const Selection& selection = m_parent.get_selection();
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
- double z_offset = volume->get_sla_shift_z();
// bounding box created from the rectangle corners - will take care of order of the corners
BoundingBox rectangle(Points{Point(m_selection_rectangle_start_corner.cast()), Point(m_selection_rectangle_end_corner.cast())});
- const Transform3d& instance_matrix_no_translation = volume->get_instance_transformation().get_matrix(true);
+ const Transform3d& instance_matrix_no_translation_no_scaling = volume->get_instance_transformation().get_matrix(true,false,true);
+
// we'll recover current look direction from the modelview matrix (in world coords)...
Vec3f direction_to_camera = camera.get_dir_forward().cast();
// ...and transform it to model coords.
- direction_to_camera = (instance_matrix_no_translation.inverse().cast() * direction_to_camera).normalized().eval();
+ Vec3f direction_to_camera_mesh = (instance_matrix_no_translation_no_scaling.inverse().cast() * direction_to_camera).normalized().eval();
+ Vec3f scaling = volume->get_instance_scaling_factor().cast();
+ direction_to_camera_mesh = Vec3f(direction_to_camera_mesh(0)*scaling(0), direction_to_camera_mesh(1)*scaling(1), direction_to_camera_mesh(2)*scaling(2));
// Iterate over all points, check if they're in the rectangle and if so, check that they are not obscured by the mesh:
for (unsigned int i=0; i() * support_point.pos;
- pos(2) += z_offset;
+ pos(2) += m_z_shift;
GLdouble out_x, out_y, out_z;
::gluProject((GLdouble)pos(0), (GLdouble)pos(1), (GLdouble)pos(2), (GLdouble*)modelview_matrix.data(), (GLdouble*)projection_matrix.data(), (GLint*)viewport.data(), &out_x, &out_y, &out_z);
out_y = m_canvas_height - out_y;
- if (rectangle.contains(Point(out_x, out_y))) {
+ if (rectangle.contains(Point(out_x, out_y)) && !is_point_clipped(support_point.pos.cast(), direction_to_camera.cast())) {
bool is_obscured = false;
// Cast a ray in the direction of the camera and look for intersection with the mesh:
std::vector hits;
// Offset the start of the ray to the front of the ball + EPSILON to account for numerical inaccuracies.
- if (m_AABB.intersect_ray(m_V, m_F, support_point.pos + direction_to_camera * (support_point.head_front_radius + EPSILON), direction_to_camera, hits))
+ if (m_AABB.intersect_ray(m_V, m_F, support_point.pos + direction_to_camera_mesh * (support_point.head_front_radius + EPSILON), direction_to_camera_mesh, hits)) {
+ std::sort(hits.begin(), hits.end(), [](const igl::Hit& h1, const igl::Hit& h2) { return h1.t < h2.t; });
+
+ if (m_clipping_plane_distance != 0.f) {
+ // If the closest hit facet normal points in the same direction as the ray,
+ // we are looking through the mesh and should therefore discard the point:
+ int fid = hits.front().id; // facet id
+ Vec3f a = (m_V.row(m_F(fid, 1)) - m_V.row(m_F(fid, 0)));
+ Vec3f b = (m_V.row(m_F(fid, 2)) - m_V.row(m_F(fid, 0)));
+ if ((a.cross(b)).dot(direction_to_camera_mesh) > 0.f)
+ is_obscured = true;
+
+ // Eradicate all hits that are on clipped surfaces:
+ for (unsigned int j=0; j(), direction_to_camera.cast())) {
+ hits.erase(hits.begin()+j);
+ --j;
+ }
+ }
+ }
+
// FIXME: the intersection could in theory be behind the camera, but as of now we only have camera direction.
// Also, the threshold is in mesh coordinates, not in actual dimensions.
- if (hits.size() > 1 || hits.front().t > 0.001f)
+ if (!hits.empty())
is_obscured = true;
+ }
if (!is_obscured) {
if (m_selection_rectangle_status == srDeselect)
@@ -570,6 +703,64 @@ void GLGizmoSlaSupports::update_cache_entry_normal(unsigned int i) const
+
+ClippingPlane GLGizmoSlaSupports::get_sla_clipping_plane() const
+{
+ if (!m_model_object || m_state == Off)
+ return ClippingPlane::ClipsNothing();
+
+ Eigen::Matrix modelview_matrix;
+ ::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix.data());
+
+ // we'll recover current look direction from the modelview matrix (in world coords):
+ Vec3d direction_to_camera(modelview_matrix.data()[2], modelview_matrix.data()[6], modelview_matrix.data()[10]);
+ float dist = direction_to_camera.dot(m_model_object->instances[m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift));
+
+ return ClippingPlane(-direction_to_camera.normalized(),(dist - (-m_active_instance_bb_radius) - m_clipping_plane_distance * 2*m_active_instance_bb_radius));
+}
+
+
+/*
+void GLGizmoSlaSupports::find_intersecting_facets(const igl::AABB* aabb, const Vec3f& normal, double offset, std::vector& idxs) const
+{
+ if (aabb->is_leaf()) { // this is a facet
+ // corner.dot(normal) - offset
+ idxs.push_back(aabb->m_primitive);
+ }
+ else { // not a leaf
+ using CornerType = Eigen::AlignedBox::CornerType;
+ bool sign = std::signbit(offset - normal.dot(aabb->m_box.corner(CornerType(0))));
+ for (unsigned int i=1; i<8; ++i)
+ if (std::signbit(offset - normal.dot(aabb->m_box.corner(CornerType(i)))) != sign) {
+ find_intersecting_facets(aabb->m_left, normal, offset, idxs);
+ find_intersecting_facets(aabb->m_right, normal, offset, idxs);
+ }
+ }
+}
+
+
+
+void GLGizmoSlaSupports::make_line_segments() const
+{
+ TriangleMeshSlicer tms(&m_model_object->volumes.front()->mesh);
+ Vec3f normal(0.f, 1.f, 1.f);
+ double d = 0.;
+
+ std::vector lines;
+ find_intersections(&m_AABB, normal, d, lines);
+ ExPolygons expolys;
+ tms.make_expolygons_simple(lines, &expolys);
+
+ SVG svg("slice_loops.svg", get_extents(expolys));
+ svg.draw(expolys);
+ //for (const IntersectionLine &l : lines[i])
+ // svg.draw(l, "red", 0);
+ //svg.draw_outline(expolygons, "black", "blue", 0);
+ svg.Close();
+}
+*/
+
+
void GLGizmoSlaSupports::on_render_input_window(float x, float y, float bottom_limit, const Selection& selection)
{
if (!m_model_object)
@@ -580,7 +771,7 @@ void GLGizmoSlaSupports::on_render_input_window(float x, float y, float bottom_l
RENDER_AGAIN:
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
- const ImVec2 window_size(m_imgui->scaled(17.f, 18.f));
+ const ImVec2 window_size(m_imgui->scaled(17.f, 20.f));
ImGui::SetNextWindowPos(ImVec2(x, y - std::max(0.f, y+window_size.y-bottom_limit) ));
ImGui::SetNextWindowSize(ImVec2(window_size));
@@ -687,6 +878,13 @@ RENDER_AGAIN:
(m_model_object->sla_points_status == sla::PointsStatus::Generating ? "Generation in progress..." : "UNKNOWN STATUS"))));
}
+
+ // Following is rendered in both editing and non-editing mode:
+ m_imgui->text("Clipping of view: ");
+ ImGui::SameLine();
+ ImGui::PushItemWidth(150.0f);
+ bool value_changed = ImGui::SliderFloat(" ", &m_clipping_plane_distance, 0.f, 1.f, "%.2f");
+
m_imgui->end();
if (m_editing_mode != m_old_editing_state) { // user toggled between editing/non-editing mode
@@ -740,12 +938,7 @@ std::string GLGizmoSlaSupports::on_get_name() const
void GLGizmoSlaSupports::on_set_state()
{
- // Following is called through CallAfter, because otherwise there was a problem
- // on OSX with the wxMessageDialog being shown several times when clicked into.
-
- wxGetApp().CallAfter([this]() {
if (m_state == On && m_old_state != On) { // the gizmo was just turned on
-
if (is_mesh_update_necessary())
update_mesh();
@@ -762,23 +955,32 @@ void GLGizmoSlaSupports::on_set_state()
m_new_point_head_diameter = static_cast(cfg.option("support_head_front_diameter"))->value;
}
if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off
- if (m_model_object) {
- if (m_unsaved_changes) {
- wxMessageDialog dlg(GUI::wxGetApp().mainframe, _(L("Do you want to save your manually edited support points ?\n")),
- _(L("Save changes?")), wxICON_QUESTION | wxYES | wxNO);
- if (dlg.ShowModal() == wxID_YES)
- editing_mode_apply_changes();
- else
- editing_mode_discard_changes();
+ wxGetApp().CallAfter([this]() {
+ // Following is called through CallAfter, because otherwise there was a problem
+ // on OSX with the wxMessageDialog being shown several times when clicked into.
+ if (m_model_object) {
+ if (m_unsaved_changes) {
+ wxMessageDialog dlg(GUI::wxGetApp().mainframe, _(L("Do you want to save your manually edited support points ?\n")),
+ _(L("Save changes?")), wxICON_QUESTION | wxYES | wxNO);
+ if (dlg.ShowModal() == wxID_YES)
+ editing_mode_apply_changes();
+ else
+ editing_mode_discard_changes();
+ }
}
- }
-
- m_parent.toggle_model_objects_visibility(true);
- m_editing_mode = false; // so it is not active next time the gizmo opens
- m_editing_mode_cache.clear();
+ m_parent.toggle_model_objects_visibility(true);
+ m_editing_mode = false; // so it is not active next time the gizmo opens
+ m_editing_mode_cache.clear();
+ m_clipping_plane_distance = 0.f;
+ // Release copy of the mesh, triangle slicer and the AABB spatial search structure.
+ m_mesh.clear();
+ m_AABB.deinit();
+ m_V = Eigen::MatrixXf();
+ m_F = Eigen::MatrixXi();
+ m_tms.reset(nullptr);
+ });
}
m_old_state = m_state;
- });
}
@@ -827,11 +1029,19 @@ void GLGizmoSlaSupports::unselect_point(int i)
void GLGizmoSlaSupports::editing_mode_discard_changes()
{
- m_editing_mode_cache.clear();
- for (const sla::SupportPoint& point : m_model_object->sla_support_points)
- m_editing_mode_cache.emplace_back(point, false);
- m_editing_mode = false;
- m_unsaved_changes = false;
+ // If the points were autogenerated, they may not be on the ModelObject yet.
+ // Because the user probably messed with the cache, we will get the data
+ // from the backend again.
+
+ if (m_model_object->sla_points_status == sla::PointsStatus::AutoGenerated)
+ get_data_from_backend();
+ else {
+ m_editing_mode_cache.clear();
+ for (const sla::SupportPoint& point : m_model_object->sla_support_points)
+ m_editing_mode_cache.emplace_back(point, false);
+ }
+ m_editing_mode = false;
+ m_unsaved_changes = false;
}
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
index c8abf15f22..c74559e2fc 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
@@ -17,12 +17,17 @@ namespace Slic3r {
namespace GUI {
+class ClippingPlane;
+
+
class GLGizmoSlaSupports : public GLGizmoBase
{
private:
ModelObject* m_model_object = nullptr;
- ModelObject* m_old_model_object = nullptr;
+ ModelID m_current_mesh_model_id = 0;
int m_active_instance = -1;
+ float m_active_instance_bb_radius; // to cache the bb
+ mutable float m_z_shift = 0.f;
std::pair unproject_on_mesh(const Vec2d& mouse_pos);
const float RenderPointScale = 1.f;
@@ -31,6 +36,8 @@ private:
Eigen::MatrixXf m_V; // vertices
Eigen::MatrixXi m_F; // facets indices
igl::AABB m_AABB;
+ TriangleMesh m_mesh;
+ mutable std::vector m_triangles;
class CacheEntry {
public:
@@ -52,7 +59,7 @@ public:
void set_sla_support_data(ModelObject* model_object, const Selection& selection);
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
void delete_selected_points(bool force = false);
- std::pair get_sla_clipping_plane() const;
+ ClippingPlane get_sla_clipping_plane() const;
private:
bool on_init();
@@ -61,7 +68,8 @@ private:
virtual void on_render_for_picking(const Selection& selection) const;
void render_selection_rectangle() const;
- void render_points(const Selection& selection, bool picking = false) const;
+ void render_points(const Selection& selection, const Vec3d& direction_to_camera, bool picking = false) const;
+ void render_clipping_plane(const Selection& selection, const Vec3d& direction_to_camera) const;
bool is_mesh_update_necessary() const;
void update_mesh();
void update_cache_entry_normal(unsigned int i) const;
@@ -74,6 +82,8 @@ private:
float m_density = 100.f;
mutable std::vector m_editing_mode_cache; // a support point and whether it is currently selected
float m_clipping_plane_distance = 0.f;
+ mutable float m_old_clipping_plane_distance = 0.f;
+ mutable Vec3d m_old_direction_to_camera;
enum SelectionRectangleStatus {
srOff = 0,
@@ -90,7 +100,11 @@ private:
int m_canvas_width;
int m_canvas_height;
+ mutable std::unique_ptr m_tms;
+
std::vector get_config_options(const std::vector& keys) const;
+ bool is_point_clipped(const Vec3d& point, const Vec3d& direction_to_camera) const;
+ void find_intersecting_facets(const igl::AABB* aabb, const Vec3f& normal, double offset, std::vector& out) const;
// Methods that do the model_object and editing cache synchronization,
// editing mode selection, etc:
diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
index ad2d786ffc..cb2d6faedf 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
@@ -467,6 +467,19 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p
return false;
}
+ClippingPlane GLGizmosManager::get_sla_clipping_plane() const
+{
+ if (!m_enabled || m_current != SlaSupports)
+ return ClippingPlane::ClipsNothing();
+
+ GizmosMap::const_iterator it = m_gizmos.find(SlaSupports);
+ if (it != m_gizmos.end())
+ return reinterpret_cast(it->second)->get_sla_clipping_plane();
+
+ return ClippingPlane::ClipsNothing();
+}
+
+
void GLGizmosManager::render_current_gizmo(const Selection& selection) const
{
if (!m_enabled)
@@ -713,7 +726,12 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt, GLCanvas3D& canvas)
{
switch (keyCode)
{
+#ifdef __APPLE__
+ case 'a':
+ case 'A':
+#else /* __APPLE__ */
case WXK_CONTROL_A:
+#endif /* __APPLE__ */
{
// Sla gizmo selects all support points
if ((m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::SelectAll))
diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp
index 16e9025225..f7a1a980e6 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp
@@ -13,6 +13,7 @@ namespace GUI {
class Selection;
class GLGizmoBase;
class GLCanvas3D;
+class ClippingPlane;
class Rect
{
@@ -146,7 +147,7 @@ public:
void set_sla_support_data(ModelObject* model_object, const Selection& selection);
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position = Vec2d::Zero(), bool shift_down = false, bool alt_down = false, bool control_down = false);
-
+ ClippingPlane get_sla_clipping_plane() const;
void render_current_gizmo(const Selection& selection) const;
void render_current_gizmo_for_picking_pass(const Selection& selection) const;
diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp
index 5f34343905..fc49514732 100644
--- a/src/slic3r/GUI/MainFrame.cpp
+++ b/src/slic3r/GUI/MainFrame.cpp
@@ -321,7 +321,7 @@ void MainFrame::init_menubar()
wxMenu* fileMenu = new wxMenu;
{
wxMenuItem* item_open = append_menu_item(fileMenu, wxID_ANY, _(L("&Open Project")) + dots + "\tCtrl+O", _(L("Open a project file")),
- [this](wxCommandEvent&) { if (m_plater) m_plater->load_project(); }, "brick_add.png");
+ [this](wxCommandEvent&) { if (m_plater) m_plater->load_project(); }, "open");
wxMenuItem* item_save = append_menu_item(fileMenu, wxID_ANY, _(L("&Save Project")) + "\tCtrl+S", _(L("Save current project file")),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(into_path(m_plater->get_project_filename())); }, "save");
wxMenuItem* item_save_as = append_menu_item(fileMenu, wxID_ANY, _(L("Save Project &as")) + dots + "\tCtrl+Alt+S", _(L("Save current project file as")),
@@ -331,30 +331,30 @@ void MainFrame::init_menubar()
wxMenu* import_menu = new wxMenu();
wxMenuItem* item_import_model = append_menu_item(import_menu, wxID_ANY, _(L("Import STL/OBJ/AM&F/3MF")) + dots + "\tCtrl+I", _(L("Load a model")),
- [this](wxCommandEvent&) { if (m_plater) m_plater->add_model(); }, "brick_add.png");
+ [this](wxCommandEvent&) { if (m_plater) m_plater->add_model(); }, "import_plater");
import_menu->AppendSeparator();
append_menu_item(import_menu, wxID_ANY, _(L("Import &Config")) + dots + "\tCtrl+L", _(L("Load exported configuration file")),
- [this](wxCommandEvent&) { load_config_file(); }, "plugin_add.png");
+ [this](wxCommandEvent&) { load_config_file(); }, "import_config");
append_menu_item(import_menu, wxID_ANY, _(L("Import Config from &project")) + dots +"\tCtrl+Alt+L", _(L("Load configuration from project file")),
- [this](wxCommandEvent&) { if (m_plater) m_plater->extract_config_from_project(); }, "plugin_add.png");
+ [this](wxCommandEvent&) { if (m_plater) m_plater->extract_config_from_project(); }, "import_config");
import_menu->AppendSeparator();
append_menu_item(import_menu, wxID_ANY, _(L("Import Config &Bundle")) + dots, _(L("Load presets from a bundle")),
- [this](wxCommandEvent&) { load_configbundle(); }, "lorry_add.png");
+ [this](wxCommandEvent&) { load_configbundle(); }, "import_config_bundle");
append_submenu(fileMenu, import_menu, wxID_ANY, _(L("&Import")), "");
wxMenu* export_menu = new wxMenu();
wxMenuItem* item_export_gcode = append_menu_item(export_menu, wxID_ANY, _(L("Export &G-code")) + dots +"\tCtrl+G", _(L("Export current plate as G-code")),
- [this](wxCommandEvent&) { if (m_plater) m_plater->export_gcode(); }, "cog_go.png");
+ [this](wxCommandEvent&) { if (m_plater) m_plater->export_gcode(); }, "export_gcode");
export_menu->AppendSeparator();
wxMenuItem* item_export_stl = append_menu_item(export_menu, wxID_ANY, _(L("Export plate as &STL")) + dots, _(L("Export current plate as STL")),
- [this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(); }, "brick_go.png");
+ [this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(); }, "export_plater");
wxMenuItem* item_export_amf = append_menu_item(export_menu, wxID_ANY, _(L("Export plate as &AMF")) + dots, _(L("Export current plate as AMF")),
- [this](wxCommandEvent&) { if (m_plater) m_plater->export_amf(); }, "brick_go.png");
+ [this](wxCommandEvent&) { if (m_plater) m_plater->export_amf(); }, "export_plater");
export_menu->AppendSeparator();
append_menu_item(export_menu, wxID_ANY, _(L("Export &Config")) +dots +"\tCtrl+E", _(L("Export current configuration to file")),
- [this](wxCommandEvent&) { export_config(); }, "plugin_go.png");
+ [this](wxCommandEvent&) { export_config(); }, "export_config");
append_menu_item(export_menu, wxID_ANY, _(L("Export Config &Bundle")) + dots, _(L("Export all presets to file")),
- [this](wxCommandEvent&) { export_configbundle(); }, "lorry_go.png");
+ [this](wxCommandEvent&) { export_configbundle(); }, "export_config_bundle");
append_submenu(fileMenu, export_menu, wxID_ANY, _(L("&Export")), "");
fileMenu->AppendSeparator();
@@ -382,10 +382,10 @@ void MainFrame::init_menubar()
fileMenu->AppendSeparator();
#endif
m_menu_item_reslice_now = append_menu_item(fileMenu, wxID_ANY, _(L("(Re)Slice &Now")) + "\tCtrl+R", _(L("Start new slicing process")),
- [this](wxCommandEvent&) { reslice_now(); }, "shape_handles.png");
+ [this](wxCommandEvent&) { reslice_now(); }, "re_slice");
fileMenu->AppendSeparator();
append_menu_item(fileMenu, wxID_ANY, _(L("&Repair STL file")) + dots, _(L("Automatically repair an STL file")),
- [this](wxCommandEvent&) { repair_stl(); }, "wrench.png");
+ [this](wxCommandEvent&) { repair_stl(); }, "wrench");
fileMenu->AppendSeparator();
append_menu_item(fileMenu, wxID_EXIT, _(L("&Quit")), _(L("Quit Slic3r")),
[this](wxCommandEvent&) { Close(false); });
@@ -425,13 +425,22 @@ void MainFrame::init_menubar()
[this](wxCommandEvent&) { m_plater->select_all(); }, "");
editMenu->AppendSeparator();
wxMenuItem* item_delete_sel = append_menu_item(editMenu, wxID_ANY, _(L("&Delete selected")) + sep + hotkey_delete, _(L("Deletes the current selection")),
- [this](wxCommandEvent&) { m_plater->remove_selected(); }, "");
+ [this](wxCommandEvent&) { m_plater->remove_selected(); }, "remove_menu");
wxMenuItem* item_delete_all = append_menu_item(editMenu, wxID_ANY, _(L("Delete &all")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + hotkey_delete, _(L("Deletes all objects")),
- [this](wxCommandEvent&) { m_plater->reset(); }, "");
+ [this](wxCommandEvent&) { m_plater->reset(); }, "delete_all_menu");
+
+ editMenu->AppendSeparator();
+
+ wxMenuItem* item_copy = append_menu_item(editMenu, wxID_ANY, _(L("&Copy")) + "\tCtrl+C", _(L("Copy selection to clipboard")),
+ [this](wxCommandEvent&) { m_plater->copy_selection_to_clipboard(); }, "copy_menu");
+ wxMenuItem* item_paste = append_menu_item(editMenu, wxID_ANY, _(L("&Paste")) + "\tCtrl+V", _(L("Paste clipboard")),
+ [this](wxCommandEvent&) { m_plater->paste_from_clipboard(); }, "paste_menu");
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_select()); }, item_select_all->GetId());
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_delete()); }, item_delete_sel->GetId());
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_delete_all()); }, item_delete_all->GetId());
+ Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(m_plater->can_copy()); }, item_copy->GetId());
+ Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(m_plater->can_paste()); }, item_paste->GetId());
}
// Window menu
@@ -440,7 +449,7 @@ void MainFrame::init_menubar()
size_t tab_offset = 0;
if (m_plater) {
append_menu_item(windowMenu, wxID_HIGHEST + 1, _(L("&Plater Tab")) + "\tCtrl+1", _(L("Show the plater")),
- [this](wxCommandEvent&) { select_tab(0); }, "application_view_tile.png");
+ [this](wxCommandEvent&) { select_tab(0); }, "plater");
tab_offset += 1;
}
if (tab_offset > 0) {
@@ -455,9 +464,9 @@ void MainFrame::init_menubar()
if (m_plater) {
windowMenu->AppendSeparator();
wxMenuItem* item_3d = append_menu_item(windowMenu, wxID_HIGHEST + 5, _(L("3&D")) + "\tCtrl+5", _(L("Show the 3D editing view")),
- [this](wxCommandEvent&) { m_plater->select_view_3D("3D"); }, "");
+ [this](wxCommandEvent&) { m_plater->select_view_3D("3D"); }, "editor_menu");
wxMenuItem* item_preview = append_menu_item(windowMenu, wxID_HIGHEST + 6, _(L("Pre&view")) + "\tCtrl+6", _(L("Show the 3D slices preview")),
- [this](wxCommandEvent&) { m_plater->select_view_3D("Preview"); }, "");
+ [this](wxCommandEvent&) { m_plater->select_view_3D("Preview"); }, "preview_menu");
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_3d->GetId());
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_preview->GetId());
@@ -478,7 +487,7 @@ void MainFrame::init_menubar()
windowMenu->AppendSeparator();
append_menu_item(windowMenu, wxID_ANY, _(L("Print &Host Upload Queue")) + "\tCtrl+J", _(L("Display the Print Host Upload Queue window")),
- [this](wxCommandEvent&) { m_printhost_queue_dlg->Show(); }, "arrow_up.png");
+ [this](wxCommandEvent&) { m_printhost_queue_dlg->Show(); }, "upload_queue");
}
// View menu
@@ -590,7 +599,7 @@ void MainFrame::quick_slice(const int qs)
dlg->ShowModal();
return;
}
- if (std::ifstream(m_qs_last_input_file.char_str())) {
+ if (std::ifstream(m_qs_last_input_file.ToUTF8().data())) {
auto dlg = new wxMessageDialog(this, _(L("Previously sliced file ("))+m_qs_last_input_file+_(L(") not found.")),
_(L("File Not Found")), wxICON_ERROR | wxOK);
dlg->ShowModal();
@@ -705,24 +714,23 @@ void MainFrame::repair_stl()
dlg->Destroy();
}
- auto output_file = input_file;
+ wxString output_file = input_file;
{
-// output_file = ~s / \.[sS][tT][lL]$ / _fixed.obj / ;
auto dlg = new wxFileDialog( this, L("Save OBJ file (less prone to coordinate errors than STL) as:"),
- get_dir_name(output_file), get_base_name(output_file),
+ get_dir_name(output_file), get_base_name(output_file, ".obj"),
file_wildcards(FT_OBJ), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if (dlg->ShowModal() != wxID_OK) {
dlg->Destroy();
- return /*undef*/;
+ return;
}
output_file = dlg->GetPath();
dlg->Destroy();
}
auto tmesh = new Slic3r::TriangleMesh();
- tmesh->ReadSTLFile(input_file.char_str());
+ tmesh->ReadSTLFile(input_file.ToUTF8().data());
tmesh->repair();
- tmesh->WriteOBJFile(output_file.char_str());
+ tmesh->WriteOBJFile(output_file.ToUTF8().data());
Slic3r::GUI::show_info(this, L("Your file was repaired."), L("Repair"));
}
@@ -962,9 +970,12 @@ void MainFrame::update_ui_from_settings()
tab->update_ui_from_settings();
}
-std::string MainFrame::get_base_name(const wxString &full_name) const
+std::string MainFrame::get_base_name(const wxString &full_name, const char *extension) const
{
- return boost::filesystem::path(full_name.wx_str()).filename().string();
+ boost::filesystem::path filename = boost::filesystem::path(full_name.wx_str()).filename();
+ if (extension != nullptr)
+ filename = filename.replace_extension(extension);
+ return filename.string();
}
std::string MainFrame::get_dir_name(const wxString &full_name) const
diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp
index 625e70b83b..a5d3a1f6de 100644
--- a/src/slic3r/GUI/MainFrame.hpp
+++ b/src/slic3r/GUI/MainFrame.hpp
@@ -54,7 +54,7 @@ class MainFrame : public DPIFrame
PrintHostQueueDialog *m_printhost_queue_dlg;
- std::string get_base_name(const wxString &full_name) const;
+ std::string get_base_name(const wxString &full_name, const char *extension = nullptr) const;
std::string get_dir_name(const wxString &full_name) const;
void on_presets_changed(SimpleEvent&);
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 5b7ac86fa0..2224eff5e3 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -144,8 +144,7 @@ ObjectInfo::ObjectInfo(wxWindow *parent) :
info_manifold_text->SetFont(wxGetApp().small_font());
info_manifold = new wxStaticText(parent, wxID_ANY, "");
info_manifold->SetFont(wxGetApp().small_font());
- wxBitmap bitmap(GUI::from_u8(Slic3r::var("error.png")), wxBITMAP_TYPE_PNG);
- manifold_warning_icon = new wxStaticBitmap(parent, wxID_ANY, bitmap);
+ manifold_warning_icon = new wxStaticBitmap(parent, wxID_ANY, create_scaled_bitmap(parent, "exclamation"));
auto *sizer_manifold = new wxBoxSizer(wxHORIZONTAL);
sizer_manifold->Add(info_manifold_text, 0);
sizer_manifold->Add(manifold_warning_icon, 0, wxLEFT, 2);
@@ -1477,6 +1476,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
view3D_canvas->Bind(EVT_GLTOOLBAR_DELETE, [q](SimpleEvent&) { q->remove_selected(); });
view3D_canvas->Bind(EVT_GLTOOLBAR_DELETE_ALL, [this](SimpleEvent&) { reset(); });
view3D_canvas->Bind(EVT_GLTOOLBAR_ARRANGE, [this](SimpleEvent&) { arrange(); });
+ view3D_canvas->Bind(EVT_GLTOOLBAR_COPY, [q](SimpleEvent&) { q->copy_selection_to_clipboard(); });
+ view3D_canvas->Bind(EVT_GLTOOLBAR_PASTE, [q](SimpleEvent&) { q->paste_from_clipboard(); });
view3D_canvas->Bind(EVT_GLTOOLBAR_MORE, [q](SimpleEvent&) { q->increase_instances(); });
view3D_canvas->Bind(EVT_GLTOOLBAR_FEWER, [q](SimpleEvent&) { q->decrease_instances(); });
view3D_canvas->Bind(EVT_GLTOOLBAR_SPLIT_OBJECTS, &priv::on_action_split_objects, this);
@@ -1719,11 +1720,11 @@ std::vector Plater::priv::load_files(const std::vector& input_
if (advanced)
{
- wxMessageDialog dlg(q, _(L("This file cannot be loaded in simple mode. Do you want to switch to expert mode?\n")),
+ wxMessageDialog dlg(q, _(L("This file cannot be loaded in a simple mode. Do you want to switch to an advanced mode?\n")),
_(L("Detected advanced data")), wxICON_WARNING | wxYES | wxNO);
if (dlg.ShowModal() == wxID_YES)
{
- Slic3r::GUI::wxGetApp().save_mode(comExpert);
+ Slic3r::GUI::wxGetApp().save_mode(comAdvanced);
view3D->set_as_dirty();
}
else
@@ -2669,25 +2670,21 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
this->statusbar()->set_progress(evt.status.percent);
this->statusbar()->set_status_text(_(L(evt.status.text)) + wxString::FromUTF8("…"));
}
- if (evt.status.flags & PrintBase::SlicingStatus::RELOAD_SCENE) {
+ if (evt.status.flags & (PrintBase::SlicingStatus::RELOAD_SCENE || PrintBase::SlicingStatus::RELOAD_SLA_SUPPORT_POINTS)) {
switch (this->printer_technology) {
case ptFFF:
this->update_fff_scene();
break;
case ptSLA:
+ // If RELOAD_SLA_SUPPORT_POINTS, then the SLA gizmo is updated (reload_scene calls update_gizmos_data)
if (view3D->is_dragging())
delayed_scene_refresh = true;
else
this->update_sla_scene();
break;
}
- }
- if (evt.status.flags & PrintBase::SlicingStatus::RELOAD_SLA_SUPPORT_POINTS) {
- // Update SLA gizmo (reload_scene calls update_gizmos_data)
- q->canvas3D()->reload_scene(true);
- }
- if (evt.status.flags & PrintBase::SlicingStatus::RELOAD_SLA_PREVIEW) {
- // Update the SLA preview
+ } else if (evt.status.flags & PrintBase::SlicingStatus::RELOAD_SLA_PREVIEW) {
+ // Update the SLA preview. Only called if not RELOAD_SLA_SUPPORT_POINTS, as the block above will refresh the preview anyways.
this->preview->reload_print();
}
}
@@ -2895,17 +2892,17 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/
wxMenuItem* item_delete = nullptr;
if (is_part) {
item_delete = append_menu_item(menu, wxID_ANY, _(L("Delete")) + "\tDel", _(L("Remove the selected object")),
- [this](wxCommandEvent&) { q->remove_selected(); }, "remove");
+ [this](wxCommandEvent&) { q->remove_selected(); }, "delete");
sidebar->obj_list()->append_menu_item_export_stl(menu);
}
else {
wxMenuItem* item_increase = append_menu_item(menu, wxID_ANY, _(L("Increase copies")) + "\t+", _(L("Place one more copy of the selected object")),
- [this](wxCommandEvent&) { q->increase_instances(); }, "instance_add");
+ [this](wxCommandEvent&) { q->increase_instances(); }, "add_copies");
wxMenuItem* item_decrease = append_menu_item(menu, wxID_ANY, _(L("Decrease copies")) + "\t-", _(L("Remove one copy of the selected object")),
- [this](wxCommandEvent&) { q->decrease_instances(); }, "instance_remove");
+ [this](wxCommandEvent&) { q->decrease_instances(); }, "remove_copies");
wxMenuItem* item_set_number_of_copies = append_menu_item(menu, wxID_ANY, _(L("Set number of copies")) + dots, _(L("Change the number of copies of the selected object")),
- [this](wxCommandEvent&) { q->set_number_of_copies(); }, "textfield.png");
+ [this](wxCommandEvent&) { q->set_number_of_copies(); }, "number_of_copies");
items_increase.push_back(item_increase);
items_decrease.push_back(item_decrease);
@@ -2913,7 +2910,7 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/
// Delete menu was moved to be after +/- instace to make it more difficult to be selected by mistake.
item_delete = append_menu_item(menu, wxID_ANY, _(L("Delete")) + "\tDel", _(L("Remove the selected object")),
- [this](wxCommandEvent&) { q->remove_selected(); }, "remove");
+ [this](wxCommandEvent&) { q->remove_selected(); }, "delete");
menu->AppendSeparator();
wxMenuItem* item_instance_to_object = sidebar->obj_list()->append_menu_item_instance_to_object(menu);
@@ -2943,11 +2940,11 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/
return false;
append_menu_item(mirror_menu, wxID_ANY, _(L("Along X axis")), _(L("Mirror the selected object along the X axis")),
- [this](wxCommandEvent&) { mirror(X); }, "bullet_red.png", menu);
+ [this](wxCommandEvent&) { mirror(X); }, "mark_X", menu);
append_menu_item(mirror_menu, wxID_ANY, _(L("Along Y axis")), _(L("Mirror the selected object along the Y axis")),
- [this](wxCommandEvent&) { mirror(Y); }, "bullet_green.png", menu);
+ [this](wxCommandEvent&) { mirror(Y); }, "mark_Y", menu);
append_menu_item(mirror_menu, wxID_ANY, _(L("Along Z axis")), _(L("Mirror the selected object along the Z axis")),
- [this](wxCommandEvent&) { mirror(Z); }, "bullet_blue.png", menu);
+ [this](wxCommandEvent&) { mirror(Z); }, "mark_Z", menu);
wxMenuItem* item_mirror = append_submenu(menu, mirror_menu, wxID_ANY, _(L("Mirror")), _(L("Mirror the selected object")));
@@ -2968,9 +2965,9 @@ bool Plater::priv::complit_init_object_menu()
return false;
wxMenuItem* item_split_objects = append_menu_item(split_menu, wxID_ANY, _(L("To objects")), _(L("Split the selected object into individual objects")),
- [this](wxCommandEvent&) { split_object(); }, "split_objects.png", &object_menu);
+ [this](wxCommandEvent&) { split_object(); }, "split_object_SMALL", &object_menu);
wxMenuItem* item_split_volumes = append_menu_item(split_menu, wxID_ANY, _(L("To parts")), _(L("Split the selected object into individual sub-parts")),
- [this](wxCommandEvent&) { split_volume(); }, "split_parts.png", &object_menu);
+ [this](wxCommandEvent&) { split_volume(); }, "split_parts_SMALL", &object_menu);
wxMenuItem* item_split = append_submenu(&object_menu, split_menu, wxID_ANY, _(L("Split")), _(L("Split the selected object"))/*, "shape_ungroup.png"*/);
object_menu.AppendSeparator();
@@ -2990,7 +2987,7 @@ bool Plater::priv::complit_init_object_menu()
bool Plater::priv::complit_init_sla_object_menu()
{
wxMenuItem* item_split = append_menu_item(&sla_object_menu, wxID_ANY, _(L("Split")), _(L("Split the selected object into individual objects")),
- [this](wxCommandEvent&) { split_object(); }, "shape_ungroup_o.png");
+ [this](wxCommandEvent&) { split_object(); }, "split_object_SMALL");
sla_object_menu.AppendSeparator();
@@ -3010,7 +3007,7 @@ bool Plater::priv::complit_init_sla_object_menu()
bool Plater::priv::complit_init_part_menu()
{
wxMenuItem* item_split = append_menu_item(&part_menu, wxID_ANY, _(L("Split")), _(L("Split the selected object into individual sub-parts")),
- [this](wxCommandEvent&) { split_volume(); }, "shape_ungroup_p.png");
+ [this](wxCommandEvent&) { split_volume(); }, "split_parts_SMALL");
part_menu.AppendSeparator();
@@ -3117,7 +3114,7 @@ void Plater::priv::set_bed_shape(const Pointfs& shape)
bool Plater::priv::can_delete() const
{
- return !get_selection().is_empty();
+ return !get_selection().is_empty() && !get_selection().is_wipe_tower();
}
bool Plater::priv::can_delete_all() const
@@ -3299,8 +3296,9 @@ void Plater::increase_instances(size_t num)
bool was_one_instance = model_object->instances.size()==1;
- float offset = 10.0;
- for (size_t i = 0; i < num; i++, offset += 10.0) {
+ double offset_base = canvas3D()->get_size_proportional_to_max_bed_size(0.05);
+ double offset = offset_base;
+ for (size_t i = 0; i < num; i++, offset += offset_base) {
Vec3d offset_vec = model_instance->get_offset() + Vec3d(offset, offset, 0.0);
model_object->add_instance(offset_vec, model_instance->get_scaling_factor(), model_instance->get_rotation(), model_instance->get_mirror());
// p->print.get_object(obj_idx)->add_copy(Slic3r::to_2d(offset_vec));
@@ -3368,7 +3366,7 @@ void Plater::set_number_of_copies(/*size_t num*/)
bool Plater::is_selection_empty() const
{
- return p->get_selection().is_empty();
+ return p->get_selection().is_empty() || p->get_selection().is_wipe_tower();
}
void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_upper, bool keep_lower, bool rotate_lower)
@@ -3771,10 +3769,43 @@ void Plater::changed_object(int obj_idx)
this->p->schedule_background_process();
}
+void Plater::schedule_background_process()
+{
+ this->p->schedule_background_process();
+}
+
void Plater::fix_through_netfabb(const int obj_idx, const int vol_idx/* = -1*/) { p->fix_through_netfabb(obj_idx, vol_idx); }
void Plater::update_object_menu() { p->update_object_menu(); }
+void Plater::copy_selection_to_clipboard()
+{
+ p->view3D->get_canvas3d()->get_selection().copy_to_clipboard();
+}
+
+void Plater::paste_from_clipboard()
+{
+ p->view3D->get_canvas3d()->get_selection().paste_from_clipboard();
+}
+
+bool Plater::can_paste_from_clipboard() const
+{
+ const Selection& selection = p->view3D->get_canvas3d()->get_selection();
+ const Selection::Clipboard& clipboard = selection.get_clipboard();
+ Selection::EMode mode = clipboard.get_mode();
+
+ if (clipboard.is_empty())
+ return false;
+
+ if ((mode == Selection::Volume) && !selection.is_from_single_instance())
+ return false;
+
+ if ((mode == Selection::Instance) && (selection.get_mode() != Selection::Instance))
+ return false;
+
+ return true;
+}
+
bool Plater::can_delete() const { return p->can_delete(); }
bool Plater::can_delete_all() const { return p->can_delete_all(); }
bool Plater::can_increase_instances() const { return p->can_increase_instances(); }
@@ -3783,5 +3814,7 @@ bool Plater::can_split_to_objects() const { return p->can_split_to_objects(); }
bool Plater::can_split_to_volumes() const { return p->can_split_to_volumes(); }
bool Plater::can_arrange() const { return p->can_arrange(); }
bool Plater::can_layers_editing() const { return p->can_layers_editing(); }
+bool Plater::can_copy() const { return !is_selection_empty(); }
+bool Plater::can_paste() const { return can_paste_from_clipboard(); }
}} // namespace Slic3r::GUI
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index b2d904a2e3..418e56c84d 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -167,6 +167,7 @@ public:
void reslice();
void reslice_SLA_supports(const ModelObject &object);
void changed_object(int obj_idx);
+ void schedule_background_process();
void fix_through_netfabb(const int obj_idx, const int vol_idx = -1);
void send_gcode();
@@ -187,6 +188,10 @@ public:
PrinterTechnology printer_technology() const;
void set_printer_technology(PrinterTechnology printer_technology);
+ void copy_selection_to_clipboard();
+ void paste_from_clipboard();
+ bool can_paste_from_clipboard() const;
+
bool can_delete() const;
bool can_delete_all() const;
bool can_increase_instances() const;
@@ -195,6 +200,8 @@ public:
bool can_split_to_volumes() const;
bool can_arrange() const;
bool can_layers_editing() const;
+ bool can_copy() const;
+ bool can_paste() const;
private:
struct priv;
diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp
index c573dd39da..01a74e15b1 100644
--- a/src/slic3r/GUI/Preset.cpp
+++ b/src/slic3r/GUI/Preset.cpp
@@ -801,12 +801,16 @@ bool PresetCollection::delete_current_preset()
void PresetCollection::load_bitmap_default(wxWindow *window, const std::string &file_name)
{
- *m_bitmap_main_frame = create_scaled_bitmap(window, file_name);
+ // XXX: See note in PresetBundle::load_compatible_bitmaps()
+ (void)window;
+ *m_bitmap_main_frame = create_scaled_bitmap(nullptr, file_name);
}
void PresetCollection::load_bitmap_add(wxWindow *window, const std::string &file_name)
{
- *m_bitmap_add = create_scaled_bitmap(window, file_name);
+ // XXX: See note in PresetBundle::load_compatible_bitmaps()
+ (void)window;
+ *m_bitmap_add = create_scaled_bitmap(nullptr, file_name);
}
const Preset* PresetCollection::get_selected_preset_parent() const
diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp
index 0a84055490..7927006b25 100644
--- a/src/slic3r/GUI/PresetBundle.cpp
+++ b/src/slic3r/GUI/PresetBundle.cpp
@@ -398,22 +398,27 @@ void PresetBundle::export_selections(AppConfig &config)
void PresetBundle::load_compatible_bitmaps(wxWindow *window)
{
- *m_bitmapCompatible = create_scaled_bitmap(window, "flag_green");
- *m_bitmapIncompatible = create_scaled_bitmap(window, "flag_red");
- *m_bitmapLock = create_scaled_bitmap(window, "lock_closed");
- *m_bitmapLockOpen = create_scaled_bitmap(window, "sys_unlock.png");
+ // We don't actually pass the window pointer here and instead generate
+ // a low DPI bitmap, because the wxBitmapComboBox and wxDataViewCtrl don't support
+ // high DPI bitmaps very well, they compute their dimensions wrong.
+ // TODO: Update this when fixed in wxWidgets
+ // See also PresetCollection::load_bitmap_default() and PresetCollection::load_bitmap_add()
+
+ (void)window;
+ *m_bitmapCompatible = create_scaled_bitmap(nullptr, "flag_green");
+ *m_bitmapIncompatible = create_scaled_bitmap(nullptr, "flag_red");
+ *m_bitmapLock = create_scaled_bitmap(nullptr, "lock_closed");
+ *m_bitmapLockOpen = create_scaled_bitmap(nullptr, "sys_unlock.png");
prints .set_bitmap_compatible(m_bitmapCompatible);
filaments .set_bitmap_compatible(m_bitmapCompatible);
sla_prints .set_bitmap_compatible(m_bitmapCompatible);
sla_materials.set_bitmap_compatible(m_bitmapCompatible);
- printers .set_bitmap_compatible(m_bitmapCompatible);
prints .set_bitmap_incompatible(m_bitmapIncompatible);
filaments .set_bitmap_incompatible(m_bitmapIncompatible);
sla_prints .set_bitmap_incompatible(m_bitmapIncompatible);
sla_materials.set_bitmap_incompatible(m_bitmapIncompatible);
- printers .set_bitmap_incompatible(m_bitmapIncompatible);
prints .set_bitmap_lock(m_bitmapLock);
filaments .set_bitmap_lock(m_bitmapLock);
diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp
index dedf55a45a..957c04d690 100644
--- a/src/slic3r/GUI/Selection.cpp
+++ b/src/slic3r/GUI/Selection.cpp
@@ -1022,6 +1022,77 @@ bool Selection::requires_local_axes() const
return (m_mode == Volume) && is_from_single_instance();
}
+void Selection::copy_to_clipboard()
+{
+ if (!m_valid)
+ return;
+
+ m_clipboard.reset();
+
+ for (const ObjectIdxsToInstanceIdxsMap::value_type& object : m_cache.content)
+ {
+ ModelObject* src_object = m_model->objects[object.first];
+ ModelObject* dst_object = m_clipboard.add_object();
+ dst_object->name = src_object->name;
+ dst_object->input_file = src_object->input_file;
+ dst_object->config = src_object->config;
+ dst_object->sla_support_points = src_object->sla_support_points;
+ dst_object->sla_points_status = src_object->sla_points_status;
+ dst_object->layer_height_ranges = src_object->layer_height_ranges;
+ dst_object->layer_height_profile = src_object->layer_height_profile;
+ dst_object->origin_translation = src_object->origin_translation;
+
+ for (int i : object.second)
+ {
+ dst_object->add_instance(*src_object->instances[i]);
+ }
+
+ for (unsigned int i : m_list)
+ {
+ // Copy the ModelVolumes only for the selected GLVolumes of the 1st selected instance.
+ const GLVolume* volume = (*m_volumes)[i];
+ if ((volume->object_idx() == object.first) && (volume->instance_idx() == *object.second.begin()))
+ {
+ int volume_idx = volume->volume_idx();
+ if ((0 <= volume_idx) && (volume_idx < (int)src_object->volumes.size()))
+ {
+ ModelVolume* src_volume = src_object->volumes[volume_idx];
+ ModelVolume* dst_volume = dst_object->add_volume(*src_volume);
+ dst_volume->set_new_unique_id();
+ } else {
+ assert(false);
+ }
+ }
+ }
+ }
+
+ m_clipboard.set_mode(m_mode);
+}
+
+void Selection::paste_from_clipboard()
+{
+ if (!m_valid || m_clipboard.is_empty())
+ return;
+
+ switch (m_clipboard.get_mode())
+ {
+ case Volume:
+ {
+ if (is_from_single_instance())
+ paste_volumes_from_clipboard();
+
+ break;
+ }
+ case Instance:
+ {
+ if (m_mode == Instance)
+ paste_objects_from_clipboard();
+
+ break;
+ }
+ }
+}
+
void Selection::update_valid()
{
m_valid = (m_volumes != nullptr) && (m_model != nullptr);
@@ -1697,5 +1768,44 @@ bool Selection::is_from_fully_selected_instance(unsigned int volume_idx) const
return count == (unsigned int)m_model->objects[object_idx]->volumes.size();
}
+void Selection::paste_volumes_from_clipboard()
+{
+ int obj_idx = get_object_idx();
+ if ((obj_idx < 0) || ((int)m_model->objects.size() <= obj_idx))
+ return;
+
+ ModelObject* src_object = m_clipboard.get_object(0);
+ if (src_object != nullptr)
+ {
+ ModelObject* dst_object = m_model->objects[obj_idx];
+
+ ModelVolumePtrs volumes;
+ for (ModelVolume* src_volume : src_object->volumes)
+ {
+ ModelVolume* dst_volume = dst_object->add_volume(*src_volume);
+ dst_volume->set_new_unique_id();
+ double offset = wxGetApp().plater()->canvas3D()->get_size_proportional_to_max_bed_size(0.05);
+ dst_volume->translate(offset, offset, 0.0);
+ volumes.push_back(dst_volume);
+ }
+ wxGetApp().obj_list()->paste_volumes_into_list(obj_idx, volumes);
+ }
+}
+
+void Selection::paste_objects_from_clipboard()
+{
+ std::vector object_idxs;
+ const ModelObjectPtrs& src_objects = m_clipboard.get_objects();
+ for (const ModelObject* src_object : src_objects)
+ {
+ ModelObject* dst_object = m_model->add_object(*src_object);
+ double offset = wxGetApp().plater()->canvas3D()->get_size_proportional_to_max_bed_size(0.05);
+ dst_object->translate(offset, offset, 0.0);
+ object_idxs.push_back(m_model->objects.size() - 1);
+ }
+
+ wxGetApp().obj_list()->paste_objects_into_list(object_idxs);
+}
+
} // namespace GUI
} // namespace Slic3r
diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp
index b03a8e89ac..a8b0c06dcb 100644
--- a/src/slic3r/GUI/Selection.hpp
+++ b/src/slic3r/GUI/Selection.hpp
@@ -138,6 +138,23 @@ public:
typedef std::set InstanceIdxsList;
typedef std::map ObjectIdxsToInstanceIdxsMap;
+ class Clipboard
+ {
+ Model m_model;
+ Selection::EMode m_mode;
+
+ public:
+ void reset() { m_model.clear_objects(); }
+ bool is_empty() const { return m_model.objects.empty(); }
+
+ ModelObject* add_object() { return m_model.add_object(); }
+ ModelObject* get_object(unsigned int id) { return (id < (unsigned int)m_model.objects.size()) ? m_model.objects[id] : nullptr; }
+ const ModelObjectPtrs& get_objects() const { return m_model.objects; }
+
+ Selection::EMode get_mode() const { return m_mode; }
+ void set_mode(Selection::EMode mode) { m_mode = mode; }
+ };
+
private:
struct Cache
{
@@ -163,6 +180,7 @@ private:
// set of indices to m_volumes
IndicesList m_list;
Cache m_cache;
+ Clipboard m_clipboard;
mutable BoundingBoxf3 m_bounding_box;
mutable bool m_bounding_box_dirty;
@@ -267,6 +285,11 @@ public:
bool requires_local_axes() const;
+ void copy_to_clipboard();
+ void paste_from_clipboard();
+
+ const Clipboard& get_clipboard() const { return m_clipboard; }
+
private:
void update_valid();
void update_type();
@@ -301,6 +324,9 @@ private:
void synchronize_unselected_volumes();
void ensure_on_bed();
bool is_from_fully_selected_instance(unsigned int volume_idx) const;
+
+ void paste_volumes_from_clipboard();
+ void paste_objects_from_clipboard();
};
} // namespace GUI
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index 2dd277d7ba..8a7ac0433d 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -165,10 +165,13 @@ void Tab::create_preset_tab()
add_scaled_bitmap(this, m_bmp_value_unlock, "lock_open");
m_bmp_non_system = &m_bmp_white_bullet;
// Bitmaps to be shown on the "Undo user changes" button next to each input field.
+// m_bmp_value_revert = create_scaled_bitmap(this, "undo");
+// m_bmp_white_bullet = create_scaled_bitmap(this, luma >= 128 ? "dot" : "dot_white"/*"bullet_white.png"*/);
+// m_bmp_question = create_scaled_bitmap(this, "question");
// m_bmp_value_revert = create_scaled_bitmap(this, "undo");
// m_bmp_white_bullet = create_scaled_bitmap(this, "bullet_white.png");
add_scaled_bitmap(this, m_bmp_value_revert, "undo");
- add_scaled_bitmap(this, m_bmp_white_bullet, "bullet_white.png");
+ add_scaled_bitmap(this, m_bmp_white_bullet, luma >= 128 ? "dot" : "dot_white"/*"bullet_white.png"*/);
fill_icon_descriptions();
set_tooltips_text();
@@ -1163,7 +1166,7 @@ void TabPrint::build()
optgroup = page->new_optgroup(_(L("Advanced")));
optgroup->append_single_option_line("interface_shells");
- page = add_options_page(_(L("Advanced")), "wrench.png");
+ page = add_options_page(_(L("Advanced")), "wrench");
optgroup = page->new_optgroup(_(L("Extrusion width")));
optgroup->append_single_option_line("extrusion_width");
optgroup->append_single_option_line("first_layer_extrusion_width");
@@ -1536,7 +1539,7 @@ void TabFilament::build()
optgroup->append_single_option_line("slowdown_below_layer_time");
optgroup->append_single_option_line("min_print_speed");
- page = add_options_page(_(L("Advanced")), "wrench.png");
+ page = add_options_page(_(L("Advanced")), "wrench");
optgroup = page->new_optgroup(_(L("Filament properties")));
optgroup->append_single_option_line("filament_type");
optgroup->append_single_option_line("filament_soluble");
@@ -1702,12 +1705,17 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
}
auto printhost_browse = [=](wxWindow* parent) {
+// auto btn = m_printhost_browse_btn = new wxButton(parent, wxID_ANY, _(L(" Browse ")) + dots,
+// wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
+// btn->SetFont(Slic3r::GUI::wxGetApp().normal_font());
+// btn->SetBitmap(create_scaled_bitmap(this, "browse"));
// auto btn = m_printhost_browse_btn = new wxButton(parent, wxID_ANY, _(L(" Browse ")) + dots,
// wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
// btn->SetBitmap(create_scaled_bitmap(this, "zoom.png"));
- add_scaled_button(parent, &m_printhost_browse_btn, "zoom.png", _(L(" Browse ")) + dots, wxBU_LEFT | wxBU_EXACTFIT);
+ add_scaled_button(parent, &m_printhost_browse_btn, "browse", _(L(" Browse ")) + dots, wxBU_LEFT | wxBU_EXACTFIT);
PrusaButton* btn = m_printhost_browse_btn;
- btn->SetFont(Slic3r::GUI::wxGetApp().normal_font());
+ btn->SetFont(Slic3r::GUI::wxGetApp().normal_font());
+
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn);
@@ -1722,6 +1730,11 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
return sizer;
};
+ auto print_host_test = [this](wxWindow* parent) {
+ auto btn = m_print_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")),
+ wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
+ btn->SetFont(Slic3r::GUI::wxGetApp().normal_font());
+ btn->SetBitmap(create_scaled_bitmap(this, "test"));
auto print_host_test = [this](wxWindow* parent) {
// auto btn = m_print_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")),
// wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT);
@@ -1765,7 +1778,7 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
auto printhost_cafile_browse = [this, optgroup] (wxWindow* parent) {
auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
btn->SetFont(Slic3r::GUI::wxGetApp().normal_font());
- btn->SetBitmap(create_scaled_bitmap(this, "zoom.png"));
+ btn->SetBitmap(create_scaled_bitmap(this, "browse"));
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn);
@@ -3330,7 +3343,8 @@ void TabSLAMaterial::build()
optgroup = page->new_optgroup(_(L("Corrections")));
optgroup->label_width = 19;//190;
std::vector corrections = {"material_correction"};
- std::vector axes{ "X", "Y", "Z" };
+// std::vector axes{ "X", "Y", "Z" };
+ std::vector axes{ "XY", "Z" };
for (auto& opt_key : corrections) {
auto line = Line{ m_config->def()->get(opt_key)->full_label, "" };
int id = 0;
@@ -3413,7 +3427,7 @@ void TabSLAPrint::build()
optgroup->append_single_option_line("layer_height");
optgroup->append_single_option_line("faded_layers");
- page = add_options_page(_(L("Supports")), "sla_supports");
+ page = add_options_page(_(L("Supports")), "support"/*"sla_supports"*/);
optgroup = page->new_optgroup(_(L("Supports")));
optgroup->append_single_option_line("supports_enable");
@@ -3441,7 +3455,7 @@ void TabSLAPrint::build()
optgroup->append_single_option_line("support_points_density_relative");
optgroup->append_single_option_line("support_points_minimal_distance");
- page = add_options_page(_(L("Pad")), "brick.png");
+ page = add_options_page(_(L("Pad")), "pad");
optgroup = page->new_optgroup(_(L("Pad")));
optgroup->append_single_option_line("pad_enable");
optgroup->append_single_option_line("pad_wall_thickness");
diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp
index c98c45295d..4479ef70e8 100644
--- a/src/slic3r/GUI/wxExtensions.cpp
+++ b/src/slic3r/GUI/wxExtensions.cpp
@@ -461,10 +461,10 @@ wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name_in, con
// ----------------------------------------------------------------------------
void PrusaObjectDataViewModelNode::set_object_action_icon() {
- m_action_icon = create_scaled_bitmap(nullptr, "add_object.png"); // FIXME: pass window ptr
+ m_action_icon = create_scaled_bitmap(nullptr, "advanced_plus"); // FIXME: pass window ptr
}
void PrusaObjectDataViewModelNode::set_part_action_icon() {
- m_action_icon = create_scaled_bitmap(nullptr, m_type == itVolume ? "cog.png" : "brick_go.png"); // FIXME: pass window ptr
+ m_action_icon = create_scaled_bitmap(nullptr, m_type == itVolume ? "cog" : "set_separate_obj"); // FIXME: pass window ptr
}
Slic3r::GUI::BitmapCache *m_bitmap_cache = nullptr;
@@ -529,10 +529,10 @@ wxDataViewItem PrusaObjectDataViewModel::Add(const wxString &name, const int ext
}
wxDataViewItem PrusaObjectDataViewModel::AddVolumeChild(const wxDataViewItem &parent_item,
- const wxString &name,
- const Slic3r::ModelVolumeType volume_type,
- const int extruder/* = 0*/,
- const bool create_frst_child/* = true*/)
+ const wxString &name,
+ const Slic3r::ModelVolumeType volume_type,
+ const int extruder/* = 0*/,
+ const bool create_frst_child/* = true*/)
{
PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent_item.GetID();
if (!root) return wxDataViewItem(0);
@@ -545,7 +545,7 @@ wxDataViewItem PrusaObjectDataViewModel::AddVolumeChild(const wxDataViewItem &pa
insert_position = -1;
if (create_frst_child && root->m_volumes_cnt == 0)
- {
+ {
const auto node = new PrusaObjectDataViewModelNode(root, root->m_name, *m_volume_bmps[0], extruder_str, 0);
insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position);
// notify control
diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp
index bd45103c17..597b4fb947 100644
--- a/src/slic3r/GUI/wxExtensions.hpp
+++ b/src/slic3r/GUI/wxExtensions.hpp
@@ -454,12 +454,12 @@ public:
~PrusaObjectDataViewModel();
wxDataViewItem Add(const wxString &name, const int extruder);
- wxDataViewItem AddVolumeChild(const wxDataViewItem &parent_item,
- const wxString &name,
- const Slic3r::ModelVolumeType volume_type,
- const int extruder = 0,
- const bool create_frst_child = true);
- wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item);
+ wxDataViewItem AddVolumeChild(const wxDataViewItem &parent_item,
+ const wxString &name,
+ const Slic3r::ModelVolumeType volume_type,
+ const int extruder = 0,
+ const bool create_frst_child = true);
+ wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item);
wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, size_t num);
wxDataViewItem Delete(const wxDataViewItem &item);
wxDataViewItem DeleteLastInstance(const wxDataViewItem &parent_item, size_t num);
diff --git a/src/slic3r_app_msvc.cpp b/src/slic3r_app_msvc.cpp
index 380d30cf40..c100f8fd9b 100644
--- a/src/slic3r_app_msvc.cpp
+++ b/src/slic3r_app_msvc.cpp
@@ -6,14 +6,20 @@
#include
#include
#include
-// Let the NVIDIA and AMD know we want to use their graphics card
-// on a dual graphics card system.
-__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
-__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
+
+#ifdef SLIC3R_GUI
+ // Let the NVIDIA and AMD know we want to use their graphics card
+ // on a dual graphics card system.
+ __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
+ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
+#endif /* SLIC3R_GUI */
#include
#include
-#include
+
+#ifdef SLIC3R_GUI
+ #include
+#endif /* SLIC3R_GUI */
#include
#include
@@ -23,6 +29,7 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
#include
+#ifdef SLIC3R_GUI
class OpenGLVersionCheck
{
public:
@@ -188,6 +195,7 @@ protected:
};
bool OpenGLVersionCheck::message_pump_exit = false;
+#endif /* SLIC3R_GUI */
extern "C" {
typedef int (__stdcall *Slic3rMainFunc)(int argc, wchar_t **argv);
@@ -206,18 +214,33 @@ int wmain(int argc, wchar_t **argv)
std::vector argv_extended;
argv_extended.emplace_back(argv[0]);
+
+#ifdef SLIC3R_GUI
// Here one may push some additional parameters based on the wrapper type.
- for (int i = 1; i < argc; ++ i)
+ bool force_mesa = false;
+#endif /* SLIC3R_GUI */
+ for (int i = 1; i < argc; ++ i) {
+#ifdef SLIC3R_GUI
+ if (wcscmp(argv[i], L"--sw-renderer") == 0)
+ force_mesa = true;
+ else if (wcscmp(argv[i], L"--no-sw-renderer") == 0)
+ force_mesa = false;
+#endif /* SLIC3R_GUI */
argv_extended.emplace_back(argv[i]);
+ }
argv_extended.emplace_back(nullptr);
+#ifdef SLIC3R_GUI
OpenGLVersionCheck opengl_version_check;
bool load_mesa =
+ // Forced from the command line.
+ force_mesa ||
// Running over a rempote desktop, and the RemoteFX is not enabled, therefore Windows will only provide SW OpenGL 1.1 context.
// In that case, use Mesa.
::GetSystemMetrics(SM_REMOTESESSION) ||
// Try to load the default OpenGL driver and test its context version.
! opengl_version_check.load_opengl_dll() || ! opengl_version_check.is_version_greater_or_equal_to(2, 0);
+#endif /* SLIC3R_GUI */
wchar_t path_to_exe[MAX_PATH + 1] = { 0 };
::GetModuleFileNameW(nullptr, path_to_exe, MAX_PATH);
@@ -228,6 +251,7 @@ int wmain(int argc, wchar_t **argv)
_wsplitpath(path_to_exe, drive, dir, fname, ext);
_wmakepath(path_to_exe, drive, dir, nullptr, nullptr);
+#ifdef SLIC3R_GUI
// https://wiki.qt.io/Cross_compiling_Mesa_for_Windows
// http://download.qt.io/development_releases/prebuilt/llvmpipe/windows/
if (load_mesa) {
@@ -242,6 +266,8 @@ int wmain(int argc, wchar_t **argv)
} else
printf("MESA OpenGL library was loaded sucessfully\n");
}
+#endif /* SLIC3R_GUI */
+
wchar_t path_to_slic3r[MAX_PATH + 1] = { 0 };
wcscpy(path_to_slic3r, path_to_exe);
@@ -267,5 +293,5 @@ int wmain(int argc, wchar_t **argv)
return -1;
}
// argc minus the trailing nullptr of the argv
- return slic3r_main(argv_extended.size() - 1, argv_extended.data());
+ return slic3r_main((int)argv_extended.size() - 1, argv_extended.data());
}
diff --git a/version.inc b/version.inc
index 18ac508b2a..6a3f00e00e 100644
--- a/version.inc
+++ b/version.inc
@@ -2,7 +2,7 @@
# (the version numbers are generated by the build script from the git current label)
set(SLIC3R_FORK_NAME "Slic3r Prusa Edition")
-set(SLIC3R_VERSION "1.42.0-beta")
+set(SLIC3R_VERSION "1.42.0-beta2")
set(SLIC3R_BUILD "${SLIC3R_VERSION}+UNKNOWN")
set(SLIC3R_BUILD_ID "${SLIC3R_BUILD_ID}")
set(SLIC3R_RC_VERSION "1,42,0,0")