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

#include <src/libslic3r/Arachne/SkeletalTrapezoidation.hpp>

+ Collaboration diagram for Slic3r::Arachne::SkeletalTrapezoidation:

Classes

struct  TransitionMidRef
 

Public Types

using Segment = PolygonsSegmentIndex
 
using PointMap = ankerl::unordered_dense::map< Point, Point, PointHash >
 
using NodeSet = ankerl::unordered_dense::set< node_t * >
 

Public Member Functions

 SkeletalTrapezoidation (const Polygons &polys, const BeadingStrategy &beading_strategy, double transitioning_angle, coord_t discretization_step_size, coord_t transition_filter_dist, coord_t allowed_filter_deviation, coord_t beading_propagation_transition_dist)
 
void generateToolpaths (std::vector< VariableWidthLines > &generated_toolpaths, bool filter_outermost_central_edges=false)
 

Public Attributes

graph_t graph
 

Protected Member Functions

void constructFromPolygons (const Polygons &polys)
 
node_tmakeNode (vd_t::vertex_type &vd_node, Point p)
 Get the node which the VD node maps to, or create a new mapping if there wasn't any yet.
 
void transferEdge (Point from, Point to, vd_t::edge_type &vd_edge, edge_t *&prev_edge, Point &start_source_point, Point &end_source_point, const std::vector< Segment > &segments)
 
Points discretize (const vd_t::edge_type &segment, const std::vector< Segment > &segments)
 
void separatePointyQuadEndNodes ()
 
void updateIsCentral ()
 
void filterCentral (coord_t max_length)
 
bool filterCentral (edge_t *starting_edge, coord_t traveled_dist, coord_t max_length)
 
void filterOuterCentral ()
 
void updateBeadCount ()
 
void filterNoncentralRegions ()
 
bool filterNoncentralRegions (edge_t *to_edge, coord_t bead_count, coord_t traveled_dist, coord_t max_dist)
 
void generateTransitionMids (ptr_vector_t< std::list< TransitionMiddle > > &edge_transitions)
 
void filterTransitionMids ()
 
std::list< TransitionMidRefdissolveNearbyTransitions (edge_t *edge_to_start, TransitionMiddle &origin_transition, coord_t traveled_dist, coord_t max_dist, bool going_up)
 
void dissolveBeadCountRegion (edge_t *edge_to_start, coord_t from_bead_count, coord_t to_bead_count)
 
bool filterEndOfCentralTransition (edge_t *edge_to_start, coord_t traveled_dist, coord_t max_dist, coord_t replacing_bead_count)
 
void generateAllTransitionEnds (ptr_vector_t< std::list< TransitionEnd > > &edge_transition_ends)
 
void applyTransitions (ptr_vector_t< std::list< TransitionEnd > > &edge_transition_ends)
 
void generateTransitioningRibs ()
 
void generateTransitionEnds (edge_t &edge, coord_t mid_R, coord_t transition_lower_bead_count, ptr_vector_t< std::list< TransitionEnd > > &edge_transition_ends)
 
bool generateTransitionEnd (edge_t &edge, coord_t start_pos, coord_t end_pos, coord_t transition_half_length, double start_rest, double end_rest, coord_t transition_lower_bead_count, ptr_vector_t< std::list< TransitionEnd > > &edge_transition_ends)
 
bool isGoingDown (edge_t *outgoing, coord_t traveled_dist, coord_t transition_half_length, coord_t lower_bead_count) const
 
bool isEndOfCentral (const edge_t &edge) const
 
void generateExtraRibs ()
 
void generateSegments ()
 
edge_tgetQuadMaxRedgeTo (edge_t *quad_start_edge)
 
void propagateBeadingsUpward (std::vector< edge_t * > &upward_quad_mids, ptr_vector_t< BeadingPropagation > &node_beadings)
 
void propagateBeadingsDownward (std::vector< edge_t * > &upward_quad_mids, ptr_vector_t< BeadingPropagation > &node_beadings)
 
void propagateBeadingsDownward (edge_t *edge_to_peak, ptr_vector_t< BeadingPropagation > &node_beadings)
 
Beading interpolate (const Beading &left, double ratio_left_to_whole, const Beading &right, coord_t switching_radius) const
 
Beading interpolate (const Beading &left, double ratio_left_to_whole, const Beading &right) const
 
std::shared_ptr< BeadingPropagationgetOrCreateBeading (node_t *node, ptr_vector_t< BeadingPropagation > &node_beadings)
 
std::shared_ptr< BeadingPropagationgetNearestBeading (node_t *node, coord_t max_dist)
 
void generateJunctions (ptr_vector_t< BeadingPropagation > &node_beadings, ptr_vector_t< LineJunctions > &edge_junctions)
 
void addToolpathSegment (const ExtrusionJunction &from, const ExtrusionJunction &to, bool is_odd, bool force_new_path, bool from_is_3way, bool to_is_3way)
 
void connectJunctions (ptr_vector_t< LineJunctions > &edge_junctions)
 
void generateLocalMaximaSingleBeads ()
 

Static Protected Member Functions

static bool computePointCellRange (vd_t::cell_type &cell, Point &start_source_point, Point &end_source_point, vd_t::edge_type *&starting_vd_edge, vd_t::edge_type *&ending_vd_edge, const std::vector< Segment > &segments)
 
static void computeSegmentCellRange (vd_t::cell_type &cell, Point &start_source_point, Point &end_source_point, vd_t::edge_type *&starting_vd_edge, vd_t::edge_type *&ending_vd_edge, const std::vector< Segment > &segments)
 

Protected Attributes

ankerl::unordered_dense::map< vd_t::edge_type *, edge_t * > vd_edge_to_he_edge
 
ankerl::unordered_dense::map< vd_t::vertex_type *, node_t * > vd_node_to_he_node
 
std::vector< VariableWidthLines > * p_generated_toolpaths
 

Private Types

using pos_t = double
 
using vd_t = boost::polygon::voronoi_diagram< pos_t >
 
using graph_t = SkeletalTrapezoidationGraph
 
using edge_t = STHalfEdge
 
using node_t = STHalfEdgeNode
 
using Beading = BeadingStrategy::Beading
 
using BeadingPropagation = SkeletalTrapezoidationJoint::BeadingPropagation
 
using TransitionMiddle = SkeletalTrapezoidationEdge::TransitionMiddle
 
using TransitionEnd = SkeletalTrapezoidationEdge::TransitionEnd
 
template<typename T >
using ptr_vector_t = std::vector< std::shared_ptr< T > >
 

Private Attributes

double transitioning_angle
 How pointy a region should be before we apply the method. Equals 180* - limit_bisector_angle.
 
coord_t discretization_step_size
 approximate size of segments when parabolic VD edges get discretized (and vertex-vertex edges)
 
coord_t transition_filter_dist
 Filter transition mids (i.e. anchors) closer together than this.
 
coord_t allowed_filter_deviation
 The allowed line width deviation induced by filtering.
 
coord_t beading_propagation_transition_dist
 When there are different beadings propagated from below and from above, use this transitioning distance.
 
const BeadingStrategybeading_strategy
 

Static Private Attributes

static constexpr coord_t central_filter_dist = scaled<coord_t>(0.02)
 Filter areas marked as 'central' smaller than this.
 
static constexpr coord_t snap_dist = scaled<coord_t>(0.02)
 Generic arithmatic inaccuracy. Only used to determine whether a transition really needs to insert an extra edge.
 

Friends

bool detect_voronoi_edge_intersecting_input_segment (const Geometry::VoronoiDiagram &voronoi_diagram, const std::vector< VoronoiUtils::Segment > &segments)
 

Detailed Description

Main class of the dynamic beading strategies.

The input polygon region is decomposed into trapezoids and represented as a half-edge data-structure.

We determine which edges are 'central' accordinding to the transitioning_angle of the beading strategy, and determine the bead count for these central regions and apply them outward when generating toolpaths. [oversimplified]

The method can be visually explained as generating the 3D union of cones surface on the outline polygons, and changing the heights along central regions of that surface so that they are flat. For more info, please consult the paper "A framework for adaptive width control of dense contour-parallel toolpaths in fused deposition modeling" by Kuipers et al. This visual explanation aid explains the use of "upward", "lower" etc, i.e. the radial distance and/or the bead count are used as heights of this visualization, there is no coordinate called 'Z'.

TODO: split this class into two:

  1. Class for generating the decomposition and aux functions for performing updates
  2. Class for editing the structure for our purposes.

Member Typedef Documentation

◆ Beading

◆ BeadingPropagation

◆ edge_t

◆ graph_t

◆ node_t

◆ NodeSet

using Slic3r::Arachne::SkeletalTrapezoidation::NodeSet = ankerl::unordered_dense::set<node_t*>

◆ PointMap

◆ pos_t

◆ ptr_vector_t

template<typename T >
using Slic3r::Arachne::SkeletalTrapezoidation::ptr_vector_t = std::vector<std::shared_ptr<T> >
private

◆ Segment

◆ TransitionEnd

◆ TransitionMiddle

◆ vd_t

using Slic3r::Arachne::SkeletalTrapezoidation::vd_t = boost::polygon::voronoi_diagram<pos_t>
private

Constructor & Destructor Documentation

◆ SkeletalTrapezoidation()

Slic3r::Arachne::SkeletalTrapezoidation::SkeletalTrapezoidation ( const Polygons polys,
const BeadingStrategy beading_strategy,
double  transitioning_angle,
coord_t  discretization_step_size,
coord_t  transition_filter_dist,
coord_t  allowed_filter_deviation,
coord_t  beading_propagation_transition_dist 
)

Construct a new trapezoidation problem to solve.

Parameters
polysThe shapes to fill with walls.
beading_strategyThe strategy to use to fill these shapes.
transitioning_angleWhere we transition to a different number of walls, how steep should this transition be? A lower angle means that the transition will be longer.
discretization_step_sizeSince g-code can't represent smooth transitions in line width, the line width must change with discretized steps. This indicates how long the line segments between those steps will be.
transition_filter_distThe minimum length of transitions. Transitions shorter than this will be considered for dissolution.
beading_propagation_transition_distWhen there are different beadings propagated from below and from above, use this transitioning distance.
449{
451}
void constructFromPolygons(const Polygons &polys)
Definition SkeletalTrapezoidation.cpp:642
const BeadingStrategy & beading_strategy
Definition SkeletalTrapezoidation.hpp:82
coord_t discretization_step_size
approximate size of segments when parabolic VD edges get discretized (and vertex-vertex edges)
Definition SkeletalTrapezoidation.hpp:67
double transitioning_angle
How pointy a region should be before we apply the method. Equals 180* - limit_bisector_angle.
Definition SkeletalTrapezoidation.hpp:66
coord_t beading_propagation_transition_dist
When there are different beadings propagated from below and from above, use this transitioning distan...
Definition SkeletalTrapezoidation.hpp:70
coord_t allowed_filter_deviation
The allowed line width deviation induced by filtering.
Definition SkeletalTrapezoidation.hpp:69
coord_t transition_filter_dist
Filter transition mids (i.e. anchors) closer together than this.
Definition SkeletalTrapezoidation.hpp:68

References constructFromPolygons().

+ Here is the call graph for this function:

Member Function Documentation

◆ addToolpathSegment()

void Slic3r::Arachne::SkeletalTrapezoidation::addToolpathSegment ( const ExtrusionJunction from,
const ExtrusionJunction to,
bool  is_odd,
bool  force_new_path,
bool  from_is_3way,
bool  to_is_3way 
)
protected

Add a new toolpath segment, defined between two extrusion-juntions.

Parameters
fromThe junction from which to add a segment.
toThe junction to which to add a segment.
is_oddWhether this segment is an odd gap filler along the middle of the skeleton.
force_new_pathWhether to prevent adding this path to an existing path which ends in from
from_is_3wayWhether the from junction is a splitting junction where two normal wall lines and a gap filler line come together.
to_is_3wayWhether the to junction is a splitting junction where two normal wall lines and a gap filler line come together.
2243{
2244 if (from == to) return;
2245
2246 std::vector<VariableWidthLines> &generated_toolpaths = *p_generated_toolpaths;
2247
2248 size_t inset_idx = from.perimeter_index;
2249 if (inset_idx >= generated_toolpaths.size())
2250 {
2251 generated_toolpaths.resize(inset_idx + 1);
2252 }
2253 assert((generated_toolpaths[inset_idx].empty() || !generated_toolpaths[inset_idx].back().junctions.empty()) && "empty extrusion lines should never have been generated");
2254 if (generated_toolpaths[inset_idx].empty()
2255 || generated_toolpaths[inset_idx].back().is_odd != is_odd
2256 || generated_toolpaths[inset_idx].back().junctions.back().perimeter_index != inset_idx // inset_idx should always be consistent
2257 )
2258 {
2259 force_new_path = true;
2260 }
2261 if (!force_new_path
2262 && shorter_then(generated_toolpaths[inset_idx].back().junctions.back().p - from.p, scaled<coord_t>(0.010))
2263 && std::abs(generated_toolpaths[inset_idx].back().junctions.back().w - from.w) < scaled<coord_t>(0.010)
2264 && ! from_is_3way // force new path at 3way intersection
2265 )
2266 {
2267 generated_toolpaths[inset_idx].back().junctions.push_back(to);
2268 }
2269 else if (!force_new_path
2270 && shorter_then(generated_toolpaths[inset_idx].back().junctions.back().p - to.p, scaled<coord_t>(0.010))
2271 && std::abs(generated_toolpaths[inset_idx].back().junctions.back().w - to.w) < scaled<coord_t>(0.010)
2272 && ! to_is_3way // force new path at 3way intersection
2273 )
2274 {
2275 if ( ! is_odd)
2276 {
2277 BOOST_LOG_TRIVIAL(error) << "Reversing even wall line causes it to be printed CCW instead of CW!";
2278 }
2279 generated_toolpaths[inset_idx].back().junctions.push_back(from);
2280 }
2281 else
2282 {
2283 generated_toolpaths[inset_idx].emplace_back(inset_idx, is_odd);
2284 generated_toolpaths[inset_idx].back().junctions.push_back(from);
2285 generated_toolpaths[inset_idx].back().junctions.push_back(to);
2286 }
2287};
std::vector< VariableWidthLines > * p_generated_toolpaths
Definition SkeletalTrapezoidation.hpp:178
bool shorter_then(const Point &p0, const coord_t len)
Definition Point.hpp:301
bool empty(const BoundingBoxBase< PointType, PointsType > &bb)
Definition BoundingBox.hpp:229
TPoint< P > back(const P &p)
Definition geometry_traits.hpp:873
static char error[256]
Definition tga.cpp:50

References Slic3r::empty(), error, Slic3r::Arachne::ExtrusionJunction::p, Slic3r::Arachne::ExtrusionJunction::perimeter_index, Slic3r::shorter_then(), and Slic3r::Arachne::ExtrusionJunction::w.

+ Here is the call graph for this function:

◆ applyTransitions()

void Slic3r::Arachne::SkeletalTrapezoidation::applyTransitions ( ptr_vector_t< std::list< TransitionEnd > > &  edge_transition_ends)
protected

Also set the rest values at nodes in between the transition ends

1628{
1629 for (edge_t& edge : graph.edges)
1630 {
1631 if (edge.twin->data.hasTransitionEnds())
1632 {
1633 coord_t length = (edge.from->p - edge.to->p).cast<int64_t>().norm();
1634 auto& twin_transition_ends = *edge.twin->data.getTransitionEnds();
1635 if (! edge.data.hasTransitionEnds())
1636 {
1637 edge_transition_ends.emplace_back(std::make_shared<std::list<TransitionEnd>>());
1638 edge.data.setTransitionEnds(edge_transition_ends.back());
1639 }
1640 auto& transition_ends = *edge.data.getTransitionEnds();
1641 for (TransitionEnd& end : twin_transition_ends)
1642 {
1643 transition_ends.emplace_back(length - end.pos, end.lower_bead_count, end.is_lower_end);
1644 }
1645 twin_transition_ends.clear();
1646 }
1647 }
1648
1649 for (edge_t& edge : graph.edges)
1650 {
1651 if (! edge.data.hasTransitionEnds())
1652 {
1653 continue;
1654 }
1655
1656 assert(edge.data.isCentral());
1657
1658 auto& transitions = *edge.data.getTransitionEnds();
1659 transitions.sort([](const TransitionEnd& a, const TransitionEnd& b) { return a.pos < b.pos; } );
1660
1661 node_t* from = edge.from;
1662 node_t* to = edge.to;
1663 Point a = from->p;
1664 Point b = to->p;
1665 Point ab = b - a;
1666 coord_t ab_size = (ab).cast<int64_t>().norm();
1667
1668 edge_t* last_edge_replacing_input = &edge;
1669 for (TransitionEnd& transition_end : transitions)
1670 {
1671 coord_t new_node_bead_count = transition_end.is_lower_end? transition_end.lower_bead_count : transition_end.lower_bead_count + 1;
1672 coord_t end_pos = transition_end.pos;
1673 node_t* close_node = (end_pos < ab_size / 2)? from : to;
1674 if ((end_pos < snap_dist || end_pos > ab_size - snap_dist)
1675 && close_node->data.bead_count == new_node_bead_count
1676 )
1677 {
1678 assert(end_pos <= ab_size);
1679 close_node->data.transition_ratio = 0;
1680 continue;
1681 }
1682 Point mid = a + normal(ab, end_pos);
1683
1684 assert(last_edge_replacing_input->data.isCentral());
1685 assert(last_edge_replacing_input->data.type != SkeletalTrapezoidationEdge::EdgeType::EXTRA_VD);
1686 last_edge_replacing_input = graph.insertNode(last_edge_replacing_input, mid, new_node_bead_count);
1687 assert(last_edge_replacing_input->data.type != SkeletalTrapezoidationEdge::EdgeType::EXTRA_VD);
1688 assert(last_edge_replacing_input->data.isCentral());
1689 }
1690 }
1691}
edge_t * insertNode(edge_t *edge, Point mid, coord_t mide_node_bead_count)
Definition SkeletalTrapezoidationGraph.cpp:428
STHalfEdgeNode node_t
Definition SkeletalTrapezoidation.hpp:57
STHalfEdge edge_t
Definition SkeletalTrapezoidation.hpp:56
static constexpr coord_t snap_dist
Generic arithmatic inaccuracy. Only used to determine whether a transition really needs to insert an ...
Definition SkeletalTrapezoidation.hpp:72
graph_t graph
Definition SkeletalTrapezoidation.hpp:122
SkeletalTrapezoidationEdge::TransitionEnd TransitionEnd
Definition SkeletalTrapezoidation.hpp:61
int32_t coord_t
Definition libslic3r.h:39
static Point normal(const Point &p0, coord_t len)
Definition SkeletalTrapezoidation.cpp:1619
double length(const Points &pts)
Definition MultiPoint.hpp:116
IGL_INLINE void edges(const Eigen::MatrixBase< DerivedF > &F, Eigen::PlainObjectBase< DerivedE > &E)
Definition edges.cpp:13
S::iterator end(S &sh, const PathTag &)
Definition geometry_traits.hpp:620
Kernel::Point_2 Point
Definition point_areas.cpp:20

References Slic3r::Arachne::SkeletalTrapezoidationJoint::bead_count, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::data, Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::data, Slic3r::Arachne::HalfEdgeGraph< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::edges, Slic3r::Arachne::SkeletalTrapezoidationEdge::EXTRA_VD, graph, Slic3r::Arachne::SkeletalTrapezoidationGraph::insertNode(), Slic3r::Arachne::SkeletalTrapezoidationEdge::isCentral(), Slic3r::length(), Slic3r::Arachne::normal(), Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::p, snap_dist, Slic3r::Arachne::SkeletalTrapezoidationJoint::transition_ratio, and Slic3r::Arachne::SkeletalTrapezoidationEdge::type.

Referenced by generateTransitioningRibs().

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

◆ computePointCellRange()

bool Slic3r::Arachne::SkeletalTrapezoidation::computePointCellRange ( vd_t::cell_type &  cell,
Point start_source_point,
Point end_source_point,
vd_t::edge_type *&  starting_vd_edge,
vd_t::edge_type *&  ending_vd_edge,
const std::vector< Segment > &  segments 
)
staticprotected

Compute the range of line segments that surround a cell of the skeletal graph that belongs to a point on the medial axis.

This should only be used on cells that belong to a corner in the skeletal graph, e.g. triangular cells, not trapezoid cells.

