Prusa Slicer 2.6.0
Loading...
Searching...
No Matches
Slic3r::SeamPlacer Class Reference

#include <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)
 
void place_seam (const Layer *layer, ExtrusionLoop &loop, bool external_first, const Point &last_pos) const
 

Public Attributes

std::unordered_map< const PrintObject *, PrintObjectSeamDatam_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 = 50.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)
 
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)
 
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
 
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
 

Detailed Description

Member Function Documentation

◆ align_seam_points()

void Slic3r::SeamPlacer::align_seam_points ( const PrintObject po,
const SeamPlacerImpl::SeamComparator comparator 
)
private
1224 {
1225 using namespace SeamPlacerImpl;
1226
1227 // Prepares Debug files for writing.
1228#ifdef DEBUG_FILES
1229 Slic3r::CNumericLocalesSetter locales_setter;
1230 auto clusters_f = debug_out_path("seam_clusters.obj");
1231 FILE *clusters = boost::nowide::fopen(clusters_f.c_str(), "w");
1232 if (clusters == nullptr) {
1233 BOOST_LOG_TRIVIAL(error)
1234 << "stl_write_obj: Couldn't open " << clusters_f << " for writing";
1235 return;
1236 }
1237 auto aligned_f = debug_out_path("aligned_clusters.obj");
1238 FILE *aligns = boost::nowide::fopen(aligned_f.c_str(), "w");
1239 if (aligns == nullptr) {
1240 BOOST_LOG_TRIVIAL(error)
1241 << "stl_write_obj: Couldn't open " << clusters_f << " for writing";
1242 return;
1243 }
1244#endif
1245
1246 //gather vector of all seams on the print_object - pair of layer_index and seam__index within that layer
1247 const std::vector<PrintObjectSeamData::LayerSeams> &layers = m_seam_per_object[po].layers;
1248 std::vector<std::pair<size_t, size_t>> seams;
1249 for (size_t layer_idx = 0; layer_idx < layers.size(); ++layer_idx) {
1250 const std::vector<SeamCandidate> &layer_perimeter_points = layers[layer_idx].points;
1251 size_t current_point_index = 0;
1252 while (current_point_index < layer_perimeter_points.size()) {
1253 seams.emplace_back(layer_idx, layer_perimeter_points[current_point_index].perimeter.seam_index);
1254 current_point_index = layer_perimeter_points[current_point_index].perimeter.end_index;
1255 }
1256 }
1257
1258 //sort them before alignment. Alignment is sensitive to initializaion, this gives it better chance to choose something nice
1259 std::stable_sort(seams.begin(), seams.end(),
1260 [&comparator, &layers](const std::pair<size_t, size_t> &left,
1261 const std::pair<size_t, size_t> &right) {
1262 return comparator.is_first_better(layers[left.first].points[left.second],
1263 layers[right.first].points[right.second]);
1264 }
1265 );
1266
1267 //align the seam points - start with the best, and check if they are aligned, if yes, skip, else start alignment
1268 // Keeping the vectors outside, so with a bit of luck they will not get reallocated after couple of for loop iterations.
1269 std::vector<std::pair<size_t, size_t>> seam_string;
1270 std::vector<std::pair<size_t, size_t>> alternative_seam_string;
1271 std::vector<Vec2f> observations;
1272 std::vector<float> observation_points;
1273 std::vector<float> weights;
1274
1275 int global_index = 0;
1276 while (global_index < int(seams.size())) {
1277 size_t layer_idx = seams[global_index].first;
1278 size_t seam_index = seams[global_index].second;
1279 global_index++;
1280 const std::vector<SeamCandidate> &layer_perimeter_points = layers[layer_idx].points;
1281 if (layer_perimeter_points[seam_index].perimeter.finalized) {
1282 // This perimeter is already aligned, skip seam
1283 continue;
1284 } else {
1285 seam_string = this->find_seam_string(po, { layer_idx, seam_index }, comparator);
1286 size_t step_size = 1 + seam_string.size() / 20;
1287 for (size_t alternative_start = 0; alternative_start < seam_string.size(); alternative_start += step_size) {
1288 size_t start_layer_idx = seam_string[alternative_start].first;
1289 size_t seam_idx =
1290 layers[start_layer_idx].points[seam_string[alternative_start].second].perimeter.seam_index;
1291 alternative_seam_string = this->find_seam_string(po,
1292 std::pair<size_t, size_t>(start_layer_idx, seam_idx), comparator);
1293 if (alternative_seam_string.size() > seam_string.size()) {
1294 seam_string = std::move(alternative_seam_string);
1295 }
1296 }
1297 if (seam_string.size() < seam_align_minimum_string_seams) {
1298 //string NOT long enough to be worth aligning, skip
1299 continue;
1300 }
1301
1302 // String is long enough, all string seams and potential string seams gathered, now do the alignment
1303 //sort by layer index
1304 std::sort(seam_string.begin(), seam_string.end(),
1305 [](const std::pair<size_t, size_t> &left, const std::pair<size_t, size_t> &right) {
1306 return left.first < right.first;
1307 });
1308
1309 //repeat the alignment for the current seam, since it could be skipped due to alternative path being aligned.
1310 global_index--;
1311
1312 // gather all positions of seams and their weights
1313 observations.resize(seam_string.size());
1314 observation_points.resize(seam_string.size());
1315 weights.resize(seam_string.size());
1316
1317 auto angle_3d = [](const Vec3f& a, const Vec3f& b){
1318 return std::abs(acosf(a.normalized().dot(b.normalized())));
1319 };
1320
1321 auto angle_weight = [](float angle){
1322 return 1.0f / (0.1f + compute_angle_penalty(angle));
1323 };
1324
1325 //gather points positions and weights
1326 float total_length = 0.0f;
1327 Vec3f last_point_pos = layers[seam_string[0].first].points[seam_string[0].second].position;
1328 for (size_t index = 0; index < seam_string.size(); ++index) {
1329 const SeamCandidate &current = layers[seam_string[index].first].points[seam_string[index].second];
1330 float layer_angle = 0.0f;
1331 if (index > 0 && index < seam_string.size() - 1) {
1332 layer_angle = angle_3d(
1333 current.position
1334 - layers[seam_string[index - 1].first].points[seam_string[index - 1].second].position,
1335 layers[seam_string[index + 1].first].points[seam_string[index + 1].second].position
1336 - current.position
1337 );
1338 }
1339 observations[index] = current.position.head<2>();
1340 observation_points[index] = current.position.z();
1341 weights[index] = angle_weight(current.local_ccw_angle);
1342 float curling_influence = layer_angle > 2.0 * std::abs(current.local_ccw_angle) ? -0.8f : 1.0f;
1343 if (current.type == EnforcedBlockedSeamPoint::Enforced) {
1344 curling_influence = 1.0f;
1345 weights[index] += 3.0f;
1346 }
1347 total_length += curling_influence * (last_point_pos - current.position).norm();
1348 last_point_pos = current.position;
1349 }
1350
1351 if (comparator.setup == spRear) {
1352 total_length *= 0.3f;
1353 }
1354
1355 // Curve Fitting
1356 size_t number_of_segments = std::max(size_t(1),
1357 size_t(std::max(0.0f,total_length) / SeamPlacer::seam_align_mm_per_segment));
1358 auto curve = Geometry::fit_cubic_bspline(observations, observation_points, weights, number_of_segments);
1359
1360 // Do alignment - compute fitted point for each point in the string from its Z coord, and store the position into
1361 // Perimeter structure of the point; also set flag aligned to true
1362 for (size_t index = 0; index < seam_string.size(); ++index) {
1363 const auto &pair = seam_string[index];
1364 float t = std::min(1.0f, std::pow(std::abs(layers[pair.first].points[pair.second].local_ccw_angle)
1366 if (layers[pair.first].points[pair.second].type == EnforcedBlockedSeamPoint::Enforced){
1367 t = std::max(0.4f, t);
1368 }
1369
1370 Vec3f current_pos = layers[pair.first].points[pair.second].position;
1371 Vec2f fitted_pos = curve.get_fitted_value(current_pos.z());
1372
1373 //interpolate between current and fitted position, prefer current pos for large weights.
1374 Vec3f final_position = t * current_pos + (1.0f - t) * to_3d(fitted_pos, current_pos.z());
1375
1376 Perimeter &perimeter = layers[pair.first].points[pair.second].perimeter;
1377 perimeter.seam_index = pair.second;
1378 perimeter.final_seam_position = final_position;
1379 perimeter.finalized = true;
1380 }
1381
1382#ifdef DEBUG_FILES
1383 auto randf = []() {
1384 return float(rand()) / float(RAND_MAX);
1385 };
1386 Vec3f color { randf(), randf(), randf() };
1387 for (size_t i = 0; i < seam_string.size(); ++i) {
1388 auto orig_seam = layers[seam_string[i].first].points[seam_string[i].second];
1389 fprintf(clusters, "v %f %f %f %f %f %f \n", orig_seam.position[0],
1390 orig_seam.position[1],
1391 orig_seam.position[2], color[0], color[1],
1392 color[2]);
1393 }
1394
1395 color = Vec3f { randf(), randf(), randf() };
1396 for (size_t i = 0; i < seam_string.size(); ++i) {
1397 const Perimeter &perimeter = layers[seam_string[i].first].points[seam_string[i].second].perimeter;
1398 fprintf(aligns, "v %f %f %f %f %f %f \n", perimeter.final_seam_position[0],
1399 perimeter.final_seam_position[1],
1400 perimeter.final_seam_position[2], color[0], color[1],
1401 color[2]);
1402 }
1403#endif
1404 }
1405 }
1406
1407#ifdef DEBUG_FILES
1408 fclose(clusters);
1409 fclose(aligns);
1410#endif
1411
1412}
Definition LocalesUtils.hpp:18
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
Definition SeamPlacer.cpp:1165
static constexpr size_t seam_align_mm_per_segment
Definition SeamPlacer.hpp:137
std::unordered_map< const PrintObject *, PrintObjectSeamData > m_seam_per_object
Definition SeamPlacer.hpp:140
static constexpr float sharp_angle_snapping_threshold
Definition SeamPlacer.hpp:118
static constexpr size_t seam_align_minimum_string_seams
Definition SeamPlacer.hpp:135
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:193
float compute_angle_penalty(float ccw_angle)
Definition SeamPlacer.cpp:52
@ spRear
Definition PrintConfig.hpp:98
std::string debug_out_path(const char *name,...)
Definition utils.cpp:218
Eigen::Matrix< typename Derived::Scalar, 3, 1, Eigen::DontAlign > to_3d(const Eigen::MatrixBase< Derived > &pt, const typename Derived::Scalar z)
Definition Point.hpp:127
Eigen::Matrix< float, 3, 1, Eigen::DontAlign > Vec3f
Definition Point.hpp:49
double angle(const Eigen::MatrixBase< Derived > &v1, const Eigen::MatrixBase< Derived2 > &v2)
Definition Point.hpp:112
double total_length(const Polygons &polylines)
Definition Polygon.hpp:112
Eigen::Matrix< float, 2, 1, Eigen::DontAlign > Vec2f
Definition Point.hpp:48
static char error[256]
Definition tga.cpp:50

References Slic3r::angle(), Slic3r::debug_out_path(), error, Slic3r::Perimeter, Slic3r::SeamPlacerImpl::SeamComparator::setup, Slic3r::spRear, Slic3r::to_3d(), and Slic3r::total_length().

+ Here is the call graph for this function:

◆ calculate_candidates_visibility()

void Slic3r::SeamPlacer::calculate_candidates_visibility ( const PrintObject po,
const SeamPlacerImpl::GlobalModelInfo global_model_info 
)
private
1033 {
1034 using namespace SeamPlacerImpl;
1035
1036 std::vector<PrintObjectSeamData::LayerSeams> &layers = m_seam_per_object[po].layers;
1037 tbb::parallel_for(tbb::blocked_range<size_t>(0, layers.size()),
1038 [&layers, &global_model_info](tbb::blocked_range<size_t> r) {
1039 for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) {
1040 for (auto &perimeter_point : layers[layer_idx].points) {
1041 perimeter_point.visibility = global_model_info.calculate_point_visibility(
1042 perimeter_point.position);
1043 }
1044 }
1045 });
1046}

◆ calculate_overhangs_and_layer_embedding()

void Slic3r::SeamPlacer::calculate_overhangs_and_layer_embedding ( const PrintObject po)
private
1048 {
1049 using namespace SeamPlacerImpl;
1050 using PerimeterDistancer = AABBTreeLines::LinesDistancer<Linef>;
1051
1052 std::vector<PrintObjectSeamData::LayerSeams> &layers = m_seam_per_object[po].layers;
1053 tbb::parallel_for(tbb::blocked_range<size_t>(0, layers.size()),
1054 [po, &layers](tbb::blocked_range<size_t> r) {
1055 std::unique_ptr<PerimeterDistancer> prev_layer_distancer;
1056 if (r.begin() > 0) { // previous layer exists
1057 prev_layer_distancer = std::make_unique<PerimeterDistancer>(to_unscaled_linesf(po->layers()[r.begin() - 1]->lslices));
1058 }
1059
1060 for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) {
1061 size_t regions_with_perimeter = 0;
1062 for (const LayerRegion *region : po->layers()[layer_idx]->regions()) {
1063 if (region->perimeters().size() > 0) {
1064 regions_with_perimeter++;
1065 }
1066 };
1067 bool should_compute_layer_embedding = regions_with_perimeter > 1;
1068 std::unique_ptr<PerimeterDistancer> current_layer_distancer = std::make_unique<PerimeterDistancer>(
1069 to_unscaled_linesf(po->layers()[layer_idx]->lslices));
1070
1071 for (SeamCandidate &perimeter_point : layers[layer_idx].points) {
1072 Vec2f point = Vec2f { perimeter_point.position.head<2>() };
1073 if (prev_layer_distancer.get() != nullptr) {
1074 perimeter_point.overhang = prev_layer_distancer->distance_from_lines<true>(point.cast<double>())
1075 + 0.6f * perimeter_point.perimeter.flow_width
1077 * po->layers()[layer_idx]->height;
1078 perimeter_point.overhang =
1079 perimeter_point.overhang < 0.0f ? 0.0f : perimeter_point.overhang;
1080 }
1081
1082 if (should_compute_layer_embedding) { // search for embedded perimeter points (points hidden inside the print ,e.g. multimaterial join, best position for seam)
1083 perimeter_point.embedded_distance = current_layer_distancer->distance_from_lines<true>(point.cast<double>())
1084 + 0.6f * perimeter_point.perimeter.flow_width;
1085 }
1086 }
1087
1088 prev_layer_distancer.swap(current_layer_distancer);
1089 }
1090 }
1091 );
1092 }
EIGEN_DEVICE_FUNC const TanReturnType tan() const
Definition ArrayCwiseUnaryOps.h:234
static constexpr float overhang_angle_threshold
Definition SeamPlacer.hpp:120
Linesf to_unscaled_linesf(const ExPolygons &src)
Definition ExPolygon.hpp:175

