From 056d7035737265e04ea2848d967fad32b5cf24f5 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 16 Feb 2023 10:18:40 +0100 Subject: [PATCH 01/90] Added cache for sequential printing clearance contours into class Print to avoid calculate them twice when needed. --- 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 3c810b178e..0570853ff2 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -471,11 +471,13 @@ std::string Print::validate(std::string* warning) const return L("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 L("Some objects are too close; your extruder will collide with them."); - if (! sequential_print_vertical_clearance_valid(*this)) - return L("Some objects are too tall and cannot be printed without extruder collisions."); + if (!sequential_print_vertical_clearance_valid(*this)) + return L("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 L("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 3795c24491..580980cfc1 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -597,6 +597,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: @@ -646,6 +647,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; // Allow PrintObject to access m_mutex and m_cancel_callback. diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 4fc59db625..c69908713a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3320,11 +3320,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 e99ee946afbaf6e6db9413ccacdd03233c4743d1 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 14 Mar 2023 08:21:38 +0100 Subject: [PATCH 02/90] Avoid updating and sending to gpu sequential print clearance contours at every frame. Cache them instead and update only their transforms. --- 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 d2b1da4b5e..1eb1b41a4f 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -466,13 +466,13 @@ std::string Print::validate(std::string* warning) const return L("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 L("Some objects are too close; your extruder will collide with them."); if (!sequential_print_vertical_clearance_valid(*this)) return L("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 L("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 580980cfc1..be0a6d5160 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -597,7 +597,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: @@ -647,8 +647,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 e137a1b3ed..6a089a7466 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)); @@ -3531,7 +3583,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; } @@ -3609,6 +3665,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; } @@ -3674,6 +3736,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; } @@ -3942,7 +4010,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) { @@ -3957,7 +4025,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; @@ -3967,14 +4035,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(); @@ -3986,38 +4064,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 b5914e4568..86a3cc71b3 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -587,22 +587,38 @@ public: return ret; } + struct ContoursList + { + // list of unique contours + std::vector 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; @@ -927,7 +943,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) { @@ -938,8 +954,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 61736f9ac9..3a7c002850 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 ef4ab6d474..b93f6fad5b 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 af93c07df3..449ebf9459 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -971,6 +971,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 a655357fd9..9cc28acfd3 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3293,10 +3293,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 3eed2e8a94ea0fc181827b9f0e78e576d119dd1d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 14 Mar 2023 15:37:20 +0100 Subject: [PATCH 03/90] 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 6a089a7466..7daf1d9e1e 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4009,6 +4009,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; @@ -4027,12 +4036,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 @@ -4055,9 +4063,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 2dfeaf0fbcf35852b478fe4a9396ba4d1c5d1a89 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 15 Mar 2023 09:44:19 +0100 Subject: [PATCH 04/90] 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 7daf1d9e1e..7a19f53bf8 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; @@ -3088,8 +3094,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 || @@ -3284,7 +3290,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; } @@ -3584,8 +3590,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); } @@ -3666,8 +3671,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); } @@ -3737,8 +3741,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); } @@ -4001,12 +4004,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) { @@ -4056,7 +4059,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); @@ -4102,16 +4105,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 @@ -5676,15 +5681,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 86a3cc71b3..c8e6cd6357 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -620,6 +620,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; }; @@ -962,7 +963,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 449ebf9459..af93c07df3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -971,8 +971,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 9cc28acfd3..79917342d1 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4397,7 +4397,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 2430eb9738c557bc87532a2c4d3446f1bb4e87c2 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 15 Mar 2023 13:33:46 +0100 Subject: [PATCH 05/90] 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 7a19f53bf8..3837c40eed 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(); } @@ -3090,17 +3089,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() && @@ -3591,7 +3585,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; @@ -3672,7 +3666,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; @@ -3742,7 +3736,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; @@ -4039,6 +4033,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]; @@ -4054,12 +4051,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); @@ -4101,7 +4097,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 { @@ -4118,9 +4114,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 c8e6cd6357..0b5aa6ed6a 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -607,18 +607,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(); } @@ -942,25 +937,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 f7290320cd..3ac475f98f 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -858,13 +858,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 79917342d1..b103244924 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3281,24 +3281,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 cb04203f31f9feac5ed138f2fbd3d0f999afb48a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 16 Mar 2023 08:46:23 +0100 Subject: [PATCH 06/90] 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 b103244924..c84cd6b92d 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3306,6 +3306,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 60bb57404f50e1a3d1038d1cf8064fe40d4d20ee Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 17 Mar 2023 11:39:22 +0100 Subject: [PATCH 07/90] 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 3837c40eed..20a4cdec5f 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); } } @@ -4081,7 +4081,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; @@ -4093,7 +4093,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)); } } @@ -4108,7 +4108,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 797dd1197e2be640b00d74b46387f151d09ab9d5 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 3 Apr 2023 08:54:04 +0200 Subject: [PATCH 08/90] Allow the user to switch between visualizing original or processed volumes in 3D scene after slicing using SLA printers --- src/slic3r/GUI/3DScene.cpp | 26 ++++- src/slic3r/GUI/GLCanvas3D.cpp | 115 +++++++++++++++++++ src/slic3r/GUI/GLCanvas3D.hpp | 44 ++++++- src/slic3r/GUI/GUI_ObjectList.cpp | 4 + src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 3 +- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 2 + src/slic3r/GUI/Plater.cpp | 4 +- src/slic3r/GUI/Selection.cpp | 37 ++++++ src/slic3r/GUI/Selection.hpp | 3 + 9 files changed, 231 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 71361e68f6..cfd4571510 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -613,10 +613,28 @@ void GLVolumeCollection::load_object_auxiliary( if (convex_hull.has_value()) v.set_convex_hull(*convex_hull); v.is_modifier = false; - v.shader_outside_printer_detection_enabled = (step == slaposSupportTree); + v.shader_outside_printer_detection_enabled = (step == slaposSupportTree || step == slaposDrillHoles); v.set_instance_transformation(model_instance.get_transformation()); }; + if (milestone == SLAPrintObjectStep::slaposDrillHoles) { + if (print_object->get_parts_to_slice().size() > 1) { + // Get the mesh. + TriangleMesh backend_mesh; + std::shared_ptr preview_mesh_ptr = print_object->get_mesh_to_print(); + if (preview_mesh_ptr != nullptr) + backend_mesh = TriangleMesh(*preview_mesh_ptr); + if (!backend_mesh.empty()) { + backend_mesh.transform(mesh_trafo_inv); + TriangleMesh convex_hull = backend_mesh.convex_hull_3d(); + for (const std::pair& instance_idx : instances) { + const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first]; + add_volume(obj_idx, (int)instance_idx.first, model_instance, slaposDrillHoles, backend_mesh, GLVolume::MODEL_COLOR[0], convex_hull); + } + } + } + } + // Get the support mesh. if (milestone == SLAPrintObjectStep::slaposSupportTree) { TriangleMesh supports_mesh = print_object->support_mesh(); @@ -624,8 +642,8 @@ void GLVolumeCollection::load_object_auxiliary( supports_mesh.transform(mesh_trafo_inv); TriangleMesh convex_hull = supports_mesh.convex_hull_3d(); for (const std::pair& instance_idx : instances) { - const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first]; - add_volume(obj_idx, (int)instance_idx.first, model_instance, slaposSupportTree, supports_mesh, GLVolume::SLA_SUPPORT_COLOR, convex_hull); + const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first]; + add_volume(obj_idx, (int)instance_idx.first, model_instance, slaposSupportTree, supports_mesh, GLVolume::SLA_SUPPORT_COLOR, convex_hull); } } } @@ -924,7 +942,7 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con } for (GLVolume* volume : volumes) { - if (volume == nullptr || volume->is_modifier || volume->is_wipe_tower || volume->volume_idx() < 0) + if (volume == nullptr || volume->is_modifier || volume->is_wipe_tower || volume->is_sla_pad() || volume->is_sla_support()) continue; int extruder_id = volume->extruder_id - 1; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 9583468494..69bcd6ab5c 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1072,6 +1072,107 @@ void GLCanvas3D::load_arrange_settings() m_arrange_settings_fff_seq_print.alignment = arr_alignment ; } +static int processed_object_idx(const Model& model, const SLAPrint& sla_print, const GLVolumePtrs& volumes) +{ + for (const GLVolume* v : volumes) { + if (v->volume_idx() == -(int)slaposDrillHoles) { + const int mo_idx = v->object_idx(); + const ModelObject* model_object = (mo_idx < (int)model.objects.size()) ? model.objects[mo_idx] : nullptr; + if (model_object != nullptr && model_object->instances[v->instance_idx()]->is_printable()) { + const SLAPrintObject* print_object = sla_print.get_print_object_by_model_object_id(model_object->id()); + if (print_object != nullptr && print_object->get_parts_to_slice().size() > 1) + return mo_idx; + } + } + } + + return -1; +}; + +GLCanvas3D::ESLAViewType GLCanvas3D::SLAView::detect_type(const GLVolumePtrs& volumes) +{ + m_type = ESLAViewType::Original; + if (m_allow_type_detection) { + for (const GLVolume* v : volumes) { + if (v->volume_idx() == -(int)slaposDrillHoles) { + m_type = ESLAViewType::Processed; + break; + } + } + } + + m_parent.set_sla_view_type(m_type); + m_allow_type_detection = false; + return m_type; +} + +bool GLCanvas3D::SLAView::set_type(ESLAViewType type) { + if (m_type != type) { + m_type = type; + return true; + } + return false; +} + +void GLCanvas3D::SLAView::update_volumes(GLVolumePtrs& volumes) +{ + const SLAPrint* sla_print = m_parent.sla_print(); + const int mo_idx = (sla_print != nullptr) ? processed_object_idx(*m_parent.get_model(), *sla_print, volumes) : -1; + const bool show_processed = m_type == ESLAViewType::Processed && mo_idx != -1; + + auto show = [show_processed](const GLVolume& v) { + return show_processed ? v.volume_idx() < 0 : v.volume_idx() != -(int)slaposDrillHoles; + }; + + std::vector>* raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); + + for (GLVolume* v : volumes) { + v->is_active = v->object_idx() != mo_idx || show(*v); + auto it = std::find_if(raycasters->begin(), raycasters->end(), [v](std::shared_ptr item) { return item->get_raycaster() == v->mesh_raycaster.get(); }); + if (it != raycasters->end()) + (*it)->set_active(v->is_active); + } +} + +void GLCanvas3D::SLAView::render_switch_button() +{ + const SLAPrint* sla_print = m_parent.sla_print(); + const int mo_idx = (sla_print != nullptr) ? processed_object_idx(*m_parent.get_model(), *sla_print, m_parent.get_volumes().volumes) : -1; + if (mo_idx == -1) + return; + + const BoundingBoxf ss_box = m_parent.get_selection().get_screen_space_bounding_box(); + if (ss_box.defined) { + ImGuiWrapper& imgui = *wxGetApp().imgui(); + ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); + ImGui::SetNextWindowPos(ImVec2((float)ss_box.max.x(), (float)ss_box.center().y()), ImGuiCond_Always, ImVec2(0.0, 0.5)); + imgui.begin(std::string("SLAViewSwitch"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration); + const wxString btn_text = (m_type == ESLAViewType::Original) ? _L("Processed") : _L("Original"); + if (imgui.button(btn_text)) { + switch (m_type) + { + case ESLAViewType::Original: { m_parent.set_sla_view_type(ESLAViewType::Processed); break; } + case ESLAViewType::Processed: { m_parent.set_sla_view_type(ESLAViewType::Original); break; } + default: { assert(false); break; } + } + } + imgui.end(); + ImGui::PopStyleColor(2); + } +} + +//void GLCanvas3D::SLAView::render_debug_window() +//{ +// ImGuiWrapper& imgui = *wxGetApp().imgui(); +// imgui.begin(std::string("SLAView"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize); +// imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Type:"); +// ImGui::SameLine(); +// const std::string text = (m_type == ESLAViewType::Original) ? "Original" : "Processed"; +// imgui.text_colored(ImGui::GetStyleColorVec4(ImGuiCol_Text), text); +// imgui.end(); +//} + PrinterTechnology GLCanvas3D::current_printer_technology() const { return m_process->current_printer_technology(); @@ -1113,6 +1214,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed) , m_render_sla_auxiliaries(true) , m_labels(*this) , m_slope(m_volumes) + , m_sla_view(*this) { if (m_canvas != nullptr) { m_timer.SetOwner(m_canvas); @@ -1642,6 +1744,13 @@ void GLCanvas3D::render() GLModel::render_statistics(); #endif // ENABLE_GLMODEL_STATISTICS + if (wxGetApp().plater()->is_view3D_shown() && current_printer_technology() == ptSLA) { + const GLGizmosManager::EType type = m_gizmos.get_current_type(); + if (type == GLGizmosManager::EType::Undefined) + m_sla_view.render_switch_button(); +// m_sla_view.render_debug_window(); + } + std::string tooltip; // Negative coordinate means out of the window, likely because the window was deactivated. @@ -2114,6 +2223,9 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re if (!instances[istep].empty()) m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp); } + + if (m_sla_view.detect_type(m_volumes.volumes) == ESLAViewType::Processed) + update_object_list = true; } // Shift-up all volumes of the object so that it has the right elevation with respect to the print bed @@ -2169,6 +2281,9 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re else m_selection.volumes_changed(map_glvolume_old_to_new); + if (printer_technology == ptSLA) + m_sla_view.update_volumes(m_volumes.volumes); + m_gizmos.update_data(); m_gizmos.refresh_on_off_state(); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index a79bcff2ed..cb76a79e9c 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -468,6 +468,12 @@ public: int alignment = 0; }; + enum class ESLAViewType + { + Original, + Processed + }; + private: wxGLCanvas* m_canvas; wxGLContext* m_context; @@ -547,6 +553,26 @@ private: bool m_tooltip_enabled{ true }; Slope m_slope; + class SLAView + { + public: + explicit SLAView(GLCanvas3D& parent) : m_parent(parent) {} + void allow_type_detection(bool allow) { m_allow_type_detection = allow; } + ESLAViewType detect_type(const GLVolumePtrs& volumes); + ESLAViewType get_type() const { return m_type; } + bool set_type(ESLAViewType type); + void update_volumes(GLVolumePtrs& volumes); + void render_switch_button(); +// void render_debug_window(); + + private: + GLCanvas3D& m_parent; + ESLAViewType m_type{ ESLAViewType::Original }; + bool m_allow_type_detection{ false }; + }; + + SLAView m_sla_view; + ArrangeSettings m_arrange_settings_fff, m_arrange_settings_sla, m_arrange_settings_fff_seq_print; @@ -656,7 +682,7 @@ private: GLModel m_background; public: - explicit GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed); + GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed); ~GLCanvas3D(); bool is_initialized() const { return m_initialized; } @@ -962,6 +988,22 @@ public: std::pair> get_layers_height_data(int object_id); + void set_sla_view_type(ESLAViewType type) { + if (type == ESLAViewType::Processed) { + assert(!m_selection.is_empty()); + const GLVolume* v = m_selection.get_first_volume(); + m_selection.add_instance(v->object_idx(), v->instance_idx()); + post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); + } + + m_dirty = type != m_sla_view.get_type(); + + if (m_sla_view.set_type(type)) + m_sla_view.update_volumes(m_volumes.volumes); + } + + void allow_sla_view_type_detection(bool allow) { m_sla_view.allow_type_detection(allow); } + private: bool _is_shown_on_screen() const; diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 5f8b9a75b5..c1536da0bf 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -747,6 +747,10 @@ void ObjectList::selection_changed() wxGetApp().obj_layers()->update_scene_from_editor_selection(); } } + else if (type & itVolume) { + if (printer_technology() == ptSLA) + wxGetApp().plater()->canvas3D()->set_sla_view_type(GLCanvas3D::ESLAViewType::Original); + } } part_selection_changed(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index fe7e1b4eae..5998d5b118 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -64,7 +64,6 @@ void GLGizmoSlaSupports::data_changed() // If we triggered autogeneration before, check backend and fetch results if they are there if (mo) { - m_c->instances_hider()->set_hide_full_scene(true); const SLAPrintObject* po = m_c->selection_info()->print_object(); const int required_step = get_min_sla_print_object_step(); auto last_comp_step = static_cast(po->last_completed_step()); @@ -83,6 +82,8 @@ void GLGizmoSlaSupports::data_changed() register_point_raycasters_for_picking(); else update_point_raycasters_for_picking_transform(); + + m_c->instances_hider()->set_hide_full_scene(true); } // m_parent.toggle_model_objects_visibility(false); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index af93c07df3..20444586d3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -969,6 +969,8 @@ bool GLGizmosManager::activate_gizmo(EType type) return false; // gizmo refused to be turned on. } + m_parent.set_sla_view_type(GLCanvas3D::ESLAViewType::Original); + new_gizmo.register_raycasters_for_picking(); // sucessful activation of gizmo diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 159a9693d2..a2ed0bef46 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3448,8 +3448,10 @@ unsigned int Plater::priv::update_restart_background_process(bool force_update_s { // bitmask of UpdateBackgroundProcessReturnState unsigned int state = this->update_background_process(false); - if (force_update_scene || (state & UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) != 0) + if (force_update_scene || (state & UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) != 0) { + view3D->get_canvas3d()->allow_sla_view_type_detection(true); view3D->reload_scene(false); + } if (force_update_preview) this->preview->reload_print(); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 28cf367fdf..b10a45928d 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -890,6 +890,43 @@ std::pair Selection::get_bounding_box_in_reference_s } #endif // ENABLE_WORLD_COORDINATE +BoundingBoxf Selection::get_screen_space_bounding_box() +{ + BoundingBoxf ss_box; + if (!is_empty()) { + const auto& [box, box_trafo] = get_bounding_box_in_current_reference_system(); + + // vertices + std::vector vertices = { + { box.min.x(), box.min.y(), box.min.z() }, + { box.max.x(), box.min.y(), box.min.z() }, + { box.max.x(), box.max.y(), box.min.z() }, + { box.min.x(), box.max.y(), box.min.z() }, + { box.min.x(), box.min.y(), box.max.z() }, + { box.max.x(), box.min.y(), box.max.z() }, + { box.max.x(), box.max.y(), box.max.z() }, + { box.min.x(), box.max.y(), box.max.z() } + }; + + const Camera& camera = wxGetApp().plater()->get_camera(); + const Matrix4d projection_view_matrix = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(); + const std::array& viewport = camera.get_viewport(); + + const double half_w = 0.5 * double(viewport[2]); + const double h = double(viewport[3]); + const double half_h = 0.5 * h; + for (const Vec3d& v : vertices) { + const Vec3d world = box_trafo * v; + const Vec4d clip = projection_view_matrix * Vec4d(world.x(), world.y(), world.z(), 1.0); + const Vec3d ndc = Vec3d(clip.x(), clip.y(), clip.z()) / clip.w(); + const Vec2d ss = Vec2d(half_w * ndc.x() + double(viewport[0]) + half_w, h - (half_h * ndc.y() + double(viewport[1]) + half_h)); + ss_box.merge(ss); + } + } + + return ss_box; +} + void Selection::setup_cache() { if (!m_valid) diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index d3f4567027..0657a8d8a5 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -400,6 +400,9 @@ public: std::pair get_bounding_box_in_reference_system(ECoordinatesType type) const; #endif // ENABLE_WORLD_COORDINATE + // Returns the screen space bounding box + BoundingBoxf get_screen_space_bounding_box(); + void setup_cache(); #if ENABLE_WORLD_COORDINATE From d1c959529c80a086bce84b301bc685805963c530 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 3 Apr 2023 11:39:53 +0200 Subject: [PATCH 09/90] Fixed assert showing up after merge with master --- src/slic3r/GUI/Selection.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 3f0deab40e..cbd61a1dde 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1742,18 +1742,11 @@ std::vector Selection::get_volume_idxs_from_object(unsigned int ob std::vector Selection::get_volume_idxs_from_instance(unsigned int object_idx, unsigned int instance_idx) const { std::vector idxs; - - const PrinterTechnology pt = wxGetApp().plater()->printer_technology(); - for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { const GLVolume* v = (*m_volumes)[i]; - const ModelVolume *mv = get_model_volume(*v, *m_model); - if (pt == ptSLA && v->is_modifier && mv && mv->is_modifier()) - continue; if (v->object_idx() == (int)object_idx && v->instance_idx() == (int)instance_idx) idxs.push_back(i); } - return idxs; } @@ -1761,8 +1754,7 @@ std::vector Selection::get_volume_idxs_from_volume(unsigned int ob { std::vector idxs; - for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) - { + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { const GLVolume* v = (*m_volumes)[i]; if (v->object_idx() == (int)object_idx && v->volume_idx() == (int)volume_idx) { if ((int)instance_idx != -1 && v->instance_idx() == (int)instance_idx) From 799e44f155ac1008d9611b63531a52ff29041174 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 4 Apr 2023 09:47:22 +0200 Subject: [PATCH 10/90] Show an icon button, in place of a plain imgui button, in the 3D scene as switch between view types of an object sliced with sla printer --- resources/icons/sla_view_original.svg | 72 ++++++++++++++++++++++++++ resources/icons/sla_view_processed.svg | 56 ++++++++++++++++++++ src/imgui/imconfig.h | 2 + src/slic3r/GUI/GCodeViewer.cpp | 59 +++++++++------------ src/slic3r/GUI/GLCanvas3D.cpp | 32 ++++++++++-- src/slic3r/GUI/ImGuiWrapper.cpp | 14 +++++ src/slic3r/GUI/ImGuiWrapper.hpp | 3 +- 7 files changed, 199 insertions(+), 39 deletions(-) create mode 100644 resources/icons/sla_view_original.svg create mode 100644 resources/icons/sla_view_processed.svg diff --git a/resources/icons/sla_view_original.svg b/resources/icons/sla_view_original.svg new file mode 100644 index 0000000000..4691721c87 --- /dev/null +++ b/resources/icons/sla_view_original.svg @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + diff --git a/resources/icons/sla_view_processed.svg b/resources/icons/sla_view_processed.svg new file mode 100644 index 0000000000..a26a0db2f1 --- /dev/null +++ b/resources/icons/sla_view_processed.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index dff8aea9f7..5aed978426 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -166,6 +166,8 @@ namespace ImGui const wchar_t PauseHoverButton = 0x261B; const wchar_t OpenButton = 0x261C; const wchar_t OpenHoverButton = 0x261D; + const wchar_t SlaViewOriginal = 0x261E; + const wchar_t SlaViewProcessed = 0x261F; const wchar_t LegendTravel = 0x2701; const wchar_t LegendWipe = 0x2702; diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 62d4314dbf..20ba4a2160 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -4049,69 +4049,58 @@ void GCodeViewer::render_legend(float& legend_height) } }; - auto image_icon = [&imgui](ImGuiWindow& window, const ImVec2& pos, float size, const wchar_t& icon_id) { - ImGuiIO& io = ImGui::GetIO(); - const ImTextureID tex_id = io.Fonts->TexID; - const float tex_w = static_cast(io.Fonts->TexWidth); - const float tex_h = static_cast(io.Fonts->TexHeight); - const ImFontAtlas::CustomRect* const rect = imgui.GetTextureCustomRect(icon_id); - const ImVec2 uv0 = { static_cast(rect->X) / tex_w, static_cast(rect->Y) / tex_h }; - const ImVec2 uv1 = { static_cast(rect->X + rect->Width) / tex_w, static_cast(rect->Y + rect->Height) / tex_h }; - window.DrawList->AddImage(tex_id, pos, { pos.x + size, pos.y + size }, uv0, uv1, ImGuiWrapper::to_ImU32({ 1.0f, 1.0f, 1.0f, 1.0f })); - }; - ImGui::Spacing(); ImGui::Separator(); ImGui::Spacing(); ImGui::Spacing(); - toggle_button(Preview::OptionType::Travel, _u8L("Travel"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { - image_icon(window, pos, size, ImGui::LegendTravel); + toggle_button(Preview::OptionType::Travel, _u8L("Travel"), [&imgui](ImGuiWindow& window, const ImVec2& pos, float size) { + imgui.draw_icon(window, pos, size, ImGui::LegendTravel); }); ImGui::SameLine(); - toggle_button(Preview::OptionType::Wipe, _u8L("Wipe"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { - image_icon(window, pos, size, ImGui::LegendWipe); + toggle_button(Preview::OptionType::Wipe, _u8L("Wipe"), [&imgui](ImGuiWindow& window, const ImVec2& pos, float size) { + imgui.draw_icon(window, pos, size, ImGui::LegendWipe); }); ImGui::SameLine(); - toggle_button(Preview::OptionType::Retractions, _u8L("Retractions"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { - image_icon(window, pos, size, ImGui::LegendRetract); + toggle_button(Preview::OptionType::Retractions, _u8L("Retractions"), [&imgui](ImGuiWindow& window, const ImVec2& pos, float size) { + imgui.draw_icon(window, pos, size, ImGui::LegendRetract); }); ImGui::SameLine(); - toggle_button(Preview::OptionType::Unretractions, _u8L("Deretractions"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { - image_icon(window, pos, size, ImGui::LegendDeretract); + toggle_button(Preview::OptionType::Unretractions, _u8L("Deretractions"), [&imgui](ImGuiWindow& window, const ImVec2& pos, float size) { + imgui.draw_icon(window, pos, size, ImGui::LegendDeretract); }); ImGui::SameLine(); - toggle_button(Preview::OptionType::Seams, _u8L("Seams"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { - image_icon(window, pos, size, ImGui::LegendSeams); + toggle_button(Preview::OptionType::Seams, _u8L("Seams"), [&imgui](ImGuiWindow& window, const ImVec2& pos, float size) { + imgui.draw_icon(window, pos, size, ImGui::LegendSeams); }); ImGui::SameLine(); - toggle_button(Preview::OptionType::ToolChanges, _u8L("Tool changes"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { - image_icon(window, pos, size, ImGui::LegendToolChanges); + toggle_button(Preview::OptionType::ToolChanges, _u8L("Tool changes"), [&imgui](ImGuiWindow& window, const ImVec2& pos, float size) { + imgui.draw_icon(window, pos, size, ImGui::LegendToolChanges); }); ImGui::SameLine(); - toggle_button(Preview::OptionType::ColorChanges, _u8L("Color changes"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { - image_icon(window, pos, size, ImGui::LegendColorChanges); + toggle_button(Preview::OptionType::ColorChanges, _u8L("Color changes"), [&imgui](ImGuiWindow& window, const ImVec2& pos, float size) { + imgui.draw_icon(window, pos, size, ImGui::LegendColorChanges); }); ImGui::SameLine(); - toggle_button(Preview::OptionType::PausePrints, _u8L("Print pauses"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { - image_icon(window, pos, size, ImGui::LegendPausePrints); + toggle_button(Preview::OptionType::PausePrints, _u8L("Print pauses"), [&imgui](ImGuiWindow& window, const ImVec2& pos, float size) { + imgui.draw_icon(window, pos, size, ImGui::LegendPausePrints); }); ImGui::SameLine(); - toggle_button(Preview::OptionType::CustomGCodes, _u8L("Custom G-codes"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { - image_icon(window, pos, size, ImGui::LegendCustomGCodes); + toggle_button(Preview::OptionType::CustomGCodes, _u8L("Custom G-codes"), [&imgui](ImGuiWindow& window, const ImVec2& pos, float size) { + imgui.draw_icon(window, pos, size, ImGui::LegendCustomGCodes); }); ImGui::SameLine(); - toggle_button(Preview::OptionType::CenterOfGravity, _u8L("Center of gravity"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { - image_icon(window, pos, size, ImGui::LegendCOG); + toggle_button(Preview::OptionType::CenterOfGravity, _u8L("Center of gravity"), [&imgui](ImGuiWindow& window, const ImVec2& pos, float size) { + imgui.draw_icon(window, pos, size, ImGui::LegendCOG); }); ImGui::SameLine(); if (!wxGetApp().is_gcode_viewer()) { - toggle_button(Preview::OptionType::Shells, _u8L("Shells"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { - image_icon(window, pos, size, ImGui::LegendShells); + toggle_button(Preview::OptionType::Shells, _u8L("Shells"), [&imgui](ImGuiWindow& window, const ImVec2& pos, float size) { + imgui.draw_icon(window, pos, size, ImGui::LegendShells); }); ImGui::SameLine(); } - toggle_button(Preview::OptionType::ToolMarker, _u8L("Tool marker"), [image_icon](ImGuiWindow& window, const ImVec2& pos, float size) { - image_icon(window, pos, size, ImGui::LegendToolMarker); + toggle_button(Preview::OptionType::ToolMarker, _u8L("Tool marker"), [&imgui](ImGuiWindow& window, const ImVec2& pos, float size) { + imgui.draw_icon(window, pos, size, ImGui::LegendToolMarker); }); bool size_dirty = !ImGui::GetCurrentWindow()->ScrollbarY && ImGui::CalcWindowNextAutoFitSize(ImGui::GetCurrentWindow()).x != ImGui::GetWindowWidth(); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 4decbdab9b..fe0a7b724e 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1146,15 +1146,41 @@ void GLCanvas3D::SLAView::render_switch_button() ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); ImGui::SetNextWindowPos(ImVec2((float)ss_box.max.x(), (float)ss_box.center().y()), ImGuiCond_Always, ImVec2(0.0, 0.5)); imgui.begin(std::string("SLAViewSwitch"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration); - const wxString btn_text = (m_type == ESLAViewType::Original) ? _L("Processed") : _L("Original"); - if (imgui.button(btn_text)) { + const float icon_size = 1.5 * ImGui::GetTextLineHeight(); + if (imgui.draw_radio_button(_u8L("SLA view"), 1.5f * icon_size, true, [this, &imgui](ImGuiWindow& window, const ImVec2& pos, float size) { + wchar_t icon_id; + switch (m_type) + { + case ESLAViewType::Original: { icon_id = ImGui::SlaViewProcessed; break; } + case ESLAViewType::Processed: { icon_id = ImGui::SlaViewOriginal; break; } + default: { assert(false); break; } + } + imgui.draw_icon(window, pos, size, icon_id); + + })) { switch (m_type) { case ESLAViewType::Original: { m_parent.set_sla_view_type(ESLAViewType::Processed); break; } case ESLAViewType::Processed: { m_parent.set_sla_view_type(ESLAViewType::Original); break; } - default: { assert(false); break; } + default: { assert(false); break; } } } + + if (ImGui::IsItemHovered()) { + ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND); + ImGui::BeginTooltip(); + wxString tooltip; + switch (m_type) + { + case ESLAViewType::Original: { tooltip = _L("Show as processed"); break; } + case ESLAViewType::Processed: { tooltip = _L("Show as original"); break; } + default: { assert(false); break; } + } + + imgui.text(tooltip); + ImGui::EndTooltip(); + ImGui::PopStyleColor(); + } imgui.end(); ImGui::PopStyleColor(2); } diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 55bf576028..3d2b926105 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -104,6 +104,8 @@ static const std::map font_icons_large = { {ImGui::PauseHoverButton , "notification_pause_hover" }, {ImGui::OpenButton , "notification_open" }, {ImGui::OpenHoverButton , "notification_open_hover" }, + {ImGui::SlaViewOriginal , "sla_view_original" }, + {ImGui::SlaViewProcessed , "sla_view_processed" }, }; static const std::map font_icons_extra_large = { @@ -490,6 +492,18 @@ bool ImGuiWrapper::radio_button(const wxString &label, bool active) return ImGui::RadioButton(label_utf8.c_str(), active); } +void ImGuiWrapper::draw_icon(ImGuiWindow& window, const ImVec2& pos, float size, wchar_t icon_id) +{ + ImGuiIO& io = ImGui::GetIO(); + const ImTextureID tex_id = io.Fonts->TexID; + const float tex_w = static_cast(io.Fonts->TexWidth); + const float tex_h = static_cast(io.Fonts->TexHeight); + const ImFontAtlas::CustomRect* const rect = GetTextureCustomRect(icon_id); + const ImVec2 uv0 = { static_cast(rect->X) / tex_w, static_cast(rect->Y) / tex_h }; + const ImVec2 uv1 = { static_cast(rect->X + rect->Width) / tex_w, static_cast(rect->Y + rect->Height) / tex_h }; + window.DrawList->AddImage(tex_id, pos, { pos.x + size, pos.y + size }, uv0, uv1, ImGuiWrapper::to_ImU32({ 1.0f, 1.0f, 1.0f, 1.0f })); +} + bool ImGuiWrapper::draw_radio_button(const std::string& name, float size, bool active, std::function draw_callback) { diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 077bf568de..d95b934f18 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -92,9 +92,10 @@ public: void end(); bool button(const wxString &label, const wxString& tooltip = {}); - bool button(const wxString& label, float width, float height); + bool button(const wxString& label, float width, float height); bool button(const wxString& label, const ImVec2 &size, bool enable); // default size = ImVec2(0.f, 0.f) bool radio_button(const wxString &label, bool active); + void draw_icon(ImGuiWindow& window, const ImVec2& pos, float size, wchar_t icon_id); bool draw_radio_button(const std::string& name, float size, bool active, std::function draw_callback); bool checkbox(const wxString &label, bool &value); static void text(const char *label); From 1835e52842ad850326ccb509a44e0bd87ae204d7 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 4 Apr 2023 09:58:14 +0200 Subject: [PATCH 11/90] Fixed visibility of volumes when switching to FFF printer after slicing an object with SLA printer --- 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 8da6d0d26f..75963a250a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -6922,6 +6922,7 @@ void Plater::on_config_change(const DynamicPrintConfig &config) p->sidebar->show_sliced_info_sizer(false); p->reset_gcode_toolpaths(); p->view3D->get_canvas3d()->reset_sequential_print_clearance(); + p->view3D->get_canvas3d()->set_sla_view_type(GLCanvas3D::ESLAViewType::Original); } else if (opt_key == "bed_shape" || opt_key == "bed_custom_texture" || opt_key == "bed_custom_model") { bed_shape_changed = true; From 3b93b316c629523ae655234c3b51772a489fc39b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 12 Apr 2023 10:07:50 +0200 Subject: [PATCH 12/90] 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 | 86 ++++++++++++++++++++++++++++++++--- src/slic3r/GUI/GLCanvas3D.hpp | 3 ++ 4 files changed, 83 insertions(+), 67 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 97975404ff..54b3bc36e9 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -815,64 +815,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 2f7e0a7672..805972ce79 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 4db4c1c2a6..854c712e8e 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1265,11 +1265,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) @@ -2235,13 +2309,13 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re m_gizmos.refresh_on_off_state(); // Update the toolbar - if (update_object_list) - post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); + if (update_object_list) + post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); // 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); @@ -5694,7 +5768,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 e94b6742f5..04ab92dfbe 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -705,6 +705,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 d9dde32b78f4f2b518ae0de49e7ed6e1fe136aaf Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 3 May 2023 13:45:15 +0200 Subject: [PATCH 13/90] SPE-1606 - Fixed a bunch of bugs/crashes related to visualizing original or processed volumes in 3D scene after slicing using SLA printers when multiple objects are on the printbed --- src/libslic3r/Technologies.hpp | 2 + src/slic3r/GUI/GLCanvas3D.cpp | 362 +++++++++++++++------- src/slic3r/GUI/GLCanvas3D.hpp | 46 ++- src/slic3r/GUI/GUI_ObjectList.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 3 +- src/slic3r/GUI/Plater.cpp | 14 +- 6 files changed, 289 insertions(+), 140 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 60d89c9e9c..adfcda6649 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -36,6 +36,8 @@ #define ENABLE_MATRICES_DEBUG 0 // Shows an imgui dialog containing data from class ObjectManipulation #define ENABLE_OBJECT_MANIPULATION_DEBUG 0 +// Shows an imgui dialog containing data for class GLCanvas3D::SLAView +#define ENABLE_SLA_VIEW_DEBUG_WINDOW 1 // Enable rendering of objects using environment map diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 98bf2e03c6..d6765a046f 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1070,132 +1070,246 @@ void GLCanvas3D::load_arrange_settings() m_arrange_settings_fff_seq_print.alignment = arr_alignment ; } -static int processed_object_idx(const Model& model, const SLAPrint& sla_print, const GLVolumePtrs& volumes) +static std::vector processed_objects_idxs(const Model& model, const SLAPrint& sla_print, const GLVolumePtrs& volumes) { - for (const GLVolume* v : volumes) { - if (v->volume_idx() == -(int)slaposDrillHoles) { - const int mo_idx = v->object_idx(); - const ModelObject* model_object = (mo_idx < (int)model.objects.size()) ? model.objects[mo_idx] : nullptr; - if (model_object != nullptr && model_object->instances[v->instance_idx()]->is_printable()) { - const SLAPrintObject* print_object = sla_print.get_print_object_by_model_object_id(model_object->id()); - if (print_object != nullptr && print_object->get_parts_to_slice().size() > 1) - return mo_idx; - } + std::vector ret; + GLVolumePtrs matching_volumes; + std::copy_if(volumes.begin(), volumes.end(), std::back_inserter(matching_volumes), [](GLVolume* v) { + return v->volume_idx() == -(int)slaposDrillHoles; }); + for (const GLVolume* v : matching_volumes) { + const int mo_idx = v->object_idx(); + const ModelObject* model_object = (mo_idx < (int)model.objects.size()) ? model.objects[mo_idx] : nullptr; + if (model_object != nullptr && model_object->instances[v->instance_idx()]->is_printable()) { + const SLAPrintObject* print_object = sla_print.get_print_object_by_model_object_id(model_object->id()); + if (print_object != nullptr && print_object->get_parts_to_slice().size() > 1) + ret.push_back(mo_idx); } } - - return -1; + std::sort(ret.begin(), ret.end()); + ret.erase(std::unique(ret.begin(), ret.end()), ret.end()); + return ret; }; -GLCanvas3D::ESLAViewType GLCanvas3D::SLAView::detect_type(const GLVolumePtrs& volumes) +static bool composite_id_match(const GLVolume::CompositeID& id1, const GLVolume::CompositeID& id2) { - m_type = ESLAViewType::Original; - if (m_allow_type_detection) { - for (const GLVolume* v : volumes) { - if (v->volume_idx() == -(int)slaposDrillHoles) { - m_type = ESLAViewType::Processed; - break; - } + return id1.object_id == id2.object_id && id1.instance_id == id2.instance_id; +} + +void GLCanvas3D::SLAView::detect_type_from_volumes(const GLVolumePtrs& volumes) +{ + for (auto& [id, type] : m_instances_cache) { + type = ESLAViewType::Original; + } + + for (const GLVolume* v : volumes) { + if (v->volume_idx() == -(int)slaposDrillHoles) { + const InstancesCacheItem* instance = find_instance_item(v->composite_id); + assert(instance != nullptr); + set_type(instance->first, ESLAViewType::Processed); } } - - m_parent.set_sla_view_type(m_type); - m_allow_type_detection = false; - return m_type; } -bool GLCanvas3D::SLAView::set_type(ESLAViewType type) { - if (m_type != type) { - m_type = type; - return true; +void GLCanvas3D::SLAView::set_type(ESLAViewType new_type) +{ + for (auto& [id, type] : m_instances_cache) { + type = new_type; + if (new_type == ESLAViewType::Processed) + select_full_instance(id); } - return false; } -void GLCanvas3D::SLAView::update_volumes(GLVolumePtrs& volumes) +void GLCanvas3D::SLAView::set_type(const GLVolume::CompositeID& id, ESLAViewType new_type) +{ + InstancesCacheItem* instance = find_instance_item(id); + assert(instance != nullptr); + instance->second = new_type; + if (new_type == ESLAViewType::Processed) + select_full_instance(id); +} + +void GLCanvas3D::SLAView::update_volumes_visibility(GLVolumePtrs& volumes) { const SLAPrint* sla_print = m_parent.sla_print(); - const int mo_idx = (sla_print != nullptr) ? processed_object_idx(*m_parent.get_model(), *sla_print, volumes) : -1; - const bool show_processed = m_type == ESLAViewType::Processed && mo_idx != -1; - - auto show = [show_processed](const GLVolume& v) { - return show_processed ? v.volume_idx() < 0 : v.volume_idx() != -(int)slaposDrillHoles; - }; + const std::vector mo_idxs = (sla_print != nullptr) ? processed_objects_idxs(*m_parent.get_model(), *sla_print, volumes) : std::vector(); std::vector>* raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); for (GLVolume* v : volumes) { - v->is_active = v->object_idx() != mo_idx || show(*v); + const int obj_idx = v->object_idx(); + bool active = std::find(mo_idxs.begin(), mo_idxs.end(), obj_idx) == mo_idxs.end(); + if (!active) { + const InstancesCacheItem* instance = find_instance_item(v->composite_id); + assert(instance != nullptr); + active = (instance->second == ESLAViewType::Processed) ? v->volume_idx() < 0 : v->volume_idx() != -(int)slaposDrillHoles; + } + v->is_active = active; auto it = std::find_if(raycasters->begin(), raycasters->end(), [v](std::shared_ptr item) { return item->get_raycaster() == v->mesh_raycaster.get(); }); if (it != raycasters->end()) (*it)->set_active(v->is_active); } } +void GLCanvas3D::SLAView::update_instances_cache(const std::vector>& new_to_old_ids_map) +{ + // First, extract current instances list from the volumes + const GLVolumePtrs& volumes = m_parent.get_volumes().volumes; + std::vector new_instances_cache; + for (const GLVolume* v : volumes) { + new_instances_cache.emplace_back(v->composite_id, ESLAViewType::Original); + } + + std::sort(new_instances_cache.begin(), new_instances_cache.end(), + [](const InstancesCacheItem& i1, const InstancesCacheItem& i2) { + return i1.first.object_id < i2.first.object_id || (i1.first.object_id == i2.first.object_id && i1.first.instance_id < i2.first.instance_id); }); + + new_instances_cache.erase(std::unique(new_instances_cache.begin(), new_instances_cache.end(), + [](const InstancesCacheItem& i1, const InstancesCacheItem& i2) { + return composite_id_match(i1.first, i2.first); }), new_instances_cache.end()); + + // Second, update instances type from previous state + for (auto& inst_type : new_instances_cache) { + const auto map_to_old_it = std::find_if(new_to_old_ids_map.begin(), new_to_old_ids_map.end(), [&inst_type](const std::pair& item) { + return composite_id_match(inst_type.first, item.first); }); + + const GLVolume::CompositeID old_inst_id = (map_to_old_it != new_to_old_ids_map.end()) ? map_to_old_it->second : inst_type.first; + const InstancesCacheItem* old_instance = find_instance_item(old_inst_id); + if (old_instance != nullptr) + inst_type.second = old_instance->second; + } + + m_instances_cache = new_instances_cache; +} + void GLCanvas3D::SLAView::render_switch_button() { const SLAPrint* sla_print = m_parent.sla_print(); - const int mo_idx = (sla_print != nullptr) ? processed_object_idx(*m_parent.get_model(), *sla_print, m_parent.get_volumes().volumes) : -1; - if (mo_idx == -1) + if (sla_print == nullptr) return; - const BoundingBoxf ss_box = m_parent.get_selection().get_screen_space_bounding_box(); - if (ss_box.defined) { - ImGuiWrapper& imgui = *wxGetApp().imgui(); - ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); - ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); - ImGui::SetNextWindowPos(ImVec2((float)ss_box.max.x(), (float)ss_box.center().y()), ImGuiCond_Always, ImVec2(0.0, 0.5)); - imgui.begin(std::string("SLAViewSwitch"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration); - const float icon_size = 1.5 * ImGui::GetTextLineHeight(); - if (imgui.draw_radio_button(_u8L("SLA view"), 1.5f * icon_size, true, [this, &imgui](ImGuiWindow& window, const ImVec2& pos, float size) { + const std::vector mo_idxs = processed_objects_idxs(*m_parent.get_model(), *sla_print, m_parent.get_volumes().volumes); + if (mo_idxs.empty()) + return; + + Selection& selection = m_parent.get_selection(); + const int obj_idx = selection.get_object_idx(); + if (std::find(mo_idxs.begin(), mo_idxs.end(), obj_idx) == mo_idxs.end()) + return; + + const int inst_idx = selection.get_instance_idx(); + if (inst_idx < 0) + return; + + const GLVolume::CompositeID composite_id(obj_idx, 0, inst_idx); + const InstancesCacheItem* sel_instance = find_instance_item(composite_id); + if (sel_instance == nullptr) + return; + + const ESLAViewType type = sel_instance->second; + + BoundingBoxf ss_box; + if (m_use_instance_bbox) { + const Selection::EMode mode = selection.get_mode(); + if (obj_idx >= 0 && inst_idx >= 0) { + const Selection::IndicesList selected_idxs = selection.get_volume_idxs(); + std::vector idxs_as_vector; + idxs_as_vector.assign(selected_idxs.begin(), selected_idxs.end()); + selection.add_instance(obj_idx, inst_idx, true); + ss_box = selection.get_screen_space_bounding_box(); + selection.add_volumes(mode, idxs_as_vector, true); + } + } + + if (!ss_box.defined) + ss_box = selection.get_screen_space_bounding_box(); + assert(ss_box.defined); + + ImGuiWrapper& imgui = *wxGetApp().imgui(); + ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); + ImGui::SetNextWindowPos(ImVec2((float)ss_box.max.x(), (float)ss_box.center().y()), ImGuiCond_Always, ImVec2(0.0, 0.5)); + imgui.begin(std::string("SLAViewSwitch"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration); + const float icon_size = 1.5 * ImGui::GetTextLineHeight(); + if (imgui.draw_radio_button(_u8L("SLA view"), 1.5f * icon_size, true, + [this, &imgui, sel_instance](ImGuiWindow& window, const ImVec2& pos, float size) { wchar_t icon_id; - switch (m_type) + switch (sel_instance->second) { case ESLAViewType::Original: { icon_id = ImGui::SlaViewProcessed; break; } case ESLAViewType::Processed: { icon_id = ImGui::SlaViewOriginal; break; } default: { assert(false); break; } } imgui.draw_icon(window, pos, size, icon_id); - - })) { - switch (m_type) - { - case ESLAViewType::Original: { m_parent.set_sla_view_type(ESLAViewType::Processed); break; } - case ESLAViewType::Processed: { m_parent.set_sla_view_type(ESLAViewType::Original); break; } - default: { assert(false); break; } - } + })) { + switch (sel_instance->second) + { + case ESLAViewType::Original: { m_parent.set_sla_view_type(sel_instance->first, ESLAViewType::Processed); break; } + case ESLAViewType::Processed: { m_parent.set_sla_view_type(sel_instance->first, ESLAViewType::Original); break; } + default: { assert(false); break; } } - - if (ImGui::IsItemHovered()) { - ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND); - ImGui::BeginTooltip(); - wxString tooltip; - switch (m_type) - { - case ESLAViewType::Original: { tooltip = _L("Show as processed"); break; } - case ESLAViewType::Processed: { tooltip = _L("Show as original"); break; } - default: { assert(false); break; } - } - - imgui.text(tooltip); - ImGui::EndTooltip(); - ImGui::PopStyleColor(); - } - imgui.end(); - ImGui::PopStyleColor(2); } + + if (ImGui::IsItemHovered()) { + ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND); + ImGui::BeginTooltip(); + wxString tooltip; + switch (type) + { + case ESLAViewType::Original: { tooltip = _L("Show as processed"); break; } + case ESLAViewType::Processed: { tooltip = _L("Show as original"); break; } + default: { assert(false); break; } + } + + imgui.text(tooltip); + ImGui::EndTooltip(); + ImGui::PopStyleColor(); + } + imgui.end(); + ImGui::PopStyleColor(2); } -//void GLCanvas3D::SLAView::render_debug_window() -//{ -// ImGuiWrapper& imgui = *wxGetApp().imgui(); -// imgui.begin(std::string("SLAView"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize); -// imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Type:"); -// ImGui::SameLine(); -// const std::string text = (m_type == ESLAViewType::Original) ? "Original" : "Processed"; -// imgui.text_colored(ImGui::GetStyleColorVec4(ImGuiCol_Text), text); -// imgui.end(); -//} +#if ENABLE_SLA_VIEW_DEBUG_WINDOW +void GLCanvas3D::SLAView::render_debug_window() +{ + ImGuiWrapper& imgui = *wxGetApp().imgui(); + imgui.begin(std::string("SLAView"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize); + for (const auto& [id, type] : m_instances_cache) { + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "(" + std::to_string(id.object_id) + ", " + std::to_string(id.instance_id) + ")"); + ImGui::SameLine(); + imgui.text_colored(ImGui::GetStyleColorVec4(ImGuiCol_Text), (type == ESLAViewType::Original) ? "Original" : "Processed"); + } + if (!m_instances_cache.empty()) + ImGui::Separator(); + + imgui.checkbox("Use instance bounding box", m_use_instance_bbox); + imgui.end(); +} +#endif // ENABLE_SLA_VIEW_DEBUG_WINDOW + +GLCanvas3D::SLAView::InstancesCacheItem* GLCanvas3D::SLAView::find_instance_item(const GLVolume::CompositeID& id) +{ + auto it = std::find_if(m_instances_cache.begin(), m_instances_cache.end(), + [&id](const InstancesCacheItem& item) { return composite_id_match(item.first, id); }); + return (it == m_instances_cache.end()) ? nullptr : &(*it); +} + +void GLCanvas3D::SLAView::select_full_instance(const GLVolume::CompositeID& id) +{ + bool extended_selection = false; + Selection& selection = m_parent.get_selection(); + const Selection::ObjectIdxsToInstanceIdxsMap& sel_cache = selection.get_content(); + auto obj_it = sel_cache.find(id.object_id); + if (obj_it != sel_cache.end()) { + auto inst_it = std::find(obj_it->second.begin(), obj_it->second.end(), id.instance_id); + if (inst_it != obj_it->second.end()) { + selection.add_instance(id.object_id, id.instance_id); + extended_selection = true; + } + } + + if (extended_selection) + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); +} PrinterTechnology GLCanvas3D::current_printer_technology() const { @@ -1775,7 +1889,9 @@ void GLCanvas3D::render() const GLGizmosManager::EType type = m_gizmos.get_current_type(); if (type == GLGizmosManager::EType::Undefined) m_sla_view.render_switch_button(); -// m_sla_view.render_debug_window(); +#if ENABLE_SLA_VIEW_DEBUG_WINDOW + m_sla_view.render_debug_window(); +#endif // ENABLE_SLA_VIEW_DEBUG_WINDOW } std::string tooltip; @@ -2017,6 +2133,8 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re size_t volume_idx; }; + std::vector> new_to_old_ids_map; + // SLA steps to pull the preview meshes for. typedef std::array SLASteps; SLASteps sla_steps = { slaposDrillHoles, slaposSupportTree, slaposPad }; @@ -2164,12 +2282,12 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re for (unsigned int obj_idx = 0; obj_idx < (unsigned int)m_model->objects.size(); ++ obj_idx) { const ModelObject &model_object = *m_model->objects[obj_idx]; for (int volume_idx = 0; volume_idx < (int)model_object.volumes.size(); ++ volume_idx) { - const ModelVolume &model_volume = *model_object.volumes[volume_idx]; + const ModelVolume &model_volume = *model_object.volumes[volume_idx]; for (int instance_idx = 0; instance_idx < (int)model_object.instances.size(); ++ instance_idx) { - const ModelInstance &model_instance = *model_object.instances[instance_idx]; - ModelVolumeState key(model_volume.id(), model_instance.id()); - auto it = std::lower_bound(model_volume_state.begin(), model_volume_state.end(), key, model_volume_state_lower); - assert(it != model_volume_state.end() && it->geometry_id == key.geometry_id); + const ModelInstance &model_instance = *model_object.instances[instance_idx]; + ModelVolumeState key(model_volume.id(), model_instance.id()); + auto it = std::lower_bound(model_volume_state.begin(), model_volume_state.end(), key, model_volume_state_lower); + assert(it != model_volume_state.end() && it->geometry_id == key.geometry_id); if (it->new_geometry()) { // New volume. auto it_old_volume = std::lower_bound(deleted_volumes.begin(), deleted_volumes.end(), GLVolumeState(it->composite_id), deleted_volumes_lower); @@ -2182,15 +2300,17 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx); m_volumes.volumes.back()->geometry_id = key.geometry_id; update_object_list = true; - } else { - // Recycling an old GLVolume. - GLVolume &existing_volume = *m_volumes.volumes[it->volume_idx]; + } + else { + // Recycling an old GLVolume. + GLVolume &existing_volume = *m_volumes.volumes[it->volume_idx]; assert(existing_volume.geometry_id == key.geometry_id); - // Update the Object/Volume/Instance indices into the current Model. - if (existing_volume.composite_id != it->composite_id) { - existing_volume.composite_id = it->composite_id; - update_object_list = true; - } + // Update the Object/Volume/Instance indices into the current Model. + if (existing_volume.composite_id != it->composite_id) { + new_to_old_ids_map.push_back(std::make_pair(it->composite_id, existing_volume.composite_id)); + existing_volume.composite_id = it->composite_id; + update_object_list = true; + } } } } @@ -2241,7 +2361,9 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re } else { // Recycling an old GLVolume. Update the Object/Instance indices into the current Model. - m_volumes.volumes[it->volume_idx]->composite_id = GLVolume::CompositeID(object_idx, m_volumes.volumes[it->volume_idx]->volume_idx(), instance_idx); + const GLVolume::CompositeID new_id(object_idx, m_volumes.volumes[it->volume_idx]->volume_idx(), instance_idx); + new_to_old_ids_map.push_back(std::make_pair(new_id, m_volumes.volumes[it->volume_idx]->composite_id)); + m_volumes.volumes[it->volume_idx]->composite_id = new_id; m_volumes.volumes[it->volume_idx]->set_instance_transformation(model_object->instances[instance_idx]->get_transformation()); } } @@ -2252,9 +2374,6 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re if (!instances[istep].empty()) m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp); } - - if (m_sla_view.detect_type(m_volumes.volumes) == ESLAViewType::Processed) - update_object_list = true; } // Shift-up all volumes of the object so that it has the right elevation with respect to the print bed @@ -2312,15 +2431,30 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re else m_selection.volumes_changed(map_glvolume_old_to_new); - if (printer_technology == ptSLA) - m_sla_view.update_volumes(m_volumes.volumes); + if (printer_technology == ptSLA) { + std::sort(new_to_old_ids_map.begin(), new_to_old_ids_map.end(), + [](const std::pair& i1, const std::pair& i2) { + return i1.first.object_id < i2.first.object_id || (i1.first.object_id == i2.first.object_id && i1.first.instance_id < i2.first.instance_id); }); + + new_to_old_ids_map.erase(std::unique(new_to_old_ids_map.begin(), new_to_old_ids_map.end(), + [](const std::pair& i1, const std::pair& i2) { + return composite_id_match(i1.first, i2.first); }), new_to_old_ids_map.end()); + + m_sla_view.update_instances_cache(new_to_old_ids_map); + if (m_sla_view_type_detection_active) { + m_sla_view.detect_type_from_volumes(m_volumes.volumes); + m_sla_view_type_detection_active = false; + } + m_sla_view.update_volumes_visibility(m_volumes.volumes); + update_object_list = true; + } m_gizmos.update_data(); m_gizmos.refresh_on_off_state(); // Update the toolbar - if (update_object_list) - post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); + if (update_object_list) + post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); // checks for geometry outside the print volume to render it accordingly if (!m_volumes.empty()) { @@ -4324,6 +4458,20 @@ std::pair> GLCanvas3D::get_layers_h return ret; } +void GLCanvas3D::set_sla_view_type(ESLAViewType type) +{ + m_sla_view.set_type(type); + m_sla_view.update_volumes_visibility(m_volumes.volumes); + m_dirty = true; +} + +void GLCanvas3D::set_sla_view_type(const GLVolume::CompositeID& id, ESLAViewType type) +{ + m_sla_view.set_type(id, type); + m_sla_view.update_volumes_visibility(m_volumes.volumes); + m_dirty = true; +} + bool GLCanvas3D::_is_shown_on_screen() const { return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 201a86fa09..a0e4df88fd 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -555,27 +555,33 @@ private: { public: explicit SLAView(GLCanvas3D& parent) : m_parent(parent) {} - void allow_type_detection(bool allow) { m_allow_type_detection = allow; } - ESLAViewType detect_type(const GLVolumePtrs& volumes); - ESLAViewType get_type() const { return m_type; } - bool set_type(ESLAViewType type); - void update_volumes(GLVolumePtrs& volumes); + void detect_type_from_volumes(const GLVolumePtrs& volumes); + void set_type(ESLAViewType type); + void set_type(const GLVolume::CompositeID& id, ESLAViewType type); + void update_volumes_visibility(GLVolumePtrs& volumes); + void update_instances_cache(const std::vector>& new_to_old_ids_map); void render_switch_button(); -// void render_debug_window(); + +#if ENABLE_SLA_VIEW_DEBUG_WINDOW + void render_debug_window(); +#endif // ENABLE_SLA_VIEW_DEBUG_WINDOW private: GLCanvas3D& m_parent; - ESLAViewType m_type{ ESLAViewType::Original }; - bool m_allow_type_detection{ false }; + typedef std::pair InstancesCacheItem; + std::vector m_instances_cache; + bool m_use_instance_bbox{ false }; + + InstancesCacheItem* find_instance_item(const GLVolume::CompositeID& id); + void select_full_instance(const GLVolume::CompositeID& id); }; SLAView m_sla_view; + bool m_sla_view_type_detection_active{ false }; ArrangeSettings m_arrange_settings_fff, m_arrange_settings_sla, m_arrange_settings_fff_seq_print; - PrinterTechnology current_printer_technology() const; - bool is_arrange_alignment_enabled() const; template @@ -793,6 +799,8 @@ public: void zoom_to_gcode(); void select_view(const std::string& direction); + PrinterTechnology current_printer_technology() const; + void update_volumes_colors_by_extruder(); bool is_dragging() const { return m_gizmos.is_dragging() || (m_moving && !m_mouse.scene_position.isApprox(m_mouse.drag.start_position_3D)); } @@ -980,21 +988,9 @@ public: std::pair> get_layers_height_data(int object_id); - void set_sla_view_type(ESLAViewType type) { - if (type == ESLAViewType::Processed) { - assert(!m_selection.is_empty()); - const GLVolume* v = m_selection.get_first_volume(); - m_selection.add_instance(v->object_idx(), v->instance_idx()); - post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); - } - - m_dirty = type != m_sla_view.get_type(); - - if (m_sla_view.set_type(type)) - m_sla_view.update_volumes(m_volumes.volumes); - } - - void allow_sla_view_type_detection(bool allow) { m_sla_view.allow_type_detection(allow); } + void set_sla_view_type(ESLAViewType type); + void set_sla_view_type(const GLVolume::CompositeID& id, ESLAViewType type); + void enable_sla_view_type_detection() { m_sla_view_type_detection_active = true; } private: bool _is_shown_on_screen() const; diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index c2e573f977..bb8eafd1c9 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -749,7 +749,7 @@ void ObjectList::selection_changed() } else if (type & itVolume) { if (printer_technology() == ptSLA) - wxGetApp().plater()->canvas3D()->set_sla_view_type(GLCanvas3D::ESLAViewType::Original); + wxGetApp().plater()->canvas3D()->set_sla_view_type(scene_selection().get_first_volume()->composite_id, GLCanvas3D::ESLAViewType::Original); } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 09211b8056..657235511c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -978,7 +978,8 @@ bool GLGizmosManager::activate_gizmo(EType type) return false; // gizmo refused to be turned on. } - m_parent.set_sla_view_type(GLCanvas3D::ESLAViewType::Original); + if (m_parent.current_printer_technology() == ptSLA) + m_parent.set_sla_view_type(GLCanvas3D::ESLAViewType::Original); new_gizmo.register_raycasters_for_picking(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 133167fe48..5bc90293d3 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3454,10 +3454,8 @@ unsigned int Plater::priv::update_restart_background_process(bool force_update_s { // bitmask of UpdateBackgroundProcessReturnState unsigned int state = this->update_background_process(false); - if (force_update_scene || (state & UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) != 0) { - view3D->get_canvas3d()->allow_sla_view_type_detection(true); + if (force_update_scene || (state & UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) != 0) view3D->reload_scene(false); - } if (force_update_preview) this->preview->reload_print(); @@ -4082,8 +4080,10 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) // If RELOAD_SLA_SUPPORT_POINTS, then the SLA gizmo is updated (reload_scene calls update_gizmos_data) if (view3D->is_dragging()) delayed_scene_refresh = true; - else + else { + view3D->get_canvas3d()->enable_sla_view_type_detection(); this->update_sla_scene(); + } break; default: break; } @@ -6946,11 +6946,13 @@ void Plater::on_config_change(const DynamicPrintConfig &config) p->config->set_key_value(opt_key, config.option(opt_key)->clone()); if (opt_key == "printer_technology") { - this->set_printer_technology(config.opt_enum(opt_key)); + const PrinterTechnology printer_technology = config.opt_enum(opt_key); + this->set_printer_technology(printer_technology); p->sidebar->show_sliced_info_sizer(false); p->reset_gcode_toolpaths(); p->view3D->get_canvas3d()->reset_sequential_print_clearance(); - p->view3D->get_canvas3d()->set_sla_view_type(GLCanvas3D::ESLAViewType::Original); + if (printer_technology == ptSLA) + p->view3D->get_canvas3d()->set_sla_view_type(GLCanvas3D::ESLAViewType::Original); } else if (opt_key == "bed_shape" || opt_key == "bed_custom_texture" || opt_key == "bed_custom_model") { bed_shape_changed = true; From 3848a8eda23d532cdbe4b226e6f4a0aecc528c35 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 10 May 2023 09:02:40 +0200 Subject: [PATCH 14/90] Fixed crash after 'vb_organic_interfaces' with zero top interfaces. --- src/libslic3r/Support/TreeSupport.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 0eb009c777..d53b3e7859 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -4558,7 +4558,7 @@ static void draw_branches( } // Subtract top contact layer polygons from support base. - SupportGeneratorLayer *top_contact_layer = top_contacts[layer_idx]; + SupportGeneratorLayer *top_contact_layer = top_contacts.empty() ? nullptr : top_contacts[layer_idx]; if (top_contact_layer && ! top_contact_layer->polygons.empty() && ! base_layer_polygons.empty()) { base_layer_polygons = diff(base_layer_polygons, top_contact_layer->polygons); if (! bottom_contact_polygons.empty()) From 35f4993ec2440e4c47ba20b985ee3e80ddccdc0d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 10 May 2023 10:13:20 +0200 Subject: [PATCH 15/90] SPE-1606 - Fixed geometry not rendered in the 3D scene after switching from SLA to FFF printer --- src/slic3r/GUI/Plater.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 5bc90293d3..b657616f0b 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -6951,8 +6951,7 @@ void Plater::on_config_change(const DynamicPrintConfig &config) p->sidebar->show_sliced_info_sizer(false); p->reset_gcode_toolpaths(); p->view3D->get_canvas3d()->reset_sequential_print_clearance(); - if (printer_technology == ptSLA) - p->view3D->get_canvas3d()->set_sla_view_type(GLCanvas3D::ESLAViewType::Original); + p->view3D->get_canvas3d()->set_sla_view_type(GLCanvas3D::ESLAViewType::Original); } else if (opt_key == "bed_shape" || opt_key == "bed_custom_texture" || opt_key == "bed_custom_model") { bed_shape_changed = true; From a4605a5e9ed378c0687789abfd636bb2826d9fce Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 10 May 2023 10:20:31 +0200 Subject: [PATCH 16/90] SPE-1606 - Do no show the switch view type icon in the 3D scene if the object does not contain negative volumes --- src/slic3r/GUI/GLCanvas3D.cpp | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 804f6058d2..3859372b67 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1095,6 +1095,19 @@ static bool composite_id_match(const GLVolume::CompositeID& id1, const GLVolume: return id1.object_id == id2.object_id && id1.instance_id == id2.instance_id; } +static bool object_contains_negative_volumes(const Model& model, int obj_id) { + bool ret = false; + if (0 <= obj_id && obj_id < model.objects.size()) { + for (const ModelVolume* v : model.objects[obj_id]->volumes) { + if (v->is_negative_volume()) { + ret = true; + break; + } + } + } + return ret; +} + void GLCanvas3D::SLAView::detect_type_from_volumes(const GLVolumePtrs& volumes) { for (auto& [id, type] : m_instances_cache) { @@ -1103,9 +1116,11 @@ void GLCanvas3D::SLAView::detect_type_from_volumes(const GLVolumePtrs& volumes) for (const GLVolume* v : volumes) { if (v->volume_idx() == -(int)slaposDrillHoles) { - const InstancesCacheItem* instance = find_instance_item(v->composite_id); - assert(instance != nullptr); - set_type(instance->first, ESLAViewType::Processed); + if (object_contains_negative_volumes(*m_parent.get_model(), v->composite_id.object_id)) { + const InstancesCacheItem* instance = find_instance_item(v->composite_id); + assert(instance != nullptr); + set_type(instance->first, ESLAViewType::Processed); + } } } } @@ -1196,6 +1211,9 @@ void GLCanvas3D::SLAView::render_switch_button() if (std::find(mo_idxs.begin(), mo_idxs.end(), obj_idx) == mo_idxs.end()) return; + if (!object_contains_negative_volumes(*m_parent.get_model(), obj_idx)) + return; + const int inst_idx = selection.get_instance_idx(); if (inst_idx < 0) return; From 5984111388f796858393cb05104f09ba015b39ae Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 10 May 2023 10:37:05 +0200 Subject: [PATCH 17/90] SPE-1606 - Placement of switch view type icon in the 3D scene by the current selected instance bounding box --- src/libslic3r/Technologies.hpp | 2 +- src/slic3r/GUI/GLCanvas3D.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index adfcda6649..602654633c 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -37,7 +37,7 @@ // Shows an imgui dialog containing data from class ObjectManipulation #define ENABLE_OBJECT_MANIPULATION_DEBUG 0 // Shows an imgui dialog containing data for class GLCanvas3D::SLAView -#define ENABLE_SLA_VIEW_DEBUG_WINDOW 1 +#define ENABLE_SLA_VIEW_DEBUG_WINDOW 0 // Enable rendering of objects using environment map diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index a0e4df88fd..38dcdb8b8c 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -570,7 +570,7 @@ private: GLCanvas3D& m_parent; typedef std::pair InstancesCacheItem; std::vector m_instances_cache; - bool m_use_instance_bbox{ false }; + bool m_use_instance_bbox{ true }; InstancesCacheItem* find_instance_item(const GLVolume::CompositeID& id); void select_full_instance(const GLVolume::CompositeID& id); From 662c3da65b2c44cc42768d5c036ba9fb0ee51a76 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 10 May 2023 11:01:50 +0200 Subject: [PATCH 18/90] Fixed warnings --- src/slic3r/GUI/GLCanvas3D.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 3859372b67..610f3bdec3 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1097,7 +1097,7 @@ static bool composite_id_match(const GLVolume::CompositeID& id1, const GLVolume: static bool object_contains_negative_volumes(const Model& model, int obj_id) { bool ret = false; - if (0 <= obj_id && obj_id < model.objects.size()) { + if (0 <= obj_id && obj_id < (int)model.objects.size()) { for (const ModelVolume* v : model.objects[obj_id]->volumes) { if (v->is_negative_volume()) { ret = true; @@ -1250,13 +1250,7 @@ void GLCanvas3D::SLAView::render_switch_button() const float icon_size = 1.5 * ImGui::GetTextLineHeight(); if (imgui.draw_radio_button(_u8L("SLA view"), 1.5f * icon_size, true, [this, &imgui, sel_instance](ImGuiWindow& window, const ImVec2& pos, float size) { - wchar_t icon_id; - switch (sel_instance->second) - { - case ESLAViewType::Original: { icon_id = ImGui::SlaViewProcessed; break; } - case ESLAViewType::Processed: { icon_id = ImGui::SlaViewOriginal; break; } - default: { assert(false); break; } - } + const wchar_t icon_id = (sel_instance->second == ESLAViewType::Original) ? ImGui::SlaViewProcessed : ImGui::SlaViewOriginal; imgui.draw_icon(window, pos, size, icon_id); })) { switch (sel_instance->second) From 2e3b5c2bbc0e31c1737a7824051002afa92c92d7 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 20 Apr 2023 17:27:09 +0200 Subject: [PATCH 19/90] Fix of backward compability issue Added recommended version checks to ConfigWizard and PresetUpdater --- src/slic3r/GUI/ConfigWizard.cpp | 41 +++++++++++++++- src/slic3r/Utils/PresetUpdater.cpp | 79 +++++++++++++++++++++--------- 2 files changed, 95 insertions(+), 25 deletions(-) diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 11b116a056..866d5a817f 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -57,6 +57,7 @@ #include "UnsavedChangesDialog.hpp" #include "slic3r/Utils/AppUpdater.hpp" #include "slic3r/GUI/I18N.hpp" +#include "slic3r/Config/Version.hpp" #if defined(__linux__) && defined(__WXGTK3__) #define wxLinux_gtk3 true @@ -118,7 +119,7 @@ BundleMap BundleMap::load() const auto vendor_dir = (boost::filesystem::path(Slic3r::data_dir()) / "vendor").make_preferred(); const auto archive_dir = (boost::filesystem::path(Slic3r::data_dir()) / "cache" / "vendor").make_preferred(); const auto rsrc_vendor_dir = (boost::filesystem::path(resources_dir()) / "profiles").make_preferred(); - + const auto cache_dir = boost::filesystem::path(Slic3r::data_dir()) / "cache"; // for Index // Load Prusa bundle from the datadir/vendor directory or from datadir/cache/vendor (archive) or from resources/profiles. auto prusa_bundle_path = (vendor_dir / PresetBundle::PRUSA_BUNDLE).replace_extension(".ini"); BundleLocation prusa_bundle_loc = BundleLocation::IN_VENDOR; @@ -138,7 +139,7 @@ BundleMap BundleMap::load() // Load the other bundles in the datadir/vendor directory // and then additionally from datadir/cache/vendor (archive) and resources/profiles. - // Should we concider case where archive has older profiles than resources (shouldnt happen)? + // Should we concider case where archive has older profiles than resources (shouldnt happen)? -> YES, it happens during re-configuration when running older PS after newer version typedef std::pair DirData; std::vector dir_list { {vendor_dir, BundleLocation::IN_VENDOR}, {archive_dir, BundleLocation::IN_ARCHIVE}, {rsrc_vendor_dir, BundleLocation::IN_RESOURCES} }; for ( auto dir : dir_list) { @@ -151,6 +152,42 @@ BundleMap BundleMap::load() // Don't load this bundle if we've already loaded it. if (res.find(id) != res.end()) { continue; } + // Fresh index should be in archive_dir, otherwise look for it in cache + fs::path idx_path (archive_dir / (id + ".idx")); + if (!boost::filesystem::exists(idx_path)) { + BOOST_LOG_TRIVIAL(warning) << format("Missing index %1% when loading bundle %2%.", idx_path.string(), id); + idx_path = fs::path(cache_dir / (id + ".idx")); + } + if (!boost::filesystem::exists(idx_path)) { + BOOST_LOG_TRIVIAL(error) << format("Could not load bundle %1% due to missing index %1%.", id, idx_path.string()); + continue; + } + Slic3r::GUI::Config::Index index; + try { + index.load(idx_path); + } + catch (const std::exception& /* err */) { + BOOST_LOG_TRIVIAL(error) << format("Could not load bundle %1% due to invalid index %1%.", id, idx_path.string()); + continue; + } + const auto recommended_it = index.recommended(); + if (recommended_it == index.end()) { + BOOST_LOG_TRIVIAL(error) << format("Could not load bundle %1% due to no recommended version in index %2%.", id, idx_path.string()); + continue; + } + const auto recommended = recommended_it->config_version; + VendorProfile vp; + try { + vp = VendorProfile::from_ini(dir_entry, true); + } + catch (const std::exception& e) { + BOOST_LOG_TRIVIAL(error) << format("Could not load bundle %1% due to corrupted profile file %2%. Message: %3%", id, dir_entry.path().string(), e.what()); + continue; + } + // Don't load + if (vp.config_version > recommended) + continue; + Bundle bundle; if (bundle.load(dir_entry.path(), dir.second)) res.emplace(std::move(id), std::move(bundle)); diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index 028c7ce0a5..e75b5420a9 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -1320,7 +1320,35 @@ bool PresetUpdater::install_bundles_rsrc_or_cache_vendor(std::vectorcache_path / idx_path.filename()); + } + if (!boost::filesystem::exists(idx_path)) { + std::string msg = GUI::format(_L("Couldn't locate index file for vendor %1% when performing updates. The profile will not be installed."), bundle); + BOOST_LOG_TRIVIAL(error) << msg; + GUI::show_error(nullptr, msg); + continue; + } + Slic3r::GUI::Config::Index index; + try { + index.load(idx_path); + } + catch (const std::exception& /* err */) { + std::string msg = GUI::format(_L("Couldn't load index file for vendor %1% when performing updates. The profile will not be installed. Reason: Corrupted index file %2%."), bundle, idx_path.string()); + BOOST_LOG_TRIVIAL(error) << msg; + GUI::show_error(nullptr, msg); + continue; + } + const auto recommended_it = index.recommended(); + const auto recommended = recommended_it->config_version; + if (is_in_cache_vendor) { Semver version_cache = Semver::zero(); try { @@ -1329,13 +1357,11 @@ bool PresetUpdater::install_bundles_rsrc_or_cache_vendor(std::vector recommended) + version_cache = Semver::zero(); + Semver version_rsrc = Semver::zero(); try { if (is_in_rsrc) { @@ -1345,26 +1371,33 @@ bool PresetUpdater::install_bundles_rsrc_or_cache_vendor(std::vector recommended) + version_rsrc = Semver::zero(); - if (!is_in_rsrc || version_cache > version_rsrc) { - // in case we are installing from cache / vendor. we should also copy index to cache - // This needs to be done now bcs the current one would be missing this version on next start - // dk: Should we copy it to vendor dir too? - auto path_idx_cache_vendor(path_in_cache_vendor); - path_idx_cache_vendor.replace_extension(".idx"); - auto path_idx_cache = (p->cache_path / bundle).replace_extension(".idx"); - // DK: do this during perform_updates() too? - if (fs::exists(path_idx_cache_vendor)) - copy_file_fix(path_idx_cache_vendor, path_idx_cache); - else // Should we dialog this? - BOOST_LOG_TRIVIAL(error) << GUI::format(_L("Couldn't locate idx file %1% when performing updates."), path_idx_cache_vendor.string()); + if (version_cache == Semver::zero() && version_rsrc == Semver::zero()) { + std::string msg = GUI::format(_L("Couldn't open profile file for vendor %1% when performing updates. The profile will not be installed. This installation might be corrupted."), bundle); + BOOST_LOG_TRIVIAL(error) << msg; + GUI::show_error(nullptr, msg); + continue; + } else if (version_cache == Semver::zero()) { + // cache vendor cannot be used, use resources + updates.updates.emplace_back(std::move(path_in_rsrc), std::move(path_in_vendors), Version(), "", ""); + } else if (version_rsrc == Semver::zero()) { + // resources cannto be used, use cache vendor + updates.updates.emplace_back(std::move(path_in_cache_vendor), std::move(path_in_vendors), Version(), "", ""); + } else if (version_cache > version_rsrc) { + // in case we are installing from cache / vendor. we should also copy index to cache + // This needs to be done now bcs the current one would be missing this version on the next start + auto path_idx_cache = (p->cache_path / bundle).replace_extension(".idx"); + if (idx_path != path_idx_cache) + copy_file_fix(idx_path, path_idx_cache); updates.updates.emplace_back(std::move(path_in_cache_vendor), std::move(path_in_vendors), Version(), "", ""); - } else { - if (is_in_rsrc) - updates.updates.emplace_back(std::move(path_in_rsrc), std::move(path_in_vendors), Version(), "", ""); + updates.updates.emplace_back(std::move(path_in_rsrc), std::move(path_in_vendors), Version(), "", ""); } } else { if (! is_in_rsrc) { From 663465299ba0665698c37bb7017b41c9990284fd Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 10 May 2023 11:15:39 +0200 Subject: [PATCH 20/90] Small refactoring --- src/libslic3r/Model.cpp | 8 ++++++++ src/libslic3r/Model.hpp | 2 ++ src/slic3r/GUI/GLCanvas3D.cpp | 11 +---------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 837f32479c..f2a1db3c95 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -2097,6 +2097,14 @@ bool ModelObject::has_solid_mesh() const return false; } +bool ModelObject::has_negative_volume_mesh() const +{ + for (const ModelVolume* volume : volumes) + if (volume->is_negative_volume()) + return true; + return false; +} + void ModelVolume::set_material_id(t_model_material_id material_id) { m_material_id = material_id; diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index ea22b968d5..359e1fbbdf 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -503,6 +503,8 @@ public: // Detect if object has at least one solid mash bool has_solid_mesh() const; + // Detect if object has at least one negative volume mash + bool has_negative_volume_mesh() const; bool is_cut() const { return cut_id.id().valid(); } bool has_connectors() const; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 610f3bdec3..fc2e04d5ad 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1096,16 +1096,7 @@ static bool composite_id_match(const GLVolume::CompositeID& id1, const GLVolume: } static bool object_contains_negative_volumes(const Model& model, int obj_id) { - bool ret = false; - if (0 <= obj_id && obj_id < (int)model.objects.size()) { - for (const ModelVolume* v : model.objects[obj_id]->volumes) { - if (v->is_negative_volume()) { - ret = true; - break; - } - } - } - return ret; + return (0 <= obj_id && obj_id < (int)model.objects.size()) ? model.objects[obj_id]->has_negative_volume_mesh() : false; } void GLCanvas3D::SLAView::detect_type_from_volumes(const GLVolumePtrs& volumes) From f346b42886d394c8a7b1388acf957ba31397e4ed Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 10 May 2023 11:17:10 +0200 Subject: [PATCH 21/90] Commented Part of config update algorythm that searches in snapshots. --- src/slic3r/Utils/PresetUpdater.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index e75b5420a9..995891db97 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -961,7 +961,7 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version BOOST_LOG_TRIVIAL(error) << format("Cannot load the installed index at `%1%`: %2%", bundle_path_idx, err.what()); } } - +#if 0 // Check if the update is already present in a snapshot if(!current_not_supported) { @@ -974,7 +974,7 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version continue; } } - +#endif // 0 updates.updates.emplace_back(std::move(new_update)); // 'Install' the index in the vendor directory. This is used to memoize // offered updates and to not offer the same update again if it was cancelled by the user. From d4ad9deb94ad239bf18f95b6188fefabeff83ad8 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 5 Apr 2023 16:58:54 +0200 Subject: [PATCH 22/90] Archive format registration refactored Fix tests --- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/Format/AnycubicSLA.hpp | 55 +++---- src/libslic3r/Format/SL1.cpp | 2 + .../Format/SLAArchiveFormatRegistry.cpp | 147 ++++++++++++++++++ .../Format/SLAArchiveFormatRegistry.hpp | 71 +++++++++ src/libslic3r/Format/SLAArchiveReader.cpp | 94 +++-------- src/libslic3r/Format/SLAArchiveReader.hpp | 9 -- src/libslic3r/Format/SLAArchiveWriter.cpp | 71 +-------- src/libslic3r/Format/SLAArchiveWriter.hpp | 6 - src/slic3r/GUI/Jobs/SLAImportDialog.hpp | 14 +- .../sla_print/sla_archive_readwrite_tests.cpp | 19 ++- 11 files changed, 285 insertions(+), 205 deletions(-) create mode 100644 src/libslic3r/Format/SLAArchiveFormatRegistry.cpp create mode 100644 src/libslic3r/Format/SLAArchiveFormatRegistry.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index d44bfcde09..297d2e3ff5 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -131,6 +131,8 @@ set(SLIC3R_SOURCES Format/AnycubicSLA.cpp Format/STEP.hpp Format/STEP.cpp + Format/SLAArchiveFormatRegistry.hpp + Format/SLAArchiveFormatRegistry.cpp GCode/ThumbnailData.cpp GCode/ThumbnailData.hpp GCode/Thumbnails.cpp diff --git a/src/libslic3r/Format/AnycubicSLA.hpp b/src/libslic3r/Format/AnycubicSLA.hpp index 46eb68d00b..d1f8adf8ae 100644 --- a/src/libslic3r/Format/AnycubicSLA.hpp +++ b/src/libslic3r/Format/AnycubicSLA.hpp @@ -4,44 +4,14 @@ #include #include "SLAArchiveWriter.hpp" +#include "SLAArchiveFormatRegistry.hpp" #include "libslic3r/PrintConfig.hpp" -#define ANYCUBIC_SLA_FORMAT_VERSION_1 1 -#define ANYCUBIC_SLA_FORMAT_VERSION_515 515 -#define ANYCUBIC_SLA_FORMAT_VERSION_516 516 -#define ANYCUBIC_SLA_FORMAT_VERSION_517 517 - -#define ANYCUBIC_SLA_FORMAT_VERSIONED(FILEFORMAT, NAME, VERSION) \ - { FILEFORMAT, { FILEFORMAT, [] (const auto &cfg) { return std::make_unique(cfg, VERSION); } } } - -#define ANYCUBIC_SLA_FORMAT(FILEFORMAT, NAME) \ - ANYCUBIC_SLA_FORMAT_VERSIONED(FILEFORMAT, NAME, ANYCUBIC_SLA_FORMAT_VERSION_1) - -/** - // Supports only ANYCUBIC_SLA_VERSION_1 - ANYCUBIC_SLA_FORMAT_VERSIONED("pws", "Photon / Photon S", ANYCUBIC_SLA_VERSION_1), - ANYCUBIC_SLA_FORMAT_VERSIONED("pw0", "Photon Zero", ANYCUBIC_SLA_VERSION_1), - ANYCUBIC_SLA_FORMAT_VERSIONED("pwx", "Photon X", ANYCUBIC_SLA_VERSION_1), - - // Supports ANYCUBIC_SLA_VERSION_1 and ANYCUBIC_SLA_VERSION_515 - ANYCUBIC_SLA_FORMAT_VERSIONED("pwmo", "Photon Mono", ANYCUBIC_SLA_VERSION_1), - ANYCUBIC_SLA_FORMAT_VERSIONED("pwms", "Photon Mono SE", ANYCUBIC_SLA_VERSION_1), - ANYCUBIC_SLA_FORMAT_VERSIONED("dlp", "Photon Ultra", ANYCUBIC_SLA_VERSION_1), - ANYCUBIC_SLA_FORMAT_VERSIONED("pwmx", "Photon Mono X", ANYCUBIC_SLA_VERSION_1), - ANYCUBIC_SLA_FORMAT_VERSIONED("pmsq", "Photon Mono SQ", ANYCUBIC_SLA_VERSION_1), - - // Supports ANYCUBIC_SLA_VERSION_515 and ANYCUBIC_SLA_VERSION_516 - ANYCUBIC_SLA_FORMAT_VERSIONED("pwma", "Photon Mono 4K", ANYCUBIC_SLA_VERSION_515), - ANYCUBIC_SLA_FORMAT_VERSIONED("pm3", "Photon M3", ANYCUBIC_SLA_VERSION_515), - ANYCUBIC_SLA_FORMAT_VERSIONED("pm3m", "Photon M3 Max", ANYCUBIC_SLA_VERSION_515), - - // Supports NYCUBIC_SLA_VERSION_515 and ANYCUBIC_SLA_VERSION_516 and ANYCUBIC_SLA_VERSION_517 - ANYCUBIC_SLA_FORMAT_VERSIONED("pwmb", "Photon Mono X 6K / Photon M3 Plus", ANYCUBIC_SLA_VERSION_515), - ANYCUBIC_SLA_FORMAT_VERSIONED("dl2p", "Photon Photon D2", ANYCUBIC_SLA_VERSION_515), - ANYCUBIC_SLA_FORMAT_VERSIONED("pmx2", "Photon Mono X2", ANYCUBIC_SLA_VERSION_515), - ANYCUBIC_SLA_FORMAT_VERSIONED("pm3r", "Photon M3 Premium", ANYCUBIC_SLA_VERSION_515), -*/ +constexpr uint16_t ANYCUBIC_SLA_FORMAT_VERSION_1 = 1; +constexpr uint16_t ANYCUBIC_SLA_FORMAT_VERSION_515 = 515; +constexpr uint16_t ANYCUBIC_SLA_FORMAT_VERSION_516 = 516; +constexpr uint16_t ANYCUBIC_SLA_FORMAT_VERSION_517 = 517; namespace Slic3r { @@ -75,6 +45,21 @@ public: const std::string &projectname = "") override; }; +inline Slic3r::ArchiveEntry anycubic_sla_format_versioned(const char *fileformat, const char *desc, uint16_t version) +{ + Slic3r::ArchiveEntry entry(fileformat); + + entry.desc = desc; + entry.ext = fileformat; + entry.wrfactoryfn = [version] (const auto &cfg) { return std::make_unique(cfg, version); }; + + return entry; +} + +inline Slic3r::ArchiveEntry anycubic_sla_format(const char *fileformat, const char *desc) +{ + return anycubic_sla_format_versioned(fileformat, desc, ANYCUBIC_SLA_FORMAT_VERSION_1); +} } // namespace Slic3r::sla diff --git a/src/libslic3r/Format/SL1.cpp b/src/libslic3r/Format/SL1.cpp index 4a5a25b08c..e9fc058e87 100644 --- a/src/libslic3r/Format/SL1.cpp +++ b/src/libslic3r/Format/SL1.cpp @@ -17,6 +17,7 @@ #include "libslic3r/GCode/ThumbnailData.hpp" #include "SLAArchiveReader.hpp" +#include "SLAArchiveFormatRegistry.hpp" #include "ZipperArchiveImport.hpp" #include "libslic3r/MarchingSquares.hpp" @@ -26,6 +27,7 @@ #include "libslic3r/SLA/RasterBase.hpp" + #include #include #include diff --git a/src/libslic3r/Format/SLAArchiveFormatRegistry.cpp b/src/libslic3r/Format/SLAArchiveFormatRegistry.cpp new file mode 100644 index 0000000000..5c40a5c51b --- /dev/null +++ b/src/libslic3r/Format/SLAArchiveFormatRegistry.cpp @@ -0,0 +1,147 @@ +#include +#include +#include + +#include "SL1.hpp" +#include "SL1_SVG.hpp" +#include "AnycubicSLA.hpp" + +#include "SLAArchiveFormatRegistry.hpp" + +namespace Slic3r { + +static std::mutex arch_mtx; + +class Registry { + static std::unique_ptr registry; + + std::set entries; +public: + + Registry () + { + entries = { + { + "SL1", // id + L("SL1 archive format"), // description + "sl1", // main extension + {"sl1s", "zip"}, // extension aliases + + // Writer factory + [] (const auto &cfg) { return std::make_unique(cfg); }, + + // Reader factory + [] (const std::string &fname, SLAImportQuality quality, const ProgrFn &progr) { + return std::make_unique(fname, quality, progr); + } + }, + { + "SL1SVG", + L("SL1SVG archive files"), + "sl1_svg", + {}, + [] (const auto &cfg) { return std::make_unique(cfg); }, + [] (const std::string &fname, SLAImportQuality quality, const ProgrFn &progr) { + return std::make_unique(fname, quality, progr); + } + }, + { + "SL2", + "", + "sl1_svg", + {}, + [] (const auto &cfg) { return std::make_unique(cfg); }, + nullptr + }, + anycubic_sla_format("pwmo", "Photon Mono"), + anycubic_sla_format("pwmx", "Photon Mono X"), + anycubic_sla_format("pwms", "Photon Mono SE"), + + /** + // Supports only ANYCUBIC_SLA_VERSION_1 + anycubic_sla_format_versioned("pws", "Photon / Photon S", ANYCUBIC_SLA_VERSION_1), + anycubic_sla_format_versioned("pw0", "Photon Zero", ANYCUBIC_SLA_VERSION_1), + anycubic_sla_format_versioned("pwx", "Photon X", ANYCUBIC_SLA_VERSION_1), + + // Supports ANYCUBIC_SLA_VERSION_1 and ANYCUBIC_SLA_VERSION_515 + anycubic_sla_format_versioned("pwmo", "Photon Mono", ANYCUBIC_SLA_VERSION_1), + anycubic_sla_format_versioned("pwms", "Photon Mono SE", ANYCUBIC_SLA_VERSION_1), + anycubic_sla_format_versioned("dlp", "Photon Ultra", ANYCUBIC_SLA_VERSION_1), + anycubic_sla_format_versioned("pwmx", "Photon Mono X", ANYCUBIC_SLA_VERSION_1), + anycubic_sla_format_versioned("pmsq", "Photon Mono SQ", ANYCUBIC_SLA_VERSION_1), + + // Supports ANYCUBIC_SLA_VERSION_515 and ANYCUBIC_SLA_VERSION_516 + anycubic_sla_format_versioned("pwma", "Photon Mono 4K", ANYCUBIC_SLA_VERSION_515), + anycubic_sla_format_versioned("pm3", "Photon M3", ANYCUBIC_SLA_VERSION_515), + anycubic_sla_format_versioned("pm3m", "Photon M3 Max", ANYCUBIC_SLA_VERSION_515), + + // Supports NYCUBIC_SLA_VERSION_515 and ANYCUBIC_SLA_VERSION_516 and ANYCUBIC_SLA_VERSION_517 + anycubic_sla_format_versioned("pwmb", "Photon Mono X 6K / Photon M3 Plus", ANYCUBIC_SLA_VERSION_515), + anycubic_sla_format_versioned("dl2p", "Photon Photon D2", ANYCUBIC_SLA_VERSION_515), + anycubic_sla_format_versioned("pmx2", "Photon Mono X2", ANYCUBIC_SLA_VERSION_515), + anycubic_sla_format_versioned("pm3r", "Photon M3 Premium", ANYCUBIC_SLA_VERSION_515), + */ + }; + } + + static Registry& get_instance() + { + if (!registry) + registry = std::make_unique(); + + return *registry; + } + + static std::set& get() + { + return get_instance().entries; + } + + std::set& get_entries() { return entries; } +}; + +std::unique_ptr Registry::registry = nullptr; + +std::set registered_sla_archives() +{ + std::lock_guard lk{arch_mtx}; + + return Registry::get(); +} + +std::vector get_extensions(const ArchiveEntry &entry) +{ + auto ret = reserve_vector(entry.ext_aliases.size() + 1); + + ret.emplace_back(entry.ext); + for (const char *alias : entry.ext_aliases) + ret.emplace_back(alias); + + return ret; +} + +ArchiveWriterFactory get_writer_factory(const char *formatid) +{ + std::lock_guard lk{arch_mtx}; + + ArchiveWriterFactory ret; + auto entry = Registry::get().find(ArchiveEntry{formatid}); + if (entry != Registry::get().end()) + ret = entry->wrfactoryfn; + + return ret; +} + +ArchiveReaderFactory get_reader_factory(const char *formatid) +{ + std::lock_guard lk{arch_mtx}; + + ArchiveReaderFactory ret; + auto entry = Registry::get().find(ArchiveEntry{formatid}); + if (entry != Registry::get().end()) + ret = entry->rdfactoryfn; + + return ret; +} + +} // namespace Slic3r::sla diff --git a/src/libslic3r/Format/SLAArchiveFormatRegistry.hpp b/src/libslic3r/Format/SLAArchiveFormatRegistry.hpp new file mode 100644 index 0000000000..fb1a18ca52 --- /dev/null +++ b/src/libslic3r/Format/SLAArchiveFormatRegistry.hpp @@ -0,0 +1,71 @@ +#ifndef SLA_ARCHIVE_FORMAT_REGISTRY_HPP +#define SLA_ARCHIVE_FORMAT_REGISTRY_HPP + +#include "SLAArchiveWriter.hpp" +#include "SLAArchiveReader.hpp" +#include + +namespace Slic3r { + +// Factory function that returns an implementation of SLAArchiveWriter given +// a printer configuration. +using ArchiveWriterFactory = std::function< + std::unique_ptr(const SLAPrinterConfig &) +>; + +// Factory function that returns an implementation of SLAArchiveReader +using ArchiveReaderFactory = std::function< + std::unique_ptr(const std::string &fname, + SLAImportQuality quality, + const ProgrFn & progr) +>; + +struct ArchiveEntry { + // Main ID for the format, for internal unique identification + const char *id; + + // Generic description (usable in GUI) about an archive format. Should only + // be marked for localization (macro L). + const char *desc = ""; + + // Main extension of the format. + const char *ext = "zip"; + + ArchiveWriterFactory wrfactoryfn; + ArchiveReaderFactory rdfactoryfn; + + // Secondary, alias extensions + std::vector ext_aliases; + + explicit ArchiveEntry(const char *formatid) : id{formatid} {} + + ArchiveEntry(const char *formatid, + const char *description, + const char *extension, + std::initializer_list extaliases, + const ArchiveWriterFactory &wrfn, + const ArchiveReaderFactory &rdfn) + : id{formatid} + , desc{description} + , ext{extension} + , ext_aliases{extaliases} + , wrfactoryfn{wrfn} + , rdfactoryfn{rdfn} + {} + + bool operator <(const ArchiveEntry &other) const + { + return std::strcmp(id, other.id) < 0; + } +}; + +std::vector get_extensions(const ArchiveEntry &entry); + +std::set registered_sla_archives(); + +ArchiveWriterFactory get_writer_factory(const char *formatid); +ArchiveReaderFactory get_reader_factory(const char *formatid); + +} // namespace Slic3r + +#endif // ARCHIVEREGISTRY_HPP diff --git a/src/libslic3r/Format/SLAArchiveReader.cpp b/src/libslic3r/Format/SLAArchiveReader.cpp index b931ea0e4d..c8a15bc5ac 100644 --- a/src/libslic3r/Format/SLAArchiveReader.cpp +++ b/src/libslic3r/Format/SLAArchiveReader.cpp @@ -8,44 +8,13 @@ #include #include +#include "SLAArchiveFormatRegistry.hpp" #include #include namespace Slic3r { -namespace { - -// Factory function that returns an implementation of SLAArchiveReader. -using ArchiveFactory = std::function< - std::unique_ptr(const std::string &fname, - SLAImportQuality quality, - const ProgrFn & progr)>; - -// Entry in the global registry of readable archive formats. -struct ArchiveEntry { - const char *descr; - std::vector extensions; - ArchiveFactory factoryfn; -}; - -// This is where the readable archive formats are registered. -static const std::map REGISTERED_ARCHIVES { - { - "SL1", - { L("SL1 / SL1S archive files"), {"sl1", "sl1s", "zip"}, - [] (const std::string &fname, SLAImportQuality quality, const ProgrFn &progr) { return std::make_unique(fname, quality, progr); } } - }, - { - "SL1SVG", - { L("SL1SVG archive files"), {"sl1_svg"/*, "zip"*/}, // also a zip but unnecessary hassle to implement single extension for multiple archives - [] (const std::string &fname, SLAImportQuality quality, const ProgrFn &progr) { return std::make_unique(fname, quality, progr); }} - }, - // TODO: pwmx and future others. -}; - -} // namespace - std::unique_ptr SLAArchiveReader::create( const std::string &fname, const std::string &format_id, @@ -64,11 +33,13 @@ std::unique_ptr SLAArchiveReader::create( std::unique_ptr ret; - auto arch_from = REGISTERED_ARCHIVES.begin(); - auto arch_to = REGISTERED_ARCHIVES.end(); + auto registry = registered_sla_archives(); - auto arch_it = REGISTERED_ARCHIVES.find(format_id); - if (arch_it != REGISTERED_ARCHIVES.end()) { + auto arch_from = registry.begin(); + auto arch_to = registry.end(); + + auto arch_it = registry.find(ArchiveEntry{format_id.c_str()}); + if (arch_it != registry.end()) { arch_from = arch_it; arch_to = arch_it; } @@ -77,52 +48,23 @@ std::unique_ptr SLAArchiveReader::create( if (ext.front() == '.') ext.erase(ext.begin()); - auto extcmp = [&ext](const auto &e) { return e == ext; }; - - for (auto it = arch_from; it != arch_to; ++it) { - const auto &[format_id, entry] = *it; - if (std::any_of(entry.extensions.begin(), entry.extensions.end(), extcmp)) - ret = entry.factoryfn(fname, quality, progr); + for (auto it = arch_from; !ret && it != arch_to; ++it) { + const auto &entry = *it; + if (entry.rdfactoryfn) { + auto extensions = get_extensions(entry); + for (const std::string& supportedext : extensions) { + if (ext == supportedext) { + ret = entry.rdfactoryfn(fname, quality, progr); + break; + } + } + } } } return ret; } -const std::vector &SLAArchiveReader::registered_archives() -{ - static std::vector archnames; - - if (archnames.empty()) { - archnames.reserve(REGISTERED_ARCHIVES.size()); - - for (auto &[name, _] : REGISTERED_ARCHIVES) - archnames.emplace_back(name.c_str()); - } - - return archnames; -} - -std::vector SLAArchiveReader::get_extensions(const char *archtype) -{ - auto it = REGISTERED_ARCHIVES.find(archtype); - - if (it != REGISTERED_ARCHIVES.end()) - return it->second.extensions; - - return {}; -} - -const char *SLAArchiveReader::get_description(const char *archtype) -{ - auto it = REGISTERED_ARCHIVES.find(archtype); - - if (it != REGISTERED_ARCHIVES.end()) - return it->second.descr; - - return nullptr; -} - struct SliceParams { double layerh = 0., initial_layerh = 0.; }; static SliceParams get_slice_params(const DynamicPrintConfig &cfg) diff --git a/src/libslic3r/Format/SLAArchiveReader.hpp b/src/libslic3r/Format/SLAArchiveReader.hpp index e7a99b0430..df93ba1ba1 100644 --- a/src/libslic3r/Format/SLAArchiveReader.hpp +++ b/src/libslic3r/Format/SLAArchiveReader.hpp @@ -47,15 +47,6 @@ public: const std::string &format_id, SLAImportQuality quality = SLAImportQuality::Balanced, const ProgrFn &progr = [](int) { return false; }); - - // Get the names of currently known archive reader implementations - static const std::vector & registered_archives(); - - // Get the understood file extensions belonging to an archive format - static std::vector get_extensions(const char *archtype); - - // Generic description (usable in GUI) about an archive format - static const char * get_description(const char *archtype); }; // Raised in import_sla_archive when a nullptr reader is returned by diff --git a/src/libslic3r/Format/SLAArchiveWriter.cpp b/src/libslic3r/Format/SLAArchiveWriter.cpp index 7546d7c46a..5d3cee7cf8 100644 --- a/src/libslic3r/Format/SLAArchiveWriter.cpp +++ b/src/libslic3r/Format/SLAArchiveWriter.cpp @@ -1,77 +1,18 @@ #include "SLAArchiveWriter.hpp" - -#include "SL1.hpp" -#include "SL1_SVG.hpp" -#include "AnycubicSLA.hpp" - -#include "libslic3r/libslic3r.h" - -#include -#include -#include -#include +#include "SLAArchiveFormatRegistry.hpp" namespace Slic3r { -using ArchiveFactory = std::function(const SLAPrinterConfig&)>; - -struct ArchiveEntry { - const char *ext; - ArchiveFactory factoryfn; -}; - -static const std::map REGISTERED_ARCHIVES { - { - "SL1", - { "sl1", [] (const auto &cfg) { return std::make_unique(cfg); } } - }, - { - "SL1SVG", - { "sl1_svg", [] (const auto &cfg) { return std::make_unique(cfg); } } - }, - { - "SL2", - { "sl1_svg", [] (const auto &cfg) { return std::make_unique(cfg); } } - }, - ANYCUBIC_SLA_FORMAT("pwmo", "Photon Mono"), - ANYCUBIC_SLA_FORMAT("pwmx", "Photon Mono X"), - ANYCUBIC_SLA_FORMAT("pwms", "Photon Mono SE"), -}; - std::unique_ptr SLAArchiveWriter::create(const std::string &archtype, const SLAPrinterConfig &cfg) { - auto entry = REGISTERED_ARCHIVES.find(archtype); + std::unique_ptr ret; + auto factory = get_writer_factory(archtype.c_str()); - if (entry != REGISTERED_ARCHIVES.end()) - return entry->second.factoryfn(cfg); + if (factory) + ret = factory(cfg); - return nullptr; -} - -const std::vector& SLAArchiveWriter::registered_archives() -{ - static std::vector archnames; - - if (archnames.empty()) { - archnames.reserve(REGISTERED_ARCHIVES.size()); - - for (auto &[name, _] : REGISTERED_ARCHIVES) - archnames.emplace_back(name.c_str()); - } - - return archnames; -} - -const char *SLAArchiveWriter::get_extension(const char *archtype) -{ - constexpr const char* DEFAULT_EXT = "zip"; - - auto entry = REGISTERED_ARCHIVES.find(archtype); - if (entry != REGISTERED_ARCHIVES.end()) - return entry->second.ext; - - return DEFAULT_EXT; + return ret; } } // namespace Slic3r diff --git a/src/libslic3r/Format/SLAArchiveWriter.hpp b/src/libslic3r/Format/SLAArchiveWriter.hpp index 86132cceba..1e6ed649b2 100644 --- a/src/libslic3r/Format/SLAArchiveWriter.hpp +++ b/src/libslic3r/Format/SLAArchiveWriter.hpp @@ -53,12 +53,6 @@ public: // Factory method to create an archiver instance static std::unique_ptr create( const std::string &archtype, const SLAPrinterConfig &); - - // Get the names of currently known archiver implementations - static const std::vector & registered_archives(); - - // Get the default file extension belonging to an archive format - static const char *get_extension(const char *archtype); }; } // namespace Slic3r diff --git a/src/slic3r/GUI/Jobs/SLAImportDialog.hpp b/src/slic3r/GUI/Jobs/SLAImportDialog.hpp index fed84600c2..aa2fb48c12 100644 --- a/src/slic3r/GUI/Jobs/SLAImportDialog.hpp +++ b/src/slic3r/GUI/Jobs/SLAImportDialog.hpp @@ -11,6 +11,7 @@ #include "libslic3r/AppConfig.hpp" #include "libslic3r/Format/SLAArchiveReader.hpp" +#include "libslic3r/Format/SLAArchiveFormatRegistry.hpp" #include "slic3r/GUI/I18N.hpp" @@ -29,11 +30,16 @@ std::string get_readers_wildcard() { std::string ret; - for (const char *archtype : SLAArchiveReader::registered_archives()) { - ret += into_u8(_(SLAArchiveReader::get_description(archtype))); + auto registry = registered_sla_archives(); + + for (const ArchiveEntry &entry : registry) { + if (!entry.rdfactoryfn) + continue; + + ret += into_u8(_(entry.desc)); ret += " ("; - auto extensions = SLAArchiveReader::get_extensions(archtype); - for (const char * ext : extensions) { + std::vector extensions = get_extensions(entry); + for (const std::string &ext : extensions) { ret += "*."; ret += ext; ret += ", "; diff --git a/tests/sla_print/sla_archive_readwrite_tests.cpp b/tests/sla_print/sla_archive_readwrite_tests.cpp index a7ed7f0a4f..fb1af3d7f7 100644 --- a/tests/sla_print/sla_archive_readwrite_tests.cpp +++ b/tests/sla_print/sla_archive_readwrite_tests.cpp @@ -3,6 +3,7 @@ #include "libslic3r/SLAPrint.hpp" #include "libslic3r/TriangleMesh.hpp" +#include "libslic3r/Format/SLAArchiveFormatRegistry.hpp" #include "libslic3r/Format/SLAArchiveWriter.hpp" #include "libslic3r/Format/SLAArchiveReader.hpp" @@ -11,16 +12,18 @@ using namespace Slic3r; TEST_CASE("Archive export test", "[sla_archives]") { + auto registry = registered_sla_archives(); + for (const char * pname : {"20mm_cube", "extruder_idler"}) - for (auto &archname : SLAArchiveWriter::registered_archives()) { - INFO(std::string("Testing archive type: ") + archname + " -- writing..."); + for (const ArchiveEntry &entry : registry) { + INFO(std::string("Testing archive type: ") + entry.id + " -- writing..."); SLAPrint print; SLAFullPrintConfig fullcfg; auto m = Model::read_from_file(TEST_DATA_DIR PATH_SEPARATOR + std::string(pname) + ".obj", nullptr); fullcfg.printer_technology.setInt(ptSLA); // FIXME this should be ensured - fullcfg.set("sla_archive_format", archname); + fullcfg.set("sla_archive_format", entry.id); fullcfg.set("supports_enable", false); fullcfg.set("pad_enable", false); @@ -32,7 +35,7 @@ TEST_CASE("Archive export test", "[sla_archives]") { print.process(); ThumbnailsList thumbnails; - auto outputfname = std::string("output_") + pname + "." + SLAArchiveWriter::get_extension(archname); + auto outputfname = std::string("output_") + pname + "." + entry.ext; print.export_print(outputfname, thumbnails, pname); @@ -41,12 +44,8 @@ TEST_CASE("Archive export test", "[sla_archives]") { double vol_written = m.mesh().volume(); - auto readable_formats = SLAArchiveReader::registered_archives(); - if (std::any_of(readable_formats.begin(), readable_formats.end(), - [&archname](const std::string &a) { return a == archname; })) { - - INFO(std::string("Testing archive type: ") + archname + " -- reading back..."); - + if (entry.rdfactoryfn) { + INFO(std::string("Testing archive type: ") + entry.id + " -- reading back..."); indexed_triangle_set its; DynamicPrintConfig cfg; From 8f680fd496aae568b539d8909b23ee07af8cfc9b Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 10 May 2023 17:05:05 +0200 Subject: [PATCH 23/90] Fix no action on sla archive import with "profile only" option --- src/libslic3r/Format/SL1.cpp | 2 +- src/libslic3r/Format/ZipperArchiveImport.cpp | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Format/SL1.cpp b/src/libslic3r/Format/SL1.cpp index e9fc058e87..6b7f57b7f6 100644 --- a/src/libslic3r/Format/SL1.cpp +++ b/src/libslic3r/Format/SL1.cpp @@ -438,7 +438,7 @@ ConfigSubstitutions SL1Reader::read(std::vector &slices, ConfigSubstitutions SL1Reader::read(DynamicPrintConfig &out) { - ZipperArchive arch = read_zipper_archive(m_fname, {}, {"png"}); + ZipperArchive arch = read_zipper_archive(m_fname, {"ini"}, {"png", "thumbnail"}); return out.load(arch.profile, ForwardCompatibilitySubstitutionRule::Enable); } diff --git a/src/libslic3r/Format/ZipperArchiveImport.cpp b/src/libslic3r/Format/ZipperArchiveImport.cpp index 2bd5f555b5..657e420bbc 100644 --- a/src/libslic3r/Format/ZipperArchiveImport.cpp +++ b/src/libslic3r/Format/ZipperArchiveImport.cpp @@ -83,8 +83,15 @@ ZipperArchive read_zipper_archive(const std::string &zipfname, })) continue; - if (name == CONFIG_FNAME) { arch.config = read_ini(entry, zip); continue; } - if (name == PROFILE_FNAME) { arch.profile = read_ini(entry, zip); continue; } + if (name == CONFIG_FNAME) { + arch.config = read_ini(entry, zip); + continue; + } + + if (name == PROFILE_FNAME) { + arch.profile = read_ini(entry, zip); + continue; + } auto it = std::lower_bound( arch.entries.begin(), arch.entries.end(), From a4cf34a49f98866d812a2a237740a7bf96488faa Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 24 Apr 2023 14:58:56 +0200 Subject: [PATCH 24/90] Fix for SPE-1657 : [LINUX - GTK3] Crash when change position of part in sidebar --- src/slic3r/GUI/GUI_ObjectList.cpp | 34 +++++++++++++++++--------- src/slic3r/GUI/ObjectDataViewModel.cpp | 10 ++++++++ src/slic3r/GUI/ObjectDataViewModel.hpp | 1 + 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index ffed1eef68..4fa9d6a16c 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1188,6 +1188,13 @@ void ObjectList::key_event(wxKeyEvent& event) void ObjectList::OnBeginDrag(wxDataViewEvent &event) { + if (m_is_editing_started) + m_is_editing_started = false; +#ifdef __WXGTK__ + const auto renderer = dynamic_cast(GetColumn(colName)->GetRenderer()); + renderer->FinishEditing(); +#endif + const wxDataViewItem item(event.GetItem()); const bool mult_sel = multiple_selection(); @@ -1221,18 +1228,11 @@ void ObjectList::OnBeginDrag(wxDataViewEvent &event) m_objects_model->GetInstanceIdByItem(item), type); - /* Under MSW or OSX, DnD moves an item to the place of another selected item - * But under GTK, DnD moves an item between another two items. - * And as a result - call EVT_CHANGE_SELECTION to unselect all items. - * To prevent such behavior use m_prevent_list_events - **/ - m_prevent_list_events = true;//it's needed for GTK - /* Under GTK, DnD requires to the wxTextDataObject been initialized with some valid value, * so set some nonempty string */ wxTextDataObject* obj = new wxTextDataObject; - obj->SetText("Some text");//it's needed for GTK + obj->SetText(mult_sel ? "SomeText" : m_objects_model->GetItemName(item));//it's needed for GTK event.SetDataObject(obj); event.SetDragFlags(wxDrag_DefaultMove); // allows both copy and move; @@ -1295,11 +1295,8 @@ bool ObjectList::can_drop(const wxDataViewItem& item) const void ObjectList::OnDropPossible(wxDataViewEvent &event) { const wxDataViewItem& item = event.GetItem(); - - if (!can_drop(item)) { + if (!can_drop(item)) event.Veto(); - m_prevent_list_events = false; - } } void ObjectList::OnDrop(wxDataViewEvent &event) @@ -1313,6 +1310,13 @@ void ObjectList::OnDrop(wxDataViewEvent &event) return; } + /* Under MSW or OSX, DnD moves an item to the place of another selected item + * But under GTK, DnD moves an item between another two items. + * And as a result - call EVT_CHANGE_SELECTION to unselect all items. + * To prevent such behavior use m_prevent_list_events + **/ + m_prevent_list_events = true;//it's needed for GTK + if (m_dragged_data.type() == itInstance) { Plater::TakeSnapshot snapshot(wxGetApp().plater(),_(L("Instances to Separated Objects"))); @@ -4815,6 +4819,9 @@ void ObjectList::sys_color_changed() void ObjectList::ItemValueChanged(wxDataViewEvent &event) { + if (!m_is_editing_started) + return; + if (event.GetColumn() == colName) update_name_in_model(event.GetItem()); else if (event.GetColumn() == colExtruder) { @@ -4837,6 +4844,9 @@ void ObjectList::OnEditingStarted(wxDataViewEvent &event) void ObjectList::OnEditingDone(wxDataViewEvent &event) { + if (!m_is_editing_started) + return; + m_is_editing_started = false; if (event.GetColumn() != colName) return; diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index fe57d7d5a4..38747d08dd 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -1042,6 +1042,16 @@ int ObjectDataViewModel::GetItemIdByLayerRange(const int obj_idx, const t_layer return GetLayerIdByItem(item); } +wxString ObjectDataViewModel::GetItemName(const wxDataViewItem &item) const +{ + if (!item.IsOk()) + return wxEmptyString; + ObjectDataViewModelNode* node = static_cast(item.GetID()); + if (!node) + return wxEmptyString; + return node->GetName(); +} + int ObjectDataViewModel::GetIdByItem(const wxDataViewItem& item) const { if(!item.IsOk()) diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp index 993b67842a..bc5b485a38 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.hpp +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -311,6 +311,7 @@ public: wxDataViewItem GetItemByLayerId(int obj_idx, int layer_idx); wxDataViewItem GetItemByLayerRange(const int obj_idx, const t_layer_height_range& layer_range); int GetItemIdByLayerRange(const int obj_idx, const t_layer_height_range& layer_range); + wxString GetItemName(const wxDataViewItem& item) const; int GetIdByItem(const wxDataViewItem& item) const; int GetIdByItemAndType(const wxDataViewItem& item, const ItemType type) const; int GetObjectIdByItem(const wxDataViewItem& item) const; From 8e3a988bff3486d04bd5bb492942ae63231e8477 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 5 May 2023 16:55:31 +0200 Subject: [PATCH 25/90] Fix for SPE-1687 : SLA - Loop/crash during editing SLA support points ManipulationPanel: Disable editable controls, when some gizmo is in editing mode --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 19 ++++--- src/slic3r/GUI/GUI_ObjectManipulation.hpp | 8 +++ src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp | 11 +++- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 57 +++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp | 5 +- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 3 +- 6 files changed, 60 insertions(+), 43 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 7c8df40b38..d86c8275ef 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -15,6 +15,8 @@ #include "MainFrame.hpp" #include "MsgDialog.hpp" +#include + #include #include "slic3r/Utils/FixModelByWin10.hpp" @@ -519,19 +521,18 @@ void ObjectManipulation::UpdateAndShow(const bool show) OG_Settings::UpdateAndShow(show); } -void ObjectManipulation::Enable(const bool enadle) +void ObjectManipulation::Enable(const bool enable) { - for (auto editor : m_editors) - editor->Enable(enadle); + m_is_enabled = m_is_enabled_size_and_scale = enable; for (wxWindow* win : std::initializer_list{ m_reset_scale_button, m_reset_rotation_button, m_drop_to_bed_button, m_check_inch, m_lock_bnt , m_reset_skew_button }) - win->Enable(enadle); + win->Enable(enable); } void ObjectManipulation::DisableScale() { - for (auto editor : m_editors) - editor->Enable(editor->has_opt_key("scale") || editor->has_opt_key("size") ? false : true); + m_is_enabled = true; + m_is_enabled_size_and_scale = false; for (wxWindow* win : std::initializer_list{ m_reset_scale_button, m_lock_bnt, m_reset_skew_button }) win->Enable(false); } @@ -1229,6 +1230,12 @@ ManipulationEditor::ManipulationEditor(ObjectManipulation* parent, this->SetSelection(-1, -1); //select all event.Skip(); })); + + this->Bind(wxEVT_UPDATE_UI, [parent, this](wxUpdateUIEvent& evt) { + const bool is_gizmo_in_editing_mode = wxGetApp().plater()->canvas3D()->get_gizmos_manager().is_in_editing_mode(); + const bool is_enabled_editing = has_opt_key("scale") || has_opt_key("size") ? parent->is_enabled_size_and_scale() : true; + evt.Enable(!is_gizmo_in_editing_mode && parent->is_enabled() && is_enabled_editing); + }); } void ManipulationEditor::msw_rescale() diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index 428ffc24b3..139171d999 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -165,6 +165,11 @@ private: std::vector m_editors; + // parameters for enabling/disabling of editors + bool m_is_enabled { true }; + bool m_is_enabled_size_and_scale { true }; + + public: ObjectManipulation(wxWindow* parent); ~ObjectManipulation() {} @@ -213,6 +218,9 @@ public: static wxString coordinate_type_str(ECoordinatesType type); + bool is_enabled() const { return m_is_enabled; } + bool is_enabled_size_and_scale()const { return m_is_enabled_size_and_scale; } + #if ENABLE_OBJECT_MANIPULATION_DEBUG void render_debug_window(); #endif // ENABLE_OBJECT_MANIPULATION_DEBUG diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp index 0688ca5b2b..8451679dd4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaBase.cpp @@ -20,8 +20,15 @@ GLGizmoSlaBase::GLGizmoSlaBase(GLCanvas3D& parent, const std::string& icon_filen void GLGizmoSlaBase::reslice_until_step(SLAPrintObjectStep step, bool postpone_error_messages) { wxGetApp().CallAfter([this, step, postpone_error_messages]() { - wxGetApp().plater()->reslice_SLA_until_step(step, *m_c->selection_info()->model_object(), postpone_error_messages); - }); + if (m_c->selection_info()) + wxGetApp().plater()->reslice_SLA_until_step(step, *m_c->selection_info()->model_object(), postpone_error_messages); + else { + const Selection& selection = m_parent.get_selection(); + const int object_idx = selection.get_object_idx(); + if (object_idx >= 0 && !selection.is_wipe_tower()) + wxGetApp().plater()->reslice_SLA_until_step(step, *wxGetApp().plater()->model().objects[object_idx], postpone_error_messages); + } + }); } CommonGizmosDataID GLGizmoSlaBase::on_get_requirements() const diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index b9ec5cf19b..6f9a090a4d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -399,7 +399,7 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous } if (action == SLAGizmoEventType::DiscardChanges) { - ask_about_changes_call_after([this](){ editing_mode_apply_changes(); }, + ask_about_changes([this](){ editing_mode_apply_changes(); }, [this](){ editing_mode_discard_changes(); }); return true; } @@ -816,39 +816,36 @@ std::string GLGizmoSlaSupports::on_get_name() const return _u8L("SLA Support Points"); } -void GLGizmoSlaSupports::ask_about_changes_call_after(std::function on_yes, std::function on_no) +bool GLGizmoSlaSupports::ask_about_changes(std::function on_yes, std::function on_no) { - wxGetApp().CallAfter([on_yes, on_no]() { - // Following is called through CallAfter, because otherwise there was a problem - // on OSX with the wxMessageDialog being shown several times when clicked into. - MessageDialog dlg(GUI::wxGetApp().mainframe, _L("Do you want to save your manually " - "edited support points?") + "\n",_L("Save support points?"), wxICON_QUESTION | wxYES | wxNO | wxCANCEL ); - int ret = dlg.ShowModal(); - if (ret == wxID_YES) - on_yes(); - else if (ret == wxID_NO) - on_no(); - }); -} + MessageDialog dlg(GUI::wxGetApp().mainframe, _L("Do you want to save your manually edited support points?") + "\n", + _L("Save support points?"), wxICON_QUESTION | wxYES | wxNO | wxCANCEL ); + const int ret = dlg.ShowModal(); + if (ret == wxID_YES) + on_yes(); + else if (ret == wxID_NO) + on_no(); + else + return false; + + return true; +} void GLGizmoSlaSupports::on_set_state() { - if (m_state == m_old_state) - return; - - if (m_state == On && m_old_state != On) { // the gizmo was just turned on + if (m_state == On) { // the gizmo was just turned on // Set default head diameter from config. const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; m_new_point_head_diameter = static_cast(cfg.option("support_head_front_diameter"))->value; } - if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off - bool will_ask = m_editing_mode && unsaved_changes() && on_is_activable(); - if (will_ask) { - ask_about_changes_call_after([this](){ editing_mode_apply_changes(); }, - [this](){ editing_mode_discard_changes(); }); - // refuse to be turned off so the gizmo is active when the CallAfter is executed - m_state = m_old_state; + else { + if (m_editing_mode && unsaved_changes() && on_is_activable()) { + if (!ask_about_changes([this]() { editing_mode_apply_changes(); }, + [this]() { editing_mode_discard_changes(); })) { + m_state = On; + return; + } } else { // we are actually shutting down @@ -856,16 +853,12 @@ void GLGizmoSlaSupports::on_set_state() m_old_mo_id = -1; } - if (m_state == Off) { - m_c->instances_hider()->set_hide_full_scene(false); - m_c->selection_info()->set_use_shift(false); // see top of on_render for details - } + m_c->instances_hider()->set_hide_full_scene(false); + m_c->selection_info()->set_use_shift(false); // see top of on_render for details + } - m_old_state = m_state; } - - void GLGizmoSlaSupports::on_start_dragging() { if (m_hover_id != -1) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index c098905aa5..1c02b6aebe 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -110,7 +110,6 @@ private: bool m_wait_for_up_event = false; bool m_selection_empty = true; - EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state) std::vector get_config_options(const std::vector& keys) const; bool is_mesh_point_clipped(const Vec3d& point) const; @@ -131,7 +130,9 @@ private: void auto_generate(); void switch_to_editing_mode(); void disable_editing_mode(); - void ask_about_changes_call_after(std::function on_yes, std::function on_no); + + // return false if Cancel was selected + bool ask_about_changes(std::function on_yes, std::function on_no); protected: void on_set_state() override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 98dd151372..9ef4631eb4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -173,7 +173,8 @@ void GLGizmosManager::reset_all_states() const EType current = get_current_type(); if (current != Undefined) // close any open gizmo - open_gizmo(current); + if (!open_gizmo(current)) + return; activate_gizmo(Undefined); m_hover = Undefined; From 9e4859ebbf9006a454e905c0aef352be1cabf251 Mon Sep 17 00:00:00 2001 From: Oleksandra Yushchenko Date: Thu, 11 May 2023 12:17:59 +0200 Subject: [PATCH 26/90] Modification of extruders filaments for support XL multitool (#36) * Fix for SPE-1659 : Wrong filaments update, when templates are allowed/suppressed * Per Extruder Filaments (related to SPE 1599) * Implemented compatibility for each extruder separately * Update of the filaments compatibility is extracted to separate function update_filaments_compatible() * Fixed synchronization of config.ini with the current selections, when preset is changed from sidebar. * Filament Settings Tab improvements: * Added extruder combobox to select active extruder * PresetCombobox is updated in respect to compatibility for active extruder * For MM printer: Cog icon near the filament will switch to Filament Settings for selected filament * Bug fixing for https://dev.prusa3d.com/browse/SPE-1599 * Next round of bugfixing for https://dev.prusa3d.com/browse/SPE-1599 * Fixed crashes from https://dev.prusa3d.com/browse/SPE-1599 + code refactoring for Tab:save_preset() --- src/libslic3r/Preset.cpp | 150 +++++++++++++- src/libslic3r/Preset.hpp | 143 ++++++++++++- src/libslic3r/PresetBundle.cpp | 302 +++++++++++++++++----------- src/libslic3r/PresetBundle.hpp | 13 +- src/slic3r/GUI/Plater.cpp | 75 ++++--- src/slic3r/GUI/Plater.hpp | 1 + src/slic3r/GUI/Preferences.cpp | 10 +- src/slic3r/GUI/PresetComboBoxes.cpp | 89 ++++---- src/slic3r/GUI/PresetComboBoxes.hpp | 5 + src/slic3r/GUI/PresetHints.cpp | 4 +- src/slic3r/GUI/Tab.cpp | 198 +++++++++++++++--- src/slic3r/GUI/Tab.hpp | 25 ++- 12 files changed, 769 insertions(+), 246 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index d20514bba3..b63576a0db 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -403,7 +403,9 @@ void Preset::set_visible_from_appconfig(const AppConfig &app_config) is_visible = app_config.get_variant(vendor->id, model, variant); } else if (type == TYPE_FILAMENT || type == TYPE_SLA_MATERIAL) { const std::string §ion_name = (type == TYPE_FILAMENT) ? AppConfig::SECTION_FILAMENTS : AppConfig::SECTION_MATERIALS; - if (app_config.has_section(section_name)) { + if (type == TYPE_FILAMENT && app_config.get_bool("no_templates") && vendor && vendor->templates_profile) + is_visible = false; + else if (app_config.has_section(section_name)) { // Check whether this profile is marked as "installed" in PrusaSlicer.ini, // or whether a profile is marked as "installed", which this profile may have been renamed from. const std::map &installed = app_config.get_section(section_name); @@ -896,8 +898,9 @@ Preset& PresetCollection::load_preset(const std::string &path, const std::string return preset; } -void PresetCollection::save_current_preset(const std::string &new_name, bool detach) +bool PresetCollection::save_current_preset(const std::string &new_name, bool detach) { + bool is_saved_as_new{ false }; // 1) Find the preset with a new_name or create a new one, // initialize it with the edited config. auto it = this->find_preset_internal(new_name); @@ -906,7 +909,7 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det Preset &preset = *it; if (preset.is_default || preset.is_external || preset.is_system) // Cannot overwrite the default preset. - return; + return false; // Overwriting an existing preset. preset.config = std::move(m_edited_preset.config); // The newly saved preset will be activated -> make it visible. @@ -919,6 +922,7 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det preset.renamed_from.clear(); } } else { + is_saved_as_new = true; // Creating a new preset. Preset &preset = *m_presets.insert(it, m_edited_preset); std::string &inherits = preset.inherits(); @@ -953,6 +957,8 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det this->select_preset_by_name(new_name, true); // 2) Store the active preset to disk. this->get_selected_preset().save(); + + return is_saved_as_new; } Preset& PresetCollection::get_preset_with_name(const std::string& new_name, const Preset* initial_preset) @@ -1212,7 +1218,13 @@ size_t PresetCollection::update_compatible_internal(const PresetWithVendorProfil if (selected) preset_selected.is_compatible = preset_edited.is_compatible; if (preset_edited.vendor && preset_edited.vendor->templates_profile) { - indices_of_template_presets.push_back(idx_preset); + if (preset_selected.is_visible) + indices_of_template_presets.push_back(idx_preset); + else { + preset_selected.is_compatible = false; + if (selected) + m_idx_selected = size_t(-1); + } } } // filter out template profiles where profile with same alias and compability exists @@ -2092,6 +2104,136 @@ bool PhysicalPrinterCollection::is_selected(PhysicalPrinterCollection::ConstIter m_selected_preset == preset_name; } +ExtruderFilaments::ExtruderFilaments(PresetCollection* filaments_collection, size_t extruder_id, std::string selected_name/* = std::string()*/) +: m_filaments (filaments_collection) +, m_extruder_id(extruder_id) +{ + const std::deque& presets = m_filaments->get_presets(); + for (size_t id = 0; id < presets.size(); id ++) + m_extr_filaments.emplace_back(&(presets[id])); + + select_filament(selected_name.empty() ? m_filaments->get_selected_preset_name() : selected_name); +} + +const std::string& ExtruderFilaments::get_preset_name_by_alias(const std::string& alias) const +{ + const auto& aliases_map = m_filaments->map_alias_to_profile_name(); + for ( + // Find the 1st profile name with the alias. + auto it = Slic3r::lower_bound_by_predicate(aliases_map.begin(), aliases_map.end(), [&alias](auto& l) { return l.first < alias; }); + // Continue over all profile names with the same alias. + it != aliases_map.end() && it->first == alias; ++it) + if (auto it_filament = find_filament_internal(it->second); + it_filament != m_extr_filaments.end() && it_filament->preset->name == it->second && + it_filament->preset->is_visible && (it_filament->is_compatible || size_t(it_filament - m_extr_filaments.begin()) == m_idx_selected)) + return it_filament->preset->name; + return alias; +} + +bool ExtruderFilaments::select_filament(const std::string &name_w_suffix, bool force/*= false*/) +{ + std::string name = Preset::remove_suffix_modified(name_w_suffix); + // 1) Try to find the preset by its name. + auto it = this->find_filament_internal(name); + size_t idx = 0; + if (it != m_extr_filaments.end() && it->preset->name == name && it->preset->is_visible) + // Preset found by its name and it is visible. + idx = it - m_extr_filaments.begin(); + else { + // Find the first visible preset. + for (size_t i = 0; i < m_extr_filaments.size(); ++i) + if (m_extr_filaments[i].preset->is_visible/* && m_extr_filaments[i].is_compatible*/) { + idx = i; + break; + } + // If the first visible preset was not found, return the 0th element, which is the default preset. + } + // 2) Select the new preset. + if (m_idx_selected != idx || force) { + this->select_filament(idx); + return true; + } + + return false; +} + +size_t ExtruderFilaments::update_compatible_internal(const PresetWithVendorProfile &active_printer, + const PresetWithVendorProfile *active_print, + PresetSelectCompatibleType unselect_if_incompatible) +{ + DynamicPrintConfig config; + config.set_key_value("printer_preset", new ConfigOptionString(active_printer.preset.name)); + const ConfigOption* opt = active_printer.preset.config.option("nozzle_diameter"); + if (opt) + config.set_key_value("num_extruders", new ConfigOptionInt((int)static_cast(opt)->values.size())); + bool some_compatible = false; + + // Adjust printer preset config to the first extruder from m_extruder_id + Preset printer_preset_adjusted = active_printer.preset; + if (m_extruder_id > 0 && !printer_preset_adjusted.config.opt_bool("single_extruder_multi_material")) { + DynamicPrintConfig& active_printer_config = printer_preset_adjusted.config; + for (const std::string& key : print_config_def.extruder_option_keys()) { + if (key == "default_filament_profile") + continue;// Ignore this field, because this parameter is not related to the extruder but to whole printer. + auto* opt = active_printer_config.option(key, false); + if (opt != nullptr && opt->is_vector()) + static_cast(opt)->set_at(opt, 0, m_extruder_id); + } + } + PresetWithVendorProfile active_printer_adjusted(printer_preset_adjusted, active_printer.vendor); + + std::vector indices_of_template_presets; + indices_of_template_presets.reserve(m_extr_filaments.size()); + + size_t num_default_presets = m_filaments->num_default_presets(); + for (size_t idx_preset = num_default_presets; idx_preset < m_extr_filaments.size(); ++idx_preset) { + const bool is_selected = idx_preset == m_idx_selected; + const Preset* preset = m_extr_filaments[idx_preset].preset; + Filament& extr_filament = m_extr_filaments[idx_preset]; + + const PresetWithVendorProfile this_preset_with_vendor_profile = m_filaments->get_preset_with_vendor_profile(*preset); + bool was_compatible = extr_filament.is_compatible; + extr_filament.is_compatible = is_compatible_with_printer(this_preset_with_vendor_profile, active_printer_adjusted, &config); + some_compatible |= extr_filament.is_compatible; + if (active_print != nullptr) + extr_filament.is_compatible &= is_compatible_with_print(this_preset_with_vendor_profile, *active_print, active_printer_adjusted); + if (!extr_filament.is_compatible && is_selected && + (unselect_if_incompatible == PresetSelectCompatibleType::Always || (unselect_if_incompatible == PresetSelectCompatibleType::OnlyIfWasCompatible && was_compatible))) + m_idx_selected = size_t(-1); + if (preset->vendor && preset->vendor->templates_profile) { + if (preset->is_visible) + indices_of_template_presets.push_back(idx_preset); + else { + extr_filament.is_compatible = false; + if (is_selected) + m_idx_selected = size_t(-1); + } + } + } + + // filter out template profiles where profile with same alias and compability exists + if (!indices_of_template_presets.empty()) { + for (size_t idx = num_default_presets; idx < m_extr_filaments.size(); ++idx) { + const Filament& filament = m_extr_filaments[idx]; + const VendorProfile* vendor = filament.preset->vendor; + if (vendor && !vendor->templates_profile && filament.is_compatible) { + const std::string& preset_alias = filament.preset->alias; + for (const auto& template_idx : indices_of_template_presets) { + if (m_extr_filaments[template_idx].preset->alias == preset_alias) { + m_extr_filaments[template_idx].is_compatible = false; + // unselect selected template filament if there is non-template alias compatible + if (template_idx == m_idx_selected && (unselect_if_incompatible != PresetSelectCompatibleType::Never)) + m_idx_selected = size_t(-1); + break; + } + } + } + } + } + + return m_idx_selected; +} + namespace PresetUtils { const VendorProfile::PrinterModel* system_printer_model(const Preset &preset) diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index 9a16a16a99..a0d8018a1c 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -341,7 +341,8 @@ public: // Save the preset under a new name. If the name is different from the old one, // a new preset is stored into the list of presets. // All presets are marked as not modified and the new preset is activated. - void save_current_preset(const std::string &new_name, bool detach = false); + // return true, if new preset is stored + bool save_current_preset(const std::string &new_name, bool detach = false); // Find the preset with a new_name or create a new one, // initialize it with the initial_preset config. @@ -507,7 +508,7 @@ public: // Generate a file path from a profile name. Add the ".ini" suffix if it is missing. std::string path_from_name(const std::string &new_name) const; - size_t num_default_presets() { return m_num_default_presets; } + size_t num_default_presets() const { return m_num_default_presets; } protected: PresetCollection() = default; @@ -566,6 +567,8 @@ public: static bool is_dirty(const Preset *edited, const Preset *reference); static std::vector dirty_options(const Preset *edited, const Preset *reference, const bool deep_compare = false); static bool is_independent_from_extruder_number_option(const std::string& opt_key); + + const std::vector>& map_alias_to_profile_name() { return m_map_alias_to_profile_name; } private: // Type of this PresetCollection: TYPE_PRINT, TYPE_FILAMENT or TYPE_PRINTER. Preset::Type m_type; @@ -827,6 +830,142 @@ private: }; +// --------------------------------- +// *** ExtruderFilaments *** +// --------------------------------- + +class Filament +{ +public: + Filament(const Preset* preset) : preset(preset) {} + // Sort lexicographically by a preset name. The preset name shall be unique across a single PresetCollection. + bool operator<(const Filament& other) const { return this->preset->name < other.preset->name; } + + const Preset* preset; + bool is_compatible{ true }; +}; + +// Collections of filaments for extruder +class ExtruderFilaments +{ + PresetCollection* m_filaments{ nullptr }; + // Selected filament. + size_t m_idx_selected{ size_t(-1) }; + // List of filaments for this extruder + std::deque m_extr_filaments; + + size_t m_extruder_id; + + std::string m_cached_selected_name{ std::string() }; + +public: + ExtruderFilaments(PresetCollection* filaments_collection, size_t extruder_id = 0, std::string selected_name = std::string()); + + typedef std::deque::iterator Iterator; + typedef std::deque::const_iterator ConstIterator; + Iterator begin() { return m_extr_filaments.begin(); } + ConstIterator begin() const { return m_extr_filaments.cbegin(); } + ConstIterator cbegin() const { return m_extr_filaments.cbegin(); } + Iterator end() { return m_extr_filaments.end(); } + ConstIterator end() const { return m_extr_filaments.cend(); } + ConstIterator cend() const { return m_extr_filaments.cend(); } + + bool empty() const { return m_extr_filaments.empty(); } + + const std::deque& operator()() const { return m_extr_filaments; } + + // Return a filament by an index. If the filament is active, a temporary copy is returned. + Filament& filament(size_t idx) { return m_extr_filaments[idx]; } + const Filament& filament(size_t idx) const { return const_cast(this)->filament(idx); } + + // Select filament by the full filament name, which contains name of filament, separator and name of selected preset + // If full_name doesn't contain name of selected preset, then select first preset in the list for this filament + bool select_filament(const std::string& name, bool force = false); + void select_filament(size_t idx) { m_idx_selected = idx; } + + std::string get_selected_preset_name() const { return m_idx_selected == size_t(-1) ? std::string() : m_extr_filaments[m_idx_selected].preset->name; } + const Preset* get_selected_preset() const { return m_idx_selected == size_t(-1) ? nullptr : m_extr_filaments[m_idx_selected].preset; } + const Filament* get_selected_filament() const { return m_idx_selected == size_t(-1) ? nullptr : &m_extr_filaments[m_idx_selected]; } + size_t get_selected_idx() const { return m_idx_selected; } + + friend class PresetBundle; + + ExtruderFilaments() = default; + ExtruderFilaments& operator=(const ExtruderFilaments& other) = default; + +private: + // Find a preset position in the sorted list of presets. + // The "-- default -- " preset is always the first, so it needs + // to be handled differently. + // If a preset does not exist, an iterator is returned indicating where to insert a preset with the same name. + std::deque::iterator find_filament_internal(const std::string& name) + { + return Slic3r::lower_bound_by_predicate(m_extr_filaments.begin(), m_extr_filaments.end(), [&name](const auto& l) { + return l.preset->name < name; + }); + } + std::deque::const_iterator find_filament_internal(const std::string& name) const + { + return const_cast(this)->find_filament_internal(name); + } + + void cache_selected_name() { m_cached_selected_name = get_selected_preset_name(); } + std::string get_cached_selected_name() const { return m_cached_selected_name; } + + // Return index of the first compatible preset. Certainly at least the '- default -' preset shall be compatible. +// If one of the prefered_alternates is compatible, select it. + template + size_t first_compatible_idx(PreferedCondition prefered_condition) const + { + size_t i = m_filaments->is_default_suppressed() ? m_filaments->num_default_presets() : 0; + size_t n = m_extr_filaments.size(); + size_t i_compatible = n; + int match_quality = -1; + for (; i < n; ++i) + // Since we use the filament selection from Wizard, it's needed to control the preset visibility too + if (m_extr_filaments[i].is_compatible && m_filaments->preset(i).is_visible) { + int this_match_quality = prefered_condition(*(m_extr_filaments[i].preset)); + if (this_match_quality > match_quality) { + if (match_quality == std::numeric_limits::max()) + // Better match will not be found. + return i; + // Store the first compatible profile with highest match quality into i_compatible. + i_compatible = i; + match_quality = this_match_quality; + } + } + return (i_compatible == n) ? + // No compatible preset found, return the default preset. + 0 : + // Compatible preset found. + i_compatible; + } + // Return index of the first compatible preset. Certainly at least the '- default -' preset shall be compatible. + size_t first_compatible_idx() const { return this->first_compatible_idx([](const /*Filament*/Preset&) -> int { return 0; }); } + + template + const Preset* first_compatible(PreferedCondition prefered_condition) { return m_extr_filaments[this->first_compatible_idx(prefered_condition)].preset;} + const Preset* first_compatible() { return m_extr_filaments[this->first_compatible_idx()].preset; } + + const std::string& get_preset_name_by_alias(const std::string& alias) const; + + size_t update_compatible_internal(const PresetWithVendorProfile& active_printer, const PresetWithVendorProfile* active_print, PresetSelectCompatibleType unselect_if_incompatible); + + // For Print / Filament presets, disable those, which are not compatible with the printer. + template + void update_compatible(const PresetWithVendorProfile& active_printer, const PresetWithVendorProfile* active_print, PresetSelectCompatibleType select_other_if_incompatible, PreferedCondition prefered_condition) + { + if (this->update_compatible_internal(active_printer, active_print, select_other_if_incompatible) == (size_t)-1) + // Find some other compatible preset, or the "-- default --" preset. + this->select_filament(this->first_compatible_idx(prefered_condition)); + } + void update_compatible(const PresetWithVendorProfile& active_printer, const PresetWithVendorProfile* active_print, PresetSelectCompatibleType select_other_if_incompatible) + { + this->update_compatible(active_printer, active_print, select_other_if_incompatible, [](const /*Filament*/Preset&) -> int { return 0; }); + } +}; + + } // namespace Slic3r #endif /* slic3r_Preset_hpp_ */ diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 899d24ecc2..730574af13 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -119,7 +119,7 @@ PresetBundle& PresetBundle::operator=(const PresetBundle &rhs) printers = rhs.printers; physical_printers = rhs.physical_printers; - filament_presets = rhs.filament_presets; + extruders_filaments = rhs.extruders_filaments; project_config = rhs.project_config; vendors = rhs.vendors; obsolete_presets = rhs.obsolete_presets; @@ -143,8 +143,7 @@ void PresetBundle::reset(bool delete_files) this->filaments .reset(delete_files); this->sla_materials.reset(delete_files); this->printers .reset(delete_files); - this->filament_presets.clear(); - this->filament_presets.emplace_back(this->filaments.get_selected_preset_name()); + this->extruders_filaments.clear(); this->obsolete_presets.prints.clear(); this->obsolete_presets.sla_prints.clear(); this->obsolete_presets.filaments.clear(); @@ -426,7 +425,26 @@ void PresetBundle::load_installed_printers(const AppConfig &config) preset.set_visible_from_appconfig(config); } -PresetCollection& PresetBundle::get_presets(Preset::Type type) +void PresetBundle::cache_extruder_filaments_names() +{ + for (ExtruderFilaments& extr_filaments : extruders_filaments) + extr_filaments.cache_selected_name(); +} + +void PresetBundle::reset_extruder_filaments() +{ + // save previously cached selected names + std::vector names; + for (const ExtruderFilaments& extr_filaments : extruders_filaments) + names.push_back(extr_filaments.get_cached_selected_name()); + + // Reset extruder_filaments and set names + this->extruders_filaments.clear(); + for (size_t id = 0; id < names.size(); ++id) + this->extruders_filaments.emplace_back(ExtruderFilaments(&filaments, id, names[id])); +} + +PresetCollection&PresetBundle::get_presets(Preset::Type type) { assert(type >= Preset::TYPE_PRINT && type <= Preset::TYPE_PRINTER); @@ -437,12 +455,15 @@ PresetCollection& PresetBundle::get_presets(Preset::Type type) } -const std::string& PresetBundle::get_preset_name_by_alias( const Preset::Type& preset_type, const std::string& alias) +const std::string& PresetBundle::get_preset_name_by_alias( const Preset::Type& preset_type, const std::string& alias, int extruder_id /*= -1*/) { // there are not aliases for Printers profiles if (preset_type == Preset::TYPE_PRINTER || preset_type == Preset::TYPE_INVALID) return alias; + if (preset_type == Preset::TYPE_FILAMENT) + return extruders_filaments[extruder_id].get_preset_name_by_alias(alias); + const PresetCollection& presets = get_presets(preset_type); return presets.get_preset_name_by_alias(alias); @@ -462,8 +483,11 @@ void PresetBundle::save_changes_for_preset(const std::string& new_name, Preset:: if (type == Preset::TYPE_PRINTER) copy_bed_model_and_texture_if_needed(presets.get_edited_preset().config); + if (type == Preset::TYPE_FILAMENT) + cache_extruder_filaments_names(); // Save the preset into Slic3r::data_dir / presets / section_name / preset_name.ini - presets.save_current_preset(new_name); + if (presets.save_current_preset(new_name) && type == Preset::TYPE_FILAMENT) + reset_extruder_filaments(); // Mark the print & filament enabled if they are compatible with the currently selected preset. // If saving the preset changes compatibility with other presets, keep the now incompatible dependent presets selected, however with a "red flag" icon showing that they are no more compatible. update_compatible(PresetSelectCompatibleType::Never); @@ -602,35 +626,37 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p // Load it even if the current printer technology is SLA. // The possibly excessive filament names will be later removed with this->update_multi_material_filament_presets() // once the FFF technology gets selected. - this->filament_presets = { filaments.get_selected_preset_name() }; + this->extruders_filaments.clear(); + this->extruders_filaments.emplace_back(ExtruderFilaments(&filaments)); for (unsigned int i = 1; i < 1000; ++ i) { char name[64]; sprintf(name, "filament_%u", i); if (! config.has("presets", name)) break; - this->filament_presets.emplace_back(remove_ini_suffix(config.get("presets", name))); + this->extruders_filaments.emplace_back(ExtruderFilaments(&filaments, i, remove_ini_suffix(config.get("presets", name)))); } + // ! update MM filaments presets before update compatibility + this->update_multi_material_filament_presets(); // Update visibility of presets based on their compatibility with the active printer. // Always try to select a compatible print and filament preset to the current printer preset, // as the application may have been closed with an active "external" preset, which does not // exist. this->update_compatible(PresetSelectCompatibleType::Always); - this->update_multi_material_filament_presets(); if (initial_printer != nullptr && (preferred_printer == nullptr || initial_printer == preferred_printer)) { // Don't run the following code, as we want to activate default filament / SLA material profiles when installing and selecting a new printer. // Only run this code if just a filament / SLA material was installed by Config Wizard for an active Printer. auto printer_technology = printers.get_selected_preset().printer_technology(); if (printer_technology == ptFFF && ! preferred_selection.filament.empty()) { - std::string preferred_preset_name = get_preset_name_by_alias(Preset::Type::TYPE_FILAMENT, preferred_selection.filament); + const std::string& preferred_preset_name = get_preset_name_by_alias(Preset::Type::TYPE_FILAMENT, preferred_selection.filament, 0); if (auto it = filaments.find_preset_internal(preferred_preset_name); it != filaments.end() && it->is_visible && it->is_compatible) { filaments.select_preset_by_name_strict(preferred_preset_name); - this->filament_presets.front() = filaments.get_selected_preset_name(); + this->extruders_filaments.front().select_filament(filaments.get_selected_preset_name()); } } else if (printer_technology == ptSLA && ! preferred_selection.sla_material.empty()) { - std::string preferred_preset_name = get_preset_name_by_alias(Preset::Type::TYPE_SLA_MATERIAL, preferred_selection.sla_material); + const std::string& preferred_preset_name = get_preset_name_by_alias(Preset::Type::TYPE_SLA_MATERIAL, preferred_selection.sla_material); if (auto it = sla_materials.find_preset_internal(preferred_preset_name); it != sla_materials.end() && it->is_visible && it->is_compatible) sla_materials.select_preset_by_name_strict(preferred_preset_name); @@ -648,15 +674,15 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p // Export selections (current print, current filaments, current printer) into config.ini void PresetBundle::export_selections(AppConfig &config) { - assert(this->printers.get_edited_preset().printer_technology() != ptFFF || filament_presets.size() >= 1); - assert(this->printers.get_edited_preset().printer_technology() != ptFFF || filament_presets.size() > 1 || filaments.get_selected_preset_name() == filament_presets.front()); + assert(this->printers.get_edited_preset().printer_technology() != ptFFF || extruders_filaments.size() >= 1); + assert(this->printers.get_edited_preset().printer_technology() != ptFFF || extruders_filaments.size() > 1 || filaments.get_selected_preset().alias == extruders_filaments.front().get_selected_preset()->alias); config.clear_section("presets"); config.set("presets", "print", prints.get_selected_preset_name()); - config.set("presets", "filament", filament_presets.front()); - for (unsigned i = 1; i < filament_presets.size(); ++i) { + config.set("presets", "filament", extruders_filaments.front().get_selected_preset_name()); + for (unsigned i = 1; i < extruders_filaments.size(); ++i) { char name[64]; sprintf(name, "filament_%u", i); - config.set("presets", name, filament_presets[i]); + config.set("presets", name, extruders_filaments[i].get_selected_preset_name()); } config.set("presets", "sla_print", sla_prints.get_selected_preset_name()); @@ -711,8 +737,8 @@ DynamicPrintConfig PresetBundle::full_fff_config() const // First collect the filament configurations based on the user selection of this->filament_presets. // Here this->filaments.find_preset() and this->filaments.first_visible() return the edited copy of the preset if active. std::vector filament_configs; - for (const std::string &filament_preset_name : this->filament_presets) - filament_configs.emplace_back(&this->filaments.find_preset(filament_preset_name, true)->config); + for (const auto& extr_filaments : this->extruders_filaments) + filament_configs.emplace_back(&this->filaments.find_preset(extr_filaments.get_selected_preset_name(), true)->config); while (filament_configs.size() < num_extruders) filament_configs.emplace_back(&this->filaments.first_visible().config); for (const DynamicPrintConfig *cfg : filament_configs) { @@ -763,7 +789,10 @@ DynamicPrintConfig PresetBundle::full_fff_config() const } out.option("print_settings_id", true)->value = this->prints.get_selected_preset_name(); - out.option("filament_settings_id", true)->values = this->filament_presets; + auto& filament_settings_id = out.option("filament_settings_id", true)->values; + filament_settings_id.clear(); + for (const auto& extr_filaments : this->extruders_filaments) + filament_settings_id.emplace_back(extr_filaments.get_selected_preset_name()); out.option("printer_settings_id", true)->value = this->printers.get_selected_preset_name(); out.option("physical_printer_settings_id", true)->value = this->physical_printers.get_selected_printer_name(); @@ -981,6 +1010,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool auto old_filament_profile_names = config.option("filament_settings_id", true); old_filament_profile_names->values.resize(num_extruders, std::string()); + this->extruders_filaments.clear(); if (num_extruders <= 1) { // Split the "compatible_printers_condition" and "inherits" from the cummulative vectors to separate filament presets. inherits = inherits_values[1]; @@ -994,8 +1024,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool loaded= &this->filaments.load_preset(this->filaments.path_from_name(name), name, config); loaded->save(); } - this->filament_presets.clear(); - this->filament_presets.emplace_back(loaded->name); + this->extruders_filaments.emplace_back(ExtruderFilaments(&filaments)); } else { assert(is_external); // Split the filament presets, load each of them separately. @@ -1014,7 +1043,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool } } // Load the configs into this->filaments and make them active. - this->filament_presets = std::vector(configs.size()); + std::vector extr_names = std::vector(configs.size()); // To avoid incorrect selection of the first filament preset (means a value of Preset->m_idx_selected) // in a case when next added preset take a place of previosly selected preset, // we should add presets from last to first @@ -1035,8 +1064,11 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool PresetCollection::LoadAndSelect::Never : PresetCollection::LoadAndSelect::OnlyIfModified); any_modified |= modified; - this->filament_presets[i] = loaded->name; + extr_names[i] = loaded->name; } + // create extruders_filaments only when all filaments are loaded + for (size_t id = 0; id < extr_names.size(); ++id) + this->extruders_filaments.emplace_back(ExtruderFilaments(&filaments, id, extr_names[id])); } // 4) Load the project config values (the per extruder wipe matrix etc). @@ -1137,9 +1169,11 @@ ConfigSubstitutions PresetBundle::load_config_file_config_bundle( load_one(this->filaments, tmp_bundle.filaments, tmp_bundle.filaments .get_selected_preset_name(), true); load_one(this->sla_materials, tmp_bundle.sla_materials, tmp_bundle.sla_materials.get_selected_preset_name(), true); load_one(this->printers, tmp_bundle.printers, tmp_bundle.printers .get_selected_preset_name(), true); + + this->extruders_filaments.clear(); this->update_multi_material_filament_presets(); - for (size_t i = 1; i < std::min(tmp_bundle.filament_presets.size(), this->filament_presets.size()); ++ i) - this->filament_presets[i] = load_one(this->filaments, tmp_bundle.filaments, tmp_bundle.filament_presets[i], false); + for (size_t i = 1; i < std::min(tmp_bundle.extruders_filaments.size(), this->extruders_filaments.size()); ++i) + this->extruders_filaments[i].select_filament(load_one(this->filaments, tmp_bundle.filaments, tmp_bundle.extruders_filaments[i].get_selected_preset_name(), false)); this->update_compatible(PresetSelectCompatibleType::Never); @@ -1622,9 +1656,12 @@ std::pair PresetBundle::load_configbundle( // Activate the first filament preset. if (! active_filaments.empty() && ! active_filaments.front().empty()) filaments.select_preset_by_name(active_filaments.front(), true); + + // Extruder_filaments have to be recreated with new loaded filaments + this->extruders_filaments.clear(); this->update_multi_material_filament_presets(); - for (size_t i = 0; i < std::min(this->filament_presets.size(), active_filaments.size()); ++ i) - this->filament_presets[i] = filaments.find_preset(active_filaments[i], true)->name; + for (size_t i = 0; i < std::min(this->extruders_filaments.size(), active_filaments.size()); ++ i) + this->extruders_filaments[i].select_filament(filaments.find_preset(active_filaments[i], true)->name); this->update_compatible(PresetSelectCompatibleType::Never); } @@ -1640,10 +1677,15 @@ void PresetBundle::update_multi_material_filament_presets() auto *nozzle_diameter = static_cast(printers.get_edited_preset().config.option("nozzle_diameter")); size_t num_extruders = nozzle_diameter->values.size(); // Verify validity of the current filament presets. - for (size_t i = 0; i < std::min(this->filament_presets.size(), num_extruders); ++ i) - this->filament_presets[i] = this->filaments.find_preset(this->filament_presets[i], true)->name; - // Append the rest of filament presets. - this->filament_presets.resize(num_extruders, this->filament_presets.empty() ? this->filaments.first_visible().name : this->filament_presets.back()); + for (size_t i = 0; i < std::min(this->extruders_filaments.size(), num_extruders); ++i) + this->extruders_filaments[i].select_filament(this->filaments.find_preset(this->extruders_filaments[i].get_selected_preset_name(), true)->name); + + if (this->extruders_filaments.size() > num_extruders) + this->extruders_filaments.resize(num_extruders); + else + // Append the rest of filament presets. + for (size_t id = extruders_filaments.size(); id < num_extruders; id++) + extruders_filaments.emplace_back(ExtruderFilaments(&filaments, id, id == 0 ? filaments.first_visible().name : extruders_filaments[id - 1].get_selected_preset_name())); // Now verify if wiping_volumes_matrix has proper size (it is used to deduce number of extruders in wipe tower generator): std::vector old_matrix = this->project_config.option("wiping_volumes_matrix")->values; @@ -1673,6 +1715,99 @@ void PresetBundle::update_multi_material_filament_presets() } } +void PresetBundle::update_filaments_compatible(PresetSelectCompatibleType select_other_filament_if_incompatible, int extruder_idx/* = -1*/) +{ + const Preset& printer_preset = this->printers.get_edited_preset(); + const PresetWithVendorProfile printer_preset_with_vendor_profile = this->printers.get_preset_with_vendor_profile(printer_preset); + const PresetWithVendorProfile print_preset_with_vendor_profile = this->prints.get_edited_preset_with_vendor_profile(); + const std::vector& prefered_filament_profiles = printer_preset.config.option("default_filament_profile")->values; + + class PreferedFilamentsProfileMatch + { + public: + PreferedFilamentsProfileMatch(const Preset* preset, const std::vector& prefered_names, int extruder_id = 0) : + m_extruder_id(extruder_id), + m_prefered_alias(preset ? preset->alias : std::string()), + m_prefered_filament_type(preset ? preset->config.opt_string("filament_type", extruder_id) : std::string()), + m_prefered_names(prefered_names) {} + + int operator()(const Preset& preset) const + { + // Don't match any properties of the "-- default --" profile or the external profiles when switching printer profile. + if (preset.is_default || preset.is_external) + return 0; + if (!m_prefered_alias.empty() && m_prefered_alias == preset.alias) + // Matching an alias, always take this preset with priority. + return std::numeric_limits::max(); + int match_quality = (std::find(m_prefered_names.begin(), m_prefered_names.end(), preset.name) != m_prefered_names.end()) + 1; + if (!m_prefered_filament_type.empty() && m_prefered_filament_type == preset.config.opt_string("filament_type", m_extruder_id)) + match_quality *= 10; + return match_quality; + } + + private: + int m_extruder_id; + const std::string m_prefered_alias; + const std::string m_prefered_filament_type; + const std::vector& m_prefered_names; + }; + + //! ysFIXME - delete after testing + //!// First select a first compatible profile for the preset editor. + //!this->filaments.update_compatible(printer_preset_with_vendor_profile, &print_preset_with_vendor_profile, select_other_filament_if_incompatible, + //! PreferedFilamentsProfileMatch(this->filaments.get_selected_idx() == size_t(-1) ? nullptr : &this->filaments.get_edited_preset(), prefered_filament_profiles)); + + // Update compatible for extruder filaments + + auto update_filament_compatible = [this, select_other_filament_if_incompatible, printer_preset_with_vendor_profile, print_preset_with_vendor_profile, prefered_filament_profiles](int idx) + { + ExtruderFilaments& extr_filaments = extruders_filaments[idx]; + + // Remember whether the filament profiles were compatible before updating the filament compatibility. + bool filament_preset_was_compatible = false; + const Filament* filament_old = extr_filaments.get_selected_filament(); + if (select_other_filament_if_incompatible != PresetSelectCompatibleType::Never) + filament_preset_was_compatible = filament_old && filament_old->is_compatible; + + extr_filaments.update_compatible(printer_preset_with_vendor_profile, &print_preset_with_vendor_profile, select_other_filament_if_incompatible, + PreferedFilamentsProfileMatch(filament_old ? filament_old->preset : nullptr, prefered_filament_profiles, idx)); + + const Filament* filament = extr_filaments.get_selected_filament(); + const bool is_compatible = filament && filament->is_compatible; + + if (is_compatible || select_other_filament_if_incompatible == PresetSelectCompatibleType::Never) + return; + + // Verify validity of the current filament presets. + if (this->extruders_filaments.size() == 1) { + // The compatible profile should have been already selected for the preset editor. Just use it. + if (select_other_filament_if_incompatible == PresetSelectCompatibleType::Always || filament_preset_was_compatible) + extr_filaments.select_filament(this->filaments.get_edited_preset().name); + } + else { + const std::string filament_name = extr_filaments.get_selected_preset_name(); + if (!filament || (!is_compatible && (select_other_filament_if_incompatible == PresetSelectCompatibleType::Always || filament_preset_was_compatible))) { + // Pick a compatible profile. If there are prefered_filament_profiles, use them. + std::string compat_filament_name = extr_filaments.first_compatible(PreferedFilamentsProfileMatch(filament->preset, prefered_filament_profiles, idx))->name; + if (filament_name != compat_filament_name) + extr_filaments.select_filament(compat_filament_name); + } + } + }; + + if (extruder_idx < 0) { + // update compatibility for all extruders + const size_t num_extruders = static_cast(printer_preset.config.option("nozzle_diameter"))->values.size(); + for (size_t idx = 0; idx < std::min(this->extruders_filaments.size(), num_extruders); idx++) + update_filament_compatible(idx); + } + else + update_filament_compatible(extruder_idx); + + if (this->filaments.get_idx_selected() == size_t(-1)) + this->filaments.select_preset(extruders_filaments[0].get_selected_idx()); +} + void PresetBundle::update_compatible(PresetSelectCompatibleType select_other_print_if_incompatible, PresetSelectCompatibleType select_other_filament_if_incompatible) { const Preset &printer_preset = this->printers.get_edited_preset(); @@ -1727,99 +1862,18 @@ void PresetBundle::update_compatible(PresetSelectCompatibleType select_other_pri const double m_prefered_layer_height; }; - // Matching by the layer height in addition. - class PreferedFilamentProfileMatch : public PreferedProfileMatch - { - public: - PreferedFilamentProfileMatch(const Preset *preset, const std::string &prefered_name) : - PreferedProfileMatch(preset ? preset->alias : std::string(), prefered_name), - m_prefered_filament_type(preset ? preset->config.opt_string("filament_type", 0) : std::string()) {} - - int operator()(const Preset &preset) const - { - // Don't match any properties of the "-- default --" profile or the external profiles when switching printer profile. - if (preset.is_default || preset.is_external) - return 0; - int match_quality = PreferedProfileMatch::operator()(preset); - if (match_quality < std::numeric_limits::max()) { - match_quality += 1; - if (! m_prefered_filament_type.empty() && m_prefered_filament_type == preset.config.opt_string("filament_type", 0)) - match_quality *= 10; - } - return match_quality; - } - - private: - const std::string m_prefered_filament_type; - }; - - // Matching by the layer height in addition. - class PreferedFilamentsProfileMatch - { - public: - PreferedFilamentsProfileMatch(const Preset *preset, const std::vector &prefered_names) : - m_prefered_alias(preset ? preset->alias : std::string()), - m_prefered_filament_type(preset ? preset->config.opt_string("filament_type", 0) : std::string()), - m_prefered_names(prefered_names) - {} - - int operator()(const Preset &preset) const - { - // Don't match any properties of the "-- default --" profile or the external profiles when switching printer profile. - if (preset.is_default || preset.is_external) - return 0; - if (! m_prefered_alias.empty() && m_prefered_alias == preset.alias) - // Matching an alias, always take this preset with priority. - return std::numeric_limits::max(); - int match_quality = (std::find(m_prefered_names.begin(), m_prefered_names.end(), preset.name) != m_prefered_names.end()) + 1; - if (! m_prefered_filament_type.empty() && m_prefered_filament_type == preset.config.opt_string("filament_type", 0)) - match_quality *= 10; - return match_quality; - } - - private: - const std::string m_prefered_alias; - const std::string m_prefered_filament_type; - const std::vector &m_prefered_names; - }; - switch (printer_preset.printer_technology()) { case ptFFF: { assert(printer_preset.config.has("default_print_profile")); assert(printer_preset.config.has("default_filament_profile")); - const std::vector &prefered_filament_profiles = printer_preset.config.option("default_filament_profile")->values; + this->prints.update_compatible(printer_preset_with_vendor_profile, nullptr, select_other_print_if_incompatible, PreferedPrintProfileMatch(this->prints.get_selected_idx() == size_t(-1) ? nullptr : &this->prints.get_edited_preset(), printer_preset.config.opt_string("default_print_profile"))); - const PresetWithVendorProfile print_preset_with_vendor_profile = this->prints.get_edited_preset_with_vendor_profile(); - // Remember whether the filament profiles were compatible before updating the filament compatibility. - std::vector filament_preset_was_compatible(this->filament_presets.size(), false); - for (size_t idx = 0; idx < this->filament_presets.size(); ++ idx) { - Preset *preset = this->filaments.find_preset(this->filament_presets[idx], false); - filament_preset_was_compatible[idx] = preset != nullptr && preset->is_compatible; - } - // First select a first compatible profile for the preset editor. - this->filaments.update_compatible(printer_preset_with_vendor_profile, &print_preset_with_vendor_profile, select_other_filament_if_incompatible, - PreferedFilamentsProfileMatch(this->filaments.get_selected_idx() == size_t(-1) ? nullptr : &this->filaments.get_edited_preset(), prefered_filament_profiles)); - if (select_other_filament_if_incompatible != PresetSelectCompatibleType::Never) { - // Verify validity of the current filament presets. - const std::string prefered_filament_profile = prefered_filament_profiles.empty() ? std::string() : prefered_filament_profiles.front(); - if (this->filament_presets.size() == 1) { - // The compatible profile should have been already selected for the preset editor. Just use it. - if (select_other_filament_if_incompatible == PresetSelectCompatibleType::Always || filament_preset_was_compatible.front()) - this->filament_presets.front() = this->filaments.get_edited_preset().name; - } else { - for (size_t idx = 0; idx < this->filament_presets.size(); ++ idx) { - std::string &filament_name = this->filament_presets[idx]; - Preset *preset = this->filaments.find_preset(filament_name, false); - if (preset == nullptr || (! preset->is_compatible && (select_other_filament_if_incompatible == PresetSelectCompatibleType::Always || filament_preset_was_compatible[idx]))) - // Pick a compatible profile. If there are prefered_filament_profiles, use them. - filament_name = this->filaments.first_compatible( - PreferedFilamentProfileMatch(preset, - (idx < prefered_filament_profiles.size()) ? prefered_filament_profiles[idx] : prefered_filament_profile)).name; - } - } - } + + // Update compatibility for all currently existent extruder_filaments. + update_filaments_compatible(select_other_filament_if_incompatible); + break; } case ptSLA: @@ -1875,13 +1929,13 @@ void PresetBundle::export_configbundle(const std::string &path, bool export_syst c << "sla_print = " << this->sla_prints.get_selected_preset_name() << std::endl; c << "sla_material = " << this->sla_materials.get_selected_preset_name() << std::endl; c << "printer = " << this->printers.get_selected_preset_name() << std::endl; - for (size_t i = 0; i < this->filament_presets.size(); ++ i) { + for (size_t i = 0; i < this->extruders_filaments.size(); ++ i) { char suffix[64]; if (i > 0) sprintf(suffix, "_%d", (int)i); else suffix[0] = 0; - c << "filament" << suffix << " = " << this->filament_presets[i] << std::endl; + c << "filament" << suffix << " = " << this->extruders_filaments[i].get_selected_preset_name() << std::endl; } if (export_physical_printers && this->physical_printers.get_selected_idx() >= 0) @@ -1901,9 +1955,11 @@ void PresetBundle::export_configbundle(const std::string &path, bool export_syst // an optional "(modified)" suffix will be removed from the filament name. void PresetBundle::set_filament_preset(size_t idx, const std::string &name) { - if (idx >= filament_presets.size()) - filament_presets.resize(idx + 1, filaments.default_preset().name); - filament_presets[idx] = Preset::remove_suffix_modified(name); + if (idx >= extruders_filaments.size()) { + for (size_t id = extruders_filaments.size(); id < idx; id++) + extruders_filaments.emplace_back(ExtruderFilaments(&filaments, id, filaments.default_preset().name)); + } + extruders_filaments[idx].select_filament(Preset::remove_suffix_modified(name)); } void PresetBundle::set_default_suppressed(bool default_suppressed) diff --git a/src/libslic3r/PresetBundle.hpp b/src/libslic3r/PresetBundle.hpp index 7d5a3a4f69..3da6699f9c 100644 --- a/src/libslic3r/PresetBundle.hpp +++ b/src/libslic3r/PresetBundle.hpp @@ -51,9 +51,12 @@ public: const PresetCollection& materials(PrinterTechnology pt) const { return pt == ptFFF ? this->filaments : this->sla_materials; } PrinterPresetCollection printers; PhysicalPrinterCollection physical_printers; - // Filament preset names for a multi-extruder or multi-material print. - // extruders.size() should be the same as printers.get_edited_preset().config.nozzle_diameter.size() - std::vector filament_presets; + + // Filament presets per extruder for a multi-extruder or multi-material print. + // extruders_filaments.size() should be the same as printers.get_edited_preset().config.nozzle_diameter.size() + std::vector extruders_filaments; + void cache_extruder_filaments_names(); + void reset_extruder_filaments(); PresetCollection& get_presets(Preset::Type preset_type); @@ -132,6 +135,8 @@ public: // update size and content of filament_presets. void update_multi_material_filament_presets(); + void update_filaments_compatible(PresetSelectCompatibleType select_other_filament_if_incompatible, int extruder_idx = -1); + // Update the is_compatible flag of all print and filament presets depending on whether they are marked // as compatible with the currently selected printer (and print in case of filament presets). // Also updates the is_visible flag of each preset. @@ -145,7 +150,7 @@ public: // If the "vendor" section is missing, enable all models and variants of the particular vendor. void load_installed_printers(const AppConfig &config); - const std::string& get_preset_name_by_alias(const Preset::Type& preset_type, const std::string& alias); + const std::string& get_preset_name_by_alias(const Preset::Type& preset_type, const std::string& alias, int extruder_id = -1); // Save current preset of a provided type under a new name. If the name is different from the old one, // Unselected option would be reverted to the beginning values diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index b657616f0b..061f39622c 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1068,12 +1068,6 @@ void Sidebar::update_presets(Preset::Type preset_type) dynamic_cast(preset_bundle.printers.get_edited_preset().config.option("nozzle_diameter"))->values.size(); const size_t filament_cnt = p->combos_filament.size() > extruder_cnt ? extruder_cnt : p->combos_filament.size(); - if (filament_cnt == 1) { - // Single filament printer, synchronize the filament presets. - const std::string &name = preset_bundle.filaments.get_selected_preset_name(); - preset_bundle.set_filament_preset(0, name); - } - for (size_t i = 0; i < filament_cnt; i++) p->combos_filament[i]->update(); @@ -1417,14 +1411,14 @@ void Sidebar::update_sliced_info_sizer() new_label = _L("Used Filament (g)"); info_text = wxString::Format("%.2f", ps.total_weight); - const std::vector& filament_presets = wxGetApp().preset_bundle->filament_presets; + const auto& extruders_filaments = wxGetApp().preset_bundle->extruders_filaments; const PresetCollection& filaments = wxGetApp().preset_bundle->filaments; if (ps.filament_stats.size() > 1) new_label += ":"; for (auto filament : ps.filament_stats) { - const Preset* filament_preset = filaments.find_preset(filament_presets[filament.first], false); + const Preset* filament_preset = filaments.find_preset(extruders_filaments[filament.first].get_selected_preset_name(), false); if (filament_preset) { double filament_weight; if (ps.filament_stats.size() == 1) @@ -2405,8 +2399,8 @@ void Plater::check_selected_presets_visibility(PrinterTechnology loaded_printer_ PresetBundle* preset_bundle = wxGetApp().preset_bundle; if (loaded_printer_technology == ptFFF) { update_selected_preset_visibility(preset_bundle->prints, names); - for (const std::string& filament : preset_bundle->filament_presets) { - Preset* preset = preset_bundle->filaments.find_preset(filament); + for (const auto& extruder_filaments : preset_bundle->extruders_filaments) { + Preset* preset = preset_bundle->filaments.find_preset(extruder_filaments.get_selected_preset_name()); if (preset && !preset->is_visible) { preset->is_visible = true; names.emplace_back(preset->name); @@ -4018,19 +4012,25 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt) //! combo->GetStringSelection().ToUTF8().data()); std::string preset_name = wxGetApp().preset_bundle->get_preset_name_by_alias(preset_type, - Preset::remove_suffix_modified(combo->GetString(selection).ToUTF8().data())); - - if (preset_type == Preset::TYPE_FILAMENT) { - wxGetApp().preset_bundle->set_filament_preset(idx, preset_name); - } + Preset::remove_suffix_modified(into_u8(combo->GetString(selection))), idx); std::string last_selected_ph_printer_name = combo->get_selected_ph_printer_name(); bool select_preset = !combo->selection_is_changed_according_to_physical_printers(); // TODO: ? - if (preset_type == Preset::TYPE_FILAMENT && sidebar->is_multifilament()) { - // Only update the plater UI for the 2nd and other filaments. - combo->update(); + if (preset_type == Preset::TYPE_FILAMENT) { + wxGetApp().preset_bundle->set_filament_preset(idx, preset_name); + + TabFilament* tab = dynamic_cast(wxGetApp().get_tab(Preset::TYPE_FILAMENT)); + if (tab && combo->get_extruder_idx() == tab->get_active_extruder() && !tab->select_preset(preset_name)) { + // revert previously selection + const std::string& old_name = wxGetApp().preset_bundle->filaments.get_edited_preset().name; + wxGetApp().preset_bundle->set_filament_preset(idx, old_name); + combo->update(); + } + else + // Synchronize config.ini with the current selections. + wxGetApp().preset_bundle->export_selections(*wxGetApp().app_config); } else if (select_preset) { wxWindowUpdateLocker noUpdates(sidebar->presets_panel()); @@ -6886,6 +6886,8 @@ void Plater::on_extruders_change(size_t num_extruders) if (num_extruders == choices.size()) return; + dynamic_cast(wxGetApp().get_tab(Preset::TYPE_FILAMENT))->update_extruder_combobox(); + wxWindowUpdateLocker noUpdates_scrolled_panel(&sidebar()/*.scrolled_panel()*/); size_t i = choices.size(); @@ -6912,16 +6914,16 @@ bool Plater::update_filament_colors_in_full_config() // There is a case, when we use filament_color instead of extruder_color (when extruder_color == ""). // Thus plater config option "filament_colour" should be filled with filament_presets values. // Otherwise, on 3dScene will be used last edited filament color for all volumes with extruder_color == "". - const std::vector filament_presets = wxGetApp().preset_bundle->filament_presets; - if (filament_presets.size() == 1 || !p->config->has("filament_colour")) + const auto& extruders_filaments = wxGetApp().preset_bundle->extruders_filaments; + if (extruders_filaments.size() == 1 || !p->config->has("filament_colour")) return false; const PresetCollection& filaments = wxGetApp().preset_bundle->filaments; std::vector filament_colors; - filament_colors.reserve(filament_presets.size()); + filament_colors.reserve(extruders_filaments.size()); - for (const std::string& filament_preset : filament_presets) - filament_colors.push_back(filaments.find_preset(filament_preset, true)->config.opt_string("filament_colour", (unsigned)0)); + for (const auto& extr_filaments : extruders_filaments) + filament_colors.push_back(filaments.find_preset(extr_filaments.get_selected_preset_name(), true)->config.opt_string("filament_colour", (unsigned)0)); p->config->option("filament_colour")->values = filament_colors; return true; @@ -7016,16 +7018,17 @@ void Plater::force_filament_colors_update() { bool update_scheduled = false; DynamicPrintConfig* config = p->config; - const std::vector filament_presets = wxGetApp().preset_bundle->filament_presets; - if (filament_presets.size() > 1 && - p->config->option("filament_colour")->values.size() == filament_presets.size()) + + const auto& extruders_filaments = wxGetApp().preset_bundle->extruders_filaments; + if (extruders_filaments.size() > 1 && + p->config->option("filament_colour")->values.size() == extruders_filaments.size()) { const PresetCollection& filaments = wxGetApp().preset_bundle->filaments; std::vector filament_colors; - filament_colors.reserve(filament_presets.size()); + filament_colors.reserve(extruders_filaments.size()); - for (const std::string& filament_preset : filament_presets) - filament_colors.push_back(filaments.find_preset(filament_preset, true)->config.opt_string("filament_colour", (unsigned)0)); + for (const auto& extr_filaments : extruders_filaments) + filament_colors.push_back(extr_filaments.get_selected_preset()->config.opt_string("filament_colour", (unsigned)0)); if (config->option("filament_colour")->values != filament_colors) { config->option("filament_colour")->values = filament_colors; @@ -7042,6 +7045,20 @@ void Plater::force_filament_colors_update() this->p->schedule_background_process(); } +void Plater::force_filament_cb_update() +{ + // Update visibility for templates presets according to app_config + PresetCollection& filaments = wxGetApp().preset_bundle->filaments; + AppConfig& config = *wxGetApp().app_config; + for (Preset& preset : filaments) + preset.set_visible_from_appconfig(config); + wxGetApp().preset_bundle->update_compatible(PresetSelectCompatibleType::Never, PresetSelectCompatibleType::OnlyIfWasCompatible); + + // Update preset comboboxes on sidebar and filaments tab + p->sidebar->update_presets(Preset::TYPE_FILAMENT); + wxGetApp().get_tab(Preset::TYPE_FILAMENT)->select_preset(wxGetApp().preset_bundle->filaments.get_selected_preset_name()); +} + void Plater::force_print_bed_update() { // Fill in the printer model key with something which cannot possibly be valid, so that Plater::on_config_change() will update the print bed diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index a76ef6f1cb..58a840a4b5 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -308,6 +308,7 @@ public: bool update_filament_colors_in_full_config(); void on_config_change(const DynamicPrintConfig &config); void force_filament_colors_update(); + void force_filament_cb_update(); void force_print_bed_update(); // On activating the parent window. void on_activate(); diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index bf1169d826..d8ce6bd4e7 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -694,8 +694,6 @@ void PreferencesDialog::accept(wxEvent&) #endif // __linux__ } - bool update_filament_sidebar = (m_values.find("no_templates") != m_values.end()); - std::vector options_to_recreate_GUI = { "no_defaults", "tabs_as_menu", "sys_menu_enabled", "font_size" }; for (const std::string& option : options_to_recreate_GUI) { @@ -761,12 +759,12 @@ void PreferencesDialog::accept(wxEvent&) wxGetApp().force_menu_update(); #endif //_MSW_DARK_MODE #endif // _WIN32 - + + if (m_values.find("no_templates") != m_values.end()) + wxGetApp().plater()->force_filament_cb_update(); + wxGetApp().update_ui_from_settings(); clear_cache(); - - if (update_filament_sidebar) - wxGetApp().plater()->sidebar().update_presets(Preset::Type::TYPE_FILAMENT); } void PreferencesDialog::revert(wxEvent&) diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 4d703bd08a..f5496833ad 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -585,10 +585,10 @@ PlaterPresetComboBox::PlaterPresetComboBox(wxWindow *parent, Preset::Type preset if (m_type == Preset::TYPE_FILAMENT) { Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &event) { - const Preset* selected_preset = m_collection->find_preset(m_preset_bundle->filament_presets[m_extruder_idx]); + const Filament* selected_filament = m_preset_bundle->extruders_filaments[m_extruder_idx].get_selected_filament(); // Wide icons are shown if the currently selected preset is not compatible with the current printer, // and red flag is drown in front of the selected preset. - bool wide_icons = selected_preset && !selected_preset->is_compatible; + const bool wide_icons = selected_filament && !selected_filament->is_compatible; float scale = m_em_unit*0.1f; int shifl_Left = wide_icons ? int(scale * 16 + 0.5) : 0; @@ -686,22 +686,16 @@ void PlaterPresetComboBox::switch_to_tab() if (int page_id = wxGetApp().tab_panel()->FindPage(tab); page_id != wxNOT_FOUND) { + //In a case of a multi-material printing, for editing another Filament Preset + //it's needed to select this preset for the "Filament settings" Tab + if (m_type == Preset::TYPE_FILAMENT && wxGetApp().extruders_edited_cnt() > 1 && + !dynamic_cast(wxGetApp().get_tab(m_type))->set_active_extruder(m_extruder_idx)) + // do nothing, if we can't set new extruder and select new preset + return; + wxGetApp().tab_panel()->SetSelection(page_id); // Switch to Settings NotePad wxGetApp().mainframe->select_tab(); - - //In a case of a multi-material printing, for editing another Filament Preset - //it's needed to select this preset for the "Filament settings" Tab - if (m_type == Preset::TYPE_FILAMENT && wxGetApp().extruders_edited_cnt() > 1) - { - const std::string& selected_preset = GetString(GetSelection()).ToUTF8().data(); - // Call select_preset() only if there is new preset and not just modified - if (!boost::algorithm::ends_with(selected_preset, Preset::suffix_modified())) - { - const std::string& preset_name = wxGetApp().preset_bundle->filaments.get_preset_name_by_alias(selected_preset); - wxGetApp().get_tab(m_type)->select_preset(preset_name); - } - } } } @@ -808,7 +802,7 @@ void PlaterPresetComboBox::update() { if (m_type == Preset::TYPE_FILAMENT && (m_preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA || - m_preset_bundle->filament_presets.size() <= (size_t)m_extruder_idx) ) + m_preset_bundle->extruders_filaments.size() <= (size_t)m_extruder_idx) ) return; // Otherwise fill in the list from scratch. @@ -816,6 +810,8 @@ void PlaterPresetComboBox::update() this->Clear(); invalidate_selection(); + const ExtruderFilaments& extruder_filaments = m_preset_bundle->extruders_filaments[m_extruder_idx >= 0 ? m_extruder_idx : 0]; + const Preset* selected_filament_preset = nullptr; std::string extruder_color; if (m_type == Preset::TYPE_FILAMENT) { @@ -823,21 +819,23 @@ void PlaterPresetComboBox::update() if (!can_decode_color(extruder_color)) // Extruder color is not defined. extruder_color.clear(); - selected_filament_preset = m_collection->find_preset(m_preset_bundle->filament_presets[m_extruder_idx]); + selected_filament_preset = extruder_filaments.get_selected_preset(); assert(selected_filament_preset); } - bool has_selection = m_collection->get_selected_idx() != size_t(-1); - const Preset* selected_preset = m_type == Preset::TYPE_FILAMENT ? selected_filament_preset : has_selection ? &m_collection->get_selected_preset() : nullptr; // Show wide icons if the currently selected preset is not compatible with the current printer, // and draw a red flag in front of the selected preset. - bool wide_icons = selected_preset && !selected_preset->is_compatible; + bool wide_icons = m_type == Preset::TYPE_FILAMENT ? + extruder_filaments.get_selected_filament() && !extruder_filaments.get_selected_filament()->is_compatible : + m_collection->get_selected_idx() != size_t(-1) && !m_collection->get_selected_preset().is_compatible; null_icon_width = (wide_icons ? 3 : 2) * norm_icon_width + thin_space_icon_width + wide_space_icon_width; std::map nonsys_presets; std::map template_presets; + const bool allow_templates = !wxGetApp().app_config->get_bool("no_templates"); + wxString selected_user_preset; wxString tooltip; const std::deque& presets = m_collection->get_presets(); @@ -848,13 +846,15 @@ void PlaterPresetComboBox::update() for (size_t i = presets.front().is_visible ? 0 : m_collection->num_default_presets(); i < presets.size(); ++i) { const Preset& preset = presets[i]; - bool is_selected = m_type == Preset::TYPE_FILAMENT ? - m_preset_bundle->filament_presets[m_extruder_idx] == preset.name : + const bool is_selected = m_type == Preset::TYPE_FILAMENT ? + selected_filament_preset->name == preset.name : // The case, when some physical printer is selected m_type == Preset::TYPE_PRINTER && m_preset_bundle->physical_printers.has_selection() ? false : i == m_collection->get_selected_idx(); - if (!preset.is_visible || (!preset.is_compatible && !is_selected)) + const bool is_compatible = m_type == Preset::TYPE_FILAMENT ? extruder_filaments.filament(i).is_compatible : preset.is_compatible; + + if (!preset.is_visible || (!is_compatible && !is_selected)) continue; std::string bitmap_key, filament_rgb, extruder_rgb, material_rgb; @@ -878,17 +878,19 @@ void PlaterPresetComboBox::update() } auto bmp = get_bmp(bitmap_key, wide_icons, bitmap_type_name, - preset.is_compatible, preset.is_system || preset.is_default, + is_compatible, preset.is_system || preset.is_default, single_bar, filament_rgb, extruder_rgb, material_rgb); assert(bmp); const std::string name = preset.alias.empty() ? preset.name : preset.alias; if (preset.is_default || preset.is_system) { if (preset.vendor && preset.vendor->templates_profile) { - template_presets.emplace(get_preset_name(preset), bmp); - if (is_selected) { - selected_user_preset = get_preset_name(preset); - tooltip = from_u8(preset.name); + if (allow_templates) { + template_presets.emplace(get_preset_name(preset), bmp); + if (is_selected) { + selected_user_preset = get_preset_name(preset); + tooltip = from_u8(preset.name); + } } } else { Append(get_preset_name(preset), *bmp); @@ -919,8 +921,7 @@ void PlaterPresetComboBox::update() } } - const AppConfig* app_config = wxGetApp().app_config; - if (!template_presets.empty() && app_config->get("no_templates") == "0") { + if (!template_presets.empty()) { set_label_marker(Append(separator(L("Template presets")), wxNullBitmap)); for (std::map::iterator it = template_presets.begin(); it != template_presets.end(); ++it) { Append(it->first, *it->second); @@ -1063,15 +1064,19 @@ void TabPresetComboBox::update() Clear(); invalidate_selection(); + const ExtruderFilaments& extruder_filaments = m_preset_bundle->extruders_filaments[m_active_extruder_idx]; + const std::deque& presets = m_collection->get_presets(); std::map> nonsys_presets; std::map> template_presets; + const bool allow_templates = !wxGetApp().app_config->get_bool("no_templates"); + wxString selected = ""; if (!presets.front().is_visible) set_label_marker(Append(separator(L("System presets")), NullBitmapBndl())); - size_t idx_selected = m_collection->get_selected_idx(); + size_t idx_selected = m_type == Preset::TYPE_FILAMENT ? extruder_filaments.get_selected_idx() : m_collection->get_selected_idx(); if (m_type == Preset::TYPE_PRINTER && m_preset_bundle->physical_printers.has_selection()) { std::string sel_preset_name = m_preset_bundle->physical_printers.get_selected_printer_preset_name(); @@ -1083,7 +1088,10 @@ void TabPresetComboBox::update() for (size_t i = presets.front().is_visible ? 0 : m_collection->num_default_presets(); i < presets.size(); ++i) { const Preset& preset = presets[i]; - if (!preset.is_visible || (!show_incompatible && !preset.is_compatible && i != idx_selected)) + + const bool is_compatible = m_type == Preset::TYPE_FILAMENT ? extruder_filaments.filament(i).is_compatible : preset.is_compatible; + + if (!preset.is_visible || (!show_incompatible && !is_compatible && i != idx_selected)) continue; // marker used for disable incompatible printer models for the selected physical printer @@ -1097,14 +1105,16 @@ void TabPresetComboBox::update() } std::string main_icon_name = m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name; - auto bmp = get_bmp(bitmap_key, main_icon_name, "lock_closed", is_enabled, preset.is_compatible, preset.is_system || preset.is_default); + auto bmp = get_bmp(bitmap_key, main_icon_name, "lock_closed", is_enabled, is_compatible, preset.is_system || preset.is_default); assert(bmp); - if (preset.is_default || preset.is_system) { + if (preset.is_default || preset.is_system) { if (preset.vendor && preset.vendor->templates_profile) { - template_presets.emplace(get_preset_name(preset), std::pair(bmp, is_enabled)); - if (i == idx_selected) - selected = get_preset_name(preset); + if (allow_templates) { + template_presets.emplace(get_preset_name(preset), std::pair(bmp, is_enabled)); + if (i == idx_selected) + selected = get_preset_name(preset); + } } else { int item_id = Append(get_preset_name(preset), *bmp); if (!is_enabled) @@ -1136,9 +1146,8 @@ void TabPresetComboBox::update() validate_selection(it->first == selected); } } - - const AppConfig* app_config = wxGetApp().app_config; - if (!template_presets.empty() && app_config->get("no_templates") == "0") { + + if (!template_presets.empty()) { set_label_marker(Append(separator(L("Template presets")), wxNullBitmap)); for (std::map>::iterator it = template_presets.begin(); it != template_presets.end(); ++it) { int item_id = Append(it->first, *it->second.first); diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp index 2a477ac115..6575c1a011 100644 --- a/src/slic3r/GUI/PresetComboBoxes.hpp +++ b/src/slic3r/GUI/PresetComboBoxes.hpp @@ -179,6 +179,8 @@ class TabPresetComboBox : public PresetComboBox { bool show_incompatible {false}; bool m_enable_all {false}; + // This parameter is used by FilamentSettings tab to show filament setting related to the active extruder + int m_active_extruder_idx {0}; public: TabPresetComboBox(wxWindow *parent, Preset::Type preset_type); @@ -197,6 +199,9 @@ public: PresetCollection* presets() const { return m_collection; } Preset::Type type() const { return m_type; } + + // used by Filaments tab to update preset list according to the particular extruder + void set_active_extruder(int extruder_idx) { m_active_extruder_idx = extruder_idx; } }; } // namespace GUI diff --git a/src/slic3r/GUI/PresetHints.cpp b/src/slic3r/GUI/PresetHints.cpp index 15017ba930..1bc2e5d71c 100644 --- a/src/slic3r/GUI/PresetHints.cpp +++ b/src/slic3r/GUI/PresetHints.cpp @@ -75,9 +75,9 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle { // Find out, to which nozzle index is the current filament profile assigned. int idx_extruder = 0; - int num_extruders = (int)preset_bundle.filament_presets.size(); + int num_extruders = (int)preset_bundle.extruders_filaments.size(); for (; idx_extruder < num_extruders; ++ idx_extruder) - if (preset_bundle.filament_presets[idx_extruder] == preset_bundle.filaments.get_selected_preset_name()) + if (preset_bundle.extruders_filaments[idx_extruder].get_selected_preset_name() == preset_bundle.filaments.get_selected_preset_name()) break; if (idx_extruder == num_extruders) // The current filament preset is not active for any extruder. diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 2cf4969cb8..b8cb3c4c62 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1236,7 +1236,8 @@ void Tab::on_presets_changed() m_dependent_tabs.clear(); // Update Project dirty state, update application title bar. - wxGetApp().plater()->update_project_dirty_from_presets(); + if (wxGetApp().mainframe) + wxGetApp().plater()->update_project_dirty_from_presets(); } void Tab::build_preset_description_line(ConfigOptionsGroup* optgroup) @@ -1933,8 +1934,64 @@ void TabFilament::update_filament_overrides_page() } } +void TabFilament::create_extruder_combobox() +{ + m_extruders_cb = new BitmapComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(12 * m_em_unit, -1), 0, nullptr, wxCB_READONLY); + m_extruders_cb->Hide(); + + m_extruders_cb->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent&) { + set_active_extruder(m_extruders_cb->GetSelection()); + }); + + m_h_buttons_sizer->AddSpacer(3*em_unit(this)); + m_h_buttons_sizer->Add(m_extruders_cb, 0, wxALIGN_CENTER_VERTICAL); +} + +void TabFilament::update_extruder_combobox() +{ + const size_t extruder_cnt = static_cast(m_preset_bundle->printers.get_edited_preset().config.option("nozzle_diameter"))->values.size(); + + m_extruders_cb->Show(extruder_cnt > 1); + + if (extruder_cnt != m_extruders_cb->GetCount()) { + m_extruders_cb->Clear(); + for (size_t id = 1; id <= extruder_cnt; id++) + m_extruders_cb->Append(format_wxstr("%1% %2%", _L("Extruder"), id), *get_bmp_bundle("funnel")); + } + + if (m_active_extruder >= int(extruder_cnt)) + m_active_extruder = 0; + + m_extruders_cb->SetSelection(m_active_extruder); +} + +bool TabFilament::set_active_extruder(int new_selected_extruder) +{ + if (m_active_extruder == new_selected_extruder) + return true; + + const int old_extruder_id = m_active_extruder; + m_active_extruder = new_selected_extruder; + m_presets_choice->set_active_extruder(m_active_extruder); + + if (!select_preset(m_preset_bundle->extruders_filaments[m_active_extruder].get_selected_preset_name())) { + m_active_extruder = old_extruder_id; + m_presets_choice->set_active_extruder(m_active_extruder); + m_extruders_cb->SetSelection(m_active_extruder); + return false; + } + + if (m_active_extruder != m_extruders_cb->GetSelection()) + m_extruders_cb->Select(m_active_extruder); + + return true; +} + void TabFilament::build() { + // add extruder combobox + create_extruder_combobox(); + m_presets = &m_preset_bundle->filaments; load_initial_data(); @@ -2189,7 +2246,7 @@ void TabFilament::update() m_update_cnt--; - if (m_update_cnt == 0) + if (m_update_cnt == 0 && wxGetApp().mainframe) wxGetApp().mainframe->on_config_changed(m_config); } @@ -2210,6 +2267,44 @@ void TabFilament::msw_rescale() Tab::msw_rescale(); } +void TabFilament::load_current_preset() +{ + assert(m_active_extruder >= 0 && m_active_extruder < m_preset_bundle->extruders_filaments.size()); + const std::string& selected_extr_filament_name = m_preset_bundle->extruders_filaments[m_active_extruder].get_selected_preset_name(); + const std::string& selected_filament_name = m_presets->get_selected_preset_name(); + if (selected_extr_filament_name != selected_filament_name) + m_presets->select_preset_by_name(selected_extr_filament_name, false); + + Tab::load_current_preset(); +} + +bool TabFilament::select_preset_by_name(const std::string &name_w_suffix, bool force) +{ + const bool is_selected_filament = Tab::select_preset_by_name(name_w_suffix, force); + const bool is_selected_extr_filament = m_preset_bundle->extruders_filaments[m_active_extruder].select_filament(name_w_suffix, force); + return is_selected_filament && is_selected_extr_filament; +} + +bool TabFilament::save_current_preset(const std::string &new_name, bool detach) +{ + m_preset_bundle->cache_extruder_filaments_names(); + const bool is_saved = Tab::save_current_preset(new_name, detach); + if (is_saved) { + m_preset_bundle->reset_extruder_filaments(); + m_preset_bundle->extruders_filaments[m_active_extruder].select_filament(m_presets->get_idx_selected()); + } + return is_saved; +} + +bool TabFilament::delete_current_preset() +{ + m_preset_bundle->cache_extruder_filaments_names(); + const bool is_deleted = Tab::delete_current_preset(); + if (is_deleted) + m_preset_bundle->reset_extruder_filaments(); + return is_deleted; +} + wxSizer* Tab::description_line_widget(wxWindow* parent, ogStaticText* *StaticText, wxString text /*= wxEmptyString*/) { *StaticText = new ogStaticText(parent, text); @@ -2339,7 +2434,6 @@ void TabPrinter::build_fff() const wxString msg_text = _(L("Single Extruder Multi Material is selected, \n" "and all extruders must have the same diameter.\n" "Do you want to change the diameter for all extruders to first extruder nozzle diameter value?")); - //wxMessageDialog dialog(parent(), msg_text, _(L("Nozzle diameter")), wxICON_WARNING | wxYES_NO); MessageDialog dialog(parent(), msg_text, _(L("Nozzle diameter")), wxICON_WARNING | wxYES_NO); DynamicPrintConfig new_conf = *m_config; @@ -2357,6 +2451,14 @@ void TabPrinter::build_fff() } } } + + m_preset_bundle->update_compatible(PresetSelectCompatibleType::Never); + // Upadte related comboboxes on Sidebar and Tabs + Sidebar& sidebar = wxGetApp().plater()->sidebar(); + for (const Preset::Type& type : {Preset::TYPE_PRINT, Preset::TYPE_FILAMENT}) { + sidebar.update_presets(type); + wxGetApp().get_tab(type)->update_tab_ui(); + } } } else { @@ -2625,15 +2727,21 @@ void TabPrinter::build_sla() void TabPrinter::extruders_count_changed(size_t extruders_count) { bool is_count_changed = false; + bool is_updated_mm_filament_presets = false; if (m_extruders_count != extruders_count) { m_extruders_count = extruders_count; m_preset_bundle->printers.get_edited_preset().set_num_extruders(extruders_count); - m_preset_bundle->update_multi_material_filament_presets(); - is_count_changed = true; + is_count_changed = is_updated_mm_filament_presets = true; } else if (m_extruders_count == 1 && - m_preset_bundle->project_config.option("wiping_volumes_matrix")->values.size()>1) + m_preset_bundle->project_config.option("wiping_volumes_matrix")->values.size()>1) { + is_updated_mm_filament_presets = true; + } + + if (is_updated_mm_filament_presets) { m_preset_bundle->update_multi_material_filament_presets(); + m_preset_bundle->update_filaments_compatible(PresetSelectCompatibleType::OnlyIfWasCompatible); + } /* This function should be call in any case because of correct updating/rebuilding * of unregular pages of a Printer Settings @@ -2768,7 +2876,10 @@ void TabPrinter::build_extruder_pages(size_t n_before_extruders) optgroup->m_on_change = [this, extruder_idx](const t_config_option_key&opt_key, boost::any value) { - if (m_config->opt_bool("single_extruder_multi_material") && m_extruders_count > 1 && opt_key.find_first_of("nozzle_diameter") != std::string::npos) + const bool is_single_extruder_MM = m_config->opt_bool("single_extruder_multi_material"); + const bool is_nozzle_diameter_changed = opt_key.find_first_of("nozzle_diameter") != std::string::npos; + + if (is_single_extruder_MM && m_extruders_count > 1 && is_nozzle_diameter_changed) { SuppressBackgroundProcessingUpdate sbpu; const double new_nd = boost::any_cast(value); @@ -2798,6 +2909,15 @@ void TabPrinter::build_extruder_pages(size_t n_before_extruders) } } + if (is_nozzle_diameter_changed) { + if (extruder_idx == 0) + // Mark the print & filament enabled if they are compatible with the currently selected preset. + // If saving the preset changes compatibility with other presets, keep the now incompatible dependent presets selected, however with a "red flag" icon showing that they are no more compatible. + m_preset_bundle->update_compatible(PresetSelectCompatibleType::Never); + else + m_preset_bundle->update_filaments_compatible(PresetSelectCompatibleType::Never, extruder_idx); + } + update_dirty(); update(); }; @@ -3040,6 +3160,7 @@ void TabPrinter::update_pages() if (m_extruders_count > 1) { m_preset_bundle->update_multi_material_filament_presets(); + m_preset_bundle->update_filaments_compatible(PresetSelectCompatibleType::OnlyIfWasCompatible); on_value_change("extruders_count", m_extruders_count); } } @@ -3392,7 +3513,7 @@ void Tab::update_preset_choice() // Called by the UI combo box when the user switches profiles, and also to delete the current profile. // Select a preset by a name.If !defined(name), then the default preset is selected. // If the current profile is modified, user is asked to save the changes. -void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, const std::string& last_selected_ph_printer_name/* =""*/) +bool Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, const std::string& last_selected_ph_printer_name/* =""*/) { if (preset_name.empty()) { if (delete_current) { @@ -3492,7 +3613,8 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, // It does not matter which preset will be made active as the preset will be re-selected from the preset_name variable. // The 'external' presets will only be removed from the preset list, their files will not be deleted. try { - m_presets->delete_current_preset(); + // cache previously selected names + delete_current_preset(); } catch (const std::exception & /* e */) { //FIXME add some error reporting! canceled = true; @@ -3513,7 +3635,7 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, } } - update_tab_ui(); + // update_tab_ui(); //! ysFIXME delete after testing // Trigger the on_presets_changed event so that we also restore the previous value in the plater selector, // if this action was initiated from the plater. @@ -3522,7 +3644,7 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, if (current_dirty) m_presets->discard_current_changes(); - const bool is_selected = m_presets->select_preset_by_name(preset_name, false) || delete_current; + const bool is_selected = select_preset_by_name(preset_name, false) || delete_current; assert(m_presets->get_edited_preset().name == preset_name || ! is_selected); // Mark the print & filament enabled if they are compatible with the currently selected preset. // The following method should not discard changes of current print or filament presets on change of a printer profile, @@ -3565,6 +3687,8 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/, if (technology_changed) wxGetApp().mainframe->technology_changed(); + + return !canceled; } // If the current preset is dirty, the user is asked whether the changes may be discarded. @@ -3812,16 +3936,12 @@ void Tab::save_preset(std::string name /*= ""*/, bool detach) // focus currently.is there anything better than this ? //! m_treectrl->OnSetFocus(); - auto& old_preset = m_presets->get_edited_preset(); + Preset& edited_preset = m_presets->get_edited_preset(); bool from_template = false; std::string edited_printer; - if (m_type == Preset::TYPE_FILAMENT && old_preset.vendor && old_preset.vendor->templates_profile) - { - //TODO: is this really the best way to get "printer_model" option of currently edited printer? - edited_printer = wxGetApp().preset_bundle->printers.get_edited_preset().config.opt("printer_model")->serialize(); - if (!edited_printer.empty()) - from_template = true; - + if (m_type == Preset::TYPE_FILAMENT && edited_preset.vendor && edited_preset.vendor->templates_profile) { + edited_printer = wxGetApp().preset_bundle->printers.get_edited_preset().config.opt_string("printer_model"); + from_template = !edited_printer.empty(); } if (name.empty()) { @@ -3836,24 +3956,21 @@ void Tab::save_preset(std::string name /*= ""*/, bool detach) if (detach && m_type == Preset::TYPE_PRINTER) m_config->opt_string("printer_model", true) = ""; + // Update compatible printers + if (from_template && !edited_printer.empty()) { + std::string cond = edited_preset.compatible_printers_condition(); + if (!cond.empty()) + cond += " and "; + cond += "printer_model == \"" + edited_printer + "\""; + edited_preset.config.opt_string("compatible_printers_condition") = cond; + } + // Save the preset into Slic3r::data_dir / presets / section_name / preset_name.ini - m_presets->save_current_preset(name, detach); + save_current_preset(name, detach); if (detach && m_type == Preset::TYPE_PRINTER) wxGetApp().mainframe->on_config_changed(m_config); - // Update compatible printers - if (from_template && !edited_printer.empty()) { - auto& new_preset = m_presets->get_edited_preset(); - std::string cond = new_preset.compatible_printers_condition(); - if (!cond.empty()) - cond += " and "; - cond += "printer_model == \""+edited_printer+"\""; - new_preset.config.set("compatible_printers_condition", cond); - new_preset.save(); - m_presets->save_current_preset(name, detach); - load_current_preset(); - } // Mark the print & filament enabled if they are compatible with the currently selected preset. // If saving the preset changes compatibility with other presets, keep the now incompatible dependent presets selected, however with a "red flag" icon showing that they are no more compatible. @@ -3980,7 +4097,7 @@ void Tab::rename_preset() // sort presets after renaming std::sort(m_presets->begin(), m_presets->end()); // update selection - m_presets->select_preset_by_name(new_name, true); + select_preset_by_name(new_name, true); m_presets_choice->update(); on_presets_changed(); @@ -4675,6 +4792,21 @@ void Tab::set_tooltips_text() "Click to reset current value to the last saved preset.")); } +bool Tab::select_preset_by_name(const std::string &name_w_suffix, bool force) +{ + return m_presets->select_preset_by_name(name_w_suffix, force); +} + +bool Tab::save_current_preset(const std::string& new_name, bool detach) +{ + return m_presets->save_current_preset(new_name, detach); +} + +bool Tab::delete_current_preset() +{ + return m_presets->delete_current_preset(); +} + Page::Page(wxWindow* parent, const wxString& title, int iconID) : m_parent(parent), m_title(title), diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 1a2edfe056..bbe1d1b17b 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -307,12 +307,13 @@ public: long style = wxBU_EXACTFIT | wxNO_BORDER); void add_scaled_bitmap(wxWindow* parent, ScalableBitmap& btn, const std::string& icon_name); void update_ui_items_related_on_parent_preset(const Preset* selected_preset_parent); - void load_current_preset(); + virtual void load_current_preset(); void rebuild_page_tree(); void update_btns_enabling(); void update_preset_choice(); // Select a new preset, possibly delete the current one. - void select_preset(std::string preset_name = "", bool delete_current = false, const std::string& last_selected_ph_printer_name = ""); + // return false, if action was canceled + bool select_preset(std::string preset_name = "", bool delete_current = false, const std::string& last_selected_ph_printer_name = ""); bool may_discard_current_dirty_preset(PresetCollection* presets = nullptr, const std::string& new_printer_name = ""); virtual void clear_pages(); @@ -402,6 +403,10 @@ protected: void fill_icon_descriptions(); void set_tooltips_text(); + virtual bool select_preset_by_name(const std::string& name_w_suffix, bool force); + virtual bool save_current_preset(const std::string& new_name, bool detach); + virtual bool delete_current_preset(); + ConfigManipulation m_config_manipulation; ConfigManipulation get_config_manipulation(); }; @@ -432,7 +437,8 @@ private: class TabFilament : public Tab { -private: + BitmapComboBox* m_extruders_cb {nullptr}; + int m_active_extruder {0}; ogStaticText* m_volumetric_speed_description_line {nullptr}; ogStaticText* m_cooling_description_line {nullptr}; @@ -440,6 +446,7 @@ private: void update_line_with_near_label_widget(ConfigOptionsGroupShp optgroup, const std::string &opt_key, int opt_index = 0, bool is_checked = true); void add_filament_overrides_page(); void update_filament_overrides_page(); + void create_extruder_combobox(); void update_volumetric_flow_preset_hints(); std::map m_overrides_options; @@ -455,6 +462,18 @@ public: void clear_pages() override; void msw_rescale() override; bool supports_printer_technology(const PrinterTechnology tech) const override { return tech == ptFFF; } + void load_current_preset() override; + + // set actiev extruder and update preset combobox if needed + // return false, if new preset wasn't selected + bool set_active_extruder(int new_selected_extruder); + void update_extruder_combobox(); + int get_active_extruder() const { return m_active_extruder; } + +protected: + bool select_preset_by_name(const std::string& name_w_suffix, bool force) override; + bool save_current_preset(const std::string& new_name, bool detach) override; + bool delete_current_preset() override; }; class TabPrinter : public Tab From c6a203cb14dbb3fe9c15474c8de287e041725214 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 11 May 2023 12:34:02 +0200 Subject: [PATCH 27/90] Do not calculate sequential print clearance contours for objects out of printbed --- src/slic3r/GUI/GLCanvas3D.cpp | 22 ++++++++++++++++++---- src/slic3r/GUI/GLCanvas3D.hpp | 2 +- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index cb2d90d413..3a56d90cb9 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4247,6 +4247,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; @@ -4296,13 +4304,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) { diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 04ab92dfbe..b26be314e3 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -589,7 +589,7 @@ public: struct ContoursList { // list of unique contours - std::vector contours; + Polygons contours; // if defined: list of transforms to apply to contours std::optional>> trafos; From 52fc7a84392f34af663339d7fcc576ff0c09825f Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 11 May 2023 14:26:16 +0200 Subject: [PATCH 28/90] Organic supports: bring "lost" branches down to an object or print bed. --- src/libslic3r/Support/TreeSupport.cpp | 6 +++++- src/libslic3r/Support/TreeSupport.hpp | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index d53b3e7859..7b11955d95 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -2249,6 +2249,7 @@ static void increase_areas_one_layer( // But as branches connecting with the model that are to small have to be culled, the bottom most point has to be not set. // A point can be set on the top most tip layer (maybe more if it should not move for a few layers). parent.state.result_on_layer_reset(); + parent.state.to_model_gracious = false; #ifdef TREE_SUPPORTS_TRACK_LOST parent.state.verylost = true; #endif // TREE_SUPPORTS_TRACK_LOST @@ -4410,7 +4411,10 @@ static void draw_branches( // Don't propagate further than 1.5 * bottom radius. //LayerIndex layers_propagate_max = 2 * bottom_radius / config.layer_height; LayerIndex layers_propagate_max = 5 * bottom_radius / config.layer_height; - LayerIndex layer_bottommost = std::max(0, layer_begin - layers_propagate_max); + LayerIndex layer_bottommost = branch.path.front()->state.verylost ? + // If the tree bottom is hanging in the air, bring it down to some surface. + 0 : + std::max(0, layer_begin - layers_propagate_max); // Only propagate until the rest area is smaller than this threshold. double support_area_stop = 0.2 * M_PI * sqr(double(bottom_radius)); // Only propagate until the rest area is smaller than this threshold. diff --git a/src/libslic3r/Support/TreeSupport.hpp b/src/libslic3r/Support/TreeSupport.hpp index 899f027242..66010b5b06 100644 --- a/src/libslic3r/Support/TreeSupport.hpp +++ b/src/libslic3r/Support/TreeSupport.hpp @@ -91,7 +91,7 @@ struct AreaIncreaseSettings struct TreeSupportSettings; -// #define TREE_SUPPORTS_TRACK_LOST +#define TREE_SUPPORTS_TRACK_LOST // C++17 does not support in place initializers of bit values, thus a constructor zeroing the bits is provided. struct SupportElementStateBits { From 683933176b63d76b3149ab35457249255793019b Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 11 May 2023 17:17:43 +0200 Subject: [PATCH 29/90] GUI_App::run_wizard : Deallocate a memory after close ConfigWizard. + Added wait cursor when ConfigWizard is created to inform user about activity --- src/slic3r/GUI/ConfigWizard.cpp | 2 ++ src/slic3r/GUI/GUI_App.cpp | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 866d5a817f..d0b7721976 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -3323,6 +3323,8 @@ ConfigWizard::ConfigWizard(wxWindow *parent) : DPIDialog(parent, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _(name()), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) , p(new priv(this)) { + wxBusyCursor wait; + this->SetFont(wxGetApp().normal_font()); p->load_vendors(); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index b0411c89c0..83c3cf13fc 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -3057,6 +3057,13 @@ bool GUI_App::run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage auto wizard = new ConfigWizard(mainframe); const bool res = wizard->run(reason, start_page); + // !!! Deallocate memory after close ConfigWizard. + // Note, that mainframe is a parent of ConfigWizard. + // So, wizard will be destroyed only during destroying of mainframe + // To avoid this state the wizard have to be disconnected from mainframe and Destroyed explicitly + mainframe->RemoveChild(wizard); + wizard->Destroy(); + if (res) { load_current_presets(); From ffbddd82eede4ebcfc01f182f5b249e5718d264e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 12 May 2023 10:03:28 +0200 Subject: [PATCH 30/90] SPE-1696 - Fixed crash when loading project while the camera is below the printbed --- src/slic3r/GUI/3DBed.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index f43afdf426..92383dc978 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -439,6 +439,14 @@ void Bed3D::render_model(const Transform3d& view_matrix, const Transform3d& proj m_model_offset = to_3d(m_build_volume.bounding_volume2d().center(), -0.03); // register for picking + const std::vector>* const raycaster = wxGetApp().plater()->canvas3D()->get_raycasters_for_picking(SceneRaycaster::EType::Bed); + if (!raycaster->empty()) { + // The raycaster may have been set by the call to init_triangles() made from render_texture() if the printbed was + // changed while the camera was pointing upward. + // In this case we need to remove it before creating a new using the model geometry + wxGetApp().plater()->canvas3D()->remove_raycasters_for_picking(SceneRaycaster::EType::Bed); + m_model.mesh_raycaster.reset(); + } register_raycasters_for_picking(m_model.model.get_geometry(), Geometry::translation_transform(m_model_offset)); // update extended bounding box From fbe102a7fe5269bd82d6f28b68990049e3ad823d Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 5 May 2023 16:34:07 +0200 Subject: [PATCH 31/90] initial impl, Created surfaces have wrong parameters --- src/libslic3r/PrintObject.cpp | 70 ++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 22 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 1c37339a23..38fc5b2b14 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -1603,11 +1604,11 @@ void PrintObject::discover_vertical_shells() } // for each region } // void PrintObject::discover_vertical_shells() -// #define DEBUG_BRIDGE_OVER_INFILL +#define DEBUG_BRIDGE_OVER_INFILL #ifdef DEBUG_BRIDGE_OVER_INFILL template void debug_draw(std::string name, const T& a, const T& b, const T& c, const T& d) { - std::vector colors = {"red", "blue", "orange", "green"}; + std::vector colors = {"red", "green", "blue", "orange"}; BoundingBox bbox = get_extents(a); bbox.merge(get_extents(b)); bbox.merge(get_extents(c)); @@ -1664,11 +1665,7 @@ void PrintObject::bridge_over_infill() // unsupported area will serve as a filter for polygons worth bridging. Polygons unsupported_area; Polygons lower_layer_solids; - bool contains_only_lightning = true; for (const LayerRegion *region : layer->lower_layer->regions()) { - if (region->region().config().fill_pattern.value != ipLightning) { - contains_only_lightning = false; - } Polygons fill_polys = to_polygons(region->fill_expolygons()); // initially consider the whole layer unsupported, but also gather solid layers to later cut off supported parts unsupported_area.insert(unsupported_area.end(), fill_polys.begin(), fill_polys.end()); @@ -2278,43 +2275,72 @@ void PrintObject::bridge_over_infill() tbb::parallel_for(tbb::blocked_range(0, this->layers().size()), [po = this, &surfaces_by_layer](tbb::blocked_range r) { PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT); for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { - if (surfaces_by_layer.find(lidx) == surfaces_by_layer.end()) + if (surfaces_by_layer.find(lidx) == surfaces_by_layer.end() && surfaces_by_layer.find(lidx + 1) == surfaces_by_layer.end()) continue; Layer *layer = po->get_layer(lidx); Polygons cut_from_infill{}; - for (const auto &surface : surfaces_by_layer.at(lidx)) { - cut_from_infill.insert(cut_from_infill.end(), surface.new_polys.begin(), surface.new_polys.end()); + if (surfaces_by_layer.find(lidx) != surfaces_by_layer.end()) { + for (const auto &surface : surfaces_by_layer.at(lidx)) { + cut_from_infill.insert(cut_from_infill.end(), surface.new_polys.begin(), surface.new_polys.end()); + } + } + + Polygons additional_ensuring_areas{}; + if (surfaces_by_layer.find(lidx + 1) != surfaces_by_layer.end()) { + for (const auto &surface : surfaces_by_layer.at(lidx + 1)) { + auto additional_area = diff(surface.new_polys, + shrink(surface.new_polys, surface.region->flow(frSolidInfill).scaled_spacing())); + additional_ensuring_areas.insert(additional_ensuring_areas.end(), additional_area.begin(), additional_area.end()); + } } for (LayerRegion *region : layer->regions()) { Surfaces new_surfaces; - SurfacesPtr internal_infills = region->m_fill_surfaces.filter_by_type(stInternal); - ExPolygons new_internal_infills = diff_ex(internal_infills, cut_from_infill); + Polygons near_perimeters = to_polygons(union_safety_offset_ex(to_polygons(region->fill_surfaces().surfaces))); + near_perimeters = diff(near_perimeters, shrink(near_perimeters, region->flow(frSolidInfill).scaled_spacing())); + ExPolygons additional_ensuring = intersection_ex(additional_ensuring_areas, near_perimeters); + + SurfacesPtr internal_infills = region->m_fill_surfaces.filter_by_type(stInternal); + ExPolygons new_internal_infills = diff_ex(internal_infills, cut_from_infill); + new_internal_infills = diff_ex(new_internal_infills, additional_ensuring); for (const ExPolygon &ep : new_internal_infills) { new_surfaces.emplace_back(*internal_infills.front(), ep); } SurfacesPtr internal_solids = region->m_fill_surfaces.filter_by_type(stInternalSolid); - for (const CandidateSurface &cs : surfaces_by_layer.at(lidx)) { - for (const Surface *surface : internal_solids) { - if (cs.original_surface == surface) { - Surface tmp{*surface, {}}; - tmp.surface_type = stInternalBridge; - tmp.bridge_angle = cs.bridge_angle; - for (const ExPolygon &ep : union_ex(cs.new_polys)) { - new_surfaces.emplace_back(tmp, ep); + if (surfaces_by_layer.find(lidx) != surfaces_by_layer.end()) { + for (const CandidateSurface &cs : surfaces_by_layer.at(lidx)) { + for (const Surface *surface : internal_solids) { + if (cs.original_surface == surface) { + Surface tmp{*surface, {}}; + tmp.surface_type = stInternalBridge; + tmp.bridge_angle = cs.bridge_angle; + for (const ExPolygon &ep : union_ex(cs.new_polys)) { + new_surfaces.emplace_back(tmp, ep); + } + break; } - break; } } } - ExPolygons new_internal_solids = diff_ex(internal_solids, cut_from_infill); + ExPolygons new_internal_solids = to_expolygons(internal_solids); + new_internal_solids.insert(new_internal_solids.end(), additional_ensuring.begin(), additional_ensuring.end()); + new_internal_solids = diff_ex(internal_solids, cut_from_infill); for (const ExPolygon &ep : new_internal_solids) { - new_surfaces.emplace_back(*internal_solids.front(), ep); + new_surfaces.emplace_back(stInternalSolid, ep); } +#ifdef DEBUG_BRIDGE_OVER_INFILL + debug_draw("AdditionalEnsuring_" + std::to_string(reinterpret_cast(®ion)), + to_polylines(additional_ensuring_areas), to_polylines(near_perimeters), to_polylines(to_polygons(internal_infills)), + to_polylines(to_polygons(internal_solids))); + debug_draw("AddditionalEnsuring_" + std::to_string(reinterpret_cast(®ion)) + "_new", + to_polylines(additional_ensuring_areas), to_polylines(near_perimeters), to_polylines(to_polygons(new_internal_infills)), + to_polylines(to_polygons(new_internal_solids))); +#endif + region->m_fill_surfaces.remove_types({stInternalSolid, stInternal}); region->m_fill_surfaces.append(new_surfaces); } From 22f7fe3e21b1fd0b14c65cb321a92ba8585ecbf9 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 9 May 2023 14:57:45 +0200 Subject: [PATCH 32/90] Fix bugs, finish the feature --- src/libslic3r/PrintObject.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 38fc5b2b14..3481551de0 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1604,7 +1604,7 @@ void PrintObject::discover_vertical_shells() } // for each region } // void PrintObject::discover_vertical_shells() -#define DEBUG_BRIDGE_OVER_INFILL +// #define DEBUG_BRIDGE_OVER_INFILL #ifdef DEBUG_BRIDGE_OVER_INFILL template void debug_draw(std::string name, const T& a, const T& b, const T& c, const T& d) { @@ -2306,7 +2306,7 @@ void PrintObject::bridge_over_infill() ExPolygons new_internal_infills = diff_ex(internal_infills, cut_from_infill); new_internal_infills = diff_ex(new_internal_infills, additional_ensuring); for (const ExPolygon &ep : new_internal_infills) { - new_surfaces.emplace_back(*internal_infills.front(), ep); + new_surfaces.emplace_back(stInternal, ep); } SurfacesPtr internal_solids = region->m_fill_surfaces.filter_by_type(stInternalSolid); @@ -2327,17 +2327,18 @@ void PrintObject::bridge_over_infill() } ExPolygons new_internal_solids = to_expolygons(internal_solids); new_internal_solids.insert(new_internal_solids.end(), additional_ensuring.begin(), additional_ensuring.end()); - new_internal_solids = diff_ex(internal_solids, cut_from_infill); + new_internal_solids = diff_ex(new_internal_solids, cut_from_infill); + new_internal_solids = union_safety_offset_ex(new_internal_solids); for (const ExPolygon &ep : new_internal_solids) { new_surfaces.emplace_back(stInternalSolid, ep); } #ifdef DEBUG_BRIDGE_OVER_INFILL - debug_draw("AdditionalEnsuring_" + std::to_string(reinterpret_cast(®ion)), - to_polylines(additional_ensuring_areas), to_polylines(near_perimeters), to_polylines(to_polygons(internal_infills)), + debug_draw("Aensuring_" + std::to_string(reinterpret_cast(®ion)), to_polylines(additional_ensuring), + to_polylines(near_perimeters), to_polylines(to_polygons(internal_infills)), to_polylines(to_polygons(internal_solids))); - debug_draw("AddditionalEnsuring_" + std::to_string(reinterpret_cast(®ion)) + "_new", - to_polylines(additional_ensuring_areas), to_polylines(near_perimeters), to_polylines(to_polygons(new_internal_infills)), + debug_draw("Aensuring_" + std::to_string(reinterpret_cast(®ion)) + "_new", to_polylines(additional_ensuring), + to_polylines(near_perimeters), to_polylines(to_polygons(new_internal_infills)), to_polylines(to_polygons(new_internal_solids))); #endif From 827230174f6a661b988714e2262b1b284b6c9e3c Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 9 May 2023 16:46:48 +0200 Subject: [PATCH 33/90] Fix tests for new ensuring under bridges --- tests/fff_print/test_cooling.cpp | 2 +- tests/fff_print/test_shells.cpp | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/fff_print/test_cooling.cpp b/tests/fff_print/test_cooling.cpp index 361a4c3114..778a7da401 100644 --- a/tests/fff_print/test_cooling.cpp +++ b/tests/fff_print/test_cooling.cpp @@ -260,7 +260,7 @@ SCENARIO("Cooling integration tests", "[Cooling]") { }); THEN("slowdown_below_layer_time is honored") { // Account for some inaccuracies. - const double slowdown_below_layer_time = config.opt("slowdown_below_layer_time")->values.front() - 0.2; + const double slowdown_below_layer_time = config.opt("slowdown_below_layer_time")->values.front() - 0.5; size_t minimum_time_honored = std::count_if(layer_times.begin(), layer_times.end(), [slowdown_below_layer_time](double t){ return t > slowdown_below_layer_time; }); REQUIRE(minimum_time_honored == layer_times.size()); diff --git a/tests/fff_print/test_shells.cpp b/tests/fff_print/test_shells.cpp index f5b9197869..fcb3a49f28 100644 --- a/tests/fff_print/test_shells.cpp +++ b/tests/fff_print/test_shells.cpp @@ -48,9 +48,11 @@ SCENARIO("Shells", "[Shells]") { REQUIRE(! has_shells(i)); } THEN("correct number of top solid layers") { - for (int i = 0; i < top_solid_layers; ++ i) + // NOTE: there is one additional layer with enusring line under the bridge layer, bridges would be otherwise anchored weakly to the perimeter. + size_t additional_ensuring_anchors = top_solid_layers > 0 ? 1 : 0; + for (int i = 0; i < top_solid_layers + additional_ensuring_anchors; ++ i) REQUIRE(has_shells(int(zs.size()) - i - 1)); - for (int i = top_solid_layers; i < int(zs.size() / 2); ++ i) + for (int i = top_solid_layers + additional_ensuring_anchors; i < int(zs.size() / 2); ++ i) REQUIRE(! has_shells(int(zs.size()) - i - 1)); } if (top_solid_layers > 0) { @@ -144,7 +146,7 @@ SCENARIO("Shells (from Perl)", "[Shells]") { for (auto z : layers_with_speed(Slic3r::Test::slice({TestMesh::V}, config), solid_speed)) if (z <= 7.2) ++ n; - REQUIRE(n == 3); + REQUIRE(n == 3 + 1/*one additional layer with ensuring for bridge anchors*/); } } From 6e9e4ee8c3d3a3a2e5db1128c25d08394a0b2aa1 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 10 May 2023 17:25:04 +0200 Subject: [PATCH 34/90] SPE-1626 - improve anchoring over lightning infill by expanding the bridges surfaces --- src/libslic3r/PrintObject.cpp | 95 ++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 3481551de0..db08e7307d 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -21,6 +21,7 @@ #include "Support/TreeSupport.hpp" #include "Surface.hpp" #include "Slicing.hpp" +#include "SurfaceCollection.hpp" #include "Tesselate.hpp" #include "TriangleMeshSlicer.hpp" #include "Utils.hpp" @@ -38,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -1726,6 +1728,98 @@ void PrintObject::bridge_over_infill() } } + // LIGHTNING INFILL SECTION - If lightning infill is used somewhere, we check the areas that are going to be bridges, and those that rely on the + // lightning infill under them get expanded. This somewhat helps to ensure that most of the extrusions are anchored to the lightning infill at the ends. + // It requires modifying this instance of print object in a specific way, so that we do not invalidate the pointers in our surfaces_by_layer structure. + bool has_lightning_infill = false; + for (size_t i = 0; i < this->num_printing_regions(); i++) { + if (this->printing_region(i).config().fill_pattern == ipLightning) { + has_lightning_infill = true; + break; + } + } + if (has_lightning_infill) { + // Prepare backup data for the Layer Region infills. Before modfiyng the layer region, we backup its fill surfaces by moving! them into this map. + // then a copy is created, modifiyed and passed to lightning infill generator. After generator is created, we restore the original state of the fills + // again by moving the data from this map back to the layer regions. This ensures that pointers to surfaces stay valid. + std::map> backup_surfaces; + for (size_t lidx = 0; lidx < this->layer_count(); lidx++) { + backup_surfaces[lidx] = {}; + } + + tbb::parallel_for(tbb::blocked_range(0, this->layers().size()), [po = this, &backup_surfaces, + &surfaces_by_layer](tbb::blocked_range r) { + PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT); + for (size_t lidx = r.begin(); lidx < r.end(); lidx++) { + if (surfaces_by_layer.find(lidx) == surfaces_by_layer.end()) + continue; + + Layer *layer = po->get_layer(lidx); + const Layer *lower_layer = layer->lower_layer; + if (lower_layer == nullptr) + continue; + + Polygons lightning_fill; + for (const LayerRegion *region : lower_layer->regions()) { + if (region->region().config().fill_pattern == ipLightning) { + Polygons lf = to_polygons(region->fill_surfaces().filter_by_type(stInternal)); + lightning_fill.insert(lightning_fill.end(), lf.begin(), lf.end()); + } + } + + if (lightning_fill.empty()) + continue; + + for (LayerRegion *region : layer->regions()) { + backup_surfaces[lidx][region] = std::move( + region->m_fill_surfaces); // Make backup copy by move!! so that pointers in candidate surfaces stay valid + // Copy the surfaces back, this will make copy, but we will later discard it anyway + region->m_fill_surfaces = backup_surfaces[lidx][region]; + } + + for (LayerRegion *region : layer->regions()) { + ExPolygons sparse_infill = to_expolygons(region->fill_surfaces().filter_by_type(stInternal)); + ExPolygons solid_infill = to_expolygons(region->fill_surfaces().filter_by_type(stInternalSolid)); + + if (sparse_infill.empty()) { + break; + } + for (const auto &surface : surfaces_by_layer[lidx]) { + if (surface.region != region) + continue; + ExPolygons expansion = intersection_ex(sparse_infill, expand(surface.new_polys, scaled(3.0))); + solid_infill.insert(solid_infill.end(), expansion.begin(), expansion.end()); + } + + solid_infill = union_safety_offset_ex(solid_infill); + sparse_infill = diff_ex(sparse_infill, solid_infill); + + region->m_fill_surfaces.remove_types({stInternalSolid, stInternal}); + for (const ExPolygon &ep : solid_infill) { + region->m_fill_surfaces.surfaces.emplace_back(stInternalSolid, ep); + } + for (const ExPolygon &ep : sparse_infill) { + region->m_fill_surfaces.surfaces.emplace_back(stInternal, ep); + } + } + } + }); + + // Use the modified surfaces to generate expanded lightning anchors + this->m_lightning_generator = this->prepare_lightning_infill_data(); + + // And now restore carefully the original surfaces, again using move to avoid reallocation and preserving the validity of the + // pointers in surface candidates + for (size_t lidx = 0; lidx < this->layer_count(); lidx++) { + Layer *layer = this->get_layer(lidx); + for (LayerRegion *region : layer->regions()) { + if (backup_surfaces[lidx].find(region) != backup_surfaces[lidx].end()) { + region->m_fill_surfaces = std::move(backup_surfaces[lidx][region]); + } + } + } + } + std::map infill_lines; // SECTION to generate infill polylines { @@ -1737,7 +1831,6 @@ void PrintObject::bridge_over_infill() } this->m_adaptive_fill_octrees = this->prepare_adaptive_infill_data(surfaces_w_bottom_z); - this->m_lightning_generator = this->prepare_lightning_infill_data(); std::vector layers_to_generate_infill; for (const auto &pair : surfaces_by_layer) { From 72f8590332e6b1323f8a871ff3fc95bb56598464 Mon Sep 17 00:00:00 2001 From: Pavel Date: Fri, 12 May 2023 10:29:09 +0200 Subject: [PATCH 35/90] Add missing include --- src/libslic3r/Format/SLAArchiveFormatRegistry.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libslic3r/Format/SLAArchiveFormatRegistry.cpp b/src/libslic3r/Format/SLAArchiveFormatRegistry.cpp index 5c40a5c51b..17d3fa9a0b 100644 --- a/src/libslic3r/Format/SLAArchiveFormatRegistry.cpp +++ b/src/libslic3r/Format/SLAArchiveFormatRegistry.cpp @@ -5,6 +5,7 @@ #include "SL1.hpp" #include "SL1_SVG.hpp" #include "AnycubicSLA.hpp" +#include "I18N.hpp" #include "SLAArchiveFormatRegistry.hpp" From 93fb77c7111b7fae58d7decc9807ee39fa02f156 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 18 Apr 2023 11:37:09 +0200 Subject: [PATCH 36/90] initial implementation --- src/libslic3r/Fill/Fill.cpp | 48 +----- src/libslic3r/Fill/FillEnsuring.cpp | 239 ++++++++++++++++++++++++++-- 2 files changed, 238 insertions(+), 49 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index a6e5e1fb4a..423329fc81 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -306,47 +306,15 @@ std::vector group_fills(const Layer &layer) } } - // Detect narrow internal solid infill area and use ipEnsuring pattern instead. - { - std::vector narrow_expolygons; - static constexpr const auto narrow_pattern = ipEnsuring; - for (size_t surface_fill_id = 0, num_old_fills = surface_fills.size(); surface_fill_id < num_old_fills; ++ surface_fill_id) - if (SurfaceFill &fill = surface_fills[surface_fill_id]; fill.surface.surface_type == stInternalSolid) { - size_t num_expolygons = fill.expolygons.size(); - narrow_expolygons.clear(); - narrow_expolygons.reserve(num_expolygons); - // Detect narrow expolygons. - int num_narrow = 0; - for (const ExPolygon &ex : fill.expolygons) { - bool narrow = offset_ex(ex, -scaled(NarrowInfillAreaThresholdMM)).empty(); - num_narrow += int(narrow); - narrow_expolygons.emplace_back(narrow); - } - if (num_narrow == num_expolygons) { - // All expolygons are narrow, change the fill pattern. - fill.params.pattern = narrow_pattern; - } else if (num_narrow > 0) { - // Some expolygons are narrow, split the fills. - params = fill.params; - params.pattern = narrow_pattern; - surface_fills.emplace_back(params); - SurfaceFill &old_fill = surface_fills[surface_fill_id]; - SurfaceFill &new_fill = surface_fills.back(); - new_fill.region_id = old_fill.region_id; - new_fill.surface.surface_type = stInternalSolid; - new_fill.surface.thickness = old_fill.surface.thickness; - new_fill.expolygons.reserve(num_narrow); - for (size_t i = 0; i < narrow_expolygons.size(); ++ i) - if (narrow_expolygons[i]) - new_fill.expolygons.emplace_back(std::move(old_fill.expolygons[i])); - old_fill.expolygons.erase(std::remove_if(old_fill.expolygons.begin(), old_fill.expolygons.end(), - [&narrow_expolygons, ex_first = old_fill.expolygons.data()](const ExPolygon& ex) { return narrow_expolygons[&ex - ex_first]; }), - old_fill.expolygons.end()); - } - } - } + // Detect narrow internal solid infill area and use ipEnsuring pattern instead. + { + for (size_t surface_fill_id = 0; surface_fill_id < surface_fills.size(); ++surface_fill_id) + if (SurfaceFill &fill = surface_fills[surface_fill_id]; fill.surface.surface_type == stInternalSolid) { + fill.params.pattern = ipEnsuring; + } + } - return surface_fills; + return surface_fills; } #ifdef SLIC3R_DEBUG_SLICE_PROCESSING diff --git a/src/libslic3r/Fill/FillEnsuring.cpp b/src/libslic3r/Fill/FillEnsuring.cpp index 2522aee938..14b2ebedcf 100644 --- a/src/libslic3r/Fill/FillEnsuring.cpp +++ b/src/libslic3r/Fill/FillEnsuring.cpp @@ -2,9 +2,20 @@ #include "../ShortestPath.hpp" #include "../Arachne/WallToolPaths.hpp" +#include "AABBTreeLines.hpp" +#include "ExPolygon.hpp" #include "FillEnsuring.hpp" +#include "Line.hpp" +#include "Polygon.hpp" +#include "Polyline.hpp" +#include "SVG.hpp" +#include "libslic3r.h" #include +#include +#include +#include +#include namespace Slic3r { @@ -13,17 +24,225 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const assert(params.use_arachne); assert(this->print_config != nullptr && this->print_object_config != nullptr && this->print_region_config != nullptr); - const coord_t scaled_spacing = scaled(this->spacing); + auto rotate_thick_polylines = [](ThickPolylines &tpolylines, double cos_angle, double sin_angle) { + for (ThickPolyline &tp : tpolylines) { + for (auto &p : tp.points) { + double px = double(p.x()); + double py = double(p.y()); + p.x() = coord_t(round(cos_angle * px - sin_angle * py)); + p.y() = coord_t(round(cos_angle * py + sin_angle * px)); + } + } + }; + + auto segments_overlap = [](coord_t alow, coord_t ahigh, coord_t blow, coord_t bhigh) { + return (alow >= blow && alow <= bhigh) || (ahigh >= blow && ahigh <= bhigh) || (blow >= alow && blow <= ahigh) || + (bhigh >= alow && bhigh <= ahigh); + }; + + Polygons filled_area = to_polygons(surface->expolygon); + double aligning_angle = -this->angle + PI * 0.5; + polygons_rotate(filled_area, aligning_angle); + Polygons internal_area = shrink(filled_area, scale_(this->overlap)); + BoundingBox bb = get_extents(internal_area); + const coord_t scaled_spacing = scaled(this->spacing); + + const size_t n_vlines = (bb.max.x() - bb.min.x() + scaled_spacing - 1) / scaled_spacing; + std::vector vertical_lines(n_vlines); + for (size_t i = 0; i < n_vlines; i++) { + coord_t x = bb.min.x() + i * scaled_spacing; + coord_t y_min = bb.min.y(); + coord_t y_max = bb.max.y(); + vertical_lines[i].a = Point{x, y_min}; + vertical_lines[i].b = Point{x, y_max}; + } + + auto internal_area_distancer = AABBTreeLines::LinesDistancer{to_lines(internal_area)}; + + std::vector> polygon_sections(n_vlines); + for (size_t i = 0; i < n_vlines; i++) { + auto area_intersections = internal_area_distancer.intersections_with_line(vertical_lines[i]); + for (int intersection_idx = 0; intersection_idx < int(area_intersections.size()) - 1; intersection_idx++) { + if (internal_area_distancer.outside( + (area_intersections[intersection_idx].first + area_intersections[intersection_idx + 1].first) / 2) < 0) { + polygon_sections[i].emplace_back(area_intersections[intersection_idx].first, area_intersections[intersection_idx + 1].first); + } + } + + for (int section_idx = 0; section_idx < int(polygon_sections[i].size()) - 1; section_idx++) { + Line §ion_a = polygon_sections[i][section_idx]; + Line §ion_b = polygon_sections[i][section_idx + 1]; + if (segments_overlap(section_a.a.y(), section_a.b.y(), section_b.a.y(), section_b.b.y())) { + section_b.a = section_a.a.y() < section_b.a.y() ? section_a.a : section_b.a; + section_b.b = section_a.b.y() < section_b.b.y() ? section_b.b : section_a.b; + section_a.a = section_a.b; + } + } + + polygon_sections[i].erase(std::remove_if(polygon_sections[i].begin(), polygon_sections[i].end(), + [](const Line &s) { return s.a == s.b; }), + polygon_sections[i].end()); + } + + struct Node{ + int section_idx; + int line_idx; + int skips_taken = 0; + bool neighbours_explored = false; + std::vector> neighbours{}; + }; + + coord_t length_filter = scale_(3); + for (int section_idx = 0; section_idx < polygon_sections.size(); section_idx++) { + for (int line_idx = 0; line_idx < polygon_sections[section_idx].size(); line_idx++) { + if (const Line &line = polygon_sections[section_idx][line_idx]; line.a != line.b && line.length() < length_filter) { + std::set> to_remove{{section_idx, line_idx}}; + std::vector to_visit{{section_idx, line_idx}}; + while (!to_visit.empty()) { + Node curr = to_visit.back(); + const Line &curr_l = polygon_sections[curr.section_idx][curr.line_idx]; + if (curr.neighbours_explored) { + bool is_valid_for_removal = (curr_l.length() < length_filter) && ((int(to_remove.size()) - curr.skips_taken > 3) || + to_remove.size() == polygon_sections.size()); + if (!is_valid_for_removal) { + for (const auto &n : curr.neighbours) { + if (to_remove.find(n) != to_remove.end()) { + is_valid_for_removal = true; + break; + } + } + } + if (!is_valid_for_removal) { + to_remove.erase({curr.section_idx, curr.line_idx}); + } + to_visit.pop_back(); + } else { + to_visit.back().neighbours_explored = true; + int curr_index = to_visit.size() - 1; + bool can_use_skip = curr_l.length() <= length_filter && curr.skips_taken < 2; + if (curr.section_idx + 1 < polygon_sections.size()) { + for (int lidx = 0; lidx < polygon_sections[curr.section_idx + 1].size(); lidx++) { + if (const Line &nl = polygon_sections[curr.section_idx + 1][lidx]; + nl.a != nl.b && segments_overlap(curr_l.a.y(), curr_l.b.y(), nl.a.y(), nl.b.y()) && + (nl.length() < length_filter || can_use_skip)) { + to_visit[curr_index].neighbours.push_back({curr.section_idx + 1, lidx}); + to_remove.insert({curr.section_idx + 1, lidx}); + Node next_node{curr.section_idx + 1, lidx, curr.skips_taken + (nl.length() >= length_filter)}; + to_visit.push_back(next_node); + } + } + } + } + } + + for (const auto &pair : to_remove) { + Line &l = polygon_sections[pair.first][pair.second]; + l.a = l.b; + } + } + } + } + + for (size_t section_idx = 0; section_idx < polygon_sections.size(); section_idx++) { + polygon_sections[section_idx].erase(std::remove_if(polygon_sections[section_idx].begin(), polygon_sections[section_idx].end(), + [](const Line &s) { return s.a == s.b; }), + polygon_sections[section_idx].end()); + } + + Polygons reconstructed_area{}; + // reconstruct polygon from polygon sections + { + struct TracedPoly + { + std::vector lows; + std::vector highs; + }; + + std::vector current_traced_polys; + for (const auto &polygon_slice : polygon_sections) { + std::unordered_set used_segments; + for (TracedPoly &traced_poly : current_traced_polys) { + auto maybe_first_overlap = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), traced_poly.lows.back(), + [](const Point &low, const Line &seg) { return seg.b.y() > low.y(); }); + + if (maybe_first_overlap != polygon_slice.end() && // segment exists + segments_overlap(traced_poly.lows.back().y(), traced_poly.highs.back().y(), maybe_first_overlap->a.y(), + maybe_first_overlap->b.y())) // segment is overlapping + { + // Overlapping segment. In that case, add it + // to the traced polygon and add segment to used segments + traced_poly.lows.push_back(traced_poly.lows.back() + Point{scaled_spacing / 2, 0}); + traced_poly.lows.push_back(maybe_first_overlap->a - Point{scaled_spacing / 2, 0}); + traced_poly.lows.push_back(maybe_first_overlap->a); + + traced_poly.highs.push_back(traced_poly.highs.back() + Point{scaled_spacing / 2, 0}); + traced_poly.highs.push_back(maybe_first_overlap->b - Point{scaled_spacing / 2, 0}); + traced_poly.highs.push_back(maybe_first_overlap->b); + used_segments.insert(&(*maybe_first_overlap)); + } else { + // Zero or multiple overlapping segments. Resolving this is nontrivial, + // so we just close this polygon and maybe open several new. This will hopefully happen much less often + traced_poly.lows.push_back(traced_poly.lows.back() + Point{scaled_spacing / 2, 0}); + traced_poly.highs.push_back(traced_poly.highs.back() + Point{scaled_spacing / 2, 0}); + Polygon &new_poly = reconstructed_area.emplace_back(std::move(traced_poly.lows)); + new_poly.points.insert(new_poly.points.end(), traced_poly.highs.rbegin(), traced_poly.highs.rend()); + traced_poly.lows.clear(); + traced_poly.highs.clear(); + } + } + + current_traced_polys.erase(std::remove_if(current_traced_polys.begin(), current_traced_polys.end(), + [](const TracedPoly &tp) { return tp.lows.empty(); }), + current_traced_polys.end()); + + for (const auto &segment : polygon_slice) { + if (used_segments.find(&segment) == used_segments.end()) { + TracedPoly &new_tp = current_traced_polys.emplace_back(); + new_tp.lows.push_back(segment.a - Point{scaled_spacing / 2, 0}); + new_tp.lows.push_back(segment.a); + new_tp.highs.push_back(segment.b - Point{scaled_spacing / 2, 0}); + new_tp.highs.push_back(segment.b); + } + } + } + + // add not closed polys + for (TracedPoly &traced_poly : current_traced_polys) { + Polygon &new_poly = reconstructed_area.emplace_back(std::move(traced_poly.lows)); + new_poly.points.insert(new_poly.points.end(), traced_poly.highs.rbegin(), traced_poly.highs.rend()); + } + } - // Perform offset. - Slic3r::ExPolygons expp = this->overlap != 0. ? offset_ex(surface->expolygon, scaled(this->overlap)) : ExPolygons{surface->expolygon}; - // Create the infills for each of the regions. ThickPolylines thick_polylines_out; - for (ExPolygon &ex_poly : expp) { - Point bbox_size = ex_poly.contour.bounding_box().size(); - coord_t loops_count = std::max(bbox_size.x(), bbox_size.y()) / scaled_spacing + 1; - Polygons polygons = to_polygons(ex_poly); - Arachne::WallToolPaths wall_tool_paths(polygons, scaled_spacing, scaled_spacing, loops_count, 0, params.layer_height, *this->print_object_config, *this->print_config); + for (const auto &a : polygon_sections) { + for (const auto &l : a) { + ThickPolyline tp{}; + tp.points = {l.a, l.b}; + tp.width = {double(scaled_spacing), double(scaled_spacing)}; + thick_polylines_out.push_back(tp); + } + } + + reconstructed_area = closing(reconstructed_area, float(SCALED_EPSILON), float(SCALED_EPSILON)); + ExPolygons gaps_for_additional_filling = diff_ex(filled_area, reconstructed_area); + if (this->overlap != 0) { + gaps_for_additional_filling = offset_ex(gaps_for_additional_filling, scaled(this->overlap)); + } + gaps_for_additional_filling = opening_ex(gaps_for_additional_filling, 0.3 * scaled_spacing); + + BoundingBox bbox = get_extents(filled_area); + bbox.offset(scale_(1.)); + ::Slic3r::SVG svg(debug_out_path(("surface" + std::to_string(surface->area())).c_str()).c_str(), bbox); + svg.draw(to_lines(filled_area), "red", scale_(0.3)); + svg.draw(to_lines(reconstructed_area), "blue", scale_(0.2)); + svg.draw(to_lines(gaps_for_additional_filling), "green", scale_(0.1)); + svg.Close(); + + for (const ExPolygon &expoly : gaps_for_additional_filling) { + Point bbox_size = expoly.contour.bounding_box().size(); + coord_t loops_count = std::max(bbox_size.x(), bbox_size.y()) / scaled_spacing + 1; + Arachne::WallToolPaths wall_tool_paths(to_polygons(expoly), scaled_spacing, scaled_spacing, loops_count, 0, params.layer_height, + *this->print_object_config, *this->print_config); if (std::vector loops = wall_tool_paths.getToolPaths(); !loops.empty()) { std::vector all_extrusions; for (Arachne::VariableWidthLines &loop : loops) { @@ -76,6 +295,8 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const } } + rotate_thick_polylines(thick_polylines_out, cos(-aligning_angle), sin(-aligning_angle)); + return thick_polylines_out; } From 9d3a7f9f2ae39cd706615d3d6ca2c8d092c9d00c Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 18 Apr 2023 17:34:18 +0200 Subject: [PATCH 37/90] Implement connecting of lines, but does not work yet --- src/libslic3r/Fill/FillEnsuring.cpp | 140 ++++++++++++++++++++++------ src/libslic3r/PrintObject.cpp | 3 +- 2 files changed, 110 insertions(+), 33 deletions(-) diff --git a/src/libslic3r/Fill/FillEnsuring.cpp b/src/libslic3r/Fill/FillEnsuring.cpp index 14b2ebedcf..a12fd59480 100644 --- a/src/libslic3r/Fill/FillEnsuring.cpp +++ b/src/libslic3r/Fill/FillEnsuring.cpp @@ -6,11 +6,13 @@ #include "ExPolygon.hpp" #include "FillEnsuring.hpp" #include "Line.hpp" +#include "Point.hpp" #include "Polygon.hpp" #include "Polyline.hpp" #include "SVG.hpp" #include "libslic3r.h" +#include #include #include #include @@ -19,6 +21,8 @@ namespace Slic3r { +static constexpr const float NarrowInfillAreaThresholdMM = 3.f; + ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const FillParams ¶ms) { assert(params.use_arachne); @@ -40,12 +44,13 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const (bhigh >= alow && bhigh <= ahigh); }; - Polygons filled_area = to_polygons(surface->expolygon); - double aligning_angle = -this->angle + PI * 0.5; - polygons_rotate(filled_area, aligning_angle); - Polygons internal_area = shrink(filled_area, scale_(this->overlap)); - BoundingBox bb = get_extents(internal_area); const coord_t scaled_spacing = scaled(this->spacing); + Polygons filled_area = to_polygons(surface->expolygon); + double aligning_angle = -this->angle + PI * 0.5; + polygons_rotate(filled_area, aligning_angle); + Polygons internal_area = shrink(filled_area, 0.5 * scaled_spacing - scale_(this->overlap)); + Polygons openned_area = opening(internal_area, scale_(NarrowInfillAreaThresholdMM)); + BoundingBox bb = get_extents(openned_area); const size_t n_vlines = (bb.max.x() - bb.min.x() + scaled_spacing - 1) / scaled_spacing; std::vector vertical_lines(n_vlines); @@ -57,14 +62,14 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const vertical_lines[i].b = Point{x, y_max}; } - auto internal_area_distancer = AABBTreeLines::LinesDistancer{to_lines(internal_area)}; + auto area_walls = AABBTreeLines::LinesDistancer{to_lines(internal_area)}; std::vector> polygon_sections(n_vlines); for (size_t i = 0; i < n_vlines; i++) { - auto area_intersections = internal_area_distancer.intersections_with_line(vertical_lines[i]); + auto area_intersections = area_walls.intersections_with_line(vertical_lines[i]); for (int intersection_idx = 0; intersection_idx < int(area_intersections.size()) - 1; intersection_idx++) { - if (internal_area_distancer.outside( - (area_intersections[intersection_idx].first + area_intersections[intersection_idx + 1].first) / 2) < 0) { + if (area_walls.outside((area_intersections[intersection_idx].first + area_intersections[intersection_idx + 1].first) / 2) < + 0) { polygon_sections[i].emplace_back(area_intersections[intersection_idx].first, area_intersections[intersection_idx + 1].first); } } @@ -92,18 +97,32 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const std::vector> neighbours{}; }; - coord_t length_filter = scale_(3); + coord_t length_filter = scale_(4); + size_t skips_allowed = 2; + size_t min_removal_conut = 3; for (int section_idx = 0; section_idx < polygon_sections.size(); section_idx++) { for (int line_idx = 0; line_idx < polygon_sections[section_idx].size(); line_idx++) { if (const Line &line = polygon_sections[section_idx][line_idx]; line.a != line.b && line.length() < length_filter) { std::set> to_remove{{section_idx, line_idx}}; std::vector to_visit{{section_idx, line_idx}}; + + bool initial_touches_long_lines = false; + if (section_idx > 0) { + for (int prev_line_idx = 0; prev_line_idx < polygon_sections[section_idx - 1].size(); prev_line_idx++) { + if (const Line &nl = polygon_sections[section_idx - 1][prev_line_idx]; + nl.a != nl.b && segments_overlap(line.a.y(), line.b.y(), nl.a.y(), nl.b.y())) { + initial_touches_long_lines = true; + } + } + } + while (!to_visit.empty()) { Node curr = to_visit.back(); const Line &curr_l = polygon_sections[curr.section_idx][curr.line_idx]; if (curr.neighbours_explored) { - bool is_valid_for_removal = (curr_l.length() < length_filter) && ((int(to_remove.size()) - curr.skips_taken > 3) || - to_remove.size() == polygon_sections.size()); + bool is_valid_for_removal = (curr_l.length() < length_filter) && + ((int(to_remove.size()) - curr.skips_taken > min_removal_conut) || + (curr.neighbours.empty() && !initial_touches_long_lines)); if (!is_valid_for_removal) { for (const auto &n : curr.neighbours) { if (to_remove.find(n) != to_remove.end()) { @@ -119,7 +138,7 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const } else { to_visit.back().neighbours_explored = true; int curr_index = to_visit.size() - 1; - bool can_use_skip = curr_l.length() <= length_filter && curr.skips_taken < 2; + bool can_use_skip = curr_l.length() <= length_filter && curr.skips_taken < skips_allowed; if (curr.section_idx + 1 < polygon_sections.size()) { for (int lidx = 0; lidx < polygon_sections[curr.section_idx + 1].size(); lidx++) { if (const Line &nl = polygon_sections[curr.section_idx + 1][lidx]; @@ -149,6 +168,8 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const polygon_sections[section_idx].end()); } + double squared_distance_limit_reconnection = 4 * scaled_spacing * scaled_spacing; + Polygons reconstructed_area{}; // reconstruct polygon from polygon sections { @@ -163,7 +184,7 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const std::unordered_set used_segments; for (TracedPoly &traced_poly : current_traced_polys) { auto maybe_first_overlap = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), traced_poly.lows.back(), - [](const Point &low, const Line &seg) { return seg.b.y() > low.y(); }); + [](const Point &low, const Line &seg) { return seg.b.y() < low.y(); }); if (maybe_first_overlap != polygon_slice.end() && // segment exists segments_overlap(traced_poly.lows.back().y(), traced_poly.highs.back().y(), maybe_first_overlap->a.y(), @@ -171,13 +192,23 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const { // Overlapping segment. In that case, add it // to the traced polygon and add segment to used segments - traced_poly.lows.push_back(traced_poly.lows.back() + Point{scaled_spacing / 2, 0}); - traced_poly.lows.push_back(maybe_first_overlap->a - Point{scaled_spacing / 2, 0}); - traced_poly.lows.push_back(maybe_first_overlap->a); + if ((traced_poly.lows.back() - maybe_first_overlap->a).cast().squaredNorm() < + squared_distance_limit_reconnection) { + traced_poly.lows.push_back(maybe_first_overlap->a); + } else { + traced_poly.lows.push_back(traced_poly.lows.back() + Point{scaled_spacing / 2, 0}); + traced_poly.lows.push_back(maybe_first_overlap->a - Point{scaled_spacing / 2, 0}); + traced_poly.lows.push_back(maybe_first_overlap->a); + } - traced_poly.highs.push_back(traced_poly.highs.back() + Point{scaled_spacing / 2, 0}); - traced_poly.highs.push_back(maybe_first_overlap->b - Point{scaled_spacing / 2, 0}); - traced_poly.highs.push_back(maybe_first_overlap->b); + if ((traced_poly.highs.back() - maybe_first_overlap->b).cast().squaredNorm() < + squared_distance_limit_reconnection) { + traced_poly.highs.push_back(maybe_first_overlap->b); + } else { + traced_poly.highs.push_back(traced_poly.highs.back() + Point{scaled_spacing / 2, 0}); + traced_poly.highs.push_back(maybe_first_overlap->b - Point{scaled_spacing / 2, 0}); + traced_poly.highs.push_back(maybe_first_overlap->b); + } used_segments.insert(&(*maybe_first_overlap)); } else { // Zero or multiple overlapping segments. Resolving this is nontrivial, @@ -214,13 +245,60 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const } ThickPolylines thick_polylines_out; - for (const auto &a : polygon_sections) { - for (const auto &l : a) { - ThickPolyline tp{}; - tp.points = {l.a, l.b}; - tp.width = {double(scaled_spacing), double(scaled_spacing)}; - thick_polylines_out.push_back(tp); + { + ThickPolylines current_traced_paths; + for (const auto &polygon_slice : polygon_sections) { + std::unordered_set used_segments; + for (ThickPolyline &traced_path : current_traced_paths) { + auto maybe_overlap = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), traced_path.last_point(), + [](const Point &low, const Line &seg) { return seg.a.y() < low.y(); }); + bool segment_added = false; + if (maybe_overlap != polygon_slice.begin()) + maybe_overlap--; + while (!segment_added && maybe_overlap != polygon_slice.end()) { + if ((traced_path.last_point() - maybe_overlap->a).cast().squaredNorm() < + squared_distance_limit_reconnection) { + traced_path.points.push_back(maybe_overlap->a); + traced_path.width.push_back(scaled_spacing); + traced_path.points.push_back(maybe_overlap->b); + traced_path.width.push_back(scaled_spacing); + used_segments.insert(&(*maybe_overlap)); + segment_added = true; + } else if ((traced_path.last_point() - maybe_overlap->b).cast().squaredNorm() < + squared_distance_limit_reconnection) { + traced_path.points.push_back(maybe_overlap->b); + traced_path.width.push_back(scaled_spacing); + traced_path.points.push_back(maybe_overlap->a); + traced_path.width.push_back(scaled_spacing); + used_segments.insert(&(*maybe_overlap)); + segment_added = true; + } + maybe_overlap++; + } + + if (!segment_added) { + // Zero overlapping segments. Finish the polyline. + thick_polylines_out.push_back(std::move(traced_path)); + traced_path.clear(); + } + } + + current_traced_paths.erase(std::remove_if(current_traced_paths.begin(), current_traced_paths.end(), + [](const ThickPolyline &tp) { return tp.empty(); }), + current_traced_paths.end()); + + for (const auto &segment : polygon_slice) { + if (used_segments.find(&segment) == used_segments.end()) { + ThickPolyline &new_path = current_traced_paths.emplace_back(); + new_path.points.push_back(segment.a); + new_path.width.push_back(scaled_spacing); + new_path.points.push_back(segment.b); + new_path.width.push_back(scaled_spacing); + } + } } + + thick_polylines_out.insert(thick_polylines_out.end(), current_traced_paths.begin(), current_traced_paths.end()); } reconstructed_area = closing(reconstructed_area, float(SCALED_EPSILON), float(SCALED_EPSILON)); @@ -238,11 +316,11 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const svg.draw(to_lines(gaps_for_additional_filling), "green", scale_(0.1)); svg.Close(); - for (const ExPolygon &expoly : gaps_for_additional_filling) { - Point bbox_size = expoly.contour.bounding_box().size(); - coord_t loops_count = std::max(bbox_size.x(), bbox_size.y()) / scaled_spacing + 1; - Arachne::WallToolPaths wall_tool_paths(to_polygons(expoly), scaled_spacing, scaled_spacing, loops_count, 0, params.layer_height, - *this->print_object_config, *this->print_config); + for (ExPolygon &ex_poly : gaps_for_additional_filling) { + Point bbox_size = ex_poly.contour.bounding_box().size(); + coord_t loops_count = std::max(bbox_size.x(), bbox_size.y()) / scaled_spacing + 1; + Polygons polygons = to_polygons(ex_poly); + Arachne::WallToolPaths wall_tool_paths(polygons, scaled_spacing, scaled_spacing, loops_count, 0, params.layer_height, *this->print_object_config, *this->print_config); if (std::vector loops = wall_tool_paths.getToolPaths(); !loops.empty()) { std::vector all_extrusions; for (Arachne::VariableWidthLines &loop : loops) { diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index db08e7307d..54fba176cf 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2149,8 +2149,7 @@ void PrintObject::bridge_over_infill() } used_segments.insert(&(*maybe_first_overlap)); } else { - // Zero or multiple overlapping segments. Resolving this is nontrivial, - // so we just close this polygon and maybe open several new. This will hopefully happen much less often + // Zero overlapping segments, we just close this polygon traced_poly.lows.push_back(traced_poly.lows.back() + Point{bridging_flow.scaled_spacing() / 2, 0}); traced_poly.highs.push_back(traced_poly.highs.back() + Point{bridging_flow.scaled_spacing() / 2, 0}); Polygon &new_poly = expanded_bridged_area.emplace_back(std::move(traced_poly.lows)); From 4409743ea4ee152b07a54527c8ab2e71af0bd74e Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 19 Apr 2023 13:32:24 +0200 Subject: [PATCH 38/90] Fixed wrong usage of thickPolyline, added description of what it should actually contain --- src/libslic3r/Fill/FillEnsuring.cpp | 18 +++++++++++++----- src/libslic3r/Polyline.hpp | 3 +++ src/libslic3r/PrintObject.cpp | 6 ++++-- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/Fill/FillEnsuring.cpp b/src/libslic3r/Fill/FillEnsuring.cpp index a12fd59480..c58188d0d0 100644 --- a/src/libslic3r/Fill/FillEnsuring.cpp +++ b/src/libslic3r/Fill/FillEnsuring.cpp @@ -168,7 +168,7 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const polygon_sections[section_idx].end()); } - double squared_distance_limit_reconnection = 4 * scaled_spacing * scaled_spacing; + double squared_distance_limit_reconnection = 4 * double(scaled_spacing) * double(scaled_spacing); Polygons reconstructed_area{}; // reconstruct polygon from polygon sections @@ -185,7 +185,9 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const for (TracedPoly &traced_poly : current_traced_polys) { auto maybe_first_overlap = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), traced_poly.lows.back(), [](const Point &low, const Line &seg) { return seg.b.y() < low.y(); }); - + if (maybe_first_overlap != polygon_slice.begin()) { + maybe_first_overlap--; + } if (maybe_first_overlap != polygon_slice.end() && // segment exists segments_overlap(traced_poly.lows.back().y(), traced_poly.highs.back().y(), maybe_first_overlap->a.y(), maybe_first_overlap->b.y())) // segment is overlapping @@ -252,22 +254,27 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const for (ThickPolyline &traced_path : current_traced_paths) { auto maybe_overlap = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), traced_path.last_point(), [](const Point &low, const Line &seg) { return seg.a.y() < low.y(); }); - bool segment_added = false; - if (maybe_overlap != polygon_slice.begin()) + if (maybe_overlap != polygon_slice.begin()) { maybe_overlap--; + } + bool segment_added = false; while (!segment_added && maybe_overlap != polygon_slice.end()) { if ((traced_path.last_point() - maybe_overlap->a).cast().squaredNorm() < squared_distance_limit_reconnection) { + traced_path.width.push_back(scaled_spacing); traced_path.points.push_back(maybe_overlap->a); traced_path.width.push_back(scaled_spacing); + traced_path.width.push_back(scaled_spacing); traced_path.points.push_back(maybe_overlap->b); traced_path.width.push_back(scaled_spacing); used_segments.insert(&(*maybe_overlap)); segment_added = true; } else if ((traced_path.last_point() - maybe_overlap->b).cast().squaredNorm() < squared_distance_limit_reconnection) { + traced_path.width.push_back(scaled_spacing); traced_path.points.push_back(maybe_overlap->b); traced_path.width.push_back(scaled_spacing); + traced_path.width.push_back(scaled_spacing); traced_path.points.push_back(maybe_overlap->a); traced_path.width.push_back(scaled_spacing); used_segments.insert(&(*maybe_overlap)); @@ -287,13 +294,14 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const [](const ThickPolyline &tp) { return tp.empty(); }), current_traced_paths.end()); - for (const auto &segment : polygon_slice) { + for (const Line &segment : polygon_slice) { if (used_segments.find(&segment) == used_segments.end()) { ThickPolyline &new_path = current_traced_paths.emplace_back(); new_path.points.push_back(segment.a); new_path.width.push_back(scaled_spacing); new_path.points.push_back(segment.b); new_path.width.push_back(scaled_spacing); + new_path.endpoints = {true,true}; } } } diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index 8766c6d864..703e50cfa2 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -206,6 +206,9 @@ struct ThickPolyline { void start_at_index(int index); Points points; + // vector of startpoint width and endpoint width of each line segment. The size should be always (points.size()-1) * 2 + // e.g. let four be points a,b,c,d. that are three lines ab, bc, cd. for each line, there should be start width, so the width vector is: + // w(a), w(b), w(b), w(c), w(c), w(d) std::vector width; std::pair endpoints { false, false }; }; diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 54fba176cf..6a9bd0aae8 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2122,8 +2122,10 @@ void PrintObject::bridge_over_infill() std::unordered_set used_segments; for (TracedPoly &traced_poly : current_traced_polys) { auto maybe_first_overlap = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), traced_poly.lows.back(), - [](const Point &low, const Line &seg) { return seg.b.y() > low.y(); }); - + [](const Point &low, const Line &seg) { return seg.b.y() < low.y(); }); + if (maybe_first_overlap != polygon_slice.begin()) { + maybe_first_overlap--; + } if (maybe_first_overlap != polygon_slice.end() && // segment exists segments_overlap(traced_poly.lows.back().y(), traced_poly.highs.back().y(), maybe_first_overlap->a.y(), maybe_first_overlap->b.y())) // segment is overlapping From 3346b422a775642d6bdbe309487aa9c73ae646ec Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 19 Apr 2023 16:53:53 +0200 Subject: [PATCH 39/90] Fixed various bugs in reconstruction of polygon from vertical lines --- src/libslic3r/Fill/FillEnsuring.cpp | 188 ++++++++++++++----------- src/libslic3r/GCode/GCodeProcessor.cpp | 1 + src/libslic3r/PrintObject.cpp | 53 +++---- 3 files changed, 133 insertions(+), 109 deletions(-) diff --git a/src/libslic3r/Fill/FillEnsuring.cpp b/src/libslic3r/Fill/FillEnsuring.cpp index c58188d0d0..ada65a216e 100644 --- a/src/libslic3r/Fill/FillEnsuring.cpp +++ b/src/libslic3r/Fill/FillEnsuring.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -87,6 +88,7 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const polygon_sections[i].erase(std::remove_if(polygon_sections[i].begin(), polygon_sections[i].end(), [](const Line &s) { return s.a == s.b; }), polygon_sections[i].end()); + std::sort(polygon_sections[i].begin(), polygon_sections[i].end(), [](const Line &a, const Line &b) { return a.a.y() < b.b.y(); }); } struct Node{ @@ -166,10 +168,88 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const polygon_sections[section_idx].erase(std::remove_if(polygon_sections[section_idx].begin(), polygon_sections[section_idx].end(), [](const Line &s) { return s.a == s.b; }), polygon_sections[section_idx].end()); + std::sort(polygon_sections[section_idx].begin(), polygon_sections[section_idx].end(), + [](const Line &a, const Line &b) { return a.a.y() < b.b.y(); }); } double squared_distance_limit_reconnection = 4 * double(scaled_spacing) * double(scaled_spacing); + ThickPolylines thick_polylines_out; + { + struct TracedPath { + ThickPolyline polyline; + const Line* origin_line; + }; + + std::vector current_traced_paths; + for (const auto &polygon_slice : polygon_sections) { + std::unordered_set used_segments; + for (TracedPath &traced_path : current_traced_paths) { + ThickPolyline& traced_polyline = traced_path.polyline; + Point max_y = traced_polyline.last_point(); + Point min_y = traced_polyline.points[traced_polyline.size() - 2]; + + if (max_y.y() < min_y.y()) + std::swap(max_y, min_y); + + auto candidates_begin = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), min_y, + [](const Point &low, const Line &seg) { return seg.b.y() > low.y(); }); + auto candidates_end = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), max_y, + [](const Point &high, const Line &seg) { return seg.a.y() > high.y(); }); + bool segment_added = false; + for (auto candidate = candidates_begin; candidate != candidates_end && !segment_added; candidate++) { + if (used_segments.find(&(*candidate)) != used_segments.end()) { + continue; + } + if ((traced_polyline.last_point() - candidate->a).cast().squaredNorm() < squared_distance_limit_reconnection) { + traced_polyline.width.push_back(scaled_spacing); + traced_polyline.points.push_back(candidate->a); + traced_polyline.width.push_back(scaled_spacing); + traced_polyline.width.push_back(scaled_spacing); + traced_polyline.points.push_back(candidate->b); + traced_polyline.width.push_back(scaled_spacing); + used_segments.insert(&(*candidate)); + segment_added = true; + } else if ((traced_polyline.last_point() - candidate->b).cast().squaredNorm() < + squared_distance_limit_reconnection) { + traced_polyline.width.push_back(scaled_spacing); + traced_polyline.points.push_back(candidate->b); + traced_polyline.width.push_back(scaled_spacing); + traced_polyline.width.push_back(scaled_spacing); + traced_polyline.points.push_back(candidate->a); + traced_polyline.width.push_back(scaled_spacing); + used_segments.insert(&(*candidate)); + segment_added = true; + } + } + + if (!segment_added) { + // Zero overlapping segments. Finish the polyline. + thick_polylines_out.push_back(std::move(traced_polyline)); + traced_polyline.clear(); + } + } + + current_traced_paths.erase(std::remove_if(current_traced_paths.begin(), current_traced_paths.end(), + [](const ThickPolyline &tp) { return tp.empty(); }), + current_traced_paths.end()); + + for (const Line &segment : polygon_slice) { + if (used_segments.find(&segment) == used_segments.end()) { + TracedPath &new_path = current_traced_paths.emplace_back(); + new_path.polyline.points.push_back(segment.a); + new_path.polyline.width.push_back(scaled_spacing); + new_path.polyline.points.push_back(segment.b); + new_path.polyline.width.push_back(scaled_spacing); + new_path.polyline.endpoints = {true, true}; + new_path.origin_line = &segment; + } + } + } + + thick_polylines_out.insert(thick_polylines_out.end(), current_traced_paths.begin(), current_traced_paths.end()); + } + Polygons reconstructed_area{}; // reconstruct polygon from polygon sections { @@ -183,36 +263,37 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const for (const auto &polygon_slice : polygon_sections) { std::unordered_set used_segments; for (TracedPoly &traced_poly : current_traced_polys) { - auto maybe_first_overlap = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), traced_poly.lows.back(), - [](const Point &low, const Line &seg) { return seg.b.y() < low.y(); }); - if (maybe_first_overlap != polygon_slice.begin()) { - maybe_first_overlap--; - } - if (maybe_first_overlap != polygon_slice.end() && // segment exists - segments_overlap(traced_poly.lows.back().y(), traced_poly.highs.back().y(), maybe_first_overlap->a.y(), - maybe_first_overlap->b.y())) // segment is overlapping - { - // Overlapping segment. In that case, add it - // to the traced polygon and add segment to used segments - if ((traced_poly.lows.back() - maybe_first_overlap->a).cast().squaredNorm() < - squared_distance_limit_reconnection) { - traced_poly.lows.push_back(maybe_first_overlap->a); + auto candidates_begin = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), traced_poly.lows.back(), + [](const Point &low, const Line &seg) { return seg.b.y() > low.y(); }); + auto candidates_end = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), traced_poly.highs.back(), + [](const Point &high, const Line &seg) { return seg.a.y() > high.y(); }); + + bool segment_added = false; + for (auto candidate = candidates_begin; candidate != candidates_end && !segment_added; candidate++) { + if (used_segments.find(&(*candidate)) != used_segments.end()) { + continue; + } + if ((traced_poly.lows.back() - candidates_begin->a).cast().squaredNorm() < squared_distance_limit_reconnection) { + traced_poly.lows.push_back(candidates_begin->a); } else { traced_poly.lows.push_back(traced_poly.lows.back() + Point{scaled_spacing / 2, 0}); - traced_poly.lows.push_back(maybe_first_overlap->a - Point{scaled_spacing / 2, 0}); - traced_poly.lows.push_back(maybe_first_overlap->a); + traced_poly.lows.push_back(candidates_begin->a - Point{scaled_spacing / 2, 0}); + traced_poly.lows.push_back(candidates_begin->a); } - if ((traced_poly.highs.back() - maybe_first_overlap->b).cast().squaredNorm() < + if ((traced_poly.highs.back() - candidates_begin->b).cast().squaredNorm() < squared_distance_limit_reconnection) { - traced_poly.highs.push_back(maybe_first_overlap->b); + traced_poly.highs.push_back(candidates_begin->b); } else { traced_poly.highs.push_back(traced_poly.highs.back() + Point{scaled_spacing / 2, 0}); - traced_poly.highs.push_back(maybe_first_overlap->b - Point{scaled_spacing / 2, 0}); - traced_poly.highs.push_back(maybe_first_overlap->b); + traced_poly.highs.push_back(candidates_begin->b - Point{scaled_spacing / 2, 0}); + traced_poly.highs.push_back(candidates_begin->b); } - used_segments.insert(&(*maybe_first_overlap)); - } else { + segment_added = true; + used_segments.insert(&(*candidates_begin)); + } + + if (!segment_added) { // Zero or multiple overlapping segments. Resolving this is nontrivial, // so we just close this polygon and maybe open several new. This will hopefully happen much less often traced_poly.lows.push_back(traced_poly.lows.back() + Point{scaled_spacing / 2, 0}); @@ -246,69 +327,6 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const } } - ThickPolylines thick_polylines_out; - { - ThickPolylines current_traced_paths; - for (const auto &polygon_slice : polygon_sections) { - std::unordered_set used_segments; - for (ThickPolyline &traced_path : current_traced_paths) { - auto maybe_overlap = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), traced_path.last_point(), - [](const Point &low, const Line &seg) { return seg.a.y() < low.y(); }); - if (maybe_overlap != polygon_slice.begin()) { - maybe_overlap--; - } - bool segment_added = false; - while (!segment_added && maybe_overlap != polygon_slice.end()) { - if ((traced_path.last_point() - maybe_overlap->a).cast().squaredNorm() < - squared_distance_limit_reconnection) { - traced_path.width.push_back(scaled_spacing); - traced_path.points.push_back(maybe_overlap->a); - traced_path.width.push_back(scaled_spacing); - traced_path.width.push_back(scaled_spacing); - traced_path.points.push_back(maybe_overlap->b); - traced_path.width.push_back(scaled_spacing); - used_segments.insert(&(*maybe_overlap)); - segment_added = true; - } else if ((traced_path.last_point() - maybe_overlap->b).cast().squaredNorm() < - squared_distance_limit_reconnection) { - traced_path.width.push_back(scaled_spacing); - traced_path.points.push_back(maybe_overlap->b); - traced_path.width.push_back(scaled_spacing); - traced_path.width.push_back(scaled_spacing); - traced_path.points.push_back(maybe_overlap->a); - traced_path.width.push_back(scaled_spacing); - used_segments.insert(&(*maybe_overlap)); - segment_added = true; - } - maybe_overlap++; - } - - if (!segment_added) { - // Zero overlapping segments. Finish the polyline. - thick_polylines_out.push_back(std::move(traced_path)); - traced_path.clear(); - } - } - - current_traced_paths.erase(std::remove_if(current_traced_paths.begin(), current_traced_paths.end(), - [](const ThickPolyline &tp) { return tp.empty(); }), - current_traced_paths.end()); - - for (const Line &segment : polygon_slice) { - if (used_segments.find(&segment) == used_segments.end()) { - ThickPolyline &new_path = current_traced_paths.emplace_back(); - new_path.points.push_back(segment.a); - new_path.width.push_back(scaled_spacing); - new_path.points.push_back(segment.b); - new_path.width.push_back(scaled_spacing); - new_path.endpoints = {true,true}; - } - } - } - - thick_polylines_out.insert(thick_polylines_out.end(), current_traced_paths.begin(), current_traced_paths.end()); - } - reconstructed_area = closing(reconstructed_area, float(SCALED_EPSILON), float(SCALED_EPSILON)); ExPolygons gaps_for_additional_filling = diff_ex(filled_area, reconstructed_area); if (this->overlap != 0) { diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 9dab740e5b..8b5f646621 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -5,6 +5,7 @@ #include "libslic3r/format.hpp" #include "libslic3r/I18N.hpp" #include "libslic3r/GCodeWriter.hpp" +#include "libslic3r/I18N.hpp" #include "GCodeProcessor.hpp" #include diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 6a9bd0aae8..5d3028c8f7 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2108,6 +2108,8 @@ void PrintObject::bridge_over_infill() polygon_sections[i].erase(std::remove_if(polygon_sections[i].begin(), polygon_sections[i].end(), [](const Line &s) { return s.a == s.b; }), polygon_sections[i].end()); + std::sort(polygon_sections[i].begin(), polygon_sections[i].end(), + [](const Line &a, const Line &b) { return a.a.y() < b.b.y(); }); } // reconstruct polygon from polygon sections @@ -2121,36 +2123,39 @@ void PrintObject::bridge_over_infill() for (const auto &polygon_slice : polygon_sections) { std::unordered_set used_segments; for (TracedPoly &traced_poly : current_traced_polys) { - auto maybe_first_overlap = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), traced_poly.lows.back(), - [](const Point &low, const Line &seg) { return seg.b.y() < low.y(); }); - if (maybe_first_overlap != polygon_slice.begin()) { - maybe_first_overlap--; - } - if (maybe_first_overlap != polygon_slice.end() && // segment exists - segments_overlap(traced_poly.lows.back().y(), traced_poly.highs.back().y(), maybe_first_overlap->a.y(), - maybe_first_overlap->b.y())) // segment is overlapping - { - // Overlapping segment. In that case, add it - // to the traced polygon and add segment to used segments - if ((traced_poly.lows.back() - maybe_first_overlap->a).cast().squaredNorm() < - 36.0 * double(bridging_flow.scaled_spacing()) * bridging_flow.scaled_spacing()) { - traced_poly.lows.push_back(maybe_first_overlap->a); - } else { - traced_poly.lows.push_back(traced_poly.lows.back() + Point{bridging_flow.scaled_spacing() / 2, 0}); - traced_poly.lows.push_back(maybe_first_overlap->a - Point{bridging_flow.scaled_spacing() / 2, 0}); - traced_poly.lows.push_back(maybe_first_overlap->a); + auto candidates_begin = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), traced_poly.lows.back(), + [](const Point &low, const Line &seg) { return seg.b.y() > low.y(); }); + auto candidates_end = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), traced_poly.highs.back(), + [](const Point &high, const Line &seg) { return seg.a.y() > high.y(); }); + + bool segment_added = false; + for (auto candidate = candidates_begin; candidate != candidates_end && !segment_added; candidate++) { + if (used_segments.find(&(*candidate)) != used_segments.end()) { + continue; } - if ((traced_poly.highs.back() - maybe_first_overlap->b).cast().squaredNorm() < + if ((traced_poly.lows.back() - candidate->a).cast().squaredNorm() < 36.0 * double(bridging_flow.scaled_spacing()) * bridging_flow.scaled_spacing()) { - traced_poly.highs.push_back(maybe_first_overlap->b); + traced_poly.lows.push_back(candidate->a); + } else { + traced_poly.lows.push_back(traced_poly.lows.back() + Point{bridging_flow.scaled_spacing() / 2, 0}); + traced_poly.lows.push_back(candidate->a - Point{bridging_flow.scaled_spacing() / 2, 0}); + traced_poly.lows.push_back(candidate->a); + } + + if ((traced_poly.highs.back() - candidate->b).cast().squaredNorm() < + 36.0 * double(bridging_flow.scaled_spacing()) * bridging_flow.scaled_spacing()) { + traced_poly.highs.push_back(candidate->b); } else { traced_poly.highs.push_back(traced_poly.highs.back() + Point{bridging_flow.scaled_spacing() / 2, 0}); - traced_poly.highs.push_back(maybe_first_overlap->b - Point{bridging_flow.scaled_spacing() / 2, 0}); - traced_poly.highs.push_back(maybe_first_overlap->b); + traced_poly.highs.push_back(candidate->b - Point{bridging_flow.scaled_spacing() / 2, 0}); + traced_poly.highs.push_back(candidate->b); } - used_segments.insert(&(*maybe_first_overlap)); - } else { + segment_added = true; + used_segments.insert(&(*candidate)); + } + + if (!segment_added) { // Zero overlapping segments, we just close this polygon traced_poly.lows.push_back(traced_poly.lows.back() + Point{bridging_flow.scaled_spacing() / 2, 0}); traced_poly.highs.push_back(traced_poly.highs.back() + Point{bridging_flow.scaled_spacing() / 2, 0}); From 8be661f0f63a339392bddcb8d2b995a334267684 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 20 Apr 2023 12:47:11 +0200 Subject: [PATCH 40/90] shortening of lines near steep differences, but has some issues --- src/libslic3r/Fill/FillEnsuring.cpp | 171 +++++++++++++++++----------- 1 file changed, 102 insertions(+), 69 deletions(-) diff --git a/src/libslic3r/Fill/FillEnsuring.cpp b/src/libslic3r/Fill/FillEnsuring.cpp index ada65a216e..32dfd706f5 100644 --- a/src/libslic3r/Fill/FillEnsuring.cpp +++ b/src/libslic3r/Fill/FillEnsuring.cpp @@ -22,7 +22,7 @@ namespace Slic3r { -static constexpr const float NarrowInfillAreaThresholdMM = 3.f; +static constexpr const float NarrowInfillAreaThresholdMM = 2.f; ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const FillParams ¶ms) { @@ -45,53 +45,95 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const (bhigh >= alow && bhigh <= ahigh); }; - const coord_t scaled_spacing = scaled(this->spacing); - Polygons filled_area = to_polygons(surface->expolygon); - double aligning_angle = -this->angle + PI * 0.5; + const coord_t scaled_spacing = scaled(this->spacing); + double squared_distance_limit_reconnection = 4 * double(scaled_spacing) * double(scaled_spacing); + Polygons filled_area = to_polygons(surface->expolygon); + double aligning_angle = -this->angle + PI * 0.5; polygons_rotate(filled_area, aligning_angle); Polygons internal_area = shrink(filled_area, 0.5 * scaled_spacing - scale_(this->overlap)); Polygons openned_area = opening(internal_area, scale_(NarrowInfillAreaThresholdMM)); BoundingBox bb = get_extents(openned_area); const size_t n_vlines = (bb.max.x() - bb.min.x() + scaled_spacing - 1) / scaled_spacing; - std::vector vertical_lines(n_vlines); + std::vector vertical_lines(2 * n_vlines + 1); + coord_t y_min = bb.min.y(); + coord_t y_max = bb.max.y(); for (size_t i = 0; i < n_vlines; i++) { - coord_t x = bb.min.x() + i * scaled_spacing; - coord_t y_min = bb.min.y(); - coord_t y_max = bb.max.y(); - vertical_lines[i].a = Point{x, y_min}; - vertical_lines[i].b = Point{x, y_max}; + coord_t x0 = bb.min.x() + i * scaled_spacing - scaled_spacing * 0.5; + coord_t x1 = bb.min.x() + i * scaled_spacing; + vertical_lines[i * 2].a = Point{x0, y_min}; + vertical_lines[i * 2].b = Point{x0, y_max}; + vertical_lines[i * 2 + 1].a = Point{x1, y_min}; + vertical_lines[i * 2 + 1].b = Point{x1, y_max}; } + vertical_lines.back().a = Point{coord_t(bb.min.x() + n_vlines * scaled_spacing + scaled_spacing * 0.5), y_min}; + vertical_lines.back().b = Point{vertical_lines.back().a.x(), y_max}; - auto area_walls = AABBTreeLines::LinesDistancer{to_lines(internal_area)}; - + auto area_walls = AABBTreeLines::LinesDistancer{to_lines(internal_area)}; + std::vector, size_t>>> vertical_lines_intersections(vertical_lines.size()); + for (int i = 0; i < vertical_lines.size(); i++) { + vertical_lines_intersections[i] = area_walls.intersections_with_line(vertical_lines[i]); + } std::vector> polygon_sections(n_vlines); + for (size_t i = 0; i < n_vlines; i++) { - auto area_intersections = area_walls.intersections_with_line(vertical_lines[i]); - for (int intersection_idx = 0; intersection_idx < int(area_intersections.size()) - 1; intersection_idx++) { - if (area_walls.outside((area_intersections[intersection_idx].first + area_intersections[intersection_idx + 1].first) / 2) < - 0) { - polygon_sections[i].emplace_back(area_intersections[intersection_idx].first, area_intersections[intersection_idx + 1].first); + const auto ¢ral_intersections = vertical_lines_intersections[i * 2 + 1]; + const auto &left_intersections = vertical_lines_intersections[i * 2]; + const auto &right_intersections = vertical_lines_intersections[i * 2 + 2]; + + for (int intersection_idx = 0; intersection_idx < int(central_intersections.size()) - 1; intersection_idx++) { + const auto &a = central_intersections[intersection_idx]; + const auto &b = central_intersections[intersection_idx + 1]; + if (area_walls.outside((a.first + b.first) / 2) < 0) { + // central part is inside. Now check for reasonable side distances + + auto get_closest_intersection_squared_dist = + [](const std::pair, size_t> &point, + const std::vector, size_t>> &sorted_intersections) { + if (sorted_intersections.empty()) { + return 0.0; + } + auto closest_higher = std::upper_bound(sorted_intersections.begin(), sorted_intersections.end(), point, + [](const std::pair, size_t> &left, + const std::pair, size_t> &right) { + return left.first.y() < right.first.y(); + }); + if (closest_higher == sorted_intersections.end()) { + return (point.first - sorted_intersections.back().first).cast().squaredNorm(); + } + double candidate_dist = (point.first - closest_higher->first).cast().squaredNorm(); + if (closest_higher != sorted_intersections.begin()) { + double closest_lower_dist = (point.first - (closest_higher--)->first).cast().squaredNorm(); + candidate_dist = std::min(candidate_dist, closest_lower_dist); + } + return candidate_dist; + }; + Point section_a = a.first; + Point section_b = b.first; + + double max_a_dist = std::max(get_closest_intersection_squared_dist(a, left_intersections), + get_closest_intersection_squared_dist(a, right_intersections)); + + double max_b_dist = std::max(get_closest_intersection_squared_dist(b, left_intersections), + get_closest_intersection_squared_dist(b, right_intersections)); + + if (max_a_dist > 0.5 * squared_distance_limit_reconnection) { + section_a.y() += std::min(2.0 * scaled_spacing, sqrt(max_a_dist)); + } + + if (max_b_dist > 0.5 * squared_distance_limit_reconnection) { + section_b.y() -= std::min(2.0 * scaled_spacing, sqrt(max_b_dist)); + } + + section_a.y() = std::min(section_a.y(), section_b.y()); + section_b.y() = std::max(section_a.y(), section_b.y()); + polygon_sections[i].emplace_back(section_a, section_b); } } - - for (int section_idx = 0; section_idx < int(polygon_sections[i].size()) - 1; section_idx++) { - Line §ion_a = polygon_sections[i][section_idx]; - Line §ion_b = polygon_sections[i][section_idx + 1]; - if (segments_overlap(section_a.a.y(), section_a.b.y(), section_b.a.y(), section_b.b.y())) { - section_b.a = section_a.a.y() < section_b.a.y() ? section_a.a : section_b.a; - section_b.b = section_a.b.y() < section_b.b.y() ? section_b.b : section_a.b; - section_a.a = section_a.b; - } - } - - polygon_sections[i].erase(std::remove_if(polygon_sections[i].begin(), polygon_sections[i].end(), - [](const Line &s) { return s.a == s.b; }), - polygon_sections[i].end()); - std::sort(polygon_sections[i].begin(), polygon_sections[i].end(), [](const Line &a, const Line &b) { return a.a.y() < b.b.y(); }); } - struct Node{ + struct Node + { int section_idx; int line_idx; int skips_taken = 0; @@ -101,7 +143,7 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const coord_t length_filter = scale_(4); size_t skips_allowed = 2; - size_t min_removal_conut = 3; + size_t min_removal_conut = 4; for (int section_idx = 0; section_idx < polygon_sections.size(); section_idx++) { for (int line_idx = 0; line_idx < polygon_sections[section_idx].size(); line_idx++) { if (const Line &line = polygon_sections[section_idx][line_idx]; line.a != line.b && line.length() < length_filter) { @@ -172,22 +214,14 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const [](const Line &a, const Line &b) { return a.a.y() < b.b.y(); }); } - double squared_distance_limit_reconnection = 4 * double(scaled_spacing) * double(scaled_spacing); - ThickPolylines thick_polylines_out; { - struct TracedPath { - ThickPolyline polyline; - const Line* origin_line; - }; - - std::vector current_traced_paths; + ThickPolylines current_traced_paths; for (const auto &polygon_slice : polygon_sections) { std::unordered_set used_segments; - for (TracedPath &traced_path : current_traced_paths) { - ThickPolyline& traced_polyline = traced_path.polyline; - Point max_y = traced_polyline.last_point(); - Point min_y = traced_polyline.points[traced_polyline.size() - 2]; + for (ThickPolyline &traced_path : current_traced_paths) { + Point max_y = traced_path.last_point(); + Point min_y = traced_path.points[traced_path.size() - 2]; if (max_y.y() < min_y.y()) std::swap(max_y, min_y); @@ -201,23 +235,23 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const if (used_segments.find(&(*candidate)) != used_segments.end()) { continue; } - if ((traced_polyline.last_point() - candidate->a).cast().squaredNorm() < squared_distance_limit_reconnection) { - traced_polyline.width.push_back(scaled_spacing); - traced_polyline.points.push_back(candidate->a); - traced_polyline.width.push_back(scaled_spacing); - traced_polyline.width.push_back(scaled_spacing); - traced_polyline.points.push_back(candidate->b); - traced_polyline.width.push_back(scaled_spacing); + if ((traced_path.last_point() - candidate->a).cast().squaredNorm() < squared_distance_limit_reconnection) { + traced_path.width.push_back(scaled_spacing); + traced_path.points.push_back(candidate->a); + traced_path.width.push_back(scaled_spacing); + traced_path.width.push_back(scaled_spacing); + traced_path.points.push_back(candidate->b); + traced_path.width.push_back(scaled_spacing); used_segments.insert(&(*candidate)); segment_added = true; - } else if ((traced_polyline.last_point() - candidate->b).cast().squaredNorm() < + } else if ((traced_path.last_point() - candidate->b).cast().squaredNorm() < squared_distance_limit_reconnection) { - traced_polyline.width.push_back(scaled_spacing); - traced_polyline.points.push_back(candidate->b); - traced_polyline.width.push_back(scaled_spacing); - traced_polyline.width.push_back(scaled_spacing); - traced_polyline.points.push_back(candidate->a); - traced_polyline.width.push_back(scaled_spacing); + traced_path.width.push_back(scaled_spacing); + traced_path.points.push_back(candidate->b); + traced_path.width.push_back(scaled_spacing); + traced_path.width.push_back(scaled_spacing); + traced_path.points.push_back(candidate->a); + traced_path.width.push_back(scaled_spacing); used_segments.insert(&(*candidate)); segment_added = true; } @@ -225,8 +259,8 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const if (!segment_added) { // Zero overlapping segments. Finish the polyline. - thick_polylines_out.push_back(std::move(traced_polyline)); - traced_polyline.clear(); + thick_polylines_out.push_back(std::move(traced_path)); + traced_path.clear(); } } @@ -236,13 +270,12 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const for (const Line &segment : polygon_slice) { if (used_segments.find(&segment) == used_segments.end()) { - TracedPath &new_path = current_traced_paths.emplace_back(); - new_path.polyline.points.push_back(segment.a); - new_path.polyline.width.push_back(scaled_spacing); - new_path.polyline.points.push_back(segment.b); - new_path.polyline.width.push_back(scaled_spacing); - new_path.polyline.endpoints = {true, true}; - new_path.origin_line = &segment; + ThickPolyline &new_path = current_traced_paths.emplace_back(); + new_path.points.push_back(segment.a); + new_path.width.push_back(scaled_spacing); + new_path.points.push_back(segment.b); + new_path.width.push_back(scaled_spacing); + new_path.endpoints = {true, true}; } } } From 1279b4d22a4a9f22b3be7931a46a92e0c929f070 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 20 Apr 2023 15:08:45 +0200 Subject: [PATCH 41/90] fix some errors --- src/libslic3r/Fill/FillEnsuring.cpp | 30 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/libslic3r/Fill/FillEnsuring.cpp b/src/libslic3r/Fill/FillEnsuring.cpp index 32dfd706f5..3e0c37a348 100644 --- a/src/libslic3r/Fill/FillEnsuring.cpp +++ b/src/libslic3r/Fill/FillEnsuring.cpp @@ -22,8 +22,6 @@ namespace Slic3r { -static constexpr const float NarrowInfillAreaThresholdMM = 2.f; - ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const FillParams ¶ms) { assert(params.use_arachne); @@ -51,22 +49,21 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const double aligning_angle = -this->angle + PI * 0.5; polygons_rotate(filled_area, aligning_angle); Polygons internal_area = shrink(filled_area, 0.5 * scaled_spacing - scale_(this->overlap)); - Polygons openned_area = opening(internal_area, scale_(NarrowInfillAreaThresholdMM)); - BoundingBox bb = get_extents(openned_area); + BoundingBox bb = get_extents(filled_area); const size_t n_vlines = (bb.max.x() - bb.min.x() + scaled_spacing - 1) / scaled_spacing; std::vector vertical_lines(2 * n_vlines + 1); coord_t y_min = bb.min.y(); coord_t y_max = bb.max.y(); for (size_t i = 0; i < n_vlines; i++) { - coord_t x0 = bb.min.x() + i * scaled_spacing - scaled_spacing * 0.5; - coord_t x1 = bb.min.x() + i * scaled_spacing; + coord_t x0 = bb.min.x() + i * double(scaled_spacing) - scaled_spacing * 0.5; + coord_t x1 = bb.min.x() + i * double(scaled_spacing); vertical_lines[i * 2].a = Point{x0, y_min}; vertical_lines[i * 2].b = Point{x0, y_max}; vertical_lines[i * 2 + 1].a = Point{x1, y_min}; vertical_lines[i * 2 + 1].b = Point{x1, y_max}; } - vertical_lines.back().a = Point{coord_t(bb.min.x() + n_vlines * scaled_spacing + scaled_spacing * 0.5), y_min}; + vertical_lines.back().a = Point{coord_t(bb.min.x() + n_vlines * double(scaled_spacing) + scaled_spacing * 0.5), y_min}; vertical_lines.back().b = Point{vertical_lines.back().a.x(), y_max}; auto area_walls = AABBTreeLines::LinesDistancer{to_lines(internal_area)}; @@ -111,18 +108,18 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const Point section_a = a.first; Point section_b = b.first; - double max_a_dist = std::max(get_closest_intersection_squared_dist(a, left_intersections), + double max_a_squared_dist = std::max(get_closest_intersection_squared_dist(a, left_intersections), get_closest_intersection_squared_dist(a, right_intersections)); - double max_b_dist = std::max(get_closest_intersection_squared_dist(b, left_intersections), + double max_b_squared_dist = std::max(get_closest_intersection_squared_dist(b, left_intersections), get_closest_intersection_squared_dist(b, right_intersections)); - if (max_a_dist > 0.5 * squared_distance_limit_reconnection) { - section_a.y() += std::min(2.0 * scaled_spacing, sqrt(max_a_dist)); + if (max_a_squared_dist > 0.4 * squared_distance_limit_reconnection) { + section_a.y() += std::min(2.0 * scaled_spacing, sqrt(max_a_squared_dist)); } - if (max_b_dist > 0.5 * squared_distance_limit_reconnection) { - section_b.y() -= std::min(2.0 * scaled_spacing, sqrt(max_b_dist)); + if (max_b_squared_dist > 0.4 * squared_distance_limit_reconnection) { + section_b.y() -= std::min(2.0 * scaled_spacing, sqrt(max_b_squared_dist)); } section_a.y() = std::min(section_a.y(), section_b.y()); @@ -370,9 +367,10 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const BoundingBox bbox = get_extents(filled_area); bbox.offset(scale_(1.)); ::Slic3r::SVG svg(debug_out_path(("surface" + std::to_string(surface->area())).c_str()).c_str(), bbox); - svg.draw(to_lines(filled_area), "red", scale_(0.3)); - svg.draw(to_lines(reconstructed_area), "blue", scale_(0.2)); - svg.draw(to_lines(gaps_for_additional_filling), "green", scale_(0.1)); + svg.draw(to_lines(filled_area), "red", scale_(0.4)); + svg.draw(to_lines(reconstructed_area), "blue", scale_(0.3)); + svg.draw(to_lines(gaps_for_additional_filling), "green", scale_(0.2)); + svg.draw(vertical_lines, "black", scale_(0.1)); svg.Close(); for (ExPolygon &ex_poly : gaps_for_additional_filling) { From 2c12a7f164cc47560737eaebd2906732ce54fba1 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 20 Apr 2023 17:26:54 +0200 Subject: [PATCH 42/90] Increased line shortening near steep walls. --- src/libslic3r/Fill/FillEnsuring.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/libslic3r/Fill/FillEnsuring.cpp b/src/libslic3r/Fill/FillEnsuring.cpp index 3e0c37a348..33b1cb8b24 100644 --- a/src/libslic3r/Fill/FillEnsuring.cpp +++ b/src/libslic3r/Fill/FillEnsuring.cpp @@ -100,7 +100,7 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const } double candidate_dist = (point.first - closest_higher->first).cast().squaredNorm(); if (closest_higher != sorted_intersections.begin()) { - double closest_lower_dist = (point.first - (closest_higher--)->first).cast().squaredNorm(); + double closest_lower_dist = (point.first - (--closest_higher)->first).cast().squaredNorm(); candidate_dist = std::min(candidate_dist, closest_lower_dist); } return candidate_dist; @@ -109,22 +109,22 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const Point section_b = b.first; double max_a_squared_dist = std::max(get_closest_intersection_squared_dist(a, left_intersections), - get_closest_intersection_squared_dist(a, right_intersections)); + get_closest_intersection_squared_dist(a, right_intersections)); double max_b_squared_dist = std::max(get_closest_intersection_squared_dist(b, left_intersections), - get_closest_intersection_squared_dist(b, right_intersections)); + get_closest_intersection_squared_dist(b, right_intersections)); - if (max_a_squared_dist > 0.4 * squared_distance_limit_reconnection) { - section_a.y() += std::min(2.0 * scaled_spacing, sqrt(max_a_squared_dist)); + if (max_a_squared_dist > 0.3 * squared_distance_limit_reconnection) { + section_a.y() += 4.0 * scaled_spacing; } - if (max_b_squared_dist > 0.4 * squared_distance_limit_reconnection) { - section_b.y() -= std::min(2.0 * scaled_spacing, sqrt(max_b_squared_dist)); + if (max_b_squared_dist > 0.3 * squared_distance_limit_reconnection) { + section_b.y() -= 4.0 * scaled_spacing; } - section_a.y() = std::min(section_a.y(), section_b.y()); - section_b.y() = std::max(section_a.y(), section_b.y()); - polygon_sections[i].emplace_back(section_a, section_b); + if (section_a.y() < section_b.y()) { + polygon_sections[i].emplace_back(section_a, section_b); + } } } } @@ -362,7 +362,7 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const if (this->overlap != 0) { gaps_for_additional_filling = offset_ex(gaps_for_additional_filling, scaled(this->overlap)); } - gaps_for_additional_filling = opening_ex(gaps_for_additional_filling, 0.3 * scaled_spacing); + // gaps_for_additional_filling = opening_ex(gaps_for_additional_filling, 0.3 * scaled_spacing); BoundingBox bbox = get_extents(filled_area); bbox.offset(scale_(1.)); @@ -430,6 +430,9 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const } } + // reconnect ThickPolylines + + rotate_thick_polylines(thick_polylines_out, cos(-aligning_angle), sin(-aligning_angle)); return thick_polylines_out; From 54eb19d589075f101288d67230aaa64721849151 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 21 Apr 2023 17:19:43 +0200 Subject: [PATCH 43/90] Connect thick polylines where applicable --- src/libslic3r/Fill/FillEnsuring.cpp | 223 ++++++++++++++++++++-------- 1 file changed, 159 insertions(+), 64 deletions(-) diff --git a/src/libslic3r/Fill/FillEnsuring.cpp b/src/libslic3r/Fill/FillEnsuring.cpp index 33b1cb8b24..935299b07c 100644 --- a/src/libslic3r/Fill/FillEnsuring.cpp +++ b/src/libslic3r/Fill/FillEnsuring.cpp @@ -5,6 +5,7 @@ #include "AABBTreeLines.hpp" #include "ExPolygon.hpp" #include "FillEnsuring.hpp" +#include "KDTreeIndirect.hpp" #include "Line.hpp" #include "Point.hpp" #include "Polygon.hpp" @@ -44,7 +45,8 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const }; const coord_t scaled_spacing = scaled(this->spacing); - double squared_distance_limit_reconnection = 4 * double(scaled_spacing) * double(scaled_spacing); + double distance_limit_reconnection = 2 * double(scaled_spacing); + double squared_distance_limit_reconnection = distance_limit_reconnection * distance_limit_reconnection; Polygons filled_area = to_polygons(surface->expolygon); double aligning_angle = -this->angle + PI * 0.5; polygons_rotate(filled_area, aligning_angle); @@ -52,78 +54,29 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const BoundingBox bb = get_extents(filled_area); const size_t n_vlines = (bb.max.x() - bb.min.x() + scaled_spacing - 1) / scaled_spacing; - std::vector vertical_lines(2 * n_vlines + 1); + std::vector vertical_lines(n_vlines); coord_t y_min = bb.min.y(); coord_t y_max = bb.max.y(); for (size_t i = 0; i < n_vlines; i++) { - coord_t x0 = bb.min.x() + i * double(scaled_spacing) - scaled_spacing * 0.5; - coord_t x1 = bb.min.x() + i * double(scaled_spacing); - vertical_lines[i * 2].a = Point{x0, y_min}; - vertical_lines[i * 2].b = Point{x0, y_max}; - vertical_lines[i * 2 + 1].a = Point{x1, y_min}; - vertical_lines[i * 2 + 1].b = Point{x1, y_max}; + coord_t x = bb.min.x() + i * double(scaled_spacing); + vertical_lines[i].a = Point{x, y_min}; + vertical_lines[i].b = Point{x, y_max}; } vertical_lines.back().a = Point{coord_t(bb.min.x() + n_vlines * double(scaled_spacing) + scaled_spacing * 0.5), y_min}; vertical_lines.back().b = Point{vertical_lines.back().a.x(), y_max}; - auto area_walls = AABBTreeLines::LinesDistancer{to_lines(internal_area)}; - std::vector, size_t>>> vertical_lines_intersections(vertical_lines.size()); - for (int i = 0; i < vertical_lines.size(); i++) { - vertical_lines_intersections[i] = area_walls.intersections_with_line(vertical_lines[i]); - } + auto area_walls = AABBTreeLines::LinesDistancer{to_lines(intersection(filled_area, opening(filled_area, scale_(2), scale_(3))))}; std::vector> polygon_sections(n_vlines); for (size_t i = 0; i < n_vlines; i++) { - const auto ¢ral_intersections = vertical_lines_intersections[i * 2 + 1]; - const auto &left_intersections = vertical_lines_intersections[i * 2]; - const auto &right_intersections = vertical_lines_intersections[i * 2 + 2]; + const auto intersections = area_walls.intersections_with_line(vertical_lines[i]); - for (int intersection_idx = 0; intersection_idx < int(central_intersections.size()) - 1; intersection_idx++) { - const auto &a = central_intersections[intersection_idx]; - const auto &b = central_intersections[intersection_idx + 1]; + for (int intersection_idx = 0; intersection_idx < int(intersections.size()) - 1; intersection_idx++) { + const auto &a = intersections[intersection_idx]; + const auto &b = intersections[intersection_idx + 1]; if (area_walls.outside((a.first + b.first) / 2) < 0) { - // central part is inside. Now check for reasonable side distances - - auto get_closest_intersection_squared_dist = - [](const std::pair, size_t> &point, - const std::vector, size_t>> &sorted_intersections) { - if (sorted_intersections.empty()) { - return 0.0; - } - auto closest_higher = std::upper_bound(sorted_intersections.begin(), sorted_intersections.end(), point, - [](const std::pair, size_t> &left, - const std::pair, size_t> &right) { - return left.first.y() < right.first.y(); - }); - if (closest_higher == sorted_intersections.end()) { - return (point.first - sorted_intersections.back().first).cast().squaredNorm(); - } - double candidate_dist = (point.first - closest_higher->first).cast().squaredNorm(); - if (closest_higher != sorted_intersections.begin()) { - double closest_lower_dist = (point.first - (--closest_higher)->first).cast().squaredNorm(); - candidate_dist = std::min(candidate_dist, closest_lower_dist); - } - return candidate_dist; - }; - Point section_a = a.first; - Point section_b = b.first; - - double max_a_squared_dist = std::max(get_closest_intersection_squared_dist(a, left_intersections), - get_closest_intersection_squared_dist(a, right_intersections)); - - double max_b_squared_dist = std::max(get_closest_intersection_squared_dist(b, left_intersections), - get_closest_intersection_squared_dist(b, right_intersections)); - - if (max_a_squared_dist > 0.3 * squared_distance_limit_reconnection) { - section_a.y() += 4.0 * scaled_spacing; - } - - if (max_b_squared_dist > 0.3 * squared_distance_limit_reconnection) { - section_b.y() -= 4.0 * scaled_spacing; - } - - if (section_a.y() < section_b.y()) { - polygon_sections[i].emplace_back(section_a, section_b); + if (std::abs(a.first.y() - b.first.y()) > scaled_spacing) { + polygon_sections[i].emplace_back(a.first, b.first); } } } @@ -140,7 +93,7 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const coord_t length_filter = scale_(4); size_t skips_allowed = 2; - size_t min_removal_conut = 4; + size_t min_removal_conut = 5; for (int section_idx = 0; section_idx < polygon_sections.size(); section_idx++) { for (int line_idx = 0; line_idx < polygon_sections[section_idx].size(); line_idx++) { if (const Line &line = polygon_sections[section_idx][line_idx]; line.a != line.b && line.length() < length_filter) { @@ -233,8 +186,9 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const continue; } if ((traced_path.last_point() - candidate->a).cast().squaredNorm() < squared_distance_limit_reconnection) { + traced_path.points.back() += Point{0.0, scaled_spacing * 0.5}; traced_path.width.push_back(scaled_spacing); - traced_path.points.push_back(candidate->a); + traced_path.points.push_back(candidate->a + Point{0.0, scaled_spacing * 0.5}); traced_path.width.push_back(scaled_spacing); traced_path.width.push_back(scaled_spacing); traced_path.points.push_back(candidate->b); @@ -243,8 +197,9 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const segment_added = true; } else if ((traced_path.last_point() - candidate->b).cast().squaredNorm() < squared_distance_limit_reconnection) { + traced_path.points.back() -= Point{0.0, scaled_spacing * 0.5}; traced_path.width.push_back(scaled_spacing); - traced_path.points.push_back(candidate->b); + traced_path.points.push_back(candidate->b - Point{0.0, scaled_spacing * 0.5}); traced_path.width.push_back(scaled_spacing); traced_path.width.push_back(scaled_spacing); traced_path.points.push_back(candidate->a); @@ -431,6 +386,64 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const } // reconnect ThickPolylines + struct EndPoint + { + Vec2d position; + size_t polyline_idx; + size_t other_end_point_idx; + bool is_first; + bool used = false; + }; + std::vector connection_endpoints; + connection_endpoints.reserve(thick_polylines_out.size() * 2); + for (size_t pl_idx = 0; pl_idx < thick_polylines_out.size(); pl_idx++) { + size_t current_idx = connection_endpoints.size(); + connection_endpoints.push_back({thick_polylines_out[pl_idx].first_point().cast(), pl_idx, current_idx + 1, true}); + connection_endpoints.push_back({thick_polylines_out[pl_idx].last_point().cast(), pl_idx, current_idx, false}); + } + auto coord_fn = [&connection_endpoints](size_t idx, size_t dim) { return connection_endpoints[idx].position[dim]; }; + KDTreeIndirect<2, double, decltype(coord_fn)> endpoints_tree{coord_fn, connection_endpoints.size()}; + for (size_t ep_idx = 0; ep_idx < connection_endpoints.size(); ep_idx++) { + EndPoint &ep = connection_endpoints[ep_idx]; + if (!ep.used) { + std::vector close_endpoints = find_nearby_points(endpoints_tree, ep.position, scaled_spacing); + for (size_t close_endpoint_idx : close_endpoints) { + EndPoint &ep2 = connection_endpoints[close_endpoint_idx]; + if (ep2.used || ep2.polyline_idx == ep.polyline_idx) { + continue; + } + + // connect ep and ep2; + ThickPolyline &tp1 = thick_polylines_out[ep.polyline_idx]; + ThickPolyline &tp2 = thick_polylines_out[ep2.polyline_idx]; + + if (ep.is_first) { + tp1.reverse(); + ep.is_first = false; + connection_endpoints[ep.other_end_point_idx].is_first = true; + } + + if (!ep2.is_first) { + tp2.reverse(); + ep2.is_first = true; + connection_endpoints[ep2.other_end_point_idx].is_first = false; + } + + tp1.points.insert(tp1.points.end(), tp2.points.begin(), tp2.points.end()); + tp1.width.push_back(tp1.width.back()); + tp1.width.push_back(tp2.width.front()); + tp1.width.insert(tp1.width.end(), tp2.width.begin(), tp2.width.end()); + ep2.used = true; + ep.used = true; + connection_endpoints[ep2.other_end_point_idx].polyline_idx = ep.polyline_idx; + connection_endpoints[ep2.other_end_point_idx].other_end_point_idx = ep_idx; + connection_endpoints[ep.other_end_point_idx].other_end_point_idx = close_endpoint_idx; + tp2.clear(); + break; + } + } + } + rotate_thick_polylines(thick_polylines_out, cos(-aligning_angle), sin(-aligning_angle)); @@ -439,3 +452,85 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const } } // namespace Slic3r + + + + + + // const size_t n_vlines = (bb.max.x() - bb.min.x() + scaled_spacing - 1) / scaled_spacing; + // std::vector vertical_lines(2 * n_vlines + 1); + // coord_t y_min = bb.min.y(); + // coord_t y_max = bb.max.y(); + // for (size_t i = 0; i < n_vlines; i++) { + // coord_t x0 = bb.min.x() + i * double(scaled_spacing) - scaled_spacing * 0.5; + // coord_t x1 = bb.min.x() + i * double(scaled_spacing); + // vertical_lines[i * 2].a = Point{x0, y_min}; + // vertical_lines[i * 2].b = Point{x0, y_max}; + // vertical_lines[i * 2 + 1].a = Point{x1, y_min}; + // vertical_lines[i * 2 + 1].b = Point{x1, y_max}; + // } + // vertical_lines.back().a = Point{coord_t(bb.min.x() + n_vlines * double(scaled_spacing) + scaled_spacing * 0.5), y_min}; + // vertical_lines.back().b = Point{vertical_lines.back().a.x(), y_max}; + + // auto area_walls = AABBTreeLines::LinesDistancer{to_lines(internal_area)}; + // std::vector, size_t>>> vertical_lines_intersections(vertical_lines.size()); + // for (int i = 0; i < vertical_lines.size(); i++) { + // vertical_lines_intersections[i] = area_walls.intersections_with_line(vertical_lines[i]); + // } + // std::vector> polygon_sections(n_vlines); + + // for (size_t i = 0; i < n_vlines; i++) { + // const auto ¢ral_intersections = vertical_lines_intersections[i * 2 + 1]; + // const auto &left_intersections = vertical_lines_intersections[i * 2]; + // const auto &right_intersections = vertical_lines_intersections[i * 2 + 2]; + + // for (int intersection_idx = 0; intersection_idx < int(central_intersections.size()) - 1; intersection_idx++) { + // const auto &a = central_intersections[intersection_idx]; + // const auto &b = central_intersections[intersection_idx + 1]; + // if (area_walls.outside((a.first + b.first) / 2) < 0) { + // // central part is inside. Now check for reasonable side distances + + // auto get_closest_intersection_squared_dist = + // [](const std::pair, size_t> &point, + // const std::vector, size_t>> &sorted_intersections) { + // if (sorted_intersections.empty()) { + // return 0.0; + // } + // auto closest_higher = std::upper_bound(sorted_intersections.begin(), sorted_intersections.end(), point, + // [](const std::pair, size_t> &left, + // const std::pair, size_t> &right) { + // return left.first.y() < right.first.y(); + // }); + // if (closest_higher == sorted_intersections.end()) { + // return (point.first - sorted_intersections.back().first).cast().squaredNorm(); + // } + // double candidate_dist = (point.first - closest_higher->first).cast().squaredNorm(); + // if (closest_higher != sorted_intersections.begin()) { + // double closest_lower_dist = (point.first - (--closest_higher)->first).cast().squaredNorm(); + // candidate_dist = std::min(candidate_dist, closest_lower_dist); + // } + // return candidate_dist; + // }; + // Point section_a = a.first; + // Point section_b = b.first; + + // double max_a_squared_dist = std::max(get_closest_intersection_squared_dist(a, left_intersections), + // get_closest_intersection_squared_dist(a, right_intersections)); + + // double max_b_squared_dist = std::max(get_closest_intersection_squared_dist(b, left_intersections), + // get_closest_intersection_squared_dist(b, right_intersections)); + + // if (max_a_squared_dist > 0.3 * squared_distance_limit_reconnection) { + // section_a.y() += 4.0 * scaled_spacing; + // } + + // if (max_b_squared_dist > 0.3 * squared_distance_limit_reconnection) { + // section_b.y() -= 4.0 * scaled_spacing; + // } + + // if (section_a.y() < section_b.y()) { + // polygon_sections[i].emplace_back(section_a, section_b); + // } + // } + // } + // } \ No newline at end of file From 663bbbaa5e74ace4ba9f63ef9779d08a729689c9 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 24 Apr 2023 14:06:52 +0200 Subject: [PATCH 44/90] fix angle, filter tiny drops, fix thin regions filtering --- src/libslic3r/Fill/FillEnsuring.cpp | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/Fill/FillEnsuring.cpp b/src/libslic3r/Fill/FillEnsuring.cpp index 935299b07c..3b4dd6b549 100644 --- a/src/libslic3r/Fill/FillEnsuring.cpp +++ b/src/libslic3r/Fill/FillEnsuring.cpp @@ -44,11 +44,12 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const (bhigh >= alow && bhigh <= ahigh); }; - const coord_t scaled_spacing = scaled(this->spacing); - double distance_limit_reconnection = 2 * double(scaled_spacing); - double squared_distance_limit_reconnection = distance_limit_reconnection * distance_limit_reconnection; - Polygons filled_area = to_polygons(surface->expolygon); - double aligning_angle = -this->angle + PI * 0.5; + const coord_t scaled_spacing = scaled(this->spacing); + double distance_limit_reconnection = 2 * double(scaled_spacing); + double squared_distance_limit_reconnection = distance_limit_reconnection * distance_limit_reconnection; + Polygons filled_area = to_polygons(surface->expolygon); + std::pair rotate_vector = this->_infill_direction(surface); + double aligning_angle = -rotate_vector.first + PI; polygons_rotate(filled_area, aligning_angle); Polygons internal_area = shrink(filled_area, 0.5 * scaled_spacing - scale_(this->overlap)); BoundingBox bb = get_extents(filled_area); @@ -65,7 +66,8 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const vertical_lines.back().a = Point{coord_t(bb.min.x() + n_vlines * double(scaled_spacing) + scaled_spacing * 0.5), y_min}; vertical_lines.back().b = Point{vertical_lines.back().a.x(), y_max}; - auto area_walls = AABBTreeLines::LinesDistancer{to_lines(intersection(filled_area, opening(filled_area, scale_(2), scale_(3))))}; + auto area_walls = AABBTreeLines::LinesDistancer{ + to_lines(intersection(filled_area, opening(filled_area, 2 * scaled_spacing, 3 * scaled_spacing)))}; std::vector> polygon_sections(n_vlines); for (size_t i = 0; i < n_vlines; i++) { @@ -444,10 +446,16 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const } } - - rotate_thick_polylines(thick_polylines_out, cos(-aligning_angle), sin(-aligning_angle)); + thick_polylines_out.erase(std::remove_if(thick_polylines_out.begin(), thick_polylines_out.end(), + [scaled_spacing](const ThickPolyline &tp) { + return tp.length() < scaled_spacing && + std::all_of(tp.width.begin(), tp.width.end(), + [scaled_spacing](double w) { return w < scaled_spacing; }); + }), + thick_polylines_out.end()); + return thick_polylines_out; } From ebbbb1505cb904dc7fbe10dfdd3be5c20d6c6326 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 24 Apr 2023 17:29:59 +0200 Subject: [PATCH 45/90] Move and enhance path sorting, fix bug caused by new hash set --- src/libslic3r/Algorithm/PathSorting.hpp | 174 ++++++++++++++++++++++++ src/libslic3r/CMakeLists.txt | 3 +- src/libslic3r/Fill/FillEnsuring.cpp | 36 +++-- src/libslic3r/PerimeterGenerator.cpp | 2 +- 4 files changed, 199 insertions(+), 16 deletions(-) create mode 100644 src/libslic3r/Algorithm/PathSorting.hpp diff --git a/src/libslic3r/Algorithm/PathSorting.hpp b/src/libslic3r/Algorithm/PathSorting.hpp new file mode 100644 index 0000000000..a4806db82d --- /dev/null +++ b/src/libslic3r/Algorithm/PathSorting.hpp @@ -0,0 +1,174 @@ +#ifndef SRC_LIBSLIC3R_PATH_SORTING_HPP_ +#define SRC_LIBSLIC3R_PATH_SORTING_HPP_ + +#include "AABBTreeLines.hpp" +#include "ankerl/unordered_dense.h" +#include +#include +#include +#include +#include +#include +#include + +namespace Slic3r { +namespace Algorithm { + +//Sorts the paths such that all paths between begin and last_seed are printed first, in some order. The rest of the paths is sorted +// such that the paths that are touching some of the already printed are printed first, sorted secondary by the distance to the last point of the last +// printed path. +// begin, end, and last_seed are random access iterators. touch_limit_distance is used to check if the paths are touching - if any part of the path gets this close +// to the second, then they touch. +// convert_to_lines is a lambda that should accept the path as argument and return it as Lines vector, in correct order. +template +void sort_paths(RandomAccessIterator begin, RandomAccessIterator end, RandomAccessIterator last_seed, double touch_limit_distance, ToLines convert_to_lines) +{ + size_t paths_count = std::distance(begin, end); + if (paths_count <= 1) + return; + + std::vector> distancers(paths_count); + for (size_t path_idx = 0; path_idx < paths_count; path_idx++) { + distancers[path_idx] = AABBTreeLines::LinesDistancer{convert_to_lines(*std::next(begin, path_idx))}; + } + + auto paths_touch = [touch_limit_distance](const AABBTreeLines::LinesDistancer &left, + const AABBTreeLines::LinesDistancer &right) { + for (const Line &l : left.get_lines()) { + if (right.distance_from_lines(l.a) < touch_limit_distance) { + return true; + } + } + if (right.distance_from_lines(left.get_lines().back().b) < touch_limit_distance) { + return true; + } + + for (const Line &l : right.get_lines()) { + if (left.distance_from_lines(l.a) < touch_limit_distance) { + return true; + } + } + if (left.distance_from_lines(right.get_lines().back().b) < touch_limit_distance) { + return true; + } + return false; + }; + + std::vector> dependencies(paths_count); + for (size_t path_idx = 0; path_idx < paths_count; path_idx++) { + for (size_t prev_path_idx = 0; prev_path_idx < path_idx; prev_path_idx++) { + if (paths_touch(distancers[path_idx], distancers[prev_path_idx])) { + dependencies[path_idx].insert(prev_path_idx); + dependencies[prev_path_idx].insert(path_idx); + } + } + } + + size_t index_of_last_fixed = std::distance(begin, last_seed); + + std::vector processed(paths_count, false); + for (size_t path_idx = 0; path_idx <= index_of_last_fixed; path_idx++) { + processed[path_idx] = true; + } + + for (size_t i = index_of_last_fixed + 1; i < paths_count; i++) { + bool change = false; + for (size_t path_idx = index_of_last_fixed + 1; path_idx < paths_count; path_idx++) { + if (processed[path_idx]) + continue; + auto processed_dep = std::find_if(dependencies[path_idx].begin(), dependencies[path_idx].end(), + [&](size_t dep) { return processed[dep]; }); + if (processed_dep != dependencies[path_idx].end()) { + for (auto it = dependencies[path_idx].begin(); it != dependencies[path_idx].end();) { + if (!processed[*it]) { + dependencies[*it].insert(path_idx); + dependencies[path_idx].erase(it++); + } else { + ++it; + } + } + processed[path_idx] = true; + change = true; + } + } + if (!change) { + break; + } + } + + Point current_point = distancers.begin()->get_lines().begin()->a; + + size_t null_idx = size_t(-1); + size_t unsorted_idx = 0; + size_t next_idx = null_idx; + bool reverse = false; + while (true) { + if (next_idx == null_idx) { // find next pidx to print + double dist = std::numeric_limits::max(); + for (size_t path_idx = 0; path_idx < paths_count; path_idx++) { + if (!dependencies[path_idx].empty()) + continue; + const auto& lines = distancers[path_idx].get_lines(); + double dist_a = (lines.front().a - current_point).cast().squaredNorm(); + if (dist_a < dist) { + dist = dist_a; + next_idx = path_idx; + reverse = false; + } + double dist_b = (lines.back().b - current_point).cast().squaredNorm(); + if (dist_b < dist) { + dist = dist_b; + next_idx = path_idx; + reverse = true; + } + } + if (next_idx == null_idx) { + break; + } + } else { + // we have valid next_idx, sort it, update dependencies, update current point and potentialy set new next_idx + std::iter_swap(std::next(begin, unsorted_idx), std::next(begin, next_idx)); // next_path is now at sorted spot + if (reverse) { + std::next(begin, unsorted_idx)->reverse(); + } + unsorted_idx++; + + assert(dependencies[next_idx].empty()); + dependencies[next_idx].insert(null_idx); + current_point = reverse ? distancers[next_idx].get_lines().front().a : distancers[next_idx].get_lines().back().b; + for (size_t path_idx = 0; path_idx < paths_count; path_idx++) { + dependencies[path_idx].erase(next_idx); + } + double dist = std::numeric_limits::max(); + next_idx = null_idx; + + for (size_t path_idx = next_idx + 1; path_idx < paths_count; path_idx++) { + if (!dependencies[path_idx].empty()) { + continue; + } + const auto &lines = distancers[path_idx].get_lines(); + double dist_a = (lines.front().a - current_point).cast().squaredNorm(); + if (dist_a < dist) { + dist = dist_a; + next_idx = path_idx; + reverse = false; + } + double dist_b = (lines.back().b - current_point).cast().squaredNorm(); + if (dist_b < dist) { + dist = dist_b; + next_idx = path_idx; + reverse = true; + } + } + if (dist > scaled(5.0)) { + next_idx = null_idx; + } + } + } +} + + +} +} + +#endif /*SRC_LIBSLIC3R_PATH_SORTING_HPP_*/ \ No newline at end of file diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 297d2e3ff5..b94f94d9b7 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -22,8 +22,9 @@ set(SLIC3R_SOURCES AABBTreeLines.hpp AABBMesh.hpp AABBMesh.cpp - Algorithm/RegionExpansion.cpp + Algorithm/PathSorting.hpp Algorithm/RegionExpansion.hpp + Algorithm/RegionExpansion.cpp AnyPtr.hpp BoundingBox.cpp BoundingBox.hpp diff --git a/src/libslic3r/Fill/FillEnsuring.cpp b/src/libslic3r/Fill/FillEnsuring.cpp index 3b4dd6b549..e97b6c4790 100644 --- a/src/libslic3r/Fill/FillEnsuring.cpp +++ b/src/libslic3r/Fill/FillEnsuring.cpp @@ -3,6 +3,7 @@ #include "../Arachne/WallToolPaths.hpp" #include "AABBTreeLines.hpp" +#include "Algorithm/PathSorting.hpp" #include "ExPolygon.hpp" #include "FillEnsuring.hpp" #include "KDTreeIndirect.hpp" @@ -242,8 +243,8 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const { struct TracedPoly { - std::vector lows; - std::vector highs; + Points lows; + Points highs; }; std::vector current_traced_polys; @@ -321,14 +322,14 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const } // gaps_for_additional_filling = opening_ex(gaps_for_additional_filling, 0.3 * scaled_spacing); - BoundingBox bbox = get_extents(filled_area); - bbox.offset(scale_(1.)); - ::Slic3r::SVG svg(debug_out_path(("surface" + std::to_string(surface->area())).c_str()).c_str(), bbox); - svg.draw(to_lines(filled_area), "red", scale_(0.4)); - svg.draw(to_lines(reconstructed_area), "blue", scale_(0.3)); - svg.draw(to_lines(gaps_for_additional_filling), "green", scale_(0.2)); - svg.draw(vertical_lines, "black", scale_(0.1)); - svg.Close(); + // BoundingBox bbox = get_extents(filled_area); + // bbox.offset(scale_(1.)); + // ::Slic3r::SVG svg(debug_out_path(("surface" + std::to_string(surface->area())).c_str()).c_str(), bbox); + // svg.draw(to_lines(filled_area), "red", scale_(0.4)); + // svg.draw(to_lines(reconstructed_area), "blue", scale_(0.3)); + // svg.draw(to_lines(gaps_for_additional_filling), "green", scale_(0.2)); + // svg.draw(vertical_lines, "black", scale_(0.1)); + // svg.Close(); for (ExPolygon &ex_poly : gaps_for_additional_filling) { Point bbox_size = ex_poly.contour.bounding_box().size(); @@ -456,15 +457,22 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const }), thick_polylines_out.end()); + Algorithm::sort_paths(thick_polylines_out.begin(), thick_polylines_out.end(), thick_polylines_out.begin(), scaled_spacing * 2, + [](const ThickPolyline &tp) { + Lines ls; + Point prev = tp.first_point(); + for (size_t i = 1; i < tp.points.size(); i++) { + ls.emplace_back(prev, tp.points[i]); + prev = ls.back().b; + } + return ls; + }); + return thick_polylines_out; } } // namespace Slic3r - - - - // const size_t n_vlines = (bb.max.x() - bb.min.x() + scaled_spacing - 1) / scaled_spacing; // std::vector vertical_lines(2 * n_vlines + 1); // coord_t y_min = bb.min.y(); diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index e1068d7636..712e8d131f 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -728,7 +728,7 @@ Polylines reconnect_polylines(const Polylines &polylines, double limit_distance) return result; } -ExtrusionPaths sort_extra_perimeters(ExtrusionPaths extra_perims, int index_of_first_unanchored, double extrusion_spacing) +ExtrusionPaths sort_extra_perimeters(const ExtrusionPaths& extra_perims, int index_of_first_unanchored, double extrusion_spacing) { if (extra_perims.empty()) return {}; From aa85d050fed276665704b0bb71d4be6505579d45 Mon Sep 17 00:00:00 2001 From: Pavel Mikus Date: Tue, 25 Apr 2023 18:30:52 +0200 Subject: [PATCH 46/90] Fix path sorting --- src/libslic3r/Algorithm/PathSorting.hpp | 215 ++++++++++-------------- src/libslic3r/Fill/FillEnsuring.cpp | 44 ++--- src/libslic3r/PerimeterGenerator.cpp | 1 + 3 files changed, 118 insertions(+), 142 deletions(-) diff --git a/src/libslic3r/Algorithm/PathSorting.hpp b/src/libslic3r/Algorithm/PathSorting.hpp index a4806db82d..5a032a39ba 100644 --- a/src/libslic3r/Algorithm/PathSorting.hpp +++ b/src/libslic3r/Algorithm/PathSorting.hpp @@ -2,12 +2,15 @@ #define SRC_LIBSLIC3R_PATH_SORTING_HPP_ #include "AABBTreeLines.hpp" +#include "BoundingBox.hpp" +#include "Line.hpp" #include "ankerl/unordered_dense.h" #include #include #include #include #include +#include #include #include @@ -21,154 +24,122 @@ namespace Algorithm { // to the second, then they touch. // convert_to_lines is a lambda that should accept the path as argument and return it as Lines vector, in correct order. template -void sort_paths(RandomAccessIterator begin, RandomAccessIterator end, RandomAccessIterator last_seed, double touch_limit_distance, ToLines convert_to_lines) +void sort_paths(RandomAccessIterator begin, RandomAccessIterator end, Point start, double touch_limit_distance, ToLines convert_to_lines) { size_t paths_count = std::distance(begin, end); if (paths_count <= 1) return; + auto paths_min_distance = [](const AABBTreeLines::LinesDistancer &left, const AABBTreeLines::LinesDistancer &right) { + double min_distance = std::numeric_limits::max(); + for (const Line &l : left.get_lines()) { + if (double dist = right.distance_from_lines(l.a); dist < min_distance) { + min_distance = dist; + } + } + if (double dist = right.distance_from_lines(left.get_lines().back().b); dist < min_distance) { + min_distance = dist; + } + + for (const Line &l : right.get_lines()) { + if (double dist = left.distance_from_lines(l.a); dist < min_distance) { + min_distance = dist; + } + } + if (double dist = left.distance_from_lines(right.get_lines().back().b); dist < min_distance) { + min_distance = dist; + } + return min_distance; + }; + std::vector> distancers(paths_count); for (size_t path_idx = 0; path_idx < paths_count; path_idx++) { distancers[path_idx] = AABBTreeLines::LinesDistancer{convert_to_lines(*std::next(begin, path_idx))}; } - auto paths_touch = [touch_limit_distance](const AABBTreeLines::LinesDistancer &left, - const AABBTreeLines::LinesDistancer &right) { - for (const Line &l : left.get_lines()) { - if (right.distance_from_lines(l.a) < touch_limit_distance) { - return true; - } - } - if (right.distance_from_lines(left.get_lines().back().b) < touch_limit_distance) { - return true; - } - - for (const Line &l : right.get_lines()) { - if (left.distance_from_lines(l.a) < touch_limit_distance) { - return true; - } - } - if (left.distance_from_lines(right.get_lines().back().b) < touch_limit_distance) { - return true; - } - return false; - }; - std::vector> dependencies(paths_count); for (size_t path_idx = 0; path_idx < paths_count; path_idx++) { - for (size_t prev_path_idx = 0; prev_path_idx < path_idx; prev_path_idx++) { - if (paths_touch(distancers[path_idx], distancers[prev_path_idx])) { - dependencies[path_idx].insert(prev_path_idx); - dependencies[prev_path_idx].insert(path_idx); + for (size_t next_path_idx = path_idx + 1; next_path_idx < paths_count; next_path_idx++) { + double dist = paths_min_distance(distancers[path_idx], distancers[next_path_idx]); + if (dist < touch_limit_distance) { + dependencies[next_path_idx].insert(path_idx); } } } - size_t index_of_last_fixed = std::distance(begin, last_seed); - - std::vector processed(paths_count, false); - for (size_t path_idx = 0; path_idx <= index_of_last_fixed; path_idx++) { - processed[path_idx] = true; + for (size_t path_idx = 0; path_idx < paths_count; path_idx++) { + std::cout << "Dependencies of " << path_idx << " are "; + for (size_t dep : dependencies[path_idx]) + std::cout << dep << ", "; + std::cout << std::endl; } - for (size_t i = index_of_last_fixed + 1; i < paths_count; i++) { - bool change = false; - for (size_t path_idx = index_of_last_fixed + 1; path_idx < paths_count; path_idx++) { - if (processed[path_idx]) + Point current_point = start; + + std::vector> correct_order_and_direction(paths_count); + size_t unsorted_idx = 0; + size_t null_idx = size_t(-1); + size_t next_idx = null_idx; + bool reverse = false; + while (unsorted_idx < paths_count) { + next_idx = null_idx; + double lines_dist = std::numeric_limits::max(); + for (size_t path_idx = 0; path_idx < paths_count; path_idx++) { + if (!dependencies[path_idx].empty()) continue; - auto processed_dep = std::find_if(dependencies[path_idx].begin(), dependencies[path_idx].end(), - [&](size_t dep) { return processed[dep]; }); - if (processed_dep != dependencies[path_idx].end()) { - for (auto it = dependencies[path_idx].begin(); it != dependencies[path_idx].end();) { - if (!processed[*it]) { - dependencies[*it].insert(path_idx); - dependencies[path_idx].erase(it++); - } else { - ++it; - } + + double ldist = distancers[path_idx].distance_from_lines(current_point); + if (ldist < lines_dist) { + const auto &lines = distancers[path_idx].get_lines(); + double dist_a = line_alg::distance_to(lines.front(), current_point); + double dist_b = line_alg::distance_to(lines.back(), current_point); + if (std::abs(dist_a - dist_b) < touch_limit_distance) { + dist_a = (lines.front().a - current_point).squaredNorm(); + dist_b = (lines.back().b - current_point).squaredNorm(); } - processed[path_idx] = true; - change = true; + next_idx = path_idx; + reverse = dist_b < dist_a; } } - if (!change) { + + // we have valid next_idx, sort it, update dependencies, update current point + correct_order_and_direction[next_idx] = {unsorted_idx, reverse}; + unsorted_idx++; + current_point = reverse ? distancers[next_idx].get_lines().front().a : distancers[next_idx].get_lines().back().b; + + dependencies[next_idx].insert(null_idx); // prevent it from being selected again + for (size_t path_idx = 0; path_idx < paths_count; path_idx++) { + dependencies[path_idx].erase(next_idx); + } + } + + std::cout << "Final order is "; + for (size_t path_idx = 0; path_idx < paths_count; path_idx++) { + std::cout << correct_order_and_direction[path_idx].first << ", "; + } + std::cout << std::endl; + + for (size_t path_idx = 0; path_idx < paths_count; path_idx++) { + if (correct_order_and_direction[path_idx].second) { + std::next(begin, path_idx)->reverse(); + } + } + + for (size_t i = 0; i < correct_order_and_direction.size() - 1; i++) { + bool swapped = false; + for (size_t j = 0; j < correct_order_and_direction.size() - i - 1; j++) { + if (correct_order_and_direction[j].first > correct_order_and_direction[j + 1].first) { + std::swap(correct_order_and_direction[j], correct_order_and_direction[j + 1]); + std::iter_swap(std::next(begin, j), std::next(begin, j + 1)); + swapped = true; + } + } + if (swapped == false) { break; } } - - Point current_point = distancers.begin()->get_lines().begin()->a; - - size_t null_idx = size_t(-1); - size_t unsorted_idx = 0; - size_t next_idx = null_idx; - bool reverse = false; - while (true) { - if (next_idx == null_idx) { // find next pidx to print - double dist = std::numeric_limits::max(); - for (size_t path_idx = 0; path_idx < paths_count; path_idx++) { - if (!dependencies[path_idx].empty()) - continue; - const auto& lines = distancers[path_idx].get_lines(); - double dist_a = (lines.front().a - current_point).cast().squaredNorm(); - if (dist_a < dist) { - dist = dist_a; - next_idx = path_idx; - reverse = false; - } - double dist_b = (lines.back().b - current_point).cast().squaredNorm(); - if (dist_b < dist) { - dist = dist_b; - next_idx = path_idx; - reverse = true; - } - } - if (next_idx == null_idx) { - break; - } - } else { - // we have valid next_idx, sort it, update dependencies, update current point and potentialy set new next_idx - std::iter_swap(std::next(begin, unsorted_idx), std::next(begin, next_idx)); // next_path is now at sorted spot - if (reverse) { - std::next(begin, unsorted_idx)->reverse(); - } - unsorted_idx++; - - assert(dependencies[next_idx].empty()); - dependencies[next_idx].insert(null_idx); - current_point = reverse ? distancers[next_idx].get_lines().front().a : distancers[next_idx].get_lines().back().b; - for (size_t path_idx = 0; path_idx < paths_count; path_idx++) { - dependencies[path_idx].erase(next_idx); - } - double dist = std::numeric_limits::max(); - next_idx = null_idx; - - for (size_t path_idx = next_idx + 1; path_idx < paths_count; path_idx++) { - if (!dependencies[path_idx].empty()) { - continue; - } - const auto &lines = distancers[path_idx].get_lines(); - double dist_a = (lines.front().a - current_point).cast().squaredNorm(); - if (dist_a < dist) { - dist = dist_a; - next_idx = path_idx; - reverse = false; - } - double dist_b = (lines.back().b - current_point).cast().squaredNorm(); - if (dist_b < dist) { - dist = dist_b; - next_idx = path_idx; - reverse = true; - } - } - if (dist > scaled(5.0)) { - next_idx = null_idx; - } - } - } } - -} -} +}} // namespace Slic3r::Algorithm #endif /*SRC_LIBSLIC3R_PATH_SORTING_HPP_*/ \ No newline at end of file diff --git a/src/libslic3r/Fill/FillEnsuring.cpp b/src/libslic3r/Fill/FillEnsuring.cpp index e97b6c4790..b32ca1ed0e 100644 --- a/src/libslic3r/Fill/FillEnsuring.cpp +++ b/src/libslic3r/Fill/FillEnsuring.cpp @@ -4,6 +4,7 @@ #include "AABBTreeLines.hpp" #include "Algorithm/PathSorting.hpp" +#include "BoundingBox.hpp" #include "ExPolygon.hpp" #include "FillEnsuring.hpp" #include "KDTreeIndirect.hpp" @@ -322,14 +323,14 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const } // gaps_for_additional_filling = opening_ex(gaps_for_additional_filling, 0.3 * scaled_spacing); - // BoundingBox bbox = get_extents(filled_area); - // bbox.offset(scale_(1.)); - // ::Slic3r::SVG svg(debug_out_path(("surface" + std::to_string(surface->area())).c_str()).c_str(), bbox); - // svg.draw(to_lines(filled_area), "red", scale_(0.4)); - // svg.draw(to_lines(reconstructed_area), "blue", scale_(0.3)); - // svg.draw(to_lines(gaps_for_additional_filling), "green", scale_(0.2)); - // svg.draw(vertical_lines, "black", scale_(0.1)); - // svg.Close(); + BoundingBox bbox = get_extents(filled_area); + bbox.offset(scale_(1.)); + ::Slic3r::SVG svg(debug_out_path(("surface" + std::to_string(surface->area())).c_str()).c_str(), bbox); + svg.draw(to_lines(filled_area), "red", scale_(0.4)); + svg.draw(to_lines(reconstructed_area), "blue", scale_(0.3)); + svg.draw(to_lines(gaps_for_additional_filling), "green", scale_(0.2)); + svg.draw(vertical_lines, "black", scale_(0.1)); + svg.Close(); for (ExPolygon &ex_poly : gaps_for_additional_filling) { Point bbox_size = ex_poly.contour.bounding_box().size(); @@ -447,8 +448,6 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const } } - rotate_thick_polylines(thick_polylines_out, cos(-aligning_angle), sin(-aligning_angle)); - thick_polylines_out.erase(std::remove_if(thick_polylines_out.begin(), thick_polylines_out.end(), [scaled_spacing](const ThickPolyline &tp) { return tp.length() < scaled_spacing && @@ -457,17 +456,22 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const }), thick_polylines_out.end()); - Algorithm::sort_paths(thick_polylines_out.begin(), thick_polylines_out.end(), thick_polylines_out.begin(), scaled_spacing * 2, - [](const ThickPolyline &tp) { - Lines ls; - Point prev = tp.first_point(); - for (size_t i = 1; i < tp.points.size(); i++) { - ls.emplace_back(prev, tp.points[i]); - prev = ls.back().b; - } - return ls; - }); + std::sort(thick_polylines_out.begin(), thick_polylines_out.end(), [](const ThickPolyline &left, const ThickPolyline &right) { + return BoundingBox(left.points).min.x() < BoundingBox(right.points).min.x(); + }); + Algorithm::sort_paths(thick_polylines_out.begin(), thick_polylines_out.end(), bb.min, scaled_spacing * 1.2, [](const ThickPolyline + &tp) { + Lines ls; + Point prev = tp.first_point(); + for (size_t i = 1; i < tp.points.size(); i++) { + ls.emplace_back(prev, tp.points[i]); + prev = ls.back().b; + } + return ls; + }); + + rotate_thick_polylines(thick_polylines_out, cos(-aligning_angle), sin(-aligning_angle)); return thick_polylines_out; } diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 712e8d131f..b6c543bd98 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include From 17c9182f417ae5a97d15a8576023fe44695837ee Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 27 Apr 2023 17:26:36 +0200 Subject: [PATCH 47/90] Reduced and reworked the connections of generated paths --- src/libslic3r/Fill/FillEnsuring.cpp | 200 ++++++++-------------------- 1 file changed, 54 insertions(+), 146 deletions(-) diff --git a/src/libslic3r/Fill/FillEnsuring.cpp b/src/libslic3r/Fill/FillEnsuring.cpp index b32ca1ed0e..5f07527d00 100644 --- a/src/libslic3r/Fill/FillEnsuring.cpp +++ b/src/libslic3r/Fill/FillEnsuring.cpp @@ -168,77 +168,6 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const [](const Line &a, const Line &b) { return a.a.y() < b.b.y(); }); } - ThickPolylines thick_polylines_out; - { - ThickPolylines current_traced_paths; - for (const auto &polygon_slice : polygon_sections) { - std::unordered_set used_segments; - for (ThickPolyline &traced_path : current_traced_paths) { - Point max_y = traced_path.last_point(); - Point min_y = traced_path.points[traced_path.size() - 2]; - - if (max_y.y() < min_y.y()) - std::swap(max_y, min_y); - - auto candidates_begin = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), min_y, - [](const Point &low, const Line &seg) { return seg.b.y() > low.y(); }); - auto candidates_end = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), max_y, - [](const Point &high, const Line &seg) { return seg.a.y() > high.y(); }); - bool segment_added = false; - for (auto candidate = candidates_begin; candidate != candidates_end && !segment_added; candidate++) { - if (used_segments.find(&(*candidate)) != used_segments.end()) { - continue; - } - if ((traced_path.last_point() - candidate->a).cast().squaredNorm() < squared_distance_limit_reconnection) { - traced_path.points.back() += Point{0.0, scaled_spacing * 0.5}; - traced_path.width.push_back(scaled_spacing); - traced_path.points.push_back(candidate->a + Point{0.0, scaled_spacing * 0.5}); - traced_path.width.push_back(scaled_spacing); - traced_path.width.push_back(scaled_spacing); - traced_path.points.push_back(candidate->b); - traced_path.width.push_back(scaled_spacing); - used_segments.insert(&(*candidate)); - segment_added = true; - } else if ((traced_path.last_point() - candidate->b).cast().squaredNorm() < - squared_distance_limit_reconnection) { - traced_path.points.back() -= Point{0.0, scaled_spacing * 0.5}; - traced_path.width.push_back(scaled_spacing); - traced_path.points.push_back(candidate->b - Point{0.0, scaled_spacing * 0.5}); - traced_path.width.push_back(scaled_spacing); - traced_path.width.push_back(scaled_spacing); - traced_path.points.push_back(candidate->a); - traced_path.width.push_back(scaled_spacing); - used_segments.insert(&(*candidate)); - segment_added = true; - } - } - - if (!segment_added) { - // Zero overlapping segments. Finish the polyline. - thick_polylines_out.push_back(std::move(traced_path)); - traced_path.clear(); - } - } - - current_traced_paths.erase(std::remove_if(current_traced_paths.begin(), current_traced_paths.end(), - [](const ThickPolyline &tp) { return tp.empty(); }), - current_traced_paths.end()); - - for (const Line &segment : polygon_slice) { - if (used_segments.find(&segment) == used_segments.end()) { - ThickPolyline &new_path = current_traced_paths.emplace_back(); - new_path.points.push_back(segment.a); - new_path.width.push_back(scaled_spacing); - new_path.points.push_back(segment.b); - new_path.width.push_back(scaled_spacing); - new_path.endpoints = {true, true}; - } - } - } - - thick_polylines_out.insert(thick_polylines_out.end(), current_traced_paths.begin(), current_traced_paths.end()); - } - Polygons reconstructed_area{}; // reconstruct polygon from polygon sections { @@ -332,6 +261,20 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const svg.draw(vertical_lines, "black", scale_(0.1)); svg.Close(); + ThickPolylines thick_polylines; + { + for (const auto &polygon_slice : polygon_sections) { + for (const Line &segment : polygon_slice) { + ThickPolyline &new_path = thick_polylines.emplace_back(); + new_path.points.push_back(segment.a); + new_path.width.push_back(scaled_spacing); + new_path.points.push_back(segment.b); + new_path.width.push_back(scaled_spacing); + new_path.endpoints = {true, true}; + } + } + } + for (ExPolygon &ex_poly : gaps_for_additional_filling) { Point bbox_size = ex_poly.contour.bounding_box().size(); coord_t loops_count = std::max(bbox_size.x(), bbox_size.y()) / scaled_spacing + 1; @@ -347,7 +290,7 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const } // Split paths using a nearest neighbor search. - size_t firts_poly_idx = thick_polylines_out.size(); + size_t firts_poly_idx = thick_polylines.size(); Point last_pos(0, 0); for (const Arachne::ExtrusionLine *extrusion : all_extrusions) { if (extrusion->empty()) @@ -365,102 +308,48 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const assert(thick_polyline.size() > 1); //assert(thick_polyline.points.size() == thick_polyline.width.size()); - thick_polylines_out.emplace_back(std::move(thick_polyline)); - last_pos = thick_polylines_out.back().last_point(); + thick_polylines.emplace_back(std::move(thick_polyline)); + last_pos = thick_polylines.back().last_point(); } // clip the paths to prevent the extruder from getting exactly on the first point of the loop // Keep valid paths only. size_t j = firts_poly_idx; - for (size_t i = firts_poly_idx; i < thick_polylines_out.size(); ++i) { + for (size_t i = firts_poly_idx; i < thick_polylines.size(); ++i) { assert(thick_polylines_out[i].size() > 1); assert(thick_polylines_out[i].length() > 0.); //assert(thick_polylines_out[i].points.size() == thick_polylines_out[i].width.size()); - thick_polylines_out[i].clip_end(this->loop_clipping); + thick_polylines[i].clip_end(this->loop_clipping); assert(thick_polylines_out[i].size() > 1); - if (thick_polylines_out[i].is_valid()) { + if (thick_polylines[i].is_valid()) { if (j < i) - thick_polylines_out[j] = std::move(thick_polylines_out[i]); + thick_polylines[j] = std::move(thick_polylines[i]); ++j; } } - if (j < thick_polylines_out.size()) - thick_polylines_out.erase(thick_polylines_out.begin() + int(j), thick_polylines_out.end()); + if (j < thick_polylines.size()) + thick_polylines.erase(thick_polylines.begin() + int(j), thick_polylines.end()); } } - // reconnect ThickPolylines - struct EndPoint - { - Vec2d position; - size_t polyline_idx; - size_t other_end_point_idx; - bool is_first; - bool used = false; - }; - std::vector connection_endpoints; - connection_endpoints.reserve(thick_polylines_out.size() * 2); - for (size_t pl_idx = 0; pl_idx < thick_polylines_out.size(); pl_idx++) { - size_t current_idx = connection_endpoints.size(); - connection_endpoints.push_back({thick_polylines_out[pl_idx].first_point().cast(), pl_idx, current_idx + 1, true}); - connection_endpoints.push_back({thick_polylines_out[pl_idx].last_point().cast(), pl_idx, current_idx, false}); - } - auto coord_fn = [&connection_endpoints](size_t idx, size_t dim) { return connection_endpoints[idx].position[dim]; }; - KDTreeIndirect<2, double, decltype(coord_fn)> endpoints_tree{coord_fn, connection_endpoints.size()}; - for (size_t ep_idx = 0; ep_idx < connection_endpoints.size(); ep_idx++) { - EndPoint &ep = connection_endpoints[ep_idx]; - if (!ep.used) { - std::vector close_endpoints = find_nearby_points(endpoints_tree, ep.position, scaled_spacing); - for (size_t close_endpoint_idx : close_endpoints) { - EndPoint &ep2 = connection_endpoints[close_endpoint_idx]; - if (ep2.used || ep2.polyline_idx == ep.polyline_idx) { - continue; - } - - // connect ep and ep2; - ThickPolyline &tp1 = thick_polylines_out[ep.polyline_idx]; - ThickPolyline &tp2 = thick_polylines_out[ep2.polyline_idx]; - - if (ep.is_first) { - tp1.reverse(); - ep.is_first = false; - connection_endpoints[ep.other_end_point_idx].is_first = true; - } - - if (!ep2.is_first) { - tp2.reverse(); - ep2.is_first = true; - connection_endpoints[ep2.other_end_point_idx].is_first = false; - } - - tp1.points.insert(tp1.points.end(), tp2.points.begin(), tp2.points.end()); - tp1.width.push_back(tp1.width.back()); - tp1.width.push_back(tp2.width.front()); - tp1.width.insert(tp1.width.end(), tp2.width.begin(), tp2.width.end()); - ep2.used = true; - ep.used = true; - connection_endpoints[ep2.other_end_point_idx].polyline_idx = ep.polyline_idx; - connection_endpoints[ep2.other_end_point_idx].other_end_point_idx = ep_idx; - connection_endpoints[ep.other_end_point_idx].other_end_point_idx = close_endpoint_idx; - tp2.clear(); - break; - } - } - } - - thick_polylines_out.erase(std::remove_if(thick_polylines_out.begin(), thick_polylines_out.end(), + thick_polylines.erase(std::remove_if(thick_polylines.begin(), thick_polylines.end(), [scaled_spacing](const ThickPolyline &tp) { return tp.length() < scaled_spacing && std::all_of(tp.width.begin(), tp.width.end(), [scaled_spacing](double w) { return w < scaled_spacing; }); }), - thick_polylines_out.end()); + thick_polylines.end()); - std::sort(thick_polylines_out.begin(), thick_polylines_out.end(), [](const ThickPolyline &left, const ThickPolyline &right) { - return BoundingBox(left.points).min.x() < BoundingBox(right.points).min.x(); + std::stable_sort(thick_polylines.begin(), thick_polylines.end(), [](const ThickPolyline &left, const ThickPolyline &right) { + BoundingBox lbb(left.points); + BoundingBox rbb(right.points); + if (lbb.min.x() == rbb.min.x()) + return lbb.min.y() < rbb.min.y(); + else + return lbb.min.x() < rbb.min.x(); }); - Algorithm::sort_paths(thick_polylines_out.begin(), thick_polylines_out.end(), bb.min, scaled_spacing * 1.2, [](const ThickPolyline + Algorithm::sort_paths(thick_polylines.begin(), thick_polylines.end(), bb.min, scaled_spacing * 1.2, [](const ThickPolyline &tp) { Lines ls; Point prev = tp.first_point(); @@ -471,8 +360,27 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const return ls; }); - rotate_thick_polylines(thick_polylines_out, cos(-aligning_angle), sin(-aligning_angle)); - return thick_polylines_out; + ThickPolylines connected_thick_polylines; + if (!thick_polylines.empty()) { + connected_thick_polylines.push_back(thick_polylines.front()); + for (ThickPolyline &tp : thick_polylines) { + ThickPolyline &tail = connected_thick_polylines.back(); + Point last = tail.last_point(); + if ((last - tp.last_point()).cast().squaredNorm() < (last - tp.first_point()).cast().squaredNorm()) { + tp.reverse(); + } + if ((last - tp.first_point()).cast().squaredNorm() < squared_distance_limit_reconnection) { + tail.points.insert(tail.points.end(), tp.points.begin(), tp.points.end()); + tail.width.push_back(tail.width.back()); + tail.width.push_back(tp.width.front()); + tail.width.insert(tail.width.end(), tp.width.begin(), tp.width.end()); + } else { + connected_thick_polylines.push_back(tp); + } + } + } + rotate_thick_polylines(connected_thick_polylines, cos(-aligning_angle), sin(-aligning_angle)); + return connected_thick_polylines; } } // namespace Slic3r From a695b66d01f001287b1dc61e99002687d1818261 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 28 Apr 2023 16:45:17 +0200 Subject: [PATCH 48/90] Stable and working version, fixed several issues in ordering and connecting --- src/libslic3r/Algorithm/PathSorting.hpp | 51 +++------ src/libslic3r/Fill/FillEnsuring.cpp | 140 ++++++++++++++++++------ 2 files changed, 121 insertions(+), 70 deletions(-) diff --git a/src/libslic3r/Algorithm/PathSorting.hpp b/src/libslic3r/Algorithm/PathSorting.hpp index 5a032a39ba..ab44627281 100644 --- a/src/libslic3r/Algorithm/PathSorting.hpp +++ b/src/libslic3r/Algorithm/PathSorting.hpp @@ -30,26 +30,26 @@ void sort_paths(RandomAccessIterator begin, RandomAccessIterator end, Point star if (paths_count <= 1) return; - auto paths_min_distance = [](const AABBTreeLines::LinesDistancer &left, const AABBTreeLines::LinesDistancer &right) { - double min_distance = std::numeric_limits::max(); + auto paths_touch = [touch_limit_distance](const AABBTreeLines::LinesDistancer &left, + const AABBTreeLines::LinesDistancer &right) { for (const Line &l : left.get_lines()) { - if (double dist = right.distance_from_lines(l.a); dist < min_distance) { - min_distance = dist; + if (right.distance_from_lines(l.a) < touch_limit_distance) { + return true; } } - if (double dist = right.distance_from_lines(left.get_lines().back().b); dist < min_distance) { - min_distance = dist; + if (right.distance_from_lines(left.get_lines().back().b) < touch_limit_distance) { + return true; } for (const Line &l : right.get_lines()) { - if (double dist = left.distance_from_lines(l.a); dist < min_distance) { - min_distance = dist; + if (left.distance_from_lines(l.a) < touch_limit_distance) { + return true; } } - if (double dist = left.distance_from_lines(right.get_lines().back().b); dist < min_distance) { - min_distance = dist; + if (left.distance_from_lines(right.get_lines().back().b) < touch_limit_distance) { + return true; } - return min_distance; + return false; }; std::vector> distancers(paths_count); @@ -60,20 +60,12 @@ void sort_paths(RandomAccessIterator begin, RandomAccessIterator end, Point star std::vector> dependencies(paths_count); for (size_t path_idx = 0; path_idx < paths_count; path_idx++) { for (size_t next_path_idx = path_idx + 1; next_path_idx < paths_count; next_path_idx++) { - double dist = paths_min_distance(distancers[path_idx], distancers[next_path_idx]); - if (dist < touch_limit_distance) { + if (paths_touch(distancers[path_idx], distancers[next_path_idx])) { dependencies[next_path_idx].insert(path_idx); } } } - for (size_t path_idx = 0; path_idx < paths_count; path_idx++) { - std::cout << "Dependencies of " << path_idx << " are "; - for (size_t dep : dependencies[path_idx]) - std::cout << dep << ", "; - std::cout << std::endl; - } - Point current_point = start; std::vector> correct_order_and_direction(paths_count); @@ -91,14 +83,11 @@ void sort_paths(RandomAccessIterator begin, RandomAccessIterator end, Point star double ldist = distancers[path_idx].distance_from_lines(current_point); if (ldist < lines_dist) { const auto &lines = distancers[path_idx].get_lines(); - double dist_a = line_alg::distance_to(lines.front(), current_point); - double dist_b = line_alg::distance_to(lines.back(), current_point); - if (std::abs(dist_a - dist_b) < touch_limit_distance) { - dist_a = (lines.front().a - current_point).squaredNorm(); - dist_b = (lines.back().b - current_point).squaredNorm(); - } - next_idx = path_idx; - reverse = dist_b < dist_a; + double dist_a = (lines.front().a - current_point).cast().squaredNorm(); + double dist_b = (lines.back().b - current_point).cast().squaredNorm(); + next_idx = path_idx; + reverse = dist_b < dist_a; + lines_dist = ldist; } } @@ -113,12 +102,6 @@ void sort_paths(RandomAccessIterator begin, RandomAccessIterator end, Point star } } - std::cout << "Final order is "; - for (size_t path_idx = 0; path_idx < paths_count; path_idx++) { - std::cout << correct_order_and_direction[path_idx].first << ", "; - } - std::cout << std::endl; - for (size_t path_idx = 0; path_idx < paths_count; path_idx++) { if (correct_order_and_direction[path_idx].second) { std::next(begin, path_idx)->reverse(); diff --git a/src/libslic3r/Fill/FillEnsuring.cpp b/src/libslic3r/Fill/FillEnsuring.cpp index 5f07527d00..518c81627d 100644 --- a/src/libslic3r/Fill/FillEnsuring.cpp +++ b/src/libslic3r/Fill/FillEnsuring.cpp @@ -47,7 +47,7 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const }; const coord_t scaled_spacing = scaled(this->spacing); - double distance_limit_reconnection = 2 * double(scaled_spacing); + double distance_limit_reconnection = double(scaled_spacing); double squared_distance_limit_reconnection = distance_limit_reconnection * distance_limit_reconnection; Polygons filled_area = to_polygons(surface->expolygon); std::pair rotate_vector = this->_infill_direction(surface); @@ -250,16 +250,15 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const if (this->overlap != 0) { gaps_for_additional_filling = offset_ex(gaps_for_additional_filling, scaled(this->overlap)); } - // gaps_for_additional_filling = opening_ex(gaps_for_additional_filling, 0.3 * scaled_spacing); - BoundingBox bbox = get_extents(filled_area); - bbox.offset(scale_(1.)); - ::Slic3r::SVG svg(debug_out_path(("surface" + std::to_string(surface->area())).c_str()).c_str(), bbox); - svg.draw(to_lines(filled_area), "red", scale_(0.4)); - svg.draw(to_lines(reconstructed_area), "blue", scale_(0.3)); - svg.draw(to_lines(gaps_for_additional_filling), "green", scale_(0.2)); - svg.draw(vertical_lines, "black", scale_(0.1)); - svg.Close(); + // BoundingBox bbox = get_extents(filled_area); + // bbox.offset(scale_(1.)); + // ::Slic3r::SVG svg(debug_out_path(("surface" + std::to_string(surface->area())).c_str()).c_str(), bbox); + // svg.draw(to_lines(filled_area), "red", scale_(0.4)); + // svg.draw(to_lines(reconstructed_area), "blue", scale_(0.3)); + // svg.draw(to_lines(gaps_for_additional_filling), "green", scale_(0.2)); + // svg.draw(vertical_lines, "black", scale_(0.1)); + // svg.Close(); ThickPolylines thick_polylines; { @@ -316,11 +315,11 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const // Keep valid paths only. size_t j = firts_poly_idx; for (size_t i = firts_poly_idx; i < thick_polylines.size(); ++i) { - assert(thick_polylines_out[i].size() > 1); - assert(thick_polylines_out[i].length() > 0.); - //assert(thick_polylines_out[i].points.size() == thick_polylines_out[i].width.size()); + assert(thick_polylines[i].size() > 1); + assert(thick_polylines[i].length() > 0.); + //assert(thick_polylines[i].points.size() == thick_polylines[i].width.size()); thick_polylines[i].clip_end(this->loop_clipping); - assert(thick_polylines_out[i].size() > 1); + assert(thick_polylines[i].size() > 1); if (thick_polylines[i].is_valid()) { if (j < i) thick_polylines[j] = std::move(thick_polylines[i]); @@ -332,6 +331,75 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const } } + // connect tiny gap fills to close colinear line + struct EndPoint + { + Vec2d position; + size_t polyline_idx; + size_t other_end_point_idx; + bool is_first; + bool used = false; + }; + std::vector connection_endpoints; + connection_endpoints.reserve(thick_polylines.size() * 2); + for (size_t pl_idx = 0; pl_idx < thick_polylines.size(); pl_idx++) { + size_t current_idx = connection_endpoints.size(); + connection_endpoints.push_back({thick_polylines[pl_idx].first_point().cast(), pl_idx, current_idx + 1, true}); + connection_endpoints.push_back({thick_polylines[pl_idx].last_point().cast(), pl_idx, current_idx, false}); + } + + auto coord_fn = [&connection_endpoints](size_t idx, size_t dim) { return connection_endpoints[idx].position[dim]; }; + KDTreeIndirect<2, double, decltype(coord_fn)> endpoints_tree{coord_fn, connection_endpoints.size()}; + for (size_t ep_idx = 0; ep_idx < connection_endpoints.size(); ep_idx++) { + EndPoint &ep1 = connection_endpoints[ep_idx]; + if (!ep1.used) { + std::vector close_endpoints = find_nearby_points(endpoints_tree, ep1.position, scaled_spacing); + for (size_t close_endpoint_idx : close_endpoints) { + EndPoint &ep2 = connection_endpoints[close_endpoint_idx]; + if (ep2.used || ep2.polyline_idx == ep1.polyline_idx) { + continue; + } + + ThickPolyline &tp1 = thick_polylines[ep1.polyline_idx]; + ThickPolyline &tp2 = thick_polylines[ep2.polyline_idx]; + + Vec2d v1 = ep1.is_first ? (tp1.points[0] - tp1.points[1]).cast() : + (tp1.points.back() - tp1.points[tp1.points.size() - 1]).cast(); + Vec2d v2 = ep2.is_first ? (tp2.points[1] - tp2.points[0]).cast() : + (tp2.points[tp2.points.size() - 1] - tp2.points.back()).cast(); + + if (std::abs(Slic3r::angle(v1, v2)) > PI / 6.0) { + continue; + } + + // connect ep and ep2; + if (ep1.is_first) { + tp1.reverse(); + ep1.is_first = false; + connection_endpoints[ep1.other_end_point_idx].is_first = true; + } + + if (!ep2.is_first) { + tp2.reverse(); + ep2.is_first = true; + connection_endpoints[ep2.other_end_point_idx].is_first = false; + } + + tp1.points.insert(tp1.points.end(), tp2.points.begin(), tp2.points.end()); + tp1.width.push_back(tp1.width.back()); + tp1.width.push_back(tp2.width.front()); + tp1.width.insert(tp1.width.end(), tp2.width.begin(), tp2.width.end()); + ep2.used = true; + ep1.used = true; + connection_endpoints[ep2.other_end_point_idx].polyline_idx = ep1.polyline_idx; + connection_endpoints[ep2.other_end_point_idx].other_end_point_idx = ep_idx; + connection_endpoints[ep1.other_end_point_idx].other_end_point_idx = close_endpoint_idx; + tp2.clear(); + break; + } + } + } + thick_polylines.erase(std::remove_if(thick_polylines.begin(), thick_polylines.end(), [scaled_spacing](const ThickPolyline &tp) { return tp.length() < scaled_spacing && @@ -360,27 +428,27 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const return ls; }); - ThickPolylines connected_thick_polylines; - if (!thick_polylines.empty()) { - connected_thick_polylines.push_back(thick_polylines.front()); - for (ThickPolyline &tp : thick_polylines) { - ThickPolyline &tail = connected_thick_polylines.back(); - Point last = tail.last_point(); - if ((last - tp.last_point()).cast().squaredNorm() < (last - tp.first_point()).cast().squaredNorm()) { - tp.reverse(); - } - if ((last - tp.first_point()).cast().squaredNorm() < squared_distance_limit_reconnection) { - tail.points.insert(tail.points.end(), tp.points.begin(), tp.points.end()); - tail.width.push_back(tail.width.back()); - tail.width.push_back(tp.width.front()); - tail.width.insert(tail.width.end(), tp.width.begin(), tp.width.end()); - } else { - connected_thick_polylines.push_back(tp); - } - } - } - rotate_thick_polylines(connected_thick_polylines, cos(-aligning_angle), sin(-aligning_angle)); - return connected_thick_polylines; + // ThickPolylines connected_thick_polylines; + // if (!thick_polylines.empty()) { + // connected_thick_polylines.push_back(thick_polylines.front()); + // for (ThickPolyline &tp : thick_polylines) { + // ThickPolyline &tail = connected_thick_polylines.back(); + // Point last = tail.last_point(); + // if ((last - tp.last_point()).cast().squaredNorm() < (last - tp.first_point()).cast().squaredNorm()) { + // tp.reverse(); + // } + // if ((last - tp.first_point()).cast().squaredNorm() < squared_distance_limit_reconnection) { + // tail.points.insert(tail.points.end(), tp.points.begin(), tp.points.end()); + // tail.width.push_back(0); + // tail.width.push_back(0); + // tail.width.insert(tail.width.end(), tp.width.begin(), tp.width.end()); + // } else { + // connected_thick_polylines.push_back(tp); + // } + // } + // } + rotate_thick_polylines(thick_polylines, cos(-aligning_angle), sin(-aligning_angle)); + return thick_polylines; } } // namespace Slic3r @@ -461,4 +529,4 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const // } // } // } - // } \ No newline at end of file + // } From 801a41a3524d7425ea31a94b8b9333f4a6663156 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 2 May 2023 13:00:24 +0200 Subject: [PATCH 49/90] Fixed some bugs in sorting and connecting. Experimenting with ordering by max coordinate - this causes concetric arachne parts to be filled from inside --- src/libslic3r/Fill/FillEnsuring.cpp | 78 +++++++++++------------------ 1 file changed, 28 insertions(+), 50 deletions(-) diff --git a/src/libslic3r/Fill/FillEnsuring.cpp b/src/libslic3r/Fill/FillEnsuring.cpp index 518c81627d..474a889262 100644 --- a/src/libslic3r/Fill/FillEnsuring.cpp +++ b/src/libslic3r/Fill/FillEnsuring.cpp @@ -275,10 +275,11 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const } for (ExPolygon &ex_poly : gaps_for_additional_filling) { - Point bbox_size = ex_poly.contour.bounding_box().size(); - coord_t loops_count = std::max(bbox_size.x(), bbox_size.y()) / scaled_spacing + 1; - Polygons polygons = to_polygons(ex_poly); - Arachne::WallToolPaths wall_tool_paths(polygons, scaled_spacing, scaled_spacing, loops_count, 0, params.layer_height, *this->print_object_config, *this->print_config); + BoundingBox ex_bb = ex_poly.contour.bounding_box(); + coord_t loops_count = (std::max(ex_bb.size().x(), ex_bb.size().y()) + scaled_spacing - 1) / scaled_spacing; + Polygons polygons = to_polygons(ex_poly); + Arachne::WallToolPaths wall_tool_paths(polygons, scaled_spacing, scaled_spacing, loops_count, 0, params.layer_height, + *this->print_object_config, *this->print_config); if (std::vector loops = wall_tool_paths.getToolPaths(); !loops.empty()) { std::vector all_extrusions; for (Arachne::VariableWidthLines &loop : loops) { @@ -288,46 +289,17 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const all_extrusions.emplace_back(&wall); } - // Split paths using a nearest neighbor search. - size_t firts_poly_idx = thick_polylines.size(); - Point last_pos(0, 0); for (const Arachne::ExtrusionLine *extrusion : all_extrusions) { - if (extrusion->empty()) + if (extrusion->junctions.size() < 2) { continue; - - ThickPolyline thick_polyline = Arachne::to_thick_polyline(*extrusion); - if (thick_polyline.length() == 0.) - //FIXME this should not happen. - continue; - assert(thick_polyline.size() > 1); - assert(thick_polyline.length() > 0.); - //assert(thick_polyline.points.size() == thick_polyline.width.size()); - if (extrusion->is_closed) - thick_polyline.start_at_index(nearest_point_index(thick_polyline.points, last_pos)); - - assert(thick_polyline.size() > 1); - //assert(thick_polyline.points.size() == thick_polyline.width.size()); - thick_polylines.emplace_back(std::move(thick_polyline)); - last_pos = thick_polylines.back().last_point(); - } - - // clip the paths to prevent the extruder from getting exactly on the first point of the loop - // Keep valid paths only. - size_t j = firts_poly_idx; - for (size_t i = firts_poly_idx; i < thick_polylines.size(); ++i) { - assert(thick_polylines[i].size() > 1); - assert(thick_polylines[i].length() > 0.); - //assert(thick_polylines[i].points.size() == thick_polylines[i].width.size()); - thick_polylines[i].clip_end(this->loop_clipping); - assert(thick_polylines[i].size() > 1); - if (thick_polylines[i].is_valid()) { - if (j < i) - thick_polylines[j] = std::move(thick_polylines[i]); - ++j; } + ThickPolyline thick_polyline = Arachne::to_thick_polyline(*extrusion); + if (extrusion->is_closed) { + thick_polyline.start_at_index(nearest_point_index(thick_polyline.points, ex_bb.min)); + thick_polyline.clip_end(scaled_spacing * 0.5); + } + thick_polylines.push_back(thick_polyline); } - if (j < thick_polylines.size()) - thick_polylines.erase(thick_polylines.begin() + int(j), thick_polylines.end()); } } @@ -353,7 +325,7 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const for (size_t ep_idx = 0; ep_idx < connection_endpoints.size(); ep_idx++) { EndPoint &ep1 = connection_endpoints[ep_idx]; if (!ep1.used) { - std::vector close_endpoints = find_nearby_points(endpoints_tree, ep1.position, scaled_spacing); + std::vector close_endpoints = find_nearby_points(endpoints_tree, ep1.position, double(scaled_spacing)); for (size_t close_endpoint_idx : close_endpoints) { EndPoint &ep2 = connection_endpoints[close_endpoint_idx]; if (ep2.used || ep2.polyline_idx == ep1.polyline_idx) { @@ -379,21 +351,27 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const connection_endpoints[ep1.other_end_point_idx].is_first = true; } + size_t new_start_idx = ep1.other_end_point_idx; + if (!ep2.is_first) { tp2.reverse(); ep2.is_first = true; connection_endpoints[ep2.other_end_point_idx].is_first = false; } + size_t new_end_idx = ep2.other_end_point_idx; + tp1.points.insert(tp1.points.end(), tp2.points.begin(), tp2.points.end()); tp1.width.push_back(tp1.width.back()); tp1.width.push_back(tp2.width.front()); tp1.width.insert(tp1.width.end(), tp2.width.begin(), tp2.width.end()); - ep2.used = true; - ep1.used = true; - connection_endpoints[ep2.other_end_point_idx].polyline_idx = ep1.polyline_idx; - connection_endpoints[ep2.other_end_point_idx].other_end_point_idx = ep_idx; - connection_endpoints[ep1.other_end_point_idx].other_end_point_idx = close_endpoint_idx; + ep1.used = true; + ep2.used = true; + + connection_endpoints[new_start_idx].polyline_idx = ep1.polyline_idx; + connection_endpoints[new_end_idx].polyline_idx = ep1.polyline_idx; + connection_endpoints[new_start_idx].other_end_point_idx = new_end_idx; + connection_endpoints[new_end_idx].other_end_point_idx = new_start_idx; tp2.clear(); break; } @@ -408,16 +386,16 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const }), thick_polylines.end()); - std::stable_sort(thick_polylines.begin(), thick_polylines.end(), [](const ThickPolyline &left, const ThickPolyline &right) { + std::sort(thick_polylines.begin(), thick_polylines.end(), [](const ThickPolyline &left, const ThickPolyline &right) { BoundingBox lbb(left.points); BoundingBox rbb(right.points); if (lbb.min.x() == rbb.min.x()) - return lbb.min.y() < rbb.min.y(); + return lbb.max.y() < rbb.max.y(); else - return lbb.min.x() < rbb.min.x(); + return lbb.max.x() < rbb.max.x(); }); - Algorithm::sort_paths(thick_polylines.begin(), thick_polylines.end(), bb.min, scaled_spacing * 1.2, [](const ThickPolyline + Algorithm::sort_paths(thick_polylines.begin(), thick_polylines.end(), bb.min, double(scaled_spacing) * 1.2, [](const ThickPolyline &tp) { Lines ls; Point prev = tp.first_point(); From 0a7c48f5fd7a8db15c7c117eda60e0faf845d172 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 2 May 2023 16:49:46 +0200 Subject: [PATCH 50/90] Little refactoring, make the fill modifiable --- src/libslic3r/Fill/FillBase.hpp | 2 +- src/libslic3r/Fill/FillEnsuring.cpp | 368 ++++++++++++++-------------- src/libslic3r/Fill/FillEnsuring.hpp | 9 +- 3 files changed, 197 insertions(+), 182 deletions(-) diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp index cf37667587..4130cc3313 100644 --- a/src/libslic3r/Fill/FillBase.hpp +++ b/src/libslic3r/Fill/FillBase.hpp @@ -147,9 +147,9 @@ protected: virtual float _layer_angle(size_t idx) const { return (idx & 1) ? float(M_PI/2.) : 0; } - virtual std::pair _infill_direction(const Surface *surface) const; public: + virtual std::pair _infill_direction(const Surface *surface) const; static void connect_infill(Polylines &&infill_ordered, const ExPolygon &boundary, Polylines &polylines_out, const double spacing, const FillParams ¶ms); static void connect_infill(Polylines &&infill_ordered, const Polygons &boundary, const BoundingBox& bbox, Polylines &polylines_out, const double spacing, const FillParams ¶ms); static void connect_infill(Polylines &&infill_ordered, const std::vector &boundary, const BoundingBox &bbox, Polylines &polylines_out, double spacing, const FillParams ¶ms); diff --git a/src/libslic3r/Fill/FillEnsuring.cpp b/src/libslic3r/Fill/FillEnsuring.cpp index 474a889262..2a24e2360e 100644 --- a/src/libslic3r/Fill/FillEnsuring.cpp +++ b/src/libslic3r/Fill/FillEnsuring.cpp @@ -25,10 +25,9 @@ namespace Slic3r { -ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const FillParams ¶ms) +ThickPolylines make_fill_polylines(const Fill *fill, const Surface *surface, const FillParams ¶ms, bool stop_vibrations, bool fill_gaps) { - assert(params.use_arachne); - assert(this->print_config != nullptr && this->print_object_config != nullptr && this->print_region_config != nullptr); + assert(fill->print_config != nullptr && fill->print_object_config != nullptr && fill->print_region_config != nullptr); auto rotate_thick_polylines = [](ThickPolylines &tpolylines, double cos_angle, double sin_angle) { for (ThickPolyline &tp : tpolylines) { @@ -46,14 +45,13 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const (bhigh >= alow && bhigh <= ahigh); }; - const coord_t scaled_spacing = scaled(this->spacing); + const coord_t scaled_spacing = scaled(fill->spacing); double distance_limit_reconnection = double(scaled_spacing); double squared_distance_limit_reconnection = distance_limit_reconnection * distance_limit_reconnection; Polygons filled_area = to_polygons(surface->expolygon); - std::pair rotate_vector = this->_infill_direction(surface); + std::pair rotate_vector = fill->_infill_direction(surface); double aligning_angle = -rotate_vector.first + PI; polygons_rotate(filled_area, aligning_angle); - Polygons internal_area = shrink(filled_area, 0.5 * scaled_spacing - scale_(this->overlap)); BoundingBox bb = get_extents(filled_area); const size_t n_vlines = (bb.max.x() - bb.min.x() + scaled_spacing - 1) / scaled_spacing; @@ -65,11 +63,18 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const vertical_lines[i].a = Point{x, y_min}; vertical_lines[i].b = Point{x, y_max}; } + vertical_lines.push_back(vertical_lines.back()); vertical_lines.back().a = Point{coord_t(bb.min.x() + n_vlines * double(scaled_spacing) + scaled_spacing * 0.5), y_min}; vertical_lines.back().b = Point{vertical_lines.back().a.x(), y_max}; - auto area_walls = AABBTreeLines::LinesDistancer{ - to_lines(intersection(filled_area, opening(filled_area, 2 * scaled_spacing, 3 * scaled_spacing)))}; + AABBTreeLines::LinesDistancer area_walls; + if (stop_vibrations) { + area_walls = AABBTreeLines::LinesDistancer{ + to_lines(intersection(filled_area, opening(filled_area, 2 * scaled_spacing, 3 * scaled_spacing)))}; + } else { + area_walls = AABBTreeLines::LinesDistancer{to_lines(filled_area)}; + } + std::vector> polygon_sections(n_vlines); for (size_t i = 0; i < n_vlines; i++) { @@ -86,75 +91,77 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const } } - struct Node - { - int section_idx; - int line_idx; - int skips_taken = 0; - bool neighbours_explored = false; - std::vector> neighbours{}; - }; + if (stop_vibrations) { + struct Node + { + int section_idx; + int line_idx; + int skips_taken = 0; + bool neighbours_explored = false; + std::vector> neighbours{}; + }; - coord_t length_filter = scale_(4); - size_t skips_allowed = 2; - size_t min_removal_conut = 5; - for (int section_idx = 0; section_idx < polygon_sections.size(); section_idx++) { - for (int line_idx = 0; line_idx < polygon_sections[section_idx].size(); line_idx++) { - if (const Line &line = polygon_sections[section_idx][line_idx]; line.a != line.b && line.length() < length_filter) { - std::set> to_remove{{section_idx, line_idx}}; - std::vector to_visit{{section_idx, line_idx}}; + coord_t length_filter = scale_(4); + size_t skips_allowed = 2; + size_t min_removal_conut = 5; + for (int section_idx = 0; section_idx < polygon_sections.size(); section_idx++) { + for (int line_idx = 0; line_idx < polygon_sections[section_idx].size(); line_idx++) { + if (const Line &line = polygon_sections[section_idx][line_idx]; line.a != line.b && line.length() < length_filter) { + std::set> to_remove{{section_idx, line_idx}}; + std::vector to_visit{{section_idx, line_idx}}; - bool initial_touches_long_lines = false; - if (section_idx > 0) { - for (int prev_line_idx = 0; prev_line_idx < polygon_sections[section_idx - 1].size(); prev_line_idx++) { - if (const Line &nl = polygon_sections[section_idx - 1][prev_line_idx]; - nl.a != nl.b && segments_overlap(line.a.y(), line.b.y(), nl.a.y(), nl.b.y())) { - initial_touches_long_lines = true; - } - } - } - - while (!to_visit.empty()) { - Node curr = to_visit.back(); - const Line &curr_l = polygon_sections[curr.section_idx][curr.line_idx]; - if (curr.neighbours_explored) { - bool is_valid_for_removal = (curr_l.length() < length_filter) && - ((int(to_remove.size()) - curr.skips_taken > min_removal_conut) || - (curr.neighbours.empty() && !initial_touches_long_lines)); - if (!is_valid_for_removal) { - for (const auto &n : curr.neighbours) { - if (to_remove.find(n) != to_remove.end()) { - is_valid_for_removal = true; - break; - } + bool initial_touches_long_lines = false; + if (section_idx > 0) { + for (int prev_line_idx = 0; prev_line_idx < polygon_sections[section_idx - 1].size(); prev_line_idx++) { + if (const Line &nl = polygon_sections[section_idx - 1][prev_line_idx]; + nl.a != nl.b && segments_overlap(line.a.y(), line.b.y(), nl.a.y(), nl.b.y())) { + initial_touches_long_lines = true; } } - if (!is_valid_for_removal) { - to_remove.erase({curr.section_idx, curr.line_idx}); - } - to_visit.pop_back(); - } else { - to_visit.back().neighbours_explored = true; - int curr_index = to_visit.size() - 1; - bool can_use_skip = curr_l.length() <= length_filter && curr.skips_taken < skips_allowed; - if (curr.section_idx + 1 < polygon_sections.size()) { - for (int lidx = 0; lidx < polygon_sections[curr.section_idx + 1].size(); lidx++) { - if (const Line &nl = polygon_sections[curr.section_idx + 1][lidx]; - nl.a != nl.b && segments_overlap(curr_l.a.y(), curr_l.b.y(), nl.a.y(), nl.b.y()) && - (nl.length() < length_filter || can_use_skip)) { - to_visit[curr_index].neighbours.push_back({curr.section_idx + 1, lidx}); - to_remove.insert({curr.section_idx + 1, lidx}); - Node next_node{curr.section_idx + 1, lidx, curr.skips_taken + (nl.length() >= length_filter)}; - to_visit.push_back(next_node); + } + + while (!to_visit.empty()) { + Node curr = to_visit.back(); + const Line &curr_l = polygon_sections[curr.section_idx][curr.line_idx]; + if (curr.neighbours_explored) { + bool is_valid_for_removal = (curr_l.length() < length_filter) && + ((int(to_remove.size()) - curr.skips_taken > min_removal_conut) || + (curr.neighbours.empty() && !initial_touches_long_lines)); + if (!is_valid_for_removal) { + for (const auto &n : curr.neighbours) { + if (to_remove.find(n) != to_remove.end()) { + is_valid_for_removal = true; + break; + } + } + } + if (!is_valid_for_removal) { + to_remove.erase({curr.section_idx, curr.line_idx}); + } + to_visit.pop_back(); + } else { + to_visit.back().neighbours_explored = true; + int curr_index = to_visit.size() - 1; + bool can_use_skip = curr_l.length() <= length_filter && curr.skips_taken < skips_allowed; + if (curr.section_idx + 1 < polygon_sections.size()) { + for (int lidx = 0; lidx < polygon_sections[curr.section_idx + 1].size(); lidx++) { + if (const Line &nl = polygon_sections[curr.section_idx + 1][lidx]; + nl.a != nl.b && segments_overlap(curr_l.a.y(), curr_l.b.y(), nl.a.y(), nl.b.y()) && + (nl.length() < length_filter || can_use_skip)) { + to_visit[curr_index].neighbours.push_back({curr.section_idx + 1, lidx}); + to_remove.insert({curr.section_idx + 1, lidx}); + Node next_node{curr.section_idx + 1, lidx, curr.skips_taken + (nl.length() >= length_filter)}; + to_visit.push_back(next_node); + } } } } } - } - for (const auto &pair : to_remove) { - Line &l = polygon_sections[pair.first][pair.second]; - l.a = l.b; + for (const auto &pair : to_remove) { + Line &l = polygon_sections[pair.first][pair.second]; + l.a = l.b; + } } } } @@ -182,9 +189,9 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const std::unordered_set used_segments; for (TracedPoly &traced_poly : current_traced_polys) { auto candidates_begin = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), traced_poly.lows.back(), - [](const Point &low, const Line &seg) { return seg.b.y() > low.y(); }); - auto candidates_end = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), traced_poly.highs.back(), - [](const Point &high, const Line &seg) { return seg.a.y() > high.y(); }); + [](const Point &low, const Line &seg) { return seg.b.y() > low.y(); }); + auto candidates_end = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), traced_poly.highs.back(), + [](const Point &high, const Line &seg) { return seg.a.y() > high.y(); }); bool segment_added = false; for (auto candidate = candidates_begin; candidate != candidates_end && !segment_added; candidate++) { @@ -247,8 +254,8 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const reconstructed_area = closing(reconstructed_area, float(SCALED_EPSILON), float(SCALED_EPSILON)); ExPolygons gaps_for_additional_filling = diff_ex(filled_area, reconstructed_area); - if (this->overlap != 0) { - gaps_for_additional_filling = offset_ex(gaps_for_additional_filling, scaled(this->overlap)); + if (fill->overlap != 0) { + gaps_for_additional_filling = offset_ex(gaps_for_additional_filling, scaled(fill->overlap)); } // BoundingBox bbox = get_extents(filled_area); @@ -274,129 +281,132 @@ ThickPolylines FillEnsuring::fill_surface_arachne(const Surface *surface, const } } - for (ExPolygon &ex_poly : gaps_for_additional_filling) { - BoundingBox ex_bb = ex_poly.contour.bounding_box(); - coord_t loops_count = (std::max(ex_bb.size().x(), ex_bb.size().y()) + scaled_spacing - 1) / scaled_spacing; - Polygons polygons = to_polygons(ex_poly); - Arachne::WallToolPaths wall_tool_paths(polygons, scaled_spacing, scaled_spacing, loops_count, 0, params.layer_height, - *this->print_object_config, *this->print_config); - if (std::vector loops = wall_tool_paths.getToolPaths(); !loops.empty()) { - std::vector all_extrusions; - for (Arachne::VariableWidthLines &loop : loops) { - if (loop.empty()) - continue; - for (const Arachne::ExtrusionLine &wall : loop) - all_extrusions.emplace_back(&wall); - } + if (fill_gaps) { + for (ExPolygon &ex_poly : gaps_for_additional_filling) { + BoundingBox ex_bb = ex_poly.contour.bounding_box(); + coord_t loops_count = (std::max(ex_bb.size().x(), ex_bb.size().y()) + scaled_spacing - 1) / scaled_spacing; + Polygons polygons = to_polygons(ex_poly); + Arachne::WallToolPaths wall_tool_paths(polygons, scaled_spacing, scaled_spacing, loops_count, 0, params.layer_height, + *fill->print_object_config, *fill->print_config); + if (std::vector loops = wall_tool_paths.getToolPaths(); !loops.empty()) { + std::vector all_extrusions; + for (Arachne::VariableWidthLines &loop : loops) { + if (loop.empty()) + continue; + for (const Arachne::ExtrusionLine &wall : loop) + all_extrusions.emplace_back(&wall); + } - for (const Arachne::ExtrusionLine *extrusion : all_extrusions) { - if (extrusion->junctions.size() < 2) { - continue; + for (const Arachne::ExtrusionLine *extrusion : all_extrusions) { + if (extrusion->junctions.size() < 2) { + continue; + } + ThickPolyline thick_polyline = Arachne::to_thick_polyline(*extrusion); + if (extrusion->is_closed) { + thick_polyline.start_at_index(nearest_point_index(thick_polyline.points, ex_bb.min)); + thick_polyline.clip_end(scaled_spacing * 0.5); + } + if (thick_polyline.is_valid() && thick_polyline.length() > 0 && thick_polyline.points.size() > 1) { + thick_polylines.push_back(thick_polyline); + } } - ThickPolyline thick_polyline = Arachne::to_thick_polyline(*extrusion); - if (extrusion->is_closed) { - thick_polyline.start_at_index(nearest_point_index(thick_polyline.points, ex_bb.min)); - thick_polyline.clip_end(scaled_spacing * 0.5); - } - thick_polylines.push_back(thick_polyline); } } - } - // connect tiny gap fills to close colinear line - struct EndPoint - { - Vec2d position; - size_t polyline_idx; - size_t other_end_point_idx; - bool is_first; - bool used = false; - }; - std::vector connection_endpoints; - connection_endpoints.reserve(thick_polylines.size() * 2); - for (size_t pl_idx = 0; pl_idx < thick_polylines.size(); pl_idx++) { - size_t current_idx = connection_endpoints.size(); - connection_endpoints.push_back({thick_polylines[pl_idx].first_point().cast(), pl_idx, current_idx + 1, true}); - connection_endpoints.push_back({thick_polylines[pl_idx].last_point().cast(), pl_idx, current_idx, false}); - } + std::sort(thick_polylines.begin(), thick_polylines.end(), [](const ThickPolyline &left, const ThickPolyline &right) { + BoundingBox lbb(left.points); + BoundingBox rbb(right.points); + if (lbb.min.x() == rbb.min.x()) + return lbb.min.y() < rbb.min.y(); + else + return lbb.min.x() < rbb.min.x(); + }); - auto coord_fn = [&connection_endpoints](size_t idx, size_t dim) { return connection_endpoints[idx].position[dim]; }; - KDTreeIndirect<2, double, decltype(coord_fn)> endpoints_tree{coord_fn, connection_endpoints.size()}; - for (size_t ep_idx = 0; ep_idx < connection_endpoints.size(); ep_idx++) { - EndPoint &ep1 = connection_endpoints[ep_idx]; - if (!ep1.used) { - std::vector close_endpoints = find_nearby_points(endpoints_tree, ep1.position, double(scaled_spacing)); - for (size_t close_endpoint_idx : close_endpoints) { - EndPoint &ep2 = connection_endpoints[close_endpoint_idx]; - if (ep2.used || ep2.polyline_idx == ep1.polyline_idx) { - continue; + // connect tiny gap fills to close colinear line + struct EndPoint + { + Vec2d position; + size_t polyline_idx; + size_t other_end_point_idx; + bool is_first; + bool used = false; + }; + std::vector connection_endpoints; + connection_endpoints.reserve(thick_polylines.size() * 2); + for (size_t pl_idx = 0; pl_idx < thick_polylines.size(); pl_idx++) { + size_t current_idx = connection_endpoints.size(); + connection_endpoints.push_back({thick_polylines[pl_idx].first_point().cast(), pl_idx, current_idx + 1, true}); + connection_endpoints.push_back({thick_polylines[pl_idx].last_point().cast(), pl_idx, current_idx, false}); + } + + auto coord_fn = [&connection_endpoints](size_t idx, size_t dim) { return connection_endpoints[idx].position[dim]; }; + KDTreeIndirect<2, double, decltype(coord_fn)> endpoints_tree{coord_fn, connection_endpoints.size()}; + for (size_t ep_idx = 0; ep_idx < connection_endpoints.size(); ep_idx++) { + EndPoint &ep1 = connection_endpoints[ep_idx]; + if (!ep1.used) { + std::vector close_endpoints = find_nearby_points(endpoints_tree, ep1.position, double(scaled_spacing)); + for (size_t close_endpoint_idx : close_endpoints) { + EndPoint &ep2 = connection_endpoints[close_endpoint_idx]; + if (ep2.used || ep2.polyline_idx == ep1.polyline_idx) { + continue; + } + + ThickPolyline &tp1 = thick_polylines[ep1.polyline_idx]; + ThickPolyline &tp2 = thick_polylines[ep2.polyline_idx]; + + Vec2d v1 = ep1.is_first ? (tp1.points[0] - tp1.points[1]).cast() : + (tp1.points.back() - tp1.points[tp1.points.size() - 1]).cast(); + Vec2d v2 = ep2.is_first ? (tp2.points[1] - tp2.points[0]).cast() : + (tp2.points[tp2.points.size() - 1] - tp2.points.back()).cast(); + + if (std::abs(Slic3r::angle(v1, v2)) > PI / 6.0) { + continue; + } + + // connect ep and ep2; + if (ep1.is_first) { + tp1.reverse(); + ep1.is_first = false; + connection_endpoints[ep1.other_end_point_idx].is_first = true; + } + + size_t new_start_idx = ep1.other_end_point_idx; + + if (!ep2.is_first) { + tp2.reverse(); + ep2.is_first = true; + connection_endpoints[ep2.other_end_point_idx].is_first = false; + } + + size_t new_end_idx = ep2.other_end_point_idx; + + tp1.points.insert(tp1.points.end(), tp2.points.begin(), tp2.points.end()); + tp1.width.push_back(tp1.width.back()); + tp1.width.push_back(tp2.width.front()); + tp1.width.insert(tp1.width.end(), tp2.width.begin(), tp2.width.end()); + ep1.used = true; + ep2.used = true; + + connection_endpoints[new_start_idx].polyline_idx = ep1.polyline_idx; + connection_endpoints[new_end_idx].polyline_idx = ep1.polyline_idx; + connection_endpoints[new_start_idx].other_end_point_idx = new_end_idx; + connection_endpoints[new_end_idx].other_end_point_idx = new_start_idx; + tp2.clear(); + break; } - - ThickPolyline &tp1 = thick_polylines[ep1.polyline_idx]; - ThickPolyline &tp2 = thick_polylines[ep2.polyline_idx]; - - Vec2d v1 = ep1.is_first ? (tp1.points[0] - tp1.points[1]).cast() : - (tp1.points.back() - tp1.points[tp1.points.size() - 1]).cast(); - Vec2d v2 = ep2.is_first ? (tp2.points[1] - tp2.points[0]).cast() : - (tp2.points[tp2.points.size() - 1] - tp2.points.back()).cast(); - - if (std::abs(Slic3r::angle(v1, v2)) > PI / 6.0) { - continue; - } - - // connect ep and ep2; - if (ep1.is_first) { - tp1.reverse(); - ep1.is_first = false; - connection_endpoints[ep1.other_end_point_idx].is_first = true; - } - - size_t new_start_idx = ep1.other_end_point_idx; - - if (!ep2.is_first) { - tp2.reverse(); - ep2.is_first = true; - connection_endpoints[ep2.other_end_point_idx].is_first = false; - } - - size_t new_end_idx = ep2.other_end_point_idx; - - tp1.points.insert(tp1.points.end(), tp2.points.begin(), tp2.points.end()); - tp1.width.push_back(tp1.width.back()); - tp1.width.push_back(tp2.width.front()); - tp1.width.insert(tp1.width.end(), tp2.width.begin(), tp2.width.end()); - ep1.used = true; - ep2.used = true; - - connection_endpoints[new_start_idx].polyline_idx = ep1.polyline_idx; - connection_endpoints[new_end_idx].polyline_idx = ep1.polyline_idx; - connection_endpoints[new_start_idx].other_end_point_idx = new_end_idx; - connection_endpoints[new_end_idx].other_end_point_idx = new_start_idx; - tp2.clear(); - break; } } - } - thick_polylines.erase(std::remove_if(thick_polylines.begin(), thick_polylines.end(), + thick_polylines.erase(std::remove_if(thick_polylines.begin(), thick_polylines.end(), [scaled_spacing](const ThickPolyline &tp) { return tp.length() < scaled_spacing && std::all_of(tp.width.begin(), tp.width.end(), [scaled_spacing](double w) { return w < scaled_spacing; }); }), thick_polylines.end()); + } - std::sort(thick_polylines.begin(), thick_polylines.end(), [](const ThickPolyline &left, const ThickPolyline &right) { - BoundingBox lbb(left.points); - BoundingBox rbb(right.points); - if (lbb.min.x() == rbb.min.x()) - return lbb.max.y() < rbb.max.y(); - else - return lbb.max.x() < rbb.max.x(); - }); - - Algorithm::sort_paths(thick_polylines.begin(), thick_polylines.end(), bb.min, double(scaled_spacing) * 1.2, [](const ThickPolyline - &tp) { + Algorithm::sort_paths(thick_polylines.begin(), thick_polylines.end(), bb.min, double(scaled_spacing) * 1.2, [](const ThickPolyline &tp) { Lines ls; Point prev = tp.first_point(); for (size_t i = 1; i < tp.points.size(); i++) { diff --git a/src/libslic3r/Fill/FillEnsuring.hpp b/src/libslic3r/Fill/FillEnsuring.hpp index faa0801535..8f36dde2df 100644 --- a/src/libslic3r/Fill/FillEnsuring.hpp +++ b/src/libslic3r/Fill/FillEnsuring.hpp @@ -6,13 +6,18 @@ namespace Slic3r { -class FillEnsuring : public FillRectilinear +ThickPolylines make_fill_polylines(const Fill *fill, const Surface *surface, const FillParams ¶ms, bool stop_vibrations, bool fill_gaps); + +class FillEnsuring : public Fill { public: Fill *clone() const override { return new FillEnsuring(*this); } ~FillEnsuring() override = default; Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override { return {}; }; - ThickPolylines fill_surface_arachne(const Surface *surface, const FillParams ¶ms) override; + ThickPolylines fill_surface_arachne(const Surface *surface, const FillParams ¶ms) override + { + return make_fill_polylines(this, surface, params, true, true); + }; protected: void fill_surface_single_arachne(const Surface &surface, const FillParams ¶ms, ThickPolylines &thick_polylines_out); From 274d8ee3e7f7143e7119025072a751ce56d39651 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 2 May 2023 17:35:31 +0200 Subject: [PATCH 51/90] Fixed and integrated connected fill version --- src/libslic3r/Fill/FillEnsuring.cpp | 332 +++++++++++----------------- src/libslic3r/Fill/FillEnsuring.hpp | 5 +- 2 files changed, 135 insertions(+), 202 deletions(-) diff --git a/src/libslic3r/Fill/FillEnsuring.cpp b/src/libslic3r/Fill/FillEnsuring.cpp index 2a24e2360e..25e28fc091 100644 --- a/src/libslic3r/Fill/FillEnsuring.cpp +++ b/src/libslic3r/Fill/FillEnsuring.cpp @@ -25,7 +25,8 @@ namespace Slic3r { -ThickPolylines make_fill_polylines(const Fill *fill, const Surface *surface, const FillParams ¶ms, bool stop_vibrations, bool fill_gaps) +ThickPolylines make_fill_polylines( + const Fill *fill, const Surface *surface, const FillParams ¶ms, bool stop_vibrations, bool fill_gaps, bool connect_extrusions) { assert(fill->print_config != nullptr && fill->print_object_config != nullptr && fill->print_region_config != nullptr); @@ -46,13 +47,20 @@ ThickPolylines make_fill_polylines(const Fill *fill, const Surface *surface, con }; const coord_t scaled_spacing = scaled(fill->spacing); - double distance_limit_reconnection = double(scaled_spacing); + double distance_limit_reconnection = 2.0 * double(scaled_spacing); double squared_distance_limit_reconnection = distance_limit_reconnection * distance_limit_reconnection; Polygons filled_area = to_polygons(surface->expolygon); std::pair rotate_vector = fill->_infill_direction(surface); double aligning_angle = -rotate_vector.first + PI; polygons_rotate(filled_area, aligning_angle); - BoundingBox bb = get_extents(filled_area); + BoundingBox bb = get_extents(filled_area); + + Polygons inner_area = stop_vibrations ? intersection(filled_area, opening(filled_area, 2 * scaled_spacing, 3 * scaled_spacing)) : + filled_area; + if (connect_extrusions) { + inner_area = shrink(inner_area, scaled_spacing * 0.5); + } + AABBTreeLines::LinesDistancer area_walls{to_lines(inner_area)}; const size_t n_vlines = (bb.max.x() - bb.min.x() + scaled_spacing - 1) / scaled_spacing; std::vector vertical_lines(n_vlines); @@ -67,14 +75,6 @@ ThickPolylines make_fill_polylines(const Fill *fill, const Surface *surface, con vertical_lines.back().a = Point{coord_t(bb.min.x() + n_vlines * double(scaled_spacing) + scaled_spacing * 0.5), y_min}; vertical_lines.back().b = Point{vertical_lines.back().a.x(), y_max}; - AABBTreeLines::LinesDistancer area_walls; - if (stop_vibrations) { - area_walls = AABBTreeLines::LinesDistancer{ - to_lines(intersection(filled_area, opening(filled_area, 2 * scaled_spacing, 3 * scaled_spacing)))}; - } else { - area_walls = AABBTreeLines::LinesDistancer{to_lines(filled_area)}; - } - std::vector> polygon_sections(n_vlines); for (size_t i = 0; i < n_vlines; i++) { @@ -175,98 +175,6 @@ ThickPolylines make_fill_polylines(const Fill *fill, const Surface *surface, con [](const Line &a, const Line &b) { return a.a.y() < b.b.y(); }); } - Polygons reconstructed_area{}; - // reconstruct polygon from polygon sections - { - struct TracedPoly - { - Points lows; - Points highs; - }; - - std::vector current_traced_polys; - for (const auto &polygon_slice : polygon_sections) { - std::unordered_set used_segments; - for (TracedPoly &traced_poly : current_traced_polys) { - auto candidates_begin = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), traced_poly.lows.back(), - [](const Point &low, const Line &seg) { return seg.b.y() > low.y(); }); - auto candidates_end = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), traced_poly.highs.back(), - [](const Point &high, const Line &seg) { return seg.a.y() > high.y(); }); - - bool segment_added = false; - for (auto candidate = candidates_begin; candidate != candidates_end && !segment_added; candidate++) { - if (used_segments.find(&(*candidate)) != used_segments.end()) { - continue; - } - if ((traced_poly.lows.back() - candidates_begin->a).cast().squaredNorm() < squared_distance_limit_reconnection) { - traced_poly.lows.push_back(candidates_begin->a); - } else { - traced_poly.lows.push_back(traced_poly.lows.back() + Point{scaled_spacing / 2, 0}); - traced_poly.lows.push_back(candidates_begin->a - Point{scaled_spacing / 2, 0}); - traced_poly.lows.push_back(candidates_begin->a); - } - - if ((traced_poly.highs.back() - candidates_begin->b).cast().squaredNorm() < - squared_distance_limit_reconnection) { - traced_poly.highs.push_back(candidates_begin->b); - } else { - traced_poly.highs.push_back(traced_poly.highs.back() + Point{scaled_spacing / 2, 0}); - traced_poly.highs.push_back(candidates_begin->b - Point{scaled_spacing / 2, 0}); - traced_poly.highs.push_back(candidates_begin->b); - } - segment_added = true; - used_segments.insert(&(*candidates_begin)); - } - - if (!segment_added) { - // Zero or multiple overlapping segments. Resolving this is nontrivial, - // so we just close this polygon and maybe open several new. This will hopefully happen much less often - traced_poly.lows.push_back(traced_poly.lows.back() + Point{scaled_spacing / 2, 0}); - traced_poly.highs.push_back(traced_poly.highs.back() + Point{scaled_spacing / 2, 0}); - Polygon &new_poly = reconstructed_area.emplace_back(std::move(traced_poly.lows)); - new_poly.points.insert(new_poly.points.end(), traced_poly.highs.rbegin(), traced_poly.highs.rend()); - traced_poly.lows.clear(); - traced_poly.highs.clear(); - } - } - - current_traced_polys.erase(std::remove_if(current_traced_polys.begin(), current_traced_polys.end(), - [](const TracedPoly &tp) { return tp.lows.empty(); }), - current_traced_polys.end()); - - for (const auto &segment : polygon_slice) { - if (used_segments.find(&segment) == used_segments.end()) { - TracedPoly &new_tp = current_traced_polys.emplace_back(); - new_tp.lows.push_back(segment.a - Point{scaled_spacing / 2, 0}); - new_tp.lows.push_back(segment.a); - new_tp.highs.push_back(segment.b - Point{scaled_spacing / 2, 0}); - new_tp.highs.push_back(segment.b); - } - } - } - - // add not closed polys - for (TracedPoly &traced_poly : current_traced_polys) { - Polygon &new_poly = reconstructed_area.emplace_back(std::move(traced_poly.lows)); - new_poly.points.insert(new_poly.points.end(), traced_poly.highs.rbegin(), traced_poly.highs.rend()); - } - } - - reconstructed_area = closing(reconstructed_area, float(SCALED_EPSILON), float(SCALED_EPSILON)); - ExPolygons gaps_for_additional_filling = diff_ex(filled_area, reconstructed_area); - if (fill->overlap != 0) { - gaps_for_additional_filling = offset_ex(gaps_for_additional_filling, scaled(fill->overlap)); - } - - // BoundingBox bbox = get_extents(filled_area); - // bbox.offset(scale_(1.)); - // ::Slic3r::SVG svg(debug_out_path(("surface" + std::to_string(surface->area())).c_str()).c_str(), bbox); - // svg.draw(to_lines(filled_area), "red", scale_(0.4)); - // svg.draw(to_lines(reconstructed_area), "blue", scale_(0.3)); - // svg.draw(to_lines(gaps_for_additional_filling), "green", scale_(0.2)); - // svg.draw(vertical_lines, "black", scale_(0.1)); - // svg.Close(); - ThickPolylines thick_polylines; { for (const auto &polygon_slice : polygon_sections) { @@ -282,6 +190,104 @@ ThickPolylines make_fill_polylines(const Fill *fill, const Surface *surface, con } if (fill_gaps) { + Polygons reconstructed_area{}; + // reconstruct polygon from polygon sections + { + struct TracedPoly + { + Points lows; + Points highs; + }; + + //additional connection thickness + Point act{0, coord_t(connect_extrusions ? 0.5 * scaled_spacing : 0)}; + + std::vector current_traced_polys; + for (const auto &polygon_slice : polygon_sections) { + std::unordered_set used_segments; + for (TracedPoly &traced_poly : current_traced_polys) { + auto candidates_begin = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), traced_poly.lows.back(), + [](const Point &low, const Line &seg) { return seg.b.y() > low.y(); }); + auto candidates_end = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), traced_poly.highs.back(), + [](const Point &high, const Line &seg) { return seg.a.y() > high.y(); }); + + bool segment_added = false; + for (auto candidate = candidates_begin; candidate != candidates_end && !segment_added; candidate++) { + if (used_segments.find(&(*candidate)) != used_segments.end()) { + continue; + } + if ((traced_poly.lows.back() - candidates_begin->a).cast().squaredNorm() < + squared_distance_limit_reconnection) { + traced_poly.lows.back() -= act; + traced_poly.lows.push_back(candidates_begin->a - act); + } else { + traced_poly.lows.push_back(traced_poly.lows.back() + Point{scaled_spacing / 2, 0}); + traced_poly.lows.push_back(candidates_begin->a - Point{scaled_spacing / 2, 0}); + traced_poly.lows.push_back(candidates_begin->a); + } + + if ((traced_poly.highs.back() - candidates_begin->b).cast().squaredNorm() < + squared_distance_limit_reconnection) { + traced_poly.highs.back() += act; + traced_poly.highs.push_back(candidates_begin->b + act); + } else { + traced_poly.highs.push_back(traced_poly.highs.back() + Point{scaled_spacing / 2, 0}); + traced_poly.highs.push_back(candidates_begin->b - Point{scaled_spacing / 2, 0}); + traced_poly.highs.push_back(candidates_begin->b); + } + segment_added = true; + used_segments.insert(&(*candidates_begin)); + } + + if (!segment_added) { + // Zero or multiple overlapping segments. Resolving this is nontrivial, + // so we just close this polygon and maybe open several new. This will hopefully happen much less often + traced_poly.lows.push_back(traced_poly.lows.back() + Point{scaled_spacing / 2, 0}); + traced_poly.highs.push_back(traced_poly.highs.back() + Point{scaled_spacing / 2, 0}); + Polygon &new_poly = reconstructed_area.emplace_back(std::move(traced_poly.lows)); + new_poly.points.insert(new_poly.points.end(), traced_poly.highs.rbegin(), traced_poly.highs.rend()); + traced_poly.lows.clear(); + traced_poly.highs.clear(); + } + } + + current_traced_polys.erase(std::remove_if(current_traced_polys.begin(), current_traced_polys.end(), + [](const TracedPoly &tp) { return tp.lows.empty(); }), + current_traced_polys.end()); + + for (const auto &segment : polygon_slice) { + if (used_segments.find(&segment) == used_segments.end()) { + TracedPoly &new_tp = current_traced_polys.emplace_back(); + new_tp.lows.push_back(segment.a - Point{scaled_spacing / 2, 0}); + new_tp.lows.push_back(segment.a); + new_tp.highs.push_back(segment.b - Point{scaled_spacing / 2, 0}); + new_tp.highs.push_back(segment.b); + } + } + } + + // add not closed polys + for (TracedPoly &traced_poly : current_traced_polys) { + Polygon &new_poly = reconstructed_area.emplace_back(std::move(traced_poly.lows)); + new_poly.points.insert(new_poly.points.end(), traced_poly.highs.rbegin(), traced_poly.highs.rend()); + } + } + + reconstructed_area = closing(reconstructed_area, float(SCALED_EPSILON), float(SCALED_EPSILON)); + ExPolygons gaps_for_additional_filling = diff_ex(filled_area, reconstructed_area); + if (fill->overlap != 0) { + gaps_for_additional_filling = offset_ex(gaps_for_additional_filling, scaled(fill->overlap)); + } + + // BoundingBox bbox = get_extents(filled_area); + // bbox.offset(scale_(1.)); + // ::Slic3r::SVG svg(debug_out_path(("surface" + std::to_string(surface->area())).c_str()).c_str(), bbox); + // svg.draw(to_lines(filled_area), "red", scale_(0.4)); + // svg.draw(to_lines(reconstructed_area), "blue", scale_(0.3)); + // svg.draw(to_lines(gaps_for_additional_filling), "green", scale_(0.2)); + // svg.draw(vertical_lines, "black", scale_(0.1)); + // svg.Close(); + for (ExPolygon &ex_poly : gaps_for_additional_filling) { BoundingBox ex_bb = ex_poly.contour.bounding_box(); coord_t loops_count = (std::max(ex_bb.size().x(), ex_bb.size().y()) + scaled_spacing - 1) / scaled_spacing; @@ -416,105 +422,31 @@ ThickPolylines make_fill_polylines(const Fill *fill, const Surface *surface, con return ls; }); - // ThickPolylines connected_thick_polylines; - // if (!thick_polylines.empty()) { - // connected_thick_polylines.push_back(thick_polylines.front()); - // for (ThickPolyline &tp : thick_polylines) { - // ThickPolyline &tail = connected_thick_polylines.back(); - // Point last = tail.last_point(); - // if ((last - tp.last_point()).cast().squaredNorm() < (last - tp.first_point()).cast().squaredNorm()) { - // tp.reverse(); - // } - // if ((last - tp.first_point()).cast().squaredNorm() < squared_distance_limit_reconnection) { - // tail.points.insert(tail.points.end(), tp.points.begin(), tp.points.end()); - // tail.width.push_back(0); - // tail.width.push_back(0); - // tail.width.insert(tail.width.end(), tp.width.begin(), tp.width.end()); - // } else { - // connected_thick_polylines.push_back(tp); - // } - // } - // } + if (connect_extrusions) { + ThickPolylines connected_thick_polylines; + if (!thick_polylines.empty()) { + connected_thick_polylines.push_back(thick_polylines.front()); + for (ThickPolyline &tp : thick_polylines) { + ThickPolyline &tail = connected_thick_polylines.back(); + Point last = tail.last_point(); + if ((last - tp.last_point()).cast().squaredNorm() < (last - tp.first_point()).cast().squaredNorm()) { + tp.reverse(); + } + if ((last - tp.first_point()).cast().squaredNorm() < squared_distance_limit_reconnection) { + tail.points.insert(tail.points.end(), tp.points.begin(), tp.points.end()); + tail.width.push_back(scaled_spacing); + tail.width.push_back(scaled_spacing); + tail.width.insert(tail.width.end(), tp.width.begin(), tp.width.end()); + } else { + connected_thick_polylines.push_back(tp); + } + } + } + thick_polylines = connected_thick_polylines; + } + rotate_thick_polylines(thick_polylines, cos(-aligning_angle), sin(-aligning_angle)); return thick_polylines; } } // namespace Slic3r - - // const size_t n_vlines = (bb.max.x() - bb.min.x() + scaled_spacing - 1) / scaled_spacing; - // std::vector vertical_lines(2 * n_vlines + 1); - // coord_t y_min = bb.min.y(); - // coord_t y_max = bb.max.y(); - // for (size_t i = 0; i < n_vlines; i++) { - // coord_t x0 = bb.min.x() + i * double(scaled_spacing) - scaled_spacing * 0.5; - // coord_t x1 = bb.min.x() + i * double(scaled_spacing); - // vertical_lines[i * 2].a = Point{x0, y_min}; - // vertical_lines[i * 2].b = Point{x0, y_max}; - // vertical_lines[i * 2 + 1].a = Point{x1, y_min}; - // vertical_lines[i * 2 + 1].b = Point{x1, y_max}; - // } - // vertical_lines.back().a = Point{coord_t(bb.min.x() + n_vlines * double(scaled_spacing) + scaled_spacing * 0.5), y_min}; - // vertical_lines.back().b = Point{vertical_lines.back().a.x(), y_max}; - - // auto area_walls = AABBTreeLines::LinesDistancer{to_lines(internal_area)}; - // std::vector, size_t>>> vertical_lines_intersections(vertical_lines.size()); - // for (int i = 0; i < vertical_lines.size(); i++) { - // vertical_lines_intersections[i] = area_walls.intersections_with_line(vertical_lines[i]); - // } - // std::vector> polygon_sections(n_vlines); - - // for (size_t i = 0; i < n_vlines; i++) { - // const auto ¢ral_intersections = vertical_lines_intersections[i * 2 + 1]; - // const auto &left_intersections = vertical_lines_intersections[i * 2]; - // const auto &right_intersections = vertical_lines_intersections[i * 2 + 2]; - - // for (int intersection_idx = 0; intersection_idx < int(central_intersections.size()) - 1; intersection_idx++) { - // const auto &a = central_intersections[intersection_idx]; - // const auto &b = central_intersections[intersection_idx + 1]; - // if (area_walls.outside((a.first + b.first) / 2) < 0) { - // // central part is inside. Now check for reasonable side distances - - // auto get_closest_intersection_squared_dist = - // [](const std::pair, size_t> &point, - // const std::vector, size_t>> &sorted_intersections) { - // if (sorted_intersections.empty()) { - // return 0.0; - // } - // auto closest_higher = std::upper_bound(sorted_intersections.begin(), sorted_intersections.end(), point, - // [](const std::pair, size_t> &left, - // const std::pair, size_t> &right) { - // return left.first.y() < right.first.y(); - // }); - // if (closest_higher == sorted_intersections.end()) { - // return (point.first - sorted_intersections.back().first).cast().squaredNorm(); - // } - // double candidate_dist = (point.first - closest_higher->first).cast().squaredNorm(); - // if (closest_higher != sorted_intersections.begin()) { - // double closest_lower_dist = (point.first - (--closest_higher)->first).cast().squaredNorm(); - // candidate_dist = std::min(candidate_dist, closest_lower_dist); - // } - // return candidate_dist; - // }; - // Point section_a = a.first; - // Point section_b = b.first; - - // double max_a_squared_dist = std::max(get_closest_intersection_squared_dist(a, left_intersections), - // get_closest_intersection_squared_dist(a, right_intersections)); - - // double max_b_squared_dist = std::max(get_closest_intersection_squared_dist(b, left_intersections), - // get_closest_intersection_squared_dist(b, right_intersections)); - - // if (max_a_squared_dist > 0.3 * squared_distance_limit_reconnection) { - // section_a.y() += 4.0 * scaled_spacing; - // } - - // if (max_b_squared_dist > 0.3 * squared_distance_limit_reconnection) { - // section_b.y() -= 4.0 * scaled_spacing; - // } - - // if (section_a.y() < section_b.y()) { - // polygon_sections[i].emplace_back(section_a, section_b); - // } - // } - // } - // } diff --git a/src/libslic3r/Fill/FillEnsuring.hpp b/src/libslic3r/Fill/FillEnsuring.hpp index 8f36dde2df..c20c93aff8 100644 --- a/src/libslic3r/Fill/FillEnsuring.hpp +++ b/src/libslic3r/Fill/FillEnsuring.hpp @@ -6,7 +6,8 @@ namespace Slic3r { -ThickPolylines make_fill_polylines(const Fill *fill, const Surface *surface, const FillParams ¶ms, bool stop_vibrations, bool fill_gaps); +ThickPolylines make_fill_polylines( + const Fill *fill, const Surface *surface, const FillParams ¶ms, bool stop_vibrations, bool fill_gaps, bool connect_extrusions); class FillEnsuring : public Fill { @@ -16,7 +17,7 @@ public: Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override { return {}; }; ThickPolylines fill_surface_arachne(const Surface *surface, const FillParams ¶ms) override { - return make_fill_polylines(this, surface, params, true, true); + return make_fill_polylines(this, surface, params, true, true, true); }; protected: From c17803812a89da032a0722e4de42cc46d3b12d3d Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 3 May 2023 12:36:19 +0200 Subject: [PATCH 52/90] fix gap fill connections breaking the extrusion order for sorting --- src/libslic3r/Fill/FillEnsuring.cpp | 55 +++++++++++++++-------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/src/libslic3r/Fill/FillEnsuring.cpp b/src/libslic3r/Fill/FillEnsuring.cpp index 25e28fc091..fa534a75c0 100644 --- a/src/libslic3r/Fill/FillEnsuring.cpp +++ b/src/libslic3r/Fill/FillEnsuring.cpp @@ -357,47 +357,50 @@ ThickPolylines make_fill_polylines( continue; } - ThickPolyline &tp1 = thick_polylines[ep1.polyline_idx]; - ThickPolyline &tp2 = thick_polylines[ep2.polyline_idx]; + EndPoint &target_ep = ep1.polyline_idx > ep2.polyline_idx ? ep1 : ep2; + EndPoint &source_ep = ep1.polyline_idx > ep2.polyline_idx ? ep2 : ep1; - Vec2d v1 = ep1.is_first ? (tp1.points[0] - tp1.points[1]).cast() : - (tp1.points.back() - tp1.points[tp1.points.size() - 1]).cast(); - Vec2d v2 = ep2.is_first ? (tp2.points[1] - tp2.points[0]).cast() : - (tp2.points[tp2.points.size() - 1] - tp2.points.back()).cast(); + ThickPolyline &target_tp = thick_polylines[target_ep.polyline_idx]; + ThickPolyline &source_tp = thick_polylines[source_ep.polyline_idx]; + + Vec2d v1 = target_ep.is_first ? (target_tp.points[0] - target_tp.points[1]).cast() : + (target_tp.points.back() - target_tp.points[target_tp.points.size() - 1]).cast(); + Vec2d v2 = source_ep.is_first ? (source_tp.points[1] - source_tp.points[0]).cast() : + (source_tp.points[source_tp.points.size() - 1] - source_tp.points.back()).cast(); if (std::abs(Slic3r::angle(v1, v2)) > PI / 6.0) { continue; } - // connect ep and ep2; - if (ep1.is_first) { - tp1.reverse(); - ep1.is_first = false; - connection_endpoints[ep1.other_end_point_idx].is_first = true; + // connect target_ep and source_ep, result is stored in target_tp, source_tp will be cleared + if (target_ep.is_first) { + target_tp.reverse(); + target_ep.is_first = false; + connection_endpoints[target_ep.other_end_point_idx].is_first = true; } - size_t new_start_idx = ep1.other_end_point_idx; + size_t new_start_idx = target_ep.other_end_point_idx; - if (!ep2.is_first) { - tp2.reverse(); - ep2.is_first = true; - connection_endpoints[ep2.other_end_point_idx].is_first = false; + if (!source_ep.is_first) { + source_tp.reverse(); + source_ep.is_first = true; + connection_endpoints[source_ep.other_end_point_idx].is_first = false; } - size_t new_end_idx = ep2.other_end_point_idx; + size_t new_end_idx = source_ep.other_end_point_idx; - tp1.points.insert(tp1.points.end(), tp2.points.begin(), tp2.points.end()); - tp1.width.push_back(tp1.width.back()); - tp1.width.push_back(tp2.width.front()); - tp1.width.insert(tp1.width.end(), tp2.width.begin(), tp2.width.end()); - ep1.used = true; - ep2.used = true; + target_tp.points.insert(target_tp.points.end(), source_tp.points.begin(), source_tp.points.end()); + target_tp.width.push_back(target_tp.width.back()); + target_tp.width.push_back(source_tp.width.front()); + target_tp.width.insert(target_tp.width.end(), source_tp.width.begin(), source_tp.width.end()); + target_ep.used = true; + source_ep.used = true; - connection_endpoints[new_start_idx].polyline_idx = ep1.polyline_idx; - connection_endpoints[new_end_idx].polyline_idx = ep1.polyline_idx; + connection_endpoints[new_start_idx].polyline_idx = target_ep.polyline_idx; + connection_endpoints[new_end_idx].polyline_idx = target_ep.polyline_idx; connection_endpoints[new_start_idx].other_end_point_idx = new_end_idx; connection_endpoints[new_end_idx].other_end_point_idx = new_start_idx; - tp2.clear(); + source_tp.clear(); break; } } From 26bb408ee265438b2949162fac622509b76034ee Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 3 May 2023 16:50:40 +0200 Subject: [PATCH 53/90] fix ordering issues, fix overlap --- src/libslic3r/Fill/FillEnsuring.cpp | 54 +++++++++++++++++++---------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/Fill/FillEnsuring.cpp b/src/libslic3r/Fill/FillEnsuring.cpp index fa534a75c0..8452a37fb7 100644 --- a/src/libslic3r/Fill/FillEnsuring.cpp +++ b/src/libslic3r/Fill/FillEnsuring.cpp @@ -199,11 +199,16 @@ ThickPolylines make_fill_polylines( Points highs; }; - //additional connection thickness - Point act{0, coord_t(connect_extrusions ? 0.5 * scaled_spacing : 0)}; + std::vector> polygon_sections_w_width = polygon_sections; + for (auto &slice : polygon_sections_w_width) { + for (Line &l : slice) { + l.a -= Point{0.0, 0.5 * scaled_spacing}; + l.b += Point{0.0, 0.5 * scaled_spacing}; + } + } std::vector current_traced_polys; - for (const auto &polygon_slice : polygon_sections) { + for (const auto &polygon_slice : polygon_sections_w_width) { std::unordered_set used_segments; for (TracedPoly &traced_poly : current_traced_polys) { auto candidates_begin = std::upper_bound(polygon_slice.begin(), polygon_slice.end(), traced_poly.lows.back(), @@ -216,20 +221,18 @@ ThickPolylines make_fill_polylines( if (used_segments.find(&(*candidate)) != used_segments.end()) { continue; } - if ((traced_poly.lows.back() - candidates_begin->a).cast().squaredNorm() < - squared_distance_limit_reconnection) { - traced_poly.lows.back() -= act; - traced_poly.lows.push_back(candidates_begin->a - act); + if (connect_extrusions && (traced_poly.lows.back() - candidates_begin->a).cast().squaredNorm() < + squared_distance_limit_reconnection) { + traced_poly.lows.push_back(candidates_begin->a); } else { traced_poly.lows.push_back(traced_poly.lows.back() + Point{scaled_spacing / 2, 0}); traced_poly.lows.push_back(candidates_begin->a - Point{scaled_spacing / 2, 0}); traced_poly.lows.push_back(candidates_begin->a); } - if ((traced_poly.highs.back() - candidates_begin->b).cast().squaredNorm() < - squared_distance_limit_reconnection) { - traced_poly.highs.back() += act; - traced_poly.highs.push_back(candidates_begin->b + act); + if (connect_extrusions && (traced_poly.highs.back() - candidates_begin->b).cast().squaredNorm() < + squared_distance_limit_reconnection) { + traced_poly.highs.push_back(candidates_begin->b); } else { traced_poly.highs.push_back(traced_poly.highs.back() + Point{scaled_spacing / 2, 0}); traced_poly.highs.push_back(candidates_begin->b - Point{scaled_spacing / 2, 0}); @@ -345,6 +348,13 @@ ThickPolylines make_fill_polylines( connection_endpoints.push_back({thick_polylines[pl_idx].last_point().cast(), pl_idx, current_idx, false}); } + std::vector linear_segment_flags(thick_polylines.size()); + for (size_t i = 0;i < thick_polylines.size(); i++) { + const ThickPolyline& tp = thick_polylines[i]; + linear_segment_flags[i] = tp.points.size() == 2 && tp.points.front().x() == tp.points.back().x() && + tp.width.front() == scaled_spacing && tp.width.back() == scaled_spacing; + } + auto coord_fn = [&connection_endpoints](size_t idx, size_t dim) { return connection_endpoints[idx].position[dim]; }; KDTreeIndirect<2, double, decltype(coord_fn)> endpoints_tree{coord_fn, connection_endpoints.size()}; for (size_t ep_idx = 0; ep_idx < connection_endpoints.size(); ep_idx++) { @@ -353,20 +363,25 @@ ThickPolylines make_fill_polylines( std::vector close_endpoints = find_nearby_points(endpoints_tree, ep1.position, double(scaled_spacing)); for (size_t close_endpoint_idx : close_endpoints) { EndPoint &ep2 = connection_endpoints[close_endpoint_idx]; - if (ep2.used || ep2.polyline_idx == ep1.polyline_idx) { + if (ep2.used || ep2.polyline_idx == ep1.polyline_idx || + (linear_segment_flags[ep1.polyline_idx] && linear_segment_flags[ep2.polyline_idx])) { continue; } EndPoint &target_ep = ep1.polyline_idx > ep2.polyline_idx ? ep1 : ep2; EndPoint &source_ep = ep1.polyline_idx > ep2.polyline_idx ? ep2 : ep1; - ThickPolyline &target_tp = thick_polylines[target_ep.polyline_idx]; - ThickPolyline &source_tp = thick_polylines[source_ep.polyline_idx]; + ThickPolyline &target_tp = thick_polylines[target_ep.polyline_idx]; + ThickPolyline &source_tp = thick_polylines[source_ep.polyline_idx]; + linear_segment_flags[target_ep.polyline_idx] = linear_segment_flags[ep1.polyline_idx] || + linear_segment_flags[ep2.polyline_idx]; - Vec2d v1 = target_ep.is_first ? (target_tp.points[0] - target_tp.points[1]).cast() : - (target_tp.points.back() - target_tp.points[target_tp.points.size() - 1]).cast(); - Vec2d v2 = source_ep.is_first ? (source_tp.points[1] - source_tp.points[0]).cast() : - (source_tp.points[source_tp.points.size() - 1] - source_tp.points.back()).cast(); + Vec2d v1 = target_ep.is_first ? + (target_tp.points[0] - target_tp.points[1]).cast() : + (target_tp.points.back() - target_tp.points[target_tp.points.size() - 1]).cast(); + Vec2d v2 = source_ep.is_first ? + (source_tp.points[1] - source_tp.points[0]).cast() : + (source_tp.points[source_tp.points.size() - 1] - source_tp.points.back()).cast(); if (std::abs(Slic3r::angle(v1, v2)) > PI / 6.0) { continue; @@ -429,7 +444,8 @@ ThickPolylines make_fill_polylines( ThickPolylines connected_thick_polylines; if (!thick_polylines.empty()) { connected_thick_polylines.push_back(thick_polylines.front()); - for (ThickPolyline &tp : thick_polylines) { + for (size_t tp_idx = 1; tp_idx < thick_polylines.size(); tp_idx++) { + ThickPolyline &tp = thick_polylines[tp_idx]; ThickPolyline &tail = connected_thick_polylines.back(); Point last = tail.last_point(); if ((last - tp.last_point()).cast().squaredNorm() < (last - tp.first_point()).cast().squaredNorm()) { From 0358eef42f7fefae123353823f6b918b2d731218 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 3 May 2023 17:52:54 +0200 Subject: [PATCH 54/90] make overlap fix unconditional --- src/libslic3r/Fill/FillEnsuring.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Fill/FillEnsuring.cpp b/src/libslic3r/Fill/FillEnsuring.cpp index 8452a37fb7..21c4dac3d8 100644 --- a/src/libslic3r/Fill/FillEnsuring.cpp +++ b/src/libslic3r/Fill/FillEnsuring.cpp @@ -57,9 +57,9 @@ ThickPolylines make_fill_polylines( Polygons inner_area = stop_vibrations ? intersection(filled_area, opening(filled_area, 2 * scaled_spacing, 3 * scaled_spacing)) : filled_area; - if (connect_extrusions) { - inner_area = shrink(inner_area, scaled_spacing * 0.5); - } + + inner_area = shrink(inner_area, scaled_spacing * 0.5 - scaled(fill->overlap)); + AABBTreeLines::LinesDistancer area_walls{to_lines(inner_area)}; const size_t n_vlines = (bb.max.x() - bb.min.x() + scaled_spacing - 1) / scaled_spacing; From a42d8571367d825a454e993a1f4e56883d3d3629 Mon Sep 17 00:00:00 2001 From: Pavel Date: Fri, 12 May 2023 11:28:35 +0200 Subject: [PATCH 55/90] fix comment to reflect the current state --- src/libslic3r/Fill/Fill.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 423329fc81..abf4b1a2b3 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -306,7 +306,7 @@ std::vector group_fills(const Layer &layer) } } - // Detect narrow internal solid infill area and use ipEnsuring pattern instead. + // Use ipEnsuring pattern for all internal Solids. { for (size_t surface_fill_id = 0; surface_fill_id < surface_fills.size(); ++surface_fill_id) if (SurfaceFill &fill = surface_fills[surface_fill_id]; fill.surface.surface_type == stInternalSolid) { From 6b41e5d9b1f2867343e3664e7fb304c105042b9c Mon Sep 17 00:00:00 2001 From: Pavel Date: Fri, 12 May 2023 13:44:58 +0200 Subject: [PATCH 56/90] Fix debug build, there was obsolete assert --- src/libslic3r/Fill/FillEnsuring.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Fill/FillEnsuring.cpp b/src/libslic3r/Fill/FillEnsuring.cpp index 21c4dac3d8..a64c0fd83c 100644 --- a/src/libslic3r/Fill/FillEnsuring.cpp +++ b/src/libslic3r/Fill/FillEnsuring.cpp @@ -28,7 +28,7 @@ namespace Slic3r { ThickPolylines make_fill_polylines( const Fill *fill, const Surface *surface, const FillParams ¶ms, bool stop_vibrations, bool fill_gaps, bool connect_extrusions) { - assert(fill->print_config != nullptr && fill->print_object_config != nullptr && fill->print_region_config != nullptr); + assert(fill->print_config != nullptr && fill->print_object_config != nullptr); auto rotate_thick_polylines = [](ThickPolylines &tpolylines, double cos_angle, double sin_angle) { for (ThickPolyline &tp : tpolylines) { From 35195dc03e5c175119b939f123b247b93e6d970e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 12 May 2023 14:09:16 +0200 Subject: [PATCH 57/90] Revert "Merge remote-tracking branch 'origin/et_outofbed_optimization'" This reverts commit a430aa41bac56875476dddb20c1b2a99b3a15833, reversing changes made to a42d8571367d825a454e993a1f4e56883d3d3629. --- src/libslic3r/Print.cpp | 10 +- src/libslic3r/Print.hpp | 4 - src/slic3r/GUI/3DScene.cpp | 58 ++++ src/slic3r/GUI/3DScene.hpp | 3 + src/slic3r/GUI/GLCanvas3D.cpp | 331 +++++----------------- src/slic3r/GUI/GLCanvas3D.hpp | 48 ++-- src/slic3r/GUI/GLModel.cpp | 32 --- src/slic3r/GUI/GLModel.hpp | 1 - src/slic3r/GUI/GUI_ObjectManipulation.cpp | 14 +- src/slic3r/GUI/Plater.cpp | 39 +-- 10 files changed, 173 insertions(+), 367 deletions(-) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 0c2310d6f9..5b23dc2606 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -469,13 +469,11 @@ 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_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."); + if (! sequential_print_horizontal_clearance_valid(*this)) + 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_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 c96b0ca9a2..30f6bb41a7 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -609,7 +609,6 @@ 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_contours() const { return m_sequential_print_clearance_contours; } static bool sequential_print_horizontal_clearance_valid(const Print& print, Polygons* polygons = nullptr); protected: @@ -659,9 +658,6 @@ private: // Estimated print time, filament consumed. PrintStatistics m_print_statistics; - // 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; // To allow GCodeProcessor to emit warnings. diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 7c5008554a..dea40fb3e5 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -833,6 +833,64 @@ 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 805972ce79..2f7e0a7672 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -450,6 +450,9 @@ 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 9df4069a86..fc2e04d5ad 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -880,22 +880,20 @@ void GLCanvas3D::Tooltip::render(const Vec2d& mouse_position, GLCanvas3D& canvas ImGui::PopStyleVar(2); } -void GLCanvas3D::SequentialPrintClearance::set_contours(const ContoursList& contours, bool generate_fill) +void GLCanvas3D::SequentialPrintClearance::set_polygons(const Polygons& polygons) { - m_contours.clear(); - m_instances.clear(); + m_perimeter.reset(); m_fill.reset(); - - if (contours.empty()) + if (polygons.empty()) return; - if (generate_fill) { + 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(contours.contours); + const ExPolygons polygons_union = union_ex(polygons); unsigned int vertices_counter = 0; for (const ExPolygon& poly : polygons_union) { const std::vector triangulation = triangulate_expolygon_3d(poly); @@ -908,48 +906,17 @@ void GLCanvas3D::SequentialPrintClearance::set_contours(const ContoursList& cont fill_data.add_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1); } } + m_fill.init_from(std::move(fill_data)); } - 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) { - 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) -{ - if (trafos.size() == m_instances.size()) { - for (size_t i = 0; i < trafos.size(); ++i) { - m_instances[i].second = trafos[i]; - } - } - else - assert(false); + m_perimeter.init_from(polygons, 0.025f); // add a small positive z to avoid z-fighting } 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 NO_FILL_EVALUATING_COLOR = { 1.0f, 1.0f, 0.0f, 1.0f }; - - if (m_contours.empty() || m_instances.empty()) - return; + 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 }; GLShaderProgram* shader = wxGetApp().get_shader("flat"); if (shader == nullptr) @@ -966,34 +933,10 @@ 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 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_fill.is_initialized() ? FILL_COLOR : m_evaluating ? NO_FILL_EVALUATING_COLOR : NO_FILL_COLOR); - m_contours[id].render(); - } - glsafe(::glDisable(GL_BLEND)); glsafe(::glEnable(GL_CULL_FACE)); glsafe(::glDisable(GL_DEPTH_TEST)); @@ -1510,85 +1453,11 @@ void GLCanvas3D::reset_volumes() ModelInstanceEPrintVolumeState GLCanvas3D::check_volumes_outside_state() const { ModelInstanceEPrintVolumeState state = ModelInstanceEPrintVolumeState::ModelInstancePVS_Inside; - if (m_initialized && !m_volumes.empty()) - check_volumes_outside_state(m_bed.build_volume(), &state); + if (m_initialized) + m_volumes.check_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) @@ -2593,7 +2462,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 = m_volumes.check_outside_state(m_bed.build_volume(), &state); const bool partlyOut = (state == ModelInstanceEPrintVolumeState::ModelInstancePVS_Partly_Outside); const bool fullyOut = (state == ModelInstanceEPrintVolumeState::ModelInstancePVS_Fully_Outside); @@ -3560,12 +3429,17 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // Not only detection of some modifiers !!! if (evt.Dragging()) { GLGizmosManager::EType c = m_gizmos.get_current_type(); - 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); + if (current_printer_technology() == ptFFF && + fff_print()->config().complete_objects){ + if (c == GLGizmosManager::EType::Move || + c == GLGizmosManager::EType::Scale || + c == GLGizmosManager::EType::Rotate ) + update_sequential_clearance(); + } else { + if (c == GLGizmosManager::EType::Move || + c == GLGizmosManager::EType::Scale || + c == GLGizmosManager::EType::Rotate) + show_sinking_contours(); } } else if (evt.LeftUp() && @@ -3765,7 +3639,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(false); + update_sequential_clearance(); wxGetApp().obj_manipul()->set_dirty(); m_dirty = true; } @@ -4071,10 +3945,7 @@ 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))); - if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects) { - update_sequential_clearance(true); - m_sequential_print_clearance.m_evaluating = true; - } + reset_sequential_print_clearance(); m_dirty = true; } @@ -4159,11 +4030,6 @@ 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) { - update_sequential_clearance(true); - m_sequential_print_clearance.m_evaluating = true; - } - m_dirty = true; } @@ -4236,11 +4102,6 @@ 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) { - update_sequential_clearance(true); - m_sequential_print_clearance.m_evaluating = true; - } - m_dirty = true; } @@ -4508,33 +4369,16 @@ void GLCanvas3D::mouse_up_cleanup() m_canvas->ReleaseMouse(); } -void GLCanvas3D::update_sequential_clearance(bool force_contours_generation) +void GLCanvas3D::update_sequential_clearance() { if (current_printer_technology() != ptFFF || !fff_print()->config().complete_objects) return; - if (m_layers_editing.is_enabled()) + 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(); - }; - - 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 + // first define temporary cache unsigned int instances_count = 0; std::vector>> instance_transforms; for (size_t obj = 0; obj < m_model->objects.size(); ++obj) { @@ -4549,95 +4393,67 @@ void GLCanvas3D::update_sequential_clearance(bool force_contours_generation) 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_wipe_tower) + if (v->is_modifier || 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]; + auto& transform = instance_transforms[v->object_idx()][v->instance_idx()]; if (!transform.has_value()) - transform = instance_transform_from_volumes(object_idx, instance_idx); + 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()); - }; - // 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(); + if (m_sequential_print_clearance_first_displacement) { + m_sequential_print_clearance.m_hull_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_hulls_2d_cache.reserve(m_model->objects.size()); + m_sequential_print_clearance.m_hull_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]; - Geometry::Transformation trafo = instance_transform_from_volumes((int)i, 0); - trafo.set_offset({ 0.0, 0.0, trafo.get_offset().z() }); - 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()) { + 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() }); + 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. - const Polygons offset_res = offset(hull_2d, shrink_factor, jtRound, mitter_limit); - if (!offset_res.empty()) - hull_2d = offset_res.front(); - } + shrink_factor, + jtRound, mitter_limit).front(); - new_hull_2d.reserve(hull_2d.points.size()); + Pointf3s& cache_hull_2d = m_sequential_print_clearance.m_hull_2d_cache.emplace_back(Pointf3s()); + cache_hull_2d.reserve(hull_2d.points.size()); const Transform3d inv_trafo = trafo.get_matrix().inverse(); for (const Point& p : hull_2d.points) { - new_hull_2d.emplace_back(Vec3d(unscale(p.x()), unscale(p.y()), 0.0)); + cache_hull_2d.emplace_back(inv_trafo * Vec3d(unscale(p.x()), unscale(p.y()), 0.0)); } } - - ContoursList contours; - contours.contours.reserve(instance_transforms.size()); - contours.trafos = std::vector>(); - (*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; - 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).emplace_back(i, instance_trafo(hull_trafo, *instance)); - } - } - - set_sequential_print_clearance_contours(contours, false); m_sequential_print_clearance_first_displacement = false; } - else { - 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)); - } + + // 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())); } - m_sequential_print_clearance.update_instances_trafos(trafos); + polygons.emplace_back(Geometry::convex_hull(std::move(inst_pts))); } } + + // 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 @@ -6117,7 +5933,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type) } } if (m_requires_check_outside_state) { - check_volumes_outside_state(build_volume, nullptr); + m_volumes.check_outside_state(build_volume, nullptr); m_requires_check_outside_state = false; } } @@ -6212,20 +6028,15 @@ void GLCanvas3D::_render_selection() void GLCanvas3D::_render_sequential_clearance() { - if (current_printer_technology() != ptFFF || !fff_print()->config().complete_objects) - return; - - if (m_layers_editing.is_enabled()) + if (m_layers_editing.is_enabled() || m_gizmos.is_dragging()) return; switch (m_gizmos.get_current_type()) { case GLGizmosManager::EType::Flatten: case GLGizmosManager::EType::Cut: - case GLGizmosManager::EType::MmuSegmentation: - case GLGizmosManager::EType::Measure: - case GLGizmosManager::EType::Emboss: - case GLGizmosManager::EType::Simplify: + case GLGizmosManager::EType::Hollow: + case GLGizmosManager::EType::SlaSupports: 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 023540068d..38dcdb8b8c 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -618,35 +618,23 @@ 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; - // list of unique contours - std::vector m_contours; - // list of transforms used to render the contours - std::vector> m_instances; - bool m_evaluating{ false }; + GLModel m_perimeter; + bool m_render_fill{ true }; + bool m_visible{ false }; - std::vector> m_hulls_2d_cache; + std::vector m_hull_2d_cache; public: - void set_contours(const ContoursList& contours, bool generate_fill); - void update_instances_trafos(const std::vector& trafos); + void set_polygons(const Polygons& polygons); + void set_render_fill(bool render_fill) { m_render_fill = render_fill; } + void set_visible(bool visible) { m_visible = visible; } void render(); - bool empty() const { return m_contours.empty(); } friend class GLCanvas3D; }; @@ -737,9 +725,6 @@ 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(); } @@ -973,23 +958,24 @@ public: } void reset_sequential_print_clearance() { - m_sequential_print_clearance.m_evaluating = false; - m_sequential_print_clearance.set_contours(ContoursList(), false); + m_sequential_print_clearance.set_visible(false); + m_sequential_print_clearance.set_render_fill(false); + m_sequential_print_clearance.set_polygons(Polygons()); } - 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_visible(bool visible) { + m_sequential_print_clearance.set_visible(visible); } - bool is_sequential_print_clearance_empty() const { - return m_sequential_print_clearance.empty(); + void set_sequential_print_clearance_render_fill(bool render_fill) { + m_sequential_print_clearance.set_render_fill(render_fill); } - bool is_sequential_print_clearance_evaluating() const { - return m_sequential_print_clearance.m_evaluating; + void set_sequential_print_clearance_polygons(const Polygons& polygons) { + m_sequential_print_clearance.set_polygons(polygons); } - void update_sequential_clearance(bool force_contours_generation); + void update_sequential_clearance(); const Print* fff_print() const; const SLAPrint* sla_print() const; diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index 3a7c002850..61736f9ac9 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -596,38 +596,6 @@ 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 3d3a8aac3c..fbf6ed5334 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -227,7 +227,6 @@ 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/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index ae40e0f766..d86c8275ef 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 944fd00159..061f39622c 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3294,43 +3294,29 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool // or hide the old one. 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(); - } + view3D->get_canvas3d()->reset_sequential_print_clearance(); + view3D->get_canvas3d()->set_as_dirty(); + view3D->get_canvas3d()->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) { - 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); - } + 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); + view3D->get_canvas3d()->set_sequential_print_clearance_polygons(polygons); } } } 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()) @@ -4432,6 +4418,7 @@ 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 4e4e5b0871662ef5fda2e7a806a6420f497001b9 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 12 May 2023 14:20:01 +0200 Subject: [PATCH 58/90] Fix broken fill bed feature --- src/slic3r/GUI/Jobs/FillBedJob.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/Jobs/FillBedJob.cpp b/src/slic3r/GUI/Jobs/FillBedJob.cpp index b8a579ed5d..11d9450c2e 100644 --- a/src/slic3r/GUI/Jobs/FillBedJob.cpp +++ b/src/slic3r/GUI/Jobs/FillBedJob.cpp @@ -25,7 +25,8 @@ void FillBedJob::prepare() return; ModelObject *model_object = m_plater->model().objects[m_object_idx]; - if (model_object->instances.empty()) return; + if (model_object->instances.empty()) + return; m_selected.reserve(model_object->instances.size()); for (ModelInstance *inst : model_object->instances) @@ -37,7 +38,8 @@ void FillBedJob::prepare() m_selected.emplace_back(ap); } - if (m_selected.empty()) return; + if (m_selected.empty()) + return; m_bedpts = get_bed_shape(*m_plater->config()); @@ -85,9 +87,11 @@ void FillBedJob::prepare() ArrangePolygon ap = template_ap; ap.poly = m_selected.front().poly; ap.bed_idx = arrangement::UNARRANGED; - ap.setter = [this, mi](const ArrangePolygon &p) { + auto m = mi->get_transformation(); + ap.setter = [this, mi, m](const ArrangePolygon &p) { ModelObject *mo = m_plater->model().objects[m_object_idx]; ModelInstance *inst = mo->add_instance(*mi); + inst->set_transformation(m); inst->apply_arrange_result(p.translation.cast(), p.rotation); }; m_selected.emplace_back(ap); @@ -166,10 +170,12 @@ void FillBedJob::finalize(bool canceled, std::exception_ptr &eptr) if (canceled || eptr) return; - if (m_object_idx == -1) return; + if (m_object_idx == -1) + return; ModelObject *model_object = m_plater->model().objects[m_object_idx]; - if (model_object->instances.empty()) return; + if (model_object->instances.empty()) + return; size_t inst_cnt = model_object->instances.size(); @@ -188,7 +194,8 @@ void FillBedJob::finalize(bool canceled, std::exception_ptr &eptr) m_plater->update(); // FIXME: somebody explain why this is needed for increase_object_instances - if (inst_cnt == 1) added_cnt++; + if (inst_cnt == 1) + added_cnt++; m_plater->sidebar() .obj_list()->increase_object_instances(m_object_idx, size_t(added_cnt)); From 96551233e035a53d7a590e7c462d0d5de4f2658c Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 12 May 2023 15:16:06 +0200 Subject: [PATCH 59/90] Auto support painting: Don't paint on upwards pointing surfaces. --- src/libslic3r/TriangleSelectorWrapper.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/TriangleSelectorWrapper.cpp b/src/libslic3r/TriangleSelectorWrapper.cpp index daf42300c5..67b79e3441 100644 --- a/src/libslic3r/TriangleSelectorWrapper.cpp +++ b/src/libslic3r/TriangleSelectorWrapper.cpp @@ -11,6 +11,9 @@ TriangleSelectorWrapper::TriangleSelectorWrapper(const TriangleMesh &mesh, const void TriangleSelectorWrapper::enforce_spot(const Vec3f &point, const Vec3f &origin, float radius) { std::vector hits; Vec3f dir = (point - origin).normalized(); + static constexpr const auto eps_angle = 89.99f; + Transform3d trafo_no_translate = mesh_transform; + trafo_no_translate.translation() = Vec3d::Zero(); if (AABBTreeIndirect::intersect_ray_all_hits(mesh.its.vertices, mesh.its.indices, triangles_tree, Vec3d(origin.cast()), Vec3d(dir.cast()), @@ -22,8 +25,8 @@ void TriangleSelectorWrapper::enforce_spot(const Vec3f &point, const Vec3f &orig if ((point - pos).norm() < radius && face_normal.dot(dir) < 0) { std::unique_ptr cursor = std::make_unique( pos, origin, radius, this->mesh_transform, TriangleSelector::ClippingPlane { }); - selector.select_patch(hit.id, std::move(cursor), EnforcerBlockerType::ENFORCER, Transform3d::Identity(), - true, 0.0f); + selector.select_patch(hit.id, std::move(cursor), EnforcerBlockerType::ENFORCER, trafo_no_translate, + true, eps_angle); break; } } @@ -36,8 +39,8 @@ void TriangleSelectorWrapper::enforce_spot(const Vec3f &point, const Vec3f &orig std::unique_ptr cursor = std::make_unique( point, origin, radius, this->mesh_transform, TriangleSelector::ClippingPlane { }); selector.select_patch(hit_idx_out, std::move(cursor), EnforcerBlockerType::ENFORCER, - Transform3d::Identity(), - true, 0.0f); + trafo_no_translate, + true, eps_angle); } } } From 1c4d43b3a498530908eaf4877289548a13984511 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 27 Feb 2023 12:34:08 +0100 Subject: [PATCH 60/90] Cut WIP: Use contours to perform a cut. --- src/libslic3r/Model.cpp | 1 - src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 159 ++++++++++++++++++++--- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 12 +- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 10 +- src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 3 +- src/slic3r/GUI/MeshUtils.cpp | 8 +- src/slic3r/GUI/MeshUtils.hpp | 3 +- 7 files changed, 171 insertions(+), 25 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index f2a1db3c95..74dabb8e1c 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1565,7 +1565,6 @@ void ModelObject::process_volume_cut(ModelVolume* volume, const Transform3d& ins if (attributes.has(ModelObjectCutAttribute::KeepLower)) lower_mesh = TriangleMesh(lower_its); } - void ModelObject::process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, Vec3d& local_displace) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index f5ee14812e..9d93446176 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -289,11 +289,28 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), mouse_event.CmdDown()); } else if (m_hover_id == CutPlane) { - if (mouse_event.LeftDown()) - m_was_cut_plane_dragged = false; - else if (mouse_event.LeftUp() && !m_was_cut_plane_dragged) + if (mouse_event.LeftDown()) { + m_was_cut_plane_dragged = m_was_contour_selected = false; + + // disable / enable current contour + Vec3d pos; + Vec3d pos_world; + m_was_contour_selected = unproject_on_cut_plane(mouse_pos.cast(), pos, pos_world, false); + if (m_was_contour_selected) { + // Following would inform the clipper about the mouse click, so it can + // toggle the respective contour as disabled. + m_c->object_clipper()->pass_mouse_click(pos_world); + process_contours(); + return true; + } + + } + else if (mouse_event.LeftUp() && !m_was_cut_plane_dragged && !m_was_contour_selected) flip_cut_plane(); } + + if (!m_cut_by_contour_glmodels.empty()) + m_parent.toggle_model_objects_visibility(false); return true; } @@ -429,6 +446,9 @@ void GLGizmoCut3D::update_clipper() on_register_raycasters_for_picking(); else update_raycasters_for_picking_transform(); + + if (!m_c->object_clipper()->has_disable_contour()) + reset_cut_by_contours(); } void GLGizmoCut3D::set_center(const Vec3d& center, bool update_tbb /*=false*/) @@ -559,6 +579,8 @@ void GLGizmoCut3D::render_move_center_input(int axis) Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Move cut plane"), UndoRedo::SnapshotType::GizmoAction); set_center(move, true); m_ar_plane_center = m_plane_center; + + reset_cut_by_contours(); } } @@ -1160,10 +1182,14 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data) { if (m_hover_id < 0) return; - if (m_hover_id == Z || m_hover_id == CutPlane) + if (m_hover_id == Z || m_hover_id == CutPlane) { dragging_grabber_z(data); - else if (m_hover_id == X || m_hover_id == Y) + reset_cut_by_contours(); + } + else if (m_hover_id == X || m_hover_id == Y) { dragging_grabber_xy(data); + reset_cut_by_contours(); + } else if (m_hover_id >= m_connectors_group_id && m_connector_mode == CutConnectorMode::Manual) dragging_connector(data); } @@ -1358,6 +1384,31 @@ void GLGizmoCut3D::render_clipper_cut() ::glEnable(GL_DEPTH_TEST); } +void GLGizmoCut3D::render_colored_parts() +{ + if (m_cut_by_contour_glmodels.empty()) + return; + assert(m_cut_by_contour_objects[0]->volumes.size() == m_cut_by_contour_glmodels.size()); + + if (GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light")) { + const Camera& camera = wxGetApp().plater()->get_camera(); + + shader->start_using(); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + + const Vec3d inst_offset = m_cut_by_contour_objects[0]->instances[0]->get_offset(); + const Transform3d view_inst_matrix= camera.get_view_matrix() * translation_transform(inst_offset); + + for (size_t id = 0; id < m_cut_by_contour_glmodels.size(); id++) { + const Vec3d volume_offset = m_cut_by_contour_objects[0]->volumes[id]->get_offset(); + shader->set_uniform("view_model_matrix", view_inst_matrix * translation_transform(volume_offset)); + m_cut_by_contour_glmodels[id].render(); + } + + shader->stop_using(); + } +} + void GLGizmoCut3D::on_render() { if (m_state == On) { @@ -1374,6 +1425,9 @@ void GLGizmoCut3D::on_render() render_connectors(); + if (!m_connectors_editing) + render_colored_parts(); + render_clipper_cut(); if (!m_hide_cut_plane && !m_connectors_editing) { @@ -1587,6 +1641,8 @@ void GLGizmoCut3D::reset_cut_plane() set_center(m_bb_center); m_start_dragging_m = m_rotation_m = Transform3d::Identity(); m_ar_plane_center = m_plane_center; + + reset_cut_by_contours(); } void GLGizmoCut3D::invalidate_cut_plane() @@ -1623,6 +1679,62 @@ void GLGizmoCut3D::flip_cut_plane() update_clipper(); } +void GLGizmoCut3D::reset_cut_by_contours() +{ + if (!m_cut_by_contour_objects.empty()) { + m_cut_by_contour_objects.clear(); + m_cut_by_contour_glmodels.clear(); + m_disabled_contours.clear(); + + m_parent.toggle_model_objects_visibility(true); + } +} + +void GLGizmoCut3D::process_contours() +{ + reset_cut_by_contours(); + + if (!m_c->object_clipper()->has_disable_contour()) + return; + + const Selection& selection = m_parent.get_selection(); + const ModelObjectPtrs& model_objects = selection.get_model()->objects; + + wxBusyCursor wait; + + const int instance_idx = selection.get_instance_idx(); + const int object_idx = selection.get_object_idx(); + + m_cut_by_contour_objects = model_objects[object_idx]->cut(instance_idx, get_cut_matrix(selection), + ModelObjectCutAttribute::KeepUpper | + ModelObjectCutAttribute::KeepLower | + ModelObjectCutAttribute::KeepAsParts | + ModelObjectCutAttribute::InvalidateCutInfo); + + assert(m_cut_by_contour_objects.size() == 1); + + const ModelVolumePtrs& volumes = m_cut_by_contour_objects[0]->volumes; + + // split to parts + for (int id = int(volumes.size())-1; id >= 0; id--) + if (volumes[id]->is_splittable()) + volumes[id]->split(1); + + m_cut_by_contour_glmodels.reserve(volumes.size()); + for (const ModelVolume* volume : volumes) { + assert(volume != nullptr); + + GLModel glmodel; + // any condition to test + if (volume->name.find("_A") != std::string::npos) + glmodel.set_color({ 0.5f, 0.9f, 0.9f, 0.5f }); // glmodel.set_color(UPPER_PART_COLOR); + else + glmodel.set_color({ 0.9f, 0.5f, 0.9f, 0.5f }); // glmodel.set_color(LOWER_PART_COLOR); + + m_cut_by_contour_glmodels.push_back(glmodel); + } +} + void GLGizmoCut3D::render_flip_plane_button(bool disable_pred /*=false*/) { ImGui::SameLine(); @@ -1949,7 +2061,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) bool GLGizmoCut3D::is_outside_of_cut_contour(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos) { // check if connector pos is out of clipping plane - if (m_c->object_clipper() && !m_c->object_clipper()->is_projection_inside_cut(cur_pos)) { + if (m_c->object_clipper() && !m_c->object_clipper()->is_projection_inside_cut(cur_pos, true)) { m_info_stats.outside_cut_contour++; return true; } @@ -1975,7 +2087,7 @@ bool GLGizmoCut3D::is_outside_of_cut_contour(size_t idx, const CutConnectors& co its_transform(mesh, translation_transform(cur_pos) * m_rotation_m); for (auto vertex : vertices) { - if (m_c->object_clipper() && !m_c->object_clipper()->is_projection_inside_cut(vertex.cast())) { + if (m_c->object_clipper() && !m_c->object_clipper()->is_projection_inside_cut(vertex.cast(), true)) { m_info_stats.outside_cut_contour++; return true; } @@ -2132,6 +2244,24 @@ void GLGizmoCut3D::apply_connectors_in_model(ModelObject* mo, bool &create_dowel } } +Transform3d GLGizmoCut3D::get_cut_matrix(const Selection& selection) +{ + const int instance_idx = selection.get_instance_idx(); + const int object_idx = selection.get_object_idx(); + ModelObject* mo = selection.get_model()->objects[object_idx]; + if (!mo) + return Transform3d::Identity(); + + // m_cut_z is the distance from the bed. Subtract possible SLA elevation. + const double sla_shift_z = selection.get_first_volume()->get_sla_shift_z(); + + const Vec3d instance_offset = mo->instances[instance_idx]->get_offset(); + Vec3d cut_center_offset = m_plane_center - instance_offset; + cut_center_offset[Z] -= sla_shift_z; + + return translation_transform(cut_center_offset) * m_rotation_m; +} + void GLGizmoCut3D::perform_cut(const Selection& selection) { if (!can_perform_cut()) @@ -2149,13 +2279,6 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) // deactivate CutGizmo and than perform a cut m_parent.reset_all_gizmos(); - // m_cut_z is the distance from the bed. Subtract possible SLA elevation. - const double sla_shift_z = selection.get_first_volume()->get_sla_shift_z(); - - const Vec3d instance_offset = mo->instances[instance_idx]->get_offset(); - Vec3d cut_center_offset = m_plane_center - instance_offset; - cut_center_offset[Z] -= sla_shift_z; - // perform cut { Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Cut by Plane")); @@ -2165,7 +2288,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) // update connectors pos as offset of its center before cut performing apply_connectors_in_model(mo, create_dowels_as_separate_object); - plater->cut(object_idx, instance_idx, translation_transform(cut_center_offset) * m_rotation_m, + plater->cut(object_idx, instance_idx, get_cut_matrix(selection), only_if(has_connectors ? true : m_keep_upper, ModelObjectCutAttribute::KeepUpper) | only_if(has_connectors ? true : m_keep_lower, ModelObjectCutAttribute::KeepLower) | only_if(has_connectors ? false: m_keep_as_parts, ModelObjectCutAttribute::KeepAsParts) | @@ -2182,7 +2305,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) // Unprojects the mouse position on the mesh and saves hit point and normal of the facet into pos_and_normal // Return false if no intersection was found, true otherwise. -bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, Vec3d& pos, Vec3d& pos_world) +bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, Vec3d& pos, Vec3d& pos_world, bool respect_disabled_contour/* = true*/) { const float sla_shift = m_c->selection_info()->get_sla_shift(); @@ -2204,7 +2327,7 @@ bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, Vec3d& po } else return false; - if (! m_c->object_clipper()->is_projection_inside_cut(hit)) + if (! m_c->object_clipper()->is_projection_inside_cut(hit, respect_disabled_contour)) return false; // recalculate hit to object's local position @@ -2284,6 +2407,8 @@ bool GLGizmoCut3D::process_cut_line(SLAGizmoEventType action, const Vec2d& mouse } if (cut_line_processing()) { + reset_cut_by_contours(); + m_line_end = pt; if (action == SLAGizmoEventType::LeftDown || action == SLAGizmoEventType::LeftUp) { Vec3d line_dir = m_line_end - m_line_beg; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 49d1848e43..ca2084aa1c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -135,6 +135,12 @@ class GLGizmoCut3D : public GLGizmoBase bool m_has_invalid_connector{ false }; bool m_was_cut_plane_dragged { false }; + bool m_was_contour_selected { false }; + + std::vector m_cut_by_contour_glmodels; + ModelObjectPtrs m_cut_by_contour_objects; + // attributes for disabled contours + std::vector> m_disabled_contours; bool m_show_shortcuts{ false }; std::vector> m_shortcuts; @@ -176,7 +182,7 @@ public: GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); std::string get_tooltip() const override; - bool unproject_on_cut_plane(const Vec2d& mouse_pos, Vec3d& pos, Vec3d& pos_world); + bool unproject_on_cut_plane(const Vec2d& mouse_pos, Vec3d& pos, Vec3d& pos_world, bool respect_disabled_contour = true); bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); bool is_in_editing_mode() const override { return m_connectors_editing; } @@ -229,6 +235,8 @@ protected: void reset_cut_plane(); void set_connectors_editing(bool connectors_editing); void flip_cut_plane(); + void process_contours(); + void reset_cut_by_contours(); void render_flip_plane_button(bool disable_pred = false); void add_vertical_scaled_interval(float interval); void add_horizontal_scaled_interval(float interval); @@ -257,6 +265,7 @@ protected: std::string get_action_snapshot_name() const override { return _u8L("Cut gizmo editing"); } void data_changed(bool is_serializing) override; + Transform3d get_cut_matrix(const Selection& selection); private: void set_center(const Vec3d& center, bool update_tbb = false); @@ -290,6 +299,7 @@ private: void init_picking_models(); void init_rendering_items(); void render_clipper_cut(); + void render_colored_parts(); void clear_selection(); void reset_connectors(); void init_connector_shapes(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 4edb01c2bd..b642da5cac 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -388,9 +388,10 @@ void ObjectClipper::render_cut() const } } -bool ObjectClipper::is_projection_inside_cut(const Vec3d& point) const +bool ObjectClipper::is_projection_inside_cut(const Vec3d& point, bool respect_disabled_contour) const { - return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [point](const auto& cl) { return cl.first->is_projection_inside_cut(point); }); + return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), + [point, respect_disabled_contour](const auto& cl) { return cl.first->is_projection_inside_cut(point, respect_disabled_contour); }); } bool ObjectClipper::has_valid_contour() const @@ -398,6 +399,11 @@ bool ObjectClipper::has_valid_contour() const return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [](const auto& cl) { return cl.first->has_valid_contour(); }); } +bool ObjectClipper::has_disable_contour() const +{ + return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [](const auto& cl) { return cl.first->has_disable_contour(); }); +} + void ObjectClipper::set_position_by_ratio(double pos, bool keep_normal) { const ModelObject* mo = get_pool()->selection_info()->model_object(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index e0d2cdb68d..15bf658149 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -250,8 +250,9 @@ public: void pass_mouse_click(const Vec3d& pt); std::vector get_disabled_contours() const; - bool is_projection_inside_cut(const Vec3d& point_in) const; + bool is_projection_inside_cut(const Vec3d& point_in, bool respect_disabled_contour) const; bool has_valid_contour() const; + bool has_disable_contour() const; protected: diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 59be6cabbe..9f6cd1960a 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -146,7 +146,7 @@ void MeshClipper::render_contour(const ColorRGBA& color) curr_shader->start_using(); } -bool MeshClipper::is_projection_inside_cut(const Vec3d& point_in) const +bool MeshClipper::is_projection_inside_cut(const Vec3d& point_in, bool respect_disabled_contour) const { if (!m_result || m_result->cut_islands.empty()) return false; @@ -155,7 +155,7 @@ bool MeshClipper::is_projection_inside_cut(const Vec3d& point_in) const for (const CutIsland& isl : m_result->cut_islands) { if (isl.expoly_bb.contains(pt_2d) && isl.expoly.contains(pt_2d)) - return !isl.disabled; + return respect_disabled_contour ? !isl.disabled : true; } return false; } @@ -165,6 +165,10 @@ bool MeshClipper::has_valid_contour() const return m_result && std::any_of(m_result->cut_islands.begin(), m_result->cut_islands.end(), [](const CutIsland& isl) { return !isl.expoly.empty(); }); } +bool MeshClipper::has_disable_contour() const +{ + return m_result && std::any_of(m_result->cut_islands.begin(), m_result->cut_islands.end(), [](const CutIsland& isl) { return isl.disabled; }); +} void MeshClipper::pass_mouse_click(const Vec3d& point_in) { diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index b10fa5ff8f..0a854e5f3c 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -120,8 +120,9 @@ public: void pass_mouse_click(const Vec3d& pt); - bool is_projection_inside_cut(const Vec3d& point) const; + bool is_projection_inside_cut(const Vec3d& point, bool respect_disabled_contour) const; bool has_valid_contour() const; + bool has_disable_contour() const; private: void recalculate_triangles(); From e5b66f125f4041b2da7f3835bf01748bb49b2fda Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 23 Feb 2023 01:45:14 +0100 Subject: [PATCH 61/90] Cut: experiment with selecting parts: Instead of clicking on contours, the user would click on the object itself. Right mouse button is used currently. Many loose ends ! Really just an experiment. --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 171 +++++++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 24 +++- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 36 ++--- src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 6 +- src/slic3r/GUI/MeshUtils.cpp | 28 +--- src/slic3r/GUI/MeshUtils.hpp | 7 +- 6 files changed, 148 insertions(+), 124 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 9d93446176..edf91aba86 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -299,8 +299,8 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) if (m_was_contour_selected) { // Following would inform the clipper about the mouse click, so it can // toggle the respective contour as disabled. - m_c->object_clipper()->pass_mouse_click(pos_world); - process_contours(); + //m_c->object_clipper()->pass_mouse_click(pos_world); + //process_contours(); return true; } @@ -309,7 +309,7 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) flip_cut_plane(); } - if (!m_cut_by_contour_glmodels.empty()) + if (m_part_selection.valid) m_parent.toggle_model_objects_visibility(false); return true; } @@ -351,6 +351,14 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) return true; } else if (mouse_event.RightDown()) { + if (! m_connectors_editing) { + // Check the internal part raycasters. + if (! m_part_selection.valid) + process_contours(); + m_part_selection.toggle_selection(mouse_pos); + return true; + } + if (m_parent.get_selection().get_object_idx() != -1 && gizmo_event(SLAGizmoEventType::RightDown, mouse_pos, false, false, false)) { // we need to set the following right up as processed to avoid showing @@ -446,9 +454,6 @@ void GLGizmoCut3D::update_clipper() on_register_raycasters_for_picking(); else update_raycasters_for_picking_transform(); - - if (!m_c->object_clipper()->has_disable_contour()) - reset_cut_by_contours(); } void GLGizmoCut3D::set_center(const Vec3d& center, bool update_tbb /*=false*/) @@ -1384,11 +1389,10 @@ void GLGizmoCut3D::render_clipper_cut() ::glEnable(GL_DEPTH_TEST); } -void GLGizmoCut3D::render_colored_parts() +void GLGizmoCut3D::PartSelection::render(const Vec3d* normal) { - if (m_cut_by_contour_glmodels.empty()) + if (! valid) return; - assert(m_cut_by_contour_objects[0]->volumes.size() == m_cut_by_contour_glmodels.size()); if (GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light")) { const Camera& camera = wxGetApp().plater()->get_camera(); @@ -1396,19 +1400,44 @@ void GLGizmoCut3D::render_colored_parts() shader->start_using(); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - const Vec3d inst_offset = m_cut_by_contour_objects[0]->instances[0]->get_offset(); + // FIXME: Cache the transforms. + + const Vec3d inst_offset = model_object->instances[0]->get_offset(); const Transform3d view_inst_matrix= camera.get_view_matrix() * translation_transform(inst_offset); - for (size_t id = 0; id < m_cut_by_contour_glmodels.size(); id++) { - const Vec3d volume_offset = m_cut_by_contour_objects[0]->volumes[id]->get_offset(); + for (size_t id=0; idvolumes[id]->get_offset(); shader->set_uniform("view_model_matrix", view_inst_matrix * translation_transform(volume_offset)); - m_cut_by_contour_glmodels[id].render(); + //parts[id].glmodel.set_color(parts[id].selected ? ColorRGBA(1.f, 0.f, 0.f, 1.f) : ColorRGBA(0.f, 1.f, 0.f, 1.f)); + parts[id].glmodel.set_color(parts[id].selected ? UPPER_PART_COLOR : LOWER_PART_COLOR); + parts[id].glmodel.render(); } shader->stop_using(); } } + +void GLGizmoCut3D::PartSelection::toggle_selection(const Vec2d& mouse_pos) +{ + // FIXME: Cache the transforms. + const Camera& camera = wxGetApp().plater()->get_camera(); + + Vec3f pos; + Vec3f normal; + + for (size_t id=0; idvolumes[id]->get_offset(); + Transform3d tr = model_object->instances.front()->get_matrix() * model_object->volumes[id]->get_matrix(); + if (parts[id].raycaster.unproject_on_mesh(mouse_pos, tr, camera, pos, normal)) { + parts[id].selected = ! parts[id].selected; + return; + } + } +} + void GLGizmoCut3D::on_render() { if (m_state == On) { @@ -1426,7 +1455,9 @@ void GLGizmoCut3D::on_render() render_connectors(); if (!m_connectors_editing) - render_colored_parts(); + m_part_selection.render(); + else + m_part_selection.render(&m_cut_normal); render_clipper_cut(); @@ -1679,60 +1710,68 @@ void GLGizmoCut3D::flip_cut_plane() update_clipper(); } -void GLGizmoCut3D::reset_cut_by_contours() + +GLGizmoCut3D::PartSelection::PartSelection(ModelObject* mo, const Vec3d& center, const Vec3d& normal) { - if (!m_cut_by_contour_objects.empty()) { - m_cut_by_contour_objects.clear(); - m_cut_by_contour_glmodels.clear(); - m_disabled_contours.clear(); + model_object = mo; // FIXME: Ownership. - m_parent.toggle_model_objects_visibility(true); - } -} - -void GLGizmoCut3D::process_contours() -{ - reset_cut_by_contours(); - - if (!m_c->object_clipper()->has_disable_contour()) - return; - - const Selection& selection = m_parent.get_selection(); - const ModelObjectPtrs& model_objects = selection.get_model()->objects; - - wxBusyCursor wait; - - const int instance_idx = selection.get_instance_idx(); - const int object_idx = selection.get_object_idx(); - - m_cut_by_contour_objects = model_objects[object_idx]->cut(instance_idx, get_cut_matrix(selection), - ModelObjectCutAttribute::KeepUpper | - ModelObjectCutAttribute::KeepLower | - ModelObjectCutAttribute::KeepAsParts | - ModelObjectCutAttribute::InvalidateCutInfo); - - assert(m_cut_by_contour_objects.size() == 1); - - const ModelVolumePtrs& volumes = m_cut_by_contour_objects[0]->volumes; + const ModelVolumePtrs& volumes = mo->volumes; // split to parts for (int id = int(volumes.size())-1; id >= 0; id--) if (volumes[id]->is_splittable()) volumes[id]->split(1); - m_cut_by_contour_glmodels.reserve(volumes.size()); + parts.clear(); for (const ModelVolume* volume : volumes) { assert(volume != nullptr); + parts.emplace_back(Part{GLModel(), MeshRaycaster(volume->mesh()), true}); + parts.back().glmodel.set_color({ 0.f, 0.f, 1.f, 1.f }); + parts.back().glmodel.init_from(volume->mesh()); - GLModel glmodel; - // any condition to test - if (volume->name.find("_A") != std::string::npos) - glmodel.set_color({ 0.5f, 0.9f, 0.9f, 0.5f }); // glmodel.set_color(UPPER_PART_COLOR); - else - glmodel.set_color({ 0.9f, 0.5f, 0.9f, 0.5f }); // glmodel.set_color(LOWER_PART_COLOR); - - m_cut_by_contour_glmodels.push_back(glmodel); + // Now check whether this part is below or above the plane. + Transform3d tr = (model_object->instances.front()->get_matrix() * volume->get_matrix()).inverse(); + Vec3f pos = (tr * center).cast(); + Vec3f norm = (tr.linear().inverse().transpose() * normal).cast(); + for (const Vec3f& v : volume->mesh().its.vertices) { + double p = (v - pos).dot(norm); + if (std::abs(p) > EPSILON) { + parts.back().selected = p > 0.; + break; + } + } } + + valid = true; +} + + +void GLGizmoCut3D::reset_cut_by_contours() +{ + m_part_selection = PartSelection(); + m_parent.toggle_model_objects_visibility(true); +} + +void GLGizmoCut3D::process_contours() +{ + reset_cut_by_contours(); + + const Selection& selection = m_parent.get_selection(); + const ModelObjectPtrs& model_objects = selection.get_model()->objects; + + wxBusyCursor wait; + const int instance_idx = selection.get_instance_idx(); + const int object_idx = selection.get_object_idx(); + + ModelObjectPtrs moptrs = model_objects[object_idx]->cut(instance_idx, get_cut_matrix(selection), + ModelObjectCutAttribute::KeepUpper | + ModelObjectCutAttribute::KeepLower | + ModelObjectCutAttribute::KeepAsParts | + ModelObjectCutAttribute::InvalidateCutInfo); + + assert(moptrs.size() == 1); + m_part_selection = PartSelection(moptrs.front(), m_plane_center, m_cut_normal); + m_parent.toggle_model_objects_visibility(false); } void GLGizmoCut3D::render_flip_plane_button(bool disable_pred /*=false*/) @@ -2061,7 +2100,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) bool GLGizmoCut3D::is_outside_of_cut_contour(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos) { // check if connector pos is out of clipping plane - if (m_c->object_clipper() && !m_c->object_clipper()->is_projection_inside_cut(cur_pos, true)) { + if (m_c->object_clipper() && m_c->object_clipper()->is_projection_inside_cut(cur_pos) == -1) { m_info_stats.outside_cut_contour++; return true; } @@ -2087,7 +2126,7 @@ bool GLGizmoCut3D::is_outside_of_cut_contour(size_t idx, const CutConnectors& co its_transform(mesh, translation_transform(cur_pos) * m_rotation_m); for (auto vertex : vertices) { - if (m_c->object_clipper() && !m_c->object_clipper()->is_projection_inside_cut(vertex.cast(), true)) { + if (m_c->object_clipper() && m_c->object_clipper()->is_projection_inside_cut(vertex.cast()) == -1) { m_info_stats.outside_cut_contour++; return true; } @@ -2327,7 +2366,7 @@ bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, Vec3d& po } else return false; - if (! m_c->object_clipper()->is_projection_inside_cut(hit, respect_disabled_contour)) + if (m_c->object_clipper()->is_projection_inside_cut(hit) == -1) return false; // recalculate hit to object's local position @@ -2555,20 +2594,8 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi if (!m_keep_upper || !m_keep_lower) return false; - if (!m_connectors_editing) { - if (0 && action == SLAGizmoEventType::LeftDown) { - // disable / enable current contour - Vec3d pos; - Vec3d pos_world; - if (unproject_on_cut_plane(mouse_position.cast(), pos, pos_world)) { - // Following would inform the clipper about the mouse click, so it can - // toggle the respective contour as disabled. - m_c->object_clipper()->pass_mouse_click(pos_world); - return true; - } - } + if (!m_connectors_editing) return false; - } CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index ca2084aa1c..9bf805188b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -137,10 +137,25 @@ class GLGizmoCut3D : public GLGizmoBase bool m_was_cut_plane_dragged { false }; bool m_was_contour_selected { false }; - std::vector m_cut_by_contour_glmodels; - ModelObjectPtrs m_cut_by_contour_objects; - // attributes for disabled contours - std::vector> m_disabled_contours; + struct PartSelection { + PartSelection() = default; + PartSelection(ModelObject* mo, const Vec3d& center, const Vec3d& normal); + + void render(const Vec3d* normal = nullptr); + void toggle_selection(const Vec2d& mouse_pos); + + struct Part { + GLModel glmodel; + MeshRaycaster raycaster; + bool selected; + bool upper; + }; + ModelObject* model_object; // FIXME: Ownership ! + std::vector parts; + bool valid = false; + }; + + PartSelection m_part_selection; bool m_show_shortcuts{ false }; std::vector> m_shortcuts; @@ -299,7 +314,6 @@ private: void init_picking_models(); void init_rendering_items(); void render_clipper_cut(); - void render_colored_parts(); void clear_selection(); void reset_connectors(); void init_connector_shapes(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index b642da5cac..5495406102 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -388,10 +388,26 @@ void ObjectClipper::render_cut() const } } -bool ObjectClipper::is_projection_inside_cut(const Vec3d& point, bool respect_disabled_contour) const + +int ObjectClipper::get_number_of_contours() const { - return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), - [point, respect_disabled_contour](const auto& cl) { return cl.first->is_projection_inside_cut(point, respect_disabled_contour); }); + int sum = 0; + for (const auto& [clipper, trafo] : m_clippers) + sum += clipper->get_number_of_contours(); + return sum; +} + +int ObjectClipper::is_projection_inside_cut(const Vec3d& point) const +{ + if (m_clp_ratio == 0.) + return -1; + int idx_offset = 0; + for (const auto& [clipper, trafo] : m_clippers) { + if (int idx = clipper->is_projection_inside_cut(point); idx != -1) + return idx_offset + idx; + idx_offset += clipper->get_number_of_contours(); + } + return -1; } bool ObjectClipper::has_valid_contour() const @@ -399,10 +415,6 @@ bool ObjectClipper::has_valid_contour() const return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [](const auto& cl) { return cl.first->has_valid_contour(); }); } -bool ObjectClipper::has_disable_contour() const -{ - return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [](const auto& cl) { return cl.first->has_disable_contour(); }); -} void ObjectClipper::set_position_by_ratio(double pos, bool keep_normal) { @@ -442,16 +454,6 @@ void ObjectClipper::set_behavior(bool hide_clipped, bool fill_cut, double contou clipper.first->set_behaviour(fill_cut, contour_width); } -void ObjectClipper::pass_mouse_click(const Vec3d& pt) -{ - for (auto& clipper : m_clippers) - clipper.first->pass_mouse_click(pt); -} - -std::vector ObjectClipper::get_disabled_contours() const -{ - return std::vector(); -} void SupportsClipper::on_update() { diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index 15bf658149..4152cce513 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -247,12 +247,10 @@ public: void set_range_and_pos(const Vec3d& cpl_normal, double cpl_offset, double pos); void set_behavior(bool hide_clipped, bool fill_cut, double contour_width); - void pass_mouse_click(const Vec3d& pt); - std::vector get_disabled_contours() const; + int get_number_of_contours() const; - bool is_projection_inside_cut(const Vec3d& point_in, bool respect_disabled_contour) const; + int is_projection_inside_cut(const Vec3d& point_in) const; bool has_valid_contour() const; - bool has_disable_contour() const; protected: diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 9f6cd1960a..cee6841151 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -146,18 +146,19 @@ void MeshClipper::render_contour(const ColorRGBA& color) curr_shader->start_using(); } -bool MeshClipper::is_projection_inside_cut(const Vec3d& point_in, bool respect_disabled_contour) const +int MeshClipper::is_projection_inside_cut(const Vec3d& point_in) const { if (!m_result || m_result->cut_islands.empty()) - return false; + return -1; Vec3d point = m_result->trafo.inverse() * point_in; Point pt_2d = Point::new_scale(Vec2d(point.x(), point.y())); - for (const CutIsland& isl : m_result->cut_islands) { + for (int i=0; icut_islands.size()); ++i) { + const CutIsland& isl = m_result->cut_islands[i]; if (isl.expoly_bb.contains(pt_2d) && isl.expoly.contains(pt_2d)) - return respect_disabled_contour ? !isl.disabled : true; + return i; // TODO: handle intersecting contours } - return false; + return -1; } bool MeshClipper::has_valid_contour() const @@ -165,23 +166,6 @@ bool MeshClipper::has_valid_contour() const return m_result && std::any_of(m_result->cut_islands.begin(), m_result->cut_islands.end(), [](const CutIsland& isl) { return !isl.expoly.empty(); }); } -bool MeshClipper::has_disable_contour() const -{ - return m_result && std::any_of(m_result->cut_islands.begin(), m_result->cut_islands.end(), [](const CutIsland& isl) { return isl.disabled; }); -} - -void MeshClipper::pass_mouse_click(const Vec3d& point_in) -{ - if (! m_result || m_result->cut_islands.empty()) - return; - Vec3d point = m_result->trafo.inverse() * point_in; - Point pt_2d = Point::new_scale(Vec2d(point.x(), point.y())); - - for (CutIsland& isl : m_result->cut_islands) { - if (isl.expoly_bb.contains(pt_2d) && isl.expoly.contains(pt_2d)) - isl.disabled = ! isl.disabled; - } -} void MeshClipper::recalculate_triangles() { diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index 0a854e5f3c..257b500f9f 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -118,11 +118,10 @@ public: void render_cut(const ColorRGBA& color); void render_contour(const ColorRGBA& color); - void pass_mouse_click(const Vec3d& pt); - - bool is_projection_inside_cut(const Vec3d& point, bool respect_disabled_contour) const; + // Returns index of the contour which was clicked, -1 otherwise. + int is_projection_inside_cut(const Vec3d& point) const; bool has_valid_contour() const; - bool has_disable_contour() const; + int get_number_of_contours() const { return m_result ? m_result->cut_islands.size() : 0; } private: void recalculate_triangles(); From 1a7f46001b9d30d13b21821777bc18477eeb5498 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 28 Feb 2023 17:38:30 +0100 Subject: [PATCH 62/90] Cut: Process cut in respect to the selected parts --- src/libslic3r/Model.cpp | 65 +++--------- src/libslic3r/Model.hpp | 8 +- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 146 +++++++++++++++++++++------ src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 5 +- src/slic3r/GUI/Plater.cpp | 6 ++ src/slic3r/GUI/Plater.hpp | 1 + 6 files changed, 145 insertions(+), 86 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 74dabb8e1c..6c316e889a 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1353,6 +1353,7 @@ void ModelObject::synchronize_model_after_cut() if (obj->is_cut() && obj->cut_id.has_same_id(this->cut_id)) obj->cut_id.copy(this->cut_id); } + this->invalidate_cut(); } void ModelObject::apply_cut_attributes(ModelObjectCutAttributes attributes) @@ -1378,7 +1379,7 @@ void ModelObject::apply_cut_attributes(ModelObjectCutAttributes attributes) void ModelObject::clone_for_cut(ModelObject** obj) { (*obj) = ModelObject::new_clone(*this); - (*obj)->set_model(nullptr); + (*obj)->set_model(this->get_model()); (*obj)->sla_support_points.clear(); (*obj)->sla_drain_holes.clear(); (*obj)->sla_points_status = sla::PointsStatus::NoPoints; @@ -1461,7 +1462,7 @@ static void add_cut_volume(TriangleMesh& mesh, ModelObject* object, const ModelV void ModelObject::process_connector_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, - std::vector& dowels, Vec3d& local_dowels_displace) + std::vector& dowels) { assert(volume->cut_info.is_connector); volume->cut_info.set_processed(); @@ -1497,9 +1498,6 @@ void ModelObject::process_connector_cut(ModelVolume* volume, const Transform3d& vol->set_rotation(Vec3d::Zero()); vol->set_offset(Z, 0.0); - // Compute the displacement (in instance coordinates) to be applied to place the dowels - local_dowels_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(1.0, 1.0, 0.0)); - dowels.push_back(dowel); } @@ -1566,7 +1564,7 @@ void ModelObject::process_volume_cut(ModelVolume* volume, const Transform3d& ins lower_mesh = TriangleMesh(lower_its); } void ModelObject::process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, - ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, Vec3d& local_displace) + ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower) { // Perform cut TriangleMesh upper_mesh, lower_mesh; @@ -1583,31 +1581,12 @@ void ModelObject::process_solid_part_cut(ModelVolume* volume, const Transform3d& if (attributes.has(ModelObjectCutAttribute::KeepUpper)) add_cut_volume(upper_mesh, upper, volume, cut_matrix); - if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower_mesh.empty()) { + if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower_mesh.empty()) add_cut_volume(lower_mesh, lower, volume, cut_matrix); - - // Compute the displacement (in instance coordinates) to be applied to place the upper parts - // The upper part displacement is set to half of the lower part bounding box - // this is done in hope at least a part of the upper part will always be visible and draggable - local_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-0.5, -0.5, 0.0)); - } } -static void invalidate_translations(ModelObject* object, const ModelInstance* src_instance) -{ - if (!object->origin_translation.isApprox(Vec3d::Zero()) && src_instance->get_offset().isApprox(Vec3d::Zero())) { - object->center_around_origin(); - object->translate_instances(-object->origin_translation); - object->origin_translation = Vec3d::Zero(); - } - else { - object->invalidate_bounding_box(); - object->center_around_origin(); - } -} - -static void reset_instance_transformation(ModelObject* object, size_t src_instance_idx, const Transform3d& cut_matrix, - bool place_on_cut = false, bool flip = false, Vec3d local_displace = Vec3d::Zero()) +void ModelObject::reset_instance_transformation(ModelObject* object, size_t src_instance_idx, const Transform3d& cut_matrix, + bool place_on_cut/* = false*/, bool flip/* = false*/) { using namespace Geometry; @@ -1615,15 +1594,8 @@ static void reset_instance_transformation(ModelObject* object, size_t src_instan for (size_t i = 0; i < object->instances.size(); ++i) { auto& obj_instance = object->instances[i]; - const Vec3d offset = obj_instance->get_offset(); const double rot_z = obj_instance->get_rotation().z(); - obj_instance->set_transformation(Transformation()); - - const Vec3d displace = local_displace.isApprox(Vec3d::Zero()) ? Vec3d::Zero() : - rotation_transform(obj_instance->get_rotation()) * local_displace; - obj_instance->set_offset(offset + displace); - Vec3d rotation = Vec3d::Zero(); if (!flip && !place_on_cut) { if ( i != src_instance_idx) @@ -1680,10 +1652,6 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix, const Transformation cut_transformation = Transformation(cut_matrix); const Transform3d inverse_cut_matrix = cut_transformation.get_rotation_matrix().inverse() * translation_transform(-1. * cut_transformation.get_offset()); - // Displacement (in instance coordinates) to be applied to place the upper parts - Vec3d local_displace = Vec3d::Zero(); - Vec3d local_dowels_displace = Vec3d::Zero(); - for (ModelVolume* volume : volumes) { volume->reset_extra_facets(); @@ -1691,10 +1659,10 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix, if (volume->cut_info.is_processed) process_modifier_cut(volume, instance_matrix, inverse_cut_matrix, attributes, upper, lower); else - process_connector_cut(volume, instance_matrix, cut_matrix, attributes, upper, lower, dowels, local_dowels_displace); + process_connector_cut(volume, instance_matrix, cut_matrix, attributes, upper, lower, dowels); } else if (!volume->mesh().empty()) - process_solid_part_cut(volume, instance_matrix, cut_matrix, attributes, upper, lower, local_displace); + process_solid_part_cut(volume, instance_matrix, cut_matrix, attributes, upper, lower); } // Post-process cut parts @@ -1707,31 +1675,22 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix, } else { if (attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper->volumes.empty()) { - invalidate_translations(upper, instances[instance]); - reset_instance_transformation(upper, instance, cut_matrix, attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper), - attributes.has(ModelObjectCutAttribute::FlipUpper), - local_displace); + attributes.has(ModelObjectCutAttribute::FlipUpper)); res.push_back(upper); } if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower->volumes.empty()) { - invalidate_translations(lower, instances[instance]); - reset_instance_transformation(lower, instance, cut_matrix, attributes.has(ModelObjectCutAttribute::PlaceOnCutLower), - attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) ? true : attributes.has(ModelObjectCutAttribute::FlipLower)); + attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) || attributes.has(ModelObjectCutAttribute::FlipLower)); res.push_back(lower); } if (attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) { for (auto dowel : dowels) { - invalidate_translations(dowel, instances[instance]); - - reset_instance_transformation(dowel, instance, Transform3d::Identity(), false, false, local_dowels_displace); - - local_dowels_displace += dowel->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-1.5, -1.5, 0.0)); + reset_instance_transformation(dowel, instance, Transform3d::Identity()); dowel->name += "-Dowel-" + dowel->volumes[0]->name; res.push_back(dowel); } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 359e1fbbdf..ccbe0bf640 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -472,13 +472,17 @@ public: void clone_for_cut(ModelObject **obj); void process_connector_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, - std::vector& dowels, Vec3d& local_dowels_displace); + std::vector& dowels); void process_modifier_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& inverse_cut_matrix, ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower); void process_volume_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes, TriangleMesh& upper_mesh, TriangleMesh& lower_mesh); void process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, - ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, Vec3d& local_displace); + ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower); + + static void reset_instance_transformation(ModelObject* object, size_t src_instance_idx, const Transform3d& cut_matrix, + bool place_on_cut = false, bool flip = false); + ModelObjectPtrs cut(size_t instance, const Transform3d&cut_matrix, ModelObjectCutAttributes attributes); void split(ModelObjectPtrs*new_objects); void merge(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index edf91aba86..92a38f9ecc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -892,6 +892,7 @@ void GLGizmoCut3D::on_set_state() // initiate archived values m_ar_plane_center = m_plane_center; m_start_dragging_m = m_rotation_m; + reset_cut_by_contours(); m_parent.request_extra_frame(); } @@ -1116,6 +1117,8 @@ void GLGizmoCut3D::dragging_grabber_z(const GLGizmoBase::UpdateData &data) projection = m_snap_step * std::round(projection / m_snap_step); const Vec3d shift = starting_vec * projection; + if (shift != Vec3d::Zero()) + reset_cut_by_contours(); // move cut plane center set_center(m_plane_center + shift, true); @@ -1153,6 +1156,9 @@ void GLGizmoCut3D::dragging_grabber_xy(const GLGizmoBase::UpdateData &data) if (m_hover_id == X) theta += 0.5 * PI; + if (!is_approx(theta, 0.0)) + reset_cut_by_contours(); + Vec3d rotation = Vec3d::Zero(); rotation[m_hover_id] = theta; @@ -1187,14 +1193,10 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data) { if (m_hover_id < 0) return; - if (m_hover_id == Z || m_hover_id == CutPlane) { + if (m_hover_id == Z || m_hover_id == CutPlane) dragging_grabber_z(data); - reset_cut_by_contours(); - } - else if (m_hover_id == X || m_hover_id == Y) { + else if (m_hover_id == X || m_hover_id == Y) dragging_grabber_xy(data); - reset_cut_by_contours(); - } else if (m_hover_id >= m_connectors_group_id && m_connector_mode == CutConnectorMode::Manual) dragging_connector(data); } @@ -1301,6 +1303,7 @@ void GLGizmoCut3D::update_bb() m_bounding_box = box; invalidate_cut_plane(); + reset_cut_by_contours(); m_max_pos = box.max; m_min_pos = box.min; @@ -1402,11 +1405,14 @@ void GLGizmoCut3D::PartSelection::render(const Vec3d* normal) // FIXME: Cache the transforms. - const Vec3d inst_offset = model_object->instances[0]->get_offset(); + const Vec3d inst_offset = model_object->instances[instance_idx]->get_offset(); const Transform3d view_inst_matrix= camera.get_view_matrix() * translation_transform(inst_offset); + const bool is_looking_forward = normal && camera.get_dir_forward().dot(*normal) < 0.05; + for (size_t id=0; idvolumes[id]->get_offset(); shader->set_uniform("view_model_matrix", view_inst_matrix * translation_transform(volume_offset)); @@ -1419,7 +1425,6 @@ void GLGizmoCut3D::PartSelection::render(const Vec3d* normal) } } - void GLGizmoCut3D::PartSelection::toggle_selection(const Vec2d& mouse_pos) { // FIXME: Cache the transforms. @@ -1430,7 +1435,7 @@ void GLGizmoCut3D::PartSelection::toggle_selection(const Vec2d& mouse_pos) for (size_t id=0; idvolumes[id]->get_offset(); - Transform3d tr = model_object->instances.front()->get_matrix() * model_object->volumes[id]->get_matrix(); + Transform3d tr = model_object->instances[instance_idx]->get_matrix() * model_object->volumes[id]->get_matrix(); if (parts[id].raycaster.unproject_on_mesh(mouse_pos, tr, camera, pos, normal)) { parts[id].selected = ! parts[id].selected; return; @@ -1438,6 +1443,12 @@ void GLGizmoCut3D::PartSelection::toggle_selection(const Vec2d& mouse_pos) } } +void GLGizmoCut3D::PartSelection::turn_over_selection() +{ + for (Part& part : parts) + part.selected = !part.selected; +} + void GLGizmoCut3D::on_render() { if (m_state == On) { @@ -1708,12 +1719,14 @@ void GLGizmoCut3D::flip_cut_plane() m_start_dragging_m = m_rotation_m; update_clipper(); + m_part_selection.turn_over_selection(); } -GLGizmoCut3D::PartSelection::PartSelection(ModelObject* mo, const Vec3d& center, const Vec3d& normal) +GLGizmoCut3D::PartSelection::PartSelection(ModelObject* mo, int instance_idx_in, const Vec3d& center, const Vec3d& normal) { model_object = mo; // FIXME: Ownership. + instance_idx = instance_idx_in; const ModelVolumePtrs& volumes = mo->volumes; @@ -1730,7 +1743,7 @@ GLGizmoCut3D::PartSelection::PartSelection(ModelObject* mo, const Vec3d& center, parts.back().glmodel.init_from(volume->mesh()); // Now check whether this part is below or above the plane. - Transform3d tr = (model_object->instances.front()->get_matrix() * volume->get_matrix()).inverse(); + Transform3d tr = (model_object->instances[instance_idx]->get_matrix() * volume->get_matrix()).inverse(); Vec3f pos = (tr * center).cast(); Vec3f norm = (tr.linear().inverse().transpose() * normal).cast(); for (const Vec3f& v : volume->mesh().its.vertices) { @@ -1749,7 +1762,10 @@ GLGizmoCut3D::PartSelection::PartSelection(ModelObject* mo, const Vec3d& center, void GLGizmoCut3D::reset_cut_by_contours() { m_part_selection = PartSelection(); - m_parent.toggle_model_objects_visibility(true); + + const Selection& selection = m_parent.get_selection(); + const ModelObjectPtrs& model_objects = selection.get_model()->objects; + m_parent.toggle_model_objects_visibility(true, model_objects[selection.get_object_idx()], selection.get_instance_idx()); } void GLGizmoCut3D::process_contours() @@ -1763,14 +1779,14 @@ void GLGizmoCut3D::process_contours() const int instance_idx = selection.get_instance_idx(); const int object_idx = selection.get_object_idx(); - ModelObjectPtrs moptrs = model_objects[object_idx]->cut(instance_idx, get_cut_matrix(selection), + m_cut_part_ptrs.clear(); + m_cut_part_ptrs = model_objects[object_idx]->cut(instance_idx, get_cut_matrix(selection), ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::KeepLower | - ModelObjectCutAttribute::KeepAsParts | - ModelObjectCutAttribute::InvalidateCutInfo); + ModelObjectCutAttribute::KeepAsParts); + assert(m_cut_part_ptrs.size() == 1); - assert(moptrs.size() == 1); - m_part_selection = PartSelection(moptrs.front(), m_plane_center, m_cut_normal); + m_part_selection = PartSelection(m_cut_part_ptrs.front(), instance_idx, m_plane_center, m_cut_normal); m_parent.toggle_model_objects_visibility(false); } @@ -1919,7 +1935,7 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) ImGuiWrapper::text(_L("Cut result") + ": "); add_vertical_scaled_interval(0.5f); - m_imgui->disabled_begin(has_connectors || m_keep_as_parts); + m_imgui->disabled_begin(has_connectors || m_keep_as_parts || m_part_selection.valid); render_part_name("A", m_keep_upper, m_imgui->to_ImU32(UPPER_PART_COLOR)); ImGui::SameLine(h_shift + ImGui::GetCurrentWindow()->WindowPadding.x); render_part_name("B", m_keep_lower, m_imgui->to_ImU32(LOWER_PART_COLOR)); @@ -1938,6 +1954,9 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) m_imgui->disabled_begin(has_connectors); ImGuiWrapper::text(_L("Cut into") + ":"); + if (m_part_selection.valid) + m_keep_as_parts = false; + add_horizontal_scaled_interval(1.2f); // TRN CutGizmo: RadioButton Cut into ... if (m_imgui->radio_button(_L("Objects"), !m_keep_as_parts)) @@ -2322,21 +2341,88 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) { Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Cut by Plane")); + const bool cut_by_contour = m_part_selection.valid && !m_cut_part_ptrs.empty(); + ModelObject* cut_mo = cut_by_contour ? m_cut_part_ptrs.front() : nullptr; + if (cut_mo) + cut_mo->cut_connectors = mo->cut_connectors; + bool create_dowels_as_separate_object = false; const bool has_connectors = !mo->cut_connectors.empty(); // update connectors pos as offset of its center before cut performing - apply_connectors_in_model(mo, create_dowels_as_separate_object); + apply_connectors_in_model(cut_mo ? cut_mo : mo , create_dowels_as_separate_object); - plater->cut(object_idx, instance_idx, get_cut_matrix(selection), - only_if(has_connectors ? true : m_keep_upper, ModelObjectCutAttribute::KeepUpper) | - only_if(has_connectors ? true : m_keep_lower, ModelObjectCutAttribute::KeepLower) | - only_if(has_connectors ? false: m_keep_as_parts, ModelObjectCutAttribute::KeepAsParts) | - only_if(m_place_on_cut_upper, ModelObjectCutAttribute::PlaceOnCutUpper) | - only_if(m_place_on_cut_lower, ModelObjectCutAttribute::PlaceOnCutLower) | - only_if(m_rotate_upper, ModelObjectCutAttribute::FlipUpper) | - only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower) | - only_if(create_dowels_as_separate_object, ModelObjectCutAttribute::CreateDowels) | - only_if(!has_connectors, ModelObjectCutAttribute::InvalidateCutInfo)); + wxBusyCursor wait; + + const Transform3d cut_matrix = get_cut_matrix(selection); + + ModelObjectCutAttributes attributes = only_if(has_connectors ? true : m_keep_upper, ModelObjectCutAttribute::KeepUpper) | + only_if(has_connectors ? true : m_keep_lower, ModelObjectCutAttribute::KeepLower) | + only_if(has_connectors ? false : m_keep_as_parts, ModelObjectCutAttribute::KeepAsParts) | + only_if(m_place_on_cut_upper, ModelObjectCutAttribute::PlaceOnCutUpper) | + only_if(m_place_on_cut_lower, ModelObjectCutAttribute::PlaceOnCutLower) | + only_if(m_rotate_upper, ModelObjectCutAttribute::FlipUpper) | + only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower) | + only_if(create_dowels_as_separate_object, ModelObjectCutAttribute::CreateDowels) | + only_if(!has_connectors, ModelObjectCutAttribute::InvalidateCutInfo); + + ModelObjectPtrs cut_object_ptrs; + if (cut_by_contour) { + // apply cut attributes for object + cut_mo->apply_cut_attributes(ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | + only_if(create_dowels_as_separate_object, ModelObjectCutAttribute::CreateDowels)); + + // Clone the object to duplicate instances, materials etc. + ModelObject* upper{ nullptr }; + cut_mo->clone_for_cut(&upper); + ModelObject* lower{ nullptr }; + cut_mo->clone_for_cut(&lower); + + auto add_cut_objects = [this, &instance_idx, &cut_matrix](ModelObjectPtrs& cut_objects, ModelObject* upper, ModelObject* lower, bool invalidate_cut = true) { + if (!upper->volumes.empty()) { + ModelObject::reset_instance_transformation(upper, instance_idx, cut_matrix, m_place_on_cut_upper, m_rotate_upper); + if (invalidate_cut) + upper->invalidate_cut(); + cut_objects.push_back(upper); + } + if (!lower->volumes.empty()) { + ModelObject::reset_instance_transformation(lower, instance_idx, cut_matrix, m_place_on_cut_lower, m_place_on_cut_lower || m_rotate_lower); + if (invalidate_cut) + lower->invalidate_cut(); + cut_objects.push_back(lower); + } + }; + + const size_t cut_parts_cnt = m_part_selection.parts.size(); + for (size_t id = 0; id < cut_parts_cnt; ++id) + (m_part_selection.parts[id].selected ? upper : lower)->add_volume(*(cut_mo->volumes[id])); + + ModelVolumePtrs& volumes = cut_mo->volumes; + if (volumes.size() == cut_parts_cnt) + add_cut_objects(cut_object_ptrs, upper, lower); + else if (volumes.size() > cut_parts_cnt) { + for (size_t id = 0; id < cut_parts_cnt; id++) + delete *(volumes.begin() + id); + volumes.erase(volumes.begin(), volumes.begin() + cut_parts_cnt); + + const auto cut_connectors_obj = cut_mo->cut(instance_idx, get_cut_matrix(selection), attributes); + assert(create_dowels_as_separate_object ? cut_connectors_obj.size() >= 3 : cut_connectors_obj.size() == 2); + + for (const ModelVolume* volume : cut_connectors_obj[0]->volumes) + upper->add_volume(*volume, volume->type()); + for (const ModelVolume* volume : cut_connectors_obj[1]->volumes) + lower->add_volume(*volume, volume->type()); + + add_cut_objects(cut_object_ptrs, upper, lower, false); + + if (cut_connectors_obj.size() >= 3) + for (size_t id = 2; id < cut_connectors_obj.size(); id++) + cut_object_ptrs.push_back(cut_connectors_obj[id]); + } + } + else + cut_object_ptrs = mo->cut(instance_idx, cut_matrix, attributes); + + plater->cut(object_idx, cut_object_ptrs); } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 9bf805188b..598bc1d5a8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -139,10 +139,11 @@ class GLGizmoCut3D : public GLGizmoBase struct PartSelection { PartSelection() = default; - PartSelection(ModelObject* mo, const Vec3d& center, const Vec3d& normal); + PartSelection(ModelObject* mo, int instance_idx, const Vec3d& center, const Vec3d& normal); void render(const Vec3d* normal = nullptr); void toggle_selection(const Vec2d& mouse_pos); + void turn_over_selection(); struct Part { GLModel glmodel; @@ -151,11 +152,13 @@ class GLGizmoCut3D : public GLGizmoBase bool upper; }; ModelObject* model_object; // FIXME: Ownership ! + int instance_idx; std::vector parts; bool valid = false; }; PartSelection m_part_selection; + ModelObjectPtrs m_cut_part_ptrs; bool m_show_shortcuts{ false }; std::vector> m_shortcuts; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 061f39622c..35308c0581 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -6266,7 +6266,11 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, const Transform3d& cut_mat wxBusyCursor wait; const auto new_objects = object->cut(instance_idx, cut_matrix, attributes); + cut(obj_idx, new_objects); +} +void Plater::cut(size_t obj_idx, const ModelObjectPtrs& new_objects) +{ model().delete_object(obj_idx); sidebar().obj_list()->delete_object_from_list(obj_idx); @@ -6284,6 +6288,8 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, const Transform3d& cut_mat size_t last_id = p->model.objects.size() - 1; for (size_t i = 0; i < new_objects.size(); ++i) selection.add_object((unsigned int)(last_id - i), i == 0); + + arrange(); } void Plater::export_gcode(bool prefer_removable) diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 58a840a4b5..0359328699 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -260,6 +260,7 @@ public: void toggle_layers_editing(bool enable); void cut(size_t obj_idx, size_t instance_idx, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes); + void cut(size_t init_obj_idx, const ModelObjectPtrs& cut_objects); void export_gcode(bool prefer_removable); void export_stl_obj(bool extended = false, bool selection_only = false); From 7ea51fc07c7e9c0cf30a8af9723f1ae924b183d9 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 28 Mar 2023 08:55:22 +0200 Subject: [PATCH 63/90] Cut: when parts are selected, the result shall have only one parts with the combined meshes --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 92a38f9ecc..a4f2f975ba 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -2418,6 +2418,26 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) for (size_t id = 2; id < cut_connectors_obj.size(); id++) cut_object_ptrs.push_back(cut_connectors_obj[id]); } + + // Now merge all model parts together: + { + for (ModelObject* mo : cut_object_ptrs) { + TriangleMesh mesh; + for (const ModelVolume* mv : mo->volumes) { + if (mv->is_model_part()) { + TriangleMesh m = mv->mesh(); + m.transform(mv->get_matrix()); + mesh.merge(m); + } + } + if (! mesh.empty()) { + ModelVolume* new_volume = mo->add_volume(mesh); + for (int i=int(mo->volumes.size())-2; i>=0; --i) + if (mo->volumes[i]->type() == ModelVolumeType::MODEL_PART) + mo->delete_volume(i); + } + } + } } else cut_object_ptrs = mo->cut(instance_idx, cut_matrix, attributes); From 732dd0f6ac472241d370d61e286ef0b6b7d0c2fd Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 28 Mar 2023 10:00:44 +0200 Subject: [PATCH 64/90] Cut: fixed rendering of individual parts, removed some unnecessary variables --- src/libslic3r/Model.cpp | 8 -------- src/libslic3r/Model.hpp | 14 +++++++++----- src/slic3r/GUI/GUI_App.cpp | 16 ---------------- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 17 ++++++++--------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 2 -- 5 files changed, 17 insertions(+), 40 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 6c316e889a..c7844f8072 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -2627,14 +2627,6 @@ bool model_has_multi_part_objects(const Model &model) return false; } -bool model_has_connectors(const Model &model) -{ - for (const ModelObject *model_object : model.objects) - if (!model_object->cut_connectors.empty()) - return true; - return false; -} - bool model_has_advanced_features(const Model &model) { auto config_is_advanced = [](const ModelConfig &config) { diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index ccbe0bf640..27adf8fd36 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -467,9 +467,15 @@ public: void invalidate_cut(); // delete volumes which are marked as connector for this object void delete_connectors(); - void synchronize_model_after_cut(); - void apply_cut_attributes(ModelObjectCutAttributes attributes); void clone_for_cut(ModelObject **obj); + + void apply_cut_attributes(ModelObjectCutAttributes attributes); +private: + // FIXME: These functions would best not be here at all. It might make sense to separate the + // cut-related methods elsewhere. Same holds for cut_connectors data member, which is currently + // just a temporary variable used by cut gizmo only. + void synchronize_model_after_cut(); + void process_connector_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, std::vector& dowels); @@ -479,7 +485,7 @@ public: ModelObjectCutAttributes attributes, TriangleMesh& upper_mesh, TriangleMesh& lower_mesh); void process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower); - +public: static void reset_instance_transformation(ModelObject* object, size_t src_instance_idx, const Transform3d& cut_matrix, bool place_on_cut = false, bool flip = false); @@ -1395,8 +1401,6 @@ bool model_has_parameter_modifiers_in_objects(const Model& model); // If the model has multi-part objects, then it is currently not supported by the SLA mode. // Either the model cannot be loaded, or a SLA printer has to be activated. bool model_has_multi_part_objects(const Model &model); -// If the model has objects with cut connectrs, then it is currently not supported by the SLA mode. -bool model_has_connectors(const Model& model); // If the model has advanced features, then it cannot be processed in simple mode. bool model_has_advanced_features(const Model &model); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 83c3cf13fc..7382f87bf1 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -3023,22 +3023,6 @@ bool GUI_App::may_switch_to_SLA_preset(const wxString& caption) caption); return false; } -/* - if (model_has_multi_part_objects(model())) { - show_info(nullptr, - _L("It's impossible to print multi-part object(s) with SLA technology.") + "\n\n" + - _L("Please check your object list before preset changing."), - caption); - return false; - } - if (model_has_connectors(model())) { - show_info(nullptr, - _L("SLA technology doesn't support cut with connectors") + "\n\n" + - _L("Please check your object list before preset changing."), - caption); - return false; - } -*/ return true; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index a4f2f975ba..2f48f4392d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -1402,6 +1402,7 @@ void GLGizmoCut3D::PartSelection::render(const Vec3d* normal) shader->start_using(); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + shader->set_uniform("emission_factor", 0.f); // FIXME: Cache the transforms. @@ -1416,7 +1417,6 @@ void GLGizmoCut3D::PartSelection::render(const Vec3d* normal) continue; const Vec3d volume_offset = model_object->volumes[id]->get_offset(); shader->set_uniform("view_model_matrix", view_inst_matrix * translation_transform(volume_offset)); - //parts[id].glmodel.set_color(parts[id].selected ? ColorRGBA(1.f, 0.f, 0.f, 1.f) : ColorRGBA(0.f, 1.f, 0.f, 1.f)); parts[id].glmodel.set_color(parts[id].selected ? UPPER_PART_COLOR : LOWER_PART_COLOR); parts[id].glmodel.render(); } @@ -1779,14 +1779,13 @@ void GLGizmoCut3D::process_contours() const int instance_idx = selection.get_instance_idx(); const int object_idx = selection.get_object_idx(); - m_cut_part_ptrs.clear(); - m_cut_part_ptrs = model_objects[object_idx]->cut(instance_idx, get_cut_matrix(selection), + ModelObjectPtrs cut_part_ptrs = model_objects[object_idx]->cut(instance_idx, get_cut_matrix(selection), ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepAsParts); - assert(m_cut_part_ptrs.size() == 1); + assert(cut_part_ptrs.size() == 1); - m_part_selection = PartSelection(m_cut_part_ptrs.front(), instance_idx, m_plane_center, m_cut_normal); + m_part_selection = PartSelection(cut_part_ptrs.front(), instance_idx, m_plane_center, m_cut_normal); m_parent.toggle_model_objects_visibility(false); } @@ -2144,7 +2143,7 @@ bool GLGizmoCut3D::is_outside_of_cut_contour(size_t idx, const CutConnectors& co } its_transform(mesh, translation_transform(cur_pos) * m_rotation_m); - for (auto vertex : vertices) { + for (const Vec3f& vertex : vertices) { if (m_c->object_clipper() && m_c->object_clipper()->is_projection_inside_cut(vertex.cast()) == -1) { m_info_stats.outside_cut_contour++; return true; @@ -2341,8 +2340,8 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) { Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Cut by Plane")); - const bool cut_by_contour = m_part_selection.valid && !m_cut_part_ptrs.empty(); - ModelObject* cut_mo = cut_by_contour ? m_cut_part_ptrs.front() : nullptr; + const bool cut_by_contour = m_part_selection.valid; + ModelObject* cut_mo = cut_by_contour ? m_part_selection.model_object : nullptr; if (cut_mo) cut_mo->cut_connectors = mo->cut_connectors; @@ -2404,7 +2403,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) delete *(volumes.begin() + id); volumes.erase(volumes.begin(), volumes.begin() + cut_parts_cnt); - const auto cut_connectors_obj = cut_mo->cut(instance_idx, get_cut_matrix(selection), attributes); + const ModelObjectPtrs cut_connectors_obj = cut_mo->cut(instance_idx, get_cut_matrix(selection), attributes); assert(create_dowels_as_separate_object ? cut_connectors_obj.size() >= 3 : cut_connectors_obj.size() == 2); for (const ModelVolume* volume : cut_connectors_obj[0]->volumes) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 598bc1d5a8..793d363582 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -149,7 +149,6 @@ class GLGizmoCut3D : public GLGizmoBase GLModel glmodel; MeshRaycaster raycaster; bool selected; - bool upper; }; ModelObject* model_object; // FIXME: Ownership ! int instance_idx; @@ -158,7 +157,6 @@ class GLGizmoCut3D : public GLGizmoBase }; PartSelection m_part_selection; - ModelObjectPtrs m_cut_part_ptrs; bool m_show_shortcuts{ false }; std::vector> m_shortcuts; From 2005882ac5db693b70f7105e96d85307ed1f8503 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 28 Mar 2023 11:22:19 +0200 Subject: [PATCH 65/90] Cut: always toggle the closest part --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 34 +++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 2f48f4392d..4aaa7ea8b0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -1428,18 +1428,25 @@ void GLGizmoCut3D::PartSelection::render(const Vec3d* normal) void GLGizmoCut3D::PartSelection::toggle_selection(const Vec2d& mouse_pos) { // FIXME: Cache the transforms. - const Camera& camera = wxGetApp().plater()->get_camera(); + const Camera& camera = wxGetApp().plater()->get_camera(); + const Vec3d& camera_pos = camera.get_position(); Vec3f pos; Vec3f normal; - + + std::vector> hits_id_and_sqdist; + for (size_t id=0; idvolumes[id]->get_offset(); Transform3d tr = model_object->instances[instance_idx]->get_matrix() * model_object->volumes[id]->get_matrix(); if (parts[id].raycaster.unproject_on_mesh(mouse_pos, tr, camera, pos, normal)) { - parts[id].selected = ! parts[id].selected; - return; - } + hits_id_and_sqdist.emplace_back(id, (camera_pos - tr*(pos.cast())).squaredNorm()); + } + } + if (! hits_id_and_sqdist.empty()) { + size_t id = std::min_element(hits_id_and_sqdist.begin(), hits_id_and_sqdist.end(), + [](const std::pair& a, const std::pair& b) { return a.second < b.second; })->first; + parts[id].selected = ! parts[id].selected; } } @@ -2470,7 +2477,22 @@ bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, Vec3d& po hit = (point + t * direction); } else return false; - + + // Now check if the hit is not obscured by a selected part on this side of the plane. + // FIXME: This would be better solved by remembering which contours are active. We will + // probably need that anyway because there is not other way to find out which contours + // to render. If you want to uncomment it, fix it first. It does not work yet. + /*for (size_t id = 0; id < m_part_selection.parts.size(); ++id) { + if (! m_part_selection.parts[id].selected) { + Vec3f pos, normal; + const ModelObject* model_object = m_part_selection.model_object; + const Vec3d volume_offset = m_part_selection.model_object->volumes[id]->get_offset(); + Transform3d tr = model_object->instances[m_part_selection.instance_idx]->get_matrix() * model_object->volumes[id]->get_matrix(); + if (m_part_selection.parts[id].raycaster.unproject_on_mesh(mouse_position, tr, camera, pos, normal)) + return false; + } + }*/ + if (m_c->object_clipper()->is_projection_inside_cut(hit) == -1) return false; From ae5fe9ce8cfd57f53362bd133a77629249faa87d Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 28 Mar 2023 13:06:11 +0200 Subject: [PATCH 66/90] Cut: separate Model for the pre-cut object --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 35 +++++++++++++++------------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 6 +++-- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 4aaa7ea8b0..38bccfb4a7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -1406,7 +1406,7 @@ void GLGizmoCut3D::PartSelection::render(const Vec3d* normal) // FIXME: Cache the transforms. - const Vec3d inst_offset = model_object->instances[instance_idx]->get_offset(); + const Vec3d inst_offset = model_object()->instances[instance_idx]->get_offset(); const Transform3d view_inst_matrix= camera.get_view_matrix() * translation_transform(inst_offset); const bool is_looking_forward = normal && camera.get_dir_forward().dot(*normal) < 0.05; @@ -1415,7 +1415,7 @@ void GLGizmoCut3D::PartSelection::render(const Vec3d* normal) if (normal && (( is_looking_forward && parts[id].selected) || (!is_looking_forward && !parts[id].selected) ) ) continue; - const Vec3d volume_offset = model_object->volumes[id]->get_offset(); + const Vec3d volume_offset = model_object()->volumes[id]->get_offset(); shader->set_uniform("view_model_matrix", view_inst_matrix * translation_transform(volume_offset)); parts[id].glmodel.set_color(parts[id].selected ? UPPER_PART_COLOR : LOWER_PART_COLOR); parts[id].glmodel.render(); @@ -1437,8 +1437,8 @@ void GLGizmoCut3D::PartSelection::toggle_selection(const Vec2d& mouse_pos) std::vector> hits_id_and_sqdist; for (size_t id=0; idvolumes[id]->get_offset(); - Transform3d tr = model_object->instances[instance_idx]->get_matrix() * model_object->volumes[id]->get_matrix(); + const Vec3d volume_offset = model_object()->volumes[id]->get_offset(); + Transform3d tr = model_object()->instances[instance_idx]->get_matrix() * model_object()->volumes[id]->get_matrix(); if (parts[id].raycaster.unproject_on_mesh(mouse_pos, tr, camera, pos, normal)) { hits_id_and_sqdist.emplace_back(id, (camera_pos - tr*(pos.cast())).squaredNorm()); } @@ -1730,12 +1730,21 @@ void GLGizmoCut3D::flip_cut_plane() } -GLGizmoCut3D::PartSelection::PartSelection(ModelObject* mo, int instance_idx_in, const Vec3d& center, const Vec3d& normal) +GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* mo, const Transform3d& cut_matrix, int instance_idx_in, const Vec3d& center, const Vec3d& normal) { - model_object = mo; // FIXME: Ownership. + model = Model(); + model.add_object(*mo); + ModelObjectPtrs cut_part_ptrs = model.objects.front()->cut(instance_idx_in, cut_matrix, + ModelObjectCutAttribute::KeepUpper | + ModelObjectCutAttribute::KeepLower | + ModelObjectCutAttribute::KeepAsParts); + assert(cut_part_ptrs.size() == 1); + model = Model(); + model.add_object(*cut_part_ptrs.front()); + instance_idx = instance_idx_in; - const ModelVolumePtrs& volumes = mo->volumes; + const ModelVolumePtrs& volumes = model_object()->volumes; // split to parts for (int id = int(volumes.size())-1; id >= 0; id--) @@ -1750,7 +1759,7 @@ GLGizmoCut3D::PartSelection::PartSelection(ModelObject* mo, int instance_idx_in, parts.back().glmodel.init_from(volume->mesh()); // Now check whether this part is below or above the plane. - Transform3d tr = (model_object->instances[instance_idx]->get_matrix() * volume->get_matrix()).inverse(); + Transform3d tr = (model_object()->instances[instance_idx]->get_matrix() * volume->get_matrix()).inverse(); Vec3f pos = (tr * center).cast(); Vec3f norm = (tr.linear().inverse().transpose() * normal).cast(); for (const Vec3f& v : volume->mesh().its.vertices) { @@ -1786,13 +1795,7 @@ void GLGizmoCut3D::process_contours() const int instance_idx = selection.get_instance_idx(); const int object_idx = selection.get_object_idx(); - ModelObjectPtrs cut_part_ptrs = model_objects[object_idx]->cut(instance_idx, get_cut_matrix(selection), - ModelObjectCutAttribute::KeepUpper | - ModelObjectCutAttribute::KeepLower | - ModelObjectCutAttribute::KeepAsParts); - assert(cut_part_ptrs.size() == 1); - - m_part_selection = PartSelection(cut_part_ptrs.front(), instance_idx, m_plane_center, m_cut_normal); + m_part_selection = PartSelection(model_objects[object_idx], get_cut_matrix(selection), instance_idx, m_plane_center, m_cut_normal); m_parent.toggle_model_objects_visibility(false); } @@ -2348,7 +2351,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Cut by Plane")); const bool cut_by_contour = m_part_selection.valid; - ModelObject* cut_mo = cut_by_contour ? m_part_selection.model_object : nullptr; + ModelObject* cut_mo = cut_by_contour ? m_part_selection.model_object() : nullptr; if (cut_mo) cut_mo->cut_connectors = mo->cut_connectors; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 793d363582..37f4610380 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -139,18 +139,20 @@ class GLGizmoCut3D : public GLGizmoBase struct PartSelection { PartSelection() = default; - PartSelection(ModelObject* mo, int instance_idx, const Vec3d& center, const Vec3d& normal); + PartSelection(const ModelObject* mo, const Transform3d& cut_matrix, int instance_idx, const Vec3d& center, const Vec3d& normal); void render(const Vec3d* normal = nullptr); void toggle_selection(const Vec2d& mouse_pos); void turn_over_selection(); + ModelObject* model_object() { return model.objects.front(); } struct Part { GLModel glmodel; MeshRaycaster raycaster; bool selected; }; - ModelObject* model_object; // FIXME: Ownership ! + + Model model; int instance_idx; std::vector parts; bool valid = false; From 8bea83c6c7611ac08e25aa2b71e918399a8ff611 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 28 Mar 2023 15:13:04 +0200 Subject: [PATCH 67/90] Cut: slightly refactored PartSelection --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 67 +++++++++++++++------------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 25 ++++++----- 2 files changed, 50 insertions(+), 42 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 38bccfb4a7..50e446292b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -309,7 +309,7 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) flip_cut_plane(); } - if (m_part_selection.valid) + if (m_part_selection.valid()) m_parent.toggle_model_objects_visibility(false); return true; } @@ -353,7 +353,7 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) else if (mouse_event.RightDown()) { if (! m_connectors_editing) { // Check the internal part raycasters. - if (! m_part_selection.valid) + if (! m_part_selection.valid()) process_contours(); m_part_selection.toggle_selection(mouse_pos); return true; @@ -1394,7 +1394,7 @@ void GLGizmoCut3D::render_clipper_cut() void GLGizmoCut3D::PartSelection::render(const Vec3d* normal) { - if (! valid) + if (! valid()) return; if (GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light")) { @@ -1406,19 +1406,19 @@ void GLGizmoCut3D::PartSelection::render(const Vec3d* normal) // FIXME: Cache the transforms. - const Vec3d inst_offset = model_object()->instances[instance_idx]->get_offset(); + const Vec3d inst_offset = model_object()->instances[m_instance_idx]->get_offset(); const Transform3d view_inst_matrix= camera.get_view_matrix() * translation_transform(inst_offset); const bool is_looking_forward = normal && camera.get_dir_forward().dot(*normal) < 0.05; - for (size_t id=0; idvolumes[id]->get_offset(); shader->set_uniform("view_model_matrix", view_inst_matrix * translation_transform(volume_offset)); - parts[id].glmodel.set_color(parts[id].selected ? UPPER_PART_COLOR : LOWER_PART_COLOR); - parts[id].glmodel.render(); + m_parts[id].glmodel.set_color(m_parts[id].selected ? UPPER_PART_COLOR : LOWER_PART_COLOR); + m_parts[id].glmodel.render(); } shader->stop_using(); @@ -1436,23 +1436,23 @@ void GLGizmoCut3D::PartSelection::toggle_selection(const Vec2d& mouse_pos) std::vector> hits_id_and_sqdist; - for (size_t id=0; idvolumes[id]->get_offset(); - Transform3d tr = model_object()->instances[instance_idx]->get_matrix() * model_object()->volumes[id]->get_matrix(); - if (parts[id].raycaster.unproject_on_mesh(mouse_pos, tr, camera, pos, normal)) { + Transform3d tr = model_object()->instances[m_instance_idx]->get_matrix() * model_object()->volumes[id]->get_matrix(); + if (m_parts[id].raycaster.unproject_on_mesh(mouse_pos, tr, camera, pos, normal)) { hits_id_and_sqdist.emplace_back(id, (camera_pos - tr*(pos.cast())).squaredNorm()); } } if (! hits_id_and_sqdist.empty()) { size_t id = std::min_element(hits_id_and_sqdist.begin(), hits_id_and_sqdist.end(), [](const std::pair& a, const std::pair& b) { return a.second < b.second; })->first; - parts[id].selected = ! parts[id].selected; + m_parts[id].selected = ! m_parts[id].selected; } } void GLGizmoCut3D::PartSelection::turn_over_selection() { - for (Part& part : parts) + for (Part& part : m_parts) part.selected = !part.selected; } @@ -1732,17 +1732,17 @@ void GLGizmoCut3D::flip_cut_plane() GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* mo, const Transform3d& cut_matrix, int instance_idx_in, const Vec3d& center, const Vec3d& normal) { - model = Model(); - model.add_object(*mo); - ModelObjectPtrs cut_part_ptrs = model.objects.front()->cut(instance_idx_in, cut_matrix, + m_model = Model(); + m_model.add_object(*mo); + ModelObjectPtrs cut_part_ptrs = m_model.objects.front()->cut(instance_idx_in, cut_matrix, ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepAsParts); assert(cut_part_ptrs.size() == 1); - model = Model(); - model.add_object(*cut_part_ptrs.front()); + m_model = Model(); + m_model.add_object(*cut_part_ptrs.front()); - instance_idx = instance_idx_in; + m_instance_idx = instance_idx_in; const ModelVolumePtrs& volumes = model_object()->volumes; @@ -1751,27 +1751,27 @@ GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* mo, const Transfor if (volumes[id]->is_splittable()) volumes[id]->split(1); - parts.clear(); + m_parts.clear(); for (const ModelVolume* volume : volumes) { assert(volume != nullptr); - parts.emplace_back(Part{GLModel(), MeshRaycaster(volume->mesh()), true}); - parts.back().glmodel.set_color({ 0.f, 0.f, 1.f, 1.f }); - parts.back().glmodel.init_from(volume->mesh()); + m_parts.emplace_back(Part{GLModel(), MeshRaycaster(volume->mesh()), true}); + m_parts.back().glmodel.set_color({ 0.f, 0.f, 1.f, 1.f }); + m_parts.back().glmodel.init_from(volume->mesh()); // Now check whether this part is below or above the plane. - Transform3d tr = (model_object()->instances[instance_idx]->get_matrix() * volume->get_matrix()).inverse(); + Transform3d tr = (model_object()->instances[m_instance_idx]->get_matrix() * volume->get_matrix()).inverse(); Vec3f pos = (tr * center).cast(); Vec3f norm = (tr.linear().inverse().transpose() * normal).cast(); for (const Vec3f& v : volume->mesh().its.vertices) { double p = (v - pos).dot(norm); if (std::abs(p) > EPSILON) { - parts.back().selected = p > 0.; + m_parts.back().selected = p > 0.; break; } } } - valid = true; + m_valid = true; } @@ -1944,7 +1944,7 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) ImGuiWrapper::text(_L("Cut result") + ": "); add_vertical_scaled_interval(0.5f); - m_imgui->disabled_begin(has_connectors || m_keep_as_parts || m_part_selection.valid); + m_imgui->disabled_begin(has_connectors || m_keep_as_parts || m_part_selection.valid()); render_part_name("A", m_keep_upper, m_imgui->to_ImU32(UPPER_PART_COLOR)); ImGui::SameLine(h_shift + ImGui::GetCurrentWindow()->WindowPadding.x); render_part_name("B", m_keep_lower, m_imgui->to_ImU32(LOWER_PART_COLOR)); @@ -1963,7 +1963,7 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) m_imgui->disabled_begin(has_connectors); ImGuiWrapper::text(_L("Cut into") + ":"); - if (m_part_selection.valid) + if (m_part_selection.valid()) m_keep_as_parts = false; add_horizontal_scaled_interval(1.2f); @@ -2350,7 +2350,10 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) { Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Cut by Plane")); - const bool cut_by_contour = m_part_selection.valid; + // This shall delete the part selection class and deallocate the memory. + ScopeGuard part_selection_killer([this]() { m_part_selection = PartSelection(); }); + + const bool cut_by_contour = m_part_selection.valid(); ModelObject* cut_mo = cut_by_contour ? m_part_selection.model_object() : nullptr; if (cut_mo) cut_mo->cut_connectors = mo->cut_connectors; @@ -2401,9 +2404,9 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) } }; - const size_t cut_parts_cnt = m_part_selection.parts.size(); + const size_t cut_parts_cnt = m_part_selection.parts().size(); for (size_t id = 0; id < cut_parts_cnt; ++id) - (m_part_selection.parts[id].selected ? upper : lower)->add_volume(*(cut_mo->volumes[id])); + (m_part_selection.parts()[id].selected ? upper : lower)->add_volume(*(cut_mo->volumes[id])); ModelVolumePtrs& volumes = cut_mo->volumes; if (volumes.size() == cut_parts_cnt) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 37f4610380..255d1a568f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -137,25 +137,30 @@ class GLGizmoCut3D : public GLGizmoBase bool m_was_cut_plane_dragged { false }; bool m_was_contour_selected { false }; - struct PartSelection { + class PartSelection { + public: PartSelection() = default; PartSelection(const ModelObject* mo, const Transform3d& cut_matrix, int instance_idx, const Vec3d& center, const Vec3d& normal); - void render(const Vec3d* normal = nullptr); - void toggle_selection(const Vec2d& mouse_pos); - void turn_over_selection(); - ModelObject* model_object() { return model.objects.front(); } - struct Part { GLModel glmodel; MeshRaycaster raycaster; bool selected; }; - Model model; - int instance_idx; - std::vector parts; - bool valid = false; + void render(const Vec3d* normal = nullptr); + void toggle_selection(const Vec2d& mouse_pos); + void turn_over_selection(); + ModelObject* model_object() { return m_model.objects.front(); } + bool valid() const { return m_valid; } + const std::vector& parts() const { return m_parts; } + + + private: + Model m_model; + int m_instance_idx; + std::vector m_parts; + bool m_valid = false; }; PartSelection m_part_selection; From b9bba5ff6b11e1f84df6e9d540536f50d5b6144c Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 6 Apr 2023 14:16:58 +0200 Subject: [PATCH 68/90] Cut: optimization - do not check invalid connectors in each frame --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 39 ++++++++++++++++++++++------ src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 3 ++- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 50e446292b..e2e9ec72af 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -459,6 +459,7 @@ void GLGizmoCut3D::update_clipper() void GLGizmoCut3D::set_center(const Vec3d& center, bool update_tbb /*=false*/) { set_center_pos(center, update_tbb); + check_and_update_connectors_state(); update_clipper(); } @@ -1199,6 +1200,7 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data) dragging_grabber_xy(data); else if (m_hover_id >= m_connectors_group_id && m_connector_mode == CutConnectorMode::Manual) dragging_connector(data); + check_and_update_connectors_state(); } void GLGizmoCut3D::on_start_dragging() @@ -1224,6 +1226,7 @@ void GLGizmoCut3D::on_stop_dragging() Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Move cut plane"), UndoRedo::SnapshotType::GizmoAction); m_ar_plane_center = m_plane_center; } + //check_and_update_connectors_state(); } void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos, bool update_tbb /*=false*/) @@ -1586,7 +1589,7 @@ void GLGizmoCut3D::apply_selected_connectors(std::function app for (size_t idx = 0; idx < m_selected.size(); idx++) if (m_selected[idx]) apply_fn(idx); - + check_and_update_connectors_state(); update_raycasters_for_picking_transform(); } @@ -2083,7 +2086,7 @@ void GLGizmoCut3D::render_input_window_warning() const { if (m_is_contour_changed) return; - if (m_has_invalid_connector) { + if (! m_invalid_connectors_idxs.empty()) { wxString out = wxString(ImGui::WarningMarkerSmall) + _L("Invalid connectors detected") + ":"; if (m_info_stats.outside_cut_contour > size_t(0)) out += "\n - " + format_wxstr(_L_PLURAL("%1$d connector is out of cut contour", "%1$d connectors are out of cut contour", m_info_stats.outside_cut_contour), @@ -2195,6 +2198,26 @@ bool GLGizmoCut3D::is_conflict_for_connector(size_t idx, const CutConnectors& co return false; } +void GLGizmoCut3D::check_and_update_connectors_state() +{ + m_invalid_connectors_idxs.clear(); + const ModelObject* mo = m_c->selection_info()->model_object(); + auto inst_id = m_c->selection_info()->get_active_instance(); + if (inst_id < 0) + return; + const CutConnectors& connectors = mo->cut_connectors; + const ModelInstance* mi = mo->instances[inst_id]; + const Vec3d& instance_offset = mi->get_offset(); + const double sla_shift = double(m_c->selection_info()->get_sla_shift()); + + for (size_t i = 0; i < connectors.size(); ++i) { + const CutConnector& connector = connectors[i]; + Vec3d pos = connector.pos + instance_offset + sla_shift * Vec3d::UnitZ(); // recalculate connector position to world position + if (is_conflict_for_connector(i, connectors, pos)) + m_invalid_connectors_idxs.emplace_back(i); + } +} + void GLGizmoCut3D::render_connectors() { ::glEnable(GL_DEPTH_TEST); @@ -2220,7 +2243,6 @@ void GLGizmoCut3D::render_connectors() const Vec3d& instance_offset = mi->get_offset(); const double sla_shift = double(m_c->selection_info()->get_sla_shift()); - m_has_invalid_connector = false; m_info_stats.invalidate(); const bool looking_forward = is_looking_forward(); @@ -2233,11 +2255,10 @@ void GLGizmoCut3D::render_connectors() Vec3d pos = connector.pos + instance_offset + sla_shift * Vec3d::UnitZ(); // First decide about the color of the point. - const bool conflict_connector = is_conflict_for_connector(i, connectors, pos); - if (conflict_connector) { - m_has_invalid_connector = true; + assert(std::is_sorted(m_invalid_connectors_idxs.begin(), m_invalid_connectors_idxs.end())); + const bool conflict_connector = std::binary_search(m_invalid_connectors_idxs.begin(), m_invalid_connectors_idxs.end(), i); + if (conflict_connector) render_color = CONNECTOR_ERR_COLOR; - } else // default connector color render_color = connector.attribs.type == CutConnectorType::Dowel ? DOWEL_COLOR : PLAG_COLOR; @@ -2277,7 +2298,7 @@ void GLGizmoCut3D::render_connectors() bool GLGizmoCut3D::can_perform_cut() const { - if (m_has_invalid_connector || (!m_keep_upper && !m_keep_lower) || m_connectors_editing) + if (! m_invalid_connectors_idxs.empty() || (!m_keep_upper && !m_keep_lower) || m_connectors_editing) return false; return true;// has_valid_contour(); @@ -2640,6 +2661,7 @@ bool GLGizmoCut3D::add_connector(CutConnectors& connectors, const Vec2d& mouse_p assert(m_selected.size() == connectors.size()); update_raycasters_for_picking(); m_parent.set_as_dirty(); + check_and_update_connectors_state(); return true; } @@ -2665,6 +2687,7 @@ bool GLGizmoCut3D::delete_selected_connectors(CutConnectors& connectors) assert(m_selected.size() == connectors.size()); update_raycasters_for_picking(); m_parent.set_as_dirty(); + check_and_update_connectors_state(); return true; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 255d1a568f..4108690a63 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -133,7 +133,7 @@ class GLGizmoCut3D : public GLGizmoBase GLSelectionRectangle m_selection_rectangle; - bool m_has_invalid_connector{ false }; + std::vector m_invalid_connectors_idxs; bool m_was_cut_plane_dragged { false }; bool m_was_contour_selected { false }; @@ -328,6 +328,7 @@ private: void update_connector_shape(); void validate_connector_settings(); bool process_cut_line(SLAGizmoEventType action, const Vec2d& mouse_position); + void check_and_update_connectors_state(); }; } // namespace GUI From d0ee5e7ca339773f20920cf2e7fab480fe4dbf38 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 20 Apr 2023 11:15:15 +0200 Subject: [PATCH 69/90] Cut: ObjectClipper now allows to not render all the contours --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 26 ++++++++++++-- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 4 ++- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 44 +++++++++++++++++++----- src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 3 +- src/slic3r/GUI/MeshUtils.cpp | 28 ++++++++++++--- src/slic3r/GUI/MeshUtils.hpp | 5 +-- 6 files changed, 92 insertions(+), 18 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index e2e9ec72af..1975d6ebae 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -1467,6 +1467,19 @@ void GLGizmoCut3D::on_render() m_c->selection_info()->set_use_shift(true); } + + ::glDisable(GL_DEPTH_TEST); + std::vector pts = m_c->object_clipper()->point_per_contour(); + if (! pts.empty()) { + const Vec3d dir = (m_plane_center-pts.front()).dot(m_cut_normal) * m_cut_normal; + for (const Vec3d& pt : pts) + render_model(m_sphere.model, ColorRGBA::GREEN(), wxGetApp().plater()->get_camera().get_view_matrix() * translation_transform(pt+dir)); + } + ::glEnable(GL_DEPTH_TEST); + + + + update_clipper(); init_picking_models(); @@ -1733,7 +1746,7 @@ void GLGizmoCut3D::flip_cut_plane() } -GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* mo, const Transform3d& cut_matrix, int instance_idx_in, const Vec3d& center, const Vec3d& normal) +GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* mo, const Transform3d& cut_matrix, int instance_idx_in, const Vec3d& center, const Vec3d& normal, const CommonGizmosDataObjects::ObjectClipper& oc) { m_model = Model(); m_model.add_object(*mo); @@ -1773,6 +1786,15 @@ GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* mo, const Transfor } } } + + // Now go through the contours and create a map from contours to parts. + if (std::vector pts = oc.point_per_contour();! pts.empty()) { + const Vec3d dir = (center-pts.front()).dot(normal) * normal; + for (Vec3d& pt : pts) + pt = pt+dir; + // pts are now in world coordinates. + } + m_valid = true; } @@ -1798,7 +1820,7 @@ void GLGizmoCut3D::process_contours() const int instance_idx = selection.get_instance_idx(); const int object_idx = selection.get_object_idx(); - m_part_selection = PartSelection(model_objects[object_idx], get_cut_matrix(selection), instance_idx, m_plane_center, m_cut_normal); + m_part_selection = PartSelection(model_objects[object_idx], get_cut_matrix(selection), instance_idx, m_plane_center, m_cut_normal, *m_c->object_clipper()); m_parent.toggle_model_objects_visibility(false); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 4108690a63..b765ce14b2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -20,6 +20,8 @@ class Selection; enum class SLAGizmoEventType : unsigned char; +namespace CommonGizmosDataObjects { class ObjectClipper; } + class GLGizmoCut3D : public GLGizmoBase { enum GrabberID { @@ -140,7 +142,7 @@ class GLGizmoCut3D : public GLGizmoBase class PartSelection { public: PartSelection() = default; - PartSelection(const ModelObject* mo, const Transform3d& cut_matrix, int instance_idx, const Vec3d& center, const Vec3d& normal); + PartSelection(const ModelObject* mo, const Transform3d& cut_matrix, int instance_idx, const Vec3d& center, const Vec3d& normal, const CommonGizmosDataObjects::ObjectClipper& oc); struct Part { GLModel glmodel; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 5495406102..3d7b77a50d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -370,21 +370,30 @@ void ObjectClipper::on_release() } -void ObjectClipper::render_cut() const +void ObjectClipper::render_cut(const std::vector* ignore_idxs) const { if (m_clp_ratio == 0.) return; const SelectionInfo* sel_info = get_pool()->selection_info(); const Geometry::Transformation inst_trafo = sel_info->model_object()->instances[sel_info->get_active_instance()]->get_transformation(); + + std::vector ignore_idxs_local = ignore_idxs ? *ignore_idxs : std::vector(); for (auto& clipper : m_clippers) { - Geometry::Transformation trafo = inst_trafo * clipper.second; - trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift())); - clipper.first->set_plane(*m_clp); - clipper.first->set_transformation(trafo); - clipper.first->set_limiting_plane(ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD)); - clipper.first->render_cut({ 1.0f, 0.37f, 0.0f, 1.0f }); - clipper.first->render_contour({ 1.f, 1.f, 1.f, 1.f }); + Geometry::Transformation trafo = inst_trafo * clipper.second; + trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift())); + clipper.first->set_plane(*m_clp); + clipper.first->set_transformation(trafo); + clipper.first->set_limiting_plane(ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD)); + clipper.first->render_cut({ 1.0f, 0.37f, 0.0f, 1.0f }, &ignore_idxs_local); + clipper.first->render_contour({ 1.f, 1.f, 1.f, 1.f }, &ignore_idxs_local); + + // Now update the ignore idxs. Find the first element belonging to the next clipper, + // and remove everything before it and decrement everything by current number of contours. + const int num_of_contours = clipper.first->get_number_of_contours(); + ignore_idxs_local.erase(ignore_idxs_local.begin(), std::find_if(ignore_idxs_local.begin(), ignore_idxs_local.end(), [num_of_contours](size_t idx) { return idx >= num_of_contours; } )); + for (size_t& idx : ignore_idxs_local) + idx -= num_of_contours; } } @@ -415,6 +424,25 @@ bool ObjectClipper::has_valid_contour() const return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [](const auto& cl) { return cl.first->has_valid_contour(); }); } +std::vector ObjectClipper::point_per_contour() const +{ + std::vector pts; + + const SelectionInfo* sel_info = get_pool()->selection_info(); + const Geometry::Transformation inst_trafo = sel_info->model_object()->instances[sel_info->get_active_instance()]->get_transformation(); + + for (auto& clipper : m_clippers) { + Geometry::Transformation trafo = inst_trafo * clipper.second; + trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift())); + + // FIXME: do not assume just one clipper + pts = clipper.first->point_per_contour(); + //for (Vec3d& v : pts) + // v = trafo.get_matrix() * v; + } + return pts; +} + void ObjectClipper::set_position_by_ratio(double pos, bool keep_normal) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index 4152cce513..e9d85de8d8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -242,12 +242,13 @@ public: void set_normal(const Vec3d& dir); double get_position() const { return m_clp_ratio; } const ClippingPlane* get_clipping_plane(bool ignore_hide_clipped = false) const; - void render_cut() const; + void render_cut(const std::vector* ignore_idxs = nullptr) const; void set_position_by_ratio(double pos, bool keep_normal); void set_range_and_pos(const Vec3d& cpl_normal, double cpl_offset, double pos); void set_behavior(bool hide_clipped, bool fill_cut, double contour_width); int get_number_of_contours() const; + std::vector point_per_contour() const; int is_projection_inside_cut(const Vec3d& point_in) const; bool has_valid_contour() const; diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index cee6841151..8e29a1c521 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -94,7 +94,7 @@ void MeshClipper::set_transformation(const Geometry::Transformation& trafo) } } -void MeshClipper::render_cut(const ColorRGBA& color) +void MeshClipper::render_cut(const ColorRGBA& color, const std::vector* ignore_idxs) { if (! m_result) recalculate_triangles(); @@ -108,7 +108,10 @@ void MeshClipper::render_cut(const ColorRGBA& color) const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("view_model_matrix", camera.get_view_matrix()); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - for (CutIsland& isl : m_result->cut_islands) { + for (size_t i=0; icut_islands.size(); ++i) { + if (ignore_idxs && std::binary_search(ignore_idxs->begin(), ignore_idxs->end(), i)) + continue; + CutIsland& isl = m_result->cut_islands[i]; isl.model.set_color(isl.disabled ? ColorRGBA(0.5f, 0.5f, 0.5f, 1.f) : color); isl.model.render(); } @@ -120,7 +123,7 @@ void MeshClipper::render_cut(const ColorRGBA& color) } -void MeshClipper::render_contour(const ColorRGBA& color) +void MeshClipper::render_contour(const ColorRGBA& color, const std::vector* ignore_idxs) { if (! m_result) recalculate_triangles(); @@ -135,7 +138,10 @@ void MeshClipper::render_contour(const ColorRGBA& color) const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("view_model_matrix", camera.get_view_matrix()); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - for (CutIsland& isl : m_result->cut_islands) { + for (size_t i=0; icut_islands.size(); ++i) { + if (ignore_idxs && std::binary_search(ignore_idxs->begin(), ignore_idxs->end(), i)) + continue; + CutIsland& isl = m_result->cut_islands[i]; isl.model_expanded.set_color(isl.disabled ? ColorRGBA(1.f, 0.f, 0.f, 1.f) : color); isl.model_expanded.render(); } @@ -166,6 +172,20 @@ bool MeshClipper::has_valid_contour() const return m_result && std::any_of(m_result->cut_islands.begin(), m_result->cut_islands.end(), [](const CutIsland& isl) { return !isl.expoly.empty(); }); } +std::vector MeshClipper::point_per_contour() const +{ + std::vector out; + if (!m_result || m_result->cut_islands.empty()) + return out; + + for (const CutIsland& isl : m_result->cut_islands) { + // FIXME: There might be holes ! + Vec2d c = unscale(isl.expoly.contour.centroid()); + out.emplace_back(m_result->trafo * Vec3d(c.x(), c.y(), 0.)); + } + return out; +} + void MeshClipper::recalculate_triangles() { diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index 257b500f9f..be95daa156 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -115,13 +115,14 @@ public: // Render the triangulated cut. Transformation matrices should // be set in world coords. - void render_cut(const ColorRGBA& color); - void render_contour(const ColorRGBA& color); + void render_cut(const ColorRGBA& color, const std::vector* ignore_idxs = nullptr); + void render_contour(const ColorRGBA& color, const std::vector* ignore_idxs = nullptr); // Returns index of the contour which was clicked, -1 otherwise. int is_projection_inside_cut(const Vec3d& point) const; bool has_valid_contour() const; int get_number_of_contours() const { return m_result ? m_result->cut_islands.size() : 0; } + std::vector point_per_contour() const; private: void recalculate_triangles(); From 44aa32d6e2613d38241bd2a961ee4e95fd1d7fa2 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 26 Apr 2023 13:18:12 +0200 Subject: [PATCH 70/90] Cut: correspondence between parts and contours --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 244 ++++++++++++++++------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 10 +- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 14 +- 3 files changed, 179 insertions(+), 89 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 1975d6ebae..897414b953 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -1390,19 +1390,110 @@ void GLGizmoCut3D::render_clipper_cut() { if (! m_connectors_editing) ::glDisable(GL_DEPTH_TEST); - m_c->object_clipper()->render_cut(); + + GLboolean cull_face = GL_FALSE; + ::glGetBooleanv(GL_CULL_FACE, &cull_face); + ::glDisable(GL_CULL_FACE); + m_c->object_clipper()->render_cut(m_part_selection.get_ignored_contours_ptr()); + if (cull_face) + ::glEnable(GL_CULL_FACE); + if (! m_connectors_editing) ::glEnable(GL_DEPTH_TEST); } -void GLGizmoCut3D::PartSelection::render(const Vec3d* normal) + +GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* mo, const Transform3d& cut_matrix, int instance_idx_in, const Vec3d& center, const Vec3d& normal, const CommonGizmosDataObjects::ObjectClipper& oc) +{ + m_model = Model(); + m_model.add_object(*mo); + ModelObjectPtrs cut_part_ptrs = m_model.objects.front()->cut(instance_idx_in, cut_matrix, + ModelObjectCutAttribute::KeepUpper | + ModelObjectCutAttribute::KeepLower | + ModelObjectCutAttribute::KeepAsParts); + assert(cut_part_ptrs.size() == 1); + m_model = Model(); + m_model.add_object(*cut_part_ptrs.front()); + + m_instance_idx = instance_idx_in; + + const ModelVolumePtrs& volumes = model_object()->volumes; + + // split to parts + for (int id = int(volumes.size())-1; id >= 0; id--) + if (volumes[id]->is_splittable()) + volumes[id]->split(1); + + m_parts.clear(); + for (const ModelVolume* volume : volumes) { + assert(volume != nullptr); + m_parts.emplace_back(Part{GLModel(), MeshRaycaster(volume->mesh()), true}); + m_parts.back().glmodel.set_color({ 0.f, 0.f, 1.f, 1.f }); + m_parts.back().glmodel.init_from(volume->mesh()); + + // Now check whether this part is below or above the plane. + Transform3d tr = (model_object()->instances[m_instance_idx]->get_matrix() * volume->get_matrix()).inverse(); + Vec3f pos = (tr * center).cast(); + Vec3f norm = (tr.linear().inverse().transpose() * normal).cast(); + for (const Vec3f& v : volume->mesh().its.vertices) { + double p = (v - pos).dot(norm); + if (std::abs(p) > EPSILON) { + m_parts.back().selected = p > 0.; + break; + } + } + } + + // Now go through the contours and create a map from contours to parts. + m_contour_points.clear(); + m_contour_to_parts.clear(); + m_debug_pts = std::vector>(m_parts.size(), std::vector()); + if (std::vector pts = oc.point_per_contour();! pts.empty()) { + + m_contour_to_parts.resize(pts.size()); + + for (size_t pt_idx=0; pt_idxinstances[m_instance_idx]->get_offset()) * translation_transform(model_object()->volumes[part_id]->get_offset())).inverse(); + for (double d : {-1., 1.}) { + const Vec3d dir_mesh = d * tr.linear().inverse().transpose() * normal; + const Vec3d src = tr * (m_contour_points[pt_idx] + d*0.01 * normal); + AABBMesh::hit_result hit = aabb.query_ray_hit(src, dir_mesh); + + m_debug_pts[part_id].emplace_back(src); + + if (hit.is_inside()) { + // This part belongs to this point. + if (d == 1.) + m_contour_to_parts[pt_idx].first.emplace_back(part_id); + else + m_contour_to_parts[pt_idx].second.emplace_back(part_id); + } + } + } + } + + } + + + m_valid = true; +} + +void GLGizmoCut3D::PartSelection::render(const Vec3d* normal, GLModel& sphere_model) { if (! valid()) return; - if (GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light")) { - const Camera& camera = wxGetApp().plater()->get_camera(); + const Camera& camera = wxGetApp().plater()->get_camera(); + if (GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light")) { shader->start_using(); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("emission_factor", 0.f); @@ -1426,8 +1517,52 @@ void GLGizmoCut3D::PartSelection::render(const Vec3d* normal) shader->stop_using(); } + + + + // { // Debugging render: + + // static int idx = -1; + // ImGui::Begin("DEBUG"); + // for (int i=0; i= m_parts.size()) + // idx = -1; + // ImGui::End(); + + // ::glDisable(GL_DEPTH_TEST); + // if (valid()) { + // for (size_t i=0; ivolumes[id]->get_offset(); - Transform3d tr = model_object()->instances[m_instance_idx]->get_matrix() * model_object()->volumes[id]->get_matrix(); + Transform3d tr = translation_transform(model_object()->instances[m_instance_idx]->get_offset()) * translation_transform(model_object()->volumes[id]->get_offset()); if (m_parts[id].raycaster.unproject_on_mesh(mouse_pos, tr, camera, pos, normal)) { hits_id_and_sqdist.emplace_back(id, (camera_pos - tr*(pos.cast())).squaredNorm()); } @@ -1450,6 +1585,20 @@ void GLGizmoCut3D::PartSelection::toggle_selection(const Vec2d& mouse_pos) size_t id = std::min_element(hits_id_and_sqdist.begin(), hits_id_and_sqdist.end(), [](const std::pair& a, const std::pair& b) { return a.second < b.second; })->first; m_parts[id].selected = ! m_parts[id].selected; + + // And now recalculate the contours which should be ignored. + m_ignored_contours.clear(); + size_t cont_id = 0; + for (const auto& [parts_above, parts_below] : m_contour_to_parts) { + for (size_t upper : parts_above) { + bool upper_sel = m_parts[upper].selected; + if (std::find_if(parts_below.begin(), parts_below.end(), [this, &upper_sel](const size_t& i) { return m_parts[i].selected == upper_sel; }) != parts_below.end()) { + m_ignored_contours.emplace_back(cont_id); + break; + } + } + ++cont_id; + } } } @@ -1468,18 +1617,6 @@ void GLGizmoCut3D::on_render() } - ::glDisable(GL_DEPTH_TEST); - std::vector pts = m_c->object_clipper()->point_per_contour(); - if (! pts.empty()) { - const Vec3d dir = (m_plane_center-pts.front()).dot(m_cut_normal) * m_cut_normal; - for (const Vec3d& pt : pts) - render_model(m_sphere.model, ColorRGBA::GREEN(), wxGetApp().plater()->get_camera().get_view_matrix() * translation_transform(pt+dir)); - } - ::glEnable(GL_DEPTH_TEST); - - - - update_clipper(); init_picking_models(); @@ -1489,9 +1626,9 @@ void GLGizmoCut3D::on_render() render_connectors(); if (!m_connectors_editing) - m_part_selection.render(); + m_part_selection.render(nullptr, m_sphere.model); else - m_part_selection.render(&m_cut_normal); + m_part_selection.render(&m_cut_normal, m_sphere.model); render_clipper_cut(); @@ -1745,61 +1882,6 @@ void GLGizmoCut3D::flip_cut_plane() m_part_selection.turn_over_selection(); } - -GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* mo, const Transform3d& cut_matrix, int instance_idx_in, const Vec3d& center, const Vec3d& normal, const CommonGizmosDataObjects::ObjectClipper& oc) -{ - m_model = Model(); - m_model.add_object(*mo); - ModelObjectPtrs cut_part_ptrs = m_model.objects.front()->cut(instance_idx_in, cut_matrix, - ModelObjectCutAttribute::KeepUpper | - ModelObjectCutAttribute::KeepLower | - ModelObjectCutAttribute::KeepAsParts); - assert(cut_part_ptrs.size() == 1); - m_model = Model(); - m_model.add_object(*cut_part_ptrs.front()); - - m_instance_idx = instance_idx_in; - - const ModelVolumePtrs& volumes = model_object()->volumes; - - // split to parts - for (int id = int(volumes.size())-1; id >= 0; id--) - if (volumes[id]->is_splittable()) - volumes[id]->split(1); - - m_parts.clear(); - for (const ModelVolume* volume : volumes) { - assert(volume != nullptr); - m_parts.emplace_back(Part{GLModel(), MeshRaycaster(volume->mesh()), true}); - m_parts.back().glmodel.set_color({ 0.f, 0.f, 1.f, 1.f }); - m_parts.back().glmodel.init_from(volume->mesh()); - - // Now check whether this part is below or above the plane. - Transform3d tr = (model_object()->instances[m_instance_idx]->get_matrix() * volume->get_matrix()).inverse(); - Vec3f pos = (tr * center).cast(); - Vec3f norm = (tr.linear().inverse().transpose() * normal).cast(); - for (const Vec3f& v : volume->mesh().its.vertices) { - double p = (v - pos).dot(norm); - if (std::abs(p) > EPSILON) { - m_parts.back().selected = p > 0.; - break; - } - } - } - - // Now go through the contours and create a map from contours to parts. - if (std::vector pts = oc.point_per_contour();! pts.empty()) { - const Vec3d dir = (center-pts.front()).dot(normal) * normal; - for (Vec3d& pt : pts) - pt = pt+dir; - // pts are now in world coordinates. - } - - - m_valid = true; -} - - void GLGizmoCut3D::reset_cut_by_contours() { m_part_selection = PartSelection(); @@ -2542,8 +2624,18 @@ bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, Vec3d& po } }*/ - if (m_c->object_clipper()->is_projection_inside_cut(hit) == -1) - return false; + { + // Do not react to clicks outside a contour (or inside a contour that is ignored) + int cont_id = m_c->object_clipper()->is_projection_inside_cut(hit); + if (cont_id == -1) + return false; + if (m_part_selection.valid()) { + const std::vector& ign = *m_part_selection.get_ignored_contours_ptr(); + if (std::find(ign.begin(), ign.end(), cont_id) != ign.end()) + return false; + } + } + // recalculate hit to object's local position Vec3d hit_d = hit; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index b765ce14b2..0f300b0fad 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -150,12 +150,18 @@ class GLGizmoCut3D : public GLGizmoBase bool selected; }; - void render(const Vec3d* normal = nullptr); + void render(const Vec3d* normal, GLModel& sphere_model); void toggle_selection(const Vec2d& mouse_pos); void turn_over_selection(); ModelObject* model_object() { return m_model.objects.front(); } bool valid() const { return m_valid; } const std::vector& parts() const { return m_parts; } + const std::vector* get_ignored_contours_ptr() const { return (valid() ? &m_ignored_contours : nullptr); } + + std::vector m_contour_points; // TEMPORARILY PUBLIC + std::vector, std::vector>> m_contour_to_parts; // for each contour, there is a vector of parts above and a vector of parts below + std::vector m_ignored_contours; // contour that should not be rendered (the parts on both sides will both be parts of the same object) + std::vector> m_debug_pts; private: @@ -312,7 +318,7 @@ private: void discard_cut_line_processing(); void render_cut_plane(); - void render_model(GLModel& model, const ColorRGBA& color, Transform3d view_model_matrix); + static void render_model(GLModel& model, const ColorRGBA& color, Transform3d view_model_matrix); void render_line(GLModel& line_model, const ColorRGBA& color, Transform3d view_model_matrix, float width); void render_rotation_snapping(GrabberID axis, const ColorRGBA& color); void render_grabber_connection(const ColorRGBA& color, Transform3d view_matrix); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 3d7b77a50d..79c77692a0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -428,17 +428,9 @@ std::vector ObjectClipper::point_per_contour() const { std::vector pts; - const SelectionInfo* sel_info = get_pool()->selection_info(); - const Geometry::Transformation inst_trafo = sel_info->model_object()->instances[sel_info->get_active_instance()]->get_transformation(); - - for (auto& clipper : m_clippers) { - Geometry::Transformation trafo = inst_trafo * clipper.second; - trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift())); - - // FIXME: do not assume just one clipper - pts = clipper.first->point_per_contour(); - //for (Vec3d& v : pts) - // v = trafo.get_matrix() * v; + for (const auto& clipper : m_clippers) { + const std::vector pts_clipper = clipper.first->point_per_contour(); + pts.insert(pts.end(), pts_clipper.begin(), pts_clipper.end());; } return pts; } From ab0ade05395e2c73aeb2d809edd6ae8f164010a3 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 27 Apr 2023 11:14:38 +0200 Subject: [PATCH 71/90] Cut: contours indexing no longer breaks when the normal is inverted, detection of the situation when all parts are assigned to one side --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 19 ++++++++++- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 12 +++---- src/slic3r/GUI/MeshUtils.cpp | 48 +++++++++++++++++++++++++--- src/slic3r/GUI/MeshUtils.hpp | 1 + 4 files changed, 68 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 897414b953..dc068e7cb4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -1563,6 +1563,21 @@ void GLGizmoCut3D::PartSelection::render(const Vec3d* normal, GLModel& sphere_mo } +bool GLGizmoCut3D::PartSelection::is_one_object() const +{ + // In theory, the implementation could be just this: + // return m_contour_to_parts.size() == m_ignored_contours.size(); + // However, this would require that the part-contour correspondence works + // flawlessly. Because it is currently not always so for self-intersecting + // objects, let's better check the parts itself: + if (m_parts.size() < 2) + return true; + return std::all_of(m_parts.begin(), m_parts.end(), [this](const Part& part) { + return part.selected == m_parts.front().selected; + }); +} + + void GLGizmoCut3D::PartSelection::toggle_selection(const Vec2d& mouse_pos) { // FIXME: Cache the transforms. @@ -1984,7 +1999,7 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) add_vertical_scaled_interval(0.75f); - m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower || m_keep_as_parts); + m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower || m_keep_as_parts || (m_part_selection.valid() && m_part_selection.is_one_object())); if (m_imgui->button(has_connectors ? _L("Edit connectors") : _L("Add connectors"))) set_connectors_editing(true); m_imgui->disabled_end(); @@ -2404,6 +2419,8 @@ bool GLGizmoCut3D::can_perform_cut() const { if (! m_invalid_connectors_idxs.empty() || (!m_keep_upper && !m_keep_lower) || m_connectors_editing) return false; + if (m_part_selection.valid()) + return ! m_part_selection.is_one_object(); return true;// has_valid_contour(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 0f300b0fad..02f62d6283 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -155,20 +155,20 @@ class GLGizmoCut3D : public GLGizmoBase void turn_over_selection(); ModelObject* model_object() { return m_model.objects.front(); } bool valid() const { return m_valid; } + bool is_one_object() const; const std::vector& parts() const { return m_parts; } const std::vector* get_ignored_contours_ptr() const { return (valid() ? &m_ignored_contours : nullptr); } - std::vector m_contour_points; // TEMPORARILY PUBLIC - std::vector, std::vector>> m_contour_to_parts; // for each contour, there is a vector of parts above and a vector of parts below - std::vector m_ignored_contours; // contour that should not be rendered (the parts on both sides will both be parts of the same object) - std::vector> m_debug_pts; - - private: Model m_model; int m_instance_idx; std::vector m_parts; bool m_valid = false; + std::vector, std::vector>> m_contour_to_parts; // for each contour, there is a vector of parts above and a vector of parts below + std::vector m_ignored_contours; // contour that should not be rendered (the parts on both sides will both be parts of the same object) + + std::vector m_contour_points; // Debugging + std::vector> m_debug_pts; // Debugging }; PartSelection m_part_selection; diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 8e29a1c521..663712ca41 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -174,13 +174,40 @@ bool MeshClipper::has_valid_contour() const std::vector MeshClipper::point_per_contour() const { + assert(m_result); std::vector out; - if (!m_result || m_result->cut_islands.empty()) - return out; - + for (const CutIsland& isl : m_result->cut_islands) { - // FIXME: There might be holes ! - Vec2d c = unscale(isl.expoly.contour.centroid()); + assert(isl.expoly.contour.size() > 2); + // Now return a point lying inside the contour but not in a hole. + // We do this by taking a point lying close to the edge, repeating + // this several times for different edges and distances from them. + // (We prefer point not extremely close to the border. + bool done = false; + Vec2d p; + size_t i = 1; + while (i < isl.expoly.contour.size()) { + const Vec2d& a = unscale(isl.expoly.contour.points[i-1]); + const Vec2d& b = unscale(isl.expoly.contour.points[i]); + Vec2d n = (b-a).normalized(); + std::swap(n.x(), n.y()); + n.x() = -1 * n.x(); + double f = 10.; + while (f > 0.05) { + p = (0.5*(b+a)) + f * n; + if (isl.expoly.contains(Point::new_scale(p))) { + done = true; + break; + } + f = f/10.; + } + if (done) + break; + i += isl.expoly.contour.size() / 5; + } + // If the above failed, just return the centroid, regardless of whether + // it is inside the contour or in a hole (we must return something). + Vec2d c = done ? p : unscale(isl.expoly.contour.centroid()); out.emplace_back(m_result->trafo * Vec3d(c.x(), c.y(), 0.)); } return out; @@ -366,7 +393,18 @@ void MeshClipper::recalculate_triangles() isl.expoly = std::move(exp); isl.expoly_bb = get_extents(exp); + isl.hash = 0; + for (const Point& pt : isl.expoly.contour) { + isl.hash ^= pt.x(); + isl.hash ^= pt.y(); + } } + + // Now sort the islands so they are in defined order. This is a hack needed by cut gizmo, which sometimes + // flips the normal of the cut, in which case the contours stay the same but their order may change. + std::sort(m_result->cut_islands.begin(), m_result->cut_islands.end(), [](const CutIsland& a, const CutIsland& b) { + return a.hash < b.hash; + }); } diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index be95daa156..e5fa97e39c 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -141,6 +141,7 @@ private: ExPolygon expoly; BoundingBox expoly_bb; bool disabled = false; + coord_t hash; }; struct ClipResult { std::vector cut_islands; From 0e1d43322caa4aa684513302848201030ea182b7 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 27 Apr 2023 12:15:19 +0200 Subject: [PATCH 72/90] Cut: Deallocate memory when the gizmo is turned off --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 7 +++++++ src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 1 + 2 files changed, 8 insertions(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index dc068e7cb4..5aa4896df7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -905,6 +905,13 @@ void GLGizmoCut3D::on_set_state() m_selected.clear(); m_parent.set_use_color_clip_plane(false); m_c->selection_info()->set_use_shift(false); + + // Make sure that the part selection data are released when the gizmo is closed. + // The CallAfter is needed because in perform_cut, the gizmo is closed BEFORE + // the cut is performed (because of undo/redo snapshots), so the data would + // be deleted prematurely. + if (m_part_selection.valid()) + wxGetApp().CallAfter([this]() { m_part_selection = PartSelection(); }); } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 02f62d6283..627f6c133e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -143,6 +143,7 @@ class GLGizmoCut3D : public GLGizmoBase public: PartSelection() = default; PartSelection(const ModelObject* mo, const Transform3d& cut_matrix, int instance_idx, const Vec3d& center, const Vec3d& normal, const CommonGizmosDataObjects::ObjectClipper& oc); + ~PartSelection() { m_model.clear_objects(); } struct Part { GLModel glmodel; From 7b576d33127c6f82299eba6050164307ac14d4d4 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 3 May 2023 13:22:18 +0200 Subject: [PATCH 73/90] Cut: - fixed incomplete message about invalid connectors - added detection of a connectors placed on an internal contour - fixed a hang during part selection calculation when a contour was too small --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 19 ++++++++++++++----- src/slic3r/GUI/MeshUtils.cpp | 2 +- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 5aa4896df7..1179830a00 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -356,6 +356,7 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) if (! m_part_selection.valid()) process_contours(); m_part_selection.toggle_selection(mouse_pos); + check_and_update_connectors_state(); // after a contour is deactivated, its connectors are inside the object return true; } @@ -2283,9 +2284,18 @@ bool GLGizmoCut3D::is_outside_of_cut_contour(size_t idx, const CutConnectors& co its_transform(mesh, translation_transform(cur_pos) * m_rotation_m); for (const Vec3f& vertex : vertices) { - if (m_c->object_clipper() && m_c->object_clipper()->is_projection_inside_cut(vertex.cast()) == -1) { - m_info_stats.outside_cut_contour++; - return true; + if (m_c->object_clipper()) { + int contour_idx = m_c->object_clipper()->is_projection_inside_cut(vertex.cast()); + bool is_invalid = (contour_idx == -1); + if (m_part_selection.valid() && ! is_invalid) { + assert(contour_idx >= 0); + const std::vector& ignored = *(m_part_selection.get_ignored_contours_ptr()); + is_invalid = (std::find(ignored.begin(), ignored.end(), size_t(contour_idx)) != ignored.end()); + } + if (is_invalid) { + m_info_stats.outside_cut_contour++; + return true; + } } } @@ -2326,6 +2336,7 @@ bool GLGizmoCut3D::is_conflict_for_connector(size_t idx, const CutConnectors& co void GLGizmoCut3D::check_and_update_connectors_state() { + m_info_stats.invalidate(); m_invalid_connectors_idxs.clear(); const ModelObject* mo = m_c->selection_info()->model_object(); auto inst_id = m_c->selection_info()->get_active_instance(); @@ -2369,8 +2380,6 @@ void GLGizmoCut3D::render_connectors() const Vec3d& instance_offset = mi->get_offset(); const double sla_shift = double(m_c->selection_info()->get_sla_shift()); - m_info_stats.invalidate(); - const bool looking_forward = is_looking_forward(); for (size_t i = 0; i < connectors.size(); ++i) { diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 663712ca41..99a241c484 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -203,7 +203,7 @@ std::vector MeshClipper::point_per_contour() const } if (done) break; - i += isl.expoly.contour.size() / 5; + i += std::max(size_t(2), isl.expoly.contour.size() / 5); } // If the above failed, just return the centroid, regardless of whether // it is inside the contour or in a hole (we must return something). From 2aa55ef950a5372d45a68a32b34d1d8e366dd945 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 3 May 2023 16:14:26 +0200 Subject: [PATCH 74/90] Cut: fixed an issue when the view rotates to the other side of the plane --- src/slic3r/GUI/MeshUtils.cpp | 11 +++++------ src/slic3r/GUI/MeshUtils.hpp | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 99a241c484..60ea6c856d 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -392,12 +392,11 @@ void MeshClipper::recalculate_triangles() } isl.expoly = std::move(exp); - isl.expoly_bb = get_extents(exp); - isl.hash = 0; - for (const Point& pt : isl.expoly.contour) { - isl.hash ^= pt.x(); - isl.hash ^= pt.y(); - } + isl.expoly_bb = get_extents(isl.expoly); + + Point centroid_scaled = isl.expoly.contour.centroid(); + Vec3d centroid_world = m_result->trafo * Vec3d(unscale(centroid_scaled).x(), unscale(centroid_scaled).y(), 0.); + isl.hash = isl.expoly.contour.size() + size_t(std::abs(100.*centroid_world.x())) + size_t(std::abs(100.*centroid_world.y())) + size_t(std::abs(100.*centroid_world.z())); } // Now sort the islands so they are in defined order. This is a hack needed by cut gizmo, which sometimes diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index e5fa97e39c..3645ecc026 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -141,7 +141,7 @@ private: ExPolygon expoly; BoundingBox expoly_bb; bool disabled = false; - coord_t hash; + size_t hash; }; struct ClipResult { std::vector cut_islands; From e4c01a58573cb00e2d2e9aca1b5c6e44c026669e Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 12 May 2023 13:25:27 +0200 Subject: [PATCH 75/90] Cut fixes: - When cutting a scaled part, do not apply the transformation twice - Right click does not trigger part selection when any modifier is used - When 'flip plane' is clicked, extra render is forced - The choice to keep object A/B is enabled even when part selection is active - 'Cut into' radio buttons are disabled when part selection is active - Added a missing update of connector state - Amended tooltip --- src/libslic3r/Model.cpp | 2 ++ src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 11 +++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index c7844f8072..9aa4cf3c63 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1595,6 +1595,8 @@ void ModelObject::reset_instance_transformation(ModelObject* object, size_t src_ for (size_t i = 0; i < object->instances.size(); ++i) { auto& obj_instance = object->instances[i]; const double rot_z = obj_instance->get_rotation().z(); + + obj_instance->set_transformation(Transformation(obj_instance->get_transformation().get_matrix_no_scaling_factor())); Vec3d rotation = Vec3d::Zero(); if (!flip && !place_on_cut) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 1179830a00..2cb0a81519 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -250,7 +250,8 @@ std::string GLGizmoCut3D::get_tooltip() const if (!m_dragging && m_hover_id == CutPlane) return _u8L("Click to flip the cut plane\n" - "Drag to move the cut plane"); + "Drag to move the cut plane\n" + "Right-click a part to assign it to the other side"); if (tooltip.empty() && (m_hover_id == X || m_hover_id == Y)) { std::string axis = m_hover_id == X ? "X" : "Y"; @@ -351,7 +352,7 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) return true; } else if (mouse_event.RightDown()) { - if (! m_connectors_editing) { + if (! m_connectors_editing && mouse_event.GetModifiers() == wxMOD_NONE) { // Check the internal part raycasters. if (! m_part_selection.valid()) process_contours(); @@ -1868,6 +1869,7 @@ void GLGizmoCut3D::reset_cut_plane() m_ar_plane_center = m_plane_center; reset_cut_by_contours(); + m_parent.request_extra_frame(); } void GLGizmoCut3D::invalidate_cut_plane() @@ -2074,7 +2076,7 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) ImGuiWrapper::text(_L("Cut result") + ": "); add_vertical_scaled_interval(0.5f); - m_imgui->disabled_begin(has_connectors || m_keep_as_parts || m_part_selection.valid()); + m_imgui->disabled_begin(has_connectors || m_keep_as_parts); render_part_name("A", m_keep_upper, m_imgui->to_ImU32(UPPER_PART_COLOR)); ImGui::SameLine(h_shift + ImGui::GetCurrentWindow()->WindowPadding.x); render_part_name("B", m_keep_lower, m_imgui->to_ImU32(LOWER_PART_COLOR)); @@ -2090,7 +2092,7 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) add_vertical_scaled_interval(0.75f); - m_imgui->disabled_begin(has_connectors); + m_imgui->disabled_begin(has_connectors || m_part_selection.valid()); ImGuiWrapper::text(_L("Cut into") + ":"); if (m_part_selection.valid()) @@ -2693,6 +2695,7 @@ void GLGizmoCut3D::reset_connectors() m_c->selection_info()->model_object()->cut_connectors.clear(); update_raycasters_for_picking(); clear_selection(); + check_and_update_connectors_state(); } void GLGizmoCut3D::init_connector_shapes() From 7553cf1007a3178e4c6e5034d4f8a266d60dd224 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 12 May 2023 14:20:04 +0200 Subject: [PATCH 76/90] Cut: Respect to the selected parts when perform a cut --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 2cb0a81519..2aaea5391f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -2540,23 +2540,24 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) ModelObjectPtrs cut_object_ptrs; if (cut_by_contour) { // apply cut attributes for object - cut_mo->apply_cut_attributes(ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | - only_if(create_dowels_as_separate_object, ModelObjectCutAttribute::CreateDowels)); + if (m_keep_upper && m_keep_lower) + cut_mo->apply_cut_attributes(ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | + only_if(create_dowels_as_separate_object, ModelObjectCutAttribute::CreateDowels)); // Clone the object to duplicate instances, materials etc. ModelObject* upper{ nullptr }; - cut_mo->clone_for_cut(&upper); + if (m_keep_upper) cut_mo->clone_for_cut(&upper); ModelObject* lower{ nullptr }; - cut_mo->clone_for_cut(&lower); + if (m_keep_lower) cut_mo->clone_for_cut(&lower); auto add_cut_objects = [this, &instance_idx, &cut_matrix](ModelObjectPtrs& cut_objects, ModelObject* upper, ModelObject* lower, bool invalidate_cut = true) { - if (!upper->volumes.empty()) { + if (upper && !upper->volumes.empty()) { ModelObject::reset_instance_transformation(upper, instance_idx, cut_matrix, m_place_on_cut_upper, m_rotate_upper); if (invalidate_cut) upper->invalidate_cut(); cut_objects.push_back(upper); } - if (!lower->volumes.empty()) { + if (lower && !lower->volumes.empty()) { ModelObject::reset_instance_transformation(lower, instance_idx, cut_matrix, m_place_on_cut_lower, m_place_on_cut_lower || m_rotate_lower); if (invalidate_cut) lower->invalidate_cut(); @@ -2565,8 +2566,10 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) }; const size_t cut_parts_cnt = m_part_selection.parts().size(); - for (size_t id = 0; id < cut_parts_cnt; ++id) - (m_part_selection.parts()[id].selected ? upper : lower)->add_volume(*(cut_mo->volumes[id])); + for (size_t id = 0; id < cut_parts_cnt; ++id) { + if (ModelObject* obj = (m_part_selection.parts()[id].selected ? upper : lower)) + obj->add_volume(*(cut_mo->volumes[id])); + } ModelVolumePtrs& volumes = cut_mo->volumes; if (volumes.size() == cut_parts_cnt) From f1f568f3fd7c1b1379f3779906249bb670c313c6 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 12 May 2023 15:39:26 +0200 Subject: [PATCH 77/90] Start using -beta config directory instead of -alpha --- src/slic3r/GUI/GUI_App.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 7382f87bf1..a380692370 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -925,8 +925,8 @@ void GUI_App::init_app_config() // Profiles for the alpha are stored into the PrusaSlicer-alpha directory to not mix with the current release. // SetAppName(SLIC3R_APP_KEY); - SetAppName(SLIC3R_APP_KEY "-alpha"); -// SetAppName(SLIC3R_APP_KEY "-beta"); +// SetAppName(SLIC3R_APP_KEY "-alpha"); + SetAppName(SLIC3R_APP_KEY "-beta"); // SetAppDisplayName(SLIC3R_APP_NAME); From 1de963d7d7b32f3c2f1bac73378b7b29515c2a58 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 12 May 2023 16:17:22 +0200 Subject: [PATCH 78/90] Organic supports: When propagating "verylost" branches down, stop when the support area is smaller then half of the branch area. --- src/libslic3r/Support/TreeSupport.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 7b11955d95..5a2e6d8ec0 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -4415,10 +4415,10 @@ static void draw_branches( // If the tree bottom is hanging in the air, bring it down to some surface. 0 : std::max(0, layer_begin - layers_propagate_max); - // Only propagate until the rest area is smaller than this threshold. - double support_area_stop = 0.2 * M_PI * sqr(double(bottom_radius)); - // Only propagate until the rest area is smaller than this threshold. - double support_area_min = 0.1 * M_PI * sqr(double(config.min_radius)); + double support_area_min_radius = M_PI * sqr(double(config.branch_radius)); + double support_area_stop = std::max(0.2 * M_PI * sqr(double(bottom_radius)), 0.5 * support_area_min_radius); + // Only propagate until the rest area is smaller than this threshold. + double support_area_min = 0.1 * support_area_min_radius; for (LayerIndex layer_idx = layer_begin - 1; layer_idx >= layer_bottommost; -- layer_idx) { rest_support = diff_clipped(rest_support.empty() ? slices.front() : rest_support, volumes.getCollision(0, layer_idx, false)); double rest_support_area = area(rest_support); From 115ac0aace752e104ae533bac09973a986ca67e2 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 12 May 2023 17:03:59 +0200 Subject: [PATCH 79/90] Revert "Fix for SPE-1657 : [LINUX - GTK3] Crash when change position of part in sidebar" This reverts commit a4cf34a49f98866d812a2a237740a7bf96488faa. --- src/slic3r/GUI/GUI_ObjectList.cpp | 34 +++++++++----------------- src/slic3r/GUI/ObjectDataViewModel.cpp | 10 -------- src/slic3r/GUI/ObjectDataViewModel.hpp | 1 - 3 files changed, 12 insertions(+), 33 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 9cbcbc3231..a5584692ce 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1192,13 +1192,6 @@ void ObjectList::key_event(wxKeyEvent& event) void ObjectList::OnBeginDrag(wxDataViewEvent &event) { - if (m_is_editing_started) - m_is_editing_started = false; -#ifdef __WXGTK__ - const auto renderer = dynamic_cast(GetColumn(colName)->GetRenderer()); - renderer->FinishEditing(); -#endif - const wxDataViewItem item(event.GetItem()); const bool mult_sel = multiple_selection(); @@ -1232,11 +1225,18 @@ void ObjectList::OnBeginDrag(wxDataViewEvent &event) m_objects_model->GetInstanceIdByItem(item), type); + /* Under MSW or OSX, DnD moves an item to the place of another selected item + * But under GTK, DnD moves an item between another two items. + * And as a result - call EVT_CHANGE_SELECTION to unselect all items. + * To prevent such behavior use m_prevent_list_events + **/ + m_prevent_list_events = true;//it's needed for GTK + /* Under GTK, DnD requires to the wxTextDataObject been initialized with some valid value, * so set some nonempty string */ wxTextDataObject* obj = new wxTextDataObject; - obj->SetText(mult_sel ? "SomeText" : m_objects_model->GetItemName(item));//it's needed for GTK + obj->SetText("Some text");//it's needed for GTK event.SetDataObject(obj); event.SetDragFlags(wxDrag_DefaultMove); // allows both copy and move; @@ -1299,8 +1299,11 @@ bool ObjectList::can_drop(const wxDataViewItem& item) const void ObjectList::OnDropPossible(wxDataViewEvent &event) { const wxDataViewItem& item = event.GetItem(); - if (!can_drop(item)) + + if (!can_drop(item)) { event.Veto(); + m_prevent_list_events = false; + } } void ObjectList::OnDrop(wxDataViewEvent &event) @@ -1314,13 +1317,6 @@ void ObjectList::OnDrop(wxDataViewEvent &event) return; } - /* Under MSW or OSX, DnD moves an item to the place of another selected item - * But under GTK, DnD moves an item between another two items. - * And as a result - call EVT_CHANGE_SELECTION to unselect all items. - * To prevent such behavior use m_prevent_list_events - **/ - m_prevent_list_events = true;//it's needed for GTK - if (m_dragged_data.type() == itInstance) { Plater::TakeSnapshot snapshot(wxGetApp().plater(),_(L("Instances to Separated Objects"))); @@ -4823,9 +4819,6 @@ void ObjectList::sys_color_changed() void ObjectList::ItemValueChanged(wxDataViewEvent &event) { - if (!m_is_editing_started) - return; - if (event.GetColumn() == colName) update_name_in_model(event.GetItem()); else if (event.GetColumn() == colExtruder) { @@ -4848,9 +4841,6 @@ void ObjectList::OnEditingStarted(wxDataViewEvent &event) void ObjectList::OnEditingDone(wxDataViewEvent &event) { - if (!m_is_editing_started) - return; - m_is_editing_started = false; if (event.GetColumn() != colName) return; diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index 38747d08dd..fe57d7d5a4 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -1042,16 +1042,6 @@ int ObjectDataViewModel::GetItemIdByLayerRange(const int obj_idx, const t_layer return GetLayerIdByItem(item); } -wxString ObjectDataViewModel::GetItemName(const wxDataViewItem &item) const -{ - if (!item.IsOk()) - return wxEmptyString; - ObjectDataViewModelNode* node = static_cast(item.GetID()); - if (!node) - return wxEmptyString; - return node->GetName(); -} - int ObjectDataViewModel::GetIdByItem(const wxDataViewItem& item) const { if(!item.IsOk()) diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp index bc5b485a38..993b67842a 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.hpp +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -311,7 +311,6 @@ public: wxDataViewItem GetItemByLayerId(int obj_idx, int layer_idx); wxDataViewItem GetItemByLayerRange(const int obj_idx, const t_layer_height_range& layer_range); int GetItemIdByLayerRange(const int obj_idx, const t_layer_height_range& layer_range); - wxString GetItemName(const wxDataViewItem& item) const; int GetIdByItem(const wxDataViewItem& item) const; int GetIdByItemAndType(const wxDataViewItem& item, const ItemType type) const; int GetObjectIdByItem(const wxDataViewItem& item) const; From a68e48b454b50cadb41b5b8ff7ce321d5f88bb69 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 12 May 2023 16:35:25 +0200 Subject: [PATCH 80/90] Bumped up version to 2.6.0-beta1, updated PrusaResearch profiles --- resources/profiles/PrusaResearch.idx | 4 ++++ resources/profiles/PrusaResearch.ini | 6 +++--- version.inc | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index 3a3e2b333e..e24ec29041 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -1,3 +1,5 @@ +min_slic3r_version = 2.6.0-beta0 +1.9.0-beta0 Updated start g-code script for MK4/XL. min_slic3r_version = 2.6.0-alpha5 1.9.0-alpha4 Updated XL and MK4 profiles. Updated PC Blend Carbon Fiber density. 1.9.0-alpha3 Updated compatibility condition for MMU1 filaments. @@ -12,11 +14,13 @@ min_slic3r_version = 2.6.0-alpha1 1.6.0-alpha1 Updated FW version notification. Decreased min layer time for PLA. 1.6.0-alpha0 Default top fill set to monotonic lines. Updated infill/perimeter overlap values. Updated output filename format. Enabled dynamic overhang speeds. min_slic3r_version = 2.5.2-rc0 +1.7.4 Updated start g-code script for MK4/XL (fixed pre-print temperature for PA filaments). 1.7.3 Updated XL and MK4 profiles. Updated PC Blend Carbon Fiber density. 1.7.2 Updated compatibility condition for MMU1 filaments. 1.7.1 Added SLA materials. Updated MK4 and XL profiles. 1.7.0 Added profiles for Original Prusa MK4. min_slic3r_version = 2.5.1-rc0 +1.6.5 Updated start g-code script for MK4/XL (fixed pre-print temperature for PA filaments). 1.6.4 Fixed compatibility condition for MMU1 filaments. 1.6.3 Added SLA materials. 1.6.2 Updated compatibility condition in some filament profiles (Prusa XL). diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index d2528a70a5..442f7bdebf 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -5,7 +5,7 @@ name = Prusa Research # Configuration version of this file. Config file will only be installed, if the config_version differs. # This means, the server may force the PrusaSlicer configuration to be downgraded. -config_version = 1.9.0-alpha4 +config_version = 1.9.0-beta0 # Where to get the updates from? config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/ changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1% @@ -15778,7 +15778,7 @@ retract_before_travel = 1.5 retract_before_wipe = 80% retract_layer_change = 1 retract_length = 0.8 -start_gcode = M17 ; enable steppers\nM862.3 P "[printer_model]" ; printer model check\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\n; set print area\nM555 X{first_layer_print_min[0]} Y{first_layer_print_min[1]} W{(first_layer_print_max[0]) - (first_layer_print_min[0])} H{(first_layer_print_max[1]) - (first_layer_print_min[1])}\n; inform about nozzle diameter\nM862.1 P[nozzle_diameter]\n; set & wait for bed and extruder temp for MBL\nM140 S[first_layer_bed_temperature] ; set bed temp\nM104 T0 S{((filament_type[0] == "PC" or filament_type[0] == "NYLON") ? (first_layer_temperature[0] - 25) : (filament_type[0] == "FLEX" ? 210 : 170))} ; set extruder temp for bed leveling\nM109 T0 R{((filament_type[0] == "PC" or filament_type[0] == "NYLON") ? (first_layer_temperature[0] - 25) : (filament_type[0] == "FLEX" ? 210 : 170))} ; wait for temp\n; home carriage, pick tool, home all\nG28 XY\nM84 E ; turn off E motor\nG28 Z\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nG29 G ; absorb heat\n; move to the nozzle cleanup area\nG1 X{(min(((((first_layer_print_min[0] + first_layer_print_max[0]) / 2) < ((print_bed_min[0] + print_bed_max[0]) / 2)) ? (((first_layer_print_min[1] - 7) < -2) ? 70 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)) : (((first_layer_print_min[1] - 7) < -2) ? 260 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32))), first_layer_print_min[0])) + 32} Y{(min((first_layer_print_min[1] - 7), first_layer_print_min[1]))} Z{5} F4800\nM302 S160 ; lower cold extrusion limit to 160C\nG1 E{-(filament_type[0] == "FLEX" ? 4 : 2)} F2400 ; retraction for nozzle cleanup\n; nozzle cleanup\nM84 E ; turn off E motor\nG29 P9 X{((((first_layer_print_min[0] + first_layer_print_max[0]) / 2) < ((print_bed_min[0] + print_bed_max[0]) / 2)) ? (((first_layer_print_min[1] - 7) < -2) ? 70 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)) : (((first_layer_print_min[1] - 7) < -2) ? 260 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)))} Y{(first_layer_print_min[1] - 7)} W{32} H{7}\nG0 Z10 F480 ; move away in Z\n{if first_layer_bed_temperature[0] > 60}\nG0 Z70 F480 ; move away (a bit more) in Z\nG0 X30 Y{print_bed_min[1]} F6000 ; move away in X/Y for higher bed temperatures\n{endif}\nM106 S100 ; cool off the nozzle\nM107 ; stop cooling off the nozzle - turn off the fan\n; MBL\nM84 E ; turn off E motor\nG29 P1 ; invalidate mbl & probe print area\nG29 P1 X30 Y0 W50 H20 C ; probe near purge place\nG29 P3.2 ; interpolate mbl probes\nG29 P3.13 ; extrapolate mbl outside probe area\nG29 A ; activate mbl\nM104 S[first_layer_temperature] ; set extruder temp\nG1 Z10 F720 ; move away in Z\nG0 X30 Y-8 F6000 ; move next to the sheet\n; wait for extruder temp\nM109 T0 S{first_layer_temperature[0]}\n;\n; purge\n;\nG92 E0 ; reset extruder position\nG0 X{(0 == 0 ? 30 : (0 == 1 ? 150 : (0 == 2 ? 210 : 330)))} Y{(0 < 4 ? -8 : -5.5)} ; move close to the sheet's edge\nG1 E{(filament_type[0] == "FLEX" ? 4 : 2)} F2400 ; deretraction after the initial one before nozzle cleaning\nG0 E10 X40 Z0.2 F500 ; purge\nG0 X70 E9 F800 ; purge\nG0 X{70 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{70 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG92 E0 ; reset extruder position\n +start_gcode = M17 ; enable steppers\nM862.3 P "[printer_model]" ; printer model check\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\n; set print area\nM555 X{first_layer_print_min[0]} Y{first_layer_print_min[1]} W{(first_layer_print_max[0]) - (first_layer_print_min[0])} H{(first_layer_print_max[1]) - (first_layer_print_min[1])}\n; inform about nozzle diameter\nM862.1 P[nozzle_diameter]\n; set & wait for bed and extruder temp for MBL\nM140 S[first_layer_bed_temperature] ; set bed temp\nM104 T0 S{((filament_type[0] == "PC" or filament_type[0] == "PA") ? (first_layer_temperature[0] - 25) : (filament_type[0] == "FLEX" ? 210 : 170))} ; set extruder temp for bed leveling\nM109 T0 R{((filament_type[0] == "PC" or filament_type[0] == "PA") ? (first_layer_temperature[0] - 25) : (filament_type[0] == "FLEX" ? 210 : 170))} ; wait for temp\n; home carriage, pick tool, home all\nG28 XY\nM84 E ; turn off E motor\nG28 Z\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nG29 G ; absorb heat\n; move to the nozzle cleanup area\nG1 X{(min(((((first_layer_print_min[0] + first_layer_print_max[0]) / 2) < ((print_bed_min[0] + print_bed_max[0]) / 2)) ? (((first_layer_print_min[1] - 7) < -2) ? 70 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)) : (((first_layer_print_min[1] - 7) < -2) ? 260 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32))), first_layer_print_min[0])) + 32} Y{(min((first_layer_print_min[1] - 7), first_layer_print_min[1]))} Z{5} F4800\nM302 S160 ; lower cold extrusion limit to 160C\nG1 E{-(filament_type[0] == "FLEX" ? 4 : 2)} F2400 ; retraction for nozzle cleanup\n; nozzle cleanup\nM84 E ; turn off E motor\nG29 P9 X{((((first_layer_print_min[0] + first_layer_print_max[0]) / 2) < ((print_bed_min[0] + print_bed_max[0]) / 2)) ? (((first_layer_print_min[1] - 7) < -2) ? 70 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)) : (((first_layer_print_min[1] - 7) < -2) ? 260 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)))} Y{(first_layer_print_min[1] - 7)} W{32} H{7}\nG0 Z10 F480 ; move away in Z\n{if first_layer_bed_temperature[0] > 60}\nG0 Z70 F480 ; move away (a bit more) in Z\nG0 X30 Y{print_bed_min[1]} F6000 ; move away in X/Y for higher bed temperatures\n{endif}\nM106 S100 ; cool off the nozzle\nM107 ; stop cooling off the nozzle - turn off the fan\n; MBL\nM84 E ; turn off E motor\nG29 P1 ; invalidate mbl & probe print area\nG29 P1 X30 Y0 W50 H20 C ; probe near purge place\nG29 P3.2 ; interpolate mbl probes\nG29 P3.13 ; extrapolate mbl outside probe area\nG29 A ; activate mbl\nM104 S[first_layer_temperature] ; set extruder temp\nG1 Z10 F720 ; move away in Z\nG0 X30 Y-8 F6000 ; move next to the sheet\n; wait for extruder temp\nM109 T0 S{first_layer_temperature[0]}\n;\n; purge\n;\nG92 E0 ; reset extruder position\nG0 X{(0 == 0 ? 30 : (0 == 1 ? 150 : (0 == 2 ? 210 : 330)))} Y{(0 < 4 ? -8 : -5.5)} ; move close to the sheet's edge\nG1 E{(filament_type[0] == "FLEX" ? 4 : 2)} F2400 ; deretraction after the initial one before nozzle cleaning\nG0 E10 X40 Z0.2 F500 ; purge\nG0 X70 E9 F800 ; purge\nG0 X{70 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{70 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG92 E0 ; reset extruder position\n default_print_profile = 0.20mm QUALITY @XL 0.4 default_filament_profile = "Prusament PLA @PG" thumbnails = 16x16,313x173,440x240 @@ -15884,7 +15884,7 @@ retract_before_travel = 1.5 retract_before_wipe = 80% retract_layer_change = 1 retract_length = 0.8 -start_gcode = M17 ; enable steppers\nM862.3 P "[printer_model]" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM555 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)} Y{(max(0, first_layer_print_min[1]) - 4)} W{((min(print_bed_max[0], max(first_layer_print_min[0] + 32, first_layer_print_max[0])))) - ((min(print_bed_max[0], first_layer_print_min[0] + 32) - 32))} H{((first_layer_print_max[1])) - ((max(0, first_layer_print_min[1]) - 4))}\n\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\n\nM140 S[first_layer_bed_temperature] ; set bed temp\n{if filament_type[initial_tool]=="PC" or filament_type[initial_tool]=="NYLON"}\nM104 S{first_layer_temperature[initial_tool]-25} ; set extruder temp for bed leveling\nM109 R{first_layer_temperature[initial_tool]-25} ; wait for temp\n{elsif filament_type[initial_tool]=="FLEX"}\nM104 S210 ; set extruder temp for bed leveling\nM109 R210 ; wait for temp\n{else}\nM104 S170 ; set extruder temp for bed leveling\nM109 R170 ; wait for temp\n{endif}\n\nM84 E ; turn off E motor\n\nG28 ; home all without mesh bed level\n\n; probe to clean the nozzle\nG1 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)+32} Y{((first_layer_print_min[1]) - 4)} Z{5} F4800\n\nM302 S160 ; lower cold extrusion limit to 160C\n\n{if filament_type[initial_tool]=="FLEX"}\nG1 E-4 F2400 ; retraction\n{else}\nG1 E-2 F2400 ; retraction\n{endif}\n\nM84 E ; turn off E motor\n\nG29 P9 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)} Y{(max(0, first_layer_print_min[1]) - 4)} W{32} H{4}\n\n{if first_layer_bed_temperature[initial_tool]<=60}M106 S100{endif}\n\nG0 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)} Y{(max(0, first_layer_print_min[1]) - 4)} Z{40} F10000\n\nM190 S[first_layer_bed_temperature] ; wait for bed temp\n\nM107\n\n;\n; MBL\n;\nM84 E ; turn off E motor\nG29 ; mesh bed leveling\nM104 S[first_layer_temperature] ; set extruder temp\nG0 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)} Y{(max(0, first_layer_print_min[1]) - 4) + 4 - 4.5} Z{30} F4800\n\nM109 S[first_layer_temperature] ; wait for extruder temp\nG1 Z0.2 F720\nG92 E0\n\nM569 S0 E ; set spreadcycle mode for extruder\n\n;\n; Extrude purge line\n;\n{if filament_type[initial_tool]=="FLEX"}\nG1 E4 F2400 ; deretraction\n{else}\nG1 E2 F2400 ; deretraction\n{endif}\n\n; move right\nG1 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32) + 32} E{32 * 0.15} F1000\n; move down\nG1 Y{(max(0, first_layer_print_min[1]) - 4) + 4 - 4.5 - 1.5} E{1.5 * 0.15} F1000\n; move left\nG1 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)} E{32 * 0.30} F800\n\nG92 E0\nM221 S100 ; set flow to 100% +start_gcode = M17 ; enable steppers\nM862.3 P "[printer_model]" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM555 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)} Y{(max(0, first_layer_print_min[1]) - 4)} W{((min(print_bed_max[0], max(first_layer_print_min[0] + 32, first_layer_print_max[0])))) - ((min(print_bed_max[0], first_layer_print_min[0] + 32) - 32))} H{((first_layer_print_max[1])) - ((max(0, first_layer_print_min[1]) - 4))}\n\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\n\nM140 S[first_layer_bed_temperature] ; set bed temp\n{if filament_type[initial_tool]=="PC" or filament_type[initial_tool]=="PA"}\nM104 S{first_layer_temperature[initial_tool]-25} ; set extruder temp for bed leveling\nM109 R{first_layer_temperature[initial_tool]-25} ; wait for temp\n{elsif filament_type[initial_tool]=="FLEX"}\nM104 S210 ; set extruder temp for bed leveling\nM109 R210 ; wait for temp\n{else}\nM104 S170 ; set extruder temp for bed leveling\nM109 R170 ; wait for temp\n{endif}\n\nM84 E ; turn off E motor\n\nG28 ; home all without mesh bed level\n\n; probe to clean the nozzle\nG1 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)+32} Y{((first_layer_print_min[1]) - 4)} Z{5} F4800\n\nM302 S160 ; lower cold extrusion limit to 160C\n\n{if filament_type[initial_tool]=="FLEX"}\nG1 E-4 F2400 ; retraction\n{else}\nG1 E-2 F2400 ; retraction\n{endif}\n\nM84 E ; turn off E motor\n\nG29 P9 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)} Y{(max(0, first_layer_print_min[1]) - 4)} W{32} H{4}\n\n{if first_layer_bed_temperature[initial_tool]<=60}M106 S100{endif}\n\nG0 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)} Y{(max(0, first_layer_print_min[1]) - 4)} Z{40} F10000\n\nM190 S[first_layer_bed_temperature] ; wait for bed temp\n\nM107\n\n;\n; MBL\n;\nM84 E ; turn off E motor\nG29 ; mesh bed leveling\nM104 S[first_layer_temperature] ; set extruder temp\nG0 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)} Y{(max(0, first_layer_print_min[1]) - 4) + 4 - 4.5} Z{30} F4800\n\nM109 S[first_layer_temperature] ; wait for extruder temp\nG1 Z0.2 F720\nG92 E0\n\nM569 S0 E ; set spreadcycle mode for extruder\n\n;\n; Extrude purge line\n;\n{if filament_type[initial_tool]=="FLEX"}\nG1 E4 F2400 ; deretraction\n{else}\nG1 E2 F2400 ; deretraction\n{endif}\n\n; move right\nG1 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32) + 32} E{32 * 0.15} F1000\n; move down\nG1 Y{(max(0, first_layer_print_min[1]) - 4) + 4 - 4.5 - 1.5} E{1.5 * 0.15} F1000\n; move left\nG1 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)} E{32 * 0.30} F800\n\nG92 E0\nM221 S100 ; set flow to 100% default_print_profile = 0.20mm QUALITY @MK4 0.4 default_filament_profile = "Prusament PLA @PG" thumbnails = 16x16,313x173,440x240 diff --git a/version.inc b/version.inc index 698bb33284..0eecbcdc6f 100644 --- a/version.inc +++ b/version.inc @@ -3,7 +3,7 @@ set(SLIC3R_APP_NAME "PrusaSlicer") set(SLIC3R_APP_KEY "PrusaSlicer") -set(SLIC3R_VERSION "2.6.0-alpha6") +set(SLIC3R_VERSION "2.6.0-beta1") set(SLIC3R_BUILD_ID "PrusaSlicer-${SLIC3R_VERSION}+UNKNOWN") set(SLIC3R_RC_VERSION "2,6,0,0") set(SLIC3R_RC_VERSION_DOTS "2.6.0.0") From a06a619228b97134fa30807fb7ccc3961ff259d2 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 15 May 2023 10:43:46 +0200 Subject: [PATCH 81/90] Fix of boost format error. issue #10556 --- src/slic3r/GUI/ConfigWizard.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index d0b7721976..60e7b00cc6 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -159,7 +159,7 @@ BundleMap BundleMap::load() idx_path = fs::path(cache_dir / (id + ".idx")); } if (!boost::filesystem::exists(idx_path)) { - BOOST_LOG_TRIVIAL(error) << format("Could not load bundle %1% due to missing index %1%.", id, idx_path.string()); + BOOST_LOG_TRIVIAL(error) << format("Could not load bundle %1% due to missing index %2%.", id, idx_path.string()); continue; } Slic3r::GUI::Config::Index index; @@ -167,7 +167,7 @@ BundleMap BundleMap::load() index.load(idx_path); } catch (const std::exception& /* err */) { - BOOST_LOG_TRIVIAL(error) << format("Could not load bundle %1% due to invalid index %1%.", id, idx_path.string()); + BOOST_LOG_TRIVIAL(error) << format("Could not load bundle %1% due to invalid index %2%.", id, idx_path.string()); continue; } const auto recommended_it = index.recommended(); From 60732fa664d8d7d3409c10b1b62743b6f932639e Mon Sep 17 00:00:00 2001 From: Pavel Mikus Date: Mon, 15 May 2023 10:48:54 +0200 Subject: [PATCH 82/90] Fix https://github.com/prusa3d/PrusaSlicer/issues/10563 Crashing of new ensuring fill due to zero vertical lines generated --- src/libslic3r/Fill/FillEnsuring.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Fill/FillEnsuring.cpp b/src/libslic3r/Fill/FillEnsuring.cpp index a64c0fd83c..aa19bf09f4 100644 --- a/src/libslic3r/Fill/FillEnsuring.cpp +++ b/src/libslic3r/Fill/FillEnsuring.cpp @@ -71,9 +71,11 @@ ThickPolylines make_fill_polylines( vertical_lines[i].a = Point{x, y_min}; vertical_lines[i].b = Point{x, y_max}; } - vertical_lines.push_back(vertical_lines.back()); - vertical_lines.back().a = Point{coord_t(bb.min.x() + n_vlines * double(scaled_spacing) + scaled_spacing * 0.5), y_min}; - vertical_lines.back().b = Point{vertical_lines.back().a.x(), y_max}; + if (vertical_lines.size() > 0) { + vertical_lines.push_back(vertical_lines.back()); + vertical_lines.back().a = Point{coord_t(bb.min.x() + n_vlines * double(scaled_spacing) + scaled_spacing * 0.5), y_min}; + vertical_lines.back().b = Point{vertical_lines.back().a.x(), y_max}; + } std::vector> polygon_sections(n_vlines); From 7f649c5509018f08b99e18238b8e0fe32e8edd46 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 15 May 2023 13:04:43 +0200 Subject: [PATCH 83/90] Some code refactoring --- src/slic3r/GUI/Plater.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 35308c0581..2d473646cf 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1411,27 +1411,25 @@ void Sidebar::update_sliced_info_sizer() new_label = _L("Used Filament (g)"); info_text = wxString::Format("%.2f", ps.total_weight); - const auto& extruders_filaments = wxGetApp().preset_bundle->extruders_filaments; - const PresetCollection& filaments = wxGetApp().preset_bundle->filaments; - if (ps.filament_stats.size() > 1) new_label += ":"; - for (auto filament : ps.filament_stats) { - const Preset* filament_preset = filaments.find_preset(extruders_filaments[filament.first].get_selected_preset_name(), false); - if (filament_preset) { + const auto& extruders_filaments = wxGetApp().preset_bundle->extruders_filaments; + for (const auto& [filament_id, filament_vol] : ps.filament_stats) { + assert(filament_id < extruders_filaments.size()); + if (const Preset* preset = extruders_filaments[filament_id].get_selected_preset()) { double filament_weight; if (ps.filament_stats.size() == 1) filament_weight = ps.total_weight; else { - double filament_density = filament_preset->config.opt_float("filament_density", 0); - filament_weight = filament.second * filament_density/* *2.4052f*/ * 0.001; // assumes 1.75mm filament diameter; + double filament_density = preset->config.opt_float("filament_density", 0); + filament_weight = filament_vol * filament_density/* *2.4052f*/ * 0.001; // assumes 1.75mm filament diameter; - new_label += "\n - " + format_wxstr(_L("Filament at extruder %1%"), filament.first + 1); + new_label += "\n - " + format_wxstr(_L("Filament at extruder %1%"), filament_id + 1); info_text += wxString::Format("\n%.2f", filament_weight); } - double spool_weight = filament_preset->config.opt_float("filament_spool_weight", 0); + double spool_weight = preset->config.opt_float("filament_spool_weight", 0); if (spool_weight != 0.0) { new_label += "\n " + _L("(including spool)"); info_text += wxString::Format(" (%.2f)\n", filament_weight + spool_weight); From 0a0ad1ef10168230452f891b0c68a42ff8fe63c4 Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Tue, 16 May 2023 10:28:56 +0200 Subject: [PATCH 84/90] Updated compatible condition. --- resources/profiles/Templates.idx | 1 + resources/profiles/Templates.ini | 8 +++----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/resources/profiles/Templates.idx b/resources/profiles/Templates.idx index 297aeb1fee..728a7da353 100644 --- a/resources/profiles/Templates.idx +++ b/resources/profiles/Templates.idx @@ -1,3 +1,4 @@ min_slic3r_version = 2.6.0-alpha0 +1.0.2 Updated compatible printer conditions. 1.0.1 Added Prusament PETG Carbon Fiber, Fiberthree F3 PA-GF30 Pro. 1.0.0 Initial diff --git a/resources/profiles/Templates.ini b/resources/profiles/Templates.ini index cf7b8ebe8f..d038d572db 100644 --- a/resources/profiles/Templates.ini +++ b/resources/profiles/Templates.ini @@ -2,7 +2,7 @@ [vendor] name = Templates -config_version = 1.0.1 +config_version = 1.0.2 config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Templates/ templates_profile = 1 @@ -11,12 +11,12 @@ templates_profile = 1 [filament:*common*] cooling = 1 compatible_printers = -compatible_printers_condition = +compatible_printers_condition = printer_notes!~/.*PRINTER_VENDOR_TRILAB.*/ and printer_notes!~/.*PRINTER_MODEL_MK4IS.*/ end_filament_gcode = "; Filament-specific end gcode" extrusion_multiplier = 1 filament_loading_speed = 14 filament_loading_speed_start = 19 -filament_unloading_speed = 90 +filament_unloading_speed = 20 filament_unloading_speed_start = 100 filament_toolchange_delay = 0 filament_cooling_moves = 1 @@ -1460,7 +1460,6 @@ cooling = 0 fan_always_on = 0 filament_max_volumetric_speed = 4 filament_type = METAL -compatible_printers_condition = nozzle_diameter[0]>=0.4 filament_colour = #FFFFFF [filament:Polymaker PC-Max] @@ -1519,7 +1518,6 @@ first_layer_temperature = 230 max_fan_speed = 20 min_fan_speed = 20 temperature = 230 -compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) [filament:Prusa PETG] inherits = *PET* From 01dec1bd5cc49adde8bc42a98803db226cda6099 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 16 May 2023 11:06:27 +0200 Subject: [PATCH 85/90] Fixed crash in Organic supports. Fixes #10551 --- src/libslic3r/Support/TreeModelVolumes.cpp | 12 ++++---- src/libslic3r/Support/TreeModelVolumes.hpp | 33 ++++++++++++---------- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/libslic3r/Support/TreeModelVolumes.cpp b/src/libslic3r/Support/TreeModelVolumes.cpp index 5df56cd62d..b726dfa27a 100644 --- a/src/libslic3r/Support/TreeModelVolumes.cpp +++ b/src/libslic3r/Support/TreeModelVolumes.cpp @@ -497,7 +497,7 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex const bool calculate_placable = m_support_rests_on_model && radius == 0; LayerPolygonCache data_placeable; if (calculate_placable) - data_placeable.allocate(data.idx_begin, data.idx_end); + data_placeable.allocate(data.begin(), data.end()); for (size_t outline_idx : layer_outline_indices) if (const std::vector &outlines = m_layer_outlines[outline_idx].second; ! outlines.empty()) { @@ -517,9 +517,9 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex // 1) Calculate offsets of collision areas in parallel. LayerPolygonCache collision_areas_offsetted; collision_areas_offsetted.allocate( - std::max(0, data.idx_begin - z_distance_bottom_layers), - std::min(outlines.size(), data.idx_end + z_distance_top_layers)); - tbb::parallel_for(tbb::blocked_range(collision_areas_offsetted.idx_begin, collision_areas_offsetted.idx_end), + std::max(0, data.begin() - z_distance_bottom_layers), + std::min(outlines.size(), data.end() + z_distance_top_layers)); + tbb::parallel_for(tbb::blocked_range(collision_areas_offsetted.begin(), collision_areas_offsetted.end()), [&outlines, &machine_border = std::as_const(m_machine_border), offset_value = radius + xy_distance, &collision_areas_offsetted, &throw_on_cancel] (const tbb::blocked_range &range) { for (LayerIndex layer_idx = range.begin(); layer_idx != range.end(); ++ layer_idx) { @@ -536,7 +536,7 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex // 2) Sum over top / bottom ranges. const bool processing_last_mesh = outline_idx == layer_outline_indices.size(); - tbb::parallel_for(tbb::blocked_range(data.idx_begin, data.idx_end), + tbb::parallel_for(tbb::blocked_range(data.begin(), data.end()), [&collision_areas_offsetted, &outlines, &machine_border = m_machine_border, &anti_overhang = m_anti_overhang, radius, xy_distance, z_distance_bottom_layers, z_distance_top_layers, min_resolution = m_min_resolution, &data, processing_last_mesh, &throw_on_cancel] (const tbb::blocked_range& range) { @@ -600,7 +600,7 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex // 3) Optionally calculate placables. if (calculate_placable) { // Now calculate the placable areas. - tbb::parallel_for(tbb::blocked_range(std::max(z_distance_bottom_layers + 1, data.idx_begin), data.idx_end), + tbb::parallel_for(tbb::blocked_range(std::max(z_distance_bottom_layers + 1, data.begin()), data.end()), [&collision_areas_offsetted, &outlines, &anti_overhang = m_anti_overhang, processing_last_mesh, min_resolution = m_min_resolution, z_distance_bottom_layers, xy_distance, &data_placeable, &throw_on_cancel] (const tbb::blocked_range& range) { diff --git a/src/libslic3r/Support/TreeModelVolumes.hpp b/src/libslic3r/Support/TreeModelVolumes.hpp index ecfa999714..1e43864681 100644 --- a/src/libslic3r/Support/TreeModelVolumes.hpp +++ b/src/libslic3r/Support/TreeModelVolumes.hpp @@ -332,23 +332,26 @@ public: private: // Caching polygons for a range of layers. - struct LayerPolygonCache { - std::vector polygons; - LayerIndex idx_begin; - LayerIndex idx_end; - + class LayerPolygonCache { + public: void allocate(LayerIndex aidx_begin, LayerIndex aidx_end) { - this->idx_begin = aidx_begin; - this->idx_end = aidx_end; - this->polygons.assign(aidx_end - aidx_begin, {}); + m_idx_begin = aidx_begin; + m_idx_end = aidx_end; + m_polygons.assign(aidx_end - aidx_begin, {}); } - LayerIndex begin() const { return idx_begin; } - LayerIndex end() const { return idx_end; } - size_t size() const { return polygons.size(); } + LayerIndex begin() const { return m_idx_begin; } + LayerIndex end() const { return m_idx_end; } + size_t size() const { return m_polygons.size(); } - bool has(LayerIndex idx) const { return idx >= idx_begin && idx < idx_end; } - Polygons& operator[](LayerIndex idx) { return polygons[idx + idx_begin]; } + bool has(LayerIndex idx) const { return idx >= m_idx_begin && idx < m_idx_end; } + Polygons& operator[](LayerIndex idx) { assert(idx >= m_idx_begin && idx < m_idx_end); return m_polygons[idx - m_idx_begin]; } + std::vector& polygons_mutable() { return m_polygons; } + + private: + std::vector m_polygons; + LayerIndex m_idx_begin; + LayerIndex m_idx_end; }; /*! @@ -388,9 +391,9 @@ private: } void insert(LayerPolygonCache &&in, coord_t radius) { std::lock_guard guard(m_mutex); - LayerIndex i = in.idx_begin; + LayerIndex i = in.begin(); allocate_layers(i + LayerIndex(in.size())); - for (auto &d : in.polygons) + for (auto &d : in.polygons_mutable()) m_data[i ++].emplace(radius, std::move(d)); } /*! From 323f4faafcb80b8206d4af903fa156980380f6c9 Mon Sep 17 00:00:00 2001 From: Pavel Mikus Date: Tue, 16 May 2023 11:15:43 +0200 Subject: [PATCH 86/90] Fix Crash after using automatic support painting, issue #10516 --- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 557cdee8e3..7c1a816b94 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -17,6 +17,7 @@ #include +#include namespace Slic3r::GUI { @@ -521,6 +522,13 @@ void GLGizmoFdmSupports::auto_generate() } ModelObject *mo = m_c->selection_info()->model_object(); + bool printable = std::any_of(mo->instances.begin(), mo->instances.end(), [](const ModelInstance *p) { return p->is_printable(); }); + if (!printable) { + MessageDialog dlg(GUI::wxGetApp().plater(), _L("Automatic painting requires printable object."), _L("Warning"), wxOK); + dlg.ShowModal(); + return; + } + bool not_painted = std::all_of(mo->volumes.begin(), mo->volumes.end(), [](const ModelVolume* vol){ return vol->type() != ModelVolumeType::MODEL_PART || vol->supported_facets.empty(); }); From a35e013111fd3a0e05f7f9190338d6cc2d6305b4 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 16 May 2023 12:52:14 +0200 Subject: [PATCH 87/90] Fixed crash with Organic supports, zero top interfaces and raft enabled. Fixes #10547 SPE-1700 --- src/libslic3r/Support/TreeSupport.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 5a2e6d8ec0..72d1bda030 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -4649,6 +4649,7 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume // ### Precalculate avoidances, collision etc. size_t num_support_layers = precalculate(print, overhangs, processing.first, processing.second, volumes, throw_on_cancel); bool has_support = num_support_layers > 0; + bool has_raft = config.raft_layers.size() > 0; num_support_layers = std::max(num_support_layers, config.raft_layers.size()); SupportParameters support_params(print_object); @@ -4661,13 +4662,13 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume SupportGeneratorLayersPtr interface_layers; SupportGeneratorLayersPtr base_interface_layers; SupportGeneratorLayersPtr intermediate_layers(num_support_layers, nullptr); - if (support_params.has_top_contacts) + if (support_params.has_top_contacts || has_raft) top_contacts.assign(num_support_layers, nullptr); if (support_params.has_bottom_contacts) bottom_contacts.assign(num_support_layers, nullptr); - if (support_params.has_interfaces()) + if (support_params.has_interfaces() || has_raft) interface_layers.assign(num_support_layers, nullptr); - if (support_params.has_base_interfaces()) + if (support_params.has_base_interfaces() || has_raft) base_interface_layers.assign(num_support_layers, nullptr); InterfacePlacer interface_placer{ From c60a10b7e4bcedb23f0aa28672572272526756e2 Mon Sep 17 00:00:00 2001 From: Pavel Mikus Date: Tue, 16 May 2023 15:45:23 +0200 Subject: [PATCH 88/90] fix SPE-1711 Vertical holes in bridging over infill --- src/libslic3r/Fill/FillEnsuring.cpp | 2 +- src/libslic3r/PrintObject.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/Fill/FillEnsuring.cpp b/src/libslic3r/Fill/FillEnsuring.cpp index aa19bf09f4..4838ad8d26 100644 --- a/src/libslic3r/Fill/FillEnsuring.cpp +++ b/src/libslic3r/Fill/FillEnsuring.cpp @@ -278,7 +278,7 @@ ThickPolylines make_fill_polylines( } } - reconstructed_area = closing(reconstructed_area, float(SCALED_EPSILON), float(SCALED_EPSILON)); + reconstructed_area = union_safety_offset(reconstructed_area); ExPolygons gaps_for_additional_filling = diff_ex(filled_area, reconstructed_area); if (fill->overlap != 0) { gaps_for_additional_filling = offset_ex(gaps_for_additional_filling, scaled(fill->overlap)); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 5d3028c8f7..c6d21015b1 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2186,6 +2186,7 @@ void PrintObject::bridge_over_infill() Polygon &new_poly = expanded_bridged_area.emplace_back(std::move(traced_poly.lows)); new_poly.points.insert(new_poly.points.end(), traced_poly.highs.rbegin(), traced_poly.highs.rend()); } + expanded_bridged_area = union_safety_offset(expanded_bridged_area); } polygons_rotate(expanded_bridged_area, -aligning_angle); From 4347263bb2f770ec7fe43141f4de1cad388341fe Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Wed, 17 May 2023 10:43:30 +0200 Subject: [PATCH 89/90] Sync with PrusaSlicer-settings. Updated cooling settings for ASA. --- resources/profiles/PrusaResearch.idx | 3 +++ resources/profiles/PrusaResearch.ini | 34 ++++++++++++++++++++++------ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index e24ec29041..10ecdcbc42 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -1,4 +1,5 @@ min_slic3r_version = 2.6.0-beta0 +1.9.0-beta1 Updated cooling settings for some ASA filaments to increase interlayer adhesion (XL/MK4). 1.9.0-beta0 Updated start g-code script for MK4/XL. min_slic3r_version = 2.6.0-alpha5 1.9.0-alpha4 Updated XL and MK4 profiles. Updated PC Blend Carbon Fiber density. @@ -14,12 +15,14 @@ min_slic3r_version = 2.6.0-alpha1 1.6.0-alpha1 Updated FW version notification. Decreased min layer time for PLA. 1.6.0-alpha0 Default top fill set to monotonic lines. Updated infill/perimeter overlap values. Updated output filename format. Enabled dynamic overhang speeds. min_slic3r_version = 2.5.2-rc0 +1.7.5 Updated cooling settings for some ASA filaments to increase interlayer adhesion (XL/MK4). Updated LA values (XL/MK4). 1.7.4 Updated start g-code script for MK4/XL (fixed pre-print temperature for PA filaments). 1.7.3 Updated XL and MK4 profiles. Updated PC Blend Carbon Fiber density. 1.7.2 Updated compatibility condition for MMU1 filaments. 1.7.1 Added SLA materials. Updated MK4 and XL profiles. 1.7.0 Added profiles for Original Prusa MK4. min_slic3r_version = 2.5.1-rc0 +1.6.6 Updated cooling settings for some ASA filaments to increase interlayer adhesion (XL/MK4). 1.6.5 Updated start g-code script for MK4/XL (fixed pre-print temperature for PA filaments). 1.6.4 Fixed compatibility condition for MMU1 filaments. 1.6.3 Added SLA materials. diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index 442f7bdebf..268171a1a8 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -5,7 +5,7 @@ name = Prusa Research # Configuration version of this file. Config file will only be installed, if the config_version differs. # This means, the server may force the PrusaSlicer configuration to be downgraded. -config_version = 1.9.0-beta0 +config_version = 1.9.0-beta1 # Where to get the updates from? config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/ changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1% @@ -3618,7 +3618,7 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no compatible_printers_condition = ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) and printer_notes!~/.*PG.*/ [filament:*PLAPG*] -start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.06{elsif nozzle_diameter[0]==0.25}0.14{elsif nozzle_diameter[0]==0.3}0.08{elsif nozzle_diameter[0]==0.35}0.07{elsif nozzle_diameter[0]==0.6}0.03{elsif nozzle_diameter[0]==0.5}0.035{elsif nozzle_diameter[0]==0.8}0.02{else}0{endif} ; Filament gcode\n\nM142 S36 ; set heatbreak target temp" +start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.05{elsif nozzle_diameter[0]==0.25}0.14{elsif nozzle_diameter[0]==0.3}0.07{elsif nozzle_diameter[0]==0.35}0.06{elsif nozzle_diameter[0]==0.6}0.03{elsif nozzle_diameter[0]==0.5}0.035{elsif nozzle_diameter[0]==0.8}0.015{else}0{endif} ; Filament gcode\n\nM142 S36 ; set heatbreak target temp" compatible_printers_condition = printer_notes=~/.*PG.*/ and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 slowdown_below_layer_time = 8 filament_cooling_final_speed = 2 @@ -3675,7 +3675,7 @@ filament_max_volumetric_speed = 15 [filament:*PETPG*] compatible_printers_condition = printer_notes=~/.*PG.*/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 filament_max_volumetric_speed = 10 -start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.08{elsif nozzle_diameter[0]==0.25}0.12{elsif nozzle_diameter[0]==0.3}0.1{elsif nozzle_diameter[0]==0.35}0.09{elsif nozzle_diameter[0]==0.6}0.04{elsif nozzle_diameter[0]==0.5}0.05{elsif nozzle_diameter[0]==0.8}0.02{else}0{endif} ; Filament gcode\n\nM142 S40 ; set heatbreak target temp" +start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.07{elsif nozzle_diameter[0]==0.25}0.12{elsif nozzle_diameter[0]==0.3}0.09{elsif nozzle_diameter[0]==0.35}0.08{elsif nozzle_diameter[0]==0.6}0.04{elsif nozzle_diameter[0]==0.5}0.05{elsif nozzle_diameter[0]==0.8}0.02{else}0{endif} ; Filament gcode\n\nM142 S40 ; set heatbreak target temp" filament_cooling_final_speed = 1 filament_cooling_initial_speed = 2 filament_cooling_moves = 1 @@ -4498,6 +4498,8 @@ filament_type = ASA [filament:Fillamentum ASA @PG] inherits = Fillamentum ASA; *ABSPG* bed_temperature = 105 +min_fan_speed = 10 +max_fan_speed = 10 [filament:Fillamentum ASA @PG 0.6] inherits = Fillamentum ASA @PG; *ABS06PG* @@ -4507,6 +4509,8 @@ inherits = Fillamentum ASA @PG; *ABS08PG* [filament:Fillamentum ASA @MK4] inherits = Fillamentum ASA; *ABSMK4* +min_fan_speed = 10 +max_fan_speed = 10 [filament:Fillamentum ASA @MK4 0.6] inherits = Fillamentum ASA @MK4; *ABS06MK4* @@ -4541,6 +4545,8 @@ compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI inherits = Prusament ASA; *ABSPG* first_layer_bed_temperature = 100 bed_temperature = 105 +min_fan_speed = 10 +max_fan_speed = 10 [filament:Prusament ASA @PG 0.6] inherits = Prusament ASA @PG; *ABS06PG* @@ -4552,6 +4558,8 @@ temperature = 265 [filament:Prusament ASA @MK4] inherits = Prusament ASA; *ABSMK4* +min_fan_speed = 10 +max_fan_speed = 10 [filament:Prusament ASA @MK4 0.6] inherits = Prusament ASA @MK4; *ABS06MK4* @@ -4590,6 +4598,8 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no [filament:Prusament PC Blend @PG] inherits = Prusament PC Blend; *PCPG* filament_max_volumetric_speed = 9 +min_fan_speed = 10 +max_fan_speed = 10 [filament:Prusament PC Blend @PG 0.6] inherits = Prusament PC Blend @PG; *PC06PG* @@ -4602,6 +4612,8 @@ filament_max_volumetric_speed = 18 [filament:Prusament PC Blend @MK4] inherits = Prusament PC Blend; *PCMK4* filament_max_volumetric_speed = 9 +min_fan_speed = 10 +max_fan_speed = 10 [filament:Prusament PC Blend @MK4 0.6] inherits = Prusament PC Blend @MK4; *PC06MK4* @@ -4634,24 +4646,28 @@ compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and [filament:Prusament PC Blend Carbon Fiber @PG] inherits = Prusament PC Blend Carbon Fiber; *PCPG* +min_fan_speed = 10 +max_fan_speed = 10 [filament:Prusament PC Blend Carbon Fiber @PG 0.6] -inherits = Prusament PC Blend Carbon Fiber; *PC06PG* +inherits = Prusament PC Blend Carbon Fiber @PG; *PC06PG* filament_max_volumetric_speed = 13 [filament:Prusament PC Blend Carbon Fiber @PG 0.8] -inherits = Prusament PC Blend Carbon Fiber; *PC08PG* +inherits = Prusament PC Blend Carbon Fiber @PG; *PC08PG* filament_max_volumetric_speed = 18 [filament:Prusament PC Blend Carbon Fiber @MK4] inherits = Prusament PC Blend Carbon Fiber; *PCMK4* +min_fan_speed = 10 +max_fan_speed = 10 [filament:Prusament PC Blend Carbon Fiber @MK4 0.6] -inherits = Prusament PC Blend Carbon Fiber; *PC06MK4* +inherits = Prusament PC Blend Carbon Fiber @MK4; *PC06MK4* filament_max_volumetric_speed = 13 [filament:Prusament PC Blend Carbon Fiber @MK4 0.8] -inherits = Prusament PC Blend Carbon Fiber; *PC08MK4* +inherits = Prusament PC Blend Carbon Fiber @MK4; *PC08MK4* filament_max_volumetric_speed = 18 [filament:Prusament PC Blend Carbon Fiber @MK2] @@ -7290,6 +7306,8 @@ inherits = Ultrafuse ASA; *ABSPG* first_layer_bed_temperature = 105 bed_temperature = 105 filament_max_volumetric_speed = 5 +min_fan_speed = 15 +max_fan_speed = 40 [filament:Ultrafuse ASA @PG 0.6] inherits = Ultrafuse ASA @PG; *ABS06PG* @@ -7302,6 +7320,8 @@ filament_max_volumetric_speed = 12 [filament:Ultrafuse ASA @MK4] inherits = Ultrafuse ASA; *ABSMK4* filament_max_volumetric_speed = 5 +min_fan_speed = 15 +max_fan_speed = 40 [filament:Ultrafuse ASA @MK4 0.6] inherits = Ultrafuse ASA @MK4; *ABS06MK4* From 6894af24ac5b0213b5577eafaffefab482897476 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 17 May 2023 10:48:51 +0200 Subject: [PATCH 90/90] Bumped up version to 2.6.0-beta2 --- version.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.inc b/version.inc index 0eecbcdc6f..f0615cbded 100644 --- a/version.inc +++ b/version.inc @@ -3,7 +3,7 @@ set(SLIC3R_APP_NAME "PrusaSlicer") set(SLIC3R_APP_KEY "PrusaSlicer") -set(SLIC3R_VERSION "2.6.0-beta1") +set(SLIC3R_VERSION "2.6.0-beta2") set(SLIC3R_BUILD_ID "PrusaSlicer-${SLIC3R_VERSION}+UNKNOWN") set(SLIC3R_RC_VERSION "2,6,0,0") set(SLIC3R_RC_VERSION_DOTS "2.6.0.0")