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 "SupportableIssuesSearch.hpp"
#include "TriangleSelectorWrapper.hpp"
#include "format.hpp"
#include <float.h>
#include <string_view>
@ -397,18 +398,26 @@ void PrintObject::ironing()
}
}
void PrintObject::find_supportable_issues()
{
if (this->set_started(posSupportableIssuesSearch)) {
BOOST_LOG_TRIVIAL(debug)
<< "Searching supportable issues - start";
//TODO status number?
m_print->set_status(75, L("Searching supportable issues"));
if (!this->m_config.support_material) {
std::vector<size_t> problematic_layers = SupportableIssues::quick_search(this);
if (!problematic_layers.empty()){
//TODO report problems
if (!problematic_layers.empty()) {
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 {
SupportableIssues::Issues issues = SupportableIssues::full_search(this);
@ -421,7 +430,7 @@ void PrintObject::find_supportable_issues()
TriangleSelectorWrapper selector { model_volume->mesh() };
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);
@ -430,7 +439,7 @@ void PrintObject::find_supportable_issues()
indexed_triangle_set copy = model_volume->mesh().its;
its_transform(copy, obj_transform * model_transformation);
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
}
}

View File

@ -5,6 +5,7 @@
#include "tbb/parallel_reduce.h"
#include <boost/log/trivial.hpp>
#include <cmath>
#include <stack>
#include "libslic3r/Layer.hpp"
#include "libslic3r/EdgeGrid.hpp"
@ -39,14 +40,24 @@ struct LayerDescriptor {
};
struct EdgeGridWrapper {
EdgeGridWrapper(coord_t resolution, ExPolygons ex_polys) :
ex_polys(ex_polys) {
EdgeGridWrapper(coord_t edge_width, ExPolygons 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();
}
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;
ExPolygons ex_polys;
coord_t edge_width;
};
#ifdef DEBUG_FILES
@ -99,8 +110,13 @@ EdgeGridWrapper compute_layer_edge_grid(const Layer *layer) {
for (const LayerRegion *layer_region : layer->regions()) {
for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) {
for (const ExtrusionEntity *perimeter : static_cast<const ExtrusionEntityCollection*>(ex_entity)->entities) {
if (perimeter->role() == ExtrusionRole::erExternalPerimeter) {
ex_polygons.push_back(ExPolygon { perimeter->as_polyline().points });
if (perimeter->role() == ExtrusionRole::erExternalPerimeter
|| 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
} // perimeter
} // ex_entity
@ -109,7 +125,16 @@ EdgeGridWrapper compute_layer_edge_grid(const Layer *layer) {
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,
coordf_t flow_width,
const EdgeGridWrapper &supported_grid,
@ -118,74 +143,87 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, size_t la
Issues issues { };
if (entity->is_collection()) {
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
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 curvature = 0;
float max_curvature = 0;
Vec2f tmp = unscale(points[0]).cast<float>();
Vec3f prev_point = Vec3f(tmp.x(), tmp.y(), slice_z);
Vec2f tmp = unscale(points.top()).cast<float>();
Vec3f prev_fpoint = Vec3f(tmp.x(), tmp.y(), slice_z);
for (size_t point_index = 0; point_index < points.size(); ++point_index) {
std::cout << "index: " << point_index << " dist: " << unsupported_distance << " curvature: "
<< curvature << " max curvature: " << max_curvature << std::endl;
const coordf_t max_allowed_dist_from_prev_layer = get_max_allowed_distance(entity->role(), flow_width,
params);
Vec2f tmp = unscale(points[point_index]).cast<float>();
Vec3f u_point = Vec3f(tmp.x(), tmp.y(), slice_z);
while (!points.empty()) {
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 };
if (!supported_grid.grid.signed_distance(points[point_index], flow_width, dist_from_prev_layer)) {
issues.supports_nedded.push_back(u_point);
if (!supported_grid.signed_distance(point, flow_width, dist_from_prev_layer)) {
issues.supports_nedded.push_back(fpoint);
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
std::cout << "index: " << point_index << " unsupported " << std::endl;
unsupported_distance += (u_point - prev_point).norm();
if (!points.empty()) {
const Vec2f v1 = (fpoint - prev_fpoint).head<2>();
const Vec2f v2 = unscale(points.top()).cast<float>() - fpoint.head<2>();
float dot = v1(0) * v2(0) + v1(1) * v2(1);
float cross = v1(0) * v2(1) - v1(1) * v2(0);
float angle = float(atan2(float(cross), float(dot)));
curvature += angle;
max_curvature = std::max(abs(curvature), max_curvature);
}
if (unsupported_distance
> params.bridge_distance
/ (1.0f + (max_curvature * params.bridge_distance_decrease_by_curvature_factor / PI))) {
issues.supports_nedded.push_back(fpoint);
std::cout << "SUPP: " << "udis: " << unsupported_distance << " curv: " << curvature << " max curv: "
<< 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 {
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 cross = v1(0) * v2(1) - v1(1) * v2(0);
float angle = float(atan2(float(cross), float(dot)));
std::cout << "index: " << point_index << " angle: " << angle << std::endl;
curvature += angle;
max_curvature = std::max(abs(curvature), max_curvature);
}
if (!(dist_from_prev_layer > flow_width * limit_overlap_factor)) {
std::cout << "index: " << point_index << " reset curvature" << std::endl;
max_curvature = 0;
curvature = 0;
}
if (unsupported_distance > params.bridge_distance / (1 + int(max_curvature * 7 / PI))) {
issues.supports_nedded.push_back(u_point);
unsupported_distance = 0;
curvature = 0;
max_curvature = 0;
}
if (max_curvature / (PI * unsupported_distance) > params.limit_curvature) {
issues.curling_up.push_back(u_point);
curvature = 0;
max_curvature = 0;
issues.curling_up.push_back(fpoint);
}
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;
@ -205,7 +243,7 @@ coordf_t get_flow_width(const LayerRegion *region, ExtrusionRole role) {
case ExtrusionRole::erSolidInfill:
return region->flow(FlowRole::frSolidInfill).scaled_width();
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 ExtrusionEntity *ex_entity : layer_region->perimeters.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()),
supported_grid, params));
} // 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 *fill : static_cast<const ExtrusionEntityCollection*>(ex_entity)->entities) {
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()),
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 ExtrusionEntity *ex_entity : layer_region->perimeters.entities) {
for (const ExtrusionEntity *perimeter : static_cast<const ExtrusionEntityCollection*>(ex_entity)->entities) {
if (perimeter->role() == ExtrusionRole::erExternalPerimeter) {
std::cout << "checking ex perimeter " << std::endl;
issues.add(check_extrusion_entity_stability(perimeter, layer_idx,
if (perimeter->role() == ExtrusionRole::erExternalPerimeter
|| perimeter->role() == ExtrusionRole::erOverhangPerimeter) {
issues.add(check_extrusion_entity_stability(perimeter,
layer->slice_z, get_flow_width(layer_region, perimeter->role()),
supported_grid, params));
}; // 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 ExtrusionEntity *ex_entity : layer_region->perimeters.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());
descriptor.segments_count++;
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) {
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],
false,
params);
false, params);
if (!layer_issues.supports_nedded.empty()) {
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;
for (size_t index = suspicious_layers_indices.size() - 1; index <= 0; ++index) {
if (!layer_needs_supports[index]) {
for (size_t index = 0; index < suspicious_layers_indices.size(); ++index) {
if (layer_needs_supports[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) {
using namespace Impl;
Issues issues { };
for (size_t layer_idx = 1; layer_idx < po->layer_count(); ++layer_idx) {
auto layer_issues = check_layer_stability(po, layer_idx, true, params);
if (!layer_issues.empty()) {
issues.add(layer_issues);
}
}
size_t layer_count = po->layer_count();
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);
if (!layer_issues.empty()) {
issues.add(layer_issues);
}
}
return issues;
},
[](Issues left, const Issues &right) {
left.add(right);
return left;
}
);
#ifdef DEBUG_FILES
Impl::debug_export(issues, "issues");
Impl::debug_export(found_issues, "issues");
#endif
// tbb::parallel_for(tbb::blocked_range<size_t>(0, suspicious_layers_indices.size()),
// [&](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;
return found_issues;
}
}

View File

@ -8,8 +8,13 @@ namespace Slic3r {
namespace SupportableIssues {
struct Params {
float bridge_distance = 5.0f;
float limit_curvature = 0.25f;
float bridge_distance = 10.0f;
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 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,
triangles_tree,
point, hit_face_index, hit_point);
if (dist < 0 || dist > radius)
if (dist < 0 || dist > radius * radius) {
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 { });
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
// etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect.
std::map<std::string, wxString> m_desc;
bool has_backend_supports() const;
void reslice_FDM_supports(bool postpone_error_messages = false) const;
};