Bugfix in extrusion quality estimator, Refactoring of alerts, rename of autogenerate button

This commit is contained in:
PavelMikus 2023-01-25 14:18:14 +01:00 committed by Pavel Mikuš
parent a4de5c6553
commit c31e3ec1a2
5 changed files with 143 additions and 134 deletions

View File

@ -201,7 +201,7 @@ std::vector<ExtendedPoint> estimate_points_properties(const std::vector<P>
new_points.push_back(ExtendedPoint{pos, float(p_dist + boundary_offset), p_near_l}); 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; points = new_points;
} }

View File

@ -1,4 +1,5 @@
#include "Exception.hpp" #include "Exception.hpp"
#include "KDTreeIndirect.hpp"
#include "Point.hpp" #include "Point.hpp"
#include "Print.hpp" #include "Print.hpp"
#include "BoundingBox.hpp" #include "BoundingBox.hpp"
@ -34,6 +35,7 @@
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
#include <tbb/parallel_for.h> #include <tbb/parallel_for.h>
#include <vector>
using namespace std::literals; 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}; this->m_shared_regions->generated_support_points = {this->trafo_centered(), supp_points};
m_print->throw_if_canceled(); m_print->throw_if_canceled();
auto check_problems = [&]() { auto alert_fn = [&](PrintStateBase::WarningLevel level, SupportSpotsGenerator::SupportPointCause cause) {
std::unordered_map<SupportSpotsGenerator::SupportPointCause, SupportSpotsGenerator::SupportPoints> sp_by_cause{}; switch (cause) {
for (const SupportSpotsGenerator::SupportPoint &sp : supp_points) { case SupportSpotsGenerator::SupportPointCause::LongBridge:
sp_by_cause[sp.cause].push_back(sp); this->active_step_add_warning(level, L("There are bridges longer than allowed distance. Consider adding supports. "));
} break;
case SupportSpotsGenerator::SupportPointCause::FloatingBridgeAnchor:
if (!sp_by_cause[SupportSpotsGenerator::SupportPointCause::SeparationFromBed].empty()) { this->active_step_add_warning(level, L("Unsupported bridges will collapse. Supports are needed."));
this->active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL, break;
L("Object part may break from the bed. Consider adding brim and/or supports.")); case SupportSpotsGenerator::SupportPointCause::FloatingExtrusion:
} if (level == PrintStateBase::WarningLevel::CRITICAL) {
this->active_step_add_warning(level, L("Clusters of unsupported extrusions found. Supports are needed."));
std::reverse(partial_objects.begin(), partial_objects.end()); } else {
std::sort(partial_objects.begin(), partial_objects.end(), this->active_step_add_warning(level, L("Some unspported extrusions found. Consider adding supports. "));
[](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;
} }
} break;
case SupportSpotsGenerator::SupportPointCause::SeparationFromBed:
if (!sp_by_cause[SupportSpotsGenerator::SupportPointCause::WeakObjectPart].empty()) { 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, this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL,
L("Thin parts of the object may break. Please add supports.")); L("Thin parts of the object may break. Supports are needed."));
return; break;
}
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. "));
}
} }
}; };
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"; BOOST_LOG_TRIVIAL(debug) << "Searching support spots - end";
this->set_done(posSupportSpotsSearch); this->set_done(posSupportSpotsSearch);

View File

