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:
parent
3b8cfc62da
commit
8ce36e9137
3 changed files with 158 additions and 156 deletions
|
@ -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();
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue