Prusa Slicer 2.6.0
Loading...
Searching...
No Matches
Slic3r::SeamPlacerImpl Namespace Reference

Classes

struct  CoordinateFunctor
 
class  Frame
 Coordinate frame. More...
 
struct  GlobalModelInfo
 
struct  Perimeter
 
struct  SeamCandidate
 
struct  SeamCandidateCoordinateFunctor
 
struct  SeamComparator
 

Enumerations

enum class  EnforcedBlockedSeamPoint { Blocked = 0 , Neutral = 1 , Enforced = 2 }
 

Functions

template<typename T >
int sgn (T val)
 
float gauss (float value, float mean_x_coord, float mean_value, float falloff_speed)
 
float compute_angle_penalty (float ccw_angle)
 
Vec3f sample_sphere_uniform (const Vec2f &samples)
 
Vec3f sample_hemisphere_uniform (const Vec2f &samples)
 
Vec3f sample_power_cosine_hemisphere (const Vec2f &samples, float power)
 
std::vector< float > raycast_visibility (const AABBTreeIndirect::Tree< 3, float > &raycasting_tree, const indexed_triangle_set &triangles, const TriangleSetSamples &samples, size_t negative_volumes_start_index)
 
std::vector< float > calculate_polygon_angles_at_vertices (const Polygon &polygon, const std::vector< float > &lengths, float min_arm_length)
 
Polygons extract_perimeter_polygons (const Layer *layer, std::vector< const LayerRegion * > &corresponding_regions_out)
 
void process_perimeter_polygon (const Polygon &orig_polygon, float z_coord, const LayerRegion *region, const GlobalModelInfo &global_model_info, PrintObjectSeamData::LayerSeams &result)
 
std::pair< size_t, size_t > find_previous_and_next_perimeter_point (const std::vector< SeamCandidate > &perimeter_points, size_t point_index)
 
void compute_global_occlusion (GlobalModelInfo &result, const PrintObject *po, std::function< void(void)> throw_if_canceled)
 
void gather_enforcers_blockers (GlobalModelInfo &result, const PrintObject *po)
 
void pick_seam_point (std::vector< SeamCandidate > &perimeter_points, size_t start_index, const SeamComparator &comparator)
 
size_t pick_nearest_seam_point_index (const std::vector< SeamCandidate > &perimeter_points, size_t start_index, const Vec2f &preffered_location)
 
void pick_random_seam_point (const std::vector< SeamCandidate > &perimeter_points, size_t start_index)
 

Class Documentation

◆ Slic3r::SeamPlacerImpl::Perimeter

struct Slic3r::SeamPlacerImpl::Perimeter
+ Collaboration diagram for Slic3r::SeamPlacerImpl::Perimeter:
Class Members
size_t end_index {}
Vec3f final_seam_position = Vec3f::Zero()
bool finalized = false
float flow_width {}
size_t seam_index {}
size_t start_index {}

Enumeration Type Documentation

◆ EnforcedBlockedSeamPoint

Function Documentation

◆ calculate_polygon_angles_at_vertices()

std::vector< float > Slic3r::SeamPlacerImpl::calculate_polygon_angles_at_vertices ( const Polygon polygon,
const std::vector< float > &  lengths,
float  min_arm_length 
)
225 {
226 std::vector<float> result(polygon.size());
227
228 if (polygon.size() == 1) {
229 result[0] = 0.0f;
230 }
231
232 size_t idx_prev = 0;
233 size_t idx_curr = 0;
234 size_t idx_next = 0;
235
236 float distance_to_prev = 0;
237 float distance_to_next = 0;
238
239 //push idx_prev far enough back as initialization
240 while (distance_to_prev < min_arm_length) {
241 idx_prev = Slic3r::prev_idx_modulo(idx_prev, polygon.size());
242 distance_to_prev += lengths[idx_prev];
243 }
244
245 for (size_t _i = 0; _i < polygon.size(); ++_i) {
246 // pull idx_prev to current as much as possible, while respecting the min_arm_length
247 while (distance_to_prev - lengths[idx_prev] > min_arm_length) {
248 distance_to_prev -= lengths[idx_prev];
249 idx_prev = Slic3r::next_idx_modulo(idx_prev, polygon.size());
250 }
251
252 //push idx_next forward as far as needed
253 while (distance_to_next < min_arm_length) {
254 distance_to_next += lengths[idx_next];
255 idx_next = Slic3r::next_idx_modulo(idx_next, polygon.size());
256 }
257
258 // Calculate angle between idx_prev, idx_curr, idx_next.
259 const Point &p0 = polygon.points[idx_prev];
260 const Point &p1 = polygon.points[idx_curr];
261 const Point &p2 = polygon.points[idx_next];
262 result[idx_curr] = float(angle(p1 - p0, p2 - p1));
263
264 // increase idx_curr by one
265 float curr_distance = lengths[idx_curr];
266 idx_curr++;
267 distance_to_prev += curr_distance;
268 distance_to_next -= curr_distance;
269 }
270
271 return result;
272}
Points points
Definition MultiPoint.hpp:18
size_t size() const
Definition MultiPoint.hpp:39
double angle(const Eigen::MatrixBase< Derived > &v1, const Eigen::MatrixBase< Derived2 > &v2)
Definition Point.hpp:112
INDEX_TYPE prev_idx_modulo(INDEX_TYPE idx, const INDEX_TYPE count)
Definition Utils.hpp:195
INDEX_TYPE next_idx_modulo(INDEX_TYPE idx, const INDEX_TYPE count)
Definition Utils.hpp:203
Kernel::Point_2 Point
Definition point_areas.cpp:20