References Slic3r::PrintObject::layers(), tan(), and Slic3r::to_unscaled_linesf().

+ Here is the call 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
private
1105 {
1106 using namespace SeamPlacerImpl;
1107 std::vector<size_t> nearby_points_indices = find_nearby_points(*layers[layer_idx].points_tree, projected_position,
1108 max_distance);
1109
1110 if (nearby_points_indices.empty()) {
1111 return {};
1112 }
1113
1114 size_t best_nearby_point_index = nearby_points_indices[0];
1115 size_t nearest_point_index = nearby_points_indices[0];
1116
1117 // Now find best nearby point, nearest point, and corresponding indices
1118 for (const size_t &nearby_point_index : nearby_points_indices) {
1119 const SeamCandidate &point = layers[layer_idx].points[nearby_point_index];
1120 if (point.perimeter.finalized) {
1121 continue; // skip over finalized perimeters, try to find some that is not finalized
1122 }
1123 if (comparator.is_first_better(point, layers[layer_idx].points[best_nearby_point_index],
1124 projected_position.head<2>())
1125 || layers[layer_idx].points[best_nearby_point_index].perimeter.finalized) {
1126 best_nearby_point_index = nearby_point_index;
1127 }
1128 if ((point.position - projected_position).squaredNorm()
1129 < (layers[layer_idx].points[nearest_point_index].position - projected_position).squaredNorm()
1130 || layers[layer_idx].points[nearest_point_index].perimeter.finalized) {
1131 nearest_point_index = nearby_point_index;
1132 }
1133 }
1134
1135 const SeamCandidate &best_nearby_point = layers[layer_idx].points[best_nearby_point_index];
1136 const SeamCandidate &nearest_point = layers[layer_idx].points[nearest_point_index];
1137
1138 if (nearest_point.perimeter.finalized) {
1139 //all points are from already finalized perimeter, skip
1140 return {};
1141 }
1142
1143 //from the nearest_point, deduce index of seam in the next layer
1144 const SeamCandidate &next_layer_seam = layers[layer_idx].points[nearest_point.perimeter.seam_index];
1145
1146 // First try to pick central enforcer if any present
1147 if (next_layer_seam.central_enforcer
1148 && (next_layer_seam.position - projected_position).squaredNorm()
1149 < sqr(3 * max_distance)) {
1150 return {std::pair<size_t, size_t> {layer_idx, nearest_point.perimeter.seam_index}};
1151 }
1152
1153 // First try to align the nearest, then try the best nearby
1154 if (comparator.is_first_not_much_worse(nearest_point, next_layer_seam)) {
1155 return {std::pair<size_t, size_t> {layer_idx, nearest_point_index}};
1156 }
1157 // If nearest point is not good enough, try it with the best nearby point.
1158 if (comparator.is_first_not_much_worse(best_nearby_point, next_layer_seam)) {
1159 return {std::pair<size_t, size_t> {layer_idx, best_nearby_point_index}};
1160 }
1161
1162 return {};
1163}
std::pair< Point, bool > nearest_point(const Points &points, const Point &pt)
Definition Point.hpp:266
std::vector< size_t > find_nearby_points(const KDTreeIndirectType &kdtree, const PointType &center, const typename KDTreeIndirectType::CoordType &max_distance, FilterFn filter)
Definition KDTreeIndirect.hpp:281
constexpr T sqr(T x)
Definition libslic3r.h:258
int nearest_point_index(const Points &points, const Point &pt)
Definition Point.cpp:118

