OrcaSlicer latest
OrcaSlicer
Loading...
Searching...
No Matches
Slic3r::SeamPlacer Class Reference

#include </home/runner/work/OrcaSlicer/OrcaSlicer/src/libslic3r/GCode/SeamPlacer.hpp>

Collaboration diagram for Slic3r::SeamPlacer:

Public Member Functions

void init (const Print &print, std::function< void(void)> throw_if_canceled_func)
const std::string & precise_seam_warning () const
void place_seam (const Layer *layer, ExtrusionLoop &loop, const Point &last_pos, float &overhang) const

Public Attributes

std::unordered_map< const PrintObject *, PrintObjectSeamData > m_seam_per_object

Static Public Attributes

static constexpr size_t raycasting_visibility_samples_count = 30000
static constexpr size_t fast_decimation_triangle_count_target = 16000
static constexpr size_t sqr_rays_per_sample_point = 5
static constexpr float sharp_angle_snapping_threshold = 55.0f * float(PI) / 180.0f
static constexpr float overhang_angle_threshold = 45.0f * float(PI) / 180.0f
static constexpr float angle_importance_aligned = 0.6f
static constexpr float angle_importance_nearest = 1.0f
static constexpr float enforcer_oversampling_distance = 0.2f
static constexpr float seam_align_score_tolerance = 0.3f
static constexpr float seam_align_tolerable_dist_factor = 4.0f
static constexpr size_t seam_align_minimum_string_seams = 6
static constexpr size_t seam_align_mm_per_segment = 4.0f

Private Member Functions

void gather_seam_candidates (const PrintObject *po, const SeamPlacerImpl::GlobalModelInfo &global_model_info, PreciseSeam::PreciseSeamWarnings *warnings=nullptr)
void calculate_candidates_visibility (const PrintObject *po, const SeamPlacerImpl::GlobalModelInfo &global_model_info)
void calculate_overhangs_and_layer_embedding (const PrintObject *po)
void align_seam_points (const PrintObject *po, const SeamPlacerImpl::SeamComparator &comparator, const std::function< void()> &throw_if_canceled)
std::vector< std::pair< size_t, size_t > > find_seam_string (const PrintObject *po, std::pair< size_t, size_t > start_seam, const SeamPlacerImpl::SeamComparator &comparator, const SeamContour::Resolver *contours=nullptr) const
std::optional< std::pair< size_t, size_t > > find_next_seam_in_layer (const std::vector< PrintObjectSeamData::LayerSeams > &layers, const Vec3f &projected_position, const size_t layer_idx, const float max_distance, const SeamPlacerImpl::SeamComparator &comparator, const SeamContour::Resolver *contours=nullptr, std::pair< size_t, size_t > previous={}) const

Private Attributes

std::string m_precise_seam_warning

Detailed Description

Member Function Documentation

◆ align_seam_points()