References Slic3r::angle(), calculate_polygon_angles_at_vertices(), Slic3r::next_idx_modulo(), Slic3r::MultiPoint::points, Slic3r::prev_idx_modulo(), and Slic3r::MultiPoint::size().

Referenced by calculate_polygon_angles_at_vertices(), and process_perimeter_polygon().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ compute_angle_penalty()

float Slic3r::SeamPlacerImpl::compute_angle_penalty ( float  ccw_angle)
52 {
53 // This function is used:
54 // ((ℯ^(((1)/(x^(2)*3+1)))-1)/(ℯ-1))*1+((1)/(2+ℯ^(-x)))
55 // looks scary, but it is gaussian combined with sigmoid,
56 // so that concave points have much smaller penalty over convex ones
57 // https://github.com/prusa3d/PrusaSlicer/tree/master/doc/seam_placement/corner_penalty_function.png
58 return gauss(ccw_angle, 0.0f, 1.0f, 3.0f) +
59 1.0f / (2 + std::exp(-ccw_angle));
60}
float gauss(float value, float mean_x_coord, float mean_value, float falloff_speed)
Definition SeamPlacer.cpp:45

References gauss().

Referenced by Slic3r::SeamPlacerImpl::SeamComparator::is_first_not_much_worse().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ compute_global_occlusion()

void Slic3r::SeamPlacerImpl::compute_global_occlusion ( GlobalModelInfo result,
const PrintObject po,
std::function< void(void)>  throw_if_canceled 
)
621 {
622 BOOST_LOG_TRIVIAL(debug)
623 << "SeamPlacer: gather occlusion meshes: start";
624 auto obj_transform = po->trafo_centered();
625 indexed_triangle_set triangle_set;
626 indexed_triangle_set negative_volumes_set;
627 //add all parts
628 for (const ModelVolume *model_volume : po->model_object()->volumes) {
629 if (model_volume->type() == ModelVolumeType::MODEL_PART
630 || model_volume->type() == ModelVolumeType::NEGATIVE_VOLUME) {
631 auto model_transformation = model_volume->get_matrix();
632 indexed_triangle_set model_its = model_volume->mesh().its;
633 its_transform(model_its, model_transformation);
634 if (model_volume->type() == ModelVolumeType::MODEL_PART) {
635 its_merge(triangle_set, model_its);
636 } else {
637 its_merge(negative_volumes_set, model_its);
638 }
639 }
640 }
641 throw_if_canceled();
642
643 BOOST_LOG_TRIVIAL(debug)
644 << "SeamPlacer: gather occlusion meshes: end";
645
646 BOOST_LOG_TRIVIAL(debug)
647 << "SeamPlacer: decimate: start";
648 its_short_edge_collpase(triangle_set, SeamPlacer::fast_decimation_triangle_count_target);
649 its_short_edge_collpase(negative_volumes_set, SeamPlacer::fast_decimation_triangle_count_target);
650
651 size_t negative_volumes_start_index = triangle_set.indices.size();
652 its_merge(triangle_set, negative_volumes_set);
653 its_transform(triangle_set, obj_transform);
654 BOOST_LOG_TRIVIAL(debug)
655 << "SeamPlacer: decimate: end";
656
657 BOOST_LOG_TRIVIAL(debug)
658 << "SeamPlacer: Compute visibility sample points: start";
659
660 result.mesh_samples = sample_its_uniform_parallel(SeamPlacer::raycasting_visibility_samples_count,
661 triangle_set);
662 result.mesh_samples_coordinate_functor = CoordinateFunctor(&result.mesh_samples.positions);
663 result.mesh_samples_tree = KDTreeIndirect<3, float, CoordinateFunctor>(result.mesh_samples_coordinate_functor,
664 result.mesh_samples.positions.size());
665
666 // The following code determines search area for random visibility samples on the mesh when calculating visibility of each perimeter point
667 // number of random samples in the given radius (area) is approximately poisson distribution
668 // to compute ideal search radius (area), we use exponential distribution (complementary distr to poisson)
669 // parameters of exponential distribution to compute area that will have with probability="probability" more than given number of samples="samples"
670 float probability = 0.9f;
671 float samples = 4;
672 float density = SeamPlacer::raycasting_visibility_samples_count / result.mesh_samples.total_area;
673 // exponential probability distrubtion function is : f(x) = P(X > x) = e^(l*x) where l is the rate parameter (computed as 1/u where u is mean value)
674 // probability that sampled area A with S samples contains more than samples count:
675 // P(S > samples in A) = e^-(samples/(density*A)); express A:
676 float search_area = samples / (-logf(probability) * density);
677 float search_radius = sqrt(search_area / PI);
678 result.mesh_samples_radius = search_radius;
679
680 BOOST_LOG_TRIVIAL(debug)
681 << "SeamPlacer: Compute visiblity sample points: end";
682 throw_if_canceled();
683
684 BOOST_LOG_TRIVIAL(debug)
685 << "SeamPlacer: Mesh sample raidus: " << result.mesh_samples_radius;
686
687 BOOST_LOG_TRIVIAL(debug)
688 << "SeamPlacer: build AABB tree: start";
689 auto raycasting_tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(triangle_set.vertices,
690 triangle_set.indices);
691
692 throw_if_canceled();
693 BOOST_LOG_TRIVIAL(debug)
694 << "SeamPlacer: build AABB tree: end";
695 result.mesh_samples_visibility = raycast_visibility(raycasting_tree, triangle_set, result.mesh_samples,
696 negative_volumes_start_index);
697 throw_if_canceled();
698#ifdef DEBUG_FILES
699 result.debug_export(triangle_set);
700#endif
701}
EIGEN_DEVICE_FUNC const SqrtReturnType sqrt() const
Definition ArrayCwiseUnaryOps.h:152
Definition Model.hpp:753
Transform3d trafo_centered() const
Definition Print.hpp:251
static constexpr double PI
Definition libslic3r.h:58
std::vector< float > raycast_visibility(const AABBTreeIndirect::Tree< 3, float > &raycasting_tree, const indexed_triangle_set &triangles, const TriangleSetSamples &samples, size_t negative_volumes_start_index)
Definition SeamPlacer.cpp:129
std::vector< Vec3f > positions
Definition TriangleSetSampling.hpp:11
float total_area
Definition TriangleSetSampling.hpp:10
void its_short_edge_collpase(indexed_triangle_set &mesh, size_t target_triangle_count)
Definition ShortEdgeCollapse.cpp:13
TriangleSetSamples sample_its_uniform_parallel(size_t samples_count, const indexed_triangle_set &triangle_set)
Definition TriangleSetSampling.cpp:9
void its_merge(indexed_triangle_set &its, indexed_triangle_set &&its_add)
Merge one triangle mesh to another Added triangle set will be consumed.
Definition TriangleMesh.cpp:1355
void its_transform(indexed_triangle_set &its, T *trafo3x4)
Definition stl.h:266
KDTreeIndirect< 3, float, CoordinateFunctor > mesh_samples_tree
Definition SeamPlacer.cpp:293
float mesh_samples_radius
Definition SeamPlacer.cpp:294
std::vector< float > mesh_samples_visibility
Definition SeamPlacer.cpp:291
TriangleSetSamples mesh_samples
Definition SeamPlacer.cpp:290
CoordinateFunctor mesh_samples_coordinate_functor
Definition SeamPlacer.cpp:292
Definition stl.h:157
std::vector< stl_vertex > vertices
Definition stl.h:165
std::vector< stl_triangle_vertex_indices > indices
Definition stl.h:164

