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

Namespaces

namespace  LinearAlg2D
 

Classes

class  BeadingStrategy
 
class  BeadingStrategyFactory
 
class  DistributedBeadingStrategy
 
struct  ExtrusionJunction
 
struct  ExtrusionLine
 
class  HalfEdge
 
class  HalfEdgeGraph
 
class  HalfEdgeNode
 
class  LimitedBeadingStrategy
 
class  OuterWallInsetBeadingStrategy
 
class  PathsPointIndex
 
struct  PathsPointIndexLocator
 
class  PointMatrix
 
struct  PolygonsPointIndexSegmentLocator
 
class  PolygonsSegmentIndex
 
class  PolylineStitcher
 
class  RedistributeBeadingStrategy
 
class  SkeletalTrapezoidation
 
class  SkeletalTrapezoidationEdge
 
class  SkeletalTrapezoidationGraph
 
class  SkeletalTrapezoidationJoint
 
class  SparseGrid
 Sparse grid which can locate spatially nearby elements efficiently. More...
 
class  SparseLineGrid
 Sparse grid which can locate spatially nearby elements efficiently. More...
 
class  SparsePointGrid
 Sparse grid which can locate spatially nearby elements efficiently. More...
 
class  SquareGrid
 
class  STHalfEdge
 
class  STHalfEdgeNode
 
class  VoronoiUtils
 
class  WallToolPaths
 
class  WideningBeadingStrategy
 

Typedefs

using BeadingStrategyPtr = std::unique_ptr< BeadingStrategy >
 
using PointMap = SkeletalTrapezoidation::PointMap
 
using NodeSet = SkeletalTrapezoidation::NodeSet
 
using LineJunctions = std::vector< ExtrusionJunction >
 
using VariableWidthLines = std::vector< ExtrusionLine >
 
using PolygonsPointIndex = PathsPointIndex< Polygons >
 
using PolygonsPointIndexLocator = PathsPointIndexLocator< Polygons >
 
typedef SparseLineGrid< PolygonsPointIndex, PolygonsPointIndexSegmentLocatorLocToLineGrid
 

Enumerations

enum class  VoronoiDiagramStatus {
  NO_ISSUE_DETECTED , MISSING_VORONOI_VERTEX , NON_PLANAR_VORONOI_DIAGRAM , VORONOI_EDGE_INTERSECTING_INPUT_SEGMENT ,
  OTHER_TYPE_OF_VORONOI_DIAGRAM_DEGENERATION
}
 

Functions

template<typename T >
constexpr T pi_div (const T div)
 
static bool has_finite_edge_with_non_finite_vertex (const Geometry::VoronoiDiagram &voronoi_diagram)
 
static bool detect_missing_voronoi_vertex (const Geometry::VoronoiDiagram &voronoi_diagram, const std::vector< SkeletalTrapezoidation::Segment > &segments)
 
static bool has_missing_twin_edge (const SkeletalTrapezoidationGraph &graph)
 
static void rotate_back_skeletal_trapezoidation_graph_after_fix (SkeletalTrapezoidationGraph &graph, const double fix_angle, const PointMap &vertex_mapping)
 
bool detect_voronoi_edge_intersecting_input_segment (const Geometry::VoronoiDiagram &voronoi_diagram, const std::vector< VoronoiUtils::Segment > &segments)
 
VoronoiDiagramStatus detect_voronoi_diagram_known_issues (const Geometry::VoronoiDiagram &voronoi_diagram, const std::vector< SkeletalTrapezoidation::Segment > &segments)
 
static std::pair< PointMap, double > try_to_fix_degenerated_voronoi_diagram_by_rotation (Geometry::VoronoiDiagram &voronoi_diagram, const Polygons &polys, Polygons &polys_rotated, std::vector< SkeletalTrapezoidation::Segment > &segments, const std::vector< double > &fix_angles)
 
static Point normal (const Point &p0, coord_t len)
 
Point operator- (const ExtrusionJunction &a, const ExtrusionJunction &b)
 
const Pointmake_point (const ExtrusionJunction &ej)
 
static Slic3r::ThickPolyline to_thick_polyline (const Arachne::ExtrusionLine &line_junctions)
 
static Slic3r::ThickPolyline to_thick_polyline (const ClipperLib_Z::Path &path)
 
static Polygon to_polygon (const ExtrusionLine &line)
 
const Pointmake_point (const Point &p)
 
void simplify (Polygon &thiss, const int64_t smallest_line_segment_squared, const int64_t allowed_error_distance_squared)
 
void simplify (Polygons &thiss, const int64_t smallest_line_segment=scaled< coord_t >(0.01), const int64_t allowed_error_distance=scaled< coord_t >(0.005))
 
std::unique_ptr< LocToLineGridcreateLocToLineGrid (const Polygons &polygons, int square_size)
 
void fixSelfIntersections (const coord_t epsilon, Polygons &thiss)
 
void removeDegenerateVerts (Polygons &thiss)
 
void removeSmallAreas (Polygons &thiss, const double min_area_size, const bool remove_holes)
 
void removeColinearEdges (Polygon &poly, const double max_deviation_angle)
 
void removeColinearEdges (Polygons &thiss, const double max_deviation_angle=0.0005)
 
template<typename T >
bool shorterThan (const T &shape, const coord_t check_length)
 

Variables

constexpr bool fill_outline_gaps = true
 
constexpr coord_t meshfix_maximum_resolution = scaled<coord_t>(0.5)
 
constexpr coord_t meshfix_maximum_deviation = scaled<coord_t>(0.025)
 
constexpr coord_t meshfix_maximum_extrusion_area_deviation = scaled<coord_t>(2.)
 

Class Documentation

◆ Slic3r::Arachne::HalfEdgeGraph

class Slic3r::Arachne::HalfEdgeGraph
template<class node_data_t, class edge_data_t, class derived_node_t, class derived_edge_t>
class Slic3r::Arachne::HalfEdgeGraph< node_data_t, edge_data_t, derived_node_t, derived_edge_t >
+ Inheritance diagram for Slic3r::Arachne::HalfEdgeGraph< node_data_t, edge_data_t, derived_node_t, derived_edge_t >:
+ Collaboration diagram for Slic3r::Arachne::HalfEdgeGraph< node_data_t, edge_data_t, derived_node_t, derived_edge_t >:
Class Members
typedef derived_edge_t edge_t
typedef list< edge_t > Edges
typedef derived_node_t node_t
typedef list< node_t > Nodes
Class Members
Edges edges
Nodes nodes