The resulting line segments is just the first and the last segment. They are linked to the neighboring segments, so you can iterate over the segments until you reach the last segment.

Parameters
cellThe cell to compute the range of line segments for.
[out]start_source_pointThe start point of the source segment of this cell.
[out]end_source_pointThe end point of the source segment of this cell.
[out]starting_vd_edgeThe edge of the Voronoi diagram where the loop around the cell starts.
[out]ending_vd_edgeThe edge of the Voronoi diagram where the loop around the cell ends.
pointsAll vertices of the input Polygons.
segmentsAll edges of the input Polygons. /return Whether the cell is inside of the polygon. If it's outside of the polygon we should skip processing it altogether.
354{
355 if (cell.incident_edge()->is_infinite())
356 return false; //Infinite edges only occur outside of the polygon. Don't copy any part of this cell.
357
358 // Check if any point of the cell is inside or outside polygon
359 // Copy whole cell into graph or not at all
360
361 // If the cell.incident_edge()->vertex0() is far away so much that it doesn't even fit into Vec2i64, then there is no way that it will be inside the input polygon.
362 if (const vd_t::vertex_type &vert = *cell.incident_edge()->vertex0();
363 vert.x() >= double(std::numeric_limits<int64_t>::max()) || vert.x() <= double(std::numeric_limits<int64_t>::lowest()) ||
364 vert.y() >= double(std::numeric_limits<int64_t>::max()) || vert.y() <= double(std::numeric_limits<int64_t>::lowest()))
365 return false; // Don't copy any part of this cell
366
367 const Point source_point = VoronoiUtils::getSourcePoint(cell, segments);
368 const PolygonsPointIndex source_point_index = VoronoiUtils::getSourcePointIndex(cell, segments);
369 Vec2i64 some_point = VoronoiUtils::p(cell.incident_edge()->vertex0());
370 if (some_point == source_point.cast<int64_t>())
371 some_point = VoronoiUtils::p(cell.incident_edge()->vertex1());
372
373 //Test if the some_point is even inside the polygon.
374 //The edge leading out of a polygon must have an endpoint that's not in the corner following the contour of the polygon at that vertex.
375 //So if it's inside the corner formed by the polygon vertex, it's all fine.
376 //But if it's outside of the corner, it must be a vertex of the Voronoi diagram that goes outside of the polygon towards infinity.
377 if (!LinearAlg2D::isInsideCorner(source_point_index.prev().p(), source_point_index.p(), source_point_index.next().p(), some_point))
378 return false; // Don't copy any part of this cell
379
380 vd_t::edge_type* vd_edge = cell.incident_edge();
381 do {
382 assert(vd_edge->is_finite());
383 if (Vec2i64 p1 = VoronoiUtils::p(vd_edge->vertex1()); p1 == source_point.cast<int64_t>()) {
384 start_source_point = source_point;
385 end_source_point = source_point;
386 starting_vd_edge = vd_edge->next();
387 ending_vd_edge = vd_edge;
388 } else {
389 assert((VoronoiUtils::p(vd_edge->vertex0()) == source_point.cast<int64_t>() || !vd_edge->is_secondary()) && "point cells must end in the point! They cannot cross the point with an edge, because collinear edges are not allowed in the input.");
390 }
391 }
392 while (vd_edge = vd_edge->next(), vd_edge != cell.incident_edge());
393 assert(starting_vd_edge && ending_vd_edge);
394 assert(starting_vd_edge != ending_vd_edge);
395 return true;
396}
static Point getSourcePoint(const vd_t::cell_type &cell, const std::vector< Segment > &segments)
Definition VoronoiUtils.cpp:24
static PolygonsPointIndex getSourcePointIndex(const vd_t::cell_type &cell, const std::vector< Segment > &segments)
Definition VoronoiUtils.cpp:53
static Vec2i64 p(const vd_t::vertex_type *node)
Definition VoronoiUtils.cpp:14
static bool isInsideCorner(const Point &a, const Point &b, const Point &c, const Vec2i64 &query_point)
Definition linearAlg2D.hpp:19
PathsPointIndex< Polygons > PolygonsPointIndex
Definition PolygonsPointIndex.hpp:130
Eigen::Matrix< int64_t, 2, 1, Eigen::DontAlign > Vec2i64
Definition Point.hpp:43
__int64 int64_t
Definition unistd.h:76

References Slic3r::Arachne::VoronoiUtils::getSourcePoint(), Slic3r::Arachne::VoronoiUtils::getSourcePointIndex(), Slic3r::Arachne::LinearAlg2D::isInsideCorner(), Slic3r::Arachne::PathsPointIndex< Paths >::next(), Slic3r::Arachne::PathsPointIndex< Paths >::p(), Slic3r::Arachne::VoronoiUtils::p(), and Slic3r::Arachne::PathsPointIndex< Paths >::prev().

Referenced by constructFromPolygons().

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

◆ computeSegmentCellRange()

void Slic3r::Arachne::SkeletalTrapezoidation::computeSegmentCellRange ( vd_t::cell_type &  cell,
Point start_source_point,
Point end_source_point,
vd_t::edge_type *&  starting_vd_edge,
vd_t::edge_type *&  ending_vd_edge,
const std::vector< Segment > &  segments 
)
staticprotected

Compute the range of line segments that surround a cell of the skeletal graph that belongs to a line segment of the medial axis.

This should only be used on cells that belong to a central line segment of the skeletal graph, e.g. trapezoid cells, not triangular cells.

The resulting line segments is just the first and the last segment. They are linked to the neighboring segments, so you can iterate over the segments until you reach the last segment.

Parameters
cellThe cell to compute the range of line segments for.
[out]start_source_pointThe start point of the source segment of this cell.
[out]end_source_pointThe end point of the source segment of this cell.
[out]starting_vd_edgeThe edge of the Voronoi diagram where the loop around the cell starts.
[out]ending_vd_edgeThe edge of the Voronoi diagram where the loop around the cell ends.
pointsAll vertices of the input Polygons.
segmentsAll edges of the input Polygons. /return Whether the cell is inside of the polygon. If it's outside of the polygon we should skip processing it altogether.
399{
400 const Segment &source_segment = VoronoiUtils::getSourceSegment(cell, segments);
401 const Point from = source_segment.from();
402 const Point to = source_segment.to();
403
404 // Find starting edge
405 // Find end edge
406 bool seen_possible_start = false;
407 bool after_start = false;
408 bool ending_edge_is_set_before_start = false;
409 vd_t::edge_type* edge = cell.incident_edge();
410 do {
411 if (edge->is_infinite())
412 continue;
413
414 Vec2i64 v0 = VoronoiUtils::p(edge->vertex0());
415 Vec2i64 v1 = VoronoiUtils::p(edge->vertex1());
416
417 assert(!(v0 == to.cast<int64_t>() && v1 == from.cast<int64_t>() ));
418 if (v0 == to.cast<int64_t>() && !after_start) { // Use the last edge which starts in source_segment.to
419 starting_vd_edge = edge;
420 seen_possible_start = true;
421 }
422 else if (seen_possible_start) {
423 after_start = true;
424 }
425
426 if (v1 == from.cast<int64_t>() && (!ending_vd_edge || ending_edge_is_set_before_start)) {
427 ending_edge_is_set_before_start = !after_start;
428 ending_vd_edge = edge;
429 }
430 } while (edge = edge->next(), edge != cell.incident_edge());
431
432 assert(starting_vd_edge && ending_vd_edge);
433 assert(starting_vd_edge != ending_vd_edge);
434
435 start_source_point = source_segment.to();
436 end_source_point = source_segment.from();
437}
PolygonsSegmentIndex Segment
Definition SkeletalTrapezoidation.hpp:85
static const Segment & getSourceSegment(const vd_t::cell_type &cell, const std::vector< Segment > &segments)
Definition VoronoiUtils.cpp:81

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

Referenced by constructFromPolygons().

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

◆ connectJunctions()

void Slic3r::Arachne::SkeletalTrapezoidation::connectJunctions ( ptr_vector_t< LineJunctions > &  edge_junctions)
protected

connect junctions in each quad

2290{
2291 using EdgeSet = ankerl::unordered_dense::set<edge_t*>;
2292
2293 EdgeSet unprocessed_quad_starts(graph.edges.size() * 5 / 2);
2294 for (edge_t& edge : graph.edges)
2295 {
2296 if (!edge.prev)
2297 {
2298 unprocessed_quad_starts.emplace(&edge);
2299 }
2300 }
2301
2302 EdgeSet passed_odd_edges;
2303
2304 while (!unprocessed_quad_starts.empty())
2305 {
2306 edge_t* poly_domain_start = *unprocessed_quad_starts.begin();
2307 edge_t* quad_start = poly_domain_start;
2308 bool new_domain_start = true;
2309 do
2310 {
2311 edge_t* quad_end = quad_start;
2312 while (quad_end->next)
2313 {
2314 quad_end = quad_end->next;
2315 }
2316
2317 edge_t* edge_to_peak = getQuadMaxRedgeTo(quad_start);
2318 // walk down on both sides and connect junctions
2319 edge_t* edge_from_peak = edge_to_peak->next; assert(edge_from_peak);
2320
2321 unprocessed_quad_starts.erase(quad_start);
2322
2323 if (! edge_to_peak->data.hasExtrusionJunctions())
2324 {
2325 edge_junctions.emplace_back(std::make_shared<LineJunctions>());
2326 edge_to_peak->data.setExtrusionJunctions(edge_junctions.back());
2327 }
2328 // The junctions on the edge(s) from the start of the quad to the node with highest R
2329 LineJunctions from_junctions = *edge_to_peak->data.getExtrusionJunctions();
2330 if (! edge_from_peak->twin->data.hasExtrusionJunctions())
2331 {
2332 edge_junctions.emplace_back(std::make_shared<LineJunctions>());
2333 edge_from_peak->twin->data.setExtrusionJunctions(edge_junctions.back());
2334 }
2335 // The junctions on the edge(s) from the end of the quad to the node with highest R
2336 LineJunctions to_junctions = *edge_from_peak->twin->data.getExtrusionJunctions();
2337 if (edge_to_peak->prev)
2338 {
2339 LineJunctions from_prev_junctions = *edge_to_peak->prev->data.getExtrusionJunctions();
2340 while (!from_junctions.empty() && !from_prev_junctions.empty() && from_junctions.back().perimeter_index <= from_prev_junctions.front().perimeter_index)
2341 {
2342 from_junctions.pop_back();
2343 }
2344 from_junctions.reserve(from_junctions.size() + from_prev_junctions.size());
2345 from_junctions.insert(from_junctions.end(), from_prev_junctions.begin(), from_prev_junctions.end());
2346 assert(!edge_to_peak->prev->prev);
2347 if(edge_to_peak->prev->prev)
2348 {
2349 BOOST_LOG_TRIVIAL(warning) << "The edge we're about to connect is already connected.";
2350 }
2351 }
2352 if (edge_from_peak->next)
2353 {
2354 LineJunctions to_next_junctions = *edge_from_peak->next->twin->data.getExtrusionJunctions();
2355 while (!to_junctions.empty() && !to_next_junctions.empty() && to_junctions.back().perimeter_index <= to_next_junctions.front().perimeter_index)
2356 {
2357 to_junctions.pop_back();
2358 }
2359 to_junctions.reserve(to_junctions.size() + to_next_junctions.size());
2360 to_junctions.insert(to_junctions.end(), to_next_junctions.begin(), to_next_junctions.end());
2361 assert(!edge_from_peak->next->next);
2362 if(edge_from_peak->next->next)
2363 {
2364 BOOST_LOG_TRIVIAL(warning) << "The edge we're about to connect is already connected!";
2365 }
2366 }
2367 assert(std::abs(int(from_junctions.size()) - int(to_junctions.size())) <= 1); // at transitions one end has more beads
2368 if(std::abs(int(from_junctions.size()) - int(to_junctions.size())) > 1)
2369 {
2370 BOOST_LOG_TRIVIAL(warning) << "Can't create a transition when connecting two perimeters where the number of beads differs too much! " << from_junctions.size() << " vs. " << to_junctions.size();
2371 }
2372
2373 size_t segment_count = std::min(from_junctions.size(), to_junctions.size());
2374 for (size_t junction_rev_idx = 0; junction_rev_idx < segment_count; junction_rev_idx++)
2375 {
2376 ExtrusionJunction& from = from_junctions[from_junctions.size() - 1 - junction_rev_idx];
2377 ExtrusionJunction& to = to_junctions[to_junctions.size() - 1 - junction_rev_idx];
2378 assert(from.perimeter_index == to.perimeter_index);
2379 if(from.perimeter_index != to.perimeter_index)
2380 {
2381 BOOST_LOG_TRIVIAL(warning) << "Connecting two perimeters with different indices! Perimeter " << from.perimeter_index << " and " << to.perimeter_index;
2382 }
2383 const bool from_is_odd =
2384 quad_start->to->data.bead_count > 0 && quad_start->to->data.bead_count % 2 == 1 // quad contains single bead segment
2385 && quad_start->to->data.transition_ratio == 0 // We're not in a transition
2386 && junction_rev_idx == segment_count - 1 // Is single bead segment
2387 && shorter_then(from.p - quad_start->to->p, scaled<coord_t>(0.005));
2388 const bool to_is_odd =
2389 quad_end->from->data.bead_count > 0 && quad_end->from->data.bead_count % 2 == 1 // quad contains single bead segment
2390 && quad_end->from->data.transition_ratio == 0 // We're not in a transition
2391 && junction_rev_idx == segment_count - 1 // Is single bead segment
2392 && shorter_then(to.p - quad_end->from->p, scaled<coord_t>(0.005));
2393 const bool is_odd_segment = from_is_odd && to_is_odd;
2394 if (is_odd_segment
2395 && passed_odd_edges.count(quad_start->next->twin) > 0) // Only generate toolpath for odd segments once
2396 {
2397 continue; // Prevent duplication of single bead segments
2398 }
2399 bool from_is_3way = from_is_odd && quad_start->to->isMultiIntersection();
2400 bool to_is_3way = to_is_odd && quad_end->from->isMultiIntersection();
2401 passed_odd_edges.emplace(quad_start->next);
2402
2403 addToolpathSegment(from, to, is_odd_segment, new_domain_start, from_is_3way, to_is_3way);
2404 }
2405 new_domain_start = false;
2406 }
2407 while(quad_start = quad_start->getNextUnconnected(), quad_start != poly_domain_start);
2408 }
2409}
edge_t * next
Definition HalfEdge.hpp:25
edge_t * getQuadMaxRedgeTo(edge_t *quad_start_edge)
Definition SkeletalTrapezoidation.cpp:1892
void addToolpathSegment(const ExtrusionJunction &from, const ExtrusionJunction &to, bool is_odd, bool force_new_path, bool from_is_3way, bool to_is_3way)
Definition SkeletalTrapezoidation.cpp:2242
std::vector< ExtrusionJunction > LineJunctions
Definition ExtrusionJunction.hpp:56
Edges edges
Definition HalfEdgeGraph.hpp:26

References Slic3r::Arachne::SkeletalTrapezoidationJoint::bead_count, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::data, Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::data, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::from, Slic3r::Arachne::SkeletalTrapezoidationEdge::getExtrusionJunctions(), Slic3r::Arachne::STHalfEdge::getNextUnconnected(), Slic3r::Arachne::SkeletalTrapezoidationEdge::hasExtrusionJunctions(), Slic3r::Arachne::STHalfEdgeNode::isMultiIntersection(), Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::next, Slic3r::Arachne::ExtrusionJunction::p, Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::p, Slic3r::Arachne::ExtrusionJunction::perimeter_index, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::prev, Slic3r::Arachne::SkeletalTrapezoidationEdge::setExtrusionJunctions(), Slic3r::shorter_then(), Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::to, Slic3r::Arachne::SkeletalTrapezoidationJoint::transition_ratio, and Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::twin.

+ Here is the call graph for this function:

◆ constructFromPolygons()

void Slic3r::Arachne::SkeletalTrapezoidation::constructFromPolygons ( const Polygons polys)
protected

Compute the skeletal trapezoidation decomposition of the input shape.

Compute the Voronoi Diagram (VD) and transfer all inside edges into our half-edge (HE) datastructure.

The algorithm is currently a bit overcomplicated, because the discretization of parabolic edges is performed at the same time as all edges are being transfered, which means that there is no one-to-one mapping from VD edges to HE edges. Instead we map from a VD edge to the last HE edge. This could be cimplified by recording the edges which should be discretized and discretizing the mafterwards.

Another complication arises because the VD uses floating logic, which can result in zero-length segments after rounding to integers. We therefore collapse edges and their whole cells afterwards.