void Slic3r::SeamPlacer::align_seam_points ( const PrintObject * po,
const SeamPlacerImpl::SeamComparator & comparator,
const std::function< void()> & throw_if_canceled )
private
1884 {
1885 using namespace SeamPlacerImpl;
1886
1887 // Prepares Debug files for writing.
1888#ifdef DEBUG_FILES
1889 Slic3r::CNumericLocalesSetter locales_setter;
1890 auto clusters_f = debug_out_path("seam_clusters.obj");
1891 FILE *clusters = boost::nowide::fopen(clusters_f.c_str(), "w");
1892 if (clusters == nullptr) {
1893 BOOST_LOG_TRIVIAL(error)
1894 << "stl_write_obj: Couldn't open " << clusters_f << " for writing";
1895 return;
1896 }
1897 auto aligned_f = debug_out_path("aligned_clusters.obj");
1898 FILE *aligns = boost::nowide::fopen(aligned_f.c_str(), "w");
1899 if (aligns == nullptr) {
1900 BOOST_LOG_TRIVIAL(error)
1901 << "stl_write_obj: Couldn't open " << clusters_f << " for writing";
1902 return;
1903 }
1904#endif
1905
1906 //gather vector of all seams on the print_object - pair of layer_index and seam__index within that layer
1907 const std::vector<PrintObjectSeamData::LayerSeams> &layers = m_seam_per_object[po].layers;
1908 // Share the largest nozzle diameter between contour bounds and corner protection.
1909 double max_nozzle_diameter = 0.0;
1910 for (double diameter : po->print()->config().nozzle_diameter.values)
1911 max_nozzle_diameter = std::max(max_nozzle_diameter, diameter);
1912 // Reuse immutable contour geometry across all alternative string starts.
1913 std::unique_ptr<SeamContour::Resolver> contour_resolver;
1914 {
1915 SeamContour::Layers geometry(layers.size());
1916 for (size_t i = 0; i < layers.size(); ++i) {
1917 throw_if_canceled(); // Geometry copies on dense multi-island objects must remain interruptible.
1918 for (const Perimeter &perimeter : layers[i].perimeters) {
1919 SeamContour::Contour contour;
1920 contour.hole = perimeter.is_hole;
1921 contour.reliable = perimeter.contour_reliable;
1922 contour.width = perimeter.flow_width;
1923 contour.nozzle = max_nozzle_diameter;
1924 for (size_t j = perimeter.start_index; j < perimeter.end_index; ++j)
1925 contour.points.push_back(layers[i].points[j].position.head<2>().cast<double>());
1926 geometry[i].push_back(std::move(contour));
1927 }
1928 }
1929 contour_resolver = std::make_unique<SeamContour::Resolver>(std::move(geometry), SeamContour::Options{}, throw_if_canceled);
1930 }
1931 std::vector<std::pair<size_t, size_t>> seams;
1932 for (size_t layer_idx = 0; layer_idx < layers.size(); ++layer_idx) {
1933 const std::vector<SeamCandidate> &layer_perimeter_points = layers[layer_idx].points;
1934 size_t current_point_index = 0;
1935 while (current_point_index < layer_perimeter_points.size()) {
1936 seams.emplace_back(layer_idx, layer_perimeter_points[current_point_index].perimeter.seam_index);
1937 current_point_index = layer_perimeter_points[current_point_index].perimeter.end_index;
1938 }
1939 }
1940
1941 //sort them before alignment. Alignment is sensitive to initializaion, this gives it better chance to choose something nice
1942 std::stable_sort(seams.begin(), seams.end(),
1943 [&comparator, &layers](const std::pair<size_t, size_t> &left,
1944 const std::pair<size_t, size_t> &right) {
1945 return comparator.is_first_better(layers[left.first].points[left.second],
1946 layers[right.first].points[right.second]);
1947 }
1948 );
1949
1950 //align the seam points - start with the best, and check if they are aligned, if yes, skip, else start alignment
1951 // Keeping the vectors outside, so with a bit of luck they will not get reallocated after couple of for loop iterations.
1952 std::vector<std::pair<size_t, size_t>> seam_string;
1953 std::vector<std::pair<size_t, size_t>> alternative_seam_string;
1954 std::vector<Vec2f> observations;
1955 std::vector<float> observation_points;
1956 std::vector<float> weights;
1957
1958 int global_index = 0;
1959 while (global_index < int(seams.size())) {
1960 throw_if_canceled();
1961 size_t layer_idx = seams[global_index].first;
1962 size_t seam_index = seams[global_index].second;
1963 global_index++;
1964 const std::vector<SeamCandidate> &layer_perimeter_points = layers[layer_idx].points;
1965 if (layer_perimeter_points[seam_index].perimeter.finalized) {
1966 // This perimeter is already aligned, skip seam
1967 continue;
1968 } else {
1969 seam_string = this->find_seam_string(po, { layer_idx, seam_index }, comparator, contour_resolver.get());
1970 size_t step_size = 1 + seam_string.size() / 20;
1971 for (size_t alternative_start = 0; alternative_start < seam_string.size(); alternative_start += step_size) {
1972 throw_if_canceled();
1973 size_t start_layer_idx = seam_string[alternative_start].first;
1974 size_t seam_idx =
1975 layers[start_layer_idx].points[seam_string[alternative_start].second].perimeter.seam_index;
1976 alternative_seam_string = this->find_seam_string(po,
1977 std::pair<size_t, size_t>(start_layer_idx, seam_idx), comparator,
1978 contour_resolver.get());
1979 if (alternative_seam_string.size() > seam_string.size()) {
1980 seam_string = std::move(alternative_seam_string);
1981 }
1982 }
1983 if (seam_string.size() < seam_align_minimum_string_seams) {
1984 //string NOT long enough to be worth aligning, skip
1985 continue;
1986 }
1987
1988 // String is long enough, all string seams and potential string seams gathered, now do the alignment
1989 //sort by layer index
1990 std::sort(seam_string.begin(), seam_string.end(),
1991 [](const std::pair<size_t, size_t> &left, const std::pair<size_t, size_t> &right) {
1992 return left.first < right.first;
1993 });
1994
1995 //repeat the alignment for the current seam, since it could be skipped due to alternative path being aligned.
1996 global_index--;
1997
1998 // gather all positions of seams and their weights
1999 observations.resize(seam_string.size());
2000 observation_points.resize(seam_string.size());
2001 weights.resize(seam_string.size());
2002
2003 auto angle_3d = [](const Vec3f& a, const Vec3f& b){
2004 return std::abs(acosf(a.normalized().dot(b.normalized())));
2005 };
2006
2007 auto angle_weight = [](float angle){
2008 return 1.0f / (0.1f + compute_angle_penalty(angle));
2009 };
2010
2011 //gather points positions and weights
2012 float total_length = 0.0f;
2013 Vec3f last_point_pos = layers[seam_string[0].first].points[seam_string[0].second].position;
2014 for (size_t index = 0; index < seam_string.size(); ++index) {
2015 const SeamCandidate &current = layers[seam_string[index].first].points[seam_string[index].second];
2016 float layer_angle = 0.0f;
2017 if (index > 0 && index < seam_string.size() - 1) {
2018 layer_angle = angle_3d(
2019 current.position
2020 - layers[seam_string[index - 1].first].points[seam_string[index - 1].second].position,
2021 layers[seam_string[index + 1].first].points[seam_string[index + 1].second].position
2022 - current.position
2023 );
2024 }
2025 observations[index] = current.position.head<2>();
2026 observation_points[index] = current.position.z();
2027 weights[index] = angle_weight(current.local_ccw_angle);
2028 float curling_influence = layer_angle > 2.0 * std::abs(current.local_ccw_angle) ? -0.8f : 1.0f;
2029 if (current.type == EnforcedBlockedSeamPoint::Enforced) {
2030 curling_influence = 1.0f;
2031 weights[index] += 3.0f;
2032 }
2033 total_length += curling_influence * (last_point_pos - current.position).norm();
2034 last_point_pos = current.position;
2035 }
2036
2037 if (comparator.setup == spRear) {
2038 total_length *= 0.3f;
2039 }
2040
2041 // Curve Fitting
2042 size_t number_of_segments = std::max(size_t(1),
2043 size_t(std::max(0.0f,total_length) / SeamPlacer::seam_align_mm_per_segment));
2044 auto curve = Geometry::fit_cubic_bspline(observations, observation_points, weights, number_of_segments);
2045
2046 // Do alignment - compute fitted point for each point in the string from its Z coord, and store the position into
2047 // Perimeter structure of the point; also set flag aligned to true
2048 for (size_t index = 0; index < seam_string.size(); ++index) {
2049 const auto &pair = seam_string[index];
2050 const std::vector<SeamCandidate> &points = layers[pair.first].points;
2051 const SeamCandidate &candidate = points[pair.second];
2052 float t = std::min(1.0f, std::pow(std::abs(candidate.local_ccw_angle)
2054
2055 Vec3f current_pos = candidate.position;
2056 Vec2f fitted_pos = curve.get_fitted_value(current_pos.z());
2057
2058 //interpolate between current and fitted position, prefer current pos for large weights.
2059 Vec3f final_position;
2060 if (candidate.type != EnforcedBlockedSeamPoint::Enforced) {
2061 final_position = t >= 1.0f ? current_pos :
2062 place_on_loop(points, pair.second, t * current_pos + (1.0f - t) * to_3d(fitted_pos, current_pos.z()));
2063 } else if (t >= 1.0f) {
2064 final_position = current_pos;
2065 } else {
2066 // Keep the 0.4 pull toward the candidate near a measured corner only, then keep the seam on the paint.
2067 float protect = std::max(candidate.perimeter.flow_width, float(max_nozzle_diameter));
2068 if (protect <= 0.0f)
2069 protect = 0.5f;
2070 const float corner = std::abs(candidate.local_ccw_angle) >= enforced_corner_angle_max ?
2071 1.0f : enforced_corner_influence(points, pair.second, protect, enforced_corner_fade_factor * protect);
2072 t = std::max(t, 0.4f * corner);
2073 final_position = place_on_loop(points, pair.second,
2074 t * current_pos + (1.0f - t) * to_3d(fitted_pos, current_pos.z()));
2075 }
2076
2077 Perimeter &perimeter = layers[pair.first].points[pair.second].perimeter;
2078 perimeter.seam_index = pair.second;
2079 perimeter.final_seam_position = final_position;
2080 perimeter.finalized = true;
2081 }
2082
2083#ifdef DEBUG_FILES
2084 auto randf = []() {
2085 return float(rand()) / float(RAND_MAX);
2086 };
2087 Vec3f color { randf(), randf(), randf() };
2088 for (size_t i = 0; i < seam_string.size(); ++i) {
2089 auto orig_seam = layers[seam_string[i].first].points[seam_string[i].second];
2090 fprintf(clusters, "v %f %f %f %f %f %f \n", orig_seam.position[0],
2091 orig_seam.position[1],
2092 orig_seam.position[2], color[0], color[1],
2093 color[2]);
2094 }
2095
2096 color = Vec3f { randf(), randf(), randf() };
2097 for (size_t i = 0; i < seam_string.size(); ++i) {
2098 const Perimeter &perimeter = layers[seam_string[i].first].points[seam_string[i].second].perimeter;
2099 fprintf(aligns, "v %f %f %f %f %f %f \n", perimeter.final_seam_position[0],
2100 perimeter.final_seam_position[1],
2101 perimeter.final_seam_position[2], color[0], color[1],
2102 color[2]);
2103 }
2104#endif
2105 }
2106 }
2107
2108#ifdef DEBUG_FILES
2109 fclose(clusters);
2110 fclose(aligns);
2111#endif
2112
2113}
Points points
Definition MultiPoint.hpp:22
static constexpr size_t seam_align_mm_per_segment
Definition SeamPlacer.hpp:160
std::unordered_map< const PrintObject *, PrintObjectSeamData > m_seam_per_object
Definition SeamPlacer.hpp:163
std::vector< std::pair< size_t, size_t > > find_seam_string(const PrintObject *po, std::pair< size_t, size_t > start_seam, const SeamPlacerImpl::SeamComparator &comparator, const SeamContour::Resolver *contours=nullptr) const
Definition SeamPlacer.cpp:1824
static constexpr float sharp_angle_snapping_threshold
Definition SeamPlacer.hpp:141
static constexpr size_t seam_align_minimum_string_seams
Definition SeamPlacer.hpp:158
PiecewiseFittedCurve< Dimension, NumberType, CubicBSplineKernel< NumberType > > fit_cubic_bspline(const std::vector< Vec< Dimension, NumberType > > &observations, std::vector< NumberType > observation_points, std::vector< NumberType > weights, size_t segments_count, size_t endpoints_level_of_freedom=0)
Definition Curves.hpp:199
std::vector< std::vector< Contour > > Layers
Definition SeamContour.hpp:23
Vec3f place_on_loop(const std::vector< SeamCandidate > &points, size_t index, const Vec3f &target)
Definition SeamPlacer.cpp:1498
constexpr float enforced_corner_angle_max
Definition SeamPlacer.cpp:1451
float enforced_corner_influence(const std::vector< SeamCandidate > &points, size_t index, float protect, float fade_end)
Definition SeamPlacer.cpp:1462
float compute_angle_penalty(float ccw_angle)
Definition SeamPlacer.cpp:82
@ spRear
Definition PrintConfig.hpp:395
std::string debug_out_path(const char *name,...)
Definition utils.cpp:382
Eigen::Matrix< typename Derived::Scalar, 3, 1, Eigen::DontAlign > to_3d(const Eigen::MatrixBase< Derived > &pt, const typename Derived::Scalar z)
Definition Point.hpp:153
Eigen::Matrix< float, 3, 1, Eigen::DontAlign > Vec3f
Definition Point.hpp:65
double angle(const Eigen::MatrixBase< Derived > &v1, const Eigen::MatrixBase< Derived2 > &v2)
Definition Point.hpp:138
double total_length(const Polygons &polylines)
Definition Polygon.hpp:127
Eigen::Matrix< float, 2, 1, Eigen::DontAlign > Vec2f
Definition Point.hpp:64
const Polygon & contour(const ExPolygon &p)
Definition AGGRaster.hpp:30
@ Perimeter
Definition Types.hpp:146

References align_seam_points(), Slic3r::angle(), Slic3r::Print::config(), Slic3r::contour(), Slic3r::debug_out_path(), find_seam_string(), Slic3r::Geometry::fit_cubic_bspline(), m_seam_per_object, Slic3r::PrintObjectBaseWithState< PrintType, PrintObjectStepEnum, COUNT >::print(), seam_align_minimum_string_seams, seam_align_mm_per_segment, Slic3r::SeamPlacerImpl::SeamComparator::setup, sharp_angle_snapping_threshold, Slic3r::spRear, Slic3r::to_3d(), and Slic3r::total_length().

Referenced by align_seam_points(), and init().

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

◆ calculate_candidates_visibility()

void Slic3r::SeamPlacer::calculate_candidates_visibility ( const PrintObject * po,
const SeamPlacerImpl::GlobalModelInfo & global_model_info )
private
1669 {
1670
1671 std::vector<PrintObjectSeamData::LayerSeams> &layers = m_seam_per_object[po].layers;
1672 tbb::parallel_for(tbb::blocked_range<size_t>(0, layers.size()),
1673 [&layers, &global_model_info](tbb::blocked_range<size_t> r) {
1674 for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) {
1675 for (auto &perimeter_point : layers[layer_idx].points) {
1676 perimeter_point.visibility = global_model_info.calculate_point_visibility(
1677 perimeter_point.position);
1678 }
1679 }
1680 });
1681}

References calculate_candidates_visibility(), and m_seam_per_object.

Referenced by calculate_candidates_visibility(), and init().

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

◆ calculate_overhangs_and_layer_embedding()

void Slic3r::SeamPlacer::calculate_overhangs_and_layer_embedding ( const PrintObject * po)
private
1683 {
1684 using namespace SeamPlacerImpl;
1685 using PerimeterDistancer = AABBTreeLines::LinesDistancer<Linef>;
1686
1687 std::vector<PrintObjectSeamData::LayerSeams> &layers = m_seam_per_object[po].layers;
1688 tbb::parallel_for(tbb::blocked_range<size_t>(0, layers.size()),
1689 [po, &layers](tbb::blocked_range<size_t> r) {
1690 std::unique_ptr<PerimeterDistancer> prev_layer_distancer;
1691 if (r.begin() > 0) { // previous layer exists
1692 prev_layer_distancer = std::make_unique<PerimeterDistancer>(to_unscaled_linesf(po->layers()[r.begin() - 1]->lslices));
1693 }
1694
1695 for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) {
1696 size_t regions_with_perimeter = 0;
1697 for (const LayerRegion *region : po->layers()[layer_idx]->regions()) {
1698 if (region->perimeters.entities.size() > 0) {
1699 regions_with_perimeter++;
1700 }
1701 };
1702 bool should_compute_layer_embedding = regions_with_perimeter > 1;
1703 std::unique_ptr<PerimeterDistancer> current_layer_distancer = std::make_unique<PerimeterDistancer>(
1704 to_unscaled_linesf(po->layers()[layer_idx]->lslices));
1705
1706 auto& layer_seams = layers[layer_idx];
1707 for (SeamCandidate &perimeter_point : layer_seams.points) {
1708 Vec2f point = Vec2f { perimeter_point.position.head<2>() };
1709 if (prev_layer_distancer.get() != nullptr) {
1710 const auto _dist = prev_layer_distancer->distance_from_lines<true>(point.cast<double>());
1711 perimeter_point.overhang = _dist
1712 + 0.65f * perimeter_point.perimeter.flow_width
1713 - tan(SeamPlacer::overhang_angle_threshold)
1714 * po->layers()[layer_idx]->height;
1715 perimeter_point.overhang =
1716 perimeter_point.overhang < 0.0f ? 0.0f : perimeter_point.overhang;
1717 perimeter_point.unsupported_dist = _dist + 0.4f * perimeter_point.perimeter.flow_width;
1718 }
1719
1720 if (should_compute_layer_embedding) { // search for embedded perimeter points (points hidden inside the print ,e.g. multimaterial join, best position for seam)
1721 perimeter_point.embedded_distance = current_layer_distancer->distance_from_lines<true>(point.cast<double>())
1722 + 0.65f * perimeter_point.perimeter.flow_width;
1723 }
1724 }
1725
1726 prev_layer_distancer.swap(current_layer_distancer);
1727 }
1728 }
1729 );
1730}
Linesf to_unscaled_linesf(const ExPolygons &src)
Definition ExPolygon.hpp:182

