UNFINISHED!

refactoring of algorithm to bottom up propagation of support islands
Added CentroidAccumulators for balance issues checking
This commit is contained in:
PavelMikus 2022-04-26 17:13:46 +02:00
parent f0bdf2760c
commit d9bd1080da
3 changed files with 192 additions and 55 deletions

View File

@ -429,8 +429,8 @@ void PrintObject::find_supportable_issues()
Transform3d inv_transform = (obj_transform * model_transformation).inverse(); Transform3d inv_transform = (obj_transform * model_transformation).inverse();
TriangleSelectorWrapper selector { model_volume->mesh() }; TriangleSelectorWrapper selector { model_volume->mesh() };
for (const Vec3f &support_point : issues.supports_nedded) { for (const SupportableIssues::SupportPoint &support_point : issues.supports_nedded) {
selector.enforce_spot(Vec3f(inv_transform.cast<float>() * support_point), 0.3f); selector.enforce_spot(Vec3f(inv_transform.cast<float>() * support_point.position), 0.3f);
} }
model_volume->supported_facets.set(selector.selector); model_volume->supported_facets.set(selector.selector);

View File

@ -5,10 +5,12 @@
#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 <unordered_set>
#include <stack> #include <stack>
#include "libslic3r/Layer.hpp" #include "libslic3r/Layer.hpp"
#include "libslic3r/ClipperUtils.hpp" #include "libslic3r/ClipperUtils.hpp"
#include "Geometry/ConvexHull.hpp"
#include "PolygonPointTest.hpp" #include "PolygonPointTest.hpp"
#define DEBUG_FILES #define DEBUG_FILES
@ -31,9 +33,68 @@ bool Issues::empty() const {
return supports_nedded.empty() && curling_up.empty(); return supports_nedded.empty() && curling_up.empty();
} }
SupportPoint::SupportPoint(const Vec3f &position, float weight) :
position(position), weight(weight) {
}
SupportPoint::SupportPoint(const Vec3f &position) :
position(position), weight(0.0f) {
}
CurledFilament::CurledFilament(const Vec3f &position, float estimated_height) :
position(position), estimated_height(estimated_height) {
}
CurledFilament::CurledFilament(const Vec3f &position) :
position(position), estimated_height(0.0f) {
}
struct Cell { struct Cell {
float weight; float weight;
int island_id; float curled_height;
int island_id = std::numeric_limits<int>::max();
};
struct CentroidAccumulator {
//TODO add base height
Polygon convex_hull;
Points points;
Vec3d accumulated_value;
float accumulated_weight;
void calculate_base_hull() {
convex_hull = Geometry::convex_hull(points);
}
};
struct CentroidAccumulators {
std::unordered_map<int, size_t> mapping;
std::vector<CentroidAccumulator> acccumulators;
explicit CentroidAccumulators(int count) {
acccumulators.resize(count);
for (int index = 0; index < count; ++index) {
mapping[index - 1] = index;
}
}
CentroidAccumulator& access(int id) {
return acccumulators[mapping[id]];
}
void merge_to(int from_id, int to_id) {
if (from_id == to_id) {
return;
}
CentroidAccumulator &from_acc = this->access(from_id);
CentroidAccumulator &to_acc = this->access(to_id);
to_acc.accumulated_value += from_acc.accumulated_value;
to_acc.accumulated_weight += from_acc.accumulated_weight;
to_acc.points.insert(to_acc.points.end(), from_acc.points.begin(), from_acc.points.end());
to_acc.calculate_base_hull();
mapping[from_id] = mapping[to_id];
}
}; };
struct WeightDistributionMatrix { struct WeightDistributionMatrix {
@ -75,11 +136,17 @@ struct WeightDistributionMatrix {
} }
Vec3i to_global_cell_coords(const Point &p, float print_z) const { Vec3i to_global_cell_coords(const Point &p, float print_z) const {
Vec3i position = Vec3crd { p.x(), p.y(), int(scale_(print_z)) }; Vec3crd position = Vec3crd { p.x(), p.y(), int(scale_(print_z)) };
Vec3i cell_coords = (position - this->global_origin).cwiseQuotient(this->cell_size); Vec3i cell_coords = (position - this->global_origin).cwiseQuotient(this->cell_size);
return cell_coords; return cell_coords;
} }
Vec3i to_global_cell_coords(const Vec3f &position) const {
Vec3crd scaled_position = scaled(position);
Vec3i cell_coords = (scaled_position - this->global_origin).cwiseQuotient(this->cell_size);
return cell_coords;
}
Vec3i to_local_cell_coords(const Point &p, float print_z) const { Vec3i to_local_cell_coords(const Point &p, float print_z) const {
Vec3i cell_coords = this->to_global_cell_coords(p, print_z); Vec3i cell_coords = this->to_global_cell_coords(p, print_z);
return this->to_local_cell_coords(cell_coords); return this->to_local_cell_coords(cell_coords);
@ -107,6 +174,10 @@ struct WeightDistributionMatrix {
return cells[this->to_cell_index(to_local_cell_coords(p, print_z))]; return cells[this->to_cell_index(to_local_cell_coords(p, print_z))];
} }
Cell& access_cell(const Vec3f &unscaled_position) {
return cells[this->to_cell_index(this->to_local_cell_coords(this->to_global_cell_coords(unscaled_position)))];
}
Cell& access_cell(const Vec3i &local_cell_coords) { Cell& access_cell(const Vec3i &local_cell_coords) {
return cells[this->to_cell_index(local_cell_coords)]; return cells[this->to_cell_index(local_cell_coords)];
} }
@ -115,7 +186,7 @@ struct WeightDistributionMatrix {
return cells[this->to_cell_index(local_cell_coords)]; return cells[this->to_cell_index(local_cell_coords)];
} }
void ditribute_edge_weight(const Point &p1, const Point &p2, float print_z, coordf_t width) { void distribute_edge_weight(const Point &p1, const Point &p2, float print_z, float unscaled_width) {
Vec2d dir = (p2 - p1).cast<double>(); Vec2d dir = (p2 - p1).cast<double>();
double length = dir.norm(); double length = dir.norm();
if (length < 0.01) { if (length < 0.01) {
@ -130,17 +201,7 @@ struct WeightDistributionMatrix {
double current_dist_payload = next_len - distributed_length; double current_dist_payload = next_len - distributed_length;
Point location = p1 + ((next_len / length) * dir).cast<coord_t>(); Point location = p1 + ((next_len / length) * dir).cast<coord_t>();
double payload = current_dist_payload * width; float payload = unscale<float>(current_dist_payload) * unscaled_width;
Vec3i local_index = this->to_local_cell_coords(location, print_z);
if (this->to_cell_index(local_index) >= this->cells.size() || this->to_cell_index(local_index) < 0) {
std::cout << "loc: " << local_index.x() << " " << local_index.y() << " " << local_index.z()
<< " globals: " << this->global_cell_count.x() << " "
<< this->global_cell_count.y() << " " << this->local_z_cell_count <<
"+" << this->local_z_cell_count << std::endl;
return;
}
this->access_cell(location, print_z).weight += payload; this->access_cell(location, print_z).weight += payload;
distributed_length = next_len; distributed_length = next_len;
@ -164,42 +225,97 @@ struct WeightDistributionMatrix {
} }
} }
void distribute_top_down() { void analyze(Issues &issues) {
CentroidAccumulators accumulators(issues.supports_nedded.size() + 1);
//TODO split base support points by connectivity!!
for (int x = 0; x < global_cell_count.x(); ++x) {
for (int y = 0; y < global_cell_count.y(); ++y) {
Cell &cell = this->access_cell(Vec3i(x, y, 0));
if (cell.weight > 0) {
cell.island_id = -1;
Vec3crd cell_center = this->get_cell_center(Vec3i(x, y, local_z_index_offset));
CentroidAccumulator &acc = accumulators.access(-1);
acc.points.push_back(Point(cell_center.head<2>()));
acc.accumulated_value += cell_center.cast<double>() * cell.weight;
acc.accumulated_weight += cell.weight;
}
}
}
std::sort(issues.supports_nedded.begin(), issues.supports_nedded.end(),
[](const SupportPoint &left, const SupportPoint &right) {
return left.position.z() < right.position.z();
});
for (int index = 0; index < int(issues.supports_nedded.size()); ++index) {
Vec3i local_coords = this->to_local_cell_coords(
this->to_global_cell_coords(issues.supports_nedded[index].position));
this->access_cell(local_coords).island_id = index;
accumulators.access(index).points.push_back(
Point(scaled(Vec2f(issues.supports_nedded[index].position.head<2>()))));
}
for (const CurledFilament &curling : issues.curling_up) {
this->access_cell(curling.position).curled_height += curling.estimated_height;
}
const auto validate_xy_coords = [&](const Vec2i &local_coords) { const auto validate_xy_coords = [&](const Vec2i &local_coords) {
return local_coords.x() >= 0 && local_coords.y() >= 0 && return local_coords.x() >= 0 && local_coords.y() >= 0 &&
local_coords.x() < this->global_cell_count.x() && local_coords.y() < this->global_cell_count.y(); local_coords.x() < this->global_cell_count.x() && local_coords.y() < this->global_cell_count.y();
}; };
Vec2i valid_coords[9]; std::unordered_set<int> modified_acc_ids;
modified_acc_ids.reserve(issues.supports_nedded.size() + 1);
for (int z = 1; z < local_z_cell_count; ++z) {
modified_acc_ids.clear();
for (int x = 0; x < global_cell_count.x(); ++x) {
for (int y = 0; y < global_cell_count.y(); ++y) {
for (int x = 0; x < global_cell_count.x(); ++x) {
for (int y = 0; y < global_cell_count.y(); ++y) {
for (int z = local_z_cell_count - 1; z > local_z_index_offset; --z) {
Cell &current = this->access_cell(Vec3i(x, y, z)); Cell &current = this->access_cell(Vec3i(x, y, z));
size_t valid_coords_count = 0; //first determine island id
if (current.weight > 0) { if (current.island_id == std::numeric_limits<int>::max()) {
for (int y_offset = -1; y_offset <= 1; ++y_offset) { for (int y_offset = -1; y_offset <= 1; ++y_offset) {
for (int x_offset = -1; x_offset <= 1; ++x_offset) { for (int x_offset = -1; x_offset <= 1; ++x_offset) {
Vec2i xy_coords { x + x_offset, y + y_offset }; Vec2i xy_coords { x + x_offset, y + y_offset };
if (validate_xy_coords(xy_coords) if (validate_xy_coords(xy_coords)) {
&& Cell &under = this->access_cell(Vec3i(x, y, z - 1));
this->access_cell(Vec3i(xy_coords.x(), xy_coords.y(), z - 1)).weight != 0) { int island_id = std::min(under.island_id, current.island_id);
valid_coords[valid_coords_count] = xy_coords; int merging_id = std::max(under.island_id, current.island_id);
valid_coords_count++; if (merging_id != std::numeric_limits<int>::max()
&& island_id != merging_id) {
accumulators.merge_to(merging_id, island_id);
}
if (island_id != std::numeric_limits<int>::max()) {
current.island_id = island_id;
modified_acc_ids.insert(current.island_id);
}
current.curled_height += under.curled_height
/ (2 + std::abs(x_offset) + std::abs(y_offset));
} }
} }
} }
float distribution = current.weight / valid_coords_count;
for (size_t index = 0; index < valid_coords_count; ++index) {
this->access_cell(Vec3i(valid_coords[index].x(), valid_coords[index].y(), z - 1)).weight +=
distribution;
}
if (valid_coords_count > 0) {
current.weight = 0;
}
} }
//Propagate to accumulators. TODO what to do if no supporter is found?
if (current.island_id != std::numeric_limits<int>::max()) {
CentroidAccumulator &acc = accumulators.access(current.island_id);
acc.accumulated_value += current.weight * this->get_cell_center(
this->to_global_cell_coords(Vec3i(x, y, z))).cast<double>();
acc.accumulated_weight += current.weight;
}
}
}
// check stability of modified centroid accumulators
for (int acc_index : modified_acc_ids) {
CentroidAccumulator &acc = accumulators.access(acc_index);
Vec3d centroid = acc.accumulated_value / acc.accumulated_weight;
if (!acc.convex_hull.contains(Point(centroid.head<2>().cast<coord_t>()))) {
//TODO :]
} }
} }
@ -232,7 +348,8 @@ struct WeightDistributionMatrix {
for (int x = 0; x < global_cell_count.x(); ++x) { for (int x = 0; x < global_cell_count.x(); ++x) {
for (int y = 0; y < global_cell_count.y(); ++y) { for (int y = 0; y < global_cell_count.y(); ++y) {
for (int z = 0; z < local_z_cell_count; ++z) { for (int z = 0; z < local_z_cell_count; ++z) {
Vec3f center = unscale(get_cell_center(to_global_cell_coords(Vec3i { x, y, z }))).cast<float>(); Vec3f center = unscale(get_cell_center(to_global_cell_coords(Vec3i { x, y, z }))).cast<
float>();
const Cell &cell = access_cell(Vec3i(x, y, z)); const Cell &cell = access_cell(Vec3i(x, y, z));
if (cell.weight != 0) { if (cell.weight != 0) {
fprintf(fp, "v %f %f %f %f %f %f\n", fprintf(fp, "v %f %f %f %f %f %f\n",
@ -262,7 +379,8 @@ struct WeightDistributionMatrix {
int local_z_cell_count { }; int local_z_cell_count { };
std::vector<Cell> cells { }; std::vector<Cell> cells { };
}; }
;
namespace Impl { namespace Impl {
@ -280,7 +398,8 @@ void debug_export(Issues issues, std::string file_name) {
for (size_t i = 0; i < issues.supports_nedded.size(); ++i) { for (size_t i = 0; i < issues.supports_nedded.size(); ++i) {
fprintf(fp, "v %f %f %f %f %f %f\n", 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), issues.supports_nedded[i].position(0), issues.supports_nedded[i].position(1),
issues.supports_nedded[i].position(2),
1.0, 0.0, 0.0 1.0, 0.0, 0.0
); );
} }
@ -297,7 +416,8 @@ void debug_export(Issues issues, std::string file_name) {
for (size_t i = 0; i < issues.curling_up.size(); ++i) { for (size_t i = 0; i < issues.curling_up.size(); ++i) {
fprintf(fp, "v %f %f %f %f %f %f\n", 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), issues.curling_up[i].position(0), issues.curling_up[i].position(1),
issues.curling_up[i].position(2),
0.0, 1.0, 0.0 0.0, 1.0, 0.0
); );
} }
@ -310,7 +430,8 @@ void debug_export(Issues issues, std::string file_name) {
EdgeGridWrapper compute_layer_edge_grid(const Layer *layer) { EdgeGridWrapper compute_layer_edge_grid(const Layer *layer) {
float min_region_flow_width { 1.0f }; float min_region_flow_width { 1.0f };
for (const auto *region : layer->regions()) { for (const auto *region : layer->regions()) {
min_region_flow_width = std::min(min_region_flow_width, region->flow(FlowRole::frExternalPerimeter).width()); min_region_flow_width = std::min(min_region_flow_width,
region->flow(FlowRole::frExternalPerimeter).width());
} }
std::vector<Points> lines; std::vector<Points> lines;
for (const LayerRegion *layer_region : layer->regions()) { for (const LayerRegion *layer_region : layer->regions()) {
@ -390,7 +511,8 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity,
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( issues.add(
check_extrusion_entity_stability(e, print_z, layer_region, supported_grid, weight_matrix, params)); check_extrusion_entity_stability(e, print_z, layer_region, supported_grid, weight_matrix,
params));
} }
} else { //single extrusion path, with possible varying parameters } else { //single extrusion path, with possible varying parameters
//prepare stack of points on the extrusion path. If there are long segments, additional points might be pushed onto the stack during the algorithm. //prepare stack of points on the extrusion path. If there are long segments, additional points might be pushed onto the stack during the algorithm.
@ -419,15 +541,14 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity,
while (!points.empty()) { while (!points.empty()) {
Point point = points.top(); Point point = points.top();
points.pop(); points.pop();
Vec2f tmp = unscale(point).cast<float>(); Vec3f fpoint = to_vec3f(point);
Vec3f fpoint = Vec3f(tmp.x(), tmp.y(), print_z);
float edge_len = (fpoint - prev_fpoint).norm(); float edge_len = (fpoint - prev_fpoint).norm();
weight_matrix.ditribute_edge_weight(prev_point, point, print_z, flow_width); weight_matrix.distribute_edge_weight(prev_point, point, print_z, unscale<float>(flow_width));
coordf_t dist_from_prev_layer { 0 }; coordf_t dist_from_prev_layer { 0 };
if (!supported_grid.signed_distance(point, flow_width, dist_from_prev_layer)) { // dist from prev layer not found, assume empty layer if (!supported_grid.signed_distance(point, flow_width, dist_from_prev_layer)) { // dist from prev layer not found, assume empty layer
issues.supports_nedded.push_back(fpoint); issues.supports_nedded.push_back(SupportPoint(fpoint));
supports_acc.reset(); supports_acc.reset();
} }
@ -451,7 +572,7 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity,
/ (1.0f / (1.0f
+ (supports_acc.max_curvature + (supports_acc.max_curvature
* params.bridge_distance_decrease_by_curvature_factor / PI))) { * params.bridge_distance_decrease_by_curvature_factor / PI))) {
issues.supports_nedded.push_back(fpoint); issues.supports_nedded.push_back(SupportPoint(fpoint));
supports_acc.reset(); supports_acc.reset();
} }
} else { } else {
@ -462,7 +583,7 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity,
if (dist_from_prev_layer > 0.0f) { //extrusion point is unsupported or poorly supported if (dist_from_prev_layer > 0.0f) { //extrusion point is unsupported or poorly supported
curling_acc.add_distance(edge_len); curling_acc.add_distance(edge_len);
if (curling_acc.max_curvature / (PI * curling_acc.distance) > params.limit_curvature) { if (curling_acc.max_curvature / (PI * curling_acc.distance) > params.limit_curvature) {
issues.curling_up.push_back(fpoint); issues.curling_up.push_back(CurledFilament(fpoint, layer_region->layer()->height));
curling_acc.reset(); curling_acc.reset();
} }
} else { } else {
@ -512,7 +633,8 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_
} // ex_entity } // ex_entity
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, issues.add(check_extrusion_entity_stability(fill,
layer->print_z, layer_region, layer->print_z, layer_region,
supported_grid, weight_matrix, params)); supported_grid, weight_matrix, params));
@ -577,7 +699,8 @@ std::vector<size_t> quick_search(const PrintObject *po, const Params &params) {
return problematic_layers; return problematic_layers;
} }
Issues full_search(const PrintObject *po, const Params &params) { Issues
full_search(const PrintObject *po, const Params &params) {
using namespace Impl; using namespace Impl;
WeightDistributionMatrix matrix { }; WeightDistributionMatrix matrix { };
@ -609,7 +732,7 @@ Issues full_search(const PrintObject *po, const Params &params) {
} }
); );
matrix.distribute_top_down(); matrix.analyze(found_issues);
matrix.debug_export("weight"); matrix.debug_export("weight");

View File

@ -16,9 +16,23 @@ struct Params {
float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / ( 1 + this factor * (curvature / PI) ) float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / ( 1 + this factor * (curvature / PI) )
}; };
struct SupportPoint {
SupportPoint(const Vec3f &position, float weight);
explicit SupportPoint(const Vec3f &position);
Vec3f position;
float weight;
};
struct CurledFilament {
CurledFilament(const Vec3f &position, float estimated_height);
explicit CurledFilament(const Vec3f &position);
Vec3f position;
float estimated_height;
};
struct Issues { struct Issues {
std::vector<Vec3f> supports_nedded; std::vector<SupportPoint> supports_nedded;
std::vector<Vec3f> curling_up; std::vector<CurledFilament> curling_up;
void add(const Issues &layer_issues); void add(const Issues &layer_issues);
bool empty() const; bool empty() const;