643{
644#ifdef ARACHNE_DEBUG
645 this->outline = polys;
646#endif
647
648 // Check self intersections.
649 assert([&polys]() -> bool {
650 EdgeGrid::Grid grid;
651 grid.set_bbox(get_extents(polys));
652 grid.create(polys, scaled<coord_t>(10.));
653 return !grid.has_intersecting_edges();
654 }());
655
656 vd_edge_to_he_edge.clear();
657 vd_node_to_he_node.clear();
658
659 std::vector<Segment> segments;
660 for (size_t poly_idx = 0; poly_idx < polys.size(); poly_idx++)
661 for (size_t point_idx = 0; point_idx < polys[poly_idx].size(); point_idx++)
662 segments.emplace_back(&polys, poly_idx, point_idx);
663
664#ifdef ARACHNE_DEBUG
665 {
666 static int iRun = 0;
667 BoundingBox bbox = get_extents(polys);
668 SVG svg(debug_out_path("arachne_voronoi-input-%d.svg", iRun++).c_str(), bbox);
669 svg.draw_outline(polys, "black", scaled<coordf_t>(0.03f));
670 }
671#endif
672
673 Geometry::VoronoiDiagram voronoi_diagram;
674 construct_voronoi(segments.begin(), segments.end(), &voronoi_diagram);
675
676#ifdef ARACHNE_DEBUG_VORONOI
677 {
678 static int iRun = 0;
679 dump_voronoi_to_svg(debug_out_path("arachne_voronoi-diagram-%d.svg", iRun++).c_str(), voronoi_diagram, to_points(polys), to_lines(polys));
680 }
681#endif
682
683 // When any Voronoi vertex is missing, the Voronoi diagram is not planar, or some voronoi edge is
684 // intersecting input segment, rotate the input polygon and try again.
685 VoronoiDiagramStatus status = detect_voronoi_diagram_known_issues(voronoi_diagram, segments);
686 const std::vector<double> fix_angles = {PI / 6, PI / 5, PI / 7, PI / 11};
687 double fixed_by_angle = fix_angles.front();
688
689 PointMap vertex_mapping;
690 // polys_copy is referenced through items stored in the std::vector segments.
691 Polygons polys_copy = polys;
694 BOOST_LOG_TRIVIAL(warning) << "Detected missing Voronoi vertex, input polygons will be rotated back and forth.";
696 BOOST_LOG_TRIVIAL(warning) << "Detected non-planar Voronoi diagram, input polygons will be rotated back and forth.";
698 BOOST_LOG_TRIVIAL(warning) << "Detected Voronoi edge intersecting input segment, input polygons will be rotated back and forth.";
699
700 std::tie(vertex_mapping, fixed_by_angle) = try_to_fix_degenerated_voronoi_diagram_by_rotation(voronoi_diagram, polys, polys_copy, segments, fix_angles);
701
702 VoronoiDiagramStatus status_after_fix = detect_voronoi_diagram_known_issues(voronoi_diagram, segments);
703 assert(status_after_fix == VoronoiDiagramStatus::NO_ISSUE_DETECTED);
704 if (status_after_fix == VoronoiDiagramStatus::MISSING_VORONOI_VERTEX)
705 BOOST_LOG_TRIVIAL(error) << "Detected missing Voronoi vertex even after the rotation of input.";
706 else if (status_after_fix == VoronoiDiagramStatus::NON_PLANAR_VORONOI_DIAGRAM)
707 BOOST_LOG_TRIVIAL(error) << "Detected non-planar Voronoi diagram even after the rotation of input.";
709 BOOST_LOG_TRIVIAL(error) << "Detected Voronoi edge intersecting input segment even after the rotation of input.";
710 }
711
712process_voronoi_diagram:
713 assert(this->graph.edges.empty() && this->graph.nodes.empty() && this->vd_edge_to_he_edge.empty() && this->vd_node_to_he_node.empty());
714 for (vd_t::cell_type cell : voronoi_diagram.cells()) {
715 if (!cell.incident_edge())
716 continue; // There is no spoon
717
718 Point start_source_point;
719 Point end_source_point;
720 vd_t::edge_type* starting_voronoi_edge = nullptr;
721 vd_t::edge_type* ending_voronoi_edge = nullptr;
722 // Compute and store result in above variables
723
724 if (cell.contains_point()) {
725 const bool keep_going = computePointCellRange(cell, start_source_point, end_source_point, starting_voronoi_edge, ending_voronoi_edge, segments);
726 if (!keep_going)
727 continue;
728 } else {
729 assert(cell.contains_segment());
730 computeSegmentCellRange(cell, start_source_point, end_source_point, starting_voronoi_edge, ending_voronoi_edge, segments);
731 }
732
733 if (!starting_voronoi_edge || !ending_voronoi_edge) {
734 assert(false && "Each cell should start / end in a polygon vertex");
735 continue;
736 }
737
738 // Copy start to end edge to graph
739 edge_t* prev_edge = nullptr;
740 assert(VoronoiUtils::p(starting_voronoi_edge->vertex1()).x() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(starting_voronoi_edge->vertex1()).x() >= std::numeric_limits<coord_t>::lowest());
741 assert(VoronoiUtils::p(starting_voronoi_edge->vertex1()).y() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(starting_voronoi_edge->vertex1()).y() >= std::numeric_limits<coord_t>::lowest());
742 transferEdge(start_source_point, VoronoiUtils::p(starting_voronoi_edge->vertex1()).cast<coord_t>(), *starting_voronoi_edge, prev_edge, start_source_point, end_source_point, segments);
743 node_t* starting_node = vd_node_to_he_node[starting_voronoi_edge->vertex0()];
744 starting_node->data.distance_to_boundary = 0;
745
746 constexpr bool is_next_to_start_or_end = true;
747 graph.makeRib(prev_edge, start_source_point, end_source_point, is_next_to_start_or_end);
748 for (vd_t::edge_type* vd_edge = starting_voronoi_edge->next(); vd_edge != ending_voronoi_edge; vd_edge = vd_edge->next()) {
749 assert(vd_edge->is_finite());
750
751 assert(VoronoiUtils::p(vd_edge->vertex0()).x() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(vd_edge->vertex0()).x() >= std::numeric_limits<coord_t>::lowest());
752 assert(VoronoiUtils::p(vd_edge->vertex0()).y() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(vd_edge->vertex0()).y() >= std::numeric_limits<coord_t>::lowest());
753 assert(VoronoiUtils::p(vd_edge->vertex1()).x() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(vd_edge->vertex1()).x() >= std::numeric_limits<coord_t>::lowest());
754 assert(VoronoiUtils::p(vd_edge->vertex1()).y() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(vd_edge->vertex1()).y() >= std::numeric_limits<coord_t>::lowest());
755
756 Point v1 = VoronoiUtils::p(vd_edge->vertex0()).cast<coord_t>();
757 Point v2 = VoronoiUtils::p(vd_edge->vertex1()).cast<coord_t>();
758 transferEdge(v1, v2, *vd_edge, prev_edge, start_source_point, end_source_point, segments);
759
760 graph.makeRib(prev_edge, start_source_point, end_source_point, vd_edge->next() == ending_voronoi_edge);
761 }
762
763 assert(VoronoiUtils::p(starting_voronoi_edge->vertex0()).x() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(starting_voronoi_edge->vertex0()).x() >= std::numeric_limits<coord_t>::lowest());
764 assert(VoronoiUtils::p(starting_voronoi_edge->vertex0()).y() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(starting_voronoi_edge->vertex0()).y() >= std::numeric_limits<coord_t>::lowest());
765 transferEdge(VoronoiUtils::p(ending_voronoi_edge->vertex0()).cast<coord_t>(), end_source_point, *ending_voronoi_edge, prev_edge, start_source_point, end_source_point, segments);
766 prev_edge->to->data.distance_to_boundary = 0;
767 }
768
769 // For some input polygons, as in GH issues #8474 and #8514 resulting Voronoi diagram is degenerated because it is not planar.
770 // When this degenerated Voronoi diagram is processed, the resulting half-edge structure contains some edges that don't have
771 // a twin edge. Based on this, we created a fast mechanism that detects those causes and tries to recompute the Voronoi
772 // diagram on slightly rotated input polygons that usually make the Voronoi generator generate a non-degenerated Voronoi diagram.
774 BOOST_LOG_TRIVIAL(warning) << "Detected degenerated Voronoi diagram, input polygons will be rotated back and forth.";
776 std::tie(vertex_mapping, fixed_by_angle) = try_to_fix_degenerated_voronoi_diagram_by_rotation(voronoi_diagram, polys, polys_copy, segments, fix_angles);
777
778 assert(!detect_missing_voronoi_vertex(voronoi_diagram, segments));
779 if (detect_missing_voronoi_vertex(voronoi_diagram, segments))
780 BOOST_LOG_TRIVIAL(error) << "Detected missing Voronoi vertex after the rotation of input.";
781
783
784 this->graph.edges.clear();
785 this->graph.nodes.clear();
786 this->vd_edge_to_he_edge.clear();
787 this->vd_node_to_he_node.clear();
788
789 goto process_voronoi_diagram;
790 }
791
793 assert(!has_missing_twin_edge(this->graph));
794
795 if (has_missing_twin_edge(this->graph))
796 BOOST_LOG_TRIVIAL(error) << "Detected degenerated Voronoi diagram even after the rotation of input.";
797 }
798
800 rotate_back_skeletal_trapezoidation_graph_after_fix(this->graph, fixed_by_angle, vertex_mapping);
801
802#ifdef ARACHNE_DEBUG
804#endif
805
807
809
810 // Set [incident_edge] the first possible edge that way we can iterate over all reachable edges from node.incident_edge,
811 // without needing to iterate backward
812 for (edge_t& edge : graph.edges)
813 if (!edge.prev)
814 edge.from->incident_edge = &edge;
815}
void makeRib(edge_t *&prev_edge, Point start_source_point, Point end_source_point, bool is_next_to_start_or_end)
Definition SkeletalTrapezoidationGraph.cpp:317
void collapseSmallEdges(coord_t snap_dist=5)
Definition SkeletalTrapezoidationGraph.cpp:182
void transferEdge(Point from, Point to, vd_t::edge_type &vd_edge, edge_t *&prev_edge, Point &start_source_point, Point &end_source_point, const std::vector< Segment > &segments)
Definition SkeletalTrapezoidation.cpp:127
static void computeSegmentCellRange(vd_t::cell_type &cell, Point &start_source_point, Point &end_source_point, vd_t::edge_type *&starting_vd_edge, vd_t::edge_type *&ending_vd_edge, const std::vector< Segment > &segments)
Definition SkeletalTrapezoidation.cpp:398
ankerl::unordered_dense::map< vd_t::vertex_type *, node_t * > vd_node_to_he_node
Definition SkeletalTrapezoidation.hpp:172
ankerl::unordered_dense::map< vd_t::edge_type *, edge_t * > vd_edge_to_he_edge
Definition SkeletalTrapezoidation.hpp:171
static bool computePointCellRange(vd_t::cell_type &cell, Point &start_source_point, Point &end_source_point, vd_t::edge_type *&starting_vd_edge, vd_t::edge_type *&ending_vd_edge, const std::vector< Segment > &segments)
Definition SkeletalTrapezoidation.cpp:353
void separatePointyQuadEndNodes()
Definition SkeletalTrapezoidation.cpp:819
ankerl::unordered_dense::map< Point, Point, PointHash > PointMap
Definition SkeletalTrapezoidation.hpp:86
static bool is_voronoi_diagram_planar_intersection(const VoronoiDiagram &voronoi_diagram)
Definition VoronoiUtilsCgal.cpp:134
if(!(yy_init))
Definition lexer.c:1190
static constexpr double PI
Definition libslic3r.h:58
static bool detect_missing_voronoi_vertex(const Geometry::VoronoiDiagram &voronoi_diagram, const std::vector< SkeletalTrapezoidation::Segment > &segments)
Definition SkeletalTrapezoidation.cpp:466
static void rotate_back_skeletal_trapezoidation_graph_after_fix(SkeletalTrapezoidationGraph &graph, const double fix_angle, const PointMap &vertex_mapping)
Definition SkeletalTrapezoidation.cpp:526
static bool has_missing_twin_edge(const SkeletalTrapezoidationGraph &graph)
Definition SkeletalTrapezoidation.cpp:516
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)
Definition SkeletalTrapezoidation.cpp:592
VoronoiDiagramStatus
Definition SkeletalTrapezoidation.cpp:567
VoronoiDiagramStatus detect_voronoi_diagram_known_issues(const Geometry::VoronoiDiagram &voronoi_diagram, const std::vector< SkeletalTrapezoidation::Segment > &segments)
Definition SkeletalTrapezoidation.cpp:577
Nodes nodes
Definition HalfEdgeGraph.hpp:27
std::vector< Polygon, PointsAllocator< Polygon > > Polygons
Definition Polygon.hpp:15
std::string debug_out_path(const char *name,...)
Definition utils.cpp:218
Lines to_lines(const ExPolygon &src)
Definition ExPolygon.hpp:117
BoundingBox get_extents(const ExPolygon &expolygon)
Definition ExPolygon.cpp:352
static void dump_voronoi_to_svg(const char *path, const Geometry::VoronoiDiagram &vd, const Points &points, const Lines &lines, const Polygons &offset_curves=Polygons(), const Lines &helper_lines=Lines(), double scale=0)
Definition VoronoiVisualUtils.hpp:296
std::vector< ArithmeticOnly< T > > grid(const T &start, const T &stop, const T &stride)
A set of equidistant values starting from 'start' (inclusive), ending in the closest multiple of 'str...
Definition MTUtils.hpp:125
Points to_points(const ExPolygons &src)
Definition ExPolygon.hpp:196

References Slic3r::Arachne::SkeletalTrapezoidationGraph::collapseSmallEdges(), computePointCellRange(), computeSegmentCellRange(), Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::data, Slic3r::debug_out_path(), Slic3r::Arachne::detect_missing_voronoi_vertex(), Slic3r::Arachne::detect_voronoi_diagram_known_issues(), Slic3r::Arachne::SkeletalTrapezoidationJoint::distance_to_boundary, Slic3r::SVG::draw_outline(), Slic3r::dump_voronoi_to_svg(), Slic3r::Arachne::HalfEdgeGraph< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::edges, error, Slic3r::get_extents(), graph, Slic3r::grid(), Slic3r::Arachne::has_missing_twin_edge(), Slic3r::Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(), Slic3r::Arachne::SkeletalTrapezoidationGraph::makeRib(), Slic3r::Arachne::MISSING_VORONOI_VERTEX, Slic3r::Arachne::NO_ISSUE_DETECTED, Slic3r::Arachne::NON_PLANAR_VORONOI_DIAGRAM, Slic3r::Arachne::OTHER_TYPE_OF_VORONOI_DIAGRAM_DEGENERATION, Slic3r::Arachne::VoronoiUtils::p(), PI, Slic3r::Arachne::rotate_back_skeletal_trapezoidation_graph_after_fix(), separatePointyQuadEndNodes(), Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::to, Slic3r::to_lines(), Slic3r::to_points(), transferEdge(), Slic3r::Arachne::try_to_fix_degenerated_voronoi_diagram_by_rotation(), vd_edge_to_he_edge, vd_node_to_he_node, and Slic3r::Arachne::VORONOI_EDGE_INTERSECTING_INPUT_SEGMENT.

Referenced by SkeletalTrapezoidation().

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

◆ discretize()

Points Slic3r::Arachne::SkeletalTrapezoidation::discretize ( const vd_t::edge_type &  segment,
const std::vector< Segment > &  segments 
)
protected

Discretize a Voronoi edge that represents the medial axis of a vertex- line region or vertex-vertex region into small segments that can be considered to have a straight medial axis and a linear line width transition.

The medial axis between a point and a line is a parabola. The rest of the algorithm doesn't want to have to deal with parabola, so this discretises the parabola into straight line segments. This is necessary if there is a sharp inner corner (acts as a point) that comes close to a straight edge.

The medial axis between a point and a point is a straight line segment. However the distance from the medial axis to either of those points draws a parabola as you go along the medial axis. That means that the resulting line width along the medial axis would not be linearly increasing or linearly decreasing, but needs to take the shape of a parabola. Instead, we'll break this edge up into tiny line segments that can approximate the parabola with tiny linear increases or decreases in line width.

Parameters
segmentThe variable-width Voronoi edge to discretize.
pointsAll vertices of the original Polygons to fill with beads.
segmentsAll line segments of the original Polygons to fill with beads.
Returns
A number of coordinates along the edge where the edge is broken up into discrete pieces.
239{
240 /*Terminology in this function assumes that the edge moves horizontally from
241 left to right. This is not necessarily the case; the edge can go in any
242 direction, but it helps to picture it in a certain direction in your head.*/
243
244 const vd_t::cell_type* left_cell = vd_edge.cell();
245 const vd_t::cell_type* right_cell = vd_edge.twin()->cell();
246
247 assert(VoronoiUtils::p(vd_edge.vertex0()).x() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(vd_edge.vertex0()).x() >= std::numeric_limits<coord_t>::lowest());
248 assert(VoronoiUtils::p(vd_edge.vertex0()).y() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(vd_edge.vertex0()).y() >= std::numeric_limits<coord_t>::lowest());
249 assert(VoronoiUtils::p(vd_edge.vertex1()).x() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(vd_edge.vertex1()).x() >= std::numeric_limits<coord_t>::lowest());
250 assert(VoronoiUtils::p(vd_edge.vertex1()).y() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(vd_edge.vertex1()).y() >= std::numeric_limits<coord_t>::lowest());
251
252 Point start = VoronoiUtils::p(vd_edge.vertex0()).cast<coord_t>();
253 Point end = VoronoiUtils::p(vd_edge.vertex1()).cast<coord_t>();
254
255 bool point_left = left_cell->contains_point();
256 bool point_right = right_cell->contains_point();
257 if ((!point_left && !point_right) || vd_edge.is_secondary()) // Source vert is directly connected to source segment
258 {
259 return Points({ start, end });
260 }
261 else if (point_left != point_right) //This is a parabolic edge between a point and a line.
262 {
263 Point p = VoronoiUtils::getSourcePoint(*(point_left ? left_cell : right_cell), segments);
264 const Segment& s = VoronoiUtils::getSourceSegment(*(point_left ? right_cell : left_cell), segments);
266 }
267 else //This is a straight edge between two points.
268 {
269 /*While the edge is straight, it is still discretized since the part
270 becomes narrower between the two points. As such it may need different
271 beadings along the way.*/
272 Point left_point = VoronoiUtils::getSourcePoint(*left_cell, segments);
273 Point right_point = VoronoiUtils::getSourcePoint(*right_cell, segments);
274 coord_t d = (right_point - left_point).cast<int64_t>().norm();
275 Point middle = (left_point + right_point) / 2;
276 Point x_axis_dir = perp(Point(right_point - left_point));
277 coord_t x_axis_length = x_axis_dir.cast<int64_t>().norm();
278
279 const auto projected_x = [x_axis_dir, x_axis_length, middle](Point from) //Project a point on the edge.
280 {
281 Point vec = from - middle;
282 assert(( vec.cast<int64_t>().dot(x_axis_dir.cast<int64_t>())/ int64_t(x_axis_length)) <= std::numeric_limits<coord_t>::max());
283 coord_t x = vec.cast<int64_t>().dot(x_axis_dir.cast<int64_t>()) / int64_t(x_axis_length);
284 return x;
285 };
286
287 coord_t start_x = projected_x(start);
288 coord_t end_x = projected_x(end);
289
290 //Part of the edge will be bound to the markings on the endpoints of the edge. Calculate how far that is.
291 float bound = 0.5 / tan((M_PI - transitioning_angle) * 0.5);
292 int64_t marking_start_x = - int64_t(d) * bound;
293 int64_t marking_end_x = int64_t(d) * bound;
294
295 assert((middle.cast<int64_t>() + x_axis_dir.cast<int64_t>() * marking_start_x / int64_t(x_axis_length)).x() <= std::numeric_limits<coord_t>::max());
296 assert((middle.cast<int64_t>() + x_axis_dir.cast<int64_t>() * marking_start_x / int64_t(x_axis_length)).y() <= std::numeric_limits<coord_t>::max());
297 assert((middle.cast<int64_t>() + x_axis_dir.cast<int64_t>() * marking_end_x / int64_t(x_axis_length)).x() <= std::numeric_limits<coord_t>::max());
298 assert((middle.cast<int64_t>() + x_axis_dir.cast<int64_t>() * marking_end_x / int64_t(x_axis_length)).y() <= std::numeric_limits<coord_t>::max());
299 Point marking_start = middle + (x_axis_dir.cast<int64_t>() * marking_start_x / int64_t(x_axis_length)).cast<coord_t>();
300 Point marking_end = middle + (x_axis_dir.cast<int64_t>() * marking_end_x / int64_t(x_axis_length)).cast<coord_t>();
301 int64_t direction = 1;
302
303 if (start_x > end_x) //Oops, the Voronoi edge is the other way around.
304 {
305 direction = -1;
306 std::swap(marking_start, marking_end);
307 std::swap(marking_start_x, marking_end_x);
308 }
309
310 //Start generating points along the edge.
311 Point a = start;
312 Point b = end;
313 Points ret;
314 ret.emplace_back(a);
315
316 //Introduce an extra edge at the borders of the markings?
317 bool add_marking_start = marking_start_x * direction > int64_t(start_x) * direction;
318 bool add_marking_end = marking_end_x * direction > int64_t(start_x) * direction;
319
320 //The edge's length may not be divisible by the step size, so calculate an integer step count and evenly distribute the vertices among those.
321 Point ab = b - a;
322 coord_t ab_size = ab.cast<int64_t>().norm();
323 coord_t step_count = (ab_size + discretization_step_size / 2) / discretization_step_size;
324 if (step_count % 2 == 1)
325 {
326 step_count++; // enforce a discretization point being added in the middle
327 }
328 for (coord_t step = 1; step < step_count; step++)
329 {
330 Point here = a + (ab.cast<int64_t>() * int64_t(step) / int64_t(step_count)).cast<coord_t>(); //Now simply interpolate the coordinates to get the new vertices!
331 coord_t x_here = projected_x(here); //If we've surpassed the position of the extra markings, we may need to insert them first.
332 if (add_marking_start && marking_start_x * direction < int64_t(x_here) * direction)
333 {
334 ret.emplace_back(marking_start);
335 add_marking_start = false;
336 }
337 if (add_marking_end && marking_end_x * direction < int64_t(x_here) * direction)
338 {
339 ret.emplace_back(marking_end);
340 add_marking_end = false;
341 }
342 ret.emplace_back(here);
343 }
344 if (add_marking_end && marking_end_x * direction < int64_t(end_x) * direction)
345 {
346 ret.emplace_back(marking_end);
347 }
348 ret.emplace_back(b);
349 return ret;
350 }
351}
EIGEN_DEVICE_FUNC const TanReturnType tan() const
Definition ArrayCwiseUnaryOps.h:234
#define M_PI
Definition ExtrusionSimulator.cpp:20
static Points discretizeParabola(const Point &source_point, const Segment &source_segment, Point start, Point end, coord_t approximate_step_size, float transitioning_angle)
Definition VoronoiUtils.cpp:141
const Scalar & y
Definition MathFunctions.h:552
Eigen::Matrix< typename Derived::Scalar, 2, 1, Eigen::DontAlign > perp(const Eigen::MatrixBase< Derived > &v)
Definition Point.hpp:104
T dot(const boost::geometry::model::d2::point_xy< T > &v1, const boost::geometry::model::d2::point_xy< T > &v2)
Definition ExtrusionSimulator.cpp:143
std::vector< Point, PointsAllocator< Point > > Points
Definition Point.hpp:58
Bound< T > bound(const T &min, const T &max)
Definition optimizer.hpp:45
TCoord< P > x(const P &p)
Definition geometry_traits.hpp:297
Coord step(const Coord &crd, Dir d)
Definition MarchingSquares.hpp:137

References discretization_step_size, Slic3r::Arachne::VoronoiUtils::discretizeParabola(), Slic3r::dot(), Slic3r::Arachne::VoronoiUtils::getSourcePoint(), Slic3r::Arachne::VoronoiUtils::getSourceSegment(), M_PI, Slic3r::Arachne::VoronoiUtils::p(), Slic3r::perp(), tan(), and transitioning_angle.