References calculate_overhangs_and_layer_embedding(), Slic3r::PrintObject::layers(), m_seam_per_object, and Slic3r::to_unscaled_linesf().

Referenced by calculate_overhangs_and_layer_embedding(), and init().

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

◆ find_next_seam_in_layer()

std::optional< std::pair< size_t, size_t > > Slic3r::SeamPlacer::find_next_seam_in_layer ( const std::vector< PrintObjectSeamData::LayerSeams > & layers,
const Vec3f & projected_position,
const size_t layer_idx,
const float max_distance,
const SeamPlacerImpl::SeamComparator & comparator,
const SeamContour::Resolver * contours = nullptr,
std::pair< size_t, size_t > previous = {} ) const
private
1745 {
1746 using namespace SeamPlacerImpl;
1747 // Contours of the geometric continuation. Empty when there is no geometric match, which preserves the legacy radius
1748 // search; occupancy never expands a known set.
1749 std::vector<size_t> continuation;
1750 if (contours) {
1751 const Perimeter &source = layers[previous.first].points[previous.second].perimeter;
1752 const int direction = layer_idx > previous.first ? 1 : -1;
1753 continuation = contours->resolve(previous.first, source.contour_index,
1754 previous.second - source.start_index, direction, max_distance).contours;
1755 }
1756
1757 // Find the best nearby point and the nearest one. A layer of a fine relief has tens of thousands of candidates within
1758 // the radius, so they are looked at as the search finds them rather than collected into a vector first.
1759 constexpr size_t none = std::numeric_limits<size_t>::max();
1760 size_t best_nearby_point_index = none;
1761 size_t nearest_point_index = none;
1762 visit_nearby_points(*layers[layer_idx].points_tree, projected_position, max_distance,
1763 [&layers, &comparator, &projected_position, &continuation, layer_idx, &best_nearby_point_index,
1764 &nearest_point_index](size_t nearby_point_index) {
1765 // Points off the continuation are skipped before the first point is taken, as they were removed before the scan.
1766 if (!continuation.empty() &&
1767 std::find(continuation.begin(), continuation.end(),
1768 layers[layer_idx].points[nearby_point_index].perimeter.contour_index) == continuation.end())
1769 return;
1770 if (best_nearby_point_index == none) {
1771 // The first point found starts both, as the first of the collected ones did.
1772 best_nearby_point_index = nearest_point_index = nearby_point_index;
1773 }
1774 const SeamCandidate &point = layers[layer_idx].points[nearby_point_index];
1775 if (point.perimeter.finalized) {
1776 return; // skip over finalized perimeters, try to find some that is not finalized
1777 }
1778 if (comparator.is_first_better(point, layers[layer_idx].points[best_nearby_point_index],
1779 projected_position.head<2>())
1780 || layers[layer_idx].points[best_nearby_point_index].perimeter.finalized) {
1781 best_nearby_point_index = nearby_point_index;
1782 }
1783 if ((point.position - projected_position).squaredNorm()
1784 < (layers[layer_idx].points[nearest_point_index].position - projected_position).squaredNorm()
1785 || layers[layer_idx].points[nearest_point_index].perimeter.finalized) {
1786 nearest_point_index = nearby_point_index;
1787 }
1788 });
1789
1790 if (best_nearby_point_index == none) {
1791 return {};
1792 }
1793
1794 const SeamCandidate &best_nearby_point = layers[layer_idx].points[best_nearby_point_index];
1795 const SeamCandidate &nearest_point = layers[layer_idx].points[nearest_point_index];
1796
1797 if (nearest_point.perimeter.finalized) {
1798 //all points are from already finalized perimeter, skip
1799 return {};
1800 }
1801
1802 //from the nearest_point, deduce index of seam in the next layer
1803 const SeamCandidate &next_layer_seam = layers[layer_idx].points[nearest_point.perimeter.seam_index];
1804
1805 // First try to pick central enforcer if any present
1806 if (next_layer_seam.central_enforcer
1807 && (next_layer_seam.position - projected_position).squaredNorm()
1808 < sqr(3 * max_distance)) {
1809 return {std::pair<size_t, size_t> {layer_idx, nearest_point.perimeter.seam_index}};
1810 }
1811
1812 // First try to align the nearest, then try the best nearby
1813 if (comparator.is_first_not_much_worse(nearest_point, next_layer_seam)) {
1814 return {std::pair<size_t, size_t> {layer_idx, nearest_point_index}};
1815 }
1816 // If nearest point is not good enough, try it with the best nearby point.
1817 if (comparator.is_first_not_much_worse(best_nearby_point, next_layer_seam)) {
1818 return {std::pair<size_t, size_t> {layer_idx, best_nearby_point_index}};
1819 }
1820
1821 return {};
1822}
void visit_nearby_points(const KDTreeIndirectType &kdtree, const PointType &center, const typename KDTreeIndirectType::CoordType &max_distance, VisitorFn visitor_fn)
Definition KDTreeIndirect.hpp:325
constexpr T sqr(T x)
Definition libslic3r.h:301
@ none
Filament synchronization not supported.
Definition IPrinterAgent.hpp:57

