diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 1b3bf5d44d6..ab50c105da1 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -425,7 +425,7 @@ void PrintObject::generate_support_spots() [](const ModelVolume* mv){return mv->supported_facets.empty();}) ) { SupportSpotsGenerator::Params params{this->print()->m_config.filament_type.values}; - auto [issues, malformations] = SupportSpotsGenerator::full_search(this, params); + SupportSpotsGenerator::Issues issues = SupportSpotsGenerator::full_search(this, params); auto obj_transform = this->trafo_centered(); for (ModelVolume *model_volume : this->model_object()->volumes) { diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index abaaabfbe0c..66c6de7a9de 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -6,6 +6,8 @@ #include "Line.hpp" #include "Point.hpp" #include "Polygon.hpp" +#include "Print.hpp" +#include "Tesselate.hpp" #include "libslic3r.h" #include "tbb/parallel_for.h" #include "tbb/blocked_range.h" @@ -18,6 +20,7 @@ #include #include #include +#include #include "AABBTreeLines.hpp" #include "KDTreeIndirect.hpp" @@ -26,7 +29,7 @@ #include "Geometry/ConvexHull.hpp" // #define DETAILED_DEBUG_LOGS -//#define DEBUG_FILES +// #define DEBUG_FILES #ifdef DEBUG_FILES #include @@ -38,164 +41,74 @@ namespace Slic3r { class ExtrusionLine { public: - ExtrusionLine() : - a(Vec2f::Zero()), b(Vec2f::Zero()), len(0.0f), origin_entity(nullptr) { - } - ExtrusionLine(const Vec2f &a, const Vec2f &b, const ExtrusionEntity *origin_entity) : - a(a), b(b), len((a - b).norm()), origin_entity(origin_entity) { - } + ExtrusionLine() : a(Vec2f::Zero()), b(Vec2f::Zero()), len(0.0f), origin_entity(nullptr) {} + ExtrusionLine(const Vec2f &a, const Vec2f &b, const ExtrusionEntity *origin_entity) + : a(a), b(b), len((a - b).norm()), origin_entity(origin_entity) + {} - float length() { - return (a - b).norm(); - } + float length() { return (a - b).norm(); } - bool is_external_perimeter() const { + bool is_external_perimeter() const + { assert(origin_entity != nullptr); return origin_entity->role() == erExternalPerimeter || origin_entity->role() == erOverhangPerimeter; } - Vec2f a; - Vec2f b; - float len; + Vec2f a; + Vec2f b; + float len; const ExtrusionEntity *origin_entity; - bool support_point_generated = false; - float malformation = 0.0f; + bool support_point_generated = false; + float malformation = 0.0f; static const constexpr int Dim = 2; - using Scalar = Vec2f::Scalar; + using Scalar = Vec2f::Scalar; }; -auto get_a(ExtrusionLine &&l) { - return l.a; -} -auto get_b(ExtrusionLine &&l) { - return l.b; -} +auto get_a(ExtrusionLine &&l) { return l.a; } +auto get_b(ExtrusionLine &&l) { return l.b; } namespace SupportSpotsGenerator { -SupportPoint::SupportPoint(const Vec3f &position, float force, float spot_radius, const Vec3f &direction) : - position(position), force(force), spot_radius(spot_radius), direction(direction) { -} - -static const size_t NULL_ISLAND = std::numeric_limits::max(); +SupportPoint::SupportPoint(const Vec3f &position, float force, float spot_radius, const Vec2f &direction) + : position(position), force(force), spot_radius(spot_radius), direction(direction) +{} using LD = AABBTreeLines::LinesDistancer; -class PixelGrid { - Vec2f pixel_size; - Vec2f origin; - Vec2f size; - Vec2i pixel_count; - - std::vector pixels { }; - -public: - PixelGrid(const PrintObject *po, float resolution) { - pixel_size = Vec2f(resolution, resolution); - - Vec2crd size_half = po->size().head<2>().cwiseQuotient(Vec2crd(2, 2)) + Vec2crd::Ones(); - Vec2f min = unscale(Vec2crd(-size_half.x(), -size_half.y())).cast(); - Vec2f max = unscale(Vec2crd(size_half.x(), size_half.y())).cast(); - - origin = min; - size = max - min; - pixel_count = size.cwiseQuotient(pixel_size).cast() + Vec2i::Ones(); - - pixels.resize(pixel_count.y() * pixel_count.x()); - clear(); - } - - void distribute_edge(const Vec2f &p1, const Vec2f &p2, size_t value) { - Vec2f dir = (p2 - p1); - float length = dir.norm(); - if (length < 0.1) { - return; - } - float step_size = this->pixel_size.x() / 2.0; - - float distributed_length = 0; - while (distributed_length < length) { - float next_len = std::min(length, distributed_length + step_size); - Vec2f location = p1 + ((next_len / length) * dir); - this->access_pixel(location) = value; - - distributed_length = next_len; - } - } - - void clear() { - for (size_t &val : pixels) { - val = NULL_ISLAND; - } - } - - float pixel_area() const { - return this->pixel_size.x() * this->pixel_size.y(); - } - - size_t get_pixel(const Vec2i &coords) const { - return pixels[this->to_pixel_index(coords)]; - } - - Vec2i get_pixel_count() { - return pixel_count; - } - - Vec2f get_pixel_center(const Vec2i &coords) const { - return origin + coords.cast().cwiseProduct(this->pixel_size) - + this->pixel_size.cwiseQuotient(Vec2f(2.0f, 2.0f)); - } - -private: - Vec2i to_pixel_coords(const Vec2f &position) const { - Vec2i pixel_coords = (position - this->origin).cwiseQuotient(this->pixel_size).cast(); - return pixel_coords; - } - - size_t to_pixel_index(const Vec2i &pixel_coords) const { - assert(pixel_coords.x() >= 0); - assert(pixel_coords.x() < pixel_count.x()); - assert(pixel_coords.y() >= 0); - assert(pixel_coords.y() < pixel_count.y()); - - return pixel_coords.y() * pixel_count.x() + pixel_coords.x(); - } - - size_t& access_pixel(const Vec2f &position) { - return pixels[this->to_pixel_index(this->to_pixel_coords(position))]; - } -}; - -struct SupportGridFilter { +struct SupportGridFilter +{ private: Vec3f cell_size; Vec3f origin; Vec3f size; Vec3i cell_count; - std::unordered_set taken_cells { }; + std::unordered_set taken_cells{}; public: - SupportGridFilter(const PrintObject *po, float voxel_size) { + SupportGridFilter(const PrintObject *po, float voxel_size) + { cell_size = Vec3f(voxel_size, voxel_size, voxel_size); Vec2crd size_half = po->size().head<2>().cwiseQuotient(Vec2crd(2, 2)) + Vec2crd::Ones(); - Vec3f min = unscale(Vec3crd(-size_half.x(), -size_half.y(), 0)).cast() - cell_size; - Vec3f max = unscale(Vec3crd(size_half.x(), size_half.y(), po->height())).cast() + cell_size; + Vec3f min = unscale(Vec3crd(-size_half.x(), -size_half.y(), 0)).cast() - cell_size; + Vec3f max = unscale(Vec3crd(size_half.x(), size_half.y(), po->height())).cast() + cell_size; - origin = min; - size = max - min; + origin = min; + size = max - min; cell_count = size.cwiseQuotient(cell_size).cast() + Vec3i::Ones(); } - Vec3i to_cell_coords(const Vec3f &position) const { + Vec3i to_cell_coords(const Vec3f &position) const + { Vec3i cell_coords = (position - this->origin).cwiseQuotient(this->cell_size).cast(); return cell_coords; } - size_t to_cell_index(const Vec3i &cell_coords) const { + size_t to_cell_index(const Vec3i &cell_coords) const + { assert(cell_coords.x() >= 0); assert(cell_coords.x() < cell_count.x()); assert(cell_coords.y() >= 0); @@ -203,43 +116,41 @@ struct SupportGridFilter { assert(cell_coords.z() >= 0); assert(cell_coords.z() < cell_count.z()); - return cell_coords.z() * cell_count.x() * cell_count.y() - + cell_coords.y() * cell_count.x() - + cell_coords.x(); + return cell_coords.z() * cell_count.x() * cell_count.y() + cell_coords.y() * cell_count.x() + cell_coords.x(); } - Vec3f get_cell_center(const Vec3i &cell_coords) const { - return origin + cell_coords.cast().cwiseProduct(this->cell_size) - + this->cell_size.cwiseQuotient(Vec3f(2.0f, 2.0f, 2.0)); + Vec3f get_cell_center(const Vec3i &cell_coords) const + { + return origin + cell_coords.cast().cwiseProduct(this->cell_size) + this->cell_size.cwiseQuotient(Vec3f(2.0f, 2.0f, 2.0)); } - void take_position(const Vec3f &position) { - taken_cells.insert(to_cell_index(to_cell_coords(position))); - } + void take_position(const Vec3f &position) { taken_cells.insert(to_cell_index(to_cell_coords(position))); } - bool position_taken(const Vec3f &position) const { + bool position_taken(const Vec3f &position) const + { return taken_cells.find(to_cell_index(to_cell_coords(position))) != taken_cells.end(); } - }; -struct IslandConnection { - float area { }; - Vec3f centroid_accumulator = Vec3f::Zero(); +struct SliceConnection +{ + float area{}; + Vec3f centroid_accumulator = Vec3f::Zero(); Vec2f second_moment_of_area_accumulator = Vec2f::Zero(); - float second_moment_of_area_covariance_accumulator { }; + float second_moment_of_area_covariance_accumulator{}; - void add(const IslandConnection &other) { + void add(const SliceConnection &other) + { this->area += other.area; this->centroid_accumulator += other.centroid_accumulator; this->second_moment_of_area_accumulator += other.second_moment_of_area_accumulator; this->second_moment_of_area_covariance_accumulator += other.second_moment_of_area_covariance_accumulator; } - void print_info(const std::string &tag) { - Vec3f centroid = centroid_accumulator / area; - Vec2f variance = - (second_moment_of_area_accumulator / area - centroid.head<2>().cwiseProduct(centroid.head<2>())); + void print_info(const std::string &tag) + { + Vec3f centroid = centroid_accumulator / area; + Vec2f variance = (second_moment_of_area_accumulator / area - centroid.head<2>().cwiseProduct(centroid.head<2>())); float covariance = second_moment_of_area_covariance_accumulator / area - centroid.x() * centroid.y(); std::cout << tag << std::endl; std::cout << "area: " << area << std::endl; @@ -249,93 +160,59 @@ struct IslandConnection { } }; -struct Island { - std::unordered_map connected_islands { }; - float volume { }; - Vec3f volume_centroid_accumulator = Vec3f::Zero(); - float sticking_area { }; // for support points present on this layer (or bed extrusions) - Vec3f sticking_centroid_accumulator = Vec3f::Zero(); - Vec2f sticking_second_moment_of_area_accumulator = Vec2f::Zero(); - float sticking_second_moment_of_area_covariance_accumulator { }; - - std::vector external_lines; -}; - -struct LayerIslands { - std::vector islands; - float layer_z; -}; - -float get_flow_width(const LayerRegion *region, ExtrusionRole role) { +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(); + 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(); } } // Accumulator of current extrusion path properties // It remembers unsuported distance and maximum accumulated curvature over that distance. // Used to determine local stability issues (too long bridges, extrusion curves into air) -struct ExtrusionPropertiesAccumulator { - float distance = 0; //accumulated distance - float curvature = 0; //accumulated signed ccw angles - float max_curvature = 0; //max absolute accumulated value +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_distance(float dist) { distance += dist; } - void add_angle(float ccw_angle) { + void add_angle(float ccw_angle) + { curvature += ccw_angle; max_curvature = std::max(max_curvature, std::abs(curvature)); } - void reset() { - distance = 0; - curvature = 0; + void reset() + { + distance = 0; + curvature = 0; max_curvature = 0; } }; // base function: ((e^(((1)/(x^(2)+1)))-1)/(e-1)) // checkout e.g. here: https://www.geogebra.org/calculator -float gauss(float value, float mean_x_coord, float mean_value, float falloff_speed) { - float shifted = value - mean_x_coord; +float gauss(float value, float mean_x_coord, float mean_value, float falloff_speed) +{ + float shifted = value - mean_x_coord; float denominator = falloff_speed * shifted * shifted + 1.0f; - float exponent = 1.0f / denominator; + float exponent = 1.0f / denominator; return mean_value * (std::exp(exponent) - 1.0f) / (std::exp(1.0f) - 1.0f); } -void push_lines(const ExtrusionEntity *e, std::vector& destination) -{ - assert(!e->is_collection()); - Polyline pl = e->as_polyline(); - for (int point_idx = 0; point_idx < int(pl.points.size() - 1); ++point_idx) { - Vec2f start = unscaled(pl.points[point_idx]).cast(); - Vec2f next = unscaled(pl.points[point_idx + 1]).cast(); - ExtrusionLine line{start, next, e}; - destination.push_back(line); - } -} - std::vector to_short_lines(const ExtrusionEntity *e, float length_limit) { assert(!e->is_collection()); - Polyline pl = e->as_polyline(); + Polyline pl = e->as_polyline(); std::vector lines; lines.reserve(pl.points.size() * 1.5f); lines.emplace_back(unscaled(pl.points[0]).cast(), unscaled(pl.points[0]).cast(), e); @@ -356,33 +233,30 @@ std::vector to_short_lines(const ExtrusionEntity *e, float length return lines; } -void check_extrusion_entity_stability(const ExtrusionEntity *entity, - std::vector &checked_lines_out, - float layer_z, - const LayerRegion *layer_region, - const LD &prev_layer_lines, - Issues &issues, - const Params ¶ms) { - +std::vector check_extrusion_entity_stability(const ExtrusionEntity *entity, + const LayerRegion *layer_region, + const LD &prev_layer_lines, + const Params ¶ms) +{ if (entity->is_collection()) { - for (const auto *e : static_cast(entity)->entities) { - check_extrusion_entity_stability(e, checked_lines_out, layer_z, layer_region, prev_layer_lines, - issues, params); + std::vector checked_lines_out; + checked_lines_out.reserve(prev_layer_lines.get_lines().size() / 3); + for (const auto *e : static_cast(entity)->entities) { + auto tmp = check_extrusion_entity_stability(e, layer_region, prev_layer_lines, params); + checked_lines_out.insert(checked_lines_out.end(), tmp.begin(), tmp.end()); } - } else { //single extrusion path, with possible varying parameters - const auto to_vec3f = [layer_z](const Vec2f &point) { - return Vec3f(point.x(), point.y(), layer_z); - }; + return checked_lines_out; + } else { // single extrusion path, with possible varying parameters + if (entity->length() < scale_(params.min_distance_to_allow_local_supports)) { return {}; } + std::vector lines = to_short_lines(entity, params.bridge_distance); - if (lines.empty()) return; - ExtrusionPropertiesAccumulator bridging_acc { }; - ExtrusionPropertiesAccumulator malformation_acc { }; + ExtrusionPropertiesAccumulator bridging_acc{}; + ExtrusionPropertiesAccumulator malformation_acc{}; bridging_acc.add_distance(params.bridge_distance + 1.0f); - const float flow_width = get_flow_width(layer_region, entity->role()); - float min_malformation_dist = flow_width - params.malformation_overlap_factor.first * flow_width; - float max_malformation_dist = flow_width - params.malformation_overlap_factor.second * flow_width; - + const float flow_width = get_flow_width(layer_region, entity->role()); + float min_malformation_dist = flow_width - params.malformation_overlap_factor.first * flow_width; + float max_malformation_dist = flow_width - params.malformation_overlap_factor.second * flow_width; for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) { ExtrusionLine ¤t_line = lines[line_idx]; @@ -393,443 +267,304 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, 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); + curr_angle = angle(v1, v2); } bridging_acc.add_angle(curr_angle); // malformation in concave angles does not happen malformation_acc.add_angle(std::max(0.0f, curr_angle)); - if (curr_angle < -20.0 * PI / 180.0) { - malformation_acc.reset(); - } + if (curr_angle < -20.0 * PI / 180.0) { malformation_acc.reset(); } auto [dist_from_prev_layer, nearest_line_idx, nearest_point] = prev_layer_lines.signed_distance_from_lines_extra(current_line.b); - if (fabs(dist_from_prev_layer) < flow_width) { + if (dist_from_prev_layer < flow_width) { bridging_acc.reset(); } else { bridging_acc.add_distance(current_line.len); // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. - bool in_layer_dist_condition = bridging_acc.distance - > params.bridge_distance / (1.0f + (bridging_acc.max_curvature - * params.bridge_distance_decrease_by_curvature_factor / PI)); - bool between_layers_condition = fabs(dist_from_prev_layer) > flow_width || - prev_layer_lines.get_line(nearest_line_idx).malformation > 3.0f * layer_region->layer()->height; - + bool in_layer_dist_condition = bridging_acc.distance > + params.bridge_distance / (1.0f + (bridging_acc.max_curvature * + params.bridge_distance_decrease_by_curvature_factor / PI)); + bool between_layers_condition = dist_from_prev_layer > max_malformation_dist; if (in_layer_dist_condition && between_layers_condition) { - issues.support_points.emplace_back(to_vec3f(current_line.b), 0.0f, params.support_points_interface_radius, Vec3f(0.f, 0.0f, -1.0f)); current_line.support_point_generated = true; bridging_acc.reset(); } } - //malformation + // malformation propagation from below if (fabs(dist_from_prev_layer) < 2.0f * flow_width) { const ExtrusionLine &nearest_line = prev_layer_lines.get_line(nearest_line_idx); current_line.malformation += 0.85 * nearest_line.malformation; } + // current line maformation if (dist_from_prev_layer > min_malformation_dist && dist_from_prev_layer < max_malformation_dist) { float factor = std::abs(dist_from_prev_layer - (max_malformation_dist + min_malformation_dist) * 0.5) / (max_malformation_dist - min_malformation_dist); malformation_acc.add_distance(current_line.len); current_line.malformation += layer_region->layer()->height * factor * (2.0f + 3.0f * (malformation_acc.max_curvature / PI)); - current_line.malformation = std::min(current_line.malformation, float(layer_region->layer()->height * params.max_malformation_factor)); + current_line.malformation = std::min(current_line.malformation, + float(layer_region->layer()->height * params.max_malformation_factor)); } else { malformation_acc.reset(); } } - checked_lines_out.insert(checked_lines_out.end(), lines.begin(), lines.end()); + return lines; } } -std::tuple reckon_islands( - const Layer *layer, bool first_layer, - size_t prev_layer_islands_count, - const PixelGrid &prev_layer_grid, - const std::vector &layer_lines, - const Params ¶ms) { - - //extract extrusions (connected paths from multiple lines) from the layer_lines. Grouping by the same polyline is determined by common origin_entity ptr. - // result is a vector of [start, end) index pairs into the layer_lines vector - std::vector> extrusions; //start and end idx (one beyond last extrusion) [start,end) - const ExtrusionEntity *current_ex = nullptr; - for (size_t lidx = 0; lidx < layer_lines.size(); ++lidx) { - const ExtrusionLine &line = layer_lines[lidx]; - if (line.origin_entity == current_ex) { - extrusions.back().second = lidx + 1; - } else { - extrusions.emplace_back(lidx, lidx + 1); - current_ex = line.origin_entity; - } - } - - std::vector> islands; // these search trees will be used to determine to which island does the extrusion belong. - for (const ExPolygon& island : layer->lslices) { - islands.emplace_back(to_lines(island)); - } - - std::vector> island_extrusions(islands.size(), - std::vector{}); // final assigment of each extrusion to an island. - for (size_t extrusion_idx = 0; extrusion_idx < extrusions.size(); extrusion_idx++) { - Point second_point = Point::new_scale(layer_lines[extrusions[extrusion_idx].first].b); - for (size_t island_idx = 0; island_idx < islands.size(); island_idx++) { - if (islands[island_idx].signed_distance_from_lines(second_point) <= 0.0) { - island_extrusions[island_idx].push_back(extrusion_idx); - } - } - } - - float flow_width = get_flow_width(layer->regions()[0], erExternalPerimeter); - // after filtering the layer lines into islands, build the result LayerIslands structure. - LayerIslands result { }; - result.layer_z = layer->slice_z; - std::vector line_to_island_mapping(layer_lines.size(), NULL_ISLAND); - for (const std::vector &island_ex : island_extrusions) { - if (island_ex.empty()) { - continue; - } - - Island island { }; - for (size_t extrusion_idx : island_ex) { - - if (layer_lines[extrusions[extrusion_idx].first].is_external_perimeter()) { - island.external_lines.insert(island.external_lines.end(), - layer_lines.begin() + extrusions[extrusion_idx].first, - layer_lines.begin() + extrusions[extrusion_idx].second); - } - - for (size_t lidx = extrusions[extrusion_idx].first; lidx < extrusions[extrusion_idx].second; ++lidx) { - line_to_island_mapping[lidx] = result.islands.size(); - const ExtrusionLine &line = layer_lines[lidx]; - float volume = line.len * layer->height * flow_width * PI / 4.0f; - island.volume += volume; - island.volume_centroid_accumulator += to_3d(Vec2f((line.a + line.b) / 2.0f), float(layer->slice_z)) - * volume; - - if (first_layer) { - float sticking_area = line.len * flow_width; - island.sticking_area += sticking_area; - Vec2f middle = Vec2f((line.a + line.b) / 2.0f); - island.sticking_centroid_accumulator += sticking_area * to_3d(middle, float(layer->slice_z)); - // Bottom infill lines can be quite long, and algined, so the middle approximaton used above does not work - Vec2f dir = (line.b - line.a).normalized(); - float segment_length = flow_width; // segments of size flow_width - for (float segment_middle_dist = std::min(line.len, segment_length * 0.5f); - segment_middle_dist < line.len; - segment_middle_dist += segment_length) { - Vec2f segment_middle = line.a + segment_middle_dist * dir; - island.sticking_second_moment_of_area_accumulator += segment_length * flow_width - * segment_middle.cwiseProduct(segment_middle); - island.sticking_second_moment_of_area_covariance_accumulator += segment_length * flow_width - * segment_middle.x() - * segment_middle.y(); - } - } else if (layer_lines[lidx].support_point_generated) { - float sticking_area = line.len * flow_width; - island.sticking_area += sticking_area; - island.sticking_centroid_accumulator += sticking_area * to_3d(line.b, float(layer->slice_z)); - island.sticking_second_moment_of_area_accumulator += sticking_area * line.b.cwiseProduct(line.b); - island.sticking_second_moment_of_area_covariance_accumulator += sticking_area * line.b.x() - * line.b.y(); - } - } - } - result.islands.push_back(island); - } - - //LayerIslands structure built. Now determine connections and their areas to the previous layer using rasterization. - PixelGrid current_layer_grid = prev_layer_grid; - current_layer_grid.clear(); - // build index image of current layer - tbb::parallel_for(tbb::blocked_range(0, layer_lines.size()), - [&layer_lines, ¤t_layer_grid, &line_to_island_mapping]( - tbb::blocked_range r) { - for (size_t i = r.begin(); i < r.end(); ++i) { - size_t island = line_to_island_mapping[i]; - const ExtrusionLine &line = layer_lines[i]; - current_layer_grid.distribute_edge(line.a, line.b, island); - } - }); - - //compare the image of previous layer with the current layer. For each pair of overlapping valid pixels, add pixel area to the respective island connection - for (size_t x = 0; x < size_t(current_layer_grid.get_pixel_count().x()); ++x) { - for (size_t y = 0; y < size_t(current_layer_grid.get_pixel_count().y()); ++y) { - Vec2i coords = Vec2i(x, y); - if (current_layer_grid.get_pixel(coords) != NULL_ISLAND - && prev_layer_grid.get_pixel(coords) != NULL_ISLAND) { - IslandConnection &connection = result.islands[current_layer_grid.get_pixel(coords)] - .connected_islands[prev_layer_grid.get_pixel(coords)]; - Vec2f current_coords = current_layer_grid.get_pixel_center(coords); - connection.area += current_layer_grid.pixel_area(); - connection.centroid_accumulator += to_3d(current_coords, result.layer_z) - * current_layer_grid.pixel_area(); - connection.second_moment_of_area_accumulator += current_coords.cwiseProduct(current_coords) - * current_layer_grid.pixel_area(); - connection.second_moment_of_area_covariance_accumulator += current_coords.x() * current_coords.y() - * current_layer_grid.pixel_area(); - } - } - } - - // filter out very small connection areas, they brake the graph building - for (Island &island : result.islands) { - std::vector conns_to_remove; - for (const auto &conn : island.connected_islands) { - if (conn.second.area < params.connections_min_considerable_area) { conns_to_remove.push_back(conn.first); } - } - for (size_t conn : conns_to_remove) { island.connected_islands.erase(conn); } - } - - return {result, current_layer_grid}; -} +// returns triangle area, first_moment_of_area_xy, second_moment_of_area_xy, second_moment_of_area_covariance +// none of the values is divided/normalized by area. +// The function computes intgeral over the area of the triangle, with function f(x,y) = x for first moments of area (y is analogous) +// f(x,y) = x^2 for second moment of area +// and f(x,y) = x*y for second moment of area covariance +std::tuple compute_triangle_moments_of_area(const Vec2f &a, const Vec2f &b, const Vec2f &c) +{ + // based on the following guide: + // Denote the vertices of S by a, b, c. Then the map + // g:(u,v)↦a+u(b−a)+v(c−a) , + // which in coordinates appears as + // g:(u,v)↦{x(u,v)y(u,v)=a1+u(b1−a1)+v(c1−a1)=a2+u(b2−a2)+v(c2−a2) ,(1) + // obviously maps S′ bijectively onto S. Therefore the transformation formula for multiple integrals steps into action, and we obtain + // ∫Sf(x,y)d(x,y)=∫S′f(x(u,v),y(u,v))∣∣Jg(u,v)∣∣ d(u,v) . + // In the case at hand the Jacobian determinant is a constant: From (1) we obtain + // Jg(u,v)=det[xuyuxvyv]=(b1−a1)(c2−a2)−(c1−a1)(b2−a2) . + // Therefore we can write + // ∫Sf(x,y)d(x,y)=∣∣Jg∣∣∫10∫1−u0f~(u,v) dv du , + // where f~ denotes the pullback of f to S′: + // f~(u,v):=f(x(u,v),y(u,v)) . + // Don't forget taking the absolute value of Jg! + + float jacobian_determinant_abs = std::abs((b.x() - a.x()) * (c.y() - a.y()) - (c.x() - a.x()) * (b.y() - a.y())); + + // coordinate transform: gx(u,v) = a.x + u * (b.x - a.x) + v * (c.x - a.x) + // coordinate transform: gy(u,v) = a.y + u * (b.y - a.y) + v * (c.y - a.y) + // second moment of area for x: f(x, y) = x^2; + // f(gx(u,v), gy(u,v)) = gx(u,v)^2 = ... (long expanded form) + + // result is Int_T func = jacobian_determinant_abs * Int_0^1 Int_0^1-u func(gx(u,v), gy(u,v)) dv du + // integral_0^1 integral_0^(1 - u) (a + u (b - a) + v (c - a))^2 dv du = 1/12 (a^2 + a (b + c) + b^2 + b c + c^2) + + Vec2f second_moment_of_area_xy = jacobian_determinant_abs * + (a.cwiseProduct(a) + b.cwiseProduct(b) + b.cwiseProduct(c) + c.cwiseProduct(c) + + a.cwiseProduct(b + c)) / + 12.0f; + // second moment of area covariance : f(x, y) = x*y; + // f(gx(u,v), gy(u,v)) = gx(u,v)*gy(u,v) = ... (long expanded form) + //(a_1 + u * (b_1 - a_1) + v * (c_1 - a_1)) * (a_2 + u * (b_2 - a_2) + v * (c_2 - a_2)) + // == (a_1 + u (b_1 - a_1) + v (c_1 - a_1)) (a_2 + u (b_2 - a_2) + v (c_2 - a_2)) + + // intermediate result: integral_0^(1 - u) (a_1 + u (b_1 - a_1) + v (c_1 - a_1)) (a_2 + u (b_2 - a_2) + v (c_2 - a_2)) dv = + // 1/6 (u - 1) (-c_1 (u - 1) (a_2 (u - 1) - 3 b_2 u) - c_2 (u - 1) (a_1 (u - 1) - 3 b_1 u + 2 c_1 (u - 1)) + 3 b_1 u (a_2 (u - 1) - 2 + // b_2 u) + a_1 (u - 1) (3 b_2 u - 2 a_2 (u - 1))) result = integral_0^1 1/6 (u - 1) (-c_1 (u - 1) (a_2 (u - 1) - 3 b_2 u) - c_2 (u - + // 1) (a_1 (u - 1) - 3 b_1 u + 2 c_1 (u - 1)) + 3 b_1 u (a_2 (u - 1) - 2 b_2 u) + a_1 (u - 1) (3 b_2 u - 2 a_2 (u - 1))) du = + // 1/24 (a_2 (b_1 + c_1) + a_1 (2 a_2 + b_2 + c_2) + b_2 c_1 + b_1 c_2 + 2 b_1 b_2 + 2 c_1 c_2) + // result is Int_T func = jacobian_determinant_abs * Int_0^1 Int_0^1-u func(gx(u,v), gy(u,v)) dv du + float second_moment_of_area_covariance = jacobian_determinant_abs * (1.0f / 24.0f) * + (a.y() * (b.x() + c.x()) + a.x() * (2.0f * a.y() + b.y() + c.y()) + b.y() * c.x() + + b.x() * c.y() + 2.0f * b.x() * b.y() + 2.0f * c.x() * c.y()); + + float area = jacobian_determinant_abs * 0.5f; + + Vec2f first_moment_of_area_xy = jacobian_determinant_abs * (a + b + c) / 6.0f; + + return {area, first_moment_of_area_xy, second_moment_of_area_xy, second_moment_of_area_covariance}; +}; -struct CoordinateFunctor { - const std::vector *coordinates; - CoordinateFunctor(const std::vector *coords) : - coordinates(coords) { - } - CoordinateFunctor() : - coordinates(nullptr) { +SliceConnection estimate_slice_connection(size_t slice_idx, const Layer *layer) +{ + SliceConnection connection; + + const LayerSlice &slice = layer->lslices_ex[slice_idx]; + ExPolygon slice_poly = layer->lslices[slice_idx]; + const Layer *lower_layer = layer->lower_layer; + + ExPolygons below_polys{}; + for (const auto &link : slice.overlaps_below) { below_polys.push_back(lower_layer->lslices[link.slice_idx]); } + ExPolygons overlap = intersection_ex({slice_poly}, below_polys); + + std::vector triangles = triangulate_expolygons_2f(overlap); + for (size_t idx = 0; idx < triangles.size(); idx += 3) { + auto [area, first_moment_of_area, second_moment_area, + second_moment_of_area_covariance] = compute_triangle_moments_of_area(triangles[idx], triangles[idx + 1], triangles[idx + 2]); + connection.area += area; + connection.centroid_accumulator += Vec3f(first_moment_of_area.x(), first_moment_of_area.y(), layer->print_z * area); + connection.second_moment_of_area_accumulator += second_moment_area; + connection.second_moment_of_area_covariance_accumulator += second_moment_of_area_covariance; } - const float& operator()(size_t idx, size_t dim) const { - return coordinates->operator [](idx)[dim]; - } + return connection; }; -class ObjectPart { - float volume { }; +class ObjectPart +{ +public: + float volume{}; Vec3f volume_centroid_accumulator = Vec3f::Zero(); - float sticking_area { }; - Vec3f sticking_centroid_accumulator = Vec3f::Zero(); + float sticking_area{}; + Vec3f sticking_centroid_accumulator = Vec3f::Zero(); Vec2f sticking_second_moment_of_area_accumulator = Vec2f::Zero(); - float sticking_second_moment_of_area_covariance_accumulator { }; + float sticking_second_moment_of_area_covariance_accumulator{}; -public: ObjectPart() = default; - ObjectPart(const Island &island) { - this->volume = island.volume; - this->volume_centroid_accumulator = island.volume_centroid_accumulator; - this->sticking_area = island.sticking_area; - this->sticking_centroid_accumulator = island.sticking_centroid_accumulator; - this->sticking_second_moment_of_area_accumulator = island.sticking_second_moment_of_area_accumulator; - this->sticking_second_moment_of_area_covariance_accumulator = - island.sticking_second_moment_of_area_covariance_accumulator; - } - - float get_volume() const { - return volume; - } - - void add(const ObjectPart &other) { + void add(const ObjectPart &other) + { this->volume_centroid_accumulator += other.volume_centroid_accumulator; this->volume += other.volume; this->sticking_area += other.sticking_area; this->sticking_centroid_accumulator += other.sticking_centroid_accumulator; this->sticking_second_moment_of_area_accumulator += other.sticking_second_moment_of_area_accumulator; - this->sticking_second_moment_of_area_covariance_accumulator += - other.sticking_second_moment_of_area_covariance_accumulator; + this->sticking_second_moment_of_area_covariance_accumulator += other.sticking_second_moment_of_area_covariance_accumulator; } - void add_support_point(const Vec3f &position, float sticking_area) { + void add_support_point(const Vec3f &position, float sticking_area) + { this->sticking_area += sticking_area; this->sticking_centroid_accumulator += sticking_area * position; - this->sticking_second_moment_of_area_accumulator += sticking_area - * position.head<2>().cwiseProduct(position.head<2>()); - this->sticking_second_moment_of_area_covariance_accumulator += sticking_area - * position.x() * position.y(); + this->sticking_second_moment_of_area_accumulator += sticking_area * position.head<2>().cwiseProduct(position.head<2>()); + this->sticking_second_moment_of_area_covariance_accumulator += sticking_area * position.x() * position.y(); } - float compute_directional_xy_variance( - const Vec2f &line_dir, - const Vec3f ¢roid_accumulator, - const Vec2f &second_moment_of_area_accumulator, - const float &second_moment_of_area_covariance_accumulator, - const float &area) const { + float compute_directional_xy_variance(const Vec2f &line_dir, + const Vec3f ¢roid_accumulator, + const Vec2f &second_moment_of_area_accumulator, + const float &second_moment_of_area_covariance_accumulator, + const float &area) const + { assert(area > 0); - Vec3f centroid = centroid_accumulator / area; - Vec2f variance = (second_moment_of_area_accumulator / area - - centroid.head<2>().cwiseProduct(centroid.head<2>())); + Vec3f centroid = centroid_accumulator / area; + Vec2f variance = (second_moment_of_area_accumulator / area - centroid.head<2>().cwiseProduct(centroid.head<2>())); float covariance = second_moment_of_area_covariance_accumulator / area - centroid.x() * centroid.y(); // Var(aX+bY)=a^2*Var(X)+b^2*Var(Y)+2*a*b*Cov(X,Y) - float directional_xy_variance = line_dir.x() * line_dir.x() * variance.x() - + line_dir.y() * line_dir.y() * variance.y() + - 2.0f * line_dir.x() * line_dir.y() * covariance; + float directional_xy_variance = line_dir.x() * line_dir.x() * variance.x() + line_dir.y() * line_dir.y() * variance.y() + + 2.0f * line_dir.x() * line_dir.y() * covariance; #ifdef DETAILED_DEBUG_LOGS - BOOST_LOG_TRIVIAL(debug) - << "centroid: " << centroid.x() << " " << centroid.y() << " " << centroid.z(); - BOOST_LOG_TRIVIAL(debug) - << "variance: " << variance.x() << " " << variance.y(); - BOOST_LOG_TRIVIAL(debug) - << "covariance: " << covariance; - BOOST_LOG_TRIVIAL(debug) - << "directional_xy_variance: " << directional_xy_variance; + BOOST_LOG_TRIVIAL(debug) << "centroid: " << centroid.x() << " " << centroid.y() << " " << centroid.z(); + BOOST_LOG_TRIVIAL(debug) << "variance: " << variance.x() << " " << variance.y(); + BOOST_LOG_TRIVIAL(debug) << "covariance: " << covariance; + BOOST_LOG_TRIVIAL(debug) << "directional_xy_variance: " << directional_xy_variance; #endif return directional_xy_variance; } - float compute_elastic_section_modulus( - const Vec2f &line_dir, - const Vec3f &extreme_point, - const Vec3f ¢roid_accumulator, - const Vec2f &second_moment_of_area_accumulator, - const float &second_moment_of_area_covariance_accumulator, - const float &area) const { - - float directional_xy_variance = compute_directional_xy_variance( - line_dir, - centroid_accumulator, - second_moment_of_area_accumulator, - second_moment_of_area_covariance_accumulator, - area); - if (directional_xy_variance < EPSILON) { - return 0.0f; - } - Vec3f centroid = centroid_accumulator / area; - float extreme_fiber_dist = line_alg::distance_to( - Linef(centroid.head<2>().cast(), - (centroid.head<2>() + Vec2f(line_dir.y(), -line_dir.x())).cast()), - extreme_point.head<2>().cast()); + float compute_elastic_section_modulus(const Vec2f &line_dir, + const Vec3f &extreme_point, + const Vec3f ¢roid_accumulator, + const Vec2f &second_moment_of_area_accumulator, + const float &second_moment_of_area_covariance_accumulator, + const float &area) const + { + float directional_xy_variance = compute_directional_xy_variance(line_dir, centroid_accumulator, second_moment_of_area_accumulator, + second_moment_of_area_covariance_accumulator, area); + if (directional_xy_variance < EPSILON) { return 0.0f; } + Vec3f centroid = centroid_accumulator / area; + float extreme_fiber_dist = line_alg::distance_to(Linef(centroid.head<2>().cast(), + (centroid.head<2>() + Vec2f(line_dir.y(), -line_dir.x())).cast()), + extreme_point.head<2>().cast()); float elastic_section_modulus = area * directional_xy_variance / extreme_fiber_dist; #ifdef DETAILED_DEBUG_LOGS - BOOST_LOG_TRIVIAL(debug) - << "extreme_fiber_dist: " << extreme_fiber_dist; - BOOST_LOG_TRIVIAL(debug) - << "elastic_section_modulus: " << elastic_section_modulus; + BOOST_LOG_TRIVIAL(debug) << "extreme_fiber_dist: " << extreme_fiber_dist; + BOOST_LOG_TRIVIAL(debug) << "elastic_section_modulus: " << elastic_section_modulus; #endif return elastic_section_modulus; } - float is_stable_while_extruding( - const IslandConnection &connection, - const ExtrusionLine &extruded_line, - const Vec3f &extreme_point, - float layer_z, - const Params ¶ms) const { - - Vec2f line_dir = (extruded_line.b - extruded_line.a).normalized(); + float is_stable_while_extruding(const SliceConnection &connection, + const ExtrusionLine &extruded_line, + const Vec3f &extreme_point, + float layer_z, + const Params ¶ms) const + { + Vec2f line_dir = (extruded_line.b - extruded_line.a).normalized(); const Vec3f &mass_centroid = this->volume_centroid_accumulator / this->volume; - float mass = this->volume * params.filament_density; - float weight = mass * params.gravity_constant; + float mass = this->volume * params.filament_density; + float weight = mass * params.gravity_constant; float movement_force = params.max_acceleration * mass; float extruder_conflict_force = params.standard_extruder_conflict_force + - std::min(extruded_line.malformation, 1.0f) * params.malformations_additive_conflict_extruder_force; + std::min(extruded_line.malformation, 1.0f) * params.malformations_additive_conflict_extruder_force; // section for bed calculations { - if (this->sticking_area < EPSILON) - return 1.0f; - - Vec3f bed_centroid = this->sticking_centroid_accumulator / this->sticking_area; - float bed_yield_torque = -compute_elastic_section_modulus( - line_dir, - extreme_point, - this->sticking_centroid_accumulator, - this->sticking_second_moment_of_area_accumulator, - this->sticking_second_moment_of_area_covariance_accumulator, - this->sticking_area) - * params.get_bed_adhesion_yield_strength(); - - Vec2f bed_weight_arm = (mass_centroid.head<2>() - bed_centroid.head<2>()); - float bed_weight_arm_len = bed_weight_arm.norm(); - float bed_weight_dir_xy_variance = compute_directional_xy_variance(bed_weight_arm, - this->sticking_centroid_accumulator, - this->sticking_second_moment_of_area_accumulator, - this->sticking_second_moment_of_area_covariance_accumulator, - this->sticking_area); - float bed_weight_sign = bed_weight_arm_len < 2.0f * sqrt(bed_weight_dir_xy_variance) ? -1.0f : 1.0f; - float bed_weight_torque = bed_weight_sign * bed_weight_arm_len * weight; - - float bed_movement_arm = std::max(0.0f, mass_centroid.z() - bed_centroid.z()); + if (this->sticking_area < EPSILON) return 1.0f; + + Vec3f bed_centroid = this->sticking_centroid_accumulator / this->sticking_area; + float bed_yield_torque = -compute_elastic_section_modulus(line_dir, extreme_point, this->sticking_centroid_accumulator, + this->sticking_second_moment_of_area_accumulator, + this->sticking_second_moment_of_area_covariance_accumulator, + this->sticking_area) * + params.get_bed_adhesion_yield_strength(); + + Vec2f bed_weight_arm = (mass_centroid.head<2>() - bed_centroid.head<2>()); + float bed_weight_arm_len = bed_weight_arm.norm(); + float bed_weight_dir_xy_variance = compute_directional_xy_variance(bed_weight_arm, this->sticking_centroid_accumulator, + this->sticking_second_moment_of_area_accumulator, + this->sticking_second_moment_of_area_covariance_accumulator, + this->sticking_area); + float bed_weight_sign = bed_weight_arm_len < 2.0f * sqrt(bed_weight_dir_xy_variance) ? -1.0f : 1.0f; + float bed_weight_torque = bed_weight_sign * bed_weight_arm_len * weight; + + float bed_movement_arm = std::max(0.0f, mass_centroid.z() - bed_centroid.z()); float bed_movement_torque = movement_force * bed_movement_arm; - float bed_conflict_torque_arm = layer_z - bed_centroid.z(); + float bed_conflict_torque_arm = layer_z - bed_centroid.z(); float bed_extruder_conflict_torque = extruder_conflict_force * bed_conflict_torque_arm; - float bed_total_torque = bed_movement_torque + bed_extruder_conflict_torque + bed_weight_torque - + bed_yield_torque; + float bed_total_torque = bed_movement_torque + bed_extruder_conflict_torque + bed_weight_torque + bed_yield_torque; #ifdef DETAILED_DEBUG_LOGS - BOOST_LOG_TRIVIAL(debug) - << "bed_centroid: " << bed_centroid.x() << " " << bed_centroid.y() << " " << bed_centroid.z(); - BOOST_LOG_TRIVIAL(debug) - << "SSG: bed_yield_torque: " << bed_yield_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: bed_weight_arm: " << bed_weight_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: bed_weight_torque: " << bed_weight_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: bed_movement_arm: " << bed_movement_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: bed_movement_torque: " << bed_movement_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: bed_conflict_torque_arm: " << bed_conflict_torque_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: extruded_line.malformation: " << extruded_line.malformation; - BOOST_LOG_TRIVIAL(debug) - << "SSG: extruder_conflict_force: " << extruder_conflict_force; - BOOST_LOG_TRIVIAL(debug) - << "SSG: bed_extruder_conflict_torque: " << bed_extruder_conflict_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: total_torque: " << bed_total_torque << " layer_z: " << layer_z; + BOOST_LOG_TRIVIAL(debug) << "bed_centroid: " << bed_centroid.x() << " " << bed_centroid.y() << " " << bed_centroid.z(); + BOOST_LOG_TRIVIAL(debug) << "SSG: bed_yield_torque: " << bed_yield_torque; + BOOST_LOG_TRIVIAL(debug) << "SSG: bed_weight_arm: " << bed_weight_arm; + BOOST_LOG_TRIVIAL(debug) << "SSG: bed_weight_torque: " << bed_weight_torque; + BOOST_LOG_TRIVIAL(debug) << "SSG: bed_movement_arm: " << bed_movement_arm; + BOOST_LOG_TRIVIAL(debug) << "SSG: bed_movement_torque: " << bed_movement_torque; + BOOST_LOG_TRIVIAL(debug) << "SSG: bed_conflict_torque_arm: " << bed_conflict_torque_arm; + BOOST_LOG_TRIVIAL(debug) << "SSG: extruded_line.malformation: " << extruded_line.malformation; + BOOST_LOG_TRIVIAL(debug) << "SSG: extruder_conflict_force: " << extruder_conflict_force; + BOOST_LOG_TRIVIAL(debug) << "SSG: bed_extruder_conflict_torque: " << bed_extruder_conflict_torque; + BOOST_LOG_TRIVIAL(debug) << "SSG: total_torque: " << bed_total_torque << " layer_z: " << layer_z; #endif - if (bed_total_torque > 0) - return bed_total_torque / bed_conflict_torque_arm; + if (bed_total_torque > 0) return bed_total_torque / bed_conflict_torque_arm; } - //section for weak connection calculations + // section for weak connection calculations { - if (connection.area < EPSILON) - return 1.0f; + if (connection.area < EPSILON) return 1.0f; Vec3f conn_centroid = connection.centroid_accumulator / connection.area; - if (layer_z - conn_centroid.z() < 3.0f) { - return -1.0f; - } - float conn_yield_torque = compute_elastic_section_modulus( - line_dir, - extreme_point, - connection.centroid_accumulator, - connection.second_moment_of_area_accumulator, - connection.second_moment_of_area_covariance_accumulator, - connection.area) * params.material_yield_strength; - - float conn_weight_arm = (conn_centroid.head<2>() - mass_centroid.head<2>()).norm(); + if (layer_z - conn_centroid.z() < 3.0f) { return -1.0f; } + float conn_yield_torque = compute_elastic_section_modulus(line_dir, extreme_point, connection.centroid_accumulator, + connection.second_moment_of_area_accumulator, + connection.second_moment_of_area_covariance_accumulator, + connection.area) * + params.material_yield_strength; + + float conn_weight_arm = (conn_centroid.head<2>() - mass_centroid.head<2>()).norm(); float conn_weight_torque = conn_weight_arm * weight * (conn_centroid.z() / layer_z); - float conn_movement_arm = std::max(0.0f, mass_centroid.z() - conn_centroid.z()); + float conn_movement_arm = std::max(0.0f, mass_centroid.z() - conn_centroid.z()); float conn_movement_torque = movement_force * conn_movement_arm; - float conn_conflict_torque_arm = layer_z - conn_centroid.z(); + float conn_conflict_torque_arm = layer_z - conn_centroid.z(); float conn_extruder_conflict_torque = extruder_conflict_force * conn_conflict_torque_arm; - float conn_total_torque = conn_movement_torque + conn_extruder_conflict_torque + conn_weight_torque - - conn_yield_torque; + float conn_total_torque = conn_movement_torque + conn_extruder_conflict_torque + conn_weight_torque - conn_yield_torque; #ifdef DETAILED_DEBUG_LOGS - BOOST_LOG_TRIVIAL(debug) - << "bed_centroid: " << conn_centroid.x() << " " << conn_centroid.y() << " " << conn_centroid.z(); - BOOST_LOG_TRIVIAL(debug) - << "SSG: conn_yield_torque: " << conn_yield_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: conn_weight_arm: " << conn_weight_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: conn_weight_torque: " << conn_weight_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: conn_movement_arm: " << conn_movement_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: conn_movement_torque: " << conn_movement_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: conn_conflict_torque_arm: " << conn_conflict_torque_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: conn_extruder_conflict_torque: " << conn_extruder_conflict_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: total_torque: " << conn_total_torque << " layer_z: " << layer_z; + BOOST_LOG_TRIVIAL(debug) << "bed_centroid: " << conn_centroid.x() << " " << conn_centroid.y() << " " << conn_centroid.z(); + BOOST_LOG_TRIVIAL(debug) << "SSG: conn_yield_torque: " << conn_yield_torque; + BOOST_LOG_TRIVIAL(debug) << "SSG: conn_weight_arm: " << conn_weight_arm; + BOOST_LOG_TRIVIAL(debug) << "SSG: conn_weight_torque: " << conn_weight_torque; + BOOST_LOG_TRIVIAL(debug) << "SSG: conn_movement_arm: " << conn_movement_arm; + BOOST_LOG_TRIVIAL(debug) << "SSG: conn_movement_torque: " << conn_movement_torque; + BOOST_LOG_TRIVIAL(debug) << "SSG: conn_conflict_torque_arm: " << conn_conflict_torque_arm; + BOOST_LOG_TRIVIAL(debug) << "SSG: conn_extruder_conflict_torque: " << conn_extruder_conflict_torque; + BOOST_LOG_TRIVIAL(debug) << "SSG: total_torque: " << conn_total_torque << " layer_z: " << layer_z; #endif return conn_total_torque / conn_conflict_torque_arm; @@ -837,57 +572,102 @@ class ObjectPart { } }; -#ifdef DETAILED_DEBUG_LOGS -void debug_print_graph(const std::vector &islands_graph) { - std::cout << "BUILT ISLANDS GRAPH:" << std::endl; - for (size_t layer_idx = 0; layer_idx < islands_graph.size(); ++layer_idx) { - std::cout << "ISLANDS AT LAYER: " << layer_idx << " AT HEIGHT: " << islands_graph[layer_idx].layer_z - << std::endl; - for (size_t island_idx = 0; island_idx < islands_graph[layer_idx].islands.size(); ++island_idx) { - const Island &island = islands_graph[layer_idx].islands[island_idx]; - std::cout << " ISLAND " << island_idx << std::endl; - std::cout << " volume: " << island.volume << std::endl; - std::cout << " sticking_area: " << island.sticking_area << std::endl; - std::cout << " connected_islands count: " << island.connected_islands.size() << std::endl; - std::cout << " lines count: " << island.external_lines.size() << std::endl; +// return new object part and actual area covered by extrusions +std::tuple build_object_part_from_slice(const LayerSlice &slice, const Layer *layer) +{ + ObjectPart new_object_part; + float area_covered_by_extrusions = 0; + + auto add_extrusions_to_object = [&new_object_part, &area_covered_by_extrusions](const ExtrusionEntity *e, const LayerRegion *region) { + float flow_width = get_flow_width(region, e->role()); + const Layer *l = region->layer(); + float slice_z = l->slice_z; + float height = l->height; + std::vector lines = to_short_lines(e, 5.0); + for (const ExtrusionLine &line : lines) { + float volume = line.len * height * flow_width * PI / 4.0f; + area_covered_by_extrusions += line.len * flow_width; + new_object_part.volume += volume; + new_object_part.volume_centroid_accumulator += to_3d(Vec2f((line.a + line.b) / 2.0f), slice_z) * volume; + + if (l->id() == 0) { // first layer + float sticking_area = line.len * flow_width; + new_object_part.sticking_area += sticking_area; + Vec2f middle = Vec2f((line.a + line.b) / 2.0f); + new_object_part.sticking_centroid_accumulator += sticking_area * to_3d(middle, slice_z); + // Bottom infill lines can be quite long, and algined, so the middle approximaton used above does not work + Vec2f dir = (line.b - line.a).normalized(); + float segment_length = flow_width; // segments of size flow_width + for (float segment_middle_dist = std::min(line.len, segment_length * 0.5f); segment_middle_dist < line.len; + segment_middle_dist += segment_length) { + Vec2f segment_middle = line.a + segment_middle_dist * dir; + new_object_part.sticking_second_moment_of_area_accumulator += segment_length * flow_width * + segment_middle.cwiseProduct(segment_middle); + new_object_part.sticking_second_moment_of_area_covariance_accumulator += segment_length * flow_width * + segment_middle.x() * segment_middle.y(); + } + } + } + }; + + for (const auto &island : slice.islands) { + const LayerRegion *perimeter_region = layer->get_region(island.perimeters.region()); + for (const auto &perimeter_idx : island.perimeters) { + for (const ExtrusionEntity *perimeter : + static_cast(perimeter_region->perimeters().entities[perimeter_idx])->entities) { + add_extrusions_to_object(perimeter, perimeter_region); + } + } + for (const LayerExtrusionRange &fill_range : island.fills) { + const LayerRegion *fill_region = layer->get_region(fill_range.region()); + for (const auto &fill_idx : fill_range) { + for (const ExtrusionEntity *fill : + static_cast(fill_region->fills().entities[fill_idx])->entities) { + add_extrusions_to_object(fill, fill_region); + } + } + } + const LayerRegion *thin_fill_region = layer->get_region(island.fill_region_id); + for (const auto &thin_fill_idx : island.thin_fills) { + add_extrusions_to_object(thin_fill_region->thin_fills().entities[thin_fill_idx], perimeter_region); } } - std::cout << "END OF GRAPH" << std::endl; + + return {new_object_part, area_covered_by_extrusions}; } -#endif -class ActiveObjectParts { - size_t next_part_idx = 0; +class ActiveObjectParts +{ + size_t next_part_idx = 0; std::unordered_map active_object_parts; - std::unordered_map active_object_parts_id_mapping; + std::unordered_map active_object_parts_id_mapping; public: - size_t get_flat_id(size_t id) { + size_t get_flat_id(size_t id) + { size_t index = active_object_parts_id_mapping.at(id); - while (index != active_object_parts_id_mapping.at(index)) { - index = active_object_parts_id_mapping.at(index); - } + while (index != active_object_parts_id_mapping.at(index)) { index = active_object_parts_id_mapping.at(index); } size_t i = id; while (index != active_object_parts_id_mapping.at(i)) { - size_t next = active_object_parts_id_mapping[i]; + size_t next = active_object_parts_id_mapping[i]; active_object_parts_id_mapping[i] = index; - i = next; + i = next; } return index; } - ObjectPart& access(size_t id) { - return this->active_object_parts.at(this->get_flat_id(id)); - } + ObjectPart &access(size_t id) { return this->active_object_parts.at(this->get_flat_id(id)); } - size_t insert(const Island &island) { - this->active_object_parts.emplace(next_part_idx, ObjectPart(island)); + size_t insert(const ObjectPart &new_part) + { + this->active_object_parts.emplace(next_part_idx, new_part); this->active_object_parts_id_mapping.emplace(next_part_idx, next_part_idx); return next_part_idx++; } - void merge(size_t from, size_t to) { - size_t to_flat = this->get_flat_id(to); + void merge(size_t from, size_t to) + { + size_t to_flat = this->get_flat_id(to); size_t from_flat = this->get_flat_id(from); active_object_parts.at(to_flat).add(active_object_parts.at(from_flat)); active_object_parts.erase(from_flat); @@ -895,282 +675,199 @@ class ActiveObjectParts { } }; -Issues check_global_stability(SupportGridFilter supports_presence_grid, - const std::vector &islands_graph, const Params ¶ms) { -#ifdef DETAILED_DEBUG_LOGS - debug_print_graph(islands_graph); -#endif - - Issues issues { }; - ActiveObjectParts active_object_parts { }; - std::unordered_map prev_island_to_object_part_mapping; - std::unordered_map next_island_to_object_part_mapping; - - std::unordered_map prev_island_weakest_connection; - std::unordered_map next_island_weakest_connection; - - for (size_t layer_idx = 0; layer_idx < islands_graph.size(); ++layer_idx) { - float layer_z = islands_graph[layer_idx].layer_z; +Issues check_stability(const PrintObject *po, const Params ¶ms) +{ + Issues issues{}; + SupportGridFilter supports_presence_grid(po, params.min_distance_between_support_points); + ActiveObjectParts active_object_parts{}; + LD prev_layer_ext_perim_lines({}); + + std::unordered_map prev_slice_idx_to_object_part_mapping; + std::unordered_map next_slice_idx_to_object_part_mapping; + std::unordered_map prev_slice_idx_to_weakest_connection; + std::unordered_map next_slice_idx_to_weakest_connection; + + for (size_t layer_idx = 0; layer_idx < po->layer_count(); ++layer_idx) { + const Layer *layer = po->get_layer(layer_idx); + float bottom_z = layer->bottom_z(); + auto create_support_point_position = [bottom_z](const Vec2f &layer_pos) { return Vec3f{layer_pos.x(), layer_pos.y(), bottom_z}; }; + + for (size_t slice_idx = 0; slice_idx < layer->lslices_ex.size(); ++slice_idx) { + const LayerSlice &slice = layer->lslices_ex.at(slice_idx); + auto [new_part, covered_area] = build_object_part_from_slice(slice, layer); + SliceConnection connection_to_below = estimate_slice_connection(slice_idx, layer); #ifdef DETAILED_DEBUG_LOGS - for (const auto &m : prev_island_to_object_part_mapping) { - std::cout << "island " << m.first << " maps to part " << m.second << std::endl; - prev_island_weakest_connection[m.first].print_info("connection info:"); - } + std::cout << "SLICE IDX: " << slice_idx << std::endl; + for (const auto &link : slice.overlaps_below) { + std::cout << "connected to slice below: " << link.slice_idx << " by area : " << link.area << std::endl; + } + connection_to_below.print_info("CONNECTION TO BELOW"); #endif - for (size_t island_idx = 0; island_idx < islands_graph[layer_idx].islands.size(); ++island_idx) { - const Island &island = islands_graph[layer_idx].islands[island_idx]; - if (island.connected_islands.empty()) { //new object part emerging - size_t part_id = active_object_parts.insert(island); - next_island_to_object_part_mapping.emplace(island_idx, part_id); - next_island_weakest_connection.emplace(island_idx, - IslandConnection { 1.0f, Vec3f::Zero(), Vec2f { INFINITY, INFINITY } }); + if (connection_to_below.area < EPSILON) { // new object part emerging + size_t part_id = active_object_parts.insert(new_part); + next_slice_idx_to_object_part_mapping.emplace(slice_idx, part_id); + next_slice_idx_to_weakest_connection.emplace(slice_idx, connection_to_below); } else { - size_t final_part_id { }; - IslandConnection transfered_weakest_connection { }; - IslandConnection new_weakest_connection { }; + size_t final_part_id{}; + SliceConnection transfered_weakest_connection{}; // MERGE parts { std::unordered_set parts_ids; - for (const auto &connection : island.connected_islands) { - size_t part_id = active_object_parts.get_flat_id( - prev_island_to_object_part_mapping.at(connection.first)); + for (const auto &link : slice.overlaps_below) { + size_t part_id = active_object_parts.get_flat_id(prev_slice_idx_to_object_part_mapping.at(link.slice_idx)); parts_ids.insert(part_id); - transfered_weakest_connection.add(prev_island_weakest_connection.at(connection.first)); - new_weakest_connection.add(connection.second); + transfered_weakest_connection.add(prev_slice_idx_to_weakest_connection.at(link.slice_idx)); } + final_part_id = *parts_ids.begin(); for (size_t part_id : parts_ids) { - if (final_part_id != part_id) { - active_object_parts.merge(part_id, final_part_id); - } + if (final_part_id != part_id) { active_object_parts.merge(part_id, final_part_id); } } } - auto estimate_conn_strength = [layer_z](const IslandConnection &conn) { - Vec3f centroid = conn.centroid_accumulator / conn.area; - Vec2f variance = (conn.second_moment_of_area_accumulator / conn.area - - centroid.head<2>().cwiseProduct(centroid.head<2>())); - float xy_variance = variance.x() + variance.y(); - float arm_len_estimate = std::max(1.0f, layer_z - (conn.centroid_accumulator.z() / conn.area)); + auto estimate_conn_strength = [bottom_z](const SliceConnection &conn) { + if (conn.area < EPSILON) { // connection is empty, does not exists. Return max strength so that it is not picked as the + // weakest connection. + return INFINITY; + } + Vec3f centroid = conn.centroid_accumulator / conn.area; + Vec2f variance = (conn.second_moment_of_area_accumulator / conn.area - + centroid.head<2>().cwiseProduct(centroid.head<2>())); + float xy_variance = variance.x() + variance.y(); + float arm_len_estimate = std::max(1.0f, bottom_z - (conn.centroid_accumulator.z() / conn.area)); return conn.area * sqrt(xy_variance) / arm_len_estimate; }; #ifdef DETAILED_DEBUG_LOGS - new_weakest_connection.print_info("new_weakest_connection"); + connection_to_below.print_info("new_weakest_connection"); transfered_weakest_connection.print_info("transfered_weakest_connection"); #endif - if (estimate_conn_strength(transfered_weakest_connection) - > estimate_conn_strength(new_weakest_connection)) { - transfered_weakest_connection = new_weakest_connection; + if (estimate_conn_strength(transfered_weakest_connection) > estimate_conn_strength(connection_to_below)) { + transfered_weakest_connection = connection_to_below; } - next_island_weakest_connection.emplace(island_idx, transfered_weakest_connection); - next_island_to_object_part_mapping.emplace(island_idx, final_part_id); + next_slice_idx_to_weakest_connection.emplace(slice_idx, transfered_weakest_connection); + next_slice_idx_to_object_part_mapping.emplace(slice_idx, final_part_id); ObjectPart &part = active_object_parts.access(final_part_id); - part.add(ObjectPart(island)); + part.add(new_part); } } - prev_island_to_object_part_mapping = next_island_to_object_part_mapping; - next_island_to_object_part_mapping.clear(); - prev_island_weakest_connection = next_island_weakest_connection; - next_island_weakest_connection.clear(); - - // All object parts updated, inactive parts removed and weakest point of each island updated as well. - // Now compute the stability of each active object part, adding supports where necessary, and also - // check each island whether the weakest point is strong enough. If not, add supports as well. - - for (size_t island_idx = 0; island_idx < islands_graph[layer_idx].islands.size(); ++island_idx) { - const Island &island = islands_graph[layer_idx].islands[island_idx]; - ObjectPart &part = active_object_parts.access(prev_island_to_object_part_mapping[island_idx]); - - IslandConnection &weakest_conn = prev_island_weakest_connection[island_idx]; + prev_slice_idx_to_object_part_mapping = next_slice_idx_to_object_part_mapping; + next_slice_idx_to_object_part_mapping.clear(); + prev_slice_idx_to_weakest_connection = next_slice_idx_to_weakest_connection; + next_slice_idx_to_weakest_connection.clear(); + + std::vector current_layer_ext_perims_lines{}; + current_layer_ext_perims_lines.reserve(prev_layer_ext_perim_lines.get_lines().size()); + // All object parts updated, and for each slice we have coresponding weakest connection. + // We can now check each slice and its corresponding weakest connection and object part for stability. + for (size_t slice_idx = 0; slice_idx < layer->lslices_ex.size(); ++slice_idx) { + const LayerSlice &slice = layer->lslices_ex.at(slice_idx); + ObjectPart &part = active_object_parts.access(prev_slice_idx_to_object_part_mapping[slice_idx]); + SliceConnection &weakest_conn = prev_slice_idx_to_weakest_connection[slice_idx]; + std::vector current_slice_ext_perims_lines{}; + current_slice_ext_perims_lines.reserve(prev_layer_ext_perim_lines.get_lines().size() / layer->lslices_ex.size()); #ifdef DETAILED_DEBUG_LOGS weakest_conn.print_info("weakest connection info: "); #endif - LD island_lines_dist(island.external_lines); - float unchecked_dist = params.min_distance_between_support_points + 1.0f; - - for (const ExtrusionLine &line : island.external_lines) { - if ((unchecked_dist + line.len < params.min_distance_between_support_points - && line.malformation < 0.3f) || line.len == 0) { - unchecked_dist += line.len; - } else { - unchecked_dist = line.len; - Vec3f pivot_site_search_point = to_3d(Vec2f(line.b + (line.b - line.a).normalized() * 300.0f), - layer_z); - auto [dist, nidx, target_point] = island_lines_dist.signed_distance_from_lines_extra(pivot_site_search_point.head<2>()); - Vec3f support_point = to_3d(target_point, layer_z); - auto force = part.is_stable_while_extruding(weakest_conn, line, support_point, layer_z, params); - if (force > 0) { - if (!supports_presence_grid.position_taken(support_point)) { - float orig_area = params.support_points_interface_radius * params.support_points_interface_radius * float(PI); - // artifically lower the area for materials that have strong bed adhesion, as this adhesion does not apply for support points - float altered_area = orig_area * params.get_support_spots_adhesion_strength() / params.get_bed_adhesion_yield_strength(); - part.add_support_point(support_point, altered_area); - - float radius = part.get_volume() < params.small_parts_threshold ? params.small_parts_support_points_interface_radius : params.support_points_interface_radius; - issues.support_points.emplace_back(support_point, force, radius, to_3d(Vec2f(line.b - line.a).normalized(), 0.0f)); - supports_presence_grid.take_position(support_point); - - weakest_conn.area += altered_area; - weakest_conn.centroid_accumulator += support_point * altered_area; - weakest_conn.second_moment_of_area_accumulator += altered_area * - support_point.head<2>().cwiseProduct(support_point.head<2>()); - weakest_conn.second_moment_of_area_covariance_accumulator += altered_area * support_point.x() * - support_point.y(); + // Function that is used when new support point is generated. It will update the ObjectPart stability, weakest conneciton info, + // and the support presence grid and add the point to the issues. + auto reckon_new_support_point = [&part, &weakest_conn, &issues, &supports_presence_grid, ¶ms, + &layer_idx](const Vec3f &support_point, float force, const Vec2f &dir) { + if (supports_presence_grid.position_taken(support_point) || layer_idx <= 1) { return; } + float area = params.support_points_interface_radius * params.support_points_interface_radius * float(PI); + part.add_support_point(support_point, area); + + float radius = params.support_points_interface_radius; + issues.support_points.emplace_back(support_point, force, radius, dir); + supports_presence_grid.take_position(support_point); + + if (weakest_conn.area > EPSILON) { // Do not add it to the weakest connection if it is not valid - does not exist + weakest_conn.area += area; + weakest_conn.centroid_accumulator += support_point * area; + weakest_conn.second_moment_of_area_accumulator += area * support_point.head<2>().cwiseProduct(support_point.head<2>()); + weakest_conn.second_moment_of_area_covariance_accumulator += area * support_point.x() * support_point.y(); + } + }; + + // first we will check local extrusion stability of bridges, then of perimeters. Perimeters are more important, they + // account for most of the curling and possible crashes, so on them we will run also global stability check + for (const auto &island : slice.islands) { + // Support bridges where needed. + for (const LayerExtrusionRange &fill_range : island.fills) { + const LayerRegion *fill_region = layer->get_region(fill_range.region()); + for (const auto &fill_idx : fill_range) { + const ExtrusionEntity *entity = fill_region->fills().entities[fill_idx]; + if (entity->role() == erBridgeInfill) { + for (const ExtrusionLine &bridge : + check_extrusion_entity_stability(entity, fill_region, prev_layer_ext_perim_lines, params)) { + if (bridge.support_point_generated) { + reckon_new_support_point(create_support_point_position(bridge.b), -EPSILON, Vec2f::Zero()); + } + } } } } - } - } - //end of iteration over layer - } - return issues; -} -std::tuple> check_extrusions_and_build_graph(const PrintObject *po, - const Params ¶ms) { -#ifdef DEBUG_FILES - FILE *segmentation_f = boost::nowide::fopen(debug_out_path("segmentation.obj").c_str(), "w"); - FILE *malform_f = boost::nowide::fopen(debug_out_path("malformations.obj").c_str(), "w"); -#endif - - Issues issues { }; - Malformations malformations{}; - std::vector islands_graph; - std::vector layer_lines; - float flow_width = get_flow_width(po->layers()[po->layer_count() - 1]->regions()[0], erExternalPerimeter); - PixelGrid prev_layer_grid(po, flow_width*2.0f); - -// PREPARE BASE LAYER - const Layer *layer = po->layers()[0]; - malformations.layers.push_back({}); // no malformations to be expected at first layer - for (const LayerRegion *layer_region : layer->regions()) { - for (const ExtrusionEntity *ex_entity : layer_region->perimeters()) { - for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { - push_lines(perimeter, layer_lines); - } // perimeter - } // ex_entity - for (const ExtrusionEntity *ex_entity : layer_region->fills()) { - for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { - push_lines(fill, layer_lines); - } // fill - } // ex_entity - } // region - - auto [layer_islands, layer_grid] = reckon_islands(layer, true, 0, prev_layer_grid, - layer_lines, params); - islands_graph.push_back(std::move(layer_islands)); -#ifdef DEBUG_FILES - for (size_t x = 0; x < size_t(layer_grid.get_pixel_count().x()); ++x) { - for (size_t y = 0; y < size_t(layer_grid.get_pixel_count().y()); ++y) { - Vec2i coords = Vec2i(x, y); - size_t island_idx = layer_grid.get_pixel(coords); - if (layer_grid.get_pixel(coords) != NULL_ISLAND) { - Vec2f pos = layer_grid.get_pixel_center(coords); - size_t pseudornd = ((island_idx + 127) * 33331 + 6907) % 23; - Vec3f color = value_to_rgbf(0.0f, float(23), float(pseudornd)); - fprintf(segmentation_f, "v %f %f %f %f %f %f\n", pos[0], - pos[1], layer->slice_z, color[0], color[1], color[2]); - } - } - } - for (const auto &line : layer_lines) { - if (line.malformation > 0.0f) { - Vec3f color = value_to_rgbf(-EPSILON, 1.0f, line.malformation); - fprintf(malform_f, "v %f %f %f %f %f %f\n", line.b[0], - line.b[1], layer->slice_z, color[0], color[1], color[2]); - } - } -#endif - LD external_lines(layer_lines); - layer_lines.clear(); - prev_layer_grid = layer_grid; - - for (size_t layer_idx = 1; layer_idx < po->layer_count(); ++layer_idx) { - const Layer *layer = po->layers()[layer_idx]; - for (const LayerRegion *layer_region : layer->regions()) { - for (const ExtrusionEntity *ex_entity : layer_region->perimeters()) { - for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { - check_extrusion_entity_stability(perimeter, layer_lines, layer->slice_z, layer_region, - external_lines, issues, params); - } // perimeter - } // ex_entity - for (const ExtrusionEntity *ex_entity : layer_region->fills()) { - for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { - if (fill->role() == ExtrusionRole::erGapFill - || fill->role() == ExtrusionRole::erBridgeInfill) { - check_extrusion_entity_stability(fill, layer_lines, layer->slice_z, layer_region, - external_lines, issues, params); - } else { - push_lines(fill, layer_lines); + const LayerRegion *perimeter_region = layer->get_region(island.perimeters.region()); + for (const auto &perimeter_idx : island.perimeters) { + const ExtrusionEntity *entity = perimeter_region->perimeters().entities[perimeter_idx]; + std::vector perims = check_extrusion_entity_stability(entity, perimeter_region, + prev_layer_ext_perim_lines, params); + for (const ExtrusionLine &perim : perims) { + if (perim.support_point_generated) { + reckon_new_support_point(create_support_point_position(perim.b), -EPSILON, Vec2f::Zero()); + } + if (perim.is_external_perimeter()) { current_slice_ext_perims_lines.push_back(perim); } } - } // fill - } // ex_entity - } // region - - auto [layer_islands, layer_grid] = reckon_islands(layer, false, 0, prev_layer_grid, - layer_lines, params); - islands_graph.push_back(std::move(layer_islands)); - - Lines malformed_lines{}; - for (const auto &line : layer_lines) { - if (line.malformation > 0.3f) { malformed_lines.push_back(Line{Point::new_scale(line.a), Point::new_scale(line.b)}); } - } - malformations.layers.push_back(malformed_lines); - -#ifdef DEBUG_FILES - for (size_t x = 0; x < size_t(layer_grid.get_pixel_count().x()); ++x) { - for (size_t y = 0; y < size_t(layer_grid.get_pixel_count().y()); ++y) { - Vec2i coords = Vec2i(x, y); - size_t island_idx = layer_grid.get_pixel(coords); - if (layer_grid.get_pixel(coords) != NULL_ISLAND) { - Vec2f pos = layer_grid.get_pixel_center(coords); - size_t pseudornd = ((island_idx + 127) * 33331 + 6907) % 23; - Vec3f color = value_to_rgbf(0.0f, float(23), float(pseudornd)); - fprintf(segmentation_f, "v %f %f %f %f %f %f\n", pos[0], - pos[1], layer->slice_z, color[0], color[1], color[2]); } } - } - for (const auto &line : layer_lines) { - if (line.malformation > 0.0f) { - Vec3f color = value_to_rgbf(-EPSILON, layer->height*params.max_malformation_factor, line.malformation); - fprintf(malform_f, "v %f %f %f %f %f %f\n", line.b[0], - line.b[1], layer->slice_z, color[0], color[1], color[2]); - } - } -#endif - external_lines = LD(layer_lines); - layer_lines.clear(); - prev_layer_grid = layer_grid; - } -#ifdef DEBUG_FILES - fclose(segmentation_f); - fclose(malform_f); -#endif + LD current_slice_lines_distancer(current_slice_ext_perims_lines); + float unchecked_dist = params.min_distance_between_support_points + 1.0f; - return {issues, malformations, islands_graph}; + for (const ExtrusionLine &line : current_slice_ext_perims_lines) { + if ((unchecked_dist + line.len < params.min_distance_between_support_points && line.malformation < 0.3f) || line.len == 0) { + unchecked_dist += line.len; + } else { + unchecked_dist = line.len; + Vec2f pivot_site_search_point = Vec2f(line.b + (line.b - line.a).normalized() * 300.0f); + auto [dist, nidx, + nearest_point] = current_slice_lines_distancer.signed_distance_from_lines_extra(pivot_site_search_point); + Vec3f support_point = create_support_point_position(nearest_point); + auto force = part.is_stable_while_extruding(weakest_conn, line, support_point, bottom_z, params); + if (force > 0) { reckon_new_support_point(support_point, force, (line.b - line.a).normalized()); } + } + } + current_layer_ext_perims_lines.insert(current_layer_ext_perims_lines.end(), current_slice_ext_perims_lines.begin(), + current_slice_ext_perims_lines.end()); + } // slice iterations + prev_layer_ext_perim_lines = LD(current_layer_ext_perims_lines); + } // layer iterations + return issues; } #ifdef DEBUG_FILES -void debug_export(Issues issues, std::string file_name) { +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"; + BOOST_LOG_TRIVIAL(error) << "Debug files: Couldn't open " << file_name << " for writing"; return; } for (size_t i = 0; i < issues.support_points.size(); ++i) { - fprintf(fp, "v %f %f %f %f %f %f\n", issues.support_points[i].position(0), - issues.support_points[i].position(1), - issues.support_points[i].position(2), 1.0, 0.0, 1.0); + if (issues.support_points[i].force <= 0) { + fprintf(fp, "v %f %f %f %f %f %f\n", issues.support_points[i].position(0), issues.support_points[i].position(1), + issues.support_points[i].position(2), 0.0, 1.0, 0.0); + } else { + fprintf(fp, "v %f %f %f %f %f %f\n", issues.support_points[i].position(0), issues.support_points[i].position(1), + issues.support_points[i].position(2), 1.0, 0.0, 0.0); + } } fclose(fp); @@ -1181,21 +878,17 @@ void debug_export(Issues issues, std::string file_name) { // std::vector quick_search(const PrintObject *po, const Params ¶ms) { // return {}; // } - -std::tuple full_search(const PrintObject *po, const Params ¶ms) { - auto [local_issues, malformations, graph] = check_extrusions_and_build_graph(po, params); - Issues global_issues = check_global_stability( { po, params.min_distance_between_support_points }, graph, params); +Issues full_search(const PrintObject *po, const Params ¶ms) +{ + Issues issues = check_stability(po, params); #ifdef DEBUG_FILES - debug_export(local_issues, "local_issues"); - debug_export(global_issues, "global_issues"); + debug_export(issues, "issues"); #endif - global_issues.support_points.insert(global_issues.support_points.end(), - local_issues.support_points.begin(), local_issues.support_points.end()); - - return {global_issues, malformations}; + return issues; } + struct LayerCurlingEstimator { LD prev_layer_lines = LD({}); diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index 6a549bd36f1..b0cbab57dc8 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -32,8 +32,7 @@ struct Params { const float min_distance_between_support_points = 3.0f; //mm const float support_points_interface_radius = 1.5f; // mm const float connections_min_considerable_area = 1.5f; //mm^2 - const float small_parts_threshold = 5.0f; //mm^3 - const float small_parts_support_points_interface_radius = 3.0f; // mm + const float min_distance_to_allow_local_supports = 2.0f; //mm std::string filament_type; const float gravity_constant = 9806.65f; // mm/s^2; gravity acceleration on Earth's surface, algorithm assumes that printer is in upwards position. @@ -61,11 +60,11 @@ struct Params { }; struct SupportPoint { - SupportPoint(const Vec3f &position, float force, float spot_radius, const Vec3f &direction); + SupportPoint(const Vec3f &position, float force, float spot_radius, const Vec2f &direction); Vec3f position; float force; float spot_radius; - Vec3f direction; + Vec2f direction; }; struct Issues { @@ -77,7 +76,7 @@ struct Malformations { }; // std::vector quick_search(const PrintObject *po, const Params ¶ms); -std::tuple full_search(const PrintObject *po, const Params ¶ms); +Issues full_search(const PrintObject *po, const Params ¶ms); void estimate_supports_malformations(SupportLayerPtrs &layers, float supports_flow_width, const Params ¶ms); void estimate_malformations(LayerPtrs &layers, const Params ¶ms);