Referenced by transferEdge().

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

◆ dissolveBeadCountRegion()

void Slic3r::Arachne::SkeletalTrapezoidation::dissolveBeadCountRegion ( edge_t edge_to_start,
coord_t  from_bead_count,
coord_t  to_bead_count 
)
protected

Spread a certain bead count over a region in the graph.

Parameters
edge_to_startOne edge of the region to spread the bead count in.
from_bead_countAll edges with this bead count will be changed.
to_bead_countThe new bead count for those edges.
1352{
1353 assert(from_bead_count != to_bead_count);
1354 if (edge_to_start->to->data.bead_count != from_bead_count)
1355 return;
1356
1357 edge_to_start->to->data.bead_count = to_bead_count;
1358 for (edge_t* edge = edge_to_start->next; edge && edge != edge_to_start->twin; edge = edge->twin->next)
1359 {
1360 if (!edge->data.isCentral())
1361 {
1362 continue;
1363 }
1364 dissolveBeadCountRegion(edge, from_bead_count, to_bead_count);
1365 }
1366}
void dissolveBeadCountRegion(edge_t *edge_to_start, coord_t from_bead_count, coord_t to_bead_count)
Definition SkeletalTrapezoidation.cpp:1351

References Slic3r::Arachne::SkeletalTrapezoidationJoint::bead_count, Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::data, dissolveBeadCountRegion(), Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::next, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::to, and Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::twin.

Referenced by dissolveBeadCountRegion(), and filterTransitionMids().

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

◆ dissolveNearbyTransitions()

std::list< SkeletalTrapezoidation::TransitionMidRef > Slic3r::Arachne::SkeletalTrapezoidation::dissolveNearbyTransitions ( edge_t edge_to_start,
TransitionMiddle origin_transition,
coord_t  traveled_dist,
coord_t  max_dist,
bool  going_up 
)
protected

Merge transitions that are too close together.

Parameters
edge_to_startEdge pointing to the node from which to start traveling in all directions except along edge_to_start .
origin_transitionThe transition for which we are checking nearby transitions.
traveled_distThe distance traveled before we came to edge_to_start.to .
going_upWhether we are traveling in the upward direction as seen from the origin_transition. If this doesn't align with the direction according to the R diff on a consecutive edge we know there was a local optimum.
Returns
Whether the origin transition should be dissolved.
1292{
1293 std::list<TransitionMidRef> to_be_dissolved;
1294 if (traveled_dist > max_dist)
1295 return to_be_dissolved;
1296
1297 bool should_dissolve = true;
1298 for (edge_t* edge = edge_to_start->next; edge && edge != edge_to_start->twin; edge = edge->twin->next){
1299 if (!edge->data.isCentral())
1300 continue;
1301
1302 Point a = edge->from->p;
1303 Point b = edge->to->p;
1304 Point ab = b - a;
1305 coord_t ab_size = ab.cast<int64_t>().norm();
1306 bool is_aligned = edge->isUpward();
1307 edge_t* aligned_edge = is_aligned? edge : edge->twin;
1308 bool seen_transition_on_this_edge = false;
1309
1310 const coord_t origin_radius = origin_transition.feature_radius;
1311 const coord_t radius_here = edge->from->data.distance_to_boundary;
1312 const bool dissolve_result_is_odd = bool(origin_transition.lower_bead_count % 2) == going_up;
1313 const coord_t width_deviation = std::abs(origin_radius - radius_here) * 2; // times by two because the deviation happens at both sides of the significant edge
1314 const coord_t line_width_deviation = dissolve_result_is_odd ? width_deviation : width_deviation / 2; // assume the deviation will be split over either 1 or 2 lines, i.e. assume wall_distribution_count = 1
1315 if (line_width_deviation > allowed_filter_deviation)
1316 should_dissolve = false;
1317
1318 if (should_dissolve && aligned_edge->data.hasTransitions()) {
1319 auto& transitions = *aligned_edge->data.getTransitions();
1320 for (auto transition_it = transitions.begin(); transition_it != transitions.end(); ++ transition_it) { // Note: this is not necessarily iterating in the traveling direction!
1321 // Check whether we should dissolve
1322 coord_t pos = is_aligned? transition_it->pos : ab_size - transition_it->pos;
1323 if (traveled_dist + pos < max_dist && transition_it->lower_bead_count == origin_transition.lower_bead_count) { // Only dissolve local optima
1324 if (traveled_dist + pos < beading_strategy.getTransitioningLength(transition_it->lower_bead_count)) {
1325 // Consecutive transitions both in/decreasing in bead count should never be closer together than the transition distance
1326 assert(going_up != is_aligned || transition_it->lower_bead_count == 0);
1327 }
1328 to_be_dissolved.emplace_back(aligned_edge, transition_it);
1329 seen_transition_on_this_edge = true;
1330 }
1331 }
1332 }
1333 if (should_dissolve && !seen_transition_on_this_edge) {
1334 std::list<SkeletalTrapezoidation::TransitionMidRef> to_be_dissolved_here = dissolveNearbyTransitions(edge, origin_transition, traveled_dist + ab_size, max_dist, going_up);
1335 if (to_be_dissolved_here.empty()) { // The region is too long to be dissolved in this direction, so it cannot be dissolved in any direction.
1336 to_be_dissolved.clear();
1337 return to_be_dissolved;
1338 }
1339 to_be_dissolved.splice(to_be_dissolved.end(), to_be_dissolved_here); // Transfer to_be_dissolved_here into to_be_dissolved
1340 should_dissolve = should_dissolve && !to_be_dissolved.empty();
1341 }
1342 }
1343
1344 if (!should_dissolve)
1345 to_be_dissolved.clear();
1346
1347 return to_be_dissolved;
1348}
virtual coord_t getTransitioningLength(coord_t lower_bead_count) const
Definition BeadingStrategy.cpp:31
edge_t * twin
Definition HalfEdge.hpp:24
node_t * from
Definition HalfEdge.hpp:27
node_data_t data
Definition HalfEdgeNode.hpp:23
std::list< TransitionMidRef > dissolveNearbyTransitions(edge_t *edge_to_start, TransitionMiddle &origin_transition, coord_t traveled_dist, coord_t max_dist, bool going_up)
Definition SkeletalTrapezoidation.cpp:1291
coord_t distance_to_boundary
Definition SkeletalTrapezoidationJoint.hpp:32
Vec3d pos(const Pt &p)
Definition ReprojectPointsOnMesh.hpp:14

References allowed_filter_deviation, beading_strategy, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::data, dissolveNearbyTransitions(), Slic3r::Arachne::SkeletalTrapezoidationEdge::TransitionMiddle::feature_radius, Slic3r::Arachne::BeadingStrategy::getTransitioningLength(), Slic3r::Arachne::SkeletalTrapezoidationEdge::getTransitions(), Slic3r::Arachne::SkeletalTrapezoidationEdge::hasTransitions(), Slic3r::Arachne::SkeletalTrapezoidationEdge::TransitionMiddle::lower_bead_count, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::next, and Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::twin.

Referenced by dissolveNearbyTransitions(), and filterTransitionMids().

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

◆ filterCentral() [1/2]

void Slic3r::Arachne::SkeletalTrapezoidation::filterCentral ( coord_t  max_length)
protected

Filter out small central areas.

Only used to get rid of small edges which get marked as central because of rounding errors because the region is so small.

963{
964 for (edge_t& edge : graph.edges)
965 {
966 if (isEndOfCentral(edge) && edge.to->isLocalMaximum() && !edge.to->isLocalMaximum())
967 {
968 filterCentral(edge.twin, 0, max_length);
969 }
970 }
971}
bool isEndOfCentral(const edge_t &edge) const
Definition SkeletalTrapezoidation.cpp:1693
void filterCentral(coord_t max_length)
Definition SkeletalTrapezoidation.cpp:962

References Slic3r::Arachne::HalfEdgeGraph< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::edges, filterCentral(), graph, and isEndOfCentral().

Referenced by filterCentral(), filterCentral(), and generateToolpaths().

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

◆ filterCentral() [2/2]

bool Slic3r::Arachne::SkeletalTrapezoidation::filterCentral ( edge_t starting_edge,
coord_t  traveled_dist,
coord_t  max_length 
)
protected

Filter central areas connected to starting_edge recursively.

Returns
Whether we should unmark this section marked as central, on the way back out of the recursion.
974{
975 coord_t length = (starting_edge->from->p - starting_edge->to->p).cast<int64_t>().norm();
976 if (traveled_dist + length > max_length)
977 {
978 return false;
979 }
980
981 bool should_dissolve = true; //Should we unmark this as central and propagate that?
982 for (edge_t* next_edge = starting_edge->next; next_edge && next_edge != starting_edge->twin; next_edge = next_edge->twin->next)
983 {
984 if (next_edge->data.isCentral())
985 {
986 should_dissolve &= filterCentral(next_edge, traveled_dist + length, max_length);
987 }
988 }
989
990 should_dissolve &= !starting_edge->to->isLocalMaximum(); // Don't filter central regions with a local maximum!
991 if (should_dissolve)
992 {
993 starting_edge->data.setIsCentral(false);
994 starting_edge->twin->data.setIsCentral(false);
995 }
996 return should_dissolve;
997}

References Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::data, filterCentral(), Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::from, Slic3r::Arachne::STHalfEdgeNode::isLocalMaximum(), Slic3r::length(), Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::next, Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::p, Slic3r::Arachne::SkeletalTrapezoidationEdge::setIsCentral(), Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::to, and Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::twin.

+ Here is the call graph for this function:

◆ filterEndOfCentralTransition()

bool Slic3r::Arachne::SkeletalTrapezoidation::filterEndOfCentralTransition ( edge_t edge_to_start,
coord_t  traveled_dist,
coord_t  max_dist,
coord_t  replacing_bead_count 
)
protected

Change the bead count if the given edge is at the end of a central region.

This is necessary to provide a transitioning bead count to the edges of a central region to transition more smoothly from a high bead count in the central region to a lower bead count at the edge.

Parameters
edge_to_startOne edge from a zone that needs to be filtered.
traveled_distThe distance along the edges we've traveled so far.
max_distanceDon't filter beyond this range.
replacing_bead_countThe new bead count for this region.
Returns
true if the bead count of this edge was changed.
1369{
1370 if (traveled_dist > max_dist)
1371 {
1372 return false;
1373 }
1374
1375 bool is_end_of_central = true;
1376 bool should_dissolve = false;
1377 for (edge_t* next_edge = edge_to_start->next; next_edge && next_edge != edge_to_start->twin; next_edge = next_edge->twin->next)
1378 {
1379 if (next_edge->data.isCentral())
1380 {
1381 coord_t length = (next_edge->to->p - next_edge->from->p).cast<int64_t>().norm();
1382 should_dissolve |= filterEndOfCentralTransition(next_edge, traveled_dist + length, max_dist, replacing_bead_count);
1383 is_end_of_central = false;
1384 }
1385 }
1386 if (is_end_of_central && traveled_dist < max_dist)
1387 {
1388 should_dissolve = true;
1389 }
1390
1391 if (should_dissolve)
1392 {
1393 edge_to_start->to->data.bead_count = replacing_bead_count;
1394 }
1395 return should_dissolve;
1396}
bool filterEndOfCentralTransition(edge_t *edge_to_start, coord_t traveled_dist, coord_t max_dist, coord_t replacing_bead_count)
Definition SkeletalTrapezoidation.cpp:1368

References Slic3r::Arachne::SkeletalTrapezoidationJoint::bead_count, Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::data, filterEndOfCentralTransition(), Slic3r::length(), Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::next, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::to, and Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::twin.

Referenced by filterEndOfCentralTransition(), and filterTransitionMids().

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

◆ filterNoncentralRegions() [1/2]

void Slic3r::Arachne::SkeletalTrapezoidation::filterNoncentralRegions ( )
protected

Add central regions and set bead counts where there is an end of the central area and when traveling upward we get to another region with the same bead count.

1043{
1044 for (edge_t& edge : graph.edges)
1045 {
1046 if (!isEndOfCentral(edge))
1047 {
1048 continue;
1049 }
1050 if(edge.to->data.bead_count < 0 && edge.to->data.distance_to_boundary != 0)
1051 {
1052 BOOST_LOG_TRIVIAL(warning) << "Encountered an uninitialized bead at the boundary!";
1053 }
1054 assert(edge.to->data.bead_count >= 0 || edge.to->data.distance_to_boundary == 0);
1055 constexpr coord_t max_dist = scaled<coord_t>(0.4);
1056 filterNoncentralRegions(&edge, edge.to->data.bead_count, 0, max_dist);
1057 }
1058}
void filterNoncentralRegions()
Definition SkeletalTrapezoidation.cpp:1042

References Slic3r::Arachne::HalfEdgeGraph< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::edges, filterNoncentralRegions(), graph, and isEndOfCentral().

Referenced by filterNoncentralRegions(), filterNoncentralRegions(), and generateToolpaths().

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

◆ filterNoncentralRegions() [2/2]

bool Slic3r::Arachne::SkeletalTrapezoidation::filterNoncentralRegions ( edge_t to_edge,
coord_t  bead_count,
coord_t  traveled_dist,
coord_t  max_dist 
)
protected

Add central regions and set bead counts for a particular edge and all of its adjacent edges.

Recursive subroutine for filterNoncentralRegions().

Returns
Whether to set the bead count on the way back
1061{
1062 coord_t r = to_edge->to->data.distance_to_boundary;
1063
1064 edge_t* next_edge = to_edge->next;
1065 for (; next_edge && next_edge != to_edge->twin; next_edge = next_edge->twin->next)
1066 {
1067 if (next_edge->to->data.distance_to_boundary >= r || shorter_then(next_edge->to->p - next_edge->from->p, scaled<coord_t>(0.01)))
1068 {
1069 break; // Only walk upward
1070 }
1071 }
1072 if (next_edge == to_edge->twin || ! next_edge)
1073 {
1074 return false;
1075 }
1076
1077 const coord_t length = (next_edge->to->p - next_edge->from->p).cast<int64_t>().norm();
1078
1079 bool dissolve = false;
1080 if (next_edge->to->data.bead_count == bead_count)
1081 {
1082 dissolve = true;
1083 }
1084 else if (next_edge->to->data.bead_count < 0)
1085 {
1086 dissolve = filterNoncentralRegions(next_edge, bead_count, traveled_dist + length, max_dist);
1087 }
1088 else // Upward bead count is different
1089 {
1090 // Dissolve if two central regions with different bead count are closer together than the max_dist (= transition distance)
1091 dissolve = (traveled_dist + length < max_dist) && std::abs(next_edge->to->data.bead_count - bead_count) == 1;
1092 }
1093
1094 if (dissolve)
1095 {
1096 next_edge->data.setIsCentral(true);
1097 next_edge->twin->data.setIsCentral(true);
1098 next_edge->to->data.bead_count = beading_strategy.getOptimalBeadCount(next_edge->to->data.distance_to_boundary * 2);
1099 next_edge->to->data.transition_ratio = 0;
1100 }
1101 return dissolve; // Dissolving only depend on the one edge going upward. There cannot be multiple edges going upward.
1102}
virtual coord_t getOptimalBeadCount(coord_t thickness) const =0

References Slic3r::Arachne::SkeletalTrapezoidationJoint::bead_count, beading_strategy, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::data, Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::data, Slic3r::Arachne::SkeletalTrapezoidationJoint::distance_to_boundary, filterNoncentralRegions(), Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::from, Slic3r::Arachne::BeadingStrategy::getOptimalBeadCount(), Slic3r::length(), Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::next, Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::p, Slic3r::Arachne::SkeletalTrapezoidationEdge::setIsCentral(), Slic3r::shorter_then(), Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::to, Slic3r::Arachne::SkeletalTrapezoidationJoint::transition_ratio, and Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::twin.

+ Here is the call graph for this function:

◆ filterOuterCentral()

void Slic3r::Arachne::SkeletalTrapezoidation::filterOuterCentral ( )
protected

Unmark the outermost edges directly connected to the outline, as not being central.

Only used to emulate some related literature.

The paper shows that this function is bad for the stability of the framework.

1000{
1001 for (edge_t& edge : graph.edges)
1002 {
1003 if (!edge.prev)
1004 {
1005 edge.data.setIsCentral(false);
1006 edge.twin->data.setIsCentral(false);
1007 }
1008 }
1009}

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

Referenced by generateToolpaths().

+ Here is the caller graph for this function:

◆ filterTransitionMids()

void Slic3r::Arachne::SkeletalTrapezoidation::filterTransitionMids ( )
protected

Removes some transition middle points.

Transitions can be removed if there are multiple intersecting transitions that are too close together. If transitions have opposite effects, both are removed.

1223{
1224 for (edge_t& edge : graph.edges)
1225 {
1226 if (! edge.data.hasTransitions())
1227 {
1228 continue;
1229 }
1230 auto& transitions = *edge.data.getTransitions();
1231
1232 // This is how stuff should be stored in transitions
1233 assert(transitions.front().lower_bead_count <= transitions.back().lower_bead_count);
1234 assert(edge.from->data.distance_to_boundary <= edge.to->data.distance_to_boundary);
1235
1236 const Point a = edge.from->p;
1237 const Point b = edge.to->p;
1238 Point ab = b - a;
1239 coord_t ab_size = ab.cast<int64_t>().norm();
1240
1241 bool going_up = true;
1242 std::list<TransitionMidRef> to_be_dissolved_back = dissolveNearbyTransitions(&edge, transitions.back(), ab_size - transitions.back().pos, transition_filter_dist, going_up);
1243 bool should_dissolve_back = !to_be_dissolved_back.empty();
1244 for (TransitionMidRef& ref : to_be_dissolved_back)
1245 {
1246 dissolveBeadCountRegion(&edge, transitions.back().lower_bead_count + 1, transitions.back().lower_bead_count);
1247 ref.edge->data.getTransitions()->erase(ref.transition_it);
1248 }
1249
1250 {
1251 coord_t trans_bead_count = transitions.back().lower_bead_count;
1252 coord_t upper_transition_half_length = (1.0 - beading_strategy.getTransitionAnchorPos(trans_bead_count)) * beading_strategy.getTransitioningLength(trans_bead_count);
1253 should_dissolve_back |= filterEndOfCentralTransition(&edge, ab_size - transitions.back().pos, upper_transition_half_length, trans_bead_count);
1254 }
1255
1256 if (should_dissolve_back)
1257 {
1258 transitions.pop_back();
1259 }
1260 if (transitions.empty())
1261 { // FilterEndOfCentralTransition gives inconsistent new bead count when executing for the same transition in two directions.
1262 continue;
1263 }
1264
1265 going_up = false;
1266 std::list<TransitionMidRef> to_be_dissolved_front = dissolveNearbyTransitions(edge.twin, transitions.front(), transitions.front().pos, transition_filter_dist, going_up);
1267 bool should_dissolve_front = !to_be_dissolved_front.empty();
1268 for (TransitionMidRef& ref : to_be_dissolved_front)
1269 {
1270 dissolveBeadCountRegion(edge.twin, transitions.front().lower_bead_count, transitions.front().lower_bead_count + 1);
1271 ref.edge->data.getTransitions()->erase(ref.transition_it);
1272 }
1273
1274 {
1275 coord_t trans_bead_count = transitions.front().lower_bead_count;
1276 coord_t lower_transition_half_length = beading_strategy.getTransitionAnchorPos(trans_bead_count) * beading_strategy.getTransitioningLength(trans_bead_count);
1277 should_dissolve_front |= filterEndOfCentralTransition(edge.twin, transitions.front().pos, lower_transition_half_length, trans_bead_count + 1);
1278 }
1279
1280 if (should_dissolve_front)
1281 {
1282 transitions.pop_front();
1283 }
1284 if (transitions.empty())
1285 { // FilterEndOfCentralTransition gives inconsistent new bead count when executing for the same transition in two directions.
1286 continue;
1287 }
1288 }
1289}
virtual float getTransitionAnchorPos(coord_t lower_bead_count) const
Definition BeadingStrategy.cpp:38

References beading_strategy, dissolveBeadCountRegion(), dissolveNearbyTransitions(), Slic3r::Arachne::HalfEdgeGraph< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::edges, filterEndOfCentralTransition(), Slic3r::Arachne::BeadingStrategy::getTransitionAnchorPos(), Slic3r::Arachne::BeadingStrategy::getTransitioningLength(), graph, and transition_filter_dist.