References Slic3r::AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(), compute_global_occlusion(), Slic3r::SeamPlacer::fast_decimation_triangle_count_target, indexed_triangle_set::indices, Slic3r::its_merge(), Slic3r::its_short_edge_collpase(), its_transform(), Slic3r::SeamPlacerImpl::GlobalModelInfo::mesh_samples, Slic3r::SeamPlacerImpl::GlobalModelInfo::mesh_samples_coordinate_functor, Slic3r::SeamPlacerImpl::GlobalModelInfo::mesh_samples_radius, Slic3r::SeamPlacerImpl::GlobalModelInfo::mesh_samples_tree, Slic3r::SeamPlacerImpl::GlobalModelInfo::mesh_samples_visibility, Slic3r::PrintObjectBase::model_object(), Slic3r::MODEL_PART, Slic3r::NEGATIVE_VOLUME, PI, Slic3r::TriangleSetSamples::positions, raycast_visibility(), Slic3r::SeamPlacer::raycasting_visibility_samples_count, Slic3r::sample_its_uniform_parallel(), sqrt(), Slic3r::TriangleSetSamples::total_area, Slic3r::PrintObject::trafo_centered(), indexed_triangle_set::vertices, and Slic3r::ModelObject::volumes.

Referenced by compute_global_occlusion().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ extract_perimeter_polygons()

Polygons Slic3r::SeamPlacerImpl::extract_perimeter_polygons ( const Layer layer,
std::vector< const LayerRegion * > &  corresponding_regions_out 
)
401 {
402 Polygons polygons;
403 for (const LayerRegion *layer_region : layer->regions()) {
404 for (const ExtrusionEntity *ex_entity : layer_region->perimeters()) {
405 if (ex_entity->is_collection()) { //collection of inner, outer, and overhang perimeters
406 for (const ExtrusionEntity *perimeter : static_cast<const ExtrusionEntityCollection*>(ex_entity)->entities) {
407 ExtrusionRole role = perimeter->role();
408 if (perimeter->is_loop()) {
409 for (const ExtrusionPath &path : static_cast<const ExtrusionLoop*>(perimeter)->paths) {
410 if (path.role() == ExtrusionRole::ExternalPerimeter) {
411 role = ExtrusionRole::ExternalPerimeter;
412 }
413 }
414 }
415
416 if (role == ExtrusionRole::ExternalPerimeter) {
417 Points p;
418 perimeter->collect_points(p);
419 polygons.emplace_back(std::move(p));
420 corresponding_regions_out.push_back(layer_region);
421 }
422 }
423 if (polygons.empty()) {
424 Points p;
425 ex_entity->collect_points(p);
426 polygons.emplace_back(std::move(p));
427 corresponding_regions_out.push_back(layer_region);
428 }
429 } else {
430 Points p;
431 ex_entity->collect_points(p);
432 polygons.emplace_back(std::move(p));
433 corresponding_regions_out.push_back(layer_region);
434 }
435 }
436 }
437
438 if (polygons.empty()) { // If there are no perimeter polygons for whatever reason (disabled perimeters .. ) insert dummy point
439 // it is easier than checking everywhere if the layer is not emtpy, no seam will be placed to this layer anyway
440 polygons.emplace_back(Points{ { 0, 0 } });
441 corresponding_regions_out.push_back(nullptr);
442 }
443
444 return polygons;
445}
Definition ExtrusionEntityCollection.hpp:26
Definition ExtrusionEntity.hpp:21
Definition ExtrusionEntity.hpp:191
Definition ExtrusionEntity.hpp:61
Definition Layer.hpp:95
#define const
Definition getopt.c:38
std::vector< Polygon, PointsAllocator< Polygon > > Polygons
Definition Polygon.hpp:15
std::vector< Point, PointsAllocator< Point > > Points
Definition Point.hpp:58
Definition ExtrusionRole.hpp:43

