refactoring,
initial work on weight distribution matrix
This commit is contained in:
parent
a46e1dc79c
commit
cfe9b27a6d
3 changed files with 224 additions and 73 deletions
|
@ -7,27 +7,37 @@
|
|||
namespace Slic3r {
|
||||
|
||||
struct EdgeGridWrapper {
|
||||
EdgeGridWrapper(coord_t resolution, ExPolygons ex_polys) :
|
||||
ex_polys(ex_polys) {
|
||||
EdgeGridWrapper(coord_t edge_width, std::vector<Points> lines) :
|
||||
lines(lines), edge_width(edge_width) {
|
||||
|
||||
grid.create(this->ex_polys, resolution);
|
||||
grid.create(this->lines, edge_width, true);
|
||||
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, point_width, tmp_dist_out);
|
||||
// decrease the distance by half of edge width of previous layer and half of flow width of current layer
|
||||
dist_out = tmp_dist_out - point_width / 2;
|
||||
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;
|
||||
std::vector<Points> lines;
|
||||
coord_t edge_width;
|
||||
};
|
||||
|
||||
namespace TODO {
|
||||
|
||||
class PolygonPointTest {
|
||||
|
||||
struct Segment {
|
||||
coord_t start;
|
||||
std::vector<size_t> lines;
|
||||
};
|
||||
|
||||
std::vector<Segment> x_coord_segments;
|
||||
|
||||
public:
|
||||
PolygonPointTest(const ExPolygons &ex_polygons) {
|
||||
std::vector<Line> lines;
|
||||
|
@ -43,30 +53,37 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<size_t, bool>> sweeping_data(lines.size());
|
||||
sweeping_data.reserve(lines.size() * 2);
|
||||
for (int line_index = 0; line_index < lines.size(); ++line_index) {
|
||||
std::vector<std::pair<size_t, bool>> sweeping_data(lines.size() * 2);
|
||||
for (size_t line_index = 0; line_index < lines.size(); ++line_index) {
|
||||
sweeping_data[line_index].first = line_index;
|
||||
sweeping_data[line_index].second = true;
|
||||
sweeping_data[line_index * 2 + 1].first = line_index;
|
||||
sweeping_data[line_index * 2 + 1].second = false;
|
||||
}
|
||||
|
||||
const auto data_comparator = [&lines](const std::pair<size_t, bool> &left,
|
||||
const std::pair<size_t, bool> &right) {
|
||||
return std::min(lines[left.first].a.x(), lines[left.first].b.x())
|
||||
< std::min(lines[right.first].a.x(), lines[right.first].b.x());
|
||||
const auto left_x =
|
||||
left.second ?
|
||||
std::min(lines[left.first].a.x(), lines[left.first].b.x()) :
|
||||
std::max(lines[left.first].a.x(), lines[left.first].b.x());
|
||||
const auto right_x =
|
||||
right.second ?
|
||||
std::min(lines[right.first].a.x(), lines[right.first].b.x()) :
|
||||
std::max(lines[right.first].a.x(), lines[right.first].b.x());
|
||||
|
||||
return left_x < right_x;
|
||||
};
|
||||
|
||||
std::make_heap(sweeping_data.begin(), sweeping_data.end(), data_comparator);
|
||||
std::sort(sweeping_data.begin(), sweeping_data.end(), data_comparator);
|
||||
std::set<size_t> active_lines;
|
||||
|
||||
//TODO continue
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,119 @@ bool Issues::empty() const {
|
|||
return supports_nedded.empty() && curling_up.empty();
|
||||
}
|
||||
|
||||
struct Cell {
|
||||
float weight;
|
||||
char last_extrusion_id;
|
||||
};
|
||||
|
||||
struct WeightDistributionMatrix {
|
||||
// Lets make Z coord half the size of X (and Y).
|
||||
// This corresponds to angle of ~26 degrees between center of one cell and other one up and sideways
|
||||
// which is approximately a limiting printable angle.
|
||||
|
||||
WeightDistributionMatrix(const PrintObject *po, size_t layer_idx_begin, size_t layer_idx_end) {
|
||||
Vec3crd object_origin = scaled(po->trafo_centered() * Vec3d::Zero());
|
||||
Vec3crd min = object_origin - po->size() / 2 - Vec3crd::Ones();
|
||||
Vec3crd max = object_origin + po->size() / 2 + Vec3crd::Ones();
|
||||
|
||||
cell_size = Vec3crd { int(cell_height * 2), int(cell_height * 2), int(cell_height) };
|
||||
|
||||
global_origin = min;
|
||||
global_size = max - min;
|
||||
global_cell_count = global_size.cwiseQuotient(cell_size);
|
||||
|
||||
coord_t local_min_z = scale_(po->layers()[layer_idx_begin]->slice_z);
|
||||
coord_t local_max_z = scale_(po->layers()[layer_idx_end]->slice_z);
|
||||
coord_t local_min_z_index = local_min_z / cell_size.z();
|
||||
coord_t local_max_z_index = local_max_z / cell_size.z();
|
||||
|
||||
local_z_index_offset = local_min_z_index;
|
||||
local_z_cell_count = local_max_z_index - local_min_z_index + 1;
|
||||
|
||||
cells.resize(local_z_cell_count * global_cell_count.y() * global_cell_count.x());
|
||||
}
|
||||
|
||||
Vec3i to_global_cell_coords(const Point &p, float slice_z) const {
|
||||
Vec3crd position = Vec3crd { p.x(), p.y(), coord_t(scale_(slice_z)) };
|
||||
Vec3i cell_coords = position.cwiseQuotient(cell_size);
|
||||
return cell_coords;
|
||||
}
|
||||
|
||||
Vec3i to_local_cell_coords(const Point &p, float slice_z) const {
|
||||
Vec3i cell_coords = to_global_cell_coords(p, slice_z);
|
||||
Vec3i local_cell_coords = cell_coords - local_z_index_offset * Vec3i::UnitZ();
|
||||
return local_cell_coords;
|
||||
}
|
||||
|
||||
size_t to_cell_index(const Vec3i &local_cell_coords) {
|
||||
assert(local_cell_coords.x() >= 0);
|
||||
assert(local_cell_coords.x() < global_cell_count.x());
|
||||
assert(local_cell_coords.y() >= 0);
|
||||
assert(local_cell_coords.y() < global_cell_count.y());
|
||||
assert(local_cell_coords.z() >= 0);
|
||||
assert(local_cell_coords.z() < local_z_cell_count);
|
||||
return local_cell_coords.z() * global_cell_count.x() * global_cell_count.y()
|
||||
+ local_cell_coords.y() * global_cell_count.x() +
|
||||
local_cell_coords.x();
|
||||
}
|
||||
|
||||
Vec3crd cell_center(const Vec3i &global_cell_coords) {
|
||||
return global_origin + global_cell_coords.cwiseProduct(cell_size);
|
||||
}
|
||||
|
||||
Cell& access_cell(const Point &p, float slice_z) {
|
||||
return cells[to_cell_index(to_local_cell_coords(p, slice_z))];
|
||||
}
|
||||
|
||||
Cell& access_cell(const Vec3i& local_cell_coords) {
|
||||
return cells[to_cell_index(local_cell_coords)];
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG_FILES
|
||||
void debug_export(std::string file_name) {
|
||||
Slic3r::CNumericLocalesSetter locales_setter;
|
||||
{
|
||||
FILE *fp = boost::nowide::fopen(debug_out_path((file_name + "_matrix.obj").c_str()).c_str(), "w");
|
||||
if (fp == nullptr) {
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< "Debug files: Couldn't open " << file_name << " for writing";
|
||||
return;
|
||||
}
|
||||
|
||||
for (int x = 0; x < global_cell_count.x(); ++x) {
|
||||
for (int y = 0; y < global_cell_count.y(); ++y) {
|
||||
for (int z = 0; z < local_z_cell_count; ++z) {
|
||||
Vec3f center = unscale(cell_center(Vec3i(x, y, z + local_z_index_offset))).cast<float>();
|
||||
Cell &cell = access_cell(Vec3i(x, y, z));
|
||||
fprintf(fp, "v %f %f %f %f %f %f\n",
|
||||
center(0), center(1),
|
||||
center(2),
|
||||
cell.weight, 0.0, 0.0
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static constexpr float cell_height = scale_(0.15f);
|
||||
|
||||
Vec3crd cell_size;
|
||||
|
||||
Vec3crd global_origin;
|
||||
Vec3crd global_size;
|
||||
Vec3i global_cell_count;
|
||||
|
||||
int local_z_index_offset;
|
||||
int local_z_cell_count;
|
||||
std::vector<Cell> cells;
|
||||
|
||||
};
|
||||
|
||||
namespace Impl {
|
||||
|
||||
#ifdef DEBUG_FILES
|
||||
|
@ -79,23 +192,20 @@ EdgeGridWrapper compute_layer_edge_grid(const Layer *layer) {
|
|||
for (const auto *region : layer->regions()) {
|
||||
min_region_flow_width = std::min(min_region_flow_width, region->flow(FlowRole::frExternalPerimeter).width());
|
||||
}
|
||||
ExPolygons ex_polygons;
|
||||
std::vector<Points> lines;
|
||||
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
|
||||
|| perimeter->role() == ExtrusionRole::erOverhangPerimeter) {
|
||||
Points perimeter_points { };
|
||||
perimeter->collect_points(perimeter_points);
|
||||
assert(perimeter->is_loop());
|
||||
perimeter_points.pop_back(); // EdgeGrid structure does not like repetition of the first/last point
|
||||
ex_polygons.push_back(ExPolygon { perimeter_points });
|
||||
} // ex_perimeter
|
||||
} // perimeter
|
||||
lines.push_back(Points { });
|
||||
ex_entity->collect_points(lines.back());
|
||||
} // ex_entity
|
||||
|
||||
for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) {
|
||||
lines.push_back(Points { });
|
||||
ex_entity->collect_points(lines.back());
|
||||
} // ex_entity
|
||||
}
|
||||
|
||||
return EdgeGridWrapper(scale_(min_region_flow_width), ex_polygons);
|
||||
return EdgeGridWrapper(scale_(min_region_flow_width), lines);
|
||||
}
|
||||
|
||||
//TODO needs revision
|
||||
|
@ -119,14 +229,36 @@ coordf_t get_flow_width(const LayerRegion *region, ExtrusionRole role) {
|
|||
coordf_t get_max_allowed_distance(ExtrusionRole role, coord_t flow_width, bool external_perimeters_first,
|
||||
const Params ¶ms) { // <= distance / flow_width (can be larger for perimeter, if not external perimeter first)
|
||||
if ((role == ExtrusionRole::erExternalPerimeter || role == ExtrusionRole::erOverhangPerimeter)
|
||||
&& !(external_perimeters_first)
|
||||
&& (external_perimeters_first)
|
||||
) {
|
||||
return params.max_ex_perim_unsupported_distance_factor * flow_width;
|
||||
return params.max_first_ex_perim_unsupported_distance_factor * flow_width;
|
||||
} else {
|
||||
return params.max_unsupported_distance_factor * flow_width;
|
||||
}
|
||||
}
|
||||
|
||||
struct SegmentAccumulator {
|
||||
float distance = 0; //accumulated distance
|
||||
float curvature = 0; //accumulated signed ccw angles
|
||||
float max_curvature = 0; //max absolute accumulated value
|
||||
|
||||
void add_distance(float dist) {
|
||||
distance += dist;
|
||||
}
|
||||
|
||||
void add_angle(float ccw_angle) {
|
||||
curvature += ccw_angle;
|
||||
max_curvature = std::max(max_curvature, std::abs(curvature));
|
||||
}
|
||||
|
||||
void reset() {
|
||||
distance = 0;
|
||||
curvature = 0;
|
||||
max_curvature = 0;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Issues check_extrusion_entity_stability(const ExtrusionEntity *entity,
|
||||
float slice_z,
|
||||
const LayerRegion *layer_region,
|
||||
|
@ -145,13 +277,17 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity,
|
|||
points.push(p);
|
||||
}
|
||||
|
||||
float unsupported_distance = params.bridge_distance + 1.0f; // initialize unsupported distance with larger than tolerable distance ->
|
||||
SegmentAccumulator supports_acc { };
|
||||
supports_acc.add_distance(params.bridge_distance + 1.0f); // initialize unsupported distance with larger than tolerable distance ->
|
||||
// -> it prevents extruding perimeter start and short loops into air.
|
||||
float curvature = 0; // current curvature of the unsupported part of the extrusion - it is accumulated value of signed ccw angles of continuously unsupported points.
|
||||
float max_curvature = 0; // max curvature (in abs value) for the current unsupported segment.
|
||||
Vec2f tmp = unscale(points.top()).cast<float>();
|
||||
Vec3f prev_fpoint = Vec3f(tmp.x(), tmp.y(), slice_z); // prev point of the path. Initialize with first point.
|
||||
SegmentAccumulator curling_acc { };
|
||||
|
||||
const auto to_vec3f = [slice_z](const Point &point) {
|
||||
Vec2f tmp = unscale(point).cast<float>();
|
||||
return Vec3f(tmp.x(), tmp.y(), slice_z);
|
||||
};
|
||||
|
||||
Vec3f prev_fpoint = to_vec3f(points.top()); // prev point of the path. Initialize with first point.
|
||||
coordf_t flow_width = get_flow_width(layer_region, entity->role());
|
||||
bool external_perimters_first = layer_region->region().config().external_perimeters_first;
|
||||
const coordf_t max_allowed_dist_from_prev_layer = get_max_allowed_distance(entity->role(), flow_width,
|
||||
|
@ -162,54 +298,50 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity,
|
|||
points.pop();
|
||||
Vec2f tmp = unscale(point).cast<float>();
|
||||
Vec3f fpoint = Vec3f(tmp.x(), tmp.y(), slice_z);
|
||||
float edge_len = (fpoint - prev_fpoint).norm();
|
||||
|
||||
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
|
||||
issues.supports_nedded.push_back(fpoint);
|
||||
unsupported_distance = 0;
|
||||
curvature = 0;
|
||||
max_curvature = 0;
|
||||
supports_acc.reset();
|
||||
}
|
||||
|
||||
float angle = 0;
|
||||
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);
|
||||
angle = float(atan2(float(cross), float(dot))); // ccw angle, TODO replace with angle func, once it gets into master
|
||||
}
|
||||
|
||||
supports_acc.add_angle(angle);
|
||||
curling_acc.add_angle(angle);
|
||||
|
||||
if (dist_from_prev_layer > max_allowed_dist_from_prev_layer) { //extrusion point is unsupported
|
||||
unsupported_distance += (fpoint - prev_fpoint).norm(); // for algorithm simplicity, expect that the whole line between prev and current point is unsupported
|
||||
supports_acc.add_distance(edge_len); // for algorithm simplicity, expect that the whole line between prev and current point is unsupported
|
||||
|
||||
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))); // ccw angle, TODO replace with angle func, once it gets into master
|
||||
|
||||
curvature += angle;
|
||||
max_curvature = std::max(abs(curvature), max_curvature);
|
||||
}
|
||||
|
||||
if (unsupported_distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports.
|
||||
if (supports_acc.distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports.
|
||||
> params.bridge_distance
|
||||
/ (1.0f + (max_curvature * params.bridge_distance_decrease_by_curvature_factor / PI))) {
|
||||
/ (1.0f
|
||||
+ (supports_acc.max_curvature
|
||||
* params.bridge_distance_decrease_by_curvature_factor / PI))) {
|
||||
issues.supports_nedded.push_back(fpoint);
|
||||
|
||||
//DEBUG stuff TODO remove
|
||||
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;
|
||||
supports_acc.reset();
|
||||
}
|
||||
} else {
|
||||
unsupported_distance = 0;
|
||||
curvature = 0;
|
||||
max_curvature = 0;
|
||||
supports_acc.reset();
|
||||
}
|
||||
|
||||
// Estimation of short curvy segments which are not supported -> problems with curling
|
||||
// Currently the curling issues are ignored
|
||||
if (max_curvature / (PI * unsupported_distance) > params.limit_curvature) {
|
||||
issues.curling_up.push_back(fpoint);
|
||||
if (dist_from_prev_layer > 0.0f) { //extrusion point is unsupported or poorly supported
|
||||
curling_acc.add_distance(edge_len);
|
||||
if (curling_acc.max_curvature / (PI * curling_acc.distance) > params.limit_curvature) {
|
||||
issues.curling_up.push_back(fpoint);
|
||||
curling_acc.reset();
|
||||
}
|
||||
} else {
|
||||
curling_acc.reset();
|
||||
}
|
||||
|
||||
prev_fpoint = fpoint;
|
||||
|
@ -309,6 +441,10 @@ std::vector<size_t> quick_search(const PrintObject *po, const Params ¶ms) {
|
|||
|
||||
Issues full_search(const PrintObject *po, const Params ¶ms) {
|
||||
using namespace Impl;
|
||||
|
||||
WeightDistributionMatrix matrix { po, 0, po->layers().size() };
|
||||
matrix.debug_export("matrix");
|
||||
|
||||
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) {
|
||||
|
|
|
@ -9,15 +9,13 @@ namespace SupportableIssues {
|
|||
|
||||
struct Params {
|
||||
float bridge_distance = 10.0f;
|
||||
float limit_curvature = 0.3f; // used to detect curling issues, but they are currently not considered anyway
|
||||
float limit_curvature = 0.15f; // used to detect curling issues
|
||||
|
||||
float max_unsupported_distance_factor = 0.0f;
|
||||
// allow printing external perimeter in the air to some extent. it hopefully attaches to the internal perimeter.
|
||||
float max_ex_perim_unsupported_distance_factor = 1.0f;
|
||||
float max_first_ex_perim_unsupported_distance_factor = 0.0f; // if external perim first, return tighter max allowed distance from previous layer extrusion
|
||||
float max_unsupported_distance_factor = 1.0f; // For internal perimeters, infill, bridges etc, allow gap of [extrusion width] size, these extrusions have usually something to stick to.
|
||||
float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / ( 1 + this factor * (curvature / PI) )
|
||||
};
|
||||
|
||||
|
||||
struct Issues {
|
||||
std::vector<Vec3f> supports_nedded;
|
||||
std::vector<Vec3f> curling_up;
|
||||
|
|
Loading…
Reference in a new issue