References Slic3r::SeamContour::Result::contours, find_next_seam_in_layer(), Slic3r::SeamPlacerImpl::SeamComparator::is_first_better(), Slic3r::SeamPlacerImpl::SeamComparator::is_first_not_much_worse(), Slic3r::SeamContour::Resolver::resolve(), Slic3r::sqr(), and Slic3r::visit_nearby_points().

Referenced by find_next_seam_in_layer(), and find_seam_string().

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

◆ find_seam_string()

std::vector< std::pair< size_t, size_t > > Slic3r::SeamPlacer::find_seam_string ( const PrintObject * po,
std::pair< size_t, size_t > start_seam,
const SeamPlacerImpl::SeamComparator & comparator,
const SeamContour::Resolver * contours = nullptr ) const
private
1826 {
1827 const std::vector<PrintObjectSeamData::LayerSeams> &layers = m_seam_per_object.find(po)->second.layers;
1828 int layer_idx = start_seam.first;
1829
1830 //initialize searching for seam string - cluster of nearby seams on previous and next layers
1831 int next_layer = layer_idx + 1;
1832 int step = 1;
1833 std::pair<size_t, size_t> prev_point_index = start_seam;
1834 std::vector<std::pair<size_t, size_t>> seam_string { start_seam };
1835
1836 auto reverse_lookup_direction = [&]() {
1837 step = -1;
1838 prev_point_index = start_seam;
1839 next_layer = layer_idx - 1;
1840 };
1841
1842 while (next_layer >= 0) {
1843 if (next_layer >= int(layers.size())) {
1844 reverse_lookup_direction();
1845 if (next_layer < 0) {
1846 break;
1847 }
1848 }
1850 layers[start_seam.first].points[start_seam.second].perimeter.flow_width;
1851 Vec3f prev_position = layers[prev_point_index.first].points[prev_point_index.second].position;
1852 Vec3f projected_position = prev_position;
1853 projected_position.z() = float(po->get_layer(next_layer)->slice_z);
1854
1855 std::optional<std::pair<size_t, size_t>> maybe_next_seam = find_next_seam_in_layer(layers, projected_position,
1856 next_layer,
1857 max_distance, comparator, contours, prev_point_index);
1858
1859 if (maybe_next_seam.has_value()) {
1860 // For old macOS (pre 10.14), std::optional does not have .value() method, so the code is using operator*() instead.
1861 seam_string.push_back(maybe_next_seam.operator*());
1862 prev_point_index = seam_string.back();
1863 //String added, prev_point_index updated
1864 } else {
1865 if (step == 1) {
1866 // Continue right below the start seam; stepping now would skip that layer.
1867 reverse_lookup_direction();
1868 continue;
1869 } else {
1870 break;
1871 }
1872 }
1873 next_layer += step;
1874 }
1875 return seam_string;
1876}
static constexpr float seam_align_tolerable_dist_factor
Definition SeamPlacer.hpp:156
std::optional< std::pair< size_t, size_t > > find_next_seam_in_layer(const std::vector< PrintObjectSeamData::LayerSeams > &layers, const Vec3f &projected_position, const size_t layer_idx, const float max_distance, const SeamPlacerImpl::SeamComparator &comparator, const SeamContour::Resolver *contours=nullptr, std::pair< size_t, size_t > previous={}) const
Definition SeamPlacer.cpp:1739
void step(Coord &crd, const Dir d, const long n=1)
Definition MarchingSquares.hpp:213

References find_next_seam_in_layer(), find_seam_string(), Slic3r::PrintObject::get_layer(), m_seam_per_object, seam_align_tolerable_dist_factor, and Slic3r::Layer::slice_z.

Referenced by align_seam_points(), and find_seam_string().

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

◆ gather_seam_candidates()