References Slic3r::ExtrusionRole::ExternalPerimeter, extract_perimeter_polygons(), and Slic3r::Layer::regions().

Referenced by extract_perimeter_polygons().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ find_previous_and_next_perimeter_point()

std::pair< size_t, size_t > Slic3r::SeamPlacerImpl::find_previous_and_next_perimeter_point ( const std::vector< SeamCandidate > &  perimeter_points,
size_t  point_index 
)
599 {
600 const SeamCandidate &current = perimeter_points[point_index];
601 int prev = point_index - 1; //for majority of points, it is true that neighbours lie behind and in front of them in the vector
602 int next = point_index + 1;
603
604 if (point_index == current.perimeter.start_index) {
605 // if point_index is equal to start, it means that the previous neighbour is at the end
606 prev = current.perimeter.end_index;
607 }
608
609 if (point_index == current.perimeter.end_index - 1) {
610 // if point_index is equal to end, than next neighbour is at the start
611 next = current.perimeter.start_index;
612 }
613
614 assert(prev >= 0);
615 assert(next >= 0);
616 return {size_t(prev),size_t(next)};
617}
size_t end_index
Definition SeamPlacer.hpp:43
size_t start_index
Definition SeamPlacer.hpp:42
Definition SeamPlacer.hpp:57
Perimeter & perimeter
Definition SeamPlacer.hpp:66

References Slic3r::SeamPlacerImpl::Perimeter::end_index, find_previous_and_next_perimeter_point(), Slic3r::SeamPlacerImpl::SeamCandidate::perimeter, and Slic3r::SeamPlacerImpl::Perimeter::start_index.

Referenced by find_previous_and_next_perimeter_point().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ gather_enforcers_blockers()

void Slic3r::SeamPlacerImpl::gather_enforcers_blockers ( GlobalModelInfo result,
const PrintObject po 
)
703 {
704 BOOST_LOG_TRIVIAL(debug)
705 << "SeamPlacer: build AABB trees for raycasting enforcers/blockers: start";
706
707 auto obj_transform = po->trafo_centered();
708
709 for (const ModelVolume *mv : po->model_object()->volumes) {
710 if (mv->is_seam_painted()) {
711 auto model_transformation = obj_transform * mv->get_matrix();
712
713 indexed_triangle_set enforcers = mv->seam_facets.get_facets(*mv, EnforcerBlockerType::ENFORCER);
714 its_transform(enforcers, model_transformation);
715 its_merge(result.enforcers, enforcers);
716
717 indexed_triangle_set blockers = mv->seam_facets.get_facets(*mv, EnforcerBlockerType::BLOCKER);
718 its_transform(blockers, model_transformation);
719 its_merge(result.blockers, blockers);
720 }
721 }
722
723 result.enforcers_tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(result.enforcers.vertices,
724 result.enforcers.indices);
725 result.blockers_tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(result.blockers.vertices,
726 result.blockers.indices);
727
728 BOOST_LOG_TRIVIAL(debug)
729 << "SeamPlacer: build AABB trees for raycasting enforcers/blockers: end";
730}
indexed_triangle_set enforcers
Definition SeamPlacer.cpp:296
AABBTreeIndirect::Tree< 3, float > blockers_tree
Definition SeamPlacer.cpp:299
AABBTreeIndirect::Tree< 3, float > enforcers_tree
Definition SeamPlacer.cpp:298
indexed_triangle_set blockers
Definition SeamPlacer.cpp:297

References Slic3r::BLOCKER, Slic3r::SeamPlacerImpl::GlobalModelInfo::blockers, Slic3r::SeamPlacerImpl::GlobalModelInfo::blockers_tree, Slic3r::AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(), Slic3r::ENFORCER, Slic3r::SeamPlacerImpl::GlobalModelInfo::enforcers, Slic3r::SeamPlacerImpl::GlobalModelInfo::enforcers_tree, gather_enforcers_blockers(), indexed_triangle_set::indices, Slic3r::its_merge(), its_transform(), Slic3r::PrintObjectBase::model_object(), Slic3r::PrintObject::trafo_centered(), indexed_triangle_set::vertices, and Slic3r::ModelObject::volumes.

Referenced by gather_enforcers_blockers().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ gauss()

float Slic3r::SeamPlacerImpl::gauss ( float  value,
float  mean_x_coord,
float  mean_value,
float  falloff_speed 
)
45 {
46 float shifted = value - mean_x_coord;
47 float denominator = falloff_speed * shifted * shifted + 1.0f;
48 float exponent = 1.0f / denominator;
49 return mean_value * (std::exp(exponent) - 1.0f) / (std::exp(1.0f) - 1.0f);
50}

Referenced by compute_angle_penalty().

+ Here is the caller graph for this function:

◆ pick_nearest_seam_point_index()

size_t Slic3r::SeamPlacerImpl::pick_nearest_seam_point_index ( const std::vector< SeamCandidate > &  perimeter_points,
size_t  start_index,
const Vec2f preffered_location 
)
921 {
922 size_t end_index = perimeter_points[start_index].perimeter.end_index;
923 SeamComparator comparator { spNearest };
924
925 size_t seam_index = start_index;
926 for (size_t index = start_index; index < end_index; ++index) {
927 if (comparator.is_first_better(perimeter_points[index], perimeter_points[seam_index], preffered_location)) {
928 seam_index = index;
929 }
930 }
931 return seam_index;
932}
@ spNearest
Definition PrintConfig.hpp:98
Definition SeamPlacer.cpp:732

