Beta version of the algorithm

Implemented long unsupported segments detection, which considers also curvature
Implemented detection of curved segments at the edge of the previous layer - danger of warping/curling
This commit is contained in:
PavelMikus 2022-04-07 12:44:50 +02:00
parent adf39805bc
commit 706cd63e61
6 changed files with 366 additions and 52 deletions

View File

@ -275,6 +275,8 @@ set(SLIC3R_SOURCES
TriangleSelector.hpp
TriangleSetSampling.cpp
TriangleSetSampling.hpp
TriangleSelectorWrapper.cpp
TriangleSelectorWrapper.hpp
MTUtils.hpp
Zipper.hpp
Zipper.cpp

View File

@ -402,12 +402,38 @@ void PrintObject::find_supportable_issues()
BOOST_LOG_TRIVIAL(debug)
<< "Searching supportable issues - start";
//TODO status number?
m_print->set_status(70, L("Searching supportable issues"));
if (this->has_support()) {
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
}
} else {
SupportableIssues::quick_search(this);
SupportableIssues::Issues issues = SupportableIssues::full_search(this);
if (!issues.supports_nedded.empty()) {
auto obj_transform = this->trafo_centered();
for (ModelVolume *model_volume : this->model_object()->volumes) {
if (model_volume->type() == ModelVolumeType::MODEL_PART) {
Transform3d model_transformation = model_volume->get_matrix();
Transform3d inv_transform = (obj_transform * model_transformation).inverse();
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);
}
model_volume->supported_facets.set(selector.selector);
#if 1
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());
#endif
}
}
}
}
m_print->throw_if_canceled();
@ -417,6 +443,7 @@ void PrintObject::find_supportable_issues()
}
}
void PrintObject::generate_support_material()
{
if (this->set_started(posSupportMaterial)) {

View File

@ -4,18 +4,31 @@
#include "tbb/blocked_range.h"
#include "tbb/parallel_reduce.h"
#include <boost/log/trivial.hpp>
#include <cmath>
#include "libslic3r/Layer.hpp"
#include "libslic3r/EdgeGrid.hpp"
#include "libslic3r/ClipperUtils.hpp"
#define DEBUG_FILES
#ifdef DEBUG_FILES
#include <boost/nowide/cstdio.hpp>
#endif
namespace Slic3r {
namespace SupportableIssues {
struct Params {
float bridge_distance = 5.0f;
float printable_protrusion_distance = 1.0f;
};
void Issues::add(const Issues &layer_issues) {
supports_nedded.insert(supports_nedded.end(),
layer_issues.supports_nedded.begin(), layer_issues.supports_nedded.end());
curling_up.insert(curling_up.end(), layer_issues.curling_up.begin(),
layer_issues.curling_up.end());
}
bool Issues::empty() const {
return supports_nedded.empty() && curling_up.empty();
}
namespace Impl {
@ -34,62 +47,218 @@ struct EdgeGridWrapper {
}
EdgeGrid::Grid grid;
ExPolygons ex_polys;
}
;
};
EdgeGridWrapper compute_layer_merged_edge_grid(const Layer *layer) {
static const float eps = float(scale_(layer->object()->config().slice_closing_radius.value));
// merge with offset
ExPolygons merged = layer->merged(eps);
// ofsset back
ExPolygons layer_outline = offset_ex(merged, -eps);
#ifdef DEBUG_FILES
void debug_export(Issues issues, std::string file_name) {
Slic3r::CNumericLocalesSetter locales_setter;
float min_region_flow_width { };
for (const auto *region : layer->regions()) {
min_region_flow_width = std::max(min_region_flow_width, region->flow(FlowRole::frExternalPerimeter).width());
}
std::cout << "min_region_flow_width: " << min_region_flow_width << std::endl;
return EdgeGridWrapper(scale_(min_region_flow_width), layer_outline);
}
void check_extrusion_entity_stability(const ExtrusionEntity *entity, const EdgeGridWrapper &supported_grid,
const Params &params) {
if (entity->is_collection()){
for (const auto* e: static_cast<ExtrusionEntityCollection>(entity).entities){
check_extrusion_entity_stability(e, supported_grid, params);
{
FILE *fp = boost::nowide::fopen(debug_out_path((file_name + "_supports.obj").c_str()).c_str(), "w");
if (fp == nullptr) {
BOOST_LOG_TRIVIAL(error)
<< "Debug files: Couldn't open " << file_name << " for writing";
return;
}
} else { //single extrusion path, with possible varying parameters
entity->as_polyline().points;
}
for (size_t i = 0; i < issues.supports_nedded.size(); ++i) {
fprintf(fp, "v %f %f %f %f %f %f\n",
issues.supports_nedded[i](0), issues.supports_nedded[i](1), issues.supports_nedded[i](2),
1.0, 0.0, 0.0
);
}
fclose(fp);
}
{
FILE *fp = boost::nowide::fopen(debug_out_path((file_name + "_curling.obj").c_str()).c_str(), "w");
if (fp == nullptr) {
BOOST_LOG_TRIVIAL(error)
<< "Debug files: Couldn't open " << file_name << " for writing";
return;
}
for (size_t i = 0; i < issues.curling_up.size(); ++i) {
fprintf(fp, "v %f %f %f %f %f %f\n",
issues.curling_up[i](0), issues.curling_up[i](1), issues.curling_up[i](2),
0.0, 1.0, 0.0
);
}
fclose(fp);
}
}
#endif
void check_layer_stability(const PrintObject *po, size_t layer_idx, const Params &params) {
if (layer_idx == 0) {
// first layer is usually ok
return;
EdgeGridWrapper compute_layer_edge_grid(const Layer *layer) {
float min_region_flow_width { 1.0f };
for (const auto *region : layer->regions()) {
min_region_flow_width = std::min(min_region_flow_width, region->flow(FlowRole::frExternalPerimeter).width());
}
const Layer *layer = po->get_layer(layer_idx);
const Layer *prev_layer = layer->lower_layer;
EdgeGridWrapper supported_grid = compute_layer_merged_edge_grid(prev_layer);
ExPolygons ex_polygons;
for (const LayerRegion *layer_region : layer->regions()) {
coordf_t flow_width = coordf_t(
scale_(layer_region->flow(FlowRole::frExternalPerimeter).width()));
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) {
check_extrusion_entity_stability(perimeter, supported_grid, params);
ex_polygons.push_back(ExPolygon { perimeter->as_polyline().points });
} // ex_perimeter
} // perimeter
} // ex_entity
}
return EdgeGridWrapper(scale_(min_region_flow_width), ex_polygons);
}
Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, size_t layer_idx,
float slice_z,
coordf_t flow_width,
const EdgeGridWrapper &supported_grid,
const Params &params) {
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));
}
} else { //single extrusion path, with possible varying parameters
Points points = entity->as_polyline().points;
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);
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;
Vec2f tmp = unscale(points[point_index]).cast<float>();
Vec3f u_point = 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);
continue;
}
constexpr float limit_overlap_factor = 0.5;
if (dist_from_prev_layer > flow_width) { //unsupported
std::cout << "index: " << point_index << " unsupported " << std::endl;
unsupported_distance += (u_point - prev_point).norm();
} 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 * 9 / 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;
}
prev_point = u_point;
}
}
return issues;
}
//TODO needs revision
coordf_t get_flow_width(const LayerRegion *region, ExtrusionRole role) {
switch (role) {
case ExtrusionRole::erBridgeInfill:
return region->flow(FlowRole::frExternalPerimeter).scaled_width();
case ExtrusionRole::erExternalPerimeter:
return region->flow(FlowRole::frExternalPerimeter).scaled_width();
case ExtrusionRole::erGapFill:
return region->flow(FlowRole::frInfill).scaled_width();
case ExtrusionRole::erPerimeter:
return region->flow(FlowRole::frPerimeter).scaled_width();
case ExtrusionRole::erSolidInfill:
return region->flow(FlowRole::frSolidInfill).scaled_width();
default:
return region->flow(FlowRole::frExternalPerimeter).scaled_width();
}
}
Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_check, const Params &params) {
std::cout << "Checking: " << layer_idx << std::endl;
if (layer_idx == 0) {
// first layer is usually ok
return {};
}
const Layer *layer = po->get_layer(layer_idx);
EdgeGridWrapper supported_grid = compute_layer_edge_grid(layer->lower_layer);
Issues issues { };
if (full_check) {
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,
layer->slice_z, get_flow_width(layer_region, perimeter->role()),
supported_grid, params));
} // perimeter
} // ex_entity
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,
layer->slice_z, get_flow_width(layer_region, fill->role()),
supported_grid, params));
}
} // fill
} // ex_entity
} // region
} else { //check only external perimeters
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,
layer->slice_z, get_flow_width(layer_region, perimeter->role()),
supported_grid, params));
}; // ex_perimeter
} // perimeter
} // ex_entity
} //region
}
return issues;
}
} //Impl End
void quick_search(const PrintObject *po, const Params &params = Params { }) {
std::vector<size_t> quick_search(const PrintObject *po, const Params &params) {
using namespace Impl;
std::vector<LayerDescriptor> descriptors(po->layer_count());
@ -129,20 +298,75 @@ void quick_search(const PrintObject *po, const Params &params = Params { }) {
} // thread
);
for (size_t desc_idx = 0; desc_idx < descriptors.size(); ++desc_idx) {
std::vector<size_t> suspicious_layers_indices { };
for (size_t desc_idx = 1; desc_idx < descriptors.size(); ++desc_idx) {
const LayerDescriptor &prev = descriptors[desc_idx - 1];
const LayerDescriptor &descriptor = descriptors[desc_idx];
if (descriptor.segments_count - prev.segments_count != 0
||
std::abs(descriptor.perimeter_length - prev.perimeter_length)
> params.perimeter_length_diff_tolerance ||
(descriptor.centroid - prev.centroid).norm() > params.centroid_offset_tolerance
) {
suspicious_layers_indices.push_back(desc_idx);
}
#ifdef DEBUG_FILES
std::cout << "SIS layer idx: " << desc_idx << " reg count: " << descriptor.segments_count << " len: "
<< descriptor.perimeter_length <<
" centroid: " << descriptor.centroid.x() << " | " << descriptor.centroid.y() << std::endl;
if (desc_idx > 0) {
const LayerDescriptor &prev = descriptors[desc_idx - 1];
std::cout << "SIS diff: " << desc_idx << " reg count: "
<< (int(descriptor.segments_count) - int(prev.segments_count)) <<
" len: " << (descriptor.perimeter_length - prev.perimeter_length) <<
" centroid: " << (descriptor.centroid - prev.centroid).norm() << std::endl;
std::cout << "SIS diff: " << desc_idx << " reg count: "
<< (int(descriptor.segments_count) - int(prev.segments_count)) <<
" len: " << (descriptor.perimeter_length - prev.perimeter_length) <<
" centroid: " << (descriptor.centroid - prev.centroid).norm() << std::endl;
#endif
}
std::vector<bool> layer_needs_supports(suspicious_layers_indices.size(), false);
tbb::parallel_for(tbb::blocked_range<size_t>(0, suspicious_layers_indices.size()),
[&](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);
if (!layer_issues.supports_nedded.empty()) {
layer_needs_supports[suspicious_index] = true;
}
}
});
std::vector<size_t> problematic_layers;
for (size_t index = suspicious_layers_indices.size() - 1; index <= 0; ++index) {
if (!layer_needs_supports[index]) {
problematic_layers.push_back(suspicious_layers_indices[index]);
}
}
return problematic_layers;
}
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);
}
}
#ifdef DEBUG_FILES
Impl::debug_export(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;
}
}

