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