References pick_nearest_seam_point_index(), and Slic3r::spNearest.

Referenced by pick_nearest_seam_point_index().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ pick_random_seam_point()

void Slic3r::SeamPlacerImpl::pick_random_seam_point ( const std::vector< SeamCandidate > &  perimeter_points,
size_t  start_index 
)
935 {
936 SeamComparator comparator { spRandom };
937
938 // algorithm keeps a list of viable points and their lengths. If it finds a point
939 // that is much better than the viable_example_index (e.g. better type, no overhang; see is_first_not_much_worse)
940 // then it throws away stored lists and starts from start
941 // in the end, the list should contain points with same type (Enforced > Neutral > Blocked) and also only those which are not
942 // big overhang.
943 size_t viable_example_index = start_index;
944 size_t end_index = perimeter_points[start_index].perimeter.end_index;
945 struct Viable {
946 // Candidate seam point index.
947 size_t index;
948 float edge_length;
949 Vec3f edge;
950 };
951 std::vector<Viable> viables;
952
953 const Vec3f pseudornd_seed = perimeter_points[viable_example_index].position;
954 float rand = std::abs(sin(pseudornd_seed.dot(Vec3f(12.9898f,78.233f, 133.3333f))) * 43758.5453f);
955 rand = rand - (int) rand;
956
957 for (size_t index = start_index; index < end_index; ++index) {
958 if (comparator.are_similar(perimeter_points[index], perimeter_points[viable_example_index])) {
959 // index ok, push info into viables
960 Vec3f edge_to_next { perimeter_points[index == end_index - 1 ? start_index : index + 1].position
961 - perimeter_points[index].position };
962 float dist_to_next = edge_to_next.norm();
963 viables.push_back( { index, dist_to_next, edge_to_next });
964 } else if (comparator.is_first_not_much_worse(perimeter_points[viable_example_index],
965 perimeter_points[index])) {
966 // index is worse then viable_example_index, skip this point
967 } else {
968 // index is better than viable example index, update example, clear gathered info, start again
969 // clear up all gathered info, start from scratch, update example index
970 viable_example_index = index;
971 viables.clear();
972
973 Vec3f edge_to_next = (perimeter_points[index == end_index - 1 ? start_index : index + 1].position
974 - perimeter_points[index].position);
975 float dist_to_next = edge_to_next.norm();
976 viables.push_back( { index, dist_to_next, edge_to_next });
977 }
978 }
979
980 // now pick random point from the stored options
981 float len_sum = std::accumulate(viables.begin(), viables.end(), 0.0f, [](const float acc, const Viable &v) {
982 return acc + v.edge_length;
983 });
984 float picked_len = len_sum * rand;
985
986 size_t point_idx = 0;
987 while (picked_len - viables[point_idx].edge_length > 0) {
988 picked_len = picked_len - viables[point_idx].edge_length;
989 point_idx++;
990 }
991
992 Perimeter &perimeter = perimeter_points[start_index].perimeter;
993 perimeter.seam_index = viables[point_idx].index;
994 perimeter.final_seam_position = perimeter_points[perimeter.seam_index].position
995 + viables[point_idx].edge.normalized() * picked_len;
996 perimeter.finalized = true;
997}
EIGEN_DEVICE_FUNC const SinReturnType sin() const
Definition ArrayCwiseUnaryOps.h:220
@ spRandom
Definition PrintConfig.hpp:98
Eigen::Matrix< float, 3, 1, Eigen::DontAlign > Vec3f
Definition Point.hpp:49

References Slic3r::SeamPlacerImpl::Perimeter::final_seam_position, Slic3r::SeamPlacerImpl::Perimeter::finalized, pick_random_seam_point(), Slic3r::SeamPlacerImpl::Perimeter::seam_index, sin(), and Slic3r::spRandom.

Referenced by pick_random_seam_point().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ pick_seam_point()

void Slic3r::SeamPlacerImpl::pick_seam_point ( std::vector< SeamCandidate > &  perimeter_points,
size_t  start_index,
const SeamComparator comparator 
)
908 {
909 size_t end_index = perimeter_points[start_index].perimeter.end_index;
910
911 size_t seam_index = start_index;
912 for (size_t index = start_index; index < end_index; ++index) {
913 if (comparator.is_first_better(perimeter_points[index], perimeter_points[seam_index])) {
914 seam_index = index;
915 }
916 }
917 perimeter_points[start_index].perimeter.seam_index = seam_index;
918}
bool is_first_better(const SeamCandidate &a, const SeamCandidate &b, const Vec2f &preffered_location=Vec2f { 0.0f, 0.0f }) const
Definition SeamPlacer.cpp:743

References Slic3r::SeamPlacerImpl::SeamComparator::is_first_better(), and pick_seam_point().

Referenced by pick_seam_point().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ process_perimeter_polygon()