@ -37,7 +37,7 @@
#include "Geometry/ConvexHull.hpp" #include "Geometry/ConvexHull.hpp"
// #define DETAILED_DEBUG_LOGS // #define DETAILED_DEBUG_LOGS
// #define DEBUG_FILES #define DEBUG_FILES
#ifdef DEBUG_FILES #ifdef DEBUG_FILES
#include <boost/nowide/cstdio.hpp> #include <boost/nowide/cstdio.hpp>
@ -313,19 +313,16 @@ std::vector<ExtrusionLine> check_extrusion_entity_stability(const ExtrusionEntit
curr_point.position.cast<float>(), line_len, entity}; curr_point.position.cast<float>(), line_len, entity};
const ExtrusionLine nearest_prev_layer_line = prev_layer_lines.get_lines().size() > 0 ? const ExtrusionLine nearest_prev_layer_line = prev_layer_lines.get_lines().size() > 0 ?
prev_layer_lines.get_line(curr_point.nearest_prev_layer_line) : prev_layer_lines.get_line(curr_point.nearest_prev_layer_line) :
ExtrusionLine{}; ExtrusionLine{};
// correctify the distance sign using slice polygons // correctify the distance sign using slice polygons
float sign = (prev_layer_boundary.distance_from_lines<true>(curr_point.position) + 0.5f * flow_width) < 0.0f ? -1.0f : 1.0f; float sign = (prev_layer_boundary.distance_from_lines<true>(curr_point.position) + 0.5f * flow_width) < 0.0f ? -1.0f : 1.0f;
curr_point.distance *= sign; curr_point.distance *= sign;
SupportPointCause potential_cause = SupportPointCause::FloatingExtrusion; SupportPointCause potential_cause = SupportPointCause::FloatingExtrusion;
if (curr_point.distance > flow_width * 5.0) { if (bridged_distance + line_len > params.bridge_distance * 0.8 && std::abs(curr_point.curvature) < 0.1) {
if (std::abs(curr_point.curvature) > 0.1) potential_cause = SupportPointCause::FloatingExtrusion;
potential_cause = SupportPointCause::LongUnsupportedExtrusion;
else
potential_cause = SupportPointCause::LongBridge;
} }
float max_bridge_len = std::max(params.support_points_interface_radius * 2.0f, float max_bridge_len = std::max(params.support_points_interface_radius * 2.0f,
@ -337,6 +334,14 @@ std::vector<ExtrusionLine> check_extrusion_entity_stability(const ExtrusionEntit
line_out.form_quality = 0.8f; line_out.form_quality = 0.8f;
bridged_distance += line_len; bridged_distance += line_len;
if (bridged_distance > max_bridge_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; line_out.support_point_generated = potential_cause;
bridged_distance = 0.0f; bridged_distance = 0.0f;
} }
@ -344,6 +349,14 @@ std::vector<ExtrusionLine> check_extrusion_entity_stability(const ExtrusionEntit
bridged_distance += line_len; bridged_distance += line_len;
line_out.form_quality = nearest_prev_layer_line.form_quality - 0.3f; line_out.form_quality = nearest_prev_layer_line.form_quality - 0.3f;
if (line_out.form_quality < 0 && bridged_distance > max_bridge_len) { 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.support_point_generated = potential_cause;
line_out.form_quality = 0.5f; line_out.form_quality = 0.5f;
bridged_distance = 0.0f; bridged_distance = 0.0f;
@ -557,7 +570,7 @@ public:
params.material_yield_strength; params.material_yield_strength;
float conn_weight_arm = (conn_centroid.head<2>() - mass_centroid.head<2>()).norm(); 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_arm = std::max(0.0f, mass_centroid.z() - conn_centroid.z());
float conn_movement_torque = movement_force * conn_movement_arm; 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::FloatingBridgeAnchor: color = {0.863281f, 0.109375f, 0.113281f}; break; //RED
case SupportPointCause::LongBridge: color = {0.960938f, 0.90625f, 0.0625f}; break; // YELLOW 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::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::SeparationFromBed: color = {0.0f, 1.0f, 0.0}; break; // GREEN
case SupportPointCause::UnstableFloatingPart: color = {0.105469f, 0.699219f, 0.84375f}; break; // BLUE case SupportPointCause::UnstableFloatingPart: color = {0.105469f, 0.699219f, 0.84375f}; break; // BLUE
case SupportPointCause::WeakObjectPart: color = {0.609375f, 0.210938f, 0.621094f}; break; // PURPLE case SupportPointCause::WeakObjectPart: color = {0.609375f, 0.210938f, 0.621094f}; break; // PURPLE
@ -1104,5 +1116,84 @@ void estimate_malformations(LayerPtrs &layers, const Params &params)
#endif #endif
} }
void raise_alerts_for_issues(const SupportPoints &support_points,
PartialObjects &partial_objects,
std::function<void(PrintStateBase::WarningLevel, SupportPointCause)> 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<SupportPoint> 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 SupportSpotsGenerator
} // namespace Slic3r } // namespace Slic3r

View File

@ -77,7 +77,6 @@ enum class SupportPointCause {
LongBridge, // point generated on bridge and straight perimeter extrusion longer than the allowed length LongBridge, // point generated on bridge and straight perimeter extrusion longer than the allowed length
FloatingBridgeAnchor, // point generated on unsupported bridge endpoint FloatingBridgeAnchor, // point generated on unsupported bridge endpoint
FloatingExtrusion, // point generated on extrusion that does not hold on its own 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) 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) 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) 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<PartialObject>;
std::tuple<SupportPoints, PartialObjects> full_search(const PrintObject *po, const PrintTryCancel& cancel_func, const Params &params); std::tuple<SupportPoints, PartialObjects> full_search(const PrintObject *po, const PrintTryCancel& cancel_func, const Params &params);
void estimate_supports_malformations(std::vector<SupportLayer*> &layers, float supports_flow_width, const Params &params); void estimate_supports_malformations(std::vector<SupportLayer *> &layers, float supports_flow_width, const Params &params);
void estimate_malformations(std::vector<Layer*> &layers, const Params &params); void estimate_malformations(std::vector<Layer *> &layers, const Params &params);
} // namespace SupportSpotsGenerator void raise_alerts_for_issues(const SupportPoints &support_points,
} PartialObjects &partial_objects,
std::function<void(PrintStateBase::WarningLevel, SupportPointCause)> alert_fn);
}} // namespace Slic3r::SupportSpotsGenerator
#endif /* SRC_LIBSLIC3R_SUPPORTABLEISSUESSEARCH_HPP_ */ #endif /* SRC_LIBSLIC3R_SUPPORTABLEISSUESSEARCH_HPP_ */