Typedef Documentation

◆ BeadingStrategyPtr

using Slic3r::Arachne::BeadingStrategyPtr = typedef std::unique_ptr<BeadingStrategy>

◆ LineJunctions

using Slic3r::Arachne::LineJunctions = typedef std::vector<ExtrusionJunction>

◆ LocToLineGrid

◆ NodeSet

◆ PointMap

◆ PolygonsPointIndex

◆ PolygonsPointIndexLocator

◆ VariableWidthLines

using Slic3r::Arachne::VariableWidthLines = typedef std::vector<ExtrusionLine>

Enumeration Type Documentation

◆ VoronoiDiagramStatus

Enumerator
NO_ISSUE_DETECTED 
MISSING_VORONOI_VERTEX 
NON_PLANAR_VORONOI_DIAGRAM 
VORONOI_EDGE_INTERSECTING_INPUT_SEGMENT 
OTHER_TYPE_OF_VORONOI_DIAGRAM_DEGENERATION 

Function Documentation

◆ createLocToLineGrid()

std::unique_ptr< LocToLineGrid > Slic3r::Arachne::createLocToLineGrid ( const Polygons polygons,
int  square_size 
)
217{
218 unsigned int n_points = 0;
219 for (const auto &poly : polygons)
220 n_points += poly.size();
221
222 auto ret = std::make_unique<LocToLineGrid>(square_size, n_points);
223
224 for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
225 for (unsigned int point_idx = 0; point_idx < polygons[poly_idx].size(); point_idx++)
226 ret->insert(PolygonsPointIndex(&polygons, poly_idx, point_idx));
227 return ret;
228}
Definition PolygonsPointIndex.hpp:24

Referenced by fixSelfIntersections().

+ Here is the caller graph for this function:

◆ detect_missing_voronoi_vertex()

static bool Slic3r::Arachne::detect_missing_voronoi_vertex ( const Geometry::VoronoiDiagram voronoi_diagram,
const std::vector< SkeletalTrapezoidation::Segment > &  segments 
)
static
466 {
467 if (has_finite_edge_with_non_finite_vertex(voronoi_diagram))
468 return true;
469
470 for (VoronoiUtils::vd_t::cell_type cell : voronoi_diagram.cells()) {
471 if (!cell.incident_edge())
472 continue; // There is no spoon
473
474 if (cell.contains_segment()) {
475 const SkeletalTrapezoidation::Segment &source_segment = VoronoiUtils::getSourceSegment(cell, segments);
476 const Point from = source_segment.from();
477 const Point to = source_segment.to();
478
479 // Find starting edge
480 // Find end edge
481 bool seen_possible_start = false;
482 bool after_start = false;
483 bool ending_edge_is_set_before_start = false;
484 VoronoiUtils::vd_t::edge_type *starting_vd_edge = nullptr;
485 VoronoiUtils::vd_t::edge_type *ending_vd_edge = nullptr;
486 VoronoiUtils::vd_t::edge_type *edge = cell.incident_edge();
487 do {
488 if (edge->is_infinite() || edge->vertex0() == nullptr || edge->vertex1() == nullptr || !VoronoiUtils::is_finite(*edge->vertex0()) || !VoronoiUtils::is_finite(*edge->vertex1()))
489 continue;
490
491 Vec2i64 v0 = VoronoiUtils::p(edge->vertex0());
492 Vec2i64 v1 = VoronoiUtils::p(edge->vertex1());
493
494 assert(!(v0 == to.cast<int64_t>() && v1 == from.cast<int64_t>()));
495 if (v0 == to.cast<int64_t>() && !after_start) { // Use the last edge which starts in source_segment.to
496 starting_vd_edge = edge;
497 seen_possible_start = true;
498 } else if (seen_possible_start) {
499 after_start = true;
500 }
501
502 if (v1 == from.cast<int64_t>() && (!ending_vd_edge || ending_edge_is_set_before_start)) {
503 ending_edge_is_set_before_start = !after_start;
504 ending_vd_edge = edge;
505 }
506 } while (edge = edge->next(), edge != cell.incident_edge());
507
508 if (!starting_vd_edge || !ending_vd_edge || starting_vd_edge == ending_vd_edge)
509 return true;
510 }
511 }
512
513 return false;
514}
The matrix class, also used for vectors and row-vectors.
Definition Matrix.h:180
Definition PolygonsSegmentIndex.hpp:18
Point to() const
Definition PolygonsSegmentIndex.hpp:25
Point from() const
Definition PolygonsSegmentIndex.hpp:23
Definition Point.hpp:158
static bool has_finite_edge_with_non_finite_vertex(const Geometry::VoronoiDiagram &voronoi_diagram)
Definition SkeletalTrapezoidation.cpp:453
__int64 int64_t
Definition unistd.h:76

References Slic3r::Arachne::PolygonsSegmentIndex::from(), Slic3r::Arachne::VoronoiUtils::getSourceSegment(), has_finite_edge_with_non_finite_vertex(), Slic3r::Arachne::VoronoiUtils::is_finite(), Slic3r::Arachne::VoronoiUtils::p(), and Slic3r::Arachne::PolygonsSegmentIndex::to().

Referenced by Slic3r::Arachne::SkeletalTrapezoidation::constructFromPolygons(), and detect_voronoi_diagram_known_issues().

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

◆ detect_voronoi_diagram_known_issues()