References Slic3r::find_nearby_points(), Slic3r::SeamPlacerImpl::SeamComparator::is_first_better(), Slic3r::SeamPlacerImpl::SeamComparator::is_first_not_much_worse(), Slic3r::nearest_point(), Slic3r::nearest_point_index(), and Slic3r::sqr().

+ Here is the call 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
private
1166 {
1167 const std::vector<PrintObjectSeamData::LayerSeams> &layers = m_seam_per_object.find(po)->second.layers;
1168 int layer_idx = start_seam.first;
1169
1170 //initialize searching for seam string - cluster of nearby seams on previous and next layers
1171 int next_layer = layer_idx + 1;
1172 int step = 1;
1173 std::pair<size_t, size_t> prev_point_index = start_seam;
1174 std::vector<std::pair<size_t, size_t>> seam_string { start_seam };
1175
1176 auto reverse_lookup_direction = [&]() {
1177 step = -1;
1178 prev_point_index = start_seam;
1179 next_layer = layer_idx - 1;
1180 };
1181
1182 while (next_layer >= 0) {
1183 if (next_layer >= int(layers.size())) {
1184 reverse_lookup_direction();
1185 if (next_layer < 0) {
1186 break;
1187 }
1188 }
1190 layers[start_seam.first].points[start_seam.second].perimeter.flow_width;
1191 Vec3f prev_position = layers[prev_point_index.first].points[prev_point_index.second].position;
1192 Vec3f projected_position = prev_position;
1193 projected_position.z() = float(po->get_layer(next_layer)->slice_z);
1194
1195 std::optional<std::pair<size_t, size_t>> maybe_next_seam = find_next_seam_in_layer(layers, projected_position,
1196 next_layer,
1197 max_distance, comparator);
1198
1199 if (maybe_next_seam.has_value()) {
1200 // For old macOS (pre 10.14), std::optional does not have .value() method, so the code is using operator*() instead.
1201 seam_string.push_back(maybe_next_seam.operator*());
1202 prev_point_index = seam_string.back();
1203 //String added, prev_point_index updated
1204 } else {
1205 if (step == 1) {
1206 reverse_lookup_direction();
1207 if (next_layer < 0) {
1208 break;
1209 }
1210 } else {
1211 break;
1212 }
1213 }
1214 next_layer += step;
1215 }
1216 return seam_string;
1217}
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
Definition SeamPlacer.cpp:1101
static constexpr float seam_align_tolerable_dist_factor
Definition SeamPlacer.hpp:133
Coord step(const Coord &crd, Dir d)
Definition MarchingSquares.hpp:137