void Slic3r::SeamPlacerImpl::process_perimeter_polygon ( const Polygon orig_polygon,
float  z_coord,
const LayerRegion region,
const GlobalModelInfo global_model_info,
PrintObjectSeamData::LayerSeams result 
)
452 {
453 if (orig_polygon.size() == 0) {
454 return;
455 }
456 Polygon polygon = orig_polygon;
457 bool was_clockwise = polygon.make_counter_clockwise();
458 float angle_arm_len = region != nullptr ? region->flow(FlowRole::frExternalPerimeter).nozzle_diameter() : 0.5f;
459
460 std::vector<float> lengths { };
461 for (size_t point_idx = 0; point_idx < polygon.size() - 1; ++point_idx) {
462 lengths.push_back((unscale(polygon[point_idx]) - unscale(polygon[point_idx + 1])).norm());
463 }
464 lengths.push_back(std::max((unscale(polygon[0]) - unscale(polygon[polygon.size() - 1])).norm(), 0.1));
465 std::vector<float> polygon_angles = calculate_polygon_angles_at_vertices(polygon, lengths,
466 angle_arm_len);
467
468 result.perimeters.push_back( { });
469 Perimeter &perimeter = result.perimeters.back();
470
471 std::queue<Vec3f> orig_polygon_points { };
472 for (size_t index = 0; index < polygon.size(); ++index) {
473 Vec2f unscaled_p = unscale(polygon[index]).cast<float>();
474 orig_polygon_points.emplace(unscaled_p.x(), unscaled_p.y(), z_coord);
475 }
476 Vec3f first = orig_polygon_points.front();
477 std::queue<Vec3f> oversampled_points { };
478 size_t orig_angle_index = 0;
479 perimeter.start_index = result.points.size();
480 perimeter.flow_width = region != nullptr ? region->flow(FlowRole::frExternalPerimeter).width() : 0.0f;
481 bool some_point_enforced = false;
482 while (!orig_polygon_points.empty() || !oversampled_points.empty()) {
483 EnforcedBlockedSeamPoint type = EnforcedBlockedSeamPoint::Neutral;
484 Vec3f position;
485 float local_ccw_angle = 0;
486 bool orig_point = false;
487 if (!oversampled_points.empty()) {
488 position = oversampled_points.front();
489 oversampled_points.pop();
490 } else {
491 position = orig_polygon_points.front();
492 orig_polygon_points.pop();
493 local_ccw_angle = was_clockwise ? -polygon_angles[orig_angle_index] : polygon_angles[orig_angle_index];
494 orig_angle_index++;
495 orig_point = true;
496 }
497
498 if (global_model_info.is_enforced(position, perimeter.flow_width)) {
499 type = EnforcedBlockedSeamPoint::Enforced;
500 }
501
502 if (global_model_info.is_blocked(position, perimeter.flow_width)) {
503 type = EnforcedBlockedSeamPoint::Blocked;
504 }
505 some_point_enforced = some_point_enforced || type == EnforcedBlockedSeamPoint::Enforced;
506
507 if (orig_point) {
508 Vec3f pos_of_next = orig_polygon_points.empty() ? first : orig_polygon_points.front();
509 float distance_to_next = (position - pos_of_next).norm();
510 if (global_model_info.is_enforced(position, distance_to_next)) {
511 Vec3f vec_to_next = (pos_of_next - position).normalized();
512 float step_size = SeamPlacer::enforcer_oversampling_distance;
513 float step = step_size;
514 while (step < distance_to_next) {
515 oversampled_points.push(position + vec_to_next * step);
516 step += step_size;
517 }
518 }
519 }
520
521 result.points.emplace_back(position, perimeter, local_ccw_angle, type);
522 }
523
524 perimeter.end_index = result.points.size();
525
526 if (some_point_enforced) {
527 // We will patches of enforced points (patch: continuous section of enforced points), choose
528 // the longest patch, and select the middle point or sharp point (depending on the angle)
529 // this point will have high priority on this perimeter
530 size_t perimeter_size = perimeter.end_index - perimeter.start_index;
531 const auto next_index = [&](size_t idx) {
532 return perimeter.start_index + Slic3r::next_idx_modulo(idx - perimeter.start_index, perimeter_size);
533 };
534
535 std::vector<size_t> patches_starts_ends;
536 for (size_t i = perimeter.start_index; i < perimeter.end_index; ++i) {
537 if (result.points[i].type != EnforcedBlockedSeamPoint::Enforced &&
538 result.points[next_index(i)].type == EnforcedBlockedSeamPoint::Enforced) {
539 patches_starts_ends.push_back(next_index(i));
540 }
541 if (result.points[i].type == EnforcedBlockedSeamPoint::Enforced &&
542 result.points[next_index(i)].type != EnforcedBlockedSeamPoint::Enforced) {
543 patches_starts_ends.push_back(next_index(i));
544 }
545 }
546 //if patches_starts_ends are empty, it means that the whole perimeter is enforced.. don't do anything in that case
547 if (!patches_starts_ends.empty()) {
548 //if the first point in the patches is not enforced, it marks a patch end. in that case, put it to the end and start on next
549 // to simplify the processing
550 assert(patches_starts_ends.size() % 2 == 0);
551 bool start_on_second = false;
552 if (result.points[patches_starts_ends[0]].type != EnforcedBlockedSeamPoint::Enforced) {
553 start_on_second = true;
554 patches_starts_ends.push_back(patches_starts_ends[0]);
555 }
556 //now pick the longest patch
557 std::pair<size_t, size_t> longest_patch { 0, 0 };
558 auto patch_len = [perimeter_size](const std::pair<size_t, size_t> &start_end) {
559 if (start_end.second < start_end.first) {
560 return start_end.first + (perimeter_size - start_end.second);
561 } else {
562 return start_end.second - start_end.first;
563 }
564 };
565 for (size_t patch_idx = start_on_second ? 1 : 0; patch_idx < patches_starts_ends.size(); patch_idx += 2) {
566 std::pair<size_t, size_t> current_patch { patches_starts_ends[patch_idx], patches_starts_ends[patch_idx
567 + 1] };
568 if (patch_len(longest_patch) < patch_len(current_patch)) {
569 longest_patch = current_patch;
570 }
571 }
572 std::vector<size_t> viable_points_indices;
573 std::vector<size_t> large_angle_points_indices;
574 for (size_t point_idx = longest_patch.first; point_idx != longest_patch.second;
575 point_idx = next_index(point_idx)) {
576 viable_points_indices.push_back(point_idx);
577 if (std::abs(result.points[point_idx].local_ccw_angle)
578 > SeamPlacer::sharp_angle_snapping_threshold) {
579 large_angle_points_indices.push_back(point_idx);
580 }
581 }
582 assert(viable_points_indices.size() > 0);
583 if (large_angle_points_indices.empty()) {
584 size_t central_idx = viable_points_indices[viable_points_indices.size() / 2];
585 result.points[central_idx].central_enforcer = true;
586 } else {
587 size_t central_idx = large_angle_points_indices.size() / 2;
588 result.points[large_angle_points_indices[central_idx]].central_enforcer = true;
589 }
590 }
591 }
592
593}
float width() const
Definition Flow.hpp:60
float nozzle_diameter() const
Definition Flow.hpp:69
Flow flow(FlowRole role) const
Definition LayerRegion.cpp:22
Definition Polygon.hpp:24
bool make_counter_clockwise()
Definition Polygon.cpp:78
EnforcedBlockedSeamPoint
Definition SeamPlacer.hpp:34
std::vector< float > calculate_polygon_angles_at_vertices(const Polygon &polygon, const std::vector< float > &lengths, float min_arm_length)
Definition SeamPlacer.cpp:224
T unscale(Q v)
Definition libslic3r.h:95
Eigen::Matrix< float, 2, 1, Eigen::DontAlign > Vec2f
Definition Point.hpp:48
Coord step(const Coord &crd, Dir d)
Definition MarchingSquares.hpp:137
std::vector< SeamPlacerImpl::SeamCandidate > points
Definition SeamPlacer.hpp:95
Slic3r::deque< SeamPlacerImpl::Perimeter > perimeters
Definition SeamPlacer.hpp:94
bool is_blocked(const Vec3f &position, float radius) const
Definition SeamPlacer.cpp:310
bool is_enforced(const Vec3f &position, float radius) const
Definition SeamPlacer.cpp:301