View File

@ -7,7 +7,25 @@ namespace Slic3r {
namespace SupportableIssues {
void quick_search(const PrintObject *po);
struct Params {
float bridge_distance = 5.0f;
float limit_curvature = 0.25f;
float perimeter_length_diff_tolerance = 8.0f;
float centroid_offset_tolerance = 1.0f;
};
struct Issues {
std::vector<Vec3f> supports_nedded;
std::vector<Vec3f> curling_up;
void add(const Issues &layer_issues);
bool empty() const;
};
std::vector<size_t> quick_search(const PrintObject *po, const Params &params = Params { });
Issues full_search(const PrintObject *po, const Params &params = Params { });
}

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,42 @@
#ifndef SRC_LIBSLIC3R_TRIANGLESELECTORWRAPPER_HPP_
#define SRC_LIBSLIC3R_TRIANGLESELECTORWRAPPER_HPP_
#include <memory>
#include "TriangleSelector.hpp"
#include "AABBTreeIndirect.hpp"
namespace Slic3r {
class TriangleSelectorWrapper {
public:
const TriangleMesh &mesh;
TriangleSelector selector;
AABBTreeIndirect::Tree<3, float> triangles_tree;
TriangleSelectorWrapper(const TriangleMesh &mesh) :
mesh(mesh), selector(mesh), triangles_tree(AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(mesh.its.vertices, mesh.its.indices)) {
}
void enforce_spot(const Vec3f &point, float radius) {
size_t hit_face_index;
Vec3f hit_point;
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)
return;
std::unique_ptr<TriangleSelector::Cursor> cursor = std::make_unique<TriangleSelector::Sphere>(point, point,
radius, Transform3d::Identity(), TriangleSelector::ClippingPlane { });
selector.select_patch(hit_face_index, std::move(cursor), EnforcerBlockerType::ENFORCER, Transform3d::Identity(), true,
0.0f);
}
};
}
#endif /* SRC_LIBSLIC3R_TRIANGLESELECTORWRAPPER_HPP_ */