VoronoiDiagramStatus Slic3r::Arachne::detect_voronoi_diagram_known_issues ( const Geometry::VoronoiDiagram voronoi_diagram,
const std::vector< SkeletalTrapezoidation::Segment > &  segments 
)
579{
580 if (const bool has_missing_voronoi_vertex = detect_missing_voronoi_vertex(voronoi_diagram, segments); has_missing_voronoi_vertex) {
581 return VoronoiDiagramStatus::MISSING_VORONOI_VERTEX;
582 } else if (const bool has_voronoi_edge_intersecting_input_segment = detect_voronoi_edge_intersecting_input_segment(voronoi_diagram, segments); has_voronoi_edge_intersecting_input_segment) {
583 // Detection if Voronoi edge is intersecting input segment detects at least one model in GH issue #8446.
584 return VoronoiDiagramStatus::VORONOI_EDGE_INTERSECTING_INPUT_SEGMENT;
585 } else if (const bool is_voronoi_diagram_planar = Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(voronoi_diagram, segments); !is_voronoi_diagram_planar) {
586 // Detection of non-planar Voronoi diagram detects at least GH issues #8474, #8514 and #8446.
587 return VoronoiDiagramStatus::NON_PLANAR_VORONOI_DIAGRAM;
588 }
589 return VoronoiDiagramStatus::NO_ISSUE_DETECTED;
590}
static bool detect_missing_voronoi_vertex(const Geometry::VoronoiDiagram &voronoi_diagram, const std::vector< SkeletalTrapezoidation::Segment > &segments)
Definition SkeletalTrapezoidation.cpp:466

References detect_missing_voronoi_vertex(), detect_voronoi_edge_intersecting_input_segment(), Slic3r::Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(), MISSING_VORONOI_VERTEX, NO_ISSUE_DETECTED, NON_PLANAR_VORONOI_DIAGRAM, and VORONOI_EDGE_INTERSECTING_INPUT_SEGMENT.

Referenced by Slic3r::Arachne::SkeletalTrapezoidation::constructFromPolygons(), and try_to_fix_degenerated_voronoi_diagram_by_rotation().

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

◆ detect_voronoi_edge_intersecting_input_segment()

bool Slic3r::Arachne::detect_voronoi_edge_intersecting_input_segment ( const Geometry::VoronoiDiagram voronoi_diagram,
const std::vector< VoronoiUtils::Segment > &  segments 
)
540{
541 for (VoronoiUtils::vd_t::cell_type cell : voronoi_diagram.cells()) {
542 if (!cell.incident_edge())
543 continue; // Degenerated cell, there is no spoon
544
545 if (!cell.contains_segment())
546 continue; // Skip cells that don't contain segments.
547
548 const VoronoiUtils::Segment &source_segment = VoronoiUtils::getSourceSegment(cell, segments);
549 const Vec2d source_segment_from = source_segment.from().cast<double>();
550 const Vec2d source_segment_vec = source_segment.to().cast<double>() - source_segment_from;
551
552 Point start_source_point, end_source_point;
553 VoronoiUtils::vd_t::edge_type *begin_voronoi_edge = nullptr, *end_voronoi_edge = nullptr;
554 SkeletalTrapezoidation::computeSegmentCellRange(cell, start_source_point, end_source_point, begin_voronoi_edge, end_voronoi_edge, segments);
555 // All Voronoi vertices must be on left side of the source segment, otherwise Voronoi diagram is invalid.
556 // FIXME Lukas H.: Be aware that begin_voronoi_edge and end_voronoi_edge could be nullptr in some specific cases.
557 // It mostly happens when there is some missing Voronoi, for example, in GH issue #8846 (IssuesWithMysteriousPerimeters.3mf).
558 if (begin_voronoi_edge != nullptr && end_voronoi_edge != nullptr)
559 for (VoronoiUtils::vd_t::edge_type *edge = begin_voronoi_edge; edge != end_voronoi_edge; edge = edge->next())
560 if (const Vec2d edge_v1(edge->vertex1()->x(), edge->vertex1()->y()); Slic3r::cross2(source_segment_vec, edge_v1 - source_segment_from) < 0)
561 return true;
562 }
563
564 return false;
565}
Derived::Scalar cross2(const Eigen::MatrixBase< Derived > &v1, const Eigen::MatrixBase< Derived2 > &v2)
Definition Point.hpp:93

Referenced by detect_voronoi_diagram_known_issues().

+ Here is the caller graph for this function:

◆ fixSelfIntersections()

void Slic3r::Arachne::fixSelfIntersections ( const coord_t  epsilon,
Polygons thiss 
)
233{
234 if (epsilon < 1) {
236 return;
237 }
238
239 const int64_t half_epsilon = (epsilon + 1) / 2;
240
241 // Points too close to line segments should be moved a little away from those line segments, but less than epsilon,
242 // so at least half-epsilon distance between points can still be guaranteed.
243 constexpr coord_t grid_size = scaled<coord_t>(2.);
244 auto query_grid = createLocToLineGrid(thiss, grid_size);
245
246 const auto move_dist = std::max<int64_t>(2L, half_epsilon - 2);
247 const int64_t half_epsilon_sqrd = half_epsilon * half_epsilon;
248
249 const size_t n = thiss.size();
250 for (size_t poly_idx = 0; poly_idx < n; poly_idx++) {
251 const size_t pathlen = thiss[poly_idx].size();
252 for (size_t point_idx = 0; point_idx < pathlen; ++point_idx) {
253 Point &pt = thiss[poly_idx][point_idx];
254 for (const auto &line : query_grid->getNearby(pt, epsilon)) {
255 const size_t line_next_idx = (line.point_idx + 1) % thiss[line.poly_idx].size();
256 if (poly_idx == line.poly_idx && (point_idx == line.point_idx || point_idx == line_next_idx))
257 continue;
258
259 const Line segment(thiss[line.poly_idx][line.point_idx], thiss[line.poly_idx][line_next_idx]);
260 Point segment_closest_point;
261 segment.distance_to_squared(pt, &segment_closest_point);
262
263 if (half_epsilon_sqrd >= (pt - segment_closest_point).cast<int64_t>().squaredNorm()) {
264 const Point &other = thiss[poly_idx][(point_idx + 1) % pathlen];
265 const Vec2i64 vec = (LinearAlg2D::pointIsLeftOfLine(other, segment.a, segment.b) > 0 ? segment.b - segment.a : segment.a - segment.b).cast<int64_t>();
266 assert(Slic3r::sqr(double(vec.x())) < double(std::numeric_limits<int64_t>::max()));
267 assert(Slic3r::sqr(double(vec.y())) < double(std::numeric_limits<int64_t>::max()));
268 const int64_t len = vec.norm();
269 pt.x() += (-vec.y() * move_dist) / len;
270 pt.y() += (vec.x() * move_dist) / len;
271 }
272 }
273 }
274 }
275
276 ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss), ClipperLib::pftEvenOdd);
277}
EIGEN_DEVICE_FUNC SegmentReturnType segment(Index start, Index n)
This is the const version of segment(Index,Index).
Definition BlockMethods.h:888
Definition ClipperUtils.hpp:137
Definition Line.hpp:155
int32_t coord_t
Definition libslic3r.h:39
@ pftEvenOdd
Definition clipper.hpp:81
Paths SimplifyPolygons(PathsProvider &&in_polys, PolyFillType fillType=pftNonZero, bool strictly_simple=true)
Definition clipper.hpp:592
constexpr T sqr(T x)
Definition libslic3r.h:258

