Paralellize SupportSpotGenerator!

Fix extra perimeters crash - problem with new ankerl hash map
Fix progress bar
This commit is contained in:
PavelMikus 2023-04-27 15:36:21 +02:00
parent cdf8cd83d5
commit 4c872b0352
3 changed files with 162 additions and 133 deletions

View File

@ -39,6 +39,7 @@
#include <ostream>
#include <stack>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
@ -681,7 +682,7 @@ Polylines reconnect_polylines(const Polylines &polylines, double limit_distance)
if (polylines.empty())
return polylines;
ankerl::unordered_dense::map<size_t, Polyline> connected;
std::unordered_map<size_t, Polyline> connected;
connected.reserve(polylines.size());
for (size_t i = 0; i < polylines.size(); i++) {
if (!polylines[i].empty()) {
@ -731,7 +732,7 @@ ExtrusionPaths sort_extra_perimeters(ExtrusionPaths extra_perims, int index_of_f
{
if (extra_perims.empty()) return {};
std::vector<ankerl::unordered_dense::set<size_t>> dependencies(extra_perims.size());
std::vector<std::unordered_set<size_t>> dependencies(extra_perims.size());
for (size_t path_idx = 0; path_idx < extra_perims.size(); path_idx++) {
for (size_t prev_path_idx = 0; prev_path_idx < path_idx; prev_path_idx++) {
if (paths_touch(extra_perims[path_idx], extra_perims[prev_path_idx], extrusion_spacing * 1.5f)) {

View File

@ -880,7 +880,6 @@ void Print::process()
BOOST_LOG_TRIVIAL(info) << "Starting the slicing process." << log_memory_info();
for (PrintObject *obj : m_objects)
obj->make_perimeters();
this->set_status(70, _u8L("Infilling layers"));
for (PrintObject *obj : m_objects)
obj->infill();
for (PrintObject *obj : m_objects)

View File

@ -27,6 +27,8 @@
#include <functional>
#include <limits>
#include <math.h>
#include <oneapi/tbb/concurrent_vector.h>
#include <oneapi/tbb/parallel_for.h>
#include <optional>
#include <unordered_map>
#include <unordered_set>
@ -172,6 +174,69 @@ struct SliceConnection
}
};
SliceConnection estimate_slice_connection(size_t slice_idx, const Layer *layer)
{
SliceConnection connection;
const LayerSlice &slice = layer->lslices_ex[slice_idx];
Polygons slice_polys = to_polygons(layer->lslices[slice_idx]);
BoundingBox slice_bb = get_extents(slice_polys);
const Layer *lower_layer = layer->lower_layer;
ExPolygons below{};
for (const auto &link : slice.overlaps_below) { below.push_back(lower_layer->lslices[link.slice_idx]); }
Polygons below_polys = to_polygons(below);
BoundingBox below_bb = get_extents(below_polys);
Polygons overlap = intersection(ClipperUtils::clip_clipper_polygons_with_subject_bbox(slice_polys, below_bb),
ClipperUtils::clip_clipper_polygons_with_subject_bbox(below_polys, slice_bb));
for (const Polygon &poly : overlap) {
Vec2f p0 = unscaled(poly.first_point()).cast<float>();
for (size_t i = 2; i < poly.points.size(); i++) {
Vec2f p1 = unscaled(poly.points[i - 1]).cast<float>();
Vec2f p2 = unscaled(poly.points[i]).cast<float>();
float sign = cross2(p1 - p0, p2 - p1) > 0 ? 1.0f : -1.0f;
auto [area, first_moment_of_area, second_moment_area,
second_moment_of_area_covariance] = compute_moments_of_area_of_triangle(p0, p1, p2);
connection.area += sign * area;
connection.centroid_accumulator += sign * Vec3f(first_moment_of_area.x(), first_moment_of_area.y(), layer->print_z * area);
connection.second_moment_of_area_accumulator += sign * second_moment_area;
connection.second_moment_of_area_covariance_accumulator += sign * second_moment_of_area_covariance;
}
}
return connection;
};
using PrecomputedSliceConnections = std::vector<std::vector<SliceConnection>>;
PrecomputedSliceConnections precompute_slices_connections(const PrintObject *po)
{
PrecomputedSliceConnections result{};
for (size_t lidx = 0; lidx < po->layer_count(); lidx++) {
result.emplace_back(std::vector<SliceConnection>{});
for (size_t slice_idx = 0; slice_idx < po->get_layer(lidx)->lslices_ex.size(); slice_idx++) {
result[lidx].push_back(SliceConnection{});
}
}
tbb::parallel_for(tbb::blocked_range<size_t>(0, po->layers().size()), [po, &result](tbb::blocked_range<size_t> r) {
for (size_t lidx = r.begin(); lidx < r.end(); lidx++) {
const Layer *l = po->get_layer(lidx);
tbb::parallel_for(tbb::blocked_range<size_t>(0, l->lslices_ex.size()), [lidx, l, &result](tbb::blocked_range<size_t> r2) {
for (size_t slice_idx = r2.begin(); slice_idx < r2.end(); slice_idx++) {
result[lidx][slice_idx] = estimate_slice_connection(slice_idx, l);
}
});
}
});
return result;
};
float get_flow_width(const LayerRegion *region, ExtrusionRole role)
{
if (role == ExtrusionRole::BridgeInfill) return region->flow(FlowRole::frExternalPerimeter).width();
@ -253,15 +318,8 @@ std::vector<ExtrusionLine> check_extrusion_entity_stability(const ExtrusionEntit
const AABBTreeLines::LinesDistancer<Linef> &prev_layer_boundary,
const Params &params)
{
if (entity->is_collection()) {
std::vector<ExtrusionLine> checked_lines_out;
checked_lines_out.reserve(prev_layer_lines.get_lines().size() / 3);
for (const auto *e : static_cast<const ExtrusionEntityCollection *>(entity)->entities) {
auto tmp = check_extrusion_entity_stability(e, layer_region, prev_layer_lines, prev_layer_boundary, params);
checked_lines_out.insert(checked_lines_out.end(), tmp.begin(), tmp.end());
}
return checked_lines_out;
} else if (entity->role().is_bridge() && !entity->role().is_perimeter()) {
assert(!entity->is_collection());
if (entity->role().is_bridge() && !entity->role().is_perimeter()) {
// pure bridges are handled separately, beacuse we need to align the forward and backward direction support points
if (entity->length() < scale_(params.min_distance_to_allow_local_supports)) {
return {};
@ -344,9 +402,10 @@ std::vector<ExtrusionLine> check_extrusion_entity_stability(const ExtrusionEntit
curr_point.distance *= sign;
SupportPointCause potential_cause = SupportPointCause::FloatingExtrusion;
if (bridged_distance + line_len > params.bridge_distance * 0.8 && std::abs(curr_point.curvature) < 0.1) {
potential_cause = SupportPointCause::FloatingExtrusion;
}
// Bridges are now separated. While long overhang perimeter is technically bridge, it would confuse the users
// if (bridged_distance + line_len > params.bridge_distance * 0.8 && std::abs(curr_point.curvature) < 0.1) {
// potential_cause = SupportPointCause::FloatingExtrusion;
// }
float max_bridge_len = std::max(params.support_points_interface_radius * 2.0f,
params.bridge_distance /
@ -383,44 +442,6 @@ std::vector<ExtrusionLine> check_extrusion_entity_stability(const ExtrusionEntit
}
}
SliceConnection estimate_slice_connection(size_t slice_idx, const Layer *layer)
{
SliceConnection connection;
const LayerSlice &slice = layer->lslices_ex[slice_idx];
Polygons slice_polys = to_polygons(layer->lslices[slice_idx]);
BoundingBox slice_bb = get_extents(slice_polys);
const Layer *lower_layer = layer->lower_layer;
ExPolygons below{};
for (const auto &link : slice.overlaps_below) { below.push_back(lower_layer->lslices[link.slice_idx]); }
Polygons below_polys = to_polygons(below);
BoundingBox below_bb = get_extents(below_polys);
Polygons overlap = intersection(ClipperUtils::clip_clipper_polygons_with_subject_bbox(slice_polys, below_bb),
ClipperUtils::clip_clipper_polygons_with_subject_bbox(below_polys, slice_bb));
for (const Polygon &poly : overlap) {
Vec2f p0 = unscaled(poly.first_point()).cast<float>();
for (size_t i = 2; i < poly.points.size(); i++) {
Vec2f p1 = unscaled(poly.points[i - 1]).cast<float>();
Vec2f p2 = unscaled(poly.points[i]).cast<float>();
float sign = cross2(p1 - p0, p2 - p1) > 0 ? 1.0f : -1.0f;
auto [area, first_moment_of_area, second_moment_area,
second_moment_of_area_covariance] = compute_moments_of_area_of_triangle(p0, p1, p2);
connection.area += sign * area;
connection.centroid_accumulator += sign * Vec3f(first_moment_of_area.x(), first_moment_of_area.y(), layer->print_z * area);
connection.second_moment_of_area_accumulator += sign * second_moment_area;
connection.second_moment_of_area_covariance_accumulator += sign * second_moment_of_area_covariance;
}
}
return connection;
};
class ObjectPart
{
public:
@ -761,7 +782,10 @@ public:
}
};
std::tuple<SupportPoints, PartialObjects> check_stability(const PrintObject *po, const PrintTryCancel &cancel_func, const Params &params)
std::tuple<SupportPoints, PartialObjects> check_stability(const PrintObject *po,
const PrecomputedSliceConnections &precomputed_slices_connections,
const PrintTryCancel &cancel_func,
const Params &params)
{
SupportPoints supp_points{};
SupportGridFilter supports_presence_grid(po, params.min_distance_between_support_points);
@ -790,8 +814,8 @@ std::tuple<SupportPoints, PartialObjects> check_stability(const PrintObject *po,
for (size_t slice_idx = 0; slice_idx < layer->lslices_ex.size(); ++slice_idx) {
const LayerSlice &slice = layer->lslices_ex.at(slice_idx);
auto [new_part, covered_area] = build_object_part_from_slice(slice_idx, layer, params);
SliceConnection connection_to_below = estimate_slice_connection(slice_idx, layer);
auto [new_part, covered_area] = build_object_part_from_slice(slice_idx, layer, params);
const SliceConnection &connection_to_below = precomputed_slices_connections[layer_idx][slice_idx];
#ifdef DETAILED_DEBUG_LOGS
std::cout << "SLICE IDX: " << slice_idx << std::endl;
@ -858,25 +882,87 @@ std::tuple<SupportPoints, PartialObjects> check_stability(const PrintObject *po,
prev_slice_idx_to_weakest_connection = next_slice_idx_to_weakest_connection;
next_slice_idx_to_weakest_connection.clear();
auto get_flat_entities = [](const ExtrusionEntity *e) {
std::vector<const ExtrusionEntity *> entities;
std::vector<const ExtrusionEntity *> queue{e};
while (!queue.empty()) {
const ExtrusionEntity *next = queue.back();
queue.pop_back();
if (next->is_collection()) {
for (const ExtrusionEntity *e : static_cast<const ExtrusionEntityCollection *>(next)->entities) {
queue.push_back(e);
}
} else {
entities.push_back(next);
}
}
return entities;
};
struct EnitityToCheck
{
const ExtrusionEntity *e;
const LayerRegion *region;
size_t slice_idx;
};
std::vector<EnitityToCheck> entities_to_check;
for (size_t slice_idx = 0; slice_idx < layer->lslices_ex.size(); ++slice_idx) {
const LayerSlice &slice = layer->lslices_ex.at(slice_idx);
for (const auto &island : slice.islands) {
for (const LayerExtrusionRange &fill_range : island.fills) {
const LayerRegion *fill_region = layer->get_region(fill_range.region());
for (const auto &fill_idx : fill_range) {
for (const ExtrusionEntity *e : get_flat_entities(fill_region->fills().entities[fill_idx])) {
if (e->role() == ExtrusionRole::BridgeInfill) {
entities_to_check.push_back({e, fill_region, slice_idx});
}
}
}
}
const LayerRegion *perimeter_region = layer->get_region(island.perimeters.region());
for (const size_t &perimeter_idx : island.perimeters) {
for (const ExtrusionEntity *e : get_flat_entities(perimeter_region->perimeters().entities[perimeter_idx])) {
entities_to_check.push_back({e, perimeter_region, slice_idx});
}
}
}
}
AABBTreeLines::LinesDistancer<Linef> prev_layer_boundary = layer->lower_layer != nullptr ?
AABBTreeLines::LinesDistancer<Linef>{
to_unscaled_linesf(layer->lower_layer->lslices)} :
AABBTreeLines::LinesDistancer<Linef>{};
std::vector<tbb::concurrent_vector<ExtrusionLine>> unstable_lines_per_slice(layer->lslices_ex.size());
std::vector<tbb::concurrent_vector<ExtrusionLine>> ext_perim_lines_per_slice(layer->lslices_ex.size());
tbb::parallel_for(tbb::blocked_range<size_t>(0, entities_to_check.size()),
[&entities_to_check, &prev_layer_ext_perim_lines, &prev_layer_boundary, &unstable_lines_per_slice,
&ext_perim_lines_per_slice, &params](tbb::blocked_range<size_t> r) {
for (size_t entity_idx = r.begin(); entity_idx < r.end(); ++entity_idx) {
const auto &e_to_check = entities_to_check[entity_idx];
for (const auto &line :
check_extrusion_entity_stability(e_to_check.e, e_to_check.region, prev_layer_ext_perim_lines,
prev_layer_boundary, params)) {
if (line.support_point_generated.has_value()) {
unstable_lines_per_slice[e_to_check.slice_idx].push_back(line);
}
if (line.is_external_perimeter()) {
ext_perim_lines_per_slice[e_to_check.slice_idx].push_back(line);
}
}
}
});
std::vector<ExtrusionLine> current_layer_ext_perims_lines{};
current_layer_ext_perims_lines.reserve(prev_layer_ext_perim_lines.get_lines().size());
// All object parts updated, and for each slice we have coresponding weakest connection.
// We can now check each slice and its corresponding weakest connection and object part for stability.
for (size_t slice_idx = 0; slice_idx < layer->lslices_ex.size(); ++slice_idx) {
const LayerSlice &slice = layer->lslices_ex.at(slice_idx);
ObjectPart &part = active_object_parts.access(prev_slice_idx_to_object_part_mapping[slice_idx]);
SliceConnection &weakest_conn = prev_slice_idx_to_weakest_connection[slice_idx];
std::vector<Linef> boundary_lines;
for (const auto &link : slice.overlaps_below) {
auto ls = to_unscaled_linesf({layer->lower_layer->lslices[link.slice_idx]});
boundary_lines.insert(boundary_lines.end(), ls.begin(), ls.end());
}
AABBTreeLines::LinesDistancer<Linef> prev_layer_boundary{std::move(boundary_lines)};
std::vector<ExtrusionLine> current_slice_ext_perims_lines{};
current_slice_ext_perims_lines.reserve(prev_layer_ext_perim_lines.get_lines().size() / layer->lslices_ex.size());
#ifdef DETAILED_DEBUG_LOGS
weakest_conn.print_info("weakest connection info: ");
#endif
@ -911,73 +997,15 @@ std::tuple<SupportPoints, PartialObjects> check_stability(const PrintObject *po,
}
};
// first we will check local extrusion stability of bridges, then of perimeters. Perimeters are more important, they
// account for most of the curling and possible crashes, so on them we will run also global stability check
for (const auto &island : slice.islands) {
// Support bridges where needed.
for (const LayerExtrusionRange &fill_range : island.fills) {
const LayerRegion *fill_region = layer->get_region(fill_range.region());
for (const auto &fill_idx : fill_range) {
const ExtrusionEntity *entity = fill_region->fills().entities[fill_idx];
if (entity->role() == ExtrusionRole::BridgeInfill) {
for (const ExtrusionLine &bridge :
check_extrusion_entity_stability(entity, fill_region, prev_layer_ext_perim_lines, prev_layer_boundary,
params)) {
if (bridge.support_point_generated.has_value()) {
reckon_new_support_point(*bridge.support_point_generated, create_support_point_position(bridge.b),
float(-EPSILON), Vec2f::Zero());
}
}
}
}
}
const LayerRegion *perimeter_region = layer->get_region(island.perimeters.region());
for (const auto &perimeter_idx : island.perimeters) {
const ExtrusionEntity *entity = perimeter_region->perimeters().entities[perimeter_idx];
std::vector<ExtrusionLine> perims = check_extrusion_entity_stability(entity, perimeter_region,
prev_layer_ext_perim_lines, prev_layer_boundary,
params);
for (const ExtrusionLine &perim : perims) {
if (perim.support_point_generated.has_value()) {
reckon_new_support_point(*perim.support_point_generated, create_support_point_position(perim.b), float(-EPSILON),
Vec2f::Zero());
}
if (perim.is_external_perimeter()) {
current_slice_ext_perims_lines.push_back(perim);
}
}
}
// DEBUG EXPORT, NOT USED NOW
// if (BR_bridge) {
// Lines scaledl;
// for (const auto &l : prev_layer_boundary.get_lines()) {
// scaledl.emplace_back(Point::new_scale(l.a), Point::new_scale(l.b));
// }
// Lines perimsl;
// for (const auto &l : current_slice_ext_perims_lines) {
// perimsl.emplace_back(Point::new_scale(l.a), Point::new_scale(l.b));
// }
// BoundingBox bb = get_extents(scaledl);
// bb.merge(get_extents(perimsl));
// ::Slic3r::SVG svg(debug_out_path(
// ("slice" + std::to_string(slice_idx) + "_" + std::to_string(layer_idx).c_str()).c_str()),
// get_extents(scaledl));
// svg.draw(scaledl, "red", scale_(0.4));
// svg.draw(perimsl, "blue", scale_(0.25));
// svg.Close();
// }
for (const auto &l : unstable_lines_per_slice[slice_idx]) {
assert(l.support_point_generated.has_value());
reckon_new_support_point(*l.support_point_generated, create_support_point_position(l.b), float(-EPSILON), Vec2f::Zero());
}
LD current_slice_lines_distancer(current_slice_ext_perims_lines);
LD current_slice_lines_distancer({ext_perim_lines_per_slice[slice_idx].begin(), ext_perim_lines_per_slice[slice_idx].end()});
float unchecked_dist = params.min_distance_between_support_points + 1.0f;
for (const ExtrusionLine &line : current_slice_ext_perims_lines) {
for (const ExtrusionLine &line : current_slice_lines_distancer.get_lines()) {
if ((unchecked_dist + line.len < params.min_distance_between_support_points && line.curled_up_height < params.curling_tolerance_limit) ||
line.len < EPSILON) {
unchecked_dist += line.len;
@ -993,8 +1021,8 @@ std::tuple<SupportPoints, PartialObjects> check_stability(const PrintObject *po,
}
}
}
current_layer_ext_perims_lines.insert(current_layer_ext_perims_lines.end(), current_slice_ext_perims_lines.begin(),
current_slice_ext_perims_lines.end());
current_layer_ext_perims_lines.insert(current_layer_ext_perims_lines.end(), current_slice_lines_distancer.get_lines().begin(),
current_slice_lines_distancer.get_lines().end());
} // slice iterations
prev_layer_ext_perim_lines = LD(current_layer_ext_perims_lines);
} // layer iterations
@ -1048,7 +1076,8 @@ void debug_export(const SupportPoints& support_points,const PartialObjects& obje
std::tuple<SupportPoints, PartialObjects> full_search(const PrintObject *po, const PrintTryCancel& cancel_func, const Params &params)
{
auto results = check_stability(po, cancel_func, params);
auto precomputed_slices_connections = precompute_slices_connections(po);
auto results = check_stability(po, precomputed_slices_connections, cancel_func, params);
#ifdef DEBUG_FILES
auto [supp_points, objects] = results;
debug_export(supp_points, objects, "issues");