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

Classes

struct  Parameters
 

Functions

void process_classic (const Parameters &params, const Surface &surface, const ExPolygons *lower_slices, Polygons &lower_slices_polygons_cache, ExtrusionEntityCollection &out_loops, ExtrusionEntityCollection &out_gap_fill, ExPolygons &out_fill_expolygons)
 
void process_arachne (const Parameters &params, const Surface &surface, const ExPolygons *lower_slices, Polygons &lower_slices_polygons_cache, ExtrusionEntityCollection &out_loops, ExtrusionEntityCollection &out_gap_fill, ExPolygons &out_fill_expolygons)
 
ExtrusionMultiPath thick_polyline_to_multi_path (const ThickPolyline &thick_polyline, ExtrusionRole role, const Flow &flow, float tolerance, float merge_tolerance)
 

Function Documentation

◆ process_arachne()

void Slic3r::PerimeterGenerator::process_arachne ( const Parameters params,
const Surface surface,
const ExPolygons lower_slices,
Polygons lower_slices_polygons_cache,
ExtrusionEntityCollection out_loops,
ExtrusionEntityCollection out_gap_fill,
ExPolygons out_fill_expolygons 
)
1089{
1090 // other perimeters
1091 coord_t perimeter_spacing = params.perimeter_flow.scaled_spacing();
1092 // external perimeters
1093 coord_t ext_perimeter_width = params.ext_perimeter_flow.scaled_width();
1094 coord_t ext_perimeter_spacing = params.ext_perimeter_flow.scaled_spacing();
1095 coord_t ext_perimeter_spacing2 = scaled<coord_t>(0.5f * (params.ext_perimeter_flow.spacing() + params.perimeter_flow.spacing()));
1096 // solid infill
1097 coord_t solid_infill_spacing = params.solid_infill_flow.scaled_spacing();
1098
1099 // prepare grown lower layer slices for overhang detection
1100 if (params.config.overhangs && lower_slices != nullptr && lower_slices_polygons_cache.empty()) {
1101 // We consider overhang any part where the entire nozzle diameter is not supported by the
1102 // lower layer, so we take lower slices and offset them by half the nozzle diameter used
1103 // in the current layer
1104 double nozzle_diameter = params.print_config.nozzle_diameter.get_at(params.config.perimeter_extruder-1);
1105 lower_slices_polygons_cache = offset(*lower_slices, float(scale_(+nozzle_diameter/2)));
1106 }
1107
1108 // we need to process each island separately because we might have different
1109 // extra perimeters for each one
1110 // detect how many perimeters must be generated for this island
1111 int loop_number = params.config.perimeters + surface.extra_perimeters - 1; // 0-indexed loops
1112 ExPolygons last = offset_ex(surface.expolygon.simplify_p(params.scaled_resolution), - float(ext_perimeter_width / 2. - ext_perimeter_spacing / 2.));
1113 Polygons last_p = to_polygons(last);
1114
1115 Arachne::WallToolPaths wallToolPaths(last_p, ext_perimeter_spacing, perimeter_spacing, coord_t(loop_number + 1), 0, params.layer_height, params.object_config, params.print_config);
1116 std::vector<Arachne::VariableWidthLines> perimeters = wallToolPaths.getToolPaths();
1117 loop_number = int(perimeters.size()) - 1;
1118
1119#ifdef ARACHNE_DEBUG
1120 {
1121 static int iRun = 0;
1122 export_perimeters_to_svg(debug_out_path("arachne-perimeters-%d-%d.svg", layer_id, iRun++), to_polygons(last), perimeters, union_ex(wallToolPaths.getInnerContour()));
1123 }
1124#endif
1125
1126 // All closed ExtrusionLine should have the same the first and the last point.
1127 // But in rare cases, Arachne produce ExtrusionLine marked as closed but without
1128 // equal the first and the last point.
1129 assert([&perimeters = std::as_const(perimeters)]() -> bool {
1130 for (const Arachne::VariableWidthLines &perimeter : perimeters)
1131 for (const Arachne::ExtrusionLine &el : perimeter)
1132 if (el.is_closed && el.junctions.front().p != el.junctions.back().p)
1133 return false;
1134 return true;
1135 }());
1136
1137 int start_perimeter = int(perimeters.size()) - 1;
1138 int end_perimeter = -1;
1139 int direction = -1;
1140
1141 if (params.config.external_perimeters_first) {
1142 start_perimeter = 0;
1143 end_perimeter = int(perimeters.size());
1144 direction = 1;
1145 }
1146
1147 std::vector<Arachne::ExtrusionLine *> all_extrusions;
1148 for (int perimeter_idx = start_perimeter; perimeter_idx != end_perimeter; perimeter_idx += direction) {
1149 if (perimeters[perimeter_idx].empty())
1150 continue;
1151 for (Arachne::ExtrusionLine &wall : perimeters[perimeter_idx])
1152 all_extrusions.emplace_back(&wall);
1153 }
1154
1155 // Find topological order with constraints from extrusions_constrains.
1156 std::vector<size_t> blocked(all_extrusions.size(), 0); // Value indicating how many extrusions it is blocking (preceding extrusions) an extrusion.
1157 std::vector<std::vector<size_t>> blocking(all_extrusions.size()); // Each extrusion contains a vector of extrusions that are blocked by this extrusion.
1158 ankerl::unordered_dense::map<const Arachne::ExtrusionLine *, size_t> map_extrusion_to_idx;
1159 for (size_t idx = 0; idx < all_extrusions.size(); idx++)
1160 map_extrusion_to_idx.emplace(all_extrusions[idx], idx);
1161
1162 Arachne::WallToolPaths::ExtrusionLineSet extrusions_constrains = Arachne::WallToolPaths::getRegionOrder(all_extrusions, params.config.external_perimeters_first);
1163 for (auto [before, after] : extrusions_constrains) {
1164 auto after_it = map_extrusion_to_idx.find(after);
1165 ++blocked[after_it->second];
1166 blocking[map_extrusion_to_idx.find(before)->second].emplace_back(after_it->second);
1167 }
1168
1169 std::vector<bool> processed(all_extrusions.size(), false); // Indicate that the extrusion was already processed.
1170 Point current_position = all_extrusions.empty() ? Point::Zero() : all_extrusions.front()->junctions.front().p; // Some starting position.
1171 std::vector<PerimeterGeneratorArachneExtrusion> ordered_extrusions; // To store our result in. At the end we'll std::swap.
1172 ordered_extrusions.reserve(all_extrusions.size());
1173
1174 while (ordered_extrusions.size() < all_extrusions.size()) {
1175 size_t best_candidate = 0;
1176 double best_distance_sqr = std::numeric_limits<double>::max();
1177 bool is_best_closed = false;
1178
1179 std::vector<size_t> available_candidates;
1180 for (size_t candidate = 0; candidate < all_extrusions.size(); ++candidate) {
1181 if (processed[candidate] || blocked[candidate])
1182 continue; // Not a valid candidate.
1183 available_candidates.push_back(candidate);
1184 }
1185
1186 std::sort(available_candidates.begin(), available_candidates.end(), [&all_extrusions](const size_t a_idx, const size_t b_idx) -> bool {
1187 return all_extrusions[a_idx]->is_closed < all_extrusions[b_idx]->is_closed;
1188 });
1189
1190 for (const size_t candidate_path_idx : available_candidates) {
1191 auto& path = all_extrusions[candidate_path_idx];
1192
1193 if (path->junctions.empty()) { // No vertices in the path. Can't find the start position then or really plan it in. Put that at the end.
1194 if (best_distance_sqr == std::numeric_limits<double>::max()) {
1195 best_candidate = candidate_path_idx;
1196 is_best_closed = path->is_closed;
1197 }
1198 continue;
1199 }
1200
1201 const Point candidate_position = path->junctions.front().p;
1202 double distance_sqr = (current_position - candidate_position).cast<double>().norm();
1203 if (distance_sqr < best_distance_sqr) { // Closer than the best candidate so far.
1204 if (path->is_closed || (!path->is_closed && best_distance_sqr != std::numeric_limits<double>::max()) || (!path->is_closed && !is_best_closed)) {
1205 best_candidate = candidate_path_idx;
1206 best_distance_sqr = distance_sqr;
1207 is_best_closed = path->is_closed;
1208 }
1209 }
1210 }
1211
1212 auto &best_path = all_extrusions[best_candidate];
1213 ordered_extrusions.push_back({best_path, best_path->is_contour(), false});
1214 processed[best_candidate] = true;
1215 for (size_t unlocked_idx : blocking[best_candidate])
1216 blocked[unlocked_idx]--;
1217
1218 if (!best_path->junctions.empty()) { //If all paths were empty, the best path is still empty. We don't upate the current position then.
1219 if(best_path->is_closed)
1220 current_position = best_path->junctions[0].p; //We end where we started.
1221 else
1222 current_position = best_path->junctions.back().p; //Pick the other end from where we started.
1223 }
1224 }
1225
1226 if (params.layer_id > 0 && params.config.fuzzy_skin != FuzzySkinType::None) {
1227 std::vector<PerimeterGeneratorArachneExtrusion *> closed_loop_extrusions;
1228 for (PerimeterGeneratorArachneExtrusion &extrusion : ordered_extrusions)
1229 if (extrusion.extrusion->inset_idx == 0) {
1230 if (extrusion.extrusion->is_closed && params.config.fuzzy_skin == FuzzySkinType::External) {
1231 closed_loop_extrusions.emplace_back(&extrusion);
1232 } else {
1233 extrusion.fuzzify = true;
1234 }
1235 }
1236
1237 if (params.config.fuzzy_skin == FuzzySkinType::External) {
1238 ClipperLib_Z::Paths loops_paths;
1239 loops_paths.reserve(closed_loop_extrusions.size());
1240 for (const auto &cl_extrusion : closed_loop_extrusions) {
1241 assert(cl_extrusion->extrusion->junctions.front() == cl_extrusion->extrusion->junctions.back());
1242 size_t loop_idx = &cl_extrusion - &closed_loop_extrusions.front();
1243 ClipperLib_Z::Path loop_path;
1244 loop_path.reserve(cl_extrusion->extrusion->junctions.size() - 1);
1245 for (auto junction_it = cl_extrusion->extrusion->junctions.begin(); junction_it != std::prev(cl_extrusion->extrusion->junctions.end()); ++junction_it)
1246 loop_path.emplace_back(junction_it->p.x(), junction_it->p.y(), loop_idx);
1247 loops_paths.emplace_back(loop_path);
1248 }
1249
1250 ClipperLib_Z::Clipper clipper;
1251 clipper.AddPaths(loops_paths, ClipperLib_Z::ptSubject, true);
1252 ClipperLib_Z::PolyTree loops_polytree;
1253 clipper.Execute(ClipperLib_Z::ctUnion, loops_polytree, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd);
1254
1255 for (const ClipperLib_Z::PolyNode *child_node : loops_polytree.Childs) {
1256 // The whole contour must have the same index.
1257 coord_t polygon_idx = child_node->Contour.front().z();
1258 bool has_same_idx = std::all_of(child_node->Contour.begin(), child_node->Contour.end(),
1259 [&polygon_idx](const ClipperLib_Z::IntPoint &point) -> bool { return polygon_idx == point.z(); });
1260 if (has_same_idx)
1261 closed_loop_extrusions[polygon_idx]->fuzzify = true;
1262 }
1263 }
1264 }
1265
1266 if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(params, lower_slices_polygons_cache, ordered_extrusions); !extrusion_coll.empty())
1267 out_loops.append(extrusion_coll);
1268
1269 ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour());
1270 const coord_t spacing = (perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing;
1271 if (offset_ex(infill_contour, -float(spacing / 2.)).empty())
1272 infill_contour.clear(); // Infill region is too small, so let's filter it out.
1273
1274 // create one more offset to be used as boundary for fill
1275 // we offset by half the perimeter spacing (to get to the actual infill boundary)
1276 // and then we offset back and forth by half the infill spacing to only consider the
1277 // non-collapsing regions
1278 coord_t inset =
1279 (loop_number < 0) ? 0 :
1280 (loop_number == 0) ?
1281 // one loop
1282 ext_perimeter_spacing:
1283 // two or more loops?
1284 perimeter_spacing;
1285
1286 inset = coord_t(scale_(params.config.get_abs_value("infill_overlap", unscale<double>(inset))));
1287 Polygons pp;
1288 for (ExPolygon &ex : infill_contour)
1289 ex.simplify_p(params.scaled_resolution, &pp);
1290 // collapse too narrow infill areas
1291 const auto min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE));
1292 // append infill areas to fill_surfaces
1293 ExPolygons infill_areas =
1294 offset2_ex(
1295 union_ex(pp),
1296 float(- min_perimeter_infill_spacing / 2.),
1297 float(inset + min_perimeter_infill_spacing / 2.));
1298
1299 if (lower_slices != nullptr && params.config.overhangs && params.config.extra_perimeters_on_overhangs &&
1300 params.config.perimeters > 0 && params.layer_id > params.object_config.raft_layers) {
1301 // Generate extra perimeters on overhang areas, and cut them to these parts only, to save print time and material
1302 auto [extra_perimeters, filled_area] = generate_extra_perimeters_over_overhangs(infill_areas,
1303 lower_slices_polygons_cache,
1304 loop_number + 1,
1305 params.overhang_flow, params.scaled_resolution,
1306 params.object_config, params.print_config);
1307 if (!extra_perimeters.empty()) {
1308 ExtrusionEntityCollection &this_islands_perimeters = static_cast<ExtrusionEntityCollection&>(*out_loops.entities.back());
1309 ExtrusionEntitiesPtr old_entities;
1310 old_entities.swap(this_islands_perimeters.entities);
1311 for (ExtrusionPaths &paths : extra_perimeters)
1312 this_islands_perimeters.append(std::move(paths));
1313 append(this_islands_perimeters.entities, old_entities);
1314 infill_areas = diff_ex(infill_areas, filled_area);
1315 }
1316 }
1317
1318 append(out_fill_expolygons, std::move(infill_areas));
1319}
Definition WallToolPaths.hpp:25
void simplify_p(double tolerance, Polygons *polygons) const
Definition ExPolygon.cpp:173
void append(const ExtrusionEntity &entity)
Definition ExtrusionEntityCollection.hpp:68
bool empty() const
Definition ExtrusionEntityCollection.hpp:65
ExtrusionEntitiesPtr entities
Definition ExtrusionEntityCollection.hpp:32
coord_t scaled_width() const
Definition Flow.hpp:61
float spacing() const
Definition Flow.hpp:66
coord_t scaled_spacing() const
Definition Flow.hpp:67
unsigned short extra_perimeters
Definition Surface.hpp:39
ExPolygon expolygon
Definition Surface.hpp:35
#define const
Definition getopt.c:38
if(!(yy_init))
Definition lexer.c:1190
#define scale_(val)
Definition libslic3r.h:69
int32_t coord_t
Definition libslic3r.h:39
static constexpr double INSET_OVERLAP_TOLERANCE
Definition libslic3r.h:63
std::vector< Polygon, PointsAllocator< Polygon > > Polygons
Definition Polygon.hpp:15
std::tuple< std::vector< ExtrusionPaths >, Polygons > generate_extra_perimeters_over_overhangs(ExPolygons infill_area, const Polygons &lower_slices_polygons, int perimeter_count, const Flow &overhang_flow, double scaled_resolution, const PrintObjectConfig &object_config, const PrintConfig &print_config)
Definition PerimeterGenerator.cpp:871
std::string debug_out_path(const char *name,...)
Definition utils.cpp:218
Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:726
Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, ClipperLib::PolyFillType fill_type)
Definition ClipperUtils.cpp:774
std::vector< ExPolygon > ExPolygons
Definition ExPolygon.hpp:13
Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType, double miterLimit)
Definition ClipperUtils.cpp:421
Polygons to_polygons(const ExPolygon &src)
Definition ExPolygon.hpp:281
static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator::Parameters &params, const Polygons &lower_slices_polygons_cache, std::vector< PerimeterGeneratorArachneExtrusion > &pg_extrusions)
Definition PerimeterGenerator.cpp:503
std::vector< ExtrusionEntity * > ExtrusionEntitiesPtr
Definition ExtrusionEntity.hpp:58
ExPolygons offset2_ex(const ExPolygons &expolygons, const float delta1, const float delta2, ClipperLib::JoinType joinType, double miterLimit)
Definition ClipperUtils.cpp:576
bool empty(const BoundingBoxBase< PointType, PointsType > &bb)
Definition BoundingBox.hpp:229
TPoint< P > front(const P &p)
Definition geometry_traits.hpp:872
void append(SurfaceCut &sc, SurfaceCut &&sc_add)
Merge two surface cuts together Added surface cut will be consumed.
Definition CutSurface.cpp:3550
STL namespace.
Kernel::Point_2 Point
Definition point_areas.cpp:20
Flow ext_perimeter_flow
Definition PerimeterGenerator.hpp:50
double scaled_resolution
Definition PerimeterGenerator.hpp:59
Flow perimeter_flow
Definition PerimeterGenerator.hpp:49
int layer_id
Definition PerimeterGenerator.hpp:48
Flow solid_infill_flow
Definition PerimeterGenerator.hpp:52
Flow overhang_flow
Definition PerimeterGenerator.hpp:51
const PrintObjectConfig & object_config
Definition PerimeterGenerator.hpp:54
const PrintRegionConfig & config
Definition PerimeterGenerator.hpp:53
const PrintConfig & print_config
Definition PerimeterGenerator.hpp:55
double layer_height
Definition PerimeterGenerator.hpp:47