References createLocToLineGrid(), ClipperLib::pftEvenOdd, Slic3r::Arachne::LinearAlg2D::pointIsLeftOfLine(), segment(), ClipperLib::SimplifyPolygons(), and Slic3r::sqr().

Referenced by Slic3r::Arachne::WallToolPaths::generate().

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

◆ has_finite_edge_with_non_finite_vertex()

static bool Slic3r::Arachne::has_finite_edge_with_non_finite_vertex ( const Geometry::VoronoiDiagram voronoi_diagram)
static
454{
455 for (const VoronoiUtils::vd_t::edge_type &edge : voronoi_diagram.edges()) {
456 if (edge.is_finite()) {
457 assert(edge.vertex0() != nullptr && edge.vertex1() != nullptr);
458 if (edge.vertex0() == nullptr || edge.vertex1() == nullptr || !VoronoiUtils::is_finite(*edge.vertex0()) ||
459 !VoronoiUtils::is_finite(*edge.vertex1()))
460 return true;
461 }
462 }
463 return false;
464}

References Slic3r::Arachne::VoronoiUtils::is_finite().

Referenced by detect_missing_voronoi_vertex().

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

◆ has_missing_twin_edge()

static bool Slic3r::Arachne::has_missing_twin_edge ( const SkeletalTrapezoidationGraph graph)
static
517{
518 for (const auto &edge : graph.edges)
519 if (edge.twin == nullptr)
520 return true;
521 return false;
522}
if(!(yy_init))
Definition lexer.c:1190

References Slic3r::Arachne::HalfEdgeGraph< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::edges.

Referenced by Slic3r::Arachne::SkeletalTrapezoidation::constructFromPolygons().

+ Here is the caller graph for this function:

◆ make_point() [1/2]

const Point & Slic3r::Arachne::make_point ( const ExtrusionJunction ej)
inline
52{
53 return ej.p;
54}
Point p
Definition ExtrusionJunction.hpp:25

References Slic3r::Arachne::ExtrusionJunction::p.

Referenced by Slic3r::Arachne::PathsPointIndexLocator< Paths >::operator()(), Slic3r::Arachne::PathsPointIndex< Paths >::p(), and Slic3r::Arachne::PolylineStitcher< Paths, Path, Junction >::stitch().

+ Here is the caller graph for this function:

◆ make_point() [2/2]

const Point & Slic3r::Arachne::make_point ( const Point p)
inline
17{ return p; }

◆ normal()

static Point Slic3r::Arachne::normal ( const Point p0,
coord_t  len 
)
inlinestatic
1620{
1621 int64_t _len = p0.cast<int64_t>().norm();
1622 if (_len < 1)
1623 return Point(len, 0);
1624 return (p0.cast<int64_t>() * int64_t(len) / _len).cast<coord_t>();
1625};
Kernel::Point_2 Point
Definition point_areas.cpp:20

Referenced by Slic3r::Arachne::SkeletalTrapezoidation::applyTransitions(), Slic3r::Arachne::SkeletalTrapezoidation::generateExtraRibs(), and Slic3r::Arachne::LinearAlg2D::isInsideCorner().

+ Here is the caller graph for this function:

◆ operator-()

Point Slic3r::Arachne::operator- ( const ExtrusionJunction a,
const ExtrusionJunction b 
)
inline
46{
47 return a.p - b.p;
48}

◆ pi_div()

template<typename T >
constexpr T Slic3r::Arachne::pi_div ( const div)
constexpr
14{ return static_cast<T>(M_PI) / div; }
#define M_PI
Definition ExtrusionSimulator.cpp:20

References M_PI.

◆ removeColinearEdges() [1/2]

void Slic3r::Arachne::removeColinearEdges ( Polygon poly,
const double  max_deviation_angle 
)
376{
377 // TODO: Can be made more efficient (for example, use pointer-types for process-/skip-indices, so we can swap them without copy).
378 size_t num_removed_in_iteration = 0;
379 do {
380 num_removed_in_iteration = 0;
381 std::vector<bool> process_indices(poly.points.size(), true);
382
383 bool go = true;
384 while (go) {
385 go = false;
386
387 const auto &rpath = poly;
388 const size_t pathlen = rpath.size();
389 if (pathlen <= 3)
390 return;
391
392 std::vector<bool> skip_indices(poly.points.size(), false);
393
394 Polygon new_path;
395 for (size_t point_idx = 0; point_idx < pathlen; ++point_idx) {
396 // Don't iterate directly over process-indices, but do it this way, because there are points _in_ process-indices that should nonetheless
397 // be skipped:
398 if (!process_indices[point_idx]) {
399 new_path.points.push_back(rpath[point_idx]);
400 continue;
401 }
402
403 // Should skip the last point for this iteration if the old first was removed (which can be seen from the fact that the new first was skipped):
404 if (point_idx == (pathlen - 1) && skip_indices[0]) {
405 skip_indices[new_path.size()] = true;
406 go = true;
407 new_path.points.push_back(rpath[point_idx]);
408 break;
409 }
410
411 const Point &prev = rpath[(point_idx - 1 + pathlen) % pathlen];
412 const Point &pt = rpath[point_idx];
413 const Point &next = rpath[(point_idx + 1) % pathlen];
414
415 float angle = LinearAlg2D::getAngleLeft(prev, pt, next); // [0 : 2 * pi]
416 if (angle >= float(M_PI)) { angle -= float(M_PI); } // map [pi : 2 * pi] to [0 : pi]
417
418 // Check if the angle is within limits for the point to 'make sense', given the maximum deviation.
419 // If the angle indicates near-parallel segments ignore the point 'pt'
420 if (angle > max_deviation_angle && angle < M_PI - max_deviation_angle) {
421 new_path.points.push_back(pt);
422 } else if (point_idx != (pathlen - 1)) {
423 // Skip the next point, since the current one was removed:
424 skip_indices[new_path.size()] = true;
425 go = true;
426 new_path.points.push_back(next);
427 ++point_idx;
428 }
429 }
430 poly = new_path;
431 num_removed_in_iteration += pathlen - poly.points.size();
432
433 process_indices.clear();
434 process_indices.insert(process_indices.end(), skip_indices.begin(), skip_indices.end());
435 }
436 } while (num_removed_in_iteration > 0);
437}
Points points
Definition MultiPoint.hpp:18
size_t size() const
Definition MultiPoint.hpp:39
Definition Polygon.hpp:24

