greatly upgraded the algorithm for support placement -

added dynamic splitting of long paths,
included flow width of current and previous layer,
refactored and renamed parameters
This commit is contained in:
PavelMikus 2022-04-11 17:20:29 +02:00
parent e516ba0dd0
commit d41b20547d
5 changed files with 147 additions and 88 deletions

View File

@ -18,6 +18,7 @@
#include "Format/STL.hpp" #include "Format/STL.hpp"
#include "SupportableIssuesSearch.hpp" #include "SupportableIssuesSearch.hpp"
#include "TriangleSelectorWrapper.hpp" #include "TriangleSelectorWrapper.hpp"
#include "format.hpp"
#include <float.h> #include <float.h>
#include <string_view> #include <string_view>
@ -397,18 +398,26 @@ void PrintObject::ironing()
} }
} }
void PrintObject::find_supportable_issues() void PrintObject::find_supportable_issues()
{ {
if (this->set_started(posSupportableIssuesSearch)) { if (this->set_started(posSupportableIssuesSearch)) {
BOOST_LOG_TRIVIAL(debug) BOOST_LOG_TRIVIAL(debug)
<< "Searching supportable issues - start"; << "Searching supportable issues - start";
//TODO status number?
m_print->set_status(75, L("Searching supportable issues")); m_print->set_status(75, L("Searching supportable issues"));
if (!this->m_config.support_material) { if (!this->m_config.support_material) {
std::vector<size_t> problematic_layers = SupportableIssues::quick_search(this); std::vector<size_t> problematic_layers = SupportableIssues::quick_search(this);
if (!problematic_layers.empty()){ if (!problematic_layers.empty()) {
//TODO report problems std::cout << "Object needs supports" << std::endl;
this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL,
L("Supportable issues found. Consider enabling supports for this object"));
this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL,
L("Supportable issues found. Consider enabling supports for this object"));
for (size_t index = 0; index < std::min(problematic_layers.size(), size_t(4)); ++index) {
this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL,
format(L("Layer with issues: %1%"), problematic_layers[index] + 1));
}
} }
} else { } else {
SupportableIssues::Issues issues = SupportableIssues::full_search(this); SupportableIssues::Issues issues = SupportableIssues::full_search(this);
@ -421,7 +430,7 @@ void PrintObject::find_supportable_issues()
TriangleSelectorWrapper selector { model_volume->mesh() }; TriangleSelectorWrapper selector { model_volume->mesh() };
for (const Vec3f &support_point : issues.supports_nedded) { for (const Vec3f &support_point : issues.supports_nedded) {
selector.enforce_spot(Vec3f(inv_transform.cast<float>() * support_point), 1.0f); selector.enforce_spot(Vec3f(inv_transform.cast<float>() * support_point), 0.3f);
} }
model_volume->supported_facets.set(selector.selector); model_volume->supported_facets.set(selector.selector);
@ -430,7 +439,7 @@ void PrintObject::find_supportable_issues()
indexed_triangle_set copy = model_volume->mesh().its; indexed_triangle_set copy = model_volume->mesh().its;
its_transform(copy, obj_transform * model_transformation); its_transform(copy, obj_transform * model_transformation);
its_write_obj(copy, its_write_obj(copy,
debug_out_path(("model" + std::to_string(model_volume->id().id) + ".obj").c_str()).c_str()); debug_out_path("model.obj").c_str());
#endif #endif
} }
} }

View File