void Slic3r::SeamPlacer::gather_seam_candidates ( const PrintObject * po,
const SeamPlacerImpl::GlobalModelInfo & global_model_info,
PreciseSeam::PreciseSeamWarnings * warnings = nullptr )
private
1636 {
1637 using namespace SeamPlacerImpl;
1638 PrintObjectSeamData &seam_data = m_seam_per_object.emplace(po, PrintObjectSeamData { }).first->second;
1639 seam_data.layers.resize(po->layer_count());
1640
1641 tbb::parallel_for(tbb::blocked_range<size_t>(0, po->layers().size()),
1642 [po, &global_model_info, &seam_data, warnings]
1643 (tbb::blocked_range<size_t> r) {
1644 for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) {
1645 PrintObjectSeamData::LayerSeams &layer_seams = seam_data.layers[layer_idx];
1646 const Layer *layer = po->get_layer(layer_idx);
1647 auto unscaled_z = layer->slice_z;
1648 std::vector<const LayerRegion*> regions;
1649 std::vector<std::optional<bool>> boundary_types;
1650 //NOTE corresponding region ptr may be null, if the layer has zero perimeters
1651 const bool has_precise_seam_modifiers = !global_model_info.precise_seam_strong_volumes.empty() ||
1652 !global_model_info.precise_seam_weak_volumes.empty();
1653 Polygons polygons = extract_perimeter_polygons(layer, regions, boundary_types, has_precise_seam_modifiers);
1654 for (size_t poly_index = 0; poly_index < polygons.size(); ++poly_index) {
1655 process_perimeter_polygon(polygons[poly_index], unscaled_z,
1656 regions[poly_index], global_model_info, layer_seams,
1657 warnings, boundary_types[poly_index]);
1658 }
1659 auto functor = SeamCandidateCoordinateFunctor { layer_seams.points };
1660 seam_data.layers[layer_idx].points_tree =
1661 std::make_unique<PrintObjectSeamData::SeamCandidatesTree>(functor,
1662 layer_seams.points.size());
1663 }
1664 }
1665 );
1666}

References gather_seam_candidates(), Slic3r::PrintObject::layer_count(), Slic3r::PrintObject::layers(), Slic3r::PrintObjectSeamData::layers, m_seam_per_object, and Slic3r::ConstVectorOfPtrsAdaptor< T >::size().

Referenced by gather_seam_candidates(), and init().

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

◆ init()