References Slic3r::angle(), Slic3r::Arachne::LinearAlg2D::getAngleLeft(), M_PI, Slic3r::MultiPoint::points, and Slic3r::MultiPoint::size().

Referenced by Slic3r::Arachne::WallToolPaths::generate(), and removeColinearEdges().

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

◆ removeColinearEdges() [2/2]

void Slic3r::Arachne::removeColinearEdges ( Polygons thiss,
const double  max_deviation_angle = 0.0005 
)
440{
441 for (int p = 0; p < int(thiss.size()); p++) {
442 removeColinearEdges(thiss[p], max_deviation_angle);
443 if (thiss[p].size() < 3) {
444 thiss.erase(thiss.begin() + p);
445 p--;
446 }
447 }
448}
void removeColinearEdges(Polygon &poly, const double max_deviation_angle)
Definition WallToolPaths.cpp:375

References removeColinearEdges().

+ Here is the call graph for this function:

◆ removeDegenerateVerts()

void Slic3r::Arachne::removeDegenerateVerts ( Polygons thiss)

Removes overlapping consecutive line segments which don't delimit a positive area.

283{
284 for (size_t poly_idx = 0; poly_idx < thiss.size(); poly_idx++) {
285 Polygon &poly = thiss[poly_idx];
286 Polygon result;
287
288 auto isDegenerate = [](const Point &last, const Point &now, const Point &next) {
289 Vec2i64 last_line = (now - last).cast<int64_t>();
290 Vec2i64 next_line = (next - now).cast<int64_t>();
291 return last_line.dot(next_line) == -1 * last_line.norm() * next_line.norm();
292 };
293 bool isChanged = false;
294 for (size_t idx = 0; idx < poly.size(); idx++) {
295 const Point &last = (result.size() == 0) ? poly.back() : result.back();
296 if (idx + 1 == poly.size() && result.size() == 0)
297 break;
298
299 const Point &next = (idx + 1 == poly.size()) ? result[0] : poly[idx + 1];
300 if (isDegenerate(last, poly[idx], next)) { // lines are in the opposite direction
301 // don't add vert to the result
302 isChanged = true;
303 while (result.size() > 1 && isDegenerate(result[result.size() - 2], result.back(), next))
304 result.points.pop_back();
305 } else {
306 result.points.emplace_back(poly[idx]);
307 }
308 }
309
310 if (isChanged) {
311 if (result.size() > 2) {
312 poly = result;
313 } else {
314 thiss.erase(thiss.begin() + poly_idx);
315 poly_idx--; // effectively the next iteration has the same poly_idx (referring to a new poly which is not yet processed)
316 }
317 }
318 }
319}
const Point & back() const
Definition MultiPoint.hpp:37

References Slic3r::MultiPoint::back(), Slic3r::MultiPoint::points, and Slic3r::MultiPoint::size().

Referenced by Slic3r::Arachne::WallToolPaths::generate().

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

◆ removeSmallAreas()

void Slic3r::Arachne::removeSmallAreas ( Polygons thiss,
const double  min_area_size,
const bool  remove_holes 
)
322{
323 auto to_path = [](const Polygon &poly) -> ClipperLib::Path {
325 for (const Point &pt : poly.points)
326 out.emplace_back(ClipperLib::cInt(pt.x()), ClipperLib::cInt(pt.y()));
327 return out;
328 };
329
330 auto new_end = thiss.end();
331 if (remove_holes) {
332 for (auto it = thiss.begin(); it < new_end;) {
333 // All polygons smaller than target are removed by replacing them with a polygon from the back of the vector.
334 if (fabs(ClipperLib::Area(to_path(*it))) < min_area_size) {
335 --new_end;
336 *it = std::move(*new_end);
337 continue; // Don't increment the iterator such that the polygon just swapped in is checked next.
338 }
339 ++it;
340 }
341 } else {
342 // For each polygon, computes the signed area, move small outlines at the end of the vector and keep pointer on small holes
343 Polygons small_holes;
344 for (auto it = thiss.begin(); it < new_end;) {
345 if (double area = ClipperLib::Area(to_path(*it)); fabs(area) < min_area_size) {
346 if (area >= 0) {
347 --new_end;
348 if (it < new_end) {
349 std::swap(*new_end, *it);
350 continue;
351 } else { // Don't self-swap the last Path
352 break;
353 }
354 } else {
355 small_holes.push_back(*it);
356 }
357 }
358 ++it;
359 }
360
361 // Removes small holes that have their first point inside one of the removed outlines
362 // Iterating in reverse ensures that unprocessed small holes won't be moved
363 const auto removed_outlines_start = new_end;
364 for (auto hole_it = small_holes.rbegin(); hole_it < small_holes.rend(); hole_it++)
365 for (auto outline_it = removed_outlines_start; outline_it < thiss.end(); outline_it++)
366 if (Polygon(*outline_it).contains(*hole_it->begin())) {
367 new_end--;
368 *hole_it = std::move(*new_end);
369 break;
370 }
371 }
372 thiss.resize(new_end-thiss.begin());
373}
bool contains(const Point &point) const
Definition Polygon.hpp:66
Definition clipper.cpp:60
std::vector< IntPoint, Allocator< IntPoint > > Path
Definition clipper.hpp:121
double Area(const Path &poly)
Definition clipper.cpp:151
std::vector< Polygon, PointsAllocator< Polygon > > Polygons
Definition Polygon.hpp:15
Slic3r::Polygon Polygon
Definition Emboss.cpp:34