References calculate_polygon_angles_at_vertices(), Slic3r::SeamPlacerImpl::Perimeter::end_index, Slic3r::SeamPlacer::enforcer_oversampling_distance, Slic3r::LayerRegion::flow(), Slic3r::SeamPlacerImpl::Perimeter::flow_width, Slic3r::frExternalPerimeter, Slic3r::SeamPlacerImpl::GlobalModelInfo::is_blocked(), Slic3r::SeamPlacerImpl::GlobalModelInfo::is_enforced(), Slic3r::Polygon::make_counter_clockwise(), Slic3r::next_idx_modulo(), Slic3r::Flow::nozzle_diameter(), Slic3r::PrintObjectSeamData::LayerSeams::perimeters, Slic3r::PrintObjectSeamData::LayerSeams::points, process_perimeter_polygon(), Slic3r::SeamPlacer::sharp_angle_snapping_threshold, Slic3r::MultiPoint::size(), Slic3r::SeamPlacerImpl::Perimeter::start_index, Slic3r::unscale(), and Slic3r::Flow::width().

Referenced by process_perimeter_polygon().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ raycast_visibility()

std::vector< float > Slic3r::SeamPlacerImpl::raycast_visibility ( const AABBTreeIndirect::Tree< 3, float > &  raycasting_tree,
const indexed_triangle_set triangles,
const TriangleSetSamples samples,
size_t  negative_volumes_start_index 
)
132 {
133 BOOST_LOG_TRIVIAL(debug)
134 << "SeamPlacer: raycast visibility of " << samples.positions.size() << " samples over " << triangles.indices.size()
135 << " triangles: end";
136
137 //prepare uniform samples of a hemisphere
138 float step_size = 1.0f / SeamPlacer::sqr_rays_per_sample_point;
139 std::vector<Vec3f> precomputed_sample_directions(
140 SeamPlacer::sqr_rays_per_sample_point * SeamPlacer::sqr_rays_per_sample_point);
141 for (size_t x_idx = 0; x_idx < SeamPlacer::sqr_rays_per_sample_point; ++x_idx) {
142 float sample_x = x_idx * step_size + step_size / 2.0;
143 for (size_t y_idx = 0; y_idx < SeamPlacer::sqr_rays_per_sample_point; ++y_idx) {
144 size_t dir_index = x_idx * SeamPlacer::sqr_rays_per_sample_point + y_idx;
145 float sample_y = y_idx * step_size + step_size / 2.0;
146 precomputed_sample_directions[dir_index] = sample_hemisphere_uniform( { sample_x, sample_y });
147 }
148 }
149
150 bool model_contains_negative_parts = negative_volumes_start_index < triangles.indices.size();
151
152 std::vector<float> result(samples.positions.size());
153 tbb::parallel_for(tbb::blocked_range<size_t>(0, result.size()),
154 [&triangles, &precomputed_sample_directions, model_contains_negative_parts, negative_volumes_start_index,
155 &raycasting_tree, &result, &samples](tbb::blocked_range<size_t> r) {
156 // Maintaining hits memory outside of the loop, so it does not have to be reallocated for each query.
157 std::vector<igl::Hit> hits;
158 for (size_t s_idx = r.begin(); s_idx < r.end(); ++s_idx) {
159 result[s_idx] = 1.0f;
160 constexpr float decrease_step = 1.0f
161 / (SeamPlacer::sqr_rays_per_sample_point * SeamPlacer::sqr_rays_per_sample_point);
162
163 const Vec3f &center = samples.positions[s_idx];
164 const Vec3f &normal = samples.normals[s_idx];
165 // apply the local direction via Frame struct - the local_dir is with respect to +Z being forward
166 Frame f;
167 f.set_from_z(normal);
168
169 for (const auto &dir : precomputed_sample_directions) {
170 Vec3f final_ray_dir = (f.to_world(dir));
171 if (!model_contains_negative_parts) {
172 igl::Hit hitpoint;
173 // FIXME: This AABBTTreeIndirect query will not compile for float ray origin and
174 // direction.
175 Vec3d final_ray_dir_d = final_ray_dir.cast<double>();
176 Vec3d ray_origin_d = (center + normal * 0.01f).cast<double>(); // start above surface.
177 bool hit = AABBTreeIndirect::intersect_ray_first_hit(triangles.vertices,
178 triangles.indices, raycasting_tree, ray_origin_d, final_ray_dir_d, hitpoint);
179 if (hit && its_face_normal(triangles, hitpoint.id).dot(final_ray_dir) <= 0) {
180 result[s_idx] -= decrease_step;
181 }
182 } else { //TODO improve logic for order based boolean operations - consider order of volumes
183 bool casting_from_negative_volume = samples.triangle_indices[s_idx]
184 >= negative_volumes_start_index;
185
186 Vec3d ray_origin_d = (center + normal * 0.01f).cast<double>(); // start above surface.
187 if (casting_from_negative_volume) { // if casting from negative volume face, invert direction, change start pos
188 final_ray_dir = -1.0 * final_ray_dir;
189 ray_origin_d = (center - normal * 0.01f).cast<double>();
190 }
191 Vec3d final_ray_dir_d = final_ray_dir.cast<double>();
192 bool some_hit = AABBTreeIndirect::intersect_ray_all_hits(triangles.vertices,
193 triangles.indices, raycasting_tree,
194 ray_origin_d, final_ray_dir_d, hits);
195 if (some_hit) {
196 int counter = 0;
197 // NOTE: iterating in reverse, from the last hit for one simple reason: We know the state of the ray at that point;
198 // It cannot be inside model, and it cannot be inside negative volume
199 for (int hit_index = int(hits.size()) - 1; hit_index >= 0; --hit_index) {
200 Vec3f face_normal = its_face_normal(triangles, hits[hit_index].id);
201 if (hits[hit_index].id >= int(negative_volumes_start_index)) { //negative volume hit
202 counter -= sgn(face_normal.dot(final_ray_dir)); // if volume face aligns with ray dir, we are leaving negative space
203 // which in reverse hit analysis means, that we are entering negative space :) and vice versa
204 } else {
205 counter += sgn(face_normal.dot(final_ray_dir));
206 }
207 }
208 if (counter == 0) {
209 result[s_idx] -= decrease_step;
210 }
211 }
212 }
213 }
214 }
215 });
216
217 BOOST_LOG_TRIVIAL(debug)
218 << "SeamPlacer: raycast visibility of " << samples.positions.size() << " samples over " << triangles.indices.size()
219 << " triangles: end";
220
221 return result;
222}
Vec3f sample_hemisphere_uniform(const Vec2f &samples)
Definition SeamPlacer.cpp:114