void Slic3r::SeamPlacer::init ( const Print & print,
std::function< void(void)> throw_if_canceled_func )
2115 {
2116 using namespace SeamPlacerImpl;
2117 m_seam_per_object.clear();
2118 m_precise_seam_warning.clear();
2119
2120 // Warning flags for Precise Seam processing — shared across all objects
2121 PreciseSeam::PreciseSeamWarnings precise_seam_warnings;
2122
2123 for (const PrintObject *po : print.objects()) {
2124 throw_if_canceled_func();
2125 SeamPosition configured_seam_preference = po->config().seam_position.value;
2126 SeamComparator comparator { configured_seam_preference };
2127
2128 {
2129 GlobalModelInfo global_model_info { };
2130 gather_enforcers_blockers(global_model_info, po);
2132 global_model_info.precise_seam_strong_volumes,
2133 global_model_info.precise_seam_weak_volumes,
2134 m_seam_per_object[po].has_precise_seam_strong_volumes,
2135 po->model_object());
2136
2137 // Slice each Precise Seam modifier once per object; both consumers read the cache.
2138 for (const ModelVolume* vol : global_model_info.precise_seam_strong_volumes)
2139 global_model_info.precise_seam_slices[vol] = PreciseSeam::prepare_modifier_slices(po->slice_single_volume_regions(vol));
2140 for (const ModelVolume* vol : global_model_info.precise_seam_weak_volumes)
2141 global_model_info.precise_seam_slices[vol] = PreciseSeam::prepare_modifier_slices(po->slice_single_volume_regions(vol));
2142 // Register usage tracking before the parallel phase; workers only set its flags. Several print
2143 // objects of one model object share volumes, and try_emplace keeps what earlier ones recorded.
2144 for (const ModelVolume* vol : global_model_info.precise_seam_strong_volumes)
2145 precise_seam_warnings.modifier_usage.try_emplace(vol);
2146 for (const ModelVolume* vol : global_model_info.precise_seam_weak_volumes)
2147 precise_seam_warnings.modifier_usage.try_emplace(vol);
2148
2149 throw_if_canceled_func();
2150 if (configured_seam_preference == spAligned || configured_seam_preference == spNearest || configured_seam_preference == spAlignedBack) {
2151 compute_global_occlusion(global_model_info, po, throw_if_canceled_func, configured_seam_preference);
2152 }
2153 throw_if_canceled_func();
2154
2155 BOOST_LOG_TRIVIAL(debug)
2156 << "SeamPlacer: gather_seam_candidates: start";
2157 gather_seam_candidates(po, global_model_info, &precise_seam_warnings);
2158 BOOST_LOG_TRIVIAL(debug)
2159 << "SeamPlacer: gather_seam_candidates: end";
2160 throw_if_canceled_func();
2161 if (configured_seam_preference == spAligned || configured_seam_preference == spNearest || configured_seam_preference == spAlignedBack) {
2162 BOOST_LOG_TRIVIAL(debug)
2163 << "SeamPlacer: calculate_candidates_visibility : start";
2164 calculate_candidates_visibility(po, global_model_info);
2165 BOOST_LOG_TRIVIAL(debug)
2166 << "SeamPlacer: calculate_candidates_visibility : end";
2167 }
2168 } // destruction of global_model_info (large structure, no longer needed)
2169 throw_if_canceled_func();
2170 BOOST_LOG_TRIVIAL(debug)
2171 << "SeamPlacer: calculate_overhangs and layer embdedding : start";
2173 BOOST_LOG_TRIVIAL(debug)
2174 << "SeamPlacer: calculate_overhangs and layer embdedding: end";
2175 throw_if_canceled_func();
2176 if (configured_seam_preference != spNearest) { // For spNearest, the seam is picked in the place_seam method with actual nozzle position information
2177 BOOST_LOG_TRIVIAL(debug)
2178 << "SeamPlacer: pick_seam_point : start";
2179 //pick seam point
2180 std::vector<PrintObjectSeamData::LayerSeams> &layers = m_seam_per_object[po].layers;
2181 tbb::parallel_for(tbb::blocked_range<size_t>(0, layers.size()),
2182 [&layers, configured_seam_preference, comparator](tbb::blocked_range<size_t> r) {
2183 for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) {
2184 std::vector<SeamCandidate> &layer_perimeter_points = layers[layer_idx].points;
2185 for (size_t current = 0; current < layer_perimeter_points.size();
2186 current = layer_perimeter_points[current].perimeter.end_index)
2187 if (configured_seam_preference == spRandom)
2188 pick_random_seam_point(layer_perimeter_points, current);
2189 else
2190 pick_seam_point(layer_perimeter_points, current, comparator);
2191 }
2192 });
2193 BOOST_LOG_TRIVIAL(debug)
2194 << "SeamPlacer: pick_seam_point : end";
2195 }
2196 throw_if_canceled_func();
2197 if (configured_seam_preference == spAligned || configured_seam_preference == spRear || configured_seam_preference == spAlignedBack) {
2198 BOOST_LOG_TRIVIAL(debug)
2199 << "SeamPlacer: align_seam_points : start";
2200 align_seam_points(po, comparator, throw_if_canceled_func);
2201 BOOST_LOG_TRIVIAL(debug)
2202 << "SeamPlacer: align_seam_points : end";
2203 }
2204
2205 // Restore precise seam positions that were potentially modified
2206 if (m_seam_per_object[po].has_precise_seam_strong_volumes) {
2208 }
2209
2210#ifdef DEBUG_FILES
2211 debug_export_points(m_seam_per_object[po].layers, po->bounding_box(), comparator);
2212#endif
2213 }
2214
2215 // Prepare one combined Precise Seam warning; G-code export issues it. Keep it single: separate
2216 // warnings would each re-push all warnings and duplicate text in the notification.
2217 {
2218 const unsigned failed_types = precise_seam_warnings.failed_types.load(std::memory_order_relaxed);
2219 const unsigned mi = precise_seam_warnings.multiple_intersections.load(std::memory_order_relaxed);
2220 const unsigned fc = precise_seam_warnings.full_containment.load(std::memory_order_relaxed);
2221 const size_t failed = precise_seam_warnings.failed_fragments.load(std::memory_order_relaxed);
2222 // All workers have finished; cancellation before this point may omit the summary.
2224 BOOST_LOG_TRIVIAL(warning) << "[PreciseSeamIntersectionFailed] " << failed
2225 << " fragments discarded; first " << PreciseSeam::failed_fragment_log_limit
2226 << " logged (parallel processing order), " << (failed - PreciseSeam::failed_fragment_log_limit)
2227 << " omitted";
2228 // Recoveries are log-only: no user warning, but the same bounded detail and a total.
2229 const size_t recovered = precise_seam_warnings.recovered_fragments.load(std::memory_order_relaxed);
2231 BOOST_LOG_TRIVIAL(warning) << "[PreciseSeamFragmentRecovered] " << recovered
2232 << " fragments recovered; first " << PreciseSeam::failed_fragment_log_limit
2233 << " logged (parallel processing order), " << (recovered - PreciseSeam::failed_fragment_log_limit)
2234 << " omitted";
2235 // Reasons name the modifier types, as the menu does, not individual modifiers: "Seam Left, Seam
2236 // Enforced" in menu order, each type once. The same msgids as the menu share its translations.
2237 const auto type_list = [](unsigned mask) {
2238 const std::pair<ModelVolumeType, std::string> types[] = {
2242 {ModelVolumeType::PRECISE_SEAM_ENFORCED, _u8L("Seam Enforced")},
2244 {ModelVolumeType::PRECISE_SEAM_NEUTRAL, _u8L("Seam Neutral")}};
2245 std::string list;
2246 for (const auto &[type, name] : types)
2248 list += (list.empty() ? "" : ", ") + name;
2249 return list;
2250 };
2251 std::vector<std::string> parts;
2252 if (failed_types != 0)
2253 parts.push_back((boost::format(_u8L("failed to process some intersections (%1%)")) % type_list(failed_types)).str());
2254 if (mi != 0)
2255 parts.push_back((boost::format(_u8L("multiple intersections with a perimeter, only one was used (%1%)")) % type_list(mi)).str());
2256 if (fc != 0)
2257 parts.push_back((boost::format(_u8L("a perimeter is fully inside a modifier, the modifier was not applied to it (%1%)")) % type_list(fc)).str());
2258 // Modifiers whose usage flags match, in print and volume order, so the named one is deterministic.
2259 const auto collect_modifiers = [&print, &precise_seam_warnings](const auto &matches) {
2260 std::vector<const ModelVolume*> found;
2261 for (const PrintObject *po : print.objects())
2262 for (const ModelVolume *volume : po->model_object()->volumes) {
2263 const auto it = precise_seam_warnings.modifier_usage.find(volume);
2264 if (it != precise_seam_warnings.modifier_usage.end() && matches(it->second) &&
2265 std::find(found.begin(), found.end(), volume) == found.end())
2266 found.push_back(volume);
2267 }
2268 return found;
2269 };
2270 // Seam Blocked modifiers whose whole perimeter stayed Blocked; the user warning names only the
2271 // first one, the log lists them all.
2272 const std::vector<const ModelVolume*> fully_blocked = collect_modifiers(
2273 [](const PreciseSeam::PreciseSeamWarnings::ModifierUsage &usage) { return usage.fully_blocked.load(std::memory_order_relaxed); });
2274 for (const ModelVolume *volume : fully_blocked)
2275 BOOST_LOG_TRIVIAL(warning) << "[PreciseSeamFullyBlocked] object=\"" << volume->get_object()->name
2276 << "\" modifier=\"" << volume->name << "\"";
2277 if (!fully_blocked.empty()) {
2278 const ModelVolume *first = fully_blocked.front();
2279 if (fully_blocked.size() == 1) {
2280 // TRN: %1% is the modifier type "Seam Blocked" as in the menu, %2% the modifier name, %3% the object name.
2281 parts.push_back((boost::format(_u8L("%1% modifier \"%2%\" of \"%3%\" covers an entire perimeter"))
2282 % _u8L("Seam Blocked") % first->name % first->get_object()->name).str());
2283 } else {
2284 // TRN: %1% is the modifier type "Seam Blocked" as in the menu, %2% the modifier name, %3% the object name, %4% the number of modifiers.
2285 parts.push_back((boost::format(_u8L("%1% modifier \"%2%\" of \"%3%\" (%4% in total) covers an entire perimeter"))
2286 % _u8L("Seam Blocked") % first->name % first->get_object()->name % fully_blocked.size()).str());
2287 }
2288 }
2289 // Modifiers evaluated somewhere that never reached a perimeter; never-evaluated ones are not reported.
2290 const std::vector<const ModelVolume*> no_effect = collect_modifiers(
2291 [](const PreciseSeam::PreciseSeamWarnings::ModifierUsage &usage) {
2292 return usage.checked.load(std::memory_order_relaxed) && !usage.reached.load(std::memory_order_relaxed);
2293 });
2294 // The user warning names only the first one; the log lists them all.
2295 for (const ModelVolume *volume : no_effect)
2296 BOOST_LOG_TRIVIAL(warning) << "[PreciseSeamNoEffect] object=\"" << volume->get_object()->name
2297 << "\" modifier=\"" << volume->name << "\"";
2298 if (!no_effect.empty()) {
2299 const ModelVolume *first = no_effect.front();
2300 if (no_effect.size() == 1)
2301 parts.push_back((boost::format(_u8L("modifier \"%1%\" of \"%2%\" had no effect on the seam (it might not reach the centerline of the printed perimeter)"))
2302 % first->name % first->get_object()->name).str());
2303 else
2304 parts.push_back((boost::format(_u8L("modifier \"%1%\" of \"%2%\" (%3% in total) had no effect on the seam (it might not reach the centerline of the printed perimeter)"))
2305 % first->name % first->get_object()->name % no_effect.size()).str());
2306 }
2307 if (!parts.empty()) {
2308 // One line: the export warnings dialog shows only the first line of each warning.
2309 std::string warning_text = _u8L("Precise Seam") + ": ";
2310 for (size_t i = 0; i < parts.size(); ++i) {
2311 if (i > 0) warning_text += "; ";
2312 warning_text += parts[i];
2313 }
2314 warning_text += ". ";
2315 warning_text += _u8L("Seam placement may differ from expected.");
2316 m_precise_seam_warning = std::move(warning_text);
2317 }
2318 }
2319}
void calculate_overhangs_and_layer_embedding(const PrintObject *po)
Definition SeamPlacer.cpp:1683
void align_seam_points(const PrintObject *po, const SeamPlacerImpl::SeamComparator &comparator, const std::function< void()> &throw_if_canceled)
Definition SeamPlacer.cpp:1883
void calculate_candidates_visibility(const PrintObject *po, const SeamPlacerImpl::GlobalModelInfo &global_model_info)
Definition SeamPlacer.cpp:1668
std::string m_precise_seam_warning
Definition SeamPlacer.hpp:173
void gather_seam_candidates(const PrintObject *po, const SeamPlacerImpl::GlobalModelInfo &global_model_info, PreciseSeam::PreciseSeamWarnings *warnings=nullptr)
Definition SeamPlacer.cpp:1635
void init_precise_seam_data(std::vector< const ModelVolume * > &strong_volumes_out, std::vector< const ModelVolume * > &weak_volumes_out, bool &has_strong_out, const ModelObject *model_object)
Definition PreciseSeam.cpp:535
constexpr size_t failed_fragment_log_limit
Definition PreciseSeam.hpp:45
void restore_precise_seam_positions(std::vector< PrintObjectSeamData::LayerSeams > &layers)
Definition PreciseSeam.cpp:1039
ModifierSlices prepare_modifier_slices(std::vector< ExPolygons > slices)
Definition PreciseSeam.cpp:39
void gather_enforcers_blockers(GlobalModelInfo &result, const PrintObject *po)
Definition SeamPlacer.cpp:1099
void compute_global_occlusion(GlobalModelInfo &result, const PrintObject *po, std::function< void(void)> throw_if_canceled, SeamPosition seam_position=spAligned)
Definition SeamPlacer.cpp:986
SeamPosition
Definition PrintConfig.hpp:394
@ spNearest
Definition PrintConfig.hpp:395
@ spAligned
Definition PrintConfig.hpp:395
@ spAlignedBack
Definition PrintConfig.hpp:395
@ PRECISE_SEAM_BLOCKED
Definition Model.hpp:368
@ PRECISE_SEAM_LEFT
Definition Model.hpp:365
@ PRECISE_SEAM_ENFORCED
Definition Model.hpp:367
@ PRECISE_SEAM_CENTER
Definition Model.hpp:364
@ PRECISE_SEAM_NEUTRAL
Definition Model.hpp:369
@ PRECISE_SEAM_RIGHT
Definition Model.hpp:366
#define _u8L(s)
Definition I18N.hpp:11
static unsigned type_bit(ModelVolumeType type)
Definition PreciseSeam.hpp:70

