From bef26fee2bf593efa694f8756029a5c459acf6f4 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 20 Jun 2022 17:43:04 +0200 Subject: [PATCH] Bugfixing and refactoring --- src/libslic3r/CMakeLists.txt | 3 +- src/libslic3r/SupportableIssuesSearch.cpp | 1500 +++++++---------- src/libslic3r/SupportableIssuesSearch.hpp | 10 +- .../SupportableIssuesSearchRefactoring.cpp | 635 ------- 4 files changed, 591 insertions(+), 1557 deletions(-) delete mode 100644 src/libslic3r/SupportableIssuesSearchRefactoring.cpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index e866ac34e..0a0062a75 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -245,8 +245,7 @@ set(SLIC3R_SOURCES SlicingAdaptive.hpp Subdivide.cpp Subdivide.hpp -# SupportableIssuesSearch.cpp - SupportableIssuesSearchRefactoring.cpp + SupportableIssuesSearch.cpp SupportableIssuesSearch.hpp SupportMaterial.cpp SupportMaterial.hpp diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp index 55d883f0a..73f89e0d6 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -21,6 +21,40 @@ #endif namespace Slic3r { + +static const size_t NULL_ACC_ID = std::numeric_limits::max(); + +class ExtrusionLine +{ +public: + ExtrusionLine() : + a(Vec2f::Zero()), b(Vec2f::Zero()), len(0.0f) { + } + ExtrusionLine(const Vec2f &_a, const Vec2f &_b) : + a(_a), b(_b), len((_a - _b).norm()) { + } + + float length() { + return (a - b).norm(); + } + + Vec2f a; + Vec2f b; + float len; + + size_t stability_accumulator_id = NULL_ACC_ID; + + static const constexpr int Dim = 2; + using Scalar = Vec2f::Scalar; +}; + +auto get_a(ExtrusionLine &&l) { + return l.a; +} +auto get_b(ExtrusionLine &&l) { + return l.b; +} + namespace SupportableIssues { void Issues::add(const Issues &layer_issues) { @@ -45,615 +79,596 @@ CurledFilament::CurledFilament(const Vec3f &position) : position(position), estimated_height(0.0f) { } -struct Cell { - float volume; - float curled_height; - int accumulator_id = std::numeric_limits::max(); +class LayerLinesDistancer { +private: + std::vector lines; + AABBTreeIndirect::Tree<2, float> tree; + +public: + explicit LayerLinesDistancer(std::vector &&lines) : + lines(lines) { + tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(lines); + } + + // negative sign means inside + float signed_distance_from_lines(const Vec2f &point, size_t &nearest_line_index_out, + Vec2f &nearest_point_out) const { + auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, point, nearest_line_index_out, + nearest_point_out); + if (distance < 0) + return std::numeric_limits::infinity(); + + distance = sqrt(distance); + const ExtrusionLine &line = lines[nearest_line_index_out]; + Vec2f v1 = line.b - line.a; + Vec2f v2 = point - line.a; + if ((v1.x() * v2.y()) - (v1.y() * v2.x()) > 0.0) { + distance *= -1; + } + return distance; + } + + const ExtrusionLine& get_line(size_t line_idx) const { + return lines[line_idx]; + } + + const std::vector& get_lines() const { + return lines; + } }; -class CentroidAccumulator { +class StabilityAccumulator { private: - Polygon convex_hull { }; - Points points { }; - public: - Vec3f accumulated_value = Vec3f::Zero(); + Polygon base_convex_hull { }; + Points support_points { }; + Vec3f centroid_accumulator = Vec3f::Zero(); float accumulated_volume { }; float base_area { }; - float additional_supports_adhesion { }; float base_height { }; - explicit CentroidAccumulator(float base_height) : +public: + explicit StabilityAccumulator(float base_height) : base_height(base_height) { } - const Polygon& base_hull() { - if (this->convex_hull.empty()) { - this->convex_hull = Geometry::convex_hull(this->points); + void add_base_extrusion(const ExtrusionLine &line, float width, float print_z, float mm3_per_mm) { + base_area += line.len * width; + support_points.push_back(Point::new_scale(line.a)); + support_points.push_back(Point::new_scale(line.b)); + base_convex_hull.clear(); + add_extrusion(line, print_z, mm3_per_mm); + } + + void add_support_point(const Point &position, float area) { + support_points.push_back(position); + base_convex_hull.clear(); + base_area += area; + } + + void add_extrusion(const ExtrusionLine &line, float print_z, float mm3_per_mm) { + float volume = line.len * mm3_per_mm; + accumulated_volume += volume; + Vec2f center = (line.a + line.b) / 2.0f; + centroid_accumulator += volume * Vec3f(center.x(), center.y(), print_z); + } + + Vec3f get_centroid() const { + return centroid_accumulator / accumulated_volume; + } + + float get_base_area() const { + return base_area; + } + float get_base_height() const { + return base_height; + } + float get_accumulated_volume() const { + return accumulated_volume; + } + + const Polygon& segment_base_hull() { + if (this->base_convex_hull.empty()) { + this->base_convex_hull = Geometry::convex_hull(this->support_points); } - return this->convex_hull; + return this->base_convex_hull; } - void add_base_points(const Points &other) { - this->points.insert(this->points.end(), other.begin(), other.end()); - convex_hull.clear(); + const Points& get_support_points() { + return support_points; } - const Points& get_base_points() { - return this->points; + void add_from(const StabilityAccumulator &acc) { + this->support_points.insert(this->support_points.end(), acc.support_points.begin(), + acc.support_points.end()); + base_convex_hull.clear(); + this->centroid_accumulator += acc.centroid_accumulator; + this->accumulated_volume += acc.accumulated_volume; + this->base_area += acc.base_area; } }; -struct CentroidAccumulators { - std::unordered_map mapping; - std::vector acccumulators; +struct StabilityAccumulators { +private: + size_t next_id = 0; + std::unordered_map mapping; + std::vector acccumulators; - explicit CentroidAccumulators(size_t reserve_count) { - acccumulators.reserve(reserve_count); - } - - CentroidAccumulator& create_accumulator(int id, float base_height) { - mapping[id] = acccumulators.size(); - acccumulators.push_back(CentroidAccumulator { base_height }); - return this->access(id); - } - - CentroidAccumulator& access(int id) { - return acccumulators[mapping[id]]; - } - - void merge_to(int from_id, int to_id) { - CentroidAccumulator &from_acc = this->access(from_id); - CentroidAccumulator &to_acc = this->access(to_id); + void merge_to(size_t from_id, size_t to_id) { + StabilityAccumulator &from_acc = this->access(from_id); + StabilityAccumulator &to_acc = this->access(to_id); if (&from_acc == &to_acc) { return; } - to_acc.accumulated_value += from_acc.accumulated_value; - to_acc.accumulated_volume += from_acc.accumulated_volume; - to_acc.add_base_points(from_acc.get_base_points()); - to_acc.base_area += from_acc.base_area; + to_acc.add_from(from_acc); mapping[from_id] = mapping[to_id]; - from_acc = CentroidAccumulator { 0.0f }; + from_acc = StabilityAccumulator { 0.0f }; } -}; - -class BalanceDistributionGrid { - - static constexpr float cell_height = scale_(0.3f); - - 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 cells { }; public: + StabilityAccumulators() = default; - BalanceDistributionGrid() = default; - - void init(const PrintObject *po, size_t layer_idx_begin, size_t layer_idx_end) { - Vec2crd size_half = po->size().head<2>().cwiseQuotient(Vec2crd(2, 2)) + Vec2crd::Ones(); - Vec3crd min = Vec3crd(-size_half.x(), -size_half.y(), 0); - Vec3crd max = Vec3crd(size_half.x(), size_half.y(), po->height()); - - cell_size = Vec3crd { int(cell_height * 2), int(cell_height * 2), int(cell_height) }; - assert(cell_size.x() == cell_size.y()); - - global_origin = min; - global_size = max - min; - global_cell_count = global_size.cwiseQuotient(cell_size) + Vec3i::Ones(); - - coord_t local_min_z = scale_(po->layers()[layer_idx_begin]->print_z); - coord_t local_max_z = scale_(po->layers()[layer_idx_end > 0 ? layer_idx_end - 1 : 0]->print_z); - int local_min_z_index = local_min_z / cell_size.z(); - int local_max_z_index = local_max_z / cell_size.z() + 1; - - local_z_index_offset = local_min_z_index; - local_z_cell_count = local_max_z_index + 1 - local_min_z_index; - - cells.resize(local_z_cell_count * global_cell_count.y() * global_cell_count.x()); + int create_accumulator(float base_height) { + size_t id = next_id; + next_id++; + mapping[id] = acccumulators.size(); + acccumulators.push_back(StabilityAccumulator { base_height }); + return id; } - Vec3i to_global_cell_coords(const Vec3i &local_cell_coords) const { - return local_cell_coords + local_z_index_offset * Vec3i::UnitZ(); + StabilityAccumulator& access(size_t id) { + return acccumulators[mapping[id]]; } - Vec3i to_local_cell_coords(const Vec3i &global_cell_coords) const { - return global_cell_coords - local_z_index_offset * Vec3i::UnitZ(); - } - - Vec3i to_global_cell_coords(const Point &p, float print_z) const { - Vec3crd position = Vec3crd { p.x(), p.y(), int(scale_(print_z)) }; - Vec3i cell_coords = (position - this->global_origin).cwiseQuotient(this->cell_size); - 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 cell_coords = this->to_global_cell_coords(p, print_z); - return this->to_local_cell_coords(cell_coords); - } - - size_t to_cell_index(const Vec3i &local_cell_coords) const { - 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 get_cell_center(const Vec3i &global_cell_coords) const { - return global_origin + global_cell_coords.cwiseProduct(this->cell_size) - + this->cell_size.cwiseQuotient(Vec3crd(2, 2, 2)); - } - - Cell& access_cell(const Point &p, float 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) { - return cells[this->to_cell_index(local_cell_coords)]; - } - - const Cell& access_cell(const Vec3i &local_cell_coords) const { - return cells[this->to_cell_index(local_cell_coords)]; - } - - void distribute_edge(const Point &p1, const Point &p2, float print_z, float unscaled_width, float unscaled_height) { - Vec2d dir = (p2 - p1).cast(); - double length = dir.norm(); - if (length < 0.1) { + void merge_accumulators(size_t from_id, size_t to_id) { + if (from_id == NULL_ACC_ID || to_id == NULL_ACC_ID) { return; } - double step_size = this->cell_size.x() / 2.0; - - float diameter = unscaled_height * unscaled_width * 0.7071f; // constant to simulate somewhat elliptical shape (1/sqrt(2)) - - double distributed_length = 0; - while (distributed_length < length) { - double next_len = std::min(length, distributed_length + step_size); - double current_dist_payload = next_len - distributed_length; - - Point location = p1 + ((next_len / length) * dir).cast(); - float payload = unscale(current_dist_payload) * diameter; - this->access_cell(location, print_z).volume += payload; - - distributed_length = next_len; + StabilityAccumulator &from_acc = this->access(from_id); + StabilityAccumulator &to_acc = this->access(to_id); + if (&from_acc == &to_acc) { + return; } - } - - void merge(const BalanceDistributionGrid &other) { - int z_start = std::max(local_z_index_offset, other.local_z_index_offset); - int z_end = std::min(local_z_index_offset + local_z_cell_count, - other.local_z_index_offset + other.local_z_cell_count); - - for (int x = 0; x < global_cell_count.x(); ++x) { - for (int y = 0; y < global_cell_count.y(); ++y) { - for (int z = z_start; z < z_end; ++z) { - Vec3i global_coords { x, y, z }; - Vec3i local_coords = this->to_local_cell_coords(global_coords); - Vec3i other_local_coords = other.to_local_cell_coords(global_coords); - this->access_cell(local_coords).volume += other.access_cell(other_local_coords).volume; - } - } - } - } - - void analyze(Issues &issues, const Params ¶ms) { - const auto validate_xy_coords = [&](const Vec2i &local_coords) { - 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(); - }; - CentroidAccumulators accumulators(issues.supports_nedded.size() + 4); // just an estimation; one for each support point from prev step, and 4 for the base - auto custom_comparator = [](const Vec2i &left, const Vec2i &right) { - if (left.x() == right.x()) { - return left.y() < right.y(); - } - return left.x() < right.x(); - }; - - //initialization of the bed accumulators from the bed cells (first layer of grid) - int next_accumulator_id = -1; // accumulators from the bed have negative ids, starting with -1. Accumulators generated by support points have nonegative ids, starting with 0, and sorted by height - // The reason is, that during merging of accumulators (when they meet at the upper cells), algorithm always keeps the one with the lower id (so bed is preffered), and discards the other - std::set coords_to_check(custom_comparator); // set of coords to check for the current accumulator (search based on connectivity) - for (int y = 0; y < global_cell_count.y(); ++y) { - for (int x = 0; x < global_cell_count.x(); ++x) { - Cell &origin_cell = this->access_cell(Vec3i(x, y, 0)); - if (origin_cell.volume > 0 && origin_cell.accumulator_id == std::numeric_limits::max()) { // cell has volume and no accumulator assigned yet - CentroidAccumulator &acc = accumulators.create_accumulator(next_accumulator_id, 0); // create new accumulator, height is 0, because we are on the bed - coords_to_check.clear(); - coords_to_check.insert(Vec2i(x, y)); - while (!coords_to_check.empty()) { // insert the origin cell coords in to the set, and search all connected cells with volume and without assigned accumulator, assign them to acc - Vec2i current_coords = *coords_to_check.begin(); - coords_to_check.erase(coords_to_check.begin()); - if (!validate_xy_coords(current_coords)) { // invalid coords, drop - continue; - } - Cell &cell = this->access_cell(Vec3i(current_coords.x(), current_coords.y(), 0)); - if (cell.volume <= 0 || cell.accumulator_id != std::numeric_limits::max()) { // empty cell or already assigned, drop - continue; - } - cell.accumulator_id = next_accumulator_id; // update cell accumulator id, update the accumulator with the new cell data, add neighbours to queue - Vec3crd cell_center = this->get_cell_center( - Vec3i(current_coords.x(), current_coords.y(), local_z_index_offset)); - acc.add_base_points( { Point(cell_center.head<2>()) }); - acc.accumulated_value += unscale(cell_center).cast() * cell.volume; - acc.accumulated_volume += cell.volume; - - for (int y_offset = -1; y_offset <= 1; ++y_offset) { - for (int x_offset = -1; x_offset <= 1; ++x_offset) { - if (y_offset != 0 || x_offset != 0) { - coords_to_check.insert( - Vec2i(current_coords.x() + x_offset, current_coords.y() + y_offset)); - } - } - } - } - next_accumulator_id--; - //base area is separated from the base convex hull - bed accumulators are initialized with convex hull area (TODO compute from number of covered cells instead ) - // but support points are initialized with constant, and during merging, the base_areas are added. - acc.base_area = unscale(unscale(acc.base_hull().area())); //apply unscale 2x, it has units of area - } - } - } - - // sort support points by height, so that their accumulators ids are also sorted by height - 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).accumulator_id = index; // assign accumulator id (in case that multiple support points fall into the same cell, they are just overriden) - CentroidAccumulator &acc = accumulators.create_accumulator(index, - issues.supports_nedded[index].position.z()); - acc.add_base_points( { Point(scaled(Vec2f(issues.supports_nedded[index].position.head<2>()))) }); - acc.base_area = params.support_points_interface_area; - } - - // add curling data to each cell - for (const CurledFilament &curling : issues.curling_up) { - this->access_cell(curling.position).curled_height += curling.estimated_height; - } - - // keep map of modified accumulator for each layer, so that discarded accumulators are not further checked for stability - // the value of the map is list of cells with curling, that should be further checked for pressure stability with repsect to the accumulator - std::unordered_map> modified_acc_ids; - // At the end of the layer check, accumulators are further filtered, since merging causes that single accumulator can have mutliple ids. - // But each accumulator should be checked only once. - std::unordered_map> filtered_active_accumulators; - modified_acc_ids.reserve(issues.supports_nedded.size() + 1); - - // For each grid layer, cells are added to the accumulators and all active accumulators are checked of stability. - for (int z = 1; z < local_z_cell_count; ++z) { - std::cout << "current z: " << z << std::endl; - - modified_acc_ids.clear(); - - for (int x = 0; x < global_cell_count.x(); ++x) { - for (int y = 0; y < global_cell_count.y(); ++y) { - Cell ¤t = this->access_cell(Vec3i(x, y, z)); - // distribute islands info - look for neighbours under the cell, and pick the smallest accumulator id - // also gather all ids, they will be merged to the smallest id accumualtor - if (current.volume > 0 && current.accumulator_id == std::numeric_limits::max()) { - int min_accumulator_id_found = std::numeric_limits::max(); - std::unordered_set ids_to_merge { }; - for (int y_offset = -2; y_offset <= 2; ++y_offset) { - for (int x_offset = -2; x_offset <= 2; ++x_offset) { - if (validate_xy_coords(Vec2i(x + x_offset, y + y_offset))) { - Cell &under = this->access_cell(Vec3i(x + x_offset, y + y_offset, z - 1)); - if (under.accumulator_id < min_accumulator_id_found) { - min_accumulator_id_found = under.accumulator_id; - } - ids_to_merge.insert(under.accumulator_id); - } - } - } - // assign accumulator and update its info - if (min_accumulator_id_found < std::numeric_limits::max()) { // accumulator id found - ids_to_merge.erase(std::numeric_limits::max()); - ids_to_merge.erase(min_accumulator_id_found); - current.accumulator_id = min_accumulator_id_found; //assign accumualtor id to the cell - for (auto id : ids_to_merge) { - accumulators.merge_to(id, min_accumulator_id_found); //merge other ids to the smallest id - } - //update the acc with new point, and store in the modified accumulators map - CentroidAccumulator &acc = accumulators.access(min_accumulator_id_found); - acc.accumulated_value += current.volume - * unscale(this->get_cell_center(this->to_global_cell_coords(Vec3i(x, y, z)))).cast< - float>(); - acc.accumulated_volume += current.volume; - modified_acc_ids.emplace(min_accumulator_id_found, std::vector { }); - } - } - - // distribute curling (add curling from neighbours under, but also decrease but some factor) - if (current.volume > 0) { - float curled_height = 0; - for (int y_offset = -2; y_offset <= 2; ++y_offset) { - for (int x_offset = -2; x_offset <= 2; ++x_offset) { - if (validate_xy_coords(Vec2i(x + x_offset, y + y_offset))) { - Cell &under = this->access_cell(Vec3i(x + x_offset, y + y_offset, z - 1)); - curled_height = std::max(curled_height, under.curled_height); - } - } - } - current.curled_height += std::max(0.0f, - float(curled_height - unscaled(this->cell_size.z()) / 1.5f)); - current.curled_height = std::min(current.curled_height, - float(unscaled(this->cell_size.z()) * 2.0f)); - - if (current.curled_height / unscaled(this->cell_size.z()) > 1.5f) { // just a magic threshold number. - modified_acc_ids[current.accumulator_id].push_back( { x, y }); - } - } - } - } - - //all cells of the grid layer checked, now further filter the modified accumulators, because multiple ids can point to the same acc - filtered_active_accumulators.clear(); - for (const auto &pair : modified_acc_ids) { - CentroidAccumulator *acc = &accumulators.access(pair.first); - filtered_active_accumulators[acc].insert(filtered_active_accumulators[acc].end(), - pair.second.begin(), - pair.second.end()); - } - - check_accumulators_stability(z, accumulators, filtered_active_accumulators, issues, params); - } - } -private: - void check_accumulators_stability(int z, CentroidAccumulators &accumulators, - std::unordered_map> filtered_active_accumulators, - Issues &issues, const Params ¶ms) const { - - for (const auto &pair : filtered_active_accumulators) { - std::cout << "Z: " << z << std::endl; - CentroidAccumulator &acc = *pair.first; - Vec3f centroid = acc.accumulated_value / acc.accumulated_volume; - Point base_centroid = acc.base_hull().centroid(); - Vec3f base_centroid_3d { }; - base_centroid_3d << unscale(base_centroid).cast(), acc.base_height; - - std::cout << "acc.accumulated_value : " << acc.accumulated_value.x() << " " - << acc.accumulated_value.y() << " " << acc.accumulated_value.z() << std::endl; - std::cout << "acc.accumulated_volume : " << acc.accumulated_volume << std::endl; - std::cout << "centroid: " << centroid.x() << " " << centroid.y() << " " << centroid.z() - << std::endl; - std::cout << "base_centroid_3d: " << base_centroid_3d.x() << " " << base_centroid_3d.y() << " " - << base_centroid_3d.z() - << std::endl; - - // find the cell that is furthest from the base centroid ( its a heurstic to find a possible problems with balance without checking all layer cells) - //TODO better result are if first pivot is chosen as the closest point of the convex hull to the base centroid, and then cell furthest in the direction defined by - // the vector from base centroid to this pivot is taken. - double max_dist_sqr = 0; - Vec3f suspicious_point = centroid; - Vec2i coords = Vec2i(0, 0); - for (int y = 0; y < global_cell_count.y(); ++y) { - for (int x = 0; x < global_cell_count.x(); ++x) { - const Cell &cell = this->access_cell(Vec3i(x, y, z)); - if (cell.accumulator_id != std::numeric_limits::max() && - &accumulators.access(cell.accumulator_id) == &acc) { - Vec3f cell_center = - unscale(this->get_cell_center(this->to_global_cell_coords(Vec3i(x, y, z)))).cast< - float>(); - float dist_sq = (cell_center - base_centroid_3d).squaredNorm(); - if (dist_sq > max_dist_sqr) { - max_dist_sqr = dist_sq; - suspicious_point = cell_center; - coords = Vec2i(x, y); - } - } - } - } - - // for the suspicious point, add movement force in xy (bed sliding, it is assumed that the worst direction is taken, for simplicity) - float xy_movement_force = acc.accumulated_volume * params.filament_density - * params.max_acceleration; - - std::cout << "xy_movement_force: " << xy_movement_force << std::endl; - - // also add weight (weight is the small factor, because the materials are very light. The weight torque will be computed much higher then what is real, - //since it does not push in the suspicoius point, but in centroid. Its approximation) - float weight = acc.accumulated_volume * params.filament_density * params.gravity_constant; - - std::cout << "weight: " << weight << std::endl; - - float force = this->check_point_stability_under_pressure(acc, base_centroid, suspicious_point, - xy_movement_force + weight + params.tolerable_extruder_conflict_force, - params); - if (force > 0) { - this->add_suppport_point(Vec3i(coords.x(), coords.y(), z), force, acc, issues, params); - } - - for (const Vec2i &cell : pair.second) { - Vec3f pressure_point = unscale( - this->get_cell_center( - this->to_global_cell_coords(Vec3i(cell.x(), cell.y(), z)))).cast(); - float force = this->check_point_stability_under_pressure(acc, base_centroid, pressure_point, - params.max_curled_conflict_extruder_force, //TODO add linear scaling of the extruder force based on the curled height (but current data about curled height are kind of unreliable in scale) - params); - if (force > 0) { - this->add_suppport_point(Vec3i(cell.x(), cell.y(), z), force, acc, issues, params); - } - } - } - } - - float check_point_stability_under_pressure(CentroidAccumulator &acc, const Point &base_centroid, - const Vec3f &pressure_point, float pressure_force, const Params ¶ms) const { - Point pressure_base_projection = Point(scaled(Vec2f(pressure_point.head<2>()))); - Point pivot; - double distance_scaled_sq = std::numeric_limits::max(); - bool inside = true; - // find pivot, which is the closest point of the accumulator base hull to pressure point (if the object should fall, it would be over this point) - if (acc.base_hull().points.size() == 1) { - pivot = acc.base_hull().points[0]; - distance_scaled_sq = (pivot - pressure_base_projection).squaredNorm(); - inside = distance_scaled_sq < params.support_points_interface_area; - } else { - for (Line line : acc.base_hull().lines()) { - Point closest_point; - double dist_sq = line.distance_to_squared(pressure_base_projection, &closest_point); - if (dist_sq < distance_scaled_sq) { - pivot = closest_point; - distance_scaled_sq = dist_sq; - } - if ((pressure_base_projection - closest_point).cast().dot(line.normal().cast()) - > 0) { - inside = false; - } - } - } -// float embedded_distance = unscaled(sqrt(distance_scaled_sq)); - float base_center_pivot_distance = float(unscale(Vec2crd(base_centroid - pivot)).norm()); - Vec3f pivot_3d; - pivot_3d << unscale(pivot).cast(), acc.base_height; - - //sticking force estimated from the base area and support points - float sticking_force = acc.base_area - * (acc.base_height == 0 ? params.base_adhesion : params.support_adhesion); - - std::cout << "sticking force: " << sticking_force << std::endl; - std::cout << "pressure force: " << pressure_force << std::endl; - float sticking_torque = sticking_force * base_center_pivot_distance; // sticking torque is computed from the distance to the centroid - - float pressure_arm = inside ? pressure_point.z() - pivot_3d.z() : (pressure_point - pivot_3d).norm(); // pressure arm is again higher then in reality, - // since it assumes the worst direction of the pressure force (perpendicular to the vector between pivot and pressure point) - float pressure_torque = pressure_arm * pressure_force; - - std::cout << "sticking_torque: " << sticking_torque << std::endl; - std::cout << "pressure_torque: " << pressure_torque << std::endl; - if (sticking_torque < pressure_torque) { - return pressure_force; - } else { - return 0.0f; - } - } - - void add_suppport_point(const Vec3i &local_coords, float expected_force, CentroidAccumulator &acc, Issues &issues, - const Params ¶ms) const { - //add support point - but first finding the lowest full cell is needed, since putting support point to the current cell may end up inside the model, - // essentially doing nothing. - int final_height_coords = local_coords.z(); - while (final_height_coords > 0 - && this->access_cell( - this->to_global_cell_coords(Vec3i(local_coords.x(), local_coords.y(), final_height_coords))).volume - > 0) { - final_height_coords--; - } - Vec3f support_point = - unscale( - this->get_cell_center( - this->to_global_cell_coords( - Vec3i(local_coords.x(), local_coords.y(), final_height_coords)))).cast< - float>(); - - std::cout << " new support point: " << support_point.x() << " | " << support_point.y() << " | " - << support_point.z() << std::endl; - std::cout << " expected_force: " << expected_force << std::endl; - - issues.supports_nedded.emplace_back(support_point, expected_force); - acc.add_base_points( { Point::new_scale(Vec2f(support_point.head<2>())) }); - acc.base_area += params.support_points_interface_area; + to_acc.add_from(from_acc); + mapping[from_id] = mapping[to_id]; + from_acc = StabilityAccumulator { 0.0f }; } #ifdef DEBUG_FILES -public: - void debug_export() const { - Slic3r::CNumericLocalesSetter locales_setter; - { - FILE *volume_grid_file = boost::nowide::fopen(debug_out_path("volume_grid.obj").c_str(), "w"); - FILE *islands_grid_file = boost::nowide::fopen(debug_out_path("islands_grid.obj").c_str(), "w"); - FILE *curling_grid_file = boost::nowide::fopen(debug_out_path("curling_grid.obj").c_str(), "w"); - - if (volume_grid_file == nullptr || islands_grid_file == nullptr || curling_grid_file == nullptr) { - BOOST_LOG_TRIVIAL(error) - << "Debug files: Couldn't open debug file for writing, destination: " << debug_out_path(""); - return; - } - - float max_volume = 0; - int min_island_id = 0; - int max_island_id = 0; - float max_curling_height = 0.5f; - - 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) { - const Cell &cell = access_cell(Vec3i(x, y, z)); - max_volume = std::max(max_volume, cell.volume); - if (cell.accumulator_id != std::numeric_limits::max()) { - min_island_id = std::min(min_island_id, cell.accumulator_id); - max_island_id = std::max(max_island_id, cell.accumulator_id); - } -// max_curling_height = std::max(max_curling_height, cell.curled_height); - } - } - } - - 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(get_cell_center(to_global_cell_coords(Vec3i { x, y, z }))).cast< - float>(); - const Cell &cell = access_cell(Vec3i(x, y, z)); - if (cell.volume != 0) { - auto volume_color = value_to_rgbf(0, cell.volume, cell.volume); - fprintf(volume_grid_file, "v %f %f %f %f %f %f\n", center(0), center(1), center(2), - volume_color.x(), volume_color.y(), volume_color.z()); - } - if (cell.accumulator_id != std::numeric_limits::max()) { - auto island_color = value_to_rgbf(min_island_id, max_island_id + 1, cell.accumulator_id); - fprintf(islands_grid_file, "v %f %f %f %f %f %f\n", center(0), center(1), - center(2), - island_color.x(), island_color.y(), island_color.z()); - } - if (cell.curled_height > 0) { - auto curling_color = value_to_rgbf(0, max_curling_height, cell.curled_height); - fprintf(curling_grid_file, "v %f %f %f %f %f %f\n", center(0), center(1), - center(2), - curling_color.x(), curling_color.y(), curling_color.z()); - } - } - } - } - - fclose(volume_grid_file); - fclose(islands_grid_file); - fclose(curling_grid_file); + Vec3f get_emerging_color(size_t id) { + if (mapping.find(id) == mapping.end()) { + std::cerr << " ERROR: uknown accumulator ID: " << id << std::endl; + return Vec3f(1.0f, 1.0f, 1.0f); } + + size_t pseudornd = ((id + 127) * 33331 + 6907) % 13; + return value_to_rgbf(0.0f, 13.0f, float(pseudornd)); + } + + Vec3f get_final_color(size_t id) { + if (mapping.find(id) == mapping.end()) { + std::cerr << " ERROR: uknown accumulator ID: " << id << std::endl; + return Vec3f(1.0f, 1.0f, 1.0f); + } + + size_t pseudornd = ((mapping[id] + 127) * 33331 + 6907) % 13; + return value_to_rgbf(0.0f, 13.0f, float(pseudornd)); } #endif -} -; +}; -namespace Impl { +float get_flow_width(const LayerRegion *region, ExtrusionRole role) { + switch (role) { + case ExtrusionRole::erBridgeInfill: + return region->flow(FlowRole::frExternalPerimeter).width(); + case ExtrusionRole::erExternalPerimeter: + return region->flow(FlowRole::frExternalPerimeter).width(); + case ExtrusionRole::erGapFill: + return region->flow(FlowRole::frInfill).width(); + case ExtrusionRole::erPerimeter: + return region->flow(FlowRole::frPerimeter).width(); + case ExtrusionRole::erSolidInfill: + return region->flow(FlowRole::frSolidInfill).width(); + case ExtrusionRole::erInternalInfill: + return region->flow(FlowRole::frInfill).width(); + case ExtrusionRole::erTopSolidInfill: + return region->flow(FlowRole::frTopSolidInfill).width(); + default: + return region->flow(FlowRole::frPerimeter).width(); + } +} + +struct ExtrusionPropertiesAccumulator { + 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; + } +}; + +void check_extrusion_entity_stability(const ExtrusionEntity *entity, + StabilityAccumulators &stability_accs, + Issues &issues, + std::vector &checked_lines, + float print_z, + const LayerRegion *layer_region, + const LayerLinesDistancer &prev_layer_lines, + const Params ¶ms) { + + if (entity->is_collection()) { + for (const auto *e : static_cast(entity)->entities) { + check_extrusion_entity_stability(e, stability_accs, issues, checked_lines, print_z, layer_region, + prev_layer_lines, + params); + } + } else { //single extrusion path, with possible varying parameters + const auto to_vec3f = [print_z](const Point &point) { + Vec2f tmp = unscale(point).cast(); + return Vec3f(tmp.x(), tmp.y(), print_z); + }; + Points points { }; + entity->collect_points(points); + std::vector lines; + lines.reserve(points.size() * 1.5); + lines.emplace_back(unscaled(points[0]).cast(), unscaled(points[0]).cast()); + for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { + Vec2f start = unscaled(points[point_idx]).cast(); + Vec2f next = unscaled(points[point_idx + 1]).cast(); + Vec2f v = next - start; // vector from next to current + float dist_to_next = v.norm(); + v.normalize(); + int lines_count = int(std::ceil(dist_to_next / params.bridge_distance)); + float step_size = dist_to_next / lines_count; + for (int i = 0; i < lines_count; ++i) { + Vec2f a(start + v * (i * step_size)); + Vec2f b(start + v * ((i + 1) * step_size)); + lines.emplace_back(a, b); + } + } + + size_t current_stability_acc = NULL_ACC_ID; + ExtrusionPropertiesAccumulator bridging_acc { }; + bridging_acc.add_distance(params.bridge_distance + 1.0f); // Initialise unsupported distance with larger than tolerable distance -> + // -> it prevents extruding perimeter start and short loops into air. + const float flow_width = get_flow_width(layer_region, entity->role()); + const float max_allowed_dist_from_prev_layer = flow_width; + + for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) { + ExtrusionLine ¤t_line = lines[line_idx]; + Point current = Point::new_scale(current_line.b); + float mm3_per_mm = float(entity->min_mm3_per_mm()); + + float curr_angle = 0; + if (line_idx + 1 < lines.size()) { + const Vec2f v1 = current_line.b - current_line.a; + const Vec2f v2 = lines[line_idx + 1].b - lines[line_idx + 1].a; + curr_angle = angle(v1, v2); + } + bridging_acc.add_angle(curr_angle); + + size_t nearest_line_idx; + Vec2f nearest_point; + float dist_from_prev_layer = prev_layer_lines.signed_distance_from_lines(current_line.b, nearest_line_idx, + nearest_point); + if (dist_from_prev_layer < max_allowed_dist_from_prev_layer) { + const ExtrusionLine &nearest_line = prev_layer_lines.get_line(nearest_line_idx); + size_t acc_id = nearest_line.stability_accumulator_id; + stability_accs.merge_accumulators(std::max(acc_id, current_stability_acc), + std::min(acc_id, current_stability_acc)); + current_stability_acc = std::min(acc_id, current_stability_acc); + current_line.stability_accumulator_id = current_stability_acc; + stability_accs.access(current_stability_acc).add_extrusion(current_line, print_z, mm3_per_mm); + bridging_acc.reset(); + // TODO curving here + } else { + bridging_acc.add_distance(current_line.len); + if (current_stability_acc == NULL_ACC_ID) { + current_stability_acc = stability_accs.create_accumulator(print_z); + } + StabilityAccumulator ¤t_segment = stability_accs.access(current_stability_acc); + current_line.stability_accumulator_id = current_stability_acc; + current_segment.add_extrusion(current_line, print_z, mm3_per_mm); + if (bridging_acc.distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. + > params.bridge_distance + / (1.0f + (bridging_acc.max_curvature + * params.bridge_distance_decrease_by_curvature_factor / PI))) { + current_segment.add_support_point(current, params.support_points_interface_area); + issues.supports_nedded.emplace_back(to_vec3f(current), 1.0); + bridging_acc.reset(); + } + } + } + checked_lines.insert(checked_lines.end(), lines.begin(), lines.end()); + } +} + +void check_layer_global_stability(StabilityAccumulators &stability_accs, + Issues &issues, + const std::vector &checked_lines, + float print_z, + const Params ¶ms) { + std::unordered_map> layer_accs_lines; + for (size_t i = 0; i < checked_lines.size(); ++i) { + layer_accs_lines[&stability_accs.access(checked_lines[i].stability_accumulator_id)].push_back(i); + } + + for (auto &acc_lines : layer_accs_lines) { + StabilityAccumulator *acc = acc_lines.first; + Vec3f centroid = acc->get_centroid(); + Vec2f hull_centroid = unscaled(acc->segment_base_hull().centroid()).cast(); + std::vector hull_lines; + for (const Line &line : acc->segment_base_hull().lines()) { + Vec2f start = unscaled(line.a).cast(); + Vec2f next = unscaled(line.b).cast(); + hull_lines.push_back( { start, next }); + } + if (hull_lines.empty()) { + if (acc->get_support_points().empty()) { + acc->add_support_point(Point::new_scale(checked_lines[acc_lines.second[0]].a), + params.support_points_interface_area); + issues.supports_nedded.emplace_back(to_3d(checked_lines[acc_lines.second[0]].a, print_z), 1.0); + } + hull_lines.push_back( { unscaled(acc->get_support_points()[0]).cast(), + unscaled(acc->get_support_points()[0]).cast() }); + hull_centroid = unscaled(acc->get_support_points()[0]).cast(); + } + + LayerLinesDistancer hull_distancer(std::move(hull_lines)); + + float sticking_force = acc->get_base_area() + * (acc->get_base_height() == 0 ? params.base_adhesion : params.support_adhesion); + float weight = acc->get_accumulated_volume() * params.filament_density * params.gravity_constant; + + for (size_t line_idx : acc_lines.second) { + const ExtrusionLine &line = checked_lines[line_idx]; + + Vec3f extruder_pressure_direction = to_3d(Vec2f(line.b - line.a), 0.0f).normalized(); + Vec2f pivot_site_search = line.b + extruder_pressure_direction.head<2>() * 1000.0f; + extruder_pressure_direction.z() = -0.1f; + extruder_pressure_direction.normalize(); + + size_t nearest_line_idx; + Vec2f pivot; + hull_distancer.signed_distance_from_lines(pivot_site_search, nearest_line_idx, pivot); + + float sticking_arm = (pivot - hull_centroid).norm(); + float sticking_torque = sticking_arm * sticking_force; + + std::cout << "sticking_arm: " << sticking_arm << std::endl; + std::cout << "sticking_torque: " << sticking_torque << std::endl; + + float weight_arm = (pivot - centroid.head<2>()).norm(); + float weight_torque = weight_arm * weight; + + std::cout << "weight_arm: " << sticking_arm << std::endl; + std::cout << "weight_torque: " << weight_torque << std::endl; + + float bed_movement_arm = centroid.z() - acc->get_base_height(); + float bed_movement_force = params.max_acceleration * weight; + float bed_movement_torque = bed_movement_force * bed_movement_arm; + + std::cout << "bed_movement_arm: " << bed_movement_arm << std::endl; + std::cout << "bed_movement_torque: " << bed_movement_torque << std::endl; + + float conflict_torque_arm = (to_3d(Vec2f(pivot - line.b), print_z).cross( + extruder_pressure_direction)).norm(); + float extruder_conflict_torque = params.tolerable_extruder_conflict_force * conflict_torque_arm; + + std::cout << "conflict_torque_arm: " << conflict_torque_arm << std::endl; + std::cout << "extruder_conflict_torque: " << extruder_conflict_torque << std::endl; + + float total_torque = bed_movement_torque + extruder_conflict_torque - weight_torque - sticking_torque; + + std::cout << "total_torque: " << total_torque << " printz: " << print_z << std::endl; + + if (total_torque > 0) { + acc->add_support_point(Point::new_scale(line.b), params.support_points_interface_area); + issues.supports_nedded.emplace_back(to_3d(line.b, print_z), extruder_conflict_torque - sticking_torque); + } + + } + } +} + +Issues check_object_stability(const PrintObject *po, const Params ¶ms) { +#ifdef DEBUG_FILES + FILE *eacc = boost::nowide::fopen(debug_out_path("emerging_accumulators.obj").c_str(), "w"); + FILE *facc = boost::nowide::fopen(debug_out_path("final_accumulators.obj").c_str(), "w"); +#endif + StabilityAccumulators stability_accs; + LayerLinesDistancer prev_layer_lines { { } }; + Issues issues { }; + std::vector checked_lines; + + // PREPARE BASE LAYER + float max_flow_width = 0.0f; + const Layer *layer = po->layers()[0]; + float base_print_z = layer->print_z; + for (const LayerRegion *layer_region : layer->regions()) { + for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { + for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { + const float flow_width = get_flow_width(layer_region, perimeter->role()); + max_flow_width = std::max(flow_width, max_flow_width); + const float mm3_per_mm = float(perimeter->min_mm3_per_mm()); + int id = stability_accs.create_accumulator(base_print_z); + StabilityAccumulator &acc = stability_accs.access(id); + Points points { }; + perimeter->collect_points(points); + for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { + Vec2f start = unscaled(points[point_idx]).cast(); + Vec2f next = unscaled(points[point_idx + 1]).cast(); + ExtrusionLine line { start, next }; + line.stability_accumulator_id = id; + acc.add_base_extrusion(line, flow_width, base_print_z, mm3_per_mm); + checked_lines.push_back(line); + } + if (perimeter->is_loop()) { + Vec2f start = unscaled(points[points.size() - 1]).cast(); + Vec2f next = unscaled(points[0]).cast(); + ExtrusionLine line { start, next }; + line.stability_accumulator_id = id; + acc.add_base_extrusion(line, flow_width, base_print_z, mm3_per_mm); + checked_lines.push_back(line); + } + } // perimeter + } // ex_entity + for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { + for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { + const float flow_width = get_flow_width(layer_region, fill->role()); + max_flow_width = std::max(flow_width, max_flow_width); + const float mm3_per_mm = float(fill->min_mm3_per_mm()); + int id = stability_accs.create_accumulator(base_print_z); + StabilityAccumulator &acc = stability_accs.access(id); + Points points { }; + fill->collect_points(points); + for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { + Vec2f start = unscaled(points[point_idx]).cast(); + Vec2f next = unscaled(points[point_idx + 1]).cast(); + ExtrusionLine line { start, next }; + line.stability_accumulator_id = id; + acc.add_base_extrusion(line, flow_width, base_print_z, mm3_per_mm); + checked_lines.push_back(line); + } + } // fill + } // ex_entity + } // region + +#ifdef DEBUG_FILES + for (const auto &line : checked_lines) { + Vec3f ecolor = stability_accs.get_emerging_color(line.stability_accumulator_id); + fprintf(eacc, "v %f %f %f %f %f %f\n", line.b[0], + line.b[1], base_print_z, ecolor[0], ecolor[1], ecolor[2]); + + Vec3f fcolor = stability_accs.get_final_color(line.stability_accumulator_id); + fprintf(facc, "v %f %f %f %f %f %f\n", line.b[0], + line.b[1], base_print_z, fcolor[0], fcolor[1], fcolor[2]); + } +#endif DEBUG_FILES + + //MERGE BASE LAYER STABILITY ACCS + prev_layer_lines = LayerLinesDistancer { std::move(checked_lines) }; + for (const ExtrusionLine &l : prev_layer_lines.get_lines()) { + size_t nearest_line_idx; + Vec2f nearest_pt; + float dist = prev_layer_lines.signed_distance_from_lines(l.a, nearest_line_idx, nearest_pt); + if (std::abs(dist) < max_flow_width) { + size_t other_line_acc_id = prev_layer_lines.get_line(nearest_line_idx).stability_accumulator_id; + size_t from_id = std::max(other_line_acc_id, l.stability_accumulator_id); + size_t to_id = std::min(other_line_acc_id, l.stability_accumulator_id); + stability_accs.merge_accumulators(from_id, to_id); + } + } + + //CHECK STABILITY OF ALL LAYERS + for (size_t layer_idx = 1; layer_idx < po->layer_count(); ++layer_idx) { + const Layer *layer = po->layers()[layer_idx]; + checked_lines = std::vector { }; + std::vector> fill_points; + float max_fill_flow_width = 0.0f; + + float print_z = layer->print_z; + for (const LayerRegion *layer_region : layer->regions()) { + for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { + for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { + check_extrusion_entity_stability(perimeter, stability_accs, issues, checked_lines, print_z, + layer_region, + prev_layer_lines, params); + } // perimeter + } // ex_entity + for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { + for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { + if (fill->role() == ExtrusionRole::erGapFill + || fill->role() == ExtrusionRole::erBridgeInfill) { + check_extrusion_entity_stability(fill, stability_accs, issues, checked_lines, print_z, + layer_region, + prev_layer_lines, params); + } else { + const float flow_width = get_flow_width(layer_region, fill->role()); + max_fill_flow_width = std::max(max_fill_flow_width, flow_width); + Vec2f start = unscaled(fill->first_point()).cast(); + size_t nearest_line_idx; + Vec2f nearest_pt; + float dist = prev_layer_lines.signed_distance_from_lines(start, nearest_line_idx, nearest_pt); + if (dist < flow_width) { + size_t acc_id = prev_layer_lines.get_line(nearest_line_idx).stability_accumulator_id; + StabilityAccumulator &acc = stability_accs.access(acc_id); + Points points { }; + const float mm3_per_mm = float(fill->min_mm3_per_mm()); + fill->collect_points(points); + for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { + Vec2f start = unscaled(points[point_idx]).cast(); + Vec2f next = unscaled(points[point_idx + 1]).cast(); + ExtrusionLine line { start, next }; + line.stability_accumulator_id = acc_id; + acc.add_extrusion(line, print_z, mm3_per_mm); + } + fill_points.emplace_back(start, acc_id); + } else { + std::cout << " SUPPORTS POINT GEN, start infill in the air? on printz: " << print_z + << std::endl; + } + } + } // fill + } // ex_entity + } // region + + prev_layer_lines = LayerLinesDistancer { std::move(checked_lines) }; + + for (const std::pair &fill_point : fill_points) { + size_t nearest_line_idx; + Vec2f nearest_pt; + float dist = prev_layer_lines.signed_distance_from_lines(fill_point.first, nearest_line_idx, nearest_pt); + if (dist < max_fill_flow_width) { + size_t other_line_acc_id = prev_layer_lines.get_line(nearest_line_idx).stability_accumulator_id; + size_t from_id = std::max(other_line_acc_id, fill_point.second); + size_t to_id = std::min(other_line_acc_id, fill_point.second); + stability_accs.merge_accumulators(from_id, to_id); + } else { + std::cout << " SUPPORTS POINT GEN, no connection on current layer for infill? on printz: " << print_z + << std::endl; + } + } + + check_layer_global_stability(stability_accs, issues, prev_layer_lines.get_lines(), print_z, params); + +#ifdef DEBUG_FILES + for (const auto &line : prev_layer_lines.get_lines()) { + Vec3f ecolor = stability_accs.get_emerging_color(line.stability_accumulator_id); + fprintf(eacc, "v %f %f %f %f %f %f\n", line.b[0], + line.b[1], print_z, ecolor[0], ecolor[1], ecolor[2]); + + Vec3f fcolor = stability_accs.get_final_color(line.stability_accumulator_id); + fprintf(facc, "v %f %f %f %f %f %f\n", line.b[0], + line.b[1], print_z, fcolor[0], fcolor[1], fcolor[2]); + } +#endif + } + +#ifdef DEBUG_FILES + fclose(eacc); + fclose(facc); +#endif + + std::cout << " SUPP: " << issues.supports_nedded.size() << std::endl; + return issues; +} #ifdef DEBUG_FILES void debug_export(Issues issues, std::string file_name) { Slic3r::CNumericLocalesSetter locales_setter; - { FILE *fp = boost::nowide::fopen(debug_out_path((file_name + "_supports.obj").c_str()).c_str(), "w"); if (fp == nullptr) { @@ -685,365 +700,20 @@ void debug_export(Issues issues, std::string file_name) { } fclose(fp); } - } #endif -struct LayerLinesDistancer { - std::vector lines; - AABBTreeIndirect::Tree<2, double> tree; - float line_width; - - LayerLinesDistancer(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()); - } - for (const LayerRegion *layer_region : layer->regions()) { - for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { - Points points { }; - ex_entity->collect_points(points); - Vec2d prev = points.size() > 0 ? unscaled(points.front()) : Vec2d::Zero(); - for (size_t idx = 1; idx < points.size(); ++idx) { - lines.emplace_back(prev, unscaled(points[idx])); - } - } // ex_entity - - for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { - Points points { }; - ex_entity->collect_points(points); - Vec2d prev = points.size() > 0 ? unscaled(points.front()) : Vec2d::Zero(); - for (size_t idx = 1; idx < points.size(); ++idx) { - lines.emplace_back(prev, unscaled(points[idx])); - } - } // ex_entity - } - tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(lines); - line_width = min_region_flow_width; - } - - float distance_from_lines(const Point &point, float point_width) const { - Vec2d p = unscaled(point); - size_t hit_idx_out; - Vec2d hit_point_out; - auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, p, hit_idx_out, hit_point_out); - if (distance < 0) - return distance; - - distance = sqrt(distance); - return std::max(float(distance - point_width / 2 - line_width / 20), 0.0f); - } -}; - -//TODO needs revision -float get_flow_width(const LayerRegion *region, ExtrusionRole role) { - switch (role) { - case ExtrusionRole::erBridgeInfill: - return region->flow(FlowRole::frExternalPerimeter).width(); - case ExtrusionRole::erExternalPerimeter: - return region->flow(FlowRole::frExternalPerimeter).width(); - case ExtrusionRole::erGapFill: - return region->flow(FlowRole::frInfill).width(); - case ExtrusionRole::erPerimeter: - return region->flow(FlowRole::frPerimeter).width(); - case ExtrusionRole::erSolidInfill: - return region->flow(FlowRole::frSolidInfill).width(); - case ExtrusionRole::erInternalInfill: - return region->flow(FlowRole::frInfill).width(); - case ExtrusionRole::erTopSolidInfill: - return region->flow(FlowRole::frTopSolidInfill).width(); - default: - return region->flow(FlowRole::frPerimeter).width(); - } -} - -float get_max_allowed_distance(ExtrusionRole role, float 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)) { - 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 print_z, - const LayerRegion *layer_region, - const LayerLinesDistancer &support_layer, const Params ¶ms) { - - Issues issues { }; - if (entity->is_collection()) { - for (const auto *e : static_cast(entity)->entities) { - issues.add( - check_extrusion_entity_stability(e, print_z, layer_region, support_layer, params)); - } - } 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. - std::stack points { }; - for (const auto &p : entity->as_polyline().points) { - points.push(p); - } - - 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. - - const auto to_vec3f = [print_z](const Point &point) { - Vec2f tmp = unscale(point).cast(); - return Vec3f(tmp.x(), tmp.y(), print_z); - }; - float region_height = layer_region->layer()->height; - - Point prev_point = points.top(); // prev point of the path. Initialize with first point. - Vec3f prev_fpoint = to_vec3f(prev_point); - float flow_width = get_flow_width(layer_region, entity->role()); - bool external_perimters_first = layer_region->region().config().external_perimeters_first; - const float max_allowed_dist_from_prev_layer = get_max_allowed_distance(entity->role(), flow_width, - external_perimters_first, params); - - while (!points.empty()) { - Point point = points.top(); - points.pop(); - Vec3f fpoint = to_vec3f(point); - float edge_len = (fpoint - prev_fpoint).norm(); - - float dist_from_prev_layer = support_layer.distance_from_lines(point, flow_width); - if (dist_from_prev_layer < 0) { // dist from prev layer not found, assume empty layer - issues.supports_nedded.push_back(SupportPoint(fpoint, 1.0f)); - supports_acc.reset(); - } - - float angle = 0; - if (!points.empty()) { - const Vec2f v1 = (fpoint - prev_fpoint).head<2>(); - const Vec2f v2 = unscale(points.top()).cast() - 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); - - if (dist_from_prev_layer > max_allowed_dist_from_prev_layer) { //extrusion 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 (supports_acc.distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. - > params.bridge_distance - / (1.0f + (supports_acc.max_curvature - * params.bridge_distance_decrease_by_curvature_factor / PI))) { - issues.supports_nedded.push_back(SupportPoint(fpoint, 1.0f)); - supports_acc.reset(); - } - } else { - supports_acc.reset(); - } - - // Estimation of short curvy segments which are not supported -> problems with curling - if (dist_from_prev_layer > -max_allowed_dist_from_prev_layer * 0.7071) { //extrusion point is unsupported or poorly supported - float dist_factor = 0.5f + 0.5f * (dist_from_prev_layer + max_allowed_dist_from_prev_layer * 0.7071) - / max_allowed_dist_from_prev_layer; - issues.curling_up.push_back( - CurledFilament(fpoint, - dist_factor - * (0.25f * region_height + region_height * 0.75f * std::abs(angle) / PI))); - } - - prev_point = point; - prev_fpoint = fpoint; - - if (!points.empty()) { //oversampling if necessary - Vec2f next = unscale(points.top()).cast(); - Vec2f reverse_v = fpoint.head<2>() - next; // vector from next to current - 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)))); - } - } - } - } - return issues; -} - -void distribute_layer_volume(const PrintObject *po, size_t layer_idx, - BalanceDistributionGrid &balance_grid) { - const Layer *layer = po->get_layer(layer_idx); - for (const LayerRegion *region : layer->regions()) { - for (const ExtrusionEntity *collections : region->fills.entities) { - for (const ExtrusionEntity *entity : static_cast(collections)->entities) { - for (const Line &line : entity->as_polyline().lines()) { - balance_grid.distribute_edge(line.a, line.b, layer->print_z, - get_flow_width(region, entity->role()), layer->height); - } - } - } - for (const ExtrusionEntity *collections : region->perimeters.entities) { - for (const ExtrusionEntity *entity : static_cast(collections)->entities) { - for (const Line &line : entity->as_polyline().lines()) { - balance_grid.distribute_edge(line.a, line.b, layer->print_z, - get_flow_width(region, entity->role()), layer->height); - } - } - } - } -} - -Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_check, - const Params ¶ms) { - const Layer *layer = po->get_layer(layer_idx); - //Prepare edge grid of previous layer, will be used to check if the extrusion path is supported - LayerLinesDistancer support_layer(layer->lower_layer); - - Issues issues { }; - if (full_check) { // If full check; check stability of perimeters, gap fills, and bridges. - for (const LayerRegion *layer_region : layer->regions()) { - for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { - for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { - issues.add( - check_extrusion_entity_stability(perimeter, layer->print_z, layer_region, - support_layer, params)); - } // perimeter - } // ex_entity - for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { - for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { - if (fill->role() == ExtrusionRole::erGapFill - || fill->role() == ExtrusionRole::erBridgeInfill) { - issues.add( - check_extrusion_entity_stability(fill, layer->print_z, layer_region, - support_layer, - params)); - } - } // fill - } // ex_entity - } // region - - } else { // If NOT full check, 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(ex_entity)->entities) { - if (perimeter->role() == ExtrusionRole::erExternalPerimeter - || perimeter->role() == ExtrusionRole::erOverhangPerimeter) { - issues.add( - check_extrusion_entity_stability(perimeter, layer->print_z, layer_region, - support_layer, params)); - }; // ex_perimeter - } // perimeter - } // ex_entity - } //region - } - - return issues; -} - -} //Impl End - std::vector quick_search(const PrintObject *po, const Params ¶ms) { - using namespace Impl; - - BalanceDistributionGrid grid { }; - grid.init(po, 0, po->layers().size()); - distribute_layer_volume(po, 0, grid); - std::mutex grid_mutex; - - size_t layer_count = po->layer_count(); - std::vector layer_needs_supports(layer_count, false); - tbb::parallel_for(tbb::blocked_range(1, layer_count), [&](tbb::blocked_range r) { - BalanceDistributionGrid balance_grid { }; - balance_grid.init(po, r.begin(), r.end()); - - for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { - distribute_layer_volume(po, layer_idx, balance_grid); - auto layer_issues = check_layer_stability(po, layer_idx, false, params); - if (!layer_issues.supports_nedded.empty()) { - layer_needs_supports[layer_idx] = true; - } - } - - grid_mutex.lock(); - grid.merge(balance_grid); - grid_mutex.unlock(); - }); - - std::vector problematic_layers; - for (size_t index = 0; index < layer_needs_supports.size(); ++index) { - if (layer_needs_supports[index]) { - problematic_layers.push_back(index); - } - } - return problematic_layers; + check_object_stability(po, params); + return {}; } Issues full_search(const PrintObject *po, const Params ¶ms) { - using namespace Impl; - - BalanceDistributionGrid grid { }; - grid.init(po, 0, po->layers().size()); - distribute_layer_volume(po, 0, grid); - std::mutex grid_mutex; - - size_t layer_count = po->layer_count(); - Issues found_issues = tbb::parallel_reduce(tbb::blocked_range(1, layer_count), Issues { }, - [&](tbb::blocked_range r, const Issues &init) { - BalanceDistributionGrid balance_grid { }; - balance_grid.init(po, r.begin(), r.end()); - Issues issues = init; - for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { - distribute_layer_volume(po, layer_idx, balance_grid); - auto layer_issues = check_layer_stability(po, layer_idx, true, params); - if (!layer_issues.empty()) { - issues.add(layer_issues); - } - } - - grid_mutex.lock(); - grid.merge(balance_grid); - grid_mutex.unlock(); - - return issues; - }, - [](Issues left, const Issues &right) { - left.add(right); - return left; - } - ); -#ifdef DEBUG_FILES - Impl::debug_export(found_issues, "pre_issues"); -#endif - - grid.analyze(found_issues, params); - -#ifdef DEBUG_FILES - grid.debug_export(); - Impl::debug_export(found_issues, "issues"); -#endif - - return found_issues; -} + auto issues = check_object_stability(po, params); + debug_export(issues, "issues"); + return issues; } +} //SupportableIssues End } + diff --git a/src/libslic3r/SupportableIssuesSearch.hpp b/src/libslic3r/SupportableIssuesSearch.hpp index d5736a3bd..e8a9f0e0c 100644 --- a/src/libslic3r/SupportableIssuesSearch.hpp +++ b/src/libslic3r/SupportableIssuesSearch.hpp @@ -14,15 +14,15 @@ struct Params { float bridge_distance = 10.0f; //mm float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / ( 1 + this factor * (curvature / PI) ) - float base_adhesion = 2000.0f; // adhesion per mm^2 of first layer; Force needed to remove the object from the bed, divided by the adhesion area (g/mm*s^2) - float support_adhesion = 1000.0f; // adhesion per mm^2 of support interface layer - float support_points_interface_area = 5.0f; // mm^2 + // Adhesion computation : from experiment, PLA holds about 3g per mm^2 of base area (with reserve); So it can withstand about 3*gravity_constant force per mm^2 + float base_adhesion = 3.0f * gravity_constant; // adhesion per mm^2 of first layer + float support_adhesion = 1.0f * gravity_constant; // adhesion per mm^2 of support interface layer + float support_points_interface_area = 2.0f; // mm^2 float max_acceleration = 1000.0f; // mm/s^2 ; max acceleration of object (bed) in XY float filament_density = 1.25f * 0.001f; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important float tolerable_extruder_conflict_force = 50.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); current value corresponds to weight of 50g - float max_curled_conflict_extruder_force = 200.0f * gravity_constant; // for areas with possible high layered curled filaments, max force to account fo ; current value corresponds to weight of 200g - + float max_curled_conflict_extruder_force = 200.0f * gravity_constant; // for areas with possible high layered curled filaments, max force to account for; current value corresponds to weight of 200g }; struct SupportPoint { diff --git a/src/libslic3r/SupportableIssuesSearchRefactoring.cpp b/src/libslic3r/SupportableIssuesSearchRefactoring.cpp deleted file mode 100644 index 35b567c55..000000000 --- a/src/libslic3r/SupportableIssuesSearchRefactoring.cpp +++ /dev/null @@ -1,635 +0,0 @@ -#include "SupportableIssuesSearch.hpp" - -#include "tbb/parallel_for.h" -#include "tbb/blocked_range.h" -#include "tbb/parallel_reduce.h" -#include -#include -#include -#include - -#include "AABBTreeLines.hpp" -#include "libslic3r/Layer.hpp" -#include "libslic3r/ClipperUtils.hpp" -#include "Geometry/ConvexHull.hpp" - -#define DEBUG_FILES - -#ifdef DEBUG_FILES -#include -#include "libslic3r/Color.hpp" -#endif - -namespace Slic3r { - -static const size_t NULL_ACC_ID = std::numeric_limits::max(); - -class ExtrusionLine -{ -public: - ExtrusionLine() : - a(Vec2f::Zero()), b(Vec2f::Zero()), len(0.0f) { - } - ExtrusionLine(const Vec2f &_a, const Vec2f &_b) : - a(_a), b(_b), len((_a - _b).norm()) { - } - - float length() { - return (a - b).norm(); - } - - Vec2f a; - Vec2f b; - float len; - - size_t supported_segment_accumulator_id = NULL_ACC_ID; - - static const constexpr int Dim = 2; - using Scalar = Vec2f::Scalar; -}; - -auto get_a(ExtrusionLine &&l) { - return l.a; -} -auto get_b(ExtrusionLine &&l) { - return l.b; -} - -namespace SupportableIssues { - -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(); -} - -SupportPoint::SupportPoint(const Vec3f &position, float weight) : - position(position), weight(weight) { -} - -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) { -} - -class LayerLinesDistancer { -private: - std::vector lines; - AABBTreeIndirect::Tree<2, float> tree; - -public: - explicit LayerLinesDistancer(std::vector &&lines) : - lines(lines) { - tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(lines); - } - - // negative sign means inside - float signed_distance_from_lines(const Vec2f &point, size_t &nearest_line_index_out, - Vec2f &nearest_point_out) const { - auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, point, nearest_line_index_out, - nearest_point_out); - if (distance < 0) - return std::numeric_limits::infinity(); - - distance = sqrt(distance); - const ExtrusionLine &line = lines[nearest_line_index_out]; - Vec2f v1 = line.b - line.a; - Vec2f v2 = point - line.a; - if ((v1.x() * v2.y()) - (v1.y() * v2.x()) > 0.0) { - distance *= -1; - } - return distance; - } - - const ExtrusionLine& get_line(size_t line_idx) const { - return lines[line_idx]; - } -}; - -class StabilityAccumulator { -private: - Polygon base_convex_hull { }; - Points support_points { }; - Vec3f centroid_accumulator = Vec3f::Zero(); - float accumulated_volume { }; - float base_area { }; - float base_height { }; - -public: - explicit StabilityAccumulator(float base_height) : - base_height(base_height) { - } - - void add_base_extrusion(const ExtrusionLine &line, float width, float print_z, float cross_section) { - base_area += line.len * width; - support_points.push_back(Point::new_scale(line.a)); - support_points.push_back(Point::new_scale(line.b)); - base_convex_hull.clear(); - add_extrusion(line, print_z, cross_section); - } - - void add_support_point(const Point &position, float area) { - support_points.push_back(position); - base_convex_hull.clear(); - base_area += area; - } - - void add_extrusion(const ExtrusionLine &line, float print_z, float cross_section) { - float volume = line.len * cross_section; - accumulated_volume += volume; - Vec2f center = (line.a + line.b) / 2.0f; - centroid_accumulator += volume * Vec3f(center.x(), center.y(), print_z); - } - - Vec3f get_centroid() const { - return centroid_accumulator / accumulated_volume; - } - - float get_base_area() const { - return base_area; - } - float get_base_height() const { - return base_height; - } - - const Polygon& segment_base_hull() { - if (this->base_convex_hull.empty()) { - this->base_convex_hull = Geometry::convex_hull(this->support_points); - } - return this->base_convex_hull; - } - - const Points& get_support_points() { - return support_points; - } - - void add_from(const StabilityAccumulator &acc) { - this->support_points.insert(this->support_points.end(), acc.support_points.begin(), - acc.support_points.end()); - base_convex_hull.clear(); - this->centroid_accumulator += acc.centroid_accumulator; - this->accumulated_volume += acc.accumulated_volume; - this->base_area += acc.base_area; - } -}; - -struct StabilityAccumulators { -private: - size_t next_id = 0; - std::unordered_map mapping; - std::vector acccumulators; - - void merge_to(size_t from_id, size_t to_id) { - StabilityAccumulator &from_acc = this->access(from_id); - StabilityAccumulator &to_acc = this->access(to_id); - if (&from_acc == &to_acc) { - return; - } - to_acc.add_from(from_acc); - mapping[from_id] = mapping[to_id]; - from_acc = StabilityAccumulator { 0.0f }; - - } - -public: - StabilityAccumulators() = default; - - int create_accumulator(float base_height) { - size_t id = next_id; - next_id++; - mapping[id] = acccumulators.size(); - acccumulators.push_back(StabilityAccumulator { base_height }); - return id; - } - - StabilityAccumulator& access(size_t id) { - return acccumulators[mapping[id]]; - } - - void merge_accumulators(size_t from_id, size_t to_id) { - if (from_id == NULL_ACC_ID || to_id == NULL_ACC_ID) { - return; - } - StabilityAccumulator &from_acc = this->access(from_id); - StabilityAccumulator &to_acc = this->access(to_id); - if (&from_acc == &to_acc) { - return; - } - to_acc.add_from(from_acc); - mapping[from_id] = mapping[to_id]; - from_acc = StabilityAccumulator { 0.0f }; - } - -#ifdef DEBUG_FILES - Vec3f get_emerging_color(size_t id) { - if (mapping.find(id) == mapping.end()) { - std::cerr << " ERROR: uknown accumulator ID: " << id << std::endl; - return Vec3f(1.0f, 1.0f, 1.0f); - } - - size_t pseudornd = ((id + 127) * 33331 + 6907) % 13; - return value_to_rgbf(0.0f, 13.0f, float(pseudornd)); - } - - Vec3f get_final_color(size_t id) { - if (mapping.find(id) == mapping.end()) { - std::cerr << " ERROR: uknown accumulator ID: " << id << std::endl; - return Vec3f(1.0f, 1.0f, 1.0f); - } - - size_t pseudornd = ((mapping[id] + 127) * 33331 + 6907) % 13; - return value_to_rgbf(0.0f, 13.0f, float(pseudornd)); - } -#endif DEBUG_FILES -}; - -float get_flow_width(const LayerRegion *region, ExtrusionRole role) { - switch (role) { - case ExtrusionRole::erBridgeInfill: - return region->flow(FlowRole::frExternalPerimeter).width(); - case ExtrusionRole::erExternalPerimeter: - return region->flow(FlowRole::frExternalPerimeter).width(); - case ExtrusionRole::erGapFill: - return region->flow(FlowRole::frInfill).width(); - case ExtrusionRole::erPerimeter: - return region->flow(FlowRole::frPerimeter).width(); - case ExtrusionRole::erSolidInfill: - return region->flow(FlowRole::frSolidInfill).width(); - case ExtrusionRole::erInternalInfill: - return region->flow(FlowRole::frInfill).width(); - case ExtrusionRole::erTopSolidInfill: - return region->flow(FlowRole::frTopSolidInfill).width(); - default: - return region->flow(FlowRole::frPerimeter).width(); - } -} - -struct ExtrusionPropertiesAccumulator { - 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; - } -}; - -void check_extrusion_entity_stability(const ExtrusionEntity *entity, - StabilityAccumulators &stability_accs, - Issues &issues, - std::vector &checked_lines, - float print_z, - const LayerRegion *layer_region, - const LayerLinesDistancer &prev_layer_lines, - const Params ¶ms) { - - if (entity->is_collection()) { - for (const auto *e : static_cast(entity)->entities) { - check_extrusion_entity_stability(e, stability_accs, issues, checked_lines, print_z, layer_region, - prev_layer_lines, - params); - } - } else { //single extrusion path, with possible varying parameters - const auto to_vec3f = [print_z](const Point &point) { - Vec2f tmp = unscale(point).cast(); - return Vec3f(tmp.x(), tmp.y(), print_z); - }; - Points points { }; - entity->collect_points(points); - std::vector lines; - lines.reserve(points.size() * 1.5); - lines.emplace_back(unscaled(points[0]).cast(), unscaled(points[0]).cast()); - for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { - Vec2f start = unscaled(points[point_idx]).cast(); - Vec2f next = unscaled(points[point_idx + 1]).cast(); - Vec2f v = next - start; // vector from next to current - float dist_to_next = v.norm(); - v.normalize(); - int lines_count = int(std::ceil(dist_to_next / params.bridge_distance)); - float step_size = dist_to_next / lines_count; - for (int i = 0; i < lines_count; ++i) { - Vec2f a(start + v * (i * step_size)); - Vec2f b(start + v * ((i + 1) * step_size)); - lines.emplace_back(a, b); - } - } - - size_t current_stability_acc = NULL_ACC_ID; - ExtrusionPropertiesAccumulator bridging_acc { }; - bridging_acc.add_distance(params.bridge_distance + 1.0f); // Initialise unsupported distance with larger than tolerable distance -> - // -> it prevents extruding perimeter start and short loops into air. - const float flow_width = get_flow_width(layer_region, entity->role()); - const float region_height = layer_region->layer()->height; - const float max_allowed_dist_from_prev_layer = flow_width; - - for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) { - ExtrusionLine ¤t_line = lines[line_idx]; - Point current = Point::new_scale(current_line.b); - float cross_section = region_height * flow_width * 0.7071f; - - float curr_angle = 0; - if (line_idx + 1 < lines.size()) { - const Vec2f v1 = current_line.b - current_line.a; - const Vec2f v2 = lines[line_idx + 1].b - lines[line_idx + 1].a; - curr_angle = angle(v1, v2); - } - bridging_acc.add_angle(curr_angle); - - size_t nearest_line_idx; - Vec2f nearest_point; - float dist_from_prev_layer = prev_layer_lines.signed_distance_from_lines(current_line.b, nearest_line_idx, - nearest_point); - if (dist_from_prev_layer < max_allowed_dist_from_prev_layer) { - const ExtrusionLine &nearest_line = prev_layer_lines.get_line(nearest_line_idx); - size_t acc_id = nearest_line.supported_segment_accumulator_id; - stability_accs.merge_accumulators(std::max(acc_id, current_stability_acc), - std::min(acc_id, current_stability_acc)); - current_stability_acc = std::min(acc_id, current_stability_acc); - current_line.supported_segment_accumulator_id = current_stability_acc; - stability_accs.access(current_stability_acc).add_extrusion(current_line, print_z, cross_section); - bridging_acc.reset(); - // TODO curving here - } else { - bridging_acc.add_distance(current_line.len); - if (current_stability_acc == NULL_ACC_ID) { - current_stability_acc = stability_accs.create_accumulator(print_z); - } - StabilityAccumulator ¤t_segment = stability_accs.access(current_stability_acc); - current_line.supported_segment_accumulator_id = current_stability_acc; - current_segment.add_extrusion(current_line, print_z, cross_section); - if (bridging_acc.distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. - > params.bridge_distance - / (1.0f + (bridging_acc.max_curvature - * params.bridge_distance_decrease_by_curvature_factor / PI))) { - current_segment.add_support_point(current, params.support_points_interface_area); - issues.supports_nedded.emplace_back(to_vec3f(current), 1.0); - bridging_acc.reset(); - } - } - } - checked_lines.insert(checked_lines.end(), lines.begin(), lines.end()); - } -} - -void check_layer_global_stability(StabilityAccumulators &stability_accs, - Issues &issues, - const std::vector &checked_lines, - float print_z, - const Params ¶ms) { - std::unordered_map> layer_accs_lines; - for (size_t i = 0; i < checked_lines.size(); ++i) { - layer_accs_lines[&stability_accs.access(checked_lines[i].supported_segment_accumulator_id)].push_back(i); - } - - for (auto &acc_lines : layer_accs_lines) { - StabilityAccumulator *acc = acc_lines.first; - Vec3f centroid = acc->get_centroid(); - Vec2f hull_centroid = unscaled(acc->segment_base_hull().centroid()).cast(); - std::vector hull_lines; - for (const Line &line : acc->segment_base_hull().lines()) { - Vec2f start = unscaled(line.a).cast(); - Vec2f next = unscaled(line.b).cast(); - hull_lines.push_back( { start, next }); - } - if (hull_lines.empty()) { - if (acc->get_support_points().empty()) { - acc->add_support_point(Point::new_scale(checked_lines[acc_lines.second[0]].a), - params.support_points_interface_area); - issues.supports_nedded.emplace_back(to_3d(checked_lines[acc_lines.second[0]].a, print_z), 1.0); - } - hull_lines.push_back( { unscaled(acc->get_support_points()[0]).cast(), - unscaled(acc->get_support_points()[0]).cast() }); - hull_centroid = unscaled(acc->get_support_points()[0]).cast(); - } - - LayerLinesDistancer hull_distancer(std::move(hull_lines)); - - size_t _li; - Vec2f _p; - bool centroid_inside_hull = hull_distancer.signed_distance_from_lines(centroid.head<2>(), _li, _p) < 0; - - float sticking_force = acc->get_base_area() - * (acc->get_base_height() == 0 ? params.base_adhesion : params.support_adhesion); -// float weight = acc-> * params.filament_density * params.gravity_constant; -// float weight_torque = embedded_distance * weight; -// if (!inside) { -// weight_torque *= -1; -// } - - for (size_t line_idx : acc_lines.second){ - const ExtrusionLine &line = checked_lines[line_idx]; - - size_t nearest_line_idx; - Vec2f nearest_hull_point; - float hull_distance = hull_distancer.signed_distance_from_lines(line.b, nearest_line_idx, - nearest_hull_point); - - float sticking_torque = (nearest_hull_point - hull_centroid).norm() * sticking_force; - - std::cout << "sticking_torque: " << sticking_torque << std::endl; - - - Vec3f extruder_pressure_direction = to_3d(Vec2f(line.b - line.a), 0.0f).normalized(); - if (hull_distance > 0) { - extruder_pressure_direction.z() = -0.333f; - extruder_pressure_direction.normalize(); - } - float pressure_torque_arm = (to_3d(Vec2f(nearest_hull_point - line.b), print_z).cross(extruder_pressure_direction)).norm(); - - float extruder_conflict_torque = params.tolerable_extruder_conflict_force * pressure_torque_arm; - - std::cout << "extruder_conflict_torque: " << extruder_conflict_torque << std::endl; - - if (extruder_conflict_torque > sticking_torque) { - acc->add_support_point(Point::new_scale(line.b), params.support_points_interface_area); - issues.supports_nedded.emplace_back(to_3d(line.b, print_z), extruder_conflict_torque - sticking_torque); - } - - } - } -} - -Issues check_object_stability(const PrintObject *po, const Params ¶ms) { -#ifdef DEBUG_FILES - FILE *eacc = boost::nowide::fopen(debug_out_path("emerging_accumulators.obj").c_str(), "w"); - FILE *facc = boost::nowide::fopen(debug_out_path("final_accumulators.obj").c_str(), "w"); -#endif DEBUG_FILES - StabilityAccumulators stability_accs; - LayerLinesDistancer prev_layer_lines { { } }; - Issues issues { }; - std::vector checked_lines; - - const Layer *layer = po->layers()[0]; - float base_print_z = layer->print_z; - for (const LayerRegion *layer_region : layer->regions()) { - for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { - for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { - const float flow_width = get_flow_width(layer_region, perimeter->role()); - const float region_height = layer_region->layer()->height; - const float cross_section = region_height * flow_width * 0.7071f; - int id = stability_accs.create_accumulator(base_print_z); - StabilityAccumulator &acc = stability_accs.access(id); - Points points { }; - perimeter->collect_points(points); - for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { - Vec2f start = unscaled(points[point_idx]).cast(); - Vec2f next = unscaled(points[point_idx + 1]).cast(); - ExtrusionLine line { start, next }; - line.supported_segment_accumulator_id = id; - acc.add_base_extrusion(line, flow_width, base_print_z, cross_section); - checked_lines.push_back(line); - } - } // perimeter - } // ex_entity - for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { - for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { - const float flow_width = get_flow_width(layer_region, fill->role()); - const float region_height = layer_region->layer()->height; - const float cross_section = region_height * flow_width * 0.7071f; - int id = stability_accs.create_accumulator(base_print_z); - StabilityAccumulator &acc = stability_accs.access(id); - Points points { }; - fill->collect_points(points); - for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { - Vec2f start = unscaled(points[point_idx]).cast(); - Vec2f next = unscaled(points[point_idx + 1]).cast(); - ExtrusionLine line { start, next }; - line.supported_segment_accumulator_id = id; - acc.add_base_extrusion(line, flow_width, base_print_z, cross_section); - checked_lines.push_back(line); - } - } // fill - } // ex_entity - } // region - -#ifdef DEBUG_FILES - for (const auto &line : checked_lines) { - Vec3f ecolor = stability_accs.get_emerging_color(line.supported_segment_accumulator_id); - fprintf(eacc, "v %f %f %f %f %f %f\n", line.b[0], - line.b[1], base_print_z, ecolor[0], ecolor[1], ecolor[2]); - - Vec3f fcolor = stability_accs.get_final_color(line.supported_segment_accumulator_id); - fprintf(facc, "v %f %f %f %f %f %f\n", line.b[0], - line.b[1], base_print_z, fcolor[0], fcolor[1], fcolor[2]); - } -#endif DEBUG_FILES - - for (size_t layer_idx = 1; layer_idx < po->layer_count(); ++layer_idx) { - const Layer *layer = po->layers()[layer_idx]; - prev_layer_lines = LayerLinesDistancer { std::move(checked_lines) }; - checked_lines = std::vector { }; - - float print_z = layer->print_z; - for (const LayerRegion *layer_region : layer->regions()) { - for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { - for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { - check_extrusion_entity_stability(perimeter, stability_accs, issues, checked_lines, print_z, - layer_region, - prev_layer_lines, params); - } // perimeter - } // ex_entity - for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { - for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { - if (fill->role() == ExtrusionRole::erGapFill - || fill->role() == ExtrusionRole::erBridgeInfill) { - check_extrusion_entity_stability(fill, stability_accs, issues, checked_lines, print_z, - layer_region, - prev_layer_lines, params); - } - } // fill - } // ex_entity - } // region - - check_layer_global_stability(stability_accs, issues, checked_lines, print_z, params); - -#ifdef DEBUG_FILES - for (const auto &line : checked_lines) { - Vec3f ecolor = stability_accs.get_emerging_color(line.supported_segment_accumulator_id); - fprintf(eacc, "v %f %f %f %f %f %f\n", line.b[0], - line.b[1], print_z, ecolor[0], ecolor[1], ecolor[2]); - - Vec3f fcolor = stability_accs.get_final_color(line.supported_segment_accumulator_id); - fprintf(facc, "v %f %f %f %f %f %f\n", line.b[0], - line.b[1], print_z, fcolor[0], fcolor[1], fcolor[2]); - } -#endif DEBUG_FILES - } - -#ifdef DEBUG_FILES - fclose(eacc); - fclose(facc); -#endif DEBUG_FILES - - std::cout << " SUPP: " << issues.supports_nedded.size() << std::endl; - return issues; -} - -#ifdef DEBUG_FILES -void debug_export(Issues issues, std::string file_name) { - Slic3r::CNumericLocalesSetter locales_setter; - { - 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; - } - - 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].position(0), - issues.supports_nedded[i].position(1), - issues.supports_nedded[i].position(2), 1.0, 0.0, 1.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].position(0), - issues.curling_up[i].position(1), - issues.curling_up[i].position(2), 0.0, 1.0, 0.0); - } - fclose(fp); - } -} -#endif - -std::vector quick_search(const PrintObject *po, const Params ¶ms) { - check_object_stability(po, params); - return {}; -} - -Issues full_search(const PrintObject *po, const Params ¶ms) { - auto issues = check_object_stability(po, params); - debug_export(issues, "issues"); - return issues; - -} -} //SupportableIssues End -} -