References indexed_triangle_set::indices, Slic3r::TriangleSetSamples::positions, sample_hemisphere_uniform(), and Slic3r::SeamPlacer::sqr_rays_per_sample_point.

Referenced by compute_global_occlusion().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ sample_hemisphere_uniform()

Vec3f Slic3r::SeamPlacerImpl::sample_hemisphere_uniform ( const Vec2f samples)
114 {
115 float term1 = 2.0f * float(PI) * samples.x();
116 float term2 = 2.0f * sqrt(samples.y() - samples.y() * samples.y());
117 return {cos(term1) * term2, sin(term1) * term2,
118 abs(1.0f - 2.0f * samples.y())};
119}
EIGEN_DEVICE_FUNC const CosReturnType cos() const
Definition ArrayCwiseUnaryOps.h:202

References cos(), PI, sin(), and sqrt().

Referenced by raycast_visibility().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ sample_power_cosine_hemisphere()

Vec3f Slic3r::SeamPlacerImpl::sample_power_cosine_hemisphere ( const Vec2f samples,
float  power 
)
121 {
122 float term1 = 2.f * float(PI) * samples.x();
123 float term2 = pow(samples.y(), 1.f / (power + 1.f));
124 float term3 = sqrt(1.f - term2 * term2);
125
126 return Vec3f(cos(term1) * term3, sin(term1) * term3, term2);
127}

References cos(), PI, sin(), and sqrt().

+ Here is the call graph for this function:

◆ sample_sphere_uniform()

Vec3f Slic3r::SeamPlacerImpl::sample_sphere_uniform ( const Vec2f samples)
107 {
108 float term1 = 2.0f * float(PI) * samples.x();
109 float term2 = 2.0f * sqrt(samples.y() - samples.y() * samples.y());
110 return {cos(term1) * term2, sin(term1) * term2,
111 1.0f - 2.0f * samples.y()};
112}

References cos(), PI, sin(), and sqrt().

+ Here is the call graph for this function:

◆ sgn()

template<typename T >
int Slic3r::SeamPlacerImpl::sgn ( val)
39 {
40 return int(T(0) < val) - int(val < T(0));
41}