References Slic3r::area(), ClipperLib::Area(), and Slic3r::Polygon::contains().

Referenced by Slic3r::Arachne::WallToolPaths::generate().

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

◆ rotate_back_skeletal_trapezoidation_graph_after_fix()

static void Slic3r::Arachne::rotate_back_skeletal_trapezoidation_graph_after_fix ( SkeletalTrapezoidationGraph graph,
const double  fix_angle,
const PointMap vertex_mapping 
)
inlinestatic
529{
530 for (STHalfEdgeNode &node : graph.nodes) {
531 // If a mapping exists between a rotated point and an original point, use this mapping. Otherwise, rotate a point in the opposite direction.
532 if (auto node_it = vertex_mapping.find(node.p); node_it != vertex_mapping.end())
533 node.p = node_it->second;
534 else
535 node.p.rotate(-fix_angle);
536 }
537}
Definition SkeletalTrapezoidationGraph.hpp:55

References Slic3r::Arachne::HalfEdgeGraph< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::nodes.

Referenced by Slic3r::Arachne::SkeletalTrapezoidation::constructFromPolygons().

+ Here is the caller graph for this function:

◆ shorterThan()

template<typename T >
bool Slic3r::Arachne::shorterThan ( const T &  shape,
const coord_t  check_length 
)
638{
639 const auto *p0 = &shape.back();
640 int64_t length = 0;
641 for (const auto &p1 : shape) {
642 length += (*p0 - p1).template cast<int64_t>().norm();
643 if (length >= check_length)
644 return false;
645 p0 = &p1;
646 }
647 return true;
648}

References Slic3r::length().

Referenced by Slic3r::Arachne::WallToolPaths::removeSmallLines().

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

◆ simplify() [1/2]

void Slic3r::Arachne::simplify ( Polygon thiss,
const int64_t  smallest_line_segment_squared,
const int64_t  allowed_error_distance_squared 
)
60{
61 if (thiss.size() < 3) {
62 thiss.points.clear();
63 return;
64 }
65 if (thiss.size() == 3)
66 return;
67
68 Polygon new_path;
69 Point previous = thiss.points.back();
70 Point previous_previous = thiss.points.at(thiss.points.size() - 2);
71 Point current = thiss.points.at(0);
72
73 /* When removing a vertex, we check the height of the triangle of the area
74 being removed from the original polygon by the simplification. However,
75 when consecutively removing multiple vertices the height of the previously
76 removed vertices w.r.t. the shortcut path changes.
77 In order to not recompute the new height value of previously removed
78 vertices we compute the height of a representative triangle, which covers
79 the same amount of area as the area being cut off. We use the Shoelace
80 formula to accumulate the area under the removed segments. This works by
81 computing the area in a 'fan' where each of the blades of the fan go from
82 the origin to one of the segments. While removing vertices the area in
83 this fan accumulates. By subtracting the area of the blade connected to
84 the short-cutting segment we obtain the total area of the cutoff region.
85 From this area we compute the height of the representative triangle using
86 the standard formula for a triangle area: A = .5*b*h
87 */
88 int64_t accumulated_area_removed = int64_t(previous.x()) * int64_t(current.y()) - int64_t(previous.y()) * int64_t(current.x()); // Twice the Shoelace formula for area of polygon per line segment.
89
90 for (size_t point_idx = 0; point_idx < thiss.points.size(); point_idx++) {
91 current = thiss.points.at(point_idx % thiss.points.size());
92
93 //Check if the accumulated area doesn't exceed the maximum.
94 Point next;
95 if (point_idx + 1 < thiss.points.size()) {
96 next = thiss.points.at(point_idx + 1);
97 } else if (point_idx + 1 == thiss.points.size() && new_path.size() > 1) { // don't spill over if the [next] vertex will then be equal to [previous]
98 next = new_path[0]; //Spill over to new polygon for checking removed area.
99 } else {
100 next = thiss.points.at((point_idx + 1) % thiss.points.size());
101 }
102 const int64_t removed_area_next = int64_t(current.x()) * int64_t(next.y()) - int64_t(current.y()) * int64_t(next.x()); // Twice the Shoelace formula for area of polygon per line segment.
103 const int64_t negative_area_closing = int64_t(next.x()) * int64_t(previous.y()) - int64_t(next.y()) * int64_t(previous.x()); // area between the origin and the short-cutting segment
104 accumulated_area_removed += removed_area_next;
105
106 const int64_t length2 = (current - previous).cast<int64_t>().squaredNorm();
107 if (length2 < scaled<int64_t>(25.)) {
108 // We're allowed to always delete segments of less than 5 micron.
109 continue;
110 }
111
112 const int64_t area_removed_so_far = accumulated_area_removed + negative_area_closing; // close the shortcut area polygon
113 const int64_t base_length_2 = (next - previous).cast<int64_t>().squaredNorm();
114
115 if (base_length_2 == 0) //Two line segments form a line back and forth with no area.
116 continue; //Remove the vertex.
117 //We want to check if the height of the triangle formed by previous, current and next vertices is less than allowed_error_distance_squared.
118 //1/2 L = A [actual area is half of the computed shoelace value] // Shoelace formula is .5*(...) , but we simplify the computation and take out the .5
119 //A = 1/2 * b * h [triangle area formula]
120 //L = b * h [apply above two and take out the 1/2]
121 //h = L / b [divide by b]
122 //h^2 = (L / b)^2 [square it]
123 //h^2 = L^2 / b^2 [factor the divisor]
124 const int64_t height_2 = double(area_removed_so_far) * double(area_removed_so_far) / double(base_length_2);
125 if ((height_2 <= Slic3r::sqr(scaled<coord_t>(0.005)) //Almost exactly colinear (barring rounding errors).
126 && Line::distance_to_infinite(current, previous, next) <= scaled<double>(0.005))) // make sure that height_2 is not small because of cancellation of positive and negative areas
127 continue;
128
129 if (length2 < smallest_line_segment_squared
130 && height_2 <= allowed_error_distance_squared) // removing the vertex doesn't introduce too much error.)
131 {
132 const int64_t next_length2 = (current - next).cast<int64_t>().squaredNorm();
133 if (next_length2 > 4 * smallest_line_segment_squared) {
134 // Special case; The next line is long. If we were to remove this, it could happen that we get quite noticeable artifacts.
135 // We should instead move this point to a location where both edges are kept and then remove the previous point that we wanted to keep.
136 // By taking the intersection of these two lines, we get a point that preserves the direction (so it makes the corner a bit more pointy).
137 // We just need to be sure that the intersection point does not introduce an artifact itself.
138 Point intersection_point;
139 bool has_intersection = Line(previous_previous, previous).intersection_infinite(Line(current, next), &intersection_point);
140 if (!has_intersection
141 || Line::distance_to_infinite_squared(intersection_point, previous, current) > double(allowed_error_distance_squared)
142 || (intersection_point - previous).cast<int64_t>().squaredNorm() > smallest_line_segment_squared // The intersection point is way too far from the 'previous'
143 || (intersection_point - next).cast<int64_t>().squaredNorm() > smallest_line_segment_squared) // and 'next' points, so it shouldn't replace 'current'
144 {
145 // We can't find a better spot for it, but the size of the line is more than 5 micron.
146 // So the only thing we can do here is leave it in...
147 }
148 else {
149 // New point seems like a valid one.
150 current = intersection_point;
151 // If there was a previous point added, remove it.
152 if(!new_path.empty()) {
153 new_path.points.pop_back();
154 previous = previous_previous;
155 }
156 }
157 } else {
158 continue; //Remove the vertex.
159 }
160 }
161 //Don't remove the vertex.
162 accumulated_area_removed = removed_area_next; // so that in the next iteration it's the area between the origin, [previous] and [current]
163 previous_previous = previous;
164 previous = current; //Note that "previous" is only updated if we don't remove the vertex.
165 new_path.points.push_back(current);
166 }
167
168 thiss = new_path;
169}
bool empty() const
Definition MultiPoint.hpp:40