Referenced by generateTransitioningRibs().

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

◆ generateAllTransitionEnds()

void Slic3r::Arachne::SkeletalTrapezoidation::generateAllTransitionEnds ( ptr_vector_t< std::list< TransitionEnd > > &  edge_transition_ends)
protected

Generate the endpoints of all transitions for all edges in the graph.

Parameters
[out]edge_transition_endsThe resulting transition endpoints.
1399{
1400 for (edge_t& edge : graph.edges)
1401 {
1402 if (! edge.data.hasTransitions())
1403 {
1404 continue;
1405 }
1406 auto& transition_positions = *edge.data.getTransitions();
1407
1408 assert(edge.from->data.distance_to_boundary <= edge.to->data.distance_to_boundary);
1409 for (TransitionMiddle& transition_middle : transition_positions)
1410 {
1411 assert(transition_positions.front().pos <= transition_middle.pos);
1412 assert(transition_middle.pos <= transition_positions.back().pos);
1413 generateTransitionEnds(edge, transition_middle.pos, transition_middle.lower_bead_count, edge_transition_ends);
1414 }
1415 }
1416}
SkeletalTrapezoidationEdge::TransitionMiddle TransitionMiddle
Definition SkeletalTrapezoidation.hpp:60
void generateTransitionEnds(edge_t &edge, coord_t mid_R, coord_t transition_lower_bead_count, ptr_vector_t< std::list< TransitionEnd > > &edge_transition_ends)
Definition SkeletalTrapezoidation.cpp:1418

References Slic3r::Arachne::HalfEdgeGraph< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::edges, generateTransitionEnds(), and graph.

Referenced by generateTransitioningRibs().

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

◆ generateExtraRibs()

void Slic3r::Arachne::SkeletalTrapezoidation::generateExtraRibs ( )
protected

Create extra ribs in the graph where the graph contains a parabolic arc or a straight between two inner corners.

There might be transitions there as the beads go through a narrow bottleneck in the polygon.

1715{
1716 for (auto edge_it = graph.edges.begin(); edge_it != graph.edges.end(); ++edge_it)
1717 {
1718 edge_t& edge = *edge_it;
1719
1720 if (!edge.data.isCentral()
1721 || shorter_then(edge.to->p - edge.from->p, discretization_step_size)
1722 || edge.from->data.distance_to_boundary >= edge.to->data.distance_to_boundary)
1723 {
1724 continue;
1725 }
1726
1727
1728 std::vector<coord_t> rib_thicknesses = beading_strategy.getNonlinearThicknesses(edge.from->data.bead_count);
1729
1730 if (rib_thicknesses.empty()) continue;
1731
1732 // Preload some variables before [edge] gets changed
1733 node_t* from = edge.from;
1734 node_t* to = edge.to;
1735 Point a = from->p;
1736 Point b = to->p;
1737 Point ab = b - a;
1738 coord_t ab_size = ab.cast<int64_t>().norm();
1739 coord_t a_R = edge.from->data.distance_to_boundary;
1740 coord_t b_R = edge.to->data.distance_to_boundary;
1741
1742 edge_t* last_edge_replacing_input = &edge;
1743 for (coord_t rib_thickness : rib_thicknesses)
1744 {
1745 if (rib_thickness / 2 <= a_R)
1746 {
1747 continue;
1748 }
1749 if (rib_thickness / 2 >= b_R)
1750 {
1751 break;
1752 }
1753
1754 coord_t new_node_bead_count = std::min(edge.from->data.bead_count, edge.to->data.bead_count);
1755 coord_t end_pos = int64_t(ab_size) * int64_t(rib_thickness / 2 - a_R) / int64_t(b_R - a_R);
1756 assert(end_pos > 0);
1757 assert(end_pos < ab_size);
1758 node_t* close_node = (end_pos < ab_size / 2)? from : to;
1759 if ((end_pos < snap_dist || end_pos > ab_size - snap_dist)
1760 && close_node->data.bead_count == new_node_bead_count
1761 )
1762 {
1763 assert(end_pos <= ab_size);
1764 close_node->data.transition_ratio = 0;
1765 continue;
1766 }
1767 Point mid = a + normal(ab, end_pos);
1768
1769 assert(last_edge_replacing_input->data.isCentral());
1770 assert(last_edge_replacing_input->data.type != SkeletalTrapezoidationEdge::EdgeType::EXTRA_VD);
1771 last_edge_replacing_input = graph.insertNode(last_edge_replacing_input, mid, new_node_bead_count);
1772 assert(last_edge_replacing_input->data.type != SkeletalTrapezoidationEdge::EdgeType::EXTRA_VD);
1773 assert(last_edge_replacing_input->data.isCentral());
1774 }
1775 }
1776}
virtual std::vector< coord_t > getNonlinearThicknesses(coord_t lower_bead_count) const
Definition BeadingStrategy.cpp:46

References Slic3r::Arachne::SkeletalTrapezoidationJoint::bead_count, beading_strategy, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::data, Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::data, discretization_step_size, Slic3r::Arachne::SkeletalTrapezoidationJoint::distance_to_boundary, Slic3r::Arachne::HalfEdgeGraph< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::edges, Slic3r::Arachne::SkeletalTrapezoidationEdge::EXTRA_VD, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::from, Slic3r::Arachne::BeadingStrategy::getNonlinearThicknesses(), graph, Slic3r::Arachne::SkeletalTrapezoidationGraph::insertNode(), Slic3r::Arachne::SkeletalTrapezoidationEdge::isCentral(), Slic3r::Arachne::normal(), Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::p, Slic3r::shorter_then(), snap_dist, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::to, Slic3r::Arachne::SkeletalTrapezoidationJoint::transition_ratio, and Slic3r::Arachne::SkeletalTrapezoidationEdge::type.

Referenced by generateToolpaths().

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

◆ generateJunctions()

void Slic3r::Arachne::SkeletalTrapezoidation::generateJunctions ( ptr_vector_t< BeadingPropagation > &  node_beadings,
ptr_vector_t< LineJunctions > &  edge_junctions 
)
protected

generate junctions for each bone

Parameters
edge_to_junctionsjunctions ordered high R to low R
2083{
2084 for (edge_t& edge_ : graph.edges)
2085 {
2086 edge_t* edge = &edge_;
2087 if (edge->from->data.distance_to_boundary > edge->to->data.distance_to_boundary)
2088 { // Only consider the upward half-edges
2089 continue;
2090 }
2091
2092 coord_t start_R = edge->to->data.distance_to_boundary; // higher R
2093 coord_t end_R = edge->from->data.distance_to_boundary; // lower R
2094
2095 if ((edge->from->data.bead_count == edge->to->data.bead_count && edge->from->data.bead_count >= 0)
2096 || end_R >= start_R)
2097 { // No beads to generate
2098 continue;
2099 }
2100
2101 Beading* beading = &getOrCreateBeading(edge->to, node_beadings)->beading;
2102 edge_junctions.emplace_back(std::make_shared<LineJunctions>());
2103 edge_.data.setExtrusionJunctions(edge_junctions.back()); // initialization
2104 LineJunctions& ret = *edge_junctions.back();
2105
2106 assert(beading->total_thickness >= edge->to->data.distance_to_boundary * 2);
2107 if(beading->total_thickness < edge->to->data.distance_to_boundary * 2)
2108 {
2109 BOOST_LOG_TRIVIAL(warning) << "Generated junction is beyond the center of total width.";
2110 }
2111
2112 Point a = edge->to->p;
2113 Point b = edge->from->p;
2114 Point ab = b - a;
2115
2116 const size_t num_junctions = beading->toolpath_locations.size();
2117 size_t junction_idx;
2118 // Compute starting junction_idx for this segment
2119 for (junction_idx = (std::max(size_t(1), beading->toolpath_locations.size()) - 1) / 2; junction_idx < num_junctions; junction_idx--)
2120 {
2121 coord_t bead_R = beading->toolpath_locations[junction_idx];
2122 // toolpath_locations computed inside DistributedBeadingStrategy could be off by 1 because of rounding errors.
2123 // In GH issue #8472, these roundings errors caused missing the middle extrusion.
2124 // Adding small epsilon should help resolve those cases.
2125 if (bead_R <= start_R + 1)
2126 { // Junction coinciding with start node is used in this function call
2127 break;
2128 }
2129 }
2130
2131 // Robustness against odd segments which might lie just slightly outside of the range due to rounding errors
2132 // not sure if this is really needed (TODO)
2133 if (junction_idx + 1 < num_junctions
2134 && beading->toolpath_locations[junction_idx + 1] <= start_R + scaled<coord_t>(0.005)
2135 && beading->total_thickness < start_R + scaled<coord_t>(0.005)
2136 )
2137 {
2138 junction_idx++;
2139 }
2140
2141 for (; junction_idx < num_junctions; junction_idx--) //When junction_idx underflows, it'll be more than num_junctions too.
2142 {
2143 coord_t bead_R = beading->toolpath_locations[junction_idx];
2144 assert(bead_R >= 0);
2145 if (bead_R < end_R)
2146 { // Junction coinciding with a node is handled by the next segment
2147 break;
2148 }
2149 Point junction(a + (ab.cast<int64_t>() * int64_t(bead_R - start_R) / int64_t(end_R - start_R)).cast<coord_t>());
2150 if (bead_R > start_R - scaled<coord_t>(0.005))
2151 { // Snap to start node if it is really close, in order to be able to see 3-way intersection later on more robustly
2152 junction = a;
2153 }
2154 ret.emplace_back(junction, beading->bead_widths[junction_idx], junction_idx);
2155 }
2156 }
2157}
node_t * to
Definition HalfEdge.hpp:28
std::shared_ptr< BeadingPropagation > getOrCreateBeading(node_t *node, ptr_vector_t< BeadingPropagation > &node_beadings)
Definition SkeletalTrapezoidation.cpp:2159
BeadingStrategy::Beading Beading
Definition SkeletalTrapezoidation.hpp:58

References Slic3r::Arachne::SkeletalTrapezoidationJoint::bead_count, Slic3r::Arachne::BeadingStrategy::Beading::bead_widths, Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::data, Slic3r::Arachne::SkeletalTrapezoidationJoint::distance_to_boundary, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::from, Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::p, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::to, Slic3r::Arachne::BeadingStrategy::Beading::toolpath_locations, and Slic3r::Arachne::BeadingStrategy::Beading::total_thickness.

◆ generateLocalMaximaSingleBeads()

void Slic3r::Arachne::SkeletalTrapezoidation::generateLocalMaximaSingleBeads ( )
protected

Genrate small segments for local maxima where the beading would only result in a single bead

2412{
2413 std::vector<VariableWidthLines> &generated_toolpaths = *p_generated_toolpaths;
2414
2415 for (auto& node : graph.nodes)
2416 {
2417 if (! node.data.hasBeading())
2418 {
2419 continue;
2420 }
2421 Beading& beading = node.data.getBeading()->beading;
2422 if (beading.bead_widths.size() % 2 == 1 && node.isLocalMaximum(true) && !node.isCentral())
2423 {
2424 const size_t inset_index = beading.bead_widths.size() / 2;
2425 constexpr bool is_odd = true;
2426 if (inset_index >= generated_toolpaths.size())
2427 {
2428 generated_toolpaths.resize(inset_index + 1);
2429 }
2430 generated_toolpaths[inset_index].emplace_back(inset_index, is_odd);
2431 ExtrusionLine& line = generated_toolpaths[inset_index].back();
2432 const coord_t width = beading.bead_widths[inset_index];
2433 // total area to be extruded is pi*(w/2)^2 = pi*w*w/4
2434 // Width a constant extrusion width w, that would be a length of pi*w/4
2435 // If we make a small circle to fill up the hole, then that circle would have a circumference of 2*pi*r
2436 // So our circle needs to be such that r=w/8
2437 const coord_t r = width / 8;
2438 constexpr coord_t n_segments = 6;
2439 for (coord_t segment = 0; segment < n_segments; segment++) {
2440 float a = 2.0 * M_PI / n_segments * segment;
2441 line.junctions.emplace_back(node.p + Point(r * cos(a), r * sin(a)), width, inset_index);
2442 }
2443 }
2444 }
2445}
EIGEN_DEVICE_FUNC const CosReturnType cos() const
Definition ArrayCwiseUnaryOps.h:202
EIGEN_DEVICE_FUNC const SinReturnType sin() const
Definition ArrayCwiseUnaryOps.h:220
EIGEN_DEVICE_FUNC SegmentReturnType segment(Index start, Index n)
This is the const version of segment(Index,Index).
Definition BlockMethods.h:888
std::vector< coord_t > bead_widths
Definition BeadingStrategy.hpp:34
coord_t width(const BoundingBox &box)
Definition Arrange.cpp:539

References Slic3r::Arachne::ExtrusionLine::back(), Slic3r::Arachne::BeadingStrategy::Beading::bead_widths, cos(), Slic3r::Arachne::ExtrusionLine::junctions, M_PI, segment(), and sin().

+ Here is the call graph for this function:

◆ generateSegments()

void Slic3r::Arachne::SkeletalTrapezoidation::generateSegments ( )
protected
Parameters
[out]segmentsthe generated segments
1787{
1788 std::vector<edge_t*> upward_quad_mids;
1789 for (edge_t& edge : graph.edges)
1790 {
1791 if (edge.prev && edge.next && edge.isUpward())
1792 {
1793 upward_quad_mids.emplace_back(&edge);
1794 }
1795 }
1796
1797 std::sort(upward_quad_mids.begin(), upward_quad_mids.end(), [](edge_t* a, edge_t* b)
1798 {
1799 if (a->to->data.distance_to_boundary == b->to->data.distance_to_boundary)
1800 { // Ordering between two 'upward' edges of the same distance is important when one of the edges is flat and connected to the other
1801 if (a->from->data.distance_to_boundary == a->to->data.distance_to_boundary
1802 && b->from->data.distance_to_boundary == b->to->data.distance_to_boundary)
1803 {
1804 coord_t max = std::numeric_limits<coord_t>::max();
1805 coord_t a_dist_from_up = std::min(a->distToGoUp().value_or(max), a->twin->distToGoUp().value_or(max)) - (a->to->p - a->from->p).cast<int64_t>().norm();
1806 coord_t b_dist_from_up = std::min(b->distToGoUp().value_or(max), b->twin->distToGoUp().value_or(max)) - (b->to->p - b->from->p).cast<int64_t>().norm();
1807 return a_dist_from_up < b_dist_from_up;
1808 }
1809 else if (a->from->data.distance_to_boundary == a->to->data.distance_to_boundary)
1810 {
1811 return true; // Edge a might be 'above' edge b
1812 }
1813 else if (b->from->data.distance_to_boundary == b->to->data.distance_to_boundary)
1814 {
1815 return false; // Edge b might be 'above' edge a
1816 }
1817 else
1818 {
1819 // Ordering is not important
1820 }
1821 }
1822 return a->to->data.distance_to_boundary > b->to->data.distance_to_boundary;
1823 });
1824
1825 ptr_vector_t<BeadingPropagation> node_beadings;
1826 { // Store beading
1827 for (node_t& node : graph.nodes)
1828 {
1829 if (node.data.bead_count <= 0)
1830 {
1831 continue;
1832 }
1833 if (node.data.transition_ratio == 0)
1834 {
1835 node_beadings.emplace_back(new BeadingPropagation(beading_strategy.compute(node.data.distance_to_boundary * 2, node.data.bead_count)));
1836 node.data.setBeading(node_beadings.back());
1837 assert(node_beadings.back()->beading.total_thickness == node.data.distance_to_boundary * 2);
1838 if(node_beadings.back()->beading.total_thickness != node.data.distance_to_boundary * 2)
1839 {
1840 BOOST_LOG_TRIVIAL(warning) << "If transitioning to an endpoint (ratio 0), the node should be exactly in the middle.";
1841 }
1842 }
1843 else
1844 {
1845 Beading low_count_beading = beading_strategy.compute(node.data.distance_to_boundary * 2, node.data.bead_count);
1846 Beading high_count_beading = beading_strategy.compute(node.data.distance_to_boundary * 2, node.data.bead_count + 1);
1847 Beading merged = interpolate(low_count_beading, 1.0 - node.data.transition_ratio, high_count_beading);
1848 node_beadings.emplace_back(new BeadingPropagation(merged));
1849 node.data.setBeading(node_beadings.back());
1850 assert(merged.total_thickness == node.data.distance_to_boundary * 2);
1851 if(merged.total_thickness != node.data.distance_to_boundary * 2)
1852 {
1853 BOOST_LOG_TRIVIAL(warning) << "If merging two beads, the new bead must be exactly in the middle.";
1854 }
1855 }
1856 }
1857 }
1858
1859#ifdef ARACHNE_DEBUG
1860 static int iRun = 0;
1861 export_graph_to_svg(debug_out_path("ST-generateSegments-before-propagation-%d.svg", iRun), this->graph, this->outline);
1862#endif
1863
1864 propagateBeadingsUpward(upward_quad_mids, node_beadings);
1865
1866#ifdef ARACHNE_DEBUG
1867 export_graph_to_svg(debug_out_path("ST-generateSegments-upward-propagation-%d.svg", iRun), this->graph, this->outline);
1868#endif
1869
1870 propagateBeadingsDownward(upward_quad_mids, node_beadings);
1871
1872#ifdef ARACHNE_DEBUG
1873 export_graph_to_svg(debug_out_path("ST-generateSegments-downward-propagation-%d.svg", iRun), this->graph, this->outline);
1874#endif
1875
1876 ptr_vector_t<LineJunctions> edge_junctions; // junctions ordered high R to low R
1877 generateJunctions(node_beadings, edge_junctions);
1878
1879#ifdef ARACHNE_DEBUG
1880 export_graph_to_svg(debug_out_path("ST-generateSegments-junctions-%d.svg", iRun), this->graph, this->outline, edge_junctions);
1881#endif
1882
1883 connectJunctions(edge_junctions);
1884
1886
1887#ifdef ARACHNE_DEBUG
1888 ++iRun;
1889#endif
1890}
virtual Beading compute(coord_t thickness, coord_t bead_count) const =0
void propagateBeadingsUpward(std::vector< edge_t * > &upward_quad_mids, ptr_vector_t< BeadingPropagation > &node_beadings)
Definition SkeletalTrapezoidation.cpp:1917
void connectJunctions(ptr_vector_t< LineJunctions > &edge_junctions)
Definition SkeletalTrapezoidation.cpp:2289
void generateLocalMaximaSingleBeads()
Definition SkeletalTrapezoidation.cpp:2411
void propagateBeadingsDownward(std::vector< edge_t * > &upward_quad_mids, ptr_vector_t< BeadingPropagation > &node_beadings)
Definition SkeletalTrapezoidation.cpp:1946
Beading interpolate(const Beading &left, double ratio_left_to_whole, const Beading &right, coord_t switching_radius) const
Definition SkeletalTrapezoidation.cpp:2018
SkeletalTrapezoidationJoint::BeadingPropagation BeadingPropagation
Definition SkeletalTrapezoidation.hpp:59
void generateJunctions(ptr_vector_t< BeadingPropagation > &node_beadings, ptr_vector_t< LineJunctions > &edge_junctions)
Definition SkeletalTrapezoidation.cpp:2082

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

Referenced by generateToolpaths().

+ Here is the caller graph for this function:

◆ generateToolpaths()

void Slic3r::Arachne::SkeletalTrapezoidation::generateToolpaths ( std::vector< VariableWidthLines > &  generated_toolpaths,
bool  filter_outermost_central_edges = false 
)

Generate the paths that the printer must extrude, to print the outlines in the input polygons.

Parameters
filter_outermost_central_edgesSome edges are "central" but still touch the outside of the polygon. If enabled, don't treat these as "central" but as if it's a obtuse corner. As a result, sharp corners will no longer end in a single line but will just loop.
855{
856#ifdef ARACHNE_DEBUG
857 static int iRun = 0;
858#endif
859
860 p_generated_toolpaths = &generated_toolpaths;
861
863
864#ifdef ARACHNE_DEBUG
865 export_graph_to_svg(debug_out_path("ST-updateIsCentral-final-%d.svg", iRun), this->graph, this->outline);
866#endif
867
869
870#ifdef ARACHNE_DEBUG
871 export_graph_to_svg(debug_out_path("ST-filterCentral-final-%d.svg", iRun), this->graph, this->outline);
872#endif
873
874 if (filter_outermost_central_edges)
876
878
879#ifdef ARACHNE_DEBUG
880 export_graph_to_svg(debug_out_path("ST-updateBeadCount-final-%d.svg", iRun), this->graph, this->outline);
881#endif
882
884
885#ifdef ARACHNE_DEBUG
886 export_graph_to_svg(debug_out_path("ST-filterNoncentralRegions-final-%d.svg", iRun), this->graph, this->outline);
887#endif
888
890
891#ifdef ARACHNE_DEBUG
892 export_graph_to_svg(debug_out_path("ST-generateTransitioningRibs-final-%d.svg", iRun), this->graph, this->outline);
893#endif
894
896
897#ifdef ARACHNE_DEBUG
898 export_graph_to_svg(debug_out_path("ST-generateExtraRibs-final-%d.svg", iRun), this->graph, this->outline);
899#endif
900
902
903#ifdef ARACHNE_DEBUG
904 export_graph_to_svg(debug_out_path("ST-generateSegments-final-%d.svg", iRun), this->graph, this->outline);
905#endif
906
907#ifdef ARACHNE_DEBUG
908 ++iRun;
909#endif
910}
void generateTransitioningRibs()
Definition SkeletalTrapezoidation.cpp:1104
void generateSegments()
Definition SkeletalTrapezoidation.cpp:1786
void filterOuterCentral()
Definition SkeletalTrapezoidation.cpp:999
void updateBeadCount()
Definition SkeletalTrapezoidation.cpp:1011
static constexpr coord_t central_filter_dist
Filter areas marked as 'central' smaller than this.
Definition SkeletalTrapezoidation.hpp:71
void generateExtraRibs()
Definition SkeletalTrapezoidation.cpp:1714
void updateIsCentral()
Definition SkeletalTrapezoidation.cpp:912

References central_filter_dist, Slic3r::debug_out_path(), filterCentral(), filterNoncentralRegions(), filterOuterCentral(), generateExtraRibs(), generateSegments(), generateTransitioningRibs(), p_generated_toolpaths, updateBeadCount(), and updateIsCentral().

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

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

◆ generateTransitionEnd()

bool Slic3r::Arachne::SkeletalTrapezoidation::generateTransitionEnd ( edge_t edge,
coord_t  start_pos,
coord_t  end_pos,
coord_t  transition_half_length,
double  start_rest,
double  end_rest,
coord_t  transition_lower_bead_count,
ptr_vector_t< std::list< TransitionEnd > > &  edge_transition_ends 
)
protected

Compute a single endpoint of a transition.

Parameters
edgeThe edge to generate the endpoint for.
start_posThe position where the transition starts.
end_posThe position where the transition ends on the other side.
transition_half_lengthThe distance to the transition middle point.
start_restThe gap between the start of the transition and the starting endpoint, as ratio of the inner bead width at the high end of the transition.
end_restThe gap between the end of the transition and the ending endpoint, as ratio of the inner bead width at the high end of the transition.
transition_lower_bead_countThe bead count at the lower end of the transition.
[out]edge_transition_endsThe list to put the resulting endpoints in.
Returns
Whether the given edge is going downward (i.e. towards a thinner region of the polygon).
1456{
1457 Point a = edge.from->p;
1458 Point b = edge.to->p;
1459 Point ab = b - a;
1460 coord_t ab_size = ab.cast<int64_t>().norm(); // TODO: prevent recalculation of these values
1461
1462 assert(start_pos <= ab_size);
1463 if(start_pos > ab_size)
1464 {
1465 BOOST_LOG_TRIVIAL(warning) << "Start position of edge is beyond edge range.";
1466 }
1467
1468 bool going_up = end_rest > start_rest;
1469
1470 assert(edge.data.isCentral());
1471 if (!edge.data.isCentral())
1472 {
1473 BOOST_LOG_TRIVIAL(warning) << "This function shouldn't generate ends in or beyond non-central regions.";
1474 return false;
1475 }
1476
1477 if (end_pos > ab_size)
1478 { // Recurse on all further edges
1479 float rest = end_rest - (start_rest - end_rest) * (end_pos - ab_size) / (start_pos - end_pos);
1480 assert(rest >= 0);
1481 assert(rest <= std::max(end_rest, start_rest));
1482 assert(rest >= std::min(end_rest, start_rest));
1483
1484 coord_t central_edge_count = 0;
1485 for (edge_t* outgoing = edge.next; outgoing && outgoing != edge.twin; outgoing = outgoing->twin->next)
1486 {
1487 if (!outgoing->data.isCentral()) continue;
1488 central_edge_count++;
1489 }
1490
1491 bool is_only_going_down = true;
1492 bool has_recursed = false;
1493 for (edge_t* outgoing = edge.next; outgoing && outgoing != edge.twin;)
1494 {
1495 edge_t* next = outgoing->twin->next; // Before we change the outgoing edge itself
1496 if (!outgoing->data.isCentral())
1497 {
1498 outgoing = next;
1499 continue; // Don't put transition ends in non-central regions
1500 }
1501 if (central_edge_count > 1 && going_up && isGoingDown(outgoing, 0, end_pos - ab_size + transition_half_length, lower_bead_count))
1502 { // We're after a 3-way_all-central_junction-node and going in the direction of lower bead count
1503 // don't introduce a transition end along this central direction, because this direction is the downward direction
1504 // while we are supposed to be [going_up]
1505 outgoing = next;
1506 continue;
1507 }
1508 bool is_going_down = generateTransitionEnd(*outgoing, 0, end_pos - ab_size, transition_half_length, rest, end_rest, lower_bead_count, edge_transition_ends);
1509 is_only_going_down &= is_going_down;
1510 outgoing = next;
1511 has_recursed = true;
1512 }
1513 if (!going_up || (has_recursed && !is_only_going_down))
1514 {
1515 edge.to->data.transition_ratio = rest;
1516 edge.to->data.bead_count = lower_bead_count;
1517 }
1518 return is_only_going_down;
1519 }
1520 else // end_pos < ab_size
1521 { // Add transition end point here
1522 bool is_lower_end = end_rest == 0; // TODO collapse this parameter into the bool for which it is used here!
1523 coord_t pos = -1;
1524
1525 edge_t* upward_edge = nullptr;
1526 if (edge.isUpward())
1527 {
1528 upward_edge = &edge;
1529 pos = end_pos;
1530 }
1531 else
1532 {
1533 upward_edge = edge.twin;
1534 pos = ab_size - end_pos;
1535 }
1536
1537 if(!upward_edge->data.hasTransitionEnds())
1538 {
1539 //This edge doesn't have a data structure yet for the transition ends. Make one.
1540 edge_transition_ends.emplace_back(std::make_shared<std::list<TransitionEnd>>());
1541 upward_edge->data.setTransitionEnds(edge_transition_ends.back());
1542 }
1543 auto transitions = upward_edge->data.getTransitionEnds();
1544
1545 //Add a transition to it (on the correct side).
1546 assert(ab_size == (edge.twin->from->p - edge.twin->to->p).cast<int64_t>().norm());
1547 assert(pos <= ab_size);
1548 if (transitions->empty() || pos < transitions->front().pos)
1549 { // Preorder so that sorting later on is faster
1550 transitions->emplace_front(pos, lower_bead_count, is_lower_end);
1551 }
1552 else
1553 {
1554 transitions->emplace_back(pos, lower_bead_count, is_lower_end);
1555 }
1556 return false;
1557 }
1558}
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE const Scalar * data() const
Definition PlainObjectBase.h:255
bool generateTransitionEnd(edge_t &edge, coord_t start_pos, coord_t end_pos, coord_t transition_half_length, double start_rest, double end_rest, coord_t transition_lower_bead_count, ptr_vector_t< std::list< TransitionEnd > > &edge_transition_ends)
Definition SkeletalTrapezoidation.cpp:1455
bool isGoingDown(edge_t *outgoing, coord_t traveled_dist, coord_t transition_half_length, coord_t lower_bead_count) const
Definition SkeletalTrapezoidation.cpp:1561
TPoint< P > front(const P &p)
Definition geometry_traits.hpp:872

References Slic3r::Arachne::SkeletalTrapezoidationJoint::bead_count, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::data, Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::data, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::from, generateTransitionEnd(), Slic3r::Arachne::SkeletalTrapezoidationEdge::getTransitionEnds(), Slic3r::Arachne::SkeletalTrapezoidationEdge::hasTransitionEnds(), Slic3r::Arachne::SkeletalTrapezoidationEdge::isCentral(), isGoingDown(), Slic3r::Arachne::STHalfEdge::isUpward(), Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::next, Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::p, Slic3r::Arachne::SkeletalTrapezoidationEdge::setTransitionEnds(), Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::to, Slic3r::Arachne::SkeletalTrapezoidationJoint::transition_ratio, and Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::twin.

Referenced by generateTransitionEnd(), and generateTransitionEnds().

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

◆ generateTransitionEnds()

void Slic3r::Arachne::SkeletalTrapezoidation::generateTransitionEnds ( edge_t edge,
coord_t  mid_R,
coord_t  transition_lower_bead_count,
ptr_vector_t< std::list< TransitionEnd > > &  edge_transition_ends 
)
protected

Generate the endpoints of a specific transition midpoint.

Parameters
edgeThe edge to create transitions on.
mid_RThe radius of the transition middle point.
transition_lower_bead_countThe bead count at the lower end of the transition.
[out]edge_transition_endsA list of endpoints to add the new endpoints to.
1419{
1420 const Point a = edge.from->p;
1421 const Point b = edge.to->p;
1422 const Point ab = b - a;
1423 const coord_t ab_size = ab.cast<int64_t>().norm();
1424
1425 const coord_t transition_length = beading_strategy.getTransitioningLength(lower_bead_count);
1426 const float transition_mid_position = beading_strategy.getTransitionAnchorPos(lower_bead_count);
1427 constexpr float inner_bead_width_ratio_after_transition = 1.0;
1428
1429 constexpr coord_t start_rest = 0;
1430 const float mid_rest = transition_mid_position * inner_bead_width_ratio_after_transition;
1431 constexpr float end_rest = inner_bead_width_ratio_after_transition;
1432
1433 { // Lower bead count transition end
1434 const coord_t start_pos = ab_size - mid_pos;
1435 const coord_t transition_half_length = transition_mid_position * int64_t(transition_length);
1436 const coord_t end_pos = start_pos + transition_half_length;
1437 generateTransitionEnd(*edge.twin, start_pos, end_pos, transition_half_length, mid_rest, start_rest, lower_bead_count, edge_transition_ends);
1438 }
1439
1440 { // Upper bead count transition end
1441 const coord_t start_pos = mid_pos;
1442 const coord_t transition_half_length = (1.0 - transition_mid_position) * transition_length;
1443 const coord_t end_pos = mid_pos + transition_half_length;
1444#ifdef DEBUG
1445 if (! generateTransitionEnd(edge, start_pos, end_pos, transition_half_length, mid_rest, end_rest, lower_bead_count, edge_transition_ends))
1446 {
1447 BOOST_LOG_TRIVIAL(warning) << "There must have been at least one direction in which the bead count is increasing enough for the transition to happen!";
1448 }
1449#else
1450 generateTransitionEnd(edge, start_pos, end_pos, transition_half_length, mid_rest, end_rest, lower_bead_count, edge_transition_ends);
1451#endif
1452 }
1453}

References beading_strategy, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::from, generateTransitionEnd(), Slic3r::Arachne::BeadingStrategy::getTransitionAnchorPos(), Slic3r::Arachne::BeadingStrategy::getTransitioningLength(), Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::p, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::to, and Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::twin.

Referenced by generateAllTransitionEnds().

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

◆ generateTransitioningRibs()

void Slic3r::Arachne::SkeletalTrapezoidation::generateTransitioningRibs ( )
protected

Create extra edges along all edges, where it needs to transition from one bead count to another.

For example, if an edge of the graph goes from a bead count of 6 to a bead count of 1, it needs to generate 5 places where the beads around this line transition to a lower bead count. These are the "ribs". They reach from the edge to the border of the polygon. Where the beads hit those ribs the beads know to make a transition.

1105{
1106 // Store the upward edges to the transitions.
1107 // We only store the halfedge for which the distance_to_boundary is higher at the end than at the beginning.
1108 ptr_vector_t<std::list<TransitionMiddle>> edge_transitions;
1109 generateTransitionMids(edge_transitions);
1110
1111 for (edge_t& edge : graph.edges)
1112 { // Check if there is a transition in between nodes with different bead counts
1113 if (edge.data.isCentral() && edge.from->data.bead_count != edge.to->data.bead_count)
1114 {
1115 assert(edge.data.hasTransitions() || edge.twin->data.hasTransitions());
1116 }
1117 }
1118
1120
1121#ifdef ARACHNE_DEBUG
1122 static int iRun = 0;
1123 export_graph_to_svg(debug_out_path("ST-generateTransitioningRibs-mids-%d.svg", iRun++), this->graph, this->outline);
1124#endif
1125
1126 ptr_vector_t<std::list<TransitionEnd>> edge_transition_ends; // We only map the half edge in the upward direction. mapped items are not sorted
1127 generateAllTransitionEnds(edge_transition_ends);
1128
1129#ifdef ARACHNE_DEBUG
1130 export_graph_to_svg(debug_out_path("ST-generateTransitioningRibs-ends-%d.svg", iRun++), this->graph, this->outline);
1131#endif
1132
1133 applyTransitions(edge_transition_ends);
1134 // Note that the shared pointer lists will be out of scope and thus destroyed here, since the remaining refs are weak_ptr.
1135
1136#ifdef ARACHNE_DEBUG
1137 ++iRun;
1138#endif
1139}
void generateAllTransitionEnds(ptr_vector_t< std::list< TransitionEnd > > &edge_transition_ends)
Definition SkeletalTrapezoidation.cpp:1398
void generateTransitionMids(ptr_vector_t< std::list< TransitionMiddle > > &edge_transitions)
Definition SkeletalTrapezoidation.cpp:1142
void applyTransitions(ptr_vector_t< std::list< TransitionEnd > > &edge_transition_ends)
Definition SkeletalTrapezoidation.cpp:1627
void filterTransitionMids()
Definition SkeletalTrapezoidation.cpp:1222

References applyTransitions(), Slic3r::debug_out_path(), Slic3r::Arachne::HalfEdgeGraph< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::edges, filterTransitionMids(), generateAllTransitionEnds(), generateTransitionMids(), and graph.

Referenced by generateToolpaths().

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

◆ generateTransitionMids()

void Slic3r::Arachne::SkeletalTrapezoidation::generateTransitionMids ( ptr_vector_t< std::list< TransitionMiddle > > &  edge_transitions)
protected

Generate middle points of all transitions on edges.

The transition middle points are saved in the graph itself. They are also returned via the output parameter.

Parameters
[out]edge_transitionsA list of transitions that were generated.
1143{
1144 for (edge_t& edge : graph.edges)
1145 {
1146 assert(edge.data.centralIsSet());
1147 if (!edge.data.isCentral())
1148 { // Only central regions introduce transitions
1149 continue;
1150 }
1151 coord_t start_R = edge.from->data.distance_to_boundary;
1152 coord_t end_R = edge.to->data.distance_to_boundary;
1153 int start_bead_count = edge.from->data.bead_count;
1154 int end_bead_count = edge.to->data.bead_count;
1155
1156 if (start_R == end_R)
1157 { // No transitions occur when both end points have the same distance_to_boundary
1158 assert(edge.from->data.bead_count == edge.to->data.bead_count);
1159 if(edge.from->data.bead_count != edge.to->data.bead_count)
1160 {
1161 BOOST_LOG_TRIVIAL(warning) << "Bead count " << edge.from->data.bead_count << " is different from " << edge.to->data.bead_count << " even though distance to boundary is the same.";
1162 }
1163 continue;
1164 }
1165 else if (start_R > end_R)
1166 { // Only consider those half-edges which are going from a lower to a higher distance_to_boundary
1167 continue;
1168 }
1169
1170 if (edge.from->data.bead_count == edge.to->data.bead_count)
1171 { // No transitions should occur according to the enforced bead counts
1172 continue;
1173 }
1174
1175 if (start_bead_count > beading_strategy.getOptimalBeadCount(start_R * 2)
1176 || end_bead_count > beading_strategy.getOptimalBeadCount(end_R * 2))
1177 { // Wasn't the case earlier in this function because of already introduced transitions
1178 BOOST_LOG_TRIVIAL(error) << "transitioning segment overlap! (?)";
1179 }
1180 assert(start_R < end_R);
1181 if(start_R >= end_R)
1182 {
1183 BOOST_LOG_TRIVIAL(warning) << "Transitioning the wrong way around! This function expects to transition from small R to big R, but was transitioning from " << start_R << " to " << end_R;
1184 }
1185 coord_t edge_size = (edge.from->p - edge.to->p).cast<int64_t>().norm();
1186 for (int transition_lower_bead_count = start_bead_count; transition_lower_bead_count < end_bead_count; transition_lower_bead_count++)
1187 {
1188 coord_t mid_R = beading_strategy.getTransitionThickness(transition_lower_bead_count) / 2;
1189 if (mid_R > end_R)
1190 {
1191 BOOST_LOG_TRIVIAL(error) << "transition on segment lies outside of segment!";
1192 mid_R = end_R;
1193 }
1194 if (mid_R < start_R)
1195 {
1196 BOOST_LOG_TRIVIAL(error) << "transition on segment lies outside of segment!";
1197 mid_R = start_R;
1198 }
1199 coord_t mid_pos = int64_t(edge_size) * int64_t(mid_R - start_R) / int64_t(end_R - start_R);
1200
1201 assert(mid_pos >= 0);
1202 assert(mid_pos <= edge_size);
1203 if(mid_pos < 0 || mid_pos > edge_size)
1204 {
1205 BOOST_LOG_TRIVIAL(warning) << "Transition mid is out of bounds of the edge.";
1206 }
1207 auto transitions = edge.data.getTransitions();
1208 constexpr bool ignore_empty = true;
1209 assert((! edge.data.hasTransitions(ignore_empty)) || mid_pos >= transitions->back().pos);
1210 if (! edge.data.hasTransitions(ignore_empty))
1211 {
1212 edge_transitions.emplace_back(std::make_shared<std::list<TransitionMiddle>>());
1213 edge.data.setTransitions(edge_transitions.back()); // initialization
1214 transitions = edge.data.getTransitions();
1215 }
1216 transitions->emplace_back(mid_pos, transition_lower_bead_count, mid_R);
1217 }
1218 assert((edge.from->data.bead_count == edge.to->data.bead_count) || edge.data.hasTransitions());
1219 }
1220}
virtual coord_t getTransitionThickness(coord_t lower_bead_count) const
Definition BeadingStrategy.cpp:71

References beading_strategy, Slic3r::Arachne::HalfEdgeGraph< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::edges, error, Slic3r::Arachne::BeadingStrategy::getOptimalBeadCount(), Slic3r::Arachne::BeadingStrategy::getTransitionThickness(), and graph.

Referenced by generateTransitioningRibs().

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

◆ getNearestBeading()

std::shared_ptr< SkeletalTrapezoidationJoint::BeadingPropagation > Slic3r::Arachne::SkeletalTrapezoidation::getNearestBeading ( node_t node,
coord_t  max_dist 
)
protected

In case we cannot find the beading of a node, get a beading from the nearest node.

Parameters
nodeThe node to attempt to get a beading from. The actual node that the returned beading is from may be a different, nearby node.
max_distThe maximum distance to search for.
Returns
A beading for the node, or nullptr if there is no node nearby with a beading.
2202{
2203 struct DistEdge
2204 {
2205 edge_t* edge_to;
2206 coord_t dist;
2207 DistEdge(edge_t* edge_to, coord_t dist)
2208 : edge_to(edge_to), dist(dist)
2209 {}
2210 };
2211
2212 auto compare = [](const DistEdge& l, const DistEdge& r) -> bool { return l.dist > r.dist; };
2213 std::priority_queue<DistEdge, std::vector<DistEdge>, decltype(compare)> further_edges(compare);
2214 bool first = true;
2215 for (edge_t* outgoing = node->incident_edge; outgoing && (first || outgoing != node->incident_edge); outgoing = outgoing->twin->next)
2216 {
2217 further_edges.emplace(outgoing, (outgoing->to->p - outgoing->from->p).cast<int64_t>().norm());
2218 first = false;
2219 }
2220
2221 for (coord_t counter = 0; counter < SKELETAL_TRAPEZOIDATION_BEAD_SEARCH_MAX; counter++)
2222 { // Prevent endless recursion
2223 if (further_edges.empty()) return nullptr;
2224 DistEdge here = further_edges.top();
2225 further_edges.pop();
2226 if (here.dist > max_dist) return nullptr;
2227 if (here.edge_to->to->data.hasBeading())
2228 {
2229 return here.edge_to->to->data.getBeading();
2230 }
2231 else
2232 { // recurse
2233 for (edge_t* further_edge = here.edge_to->next; further_edge && further_edge != here.edge_to->twin; further_edge = further_edge->twin->next)
2234 {
2235 further_edges.emplace(further_edge, here.dist + (further_edge->to->p - further_edge->from->p).cast<int64_t>().norm());
2236 }
2237 }
2238 }
2239 return nullptr;
2240}
#define SKELETAL_TRAPEZOIDATION_BEAD_SEARCH_MAX
Definition SkeletalTrapezoidation.cpp:22
T dist(const boost::polygon::point_data< T > &p1, const boost::polygon::point_data< T > &p2)
Definition Geometry.cpp:280

References Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::incident_edge, and SKELETAL_TRAPEZOIDATION_BEAD_SEARCH_MAX.

◆ getOrCreateBeading()

std::shared_ptr< SkeletalTrapezoidationJoint::BeadingPropagation > Slic3r::Arachne::SkeletalTrapezoidation::getOrCreateBeading ( node_t node,
ptr_vector_t< BeadingPropagation > &  node_beadings 
)
protected

Get the beading at a certain node of the skeletal graph, or create one if it doesn't have one yet.

This is a lazy get.

Parameters
nodeThe node to get the beading from.
node_beadingsA list of all beadings for nodes.
Returns
The beading of that node.
2160{
2161 if (! node->data.hasBeading())
2162 {
2163 if (node->data.bead_count == -1)
2164 { // This bug is due to too small central edges
2165 constexpr coord_t nearby_dist = scaled<coord_t>(0.1);
2166 auto nearest_beading = getNearestBeading(node, nearby_dist);
2167 if (nearest_beading)
2168 {
2169 return nearest_beading;
2170 }
2171
2172 // Else make a new beading:
2173 bool has_central_edge = false;
2174 bool first = true;
2175 coord_t dist = std::numeric_limits<coord_t>::max();
2176 for (edge_t* edge = node->incident_edge; edge && (first || edge != node->incident_edge); edge = edge->twin->next)
2177 {
2178 if (edge->data.isCentral())
2179 {
2180 has_central_edge = true;
2181 }
2182 assert(edge->to->data.distance_to_boundary >= 0);
2183 dist = std::min(dist, edge->to->data.distance_to_boundary + coord_t((edge->to->p - edge->from->p).cast<int64_t>().norm()));
2184 first = false;
2185 }
2186 if (!has_central_edge)
2187 {
2188 BOOST_LOG_TRIVIAL(error) << "Unknown beading for non-central node!";
2189 }
2190 assert(dist != std::numeric_limits<coord_t>::max());
2191 node->data.bead_count = beading_strategy.getOptimalBeadCount(dist * 2);
2192 }
2193 assert(node->data.bead_count != -1);
2194 node_beadings.emplace_back(new BeadingPropagation(beading_strategy.compute(node->data.distance_to_boundary * 2, node->data.bead_count)));
2195 node->data.setBeading(node_beadings.back());
2196 }
2197 assert(node->data.hasBeading());
2198 return node->data.getBeading();
2199}
std::shared_ptr< BeadingPropagation > getNearestBeading(node_t *node, coord_t max_dist)
Definition SkeletalTrapezoidation.cpp:2201

References Slic3r::Arachne::SkeletalTrapezoidationJoint::bead_count, Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::data, Slic3r::Arachne::SkeletalTrapezoidationJoint::distance_to_boundary, error, Slic3r::Arachne::SkeletalTrapezoidationJoint::getBeading(), Slic3r::Arachne::SkeletalTrapezoidationJoint::hasBeading(), Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::incident_edge, and Slic3r::Arachne::SkeletalTrapezoidationJoint::setBeading().

+ Here is the call graph for this function:

◆ getQuadMaxRedgeTo()

SkeletalTrapezoidation::edge_t * Slic3r::Arachne::SkeletalTrapezoidation::getQuadMaxRedgeTo ( edge_t quad_start_edge)
protected

From a quad (a group of linked edges in one cell of the Voronoi), find the edge pointing to the node that is furthest away from the border of the polygon.

Parameters
quad_start_edgeThe first edge of the quad.
Returns
The edge of the quad that is furthest away from the border.
1893{
1894 assert(quad_start_edge->prev == nullptr);
1895 assert(quad_start_edge->from->data.distance_to_boundary == 0);
1896 coord_t max_R = -1;
1897 edge_t* ret = nullptr;
1898 for (edge_t* edge = quad_start_edge; edge; edge = edge->next)
1899 {
1901 if (r > max_R)
1902 {
1903 max_R = r;
1904 ret = edge;
1905 }
1906 }
1907
1908 if (!ret->next && ret->to->data.distance_to_boundary - scaled<coord_t>(0.005) < ret->from->data.distance_to_boundary)
1909 {
1910 ret = ret->prev;
1911 }
1912 assert(ret);
1913 assert(ret->next);
1914 return ret;
1915}

References Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::data, Slic3r::Arachne::SkeletalTrapezoidationJoint::distance_to_boundary, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::from, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::next, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::prev, and Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::to.

◆ interpolate() [1/2]

SkeletalTrapezoidation::Beading Slic3r::Arachne::SkeletalTrapezoidation::interpolate ( const Beading left,
double  ratio_left_to_whole,
const Beading right 
) const
protected

Subroutine of interpolate(const Beading&, Ratio, const Beading&, coord_t)

This creates a new Beading between two beadings, assuming that both have the same number of beads.

Parameters
leftOne of the beadings to interpolate between.
ratio_left_to_wholeThe position within the two beadings to sample an interpolation. Should be a ratio between 0 and 1.
rightOne of the beadings to interpolate between.
Returns
The beading at the interpolated location.
2062{
2063 assert(ratio_left_to_whole >= 0.0 && ratio_left_to_whole <= 1.0);
2064 float ratio_right_to_whole = 1.0 - ratio_left_to_whole;
2065
2066 Beading ret = (left.total_thickness > right.total_thickness)? left : right;
2067 for (size_t inset_idx = 0; inset_idx < std::min(left.bead_widths.size(), right.bead_widths.size()); inset_idx++)
2068 {
2069 if(left.bead_widths[inset_idx] == 0 || right.bead_widths[inset_idx] == 0)
2070 {
2071 ret.bead_widths[inset_idx] = 0; //0-width wall markers stay 0-width.
2072 }
2073 else
2074 {
2075 ret.bead_widths[inset_idx] = ratio_left_to_whole * left.bead_widths[inset_idx] + ratio_right_to_whole * right.bead_widths[inset_idx];
2076 }
2077 ret.toolpath_locations[inset_idx] = ratio_left_to_whole * left.toolpath_locations[inset_idx] + ratio_right_to_whole * right.toolpath_locations[inset_idx];
2078 }
2079 return ret;
2080}

References Slic3r::Arachne::BeadingStrategy::Beading::bead_widths, and Slic3r::Arachne::BeadingStrategy::Beading::toolpath_locations.

◆ interpolate() [2/2]

SkeletalTrapezoidation::Beading Slic3r::Arachne::SkeletalTrapezoidation::interpolate ( const Beading left,
double  ratio_left_to_whole,
const Beading right,
coord_t  switching_radius 
) const
protected

Find a beading in between two other beadings.

This creates a new beading. With this we can find the coordinates of the endpoints of the actual line segments to draw.

The parameters left and right are not actually always left or right but just arbitrary directions to visually indicate the difference.

Parameters
leftOne of the beadings to interpolate between.
ratio_left_to_wholeThe position within the two beadings to sample an interpolation. Should be a ratio between 0 and 1.
rightOne of the beadings to interpolate between.
switching_radiusThe bead radius at which we switch from the left beading to the merged beading, if the beadings have a different number of beads.
Returns
The beading at the interpolated location.
2019{
2020 assert(ratio_left_to_whole >= 0.0 && ratio_left_to_whole <= 1.0);
2021 Beading ret = interpolate(left, ratio_left_to_whole, right);
2022
2023 // TODO: don't use toolpath locations past the middle!
2024 // TODO: stretch bead widths and locations of the higher bead count beading to fit in the left over space
2025 coord_t next_inset_idx;
2026 for (next_inset_idx = left.toolpath_locations.size() - 1; next_inset_idx >= 0; next_inset_idx--)
2027 {
2028 if (switching_radius > left.toolpath_locations[next_inset_idx])
2029 {
2030 break;
2031 }
2032 }
2033 if (next_inset_idx < 0)
2034 { // There is no next inset, because there is only one
2035 assert(left.toolpath_locations.empty() || left.toolpath_locations.front() >= switching_radius);
2036 return ret;
2037 }
2038 if (next_inset_idx + 1 == coord_t(left.toolpath_locations.size()))
2039 { // We cant adjust to fit the next edge because there is no previous one?!
2040 return ret;
2041 }
2042 assert(next_inset_idx < coord_t(left.toolpath_locations.size()));
2043 assert(left.toolpath_locations[next_inset_idx] <= switching_radius);
2044 assert(left.toolpath_locations[next_inset_idx + 1] >= switching_radius);
2045 if (ret.toolpath_locations[next_inset_idx] > switching_radius)
2046 { // One inset disappeared between left and the merged one
2047 // solve for ratio f:
2048 // f*l + (1-f)*r = s
2049 // f*l + r - f*r = s
2050 // f*(l-r) + r = s
2051 // f*(l-r) = s - r
2052 // f = (s-r) / (l-r)
2053 float new_ratio = static_cast<float>(switching_radius - right.toolpath_locations[next_inset_idx]) / static_cast<float>(left.toolpath_locations[next_inset_idx] - right.toolpath_locations[next_inset_idx]);
2054 new_ratio = std::min(1.0, new_ratio + 0.1);
2055 return interpolate(left, new_ratio, right);
2056 }
2057 return ret;
2058}

References Slic3r::Arachne::BeadingStrategy::Beading::toolpath_locations.

◆ isEndOfCentral()

bool Slic3r::Arachne::SkeletalTrapezoidation::isEndOfCentral ( const edge_t edge) const
protected

Determines whether this edge marks the end of the central region.

Parameters
edgeThe edge to check.
Returns
true if this edge goes from a central region to a non-central region, or false in every other case (central to central, non-central to non-central, non-central to central, or end-of-the-line).
1694{
1695 if (!edge_to.data.isCentral())
1696 {
1697 return false;
1698 }
1699 if (!edge_to.next)
1700 {
1701 return true;
1702 }
1703 for (const edge_t* edge = edge_to.next; edge && edge != edge_to.twin; edge = edge->twin->next)
1704 {
1705 if (edge->data.isCentral())
1706 {
1707 return false;
1708 }
1709 assert(edge->twin);
1710 }
1711 return true;
1712}

References Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::data, Slic3r::Arachne::SkeletalTrapezoidationEdge::isCentral(), Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::next, and Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::twin.

Referenced by filterCentral(), and filterNoncentralRegions().

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

◆ isGoingDown()

bool Slic3r::Arachne::SkeletalTrapezoidation::isGoingDown ( edge_t outgoing,
coord_t  traveled_dist,
coord_t  transition_half_length,
coord_t  lower_bead_count 
) const
protected

Determines whether an edge is going downwards or upwards in the graph.

An edge is said to go "downwards" if it's going towards a narrower part of the polygon. The notion of "downwards" comes from the conical representation of the graph, where the polygon is filled with a cone of maximum radius.

This function works by recursively checking adjacent edges until the edge is reached.

Parameters
outgoingThe edge to check.
traveled_distThe distance traversed so far.
transition_half_lengthThe radius of the transition width.
lower_bead_countThe bead count at the lower end of the edge.
Returns
true if this edge is going down, or false if it's going up.
1562{
1563 // NOTE: the logic below is not fully thought through.
1564 // TODO: take transition mids into account
1565 if (outgoing->to->data.distance_to_boundary == 0)
1566 {
1567 return true;
1568 }
1569 bool is_upward = outgoing->to->data.distance_to_boundary >= outgoing->from->data.distance_to_boundary;
1570 edge_t* upward_edge = is_upward? outgoing : outgoing->twin;
1571 if (outgoing->to->data.bead_count > lower_bead_count + 1)
1572 {
1573 assert(upward_edge->data.hasTransitions() && "If the bead count is going down there has to be a transition mid!");
1574 if(!upward_edge->data.hasTransitions())
1575 {
1576 BOOST_LOG_TRIVIAL(warning) << "If the bead count is going down there has to be a transition mid!";
1577 }
1578 return false;
1579 }
1580 coord_t length = (outgoing->to->p - outgoing->from->p).cast<int64_t>().norm();
1581 if (upward_edge->data.hasTransitions())
1582 {
1583 auto& transition_mids = *upward_edge->data.getTransitions();
1584 TransitionMiddle& mid = is_upward? transition_mids.front() : transition_mids.back();
1585 if (
1586 mid.lower_bead_count == lower_bead_count &&
1587 ((is_upward && mid.pos + traveled_dist < max_dist)
1588 || (!is_upward && length - mid.pos + traveled_dist < max_dist))
1589 )
1590 {
1591 return true;
1592 }
1593 }
1594 if (traveled_dist + length > max_dist)
1595 {
1596 return false;
1597 }
1598 if (outgoing->to->data.bead_count <= lower_bead_count
1599 && !(outgoing->to->data.bead_count == lower_bead_count && outgoing->to->data.transition_ratio > 0.0))
1600 {
1601 return true;
1602 }
1603
1604 bool is_only_going_down = true;
1605 bool has_recursed = false;
1606 for (edge_t* next = outgoing->next; next && next != outgoing->twin; next = next->twin->next)
1607 {
1608 if (!next->data.isCentral())
1609 {
1610 continue;
1611 }
1612 bool is_going_down = isGoingDown(next, traveled_dist + length, max_dist, lower_bead_count);
1613 is_only_going_down &= is_going_down;
1614 has_recursed = true;
1615 }
1616 return has_recursed && is_only_going_down;
1617}

References Slic3r::Arachne::SkeletalTrapezoidationJoint::bead_count, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::data, Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::data, Slic3r::Arachne::SkeletalTrapezoidationJoint::distance_to_boundary, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::from, Slic3r::Arachne::SkeletalTrapezoidationEdge::getTransitions(), Slic3r::Arachne::SkeletalTrapezoidationEdge::hasTransitions(), isGoingDown(), Slic3r::length(), Slic3r::Arachne::SkeletalTrapezoidationEdge::TransitionMiddle::lower_bead_count, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::next, Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::p, Slic3r::Arachne::SkeletalTrapezoidationEdge::TransitionMiddle::pos, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::to, Slic3r::Arachne::SkeletalTrapezoidationJoint::transition_ratio, and Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::twin.

Referenced by generateTransitionEnd(), and isGoingDown().

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

◆ makeNode()

SkeletalTrapezoidation::node_t & Slic3r::Arachne::SkeletalTrapezoidation::makeNode ( vd_t::vertex_type &  vd_node,
Point  p 
)
protected

Get the node which the VD node maps to, or create a new mapping if there wasn't any yet.

112{
113 auto he_node_it = vd_node_to_he_node.find(&vd_node);
114 if (he_node_it == vd_node_to_he_node.end())
115 {
116 graph.nodes.emplace_front(SkeletalTrapezoidationJoint(), p);
117 node_t& node = graph.nodes.front();
118 vd_node_to_he_node.emplace(&vd_node, &node);
119 return node;
120 }
121 else
122 {
123 return *he_node_it->second;
124 }
125}

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

Referenced by transferEdge().

+ Here is the caller graph for this function:

◆ propagateBeadingsDownward() [1/2]

void Slic3r::Arachne::SkeletalTrapezoidation::propagateBeadingsDownward ( edge_t edge_to_peak,
ptr_vector_t< BeadingPropagation > &  node_beadings 
)
protected

Subroutine of propagateBeadingsDownward(std::vector<edge_t*>&, ptr_vector_t<BeadingPropagation>&)

1970{
1971 coord_t length = (edge_to_peak->to->p - edge_to_peak->from->p).cast<int64_t>().norm();
1972 BeadingPropagation& top_beading = *getOrCreateBeading(edge_to_peak->to, node_beadings);
1973 assert(top_beading.beading.total_thickness >= edge_to_peak->to->data.distance_to_boundary * 2);
1974 if(top_beading.beading.total_thickness < edge_to_peak->to->data.distance_to_boundary * 2)
1975 {
1976 BOOST_LOG_TRIVIAL(warning) << "Top bead is beyond the center of the total width.";
1977 }
1978 assert(!top_beading.is_upward_propagated_only);
1979
1980 if(!edge_to_peak->from->data.hasBeading())
1981 { // Set new beading if there is no beading associated with the node yet
1982 BeadingPropagation propagated_beading = top_beading;
1983 propagated_beading.dist_from_top_source += length;
1984 node_beadings.emplace_back(new BeadingPropagation(propagated_beading));
1985 edge_to_peak->from->data.setBeading(node_beadings.back());
1986 assert(propagated_beading.beading.total_thickness >= edge_to_peak->from->data.distance_to_boundary * 2);
1987 if(propagated_beading.beading.total_thickness < edge_to_peak->from->data.distance_to_boundary * 2)
1988 {
1989 BOOST_LOG_TRIVIAL(warning) << "Propagated bead is beyond the center of the total width.";
1990 }
1991 }
1992 else
1993 {
1994 BeadingPropagation& bottom_beading = *edge_to_peak->from->data.getBeading();
1995 coord_t total_dist = top_beading.dist_from_top_source + length + bottom_beading.dist_to_bottom_source;
1996 double ratio_of_top = static_cast<float>(bottom_beading.dist_to_bottom_source) / std::min(total_dist, beading_propagation_transition_dist);
1997 ratio_of_top = std::max(0.0, ratio_of_top);
1998 if (ratio_of_top >= 1.0)
1999 {
2000 bottom_beading = top_beading;
2001 bottom_beading.dist_from_top_source += length;
2002 }
2003 else
2004 {
2005 Beading merged_beading = interpolate(top_beading.beading, ratio_of_top, bottom_beading.beading, edge_to_peak->from->data.distance_to_boundary);
2006 bottom_beading = BeadingPropagation(merged_beading);
2007 bottom_beading.is_upward_propagated_only = false;
2008 assert(merged_beading.total_thickness >= edge_to_peak->from->data.distance_to_boundary * 2);
2009 if(merged_beading.total_thickness < edge_to_peak->from->data.distance_to_boundary * 2)
2010 {
2011 BOOST_LOG_TRIVIAL(warning) << "Merged bead is beyond the center of the total width.";
2012 }
2013 }
2014 }
2015}
coord_t dist_from_top_source
Definition SkeletalTrapezoidationJoint.hpp:22

References Slic3r::Arachne::SkeletalTrapezoidationJoint::BeadingPropagation::beading, Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::data, Slic3r::Arachne::SkeletalTrapezoidationJoint::BeadingPropagation::dist_from_top_source, Slic3r::Arachne::SkeletalTrapezoidationJoint::BeadingPropagation::dist_to_bottom_source, Slic3r::Arachne::SkeletalTrapezoidationJoint::distance_to_boundary, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::from, Slic3r::Arachne::SkeletalTrapezoidationJoint::getBeading(), Slic3r::Arachne::SkeletalTrapezoidationJoint::hasBeading(), Slic3r::Arachne::SkeletalTrapezoidationJoint::BeadingPropagation::is_upward_propagated_only, Slic3r::length(), Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::p, Slic3r::Arachne::SkeletalTrapezoidationJoint::setBeading(), Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::to, and Slic3r::Arachne::BeadingStrategy::Beading::total_thickness.

+ Here is the call graph for this function:

◆ propagateBeadingsDownward() [2/2]

void Slic3r::Arachne::SkeletalTrapezoidation::propagateBeadingsDownward ( std::vector< edge_t * > &  upward_quad_mids,
ptr_vector_t< BeadingPropagation > &  node_beadings 
)
protected

propagate beading info from higher R nodes to lower R nodes

merge with upward propagated beadings if they are encountered

don't transfer to nodes which lie on the outline polygon

edges are sorted so that we can do a depth-first walk without employing a recursive algorithm