References Slic3r::ExtrusionEntityCollection::append(), Slic3r::append(), Slic3r::PerimeterGenerator::Parameters::config, Slic3r::debug_out_path(), Slic3r::diff_ex(), Slic3r::ExtrusionEntityCollection::empty(), Slic3r::empty(), Slic3r::ExtrusionEntityCollection::entities, Slic3r::Surface::expolygon, Slic3r::PerimeterGenerator::Parameters::ext_perimeter_flow, Slic3r::External, Slic3r::Surface::extra_perimeters, Slic3r::generate_extra_perimeters_over_overhangs(), Slic3r::Arachne::WallToolPaths::getInnerContour(), Slic3r::Arachne::WallToolPaths::getRegionOrder(), Slic3r::Arachne::WallToolPaths::getToolPaths(), INSET_OVERLAP_TOLERANCE, Slic3r::PerimeterGenerator::Parameters::layer_height, Slic3r::PerimeterGenerator::Parameters::layer_id, Slic3r::None, Slic3r::PerimeterGenerator::Parameters::object_config, Slic3r::offset(), Slic3r::offset2_ex(), Slic3r::offset_ex(), Slic3r::PerimeterGenerator::Parameters::overhang_flow, Slic3r::PerimeterGenerator::Parameters::perimeter_flow, Slic3r::PerimeterGenerator::Parameters::print_config, scale_, Slic3r::PerimeterGenerator::Parameters::scaled_resolution, Slic3r::Flow::scaled_spacing(), Slic3r::Flow::scaled_width(), Slic3r::ExPolygon::simplify_p(), Slic3r::PerimeterGenerator::Parameters::solid_infill_flow, Slic3r::Flow::spacing(), Slic3r::ExtrusionEntityCollection::swap(), Slic3r::to_polygons(), Slic3r::traverse_extrusions(), and Slic3r::union_ex().

