mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-14 17:25:53 +08:00
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:
parent
e516ba0dd0
commit
d41b20547d
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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 ¶ms) { // <= 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 ¶ms) {
|
||||
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 ¶ms) {
|
||||
[&](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 ¶ms) {
|
||||
|
||||
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 ¶ms) {
|
||||
|
||||
Issues full_search(const PrintObject *po, const Params ¶ms) {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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(),
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user