diff --git a/src/libslic3r/GCode/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp index 7d8cc4c73..1525624da 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.hpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -201,7 +201,7 @@ std::vector estimate_points_properties(const std::vector

new_points.push_back(ExtendedPoint{pos, float(p_dist + boundary_offset), p_near_l}); } } - new_points.push_back(new_points.back()); + new_points.push_back(points.back()); } points = new_points; } diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index f163bfbc8..09b35157c 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1,4 +1,5 @@ #include "Exception.hpp" +#include "KDTreeIndirect.hpp" #include "Point.hpp" #include "Print.hpp" #include "BoundingBox.hpp" @@ -34,6 +35,7 @@ #include #include +#include using namespace std::literals; @@ -425,123 +427,37 @@ void PrintObject::generate_support_spots() this->m_shared_regions->generated_support_points = {this->trafo_centered(), supp_points}; m_print->throw_if_canceled(); - auto check_problems = [&]() { - std::unordered_map sp_by_cause{}; - for (const SupportSpotsGenerator::SupportPoint &sp : supp_points) { - sp_by_cause[sp.cause].push_back(sp); - } - - if (!sp_by_cause[SupportSpotsGenerator::SupportPointCause::SeparationFromBed].empty()) { - this->active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL, - L("Object part may break from the bed. Consider adding brim and/or supports.")); - } - - std::reverse(partial_objects.begin(), partial_objects.end()); - std::sort(partial_objects.begin(), partial_objects.end(), - [](const SupportSpotsGenerator::PartialObject &left, const SupportSpotsGenerator::PartialObject &right) { - return left.volume > right.volume; - }); - - float max_volume_part = partial_objects.front().volume; - for (const SupportSpotsGenerator::PartialObject &p : partial_objects) { - if (p.volume > max_volume_part / 1000.0f && !p.connected_to_bed) { - this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, - L("Floating object parts detected. Please add supports.")); - return; + auto alert_fn = [&](PrintStateBase::WarningLevel level, SupportSpotsGenerator::SupportPointCause cause) { + switch (cause) { + case SupportSpotsGenerator::SupportPointCause::LongBridge: + this->active_step_add_warning(level, L("There are bridges longer than allowed distance. Consider adding supports. ")); + break; + case SupportSpotsGenerator::SupportPointCause::FloatingBridgeAnchor: + this->active_step_add_warning(level, L("Unsupported bridges will collapse. Supports are needed.")); + break; + case SupportSpotsGenerator::SupportPointCause::FloatingExtrusion: + if (level == PrintStateBase::WarningLevel::CRITICAL) { + this->active_step_add_warning(level, L("Clusters of unsupported extrusions found. Supports are needed.")); + } else { + this->active_step_add_warning(level, L("Some unspported extrusions found. Consider adding supports. ")); } - } - - if (!sp_by_cause[SupportSpotsGenerator::SupportPointCause::WeakObjectPart].empty()) { + break; + case SupportSpotsGenerator::SupportPointCause::SeparationFromBed: + this->active_step_add_warning(level, L("Object part may break from the bed. Consider adding brim and/or supports.")); + break; + case SupportSpotsGenerator::SupportPointCause::UnstableFloatingPart: + this->active_step_add_warning(level, L("Floating object parts detected. Supports are needed.")); + break; + case SupportSpotsGenerator::SupportPointCause::WeakObjectPart: this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, - L("Thin parts of the object may break. Please add supports.")); - return; - } - - if (!sp_by_cause[SupportSpotsGenerator::SupportPointCause::FloatingBridgeAnchor].empty()) { - Vec3f last_pos = Vec3f::Zero(); - size_t count = 0; - for (const SupportSpotsGenerator::SupportPoint &sp : - sp_by_cause[SupportSpotsGenerator::SupportPointCause::FloatingBridgeAnchor]) { - if ((sp.position - last_pos).squaredNorm() < 9.0f) { - count++; - last_pos = sp.position; - } else { - last_pos = sp.position; - count = 1; - } - if (count > 1) { - this->active_step_add_warning( - PrintStateBase::WarningLevel::CRITICAL, - L("Bridges without supported endpoints will collapse. Please add supports. ")); - break; - } - } - } - - if (!sp_by_cause[SupportSpotsGenerator::SupportPointCause::LongUnsupportedExtrusion].empty()) { - Vec3f last_pos = Vec3f::Zero(); - size_t count = 0; - for (const SupportSpotsGenerator::SupportPoint &sp : - sp_by_cause[SupportSpotsGenerator::SupportPointCause::LongUnsupportedExtrusion]) { - if ((sp.position - last_pos).squaredNorm() < 9.0f) { - count++; - last_pos = sp.position; - } else { - last_pos = sp.position; - count = 1; - } - if (count > 1) { - this->active_step_add_warning( - PrintStateBase::WarningLevel::CRITICAL, - L("Long unsupported extrusions will collapse. Please add supports. ")); - break; - } - } - } - - if (!sp_by_cause[SupportSpotsGenerator::SupportPointCause::LongBridge].empty()) { - this->active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL, - L("There are bridges longer than allowed distance. Consider adding supports. ")); - } - - if (!sp_by_cause[SupportSpotsGenerator::SupportPointCause::FloatingExtrusion].empty()) { - Vec3f last_pos = Vec3f::Zero(); - size_t count = 0; - bool small_warning = false; - for (const SupportSpotsGenerator::SupportPoint &sp : - sp_by_cause[SupportSpotsGenerator::SupportPointCause::FloatingExtrusion]) { - if ((sp.position - last_pos).squaredNorm() < - params.bridge_distance + EPSILON) { - count++; - last_pos = sp.position; - } else { - if (count > 6) { - this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, - L("Object has large part with loose extrusions. Please enable supports. ")); - small_warning = false; - break; - } - if (count > 3) { - small_warning = true; - } - - last_pos = sp.position; - count = 1; - } - } - if (small_warning) { - this->active_step_add_warning( - PrintStateBase::WarningLevel::NON_CRITICAL, - L("Object has parts with loose extrusions and may look bad. Consider enabling supports. ")); - } else if (sp_by_cause[SupportSpotsGenerator::SupportPointCause::FloatingExtrusion].size() > max_volume_part / 100) { - this->active_step_add_warning( - PrintStateBase::WarningLevel::NON_CRITICAL, - L("There are many loose surface extrusions on this object. Consider enabling supports. ")); - } + L("Thin parts of the object may break. Supports are needed.")); + break; } }; - check_problems(); + if (!this->has_support()) { + SupportSpotsGenerator::raise_alerts_for_issues(supp_points, partial_objects, alert_fn); + } } BOOST_LOG_TRIVIAL(debug) << "Searching support spots - end"; this->set_done(posSupportSpotsSearch); diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 464ee1d67..7eec832c1 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -37,7 +37,7 @@ #include "Geometry/ConvexHull.hpp" // #define DETAILED_DEBUG_LOGS -// #define DEBUG_FILES +#define DEBUG_FILES #ifdef DEBUG_FILES #include @@ -313,19 +313,16 @@ std::vector check_extrusion_entity_stability(const ExtrusionEntit curr_point.position.cast(), line_len, entity}; const ExtrusionLine nearest_prev_layer_line = prev_layer_lines.get_lines().size() > 0 ? - prev_layer_lines.get_line(curr_point.nearest_prev_layer_line) : - ExtrusionLine{}; + prev_layer_lines.get_line(curr_point.nearest_prev_layer_line) : + ExtrusionLine{}; // correctify the distance sign using slice polygons float sign = (prev_layer_boundary.distance_from_lines(curr_point.position) + 0.5f * flow_width) < 0.0f ? -1.0f : 1.0f; curr_point.distance *= sign; SupportPointCause potential_cause = SupportPointCause::FloatingExtrusion; - if (curr_point.distance > flow_width * 5.0) { - if (std::abs(curr_point.curvature) > 0.1) - potential_cause = SupportPointCause::LongUnsupportedExtrusion; - else - potential_cause = SupportPointCause::LongBridge; + if (bridged_distance + line_len > params.bridge_distance * 0.8 && std::abs(curr_point.curvature) < 0.1) { + potential_cause = SupportPointCause::FloatingExtrusion; } float max_bridge_len = std::max(params.support_points_interface_radius * 2.0f, @@ -337,6 +334,14 @@ std::vector check_extrusion_entity_stability(const ExtrusionEntit line_out.form_quality = 0.8f; bridged_distance += line_len; if (bridged_distance > max_bridge_len) { + std::cout << "Problem found A: " << std::endl; + std::cout << "bridged_distance: " << bridged_distance << std::endl; + std::cout << "max_bridge_len: " << max_bridge_len << std::endl; + std::cout << "line_out.form_quality: " << line_out.form_quality << std::endl; + std::cout << "curr_point.distance: " << curr_point.distance << std::endl; + std::cout << "curr_point.curvature: " << curr_point.curvature << std::endl; + std::cout << "flow_width: " << flow_width << std::endl; + line_out.support_point_generated = potential_cause; bridged_distance = 0.0f; } @@ -344,6 +349,14 @@ std::vector check_extrusion_entity_stability(const ExtrusionEntit bridged_distance += line_len; line_out.form_quality = nearest_prev_layer_line.form_quality - 0.3f; if (line_out.form_quality < 0 && bridged_distance > max_bridge_len) { + std::cout << "Problem found B: " << std::endl; + std::cout << "bridged_distance: " << bridged_distance << std::endl; + std::cout << "max_bridge_len: " << max_bridge_len << std::endl; + std::cout << "line_out.form_quality: " << line_out.form_quality << std::endl; + std::cout << "curr_point.distance: " << curr_point.distance << std::endl; + std::cout << "curr_point.curvature: " << curr_point.curvature << std::endl; + std::cout << "flow_width: " << flow_width << std::endl; + line_out.support_point_generated = potential_cause; line_out.form_quality = 0.5f; bridged_distance = 0.0f; @@ -557,7 +570,7 @@ public: params.material_yield_strength; float conn_weight_arm = (conn_centroid.head<2>() - mass_centroid.head<2>()).norm(); - float conn_weight_torque = conn_weight_arm * weight * (1.0f - conn_centroid.z() / layer_z); + float conn_weight_torque = conn_weight_arm * weight * (1.0f - conn_centroid.z() / layer_z) * (1.0f - conn_centroid.z() / layer_z); float conn_movement_arm = std::max(0.0f, mass_centroid.z() - conn_centroid.z()); float conn_movement_torque = movement_force * conn_movement_arm; @@ -925,7 +938,6 @@ void debug_export(const SupportPoints& support_points,const PartialObjects& obje case SupportPointCause::FloatingBridgeAnchor: color = {0.863281f, 0.109375f, 0.113281f}; break; //RED case SupportPointCause::LongBridge: color = {0.960938f, 0.90625f, 0.0625f}; break; // YELLOW case SupportPointCause::FloatingExtrusion: color = {0.921875f, 0.515625f, 0.101563f}; break; // ORANGE - case SupportPointCause::LongUnsupportedExtrusion: color = {0.863281f, 0.109375f, 0.113281f}; break; // RED case SupportPointCause::SeparationFromBed: color = {0.0f, 1.0f, 0.0}; break; // GREEN case SupportPointCause::UnstableFloatingPart: color = {0.105469f, 0.699219f, 0.84375f}; break; // BLUE case SupportPointCause::WeakObjectPart: color = {0.609375f, 0.210938f, 0.621094f}; break; // PURPLE @@ -1104,5 +1116,84 @@ void estimate_malformations(LayerPtrs &layers, const Params ¶ms) #endif } +void raise_alerts_for_issues(const SupportPoints &support_points, + PartialObjects &partial_objects, + std::function alert_fn) +{ + for (const SupportPoint &sp : support_points) { + if (sp.cause == SupportPointCause::SeparationFromBed) { + alert_fn(PrintStateBase::WarningLevel::NON_CRITICAL, SupportPointCause::SeparationFromBed); + break; + } + } + + std::reverse(partial_objects.begin(), partial_objects.end()); + std::sort(partial_objects.begin(), partial_objects.end(), + [](const PartialObject &left, const PartialObject &right) { return left.volume > right.volume; }); + + float max_volume_part = partial_objects.front().volume; + for (const PartialObject &p : partial_objects) { + if (p.volume > max_volume_part / 500.0f && !p.connected_to_bed) { + alert_fn(PrintStateBase::WarningLevel::CRITICAL, SupportPointCause::UnstableFloatingPart); + return; + } + } + + for (const SupportPoint &sp : support_points) { + if (sp.cause == SupportPointCause::UnstableFloatingPart) { + alert_fn(PrintStateBase::WarningLevel::CRITICAL, SupportPointCause::UnstableFloatingPart); + return; + } + } + + for (const SupportPoint &sp : support_points) { + if (sp.cause == SupportPointCause::WeakObjectPart) { + alert_fn(PrintStateBase::WarningLevel::CRITICAL, SupportPointCause::WeakObjectPart); + return; + } + } + + std::vector ext_supp_points{}; + ext_supp_points.reserve(support_points.size()); + for (const SupportPoint &sp : support_points) { + switch (sp.cause) { + case SupportPointCause::FloatingBridgeAnchor: + case SupportPointCause::FloatingExtrusion: ext_supp_points.push_back(sp); break; + default: break; + } + } + + auto coord_fn = [&ext_supp_points](size_t idx, size_t dim) { return ext_supp_points[idx].position[dim]; }; + KDTreeIndirect<3, float, decltype(coord_fn)> ext_points_tree{coord_fn, ext_supp_points.size()}; + for (const SupportPoint &sp : ext_supp_points) { + auto cluster = find_nearby_points(ext_points_tree, sp.position, 3.0); + int score = 0; + bool floating_bridge = false; + for (size_t idx : cluster) { + score += ext_supp_points[idx].cause == SupportPointCause::FloatingBridgeAnchor ? 3 : 1; + floating_bridge = floating_bridge || ext_supp_points[idx].cause == SupportPointCause::FloatingBridgeAnchor; + } + if (score > 5) { + if (floating_bridge) { + alert_fn(PrintStateBase::WarningLevel::CRITICAL, SupportPointCause::FloatingBridgeAnchor); + } else { + alert_fn(PrintStateBase::WarningLevel::CRITICAL, SupportPointCause::FloatingExtrusion); + } + return; + } + } + + if (ext_supp_points.size() > 5) { + alert_fn(PrintStateBase::WarningLevel::NON_CRITICAL, SupportPointCause::FloatingExtrusion); + } + + for (const SupportPoint &sp : support_points) { + if (sp.cause == SupportPointCause::LongBridge) { + alert_fn(PrintStateBase::WarningLevel::CRITICAL, SupportPointCause::LongBridge); + return; + } + } +} + } // namespace SupportSpotsGenerator } // namespace Slic3r diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index 56762d544..bd3301874 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -77,7 +77,6 @@ enum class SupportPointCause { LongBridge, // point generated on bridge and straight perimeter extrusion longer than the allowed length FloatingBridgeAnchor, // point generated on unsupported bridge endpoint FloatingExtrusion, // point generated on extrusion that does not hold on its own - LongUnsupportedExtrusion, // similar to above, but with large distance to object. This really needs supports. SeparationFromBed, // point generated for object parts that are connected to the bed, but the area is too small and there is a risk of separation (brim may help) UnstableFloatingPart, // point generated for object parts not connected to the bed, holded only by the other support points (brim will not help here) WeakObjectPart // point generated when some part of the object is too weak to hold the upper part and may break (imagine hourglass) @@ -143,10 +142,13 @@ using PartialObjects = std::vector; std::tuple full_search(const PrintObject *po, const PrintTryCancel& cancel_func, const Params ¶ms); -void estimate_supports_malformations(std::vector &layers, float supports_flow_width, const Params ¶ms); -void estimate_malformations(std::vector &layers, const Params ¶ms); +void estimate_supports_malformations(std::vector &layers, float supports_flow_width, const Params ¶ms); +void estimate_malformations(std::vector &layers, const Params ¶ms); -} // namespace SupportSpotsGenerator -} +void raise_alerts_for_issues(const SupportPoints &support_points, + PartialObjects &partial_objects, + std::function alert_fn); + +}} // namespace Slic3r::SupportSpotsGenerator #endif /* SRC_LIBSLIC3R_SUPPORTABLEISSUESSEARCH_HPP_ */ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 6e12527d5..ec993fcd4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -43,8 +43,8 @@ bool GLGizmoFdmSupports::on_init() { m_shortcut_key = WXK_CONTROL_L; - m_desc["auto_generate"] = _L("Auto-generate supports"); - m_desc["generating"] = _L("Generating supports..."); + m_desc["autopaint"] = _L("Automatic painting"); + m_desc["painting"] = _L("painting..."); m_desc["clipping_of_view"] = _L("Clipping of view") + ": "; m_desc["reset_direction"] = _L("Reset direction"); m_desc["cursor_size"] = _L("Brush size") + ": "; @@ -160,9 +160,9 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l ImGui::Separator(); if (waiting_for_autogenerated_supports) { - m_imgui->text(m_desc.at("generating")); + m_imgui->text(m_desc.at("painting")); } else { - bool generate = m_imgui->button(m_desc.at("auto_generate")); + bool generate = m_imgui->button(m_desc.at("autopaint")); if (generate) auto_generate(); } @@ -525,12 +525,12 @@ void GLGizmoFdmSupports::auto_generate() }); MessageDialog dlg(GUI::wxGetApp().plater(), - _L("Autogeneration will erase all currently painted areas.") + "\n\n" + + _L("Automatic painting will erase all currently painted areas.") + "\n\n" + _L("Are you sure you want to do it?") + "\n", _L("Warning"), wxICON_WARNING | wxYES | wxNO); if (not_painted || dlg.ShowModal() == wxID_YES) { - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Autogenerate support points")); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Automatic painting support points")); int mesh_id = -1.0f; for (ModelVolume *mv : mo->volumes) { if (mv->is_model_part()) {