@ -5,6 +5,7 @@
#include "tbb/parallel_reduce.h" #include "tbb/parallel_reduce.h"
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
#include <cmath> #include <cmath>
#include <stack>
#include "libslic3r/Layer.hpp" #include "libslic3r/Layer.hpp"
#include "libslic3r/EdgeGrid.hpp" #include "libslic3r/EdgeGrid.hpp"
@ -39,14 +40,24 @@ struct LayerDescriptor {
}; };
struct EdgeGridWrapper { struct EdgeGridWrapper {
EdgeGridWrapper(coord_t resolution, ExPolygons ex_polys) : EdgeGridWrapper(coord_t edge_width, ExPolygons ex_polys) :
ex_polys(ex_polys) { ex_polys(ex_polys), edge_width(edge_width) {
grid.create(this->ex_polys, resolution); grid.create(this->ex_polys, edge_width);
grid.calculate_sdf(); grid.calculate_sdf();
} }
bool signed_distance(const Point &point, coordf_t point_width, coordf_t &dist_out) const {
coordf_t tmp_dist_out;
bool found = grid.signed_distance(point, edge_width, tmp_dist_out);
dist_out = tmp_dist_out - edge_width / 2 - point_width / 2;
return found;
}
EdgeGrid::Grid grid; EdgeGrid::Grid grid;
ExPolygons ex_polys; ExPolygons ex_polys;
coord_t edge_width;
}; };
#ifdef DEBUG_FILES #ifdef DEBUG_FILES
@ -99,8 +110,13 @@ EdgeGridWrapper compute_layer_edge_grid(const Layer *layer) {
for (const LayerRegion *layer_region : layer->regions()) { for (const LayerRegion *layer_region : layer->regions()) {
for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) {
for (const ExtrusionEntity *perimeter : static_cast<const ExtrusionEntityCollection*>(ex_entity)->entities) { for (const ExtrusionEntity *perimeter : static_cast<const ExtrusionEntityCollection*>(ex_entity)->entities) {
if (perimeter->role() == ExtrusionRole::erExternalPerimeter) { if (perimeter->role() == ExtrusionRole::erExternalPerimeter
ex_polygons.push_back(ExPolygon { perimeter->as_polyline().points }); || perimeter->role() == ExtrusionRole::erOverhangPerimeter) {
Points perimeter_points { };
perimeter->collect_points(perimeter_points);
assert(perimeter->is_loop());
perimeter_points.pop_back();
ex_polygons.push_back(ExPolygon { perimeter_points });
} // ex_perimeter } // ex_perimeter
} // perimeter } // perimeter
} // ex_entity } // ex_entity
@ -109,7 +125,16 @@ EdgeGridWrapper compute_layer_edge_grid(const Layer *layer) {
return EdgeGridWrapper(scale_(min_region_flow_width), ex_polygons); return EdgeGridWrapper(scale_(min_region_flow_width), ex_polygons);
} }
Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, size_t layer_idx, coordf_t get_max_allowed_distance(ExtrusionRole role, coord_t flow_width, const Params &params) { // <= distance / flow_width (can be larger for perimeter, if not external perimeter first)
if (!params.external_perimeter_first
&& (role == ExtrusionRole::erExternalPerimeter || role == ExtrusionRole::erOverhangPerimeter)) {
return params.max_ex_perim_unsupported_distance_factor * flow_width;
} else {
return params.max_unsupported_distance_factor * flow_width;
}
}
Issues check_extrusion_entity_stability(const ExtrusionEntity *entity,
float slice_z, float slice_z,
coordf_t flow_width, coordf_t flow_width,
const EdgeGridWrapper &supported_grid, const EdgeGridWrapper &supported_grid,
@ -118,74 +143,87 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, size_t la
Issues issues { }; Issues issues { };
if (entity->is_collection()) { if (entity->is_collection()) {
for (const auto *e : static_cast<const ExtrusionEntityCollection*>(entity)->entities) { for (const auto *e : static_cast<const ExtrusionEntityCollection*>(entity)->entities) {
issues.add(check_extrusion_entity_stability(e, layer_idx, slice_z, flow_width, supported_grid, params)); issues.add(check_extrusion_entity_stability(e, slice_z, flow_width, supported_grid, params));
} }
} else { //single extrusion path, with possible varying parameters } else { //single extrusion path, with possible varying parameters
Points points = entity->as_polyline().points; std::stack<Point> points { };
for (const auto &p : entity->as_polyline().points) {
points.push(p);
}
float unsupported_distance = params.bridge_distance + 1.0f; float unsupported_distance = params.bridge_distance + 1.0f;
float curvature = 0; float curvature = 0;
float max_curvature = 0; float max_curvature = 0;
Vec2f tmp = unscale(points[0]).cast<float>(); Vec2f tmp = unscale(points.top()).cast<float>();
Vec3f prev_point = Vec3f(tmp.x(), tmp.y(), slice_z); Vec3f prev_fpoint = Vec3f(tmp.x(), tmp.y(), slice_z);
for (size_t point_index = 0; point_index < points.size(); ++point_index) { const coordf_t max_allowed_dist_from_prev_layer = get_max_allowed_distance(entity->role(), flow_width,
std::cout << "index: " << point_index << " dist: " << unsupported_distance << " curvature: " params);
<< curvature << " max curvature: " << max_curvature << std::endl;
Vec2f tmp = unscale(points[point_index]).cast<float>(); while (!points.empty()) {
Vec3f u_point = Vec3f(tmp.x(), tmp.y(), slice_z); Point point = points.top();
points.pop();
Vec2f tmp = unscale(point).cast<float>();
Vec3f fpoint = Vec3f(tmp.x(), tmp.y(), slice_z);
coordf_t dist_from_prev_layer { 0 }; coordf_t dist_from_prev_layer { 0 };
if (!supported_grid.grid.signed_distance(points[point_index], flow_width, dist_from_prev_layer)) { if (!supported_grid.signed_distance(point, flow_width, dist_from_prev_layer)) {
issues.supports_nedded.push_back(u_point); issues.supports_nedded.push_back(fpoint);
continue; continue;
} }
constexpr float limit_overlap_factor = 0.5; if (dist_from_prev_layer > max_allowed_dist_from_prev_layer) { //unsupported
unsupported_distance += (fpoint - prev_fpoint).norm();
if (dist_from_prev_layer > flow_width) { //unsupported if (!points.empty()) {
std::cout << "index: " << point_index << " unsupported " << std::endl; const Vec2f v1 = (fpoint - prev_fpoint).head<2>();
unsupported_distance += (u_point - prev_point).norm(); const Vec2f v2 = unscale(points.top()).cast<float>() - fpoint.head<2>();
} else {
std::cout << "index: " << point_index << " grounded " << std::endl;
unsupported_distance = 0;
}
std::cout << "index: " << point_index << " dfromprev: " << dist_from_prev_layer << std::endl;
if (dist_from_prev_layer > flow_width * limit_overlap_factor && point_index < points.size() - 1) {
const Vec2f v1 = (u_point - prev_point).head<2>();
const Vec2f v2 = unscale(points[point_index + 1]).cast<float>() - u_point.head<2>();
float dot = v1(0) * v2(0) + v1(1) * v2(1); float dot = v1(0) * v2(0) + v1(1) * v2(1);
float cross = v1(0) * v2(1) - v1(1) * v2(0); float cross = v1(0) * v2(1) - v1(1) * v2(0);
float angle = float(atan2(float(cross), float(dot))); float angle = float(atan2(float(cross), float(dot)));
std::cout << "index: " << point_index << " angle: " << angle << std::endl;
curvature += angle; curvature += angle;
max_curvature = std::max(abs(curvature), max_curvature); max_curvature = std::max(abs(curvature), max_curvature);
} }
if (!(dist_from_prev_layer > flow_width * limit_overlap_factor)) { if (unsupported_distance
std::cout << "index: " << point_index << " reset curvature" << std::endl; > params.bridge_distance
max_curvature = 0; / (1.0f + (max_curvature * params.bridge_distance_decrease_by_curvature_factor / PI))) {
curvature = 0; issues.supports_nedded.push_back(fpoint);
}
if (unsupported_distance > params.bridge_distance / (1 + int(max_curvature * 7 / PI))) { std::cout << "SUPP: " << "udis: " << unsupported_distance << " curv: " << curvature << " max curv: "
issues.supports_nedded.push_back(u_point); << max_curvature << std::endl;
std::cout << "max dist from layer: " << max_allowed_dist_from_prev_layer << " measured dist: "
<< dist_from_prev_layer << " FW: " << flow_width << std::endl;
unsupported_distance = 0;
curvature = 0;
max_curvature = 0;
}
} else {
unsupported_distance = 0; unsupported_distance = 0;
curvature = 0; curvature = 0;
max_curvature = 0; max_curvature = 0;
} }
if (max_curvature / (PI * unsupported_distance) > params.limit_curvature) { if (max_curvature / (PI * unsupported_distance) > params.limit_curvature) {
issues.curling_up.push_back(u_point); issues.curling_up.push_back(fpoint);
curvature = 0; }
max_curvature = 0;
prev_fpoint = fpoint;
if (!points.empty()) {
Vec2f next = unscale(points.top()).cast<float>();
Vec2f reverse_v = fpoint.head<2>() - next;
float dist_to_next = reverse_v.norm();
reverse_v.normalize();
int new_points_count = dist_to_next / params.bridge_distance;
float step_size = dist_to_next / (new_points_count + 1);
for (int i = 1; i <= new_points_count; ++i) {
points.push(Point::new_scale(Vec2f(next + reverse_v * (i * step_size))));
}
} }
prev_point = u_point;
} }
} }
return issues; return issues;
@ -205,7 +243,7 @@ coordf_t get_flow_width(const LayerRegion *region, ExtrusionRole role) {
case ExtrusionRole::erSolidInfill: case ExtrusionRole::erSolidInfill:
return region->flow(FlowRole::frSolidInfill).scaled_width(); return region->flow(FlowRole::frSolidInfill).scaled_width();
default: default:
return region->flow(FlowRole::frExternalPerimeter).scaled_width(); return region->flow(FlowRole::frPerimeter).scaled_width();
} }
} }
@ -223,7 +261,7 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_
for (const LayerRegion *layer_region : layer->regions()) { for (const LayerRegion *layer_region : layer->regions()) {
for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) {
for (const ExtrusionEntity *perimeter : static_cast<const ExtrusionEntityCollection*>(ex_entity)->entities) { for (const ExtrusionEntity *perimeter : static_cast<const ExtrusionEntityCollection*>(ex_entity)->entities) {
issues.add(check_extrusion_entity_stability(perimeter, layer_idx, issues.add(check_extrusion_entity_stability(perimeter,
layer->slice_z, get_flow_width(layer_region, perimeter->role()), layer->slice_z, get_flow_width(layer_region, perimeter->role()),
supported_grid, params)); supported_grid, params));
} // perimeter } // perimeter
@ -231,7 +269,7 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_
for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) {
for (const ExtrusionEntity *fill : static_cast<const ExtrusionEntityCollection*>(ex_entity)->entities) { for (const ExtrusionEntity *fill : static_cast<const ExtrusionEntityCollection*>(ex_entity)->entities) {
if (fill->role() == ExtrusionRole::erGapFill || fill->role() == ExtrusionRole::erBridgeInfill) { if (fill->role() == ExtrusionRole::erGapFill || fill->role() == ExtrusionRole::erBridgeInfill) {
issues.add(check_extrusion_entity_stability(fill, layer_idx, issues.add(check_extrusion_entity_stability(fill,
layer->slice_z, get_flow_width(layer_region, fill->role()), layer->slice_z, get_flow_width(layer_region, fill->role()),
supported_grid, params)); supported_grid, params));
} }
@ -242,9 +280,9 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_
for (const LayerRegion *layer_region : layer->regions()) { for (const LayerRegion *layer_region : layer->regions()) {
for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) {
for (const ExtrusionEntity *perimeter : static_cast<const ExtrusionEntityCollection*>(ex_entity)->entities) { for (const ExtrusionEntity *perimeter : static_cast<const ExtrusionEntityCollection*>(ex_entity)->entities) {
if (perimeter->role() == ExtrusionRole::erExternalPerimeter) { if (perimeter->role() == ExtrusionRole::erExternalPerimeter
std::cout << "checking ex perimeter " << std::endl; || perimeter->role() == ExtrusionRole::erOverhangPerimeter) {
issues.add(check_extrusion_entity_stability(perimeter, layer_idx, issues.add(check_extrusion_entity_stability(perimeter,
layer->slice_z, get_flow_width(layer_region, perimeter->role()), layer->slice_z, get_flow_width(layer_region, perimeter->role()),
supported_grid, params)); supported_grid, params));
}; // ex_perimeter }; // ex_perimeter
@ -273,7 +311,7 @@ std::vector<size_t> quick_search(const PrintObject *po, const Params &params) {
for (const LayerRegion *layer_region : layer->regions()) { for (const LayerRegion *layer_region : layer->regions()) {
for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) {
for (const ExtrusionEntity *perimeter : static_cast<const ExtrusionEntityCollection*>(ex_entity)->entities) { for (const ExtrusionEntity *perimeter : static_cast<const ExtrusionEntityCollection*>(ex_entity)->entities) {
if (perimeter->role() == ExtrusionRole::erExternalPerimeter) { if (perimeter->role() == ExtrusionRole::erExternalPerimeter || perimeter->role() == ExtrusionRole::erOverhangPerimeter) {
assert(perimeter->is_loop()); assert(perimeter->is_loop());
descriptor.segments_count++; descriptor.segments_count++;
const ExtrusionLoop *loop = static_cast<const ExtrusionLoop*>(perimeter); const ExtrusionLoop *loop = static_cast<const ExtrusionLoop*>(perimeter);
@ -327,8 +365,7 @@ std::vector<size_t> quick_search(const PrintObject *po, const Params &params) {
[&](tbb::blocked_range<size_t> r) { [&](tbb::blocked_range<size_t> r) {
for (size_t suspicious_index = r.begin(); suspicious_index < r.end(); ++suspicious_index) { for (size_t suspicious_index = r.begin(); suspicious_index < r.end(); ++suspicious_index) {
auto layer_issues = check_layer_stability(po, suspicious_layers_indices[suspicious_index], auto layer_issues = check_layer_stability(po, suspicious_layers_indices[suspicious_index],
false, false, params);
params);
if (!layer_issues.supports_nedded.empty()) { if (!layer_issues.supports_nedded.empty()) {
layer_needs_supports[suspicious_index] = true; layer_needs_supports[suspicious_index] = true;
} }
@ -337,8 +374,8 @@ std::vector<size_t> quick_search(const PrintObject *po, const Params &params) {
std::vector<size_t> problematic_layers; std::vector<size_t> problematic_layers;
for (size_t index = suspicious_layers_indices.size() - 1; index <= 0; ++index) { for (size_t index = 0; index < suspicious_layers_indices.size(); ++index) {
if (!layer_needs_supports[index]) { if (layer_needs_supports[index]) {
problematic_layers.push_back(suspicious_layers_indices[index]); problematic_layers.push_back(suspicious_layers_indices[index]);
} }
} }
@ -347,26 +384,29 @@ std::vector<size_t> quick_search(const PrintObject *po, const Params &params) {
Issues full_search(const PrintObject *po, const Params &params) { Issues full_search(const PrintObject *po, const Params &params) {
using namespace Impl; using namespace Impl;
Issues issues { }; size_t layer_count = po->layer_count();
for (size_t layer_idx = 1; layer_idx < po->layer_count(); ++layer_idx) { Issues found_issues = tbb::parallel_reduce(tbb::blocked_range<size_t>(1, layer_count), Issues { },
[&](tbb::blocked_range<size_t> r, const Issues &init) {
Issues issues = init;
for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) {
auto layer_issues = check_layer_stability(po, layer_idx, true, params); auto layer_issues = check_layer_stability(po, layer_idx, true, params);
if (!layer_issues.empty()) { if (!layer_issues.empty()) {
issues.add(layer_issues); issues.add(layer_issues);
} }
} }
return issues;
},
[](Issues left, const Issues &right) {
left.add(right);
return left;
}
);
#ifdef DEBUG_FILES #ifdef DEBUG_FILES
Impl::debug_export(issues, "issues"); Impl::debug_export(found_issues, "issues");
#endif #endif
// tbb::parallel_for(tbb::blocked_range<size_t>(0, suspicious_layers_indices.size()), return found_issues;
// [&](tbb::blocked_range<size_t> r) {
// for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) {
// check_layer_stability(po, suspicious_layers_indices[layer_idx], params);
// }
// });
return issues;
} }
} }

View File

@ -8,8 +8,13 @@ namespace Slic3r {
namespace SupportableIssues { namespace SupportableIssues {
struct Params { struct Params {
float bridge_distance = 5.0f; float bridge_distance = 10.0f;
float limit_curvature = 0.25f; float limit_curvature = 0.3f;
bool external_perimeter_first = false;
float max_unsupported_distance_factor = 0.0f;
float max_ex_perim_unsupported_distance_factor = 1.0f;
float bridge_distance_decrease_by_curvature_factor = 5.0f;
float perimeter_length_diff_tolerance = 8.0f; float perimeter_length_diff_tolerance = 8.0f;
float centroid_offset_tolerance = 1.0f; float centroid_offset_tolerance = 1.0f;

View File

@ -16,10 +16,11 @@ void TriangleSelectorWrapper::enforce_spot(const Vec3f &point, float radius) {
auto dist = AABBTreeIndirect::squared_distance_to_indexed_triangle_set(mesh.its.vertices, mesh.its.indices, auto dist = AABBTreeIndirect::squared_distance_to_indexed_triangle_set(mesh.its.vertices, mesh.its.indices,
triangles_tree, triangles_tree,
point, hit_face_index, hit_point); point, hit_face_index, hit_point);
if (dist < 0 || dist > radius) if (dist < 0 || dist > radius * radius) {
return; return;
}
std::unique_ptr<TriangleSelector::Cursor> cursor = std::make_unique<TriangleSelector::Sphere>(point, point, std::unique_ptr<TriangleSelector::Cursor> cursor = std::make_unique<TriangleSelector::Sphere>(hit_point, point,
radius, Transform3d::Identity(), TriangleSelector::ClippingPlane { }); radius, Transform3d::Identity(), TriangleSelector::ClippingPlane { });
selector.select_patch(hit_face_index, std::move(cursor), EnforcerBlockerType::ENFORCER, Transform3d::Identity(), selector.select_patch(hit_face_index, std::move(cursor), EnforcerBlockerType::ENFORCER, Transform3d::Identity(),

View File

@ -39,6 +39,10 @@ private:
// This map holds all translated description texts, so they can be easily referenced during layout calculations // This map holds all translated description texts, so they can be easily referenced during layout calculations
// etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect.
std::map<std::string, wxString> m_desc; std::map<std::string, wxString> m_desc;
bool has_backend_supports() const;
void reslice_FDM_supports(bool postpone_error_messages = false) const;
}; };