Referenced by Slic3r::LayerRegion::make_perimeters().

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

◆ process_classic()

void Slic3r::PerimeterGenerator::process_classic ( const Parameters params,
const Surface surface,
const ExPolygons lower_slices,
Polygons lower_slices_polygons_cache,
ExtrusionEntityCollection out_loops,
ExtrusionEntityCollection out_gap_fill,
ExPolygons out_fill_expolygons 
)
1335{
1336 // other perimeters
1337 coord_t perimeter_width = params.perimeter_flow.scaled_width();
1338 coord_t perimeter_spacing = params.perimeter_flow.scaled_spacing();
1339 // external perimeters
1340 coord_t ext_perimeter_width = params.ext_perimeter_flow.scaled_width();
1341 coord_t ext_perimeter_spacing = params.ext_perimeter_flow.scaled_spacing();
1342 coord_t ext_perimeter_spacing2 = scaled<coord_t>(0.5f * (params.ext_perimeter_flow.spacing() + params.perimeter_flow.spacing()));
1343 // solid infill
1344 coord_t solid_infill_spacing = params.solid_infill_flow.scaled_spacing();
1345
1346 // Calculate the minimum required spacing between two adjacent traces.
1347 // This should be equal to the nominal flow spacing but we experiment
1348 // with some tolerance in order to avoid triggering medial axis when
1349 // some squishing might work. Loops are still spaced by the entire
1350 // flow spacing; this only applies to collapsing parts.
1351 // For ext_min_spacing we use the ext_perimeter_spacing calculated for two adjacent
1352 // external loops (which is the correct way) instead of using ext_perimeter_spacing2
1353 // which is the spacing between external and internal, which is not correct
1354 // and would make the collapsing (thus the details resolution) dependent on
1355 // internal flow which is unrelated.
1356 coord_t min_spacing = coord_t(perimeter_spacing * (1 - INSET_OVERLAP_TOLERANCE));
1357 coord_t ext_min_spacing = coord_t(ext_perimeter_spacing * (1 - INSET_OVERLAP_TOLERANCE));
1358 bool has_gap_fill = params.config.gap_fill_enabled.value && params.config.gap_fill_speed.value > 0;
1359
1360 // prepare grown lower layer slices for overhang detection
1361 if (params.config.overhangs && lower_slices != nullptr && lower_slices_polygons_cache.empty()) {
1362 // We consider overhang any part where the entire nozzle diameter is not supported by the
1363 // lower layer, so we take lower slices and offset them by half the nozzle diameter used
1364 // in the current layer
1365 double nozzle_diameter = params.print_config.nozzle_diameter.get_at(params.config.perimeter_extruder-1);
1366 lower_slices_polygons_cache = offset(*lower_slices, float(scale_(+nozzle_diameter/2)));
1367 }
1368
1369 // we need to process each island separately because we might have different
1370 // extra perimeters for each one
1371 // detect how many perimeters must be generated for this island
1372 int loop_number = params.config.perimeters + surface.extra_perimeters - 1; // 0-indexed loops
1374 ExPolygons gaps;
1375 if (loop_number >= 0) {
1376 // In case no perimeters are to be generated, loop_number will equal to -1.
1377 std::vector<PerimeterGeneratorLoops> contours(loop_number+1); // depth => loops
1378 std::vector<PerimeterGeneratorLoops> holes(loop_number+1); // depth => loops
1380 // we loop one time more than needed in order to find gaps after the last perimeter was applied
1381 for (int i = 0;; ++ i) { // outer loop is 0
1382 // Calculate next onion shell of perimeters.
1383 ExPolygons offsets;
1384 if (i == 0) {
1385 // the minimum thickness of a single loop is:
1386 // ext_width/2 + ext_spacing/2 + spacing/2 + width/2
1387 offsets = params.config.thin_walls ?
1388 offset2_ex(
1389 last,
1390 - float(ext_perimeter_width / 2. + ext_min_spacing / 2. - 1),
1391 + float(ext_min_spacing / 2. - 1)) :
1392 offset_ex(last, - float(ext_perimeter_width / 2.));
1393 // look for thin walls
1394 if (params.config.thin_walls) {
1395 // the following offset2 ensures almost nothing in @thin_walls is narrower than $min_width
1396 // (actually, something larger than that still may exist due to mitering or other causes)
1397 coord_t min_width = coord_t(scale_(params.ext_perimeter_flow.nozzle_diameter() / 3));
1398 ExPolygons expp = opening_ex(
1399 // medial axis requires non-overlapping geometry
1400 diff_ex(last, offset(offsets, float(ext_perimeter_width / 2.) + ClipperSafetyOffset)),
1401 float(min_width / 2.));
1402 // the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop
1403 for (ExPolygon &ex : expp)
1404 ex.medial_axis(min_width, ext_perimeter_width + ext_perimeter_spacing2, &thin_walls);
1405 }
1406 if (params.spiral_vase && offsets.size() > 1) {
1407 // Remove all but the largest area polygon.
1408 keep_largest_contour_only(offsets);
1409 }
1410 } else {
1411 //FIXME Is this offset correct if the line width of the inner perimeters differs
1412 // from the line width of the infill?
1413 coord_t distance = (i == 1) ? ext_perimeter_spacing2 : perimeter_spacing;
1414 offsets = params.config.thin_walls ?
1415 // This path will ensure, that the perimeters do not overfill, as in
1416 // prusa3d/Slic3r GH #32, but with the cost of rounding the perimeters
1417 // excessively, creating gaps, which then need to be filled in by the not very
1418 // reliable gap fill algorithm.
1419 // Also the offset2(perimeter, -x, x) may sometimes lead to a perimeter, which is larger than
1420 // the original.
1421 offset2_ex(last,
1422 - float(distance + min_spacing / 2. - 1.),
1423 float(min_spacing / 2. - 1.)) :
1424 // If "detect thin walls" is not enabled, this paths will be entered, which
1425 // leads to overflows, as in prusa3d/Slic3r GH #32
1426 offset_ex(last, - float(distance));
1427 // look for gaps
1428 if (has_gap_fill)
1429 // not using safety offset here would "detect" very narrow gaps
1430 // (but still long enough to escape the area threshold) that gap fill
1431 // won't be able to fill but we'd still remove from infill area
1432 append(gaps, diff_ex(
1433 offset(last, - float(0.5 * distance)),
1434 offset(offsets, float(0.5 * distance + 10)))); // safety offset
1435 }
1436 if (offsets.empty()) {
1437 // Store the number of loops actually generated.
1438 loop_number = i - 1;
1439 // No region left to be filled in.
1440 last.clear();
1441 break;
1442 } else if (i > loop_number) {
1443 // If i > loop_number, we were looking just for gaps.
1444 break;
1445 }
1446 {
1447 const bool fuzzify_contours = params.config.fuzzy_skin != FuzzySkinType::None && i == 0 && params.layer_id > 0;
1448 const bool fuzzify_holes = fuzzify_contours && params.config.fuzzy_skin == FuzzySkinType::All;
1449 for (const ExPolygon &expolygon : offsets) {
1450 // Outer contour may overlap with an inner contour,
1451 // inner contour may overlap with another inner contour,
1452 // outer contour may overlap with itself.
1453 //FIXME evaluate the overlaps, annotate each point with an overlap depth,
1454 // compensate for the depth of intersection.
1455 contours[i].emplace_back(expolygon.contour, i, true, fuzzify_contours);
1456
1457 if (! expolygon.holes.empty()) {
1458 holes[i].reserve(holes[i].size() + expolygon.holes.size());
1459 for (const Polygon &hole : expolygon.holes)
1460 holes[i].emplace_back(hole, i, false, fuzzify_holes);
1461 }
1462 }
1463 }
1464 last = std::move(offsets);
1465 if (i == loop_number && (! has_gap_fill || params.config.fill_density.value == 0)) {
1466 // The last run of this loop is executed to collect gaps for gap fill.
1467 // As the gap fill is either disabled or not
1468 break;
1469 }
1470 }
1471
1472 // nest loops: holes first
1473 for (int d = 0; d <= loop_number; ++ d) {
1474 PerimeterGeneratorLoops &holes_d = holes[d];
1475 // loop through all holes having depth == d
1476 for (int i = 0; i < (int)holes_d.size(); ++ i) {
1477 const PerimeterGeneratorLoop &loop = holes_d[i];
1478 // find the hole loop that contains this one, if any
1479 for (int t = d + 1; t <= loop_number; ++ t) {
1480 for (int j = 0; j < (int)holes[t].size(); ++ j) {
1481 PerimeterGeneratorLoop &candidate_parent = holes[t][j];
1482 if (candidate_parent.polygon.contains(loop.polygon.first_point())) {
1483 candidate_parent.children.push_back(loop);
1484 holes_d.erase(holes_d.begin() + i);
1485 -- i;
1486 goto NEXT_LOOP;
1487 }
1488 }
1489 }
1490 // if no hole contains this hole, find the contour loop that contains it
1491 for (int t = loop_number; t >= 0; -- t) {
1492 for (int j = 0; j < (int)contours[t].size(); ++ j) {
1493 PerimeterGeneratorLoop &candidate_parent = contours[t][j];
1494 if (candidate_parent.polygon.contains(loop.polygon.first_point())) {
1495 candidate_parent.children.push_back(loop);
1496 holes_d.erase(holes_d.begin() + i);
1497 -- i;
1498 goto NEXT_LOOP;
1499 }
1500 }
1501 }
1502 NEXT_LOOP: ;
1503 }
1504 }
1505 // nest contour loops
1506 for (int d = loop_number; d >= 1; -- d) {
1507 PerimeterGeneratorLoops &contours_d = contours[d];
1508 // loop through all contours having depth == d
1509 for (int i = 0; i < (int)contours_d.size(); ++ i) {
1510 const PerimeterGeneratorLoop &loop = contours_d[i];
1511 // find the contour loop that contains it
1512 for (int t = d - 1; t >= 0; -- t) {
1513 for (size_t j = 0; j < contours[t].size(); ++ j) {
1514 PerimeterGeneratorLoop &candidate_parent = contours[t][j];
1515 if (candidate_parent.polygon.contains(loop.polygon.first_point())) {
1516 candidate_parent.children.push_back(loop);
1517 contours_d.erase(contours_d.begin() + i);
1518 -- i;
1519 goto NEXT_CONTOUR;
1520 }
1521 }
1522 }
1523 NEXT_CONTOUR: ;
1524 }
1525 }
1526 // at this point, all loops should be in contours[0]
1527 ExtrusionEntityCollection entities = traverse_loops_classic(params, lower_slices_polygons_cache, contours.front(), thin_walls);
1528 // if brim will be printed, reverse the order of perimeters so that
1529 // we continue inwards after having finished the brim
1530 // TODO: add test for perimeter order
1531 if (params.config.external_perimeters_first ||
1532 (params.layer_id == 0 && params.object_config.brim_width.value > 0))
1533 entities.reverse();
1534 // append perimeters for this slice as a collection
1535 if (! entities.empty())
1536 out_loops.append(entities);
1537 } // for each loop of an island
1538
1539 // fill gaps
1540 if (! gaps.empty()) {
1541 // collapse
1542 double min = 0.2 * perimeter_width * (1 - INSET_OVERLAP_TOLERANCE);
1543 double max = 2. * perimeter_spacing;
1544 ExPolygons gaps_ex = diff_ex(
1545 //FIXME offset2 would be enough and cheaper.
1546 opening_ex(gaps, float(min / 2.)),
1547 offset2_ex(gaps, - float(max / 2.), float(max / 2. + ClipperSafetyOffset)));
1548 ThickPolylines polylines;
1549 for (const ExPolygon &ex : gaps_ex)
1550 ex.medial_axis(min, max, &polylines);
1551 if (! polylines.empty()) {
1552 ExtrusionEntityCollection gap_fill;
1553 variable_width_classic(polylines, ExtrusionRole::GapFill, params.solid_infill_flow, gap_fill.entities);
1554 /* Make sure we don't infill narrow parts that are already gap-filled
1555 (we only consider this surface's gaps to reduce the diff() complexity).
1556 Growing actual extrusions ensures that gaps not filled by medial axis
1557 are not subtracted from fill surfaces (they might be too short gaps
1558 that medial axis skips but infill might join with other infill regions
1559 and use zigzag). */
1560 //FIXME Vojtech: This grows by a rounded extrusion width, not by line spacing,
1561 // therefore it may cover the area, but no the volume.
1562 last = diff_ex(last, gap_fill.polygons_covered_by_width(10.f));
1563 out_gap_fill.append(std::move(gap_fill.entities));
1564 }
1565 }
1566
1567 // create one more offset to be used as boundary for fill
1568 // we offset by half the perimeter spacing (to get to the actual infill boundary)
1569 // and then we offset back and forth by half the infill spacing to only consider the
1570 // non-collapsing regions
1571 coord_t inset =
1572 (loop_number < 0) ? 0 :
1573 (loop_number == 0) ?
1574 // one loop
1575 ext_perimeter_spacing / 2 :
1576 // two or more loops?
1577 perimeter_spacing / 2;
1578 // only apply infill overlap if we actually have one perimeter
1579 if (inset > 0)
1580 inset -= coord_t(scale_(params.config.get_abs_value("infill_overlap", unscale<double>(inset + solid_infill_spacing / 2))));
1581 // simplify infill contours according to resolution
1582 Polygons pp;
1583 for (ExPolygon &ex : last)
1584 ex.simplify_p(params.scaled_resolution, &pp);
1585 // collapse too narrow infill areas
1586 coord_t min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE));
1587 // append infill areas to fill_surfaces
1588 ExPolygons infill_areas =
1589 offset2_ex(
1590 union_ex(pp),
1591 float(- inset - min_perimeter_infill_spacing / 2.),
1592 float(min_perimeter_infill_spacing / 2.));
1593
1594 if (lower_slices != nullptr && params.config.overhangs && params.config.extra_perimeters_on_overhangs &&
1595 params.config.perimeters > 0 && params.layer_id > params.object_config.raft_layers) {
1596 // Generate extra perimeters on overhang areas, and cut them to these parts only, to save print time and material
1597 auto [extra_perimeters, filled_area] = generate_extra_perimeters_over_overhangs(infill_areas,
1598 lower_slices_polygons_cache,
1599 loop_number + 1,
1600 params.overhang_flow, params.scaled_resolution,
1601 params.object_config, params.print_config);
1602 if (!extra_perimeters.empty()) {
1603 ExtrusionEntityCollection &this_islands_perimeters = static_cast<ExtrusionEntityCollection&>(*out_loops.entities.back());
1604 ExtrusionEntitiesPtr old_entities;
1605 old_entities.swap(this_islands_perimeters.entities);
1606 for (ExtrusionPaths &paths : extra_perimeters)
1607 this_islands_perimeters.append(std::move(paths));
1608 append(this_islands_perimeters.entities, old_entities);
1609 infill_areas = diff_ex(infill_areas, filled_area);
1610 }
1611 }
1612
1613 append(out_fill_expolygons, std::move(infill_areas));
1614}
Definition ExPolygon.hpp:16
float nozzle_diameter() const
Definition Flow.hpp:69
EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half() min(const half &a, const half &b)
Definition Half.h:507
EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half() max(const half &a, const half &b)
Definition Half.h:516
std::vector< PerimeterGeneratorLoop > PerimeterGeneratorLoops
Definition PerimeterGenerator.cpp:288
static void variable_width_classic(const ThickPolylines &polylines, ExtrusionRole role, const Flow &flow, std::vector< ExtrusionEntity * > &out)
Definition PerimeterGenerator.cpp:153
Slic3r::ExPolygons opening_ex(const Slic3r::ExPolygons &polygons, const float delta, ClipperLib::JoinType joinType=DefaultJoinType, double miterLimit=DefaultMiterLimit)
Definition ClipperUtils.hpp:408
std::vector< ThickPolyline > ThickPolylines
Definition Polyline.hpp:15
thin_walls((ConfigOptionFloatOrPercent, top_infill_extrusion_width))((ConfigOptionInt
static ExtrusionEntityCollection traverse_loops_classic(const PerimeterGenerator::Parameters &params, const Polygons &lower_slices_polygons_cache, const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls)
Definition PerimeterGenerator.cpp:290
constexpr auto size(const C &c) -> decltype(c.size())
Definition span.hpp:183
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
double distance(const P &p1, const P &p2)
Definition geometry_traits.hpp:329
void offset(Slic3r::ExPolygon &sh, coord_t distance, const PolygonTag &)
Definition geometries.hpp:132
Slic3r::Polygon & hole(Slic3r::ExPolygon &sh, unsigned long idx)
Definition geometries.hpp:200
const THolesContainer< PolygonImpl > & holes(const Slic3r::ExPolygon &sh)
Definition geometries.hpp:189
bool spiral_vase
Definition PerimeterGenerator.hpp:58

References Slic3r::All, Slic3r::ExtrusionEntityCollection::append(), Slic3r::append(), Slic3r::PerimeterGeneratorLoop::children, Slic3r::ClipperSafetyOffset, Slic3r::PerimeterGenerator::Parameters::config, Slic3r::Polygon::contains(), Slic3r::diff_ex(), Slic3r::ExtrusionEntityCollection::empty(), Slic3r::ExtrusionEntityCollection::entities, Slic3r::Surface::expolygon, Slic3r::PerimeterGenerator::Parameters::ext_perimeter_flow, Slic3r::Surface::extra_perimeters, Slic3r::ExtrusionRole::GapFill, Slic3r::generate_extra_perimeters_over_overhangs(), Slic3r::holes(), INSET_OVERLAP_TOLERANCE, Slic3r::keep_largest_contour_only(), Slic3r::PerimeterGenerator::Parameters::layer_id, Slic3r::None, Slic3r::Flow::nozzle_diameter(), Slic3r::PerimeterGenerator::Parameters::object_config, Slic3r::offset(), Slic3r::offset2_ex(), Slic3r::offset_ex(), Slic3r::opening_ex(), Slic3r::PerimeterGenerator::Parameters::overhang_flow, Slic3r::PerimeterGenerator::Parameters::perimeter_flow, Slic3r::PerimeterGeneratorLoop::polygon, Slic3r::ExtrusionEntityCollection::polygons_covered_by_width(), Slic3r::PerimeterGenerator::Parameters::print_config, Slic3r::ExtrusionEntityCollection::reverse(), scale_, Slic3r::PerimeterGenerator::Parameters::scaled_resolution, Slic3r::Flow::scaled_spacing(), Slic3r::Flow::scaled_width(), Slic3r::ExPolygon::simplify_p(), Slic3r::PerimeterGenerator::Parameters::solid_infill_flow, Slic3r::Flow::spacing(), Slic3r::PerimeterGenerator::Parameters::spiral_vase, Slic3r::ExtrusionEntityCollection::swap(), Slic3r::thin_walls(), Slic3r::traverse_loops_classic(), Slic3r::union_ex(), and Slic3r::variable_width_classic().

Referenced by Slic3r::LayerRegion::make_perimeters().

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

◆ thick_polyline_to_multi_path()

ExtrusionMultiPath Slic3r::PerimeterGenerator::thick_polyline_to_multi_path ( const ThickPolyline thick_polyline,
ExtrusionRole  role,
const Flow flow,
float  tolerance,
float  merge_tolerance 
)
61{
62 ExtrusionMultiPath multi_path;
63 ExtrusionPath path(role);
64 ThickLines lines = thick_polyline.thicklines();
65
66 for (int i = 0; i < (int)lines.size(); ++i) {
67 const ThickLine& line = lines[i];
68 assert(line.a_width >= SCALED_EPSILON && line.b_width >= SCALED_EPSILON);
69
70 const coordf_t line_len = line.length();
71 if (line_len < SCALED_EPSILON) {
72 // The line is so tiny that we don't care about its width when we connect it to another line.
73 if (!path.empty())
74 path.polyline.points.back() = line.b; // If the variable path is non-empty, connect this tiny line to it.
75 else if (i + 1 < (int)lines.size()) // If there is at least one following line, connect this tiny line to it.
76 lines[i + 1].a = line.a;
77 else if (!multi_path.paths.empty())
78 multi_path.paths.back().polyline.points.back() = line.b; // Connect this tiny line to the last finished path.
79
80 // If any of the above isn't satisfied, then remove this tiny line.
81 continue;
82 }
83
84 double thickness_delta = fabs(line.a_width - line.b_width);
85 if (thickness_delta > tolerance) {
86 const auto segments = (unsigned int)ceil(thickness_delta / tolerance);
87 const coordf_t seg_len = line_len / segments;
88 Points pp;
89 std::vector<coordf_t> width;
90 {
91 pp.push_back(line.a);
92 width.push_back(line.a_width);
93 for (size_t j = 1; j < segments; ++j) {
94 pp.push_back((line.a.cast<double>() + (line.b - line.a).cast<double>().normalized() * (j * seg_len)).cast<coord_t>());
95
96 coordf_t w = line.a_width + (j*seg_len) * (line.b_width-line.a_width) / line_len;
97 width.push_back(w);
98 width.push_back(w);
99 }
100 pp.push_back(line.b);
101 width.push_back(line.b_width);
102
103 assert(pp.size() == segments + 1u);
104 assert(width.size() == segments*2);
105 }
106
107 // delete this line and insert new ones
108 lines.erase(lines.begin() + i);
109 for (size_t j = 0; j < segments; ++j) {
110 ThickLine new_line(pp[j], pp[j+1]);
111 new_line.a_width = width[2*j];
112 new_line.b_width = width[2*j+1];
113 lines.insert(lines.begin() + i + j, new_line);
114 }
115
116 -- i;
117 continue;
118 }
119
120 const double w = fmax(line.a_width, line.b_width);
121 const Flow new_flow = (role.is_bridge() && flow.bridge()) ? flow : flow.with_width(unscale<float>(w) + flow.height() * float(1. - 0.25 * PI));
122 if (path.polyline.points.empty()) {
123 path.polyline.append(line.a);
124 path.polyline.append(line.b);
125 // Convert from spacing to extrusion width based on the extrusion model
126 // of a square extrusion ended with semi circles.
127 #ifdef SLIC3R_DEBUG
128 printf(" filling %f gap\n", flow.width);
129 #endif
130 path.mm3_per_mm = new_flow.mm3_per_mm();
131 path.width = new_flow.width();
132 path.height = new_flow.height();
133 } else {
134 assert(path.width >= EPSILON);
135 thickness_delta = scaled<double>(fabs(path.width - new_flow.width()));
136 if (thickness_delta <= merge_tolerance) {
137 // the width difference between this line and the current flow
138 // (of the previous line) width is within the accepted tolerance
139 path.polyline.append(line.b);
140 } else {
141 // we need to initialize a new line
142 multi_path.paths.emplace_back(std::move(path));
143 path = ExtrusionPath(role);
144 -- i;
145 }
146 }
147 }
148 if (path.polyline.is_valid())
149 multi_path.paths.emplace_back(std::move(path));
150 return multi_path;
151}
EIGEN_DEVICE_FUNC const CeilReturnType ceil() const
Definition ArrayCwiseUnaryOps.h:402
Definition ExtrusionEntity.hpp:139
ExtrusionPaths paths
Definition ExtrusionEntity.hpp:141
Definition ExtrusionEntity.hpp:61
float width() const
Definition Flow.hpp:60
bool bridge() const
Definition Flow.hpp:71
double length() const
Definition Line.hpp:165
Point b
Definition Line.hpp:198
Point a
Definition Line.hpp:197
Definition Line.hpp:205
double b_width
Definition Line.hpp:211
double a_width
Definition Line.hpp:211
static constexpr double PI
Definition libslic3r.h:58
#define SCALED_EPSILON
Definition libslic3r.h:71
static constexpr double EPSILON
Definition libslic3r.h:51
coord_t width(const BoundingBox &box)
Definition Arrange.cpp:539
coord_t height(const BoundingBox &box)
Definition Arrange.cpp:540
double coordf_t
Definition GUI_ObjectList.hpp:36
std::vector< ThickLine > ThickLines
Definition Line.hpp:19
T unscale(Q v)
Definition libslic3r.h:95
std::vector< Point, PointsAllocator< Point > > Points
Definition Point.hpp:58
bool is_bridge() const
Definition ExtrusionRole.hpp:86
ThickLines thicklines() const
Definition Polyline.cpp:256

References Slic3r::Line::a, Slic3r::ThickLine::a_width, Slic3r::Polyline::append(), Slic3r::Line::b, Slic3r::ThickLine::b_width, Slic3r::Flow::bridge(), ceil(), Slic3r::ExtrusionPath::empty(), EPSILON, Slic3r::ExtrusionPath::height, Slic3r::Flow::height(), Slic3r::ExtrusionRole::is_bridge(), Slic3r::MultiPoint::is_valid(), Slic3r::Line::length(), Slic3r::ExtrusionPath::mm3_per_mm, Slic3r::Flow::mm3_per_mm(), Slic3r::ExtrusionMultiPath::paths, PI, Slic3r::MultiPoint::points, Slic3r::ExtrusionPath::polyline, SCALED_EPSILON, Slic3r::ThickPolyline::thicklines(), Slic3r::ExtrusionPath::width, Slic3r::Flow::width(), and Slic3r::Flow::with_width().

Referenced by Slic3r::extrusion_paths_append(), Slic3r::extrusion_paths_append(), and Slic3r::variable_width_classic().

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