References Slic3r::Line::distance_to_infinite(), Slic3r::Line::distance_to_infinite_squared(), Slic3r::MultiPoint::empty(), Slic3r::Line::intersection_infinite(), Slic3r::MultiPoint::points, Slic3r::MultiPoint::size(), and Slic3r::sqr().

Referenced by Slic3r::Arachne::WallToolPaths::generate(), and simplify().

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

◆ simplify() [2/2]

void Slic3r::Arachne::simplify ( Polygons thiss,
const int64_t  smallest_line_segment = scaled<coord_t>(0.01),
const int64_t  allowed_error_distance = scaled<coord_t>(0.005) 
)

Removes vertices of the polygons to make sure that they are not too high resolution.

This removes points which are connected to line segments that are shorter than the smallest_line_segment, unless that would introduce a deviation in the contour of more than allowed_error_distance.

Criteria:

  1. Never remove a vertex if either of the connceted segments is larger than smallest_line_segment
  2. Never remove a vertex if the distance between that vertex and the final resulting polygon would be higher than allowed_error_distance
  3. The direction of segments longer than smallest_line_segment always remains unaltered (but their end points may change if it is connected to a small segment)

Simplify uses a heuristic and doesn't neccesarily remove all removable vertices under the above criteria, but simplify may never violate these criteria. Unless the segments or the distance is smaller than the rounding error of 5 micron.

Vertices which introduce an error of less than 5 microns are removed anyway, even if the segments are longer than the smallest line segment. This makes sure that (practically) colinear line segments are joined into a single line segment.

Parameters
smallest_line_segmentMaximal length of removed line segments.
allowed_error_distanceIf removing a vertex introduces a deviation from the original path that is more than this distance, the vertex may not be removed.
201{
202 const int64_t allowed_error_distance_squared = int64_t(allowed_error_distance) * int64_t(allowed_error_distance);
203 const int64_t smallest_line_segment_squared = int64_t(smallest_line_segment) * int64_t(smallest_line_segment);
204 for (size_t p = 0; p < thiss.size(); p++)
205 {
206 simplify(thiss[p], smallest_line_segment_squared, allowed_error_distance_squared);
207 if (thiss[p].size() < 3)
208 {
209 thiss.erase(thiss.begin() + p);
210 p--;
211 }
212 }
213}
void simplify(Polygon &thiss, const int64_t smallest_line_segment_squared, const int64_t allowed_error_distance_squared)
Definition WallToolPaths.cpp:59

References simplify().

+ Here is the call graph for this function:

◆ to_polygon()

static Polygon Slic3r::Arachne::to_polygon ( const ExtrusionLine line)
inlinestatic
238{
239 Polygon out;
240 assert(line.junctions.size() >= 3);
241 assert(line.junctions.front().p == line.junctions.back().p);
242 out.points.reserve(line.junctions.size() - 1);
243 for (auto it = line.junctions.begin(); it != line.junctions.end() - 1; ++it)
244 out.points.emplace_back(it->p);
245 return out;
246}
std::vector< ExtrusionJunction > junctions
Definition ExtrusionLine.hpp:69

References Slic3r::Arachne::ExtrusionLine::junctions, and Slic3r::MultiPoint::points.

◆ to_thick_polyline() [1/2]

