From 5a84b46ec9a037adf677489d2051b3baef9fe4e1 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 16 Sep 2021 13:38:02 +0200 Subject: [PATCH 01/23] Fix of detection of the out of bed state for sinking objects --- src/libslic3r/Technologies.hpp | 2 ++ src/libslic3r/TriangleMesh.cpp | 26 ++++++++++++++++++++++++++ src/libslic3r/TriangleMesh.hpp | 4 ++++ src/slic3r/GUI/3DScene.cpp | 21 +++++++++++++++++++++ src/slic3r/GUI/3DScene.hpp | 18 ++++++++++++++++++ 5 files changed, 71 insertions(+) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 8da21d56a..df7414eda 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -62,6 +62,8 @@ #define ENABLE_FIX_PREVIEW_OPTIONS_Z (1 && ENABLE_SEAMS_USING_MODELS && ENABLE_2_4_0_ALPHA2) // Enable replacing a missing file during reload from disk command #define ENABLE_RELOAD_FROM_DISK_REPLACE_FILE (1 && ENABLE_2_4_0_ALPHA2) +// Enable the fix for the detection of the out of bed state for sinking objects +#define ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION (1 && ENABLE_2_4_0_ALPHA2) #endif // _prusaslicer_technologies_h_ diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index d289fca14..822548412 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -574,6 +574,32 @@ BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d &trafo) c return bbox; } +#if ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION +BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d& trafo, double world_min_z) const +{ + assert(!its.vertices.empty()); + + BoundingBoxf3 bbox; + // add vertices above the cut + for (const stl_vertex& v : its.vertices) { + const Vec3d world_v = trafo * v.cast(); + if (world_v.z() > world_min_z) + bbox.merge(world_v); + } + + // add new vertices along the cut + MeshSlicingParams slicing_params; + slicing_params.trafo = trafo; + Polygons polygons = union_(slice_mesh(its, world_min_z, slicing_params)); + for (const Polygon& polygon : polygons) { + for (const Point& p : polygon.points) { + bbox.merge(unscale(p.x(), p.y(), world_min_z)); + } + } + return bbox; +} +#endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION + TriangleMesh TriangleMesh::convex_hull_3d() const { // The qhull call: diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index 60ab975c4..a603dd064 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -57,6 +57,10 @@ public: BoundingBoxf3 bounding_box() const; // Returns the bbox of this TriangleMesh transformed by the given transformation BoundingBoxf3 transformed_bounding_box(const Transform3d &trafo) const; +#if ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION + // Variant returning the bbox of the part of this TriangleMesh above the given world_min_z + BoundingBoxf3 transformed_bounding_box(const Transform3d& trafo, double world_min_z) const; +#endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION // Return the size of the mesh in coordinates. Vec3d size() const { return stl.stats.size.cast(); } /// Return the center of the related bounding box. diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index f693143c4..19864b36a 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -524,6 +524,23 @@ BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box(const Transform3d & bounding_box().transformed(trafo); } +#if ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION +BoundingBoxf3 GLVolume::transformed_non_sinking_bounding_box(const Transform3d& trafo) const +{ + return GUI::wxGetApp().plater()->model().objects[object_idx()]->volumes[volume_idx()]->mesh().transformed_bounding_box(trafo, 0.0); +} + +const BoundingBoxf3& GLVolume::transformed_non_sinking_bounding_box() const +{ + if (!m_transformed_non_sinking_bounding_box.has_value()) { + std::optional* trans_box = const_cast*>(&m_transformed_non_sinking_bounding_box); + const Transform3d& trafo = world_matrix(); + *trans_box = transformed_non_sinking_bounding_box(trafo); + } + return *m_transformed_non_sinking_bounding_box; +} +#endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION + void GLVolume::set_range(double min_z, double max_z) { this->qverts_range.first = 0; @@ -936,7 +953,11 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M if (volume->is_modifier || (!volume->shader_outside_printer_detection_enabled && (volume->is_wipe_tower || volume->composite_id.volume_id < 0))) continue; +#if ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION + const BoundingBoxf3& bb = volume->transformed_non_sinking_bounding_box(); +#else const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box(); +#endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION bool contained = print_volume.contains(bb); volume->is_outside = !contained; diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 78b9a96d9..4db1611e7 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -279,6 +279,10 @@ private: std::shared_ptr m_convex_hull; // Bounding box of this volume, in unscaled coordinates. std::optional m_transformed_convex_hull_bounding_box; +#if ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION + // Bounding box of the non sinking part of this volume, in unscaled coordinates. + std::optional m_transformed_non_sinking_bounding_box; +#endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION class SinkingContours { @@ -469,6 +473,12 @@ public: BoundingBoxf3 transformed_convex_hull_bounding_box(const Transform3d &trafo) const; // caching variant const BoundingBoxf3& transformed_convex_hull_bounding_box() const; +#if ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION + // non-caching variant + BoundingBoxf3 transformed_non_sinking_bounding_box(const Transform3d& trafo) const; + // caching variant + const BoundingBoxf3& transformed_non_sinking_bounding_box() const; +#endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION // convex hull const TriangleMesh* convex_hull() const { return m_convex_hull.get(); } @@ -481,7 +491,15 @@ public: void finalize_geometry(bool opengl_initialized) { this->indexed_vertex_array.finalize_geometry(opengl_initialized); } void release_geometry() { this->indexed_vertex_array.release_geometry(); } +#if ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION + void set_bounding_boxes_as_dirty() { + m_transformed_bounding_box.reset(); + m_transformed_convex_hull_bounding_box.reset(); + m_transformed_non_sinking_bounding_box.reset(); + } +#else void set_bounding_boxes_as_dirty() { m_transformed_bounding_box.reset(); m_transformed_convex_hull_bounding_box.reset(); } +#endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION bool is_sla_support() const; bool is_sla_pad() const; From 2f95c7721f9a1093bf87be296eacfc3800439981 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 17 Sep 2021 08:42:01 +0200 Subject: [PATCH 02/23] Follow-up of 5a84b46ec9a037adf677489d2051b3baef9fe4e1 - Faster implementation of method TriangleMesh::transformed_bounding_box(const Transform3d& trafo, double world_min_z) --- src/libslic3r/TriangleMesh.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 822548412..c8bc2f763 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -588,14 +588,12 @@ BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d& trafo, d } // add new vertices along the cut - MeshSlicingParams slicing_params; - slicing_params.trafo = trafo; - Polygons polygons = union_(slice_mesh(its, world_min_z, slicing_params)); - for (const Polygon& polygon : polygons) { - for (const Point& p : polygon.points) { - bbox.merge(unscale(p.x(), p.y(), world_min_z)); - } + Points all_pts; + its_collect_mesh_projection_points_above(its, trafo.cast(), static_cast(world_min_z), all_pts); + for (const Point& p : all_pts) { + bbox.merge(unscale(p.x(), p.y(), world_min_z)); } + return bbox; } #endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION From 6d70ada637fc5a95afe8a7c3a3cfb68d42cb7711 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 17 Sep 2021 09:45:50 +0200 Subject: [PATCH 03/23] Follow-up of 2f95c7721f9a1093bf87be296eacfc3800439981 - Even faster implementation of method TriangleMesh::transformed_bounding_box(const Transform3d& trafo, double world_min_z) --- src/libslic3r/TriangleMesh.cpp | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index c8bc2f763..cfd3436b2 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -577,23 +577,24 @@ BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d &trafo) c #if ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d& trafo, double world_min_z) const { - assert(!its.vertices.empty()); - BoundingBoxf3 bbox; - // add vertices above the cut - for (const stl_vertex& v : its.vertices) { - const Vec3d world_v = trafo * v.cast(); - if (world_v.z() > world_min_z) - bbox.merge(world_v); + const Transform3f ftrafo = trafo.cast(); + for (const stl_triangle_vertex_indices& tri : its.indices) { + const Vec3f pts[3] = { ftrafo * its.vertices[tri(0)], ftrafo * its.vertices[tri(1)], ftrafo * its.vertices[tri(2)] }; + int iprev = 2; + for (int iedge = 0; iedge < 3; ++iedge) { + const Vec3f& p1 = pts[iprev]; + const Vec3f& p2 = pts[iedge]; + if ((p1.z() < world_min_z && p2.z() > world_min_z) || (p2.z() < world_min_z && p1.z() > world_min_z)) { + // Edge crosses the z plane. Calculate intersection point with the plane. + const float t = (world_min_z - p1.z()) / (p2.z() - p1.z()); + bbox.merge(Vec3f(p1.x() + (p2.x() - p1.x()) * t, p1.y() + (p2.y() - p1.y()) * t, world_min_z).cast()); + } + if (p2.z() >= world_min_z) + bbox.merge(p2.cast()); + iprev = iedge; + } } - - // add new vertices along the cut - Points all_pts; - its_collect_mesh_projection_points_above(its, trafo.cast(), static_cast(world_min_z), all_pts); - for (const Point& p : all_pts) { - bbox.merge(unscale(p.x(), p.y(), world_min_z)); - } - return bbox; } #endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION From 92bf9a664d94474892ac27f33fa011599cedeeb0 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 17 Sep 2021 14:47:19 +0200 Subject: [PATCH 04/23] Fix of detection of the out of bed state for sinking objects in backend --- src/libslic3r/Model.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 2844f644c..4efa0264a 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1545,7 +1545,11 @@ unsigned int ModelObject::check_instances_print_volume_state(const BoundingBoxf3 unsigned int inside_outside = 0; for (const ModelVolume *vol : this->volumes) if (vol->is_model_part()) { +#if ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION + BoundingBoxf3 bb = vol->mesh().transformed_bounding_box(model_instance->get_matrix() * vol->get_matrix(), 0.0); +#else BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(model_instance->get_matrix() * vol->get_matrix()); +#endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION if (print_volume.contains(bb)) inside_outside |= INSIDE; else if (print_volume.intersects(bb)) From 1af0c5c73b779ac00ef3d54a7f9af4709781491b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 20 Sep 2021 13:40:34 +0200 Subject: [PATCH 05/23] Tech ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - Call GLVolumeCollection::check_outside_state() only when needed --- src/libslic3r/Technologies.hpp | 2 ++ src/slic3r/GUI/3DScene.cpp | 13 ++++++------- src/slic3r/GUI/GLCanvas3D.cpp | 9 ++++++++- src/slic3r/GUI/GLCanvas3D.hpp | 6 ++++++ src/slic3r/GUI/Selection.cpp | 25 +++++++++++++++++-------- 5 files changed, 39 insertions(+), 16 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 20fc0a38f..1ca589fe8 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -62,6 +62,8 @@ #define ENABLE_RELOAD_FROM_DISK_REPLACE_FILE (1 && ENABLE_2_4_0_ALPHA2) // Enable the fix for the detection of the out of bed state for sinking objects #define ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION (1 && ENABLE_2_4_0_ALPHA2) +// Enable detection of out of bed using the bed perimeter and other improvements +#define ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS (1 && ENABLE_2_4_0_ALPHA2) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 19864b36a..3aba88a56 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -936,14 +936,13 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M const BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values)); BoundingBoxf3 print_volume({ unscale(bed_box_2D.min.x()), unscale(bed_box_2D.min.y()), 0.0 }, - { unscale(bed_box_2D.max.x()), unscale(bed_box_2D.max.y()), - config->opt_float("max_print_height") }); + { unscale(bed_box_2D.max.x()), unscale(bed_box_2D.max.y()), config->opt_float("max_print_height") }); // Allow the objects to protrude below the print bed - print_volume.min(2) = -1e10; - print_volume.min(0) -= BedEpsilon; - print_volume.min(1) -= BedEpsilon; - print_volume.max(0) += BedEpsilon; - print_volume.max(1) += BedEpsilon; + print_volume.min.z() = -1e10; + print_volume.min.x() -= BedEpsilon; + print_volume.min.y() -= BedEpsilon; + print_volume.max.x() += BedEpsilon; + print_volume.max.y() += BedEpsilon; ModelInstanceEPrintVolumeState state = ModelInstancePVS_Inside; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 83b907f01..68b0fd5f4 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5074,8 +5074,15 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type) if (m_config != nullptr) { const BoundingBoxf3& bed_bb = wxGetApp().plater()->get_bed().get_bounding_box(false); - m_volumes.set_print_box((float)bed_bb.min(0) - BedEpsilon, (float)bed_bb.min(1) - BedEpsilon, 0.0f, (float)bed_bb.max(0) + BedEpsilon, (float)bed_bb.max(1) + BedEpsilon, (float)m_config->opt_float("max_print_height")); + m_volumes.set_print_box((float)bed_bb.min.x() - BedEpsilon, (float)bed_bb.min.y() - BedEpsilon, 0.0f, (float)bed_bb.max.x() + BedEpsilon, (float)bed_bb.max.y() + BedEpsilon, (float)m_config->opt_float("max_print_height")); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + if (m_requires_check_outside_state) { + m_volumes.check_outside_state(m_config, nullptr); + m_requires_check_outside_state = false; + } +#else m_volumes.check_outside_state(m_config, nullptr); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS } } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index e03c4a71d..bbdcb9de6 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -475,6 +475,9 @@ private: const DynamicPrintConfig* m_config; Model* m_model; BackgroundSlicingProcess *m_process; +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + bool m_requires_check_outside_state{ false }; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS std::array m_old_size{ 0, 0 }; @@ -611,6 +614,9 @@ public: void post_event(wxEvent &&event); void set_as_dirty(); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + void requires_check_outside_state() { m_requires_check_outside_state = true; } +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS unsigned int get_volumes_count() const; const GLVolumeCollection& get_volumes() const { return m_volumes; } diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 4a9c7cd56..6946bab0e 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -715,6 +715,9 @@ void Selection::translate(const Vec3d& displacement, bool local) ensure_not_below_bed(); set_bounding_boxes_dirty(); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + wxGetApp().plater()->canvas3D()->requires_check_outside_state(); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS } // Rotate an object around one of the axes. Only one rotation component is expected to be changing. @@ -827,7 +830,10 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ volume.set_volume_offset(volume.get_volume_offset() + center_local - center_local_new); } - this->set_bounding_boxes_dirty(); + set_bounding_boxes_dirty(); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + wxGetApp().plater()->canvas3D()->requires_check_outside_state(); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS } void Selection::flattening_rotate(const Vec3d& normal) @@ -926,6 +932,9 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type ensure_on_bed(); set_bounding_boxes_dirty(); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + wxGetApp().plater()->canvas3D()->requires_check_outside_state(); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS } void Selection::scale_to_fit_print_volume(const DynamicPrintConfig& config) @@ -2125,10 +2134,10 @@ void Selection::ensure_not_below_bed() GLVolume* volume = (*m_volumes)[i]; if (!volume->is_wipe_tower && !volume->is_modifier) { const double max_z = volume->transformed_convex_hull_bounding_box().max.z(); - std::pair instance = std::make_pair(volume->object_idx(), volume->instance_idx()); + const std::pair instance = std::make_pair(volume->object_idx(), volume->instance_idx()); InstancesToZMap::iterator it = instances_max_z.find(instance); if (it == instances_max_z.end()) - it = instances_max_z.insert(InstancesToZMap::value_type(instance, -DBL_MAX)).first; + it = instances_max_z.insert({ instance, -DBL_MAX }).first; it->second = std::max(it->second, max_z); } @@ -2137,17 +2146,17 @@ void Selection::ensure_not_below_bed() if (is_any_volume()) { for (unsigned int i : m_list) { GLVolume& volume = *(*m_volumes)[i]; - std::pair instance = std::make_pair(volume.object_idx(), volume.instance_idx()); - InstancesToZMap::iterator it = instances_max_z.find(instance); - double z_shift = SINKING_MIN_Z_THRESHOLD - it->second; + const std::pair instance = std::make_pair(volume.object_idx(), volume.instance_idx()); + InstancesToZMap::const_iterator it = instances_max_z.find(instance); + const double z_shift = SINKING_MIN_Z_THRESHOLD - it->second; if (it != instances_max_z.end() && z_shift > 0.0) volume.set_volume_offset(Z, volume.get_volume_offset(Z) + z_shift); } } else { for (GLVolume* volume : *m_volumes) { - std::pair instance = std::make_pair(volume->object_idx(), volume->instance_idx()); - InstancesToZMap::iterator it = instances_max_z.find(instance); + const std::pair instance = std::make_pair(volume->object_idx(), volume->instance_idx()); + InstancesToZMap::const_iterator it = instances_max_z.find(instance); if (it != instances_max_z.end() && it->second < SINKING_MIN_Z_THRESHOLD) volume->set_instance_offset(Z, volume->get_instance_offset(Z) + SINKING_MIN_Z_THRESHOLD - it->second); } From cf380fb4564fec4bd731e34819102f66fda23e75 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 21 Sep 2021 13:51:57 +0200 Subject: [PATCH 06/23] Tech ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - Out of bed detection for circular printbeds --- resources/shaders/gouraud_mod.fs | 130 +++++++++++ resources/shaders/gouraud_mod.vs | 78 +++++++ src/libslic3r/Geometry.cpp | 9 + src/libslic3r/Geometry.hpp | 3 + src/libslic3r/Line.cpp | 7 + src/libslic3r/Line.hpp | 4 + src/slic3r/GUI/3DBed.cpp | 100 ++++++++- src/slic3r/GUI/3DBed.hpp | 37 +++- src/slic3r/GUI/3DScene.cpp | 54 +++++ src/slic3r/GUI/3DScene.hpp | 24 ++ src/slic3r/GUI/BedShapeDialog.cpp | 208 ++++++++++++------ src/slic3r/GUI/BedShapeDialog.hpp | 17 ++ src/slic3r/GUI/GLCanvas3D.cpp | 64 ++++++ src/slic3r/GUI/GLShadersManager.cpp | 16 +- .../GUI/Gizmos/GLGizmoMmuSegmentation.cpp | 4 + src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 16 ++ 16 files changed, 692 insertions(+), 79 deletions(-) create mode 100644 resources/shaders/gouraud_mod.fs create mode 100644 resources/shaders/gouraud_mod.vs diff --git a/resources/shaders/gouraud_mod.fs b/resources/shaders/gouraud_mod.fs new file mode 100644 index 000000000..959c9cb27 --- /dev/null +++ b/resources/shaders/gouraud_mod.fs @@ -0,0 +1,130 @@ +#version 110 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +#define INTENSITY_AMBIENT 0.3 + +const vec3 ZERO = vec3(0.0, 0.0, 0.0); +const vec3 GREEN = vec3(0.0, 0.7, 0.0); +const vec3 YELLOW = vec3(0.5, 0.7, 0.0); +const vec3 RED = vec3(0.7, 0.0, 0.0); +const vec3 WHITE = vec3(1.0, 1.0, 1.0); +const float EPSILON = 0.0001; +const float BANDS_WIDTH = 10.0; + +struct PrintVolumeDetection +{ + // 0 = rectangle, 1 = circle, 2 = custom, 3 = invalid + int type; + // type = 0 (rectangle): + // x = min.x, y = min.y, z = max.x, w = max.y + // type = 1 (circle): + // x = center.x, y = center.y, z = radius + vec4 xy_data; + // x = min z, y = max z + vec2 z_data; +}; + +struct SlopeDetection +{ + bool actived; + float normal_z; + mat3 volume_world_normal_matrix; +}; + +uniform vec4 uniform_color; +uniform SlopeDetection slope; + +#ifdef ENABLE_ENVIRONMENT_MAP + uniform sampler2D environment_tex; + uniform bool use_environment_tex; +#endif // ENABLE_ENVIRONMENT_MAP + +varying vec3 clipping_planes_dots; + +// x = diffuse, y = specular; +varying vec2 intensity; + +uniform PrintVolumeDetection print_volume; + +varying vec4 model_pos; +varying vec4 world_pos; +varying float world_normal_z; +varying vec3 eye_normal; + +uniform bool compute_triangle_normals_in_fs; + +void main() +{ + if (any(lessThan(clipping_planes_dots, ZERO))) + discard; + vec3 color = uniform_color.rgb; + float alpha = uniform_color.a; + + vec2 intensity_fs = intensity; + vec3 eye_normal_fs = eye_normal; + float world_normal_z_fs = world_normal_z; + if (compute_triangle_normals_in_fs) { + vec3 triangle_normal = normalize(cross(dFdx(model_pos.xyz), dFdy(model_pos.xyz))); +#ifdef FLIP_TRIANGLE_NORMALS + triangle_normal = -triangle_normal; +#endif + + // First transform the normal into camera space and normalize the result. + eye_normal_fs = normalize(gl_NormalMatrix * triangle_normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(eye_normal_fs, LIGHT_TOP_DIR), 0.0); + + intensity_fs = vec2(0.0, 0.0); + intensity_fs.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec3 position = (gl_ModelViewMatrix * model_pos).xyz; + intensity_fs.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, eye_normal_fs)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(eye_normal_fs, LIGHT_FRONT_DIR), 0.0); + intensity_fs.x += NdotL * LIGHT_FRONT_DIFFUSE; + + // z component of normal vector in world coordinate used for slope shading + world_normal_z_fs = slope.actived ? (normalize(slope.volume_world_normal_matrix * triangle_normal)).z : 0.0; + } + + if (slope.actived && world_normal_z_fs < slope.normal_z - EPSILON) { + color = vec3(0.7, 0.7, 1.0); + alpha = 1.0; + } + + // if the fragment is outside the print volume -> use darker color + vec3 pv_check_min = ZERO; + vec3 pv_check_max = ZERO; + if (print_volume.type == 0) { + // rectangle + pv_check_min = world_pos.xyz - vec3(print_volume.xy_data.x, print_volume.xy_data.y, print_volume.z_data.x); + pv_check_max = world_pos.xyz - vec3(print_volume.xy_data.z, print_volume.xy_data.w, print_volume.z_data.y); + } + else if (print_volume.type == 1) { + // circle + float delta_radius = print_volume.xy_data.z - distance(world_pos.xy, print_volume.xy_data.xy); + pv_check_min = vec3(delta_radius, 0.0, world_pos.z - print_volume.z_data.x); + pv_check_max = vec3(0.0, 0.0, world_pos.z - print_volume.z_data.y); + } + color = (any(lessThan(pv_check_min, ZERO)) || any(greaterThan(pv_check_max, ZERO))) ? mix(color, ZERO, 0.3333) : color; + +#ifdef ENABLE_ENVIRONMENT_MAP + if (use_environment_tex) + gl_FragColor = vec4(0.45 * texture2D(environment_tex, normalize(eye_normal_fs).xy * 0.5 + 0.5).xyz + 0.8 * color * intensity_fs.x, alpha); + else +#endif + gl_FragColor = vec4(vec3(intensity_fs.y) + color * intensity_fs.x, alpha); +} diff --git a/resources/shaders/gouraud_mod.vs b/resources/shaders/gouraud_mod.vs new file mode 100644 index 000000000..bc83c38fb --- /dev/null +++ b/resources/shaders/gouraud_mod.vs @@ -0,0 +1,78 @@ +#version 110 + +#define INTENSITY_CORRECTION 0.6 + +// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31) +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +// normalized values for (1./1.43, 0.2/1.43, 1./1.43) +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) +//#define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION) +//#define LIGHT_FRONT_SHININESS 5.0 + +#define INTENSITY_AMBIENT 0.3 + +const vec3 ZERO = vec3(0.0, 0.0, 0.0); + +struct SlopeDetection +{ + bool actived; + float normal_z; + mat3 volume_world_normal_matrix; +}; + +uniform mat4 volume_world_matrix; +uniform SlopeDetection slope; + +// 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 = diffuse, y = specular; +varying vec2 intensity; + +varying vec3 clipping_planes_dots; + +varying vec4 model_pos; +varying vec4 world_pos; +varying float world_normal_z; +varying vec3 eye_normal; + +uniform bool compute_triangle_normals_in_fs; + +void main() +{ + if (!compute_triangle_normals_in_fs) { + // First transform the normal into camera space and normalize the result. + eye_normal = normalize(gl_NormalMatrix * gl_Normal); + + // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. + // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. + float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0); + + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec3 position = (gl_ModelViewMatrix * gl_Vertex).xyz; + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS); + + // Perform the same lighting calculation for the 2nd light source (no specular applied). + NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + } + + model_pos = gl_Vertex; + // Point in homogenous coordinates. + world_pos = volume_world_matrix * gl_Vertex; + + // z component of normal vector in world coordinate used for slope shading + if (!compute_triangle_normals_in_fs) + world_normal_z = slope.actived ? (normalize(slope.volume_world_normal_matrix * gl_Normal)).z : 0.0; + + gl_Position = ftransform(); + // 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/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index 321443204..c4d48efd4 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -305,6 +305,15 @@ bool directions_parallel(double angle1, double angle2, double max_diff) return diff < max_diff || fabs(diff - PI) < max_diff; } +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS +bool directions_perpendicular(double angle1, double angle2, double max_diff) +{ + double diff = fabs(angle1 - angle2); + max_diff += EPSILON; + return fabs(diff - 0.5 * PI) < max_diff || fabs(diff - 1.5 * PI) < max_diff; +} +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + template bool contains(const std::vector &vector, const Point &point) { diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index c6af515c8..2ac19b502 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -336,6 +336,9 @@ Polygon convex_hull(Points points); Polygon convex_hull(const Polygons &polygons); bool directions_parallel(double angle1, double angle2, double max_diff = 0); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS +bool directions_perpendicular(double angle1, double angle2, double max_diff = 0); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS template bool contains(const std::vector &vector, const Point &point); template T rad2deg(T angle) { return T(180.0) * angle / T(PI); } double rad2deg_dir(double angle); diff --git a/src/libslic3r/Line.cpp b/src/libslic3r/Line.cpp index 8a2a2875b..1a96b8b1f 100644 --- a/src/libslic3r/Line.cpp +++ b/src/libslic3r/Line.cpp @@ -63,6 +63,13 @@ bool Line::parallel_to(double angle) const return Slic3r::Geometry::directions_parallel(this->direction(), angle); } +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS +bool Line::perpendicular_to(double angle) const +{ + return Slic3r::Geometry::directions_perpendicular(this->direction(), angle); +} +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + bool Line::intersection(const Line &l2, Point *intersection) const { const Line &l1 = *this; diff --git a/src/libslic3r/Line.hpp b/src/libslic3r/Line.hpp index b62775bfe..b662daa2b 100644 --- a/src/libslic3r/Line.hpp +++ b/src/libslic3r/Line.hpp @@ -85,6 +85,10 @@ public: double perp_distance_to(const Point &point) const; bool parallel_to(double angle) const; bool parallel_to(const Line &line) const { return this->parallel_to(line.direction()); } +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + bool perpendicular_to(double angle) const; + bool perpendicular_to(const Line& line) const { return this->perpendicular_to(line.direction()); } +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS double atan2_() const { return atan2(this->b(1) - this->a(1), this->b(0) - this->a(0)); } double orientation() const; double direction() const; diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 5b7218c87..6e04664e2 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -7,11 +7,10 @@ #include "libslic3r/BoundingBox.hpp" #include "libslic3r/Geometry.hpp" #include "libslic3r/Tesselate.hpp" +#include "libslic3r/PresetBundle.hpp" #include "GUI_App.hpp" -#include "libslic3r/PresetBundle.hpp" #include "GLCanvas3D.hpp" -#include "3DScene.hpp" #include @@ -154,7 +153,11 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c std::string model; std::string texture; if (force_as_custom) +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + type = EType::Custom; +#else type = Custom; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS else { auto [new_type, system_model, system_texture] = detect_type(shape); type = new_type; @@ -174,7 +177,12 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c model_filename.clear(); } +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + EShapeType shape_type = detect_shape_type(shape); + if (m_shape == shape && m_type == type && m_shape_type == shape_type && m_texture_filename == texture_filename && m_model_filename == model_filename) +#else if (m_shape == shape && m_type == type && m_texture_filename == texture_filename && m_model_filename == model_filename) +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS // No change, no need to update the UI. return false; @@ -182,6 +190,9 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c m_texture_filename = texture_filename; m_model_filename = model_filename; m_type = type; +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + m_shape_type = shape_type; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS calc_bounding_boxes(); @@ -229,6 +240,77 @@ void Bed3D::render_for_picking(GLCanvas3D& canvas, bool bottom, float scale_fact render_internal(canvas, bottom, scale_factor, false, false, true); } +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS +bool Bed3D::is_rectangle(const Pointfs& shape, Vec2d* min, Vec2d* max) +{ + const Lines lines = Polygon::new_scale(shape).lines(); + bool ret = lines.size() == 4 && lines[0].parallel_to(lines[2]) && lines[1].parallel_to(lines[3]) && lines[0].perpendicular_to(lines[1]); + if (ret) { + if (min != nullptr) { + *min = shape.front(); + for (const Vec2d& pt : shape) { + min->x() = std::min(min->x(), pt.x()); + min->y() = std::min(min->y(), pt.y()); + } + } + if (max != nullptr) { + *max = shape.front(); + for (const Vec2d& pt : shape) { + max->x() = std::max(max->x(), pt.x()); + max->y() = std::max(max->y(), pt.y()); + } + } + } + return ret; +} + +bool Bed3D::is_circle(const Pointfs& shape, Vec2d* center, double* radius) +{ + if (shape.size() < 3) + return false; + + // Analyze the array of points. + // Do they reside on a circle ? + const Vec2d box_center = BoundingBoxf(shape).center(); + std::vector vertex_distances; + double avg_dist = 0.0; + for (const Vec2d& pt : shape) { + double distance = (pt - box_center).norm(); + vertex_distances.push_back(distance); + avg_dist += distance; + } + + avg_dist /= vertex_distances.size(); + + bool defined_value = true; + for (double el : vertex_distances) { + if (fabs(el - avg_dist) > 10.0 * SCALED_EPSILON) + defined_value = false; + break; + } + + if (center != nullptr) + *center = box_center; + + if (radius != nullptr) + *radius = avg_dist; + + return defined_value; +} + +Bed3D::EShapeType Bed3D::detect_shape_type(const Pointfs& shape) +{ + if (shape.size() < 3) + return EShapeType::Invalid; + else if (is_rectangle(shape)) + return EShapeType::Rectangle; + else if (is_circle(shape)) + return EShapeType::Circle; + else + return EShapeType::Custom; +} +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + void Bed3D::render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor, bool show_axes, bool show_texture, bool picking) { @@ -244,9 +326,15 @@ void Bed3D::render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor, switch (m_type) { +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + case EType::System: { render_system(canvas, bottom, show_texture); break; } + default: + case EType::Custom: { render_custom(canvas, bottom, show_texture, picking); break; } +#else case System: { render_system(canvas, bottom, show_texture); break; } default: case Custom: { render_custom(canvas, bottom, show_texture, picking); break; } +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS } glsafe(::glDisable(GL_DEPTH_TEST)); @@ -320,7 +408,11 @@ std::tuple Bed3D::detect_type(const Poin std::string model_filename = PresetUtils::system_printer_bed_model(*curr); std::string texture_filename = PresetUtils::system_printer_bed_texture(*curr); if (!model_filename.empty() && !texture_filename.empty()) +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + return { EType::System, model_filename, texture_filename }; +#else return { System, model_filename, texture_filename }; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS } } @@ -328,7 +420,11 @@ std::tuple Bed3D::detect_type(const Poin } } +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + return { EType::Custom, "", "" }; +#else return { Custom, "", "" }; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS } void Bed3D::render_axes() const diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp index a2a643519..baa0ef5b7 100644 --- a/src/slic3r/GUI/3DBed.hpp +++ b/src/slic3r/GUI/3DBed.hpp @@ -62,15 +62,36 @@ class Bed3D }; public: +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + enum class EType : unsigned char + { + System, + Custom + }; + + enum class EShapeType : unsigned char + { + Rectangle, + Circle, + Custom, + Invalid + }; +#else enum EType : unsigned char { System, Custom, Num_Types }; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS private: +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + EType m_type{ EType::Custom }; + EShapeType m_shape_type{ EShapeType::Invalid }; +#else EType m_type{ Custom }; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS Pointfs m_shape; std::string m_texture_filename; std::string m_model_filename; @@ -94,16 +115,18 @@ public: ~Bed3D() { reset(); } EType get_type() const { return m_type; } - +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + EShapeType get_shape_type() const { return m_shape_type; } + bool is_custom() const { return m_type == EType::Custom; } +#else bool is_custom() const { return m_type == Custom; } +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS const Pointfs& get_shape() const { return m_shape; } // Return true if the bed shape changed, so the calee will update the UI. bool set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom = false); - const BoundingBoxf3& get_bounding_box(bool extended) const { - return extended ? m_extended_bounding_box : m_bounding_box; - } + const BoundingBoxf3& get_bounding_box(bool extended) const { return extended ? m_extended_bounding_box : m_bounding_box; } bool contains(const Point& point) const; Point point_projection(const Point& point) const; @@ -113,6 +136,12 @@ public: void render_for_picking(GLCanvas3D& canvas, bool bottom, float scale_factor); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + static bool is_rectangle(const Pointfs& shape, Vec2d* min = nullptr, Vec2d* max = nullptr); + static bool is_circle(const Pointfs& shape, Vec2d* center = nullptr, double* radius = nullptr); + static EShapeType detect_shape_type(const Pointfs& shape); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + private: void calc_bounding_boxes() const; void calc_triangles(const ExPolygon& poly); diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 3aba88a56..59535910d 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -873,10 +873,17 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab shader->set_uniform("uniform_color", volume.first->render_color); shader->set_uniform("z_range", m_z_range, 2); shader->set_uniform("clipping_plane", m_clipping_plane, 4); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + shader->set_uniform("print_volume.type", static_cast(m_print_volume.type)); + shader->set_uniform("print_volume.xy_data", m_print_volume.data); + shader->set_uniform("print_volume.z_data", m_print_volume.zs); + shader->set_uniform("volume_world_matrix", volume.first->world_matrix()); +#else shader->set_uniform("print_box.min", m_print_box_min, 3); shader->set_uniform("print_box.max", m_print_box_max, 3); shader->set_uniform("print_box.actived", volume.first->shader_outside_printer_detection_enabled); shader->set_uniform("print_box.volume_world_matrix", volume.first->world_matrix()); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS shader->set_uniform("slope.actived", m_slope.active && !volume.first->is_modifier && !volume.first->is_wipe_tower); shader->set_uniform("slope.volume_world_normal_matrix", static_cast(volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast())); shader->set_uniform("slope.normal_z", m_slope.normal_z); @@ -925,6 +932,31 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab glsafe(::glDisable(GL_BLEND)); } +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS +static bool same(const Polygon& lhs, const Polygon& rhs) +{ + if (lhs.points.size() != rhs.points.size()) + return false; + + size_t rhs_id = 0; + while (rhs_id < rhs.points.size()) { + if (rhs.points[rhs_id].isApprox(lhs.points.front())) + break; + ++rhs_id; + } + + if (rhs_id == rhs.points.size()) + return false; + + for (size_t i = 0; i < lhs.points.size(); ++i) { + if (!lhs.points[i].isApprox(rhs.points[(i + rhs_id) % lhs.points.size()])) + return false; + } + + return true; +} +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state) const { if (config == nullptr) @@ -934,6 +966,10 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M if (opt == nullptr) return false; +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + const Polygon bed_poly = offset(Polygon::new_scale(opt->values), static_cast(scale_(BedEpsilon))).front(); + const float bed_height = config->opt_float("max_print_height"); +#else const BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values)); BoundingBoxf3 print_volume({ unscale(bed_box_2D.min.x()), unscale(bed_box_2D.min.y()), 0.0 }, { unscale(bed_box_2D.max.x()), unscale(bed_box_2D.max.y()), config->opt_float("max_print_height") }); @@ -943,6 +979,7 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M print_volume.min.y() -= BedEpsilon; print_volume.max.x() += BedEpsilon; print_volume.max.y() += BedEpsilon; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS ModelInstanceEPrintVolumeState state = ModelInstancePVS_Inside; @@ -957,7 +994,19 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M #else const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box(); #endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + const indexed_triangle_set& its = GUI::wxGetApp().plater()->model().objects[volume->object_idx()]->volumes[volume->volume_idx()]->mesh().its; + const Polygon volume_hull_2d = its_convex_hull_2d_above(its, volume->world_matrix().cast(), 0.0f); + Polygons intersection_polys = intersection(bed_poly, volume_hull_2d); + bool contained_xy = !intersection_polys.empty() && same(intersection_polys.front(), volume_hull_2d); + bool contained_z = -1e10 < bb.min.z() && bb.max.z() < bed_height; + bool contained = contained_xy && contained_z; + bool intersects_xy = !contained_xy && !intersection_polys.empty(); + bool intersects_z = !contained_z && bb.min.z() < bed_height && -1e10 < bb.max.z(); + bool intersects = intersects_xy || intersects_z; +#else bool contained = print_volume.contains(bb); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS volume->is_outside = !contained; if (!volume->printable) @@ -968,8 +1017,13 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M if (state == ModelInstancePVS_Inside && volume->is_outside) state = ModelInstancePVS_Fully_Outside; +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + if (state == ModelInstancePVS_Fully_Outside && volume->is_outside && intersects) + state = ModelInstancePVS_Partly_Outside; +#else if (state == ModelInstancePVS_Fully_Outside && volume->is_outside && print_volume.intersects(bb)) state = ModelInstancePVS_Partly_Outside; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS } if (out_state != nullptr) diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 4db1611e7..9bfd6edeb 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -532,10 +532,30 @@ public: All }; +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + struct PrintVolume + { + // see: Bed3D::EShapeType + int type{ 0 }; + // data contains: + // Rectangle: + // [0] = min.x, [1] = min.y, [2] = max.x, [3] = max.y + // Circle: + // [0] = center.x, [1] = center.y, [3] = radius + std::array data; + // [0] = min z, [1] = max z + std::array zs; + }; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + private: +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + PrintVolume m_print_volume; +#else // min and max vertex of the print box volume float m_print_box_min[3]; float m_print_box_max[3]; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS // z range for clipping in shaders float m_z_range[2]; @@ -607,10 +627,14 @@ public: bool empty() const { return volumes.empty(); } void set_range(double low, double high) { for (GLVolume *vol : this->volumes) vol->set_range(low, high); } +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + void set_print_volume(const PrintVolume& print_volume) { m_print_volume = print_volume; } +#else void set_print_box(float min_x, float min_y, float min_z, float max_x, float max_y, float max_z) { m_print_box_min[0] = min_x; m_print_box_min[1] = min_y; m_print_box_min[2] = min_z; m_print_box_max[0] = max_x; m_print_box_max[1] = max_y; m_print_box_max[2] = max_z; } +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS void set_z_range(float min_z, float max_z) { m_z_range[0] = min_z; m_z_range[1] = max_z; } void set_clipping_plane(const double* coeffs) { m_clipping_plane[0] = coeffs[0]; m_clipping_plane[1] = coeffs[1]; m_clipping_plane[2] = coeffs[2]; m_clipping_plane[3] = coeffs[3]; } diff --git a/src/slic3r/GUI/BedShapeDialog.cpp b/src/slic3r/GUI/BedShapeDialog.cpp index 40d9ee3b2..0b8e31e13 100644 --- a/src/slic3r/GUI/BedShapeDialog.cpp +++ b/src/slic3r/GUI/BedShapeDialog.cpp @@ -22,9 +22,25 @@ namespace GUI { BedShape::BedShape(const ConfigOptionPoints& points) { - auto polygon = Polygon::new_scale(points.values); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + if (points.size() < 3) { + m_type = Bed3D::EShapeType::Invalid; + return; + } +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS // is this a rectangle ? +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + Vec2d min; + Vec2d max; + if (Bed3D::is_rectangle(points.values, &min, &max)) { + m_type = Bed3D::EShapeType::Rectangle; + m_rectSize = max - min; + m_rectOrigin = -min; + return; + } +#else + Polygon polygon = Polygon::new_scale(points.values); if (points.size() == 4) { auto lines = polygon.lines(); if (lines[0].parallel_to(lines[2]) && lines[1].parallel_to(lines[3])) { @@ -48,8 +64,21 @@ BedShape::BedShape(const ConfigOptionPoints& points) return; } } +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS // is this a circle ? +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + Vec2d center; + double radius; + if (Bed3D::is_circle(points.values, ¢er, &radius)) { + m_type = Bed3D::EShapeType::Circle; + m_diameter = 2.0 * radius; + return; + } + + // This is a custom bed shape, use the polygon provided. + m_type = Bed3D::EShapeType::Custom; +#else { // Analyze the array of points.Do they reside on a circle ? auto center = polygon.bounding_box().center(); @@ -79,11 +108,12 @@ BedShape::BedShape(const ConfigOptionPoints& points) } } - if (points.size() < 3) + if (points.size() < 3) return; // This is a custom bed shape, use the polygon provided. m_type = Type::Custom; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS } static std::string get_option_label(BedShape::Parameter param) @@ -134,31 +164,56 @@ void BedShape::append_option_line(ConfigOptionsGroupShp optgroup, Parameter para } } +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS +wxString BedShape::get_name(Bed3D::EShapeType type) +{ + switch (type) { + case Bed3D::EShapeType::Rectangle: { return _L("Rectangular"); } + case Bed3D::EShapeType::Circle: { return _L("Circular"); } + case Bed3D::EShapeType::Custom: { return _L("Custom"); } + case Bed3D::EShapeType::Invalid: + default: return _L("Invalid"); + } +} +#else wxString BedShape::get_name(Type type) { switch (type) { - case Type::Rectangular : return _L("Rectangular"); - case Type::Circular : return _L("Circular"); - case Type::Custom : return _L("Custom"); - case Type::Invalid : - default : return _L("Invalid"); + case Type::Rectangular: return _L("Rectangular"); + case Type::Circular: return _L("Circular"); + case Type::Custom: return _L("Custom"); + case Type::Invalid: + default: return _L("Invalid"); } } +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS size_t BedShape::get_type() { +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + return static_cast(m_type == Bed3D::EShapeType::Invalid ? Bed3D::EShapeType::Rectangle : m_type); +#else return static_cast(m_type == Type::Invalid ? Type::Rectangular : m_type); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS } wxString BedShape::get_full_name_with_params() { wxString out = _L("Shape") + ": " + get_name(m_type); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + if (m_type == Bed3D::EShapeType::Rectangle) { +#else if (m_type == Type::Rectangular) { +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS out += "\n" + _(get_option_label(Parameter::RectSize)) + ": [" + ConfigOptionPoint(m_rectSize).serialize() + "]"; out += "\n" + _(get_option_label(Parameter::RectOrigin))+ ": [" + ConfigOptionPoint(m_rectOrigin).serialize() + "]"; } +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + else if (m_type == Bed3D::EShapeType::Circle) +#else else if (m_type == Type::Circular) +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS out += "\n" + _L(get_option_label(Parameter::Diameter)) + ": [" + double_to_string(m_diameter) + "]"; return out; @@ -166,11 +221,19 @@ wxString BedShape::get_full_name_with_params() void BedShape::apply_optgroup_values(ConfigOptionsGroupShp optgroup) { +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + if (m_type == Bed3D::EShapeType::Rectangle || m_type == Bed3D::EShapeType::Invalid) { +#else if (m_type == Type::Rectangular || m_type == Type::Invalid) { +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS optgroup->set_value("rect_size" , new ConfigOptionPoints{ m_rectSize }); optgroup->set_value("rect_origin" , new ConfigOptionPoints{ m_rectOrigin }); } +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + else if (m_type == Bed3D::EShapeType::Circle) +#else else if (m_type == Type::Circular) +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS optgroup->set_value("diameter", double_to_string(m_diameter)); } @@ -222,7 +285,7 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const Conf m_custom_texture = custom_texture.value.empty() ? NONE : custom_texture.value; m_custom_model = custom_model.value.empty() ? NONE : custom_model.value; - auto sbsizer = new wxStaticBoxSizer(wxVERTICAL, this, _(L("Shape"))); + auto sbsizer = new wxStaticBoxSizer(wxVERTICAL, this, _L("Shape")); sbsizer->GetStaticBox()->SetFont(wxGetApp().bold_font()); wxGetApp().UpdateDarkUI(sbsizer->GetStaticBox()); @@ -232,16 +295,28 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const Conf sbsizer->Add(m_shape_options_book); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + auto optgroup = init_shape_options_page(BedShape::get_name(Bed3D::EShapeType::Rectangle)); +#else auto optgroup = init_shape_options_page(BedShape::get_name(BedShape::Type::Rectangular)); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS BedShape::append_option_line(optgroup, BedShape::Parameter::RectSize); BedShape::append_option_line(optgroup, BedShape::Parameter::RectOrigin); activate_options_page(optgroup); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + optgroup = init_shape_options_page(BedShape::get_name(Bed3D::EShapeType::Circle)); +#else optgroup = init_shape_options_page(BedShape::get_name(BedShape::Type::Circular)); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS BedShape::append_option_line(optgroup, BedShape::Parameter::Diameter); activate_options_page(optgroup); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + optgroup = init_shape_options_page(BedShape::get_name(Bed3D::EShapeType::Custom)); +#else optgroup = init_shape_options_page(BedShape::get_name(BedShape::Type::Custom)); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS Line line{ "", "" }; line.full_width = 1; @@ -265,10 +340,7 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const Conf wxPanel* texture_panel = init_texture_panel(); wxPanel* model_panel = init_model_panel(); - Bind(wxEVT_CHOICEBOOK_PAGE_CHANGED, ([this](wxCommandEvent& e) - { - update_shape(); - })); + Bind(wxEVT_CHOICEBOOK_PAGE_CHANGED, ([this](wxCommandEvent& e) { update_shape(); })); // right pane with preview canvas m_canvas = new Bed_2D(this); @@ -295,7 +367,7 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const Conf ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(const wxString& title) { wxPanel* panel = new wxPanel(m_shape_options_book); - ConfigOptionsGroupShp optgroup = std::make_shared(panel, _(L("Settings"))); + ConfigOptionsGroupShp optgroup = std::make_shared(panel, _L("Settings")); optgroup->label_width = 10; optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) { @@ -319,7 +391,7 @@ wxPanel* BedShapePanel::init_texture_panel() { wxPanel* panel = new wxPanel(this); wxGetApp().UpdateDarkUI(panel, true); - ConfigOptionsGroupShp optgroup = std::make_shared(panel, _(L("Texture"))); + ConfigOptionsGroupShp optgroup = std::make_shared(panel, _L("Texture")); optgroup->label_width = 10; optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) { @@ -329,7 +401,7 @@ wxPanel* BedShapePanel::init_texture_panel() Line line{ "", "" }; line.full_width = 1; line.widget = [this](wxWindow* parent) { - wxButton* load_btn = new wxButton(parent, wxID_ANY, _(L("Load..."))); + wxButton* load_btn = new wxButton(parent, wxID_ANY, _L("Load...")); wxSizer* load_sizer = new wxBoxSizer(wxHORIZONTAL); load_sizer->Add(load_btn, 1, wxEXPAND); @@ -338,7 +410,7 @@ wxPanel* BedShapePanel::init_texture_panel() wxSizer* filename_sizer = new wxBoxSizer(wxHORIZONTAL); filename_sizer->Add(filename_lbl, 1, wxEXPAND); - wxButton* remove_btn = new wxButton(parent, wxID_ANY, _(L("Remove"))); + wxButton* remove_btn = new wxButton(parent, wxID_ANY, _L("Remove")); wxSizer* remove_sizer = new wxBoxSizer(wxHORIZONTAL); remove_sizer->Add(remove_btn, 1, wxEXPAND); @@ -347,31 +419,23 @@ wxPanel* BedShapePanel::init_texture_panel() sizer->Add(load_sizer, 1, wxEXPAND); sizer->Add(remove_sizer, 1, wxEXPAND | wxTOP, 2); - load_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) - { - load_texture(); - })); - - remove_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) - { + load_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) { load_texture(); })); + remove_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) { m_custom_texture = NONE; update_shape(); })); - filename_lbl->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e) - { + filename_lbl->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e) { e.SetText(_(boost::filesystem::path(m_custom_texture).filename().string())); wxStaticText* lbl = dynamic_cast(e.GetEventObject()); - if (lbl != nullptr) - { + if (lbl != nullptr) { bool exists = (m_custom_texture == NONE) || boost::filesystem::exists(m_custom_texture); lbl->SetForegroundColour(exists ? /*wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)*/wxGetApp().get_label_clr_default() : wxColor(*wxRED)); wxString tooltip_text = ""; - if (m_custom_texture != NONE) - { + if (m_custom_texture != NONE) { if (!exists) - tooltip_text += _(L("Not found:")) + " "; + tooltip_text += _L("Not found:") + " "; tooltip_text += _(m_custom_texture); } @@ -382,10 +446,7 @@ wxPanel* BedShapePanel::init_texture_panel() } })); - remove_btn->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e) - { - e.Enable(m_custom_texture != NONE); - })); + remove_btn->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e) { e.Enable(m_custom_texture != NONE); })); return sizer; }; @@ -401,7 +462,7 @@ wxPanel* BedShapePanel::init_model_panel() { wxPanel* panel = new wxPanel(this); wxGetApp().UpdateDarkUI(panel, true); - ConfigOptionsGroupShp optgroup = std::make_shared(panel, _(L("Model"))); + ConfigOptionsGroupShp optgroup = std::make_shared(panel, _L("Model")); optgroup->label_width = 10; optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) { @@ -411,7 +472,7 @@ wxPanel* BedShapePanel::init_model_panel() Line line{ "", "" }; line.full_width = 1; line.widget = [this](wxWindow* parent) { - wxButton* load_btn = new wxButton(parent, wxID_ANY, _(L("Load..."))); + wxButton* load_btn = new wxButton(parent, wxID_ANY, _L("Load...")); wxSizer* load_sizer = new wxBoxSizer(wxHORIZONTAL); load_sizer->Add(load_btn, 1, wxEXPAND); @@ -419,7 +480,7 @@ wxPanel* BedShapePanel::init_model_panel() wxSizer* filename_sizer = new wxBoxSizer(wxHORIZONTAL); filename_sizer->Add(filename_lbl, 1, wxEXPAND); - wxButton* remove_btn = new wxButton(parent, wxID_ANY, _(L("Remove"))); + wxButton* remove_btn = new wxButton(parent, wxID_ANY, _L("Remove")); wxSizer* remove_sizer = new wxBoxSizer(wxHORIZONTAL); remove_sizer->Add(remove_btn, 1, wxEXPAND); @@ -428,31 +489,24 @@ wxPanel* BedShapePanel::init_model_panel() sizer->Add(load_sizer, 1, wxEXPAND); sizer->Add(remove_sizer, 1, wxEXPAND | wxTOP, 2); - load_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) - { - load_model(); - })); + load_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) { load_model(); })); - remove_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) - { + remove_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) { m_custom_model = NONE; update_shape(); })); - filename_lbl->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e) - { + filename_lbl->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e) { e.SetText(_(boost::filesystem::path(m_custom_model).filename().string())); wxStaticText* lbl = dynamic_cast(e.GetEventObject()); - if (lbl != nullptr) - { + if (lbl != nullptr) { bool exists = (m_custom_model == NONE) || boost::filesystem::exists(m_custom_model); lbl->SetForegroundColour(exists ? /*wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)*/wxGetApp().get_label_clr_default() : wxColor(*wxRED)); wxString tooltip_text = ""; - if (m_custom_model != NONE) - { + if (m_custom_model != NONE) { if (!exists) - tooltip_text += _(L("Not found:")) + " "; + tooltip_text += _L("Not found:") + " "; tooltip_text += _(m_custom_model); } @@ -463,10 +517,7 @@ wxPanel* BedShapePanel::init_model_panel() } })); - remove_btn->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e) - { - e.Enable(m_custom_model != NONE); - })); + remove_btn->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e) { e.Enable(m_custom_model != NONE); })); return sizer; }; @@ -511,10 +562,18 @@ void BedShapePanel::update_shape() auto page_idx = m_shape_options_book->GetSelection(); auto opt_group = m_optgroups[page_idx]; +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + Bed3D::EShapeType page_type = static_cast(page_idx); +#else BedShape::Type page_type = static_cast(page_idx); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - if (page_type == BedShape::Type::Rectangular) { - Vec2d rect_size(Vec2d::Zero()); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + if (page_type == Bed3D::EShapeType::Rectangle) { +#else + if (page_type == BedShape::Type::Rectangular) { +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + Vec2d rect_size(Vec2d::Zero()); Vec2d rect_origin(Vec2d::Zero()); try { rect_size = boost::any_cast(opt_group->get_value("rect_size")); } @@ -544,8 +603,12 @@ void BedShapePanel::update_shape() Vec2d(x1, y1), Vec2d(x0, y1) }; } +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + else if (page_type == Bed3D::EShapeType::Circle) { +#else else if (page_type == BedShape::Type::Circular) { - double diameter; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + double diameter; try { diameter = boost::any_cast(opt_group->get_value("diameter")); } catch (const std::exception & /* e */) { return; } @@ -560,7 +623,11 @@ void BedShapePanel::update_shape() } m_shape = points; } +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + else if (page_type == Bed3D::EShapeType::Custom) +#else else if (page_type == BedShape::Type::Custom) +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS m_shape = m_loaded_shape; update_preview(); @@ -569,14 +636,13 @@ void BedShapePanel::update_shape() // Loads an stl file, projects it to the XY plane and calculates a polygon. void BedShapePanel::load_stl() { - wxFileDialog dialog(this, _(L("Choose an STL file to import bed shape from:")), "", "", file_wildcards(FT_STL), wxFD_OPEN | wxFD_FILE_MUST_EXIST); + wxFileDialog dialog(this, _L("Choose an STL file to import bed shape from:"), "", "", file_wildcards(FT_STL), wxFD_OPEN | wxFD_FILE_MUST_EXIST); if (dialog.ShowModal() != wxID_OK) return; std::string file_name = dialog.GetPath().ToUTF8().data(); - if (!boost::algorithm::iends_with(file_name, ".stl")) - { - show_error(this, _(L("Invalid file format."))); + if (!boost::algorithm::iends_with(file_name, ".stl")) { + show_error(this, _L("Invalid file format.")); return; } @@ -587,7 +653,7 @@ void BedShapePanel::load_stl() model = Model::read_from_file(file_name); } catch (std::exception &) { - show_error(this, _(L("Error! Invalid model"))); + show_error(this, _L("Error! Invalid model")); return; } @@ -595,11 +661,11 @@ void BedShapePanel::load_stl() auto expolygons = mesh.horizontal_projection(); if (expolygons.size() == 0) { - show_error(this, _(L("The selected file contains no geometry."))); + show_error(this, _L("The selected file contains no geometry.")); return; } if (expolygons.size() > 1) { - show_error(this, _(L("The selected file contains several disjoint areas. This is not supported."))); + show_error(this, _L("The selected file contains several disjoint areas. This is not supported.")); return; } @@ -614,7 +680,7 @@ void BedShapePanel::load_stl() void BedShapePanel::load_texture() { - wxFileDialog dialog(this, _(L("Choose a file to import bed texture from (PNG/SVG):")), "", "", + wxFileDialog dialog(this, _L("Choose a file to import bed texture from (PNG/SVG):"), "", "", file_wildcards(FT_TEX), wxFD_OPEN | wxFD_FILE_MUST_EXIST); if (dialog.ShowModal() != wxID_OK) @@ -623,9 +689,8 @@ void BedShapePanel::load_texture() m_custom_texture = NONE; std::string file_name = dialog.GetPath().ToUTF8().data(); - if (!boost::algorithm::iends_with(file_name, ".png") && !boost::algorithm::iends_with(file_name, ".svg")) - { - show_error(this, _(L("Invalid file format."))); + if (!boost::algorithm::iends_with(file_name, ".png") && !boost::algorithm::iends_with(file_name, ".svg")) { + show_error(this, _L("Invalid file format.")); return; } @@ -637,7 +702,7 @@ void BedShapePanel::load_texture() void BedShapePanel::load_model() { - wxFileDialog dialog(this, _(L("Choose an STL file to import bed model from:")), "", "", + wxFileDialog dialog(this, _L("Choose an STL file to import bed model from:"), "", "", file_wildcards(FT_STL), wxFD_OPEN | wxFD_FILE_MUST_EXIST); if (dialog.ShowModal() != wxID_OK) @@ -646,9 +711,8 @@ void BedShapePanel::load_model() m_custom_model = NONE; std::string file_name = dialog.GetPath().ToUTF8().data(); - if (!boost::algorithm::iends_with(file_name, ".stl")) - { - show_error(this, _(L("Invalid file format."))); + if (!boost::algorithm::iends_with(file_name, ".stl")) { + show_error(this, _L("Invalid file format.")); return; } diff --git a/src/slic3r/GUI/BedShapeDialog.hpp b/src/slic3r/GUI/BedShapeDialog.hpp index 370129f2e..af84ffb95 100644 --- a/src/slic3r/GUI/BedShapeDialog.hpp +++ b/src/slic3r/GUI/BedShapeDialog.hpp @@ -5,6 +5,9 @@ #include "GUI_Utils.hpp" #include "2DBed.hpp" +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS +#include "3DBed.hpp" +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS #include "I18N.hpp" #include @@ -19,12 +22,14 @@ using ConfigOptionsGroupShp = std::shared_ptr; struct BedShape { +#if !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS enum class Type { Rectangular = 0, Circular, Custom, Invalid }; +#endif // !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS enum class Parameter { RectSize, @@ -34,10 +39,18 @@ struct BedShape BedShape(const ConfigOptionPoints& points); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + bool is_custom() { return m_type == Bed3D::EShapeType::Custom; } +#else bool is_custom() { return m_type == Type::Custom; } +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS static void append_option_line(ConfigOptionsGroupShp optgroup, Parameter param); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + static wxString get_name(Bed3D::EShapeType type); +#else static wxString get_name(Type type); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS // convert Type to size_t size_t get_type(); @@ -46,7 +59,11 @@ struct BedShape void apply_optgroup_values(ConfigOptionsGroupShp optgroup); private: +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + Bed3D::EShapeType m_type{ Bed3D::EShapeType::Invalid }; +#else Type m_type {Type::Invalid}; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS Vec2d m_rectSize {200, 200}; Vec2d m_rectOrigin {0, 0}; double m_diameter {0}; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 68b0fd5f4..4f9b2d0b3 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2115,8 +2115,38 @@ void GLCanvas3D::load_sla_preview() // Release OpenGL data before generating new data. reset_volumes(); _load_sla_shells(); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + Bed3D::EShapeType type = wxGetApp().plater()->get_bed().get_shape_type(); + switch (type) + { + case Bed3D::EShapeType::Circle: { + Vec2d center; + double radius; + if (Bed3D::is_circle(wxGetApp().plater()->get_bed().get_shape(), ¢er, &radius)) { + m_volumes.set_print_volume({ static_cast(type), + { float(center.x()), float(center.y()), float(radius) + BedEpsilon, 0.0f }, + { 0.0f, float(m_config->opt_float("max_print_height")) } }); + } + break; + } + case Bed3D::EShapeType::Rectangle: { + const BoundingBoxf3& bed_bb = wxGetApp().plater()->get_bed().get_bounding_box(false); + m_volumes.set_print_volume({ static_cast(type), + { float(bed_bb.min.x()) - BedEpsilon, float(bed_bb.min.y()) - BedEpsilon, float(bed_bb.max.x()) + BedEpsilon, float(bed_bb.max.y()) + BedEpsilon }, + { 0.0f, float(m_config->opt_float("max_print_height")) } }); + break; + } + default: + case Bed3D::EShapeType::Custom: { + m_volumes.set_print_volume({ static_cast(type), + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }); + } + } +#else const BoundingBoxf3& bed_bb = wxGetApp().plater()->get_bed().get_bounding_box(false); m_volumes.set_print_box(float(bed_bb.min.x()) - BedEpsilon, float(bed_bb.min.y()) - BedEpsilon, 0.0f, float(bed_bb.max.x()) + BedEpsilon, float(bed_bb.max.y()) + BedEpsilon, (float)m_config->opt_float("max_print_height")); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS _update_sla_shells_outside_state(); _set_warning_notification_if_needed(EWarning::SlaSupportsOutside); } @@ -5073,8 +5103,38 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type) m_layers_editing.select_object(*m_model, this->is_layers_editing_enabled() ? m_selection.get_object_idx() : -1); if (m_config != nullptr) { +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + Bed3D::EShapeType type = wxGetApp().plater()->get_bed().get_shape_type(); + switch (type) + { + case Bed3D::EShapeType::Circle: { + Vec2d center; + double radius; + if (Bed3D::is_circle(wxGetApp().plater()->get_bed().get_shape(), ¢er, &radius)) { + m_volumes.set_print_volume({ static_cast(type), + { float(center.x()), float(center.y()), float(radius) + BedEpsilon, 0.0f }, + { 0.0f, float(m_config->opt_float("max_print_height")) } }); + } + break; + } + case Bed3D::EShapeType::Rectangle: { + const BoundingBoxf3& bed_bb = wxGetApp().plater()->get_bed().get_bounding_box(false); + m_volumes.set_print_volume({ static_cast(type), + { float(bed_bb.min.x()) - BedEpsilon, float(bed_bb.min.y()) - BedEpsilon, float(bed_bb.max.x()) + BedEpsilon, float(bed_bb.max.y()) + BedEpsilon }, + { 0.0f, float(m_config->opt_float("max_print_height")) } }); + break; + } + default: + case Bed3D::EShapeType::Custom: { + m_volumes.set_print_volume({ static_cast(type), + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }); + } + } +#else const BoundingBoxf3& bed_bb = wxGetApp().plater()->get_bed().get_bounding_box(false); m_volumes.set_print_box((float)bed_bb.min.x() - BedEpsilon, (float)bed_bb.min.y() - BedEpsilon, 0.0f, (float)bed_bb.max.x() + BedEpsilon, (float)bed_bb.max.y() + BedEpsilon, (float)m_config->opt_float("max_print_height")); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS if (m_requires_check_outside_state) { m_volumes.check_outside_state(m_config, nullptr); @@ -5094,7 +5154,11 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type) m_volumes.set_clipping_plane(m_camera_clipping_plane.get_data()); m_volumes.set_show_sinking_contours(! m_gizmos.is_hiding_instances()); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_mod"); +#else GLShaderProgram* shader = wxGetApp().get_shader("gouraud"); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS if (shader != nullptr) { shader->start_using(); diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index 5dd478b57..bf3ee7eee 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -64,17 +64,31 @@ std::pair GLShadersManager::init() // For Apple's on Arm CPU computed triangle normals inside fragment shader using dFdx and dFdy has the opposite direction. // Because of this, objects had darker colors inside the multi-material gizmo. // Based on https://stackoverflow.com/a/66206648, the similar behavior was also spotted on some other devices with Arm CPU. +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + if (platform_flavor() == PlatformFlavor::OSXOnArm) + valid &= append_shader("gouraud_mod", { "gouraud_mod.vs", "gouraud_mod.fs" }, { "FLIP_TRIANGLE_NORMALS"sv +#if ENABLE_ENVIRONMENT_MAP + , "ENABLE_ENVIRONMENT_MAP"sv +#endif + }); + else + valid &= append_shader("gouraud_mod", { "gouraud_mod.vs", "gouraud_mod.fs" } +#if ENABLE_ENVIRONMENT_MAP + , { "ENABLE_ENVIRONMENT_MAP"sv } +#endif +#else if (platform_flavor() == PlatformFlavor::OSXOnArm) valid &= append_shader("gouraud", { "gouraud.vs", "gouraud.fs" }, { "FLIP_TRIANGLE_NORMALS"sv #if ENABLE_ENVIRONMENT_MAP , "ENABLE_ENVIRONMENT_MAP"sv #endif - }); + }); else valid &= append_shader("gouraud", { "gouraud.vs", "gouraud.fs" } #if ENABLE_ENVIRONMENT_MAP , { "ENABLE_ENVIRONMENT_MAP"sv } #endif +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS ); // used to render variable layers heights in 3d editor valid &= append_shader("variable_layer_height", { "variable_layer_height.vs", "variable_layer_height.fs" }); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index 30f7ff7cf..b460d93b0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -607,7 +607,11 @@ void TriangleSelectorMmGui::render(ImGuiWrapper *imgui) auto *shader = wxGetApp().get_current_shader(); if (!shader) return; +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + assert(shader->get_name() == "gouraud_mod"); +#else assert(shader->get_name() == "gouraud"); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS ScopeGuard guard([shader]() { if (shader) shader->set_uniform("compute_triangle_normals_in_fs", false);}); shader->set_uniform("compute_triangle_normals_in_fs", true); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index aac4d6ff7..19b5549ae 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -97,12 +97,20 @@ void GLGizmoPainterBase::render_triangles(const Selection& selection, const bool clp_dataf[3] = float(clp->get_data()[3]); } +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + auto* shader = wxGetApp().get_shader("gouraud_mod"); +#else auto *shader = wxGetApp().get_shader("gouraud"); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS if (! shader) return; shader->start_using(); shader->set_uniform("slope.actived", false); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + shader->set_uniform("print_volume.type", 0); +#else shader->set_uniform("print_box.actived", false); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS shader->set_uniform("clipping_plane", clp_dataf, 4); ScopeGuard guard([shader]() { if (shader) shader->stop_using(); }); @@ -128,7 +136,11 @@ void GLGizmoPainterBase::render_triangles(const Selection& selection, const bool // to the shader input variable print_box.volume_world_matrix before // rendering the painted triangles. When this matrix is not set, the // wrong transformation matrix is used for "Clipping of view". +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + shader->set_uniform("volume_world_matrix", trafo_matrix); +#else shader->set_uniform("print_box.volume_world_matrix", trafo_matrix); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS m_triangle_selectors[mesh_id]->render(m_imgui); @@ -591,7 +603,11 @@ void TriangleSelectorGUI::render(ImGuiWrapper* imgui) auto* shader = wxGetApp().get_current_shader(); if (! shader) return; +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + assert(shader->get_name() == "gouraud_mod"); +#else assert(shader->get_name() == "gouraud"); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS for (auto iva : {std::make_pair(&m_iva_enforcers, enforcers_color), std::make_pair(&m_iva_blockers, blockers_color)}) { From d854fd0b29673e06f0273f243a4976fc92828ba3 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 21 Sep 2021 15:48:17 +0200 Subject: [PATCH 07/23] Tech ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - Fixed GLVolumeCollection::check_outside_state() for SLA printers --- src/libslic3r/Technologies.hpp | 2 +- src/slic3r/GUI/3DScene.cpp | 49 +++++++++++++++++++++------------- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 61c1b7029..eb2f6cfc1 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -63,7 +63,7 @@ // Enable the fix for the detection of the out of bed state for sinking objects #define ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION (1 && ENABLE_2_4_0_ALPHA2) // Enable detection of out of bed using the bed perimeter and other improvements -#define ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS (1 && ENABLE_2_4_0_ALPHA2) +#define ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS (1 && ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index d12986e06..a69f95a15 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -966,7 +966,8 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS const Polygon bed_poly = offset(Polygon::new_scale(opt->values), static_cast(scale_(BedEpsilon))).front(); const float bed_height = config->opt_float("max_print_height"); -#else +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + const BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values)); BoundingBoxf3 print_volume({ unscale(bed_box_2D.min.x()), unscale(bed_box_2D.min.y()), 0.0 }, { unscale(bed_box_2D.max.x()), unscale(bed_box_2D.max.y()), config->opt_float("max_print_height") }); @@ -976,7 +977,6 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M print_volume.min.y() -= BedEpsilon; print_volume.max.x() += BedEpsilon; print_volume.max.y() += BedEpsilon; -#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS ModelInstanceEPrintVolumeState state = ModelInstancePVS_Inside; @@ -987,23 +987,36 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M continue; #if ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION - const BoundingBoxf3& bb = volume->transformed_non_sinking_bounding_box(); + bool contained = false; + bool intersects = false; + if (GUI::wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) { + const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box(); + contained = print_volume.contains(bb); + intersects = print_volume.intersects(bb); + } + else { + const BoundingBoxf3& bb = volume->transformed_non_sinking_bounding_box(); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + const indexed_triangle_set& its = GUI::wxGetApp().plater()->model().objects[volume->object_idx()]->volumes[volume->volume_idx()]->mesh().its; + const Polygon volume_hull_2d = its_convex_hull_2d_above(its, volume->world_matrix().cast(), 0.0f); + Polygons intersection_polys = intersection(bed_poly, volume_hull_2d); + bool contained_xy = !intersection_polys.empty() && same(intersection_polys.front(), volume_hull_2d); + bool contained_z = -1e10 < bb.min.z() && bb.max.z() < bed_height; + contained = contained_xy && contained_z; + bool intersects_xy = !contained_xy && !intersection_polys.empty(); + bool intersects_z = !contained_z && bb.min.z() < bed_height && -1e10 < bb.max.z(); + intersects = intersects_xy || intersects_z; +#else + contained = print_volume.contains(bb); + intersects = print_volume.intersects(bb); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS #else const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box(); -#endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION -#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - const indexed_triangle_set& its = GUI::wxGetApp().plater()->model().objects[volume->object_idx()]->volumes[volume->volume_idx()]->mesh().its; - const Polygon volume_hull_2d = its_convex_hull_2d_above(its, volume->world_matrix().cast(), 0.0f); - Polygons intersection_polys = intersection(bed_poly, volume_hull_2d); - bool contained_xy = !intersection_polys.empty() && same(intersection_polys.front(), volume_hull_2d); - bool contained_z = -1e10 < bb.min.z() && bb.max.z() < bed_height; - bool contained = contained_xy && contained_z; - bool intersects_xy = !contained_xy && !intersection_polys.empty(); - bool intersects_z = !contained_z && bb.min.z() < bed_height && -1e10 < bb.max.z(); - bool intersects = intersects_xy || intersects_z; -#else bool contained = print_volume.contains(bb); -#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS +#endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION +#if ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION + } +#endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION volume->is_outside = !contained; if (!volume->printable) @@ -1014,13 +1027,13 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M if (state == ModelInstancePVS_Inside && volume->is_outside) state = ModelInstancePVS_Fully_Outside; -#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS +#if ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION if (state == ModelInstancePVS_Fully_Outside && volume->is_outside && intersects) state = ModelInstancePVS_Partly_Outside; #else if (state == ModelInstancePVS_Fully_Outside && volume->is_outside && print_volume.intersects(bb)) state = ModelInstancePVS_Partly_Outside; -#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS +#endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION } if (out_state != nullptr) From e24bd2d2d4e17944c1e25b06bd72ca867d24868b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 30 Sep 2021 10:23:38 +0200 Subject: [PATCH 08/23] Tech ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION - Removed duplicated code --- src/slic3r/GUI/3DScene.cpp | 4 +--- src/slic3r/GUI/GLCanvas3D.cpp | 37 +++++++---------------------------- 2 files changed, 8 insertions(+), 33 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index a69f95a15..e414a89ce 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -1010,13 +1010,11 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M contained = print_volume.contains(bb); intersects = print_volume.intersects(bb); #endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + } #else const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box(); bool contained = print_volume.contains(bb); #endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION -#if ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION - } -#endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION volume->is_outside = !contained; if (!volume->printable) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 4f9b2d0b3..32c472318 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2115,38 +2115,10 @@ void GLCanvas3D::load_sla_preview() // Release OpenGL data before generating new data. reset_volumes(); _load_sla_shells(); -#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - Bed3D::EShapeType type = wxGetApp().plater()->get_bed().get_shape_type(); - switch (type) - { - case Bed3D::EShapeType::Circle: { - Vec2d center; - double radius; - if (Bed3D::is_circle(wxGetApp().plater()->get_bed().get_shape(), ¢er, &radius)) { - m_volumes.set_print_volume({ static_cast(type), - { float(center.x()), float(center.y()), float(radius) + BedEpsilon, 0.0f }, - { 0.0f, float(m_config->opt_float("max_print_height")) } }); - } - break; - } - case Bed3D::EShapeType::Rectangle: { - const BoundingBoxf3& bed_bb = wxGetApp().plater()->get_bed().get_bounding_box(false); - m_volumes.set_print_volume({ static_cast(type), - { float(bed_bb.min.x()) - BedEpsilon, float(bed_bb.min.y()) - BedEpsilon, float(bed_bb.max.x()) + BedEpsilon, float(bed_bb.max.y()) + BedEpsilon }, - { 0.0f, float(m_config->opt_float("max_print_height")) } }); - break; - } - default: - case Bed3D::EShapeType::Custom: { - m_volumes.set_print_volume({ static_cast(type), - { 0.0f, 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }); - } - } -#else +#if !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS const BoundingBoxf3& bed_bb = wxGetApp().plater()->get_bed().get_bounding_box(false); m_volumes.set_print_box(float(bed_bb.min.x()) - BedEpsilon, float(bed_bb.min.y()) - BedEpsilon, 0.0f, float(bed_bb.max.x()) + BedEpsilon, float(bed_bb.max.y()) + BedEpsilon, (float)m_config->opt_float("max_print_height")); -#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS +#endif // !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS _update_sla_shells_outside_state(); _set_warning_notification_if_needed(EWarning::SlaSupportsOutside); } @@ -5101,6 +5073,9 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type) if (m_picking_enabled) { // Update the layer editing selection to the first object selected, update the current object maximum Z. m_layers_editing.select_object(*m_model, this->is_layers_editing_enabled() ? m_selection.get_object_idx() : -1); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + } +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS if (m_config != nullptr) { #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS @@ -5144,7 +5119,9 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type) m_volumes.check_outside_state(m_config, nullptr); #endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS } +#if !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS } +#endif // !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS if (m_use_clipping_planes) m_volumes.set_z_range(-m_clipping_planes[0].get_data()[3], m_clipping_planes[1].get_data()[3]); From 4eb21d478791986e338ecd54b6f9c33394cd01c7 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 30 Sep 2021 10:35:53 +0200 Subject: [PATCH 09/23] Tech ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION - Enable detection of collision with circular printbed also for SLA printers --- src/slic3r/GUI/3DScene.cpp | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index e414a89ce..59fac373d 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -989,28 +989,21 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M #if ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION bool contained = false; bool intersects = false; - if (GUI::wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) { - const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box(); - contained = print_volume.contains(bb); - intersects = print_volume.intersects(bb); - } - else { - const BoundingBoxf3& bb = volume->transformed_non_sinking_bounding_box(); + const BoundingBoxf3& bb = volume->transformed_non_sinking_bounding_box(); #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - const indexed_triangle_set& its = GUI::wxGetApp().plater()->model().objects[volume->object_idx()]->volumes[volume->volume_idx()]->mesh().its; - const Polygon volume_hull_2d = its_convex_hull_2d_above(its, volume->world_matrix().cast(), 0.0f); - Polygons intersection_polys = intersection(bed_poly, volume_hull_2d); - bool contained_xy = !intersection_polys.empty() && same(intersection_polys.front(), volume_hull_2d); - bool contained_z = -1e10 < bb.min.z() && bb.max.z() < bed_height; - contained = contained_xy && contained_z; - bool intersects_xy = !contained_xy && !intersection_polys.empty(); - bool intersects_z = !contained_z && bb.min.z() < bed_height && -1e10 < bb.max.z(); - intersects = intersects_xy || intersects_z; + const indexed_triangle_set& its = GUI::wxGetApp().plater()->model().objects[volume->object_idx()]->volumes[volume->volume_idx()]->mesh().its; + const Polygon volume_hull_2d = its_convex_hull_2d_above(its, volume->world_matrix().cast(), 0.0f); + Polygons intersection_polys = intersection(bed_poly, volume_hull_2d); + bool contained_xy = !intersection_polys.empty() && same(intersection_polys.front(), volume_hull_2d); + bool contained_z = -1e10 < bb.min.z() && bb.max.z() < bed_height; + contained = contained_xy && contained_z; + bool intersects_xy = !contained_xy && !intersection_polys.empty(); + bool intersects_z = !contained_z && bb.min.z() < bed_height && -1e10 < bb.max.z(); + intersects = intersects_xy || intersects_z; #else - contained = print_volume.contains(bb); - intersects = print_volume.intersects(bb); + contained = print_volume.contains(bb); + intersects = print_volume.intersects(bb); #endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - } #else const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box(); bool contained = print_volume.contains(bb); From 448911df9f859fb83c56d731cb0a9ffbdaa8eefb Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 30 Sep 2021 10:55:41 +0200 Subject: [PATCH 10/23] Tech ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - Removed unused code --- src/slic3r/GUI/3DScene.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 59fac373d..26d32f91b 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -966,8 +966,7 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS const Polygon bed_poly = offset(Polygon::new_scale(opt->values), static_cast(scale_(BedEpsilon))).front(); const float bed_height = config->opt_float("max_print_height"); -#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - +#else const BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values)); BoundingBoxf3 print_volume({ unscale(bed_box_2D.min.x()), unscale(bed_box_2D.min.y()), 0.0 }, { unscale(bed_box_2D.max.x()), unscale(bed_box_2D.max.y()), config->opt_float("max_print_height") }); @@ -977,6 +976,7 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M print_volume.min.y() -= BedEpsilon; print_volume.max.x() += BedEpsilon; print_volume.max.y() += BedEpsilon; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS ModelInstanceEPrintVolumeState state = ModelInstancePVS_Inside; From 7cfe0826211e63f9d9de1be9967ff2f2d580ecaf Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 30 Sep 2021 12:08:05 +0200 Subject: [PATCH 11/23] Tech ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - Enable detection of collision with circular printbed for toolpaths --- src/libslic3r/Geometry.hpp | 26 +++++++++++++ src/slic3r/GUI/3DScene.cpp | 73 +++++++++++++++-------------------- src/slic3r/GUI/3DScene.hpp | 5 +++ src/slic3r/GUI/GLCanvas3D.cpp | 16 ++++++++ 4 files changed, 79 insertions(+), 41 deletions(-) diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index 2ac19b502..11d405e4c 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -84,6 +84,32 @@ static inline bool is_ccw(const Polygon &poly) return o == ORIENTATION_CCW; } +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS +// returns true if the given polygons are identical +static bool are_approx(const Polygon& lhs, const Polygon& rhs) +{ + if (lhs.points.size() != rhs.points.size()) + return false; + + size_t rhs_id = 0; + while (rhs_id < rhs.points.size()) { + if (rhs.points[rhs_id].isApprox(lhs.points.front())) + break; + ++rhs_id; + } + + if (rhs_id == rhs.points.size()) + return false; + + for (size_t i = 0; i < lhs.points.size(); ++i) { + if (!lhs.points[i].isApprox(rhs.points[(i + rhs_id) % lhs.points.size()])) + return false; + } + + return true; +} +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + inline bool ray_ray_intersection(const Vec2d &p1, const Vec2d &v1, const Vec2d &p2, const Vec2d &v2, Vec2d &res) { double denom = v1(0) * v2(1) - v2(0) * v1(1); diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 26d32f91b..9664f27f6 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -10,6 +10,7 @@ #include "GLShader.hpp" #include "GUI_App.hpp" #include "Plater.hpp" +#include "BitmapCache.hpp" #include "libslic3r/ExtrusionEntity.hpp" #include "libslic3r/ExtrusionEntityCollection.hpp" @@ -17,7 +18,6 @@ #include "libslic3r/Print.hpp" #include "libslic3r/SLAPrint.hpp" #include "libslic3r/Slicing.hpp" -#include "slic3r/GUI/BitmapCache.hpp" #include "libslic3r/Format/STL.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/AppConfig.hpp" @@ -67,6 +67,26 @@ void glAssertRecentCallImpl(const char* file_name, unsigned int line, const char namespace Slic3r { +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS +ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const Polygon& obj_hull_2d, double obj_min_z, double obj_max_z) +{ + static const double Z_TOLERANCE = -1e10; + + const Polygons intersection_polys = intersection(printbed_shape, obj_hull_2d); + const bool contained_xy = !intersection_polys.empty() && Geometry::are_approx(intersection_polys.front(), obj_hull_2d); + const bool contained_z = Z_TOLERANCE < obj_min_z && obj_max_z < print_volume_height; + if (contained_xy && contained_z) + return ModelInstancePVS_Inside; + + const bool intersects_xy = !contained_xy && !intersection_polys.empty(); + const bool intersects_z = !contained_z && obj_min_z < print_volume_height && Z_TOLERANCE < obj_max_z; + if (intersects_xy || intersects_z) + return ModelInstancePVS_Partly_Outside; + + return ModelInstancePVS_Fully_Outside; +} +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + #if ENABLE_SMOOTH_NORMALS static void smooth_normals_corner(TriangleMesh& mesh, std::vector& normals) { @@ -929,31 +949,6 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab glsafe(::glDisable(GL_BLEND)); } -#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS -static bool same(const Polygon& lhs, const Polygon& rhs) -{ - if (lhs.points.size() != rhs.points.size()) - return false; - - size_t rhs_id = 0; - while (rhs_id < rhs.points.size()) { - if (rhs.points[rhs_id].isApprox(lhs.points.front())) - break; - ++rhs_id; - } - - if (rhs_id == rhs.points.size()) - return false; - - for (size_t i = 0; i < lhs.points.size(); ++i) { - if (!lhs.points[i].isApprox(rhs.points[(i + rhs_id) % lhs.points.size()])) - return false; - } - - return true; -} -#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state) const { if (config == nullptr) @@ -978,7 +973,7 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M print_volume.max.y() += BedEpsilon; #endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - ModelInstanceEPrintVolumeState state = ModelInstancePVS_Inside; + ModelInstanceEPrintVolumeState overall_state = ModelInstancePVS_Inside; bool contained_min_one = false; @@ -993,13 +988,9 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS const indexed_triangle_set& its = GUI::wxGetApp().plater()->model().objects[volume->object_idx()]->volumes[volume->volume_idx()]->mesh().its; const Polygon volume_hull_2d = its_convex_hull_2d_above(its, volume->world_matrix().cast(), 0.0f); - Polygons intersection_polys = intersection(bed_poly, volume_hull_2d); - bool contained_xy = !intersection_polys.empty() && same(intersection_polys.front(), volume_hull_2d); - bool contained_z = -1e10 < bb.min.z() && bb.max.z() < bed_height; - contained = contained_xy && contained_z; - bool intersects_xy = !contained_xy && !intersection_polys.empty(); - bool intersects_z = !contained_z && bb.min.z() < bed_height && -1e10 < bb.max.z(); - intersects = intersects_xy || intersects_z; + const ModelInstanceEPrintVolumeState volume_state = printbed_collision_state(bed_poly, bed_height, volume_hull_2d, bb.min.z(), bb.max.z()); + contained = (volume_state == ModelInstancePVS_Inside); + intersects = (volume_state == ModelInstancePVS_Partly_Outside); #else contained = print_volume.contains(bb); intersects = print_volume.intersects(bb); @@ -1015,20 +1006,20 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M contained_min_one |= contained; - if (state == ModelInstancePVS_Inside && volume->is_outside) - state = ModelInstancePVS_Fully_Outside; + if (overall_state == ModelInstancePVS_Inside && volume->is_outside) + overall_state = ModelInstancePVS_Fully_Outside; #if ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION - if (state == ModelInstancePVS_Fully_Outside && volume->is_outside && intersects) - state = ModelInstancePVS_Partly_Outside; + if (overall_state == ModelInstancePVS_Fully_Outside && volume->is_outside && intersects) + overall_state = ModelInstancePVS_Partly_Outside; #else - if (state == ModelInstancePVS_Fully_Outside && volume->is_outside && print_volume.intersects(bb)) - state = ModelInstancePVS_Partly_Outside; + if (overall_state == ModelInstancePVS_Fully_Outside && volume->is_outside && print_volume.intersects(bb)) + overall_state = ModelInstancePVS_Partly_Outside; #endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION } if (out_state != nullptr) - *out_state = state; + *out_state = overall_state; return contained_min_one; } diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 9bfd6edeb..b305d0231 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -41,6 +41,11 @@ enum ModelInstanceEPrintVolumeState : unsigned char; // Return appropriate color based on the ModelVolume. std::array color_from_model_volume(const ModelVolume& model_volume); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS +// return the state of given object volume (extrusion along z of obj_hull_2d by obj_height) +// with respect to the given print volume (extrusion along z of printbed_shape by print_volume_height) +ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const Polygon& obj_hull_2d, double obj_min_z, double obj_max_z); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS // A container for interleaved arrays of 3D vertices and normals, // possibly indexed by triangles and / or quads. diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 80cf4f46e..12e829f8b 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5011,9 +5011,25 @@ void GLCanvas3D::_render_background() const if (!m_volumes.empty()) use_error_color &= _is_any_volume_outside(); else { +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + const ConfigOptionPoints* opt = dynamic_cast(m_config->option("bed_shape")); + if (opt != nullptr) { + const Polygon bed_poly = offset(Polygon::new_scale(opt->values), static_cast(scale_(BedEpsilon))).front(); + const float bed_height = m_config->opt_float("max_print_height"); + const BoundingBoxf3& paths_volume = m_gcode_viewer.get_paths_bounding_box(); + Polygon paths_hull_2d; + paths_hull_2d.append({ scale_(paths_volume.min.x()), scale_(paths_volume.min.y()) }); + paths_hull_2d.append({ scale_(paths_volume.max.x()), scale_(paths_volume.min.y()) }); + paths_hull_2d.append({ scale_(paths_volume.max.x()), scale_(paths_volume.max.y()) }); + paths_hull_2d.append({ scale_(paths_volume.min.x()), scale_(paths_volume.max.y()) }); + const ModelInstanceEPrintVolumeState state = printbed_collision_state(bed_poly, bed_height, paths_hull_2d, paths_volume.min.z(), paths_volume.max.z()); + use_error_color &= state != ModelInstancePVS_Inside; + } +#else const BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); const BoundingBoxf3& paths_volume = m_gcode_viewer.get_paths_bounding_box(); use_error_color &= (test_volume.radius() > 0.0 && paths_volume.radius() > 0.0) ? !test_volume.contains(paths_volume) : false; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS } } From 430c7a69b32171b1d04223a9430bd6b590b63f3c Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 30 Sep 2021 13:16:05 +0200 Subject: [PATCH 12/23] Tech ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - Completed detection of collision with circular printbed for toolpaths and sla auxyliary volumes --- src/slic3r/GUI/3DScene.cpp | 25 +++++++++++--- src/slic3r/GUI/3DScene.hpp | 3 ++ src/slic3r/GUI/GLCanvas3D.cpp | 61 ++++++++++++++++++++++++++++------- 3 files changed, 73 insertions(+), 16 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 9664f27f6..a0d2e8c51 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -85,6 +85,17 @@ ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_ return ModelInstancePVS_Fully_Outside; } + +ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const BoundingBoxf3& box) +{ + const Polygon box_hull_2d({ + { scale_(box.min.x()), scale_(box.min.y()) }, + { scale_(box.max.x()), scale_(box.min.y()) }, + { scale_(box.max.x()), scale_(box.max.y()) }, + { scale_(box.min.x()), scale_(box.max.y()) } + }); + return printbed_collision_state(printbed_shape, print_volume_height, box_hull_2d, box.min.z(), box.max.z()); +} #endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS #if ENABLE_SMOOTH_NORMALS @@ -984,11 +995,17 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M #if ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION bool contained = false; bool intersects = false; - const BoundingBoxf3& bb = volume->transformed_non_sinking_bounding_box(); + bool is_sla = GUI::wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA; + const BoundingBoxf3 bb = is_sla ? volume->transformed_convex_hull_bounding_box() : volume->transformed_non_sinking_bounding_box(); #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - const indexed_triangle_set& its = GUI::wxGetApp().plater()->model().objects[volume->object_idx()]->volumes[volume->volume_idx()]->mesh().its; - const Polygon volume_hull_2d = its_convex_hull_2d_above(its, volume->world_matrix().cast(), 0.0f); - const ModelInstanceEPrintVolumeState volume_state = printbed_collision_state(bed_poly, bed_height, volume_hull_2d, bb.min.z(), bb.max.z()); + ModelInstanceEPrintVolumeState volume_state; + if (is_sla) + volume_state = printbed_collision_state(bed_poly, bed_height, bb); + else { + const indexed_triangle_set& its = GUI::wxGetApp().plater()->model().objects[volume->object_idx()]->volumes[volume->volume_idx()]->mesh().its; + const Polygon volume_hull_2d = its_convex_hull_2d_above(its, volume->world_matrix().cast(), 0.0f); + volume_state = printbed_collision_state(bed_poly, bed_height, volume_hull_2d, bb.min.z(), bb.max.z()); + } contained = (volume_state == ModelInstancePVS_Inside); intersects = (volume_state == ModelInstancePVS_Partly_Outside); #else diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index b305d0231..ce57d5410 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -45,6 +45,9 @@ std::array color_from_model_volume(const ModelVolume& model_volume); // return the state of given object volume (extrusion along z of obj_hull_2d by obj_height) // with respect to the given print volume (extrusion along z of printbed_shape by print_volume_height) ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const Polygon& obj_hull_2d, double obj_min_z, double obj_max_z); +// return the state of given box +// with respect to the given print volume (extrusion along z of printbed_shape by print_volume_height) +ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const BoundingBoxf3& box); #endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS // A container for interleaved arrays of 3D vertices and normals, diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 12e829f8b..a5bba3471 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4984,6 +4984,7 @@ void GLCanvas3D::_rectangular_selection_picking_pass() _update_volumes_hover_state(); } +#if !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS static BoundingBoxf3 print_volume(const DynamicPrintConfig& config) { // tolerance to avoid false detection at bed edges @@ -5000,6 +5001,7 @@ static BoundingBoxf3 print_volume(const DynamicPrintConfig& config) } return ret; } +#endif // !ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS void GLCanvas3D::_render_background() const { @@ -5013,18 +5015,17 @@ void GLCanvas3D::_render_background() const else { #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS const ConfigOptionPoints* opt = dynamic_cast(m_config->option("bed_shape")); - if (opt != nullptr) { - const Polygon bed_poly = offset(Polygon::new_scale(opt->values), static_cast(scale_(BedEpsilon))).front(); - const float bed_height = m_config->opt_float("max_print_height"); - const BoundingBoxf3& paths_volume = m_gcode_viewer.get_paths_bounding_box(); - Polygon paths_hull_2d; - paths_hull_2d.append({ scale_(paths_volume.min.x()), scale_(paths_volume.min.y()) }); - paths_hull_2d.append({ scale_(paths_volume.max.x()), scale_(paths_volume.min.y()) }); - paths_hull_2d.append({ scale_(paths_volume.max.x()), scale_(paths_volume.max.y()) }); - paths_hull_2d.append({ scale_(paths_volume.min.x()), scale_(paths_volume.max.y()) }); - const ModelInstanceEPrintVolumeState state = printbed_collision_state(bed_poly, bed_height, paths_hull_2d, paths_volume.min.z(), paths_volume.max.z()); - use_error_color &= state != ModelInstancePVS_Inside; - } + const Polygon bed_poly = offset(Polygon::new_scale(opt->values), static_cast(scale_(BedEpsilon))).front(); + const float bed_height = m_config->opt_float("max_print_height"); + const ModelInstanceEPrintVolumeState state = printbed_collision_state(bed_poly, bed_height, m_gcode_viewer.get_paths_bounding_box()); +// const BoundingBoxf3& paths_volume = m_gcode_viewer.get_paths_bounding_box(); +// Polygon paths_hull_2d; +// paths_hull_2d.append({ scale_(paths_volume.min.x()), scale_(paths_volume.min.y()) }); +// paths_hull_2d.append({ scale_(paths_volume.max.x()), scale_(paths_volume.min.y()) }); +// paths_hull_2d.append({ scale_(paths_volume.max.x()), scale_(paths_volume.max.y()) }); +// paths_hull_2d.append({ scale_(paths_volume.min.x()), scale_(paths_volume.max.y()) }); +// const ModelInstanceEPrintVolumeState state = printbed_collision_state(bed_poly, bed_height, paths_hull_2d, paths_volume.min.z(), paths_volume.max.z()); + use_error_color &= state != ModelInstancePVS_Inside; #else const BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); const BoundingBoxf3& paths_volume = m_gcode_viewer.get_paths_bounding_box(); @@ -6355,18 +6356,46 @@ void GLCanvas3D::_load_sla_shells() void GLCanvas3D::_update_toolpath_volumes_outside_state() { +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + const ConfigOptionPoints* opt = dynamic_cast(m_config->option("bed_shape")); + const Polygon bed_poly = offset(Polygon::new_scale(opt->values), static_cast(scale_(BedEpsilon))).front(); + const float bed_height = m_config->opt_float("max_print_height"); + for (GLVolume* volume : m_volumes.volumes) { + if (volume->is_extrusion_path) { + const ModelInstanceEPrintVolumeState state = printbed_collision_state(bed_poly, bed_height, volume->bounding_box()); + volume->is_outside = (state != ModelInstancePVS_Inside); + } + else + volume->is_outside = false; + } +#else BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); for (GLVolume* volume : m_volumes.volumes) { volume->is_outside = (test_volume.radius() > 0.0 && volume->is_extrusion_path) ? !test_volume.contains(volume->bounding_box()) : false; } +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS } void GLCanvas3D::_update_sla_shells_outside_state() { +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + const ConfigOptionPoints* opt = dynamic_cast(m_config->option("bed_shape")); + const Polygon bed_poly = offset(Polygon::new_scale(opt->values), static_cast(scale_(BedEpsilon))).front(); + const float bed_height = m_config->opt_float("max_print_height"); + for (GLVolume* volume : m_volumes.volumes) { + if (volume->shader_outside_printer_detection_enabled) { + const ModelInstanceEPrintVolumeState state = printbed_collision_state(bed_poly, bed_height, volume->transformed_convex_hull_bounding_box()); + volume->is_outside = (state != ModelInstancePVS_Inside); + } + else + volume->is_outside = false; + } +#else BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); for (GLVolume* volume : m_volumes.volumes) { volume->is_outside = (test_volume.radius() > 0.0 && volume->shader_outside_printer_detection_enabled) ? !test_volume.contains(volume->transformed_convex_hull_bounding_box()) : false; } +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS } void GLCanvas3D::_set_warning_notification_if_needed(EWarning warning) @@ -6377,10 +6406,18 @@ void GLCanvas3D::_set_warning_notification_if_needed(EWarning warning) show = _is_any_volume_outside(); else { if (wxGetApp().is_editor()) { +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + const ConfigOptionPoints* opt = dynamic_cast(m_config->option("bed_shape")); + const Polygon bed_poly = offset(Polygon::new_scale(opt->values), static_cast(scale_(BedEpsilon))).front(); + const float bed_height = m_config->opt_float("max_print_height"); + const ModelInstanceEPrintVolumeState state = printbed_collision_state(bed_poly, bed_height, m_gcode_viewer.get_paths_bounding_box()); + show = state != ModelInstancePVS_Inside; +#else BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); const BoundingBoxf3& paths_volume = m_gcode_viewer.get_paths_bounding_box(); if (test_volume.radius() > 0.0 && paths_volume.radius() > 0.0) show = !test_volume.contains(paths_volume); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS } } _set_warning_notification(warning, show); From 13ef817a994a4b944a613d0e70a6b20fafc4b861 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 30 Sep 2021 14:51:09 +0200 Subject: [PATCH 13/23] Tech ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - Enable detection of collision with circular printbed in backend --- src/libslic3r/Model.cpp | 78 ++++++++++++++++++++++++++++++++++- src/libslic3r/Model.hpp | 18 +++++++- src/slic3r/GUI/3DScene.cpp | 31 -------------- src/slic3r/GUI/3DScene.hpp | 9 ---- src/slic3r/GUI/GLCanvas3D.cpp | 21 +++++----- src/slic3r/GUI/Plater.cpp | 7 ++++ 6 files changed, 111 insertions(+), 53 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 7bc28f904..4546444ee 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -26,6 +26,37 @@ namespace Slic3r { +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS +ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const Polygon& obj_hull_2d, double obj_min_z, double obj_max_z) +{ + static const double Z_TOLERANCE = -1e10; + + const Polygons intersection_polys = intersection(printbed_shape, obj_hull_2d); + const bool contained_xy = !intersection_polys.empty() && Geometry::are_approx(intersection_polys.front(), obj_hull_2d); + const bool contained_z = Z_TOLERANCE < obj_min_z && obj_max_z < print_volume_height; + if (contained_xy && contained_z) + return ModelInstancePVS_Inside; + + const bool intersects_xy = !contained_xy && !intersection_polys.empty(); + const bool intersects_z = !contained_z && obj_min_z < print_volume_height&& Z_TOLERANCE < obj_max_z; + if (intersects_xy || intersects_z) + return ModelInstancePVS_Partly_Outside; + + return ModelInstancePVS_Fully_Outside; +} + +ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const BoundingBoxf3& box) +{ + const Polygon box_hull_2d({ + { scale_(box.min.x()), scale_(box.min.y()) }, + { scale_(box.max.x()), scale_(box.min.y()) }, + { scale_(box.max.x()), scale_(box.max.y()) }, + { scale_(box.min.x()), scale_(box.max.y()) } + }); + return printbed_collision_state(printbed_shape, print_volume_height, box_hull_2d, box.min.z(), box.max.z()); +} +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + Model& Model::assign_copy(const Model &rhs) { this->copy_id(rhs); @@ -330,13 +361,23 @@ BoundingBoxf3 Model::bounding_box() const return bb; } -unsigned int Model::update_print_volume_state(const BoundingBoxf3 &print_volume) +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS +unsigned int Model::update_print_volume_state(const Polygon& printbed_shape, double print_volume_height) +{ + unsigned int num_printable = 0; + for (ModelObject* model_object : this->objects) + num_printable += model_object->check_instances_print_volume_state(printbed_shape, print_volume_height); + return num_printable; +} +#else +unsigned int Model::update_print_volume_state(const BoundingBoxf3 &print_volume) { unsigned int num_printable = 0; for (ModelObject *model_object : this->objects) num_printable += model_object->check_instances_print_volume_state(print_volume); return num_printable; } +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS bool Model::center_instances_around_point(const Vec2d &point) { @@ -1513,6 +1554,40 @@ double ModelObject::get_instance_max_z(size_t instance_idx) const return max_z + inst->get_offset(Z); } +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS +unsigned int ModelObject::check_instances_print_volume_state(const Polygon& printbed_shape, double print_volume_height) +{ + unsigned int num_printable = 0; + enum { + INSIDE = 1, + OUTSIDE = 2 + }; + for (ModelInstance* model_instance : this->instances) { + unsigned int inside_outside = 0; + for (const ModelVolume* vol : this->volumes) + if (vol->is_model_part()) { +#if ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION + const BoundingBoxf3 bb = vol->mesh().transformed_bounding_box(model_instance->get_matrix() * vol->get_matrix(), 0.0); +#else + const BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(model_instance->get_matrix() * vol->get_matrix()); +#endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION + ModelInstanceEPrintVolumeState state = printbed_collision_state(printbed_shape, print_volume_height, bb); + if (state == ModelInstancePVS_Inside) + inside_outside |= INSIDE; + else if (state == ModelInstancePVS_Fully_Outside) + inside_outside |= OUTSIDE; + else + inside_outside |= INSIDE | OUTSIDE; + } + model_instance->print_volume_state = + (inside_outside == (INSIDE | OUTSIDE)) ? ModelInstancePVS_Partly_Outside : + (inside_outside == INSIDE) ? ModelInstancePVS_Inside : ModelInstancePVS_Fully_Outside; + if (inside_outside == INSIDE) + ++num_printable; + } + return num_printable; +} +#else unsigned int ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume) { unsigned int num_printable = 0; @@ -1544,6 +1619,7 @@ unsigned int ModelObject::check_instances_print_volume_state(const BoundingBoxf3 } return num_printable; } +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS void ModelObject::print_info() const { diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index ea1d0ed17..1b92e01ed 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -367,7 +367,11 @@ public: double get_instance_max_z(size_t instance_idx) const; // Called by Print::validate() from the UI thread. +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + unsigned int check_instances_print_volume_state(const Polygon& printbed_shape, double print_volume_height); +#else unsigned int check_instances_print_volume_state(const BoundingBoxf3& print_volume); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS // Print object statistics to console. void print_info() const; @@ -904,6 +908,14 @@ enum ModelInstanceEPrintVolumeState : unsigned char ModelInstanceNum_BedStates }; +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS +// return the state of the given object's volume (extrusion along z of obj_hull_2d from obj_min_z to obj_max_z) +// with respect to the given print volume (extrusion along z of printbed_shape from zero to print_volume_height) +ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const Polygon& obj_hull_2d, double obj_min_z, double obj_max_z); +// return the state of the given box +// with respect to the given print volume (extrusion along z of printbed_shape from zero to print_volume_height) +ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const BoundingBoxf3& box); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS // A single instance of a ModelObject. // Knows the affine transformation of an object. @@ -1109,8 +1121,12 @@ public: BoundingBoxf3 bounding_box() const; // Set the print_volume_state of PrintObject::instances, // return total number of printable objects. +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + unsigned int update_print_volume_state(const Polygon& printbed_shape, double print_volume_height); +#else unsigned int update_print_volume_state(const BoundingBoxf3 &print_volume); - // Returns true if any ModelObject was modified. +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + // Returns true if any ModelObject was modified. bool center_instances_around_point(const Vec2d &point); void translate(coordf_t x, coordf_t y, coordf_t z) { for (ModelObject *o : this->objects) o->translate(x, y, z); } TriangleMesh mesh() const; diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index a0d2e8c51..78da38b13 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -67,37 +67,6 @@ void glAssertRecentCallImpl(const char* file_name, unsigned int line, const char namespace Slic3r { -#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS -ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const Polygon& obj_hull_2d, double obj_min_z, double obj_max_z) -{ - static const double Z_TOLERANCE = -1e10; - - const Polygons intersection_polys = intersection(printbed_shape, obj_hull_2d); - const bool contained_xy = !intersection_polys.empty() && Geometry::are_approx(intersection_polys.front(), obj_hull_2d); - const bool contained_z = Z_TOLERANCE < obj_min_z && obj_max_z < print_volume_height; - if (contained_xy && contained_z) - return ModelInstancePVS_Inside; - - const bool intersects_xy = !contained_xy && !intersection_polys.empty(); - const bool intersects_z = !contained_z && obj_min_z < print_volume_height && Z_TOLERANCE < obj_max_z; - if (intersects_xy || intersects_z) - return ModelInstancePVS_Partly_Outside; - - return ModelInstancePVS_Fully_Outside; -} - -ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const BoundingBoxf3& box) -{ - const Polygon box_hull_2d({ - { scale_(box.min.x()), scale_(box.min.y()) }, - { scale_(box.max.x()), scale_(box.min.y()) }, - { scale_(box.max.x()), scale_(box.max.y()) }, - { scale_(box.min.x()), scale_(box.max.y()) } - }); - return printbed_collision_state(printbed_shape, print_volume_height, box_hull_2d, box.min.z(), box.max.z()); -} -#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - #if ENABLE_SMOOTH_NORMALS static void smooth_normals_corner(TriangleMesh& mesh, std::vector& normals) { diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index ce57d5410..7df772c7f 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -41,15 +41,6 @@ enum ModelInstanceEPrintVolumeState : unsigned char; // Return appropriate color based on the ModelVolume. std::array color_from_model_volume(const ModelVolume& model_volume); -#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS -// return the state of given object volume (extrusion along z of obj_hull_2d by obj_height) -// with respect to the given print volume (extrusion along z of printbed_shape by print_volume_height) -ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const Polygon& obj_hull_2d, double obj_min_z, double obj_max_z); -// return the state of given box -// with respect to the given print volume (extrusion along z of printbed_shape by print_volume_height) -ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const BoundingBoxf3& box); -#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - // A container for interleaved arrays of 3D vertices and normals, // possibly indexed by triangles and / or quads. class GLIndexedVertexArray { diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index a5bba3471..66e231566 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5014,17 +5014,16 @@ void GLCanvas3D::_render_background() const use_error_color &= _is_any_volume_outside(); else { #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - const ConfigOptionPoints* opt = dynamic_cast(m_config->option("bed_shape")); - const Polygon bed_poly = offset(Polygon::new_scale(opt->values), static_cast(scale_(BedEpsilon))).front(); - const float bed_height = m_config->opt_float("max_print_height"); - const ModelInstanceEPrintVolumeState state = printbed_collision_state(bed_poly, bed_height, m_gcode_viewer.get_paths_bounding_box()); -// const BoundingBoxf3& paths_volume = m_gcode_viewer.get_paths_bounding_box(); -// Polygon paths_hull_2d; -// paths_hull_2d.append({ scale_(paths_volume.min.x()), scale_(paths_volume.min.y()) }); -// paths_hull_2d.append({ scale_(paths_volume.max.x()), scale_(paths_volume.min.y()) }); -// paths_hull_2d.append({ scale_(paths_volume.max.x()), scale_(paths_volume.max.y()) }); -// paths_hull_2d.append({ scale_(paths_volume.min.x()), scale_(paths_volume.max.y()) }); -// const ModelInstanceEPrintVolumeState state = printbed_collision_state(bed_poly, bed_height, paths_hull_2d, paths_volume.min.z(), paths_volume.max.z()); + ModelInstanceEPrintVolumeState state; + if (m_gcode_viewer.has_data()) { + const ConfigOptionPoints* opt = dynamic_cast(m_config->option("bed_shape")); + const Polygon bed_poly = offset(Polygon::new_scale(opt->values), static_cast(scale_(BedEpsilon))).front(); + const float bed_height = m_config->opt_float("max_print_height"); + state = printbed_collision_state(bed_poly, bed_height, m_gcode_viewer.get_paths_bounding_box()); + } + else + state = ModelInstancePVS_Inside; + use_error_color &= state != ModelInstancePVS_Inside; #else const BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 87b83ec24..373323038 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2946,12 +2946,19 @@ void Plater::priv::schedule_background_process() void Plater::priv::update_print_volume_state() { +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + const ConfigOptionPoints* opt = dynamic_cast(this->config->option("bed_shape")); + const Polygon bed_poly = offset(Polygon::new_scale(opt->values), static_cast(scale_(BedEpsilon))).front(); + const float bed_height = this->config->opt_float("max_print_height"); + this->q->model().update_print_volume_state(bed_poly, bed_height); +#else BoundingBox bed_box_2D = get_extents(Polygon::new_scale(this->config->opt("bed_shape")->values)); BoundingBoxf3 print_volume(unscale(bed_box_2D.min(0), bed_box_2D.min(1), 0.0), unscale(bed_box_2D.max(0), bed_box_2D.max(1), scale_(this->config->opt_float("max_print_height")))); // Allow the objects to protrude below the print bed, only the part of the object above the print bed will be sliced. print_volume.offset(BedEpsilon); print_volume.min(2) = -1e10; this->q->model().update_print_volume_state(print_volume); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS } From f3bd5e96c5769401caaac007fcae097d46f260e4 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 1 Oct 2021 07:28:10 +0200 Subject: [PATCH 14/23] Tech ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - Fixed detection of collision with circular printbed in backend --- src/libslic3r/Geometry.hpp | 2 +- src/libslic3r/Model.cpp | 8 +++++--- src/libslic3r/Technologies.hpp | 9 ++++++++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index 11d405e4c..7c670bb7c 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -86,7 +86,7 @@ static inline bool is_ccw(const Polygon &poly) #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS // returns true if the given polygons are identical -static bool are_approx(const Polygon& lhs, const Polygon& rhs) +static inline bool are_approx(const Polygon& lhs, const Polygon& rhs) { if (lhs.points.size() != rhs.points.size()) return false; diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 4546444ee..73661723a 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1566,12 +1566,14 @@ unsigned int ModelObject::check_instances_print_volume_state(const Polygon& prin unsigned int inside_outside = 0; for (const ModelVolume* vol : this->volumes) if (vol->is_model_part()) { + const Transform3d matrix = model_instance->get_matrix() * vol->get_matrix(); #if ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION - const BoundingBoxf3 bb = vol->mesh().transformed_bounding_box(model_instance->get_matrix() * vol->get_matrix(), 0.0); + const BoundingBoxf3 bb = vol->mesh().transformed_bounding_box(matrix, 0.0); #else - const BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(model_instance->get_matrix() * vol->get_matrix()); + const BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(matrix); #endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION - ModelInstanceEPrintVolumeState state = printbed_collision_state(printbed_shape, print_volume_height, bb); + const Polygon volume_hull_2d = its_convex_hull_2d_above(vol->mesh().its, matrix.cast(), 0.0f); + ModelInstanceEPrintVolumeState state = printbed_collision_state(printbed_shape, print_volume_height, volume_hull_2d, bb.min.z(), bb.max.z()); if (state == ModelInstancePVS_Inside) inside_outside |= INSIDE; else if (state == ModelInstancePVS_Fully_Outside) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index c096dfcf6..365493a66 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -71,8 +71,15 @@ // Enable fixing loading of gcode files generated with SuperSlicer in GCodeViewer #define ENABLE_FIX_SUPERSLICER_GCODE_IMPORT (1 && ENABLE_2_4_0_ALPHA3) + + +//==================== +// 2.4.0.alpha4 techs +//==================== +#define ENABLE_2_4_0_ALPHA4 1 + // Enable the fix for the detection of the out of bed state for sinking objects -#define ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION (1 && ENABLE_2_4_0_ALPHA3) +#define ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION (1 && ENABLE_2_4_0_ALPHA4) // Enable detection of out of bed using the bed perimeter and other improvements #define ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS (1 && ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION) From 6ff4d6c3f5a748d4797a911e60064dcd6638422f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 1 Oct 2021 12:15:14 +0200 Subject: [PATCH 15/23] Tech ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - Fixed detection of collision with circular printbed for pre-gcode preview and sla preview --- src/slic3r/GUI/3DScene.cpp | 144 +++++++++++++++++++++++++++++----- src/slic3r/GUI/3DScene.hpp | 6 ++ src/slic3r/GUI/GLCanvas3D.cpp | 58 +++++++++----- 3 files changed, 170 insertions(+), 38 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 78da38b13..4eb1837c7 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -37,6 +37,12 @@ #include +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS +#include +#include +#include +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + #ifdef HAS_GLSAFE void glAssertRecentCallImpl(const char* file_name, unsigned int line, const char* function_name) { @@ -614,6 +620,106 @@ void GLVolume::render_sinking_contours() m_sinking_contours.render(); } +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS +void GLVolume::calc_convex_hull_3d() +{ + if (this->indexed_vertex_array.vertices_and_normals_interleaved.empty()) + return; + + TriangleMesh mesh; + for (size_t i = 0; i < this->indexed_vertex_array.vertices_and_normals_interleaved.size(); i += 6) { + const size_t v_id = 3 + i; + mesh.its.vertices.push_back({ this->indexed_vertex_array.vertices_and_normals_interleaved[v_id + 0], + this->indexed_vertex_array.vertices_and_normals_interleaved[v_id + 1], + this->indexed_vertex_array.vertices_and_normals_interleaved[v_id + 2] + }); + } + + const std::vector& vertices = mesh.its.vertices; + + // The qhull call: + orgQhull::Qhull qhull; + qhull.disableOutputStream(); // we want qhull to be quiet + std::vector src_vertices; + try + { +#if REALfloat + qhull.runQhull("", 3, (int)vertices.size(), (const realT*)(vertices.front().data()), "Qt"); +#else + src_vertices.reserve(vertices.size() * 3); + // We will now fill the vector with input points for computation: + for (const stl_vertex& v : vertices) + for (int i = 0; i < 3; ++i) + src_vertices.emplace_back(v(i)); + qhull.runQhull("", 3, (int)src_vertices.size() / 3, src_vertices.data(), "Qt"); +#endif + } + catch (...) + { + std::cout << "GLVolume::calc_convex_hull_3d() - Unable to create convex hull" << std::endl; + return ; + } + + // Let's collect results: + std::vector dst_vertices; + std::vector dst_facets; + // Map of QHull's vertex ID to our own vertex ID (pointing to dst_vertices). + std::vector map_dst_vertices; +#ifndef NDEBUG + Vec3f centroid = Vec3f::Zero(); + for (const auto& pt : vertices) + centroid += pt; + centroid /= float(vertices.size()); +#endif // NDEBUG + for (const orgQhull::QhullFacet& facet : qhull.facetList()) { + // Collect face vertices first, allocate unique vertices in dst_vertices based on QHull's vertex ID. + Vec3i indices; + int cnt = 0; + for (const orgQhull::QhullVertex vertex : facet.vertices()) { + const int id = vertex.id(); + assert(id >= 0); + if (id >= int(map_dst_vertices.size())) + map_dst_vertices.resize(next_highest_power_of_2(size_t(id + 1)), -1); + if (int i = map_dst_vertices[id]; i == -1) { + // Allocate a new vertex. + i = int(dst_vertices.size()); + map_dst_vertices[id] = i; + orgQhull::QhullPoint pt(vertex.point()); + dst_vertices.emplace_back(pt[0], pt[1], pt[2]); + indices[cnt] = i; + } + else + // Reuse existing vertex. + indices[cnt] = i; + + if (cnt++ == 3) + break; + } + assert(cnt == 3); + if (cnt == 3) { + // QHull sorts vertices of a face lexicographically by their IDs, not by face normals. + // Calculate face normal based on the order of vertices. + const Vec3f n = (dst_vertices[indices(1)] - dst_vertices[indices(0)]).cross(dst_vertices[indices(2)] - dst_vertices[indices(1)]); + auto* n2 = facet.getBaseT()->normal; + const auto d = n.x() * n2[0] + n.y() * n2[1] + n.z() * n2[2]; +#ifndef NDEBUG + const Vec3f n3 = (dst_vertices[indices(0)] - centroid); + const auto d3 = n.dot(n3); + assert((d < 0.f) == (d3 < 0.f)); +#endif // NDEBUG + // Get the face normal from QHull. + if (d < 0.f) + // Fix face orientation. + std::swap(indices[1], indices[2]); + dst_facets.emplace_back(indices); + } + } + + TriangleMesh out_mesh{ std::move(dst_vertices), std::move(dst_facets) }; + this->set_convex_hull(out_mesh); +} +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + std::vector GLVolumeCollection::load_object( const ModelObject *model_object, int obj_idx, @@ -771,7 +877,10 @@ int GLVolumeCollection::load_wipe_tower_preview( volumes.emplace_back(new GLVolume(color)); GLVolume& v = *volumes.back(); - v.indexed_vertex_array.load_mesh(mesh); + v.indexed_vertex_array.load_mesh(mesh); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + v.calc_convex_hull_3d(); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS v.indexed_vertex_array.finalize_geometry(opengl_initialized); v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0)); v.set_volume_rotation(Vec3d(0., 0., (M_PI / 180.) * rotation_angle)); @@ -964,12 +1073,16 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M #if ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION bool contained = false; bool intersects = false; - bool is_sla = GUI::wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA; - const BoundingBoxf3 bb = is_sla ? volume->transformed_convex_hull_bounding_box() : volume->transformed_non_sinking_bounding_box(); + bool is_aux_volume = volume->is_sla_support() || volume->is_sla_pad() || volume->is_wipe_tower; + const BoundingBoxf3 bb = is_aux_volume ? volume->transformed_convex_hull_bounding_box() : volume->transformed_non_sinking_bounding_box(); #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS ModelInstanceEPrintVolumeState volume_state; - if (is_sla) - volume_state = printbed_collision_state(bed_poly, bed_height, bb); + if (is_aux_volume) { + if (volume->is_sla_support() || volume->is_wipe_tower) { + const Polygon volume_hull_2d = its_convex_hull_2d_above(volume->convex_hull()->its, volume->world_matrix().cast(), 0.0f); + volume_state = printbed_collision_state(bed_poly, bed_height, volume_hull_2d, bb.min.z(), bb.max.z()); + } + } else { const indexed_triangle_set& its = GUI::wxGetApp().plater()->model().objects[volume->object_idx()]->volumes[volume->volume_idx()]->mesh().its; const Polygon volume_hull_2d = its_convex_hull_2d_above(its, volume->world_matrix().cast(), 0.0f); @@ -1061,35 +1174,28 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con std::vector colors(colors_count); unsigned char rgb[3]; - for (unsigned int i = 0; i < colors_count; ++i) - { + for (unsigned int i = 0; i < colors_count; ++i) { const std::string& txt_color = config->opt_string("extruder_colour", i); if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb)) - { colors[i].set(txt_color, rgb); - } - else - { + else { const std::string& txt_color = config->opt_string("filament_colour", i); if (Slic3r::GUI::BitmapCache::parse_color(txt_color, rgb)) colors[i].set(txt_color, rgb); } } - for (GLVolume* volume : volumes) - { - if ((volume == nullptr) || volume->is_modifier || volume->is_wipe_tower || (volume->volume_idx() < 0)) + for (GLVolume* volume : volumes) { + if (volume == nullptr || volume->is_modifier || volume->is_wipe_tower || (volume->volume_idx() < 0)) continue; int extruder_id = volume->extruder_id - 1; - if ((extruder_id < 0) || ((int)colors.size() <= extruder_id)) + if (extruder_id < 0 || (int)colors.size() <= extruder_id) extruder_id = 0; const Color& color = colors[extruder_id]; - if (!color.text.empty()) - { - for (int i = 0; i < 3; ++i) - { + if (!color.text.empty()) { + for (int i = 0; i < 3; ++i) { volume->color[i] = (float)color.rgb[i] * inv_255; } } diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 7df772c7f..8aaf48549 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -515,6 +515,12 @@ public: // Return an estimate of the memory held by GPU vertex buffers. size_t gpu_memory_used() const { return this->indexed_vertex_array.gpu_memory_used(); } size_t total_memory_used() const { return this->cpu_memory_used() + this->gpu_memory_used(); } + +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + // calculates the 3D convex hull from indexed_vertex_array.vertices_and_normals_interleaved + // must be called before calling indexed_vertex_array.finalize_geometry(); + void calc_convex_hull_3d(); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS }; typedef std::vector GLVolumePtrs; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 305cdd9d3..84c5053ef 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1826,8 +1826,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re assert(volume_idx_wipe_tower_old == -1); volume_idx_wipe_tower_old = (int)volume_id; } - if (!m_reload_delayed) - { + if (!m_reload_delayed) { deleted_volumes.emplace_back(volume, volume_id); delete volume; } @@ -5016,8 +5015,7 @@ void GLCanvas3D::_render_background() const #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS ModelInstanceEPrintVolumeState state; if (m_gcode_viewer.has_data()) { - const ConfigOptionPoints* opt = dynamic_cast(m_config->option("bed_shape")); - const Polygon bed_poly = offset(Polygon::new_scale(opt->values), static_cast(scale_(BedEpsilon))).front(); + const Polygon bed_poly = offset(Polygon::new_scale(wxGetApp().plater()->get_bed().get_shape()), static_cast(scale_(BedEpsilon))).front(); const float bed_height = m_config->opt_float("max_print_height"); state = printbed_collision_state(bed_poly, bed_height, m_gcode_viewer.get_paths_bounding_box()); } @@ -5813,7 +5811,7 @@ void GLCanvas3D::_load_print_toolpaths() total_layer_count = std::max(total_layer_count, print_object->total_layer_count()); } size_t skirt_height = print->has_infinite_skirt() ? total_layer_count : std::min(print->config().skirt_height.value, total_layer_count); - if ((skirt_height == 0) && print->has_brim()) + if (skirt_height == 0 && print->has_brim()) skirt_height = 1; // Get first skirt_height layers. @@ -5846,6 +5844,9 @@ void GLCanvas3D::_load_print_toolpaths() reserve_new_volume_finalize_old_volume(*volume, vol, m_initialized); } } +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + volume->calc_convex_hull_3d(); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS volume->indexed_vertex_array.finalize_geometry(m_initialized); } @@ -6136,8 +6137,16 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(), [](const GLVolume *volume) { return volume->empty(); }), m_volumes.volumes.end()); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) { + GLVolume* v = m_volumes.volumes[i]; + v->calc_convex_hull_3d(); + v->indexed_vertex_array.finalize_geometry(m_initialized); + } +#else for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_initialized); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end" << m_volumes.log_memory_info() << log_memory_info(); } @@ -6145,7 +6154,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector& str_tool_colors) { const Print *print = this->fff_print(); - if ((print == nullptr) || print->wipe_tower_data().tool_changes.empty()) + if (print == nullptr || print->wipe_tower_data().tool_changes.empty()) return; if (!print->is_step_done(psWipeTower)) @@ -6293,8 +6302,16 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector& str_ std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(), [](const GLVolume *volume) { return volume->empty(); }), m_volumes.volumes.end()); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) { + GLVolume* v = m_volumes.volumes[i]; + v->calc_convex_hull_3d(); + v->indexed_vertex_array.finalize_geometry(m_initialized); + } +#else for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_initialized); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end" << m_volumes.log_memory_info() << log_memory_info(); } @@ -6357,12 +6374,13 @@ void GLCanvas3D::_load_sla_shells() void GLCanvas3D::_update_toolpath_volumes_outside_state() { #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - const ConfigOptionPoints* opt = dynamic_cast(m_config->option("bed_shape")); - const Polygon bed_poly = offset(Polygon::new_scale(opt->values), static_cast(scale_(BedEpsilon))).front(); + const Polygon bed_poly = offset(Polygon::new_scale(wxGetApp().plater()->get_bed().get_shape()), static_cast(scale_(BedEpsilon))).front(); const float bed_height = m_config->opt_float("max_print_height"); for (GLVolume* volume : m_volumes.volumes) { if (volume->is_extrusion_path) { - const ModelInstanceEPrintVolumeState state = printbed_collision_state(bed_poly, bed_height, volume->bounding_box()); + const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box(); + const Polygon volume_hull_2d = its_convex_hull_2d_above(volume->convex_hull()->its, volume->world_matrix().cast(), 0.0f); + const ModelInstanceEPrintVolumeState state = printbed_collision_state(bed_poly, bed_height, volume_hull_2d, bb.min.z(), bb.max.z()); volume->is_outside = (state != ModelInstancePVS_Inside); } else @@ -6379,12 +6397,13 @@ void GLCanvas3D::_update_toolpath_volumes_outside_state() void GLCanvas3D::_update_sla_shells_outside_state() { #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - const ConfigOptionPoints* opt = dynamic_cast(m_config->option("bed_shape")); - const Polygon bed_poly = offset(Polygon::new_scale(opt->values), static_cast(scale_(BedEpsilon))).front(); + const Polygon bed_poly = offset(Polygon::new_scale(wxGetApp().plater()->get_bed().get_shape()), static_cast(scale_(BedEpsilon))).front(); const float bed_height = m_config->opt_float("max_print_height"); for (GLVolume* volume : m_volumes.volumes) { if (volume->shader_outside_printer_detection_enabled) { - const ModelInstanceEPrintVolumeState state = printbed_collision_state(bed_poly, bed_height, volume->transformed_convex_hull_bounding_box()); + const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box(); + const Polygon volume_hull_2d = its_convex_hull_2d_above(volume->convex_hull()->its, volume->world_matrix().cast(), 0.0f); + const ModelInstanceEPrintVolumeState state = printbed_collision_state(bed_poly, bed_height, volume_hull_2d, bb.min.z(), bb.max.z()); volume->is_outside = (state != ModelInstancePVS_Inside); } else @@ -6407,11 +6426,12 @@ void GLCanvas3D::_set_warning_notification_if_needed(EWarning warning) else { if (wxGetApp().is_editor()) { #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - const ConfigOptionPoints* opt = dynamic_cast(m_config->option("bed_shape")); - const Polygon bed_poly = offset(Polygon::new_scale(opt->values), static_cast(scale_(BedEpsilon))).front(); - const float bed_height = m_config->opt_float("max_print_height"); - const ModelInstanceEPrintVolumeState state = printbed_collision_state(bed_poly, bed_height, m_gcode_viewer.get_paths_bounding_box()); - show = state != ModelInstancePVS_Inside; + if (current_printer_technology() != ptSLA) { + const Polygon bed_poly = offset(Polygon::new_scale(wxGetApp().plater()->get_bed().get_shape()), static_cast(scale_(BedEpsilon))).front(); + const float bed_height = m_config->opt_float("max_print_height"); + const ModelInstanceEPrintVolumeState state = printbed_collision_state(bed_poly, bed_height, m_gcode_viewer.get_paths_bounding_box()); + show = state != ModelInstancePVS_Inside; + } #else BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); const BoundingBoxf3& paths_volume = m_gcode_viewer.get_paths_bounding_box(); @@ -6464,7 +6484,7 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) "Resolve the current problem to continue slicing."); error = ErrorType::PLATER_ERROR; break; -} + } auto& notification_manager = *wxGetApp().plater()->get_notification_manager(); switch (error) { @@ -6494,7 +6514,7 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) bool GLCanvas3D::_is_any_volume_outside() const { for (const GLVolume* volume : m_volumes.volumes) { - if ((volume != nullptr) && volume->is_outside) + if (volume != nullptr && volume->is_outside) return true; } From f84838028b5a8b86338b861777eb507c298f7bba Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 1 Oct 2021 13:13:26 +0200 Subject: [PATCH 16/23] Tech ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION merged into tech ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS --- src/libslic3r/Model.cpp | 8 -------- src/libslic3r/Technologies.hpp | 5 ++--- src/libslic3r/TriangleMesh.cpp | 4 ++-- src/libslic3r/TriangleMesh.hpp | 4 ++-- src/slic3r/GUI/3DScene.cpp | 17 ++++++----------- src/slic3r/GUI/3DScene.hpp | 12 ++++++------ 6 files changed, 18 insertions(+), 32 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 73661723a..95d2c8f81 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1567,11 +1567,7 @@ unsigned int ModelObject::check_instances_print_volume_state(const Polygon& prin for (const ModelVolume* vol : this->volumes) if (vol->is_model_part()) { const Transform3d matrix = model_instance->get_matrix() * vol->get_matrix(); -#if ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION const BoundingBoxf3 bb = vol->mesh().transformed_bounding_box(matrix, 0.0); -#else - const BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(matrix); -#endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION const Polygon volume_hull_2d = its_convex_hull_2d_above(vol->mesh().its, matrix.cast(), 0.0f); ModelInstanceEPrintVolumeState state = printbed_collision_state(printbed_shape, print_volume_height, volume_hull_2d, bb.min.z(), bb.max.z()); if (state == ModelInstancePVS_Inside) @@ -1601,11 +1597,7 @@ unsigned int ModelObject::check_instances_print_volume_state(const BoundingBoxf3 unsigned int inside_outside = 0; for (const ModelVolume *vol : this->volumes) if (vol->is_model_part()) { -#if ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION - BoundingBoxf3 bb = vol->mesh().transformed_bounding_box(model_instance->get_matrix() * vol->get_matrix(), 0.0); -#else BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(model_instance->get_matrix() * vol->get_matrix()); -#endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION if (print_volume.contains(bb)) inside_outside |= INSIDE; else if (print_volume.intersects(bb)) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 365493a66..7f24b6ede 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -79,9 +79,8 @@ #define ENABLE_2_4_0_ALPHA4 1 // Enable the fix for the detection of the out of bed state for sinking objects -#define ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION (1 && ENABLE_2_4_0_ALPHA4) -// Enable detection of out of bed using the bed perimeter and other improvements -#define ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS (1 && ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION) +// and detection of out of bed using the bed perimeter +#define ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS (1 && ENABLE_2_4_0_ALPHA4) #endif // _prusaslicer_technologies_h_ diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 6386591ae..290cb95e1 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -429,7 +429,7 @@ BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d &trafo) c return bbox; } -#if ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d& trafo, double world_min_z) const { BoundingBoxf3 bbox; @@ -452,7 +452,7 @@ BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d& trafo, d } return bbox; } -#endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS TriangleMesh TriangleMesh::convex_hull_3d() const { diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index b6a3c7249..b730a7608 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -113,10 +113,10 @@ public: BoundingBoxf3 bounding_box() const; // Returns the bbox of this TriangleMesh transformed by the given transformation BoundingBoxf3 transformed_bounding_box(const Transform3d &trafo) const; -#if ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS // Variant returning the bbox of the part of this TriangleMesh above the given world_min_z BoundingBoxf3 transformed_bounding_box(const Transform3d& trafo, double world_min_z) const; -#endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS // Return the size of the mesh in coordinates. Vec3d size() const { return m_stats.size.cast(); } /// Return the center of the related bounding box. diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 4eb1837c7..f3e4cfad1 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -529,7 +529,7 @@ BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box(const Transform3d & bounding_box().transformed(trafo); } -#if ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS BoundingBoxf3 GLVolume::transformed_non_sinking_bounding_box(const Transform3d& trafo) const { return GUI::wxGetApp().plater()->model().objects[object_idx()]->volumes[volume_idx()]->mesh().transformed_bounding_box(trafo, 0.0); @@ -544,7 +544,7 @@ const BoundingBoxf3& GLVolume::transformed_non_sinking_bounding_box() const } return *m_transformed_non_sinking_bounding_box; } -#endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS void GLVolume::set_range(double min_z, double max_z) { @@ -1070,12 +1070,11 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M if (volume->is_modifier || (!volume->shader_outside_printer_detection_enabled && (volume->is_wipe_tower || volume->composite_id.volume_id < 0))) continue; -#if ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS bool contained = false; bool intersects = false; bool is_aux_volume = volume->is_sla_support() || volume->is_sla_pad() || volume->is_wipe_tower; const BoundingBoxf3 bb = is_aux_volume ? volume->transformed_convex_hull_bounding_box() : volume->transformed_non_sinking_bounding_box(); -#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS ModelInstanceEPrintVolumeState volume_state; if (is_aux_volume) { if (volume->is_sla_support() || volume->is_wipe_tower) { @@ -1090,14 +1089,10 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M } contained = (volume_state == ModelInstancePVS_Inside); intersects = (volume_state == ModelInstancePVS_Partly_Outside); -#else - contained = print_volume.contains(bb); - intersects = print_volume.intersects(bb); -#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS #else const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box(); bool contained = print_volume.contains(bb); -#endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS volume->is_outside = !contained; if (!volume->printable) @@ -1108,13 +1103,13 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M if (overall_state == ModelInstancePVS_Inside && volume->is_outside) overall_state = ModelInstancePVS_Fully_Outside; -#if ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS if (overall_state == ModelInstancePVS_Fully_Outside && volume->is_outside && intersects) overall_state = ModelInstancePVS_Partly_Outside; #else if (overall_state == ModelInstancePVS_Fully_Outside && volume->is_outside && print_volume.intersects(bb)) overall_state = ModelInstancePVS_Partly_Outside; -#endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS } if (out_state != nullptr) diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 8aaf48549..0b1293f56 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -278,10 +278,10 @@ private: std::shared_ptr m_convex_hull; // Bounding box of this volume, in unscaled coordinates. std::optional m_transformed_convex_hull_bounding_box; -#if ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS // Bounding box of the non sinking part of this volume, in unscaled coordinates. std::optional m_transformed_non_sinking_bounding_box; -#endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS class SinkingContours { @@ -472,12 +472,12 @@ public: BoundingBoxf3 transformed_convex_hull_bounding_box(const Transform3d &trafo) const; // caching variant const BoundingBoxf3& transformed_convex_hull_bounding_box() const; -#if ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS // non-caching variant BoundingBoxf3 transformed_non_sinking_bounding_box(const Transform3d& trafo) const; // caching variant const BoundingBoxf3& transformed_non_sinking_bounding_box() const; -#endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS // convex hull const TriangleMesh* convex_hull() const { return m_convex_hull.get(); } @@ -490,7 +490,7 @@ public: void finalize_geometry(bool opengl_initialized) { this->indexed_vertex_array.finalize_geometry(opengl_initialized); } void release_geometry() { this->indexed_vertex_array.release_geometry(); } -#if ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS void set_bounding_boxes_as_dirty() { m_transformed_bounding_box.reset(); m_transformed_convex_hull_bounding_box.reset(); @@ -498,7 +498,7 @@ public: } #else void set_bounding_boxes_as_dirty() { m_transformed_bounding_box.reset(); m_transformed_convex_hull_bounding_box.reset(); } -#endif // ENABLE_FIX_SINKING_OBJECT_OUT_OF_BED_DETECTION +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS bool is_sla_support() const; bool is_sla_pad() const; From 45db1c13bad6d0558a35a977e7378edbf1b8b2ce Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 4 Oct 2021 08:30:04 +0200 Subject: [PATCH 17/23] Tech ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - Faster update of object manipulation fields while dragging gizmo Move/Rotate/Scale --- src/slic3r/GUI/GLCanvas3D.cpp | 5 +++++ src/slic3r/GUI/GUI_ObjectManipulation.cpp | 7 ++++++- src/slic3r/GUI/Selection.cpp | 7 +++++++ src/slic3r/GUI/Selection.hpp | 8 ++++++++ 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 84c5053ef..2cc92f987 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3215,6 +3215,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } } else if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) { +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + if (evt.LeftUp()) + m_selection.stop_dragging(); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + if (m_layers_editing.state != LayersEditing::Unknown) { m_layers_editing.state = LayersEditing::Unknown; _stop_timer(); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 6eaa6316d..0f68d01c9 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -631,7 +631,6 @@ void ObjectManipulation::update_if_dirty() update(m_cache.rotation, m_cache.rotation_rounded, meRotation, m_new_rotation); } - if (selection.requires_uniform_scale()) { m_lock_bnt->SetLock(true); m_lock_bnt->SetToolTip(_L("You cannot use non-uniform scaling mode for multiple objects/parts selection")); @@ -654,8 +653,14 @@ void ObjectManipulation::update_if_dirty() else m_og->disable(); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + if (!selection.is_dragging()) { +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS update_reset_buttons_visibility(); update_mirror_buttons_visibility(); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + } +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS m_dirty = false; } diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 0c863665d..6aff77cf7 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -112,6 +112,9 @@ Selection::Selection() , m_type(Empty) , m_valid(false) , m_scale_factor(1.0f) +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + , m_dragging(false) +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS { this->set_bounding_boxes_dirty(); } @@ -676,6 +679,10 @@ void Selection::start_dragging() if (!m_valid) return; +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + m_dragging = true; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + set_caches(); } diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index dea507511..cb8f38d50 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -220,6 +220,10 @@ private: float m_scale_factor; +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + bool m_dragging; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + public: Selection(); @@ -312,6 +316,10 @@ public: const BoundingBoxf3& get_scaled_instance_bounding_box() const; void start_dragging(); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + void stop_dragging() { m_dragging = false; } + bool is_dragging() const { return m_dragging; } +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS void translate(const Vec3d& displacement, bool local = false); void rotate(const Vec3d& rotation, TransformationType transformation_type); From 4521945bb3aaafdc63b4ed54c03c753dfa08594c Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 4 Oct 2021 14:07:45 +0200 Subject: [PATCH 18/23] Tech ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - Faster printbed collision detection using the new function Geometry::intersect() --- src/libslic3r/Model.cpp | 24 +++++++++++------------- src/slic3r/GUI/3DScene.cpp | 4 ++-- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 95d2c8f81..885e620e0 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -29,20 +29,18 @@ namespace Slic3r { #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const Polygon& obj_hull_2d, double obj_min_z, double obj_max_z) { - static const double Z_TOLERANCE = -1e10; + if (!Geometry::intersects(printbed_shape, obj_hull_2d)) + return ModelInstancePVS_Fully_Outside; - const Polygons intersection_polys = intersection(printbed_shape, obj_hull_2d); - const bool contained_xy = !intersection_polys.empty() && Geometry::are_approx(intersection_polys.front(), obj_hull_2d); - const bool contained_z = Z_TOLERANCE < obj_min_z && obj_max_z < print_volume_height; - if (contained_xy && contained_z) - return ModelInstancePVS_Inside; - - const bool intersects_xy = !contained_xy && !intersection_polys.empty(); - const bool intersects_z = !contained_z && obj_min_z < print_volume_height&& Z_TOLERANCE < obj_max_z; - if (intersects_xy || intersects_z) - return ModelInstancePVS_Partly_Outside; - - return ModelInstancePVS_Fully_Outside; + bool contained_xy = true; + for (const Point& p : obj_hull_2d) { + if (!printbed_shape.contains(p)) { + contained_xy = false; + break; + } + } + const bool contained_z = -1e10 < obj_min_z && obj_max_z < print_volume_height; + return (contained_xy && contained_z) ? ModelInstancePVS_Inside : ModelInstancePVS_Partly_Outside; } ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const BoundingBoxf3& box) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index f3e4cfad1..3cba38a6e 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -1083,8 +1083,8 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M } } else { - const indexed_triangle_set& its = GUI::wxGetApp().plater()->model().objects[volume->object_idx()]->volumes[volume->volume_idx()]->mesh().its; - const Polygon volume_hull_2d = its_convex_hull_2d_above(its, volume->world_matrix().cast(), 0.0f); + const TriangleMesh* mesh = volume->is_sinking() ? &GUI::wxGetApp().plater()->model().objects[volume->object_idx()]->volumes[volume->volume_idx()]->mesh() : volume->convex_hull(); + const Polygon volume_hull_2d = its_convex_hull_2d_above(mesh->its, volume->world_matrix().cast(), 0.0f); volume_state = printbed_collision_state(bed_poly, bed_height, volume_hull_2d, bb.min.z(), bb.max.z()); } contained = (volume_state == ModelInstancePVS_Inside); From 4ff13a5d63297438d1c489c40936c4274bb5f562 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 6 Oct 2021 13:47:54 +0200 Subject: [PATCH 19/23] Tech ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - Reworked detection of collision with printbed. The detection uses now different algorithms in dependence of the printbed type (rectangular, circular, convex) to improve performance. --- src/libslic3r/GCode/GCodeProcessor.cpp | 16 ++++ src/libslic3r/GCode/GCodeProcessor.hpp | 3 + src/slic3r/GUI/3DBed.cpp | 9 +- src/slic3r/GUI/3DBed.hpp | 1 + src/slic3r/GUI/3DScene.cpp | 115 ++++++++++++++++++------- src/slic3r/GUI/3DScene.hpp | 4 + src/slic3r/GUI/GCodeViewer.cpp | 61 ++++++++++++- src/slic3r/GUI/GCodeViewer.hpp | 11 +++ src/slic3r/GUI/GLCanvas3D.cpp | 59 ++++--------- src/slic3r/GUI/GLCanvas3D.hpp | 4 + 10 files changed, 209 insertions(+), 74 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 9c90535c4..45c0de616 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -720,6 +720,9 @@ void GCodeProcessor::UsedFilaments::process_caches(GCodeProcessor* processor) void GCodeProcessor::Result::reset() { moves = std::vector(); bed_shape = Pointfs(); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + max_print_height = 0.0f; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS settings_ids.reset(); extruders_count = 0; extruder_colors = std::vector(); @@ -734,6 +737,9 @@ void GCodeProcessor::Result::reset() { moves.clear(); lines_ends.clear(); bed_shape = Pointfs(); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + max_print_height = 0.0f; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS settings_ids.reset(); extruders_count = 0; extruder_colors = std::vector(); @@ -883,6 +889,10 @@ void GCodeProcessor::apply_config(const PrintConfig& config) const ConfigOptionFloatOrPercent* first_layer_height = config.option("first_layer_height"); if (first_layer_height != nullptr) m_first_layer_height = std::abs(first_layer_height->value); + +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + m_result.max_print_height = config.max_print_height; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS } void GCodeProcessor::apply_config(const DynamicPrintConfig& config) @@ -1112,6 +1122,12 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) const ConfigOptionFloatOrPercent* first_layer_height = config.option("first_layer_height"); if (first_layer_height != nullptr) m_first_layer_height = std::abs(first_layer_height->value); + +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + const ConfigOptionFloat* max_print_height = config.option("max_print_height"); + if (max_print_height != nullptr) + m_result.max_print_height = max_print_height->value; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS } void GCodeProcessor::enable_stealth_time_estimator(bool enabled) diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index fce888233..e7d602155 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -351,6 +351,9 @@ namespace Slic3r { // Positions of ends of lines of the final G-code this->filename after TimeProcessor::post_process() finalizes the G-code. std::vector lines_ends; Pointfs bed_shape; +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + float max_print_height; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS SettingsIds settings_ids; size_t extruders_count; std::vector extruder_colors; diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 6e04664e2..18c017da1 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -282,9 +282,11 @@ bool Bed3D::is_circle(const Pointfs& shape, Vec2d* center, double* radius) avg_dist /= vertex_distances.size(); + double tolerance = avg_dist * 0.01; + bool defined_value = true; for (double el : vertex_distances) { - if (fabs(el - avg_dist) > 10.0 * SCALED_EPSILON) + if (fabs(el - avg_dist) > tolerance) defined_value = false; break; } @@ -298,6 +300,11 @@ bool Bed3D::is_circle(const Pointfs& shape, Vec2d* center, double* radius) return defined_value; } +bool Bed3D::is_convex(const Pointfs& shape) +{ + return Polygon::new_scale(shape).convex_points().size() == shape.size(); +} + Bed3D::EShapeType Bed3D::detect_shape_type(const Pointfs& shape) { if (shape.size() < 3) diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp index baa0ef5b7..07b9f1758 100644 --- a/src/slic3r/GUI/3DBed.hpp +++ b/src/slic3r/GUI/3DBed.hpp @@ -139,6 +139,7 @@ public: #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS static bool is_rectangle(const Pointfs& shape, Vec2d* min = nullptr, Vec2d* max = nullptr); static bool is_circle(const Pointfs& shape, Vec2d* center = nullptr, double* radius = nullptr); + static bool is_convex(const Pointfs& shape); static EShapeType detect_shape_type(const Pointfs& shape); #endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 3cba38a6e..d6999df61 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -11,6 +11,9 @@ #include "GUI_App.hpp" #include "Plater.hpp" #include "BitmapCache.hpp" +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS +#include "3DBed.hpp" +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS #include "libslic3r/ExtrusionEntity.hpp" #include "libslic3r/ExtrusionEntityCollection.hpp" @@ -267,6 +270,12 @@ void GLIndexedVertexArray::render( const std::pair& tverts_range, const std::pair& qverts_range) const { +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + // this method has been called before calling finalize() ? + if (this->vertices_and_normals_interleaved_VBO_id == 0 && !this->vertices_and_normals_interleaved.empty()) + return; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + assert(this->vertices_and_normals_interleaved_VBO_id != 0); assert(this->triangle_indices_VBO_id != 0 || this->quad_indices_VBO_id != 0); @@ -879,7 +888,7 @@ int GLVolumeCollection::load_wipe_tower_preview( GLVolume& v = *volumes.back(); v.indexed_vertex_array.load_mesh(mesh); #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - v.calc_convex_hull_3d(); + v.set_convex_hull(mesh.convex_hull_3d()); #endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS v.indexed_vertex_array.finalize_geometry(opengl_initialized); v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0)); @@ -1038,7 +1047,11 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab glsafe(::glDisable(GL_BLEND)); } +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS +bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state, bool as_toolpaths) const +#else bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state) const +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS { if (config == nullptr) return false; @@ -1050,10 +1063,61 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS const Polygon bed_poly = offset(Polygon::new_scale(opt->values), static_cast(scale_(BedEpsilon))).front(); const float bed_height = config->opt_float("max_print_height"); + const BoundingBox bed_box_2D = get_extents(bed_poly); + BoundingBoxf3 print_volume({ unscale(bed_box_2D.min.x()), unscale(bed_box_2D.min.y()), -1e10 }, + { unscale(bed_box_2D.max.x()), unscale(bed_box_2D.max.y()), bed_height }); + + auto check_against_rectangular_bed = [&print_volume](GLVolume& volume, ModelInstanceEPrintVolumeState& state) { + const BoundingBoxf3* const bb = volume.is_sinking() ? &volume.transformed_non_sinking_bounding_box() : &volume.transformed_convex_hull_bounding_box(); + volume.is_outside = !print_volume.contains(*bb); + if (volume.printable) { + if (state == ModelInstancePVS_Inside && volume.is_outside) + state = ModelInstancePVS_Fully_Outside; + if (state == ModelInstancePVS_Fully_Outside && volume.is_outside && print_volume.intersects(*bb)) + state = ModelInstancePVS_Partly_Outside; + } + }; + + auto check_against_circular_bed = [](GLVolume& volume, ModelInstanceEPrintVolumeState& state, const Vec2d& center, double radius) { + const TriangleMesh* mesh = volume.is_sinking() ? &GUI::wxGetApp().plater()->model().objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : volume.convex_hull(); + const Polygon volume_hull_2d = its_convex_hull_2d_above(mesh->its, volume.world_matrix().cast(), 0.0f); + size_t outside_count = 0; + const double sq_radius = sqr(radius); + for (const Point& p : volume_hull_2d.points) { + if (sq_radius < (unscale(p) - center).squaredNorm()) + ++outside_count; + } + + volume.is_outside = outside_count > 0; + if (volume.printable) { + if (state == ModelInstancePVS_Inside && volume.is_outside) + state = ModelInstancePVS_Fully_Outside; + if (state == ModelInstancePVS_Fully_Outside && volume.is_outside && outside_count < volume_hull_2d.size()) + state = ModelInstancePVS_Partly_Outside; + } + }; + + auto check_against_convex_bed = [&bed_poly, bed_height](GLVolume& volume, ModelInstanceEPrintVolumeState& state) { + const TriangleMesh* mesh = volume.is_sinking() ? &GUI::wxGetApp().plater()->model().objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : volume.convex_hull(); + const Polygon volume_hull_2d = its_convex_hull_2d_above(mesh->its, volume.world_matrix().cast(), 0.0f); + const BoundingBoxf3* const bb = volume.is_sinking() ? &volume.transformed_non_sinking_bounding_box() : &volume.transformed_convex_hull_bounding_box(); + ModelInstanceEPrintVolumeState volume_state = printbed_collision_state(bed_poly, bed_height, volume_hull_2d, bb->min.z(), bb->max.z()); + bool contained = (volume_state == ModelInstancePVS_Inside); + bool intersects = (volume_state == ModelInstancePVS_Partly_Outside); + + volume.is_outside = !contained; + if (volume.printable) { + if (state == ModelInstancePVS_Inside && volume.is_outside) + state = ModelInstancePVS_Fully_Outside; + + if (state == ModelInstancePVS_Fully_Outside && volume.is_outside && intersects) + state = ModelInstancePVS_Partly_Outside; + } + }; #else const BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values)); - BoundingBoxf3 print_volume({ unscale(bed_box_2D.min.x()), unscale(bed_box_2D.min.y()), 0.0 }, - { unscale(bed_box_2D.max.x()), unscale(bed_box_2D.max.y()), config->opt_float("max_print_height") }); + BoundingBoxf3 print_volume({ unscale(bed_box_2D.min.x()), unscale(bed_box_2D.min.y()), 0.0 }, + { unscale(bed_box_2D.max.x()), unscale(bed_box_2D.max.y()), config->opt_float("max_print_height") }); // Allow the objects to protrude below the print bed print_volume.min.z() = -1e10; print_volume.min.x() -= BedEpsilon; @@ -1063,36 +1127,33 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M #endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS ModelInstanceEPrintVolumeState overall_state = ModelInstancePVS_Inside; - bool contained_min_one = false; for (GLVolume* volume : this->volumes) { +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + if (as_toolpaths && !volume->is_extrusion_path) + continue; + else if (!as_toolpaths && (volume->is_modifier || (!volume->shader_outside_printer_detection_enabled && (volume->is_wipe_tower || volume->composite_id.volume_id < 0)))) + continue; + + if (GUI::Bed3D::is_rectangle(opt->values)) + check_against_rectangular_bed(*volume, overall_state); + else { + Vec2d center; + double radius; + if (GUI::Bed3D::is_circle(opt->values, ¢er, &radius)) + check_against_circular_bed(*volume, overall_state, center, radius); + else if (GUI::Bed3D::is_convex(opt->values)) + check_against_convex_bed(*volume, overall_state); + } + + contained_min_one |= !volume->is_outside; +#else if (volume->is_modifier || (!volume->shader_outside_printer_detection_enabled && (volume->is_wipe_tower || volume->composite_id.volume_id < 0))) continue; -#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - bool contained = false; - bool intersects = false; - bool is_aux_volume = volume->is_sla_support() || volume->is_sla_pad() || volume->is_wipe_tower; - const BoundingBoxf3 bb = is_aux_volume ? volume->transformed_convex_hull_bounding_box() : volume->transformed_non_sinking_bounding_box(); - ModelInstanceEPrintVolumeState volume_state; - if (is_aux_volume) { - if (volume->is_sla_support() || volume->is_wipe_tower) { - const Polygon volume_hull_2d = its_convex_hull_2d_above(volume->convex_hull()->its, volume->world_matrix().cast(), 0.0f); - volume_state = printbed_collision_state(bed_poly, bed_height, volume_hull_2d, bb.min.z(), bb.max.z()); - } - } - else { - const TriangleMesh* mesh = volume->is_sinking() ? &GUI::wxGetApp().plater()->model().objects[volume->object_idx()]->volumes[volume->volume_idx()]->mesh() : volume->convex_hull(); - const Polygon volume_hull_2d = its_convex_hull_2d_above(mesh->its, volume->world_matrix().cast(), 0.0f); - volume_state = printbed_collision_state(bed_poly, bed_height, volume_hull_2d, bb.min.z(), bb.max.z()); - } - contained = (volume_state == ModelInstancePVS_Inside); - intersects = (volume_state == ModelInstancePVS_Partly_Outside); -#else const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box(); bool contained = print_volume.contains(bb); -#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS volume->is_outside = !contained; if (!volume->printable) @@ -1103,10 +1164,6 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M if (overall_state == ModelInstancePVS_Inside && volume->is_outside) overall_state = ModelInstancePVS_Fully_Outside; -#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - if (overall_state == ModelInstancePVS_Fully_Outside && volume->is_outside && intersects) - overall_state = ModelInstancePVS_Partly_Outside; -#else if (overall_state == ModelInstancePVS_Fully_Outside && volume->is_outside && print_volume.intersects(bb)) overall_state = ModelInstancePVS_Partly_Outside; #endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 0b1293f56..14597f22a 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -654,7 +654,11 @@ public: // 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 +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + bool check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state, bool as_toolpaths = false) const; +#else bool check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state) const; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS void reset_outside_state(); void update_colors_by_extruder(const DynamicPrintConfig* config); diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index fd2fe9cbc..00fa066c9 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -6,10 +6,11 @@ #include "libslic3r/Model.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/LocalesUtils.hpp" +#include "libslic3r/PresetBundle.hpp" + #include "GUI_App.hpp" #include "MainFrame.hpp" #include "Plater.hpp" -#include "libslic3r/PresetBundle.hpp" #include "Camera.hpp" #include "I18N.hpp" #include "GUI_Utils.hpp" @@ -19,6 +20,10 @@ #include "GLToolbar.hpp" #include "GUI_Preview.hpp" #include "GUI_ObjectManipulation.hpp" +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS +#include "3DBed.hpp" +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + #include #include @@ -674,6 +679,10 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& if (wxGetApp().is_gcode_viewer()) m_custom_gcode_per_print_z = gcode_result.custom_gcode_per_print_z; +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + m_max_print_height = gcode_result.max_print_height; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + load_toolpaths(gcode_result); if (m_layers.empty()) @@ -819,6 +828,9 @@ void GCodeViewer::reset() m_paths_bounding_box = BoundingBoxf3(); m_max_bounding_box = BoundingBoxf3(); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + m_max_print_height = 0.0f; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS m_tool_colors = std::vector(); m_extruders_count = 0; m_extruder_ids = std::vector(); @@ -835,6 +847,9 @@ void GCodeViewer::reset() #if ENABLE_GCODE_VIEWER_STATISTICS m_statistics.reset_all(); #endif // ENABLE_GCODE_VIEWER_STATISTICS +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + m_contained_in_bed = true; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS } void GCodeViewer::render() @@ -1554,7 +1569,49 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) // set approximate max bounding box (take in account also the tool marker) m_max_bounding_box = m_paths_bounding_box; - m_max_bounding_box.merge(m_paths_bounding_box.max + m_sequential_view.marker.get_bounding_box().size()[2] * Vec3d::UnitZ()); + m_max_bounding_box.merge(m_paths_bounding_box.max + m_sequential_view.marker.get_bounding_box().size().z() * Vec3d::UnitZ()); + +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + if (wxGetApp().is_editor()) { + const Bed3D::EShapeType bed_type = wxGetApp().plater()->get_bed().get_shape_type(); + if (bed_type == Bed3D::EShapeType::Rectangle) { + BoundingBoxf3 print_volume = wxGetApp().plater()->get_bed().get_bounding_box(false); + print_volume.min.z() = -1e10; + print_volume.max.z() = m_max_print_height; + print_volume.min -= Vec3f(BedEpsilon, BedEpsilon, 0.0f).cast(); + print_volume.max += Vec3f(BedEpsilon, BedEpsilon, 0.0f).cast(); + m_contained_in_bed = print_volume.contains(m_paths_bounding_box); + } + else if (bed_type == Bed3D::EShapeType::Circle) { + Vec2d center; + double radius; + Bed3D::is_circle(wxGetApp().plater()->get_bed().get_shape(), ¢er, &radius); + const double sq_radius = sqr(radius); + for (const GCodeProcessor::MoveVertex& move : gcode_result.moves) { + if (move.type == EMoveType::Extrude && move.extrusion_role != erCustom && move.width != 0.0f && move.height != 0.0f) { + if (sq_radius < (Vec2d(move.position.x(), move.position.y()) - center).squaredNorm()) { + m_contained_in_bed = false; + break; + } + } + } + } + else if (bed_type == Bed3D::EShapeType::Custom) { + const Pointfs& shape = wxGetApp().plater()->get_bed().get_shape(); + if (Bed3D::is_convex(shape)) { + const Polygon poly = Polygon::new_scale(shape); + for (const GCodeProcessor::MoveVertex& move : gcode_result.moves) { + if (move.type == EMoveType::Extrude && move.extrusion_role != erCustom && move.width != 0.0f && move.height != 0.0f) { + if (!poly.contains(Point::new_scale(Vec2d(move.position.x(), move.position.y())))) { + m_contained_in_bed = false; + break; + } + } + } + } + } + } +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS #if ENABLE_FIX_SEAMS_SYNCH m_sequential_view.gcode_ids.clear(); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 1b5a53f9d..66fcba2bc 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -780,6 +780,9 @@ private: BoundingBoxf3 m_paths_bounding_box; // bounding box of toolpaths + marker tools BoundingBoxf3 m_max_bounding_box; +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + float m_max_print_height{ 0.0f }; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS std::vector m_tool_colors; Layers m_layers; std::array m_layers_z_range; @@ -804,6 +807,10 @@ private: std::vector m_custom_gcode_per_print_z; +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + bool m_contained_in_bed{ true }; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + public: GCodeViewer(); ~GCodeViewer() { reset(); } @@ -832,6 +839,10 @@ public: const SequentialView& get_sequential_view() const { return m_sequential_view; } void update_sequential_view_current(unsigned int first, unsigned int last); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + bool is_contained_in_bed() const { return m_contained_in_bed; } +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + EViewType get_view_type() const { return m_view_type; } void set_view_type(EViewType type) { if (type == EViewType::Count) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 2cc92f987..2d8c2f9bd 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1108,10 +1108,18 @@ void GLCanvas3D::reset_volumes() _set_warning_notification(EWarning::ObjectOutside, false); } +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS +ModelInstanceEPrintVolumeState GLCanvas3D::check_volumes_outside_state(bool as_toolpaths) const +#else ModelInstanceEPrintVolumeState GLCanvas3D::check_volumes_outside_state() const +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS { ModelInstanceEPrintVolumeState state; +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + m_volumes.check_outside_state(m_config, &state, as_toolpaths); +#else m_volumes.check_outside_state(m_config, &state); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS return state; } @@ -5016,24 +5024,16 @@ void GLCanvas3D::_render_background() const if (!m_volumes.empty()) use_error_color &= _is_any_volume_outside(); - else { + else #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - ModelInstanceEPrintVolumeState state; - if (m_gcode_viewer.has_data()) { - const Polygon bed_poly = offset(Polygon::new_scale(wxGetApp().plater()->get_bed().get_shape()), static_cast(scale_(BedEpsilon))).front(); - const float bed_height = m_config->opt_float("max_print_height"); - state = printbed_collision_state(bed_poly, bed_height, m_gcode_viewer.get_paths_bounding_box()); - } - else - state = ModelInstancePVS_Inside; - - use_error_color &= state != ModelInstancePVS_Inside; + use_error_color &= m_gcode_viewer.has_data() && !m_gcode_viewer.is_contained_in_bed(); #else + { const BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); const BoundingBoxf3& paths_volume = m_gcode_viewer.get_paths_bounding_box(); use_error_color &= (test_volume.radius() > 0.0 && paths_volume.radius() > 0.0) ? !test_volume.contains(paths_volume) : false; -#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS } +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS } glsafe(::glPushMatrix()); @@ -6379,18 +6379,7 @@ void GLCanvas3D::_load_sla_shells() void GLCanvas3D::_update_toolpath_volumes_outside_state() { #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - const Polygon bed_poly = offset(Polygon::new_scale(wxGetApp().plater()->get_bed().get_shape()), static_cast(scale_(BedEpsilon))).front(); - const float bed_height = m_config->opt_float("max_print_height"); - for (GLVolume* volume : m_volumes.volumes) { - if (volume->is_extrusion_path) { - const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box(); - const Polygon volume_hull_2d = its_convex_hull_2d_above(volume->convex_hull()->its, volume->world_matrix().cast(), 0.0f); - const ModelInstanceEPrintVolumeState state = printbed_collision_state(bed_poly, bed_height, volume_hull_2d, bb.min.z(), bb.max.z()); - volume->is_outside = (state != ModelInstancePVS_Inside); - } - else - volume->is_outside = false; - } + check_volumes_outside_state(true); #else BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); for (GLVolume* volume : m_volumes.volumes) { @@ -6402,18 +6391,7 @@ void GLCanvas3D::_update_toolpath_volumes_outside_state() void GLCanvas3D::_update_sla_shells_outside_state() { #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - const Polygon bed_poly = offset(Polygon::new_scale(wxGetApp().plater()->get_bed().get_shape()), static_cast(scale_(BedEpsilon))).front(); - const float bed_height = m_config->opt_float("max_print_height"); - for (GLVolume* volume : m_volumes.volumes) { - if (volume->shader_outside_printer_detection_enabled) { - const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box(); - const Polygon volume_hull_2d = its_convex_hull_2d_above(volume->convex_hull()->its, volume->world_matrix().cast(), 0.0f); - const ModelInstanceEPrintVolumeState state = printbed_collision_state(bed_poly, bed_height, volume_hull_2d, bb.min.z(), bb.max.z()); - volume->is_outside = (state != ModelInstancePVS_Inside); - } - else - volume->is_outside = false; - } + check_volumes_outside_state(); #else BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); for (GLVolume* volume : m_volumes.volumes) { @@ -6431,12 +6409,8 @@ void GLCanvas3D::_set_warning_notification_if_needed(EWarning warning) else { if (wxGetApp().is_editor()) { #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - if (current_printer_technology() != ptSLA) { - const Polygon bed_poly = offset(Polygon::new_scale(wxGetApp().plater()->get_bed().get_shape()), static_cast(scale_(BedEpsilon))).front(); - const float bed_height = m_config->opt_float("max_print_height"); - const ModelInstanceEPrintVolumeState state = printbed_collision_state(bed_poly, bed_height, m_gcode_viewer.get_paths_bounding_box()); - show = state != ModelInstancePVS_Inside; - } + if (current_printer_technology() != ptSLA) + show = m_gcode_viewer.has_data() && !m_gcode_viewer.is_contained_in_bed(); #else BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); const BoundingBoxf3& paths_volume = m_gcode_viewer.get_paths_bounding_box(); @@ -6445,6 +6419,7 @@ void GLCanvas3D::_set_warning_notification_if_needed(EWarning warning) #endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS } } + _set_warning_notification(warning, show); } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index beb33c89b..d3c24694c 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -621,7 +621,11 @@ public: unsigned int get_volumes_count() const; const GLVolumeCollection& get_volumes() const { return m_volumes; } void reset_volumes(); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + ModelInstanceEPrintVolumeState check_volumes_outside_state(bool as_toolpaths = false) const; +#else ModelInstanceEPrintVolumeState check_volumes_outside_state() const; +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS #if ENABLE_SEAMS_USING_MODELS void init_gcode_viewer() { m_gcode_viewer.init(); } From 5e735a59d02a17bab3bac8a06d9f64e99cc8a399 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 18 Oct 2021 16:59:32 +0200 Subject: [PATCH 20/23] Fixed planning of support interface layers with rafts and larger Z gap for supports than for the raft. --- src/libslic3r/SupportMaterial.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 1322b4071..a668a385b 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -1703,15 +1703,18 @@ static inline std::pair 1 ? slicing_params.raft_interface_top_z + support_layer_height_min + EPSILON : slicing_params.first_print_layer_height - EPSILON; - if (print_z < min_print_z) { + if (print_z < slicing_params.first_print_layer_height - EPSILON) { // This contact layer is below the first layer height, therefore not printable. Don't support this surface. return std::pair(nullptr, nullptr); - } else if (print_z < slicing_params.first_print_layer_height + EPSILON) { - // Align the layer with the 1st layer height. - print_z = slicing_params.first_print_layer_height; - bottom_z = 0; - height = slicing_params.first_print_layer_height; + } + const bool has_raft = slicing_params.raft_layers() > 1; + const coordf_t min_print_z = has_raft ? slicing_params.raft_contact_top_z : slicing_params.first_print_layer_height; + if (print_z < min_print_z + support_layer_height_min) { + // Align the layer with the 1st layer height or the raft contact layer. + // With raft active, any contact layer below the raft_contact_top_z will be brought to raft_contact_top_z to extend the raft area. + print_z = min_print_z; + bottom_z = has_raft ? slicing_params.raft_interface_top_z : 0; + height = has_raft ? slicing_params.contact_raft_layer_height : min_print_z; } else { // Don't know the height of the top contact layer yet. The top contact layer is printed with a normal flow and // its height will be set adaptively later on. @@ -1727,9 +1730,9 @@ static inline std::pair= min_print_z) { // Not below the first layer height means this layer is printable. - if (print_z < slicing_params.first_print_layer_height + EPSILON) { - // Align the layer with the 1st layer height. - bridging_print_z = slicing_params.first_print_layer_height; + if (print_z < min_print_z + support_layer_height_min) { + // Align the layer with the 1st layer height or the raft contact layer. + bridging_print_z = min_print_z; } if (bridging_print_z < print_z - EPSILON) { // Allocate the new layer. @@ -3108,7 +3111,7 @@ std::pair Date: Tue, 19 Oct 2021 09:19:47 +0200 Subject: [PATCH 21/23] Fixed visualization of the "sinking contours" for complex objects. --- src/slic3r/GUI/3DScene.cpp | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 27eb69363..98665177f 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -319,19 +319,7 @@ void GLVolume::SinkingContours::update() MeshSlicingParams slicing_params; slicing_params.trafo = m_parent.world_matrix(); Polygons polygons = union_(slice_mesh(mesh.its, 0.0f, slicing_params)); - for (Polygon& polygon : polygons) { - if (polygon.is_clockwise()) - polygon.reverse(); - Polygons outer_polys = offset(polygon, float(scale_(HalfWidth))); - assert(outer_polys.size() == 1); - if (outer_polys.empty()) - // no outer contour, skip - continue; - - ExPolygon expoly(std::move(outer_polys.front())); - expoly.holes = offset(polygon, -float(scale_(HalfWidth))); - polygons_reverse(expoly.holes); - + for (ExPolygon &expoly : diff_ex(expand(polygons, float(scale_(HalfWidth))), shrink(polygons, float(scale_(HalfWidth))))) { GUI::GLModel::InitializationData::Entity entity; entity.type = GUI::GLModel::PrimitiveType::Triangles; const std::vector triangulation = triangulate_expolygon_3d(expoly); From aa47729c470dbe399bd320dadf3b90f8f5defb62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Tue, 19 Oct 2021 11:53:21 +0200 Subject: [PATCH 22/23] Added a missing include (GCC 11.1). --- src/slic3r/GUI/Plater.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 1f5c2b6e7..97e4af218 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -48,6 +48,7 @@ #include "libslic3r/SLAPrint.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/PresetBundle.hpp" +#include "libslic3r/ClipperUtils.hpp" #include "GUI.hpp" #include "GUI_App.hpp" From 5a1809579ec60397a7ff60d64ea393f40b9bb4d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Tue, 19 Oct 2021 12:54:27 +0200 Subject: [PATCH 23/23] Fixed unintended space after ImGui::SliderFloat in the hollow and sla supports gizmos. --- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 49 +++++++++++--------- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 8 ++-- src/slic3r/GUI/ImGuiWrapper.cpp | 18 ++++++- src/slic3r/GUI/ImGuiWrapper.hpp | 6 ++- 4 files changed, 51 insertions(+), 30 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index f7feed44a..d45a2e613 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -508,20 +508,23 @@ RENDER_AGAIN: m_imgui->begin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that: - const float settings_sliders_left = - std::max({m_imgui->calc_text_size(m_desc.at("offset")).x, - m_imgui->calc_text_size(m_desc.at("quality")).x, - m_imgui->calc_text_size(m_desc.at("closing_distance")).x, - m_imgui->calc_text_size(m_desc.at("hole_diameter")).x, - m_imgui->calc_text_size(m_desc.at("hole_depth")).x}) - + m_imgui->scaled(1.f); + const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, + m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(0.5f); + + const float settings_sliders_left = + std::max(std::max({m_imgui->calc_text_size(m_desc.at("offset")).x, + m_imgui->calc_text_size(m_desc.at("quality")).x, + m_imgui->calc_text_size(m_desc.at("closing_distance")).x, + m_imgui->calc_text_size(m_desc.at("hole_diameter")).x, + m_imgui->calc_text_size(m_desc.at("hole_depth")).x}) + m_imgui->scaled(0.5f), clipping_slider_left); - const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f); const float diameter_slider_left = settings_sliders_left; //m_imgui->calc_text_size(m_desc.at("hole_diameter")).x + m_imgui->scaled(1.f); const float minimal_slider_width = m_imgui->scaled(4.f); + const float button_preview_width = m_imgui->calc_button_size(m_desc.at("preview")).x; + float window_width = minimal_slider_width + std::max({settings_sliders_left, clipping_slider_left, diameter_slider_left}); - window_width = std::max(window_width, m_imgui->calc_text_size(m_desc.at("preview")).x); + window_width = std::max(window_width, button_preview_width); if (m_imgui->button(m_desc["preview"])) hollow_mesh(); @@ -544,9 +547,9 @@ RENDER_AGAIN: float max_tooltip_width = ImGui::GetFontSize() * 20.0f; ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc.at("offset")); - ImGui::SameLine(settings_sliders_left); + ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x); ImGui::PushItemWidth(window_width - settings_sliders_left); - m_imgui->slider_float(" ", &offset, offset_min, offset_max, "%.1f mm"); + m_imgui->slider_float("##offset", &offset, offset_min, offset_max, "%.1f mm"); if (ImGui::IsItemHovered()) m_imgui->tooltip((_utf8(opts[0].second->tooltip)).c_str(), max_tooltip_width); @@ -557,8 +560,8 @@ RENDER_AGAIN: if (current_mode >= quality_mode) { ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc.at("quality")); - ImGui::SameLine(settings_sliders_left); - m_imgui->slider_float(" ", &quality, quality_min, quality_max, "%.1f"); + ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x); + m_imgui->slider_float("##quality", &quality, quality_min, quality_max, "%.1f"); if (ImGui::IsItemHovered()) m_imgui->tooltip((_utf8(opts[1].second->tooltip)).c_str(), max_tooltip_width); @@ -570,8 +573,8 @@ RENDER_AGAIN: if (current_mode >= closing_d_mode) { ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc.at("closing_distance")); - ImGui::SameLine(settings_sliders_left); - m_imgui->slider_float(" ", &closing_d, closing_d_min, closing_d_max, "%.1f mm"); + ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x); + m_imgui->slider_float("##closing_distance", &closing_d, closing_d_min, closing_d_max, "%.1f mm"); if (ImGui::IsItemHovered()) m_imgui->tooltip((_utf8(opts[2].second->tooltip)).c_str(), max_tooltip_width); @@ -614,11 +617,11 @@ RENDER_AGAIN: m_new_hole_radius = diameter_upper_cap / 2.f; ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc.at("hole_diameter")); - ImGui::SameLine(diameter_slider_left); + ImGui::SameLine(diameter_slider_left, m_imgui->get_item_spacing().x); ImGui::PushItemWidth(window_width - diameter_slider_left); float diam = 2.f * m_new_hole_radius; - m_imgui->slider_float("", &diam, 1.f, 15.f, "%.1f mm", 1.f, false); + m_imgui->slider_float("##hole_diameter", &diam, 1.f, 15.f, "%.1f mm", 1.f, false); // Let's clamp the value (which could have been entered by keyboard) to a larger range // than the slider. This allows entering off-scale values and still protects against //complete non-sense. @@ -630,8 +633,8 @@ RENDER_AGAIN: ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc["hole_depth"]); - ImGui::SameLine(diameter_slider_left); - m_imgui->slider_float(" ", &m_new_hole_height, 0.f, 10.f, "%.1f mm", 1.f, false); + ImGui::SameLine(diameter_slider_left, m_imgui->get_item_spacing().x); + m_imgui->slider_float("##hole_depth", &m_new_hole_height, 0.f, 10.f, "%.1f mm", 1.f, false); // Same as above: m_new_hole_height = std::clamp(m_new_hole_height, 0.f, 100.f); @@ -697,10 +700,10 @@ RENDER_AGAIN: } } - ImGui::SameLine(clipping_slider_left); - ImGui::PushItemWidth(window_width - clipping_slider_left); + ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x); + ImGui::PushItemWidth(window_width - settings_sliders_left); float clp_dist = m_c->object_clipper()->get_position(); - if (m_imgui->slider_float(" ", &clp_dist, 0.f, 1.f, "%.2f")) + if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f")) m_c->object_clipper()->set_position(clp_dist, true); // make sure supports are shown/hidden as appropriate @@ -732,7 +735,7 @@ RENDER_AGAIN: if (force_refresh) m_parent.set_as_dirty(); - + if (config_changed) m_parent.post_event(SimpleEvent(EVT_GLCANVAS_FORCE_UPDATE)); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index b7a6d89fa..ccc67b630 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -673,7 +673,7 @@ RENDER_AGAIN: // - keep updating the head radius during sliding so it is continuosly refreshed in 3D scene // - take correct undo/redo snapshot after the user is done with moving the slider float initial_value = m_new_point_head_diameter; - m_imgui->slider_float("", &m_new_point_head_diameter, 0.1f, diameter_upper_cap, "%.1f"); + m_imgui->slider_float("##head_diameter", &m_new_point_head_diameter, 0.1f, diameter_upper_cap, "%.1f"); if (ImGui::IsItemClicked()) { if (m_old_point_head_diameter == 0.f) m_old_point_head_diameter = initial_value; @@ -733,7 +733,7 @@ RENDER_AGAIN: float density = static_cast(opts[0])->value; float minimal_point_distance = static_cast(opts[1])->value; - m_imgui->slider_float("", &minimal_point_distance, 0.f, 20.f, "%.f mm"); + m_imgui->slider_float("##minimal_point_distance", &minimal_point_distance, 0.f, 20.f, "%.f mm"); bool slider_clicked = ImGui::IsItemClicked(); // someone clicked the slider bool slider_edited = ImGui::IsItemEdited(); // someone is dragging the slider bool slider_released = ImGui::IsItemDeactivatedAfterEdit(); // someone has just released the slider @@ -742,7 +742,7 @@ RENDER_AGAIN: m_imgui->text(m_desc.at("points_density")); ImGui::SameLine(settings_sliders_left); - m_imgui->slider_float(" ", &density, 0.f, 200.f, "%.f %%"); + m_imgui->slider_float("##points_density", &density, 0.f, 200.f, "%.f %%"); slider_clicked |= ImGui::IsItemClicked(); slider_edited |= ImGui::IsItemEdited(); slider_released |= ImGui::IsItemDeactivatedAfterEdit(); @@ -802,7 +802,7 @@ RENDER_AGAIN: ImGui::SameLine(clipping_slider_left); ImGui::PushItemWidth(window_width - clipping_slider_left); float clp_dist = m_c->object_clipper()->get_position(); - if (m_imgui->slider_float(" ", &clp_dist, 0.f, 1.f, "%.2f")) + if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f")) m_c->object_clipper()->set_position(clp_dist, true); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index fe6c18db9..22dccc695 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -280,7 +280,7 @@ void ImGuiWrapper::render() m_new_frame_open = false; } -ImVec2 ImGuiWrapper::calc_text_size(const wxString &text, float wrap_width) +ImVec2 ImGuiWrapper::calc_text_size(const wxString &text, float wrap_width) const { auto text_utf8 = into_u8(text); ImVec2 size = ImGui::CalcTextSize(text_utf8.c_str(), NULL, false, wrap_width); @@ -293,6 +293,22 @@ ImVec2 ImGuiWrapper::calc_text_size(const wxString &text, float wrap_width) return size; } +ImVec2 ImGuiWrapper::calc_button_size(const wxString &text, const ImVec2 &button_size) const +{ + const ImVec2 text_size = this->calc_text_size(text); + const ImGuiContext &g = *GImGui; + const ImGuiStyle &style = g.Style; + + return ImGui::CalcItemSize(button_size, text_size.x + style.FramePadding.x * 2.0f, text_size.y + style.FramePadding.y * 2.0f); +} + +ImVec2 ImGuiWrapper::get_item_spacing() const +{ + const ImGuiContext &g = *GImGui; + const ImGuiStyle &style = g.Style; + return g.Style.ItemSpacing; +} + float ImGuiWrapper::get_slider_float_height() const { const ImGuiContext& g = *GImGui; diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 3712ff6a8..27cd098ff 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -53,9 +53,11 @@ public: float scaled(float x) const { return x * m_font_size; } ImVec2 scaled(float x, float y) const { return ImVec2(x * m_font_size, y * m_font_size); } - ImVec2 calc_text_size(const wxString &text, float wrap_width = -1.0f); + ImVec2 calc_text_size(const wxString &text, float wrap_width = -1.0f) const; + ImVec2 calc_button_size(const wxString &text, const ImVec2 &button_size = ImVec2(0, 0)) const; - float get_slider_float_height() const; + ImVec2 get_item_spacing() const; + float get_slider_float_height() const; void set_next_window_pos(float x, float y, int flag, float pivot_x = 0.0f, float pivot_y = 0.0f); void set_next_window_bg_alpha(float alpha);