From e8f740dabbfaac8fe1e1b2b2d52932ae1702f041 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 10 Feb 2022 17:34:36 +0100 Subject: [PATCH] implemented overhang calculation and alignemnt iterations for seams now only external perimeters are considered which reduced time complexity --- src/libslic3r/GCode/SeamPlacerNG.cpp | 502 ++++++++++++++++++--------- src/libslic3r/GCode/SeamPlacerNG.hpp | 23 +- 2 files changed, 362 insertions(+), 163 deletions(-) diff --git a/src/libslic3r/GCode/SeamPlacerNG.cpp b/src/libslic3r/GCode/SeamPlacerNG.cpp index ecb19acc2..23ce7f81b 100644 --- a/src/libslic3r/GCode/SeamPlacerNG.cpp +++ b/src/libslic3r/GCode/SeamPlacerNG.cpp @@ -100,17 +100,17 @@ std::vector raycast_visibility(size_t ray_count, }; BOOST_LOG_TRIVIAL(debug) - << "PM: generate random samples: start"; + << "SeamPlacer: generate random samples: start"; std::vector global_dir_random_samples(ray_count); generate(begin(global_dir_random_samples), end(global_dir_random_samples), gen); std::vector local_dir_random_samples(ray_count); generate(begin(local_dir_random_samples), end(local_dir_random_samples), gen); BOOST_LOG_TRIVIAL(debug) - << "PM: generate random samples: end"; + << "SeamPlacer: generate random samples: end"; BOOST_LOG_TRIVIAL(debug) - << "PM: raycast visibility for " << ray_count << " rays: start"; + << "SeamPlacer: raycast visibility for " << ray_count << " rays: start"; // raycast visibility std::vector hit_points = tbb::parallel_reduce(tbb::blocked_range(0, ray_count), std::vector { }, @@ -151,24 +151,7 @@ std::vector raycast_visibility(size_t ray_count, ); BOOST_LOG_TRIVIAL(debug) - << "PM: raycast visibility for " << ray_count << " rays: end"; - -//TODO disable, only debug code -#ifdef DEBUG_FILES - its_write_obj(triangles, "triangles.obj"); - - Slic3r::CNumericLocalesSetter locales_setter; - FILE *fp = boost::nowide::fopen("hits.obj", "w"); - if (fp == nullptr) { - BOOST_LOG_TRIVIAL(error) - << "Couldn't open " << "hits.obj" << " for writing"; - } - - for (size_t i = 0; i < hit_points.size(); ++i) - fprintf(fp, "v %f %f %f \n", hit_points[i].m_position[0], hit_points[i].m_position[1], - hit_points[i].m_position[2]); - fclose(fp); -#endif + << "SeamPlacer: raycast visibility for " << ray_count << " rays: end"; return hit_points; } @@ -230,18 +213,59 @@ std::vector calculate_polygon_angles_at_vertices(const Polygon &polygon, return angles; } -template -void process_perimeter_polygon(const Polygon &polygon, coordf_t z_coord, std::vector &result_vec, - const EnforcerDistance &enforcer_distance_check, const BlockerDistance &blocker_distance_check) { +struct GlobalModelInfo { + std::vector geometry_raycast_hits; + KDTreeIndirect<3, coordf_t, HitInfoCoordinateFunctor> raycast_hits_tree; + indexed_triangle_set enforcers; + indexed_triangle_set blockers; + AABBTreeIndirect::Tree<3, float> enforcers_tree; + AABBTreeIndirect::Tree<3, float> blockers_tree; + + GlobalModelInfo() : + raycast_hits_tree(HitInfoCoordinateFunctor { &geometry_raycast_hits }) { + } + + double enforcer_distance_check(const Vec3d &position) const { + size_t hit_idx_out; + Vec3d closest_vec3d; + return AABBTreeIndirect::squared_distance_to_indexed_triangle_set(enforcers.vertices, enforcers.indices, + enforcers_tree, position, hit_idx_out, closest_vec3d); + } + + double blocker_distance_check(const Vec3d &position) const { + size_t hit_idx_out; + Vec3d closest_vec3d; + return AABBTreeIndirect::squared_distance_to_indexed_triangle_set(blockers.vertices, blockers.indices, + blockers_tree, position, hit_idx_out, closest_vec3d); + } + + double calculate_point_visibility(const Vec3d &position, double max_distance) const { + auto nearby_points = find_nearby_points(raycast_hits_tree, position, max_distance); + double visibility = 0; + for (const auto &hit_point_index : nearby_points) { + double distance = + (position - geometry_raycast_hits[hit_point_index].m_position).norm(); + visibility += max_distance - distance; // The further away from the perimeter point, + // the less representative ray hit is + } + return visibility; + + } +} +; + +void process_perimeter_polygon(const Polygon &orig_polygon, coordf_t z_coord, std::vector &result_vec, + const GlobalModelInfo &global_model_info) { + Polygon polygon = orig_polygon; + polygon.make_counter_clockwise(); std::vector lengths = polygon.parameter_by_length(); std::vector angles = calculate_polygon_angles_at_vertices(polygon, lengths, SeamPlacer::polygon_angles_arm_distance); - bool is_ccw = polygon.is_counter_clockwise(); Vec3d last_enforcer_checked_point { 0, 0, -1 }; - double enforcer_dist_sqr = enforcer_distance_check(last_enforcer_checked_point); + double enforcer_dist_sqr = global_model_info.enforcer_distance_check(last_enforcer_checked_point); Vec3d last_blocker_checked_point { 0, 0, -1 }; - double blocker_dist_sqr = blocker_distance_check(last_blocker_checked_point); + double blocker_dist_sqr = global_model_info.blocker_distance_check(last_blocker_checked_point); for (size_t index = 0; index < polygon.size(); ++index) { Vec2d unscaled_p = unscale(polygon[index]); @@ -249,9 +273,6 @@ void process_perimeter_polygon(const Polygon &polygon, coordf_t z_coord, std::ve EnforcedBlockedSeamPoint type = EnforcedBlockedSeamPoint::NONE; float ccw_angle = angles[index]; - if (!is_ccw) { - ccw_angle = -ccw_angle; - } if (enforcer_dist_sqr >= 0) { // if enforcer dist < 0, it means there are no enforcers, skip //if there is enforcer, any other enforcer cannot be in a sphere defined by last check point and enforcer distance @@ -262,7 +283,7 @@ void process_perimeter_polygon(const Polygon &polygon, coordf_t z_coord, std::ve (last_enforcer_checked_point - unscaled_position).squaredNorm() >= enforcer_dist_sqr - 2 * SeamPlacer::enforcer_blocker_sqr_distance_tolerance) { //do check - enforcer_dist_sqr = enforcer_distance_check(unscaled_position); + enforcer_dist_sqr = global_model_info.enforcer_distance_check(unscaled_position); last_enforcer_checked_point = unscaled_position; if (enforcer_dist_sqr < SeamPlacer::enforcer_blocker_sqr_distance_tolerance) { type = EnforcedBlockedSeamPoint::ENFORCED; @@ -275,7 +296,7 @@ void process_perimeter_polygon(const Polygon &polygon, coordf_t z_coord, std::ve || (last_blocker_checked_point - unscaled_position).squaredNorm() >= blocker_dist_sqr - 2 * SeamPlacer::enforcer_blocker_sqr_distance_tolerance) { - blocker_dist_sqr = blocker_distance_check(unscaled_position); + blocker_dist_sqr = global_model_info.blocker_distance_check(unscaled_position); last_blocker_checked_point = unscaled_position; if (blocker_dist_sqr < SeamPlacer::enforcer_blocker_sqr_distance_tolerance) { type = EnforcedBlockedSeamPoint::BLOCKED; @@ -287,157 +308,288 @@ void process_perimeter_polygon(const Polygon &polygon, coordf_t z_coord, std::ve } } -void pick_seam_point(std::vector &perimeter_points, size_t start_index, size_t end_index) { - auto min_visibility = perimeter_points[start_index].m_visibility; - auto type = perimeter_points[start_index].m_type; +std::pair find_previous_and_next_perimeter_point(const std::vector &perimeter_points, + size_t index) { + const SeamCandidate ¤t = perimeter_points[index]; + + size_t prev = index > 0 ? index - 1 : index; + size_t next = index + 1 < perimeter_points.size() ? index + 1 : index; + + //NOTE: dont forget that m_polygon_index_reverse are reversed indexes, so 0 is last point + if (current.m_polygon_index_reverse == 0) { + // next is at the start of loop + //find start + size_t start = index; + while (start > 0 && perimeter_points[start - 1].m_polygon_index_reverse != 0) { + start--; + } + next = start; + } + + if (index > 1 && perimeter_points[index - 1].m_polygon_index_reverse == 0) { + //prev is at the end of loop + prev = index + perimeter_points[index].m_polygon_index_reverse; + } + + return {prev,next}; +} + +float calculate_overhang(const SeamCandidate &point, const SeamCandidate &under_a, const SeamCandidate &under_b, + const SeamCandidate &under_c) { + auto p = Vec2d { point.m_position.x(), point.m_position.y() }; + auto a = Vec2d { under_a.m_position.x(), under_a.m_position.y() }; + auto b = Vec2d { under_b.m_position.x(), under_b.m_position.y() }; + auto c = Vec2d { under_c.m_position.x(), under_c.m_position.y() }; + + auto oriented_line_dist = [](const Vec2d a, const Vec2d b, const Vec2d p) { + return -((b.x() - a.x()) * (a.y() - p.y()) - (a.x() - p.x()) * (b.y() - a.y())) / (a - b).norm(); + }; + + auto dist_ab = oriented_line_dist(a, b, p); + auto dist_bc = oriented_line_dist(b, c, p); + + if (under_b.m_ccw_angle > 0 && dist_ab > 0 && dist_bc > 0) { //convex shape, p is inside + return 0; + } + + if (under_b.m_ccw_angle < 0 && (dist_ab < 0 || dist_bc < 0)) { //concave shape, p is inside + return 0; + } + + return Vec2d((p - b).norm(), std::min(abs(dist_ab), abs(dist_bc))).norm(); + +} + +template +void pick_seam_point(std::vector &perimeter_points, size_t start_index, size_t end_index, + const CompareFunc &is_first_better) { size_t seam_index = start_index; for (size_t index = start_index + 1; index <= end_index; ++index) { - if ((perimeter_points[index].m_visibility < min_visibility && perimeter_points[index].m_type == type) - || - (perimeter_points[index].m_type > type)) { - min_visibility = perimeter_points[index].m_visibility; - type = perimeter_points[index].m_type; + if (is_first_better(perimeter_points[index], perimeter_points[seam_index])) { seam_index = index; } } for (size_t index = start_index; index <= end_index; ++index) { perimeter_points[index].m_seam_index = seam_index; + perimeter_points[index].m_nearby_seam_points.get()->store(0, std::memory_order_relaxed); } } +void gather_global_model_info(GlobalModelInfo &result, const PrintObject *po) { + BOOST_LOG_TRIVIAL(debug) + << "SeamPlacer: build AABB tree for raycasting and gather occlusion info: start"; +// Build AABB tree for raycasting + auto obj_transform = po->trafo_centered(); + auto triangle_set = po->model_object()->raw_indexed_triangle_set(); + its_transform(triangle_set, obj_transform); + + auto raycasting_tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(triangle_set.vertices, + triangle_set.indices); + + result.geometry_raycast_hits = raycast_visibility(SeamPlacer::ray_count_per_object, raycasting_tree, + triangle_set); + result.raycast_hits_tree.build(result.geometry_raycast_hits.size()); + + BOOST_LOG_TRIVIAL(debug) + << "SeamPlacer: build AABB tree for raycasting and gather occlusion info: end"; + + BOOST_LOG_TRIVIAL(debug) + << "SeamPlacer: build AABB trees for raycasting enforcers/blockers: start"; + + for (const ModelVolume *mv : po->model_object()->volumes) { + if (mv->is_model_part()) { + its_merge(result.enforcers, mv->seam_facets.get_facets(*mv, EnforcerBlockerType::ENFORCER)); + its_merge(result.blockers, mv->seam_facets.get_facets(*mv, EnforcerBlockerType::BLOCKER)); + } + } + its_transform(result.enforcers, obj_transform); + its_transform(result.blockers, obj_transform); + + result.enforcers_tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(result.enforcers.vertices, + result.enforcers.indices); + result.blockers_tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(result.blockers.vertices, + result.blockers.indices); + + BOOST_LOG_TRIVIAL(debug) + << "SeamPlacer: build AABB trees for raycasting enforcers/blockers: end"; +} + +struct DefaultSeamComparator { + //is a better? + bool operator()(const SeamCandidate &a, const SeamCandidate &b) const { + if (a.m_type > b.m_type) { + return true; + } + if (b.m_type > a.m_type) { + return false; + } + +// if (a.m_overhang > 0.2 && b.m_overhang < a.m_overhang) { +// return false; +// } +// +// if (b.m_ccw_angle < -float(0.3 * PI) && a.m_ccw_angle > -float(0.3 * PI)){ +// return false; +// } + + if (*b.m_nearby_seam_points > *a.m_nearby_seam_points) { + return false; + } + + if (b.m_visibility < 1.2*a.m_visibility) { + return false; + } + + + return true; + } +} +; + } // namespace SeamPlacerImpl void SeamPlacer::init(const Print &print) { - using namespace SeamPlacerImpl; - m_perimeter_points_trees_per_object.clear(); - m_perimeter_points_per_object.clear(); +using namespace SeamPlacerImpl; +m_perimeter_points_trees_per_object.clear(); +m_perimeter_points_per_object.clear(); - for (const PrintObject *po : print.objects()) { - BOOST_LOG_TRIVIAL(debug) - << "PM: build AABB tree for raycasting: start"; - // Build AABB tree for raycasting - auto obj_transform = po->trafo_centered(); - auto triangle_set = po->model_object()->raw_indexed_triangle_set(); - its_transform(triangle_set, obj_transform); +for (const PrintObject *po : print.objects()) { - auto raycasting_tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(triangle_set.vertices, - triangle_set.indices); + GlobalModelInfo global_model_info { }; + gather_global_model_info(global_model_info, po); - BOOST_LOG_TRIVIAL(debug) - << "PM: build AABB tree for raycasting: end"; + BOOST_LOG_TRIVIAL(debug) + << "SeamPlacer: gather and build KD trees with seam candidates: start"; - BOOST_LOG_TRIVIAL(debug) - << "PM: build AABB trees for raycasting enforcers/blockers: start"; + m_perimeter_points_per_object.emplace(po, po->layer_count()); + m_perimeter_points_trees_per_object.emplace(po, po->layer_count()); - indexed_triangle_set enforcers { }; - indexed_triangle_set blockers { }; - for (const ModelVolume *mv : po->model_object()->volumes) { - if (mv->is_model_part()) { - its_merge(enforcers, mv->seam_facets.get_facets(*mv, EnforcerBlockerType::ENFORCER)); - its_merge(blockers, mv->seam_facets.get_facets(*mv, EnforcerBlockerType::BLOCKER)); + tbb::parallel_for(tbb::blocked_range(0, po->layers().size()), + [&](tbb::blocked_range r) { + for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { + std::vector &layer_candidates = m_perimeter_points_per_object[po][layer_idx]; + const auto layer = po->get_layer(layer_idx); + auto unscaled_z = layer->slice_z; + for (const LayerRegion *layer_region : layer->regions()) { + for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { + Polygons polygons; + if (ex_entity->is_collection()) { //collection of inner, outer, and overhang perimeters + for (const ExtrusionEntity *perimeter : + static_cast(ex_entity)->entities) { + if (perimeter->role() == ExtrusionRole::erExternalPerimeter) { + perimeter->polygons_covered_by_width(polygons, 0); + } + } + } else { + polygons = ex_entity->polygons_covered_by_width(); + } + for (const auto &poly : polygons) { + process_perimeter_polygon(poly, unscaled_z, layer_candidates, + global_model_info); } } - its_transform(enforcers, obj_transform); - its_transform(blockers, obj_transform); + } + auto functor = SeamCandidateCoordinateFunctor { &layer_candidates }; + m_perimeter_points_trees_per_object[po][layer_idx] = (std::make_unique( + functor, layer_candidates.size())); - auto enforcers_tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(enforcers.vertices, - enforcers.indices); - auto blockers_tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(blockers.vertices, - blockers.indices); +} +} ); - auto enforcer_distance_check = [&](const Vec3d ¢er) { - size_t hit_idx_out; - Vec3d closest_vec3d; - return AABBTreeIndirect::squared_distance_to_indexed_triangle_set(enforcers.vertices, enforcers.indices, - enforcers_tree, center, hit_idx_out, closest_vec3d); - }; - auto blocker_distance_check = [&](const Vec3d ¢er) { - size_t hit_idx_out; - Vec3d closest_vec3d; - return AABBTreeIndirect::squared_distance_to_indexed_triangle_set(blockers.vertices, blockers.indices, - blockers_tree, center, hit_idx_out, closest_vec3d); - }; + BOOST_LOG_TRIVIAL(debug) + << "SeamPlacer: gather and build KD tree with seam candidates: end"; - BOOST_LOG_TRIVIAL(debug) - << "PM: build AABB trees for raycasting enforcers/blockers: end"; - - std::vector hit_points = raycast_visibility(ray_count_per_object, raycasting_tree, triangle_set); - HitInfoCoordinateFunctor hit_points_functor { &hit_points }; - KDTreeIndirect<3, coordf_t, HitInfoCoordinateFunctor> hit_points_tree { hit_points_functor, hit_points.size() }; - - BOOST_LOG_TRIVIAL(debug) - << "PM: gather and build KD trees with seam candidates: start"; - - m_perimeter_points_per_object.emplace(po, po->layer_count()); - m_perimeter_points_trees_per_object.emplace(po, po->layer_count()); - - tbb::parallel_for(tbb::blocked_range(0, po->layers().size()), - [&](tbb::blocked_range r) { - for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { - std::vector &layer_candidates = m_perimeter_points_per_object[po][layer_idx]; - - const auto layer = po->get_layer(layer_idx); - auto unscaled_z = layer->slice_z; - for (const LayerRegion *layer_region : layer->regions()) { - for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { - auto polygons = ex_entity->polygons_covered_by_width(); - for (const auto &poly : polygons) { - process_perimeter_polygon(poly, unscaled_z, layer_candidates, - enforcer_distance_check, blocker_distance_check); - } - } - } - auto functor = SeamCandidateCoordinateFunctor { &layer_candidates }; - m_perimeter_points_trees_per_object[po][layer_idx] = (std::make_unique( - functor, layer_candidates.size())); + BOOST_LOG_TRIVIAL(debug) + << "SeamPlacer: gather visibility data into perimeter points : start"; + tbb::parallel_for(tbb::blocked_range(0, m_perimeter_points_per_object[po].size()), + [&](tbb::blocked_range r) { + for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { + for (auto &perimeter_point : m_perimeter_points_per_object[po][layer_idx]) { + perimeter_point.m_visibility = global_model_info.calculate_point_visibility( + perimeter_point.m_position, considered_hits_distance); } - }); + } + }); - BOOST_LOG_TRIVIAL(debug) - << "PM: gather and build KD tree with seam candidates: end"; + BOOST_LOG_TRIVIAL(debug) + << "SeamPlacer: gather visibility data into perimeter points : end"; - BOOST_LOG_TRIVIAL(debug) - << "PM: gather visibility data into perimeter points : start"; + BOOST_LOG_TRIVIAL(debug) + << "SeamPlacer: compute overhangs : start"; + + tbb::parallel_for(tbb::blocked_range(0, m_perimeter_points_per_object[po].size()), + [&](tbb::blocked_range r) { + for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { + for (SeamCandidate &perimeter_point : m_perimeter_points_per_object[po][layer_idx]) { + if (layer_idx > 0) { + size_t closest_supporter = find_closest_point( + *m_perimeter_points_trees_per_object[po][layer_idx - 1], + perimeter_point.m_position); + const SeamCandidate &supporter_point = + m_perimeter_points_per_object[po][layer_idx - 1][closest_supporter]; + + auto prev_next = find_previous_and_next_perimeter_point(m_perimeter_points_per_object[po][layer_idx-1], closest_supporter); + const SeamCandidate &prev_point = + m_perimeter_points_per_object[po][layer_idx - 1][prev_next.first]; + const SeamCandidate &next_point = + m_perimeter_points_per_object[po][layer_idx - 1][prev_next.second]; + + perimeter_point.m_overhang = calculate_overhang(perimeter_point, prev_point, + supporter_point, next_point); - tbb::parallel_for(tbb::blocked_range(0, m_perimeter_points_per_object[po].size()), - [&](tbb::blocked_range r) { - for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { - for (auto &perimeter_point : m_perimeter_points_per_object[po][layer_idx]) { - auto nearby_points = find_nearby_points(hit_points_tree, perimeter_point.m_position, - considered_hits_distance); - double visibility = 0; - for (const auto &hit_point_index : nearby_points) { - double distance = - (perimeter_point.m_position - hit_points[hit_point_index].m_position).norm(); - visibility += considered_hits_distance - distance; // The further away from the perimeter point, - // the less representative ray hit is - } - perimeter_point.m_visibility = visibility; } } - }); + } + }); - BOOST_LOG_TRIVIAL(debug) - << "PM: gather visibility data into perimeter points : end"; + BOOST_LOG_TRIVIAL(debug) + << "SeamPlacer: compute overhangs : end"; + + for (size_t iteration = 0; iteration < seam_align_iterations; ++iteration) { + if (iteration > 0) { //skip this in first iteration, no seam has been picked yet + BOOST_LOG_TRIVIAL(debug) + << "SeamPlacer: distribute seam positions to other layers : start"; + + tbb::parallel_for(tbb::blocked_range(0, m_perimeter_points_per_object[po].size()), + [&](tbb::blocked_range r) { + for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { + std::vector &layer_perimeter_points = + m_perimeter_points_per_object[po][layer_idx]; + size_t current = 0; + while (current < layer_perimeter_points.size()) { + auto seam_position = + layer_perimeter_points[layer_perimeter_points[current].m_seam_index].m_position; + + size_t other_layer_idx_start = std::max( + (int) layer_idx - (int) seam_align_layer_dist, 0); + size_t other_layer_idx_end = std::min(layer_idx + seam_align_layer_dist, + m_perimeter_points_per_object[po].size() - 1); + + for (size_t other_layer_idx = other_layer_idx_start; + other_layer_idx <= other_layer_idx_end; ++other_layer_idx) { + + size_t closest_point_idx = find_closest_point( + *m_perimeter_points_trees_per_object[po][other_layer_idx], + seam_position); + + m_perimeter_points_per_object[po][other_layer_idx][closest_point_idx].m_nearby_seam_points->fetch_add( + 1, std::memory_order_relaxed); -#ifdef DEBUG_FILES - Slic3r::CNumericLocalesSetter locales_setter; - FILE *fp = boost::nowide::fopen("perimeters.obj", "w"); - if (fp == nullptr) { - BOOST_LOG_TRIVIAL(error) - << "Couldn't open " << "perimeters.obj" << " for writing"; } - for (size_t i = 0; i < perimeter_points.size(); ++i) - fprintf(fp, "v %f %f %f %f\n", perimeter_points[i].m_position[0], perimeter_points[i].m_position[1], - perimeter_points[i].m_position[2], perimeter_points[i].m_visibility); - fclose(fp); -#endif + current += layer_perimeter_points[current].m_polygon_index_reverse + 1; + } + } + }); + + BOOST_LOG_TRIVIAL(debug) + << "SeamPlacer: distribute seam positions to other layers : end"; + } BOOST_LOG_TRIVIAL(debug) - << "PM: find seam for each perimeter polygon and store its position in each member of the polygon : start"; + << "SeamPlacer: find seam for each perimeter polygon and store its position in each member of the polygon : start"; tbb::parallel_for(tbb::blocked_range(0, m_perimeter_points_per_object[po].size()), [&](tbb::blocked_range r) { @@ -447,29 +599,37 @@ void SeamPlacer::init(const Print &print) { size_t current = 0; while (current < layer_perimeter_points.size()) { pick_seam_point(layer_perimeter_points, current, - current + layer_perimeter_points[current].m_polygon_index_reverse); + current + layer_perimeter_points[current].m_polygon_index_reverse, + DefaultSeamComparator{}); current += layer_perimeter_points[current].m_polygon_index_reverse + 1; } } }); BOOST_LOG_TRIVIAL(debug) - << "PM: find seam for each perimeter polygon and store its position in each member of the polygon : end"; + << "SeamPlacer: find seam for each perimeter polygon and store its position in each member of the polygon : end"; } } +} void SeamPlacer::place_seam(const PrintObject *po, ExtrusionLoop &loop, coordf_t unscaled_z, int layer_index, - bool external_first) { - assert(m_perimeter_points_trees_per_object.find(po) != nullptr); - assert(m_perimeter_points_per_object.find(po) != nullptr); + bool external_first) { +assert(m_perimeter_points_trees_per_object.find(po) != nullptr); +assert(m_perimeter_points_per_object.find(po) != nullptr); - assert(layer_index >= 0); - const auto &perimeter_points_tree = *m_perimeter_points_trees_per_object[po][layer_index]; - const auto &perimeter_points = m_perimeter_points_per_object[po][layer_index]; +assert(layer_index >= 0); +const auto &perimeter_points_tree = *m_perimeter_points_trees_per_object[po][layer_index]; +const auto &perimeter_points = m_perimeter_points_per_object[po][layer_index]; - const Point &fp = loop.first_point(); +const Point &fp = loop.first_point(); +//This is backup check, so that slicer does not crash if something weird is going on +if (perimeter_points.empty()) { + BOOST_LOG_TRIVIAL(error) + << "SeamPlacer: Trying to place seam for index which does not contain any outer or overhang perimeter points, maybe new perimeter type option?"; + loop.split_at(fp, true); +} else { auto unscaled_p = unscale(fp); auto closest_perimeter_point_index = find_closest_point(perimeter_points_tree, Vec3d { unscaled_p.x(), unscaled_p.y(), unscaled_z }); @@ -483,5 +643,37 @@ void SeamPlacer::place_seam(const PrintObject *po, ExtrusionLoop &loop, coordf_t // Insert it. loop.split_at(seam_point, true); } +} + +#ifdef DEBUG_FILES + Slic3r::CNumericLocalesSetter locales_setter; + FILE *fp = boost::nowide::fopen("perimeters.obj", "w"); + if (fp == nullptr) { + BOOST_LOG_TRIVIAL(error) + << "Couldn't open " << "perimeters.obj" << " for writing"; + } + + for (size_t i = 0; i < perimeter_points.size(); ++i) + fprintf(fp, "v %f %f %f %f\n", perimeter_points[i].m_position[0], perimeter_points[i].m_position[1], + perimeter_points[i].m_position[2], perimeter_points[i].m_visibility); + fclose(fp); +#endif + +//TODO disable, only debug code +#ifdef DEBUG_FILES + its_write_obj(triangles, "triangles.obj"); + + Slic3r::CNumericLocalesSetter locales_setter; + FILE *fp = boost::nowide::fopen("hits.obj", "w"); + if (fp == nullptr) { + BOOST_LOG_TRIVIAL(error) + << "Couldn't open " << "hits.obj" << " for writing"; + } + + for (size_t i = 0; i < hit_points.size(); ++i) + fprintf(fp, "v %f %f %f \n", hit_points[i].m_position[0], hit_points[i].m_position[1], + hit_points[i].m_position[2]); + fclose(fp); + #endif } // namespace Slic3r diff --git a/src/libslic3r/GCode/SeamPlacerNG.hpp b/src/libslic3r/GCode/SeamPlacerNG.hpp index f7cac19ff..052823c9d 100644 --- a/src/libslic3r/GCode/SeamPlacerNG.hpp +++ b/src/libslic3r/GCode/SeamPlacerNG.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "libslic3r/ExtrusionEntity.hpp" #include "libslic3r/Polygon.hpp" @@ -25,22 +26,26 @@ class Grid; namespace SeamPlacerImpl { -enum EnforcedBlockedSeamPoint{ - BLOCKED = 0, - NONE = 1, - ENFORCED = 2, +enum EnforcedBlockedSeamPoint { + BLOCKED = 0, + NONE = 1, + ENFORCED = 2, }; struct SeamCandidate { SeamCandidate(const Vec3d &pos, size_t polygon_index_reverse, float ccw_angle, EnforcedBlockedSeamPoint type) : - m_position(pos), m_visibility(0.0), m_polygon_index_reverse(polygon_index_reverse), m_seam_index(0), m_ccw_angle( + m_position(pos), m_visibility(0.0), m_overhang(0.0), m_polygon_index_reverse(polygon_index_reverse), m_seam_index( + 0), m_ccw_angle( ccw_angle), m_type(type) { + m_nearby_seam_points = std::make_unique>(0); } Vec3d m_position; float m_visibility; + float m_overhang; size_t m_polygon_index_reverse; size_t m_seam_index; float m_ccw_angle; + std::unique_ptr> m_nearby_seam_points; EnforcedBlockedSeamPoint m_type; }; @@ -74,11 +79,13 @@ class SeamPlacer { public: using SeamCandidatesTree = KDTreeIndirect<3, coordf_t, SeamPlacerImpl::SeamCandidateCoordinateFunctor>; - const size_t ray_count_per_object = 100000; - const double considered_hits_distance = 2.0; + static constexpr size_t ray_count_per_object = 200000; + static constexpr double considered_hits_distance = 3.0; static constexpr float cosine_hemisphere_sampling_power = 1.5; static constexpr float polygon_angles_arm_distance = 0.6; - static constexpr float enforcer_blocker_sqr_distance_tolerance = 0.02; + static constexpr float enforcer_blocker_sqr_distance_tolerance = 0.04; + static constexpr size_t seam_align_iterations = 3; + static constexpr size_t seam_align_layer_dist = 50; //perimeter points per object per layer idx, and their corresponding KD trees std::unordered_map>> m_perimeter_points_per_object; std::unordered_map>> m_perimeter_points_trees_per_object;