References align_seam_points(), Slic3r::PrintObject::bounding_box(), calculate_candidates_visibility(), calculate_overhangs_and_layer_embedding(), Slic3r::PrintObject::config(), gather_seam_candidates(), init(), Slic3r::PreciseSeam::init_precise_seam_data(), m_precise_seam_warning, m_seam_per_object, Slic3r::PreciseSeam::PreciseSeamWarnings::modifier_usage, Slic3r::Print::objects(), Slic3r::PreciseSeam::prepare_modifier_slices(), Slic3r::PreciseSeam::restore_precise_seam_positions(), Slic3r::spAligned, Slic3r::spAlignedBack, Slic3r::spNearest, and Slic3r::spRear.

Referenced by init().

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

◆ place_seam()

void Slic3r::SeamPlacer::place_seam ( const Layer * layer,
ExtrusionLoop & loop,
const Point & last_pos,
float & overhang ) const
2322 {
2323 using namespace SeamPlacerImpl;
2324 const PrintObject *po = layer->object();
2325 // Must not be called with supprot layer.
2326 assert(dynamic_cast<const SupportLayer*>(layer) == nullptr);
2327 // Object layer IDs are incremented by the number of raft layers.
2328 assert(layer->id() >= po->slicing_parameters().raft_layers());
2329 const size_t layer_index = layer->id() - po->slicing_parameters().raft_layers();
2330 const double unscaled_z = layer->slice_z;
2331
2332 auto get_next_loop_point = [loop](ExtrusionLoop::ClosestPathPoint current) {
2333 current.segment_idx += 1;
2334 if (current.segment_idx >= loop.paths[current.path_idx].polyline.points.size()) {
2335 current.path_idx = next_idx_modulo(current.path_idx, loop.paths.size());
2336 current.segment_idx = 0;
2337 }
2338 current.foot_pt = loop.paths[current.path_idx].polyline.points[current.segment_idx].to_point();
2339 return current;
2340 };
2341
2342 const PrintObjectSeamData::LayerSeams &layer_perimeters =
2343 m_seam_per_object.find(layer->object())->second.layers[layer_index];
2344
2345 // Find the closest perimeter in the SeamPlacer to this loop.
2346 // Repeat search until two consecutive points of the loop are found, that result in the same closest_perimeter
2347 // This is beacuse with arachne, T-Junctions may exist and sometimes the wrong perimeter was chosen
2348 size_t closest_perimeter_point_index = 0;
2349 { // local space for the closest_perimeter_point_index
2350 Perimeter *closest_perimeter = nullptr;
2351 ExtrusionLoop::ClosestPathPoint closest_point{0, 0, loop.paths[0].polyline.points[0].to_point()};
2352 size_t points_count = std::accumulate(loop.paths.begin(), loop.paths.end(), 0, [](size_t acc,const ExtrusionPath& p) {
2353 return acc + p.polyline.points.size();
2354 });
2355 for (size_t i = 0; i < points_count; ++i) {
2356 Vec2f unscaled_p = unscaled<float>(closest_point.foot_pt);
2357 closest_perimeter_point_index = find_closest_point(*layer_perimeters.points_tree.get(),
2358 to_3d(unscaled_p, float(unscaled_z)));
2359 if (closest_perimeter != &layer_perimeters.points[closest_perimeter_point_index].perimeter) {
2360 closest_perimeter = &layer_perimeters.points[closest_perimeter_point_index].perimeter;
2361 closest_point = get_next_loop_point(closest_point);
2362 } else {
2363 break;
2364 }
2365 }
2366 }
2367
2368 Vec3f seam_position;
2369 size_t seam_index;
2370 if (const Perimeter &perimeter = layer_perimeters.points[closest_perimeter_point_index].perimeter;
2371 perimeter.finalized) {
2372 seam_position = perimeter.final_seam_position;
2373 seam_index = perimeter.seam_index;
2374 } else {
2375 seam_index =
2376 po->config().seam_position == spNearest ?
2377 pick_nearest_seam_point_index(layer_perimeters.points, perimeter.start_index,
2378 unscaled<float>(last_pos)) :
2379 perimeter.seam_index;
2380 seam_position = layer_perimeters.points[seam_index].position;
2381 }
2382
2383 Point seam_point = Point::new_scale(seam_position.x(), seam_position.y());
2384 overhang = layer_perimeters.points[seam_index].unsupported_dist;
2385
2386 if (loop.role() == ExtrusionRole::erPerimeter) { //Hopefully inner perimeter
2387 const SeamCandidate &perimeter_point = layer_perimeters.points[seam_index];
2388 ExtrusionLoop::ClosestPathPoint projected_point = loop.get_closest_path_and_point(seam_point, false);
2389 // determine depth of the seam point.
2390 float depth = (float) unscale(Point(seam_point - projected_point.foot_pt)).norm();
2391 float beta_angle = cos(perimeter_point.local_ccw_angle / 2.0f);
2392 size_t index_of_prev =
2393 seam_index == perimeter_point.perimeter.start_index ?
2394 perimeter_point.perimeter.end_index - 1 :
2395 seam_index - 1;
2396 size_t index_of_next =
2397 seam_index == perimeter_point.perimeter.end_index - 1 ?
2398 perimeter_point.perimeter.start_index :
2399 seam_index + 1;
2400
2401 if ((seam_position - perimeter_point.position).squaredNorm() < depth && // seam is on perimeter point
2402 perimeter_point.local_ccw_angle < -EPSILON // In concave angles
2403 ) { // In this case, we are at internal perimeter, where the external perimeter has seam in concave angle. We want to align
2404 // the internal seam into the concave corner, and not on the perpendicular projection on the closest edge (which is what the split_at function does)
2405 Vec2f dir_to_middle =
2406 ((perimeter_point.position - layer_perimeters.points[index_of_prev].position).head<2>().normalized()
2407 + (perimeter_point.position - layer_perimeters.points[index_of_next].position).head<2>().normalized())
2408 * 0.5;
2409 depth = 1.4142 * depth / beta_angle;
2410 // There are some nice geometric identities in determination of the correct depth of new seam point.
2411 //overshoot the target depth, in concave angles it will correctly snap to the corner; TODO: find out why such big overshoot is needed.
2412 Vec2f final_pos = perimeter_point.position.head<2>() + depth * dir_to_middle;
2413 projected_point = loop.get_closest_path_and_point(Point::new_scale(final_pos.x(), final_pos.y()), false);
2414 } else { // not concave angle, in that case the nearest point is the good candidate
2415 // but for staggering, we also need to recompute depth of the inner perimter, because in convex corners, the distance is larger than layer width
2416 // we want the perpendicular depth, not distance to nearest point
2417 depth = depth * beta_angle / 1.4142;
2418 }
2419
2420 seam_point = projected_point.foot_pt;
2421
2422 //lastly, for internal perimeters, do the staggering if requested
2423 if (po->config().staggered_inner_seams && loop.length() > 0.0) {
2424 //fix depth, it is sometimes strongly underestimated
2425 depth = std::max(loop.paths[projected_point.path_idx].width, depth);
2426
2427 while (depth > 0.0f) {
2428 auto next_point = get_next_loop_point(projected_point);
2429 Vec2f a = unscale(projected_point.foot_pt).cast<float>();
2430 Vec2f b = unscale(next_point.foot_pt).cast<float>();
2431 float dist = (a - b).norm();
2432 if (dist > depth) {
2433 Vec2f final_pos = a + (b - a) * depth / dist;
2434 next_point.foot_pt = Point::new_scale(final_pos.x(), final_pos.y());
2435 }
2436 depth -= dist;
2437 projected_point = next_point;
2438 }
2439 seam_point = projected_point.foot_pt;
2440 }
2441 }
2442
2443 // Because the G-code export has 1um resolution, don't generate segments shorter than 1.5 microns,
2444 // thus empty path segments will not be produced by G-code export.
2445 if (!loop.split_at_vertex(seam_point, scaled<double>(0.0015))) {
2446 // The point is not in the original loop.
2447 // Insert it.
2448 loop.split_at(seam_point, true);
2449 }
2450
2451}
#define EPSILON
Definition ColorUtils.cpp:64
static Point new_scale(coordf_t x, coordf_t y)
Definition Point.hpp:214
T dist(const boost::polygon::point_data< T > &p1, const boost::polygon::point_data< T > &p2)
Definition Geometry.cpp:298
size_t pick_nearest_seam_point_index(const std::vector< SeamCandidate > &perimeter_points, size_t start_index, const Vec2f &preffered_location)
Definition SeamPlacer.cpp:1352
Kernel::Point_3 Point
Definition MeshRepair.cpp:81
BoundingBox scaled(const BoundingBoxf &bb)
Definition BoundingBox.hpp:284
@ erPerimeter
Definition ExtrusionEntity.hpp:28
INDEX_TYPE next_idx_modulo(INDEX_TYPE idx, const INDEX_TYPE count)
Definition Utils.hpp:453
constexpr Eigen::Matrix< Tout, 2, EigenArgs... > unscaled(const Slic3r::Point &v) noexcept
Definition Arrange.cpp:81
size_t find_closest_point(const KDTreeIndirect< D, CoordT, CoordFn > &kdtree, const PointType &point, FilterFn filter)
Definition KDTreeIndirect.hpp:271
T unscale(Q v)
Definition libslic3r.h:138

References Slic3r::PrintObject::config(), Slic3r::Geometry::dist(), EPSILON, Slic3r::erPerimeter, Slic3r::find_closest_point(), Slic3r::ExtrusionLoop::ClosestPathPoint::foot_pt, Slic3r::ExtrusionLoop::get_closest_path_and_point(), Slic3r::Layer::id(), Slic3r::ExtrusionLoop::length(), m_seam_per_object, Slic3r::Point::new_scale(), Slic3r::next_idx_modulo(), Slic3r::Layer::object(), Slic3r::ExtrusionLoop::ClosestPathPoint::path_idx, Slic3r::ExtrusionLoop::paths, place_seam(), Slic3r::PrintObjectSeamData::LayerSeams::points, Slic3r::PrintObjectSeamData::LayerSeams::points_tree, Slic3r::SlicingParameters::raft_layers(), Slic3r::ExtrusionLoop::role(), Slic3r::scaled(), Slic3r::Layer::slice_z, Slic3r::PrintObject::slicing_parameters(), Slic3r::ExtrusionLoop::split_at(), Slic3r::ExtrusionLoop::split_at_vertex(), Slic3r::spNearest, Slic3r::to_3d(), Slic3r::unscale(), and Slic3r::unscaled().

Referenced by place_seam().

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

◆ precise_seam_warning()

const std::string & Slic3r::SeamPlacer::precise_seam_warning ( ) const
inline
169{ return m_precise_seam_warning; }

References m_precise_seam_warning.

Member Data Documentation

◆ angle_importance_aligned

float Slic3r::SeamPlacer::angle_importance_aligned = 0.6f
staticconstexpr

◆ angle_importance_nearest

float Slic3r::SeamPlacer::angle_importance_nearest = 1.0f
staticconstexpr

◆ enforcer_oversampling_distance

float Slic3r::SeamPlacer::enforcer_oversampling_distance = 0.2f
staticconstexpr

◆ fast_decimation_triangle_count_target

size_t Slic3r::SeamPlacer::fast_decimation_triangle_count_target = 16000
staticconstexpr

◆ m_precise_seam_warning

std::string Slic3r::SeamPlacer::m_precise_seam_warning
private

Referenced by init(), and precise_seam_warning().

◆ m_seam_per_object

◆ overhang_angle_threshold

float Slic3r::SeamPlacer::overhang_angle_threshold = 45.0f * float(PI) / 180.0f
staticconstexpr

◆ raycasting_visibility_samples_count

size_t Slic3r::SeamPlacer::raycasting_visibility_samples_count = 30000
staticconstexpr

◆ seam_align_minimum_string_seams

size_t Slic3r::SeamPlacer::seam_align_minimum_string_seams = 6
staticconstexpr

Referenced by align_seam_points().

◆ seam_align_mm_per_segment

size_t Slic3r::SeamPlacer::seam_align_mm_per_segment = 4.0f
staticconstexpr

Referenced by align_seam_points().

◆ seam_align_score_tolerance

float Slic3r::SeamPlacer::seam_align_score_tolerance = 0.3f
staticconstexpr

◆ seam_align_tolerable_dist_factor

float Slic3r::SeamPlacer::seam_align_tolerable_dist_factor = 4.0f
staticconstexpr

Referenced by find_seam_string().

◆ sharp_angle_snapping_threshold

float Slic3r::SeamPlacer::sharp_angle_snapping_threshold = 55.0f * float(PI) / 180.0f
staticconstexpr

◆ sqr_rays_per_sample_point

size_t Slic3r::SeamPlacer::sqr_rays_per_sample_point = 5
staticconstexpr

The documentation for this class was generated from the following files:
  • /home/runner/work/OrcaSlicer/OrcaSlicer/src/libslic3r/GCode/SeamPlacer.hpp
  • /home/runner/work/OrcaSlicer/OrcaSlicer/src/libslic3r/GCode/SeamPlacer.cpp