References Slic3r::PrintObject::get_layer(), and Slic3r::Layer::slice_z.

+ Here is the call graph for this function:

◆ gather_seam_candidates()

void Slic3r::SeamPlacer::gather_seam_candidates ( const PrintObject po,
const SeamPlacerImpl::GlobalModelInfo global_model_info 
)
private
1004 {
1005 using namespace SeamPlacerImpl;
1006 PrintObjectSeamData &seam_data = m_seam_per_object.emplace(po, PrintObjectSeamData { }).first->second;
1007 seam_data.layers.resize(po->layer_count());
1008
1009 tbb::parallel_for(tbb::blocked_range<size_t>(0, po->layers().size()),
1010 [po, &global_model_info, &seam_data]
1011 (tbb::blocked_range<size_t> r) {
1012 for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) {
1013 PrintObjectSeamData::LayerSeams &layer_seams = seam_data.layers[layer_idx];
1014 const Layer *layer = po->get_layer(layer_idx);
1015 auto unscaled_z = layer->slice_z;
1016 std::vector<const LayerRegion*> regions;
1017 //NOTE corresponding region ptr may be null, if the layer has zero perimeters
1018 Polygons polygons = extract_perimeter_polygons(layer, regions);
1019 for (size_t poly_index = 0; poly_index < polygons.size(); ++poly_index) {
1020 process_perimeter_polygon(polygons[poly_index], unscaled_z,
1021 regions[poly_index], global_model_info, layer_seams);
1022 }
1023 auto functor = SeamCandidateCoordinateFunctor { layer_seams.points };
1024 seam_data.layers[layer_idx].points_tree =
1025 std::make_unique<PrintObjectSeamData::SeamCandidatesTree>(functor,
1026 layer_seams.points.size());
1027 }
1028 }
1029 );
1030}

