Refactoring of SeamPlacer:

Replaced shared_ptr<> with deque.
Merged multiple vectors into one.
Refactoring using common helper functions (prev/next_idx_modulo(),
angle(), ...)
AABBTreeIndirect::intersect_ray_all_hits(): Reuse memory of the hits cache.
This commit is contained in:
Vojtech Bubnik 2022-04-05 16:13:40 +02:00 committed by PavelMikus
parent 3b8cfc62da
commit 8ce36e9137
3 changed files with 158 additions and 156 deletions

View file

@ -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();

View file

@ -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<FaceVisibilityInfo> raycast_visibility(const AABBTreeIndirect::Tree<
std::vector<FaceVisibilityInfo> result(triangles.indices.size());
tbb::parallel_for(tbb::blocked_range<size_t>(0, result.size()),
[&](tbb::blocked_range<size_t> r) {
[&triangles, &precomputed_sample_directions, model_contains_negative_parts, negative_volumes_start_index, &raycasting_tree, &result](tbb::blocked_range<size_t> r) {
// Maintaining hits memory outside of the loop, so it does not have to be reallocated for each query.
std::vector<igl::Hit> 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<FaceVisibilityInfo> raycast_visibility(const AABBTreeIndirect::Tree<
dest.visibility -= decrease;
}
} else {
std::vector<igl::Hit> 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<float> 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<float> 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<SeamCandidate> &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<float> local_angles = calculate_polygon_angles_at_vertices(polygon, lengths,
SeamPlacer::polygon_local_angles_arm_distance);
std::shared_ptr<Perimeter> perimeter = std::make_shared<Perimeter>();
result.perimeters.push_back({});
Perimeter &perimeter = result.perimeters.back();
std::queue<Vec3f> 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<Vec3f> 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<size_t> 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<size_t, size_t> 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<std::vector<SeamPlacerImpl::SeamCandi
// Pick best seam point based on the given comparator
void pick_seam_point(std::vector<SeamCandidate> &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<SeamCandidate> &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<SeamCandidate> &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<SeamCandidate> &perimeter
}
// picks random seam point uniformly, respecting enforcers blockers and overhang avoidance.
void pick_random_seam_point(std::vector<SeamCandidate> &perimeter_points, size_t start_index) {
void pick_random_seam_point(const std::vector<SeamCandidate> &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<SeamCandidate> &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<size_t> viable_indices;
std::vector<float> viable_edges_lengths;
std::vector<Vec3f> viable_edges;
@ -906,11 +897,11 @@ void pick_random_seam_point(std::vector<SeamCandidate> &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<size_t>(0, po->layers().size()),
[&](tbb::blocked_range<size_t> r) {
[po, configured_seam_preference, &global_model_info, &seam_data](tbb::blocked_range<size_t> r) {
for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) {
std::vector<SeamCandidate> &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<const LayerRegion*> 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<SeamCandidatesTree>(functor, layer_candidates.size());
auto functor = SeamCandidateCoordinateFunctor { &layer_seams.points };
seam_data.layers[layer_idx].points_tree =
std::make_unique<PrintObjectSeamData::SeamCandidatesTree>(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<size_t>(0, m_perimeter_points_per_object[po].size()),
[&](tbb::blocked_range<size_t> r) {
std::vector<PrintObjectSeamData::LayerSeams> &layers = m_seam_per_object[po].layers;
tbb::parallel_for(tbb::blocked_range<size_t>(0, layers.size()),
[&layers, &global_model_info](tbb::blocked_range<size_t> 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<size_t>(0, m_perimeter_points_per_object[po].size()),
[&](tbb::blocked_range<size_t> r) {
std::vector<PrintObjectSeamData::LayerSeams> &layers = m_seam_per_object[po].layers;
tbb::parallel_for(tbb::blocked_range<size_t>(0, layers.size()),
[po, &layers](tbb::blocked_range<size_t> r) {
std::unique_ptr<EdgeGridWrapper> prev_layer_grid;
if (r.begin() > 0) { // previous layer exists
prev_layer_grid = std::make_unique<EdgeGridWrapper>(
@ -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<EdgeGridWrapper> current_layer_grid = std::make_unique<EdgeGridWrapper>(
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<float>(overhang_dist) - perimeter_point.perimeter->flow_width;
unscale<float>(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<std::pair<size_t, size_t>> &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<PrintObjectSeamData::LayerSeams> &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<size_t, size_t> { layer_idx, closest_point.perimeter->seam_index };
seam_string.push_back( { layer_idx, closest_point.perimeter.seam_index });
last_point_indexes = std::pair<size_t, size_t> { 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<PrintObjectSeamData::LayerSeams> &layers = m_seam_per_object[po].layers;
std::vector<std::pair<size_t, size_t>> seams;
for (size_t layer_idx = 0; layer_idx < m_perimeter_points_per_object[po].size(); ++layer_idx) {
std::vector<SeamCandidate> &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<SeamCandidate> &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<size_t, size_t> &left, const std::pair<size_t, size_t> &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<size_t, size_t> &left, const std::pair<size_t, size_t> &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<size_t, size_t> &seam : seams) {
size_t layer_idx = seam.first;
size_t seam_index = seam.second;
std::vector<SeamCandidate> &layer_perimeter_points =
m_perimeter_points_per_object[po][layer_idx];
if (layer_perimeter_points[seam_index].perimeter->finalized) {
const std::vector<SeamCandidate> &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<std::pair<size_t, size_t>> seam_string { std::pair<size_t, size_t>(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<size_t>(0, m_perimeter_points_per_object[po].size()),
[&](tbb::blocked_range<size_t> r) {
std::vector<PrintObjectSeamData::LayerSeams> &layers = m_seam_per_object[po].layers;
tbb::parallel_for(tbb::blocked_range<size_t>(0, layers.size()),
[&layers, configured_seam_preference, comparator](tbb::blocked_range<size_t> r) {
for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) {
std::vector<SeamCandidate> &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<SeamCandidate> &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<float>();
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<float>(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<float>());
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<float>(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.

View file

@ -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> 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> 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<SeamPlacerImpl::Perimeter> perimeters;
std::vector<SeamPlacerImpl::SeamCandidate> points;
std::unique_ptr<SeamCandidatesTree> points_tree;
};
// Map of PrintObjects (PO) -> vector of layers of PO -> vector of perimeter
std::vector<LayerSeams> 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<const PrintObject*, std::vector<std::vector<SeamPlacerImpl::SeamCandidate>>> 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<const PrintObject*, std::vector<std::unique_ptr<SeamCandidatesTree>>> m_perimeter_points_trees_per_object;
//The following data structures hold all perimeter points for all PrintObject.
std::unordered_map<const PrintObject*, PrintObjectSeamData> m_seam_per_object;
void init(const Print &print);