static Slic3r::ThickPolyline Slic3r::Arachne::to_thick_polyline ( const Arachne::ExtrusionLine line_junctions)
inlinestatic
198{
199 assert(line_junctions.size() >= 2);
201 out.points.emplace_back(line_junctions.front().p);
202 out.width.emplace_back(line_junctions.front().w);
203 out.points.emplace_back(line_junctions[1].p);
204 out.width.emplace_back(line_junctions[1].w);
205
206 auto it_prev = line_junctions.begin() + 1;
207 for (auto it = line_junctions.begin() + 2; it != line_junctions.end(); ++it) {
208 out.points.emplace_back(it->p);
209 out.width.emplace_back(it_prev->w);
210 out.width.emplace_back(it->w);
211 it_prev = it;
212 }
213
214 return out;
215}
std::vector< ExtrusionJunction >::const_reference front() const
Definition ExtrusionLine.hpp:97
size_t size() const
Definition ExtrusionLine.hpp:57
std::vector< ExtrusionJunction >::const_iterator end() const
Definition ExtrusionLine.hpp:94
std::vector< ExtrusionJunction >::const_iterator begin() const
Definition ExtrusionLine.hpp:93
Definition Polyline.hpp:182
std::vector< coordf_t > width
Definition Polyline.hpp:212
Points points
Definition Polyline.hpp:208

References Slic3r::Arachne::ExtrusionLine::begin(), Slic3r::Arachne::ExtrusionLine::end(), Slic3r::Arachne::ExtrusionLine::front(), Slic3r::ThickPolyline::points, Slic3r::Arachne::ExtrusionLine::size(), and Slic3r::ThickPolyline::width.

Referenced by Slic3r::FillConcentric::_fill_surface_single(), Slic3r::extrusion_paths_append(), and Slic3r::extrusion_paths_append().

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

◆ to_thick_polyline() [2/2]

static Slic3r::ThickPolyline Slic3r::Arachne::to_thick_polyline ( const ClipperLib_Z::Path &  path)
inlinestatic
218{
219 assert(path.size() >= 2);
221 out.points.emplace_back(path.front().x(), path.front().y());
222 out.width.emplace_back(path.front().z());
223 out.points.emplace_back(path[1].x(), path[1].y());
224 out.width.emplace_back(path[1].z());
225
226 auto it_prev = path.begin() + 1;
227 for (auto it = path.begin() + 2; it != path.end(); ++it) {
228 out.points.emplace_back(it->x(), it->y());
229 out.width.emplace_back(it_prev->z());
230 out.width.emplace_back(it->z());
231 it_prev = it;
232 }
233
234 return out;
235}

References Slic3r::ThickPolyline::points, and Slic3r::ThickPolyline::width.

◆ try_to_fix_degenerated_voronoi_diagram_by_rotation()

static std::pair< PointMap, double > Slic3r::Arachne::try_to_fix_degenerated_voronoi_diagram_by_rotation ( Geometry::VoronoiDiagram voronoi_diagram,
const Polygons polys,
Polygons polys_rotated,
std::vector< SkeletalTrapezoidation::Segment > &  segments,
const std::vector< double > &  fix_angles 
)
inlinestatic
598{
599 const Polygons polys_rotated_original = polys_rotated;
600 double fixed_by_angle = fix_angles.front();
601 PointMap vertex_mapping;
602
603 for (const double &fix_angle : fix_angles) {
604 vertex_mapping.clear();
605 polys_rotated = polys_rotated_original;
606 fixed_by_angle = fix_angle;
607
608 for (Polygon &poly : polys_rotated)
609 poly.rotate(fix_angle);
610
611 assert(polys_rotated.size() == polys.size());
612 for (size_t poly_idx = 0; poly_idx < polys.size(); ++poly_idx) {
613 assert(polys_rotated[poly_idx].size() == polys[poly_idx].size());
614 for (size_t point_idx = 0; point_idx < polys[poly_idx].size(); ++point_idx)
615 vertex_mapping.insert({polys_rotated[poly_idx][point_idx], polys[poly_idx][point_idx]});
616 }
617
618 segments.clear();
619 for (size_t poly_idx = 0; poly_idx < polys_rotated.size(); poly_idx++)
620 for (size_t point_idx = 0; point_idx < polys_rotated[poly_idx].size(); point_idx++)
621 segments.emplace_back(&polys_rotated, poly_idx, point_idx);
622
623 voronoi_diagram.clear();
624 construct_voronoi(segments.begin(), segments.end(), &voronoi_diagram);
625
626#ifdef ARACHNE_DEBUG_VORONOI
627 {
628 static int iRun = 0;
629 dump_voronoi_to_svg(debug_out_path("arachne_voronoi-diagram-rotated-%d.svg", iRun++).c_str(), voronoi_diagram, to_points(polys), to_lines(polys));
630 }
631#endif
632
633 if (detect_voronoi_diagram_known_issues(voronoi_diagram, segments) == VoronoiDiagramStatus::NO_ISSUE_DETECTED)
634 break;
635 }
636
637 assert(Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(voronoi_diagram));
638
639 return {vertex_mapping, fixed_by_angle};
640}
SkeletalTrapezoidation::PointMap PointMap
Definition SkeletalTrapezoidation.cpp:524
std::string debug_out_path(const char *name,...)
Definition utils.cpp:218
Lines to_lines(const ExPolygon &src)
Definition ExPolygon.hpp:117
Points to_points(const ExPolygons &src)
Definition ExPolygon.hpp:196

References Slic3r::debug_out_path(), detect_voronoi_diagram_known_issues(), Slic3r::dump_voronoi_to_svg(), Slic3r::Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(), NO_ISSUE_DETECTED, Slic3r::to_lines(), and Slic3r::to_points().

Referenced by Slic3r::Arachne::SkeletalTrapezoidation::constructFromPolygons().

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

Variable Documentation

◆ fill_outline_gaps

constexpr bool Slic3r::Arachne::fill_outline_gaps = true
constexpr

◆ meshfix_maximum_deviation

constexpr coord_t Slic3r::Arachne::meshfix_maximum_deviation = scaled<coord_t>(0.025)
constexpr

◆ meshfix_maximum_extrusion_area_deviation

constexpr coord_t Slic3r::Arachne::meshfix_maximum_extrusion_area_deviation = scaled<coord_t>(2.)
constexpr

◆ meshfix_maximum_resolution

constexpr coord_t Slic3r::Arachne::meshfix_maximum_resolution = scaled<coord_t>(0.5)
constexpr