References Slic3r::PrintObject::layer_count(), Slic3r::PrintObjectSeamData::layers, and Slic3r::PrintObject::layers().

+ Here is the call graph for this function:

◆ init()

void Slic3r::SeamPlacer::init ( const Print print,
std::function< void(void)>  throw_if_canceled_func 
)
1414 {
1415 using namespace SeamPlacerImpl;
1416 m_seam_per_object.clear();
1417
1418 for (const PrintObject *po : print.objects()) {
1419 throw_if_canceled_func();
1420 SeamPosition configured_seam_preference = po->config().seam_position.value;
1421 SeamComparator comparator { configured_seam_preference };
1422
1423 {
1424 GlobalModelInfo global_model_info { };
1425 gather_enforcers_blockers(global_model_info, po);
1426 throw_if_canceled_func();
1427 if (configured_seam_preference == spAligned || configured_seam_preference == spNearest) {
1428 compute_global_occlusion(global_model_info, po, throw_if_canceled_func);
1429 }
1430 throw_if_canceled_func();
1431 BOOST_LOG_TRIVIAL(debug)
1432 << "SeamPlacer: gather_seam_candidates: start";
1433 gather_seam_candidates(po, global_model_info);
1434 BOOST_LOG_TRIVIAL(debug)
1435 << "SeamPlacer: gather_seam_candidates: end";
1436 throw_if_canceled_func();
1437 if (configured_seam_preference == spAligned || configured_seam_preference == spNearest) {
1438 BOOST_LOG_TRIVIAL(debug)
1439 << "SeamPlacer: calculate_candidates_visibility : start";
1440 calculate_candidates_visibility(po, global_model_info);
1441 BOOST_LOG_TRIVIAL(debug)
1442 << "SeamPlacer: calculate_candidates_visibility : end";
1443 }
1444 } // destruction of global_model_info (large structure, no longer needed)
1445 throw_if_canceled_func();
1446 BOOST_LOG_TRIVIAL(debug)
1447 << "SeamPlacer: calculate_overhangs and layer embdedding : start";
1449 BOOST_LOG_TRIVIAL(debug)
1450 << "SeamPlacer: calculate_overhangs and layer embdedding: end";
1451 throw_if_canceled_func();
1452 if (configured_seam_preference != spNearest) { // For spNearest, the seam is picked in the place_seam method with actual nozzle position information
1453 BOOST_LOG_TRIVIAL(debug)
1454 << "SeamPlacer: pick_seam_point : start";
1455 //pick seam point
1456 std::vector<PrintObjectSeamData::LayerSeams> &layers = m_seam_per_object[po].layers;
1457 tbb::parallel_for(tbb::blocked_range<size_t>(0, layers.size()),
1458 [&layers, configured_seam_preference, comparator](tbb::blocked_range<size_t> r) {
1459 for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) {
1460 std::vector<SeamCandidate> &layer_perimeter_points = layers[layer_idx].points;
1461 for (size_t current = 0; current < layer_perimeter_points.size();
1462 current = layer_perimeter_points[current].perimeter.end_index)
1463 if (configured_seam_preference == spRandom)
1464 pick_random_seam_point(layer_perimeter_points, current);
1465 else
1466 pick_seam_point(layer_perimeter_points, current, comparator);
1467 }
1468 });
1469 BOOST_LOG_TRIVIAL(debug)
1470 << "SeamPlacer: pick_seam_point : end";
1471 }
1472 throw_if_canceled_func();
1473 if (configured_seam_preference == spAligned || configured_seam_preference == spRear) {
1474 BOOST_LOG_TRIVIAL(debug)
1475 << "SeamPlacer: align_seam_points : start";
1476 align_seam_points(po, comparator);
1477 BOOST_LOG_TRIVIAL(debug)
1478 << "SeamPlacer: align_seam_points : end";
1479 }
1480
1481#ifdef DEBUG_FILES
1482 debug_export_points(m_seam_per_object[po].layers, po->bounding_box(), comparator);
1483#endif
1484 }
1485}
void calculate_overhangs_and_layer_embedding(const PrintObject *po)
Definition SeamPlacer.cpp:1048
void gather_seam_candidates(const PrintObject *po, const SeamPlacerImpl::GlobalModelInfo &global_model_info)
Definition SeamPlacer.cpp:1004
void calculate_candidates_visibility(const PrintObject *po, const SeamPlacerImpl::GlobalModelInfo &global_model_info)
Definition SeamPlacer.cpp:1032
void align_seam_points(const PrintObject *po, const SeamPlacerImpl::SeamComparator &comparator)
Definition SeamPlacer.cpp:1224
void gather_enforcers_blockers(GlobalModelInfo &result, const PrintObject *po)
Definition SeamPlacer.cpp:703
void compute_global_occlusion(GlobalModelInfo &result, const PrintObject *po, std::function< void(void)> throw_if_canceled)
Definition SeamPlacer.cpp:620
SeamPosition
Definition PrintConfig.hpp:97
@ spNearest
Definition PrintConfig.hpp:98
@ spAligned
Definition PrintConfig.hpp:98