View File

@ -43,8 +43,8 @@ bool GLGizmoFdmSupports::on_init()
{ {
m_shortcut_key = WXK_CONTROL_L; m_shortcut_key = WXK_CONTROL_L;
m_desc["auto_generate"] = _L("Auto-generate supports"); m_desc["autopaint"] = _L("Automatic painting");
m_desc["generating"] = _L("Generating supports..."); m_desc["painting"] = _L("painting...");
m_desc["clipping_of_view"] = _L("Clipping of view") + ": "; m_desc["clipping_of_view"] = _L("Clipping of view") + ": ";
m_desc["reset_direction"] = _L("Reset direction"); m_desc["reset_direction"] = _L("Reset direction");
m_desc["cursor_size"] = _L("Brush size") + ": "; 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(); ImGui::Separator();
if (waiting_for_autogenerated_supports) { if (waiting_for_autogenerated_supports) {
m_imgui->text(m_desc.at("generating")); m_imgui->text(m_desc.at("painting"));
} else { } else {
bool generate = m_imgui->button(m_desc.at("auto_generate")); bool generate = m_imgui->button(m_desc.at("autopaint"));
if (generate) if (generate)
auto_generate(); auto_generate();
} }
@ -525,12 +525,12 @@ void GLGizmoFdmSupports::auto_generate()
}); });
MessageDialog dlg(GUI::wxGetApp().plater(), 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("Are you sure you want to do it?") + "\n",
_L("Warning"), wxICON_WARNING | wxYES | wxNO); _L("Warning"), wxICON_WARNING | wxYES | wxNO);
if (not_painted || dlg.ShowModal() == wxID_YES) { 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; int mesh_id = -1.0f;
for (ModelVolume *mv : mo->volumes) { for (ModelVolume *mv : mo->volumes) {
if (mv->is_model_part()) { if (mv->is_model_part()) {