Parameters
upward_quad_midsall upward halfedges of the inner skeletal edges (not directly connected to the outline) sorted on their highest [distance_to_boundary]. Higher dist first.
1947{
1948 for (edge_t* upward_quad_mid : upward_quad_mids)
1949 {
1950 // Transfer beading information to lower nodes
1951 if (!upward_quad_mid->data.isCentral())
1952 {
1953 // for equidistant edge: propagate from known beading to node with unknown beading
1954 if (upward_quad_mid->from->data.distance_to_boundary == upward_quad_mid->to->data.distance_to_boundary
1955 && upward_quad_mid->from->data.hasBeading()
1956 && ! upward_quad_mid->to->data.hasBeading()
1957 )
1958 {
1959 propagateBeadingsDownward(upward_quad_mid->twin, node_beadings);
1960 }
1961 else
1962 {
1963 propagateBeadingsDownward(upward_quad_mid, node_beadings);
1964 }
1965 }
1966 }
1967}

◆ propagateBeadingsUpward()

void Slic3r::Arachne::SkeletalTrapezoidation::propagateBeadingsUpward ( std::vector< edge_t * > &  upward_quad_mids,
ptr_vector_t< BeadingPropagation > &  node_beadings 
)
protected

Propagate beading information from nodes that are closer to the edge (low radius R) to nodes that are farther from the edge (high R).

only propagate from nodes with beading info upward to nodes without beading info

Edges are sorted by their radius, so that we can do a depth-first walk without employing a recursive algorithm.

In upward propagated beadings we store the distance traveled, so that we can merge these beadings with the downward propagated beadings in propagateBeadingsDownward(.)

Parameters
upward_quad_midsall upward halfedges of the inner skeletal edges (not directly connected to the outline) sorted on their highest [distance_to_boundary]. Higher dist first.
1918{
1919 for (auto upward_quad_mids_it = upward_quad_mids.rbegin(); upward_quad_mids_it != upward_quad_mids.rend(); ++upward_quad_mids_it)
1920 {
1921 edge_t* upward_edge = *upward_quad_mids_it;
1922 if (upward_edge->to->data.bead_count >= 0)
1923 { // Don't override local beading
1924 continue;
1925 }
1926 if (! upward_edge->from->data.hasBeading())
1927 { // Only propagate if we have something to propagate
1928 continue;
1929 }
1930 BeadingPropagation& lower_beading = *upward_edge->from->data.getBeading();
1931 if (upward_edge->to->data.hasBeading())
1932 { // Only propagate to places where there is place
1933 continue;
1934 }
1935 assert((upward_edge->from->data.distance_to_boundary != upward_edge->to->data.distance_to_boundary || shorter_then(upward_edge->to->p - upward_edge->from->p, central_filter_dist)) && "zero difference R edges should always be central");
1936 coord_t length = (upward_edge->to->p - upward_edge->from->p).cast<int64_t>().norm();
1937 BeadingPropagation upper_beading = lower_beading;
1938 upper_beading.dist_to_bottom_source += length;
1939 upper_beading.is_upward_propagated_only = true;
1940 node_beadings.emplace_back(new BeadingPropagation(upper_beading));
1941 upward_edge->to->data.setBeading(node_beadings.back());
1942 assert(upper_beading.beading.total_thickness <= upward_edge->to->data.distance_to_boundary * 2);
1943 }
1944}
coord_t dist_to_bottom_source
Definition SkeletalTrapezoidationJoint.hpp:21

References Slic3r::Arachne::SkeletalTrapezoidationJoint::bead_count, Slic3r::Arachne::SkeletalTrapezoidationJoint::BeadingPropagation::beading, Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::data, Slic3r::Arachne::SkeletalTrapezoidationJoint::BeadingPropagation::dist_to_bottom_source, Slic3r::Arachne::SkeletalTrapezoidationJoint::distance_to_boundary, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::from, Slic3r::Arachne::SkeletalTrapezoidationJoint::getBeading(), Slic3r::Arachne::SkeletalTrapezoidationJoint::hasBeading(), Slic3r::Arachne::SkeletalTrapezoidationJoint::BeadingPropagation::is_upward_propagated_only, Slic3r::length(), Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::p, Slic3r::Arachne::SkeletalTrapezoidationJoint::setBeading(), Slic3r::shorter_then(), Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::to, and Slic3r::Arachne::BeadingStrategy::Beading::total_thickness.

+ Here is the call graph for this function:

◆ separatePointyQuadEndNodes()

void Slic3r::Arachne::SkeletalTrapezoidation::separatePointyQuadEndNodes ( )
protected

For VD cells associated with an input polygon vertex, we need to separate the node at the end and start of the cell into two That way we can reach both the quad_start and the quad_end from the [incident_edge] of the two new nodes Otherwise if node.incident_edge = quad_start you couldnt reach quad_end.twin by normal iteration (i.e. it = it.twin.next)

820{
821 NodeSet visited_nodes;
822 for (edge_t& edge : graph.edges)
823 {
824 if (edge.prev)
825 {
826 continue;
827 }
828 edge_t* quad_start = &edge;
829 if (visited_nodes.find(quad_start->from) == visited_nodes.end())
830 {
831 visited_nodes.emplace(quad_start->from);
832 }
833 else
834 { // Needs to be duplicated
835 graph.nodes.emplace_back(*quad_start->from);
836 node_t* new_node = &graph.nodes.back();
837 new_node->incident_edge = quad_start;
838 quad_start->from = new_node;
839 quad_start->twin->to = new_node;
840 }
841 }
842}
ankerl::unordered_dense::set< node_t * > NodeSet
Definition SkeletalTrapezoidation.hpp:87

References Slic3r::Arachne::HalfEdgeGraph< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::edges, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::from, graph, Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::incident_edge, Slic3r::Arachne::HalfEdgeGraph< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::nodes, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::to, and Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::twin.

Referenced by constructFromPolygons().

+ Here is the caller graph for this function:

◆ transferEdge()

void Slic3r::Arachne::SkeletalTrapezoidation::transferEdge ( Point  from,
Point  to,
vd_t::edge_type &  vd_edge,
edge_t *&  prev_edge,
Point start_source_point,
Point end_source_point,
const std::vector< Segment > &  segments 
)
protected

Transfer an edge from the VD to the HE and perform discretization of parabolic edges (and vertex-vertex edges) prev_edge serves as input and output. May be null as input.

128{
129 auto he_edge_it = vd_edge_to_he_edge.find(vd_edge.twin());
130 if (he_edge_it != vd_edge_to_he_edge.end())
131 { // Twin segment(s) have already been made
132 edge_t* source_twin = he_edge_it->second;
133 assert(source_twin);
134 auto end_node_it = vd_node_to_he_node.find(vd_edge.vertex1());
135 assert(end_node_it != vd_node_to_he_node.end());
136 node_t* end_node = end_node_it->second;
137 for (edge_t* twin = source_twin; ;twin = twin->prev->twin->prev)
138 {
139 if(!twin)
140 {
141 BOOST_LOG_TRIVIAL(warning) << "Encountered a voronoi edge without twin.";
142 continue; //Prevent reading unallocated memory.
143 }
144 assert(twin);
145 graph.edges.emplace_front(SkeletalTrapezoidationEdge());
146 edge_t* edge = &graph.edges.front();
147 edge->from = twin->to;
148 edge->to = twin->from;
149 edge->twin = twin;
150 twin->twin = edge;
151 edge->from->incident_edge = edge;
152
153 if (prev_edge)
154 {
155 edge->prev = prev_edge;
156 prev_edge->next = edge;
157 }
158
159 prev_edge = edge;
160
161 if (prev_edge->to == end_node)
162 {
163 return;
164 }
165
166 if (!twin->prev || !twin->prev->twin || !twin->prev->twin->prev)
167 {
168 BOOST_LOG_TRIVIAL(error) << "Discretized segment behaves oddly!";
169 return;
170 }
171
172 assert(twin->prev); // Forth rib
173 assert(twin->prev->twin); // Back rib
174 assert(twin->prev->twin->prev); // Prev segment along parabola
175
176 constexpr bool is_not_next_to_start_or_end = false; // Only ribs at the end of a cell should be skipped
177 graph.makeRib(prev_edge, start_source_point, end_source_point, is_not_next_to_start_or_end);
178 }
179 assert(prev_edge);
180 }
181 else
182 {
183 Points discretized = discretize(vd_edge, segments);
184 assert(discretized.size() >= 2);
185 if(discretized.size() < 2)
186 {
187 BOOST_LOG_TRIVIAL(warning) << "Discretized Voronoi edge is degenerate.";
188 }
189
190 assert(!prev_edge || prev_edge->to);
191 if(prev_edge && !prev_edge->to)
192 {
193 BOOST_LOG_TRIVIAL(warning) << "Previous edge doesn't go anywhere.";
194 }
195 node_t* v0 = (prev_edge)? prev_edge->to : &makeNode(*vd_edge.vertex0(), from); // TODO: investigate whether boost:voronoi can produce multiple verts and violates consistency
196 Point p0 = discretized.front();
197 for (size_t p1_idx = 1; p1_idx < discretized.size(); p1_idx++)
198 {
199 Point p1 = discretized[p1_idx];
200 node_t* v1;
201 if (p1_idx < discretized.size() - 1)
202 {
203 graph.nodes.emplace_front(SkeletalTrapezoidationJoint(), p1);
204 v1 = &graph.nodes.front();
205 }
206 else
207 {
208 v1 = &makeNode(*vd_edge.vertex1(), to);
209 }
210
211 graph.edges.emplace_front(SkeletalTrapezoidationEdge());
212 edge_t* edge = &graph.edges.front();
213 edge->from = v0;
214 edge->to = v1;
215 edge->from->incident_edge = edge;
216
217 if (prev_edge)
218 {
219 edge->prev = prev_edge;
220 prev_edge->next = edge;
221 }
222
223 prev_edge = edge;
224 p0 = p1;
225 v0 = v1;
226
227 if (p1_idx < discretized.size() - 1)
228 { // Rib for last segment gets introduced outside this function!
229 constexpr bool is_not_next_to_start_or_end = false; // Only ribs at the end of a cell should be skipped
230 graph.makeRib(prev_edge, start_source_point, end_source_point, is_not_next_to_start_or_end);
231 }
232 }
233 assert(prev_edge);
234 vd_edge_to_he_edge.emplace(&vd_edge, prev_edge);
235 }
236}
edge_t * prev
Definition HalfEdge.hpp:26
node_t & makeNode(vd_t::vertex_type &vd_node, Point p)
Get the node which the VD node maps to, or create a new mapping if there wasn't any yet.
Definition SkeletalTrapezoidation.cpp:111
Points discretize(const vd_t::edge_type &segment, const std::vector< Segment > &segments)
Definition SkeletalTrapezoidation.cpp:238

References discretize(), Slic3r::Arachne::HalfEdgeGraph< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::edges, error, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::from, graph, Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::incident_edge, makeNode(), Slic3r::Arachne::SkeletalTrapezoidationGraph::makeRib(), Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::next, Slic3r::Arachne::HalfEdgeGraph< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::nodes, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::prev, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::to, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::twin, vd_edge_to_he_edge, and vd_node_to_he_node.

Referenced by constructFromPolygons().

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

◆ updateBeadCount()

void Slic3r::Arachne::SkeletalTrapezoidation::updateBeadCount ( )
protected

Set bead count in central regions based on the optimal_bead_count of the beading strategy.

1012{
1013 for (edge_t& edge : graph.edges)
1014 {
1015 if (edge.data.isCentral())
1016 {
1017 edge.to->data.bead_count = beading_strategy.getOptimalBeadCount(edge.to->data.distance_to_boundary * 2);
1018 }
1019 }
1020
1021 // Fix bead count at locally maximal R, also for central regions!! See TODO s in generateTransitionEnd(.)
1022 for (node_t& node : graph.nodes)
1023 {
1024 if (node.isLocalMaximum())
1025 {
1026 if (node.data.distance_to_boundary < 0)
1027 {
1028 BOOST_LOG_TRIVIAL(warning) << "Distance to boundary not yet computed for local maximum!";
1029 node.data.distance_to_boundary = std::numeric_limits<coord_t>::max();
1030 edge_t* edge = node.incident_edge;
1031 do
1032 {
1033 node.data.distance_to_boundary = std::min(node.data.distance_to_boundary, edge->to->data.distance_to_boundary + coord_t((edge->from->p - edge->to->p).cast<int64_t>().norm()));
1034 } while (edge = edge->twin->next, edge != node.incident_edge);
1035 }
1036 coord_t bead_count = beading_strategy.getOptimalBeadCount(node.data.distance_to_boundary * 2);
1037 node.data.bead_count = bead_count;
1038 }
1039 }
1040}
edge_data_t data
Definition HalfEdge.hpp:23

References beading_strategy, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::data, Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::data, Slic3r::Arachne::SkeletalTrapezoidationJoint::distance_to_boundary, Slic3r::Arachne::HalfEdgeGraph< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::edges, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::from, Slic3r::Arachne::BeadingStrategy::getOptimalBeadCount(), graph, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::next, Slic3r::Arachne::HalfEdgeGraph< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::nodes, Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::p, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::to, and Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::twin.

Referenced by generateToolpaths().

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

◆ updateIsCentral()

void Slic3r::Arachne::SkeletalTrapezoidation::updateIsCentral ( )
protected
913{
914 // _.-'^` A and B are the endpoints of an edge we're checking.
915 // _.-'^` Part of the line AB will be used as a cap,
916 // _.-'^` \ because the polygon is too narrow there.
917 // _.-'^` \ If |AB| minus the cap is still bigger than dR,
918 // _.-'^` \ R2 the edge AB is considered central. It's then
919 // _.-'^` \ _.-'\`\ significant compared to the edges around it.
920 // _.-'^` \R1 _.-'^` '`\ dR
921 // _.-'^`a/2 \_.-'^`a \ Line AR2 is parallel to the polygon contour.
922 // `^'-._````````````````A```````````v````````B``````` dR is the remaining diameter at B.
923 // `^'-._ dD = |AB| As a result, AB is less often central if the polygon
924 // `^'-._ corner is obtuse.
925 // sin a = dR / dD
926
927 coord_t outer_edge_filter_length = beading_strategy.getTransitionThickness(0) / 2;
928
929 float cap = sin(beading_strategy.getTransitioningAngle() * 0.5); // = cos(bisector_angle / 2)
930 for (edge_t& edge: graph.edges)
931 {
932 assert(edge.twin);
933 if(!edge.twin)
934 {
935 BOOST_LOG_TRIVIAL(warning) << "Encountered a Voronoi edge without twin!";
936 continue;
937 }
938 if(edge.twin->data.centralIsSet())
939 {
940 edge.data.setIsCentral(edge.twin->data.isCentral());
941 }
942 else if(edge.data.type == SkeletalTrapezoidationEdge::EdgeType::EXTRA_VD)
943 {
944 edge.data.setIsCentral(false);
945 }
946 else if(std::max(edge.from->data.distance_to_boundary, edge.to->data.distance_to_boundary) < outer_edge_filter_length)
947 {
948 edge.data.setIsCentral(false);
949 }
950 else
951 {
952 Point a = edge.from->p;
953 Point b = edge.to->p;
954 Point ab = b - a;
955 coord_t dR = std::abs(edge.to->data.distance_to_boundary - edge.from->data.distance_to_boundary);
956 coord_t dD = ab.cast<int64_t>().norm();
957 edge.data.setIsCentral(dR < dD * cap);
958 }
959 }
960}
double getTransitioningAngle() const
Definition BeadingStrategy.cpp:61

References beading_strategy, Slic3r::Arachne::HalfEdgeGraph< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::edges, Slic3r::Arachne::SkeletalTrapezoidationEdge::EXTRA_VD, Slic3r::Arachne::BeadingStrategy::getTransitioningAngle(), Slic3r::Arachne::BeadingStrategy::getTransitionThickness(), graph, and sin().

Referenced by generateToolpaths().

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

Friends And Related Symbol Documentation

◆ detect_voronoi_edge_intersecting_input_segment

bool detect_voronoi_edge_intersecting_input_segment ( const Geometry::VoronoiDiagram voronoi_diagram,
const std::vector< VoronoiUtils::Segment > &  segments 
)
friend
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}
PolygonsSegmentIndex Segment
Definition VoronoiUtils.hpp:23
Derived::Scalar cross2(const Eigen::MatrixBase< Derived > &v1, const Eigen::MatrixBase< Derived2 > &v2)
Definition Point.hpp:93
Eigen::Matrix< double, 2, 1, Eigen::DontAlign > Vec2d
Definition Point.hpp:51

Member Data Documentation

◆ allowed_filter_deviation

coord_t Slic3r::Arachne::SkeletalTrapezoidation::allowed_filter_deviation
private

The allowed line width deviation induced by filtering.

Referenced by dissolveNearbyTransitions().

◆ beading_propagation_transition_dist

coord_t Slic3r::Arachne::SkeletalTrapezoidation::beading_propagation_transition_dist
private

When there are different beadings propagated from below and from above, use this transitioning distance.

◆ beading_strategy

const BeadingStrategy& Slic3r::Arachne::SkeletalTrapezoidation::beading_strategy
private

The strategy to use to fill a certain shape with lines.

Various BeadingStrategies are available that differ in which lines get to print at their optimal width, where the play is being compensated, and how the joints are handled where we transition to different numbers of lines.

Referenced by dissolveNearbyTransitions(), filterNoncentralRegions(), filterTransitionMids(), generateExtraRibs(), generateTransitionEnds(), generateTransitionMids(), updateBeadCount(), and updateIsCentral().

◆ central_filter_dist

constexpr coord_t Slic3r::Arachne::SkeletalTrapezoidation::central_filter_dist = scaled<coord_t>(0.02)
staticconstexprprivate

Filter areas marked as 'central' smaller than this.

Referenced by generateToolpaths().

◆ discretization_step_size

coord_t Slic3r::Arachne::SkeletalTrapezoidation::discretization_step_size
private

approximate size of segments when parabolic VD edges get discretized (and vertex-vertex edges)

Referenced by discretize(), and generateExtraRibs().

◆ graph

graph_t Slic3r::Arachne::SkeletalTrapezoidation::graph

A skeletal graph through the polygons that we need to fill with beads.

The skeletal graph represents the medial axes through each part of the polygons, and the lines from these medial axes towards each vertex of the polygons. The graph can be used to see what the width is of a polygon in each place and where the width transitions.

Referenced by applyTransitions(), constructFromPolygons(), filterCentral(), filterNoncentralRegions(), filterOuterCentral(), filterTransitionMids(), generateAllTransitionEnds(), generateExtraRibs(), generateSegments(), generateTransitioningRibs(), generateTransitionMids(), makeNode(), separatePointyQuadEndNodes(), transferEdge(), updateBeadCount(), and updateIsCentral().

◆ p_generated_toolpaths

std::vector<VariableWidthLines>* Slic3r::Arachne::SkeletalTrapezoidation::p_generated_toolpaths
protected

(Eventual) returned 'polylines per index' result (from generateToolpaths):

Referenced by generateToolpaths().

◆ snap_dist

constexpr coord_t Slic3r::Arachne::SkeletalTrapezoidation::snap_dist = scaled<coord_t>(0.02)
staticconstexprprivate

Generic arithmatic inaccuracy. Only used to determine whether a transition really needs to insert an extra edge.

Referenced by applyTransitions(), and generateExtraRibs().

◆ transition_filter_dist

coord_t Slic3r::Arachne::SkeletalTrapezoidation::transition_filter_dist
private

Filter transition mids (i.e. anchors) closer together than this.

Referenced by filterTransitionMids().

◆ transitioning_angle

double Slic3r::Arachne::SkeletalTrapezoidation::transitioning_angle
private

How pointy a region should be before we apply the method. Equals 180* - limit_bisector_angle.

Referenced by discretize().

◆ vd_edge_to_he_edge

ankerl::unordered_dense::map<vd_t::edge_type*, edge_t*> Slic3r::Arachne::SkeletalTrapezoidation::vd_edge_to_he_edge
protected

mapping each voronoi VD edge to the corresponding halfedge HE edge In case the result segment is discretized, we map the VD edge to the last HE edge

Referenced by constructFromPolygons(), and transferEdge().

◆ vd_node_to_he_node

ankerl::unordered_dense::map<vd_t::vertex_type*, node_t*> Slic3r::Arachne::SkeletalTrapezoidation::vd_node_to_he_node
protected

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