References Slic3r::PrintObject::bounding_box(), Slic3r::PrintObject::config(), Slic3r::Print::objects(), Slic3r::spAligned, Slic3r::spNearest, and Slic3r::spRear.

Referenced by Slic3r::GCode::_do_export().

+ 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,
bool  external_first,
const Point last_pos 
) const
1488 {
1489 using namespace SeamPlacerImpl;
1490 const PrintObject *po = layer->object();
1491 // Must not be called with supprot layer.
1492 assert(dynamic_cast<const SupportLayer*>(layer) == nullptr);
1493 // Object layer IDs are incremented by the number of raft layers.
1494 assert(layer->id() >= po->slicing_parameters().raft_layers());
1495 const size_t layer_index = layer->id() - po->slicing_parameters().raft_layers();
1496 const double unscaled_z = layer->slice_z;
1497
1498 auto get_next_loop_point = [loop](ExtrusionLoop::ClosestPathPoint current) {
1499 current.segment_idx += 1;
1500 if (current.segment_idx >= loop.paths[current.path_idx].polyline.points.size()) {
1501 current.path_idx = next_idx_modulo(current.path_idx, loop.paths.size());
1502 current.segment_idx = 0;
1503 }
1504 current.foot_pt = loop.paths[current.path_idx].polyline.points[current.segment_idx];
1505 return current;
1506 };
1507
1508 const PrintObjectSeamData::LayerSeams &layer_perimeters =
1509 m_seam_per_object.find(layer->object())->second.layers[layer_index];
1510
1511 // Find the closest perimeter in the SeamPlacer to this loop.
1512 // Repeat search until two consecutive points of the loop are found, that result in the same closest_perimeter
1513 // This is beacuse with arachne, T-Junctions may exist and sometimes the wrong perimeter was chosen
1514 size_t closest_perimeter_point_index = 0;
1515 { // local space for the closest_perimeter_point_index
1516 Perimeter *closest_perimeter = nullptr;
1517 ExtrusionLoop::ClosestPathPoint closest_point{0,0,loop.paths[0].polyline.points[0]};
1518 size_t points_count = std::accumulate(loop.paths.begin(), loop.paths.end(), 0, [](size_t acc,const ExtrusionPath& p) {
1519 return acc + p.polyline.points.size();
1520 });
1521 for (size_t i = 0; i < points_count; ++i) {
1522 Vec2f unscaled_p = unscaled<float>(closest_point.foot_pt);
1523 closest_perimeter_point_index = find_closest_point(*layer_perimeters.points_tree.get(),
1524 to_3d(unscaled_p, float(unscaled_z)));
1525 if (closest_perimeter != &layer_perimeters.points[closest_perimeter_point_index].perimeter) {
1526 closest_perimeter = &layer_perimeters.points[closest_perimeter_point_index].perimeter;
1527 closest_point = get_next_loop_point(closest_point);
1528 } else {
1529 break;
1530 }
1531 }
1532 }
1533
1534 Vec3f seam_position;
1535 size_t seam_index;
1536 if (const Perimeter &perimeter = layer_perimeters.points[closest_perimeter_point_index].perimeter;
1537 perimeter.finalized) {
1538 seam_position = perimeter.final_seam_position;
1539 seam_index = perimeter.seam_index;
1540 } else {
1541 seam_index =
1542 po->config().seam_position == spNearest ?
1543 pick_nearest_seam_point_index(layer_perimeters.points, perimeter.start_index,
1544 unscaled<float>(last_pos)) :
1545 perimeter.seam_index;
1546 seam_position = layer_perimeters.points[seam_index].position;
1547 }
1548
1549 Point seam_point = Point::new_scale(seam_position.x(), seam_position.y());
1550
1551 if (loop.role() == ExtrusionRole::Perimeter) { //Hopefully inner perimeter
1552 const SeamCandidate &perimeter_point = layer_perimeters.points[seam_index];
1553 ExtrusionLoop::ClosestPathPoint projected_point = loop.get_closest_path_and_point(seam_point, false);
1554 // determine depth of the seam point.
1555 float depth = (float) unscale(Point(seam_point - projected_point.foot_pt)).norm();
1556 float beta_angle = cos(perimeter_point.local_ccw_angle / 2.0f);
1557 size_t index_of_prev =
1558 seam_index == perimeter_point.perimeter.start_index ?
1559 perimeter_point.perimeter.end_index - 1 :
1560 seam_index - 1;
1561 size_t index_of_next =
1562 seam_index == perimeter_point.perimeter.end_index - 1 ?
1563 perimeter_point.perimeter.start_index :
1564 seam_index + 1;
1565
1566 if ((seam_position - perimeter_point.position).squaredNorm() < depth && // seam is on perimeter point
1567 perimeter_point.local_ccw_angle < -EPSILON // In concave angles
1568 ) { // In this case, we are at internal perimeter, where the external perimeter has seam in concave angle. We want to align
1569 // 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)
1570 Vec2f dir_to_middle =
1571 ((perimeter_point.position - layer_perimeters.points[index_of_prev].position).head<2>().normalized()
1572 + (perimeter_point.position - layer_perimeters.points[index_of_next].position).head<2>().normalized())
1573 * 0.5;
1574 depth = 1.4142 * depth / beta_angle;
1575 // There are some nice geometric identities in determination of the correct depth of new seam point.
1576 //overshoot the target depth, in concave angles it will correctly snap to the corner; TODO: find out why such big overshoot is needed.
1577 Vec2f final_pos = perimeter_point.position.head<2>() + depth * dir_to_middle;
1578 projected_point = loop.get_closest_path_and_point(Point::new_scale(final_pos.x(), final_pos.y()), false);
1579 } else { // not concave angle, in that case the nearest point is the good candidate
1580 // but for staggering, we also need to recompute depth of the inner perimter, because in convex corners, the distance is larger than layer width
1581 // we want the perpendicular depth, not distance to nearest point
1582 depth = depth * beta_angle / 1.4142;
1583 }
1584
1585 seam_point = projected_point.foot_pt;
1586
1587 //lastly, for internal perimeters, do the staggering if requested
1588 if (po->config().staggered_inner_seams && loop.length() > 0.0) {
1589 //fix depth, it is sometimes strongly underestimated
1590 depth = std::max(loop.paths[projected_point.path_idx].width, depth);
1591
1592 while (depth > 0.0f) {
1593 auto next_point = get_next_loop_point(projected_point);
1594 Vec2f a = unscale(projected_point.foot_pt).cast<float>();
1595 Vec2f b = unscale(next_point.foot_pt).cast<float>();
1596 float dist = (a - b).norm();
1597 if (dist > depth) {
1598 Vec2f final_pos = a + (b - a) * depth / dist;
1599 next_point.foot_pt = Point::new_scale(final_pos.x(), final_pos.y());
1600 }
1601 depth -= dist;
1602 projected_point = next_point;
1603 }
1604 seam_point = projected_point.foot_pt;
1605 }
1606 }
1607
1608 // Because the G-code export has 1um resolution, don't generate segments shorter than 1.5 microns,
1609 // thus empty path segments will not be produced by G-code export.
1610 if (!loop.split_at_vertex(seam_point, scaled<double>(0.0015))) {
1611 // The point is not in the original loop.
1612 // Insert it.
1613 loop.split_at(seam_point, true);
1614 }
1615
1616}
EIGEN_DEVICE_FUNC const CosReturnType cos() const
Definition ArrayCwiseUnaryOps.h:202
static constexpr double EPSILON
Definition libslic3r.h:51
T dist(const boost::polygon::point_data< T > &p1, const boost::polygon::point_data< T > &p2)
Definition Geometry.cpp:280
size_t pick_nearest_seam_point_index(const std::vector< SeamCandidate > &perimeter_points, size_t start_index, const Vec2f &preffered_location)
Definition SeamPlacer.cpp:920
INDEX_TYPE next_idx_modulo(INDEX_TYPE idx, const INDEX_TYPE count)
Definition Utils.hpp:203
size_t find_closest_point(const KDTreeIndirect< D, CoordT, CoordFn > &kdtree, const PointType &point, FilterFn filter)
Definition KDTreeIndirect.hpp:266
T unscale(Q v)
Definition libslic3r.h:95
IGL_INLINE void loop(const int n_verts, const Eigen::PlainObjectBase< DerivedF > &F, Eigen::SparseMatrix< SType > &S, Eigen::PlainObjectBase< DerivedNF > &NF)
Definition loop.cpp:21
Kernel::Point_2 Point
Definition point_areas.cpp:20
static constexpr const ExtrusionRoleModifiers Perimeter
Definition ExtrusionRole.hpp:49

