diff --git a/src/libslic3r/AABBTreeIndirect.hpp b/src/libslic3r/AABBTreeIndirect.hpp index d47ea5562b..3ea18de8dc 100644 --- a/src/libslic3r/AABBTreeIndirect.hpp +++ b/src/libslic3r/AABBTreeIndirect.hpp @@ -709,10 +709,15 @@ inline bool intersect_ray_all_hits( origin, dir, VectorType(dir.cwiseInverse()), eps } }; - if (! tree.empty()) { + if (tree.empty()) { + hits.clear(); + } else { + // Reusing the output memory if there is some memory already pre-allocated. + ray_intersector.hits = std::move(hits); + ray_intersector.hits.clear(); ray_intersector.hits.reserve(8); detail::intersect_ray_recursive_all_hits(ray_intersector, 0); - std::swap(hits, ray_intersector.hits); + hits = std::move(ray_intersector.hits); std::sort(hits.begin(), hits.end(), [](const auto &l, const auto &r) { return l.t < r.t; }); } return ! hits.empty(); diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 5f366730a1..614ab4377d 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -19,6 +19,8 @@ #include "libslic3r/Geometry/Curves.hpp" +#include "libslic3r/Utils.hpp" + //#define DEBUG_FILES #ifdef DEBUG_FILES @@ -144,7 +146,9 @@ std::vector raycast_visibility(const AABBTreeIndirect::Tree< std::vector result(triangles.indices.size()); tbb::parallel_for(tbb::blocked_range(0, result.size()), - [&](tbb::blocked_range r) { + [&triangles, &precomputed_sample_directions, model_contains_negative_parts, negative_volumes_start_index, &raycasting_tree, &result](tbb::blocked_range r) { + // Maintaining hits memory outside of the loop, so it does not have to be reallocated for each query. + std::vector hits; for (size_t face_index = r.begin(); face_index < r.end(); ++face_index) { FaceVisibilityInfo &dest = result[face_index]; dest.visibility = 1.0f; @@ -174,7 +178,6 @@ std::vector raycast_visibility(const AABBTreeIndirect::Tree< dest.visibility -= decrease; } } else { - std::vector hits; int in_negative = 0; if (face_index >= negative_volumes_start_index) { // if casting from negative volume face, invert direction final_ray_dir = -1.0 * final_ray_dir; @@ -219,23 +222,16 @@ std::vector calculate_polygon_angles_at_vertices(const Polygon &polygon, result[0] = 0.0f; } - auto make_idx_circular = [&](int index) { - while (index < 0) { - index += polygon.size(); - } - return index % polygon.size(); - }; - - int idx_prev = 0; - int idx_curr = 0; - int idx_next = 0; + size_t idx_prev = 0; + size_t idx_curr = 0; + size_t idx_next = 0; float distance_to_prev = 0; float distance_to_next = 0; //push idx_prev far enough back as initialization while (distance_to_prev < min_arm_length) { - idx_prev = make_idx_circular(idx_prev - 1); + idx_prev = Slic3r::prev_idx_modulo(idx_prev, polygon.size()); distance_to_prev += lengths[idx_prev]; } @@ -243,25 +239,20 @@ std::vector calculate_polygon_angles_at_vertices(const Polygon &polygon, // pull idx_prev to current as much as possible, while respecting the min_arm_length while (distance_to_prev - lengths[idx_prev] > min_arm_length) { distance_to_prev -= lengths[idx_prev]; - idx_prev = make_idx_circular(idx_prev + 1); + idx_prev = Slic3r::next_idx_modulo(idx_prev, polygon.size()); } //push idx_next forward as far as needed while (distance_to_next < min_arm_length) { distance_to_next += lengths[idx_next]; - idx_next = make_idx_circular(idx_next + 1); + idx_next = Slic3r::next_idx_modulo(idx_next, polygon.size()); } // Calculate angle between idx_prev, idx_curr, idx_next. const Point &p0 = polygon.points[idx_prev]; const Point &p1 = polygon.points[idx_curr]; const Point &p2 = polygon.points[idx_next]; - const Point v1 = p1 - p0; - const Point v2 = p2 - p1; - int64_t dot = int64_t(v1(0)) * int64_t(v2(0)) + int64_t(v1(1)) * int64_t(v2(1)); - int64_t cross = int64_t(v1(0)) * int64_t(v2(1)) - int64_t(v1(1)) * int64_t(v2(0)); - float angle = float(atan2(float(cross), float(dot))); - result[idx_curr] = angle; + result[idx_curr] = float(angle(p1 - p0, p2 - p1)); // increase idx_curr by one float curr_distance = lengths[idx_curr]; @@ -357,20 +348,20 @@ Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition confi && configured_seam_preference == spRandom)) { //for random seam alignment, extract all perimeters Points p; perimeter->collect_points(p); - polygons.emplace_back(p); + polygons.emplace_back(std::move(p)); corresponding_regions_out.push_back(layer_region); } } if (polygons.empty()) { Points p; ex_entity->collect_points(p); - polygons.emplace_back(p); + polygons.emplace_back(std::move(p)); corresponding_regions_out.push_back(layer_region); } } else { Points p; ex_entity->collect_points(p); - polygons.emplace_back(p); + polygons.emplace_back(std::move(p)); corresponding_regions_out.push_back(layer_region); } } @@ -390,7 +381,7 @@ Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition confi //each SeamCandidate also contains pointer to shared Perimeter structure representing the polygon // if Custom Seam modifiers are present, oversamples the polygon if necessary to better fit user intentions void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const LayerRegion *region, - const GlobalModelInfo &global_model_info, std::vector &result_vec) { + const GlobalModelInfo &global_model_info, PrintObjectSeamData::LayerSeams &result) { if (orig_polygon.size() == 0) { return; } @@ -406,7 +397,9 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const std::vector local_angles = calculate_polygon_angles_at_vertices(polygon, lengths, SeamPlacer::polygon_local_angles_arm_distance); - std::shared_ptr perimeter = std::make_shared(); + + result.perimeters.push_back({}); + Perimeter &perimeter = result.perimeters.back(); std::queue orig_polygon_points { }; for (size_t index = 0; index < polygon.size(); ++index) { @@ -416,8 +409,8 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const Vec3f first = orig_polygon_points.front(); std::queue oversampled_points { }; size_t orig_angle_index = 0; - perimeter->start_index = result_vec.size(); - perimeter->flow_width = region != nullptr ? region->flow(FlowRole::frExternalPerimeter).width() : 0.0f; + perimeter.start_index = result.points.size(); + perimeter.flow_width = region != nullptr ? region->flow(FlowRole::frExternalPerimeter).width() : 0.0f; bool some_point_enforced = false; while (!orig_polygon_points.empty() || !oversampled_points.empty()) { EnforcedBlockedSeamPoint type = EnforcedBlockedSeamPoint::Neutral; @@ -458,18 +451,18 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const } } - result_vec.emplace_back(position, perimeter, local_ccw_angle, type); + result.points.emplace_back(position, perimeter, local_ccw_angle, type); } - perimeter->end_index = result_vec.size() - 1; + perimeter.end_index = result.points.size() - 1; // We will find first patch of enforced points (patch: continous section of enforced points) and select the middle // point, which will have priority during alignemnt // If there are multiple enforced patches in the perimeter, others are ignored if (some_point_enforced) { - size_t first_enforced_idx = perimeter->start_index; - while (first_enforced_idx <= perimeter->end_index - && result_vec[first_enforced_idx].type != EnforcedBlockedSeamPoint::Enforced) { + size_t first_enforced_idx = perimeter.start_index; + while (first_enforced_idx <= perimeter.end_index + && result.points[first_enforced_idx].type != EnforcedBlockedSeamPoint::Enforced) { first_enforced_idx++; } @@ -477,9 +470,9 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const // If there are any, the middle point will be picked from those (makes drawing over sharp corners easier) std::vector orig_large_angle_points_indices{}; size_t last_enforced_idx = first_enforced_idx; - while (last_enforced_idx < perimeter->end_index - && result_vec[last_enforced_idx + 1].type == EnforcedBlockedSeamPoint::Enforced) { - if (abs(result_vec[last_enforced_idx].local_ccw_angle) > 0.4 * PI) { + while (last_enforced_idx < perimeter.end_index + && result.points[last_enforced_idx + 1].type == EnforcedBlockedSeamPoint::Enforced) { + if (abs(result.points[last_enforced_idx].local_ccw_angle) > 0.4 * PI) { orig_large_angle_points_indices.push_back(last_enforced_idx); } last_enforced_idx++; @@ -487,10 +480,10 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const if (orig_large_angle_points_indices.empty()) { size_t central_idx = (first_enforced_idx + last_enforced_idx) / 2; - result_vec[central_idx].central_enforcer = true; + result.points[central_idx].central_enforcer = true; } else { size_t central_idx = orig_large_angle_points_indices.size() / 2; - result_vec[orig_large_angle_points_indices[central_idx]].central_enforcer = true; + result.points[orig_large_angle_points_indices[central_idx]].central_enforcer = true; } } @@ -505,14 +498,14 @@ std::pair find_previous_and_next_perimeter_point(const std::vect int prev = point_index - 1; //for majority of points, it is true that neighbours lie behind and in front of them in the vector int next = point_index + 1; - if (point_index == current.perimeter->start_index) { + if (point_index == current.perimeter.start_index) { // if point_index is equal to start, it means that the previous neighbour is at the end - prev = current.perimeter->end_index; + prev = current.perimeter.end_index; } - if (point_index == current.perimeter->end_index) { + if (point_index == current.perimeter.end_index) { // if point_index is equal to end, than next neighbour is at the start - next = current.perimeter->start_index; + next = current.perimeter.start_index; } assert(prev >= 0); @@ -590,7 +583,7 @@ void gather_enforcers_blockers(GlobalModelInfo &result, const PrintObject *po) { for (const ModelVolume *mv : po->model_object()->volumes) { if (mv->is_seam_painted()) { - auto model_transformation = mv->get_matrix(); + auto model_transformation = obj_transform * mv->get_matrix(); indexed_triangle_set enforcers = mv->seam_facets.get_facets(*mv, EnforcerBlockerType::ENFORCER); its_transform(enforcers, model_transformation); @@ -601,8 +594,6 @@ void gather_enforcers_blockers(GlobalModelInfo &result, const PrintObject *po) { its_merge(result.blockers, blockers); } } - 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); @@ -817,7 +808,7 @@ void debug_export_points(const std::vector &perimeter_points, size_t start_index, const SeamComparator &comparator) { - size_t end_index = perimeter_points[start_index].perimeter->end_index; + size_t end_index = perimeter_points[start_index].perimeter.end_index; size_t seam_index = start_index; for (size_t index = start_index; index <= end_index; ++index) { @@ -825,12 +816,12 @@ void pick_seam_point(std::vector &perimeter_points, size_t start_ seam_index = index; } } - perimeter_points[start_index].perimeter->seam_index = seam_index; + perimeter_points[start_index].perimeter.seam_index = seam_index; } size_t pick_nearest_seam_point_index(const std::vector &perimeter_points, size_t start_index, const Vec2f &preffered_location) { - size_t end_index = perimeter_points[start_index].perimeter->end_index; + size_t end_index = perimeter_points[start_index].perimeter.end_index; SeamComparator comparator { spNearest }; size_t seam_index = start_index; @@ -843,7 +834,7 @@ size_t pick_nearest_seam_point_index(const std::vector &perimeter } // picks random seam point uniformly, respecting enforcers blockers and overhang avoidance. -void pick_random_seam_point(std::vector &perimeter_points, size_t start_index) { +void pick_random_seam_point(const std::vector &perimeter_points, size_t start_index) { SeamComparator comparator { spRandom }; // algorithm keeps a list of viable points and their lengths. If it finds a point @@ -852,7 +843,7 @@ void pick_random_seam_point(std::vector &perimeter_points, size_t // in the end, the list should contain points with same type (Enforced > Neutral > Blocked) and also only those which are not // big overhang. size_t viable_example_index = start_index; - size_t end_index = perimeter_points[start_index].perimeter->end_index; + size_t end_index = perimeter_points[start_index].perimeter.end_index; std::vector viable_indices; std::vector viable_edges_lengths; std::vector viable_edges; @@ -906,11 +897,11 @@ void pick_random_seam_point(std::vector &perimeter_points, size_t point_idx++; } - Perimeter *perimeter = perimeter_points[start_index].perimeter.get(); - perimeter->seam_index = viable_indices[point_idx]; - perimeter->final_seam_position = perimeter_points[perimeter->seam_index].position + Perimeter &perimeter = perimeter_points[start_index].perimeter; + perimeter.seam_index = viable_indices[point_idx]; + perimeter.final_seam_position = perimeter_points[perimeter.seam_index].position + viable_edges[point_idx].normalized() * picked_len; - perimeter->finalized = true; + perimeter.finalized = true; } @@ -940,19 +931,18 @@ EdgeGridWrapper compute_layer_merged_edge_grid(const Layer *layer) { // Parallel process and extract each perimeter polygon of the given print object. // Gather SeamCandidates of each layer into vector and build KDtree over them -// Store results in the SeamPlacer varaibles m_perimeter_points_per_object and m_perimeter_points_trees_per_object +// Store results in the SeamPlacer variables m_seam_per_object void SeamPlacer::gather_seam_candidates(const PrintObject *po, const SeamPlacerImpl::GlobalModelInfo &global_model_info, const SeamPosition configured_seam_preference) { using namespace SeamPlacerImpl; - m_perimeter_points_per_object.emplace(po, po->layer_count()); - m_perimeter_points_trees_per_object.emplace(po, po->layer_count()); + PrintObjectSeamData &seam_data = m_seam_per_object.emplace(po, PrintObjectSeamData{}).first->second; + seam_data.layers.resize(po->layer_count()); tbb::parallel_for(tbb::blocked_range(0, po->layers().size()), - [&](tbb::blocked_range r) { + [po, configured_seam_preference, &global_model_info, &seam_data](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]; + PrintObjectSeamData::LayerSeams &layer_seams = seam_data.layers[layer_idx]; const Layer *layer = po->get_layer(layer_idx); auto unscaled_z = layer->slice_z; std::vector regions; @@ -960,11 +950,11 @@ void SeamPlacer::gather_seam_candidates(const PrintObject *po, Polygons polygons = extract_perimeter_polygons(layer, configured_seam_preference, regions); for (size_t poly_index = 0; poly_index < polygons.size(); ++poly_index) { process_perimeter_polygon(polygons[poly_index], unscaled_z, - regions[poly_index], global_model_info, layer_candidates); + regions[poly_index], global_model_info, layer_seams); } - auto functor = SeamCandidateCoordinateFunctor { &layer_candidates }; - m_perimeter_points_trees_per_object[po][layer_idx] = - std::make_unique(functor, layer_candidates.size()); + auto functor = SeamCandidateCoordinateFunctor { &layer_seams.points }; + seam_data.layers[layer_idx].points_tree = + std::make_unique(functor, layer_seams.points.size()); } } ); @@ -974,10 +964,11 @@ void SeamPlacer::calculate_candidates_visibility(const PrintObject *po, const SeamPlacerImpl::GlobalModelInfo &global_model_info) { using namespace SeamPlacerImpl; - tbb::parallel_for(tbb::blocked_range(0, m_perimeter_points_per_object[po].size()), - [&](tbb::blocked_range r) { + std::vector &layers = m_seam_per_object[po].layers; + tbb::parallel_for(tbb::blocked_range(0, layers.size()), + [&layers, &global_model_info](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]) { + for (auto &perimeter_point : layers[layer_idx].points) { perimeter_point.visibility = global_model_info.calculate_point_visibility( perimeter_point.position); } @@ -988,8 +979,9 @@ void SeamPlacer::calculate_candidates_visibility(const PrintObject *po, void SeamPlacer::calculate_overhangs_and_layer_embedding(const PrintObject *po) { using namespace SeamPlacerImpl; - tbb::parallel_for(tbb::blocked_range(0, m_perimeter_points_per_object[po].size()), - [&](tbb::blocked_range r) { + std::vector &layers = m_seam_per_object[po].layers; + tbb::parallel_for(tbb::blocked_range(0, layers.size()), + [po, &layers](tbb::blocked_range r) { std::unique_ptr prev_layer_grid; if (r.begin() > 0) { // previous layer exists prev_layer_grid = std::make_unique( @@ -998,19 +990,19 @@ void SeamPlacer::calculate_overhangs_and_layer_embedding(const PrintObject *po) for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { bool layer_has_multiple_loops = - m_perimeter_points_per_object[po][layer_idx][0].perimeter->end_index - < m_perimeter_points_per_object[po][layer_idx].size() - 1; + layers[layer_idx].points[0].perimeter.end_index + < layers[layer_idx].points.size() - 1; std::unique_ptr current_layer_grid = std::make_unique( compute_layer_merged_edge_grid(po->layers()[layer_idx])); - for (SeamCandidate &perimeter_point : m_perimeter_points_per_object[po][layer_idx]) { + for (SeamCandidate &perimeter_point : layers[layer_idx].points) { Point point = Point::new_scale(Vec2f { perimeter_point.position.head<2>() }); if (prev_layer_grid.get() != nullptr) { coordf_t overhang_dist; - prev_layer_grid->grid.signed_distance(point, scaled(perimeter_point.perimeter->flow_width), + prev_layer_grid->grid.signed_distance(point, scaled(perimeter_point.perimeter.flow_width), overhang_dist); perimeter_point.overhang = - unscale(overhang_dist) - perimeter_point.perimeter->flow_width; + unscale(overhang_dist) - perimeter_point.perimeter.flow_width; } if (layer_has_multiple_loops) { // search for embedded perimeter points (points hidden inside the print ,e.g. multimaterial join, best position for seam) @@ -1040,33 +1032,31 @@ bool SeamPlacer::find_next_seam_in_layer(const PrintObject *po, std::vector> &seam_string) { using namespace SeamPlacerImpl; - const SeamCandidate &last_point = - m_perimeter_points_per_object[po][last_point_indexes.first][last_point_indexes.second]; + const std::vector &layers = m_seam_per_object[po].layers; + const SeamCandidate &last_point = layers[last_point_indexes.first].points[last_point_indexes.second]; Vec3f projected_position { last_point.position.x(), last_point.position.y(), float( po->get_layer(layer_idx)->slice_z) }; //find closest point in next layer - size_t closest_point_index = find_closest_point( - *m_perimeter_points_trees_per_object[po][layer_idx], projected_position); + size_t closest_point_index = find_closest_point(*layers[layer_idx].points_tree, projected_position); - SeamCandidate &closest_point = m_perimeter_points_per_object[po][layer_idx][closest_point_index]; + const SeamCandidate &closest_point = layers[layer_idx].points[closest_point_index]; - if (closest_point.perimeter->finalized) { //already finalized, skip + if (closest_point.perimeter.finalized) { //already finalized, skip return false; } //from the closest point, deduce index of seam in the next layer - SeamCandidate &next_layer_seam = - m_perimeter_points_per_object[po][layer_idx][closest_point.perimeter->seam_index]; + const SeamCandidate &next_layer_seam = layers[layer_idx].points[closest_point.perimeter.seam_index]; if (next_layer_seam.central_enforcer && (next_layer_seam.position - projected_position).norm() < 3 * SeamPlacer::seam_align_tolerable_dist) { - seam_string.push_back( { layer_idx, closest_point.perimeter->seam_index }); - last_point_indexes = std::pair { layer_idx, closest_point.perimeter->seam_index }; + seam_string.push_back( { layer_idx, closest_point.perimeter.seam_index }); + last_point_indexes = std::pair { layer_idx, closest_point.perimeter.seam_index }; return true; } - auto are_similar = [&](const SeamCandidate &a, const SeamCandidate &b) { + auto are_similar = [&comparator](const SeamCandidate &a, const SeamCandidate &b) { return comparator.is_first_not_much_worse(a, b) && comparator.is_first_not_much_worse(b, a); }; @@ -1110,22 +1100,22 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl:: #endif //gather vector of all seams on the print_object - pair of layer_index and seam__index within that layer + const std::vector &layers = m_seam_per_object[po].layers; std::vector> seams; - for (size_t layer_idx = 0; layer_idx < m_perimeter_points_per_object[po].size(); ++layer_idx) { - std::vector &layer_perimeter_points = - m_perimeter_points_per_object[po][layer_idx]; + for (size_t layer_idx = 0; layer_idx < layers.size(); ++layer_idx) { + const std::vector &layer_perimeter_points = layers[layer_idx].points; size_t current_point_index = 0; while (current_point_index < layer_perimeter_points.size()) { - seams.emplace_back(layer_idx, layer_perimeter_points[current_point_index].perimeter->seam_index); - current_point_index = layer_perimeter_points[current_point_index].perimeter->end_index + 1; + seams.emplace_back(layer_idx, layer_perimeter_points[current_point_index].perimeter.seam_index); + current_point_index = layer_perimeter_points[current_point_index].perimeter.end_index + 1; } } //sort them before alignment. Alignment is sensitive to initializaion, this gives it better chance to choose something nice std::sort(seams.begin(), seams.end(), - [&](const std::pair &left, const std::pair &right) { - return comparator.is_first_better(m_perimeter_points_per_object[po][left.first][left.second], - m_perimeter_points_per_object[po][right.first][right.second]); + [&comparator, &layers](const std::pair &left, const std::pair &right) { + return comparator.is_first_better(layers[left.first].points[left.second], + layers[right.first].points[right.second]); } ); @@ -1133,9 +1123,8 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl:: for (const std::pair &seam : seams) { size_t layer_idx = seam.first; size_t seam_index = seam.second; - std::vector &layer_perimeter_points = - m_perimeter_points_per_object[po][layer_idx]; - if (layer_perimeter_points[seam_index].perimeter->finalized) { + const std::vector &layer_perimeter_points = layers[layer_idx].points; + if (layer_perimeter_points[seam_index].perimeter.finalized) { // This perimeter is already aligned, skip seam continue; } else { @@ -1148,7 +1137,7 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl:: std::vector> seam_string { std::pair(layer_idx, seam_index) }; //find seams or potential seams in forward direction; there is a budget of skips allowed - while (skips >= 0 && next_layer < int(m_perimeter_points_per_object[po].size())) { + while (skips >= 0 && next_layer < int(layers.size())) { if (find_next_seam_in_layer(po, last_point_indexes, next_layer, comparator, seam_string)) { //String added, last_point_pos updated, nothing to be done } else { @@ -1191,16 +1180,15 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl:: //init min_weight by the first point float min_weight = -comparator.get_penalty( - m_perimeter_points_per_object[po][seam_string[0].first][seam_string[0].second]); + layers[seam_string[0].first].points[seam_string[0].second]); //gather points positions and weights, update min_weight in each step for (size_t index = 0; index < seam_string.size(); ++index) { - Vec3f pos = - m_perimeter_points_per_object[po][seam_string[index].first][seam_string[index].second].position; + Vec3f pos = layers[seam_string[index].first].points[seam_string[index].second].position; observations[index] = pos.head<2>(); observation_points[index] = pos.z(); weights[index] = -comparator.get_penalty( - m_perimeter_points_per_object[po][seam_string[index].first][seam_string[index].second]); + layers[seam_string[index].first].points[seam_string[index].second]); min_weight = std::min(min_weight, weights[index]); } @@ -1217,13 +1205,12 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl:: // Do alignment - compute fitted point for each point in the string from its Z coord, and store the position into // Perimeter structure of the point; also set flag aligned to true for (const auto &pair : seam_string) { - Vec3f current_pos = m_perimeter_points_per_object[po][pair.first][pair.second].position; + Vec3f current_pos = layers[pair.first].points[pair.second].position; Vec2f fitted_pos = curve.get_fitted_value(current_pos.z()); - Perimeter *perimeter = - m_perimeter_points_per_object[po][pair.first][pair.second].perimeter.get(); - perimeter->final_seam_position = Vec3f { fitted_pos.x(), fitted_pos.y(), current_pos.z() }; - perimeter->finalized = true; + Perimeter &perimeter = layers[pair.first].points[pair.second].perimeter; + perimeter.final_seam_position = to_3d(fitted_pos, current_pos.z()); + perimeter.finalized = true; } #ifdef DEBUG_FILES @@ -1232,7 +1219,7 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl:: }; Vec3f color { randf(), randf(), randf() }; for (size_t i = 0; i < seam_string.size(); ++i) { - auto orig_seam = m_perimeter_points_per_object[po][seam_string[i].first][seam_string[i].second]; + auto orig_seam = perimeter_points[seam_string[i].first][seam_string[i].second]; fprintf(clusters, "v %f %f %f %f %f %f \n", orig_seam.position[0], orig_seam.position[1], orig_seam.position[2], color[0], color[1], @@ -1241,11 +1228,10 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl:: color = Vec3f { randf(), randf(), randf() }; for (size_t i = 0; i < seam_string.size(); ++i) { - Perimeter *perimeter = - m_perimeter_points_per_object[po][seam_string[i].first][seam_string[i].second].perimeter.get(); - fprintf(aligns, "v %f %f %f %f %f %f \n", perimeter->final_seam_position[0], - perimeter->final_seam_position[1], - perimeter->final_seam_position[2], color[0], color[1], + Perimeter &perimeter = perimeter_points[seam_string[i].first][seam_string[i].second].perimeter; + fprintf(aligns, "v %f %f %f %f %f %f \n", perimeter.final_seam_position[0], + perimeter.final_seam_position[1], + perimeter.final_seam_position[2], color[0], color[1], color[2]); } #endif @@ -1261,8 +1247,7 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl:: void SeamPlacer::init(const Print &print) { using namespace SeamPlacerImpl; - m_perimeter_points_trees_per_object.clear(); - m_perimeter_points_per_object.clear(); + m_seam_per_object.clear(); for (const PrintObject *po : print.objects()) { @@ -1299,20 +1284,16 @@ void SeamPlacer::init(const Print &print) { BOOST_LOG_TRIVIAL(debug) << "SeamPlacer: pick_seam_point : start"; //pick seam point - tbb::parallel_for(tbb::blocked_range(0, m_perimeter_points_per_object[po].size()), - [&](tbb::blocked_range r) { + std::vector &layers = m_seam_per_object[po].layers; + tbb::parallel_for(tbb::blocked_range(0, layers.size()), + [&layers, configured_seam_preference, comparator](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()) { - if (configured_seam_preference == spRandom) { + std::vector &layer_perimeter_points = layers[layer_idx].points; + for (size_t current = 0; current < layer_perimeter_points.size(); current = layer_perimeter_points[current].perimeter.end_index + 1) + if (configured_seam_preference == spRandom) pick_random_seam_point(layer_perimeter_points, current); - } else { + else pick_seam_point(layer_perimeter_points, current, comparator); - } - current = layer_perimeter_points[current].perimeter->end_index + 1; - } } }); BOOST_LOG_TRIVIAL(debug) @@ -1327,7 +1308,7 @@ void SeamPlacer::init(const Print &print) { } #ifdef DEBUG_FILES - debug_export_points(m_perimeter_points_per_object[po], po->bounding_box(), std::to_string(po->id().id), + debug_export_points(perimeter_points, po->bounding_box(), std::to_string(po->id().id), comparator); #endif } @@ -1341,29 +1322,29 @@ void SeamPlacer::place_seam(const Layer *layer, ExtrusionLoop &loop, bool extern size_t layer_index = std::max(0, int(layer->id()) - int(po->slicing_parameters().raft_layers())); double unscaled_z = layer->slice_z; - const auto &perimeter_points_tree = *m_perimeter_points_trees_per_object.find(po)->second[layer_index]; - const auto &perimeter_points = m_perimeter_points_per_object.find(po)->second[layer_index]; + const PrintObjectSeamData::LayerSeams &layer_perimeters = m_seam_per_object.find(layer->object())->second.layers[layer_index]; const Point &fp = loop.first_point(); - Vec2f unscaled_p = unscale(fp).cast(); - size_t closest_perimeter_point_index = find_closest_point(perimeter_points_tree, - Vec3f { unscaled_p.x(), unscaled_p.y(), float(unscaled_z) }); - const Perimeter *perimeter = perimeter_points[closest_perimeter_point_index].perimeter.get(); + Vec2f unscaled_p = unscaled(fp); + size_t closest_perimeter_point_index = find_closest_point(*layer_perimeters.points_tree.get(), to_3d(unscaled_p, float(unscaled_z))); - size_t seam_index; - if (po->config().seam_position == spNearest) { - seam_index = pick_nearest_seam_point_index(perimeter_points, perimeter->start_index, - unscale(last_pos).cast()); + Vec3f seam_position; + if (const Perimeter &perimeter = layer_perimeters.points[closest_perimeter_point_index].perimeter; + perimeter.finalized) { + seam_position = perimeter.final_seam_position; } else { - seam_index = perimeter->seam_index; + size_t seam_index; + if (po->config().seam_position == spNearest) { + seam_index = pick_nearest_seam_point_index(layer_perimeters.points, perimeter.start_index, + unscaled(last_pos)); + } else { + seam_index = perimeter.seam_index; + } + seam_position = layer_perimeters.points[seam_index].position; } - Vec3f seam_position = perimeter_points[seam_index].position; - if (perimeter->finalized) { - seam_position = perimeter->final_seam_position; - } - Point seam_point = scaled(Vec2d { seam_position.x(), seam_position.y() }); + auto seam_point = Point::new_scale(seam_position.x(), seam_position.y()); if (!loop.split_at_vertex(seam_point)) // The point is not in the original loop. diff --git a/src/libslic3r/GCode/SeamPlacer.hpp b/src/libslic3r/GCode/SeamPlacer.hpp index aad783747c..04e6b1bc6f 100644 --- a/src/libslic3r/GCode/SeamPlacer.hpp +++ b/src/libslic3r/GCode/SeamPlacer.hpp @@ -51,20 +51,20 @@ struct Perimeter { //Struct over which all processing of perimeters is done. For each perimeter point, its respective candidate is created, // then all the needed attributes are computed and finally, for each perimeter one point is chosen as seam. -// This seam position can be than further aligned +// This seam position can be then further aligned struct SeamCandidate { - SeamCandidate(const Vec3f &pos, std::shared_ptr perimeter, + SeamCandidate(const Vec3f &pos, Perimeter &perimeter, float local_ccw_angle, EnforcedBlockedSeamPoint type) : position(pos), perimeter(perimeter), visibility(0.0f), overhang(0.0f), embedded_distance(0.0f), local_ccw_angle( local_ccw_angle), type(type), central_enforcer(false) { } const Vec3f position; - // pointer to Perimter loop of this point. It is shared across all points of the loop - const std::shared_ptr perimeter; + // pointer to Perimeter loop of this point. It is shared across all points of the loop + Perimeter &perimeter; float visibility; float overhang; - // distance inside the merged layer regions, for detecting perimter points which are hidden indside the print (e.g. multimaterial join) + // distance inside the merged layer regions, for detecting perimeter points which are hidden indside the print (e.g. multimaterial join) // Negative sign means inside the print, comes from EdgeGrid structure float embedded_distance; float local_ccw_angle; @@ -87,10 +87,29 @@ struct SeamCandidateCoordinateFunctor { }; } // namespace SeamPlacerImpl +struct PrintObjectSeamData +{ + using SeamCandidatesTree = KDTreeIndirect<3, float, SeamPlacerImpl::SeamCandidateCoordinateFunctor>; + + struct LayerSeams + { + std::deque perimeters; + std::vector points; + std::unique_ptr points_tree; + }; + // Map of PrintObjects (PO) -> vector of layers of PO -> vector of perimeter + std::vector layers; + // Map of PrintObjects (PO) -> vector of layers of PO -> unique_ptr to KD + // tree of all points of the given layer + + void clear() + { + layers.clear(); + } +}; + class SeamPlacer { public: - using SeamCandidatesTree = - KDTreeIndirect<3, float, SeamPlacerImpl::SeamCandidateCoordinateFunctor>; static constexpr size_t raycasting_decimation_target_triangle_count = 10000; static constexpr float raycasting_subdivision_target_length = 2.0f; //square of number of rays per triangle @@ -120,11 +139,8 @@ public: // points covered by spline; determines number of splines for the given string static constexpr size_t seam_align_seams_per_segment = 8; - //The following data structures hold all perimeter points for all PrintObject. The structure is as follows: - // Map of PrintObjects (PO) -> vector of layers of PO -> vector of perimeter points of the given layer - std::unordered_map>> m_perimeter_points_per_object; - // Map of PrintObjects (PO) -> vector of layers of PO -> unique_ptr to KD tree of all points of the given layer - std::unordered_map>> m_perimeter_points_trees_per_object; + //The following data structures hold all perimeter points for all PrintObject. + std::unordered_map m_seam_per_object; void init(const Print &print);