From 77da8929270191fc0f978bb0b6bd2e0bec0ace76 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 16 Feb 2023 10:18:40 +0100 Subject: [PATCH 01/15] Added cache for sequential printing clearance contours into class Print to avoid calculate them twice when needed. -Fixed conflicts after cherry-picking 056d7035737265e04ea2848d967fad32b5cf24f5 --- src/libslic3r/Print.cpp | 8 +++++--- src/libslic3r/Print.hpp | 4 ++++ src/slic3r/GUI/Plater.cpp | 8 +++----- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 5b23dc260..4c5d7b851 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -469,11 +469,13 @@ std::string Print::validate(std::string* warning) const return _u8L("The supplied settings will cause an empty print."); if (m_config.complete_objects) { - if (! sequential_print_horizontal_clearance_valid(*this)) + if (!sequential_print_horizontal_clearance_valid(*this, const_cast(&m_sequential_print_clearance_polygons))) return _u8L("Some objects are too close; your extruder will collide with them."); - if (! sequential_print_vertical_clearance_valid(*this)) - return _u8L("Some objects are too tall and cannot be printed without extruder collisions."); + if (!sequential_print_vertical_clearance_valid(*this)) + return _u8L("Some objects are too tall and cannot be printed without extruder collisions."); } + else + const_cast(&m_sequential_print_clearance_polygons)->clear(); if (m_config.avoid_crossing_perimeters && m_config.avoid_crossing_curled_overhangs) { return _u8L("Avoid crossing perimeters option and avoid crossing curled overhangs option cannot be both enabled together."); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 30f6bb41a..ba0a448c7 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -609,6 +609,7 @@ public: const PrintRegion& get_print_region(size_t idx) const { return *m_print_regions[idx]; } const ToolOrdering& get_tool_ordering() const { return m_wipe_tower_data.tool_ordering; } + const Polygons& get_sequential_print_clearance_polygons() const { return m_sequential_print_clearance_polygons; } static bool sequential_print_horizontal_clearance_valid(const Print& print, Polygons* polygons = nullptr); protected: @@ -658,6 +659,9 @@ private: // Estimated print time, filament consumed. PrintStatistics m_print_statistics; + // Cache to store sequential print clearance polygons + Polygons m_sequential_print_clearance_polygons; + // To allow GCode to set the Print's GCodeExport step status. friend class GCode; // To allow GCodeProcessor to emit warnings. diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 35308c058..70296e137 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3306,11 +3306,9 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool return_state |= UPDATE_BACKGROUND_PROCESS_INVALID; if (printer_technology == ptFFF) { const Print* print = background_process.fff_print(); - Polygons polygons; - if (print->config().complete_objects) - Print::sequential_print_horizontal_clearance_valid(*print, &polygons); - view3D->get_canvas3d()->set_sequential_print_clearance_visible(true); - view3D->get_canvas3d()->set_sequential_print_clearance_render_fill(true); + const Polygons polygons = print->get_sequential_print_clearance_polygons(); + view3D->get_canvas3d()->set_sequential_print_clearance_visible(!polygons.empty()); + view3D->get_canvas3d()->set_sequential_print_clearance_render_fill(!polygons.empty()); view3D->get_canvas3d()->set_sequential_print_clearance_polygons(polygons); } } From 6974962dd02b5c1e5e13de3caa29749735ef0e11 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 14 Mar 2023 08:21:38 +0100 Subject: [PATCH 02/15] Avoid updating and sending to gpu sequential print clearance contours at every frame. Cache them instead and update only their transforms. Fixed conflicts after cherry-picking e99ee946afbaf6e6db9413ccacdd03233c4743d1 --- src/libslic3r/Print.cpp | 4 +- src/libslic3r/Print.hpp | 6 +- src/slic3r/GUI/GLCanvas3D.cpp | 165 +++++++++++++++++----- src/slic3r/GUI/GLCanvas3D.hpp | 32 ++++- src/slic3r/GUI/GLModel.cpp | 32 +++++ src/slic3r/GUI/GLModel.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 2 + src/slic3r/GUI/Plater.cpp | 10 +- 8 files changed, 200 insertions(+), 52 deletions(-) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 4c5d7b851..7e196c85f 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -469,13 +469,13 @@ std::string Print::validate(std::string* warning) const return _u8L("The supplied settings will cause an empty print."); if (m_config.complete_objects) { - if (!sequential_print_horizontal_clearance_valid(*this, const_cast(&m_sequential_print_clearance_polygons))) + if (!sequential_print_horizontal_clearance_valid(*this, const_cast(&m_sequential_print_clearance_contours))) return _u8L("Some objects are too close; your extruder will collide with them."); if (!sequential_print_vertical_clearance_valid(*this)) return _u8L("Some objects are too tall and cannot be printed without extruder collisions."); } else - const_cast(&m_sequential_print_clearance_polygons)->clear(); + const_cast(&m_sequential_print_clearance_contours)->clear(); if (m_config.avoid_crossing_perimeters && m_config.avoid_crossing_curled_overhangs) { return _u8L("Avoid crossing perimeters option and avoid crossing curled overhangs option cannot be both enabled together."); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index ba0a448c7..c96b0ca9a 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -609,7 +609,7 @@ public: const PrintRegion& get_print_region(size_t idx) const { return *m_print_regions[idx]; } const ToolOrdering& get_tool_ordering() const { return m_wipe_tower_data.tool_ordering; } - const Polygons& get_sequential_print_clearance_polygons() const { return m_sequential_print_clearance_polygons; } + const Polygons& get_sequential_print_clearance_contours() const { return m_sequential_print_clearance_contours; } static bool sequential_print_horizontal_clearance_valid(const Print& print, Polygons* polygons = nullptr); protected: @@ -659,8 +659,8 @@ private: // Estimated print time, filament consumed. PrintStatistics m_print_statistics; - // Cache to store sequential print clearance polygons - Polygons m_sequential_print_clearance_polygons; + // Cache to store sequential print clearance contours + Polygons m_sequential_print_clearance_contours; // To allow GCode to set the Print's GCodeExport step status. friend class GCode; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index fc2e04d5a..3b1b73b81 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -880,20 +880,22 @@ void GLCanvas3D::Tooltip::render(const Vec2d& mouse_position, GLCanvas3D& canvas ImGui::PopStyleVar(2); } -void GLCanvas3D::SequentialPrintClearance::set_polygons(const Polygons& polygons) +void GLCanvas3D::SequentialPrintClearance::set_contours(const ContoursList& contours) { - m_perimeter.reset(); + m_contours.clear(); + m_instances.clear(); m_fill.reset(); - if (polygons.empty()) + + if (contours.empty()) return; if (m_render_fill) { GLModel::Geometry fill_data; fill_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; - fill_data.color = { 0.3333f, 0.0f, 0.0f, 0.5f }; + fill_data.color = { 0.3333f, 0.0f, 0.0f, 0.5f }; // vertices + indices - const ExPolygons polygons_union = union_ex(polygons); + const ExPolygons polygons_union = union_ex(contours.contours); unsigned int vertices_counter = 0; for (const ExPolygon& poly : polygons_union) { const std::vector triangulation = triangulate_expolygon_3d(poly); @@ -906,17 +908,42 @@ void GLCanvas3D::SequentialPrintClearance::set_polygons(const Polygons& polygons fill_data.add_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1); } } - m_fill.init_from(std::move(fill_data)); } - m_perimeter.init_from(polygons, 0.025f); // add a small positive z to avoid z-fighting + for (size_t i = 0; i < contours.contours.size(); ++i) { + GLModel& model = m_contours.emplace_back(GLModel()); + model.init_from(contours.contours[i], 0.025f); // add a small positive z to avoid z-fighting + } + + if (contours.trafos.has_value()) { + // create the requested instances + for (const auto& instance : contours.trafos.value()) { + m_instances.emplace_back(instance.first, instance.second); + } + } + else { + // no instances have been specified + // create one instance for every polygon + for (size_t i = 0; i < contours.contours.size(); ++i) { + m_instances.emplace_back(i, Transform3f::Identity()); + } + } +} + +void GLCanvas3D::SequentialPrintClearance::update_instances_trafos(const std::vector& trafos) +{ + assert(trafos.size() == m_instances.size()); + for (size_t i = 0; i < trafos.size(); ++i) { + m_instances[i].second = trafos[i]; + } } void GLCanvas3D::SequentialPrintClearance::render() { - const ColorRGBA FILL_COLOR = { 1.0f, 0.0f, 0.0f, 0.5f }; - const ColorRGBA NO_FILL_COLOR = { 1.0f, 1.0f, 1.0f, 0.75f }; + const ColorRGBA FILL_COLOR = { 1.0f, 0.0f, 0.0f, 0.5f }; + const ColorRGBA NO_FILL_COLOR = { 1.0f, 1.0f, 1.0f, 0.75f }; + const ColorRGBA NO_FILL_EVALUATING_COLOR = { 1.0f, 1.0f, 0.0f, 1.0f }; GLShaderProgram* shader = wxGetApp().get_shader("flat"); if (shader == nullptr) @@ -933,9 +960,34 @@ void GLCanvas3D::SequentialPrintClearance::render() glsafe(::glEnable(GL_BLEND)); glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - m_perimeter.set_color(m_render_fill ? FILL_COLOR : NO_FILL_COLOR); - m_perimeter.render(); - m_fill.render(); + if (m_render_fill) + m_fill.render(); + +#if ENABLE_GL_CORE_PROFILE + if (OpenGLManager::get_gl_info().is_core_profile()) { + shader->stop_using(); + + shader = wxGetApp().get_shader("dashed_thick_lines"); + if (shader == nullptr) + return; + + shader->start_using(); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + const std::array& viewport = camera.get_viewport(); + shader->set_uniform("viewport_size", Vec2d(double(viewport[2]), double(viewport[3]))); + shader->set_uniform("width", 1.0f); + shader->set_uniform("gap_size", 0.0f); + } + else +#endif // ENABLE_GL_CORE_PROFILE + glsafe(::glLineWidth(2.0f)); + + for (const auto& [id, trafo] : m_instances) { + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * trafo); + assert(id < m_contours.size()); + m_contours[id].set_color(m_render_fill ? FILL_COLOR : m_evaluating ? NO_FILL_EVALUATING_COLOR : NO_FILL_COLOR); + m_contours[id].render(); + } glsafe(::glDisable(GL_BLEND)); glsafe(::glEnable(GL_CULL_FACE)); @@ -3945,7 +3997,11 @@ void GLCanvas3D::do_move(const std::string& snapshot_type) if (wipe_tower_origin != Vec3d::Zero()) post_event(Vec3dEvent(EVT_GLCANVAS_WIPETOWER_MOVED, std::move(wipe_tower_origin))); - reset_sequential_print_clearance(); + if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects) { + m_sequential_print_clearance_first_displacement = true; + update_sequential_clearance(); + m_sequential_print_clearance.set_evaluating(true); + } m_dirty = true; } @@ -4030,6 +4086,12 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type) if (!done.empty()) post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED)); + if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects) { + m_sequential_print_clearance_first_displacement = true; + update_sequential_clearance(); + m_sequential_print_clearance.set_evaluating(true); + } + m_dirty = true; } @@ -4102,6 +4164,12 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type) if (!done.empty()) post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_SCALED)); + if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects) { + m_sequential_print_clearance_first_displacement = true; + update_sequential_clearance(); + m_sequential_print_clearance.set_evaluating(true); + } + m_dirty = true; } @@ -4378,7 +4446,7 @@ void GLCanvas3D::update_sequential_clearance() return; // collects instance transformations from volumes - // first define temporary cache + // first: define temporary cache unsigned int instances_count = 0; std::vector>> instance_transforms; for (size_t obj = 0; obj < m_model->objects.size(); ++obj) { @@ -4393,7 +4461,7 @@ void GLCanvas3D::update_sequential_clearance() if (instances_count == 1) return; - // second fill temporary cache with data from volumes + // second: fill temporary cache with data from volumes for (const GLVolume* v : m_volumes.volumes) { if (v->is_modifier || v->is_wipe_tower) continue; @@ -4403,14 +4471,24 @@ void GLCanvas3D::update_sequential_clearance() transform = v->get_instance_transformation(); } + // helper function to calculate the transformation to be applied to the sequential print clearance contours + auto instance_trafo = [](const Transform3d& hull_trafo, const Geometry::Transformation& inst_trafo) { + Vec3d offset = inst_trafo.get_offset() - hull_trafo.translation(); + offset.z() = 0.0; + return Geometry::translation_transform(offset) * + Geometry::rotation_transform(Geometry::rotation_diff_z(hull_trafo, inst_trafo.get_matrix()) * Vec3d::UnitZ()); + }; + + set_sequential_print_clearance_render_fill(false); + // calculates objects 2d hulls (see also: Print::sequential_print_horizontal_clearance_valid()) // this is done only the first time this method is called while moving the mouse, // the results are then cached for following displacements if (m_sequential_print_clearance_first_displacement) { - m_sequential_print_clearance.m_hull_2d_cache.clear(); + m_sequential_print_clearance.m_hulls_2d_cache.clear(); const float shrink_factor = static_cast(scale_(0.5 * fff_print()->config().extruder_clearance_radius.value - EPSILON)); const double mitter_limit = scale_(0.1); - m_sequential_print_clearance.m_hull_2d_cache.reserve(m_model->objects.size()); + m_sequential_print_clearance.m_hulls_2d_cache.reserve(m_model->objects.size()); for (size_t i = 0; i < m_model->objects.size(); ++i) { ModelObject* model_object = m_model->objects[i]; ModelInstance* model_instance0 = model_object->instances.front(); @@ -4422,38 +4500,51 @@ void GLCanvas3D::update_sequential_clearance() shrink_factor, jtRound, mitter_limit).front(); - Pointf3s& cache_hull_2d = m_sequential_print_clearance.m_hull_2d_cache.emplace_back(Pointf3s()); - cache_hull_2d.reserve(hull_2d.points.size()); + Pointf3s& new_hull_2d = m_sequential_print_clearance.m_hulls_2d_cache.emplace_back(std::make_pair(Pointf3s(), trafo.get_matrix())).first; + new_hull_2d.reserve(hull_2d.points.size()); const Transform3d inv_trafo = trafo.get_matrix().inverse(); for (const Point& p : hull_2d.points) { - cache_hull_2d.emplace_back(inv_trafo * Vec3d(unscale(p.x()), unscale(p.y()), 0.0)); + new_hull_2d.emplace_back(Vec3d(unscale(p.x()), unscale(p.y()), 0.0)); } } + + ContoursList contours; + contours.contours.reserve(instance_transforms.size()); + contours.trafos = std::vector>(); + contours.trafos.value().reserve(instances_count); + for (size_t i = 0; i < instance_transforms.size(); ++i) { + const auto& [hull, hull_trafo] = m_sequential_print_clearance.m_hulls_2d_cache[i]; + Points hull_pts; + hull_pts.reserve(hull.size()); + for (size_t j = 0; j < hull.size(); ++j) { + hull_pts.emplace_back(scaled(hull[j].x()), scaled(hull[j].y())); + } + contours.contours.emplace_back(Geometry::convex_hull(std::move(hull_pts))); + + const auto& instances = instance_transforms[i]; + for (const auto& instance : instances) { + contours.trafos.value().emplace_back(i, instance_trafo(hull_trafo, instance.value())); + } + } + + set_sequential_print_clearance_contours(contours); m_sequential_print_clearance_first_displacement = false; } - - // calculates instances 2d hulls (see also: Print::sequential_print_horizontal_clearance_valid()) - Polygons polygons; - polygons.reserve(instances_count); - for (size_t i = 0; i < instance_transforms.size(); ++i) { - const auto& instances = instance_transforms[i]; - for (const auto& instance : instances) { - const Transform3d& trafo = instance->get_matrix(); - const Pointf3s& hull_2d = m_sequential_print_clearance.m_hull_2d_cache[i]; - Points inst_pts; - inst_pts.reserve(hull_2d.size()); - for (size_t j = 0; j < hull_2d.size(); ++j) { - const Vec3d p = trafo * hull_2d[j]; - inst_pts.emplace_back(scaled(p.x()), scaled(p.y())); + else { + std::vector trafos; + trafos.reserve(instances_count); + for (size_t i = 0; i < instance_transforms.size(); ++i) { + const auto& [hull, hull_trafo] = m_sequential_print_clearance.m_hulls_2d_cache[i]; + const auto& instances = instance_transforms[i]; + for (const auto& instance : instances) { + trafos.emplace_back(instance_trafo(hull_trafo, instance.value())); } - polygons.emplace_back(Geometry::convex_hull(std::move(inst_pts))); } + m_sequential_print_clearance.update_instances_trafos(trafos); } // sends instances 2d hulls to be rendered set_sequential_print_clearance_visible(true); - set_sequential_print_clearance_render_fill(false); - set_sequential_print_clearance_polygons(polygons); } bool GLCanvas3D::is_object_sinking(int object_idx) const diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 38dcdb8b8..f82076d57 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -618,22 +618,38 @@ public: return ret; } + struct ContoursList + { + // list of unique contours + Polygons contours; + // if defined: list of transforms to apply to contours + std::optional>> trafos; + + bool empty() const { return contours.empty(); } + }; + private: void load_arrange_settings(); class SequentialPrintClearance { GLModel m_fill; - GLModel m_perimeter; + // list of unique contours + std::vector m_contours; + // list of transforms used to render the contours + std::vector> m_instances; bool m_render_fill{ true }; bool m_visible{ false }; + bool m_evaluating{ false }; - std::vector m_hull_2d_cache; + std::vector> m_hulls_2d_cache; public: - void set_polygons(const Polygons& polygons); + void set_contours(const ContoursList& contours); + void update_instances_trafos(const std::vector& trafos); void set_render_fill(bool render_fill) { m_render_fill = render_fill; } void set_visible(bool visible) { m_visible = visible; } + void set_evaluating(bool evaluating) { m_evaluating = evaluating; } void render(); friend class GLCanvas3D; @@ -960,7 +976,7 @@ public: void reset_sequential_print_clearance() { m_sequential_print_clearance.set_visible(false); m_sequential_print_clearance.set_render_fill(false); - m_sequential_print_clearance.set_polygons(Polygons()); + m_sequential_print_clearance.set_contours(ContoursList()); } void set_sequential_print_clearance_visible(bool visible) { @@ -971,8 +987,12 @@ public: m_sequential_print_clearance.set_render_fill(render_fill); } - void set_sequential_print_clearance_polygons(const Polygons& polygons) { - m_sequential_print_clearance.set_polygons(polygons); + void set_sequential_print_clearance_contours(const ContoursList& contours) { + m_sequential_print_clearance.set_contours(contours); + } + + void set_sequential_print_clearance_evaluating(bool evaluating) { + m_sequential_print_clearance.set_evaluating(evaluating); } void update_sequential_clearance(); diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index 61736f9ac..3a7c00285 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -596,6 +596,38 @@ void GLModel::init_from(const indexed_triangle_set& its) } } +void GLModel::init_from(const Polygon& polygon, float z) +{ + if (is_initialized()) { + // call reset() if you want to reuse this model + assert(false); + return; + } + + Geometry& data = m_render_data.geometry; + data.format = { Geometry::EPrimitiveType::Lines, Geometry::EVertexLayout::P3 }; + + const size_t segments_count = polygon.points.size(); + data.reserve_vertices(2 * segments_count); + data.reserve_indices(2 * segments_count); + + // vertices + indices + unsigned int vertices_counter = 0; + for (size_t i = 0; i < segments_count; ++i) { + const Point& p0 = polygon.points[i]; + const Point& p1 = (i == segments_count - 1) ? polygon.points.front() : polygon.points[i + 1]; + data.add_vertex(Vec3f(unscale(p0.x()), unscale(p0.y()), z)); + data.add_vertex(Vec3f(unscale(p1.x()), unscale(p1.y()), z)); + vertices_counter += 2; + data.add_line(vertices_counter - 2, vertices_counter - 1); + } + + // update bounding box + for (size_t i = 0; i < vertices_count(); ++i) { + m_bounding_box.merge(data.extract_position_3(i).cast()); + } +} + void GLModel::init_from(const Polygons& polygons, float z) { if (is_initialized()) { diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index fbf6ed533..3d3a8aac3 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -227,6 +227,7 @@ namespace GUI { void init_from(const TriangleMesh& mesh); #endif // ENABLE_SMOOTH_NORMALS void init_from(const indexed_triangle_set& its); + void init_from(const Polygon& polygon, float z); void init_from(const Polygons& polygons, float z); bool init_from_file(const std::string& filename); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index ad67b99bb..1d3cb4a6e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -984,6 +984,8 @@ bool GLGizmosManager::activate_gizmo(EType type) new_gizmo.register_raycasters_for_picking(); + m_parent.reset_sequential_print_clearance(); + // sucessful activation of gizmo return true; } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 70296e137..87ce3e105 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3306,10 +3306,12 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool return_state |= UPDATE_BACKGROUND_PROCESS_INVALID; if (printer_technology == ptFFF) { const Print* print = background_process.fff_print(); - const Polygons polygons = print->get_sequential_print_clearance_polygons(); - view3D->get_canvas3d()->set_sequential_print_clearance_visible(!polygons.empty()); - view3D->get_canvas3d()->set_sequential_print_clearance_render_fill(!polygons.empty()); - view3D->get_canvas3d()->set_sequential_print_clearance_polygons(polygons); + GLCanvas3D::ContoursList contours; + contours.contours = print->get_sequential_print_clearance_contours(); + view3D->get_canvas3d()->set_sequential_print_clearance_visible(!contours.empty()); + view3D->get_canvas3d()->set_sequential_print_clearance_render_fill(!contours.empty()); + view3D->get_canvas3d()->set_sequential_print_clearance_contours(contours); + view3D->get_canvas3d()->set_sequential_print_clearance_evaluating(false); } } } From 9da788eae1ac3fcbd023afebcfdc8468acbb0681 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 14 Mar 2023 15:37:20 +0100 Subject: [PATCH 03/15] Method GLCanvas3D::update_sequential_clearance() modified to use frontend data to calculate sequential print clearance contours --- src/slic3r/GUI/GLCanvas3D.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 3b1b73b81..9f516ee18 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4445,6 +4445,15 @@ void GLCanvas3D::update_sequential_clearance() if (m_layers_editing.is_enabled() || m_gizmos.is_dragging()) return; + auto instance_transform_from_volumes = [this](int object_idx, int instance_idx) { + for (const GLVolume* v : m_volumes.volumes) { + if (v->object_idx() == object_idx && v->instance_idx() == instance_idx) + return v->get_instance_transformation(); + } + assert(false); + return Geometry::Transformation(); + }; + // collects instance transformations from volumes // first: define temporary cache unsigned int instances_count = 0; @@ -4463,12 +4472,11 @@ void GLCanvas3D::update_sequential_clearance() // second: fill temporary cache with data from volumes for (const GLVolume* v : m_volumes.volumes) { - if (v->is_modifier || v->is_wipe_tower) - continue; - - auto& transform = instance_transforms[v->object_idx()][v->instance_idx()]; + const int object_idx = v->object_idx(); + const int instance_idx = v->instance_idx(); + auto& transform = instance_transforms[object_idx][instance_idx]; if (!transform.has_value()) - transform = v->get_instance_transformation(); + transform = instance_transform_from_volumes(object_idx, instance_idx); } // helper function to calculate the transformation to be applied to the sequential print clearance contours @@ -4491,9 +4499,8 @@ void GLCanvas3D::update_sequential_clearance() m_sequential_print_clearance.m_hulls_2d_cache.reserve(m_model->objects.size()); for (size_t i = 0; i < m_model->objects.size(); ++i) { ModelObject* model_object = m_model->objects[i]; - ModelInstance* model_instance0 = model_object->instances.front(); - Geometry::Transformation trafo = model_instance0->get_transformation(); - trafo.set_offset({ 0.0, 0.0, model_instance0->get_offset().z() }); + Geometry::Transformation trafo = instance_transform_from_volumes((int)i, 0); + trafo.set_offset({ 0.0, 0.0, trafo.get_offset().z() }); const Polygon hull_2d = offset(model_object->convex_hull_2d(trafo.get_matrix()), // Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects // exactly by satisfying the extruder_clearance_radius, this test will not trigger collision. From 5cc1359c0531fd6455588ea3ff4227ceabc69c1d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 15 Mar 2023 09:44:19 +0100 Subject: [PATCH 04/15] Show and update sequential print clearance contours while manipulating the scene using Move/Rotate/Scale gizmos --- src/slic3r/GUI/GLCanvas3D.cpp | 62 +++++++++++++---------- src/slic3r/GUI/GLCanvas3D.hpp | 3 +- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 2 - src/slic3r/GUI/Plater.cpp | 1 - 4 files changed, 38 insertions(+), 30 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 9f516ee18..fd9fe3a71 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -933,10 +933,13 @@ void GLCanvas3D::SequentialPrintClearance::set_contours(const ContoursList& cont void GLCanvas3D::SequentialPrintClearance::update_instances_trafos(const std::vector& trafos) { - assert(trafos.size() == m_instances.size()); - for (size_t i = 0; i < trafos.size(); ++i) { - m_instances[i].second = trafos[i]; + if (trafos.size() == m_instances.size()) { + for (size_t i = 0; i < trafos.size(); ++i) { + m_instances[i].second = trafos[i]; + } } + else + assert(false); } void GLCanvas3D::SequentialPrintClearance::render() @@ -945,6 +948,9 @@ void GLCanvas3D::SequentialPrintClearance::render() const ColorRGBA NO_FILL_COLOR = { 1.0f, 1.0f, 1.0f, 0.75f }; const ColorRGBA NO_FILL_EVALUATING_COLOR = { 1.0f, 1.0f, 0.0f, 1.0f }; + if (m_contours.empty() || m_instances.empty()) + return; + GLShaderProgram* shader = wxGetApp().get_shader("flat"); if (shader == nullptr) return; @@ -3485,8 +3491,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) fff_print()->config().complete_objects){ if (c == GLGizmosManager::EType::Move || c == GLGizmosManager::EType::Scale || - c == GLGizmosManager::EType::Rotate ) - update_sequential_clearance(); + c == GLGizmosManager::EType::Rotate) + update_sequential_clearance(true); } else { if (c == GLGizmosManager::EType::Move || c == GLGizmosManager::EType::Scale || @@ -3691,7 +3697,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) trafo_type.set_relative(); m_selection.translate(cur_pos - m_mouse.drag.start_position_3D, trafo_type); if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects) - update_sequential_clearance(); + update_sequential_clearance(false); wxGetApp().obj_manipul()->set_dirty(); m_dirty = true; } @@ -3998,8 +4004,7 @@ void GLCanvas3D::do_move(const std::string& snapshot_type) post_event(Vec3dEvent(EVT_GLCANVAS_WIPETOWER_MOVED, std::move(wipe_tower_origin))); if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects) { - m_sequential_print_clearance_first_displacement = true; - update_sequential_clearance(); + update_sequential_clearance(true); m_sequential_print_clearance.set_evaluating(true); } @@ -4087,8 +4092,7 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type) post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED)); if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects) { - m_sequential_print_clearance_first_displacement = true; - update_sequential_clearance(); + update_sequential_clearance(true); m_sequential_print_clearance.set_evaluating(true); } @@ -4165,8 +4169,7 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type) post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_SCALED)); if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects) { - m_sequential_print_clearance_first_displacement = true; - update_sequential_clearance(); + update_sequential_clearance(true); m_sequential_print_clearance.set_evaluating(true); } @@ -4437,12 +4440,12 @@ void GLCanvas3D::mouse_up_cleanup() m_canvas->ReleaseMouse(); } -void GLCanvas3D::update_sequential_clearance() +void GLCanvas3D::update_sequential_clearance(bool force_contours_generation) { if (current_printer_technology() != ptFFF || !fff_print()->config().complete_objects) return; - if (m_layers_editing.is_enabled() || m_gizmos.is_dragging()) + if (m_layers_editing.is_enabled()) return; auto instance_transform_from_volumes = [this](int object_idx, int instance_idx) { @@ -4492,7 +4495,7 @@ void GLCanvas3D::update_sequential_clearance() // calculates objects 2d hulls (see also: Print::sequential_print_horizontal_clearance_valid()) // this is done only the first time this method is called while moving the mouse, // the results are then cached for following displacements - if (m_sequential_print_clearance_first_displacement) { + if (force_contours_generation || m_sequential_print_clearance_first_displacement) { m_sequential_print_clearance.m_hulls_2d_cache.clear(); const float shrink_factor = static_cast(scale_(0.5 * fff_print()->config().extruder_clearance_radius.value - EPSILON)); const double mitter_limit = scale_(0.1); @@ -4538,16 +4541,18 @@ void GLCanvas3D::update_sequential_clearance() m_sequential_print_clearance_first_displacement = false; } else { - std::vector trafos; - trafos.reserve(instances_count); - for (size_t i = 0; i < instance_transforms.size(); ++i) { - const auto& [hull, hull_trafo] = m_sequential_print_clearance.m_hulls_2d_cache[i]; - const auto& instances = instance_transforms[i]; - for (const auto& instance : instances) { - trafos.emplace_back(instance_trafo(hull_trafo, instance.value())); + if (!m_sequential_print_clearance.empty()) { + std::vector trafos; + trafos.reserve(instances_count); + for (size_t i = 0; i < instance_transforms.size(); ++i) { + const auto& [hull, hull_trafo] = m_sequential_print_clearance.m_hulls_2d_cache[i]; + const auto& instances = instance_transforms[i]; + for (const auto& instance : instances) { + trafos.emplace_back(instance_trafo(hull_trafo, instance.value())); + } } + m_sequential_print_clearance.update_instances_trafos(trafos); } - m_sequential_print_clearance.update_instances_trafos(trafos); } // sends instances 2d hulls to be rendered @@ -6126,15 +6131,20 @@ void GLCanvas3D::_render_selection() void GLCanvas3D::_render_sequential_clearance() { - if (m_layers_editing.is_enabled() || m_gizmos.is_dragging()) + if (current_printer_technology() != ptFFF || !fff_print()->config().complete_objects) + return; + + if (m_layers_editing.is_enabled()) return; switch (m_gizmos.get_current_type()) { case GLGizmosManager::EType::Flatten: case GLGizmosManager::EType::Cut: - case GLGizmosManager::EType::Hollow: - case GLGizmosManager::EType::SlaSupports: + case GLGizmosManager::EType::MmuSegmentation: + case GLGizmosManager::EType::Measure: + case GLGizmosManager::EType::Emboss: + case GLGizmosManager::EType::Simplify: case GLGizmosManager::EType::FdmSupports: case GLGizmosManager::EType::Seam: { return; } default: { break; } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index f82076d57..68b532f66 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -651,6 +651,7 @@ private: void set_visible(bool visible) { m_visible = visible; } void set_evaluating(bool evaluating) { m_evaluating = evaluating; } void render(); + bool empty() const { return m_contours.empty(); } friend class GLCanvas3D; }; @@ -995,7 +996,7 @@ public: m_sequential_print_clearance.set_evaluating(evaluating); } - void update_sequential_clearance(); + void update_sequential_clearance(bool force_contours_generation); const Print* fff_print() const; const SLAPrint* sla_print() const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 1d3cb4a6e..ad67b99bb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -984,8 +984,6 @@ bool GLGizmosManager::activate_gizmo(EType type) new_gizmo.register_raycasters_for_picking(); - m_parent.reset_sequential_print_clearance(); - // sucessful activation of gizmo return true; } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 87ce3e105..5a815892e 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4418,7 +4418,6 @@ void Plater::priv::on_update_geometry(Vec3dsEvent<2>&) void Plater::priv::on_3dcanvas_mouse_dragging_started(SimpleEvent&) { - view3D->get_canvas3d()->reset_sequential_print_clearance(); } // Update the scene from the background processing, From 2dba40789f2d9a9042bcd342a2efd0b826834ac2 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 15 Mar 2023 13:33:46 +0100 Subject: [PATCH 05/15] Some refactoring related to sequential print clearance contours --- src/slic3r/GUI/GLCanvas3D.cpp | 41 ++++++++++------------- src/slic3r/GUI/GLCanvas3D.hpp | 28 +++++----------- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 14 ++++---- src/slic3r/GUI/Plater.cpp | 26 +++++++------- 4 files changed, 47 insertions(+), 62 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index fd9fe3a71..661d276cb 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -880,7 +880,7 @@ void GLCanvas3D::Tooltip::render(const Vec2d& mouse_position, GLCanvas3D& canvas ImGui::PopStyleVar(2); } -void GLCanvas3D::SequentialPrintClearance::set_contours(const ContoursList& contours) +void GLCanvas3D::SequentialPrintClearance::set_contours(const ContoursList& contours, bool generate_fill) { m_contours.clear(); m_instances.clear(); @@ -889,7 +889,7 @@ void GLCanvas3D::SequentialPrintClearance::set_contours(const ContoursList& cont if (contours.empty()) return; - if (m_render_fill) { + if (generate_fill) { GLModel::Geometry fill_data; fill_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; fill_data.color = { 0.3333f, 0.0f, 0.0f, 0.5f }; @@ -966,8 +966,7 @@ void GLCanvas3D::SequentialPrintClearance::render() glsafe(::glEnable(GL_BLEND)); glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - if (m_render_fill) - m_fill.render(); + m_fill.render(); #if ENABLE_GL_CORE_PROFILE if (OpenGLManager::get_gl_info().is_core_profile()) { @@ -991,7 +990,7 @@ void GLCanvas3D::SequentialPrintClearance::render() for (const auto& [id, trafo] : m_instances) { shader->set_uniform("view_model_matrix", camera.get_view_matrix() * trafo); assert(id < m_contours.size()); - m_contours[id].set_color(m_render_fill ? FILL_COLOR : m_evaluating ? NO_FILL_EVALUATING_COLOR : NO_FILL_COLOR); + m_contours[id].set_color(m_fill.is_initialized() ? FILL_COLOR : m_evaluating ? NO_FILL_EVALUATING_COLOR : NO_FILL_COLOR); m_contours[id].render(); } @@ -3487,17 +3486,12 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // Not only detection of some modifiers !!! if (evt.Dragging()) { GLGizmosManager::EType c = m_gizmos.get_current_type(); - if (current_printer_technology() == ptFFF && - fff_print()->config().complete_objects){ - if (c == GLGizmosManager::EType::Move || - c == GLGizmosManager::EType::Scale || - c == GLGizmosManager::EType::Rotate) + if (c == GLGizmosManager::EType::Move || + c == GLGizmosManager::EType::Scale || + c == GLGizmosManager::EType::Rotate) { + show_sinking_contours(); + if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects) update_sequential_clearance(true); - } else { - if (c == GLGizmosManager::EType::Move || - c == GLGizmosManager::EType::Scale || - c == GLGizmosManager::EType::Rotate) - show_sinking_contours(); } } else if (evt.LeftUp() && @@ -4005,7 +3999,7 @@ void GLCanvas3D::do_move(const std::string& snapshot_type) if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects) { update_sequential_clearance(true); - m_sequential_print_clearance.set_evaluating(true); + m_sequential_print_clearance.m_evaluating = true; } m_dirty = true; @@ -4093,7 +4087,7 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type) if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects) { update_sequential_clearance(true); - m_sequential_print_clearance.set_evaluating(true); + m_sequential_print_clearance.m_evaluating = true; } m_dirty = true; @@ -4170,7 +4164,7 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type) if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects) { update_sequential_clearance(true); - m_sequential_print_clearance.set_evaluating(true); + m_sequential_print_clearance.m_evaluating = true; } m_dirty = true; @@ -4475,6 +4469,9 @@ void GLCanvas3D::update_sequential_clearance(bool force_contours_generation) // second: fill temporary cache with data from volumes for (const GLVolume* v : m_volumes.volumes) { + if (v->is_wipe_tower) + continue; + const int object_idx = v->object_idx(); const int instance_idx = v->instance_idx(); auto& transform = instance_transforms[object_idx][instance_idx]; @@ -4490,12 +4487,11 @@ void GLCanvas3D::update_sequential_clearance(bool force_contours_generation) Geometry::rotation_transform(Geometry::rotation_diff_z(hull_trafo, inst_trafo.get_matrix()) * Vec3d::UnitZ()); }; - set_sequential_print_clearance_render_fill(false); - // calculates objects 2d hulls (see also: Print::sequential_print_horizontal_clearance_valid()) // this is done only the first time this method is called while moving the mouse, // the results are then cached for following displacements if (force_contours_generation || m_sequential_print_clearance_first_displacement) { + m_sequential_print_clearance.m_evaluating = false; m_sequential_print_clearance.m_hulls_2d_cache.clear(); const float shrink_factor = static_cast(scale_(0.5 * fff_print()->config().extruder_clearance_radius.value - EPSILON)); const double mitter_limit = scale_(0.1); @@ -4537,7 +4533,7 @@ void GLCanvas3D::update_sequential_clearance(bool force_contours_generation) } } - set_sequential_print_clearance_contours(contours); + set_sequential_print_clearance_contours(contours, false); m_sequential_print_clearance_first_displacement = false; } else { @@ -4554,9 +4550,6 @@ void GLCanvas3D::update_sequential_clearance(bool force_contours_generation) m_sequential_print_clearance.update_instances_trafos(trafos); } } - - // sends instances 2d hulls to be rendered - set_sequential_print_clearance_visible(true); } bool GLCanvas3D::is_object_sinking(int object_idx) const diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 68b532f66..7e8cc5d25 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -638,18 +638,13 @@ private: std::vector m_contours; // list of transforms used to render the contours std::vector> m_instances; - bool m_render_fill{ true }; - bool m_visible{ false }; bool m_evaluating{ false }; std::vector> m_hulls_2d_cache; public: - void set_contours(const ContoursList& contours); + void set_contours(const ContoursList& contours, bool generate_fill); void update_instances_trafos(const std::vector& trafos); - void set_render_fill(bool render_fill) { m_render_fill = render_fill; } - void set_visible(bool visible) { m_visible = visible; } - void set_evaluating(bool evaluating) { m_evaluating = evaluating; } void render(); bool empty() const { return m_contours.empty(); } @@ -975,25 +970,20 @@ public: } void reset_sequential_print_clearance() { - m_sequential_print_clearance.set_visible(false); - m_sequential_print_clearance.set_render_fill(false); - m_sequential_print_clearance.set_contours(ContoursList()); + m_sequential_print_clearance.m_evaluating = false; + m_sequential_print_clearance.set_contours(ContoursList(), false); } - void set_sequential_print_clearance_visible(bool visible) { - m_sequential_print_clearance.set_visible(visible); + void set_sequential_print_clearance_contours(const ContoursList& contours, bool generate_fill) { + m_sequential_print_clearance.set_contours(contours, generate_fill); } - void set_sequential_print_clearance_render_fill(bool render_fill) { - m_sequential_print_clearance.set_render_fill(render_fill); + bool is_sequential_print_clearance_empty() const { + return m_sequential_print_clearance.empty(); } - void set_sequential_print_clearance_contours(const ContoursList& contours) { - m_sequential_print_clearance.set_contours(contours); - } - - void set_sequential_print_clearance_evaluating(bool evaluating) { - m_sequential_print_clearance.set_evaluating(evaluating); + bool is_sequential_print_clearance_evaluating() const { + return m_sequential_print_clearance.m_evaluating; } void update_sequential_clearance(bool force_contours_generation); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index d86c8275e..ae40e0f76 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -859,13 +859,13 @@ wxString ObjectManipulation::coordinate_type_str(ECoordinatesType type) #if ENABLE_OBJECT_MANIPULATION_DEBUG void ObjectManipulation::render_debug_window() { - ImGuiWrapper& imgui = *wxGetApp().imgui(); -// ImGui::SetNextWindowCollapsed(true, ImGuiCond_Once); - imgui.begin(std::string("ObjectManipulation"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize); - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Coordinates type"); - ImGui::SameLine(); - imgui.text(coordinate_type_str(m_coordinates_type)); - imgui.end(); + ImGuiWrapper& imgui = *wxGetApp().imgui(); +// ImGui::SetNextWindowCollapsed(true, ImGuiCond_Once); + imgui.begin(std::string("ObjectManipulation"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Coordinates type"); + ImGui::SameLine(); + imgui.text(coordinate_type_str(m_coordinates_type)); + imgui.end(); } #endif // ENABLE_OBJECT_MANIPULATION_DEBUG diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 5a815892e..27f6a38f9 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3294,24 +3294,26 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool // or hide the old one. process_validation_warning(warning); if (printer_technology == ptFFF) { - view3D->get_canvas3d()->reset_sequential_print_clearance(); - view3D->get_canvas3d()->set_as_dirty(); - view3D->get_canvas3d()->request_extra_frame(); + GLCanvas3D* canvas = view3D->get_canvas3d(); + if (canvas->is_sequential_print_clearance_evaluating()) { + canvas->reset_sequential_print_clearance(); + canvas->set_as_dirty(); + canvas->request_extra_frame(); + } } } else { - // The print is not valid. - // Show error as notification. + // The print is not valid. + // Show error as notification. notification_manager->push_validate_error_notification(err); return_state |= UPDATE_BACKGROUND_PROCESS_INVALID; if (printer_technology == ptFFF) { - const Print* print = background_process.fff_print(); - GLCanvas3D::ContoursList contours; - contours.contours = print->get_sequential_print_clearance_contours(); - view3D->get_canvas3d()->set_sequential_print_clearance_visible(!contours.empty()); - view3D->get_canvas3d()->set_sequential_print_clearance_render_fill(!contours.empty()); - view3D->get_canvas3d()->set_sequential_print_clearance_contours(contours); - view3D->get_canvas3d()->set_sequential_print_clearance_evaluating(false); + GLCanvas3D* canvas = view3D->get_canvas3d(); + if (canvas->is_sequential_print_clearance_empty() || canvas->is_sequential_print_clearance_evaluating()) { + GLCanvas3D::ContoursList contours; + contours.contours = background_process.fff_print()->get_sequential_print_clearance_contours(); + canvas->set_sequential_print_clearance_contours(contours, true); + } } } } From 0b20019ff2b56f2a30df4b15eed96c61979eeda9 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 16 Mar 2023 08:46:23 +0100 Subject: [PATCH 06/15] Fixed update of sequential print clearance contours when the final result, of manipulating objects with the gizmos, ends up in an identity matrix --- src/slic3r/GUI/Plater.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 27f6a38f9..5bfc60ea0 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3319,6 +3319,18 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool } else { if (invalidated == Print::APPLY_STATUS_UNCHANGED && !background_process.empty()) { + if (printer_technology == ptFFF) { + // Object manipulation with gizmos may end up in a null transformation. + // In this case, we need to trigger the completion of the sequential print clearance contours evaluation + GLCanvas3D* canvas = view3D->get_canvas3d(); + if (canvas->is_sequential_print_clearance_evaluating()) { + GLCanvas3D::ContoursList contours; + contours.contours = background_process.fff_print()->get_sequential_print_clearance_contours(); + canvas->set_sequential_print_clearance_contours(contours, true); + canvas->set_as_dirty(); + canvas->request_extra_frame(); + } + } std::string warning; std::string err = background_process.validate(&warning); if (!err.empty()) From fa2ff496c66d2cc0542405078296ae575893f6e6 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 17 Mar 2023 11:39:22 +0100 Subject: [PATCH 07/15] Fixed build on MAC OS --- src/slic3r/GUI/GLCanvas3D.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 661d276cb..fbd41e57a 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -918,7 +918,7 @@ void GLCanvas3D::SequentialPrintClearance::set_contours(const ContoursList& cont if (contours.trafos.has_value()) { // create the requested instances - for (const auto& instance : contours.trafos.value()) { + for (const auto& instance : *contours.trafos) { m_instances.emplace_back(instance.first, instance.second); } } @@ -4517,7 +4517,7 @@ void GLCanvas3D::update_sequential_clearance(bool force_contours_generation) ContoursList contours; contours.contours.reserve(instance_transforms.size()); contours.trafos = std::vector>(); - contours.trafos.value().reserve(instances_count); + (*contours.trafos).reserve(instances_count); for (size_t i = 0; i < instance_transforms.size(); ++i) { const auto& [hull, hull_trafo] = m_sequential_print_clearance.m_hulls_2d_cache[i]; Points hull_pts; @@ -4529,7 +4529,7 @@ void GLCanvas3D::update_sequential_clearance(bool force_contours_generation) const auto& instances = instance_transforms[i]; for (const auto& instance : instances) { - contours.trafos.value().emplace_back(i, instance_trafo(hull_trafo, instance.value())); + (*contours.trafos).emplace_back(i, instance_trafo(hull_trafo, *instance)); } } @@ -4544,7 +4544,7 @@ void GLCanvas3D::update_sequential_clearance(bool force_contours_generation) const auto& [hull, hull_trafo] = m_sequential_print_clearance.m_hulls_2d_cache[i]; const auto& instances = instance_transforms[i]; for (const auto& instance : instances) { - trafos.emplace_back(instance_trafo(hull_trafo, instance.value())); + trafos.emplace_back(instance_trafo(hull_trafo, *instance)); } } m_sequential_print_clearance.update_instances_trafos(trafos); From beabf5d652313f567846e4cfc74d067854c4f081 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 11 May 2023 12:34:02 +0200 Subject: [PATCH 08/15] Do not calculate sequential print clearance contours for objects out of printbed --- src/slic3r/GUI/GLCanvas3D.cpp | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index fbd41e57a..812c75a33 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4451,6 +4451,14 @@ void GLCanvas3D::update_sequential_clearance(bool force_contours_generation) return Geometry::Transformation(); }; + auto is_object_outside_printbed = [this](int object_idx) { + for (const GLVolume* v : m_volumes.volumes) { + if (v->object_idx() == object_idx && v->is_outside) + return true; + } + return false; + }; + // collects instance transformations from volumes // first: define temporary cache unsigned int instances_count = 0; @@ -4500,13 +4508,19 @@ void GLCanvas3D::update_sequential_clearance(bool force_contours_generation) ModelObject* model_object = m_model->objects[i]; Geometry::Transformation trafo = instance_transform_from_volumes((int)i, 0); trafo.set_offset({ 0.0, 0.0, trafo.get_offset().z() }); - const Polygon hull_2d = offset(model_object->convex_hull_2d(trafo.get_matrix()), + Pointf3s& new_hull_2d = m_sequential_print_clearance.m_hulls_2d_cache.emplace_back(std::make_pair(Pointf3s(), trafo.get_matrix())).first; + if (is_object_outside_printbed((int)i)) + continue; + + Polygon hull_2d = model_object->convex_hull_2d(trafo.get_matrix()); + if (!hull_2d.empty()) { // Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects // exactly by satisfying the extruder_clearance_radius, this test will not trigger collision. - shrink_factor, - jtRound, mitter_limit).front(); + const Polygons offset_res = offset(hull_2d, shrink_factor, jtRound, mitter_limit); + if (!offset_res.empty()) + hull_2d = offset_res.front(); + } - Pointf3s& new_hull_2d = m_sequential_print_clearance.m_hulls_2d_cache.emplace_back(std::make_pair(Pointf3s(), trafo.get_matrix())).first; new_hull_2d.reserve(hull_2d.points.size()); const Transform3d inv_trafo = trafo.get_matrix().inverse(); for (const Point& p : hull_2d.points) { From 568dc2a0e8b1eaec9b7198df67f976194010cad4 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 15 May 2023 12:35:25 +0200 Subject: [PATCH 09/15] Fixed update of sequential printing clearance contours after deleting an object --- src/slic3r/GUI/GLCanvas3D.cpp | 5 +++-- src/slic3r/GUI/GLCanvas3D.hpp | 1 + src/slic3r/GUI/Plater.cpp | 14 ++++++++------ src/slic3r/GUI/Selection.cpp | 2 ++ 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 812c75a33..b4ce7fbff 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -966,7 +966,8 @@ void GLCanvas3D::SequentialPrintClearance::render() glsafe(::glEnable(GL_BLEND)); glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - m_fill.render(); + if (!m_evaluating) + m_fill.render(); #if ENABLE_GL_CORE_PROFILE if (OpenGLManager::get_gl_info().is_core_profile()) { @@ -990,7 +991,7 @@ void GLCanvas3D::SequentialPrintClearance::render() for (const auto& [id, trafo] : m_instances) { shader->set_uniform("view_model_matrix", camera.get_view_matrix() * trafo); assert(id < m_contours.size()); - m_contours[id].set_color(m_fill.is_initialized() ? FILL_COLOR : m_evaluating ? NO_FILL_EVALUATING_COLOR : NO_FILL_COLOR); + m_contours[id].set_color((!m_evaluating && m_fill.is_initialized()) ? FILL_COLOR : m_evaluating ? NO_FILL_EVALUATING_COLOR : NO_FILL_COLOR); m_contours[id].render(); } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 7e8cc5d25..ef4b0c76c 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -987,6 +987,7 @@ public: } void update_sequential_clearance(bool force_contours_generation); + void set_sequential_clearance_as_evaluating() { m_sequential_print_clearance.m_evaluating = true; } const Print* fff_print() const; const SLAPrint* sla_print() const; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 5bfc60ea0..18f63a3aa 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3057,8 +3057,10 @@ bool Plater::priv::delete_object_from_model(size_t obj_idx) sidebar->obj_list()->invalidate_cut_info_for_object(obj_idx); model.delete_object(obj_idx); + update(); object_list_changed(); + return true; } @@ -3255,7 +3257,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool if (view3D->is_layers_editing_enabled()) view3D->get_wxglcanvas()->Refresh(); - if (background_process.empty()) + if (invalidated == Print::APPLY_STATUS_CHANGED || background_process.empty()) view3D->get_canvas3d()->reset_sequential_print_clearance(); if (invalidated == Print::APPLY_STATUS_INVALIDATED) { @@ -3295,11 +3297,9 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool process_validation_warning(warning); if (printer_technology == ptFFF) { GLCanvas3D* canvas = view3D->get_canvas3d(); - if (canvas->is_sequential_print_clearance_evaluating()) { - canvas->reset_sequential_print_clearance(); - canvas->set_as_dirty(); - canvas->request_extra_frame(); - } + canvas->reset_sequential_print_clearance(); + canvas->set_as_dirty(); + canvas->request_extra_frame(); } } else { @@ -3313,6 +3313,8 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool GLCanvas3D::ContoursList contours; contours.contours = background_process.fff_print()->get_sequential_print_clearance_contours(); canvas->set_sequential_print_clearance_contours(contours, true); + canvas->set_as_dirty(); + canvas->request_extra_frame(); } } } diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index cbd61a1dd..cfd7233c2 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1538,6 +1538,8 @@ void Selection::erase() wxGetApp().obj_list()->delete_from_model_and_list(items); ensure_not_below_bed(); } + + wxGetApp().plater()->canvas3D()->set_sequential_clearance_as_evaluating(); } void Selection::render(float scale_factor) From 00e35807abaf68c6ee1c2f1f879fc617b759c748 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 15 May 2023 12:45:54 +0200 Subject: [PATCH 10/15] Follow-up of 568dc2a0e8b1eaec9b7198df67f976194010cad4 - Fixed update of sequential printing clearance contours after deleting a part of an object --- src/slic3r/GUI/Selection.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index cfd7233c2..f3ba5f49c 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1539,7 +1539,10 @@ void Selection::erase() ensure_not_below_bed(); } - wxGetApp().plater()->canvas3D()->set_sequential_clearance_as_evaluating(); + GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); + canvas->set_sequential_clearance_as_evaluating(); + canvas->set_as_dirty(); + canvas->request_extra_frame(); } void Selection::render(float scale_factor) From 15bd82edc356c9aa19c940cc381c5544058ed391 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 12 Apr 2023 10:07:50 +0200 Subject: [PATCH 11/15] SPE-1654 - Out of printbed detection applied only to selected volumes when the selection is not empty --- src/slic3r/GUI/3DScene.cpp | 58 ------------------------- src/slic3r/GUI/3DScene.hpp | 3 -- src/slic3r/GUI/GLCanvas3D.cpp | 82 +++++++++++++++++++++++++++++++++-- src/slic3r/GUI/GLCanvas3D.hpp | 3 ++ 4 files changed, 81 insertions(+), 65 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index dea40fb3e..7c5008554 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -833,64 +833,6 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab } } -bool GLVolumeCollection::check_outside_state(const BuildVolume &build_volume, ModelInstanceEPrintVolumeState *out_state) const -{ - const Model& model = GUI::wxGetApp().plater()->model(); - auto volume_below = [](GLVolume& volume) -> bool - { return volume.object_idx() != -1 && volume.volume_idx() != -1 && volume.is_below_printbed(); }; - // Volume is partially below the print bed, thus a pre-calculated convex hull cannot be used. - auto volume_sinking = [](GLVolume& volume) -> bool - { return volume.object_idx() != -1 && volume.volume_idx() != -1 && volume.is_sinking(); }; - // Cached bounding box of a volume above the print bed. - auto volume_bbox = [volume_sinking](GLVolume& volume) -> BoundingBoxf3 - { return volume_sinking(volume) ? volume.transformed_non_sinking_bounding_box() : volume.transformed_convex_hull_bounding_box(); }; - // Cached 3D convex hull of a volume above the print bed. - auto volume_convex_mesh = [volume_sinking, &model](GLVolume& volume) -> const TriangleMesh& - { return volume_sinking(volume) ? model.objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : *volume.convex_hull(); }; - - ModelInstanceEPrintVolumeState overall_state = ModelInstancePVS_Inside; - bool contained_min_one = false; - - for (GLVolume* volume : this->volumes) - if (! volume->is_modifier && (volume->shader_outside_printer_detection_enabled || (! volume->is_wipe_tower && volume->composite_id.volume_id >= 0))) { - BuildVolume::ObjectState state; - if (volume_below(*volume)) - state = BuildVolume::ObjectState::Below; - else { - switch (build_volume.type()) { - case BuildVolume::Type::Rectangle: - //FIXME this test does not evaluate collision of a build volume bounding box with non-convex objects. - state = build_volume.volume_state_bbox(volume_bbox(*volume)); - break; - case BuildVolume::Type::Circle: - case BuildVolume::Type::Convex: - //FIXME doing test on convex hull until we learn to do test on non-convex polygons efficiently. - case BuildVolume::Type::Custom: - state = build_volume.object_state(volume_convex_mesh(*volume).its, volume->world_matrix().cast(), volume_sinking(*volume)); - break; - default: - // Ignore, don't produce any collision. - state = BuildVolume::ObjectState::Inside; - break; - } - assert(state != BuildVolume::ObjectState::Below); - } - volume->is_outside = state != BuildVolume::ObjectState::Inside; - if (volume->printable) { - if (overall_state == ModelInstancePVS_Inside && volume->is_outside) - overall_state = ModelInstancePVS_Fully_Outside; - if (overall_state == ModelInstancePVS_Fully_Outside && volume->is_outside && state == BuildVolume::ObjectState::Colliding) - overall_state = ModelInstancePVS_Partly_Outside; - contained_min_one |= !volume->is_outside; - } - } - - if (out_state != nullptr) - *out_state = overall_state; - - return contained_min_one; -} - void GLVolumeCollection::reset_outside_state() { for (GLVolume* volume : this->volumes) { diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 2f7e0a767..805972ce7 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -450,9 +450,6 @@ public: void set_show_sinking_contours(bool show) { m_show_sinking_contours = show; } void set_show_non_manifold_edges(bool show) { m_show_non_manifold_edges = show; } - // 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 - bool check_outside_state(const Slic3r::BuildVolume& build_volume, ModelInstanceEPrintVolumeState* out_state) const; void reset_outside_state(); void update_colors_by_extruder(const DynamicPrintConfig* config); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index b4ce7fbff..5d2e8c024 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1511,11 +1511,85 @@ void GLCanvas3D::reset_volumes() ModelInstanceEPrintVolumeState GLCanvas3D::check_volumes_outside_state() const { ModelInstanceEPrintVolumeState state = ModelInstanceEPrintVolumeState::ModelInstancePVS_Inside; - if (m_initialized) - m_volumes.check_outside_state(m_bed.build_volume(), &state); + if (m_initialized && !m_volumes.empty()) + check_volumes_outside_state(m_bed.build_volume(), &state); return state; } +bool GLCanvas3D::check_volumes_outside_state(const Slic3r::BuildVolume& build_volume, ModelInstanceEPrintVolumeState* out_state) const +{ + auto volume_below = [](GLVolume& volume) -> bool + { return volume.object_idx() != -1 && volume.volume_idx() != -1 && volume.is_below_printbed(); }; + // Volume is partially below the print bed, thus a pre-calculated convex hull cannot be used. + auto volume_sinking = [](GLVolume& volume) -> bool + { return volume.object_idx() != -1 && volume.volume_idx() != -1 && volume.is_sinking(); }; + // Cached bounding box of a volume above the print bed. + auto volume_bbox = [volume_sinking](GLVolume& volume) -> BoundingBoxf3 + { return volume_sinking(volume) ? volume.transformed_non_sinking_bounding_box() : volume.transformed_convex_hull_bounding_box(); }; + // Cached 3D convex hull of a volume above the print bed. + auto volume_convex_mesh = [this, volume_sinking](GLVolume& volume) -> const TriangleMesh& + { return volume_sinking(volume) ? m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : *volume.convex_hull(); }; + + auto volumes_to_process_idxs = [this]() { + std::vector ret; + if (m_selection.is_empty()) { + ret = std::vector(m_volumes.volumes.size()); + std::iota(ret.begin(), ret.end(), 0); + } + else { + const GUI::Selection::IndicesList& selected_volume_idxs = m_selection.get_volume_idxs(); + ret.assign(selected_volume_idxs.begin(), selected_volume_idxs.end()); + } + return ret; + }; + + ModelInstanceEPrintVolumeState overall_state = ModelInstancePVS_Inside; + bool contained_min_one = false; + + const std::vector volumes_idxs = volumes_to_process_idxs(); + + for (unsigned int vol_idx : volumes_idxs) { + GLVolume* volume = m_volumes.volumes[vol_idx]; + if (!volume->is_modifier && (volume->shader_outside_printer_detection_enabled || (!volume->is_wipe_tower && volume->composite_id.volume_id >= 0))) { + BuildVolume::ObjectState state; + if (volume_below(*volume)) + state = BuildVolume::ObjectState::Below; + else { + switch (build_volume.type()) { + case BuildVolume::Type::Rectangle: + //FIXME this test does not evaluate collision of a build volume bounding box with non-convex objects. + state = build_volume.volume_state_bbox(volume_bbox(*volume)); + break; + case BuildVolume::Type::Circle: + case BuildVolume::Type::Convex: + //FIXME doing test on convex hull until we learn to do test on non-convex polygons efficiently. + case BuildVolume::Type::Custom: + state = build_volume.object_state(volume_convex_mesh(*volume).its, volume->world_matrix().cast(), volume_sinking(*volume)); + break; + default: + // Ignore, don't produce any collision. + state = BuildVolume::ObjectState::Inside; + break; + } + assert(state != BuildVolume::ObjectState::Below); + } + volume->is_outside = state != BuildVolume::ObjectState::Inside; + if (volume->printable) { + if (overall_state == ModelInstancePVS_Inside && volume->is_outside) + overall_state = ModelInstancePVS_Fully_Outside; + if (overall_state == ModelInstancePVS_Fully_Outside && volume->is_outside && state == BuildVolume::ObjectState::Colliding) + overall_state = ModelInstancePVS_Partly_Outside; + contained_min_one |= !volume->is_outside; + } + } + } + + if (out_state != nullptr) + *out_state = overall_state; + + return contained_min_one; +} + void GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo, int instance_idx) { if (current_printer_technology() != ptSLA) @@ -2520,7 +2594,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re // checks for geometry outside the print volume to render it accordingly if (!m_volumes.empty()) { ModelInstanceEPrintVolumeState state; - const bool contained_min_one = m_volumes.check_outside_state(m_bed.build_volume(), &state); + const bool contained_min_one = check_volumes_outside_state(m_bed.build_volume(), &state); const bool partlyOut = (state == ModelInstanceEPrintVolumeState::ModelInstancePVS_Partly_Outside); const bool fullyOut = (state == ModelInstanceEPrintVolumeState::ModelInstancePVS_Fully_Outside); @@ -6044,7 +6118,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type) } } if (m_requires_check_outside_state) { - m_volumes.check_outside_state(build_volume, nullptr); + check_volumes_outside_state(build_volume, nullptr); m_requires_check_outside_state = false; } } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index ef4b0c76c..2b2b92fcb 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -737,6 +737,9 @@ public: const GLVolumeCollection& get_volumes() const { return m_volumes; } void reset_volumes(); ModelInstanceEPrintVolumeState check_volumes_outside_state() const; + // 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 + bool check_volumes_outside_state(const Slic3r::BuildVolume& build_volume, ModelInstanceEPrintVolumeState* out_state) const; void init_gcode_viewer() { m_gcode_viewer.init(); } void reset_gcode_toolpaths() { m_gcode_viewer.reset(); } From 3377043154e17ebe5f4c9a7e0b4e9c27b8469489 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 15 May 2023 14:05:23 +0200 Subject: [PATCH 12/15] SPE-1698 - Follow-up of 15bd82edc356c9aa19c940cc381c5544058ed391 - Fixed detection of out of printbed --- src/slic3r/GUI/GLCanvas3D.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 5d2e8c024..73a320983 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1584,6 +1584,15 @@ bool GLCanvas3D::check_volumes_outside_state(const Slic3r::BuildVolume& build_vo } } + for (unsigned int vol_idx = 0; vol_idx < m_volumes.volumes.size(); ++vol_idx) { + if (std::find(volumes_idxs.begin(), volumes_idxs.end(), vol_idx) == volumes_idxs.end()) { + if (!m_volumes.volumes[vol_idx]->is_outside) { + contained_min_one = true; + break; + } + } + } + if (out_state != nullptr) *out_state = overall_state; From 2f7f3578d531f2d34f7732a64449606d86bb4aaa Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 16 May 2023 13:04:24 +0200 Subject: [PATCH 13/15] Faster sequential print clearance contours calculation by parallelizing method ModelObject::convex_hull_2d() and function its_convex_hull_2d_above() --- src/libslic3r/Model.cpp | 20 ++++++++++++------ src/libslic3r/TriangleMesh.cpp | 37 ++++++++++++++++++++++++++++++---- 2 files changed, 47 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 9aa4cf3c6..10ef9127c 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -23,6 +23,8 @@ #include #include +#include + #include "SVG.hpp" #include #include "GCodeWriter.hpp" @@ -1055,12 +1057,18 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_ // This method is used by the auto arrange function. Polygon ModelObject::convex_hull_2d(const Transform3d& trafo_instance) const { - Points pts; - for (const ModelVolume* v : volumes) { - if (v->is_model_part()) - append(pts, its_convex_hull_2d_above(v->mesh().its, (trafo_instance * v->get_matrix()).cast(), 0.0f).points); - } - return Geometry::convex_hull(std::move(pts)); + tbb::concurrent_vector chs; + chs.reserve(volumes.size()); + tbb::parallel_for(tbb::blocked_range(0, volumes.size()), [&](const tbb::blocked_range& range) { + for (size_t i = range.begin(); i < range.end(); ++i) { + const ModelVolume* v = volumes[i]; + chs.emplace_back(its_convex_hull_2d_above(v->mesh().its, (trafo_instance * v->get_matrix()).cast(), 0.0f)); + } + }); + + Polygons polygons; + polygons.assign(chs.begin(), chs.end()); + return Geometry::convex_hull(polygons); } void ModelObject::center_around_origin(bool include_modifiers) diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index d35251d20..66bf913e0 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -26,6 +26,8 @@ #include #include +#include + #include #include @@ -871,11 +873,38 @@ void its_collect_mesh_projection_points_above(const indexed_triangle_set &its, c } template -Polygon its_convex_hull_2d_above(const indexed_triangle_set &its, const TransformVertex &transform_fn, const float z) +Polygon its_convex_hull_2d_above(const indexed_triangle_set& its, const TransformVertex& transform_fn, const float z) { - Points all_pts; - its_collect_mesh_projection_points_above(its, transform_fn, z, all_pts); - return Geometry::convex_hull(std::move(all_pts)); + auto collect_mesh_projection_points_above = [&](const tbb::blocked_range& range) { + Points pts; + pts.reserve(range.size() * 4); // there can be up to 4 vertices per triangle + for (size_t i = range.begin(); i < range.end(); ++i) { + const stl_triangle_vertex_indices& tri = its.indices[i]; + const Vec3f tri_pts[3] = { transform_fn(its.vertices[tri(0)]), transform_fn(its.vertices[tri(1)]), transform_fn(its.vertices[tri(2)]) }; + int iprev = 2; + for (int iedge = 0; iedge < 3; ++iedge) { + const Vec3f& p1 = tri_pts[iprev]; + const Vec3f& p2 = tri_pts[iedge]; + if ((p1.z() < z && p2.z() > z) || (p2.z() < z && p1.z() > z)) { + // Edge crosses the z plane. Calculate intersection point with the plane. + const float t = (z - p1.z()) / (p2.z() - p1.z()); + pts.emplace_back(scaled(p1.x() + (p2.x() - p1.x()) * t), scaled(p1.y() + (p2.y() - p1.y()) * t)); + } + if (p2.z() >= z) + pts.emplace_back(scaled(p2.x()), scaled(p2.y())); + iprev = iedge; + } + } + return Geometry::convex_hull(std::move(pts)); + }; + + tbb::concurrent_vector chs; + tbb::parallel_for(tbb::blocked_range(0, its.indices.size()), [&](const tbb::blocked_range& range) { + chs.push_back(collect_mesh_projection_points_above(range)); + }); + + const Polygons polygons(chs.begin(), chs.end()); + return Geometry::convex_hull(polygons); } Polygon its_convex_hull_2d_above(const indexed_triangle_set &its, const Matrix3f &m, const float z) From 0dacdb2061e06813881a50305bd214c86a6f24c5 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 19 May 2023 09:42:30 +0200 Subject: [PATCH 14/15] Follow-up of 15bd82edc356c9aa19c940cc381c5544058ed391 - Fixed detection of out of printbed after arrange --- src/slic3r/GUI/GLCanvas3D.cpp | 12 ++++++------ src/slic3r/GUI/GLCanvas3D.hpp | 4 ++-- src/slic3r/GUI/Jobs/ArrangeJob.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 7 +------ src/slic3r/GUI/Plater.hpp | 7 ++++++- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 73a320983..f7b7bf140 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1508,15 +1508,15 @@ void GLCanvas3D::reset_volumes() _set_warning_notification(EWarning::ObjectOutside, false); } -ModelInstanceEPrintVolumeState GLCanvas3D::check_volumes_outside_state() const +ModelInstanceEPrintVolumeState GLCanvas3D::check_volumes_outside_state(bool selection_only) const { ModelInstanceEPrintVolumeState state = ModelInstanceEPrintVolumeState::ModelInstancePVS_Inside; if (m_initialized && !m_volumes.empty()) - check_volumes_outside_state(m_bed.build_volume(), &state); + check_volumes_outside_state(m_bed.build_volume(), &state, selection_only); return state; } -bool GLCanvas3D::check_volumes_outside_state(const Slic3r::BuildVolume& build_volume, ModelInstanceEPrintVolumeState* out_state) const +bool GLCanvas3D::check_volumes_outside_state(const Slic3r::BuildVolume& build_volume, ModelInstanceEPrintVolumeState* out_state, bool selection_only) const { auto volume_below = [](GLVolume& volume) -> bool { return volume.object_idx() != -1 && volume.volume_idx() != -1 && volume.is_below_printbed(); }; @@ -1530,9 +1530,9 @@ bool GLCanvas3D::check_volumes_outside_state(const Slic3r::BuildVolume& build_vo auto volume_convex_mesh = [this, volume_sinking](GLVolume& volume) -> const TriangleMesh& { return volume_sinking(volume) ? m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : *volume.convex_hull(); }; - auto volumes_to_process_idxs = [this]() { + auto volumes_to_process_idxs = [this, selection_only]() { std::vector ret; - if (m_selection.is_empty()) { + if (!selection_only || m_selection.is_empty()) { ret = std::vector(m_volumes.volumes.size()); std::iota(ret.begin(), ret.end(), 0); } @@ -2603,7 +2603,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re // checks for geometry outside the print volume to render it accordingly if (!m_volumes.empty()) { ModelInstanceEPrintVolumeState state; - const bool contained_min_one = check_volumes_outside_state(m_bed.build_volume(), &state); + const bool contained_min_one = check_volumes_outside_state(m_bed.build_volume(), &state, !force_full_scene_refresh); const bool partlyOut = (state == ModelInstanceEPrintVolumeState::ModelInstancePVS_Partly_Outside); const bool fullyOut = (state == ModelInstanceEPrintVolumeState::ModelInstancePVS_Fully_Outside); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 2b2b92fcb..d44a78155 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -736,10 +736,10 @@ public: unsigned int get_volumes_count() const; const GLVolumeCollection& get_volumes() const { return m_volumes; } void reset_volumes(); - ModelInstanceEPrintVolumeState check_volumes_outside_state() const; + ModelInstanceEPrintVolumeState check_volumes_outside_state(bool selection_only = true) const; // 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 - bool check_volumes_outside_state(const Slic3r::BuildVolume& build_volume, ModelInstanceEPrintVolumeState* out_state) const; + bool check_volumes_outside_state(const Slic3r::BuildVolume& build_volume, ModelInstanceEPrintVolumeState* out_state, bool selection_only = true) const; void init_gcode_viewer() { m_gcode_viewer.init(); } void reset_gcode_toolpaths() { m_gcode_viewer.reset(); } diff --git a/src/slic3r/GUI/Jobs/ArrangeJob.cpp b/src/slic3r/GUI/Jobs/ArrangeJob.cpp index d7c01c368..2aeb525ca 100644 --- a/src/slic3r/GUI/Jobs/ArrangeJob.cpp +++ b/src/slic3r/GUI/Jobs/ArrangeJob.cpp @@ -355,7 +355,7 @@ void ArrangeJob::finalize(bool canceled, std::exception_ptr &eptr) { ap.apply(); } - m_plater->update(); + m_plater->update((unsigned int)Plater::UpdateParams::FORCE_FULL_SCREEN_REFRESH); wxGetApp().obj_manipul()->set_dirty(); if (!m_unarranged.empty()) { diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 18f63a3aa..149c2e1db 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1767,11 +1767,6 @@ struct Plater::priv void render_project_state_debug_window() const { dirty_state.render_debug_window(); } #endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW - enum class UpdateParams { - FORCE_FULL_SCREEN_REFRESH = 1, - FORCE_BACKGROUND_PROCESSING_UPDATE = 2, - POSTPONE_VALIDATION_ERROR_MESSAGE = 4, - }; void update(unsigned int flags = 0); void select_view(const std::string& direction); void select_view_3D(const std::string& name); @@ -6022,7 +6017,7 @@ bool Plater::load_files(const wxArrayString& filenames, bool delete_after_load/* return true; } -void Plater::update() { p->update(); } +void Plater::update(unsigned int flags) { p->update(flags); } Worker &Plater::get_ui_job_worker() { return p->m_worker; } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 035932869..00f31664c 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -185,7 +185,12 @@ public: const wxString& get_last_loaded_gcode() const { return m_last_loaded_gcode; } - void update(); + enum class UpdateParams { + FORCE_FULL_SCREEN_REFRESH = 1, + FORCE_BACKGROUND_PROCESSING_UPDATE = 2, + POSTPONE_VALIDATION_ERROR_MESSAGE = 4, + }; + void update(unsigned int flags = 0); // Get the worker handling the UI jobs (arrange, fill bed, etc...) // Here is an example of starting up an ad-hoc job: From e62c25189865f85053948051e545b3ebce67d124 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 22 May 2023 09:05:44 +0200 Subject: [PATCH 15/15] Follow-up of 15bd82e - Fixed detection of out of printbed after copy and paste --- src/slic3r/GUI/GUI_ObjectList.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 88e8f8691..76c8cecad 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -4010,8 +4010,10 @@ void ObjectList::update_selections_on_canvas() selection.add_volumes(mode, volume_idxs, single_selection); } - wxGetApp().plater()->canvas3D()->update_gizmos_on_off_state(); - wxGetApp().plater()->canvas3D()->render(); + GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); + canvas->update_gizmos_on_off_state(); + canvas->check_volumes_outside_state(); + canvas->render(); } void ObjectList::select_item(const wxDataViewItem& item)