References Slic3r::PrintObject::config(), cos(), EPSILON, Slic3r::find_closest_point(), Slic3r::ExtrusionLoop::ClosestPathPoint::foot_pt, Slic3r::Layer::id(), Slic3r::next_idx_modulo(), Slic3r::Layer::object(), Slic3r::ExtrusionLoop::ClosestPathPoint::path_idx, Slic3r::Perimeter, Slic3r::PrintObjectSeamData::LayerSeams::points, Slic3r::PrintObjectSeamData::LayerSeams::points_tree, Slic3r::SlicingParameters::raft_layers(), Slic3r::Layer::slice_z, Slic3r::PrintObject::slicing_parameters(), Slic3r::spNearest, Slic3r::to_3d(), and Slic3r::unscale().

Referenced by Slic3r::GCode::extrude_loop().

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

Member Data Documentation

◆ angle_importance_aligned

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

◆ angle_importance_nearest

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

◆ enforcer_oversampling_distance

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

◆ fast_decimation_triangle_count_target

constexpr size_t Slic3r::SeamPlacer::fast_decimation_triangle_count_target = 16000
staticconstexpr

◆ m_seam_per_object

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

◆ overhang_angle_threshold

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

◆ raycasting_visibility_samples_count

constexpr size_t Slic3r::SeamPlacer::raycasting_visibility_samples_count = 30000
staticconstexpr

◆ seam_align_minimum_string_seams

constexpr size_t Slic3r::SeamPlacer::seam_align_minimum_string_seams = 6
staticconstexpr

◆ seam_align_mm_per_segment

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

◆ seam_align_score_tolerance

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

◆ seam_align_tolerable_dist_factor

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

◆ sharp_angle_snapping_threshold

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

◆ sqr_rays_per_sample_point

constexpr size_t Slic3r::SeamPlacer::sqr_rays_per_sample_point = 5
staticconstexpr

The documentation for this class was generated from the following files: