OrcaSlicer latest
OrcaSlicer
Loading...
Searching...
No Matches
Slic3r::Arachne::SkeletalTrapezoidation Class Reference

#include </home/runner/work/OrcaSlicer/OrcaSlicer/src/libslic3r/Arachne/SkeletalTrapezoidation.hpp>

Collaboration diagram for Slic3r::Arachne::SkeletalTrapezoidation:

Classes

struct  TransitionMidRef

Public Types

using Segment = PolygonsSegmentIndex
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_t & makeNode (const VD::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 (const Point &from, const Point &to, const VD::edge_type &vd_edge, edge_t *&prev_edge, const Point &start_source_point, const Point &end_source_point, const std::vector< Segment > &segments)
Points discretize (const VD::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< TransitionMidRef > dissolveNearbyTransitions (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_t * getQuadMaxRedgeTo (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)
std::shared_ptr< BeadingPropagation > getOrCreateBeading (node_t *node, ptr_vector_t< BeadingPropagation > &node_beadings)
std::shared_ptr< BeadingPropagation > getNearestBeading (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 Beading interpolate (const Beading &left, double ratio_left_to_whole, const Beading &right, coord_t switching_radius)
static Beading interpolate (const Beading &left, double ratio_left_to_whole, const Beading &right)

Protected Attributes

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

Private Types

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 Member Functions

coord_t central_filter_dist ()
 Generic arithmatic inaccuracy. Only used to determine whether a transition really needs to insert an extra edge.
coord_t snap_dist ()

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 Filter areas marked as 'central' smaller than this.
const BeadingStrategy & beading_strategy

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*>

◆ ptr_vector_t

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

◆ Segment

◆ TransitionEnd

◆ TransitionMiddle

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.
352{
354}
void constructFromPolygons(const Polygons &polys)
Definition SkeletalTrapezoidation.cpp:356
const BeadingStrategy & beading_strategy
Definition SkeletalTrapezoidation.hpp:84
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 allowed_filter_deviation, beading_propagation_transition_dist, beading_strategy, constructFromPolygons(), discretization_step_size, transition_filter_dist, and transitioning_angle.

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.
1902{
1903 if (from == to) return;
1904
1905 std::vector<VariableWidthLines> &generated_toolpaths = *p_generated_toolpaths;
1906
1907 size_t inset_idx = from.perimeter_index;
1908 if (inset_idx >= generated_toolpaths.size())
1909 {
1910 generated_toolpaths.resize(inset_idx + 1);
1911 }
1912 assert((generated_toolpaths[inset_idx].empty() || !generated_toolpaths[inset_idx].back().junctions.empty()) && "empty extrusion lines should never have been generated");
1913 if (generated_toolpaths[inset_idx].empty()
1914 || generated_toolpaths[inset_idx].back().is_odd != is_odd
1915 || generated_toolpaths[inset_idx].back().junctions.back().perimeter_index != inset_idx // inset_idx should always be consistent
1916 )
1917 {
1918 force_new_path = true;
1919 }
1920 if (!force_new_path
1921 && shorter_then(generated_toolpaths[inset_idx].back().junctions.back().p - from.p, scaled<coord_t>(0.010))
1922 && std::abs(generated_toolpaths[inset_idx].back().junctions.back().w - from.w) < scaled<coord_t>(0.010)
1923 && ! from_is_3way // force new path at 3way intersection
1924 )
1925 {
1926 generated_toolpaths[inset_idx].back().junctions.push_back(to);
1927 }
1928 else if (!force_new_path
1929 && shorter_then(generated_toolpaths[inset_idx].back().junctions.back().p - to.p, scaled<coord_t>(0.010))
1930 && std::abs(generated_toolpaths[inset_idx].back().junctions.back().w - to.w) < scaled<coord_t>(0.010)
1931 && ! to_is_3way // force new path at 3way intersection
1932 )
1933 {
1934 if ( ! is_odd)
1935 {
1936 BOOST_LOG_TRIVIAL(error) << "Reversing even wall line causes it to be printed CCW instead of CW!";
1937 }
1938 generated_toolpaths[inset_idx].back().junctions.push_back(from);
1939 }
1940 else
1941 {
1942 generated_toolpaths[inset_idx].emplace_back(inset_idx, is_odd);
1943 generated_toolpaths[inset_idx].back().junctions.push_back(from);
1944 generated_toolpaths[inset_idx].back().junctions.push_back(to);
1945 }
1946};
std::vector< VariableWidthLines > * p_generated_toolpaths
Definition SkeletalTrapezoidation.hpp:179
bool shorter_then(const Point &p0, const coord_t len)
Definition Point.hpp:486
BoundingBox scaled(const BoundingBoxf &bb)
Definition BoundingBox.hpp:284
bool empty(const BoundingBoxBase< PointType, PointsType > &bb)
Definition BoundingBox.hpp:273

References addToolpathSegment(), Slic3r::empty(), Slic3r::Arachne::ExtrusionJunction::p, p_generated_toolpaths, Slic3r::Arachne::ExtrusionJunction::perimeter_index, Slic3r::scaled(), Slic3r::shorter_then(), and Slic3r::Arachne::ExtrusionJunction::w.

Referenced by addToolpathSegment(), and connectJunctions().

Here is the call graph for this function:
Here is the caller 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

1278{
1279 const auto _snap_dist = snap_dist();
1280 for (edge_t& edge : graph.edges)
1281 {
1282 if (edge.twin->data.hasTransitionEnds())
1283 {
1284 coord_t length = (edge.from->p - edge.to->p).cast<int64_t>().norm();
1285 auto& twin_transition_ends = *edge.twin->data.getTransitionEnds();
1286 if (! edge.data.hasTransitionEnds())
1287 {
1288 edge_transition_ends.emplace_back(std::make_shared<std::list<TransitionEnd>>());
1289 edge.data.setTransitionEnds(edge_transition_ends.back());
1290 }
1291 auto& transition_ends = *edge.data.getTransitionEnds();
1292 for (TransitionEnd& end : twin_transition_ends)
1293 {
1294 transition_ends.emplace_back(length - end.pos, end.lower_bead_count, end.is_lower_end);
1295 }
1296 twin_transition_ends.clear();
1297 }
1298 }
1299
1300 for (edge_t& edge : graph.edges)
1301 {
1302 if (! edge.data.hasTransitionEnds())
1303 {
1304 continue;
1305 }
1306
1307 assert(edge.data.isCentral());
1308
1309 auto& transitions = *edge.data.getTransitionEnds();
1310 transitions.sort([](const TransitionEnd& a, const TransitionEnd& b) { return a.pos < b.pos; } );
1311
1312 node_t* from = edge.from;
1313 node_t* to = edge.to;
1314 Point a = from->p;
1315 Point b = to->p;
1316 Point ab = b - a;
1317 coord_t ab_size = (ab).cast<int64_t>().norm();
1318
1319 edge_t* last_edge_replacing_input = &edge;
1320 for (TransitionEnd& transition_end : transitions)
1321 {
1322 coord_t new_node_bead_count = transition_end.is_lower_end? transition_end.lower_bead_count : transition_end.lower_bead_count + 1;
1323 coord_t end_pos = transition_end.pos;
1324 node_t* close_node = (end_pos < ab_size / 2)? from : to;
1325 if ((end_pos < _snap_dist || end_pos > ab_size - _snap_dist)
1326 && close_node->data.bead_count == new_node_bead_count
1327 )
1328 {
1329 assert(end_pos <= ab_size);
1330 close_node->data.transition_ratio = 0;
1331 continue;
1332 }
1333 Point mid = a + normal(ab, end_pos);
1334
1335 assert(last_edge_replacing_input->data.isCentral());
1336 assert(last_edge_replacing_input->data.type != SkeletalTrapezoidationEdge::EdgeType::EXTRA_VD);
1337 last_edge_replacing_input = graph.insertNode(last_edge_replacing_input, mid, new_node_bead_count);
1338 assert(last_edge_replacing_input->data.type != SkeletalTrapezoidationEdge::EdgeType::EXTRA_VD);
1339 assert(last_edge_replacing_input->data.isCentral());
1340 }
1341 }
1342}
@ EXTRA_VD
Definition SkeletalTrapezoidationEdge.hpp:54
STHalfEdgeNode node_t
Definition SkeletalTrapezoidation.hpp:57
STHalfEdge edge_t
Definition SkeletalTrapezoidation.hpp:56
graph_t graph
Definition SkeletalTrapezoidation.hpp:123
coord_t snap_dist()
Definition SkeletalTrapezoidation.hpp:74
SkeletalTrapezoidationEdge::TransitionEnd TransitionEnd
Definition SkeletalTrapezoidation.hpp:61
int64_t coord_t
Definition libslic3r.h:48
T * end(Slic3r::Mat< N, M, T > &mat)
Definition Point.hpp:841
static Point normal(const Point &p0, coord_t len)
Definition SkeletalTrapezoidation.cpp:1269
Kernel::Point_3 Point
Definition MeshRepair.cpp:81
double length(const Points &pts)
Definition MultiPoint.hpp:181
auto cast(const BoundingBoxBase< Tin > &b)
Definition BoundingBox.hpp:299

References Slic3r::Arachne::SkeletalTrapezoidationJoint::bead_count, Slic3r::cast(), 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::SkeletalTrapezoidationEdge::EXTRA_VD, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::from, Slic3r::Arachne::SkeletalTrapezoidationEdge::getTransitionEnds(), graph, Slic3r::Arachne::SkeletalTrapezoidationEdge::hasTransitionEnds(), 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, Slic3r::Arachne::SkeletalTrapezoidationEdge::TransitionEnd::pos, Slic3r::Arachne::SkeletalTrapezoidationEdge::setTransitionEnds(), snap_dist(), Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::to, Slic3r::Arachne::SkeletalTrapezoidationJoint::transition_ratio, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::twin, and Slic3r::Arachne::SkeletalTrapezoidationEdge::type.

Referenced by generateTransitioningRibs().

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

◆ central_filter_dist()

coord_t Slic3r::Arachne::SkeletalTrapezoidation::central_filter_dist ( )
inlineprivate

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

References Slic3r::scaled().

Referenced by generateToolpaths(), and propagateBeadingsUpward().

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

1949{
1950 using EdgeSet = ankerl::unordered_dense::set<edge_t*>;
1951
1952 EdgeSet unprocessed_quad_starts(graph.edges.size() * 5 / 2);
1953 for (edge_t& edge : graph.edges)
1954 {
1955 if (!edge.prev)
1956 {
1957 unprocessed_quad_starts.emplace(&edge);
1958 }
1959 }
1960
1961 EdgeSet passed_odd_edges;
1962
1963 while (!unprocessed_quad_starts.empty())
1964 {
1965 edge_t* poly_domain_start = *unprocessed_quad_starts.begin();
1966 edge_t* quad_start = poly_domain_start;
1967 bool new_domain_start = true;
1968 do
1969 {
1970 edge_t* quad_end = quad_start;
1971 while (quad_end->next)
1972 {
1973 quad_end = quad_end->next;
1974 }
1975
1976 edge_t* edge_to_peak = getQuadMaxRedgeTo(quad_start);
1977 // walk down on both sides and connect junctions
1978 edge_t* edge_from_peak = edge_to_peak->next; assert(edge_from_peak);
1979
1980 unprocessed_quad_starts.erase(quad_start);
1981
1982 if (! edge_to_peak->data.hasExtrusionJunctions())
1983 {
1984 edge_junctions.emplace_back(std::make_shared<LineJunctions>());
1985 edge_to_peak->data.setExtrusionJunctions(edge_junctions.back());
1986 }
1987 // The junctions on the edge(s) from the start of the quad to the node with highest R
1988 LineJunctions from_junctions = *edge_to_peak->data.getExtrusionJunctions();
1989 if (! edge_from_peak->twin->data.hasExtrusionJunctions())
1990 {
1991 edge_junctions.emplace_back(std::make_shared<LineJunctions>());
1992 edge_from_peak->twin->data.setExtrusionJunctions(edge_junctions.back());
1993 }
1994 // The junctions on the edge(s) from the end of the quad to the node with highest R
1995 LineJunctions to_junctions = *edge_from_peak->twin->data.getExtrusionJunctions();
1996 if (edge_to_peak->prev)
1997 {
1998 LineJunctions from_prev_junctions = *edge_to_peak->prev->data.getExtrusionJunctions();
1999 while (!from_junctions.empty() && !from_prev_junctions.empty() && from_junctions.back().perimeter_index <= from_prev_junctions.front().perimeter_index)
2000 {
2001 from_junctions.pop_back();
2002 }
2003 from_junctions.reserve(from_junctions.size() + from_prev_junctions.size());
2004 from_junctions.insert(from_junctions.end(), from_prev_junctions.begin(), from_prev_junctions.end());
2005 assert(!edge_to_peak->prev->prev);
2006 if(edge_to_peak->prev->prev)
2007 {
2008 BOOST_LOG_TRIVIAL(warning) << "The edge we're about to connect is already connected.";
2009 }
2010 }
2011 if (edge_from_peak->next)
2012 {
2013 LineJunctions to_next_junctions = *edge_from_peak->next->twin->data.getExtrusionJunctions();
2014 while (!to_junctions.empty() && !to_next_junctions.empty() && to_junctions.back().perimeter_index <= to_next_junctions.front().perimeter_index)
2015 {
2016 to_junctions.pop_back();
2017 }
2018 to_junctions.reserve(to_junctions.size() + to_next_junctions.size());
2019 to_junctions.insert(to_junctions.end(), to_next_junctions.begin(), to_next_junctions.end());
2020 assert(!edge_from_peak->next->next);
2021 if(edge_from_peak->next->next)
2022 {
2023 BOOST_LOG_TRIVIAL(warning) << "The edge we're about to connect is already connected!";
2024 }
2025 }
2026 assert(std::abs(int(from_junctions.size()) - int(to_junctions.size())) <= 1); // at transitions one end has more beads
2027 if(std::abs(int(from_junctions.size()) - int(to_junctions.size())) > 1)
2028 {
2029 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();
2030 }
2031
2032 size_t segment_count = std::min(from_junctions.size(), to_junctions.size());
2033 for (size_t junction_rev_idx = 0; junction_rev_idx < segment_count; junction_rev_idx++)
2034 {
2035 ExtrusionJunction& from = from_junctions[from_junctions.size() - 1 - junction_rev_idx];
2036 ExtrusionJunction& to = to_junctions[to_junctions.size() - 1 - junction_rev_idx];
2037 assert(from.perimeter_index == to.perimeter_index);
2038 if(from.perimeter_index != to.perimeter_index)
2039 {
2040 BOOST_LOG_TRIVIAL(warning) << "Connecting two perimeters with different indices! Perimeter " << from.perimeter_index << " and " << to.perimeter_index;
2041 }
2042 const bool from_is_odd =
2043 quad_start->to->data.bead_count > 0 && quad_start->to->data.bead_count % 2 == 1 // quad contains single bead segment
2044 && quad_start->to->data.transition_ratio == 0 // We're not in a transition
2045 && junction_rev_idx == segment_count - 1 // Is single bead segment
2046 && shorter_then(from.p - quad_start->to->p, scaled<coord_t>(0.005));
2047 const bool to_is_odd =
2048 quad_end->from->data.bead_count > 0 && quad_end->from->data.bead_count % 2 == 1 // quad contains single bead segment
2049 && quad_end->from->data.transition_ratio == 0 // We're not in a transition
2050 && junction_rev_idx == segment_count - 1 // Is single bead segment
2051 && shorter_then(to.p - quad_end->from->p, scaled<coord_t>(0.005));
2052 const bool is_odd_segment = from_is_odd && to_is_odd;
2053 if (is_odd_segment
2054 && passed_odd_edges.count(quad_start->next->twin) > 0) // Only generate toolpath for odd segments once
2055 {
2056 continue; // Prevent duplication of single bead segments
2057 }
2058 bool from_is_3way = from_is_odd && quad_start->to->isMultiIntersection();
2059 bool to_is_3way = to_is_odd && quad_end->from->isMultiIntersection();
2060 passed_odd_edges.emplace(quad_start->next);
2061
2062 addToolpathSegment(from, to, is_odd_segment, new_domain_start, from_is_3way, to_is_3way);
2063 }
2064 new_domain_start = false;
2065 }
2066 while(quad_start = quad_start->getNextUnconnected(), quad_start != poly_domain_start);
2067 }
2068}
edge_t * getQuadMaxRedgeTo(edge_t *quad_start_edge)
Definition SkeletalTrapezoidation.cpp:1544
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:1901
std::vector< ExtrusionJunction > LineJunctions
Definition ExtrusionJunction.hpp:65

References addToolpathSegment(), Slic3r::Arachne::SkeletalTrapezoidationJoint::bead_count, connectJunctions(), 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(), getQuadMaxRedgeTo(), graph, 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::scaled(), 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.

Referenced by connectJunctions().

Here is the call graph for this function:
Here is the caller 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.

357{
358#ifdef ARACHNE_DEBUG
359 this->outline = polys;
360#endif
361
362 // Check self intersections.
363 assert([&polys]() -> bool {
364 EdgeGrid::Grid grid;
365 grid.set_bbox(get_extents(polys));
366 grid.create(polys, scaled<coord_t>(10.));
367 return !grid.has_intersecting_edges();
368 }());
369
370 vd_edge_to_he_edge.clear();
371 vd_node_to_he_node.clear();
372
373 std::vector<Segment> segments;
374 for (size_t poly_idx = 0; poly_idx < polys.size(); poly_idx++)
375 for (size_t point_idx = 0; point_idx < polys[poly_idx].size(); point_idx++)
376 segments.emplace_back(&polys, poly_idx, point_idx);
377
378#ifdef ARACHNE_DEBUG
379 {
380 static int iRun = 0;
381 BoundingBox bbox = get_extents(polys);
382 SVG svg(debug_out_path("arachne_voronoi-input-%d.svg", iRun++).c_str(), bbox);
383 svg.draw_outline(polys, "black", scaled<coordf_t>(0.03f));
384 }
385#endif
386
387 VD voronoi_diagram;
388 voronoi_diagram.construct_voronoi(segments.cbegin(), segments.cend());
389
390#ifdef ARACHNE_DEBUG_VORONOI
391 {
392 static int iRun = 0;
393 dump_voronoi_to_svg(debug_out_path("arachne_voronoi-diagram-%d.svg", iRun++).c_str(), voronoi_diagram, to_points(polys), to_lines(polys));
394 }
395#endif
396
397 assert(this->graph.edges.empty() && this->graph.nodes.empty() && this->vd_edge_to_he_edge.empty() && this->vd_node_to_he_node.empty());
398 for (const VD::cell_type &cell : voronoi_diagram.cells()) {
399 if (!cell.incident_edge())
400 continue; // There is no spoon
401
402 Point start_source_point;
403 Point end_source_point;
404 const VD::edge_type *starting_voronoi_edge = nullptr;
405 const VD::edge_type *ending_voronoi_edge = nullptr;
406 // Compute and store result in above variables
407
408 if (cell.contains_point()) {
409 Geometry::PointCellRange<Point> cell_range = Geometry::VoronoiUtils::compute_point_cell_range(cell, segments.cbegin(), segments.cend());
410 start_source_point = cell_range.source_point;
411 end_source_point = cell_range.source_point;
412 starting_voronoi_edge = cell_range.edge_begin;
413 ending_voronoi_edge = cell_range.edge_end;
414
415 if (!cell_range.is_valid())
416 continue;
417 } else {
418 assert(cell.contains_segment());
419 Geometry::SegmentCellRange<Point> cell_range = Geometry::VoronoiUtils::compute_segment_cell_range(cell, segments.cbegin(), segments.cend());
420 assert(cell_range.is_valid());
421 start_source_point = cell_range.source_segment_start_point;
422 end_source_point = cell_range.source_segment_end_point;
423 starting_voronoi_edge = cell_range.edge_begin;
424 ending_voronoi_edge = cell_range.edge_end;
425 }
426
427 if (!starting_voronoi_edge || !ending_voronoi_edge) {
428 assert(false && "Each cell should start / end in a polygon vertex");
429 continue;
430 }
431
432 // Copy start to end edge to graph
433 assert(Geometry::VoronoiUtils::is_in_range<coord_t>(*starting_voronoi_edge));
434 edge_t *prev_edge = nullptr;
435 transferEdge(start_source_point, Geometry::VoronoiUtils::to_point(starting_voronoi_edge->vertex1()).cast<coord_t>(), *starting_voronoi_edge, prev_edge, start_source_point, end_source_point, segments);
436 node_t *starting_node = vd_node_to_he_node[starting_voronoi_edge->vertex0()];
437 starting_node->data.distance_to_boundary = 0;
438
439 graph.makeRib(prev_edge, start_source_point, end_source_point);
440 for (const VD::edge_type* vd_edge = starting_voronoi_edge->next(); vd_edge != ending_voronoi_edge; vd_edge = vd_edge->next()) {
441 assert(vd_edge->is_finite());
443
444 Point v1 = Geometry::VoronoiUtils::to_point(vd_edge->vertex0()).cast<coord_t>();
445 Point v2 = Geometry::VoronoiUtils::to_point(vd_edge->vertex1()).cast<coord_t>();
446 transferEdge(v1, v2, *vd_edge, prev_edge, start_source_point, end_source_point, segments);
447 graph.makeRib(prev_edge, start_source_point, end_source_point);
448 }
449
450 transferEdge(Geometry::VoronoiUtils::to_point(ending_voronoi_edge->vertex0()).cast<coord_t>(), end_source_point, *ending_voronoi_edge, prev_edge, start_source_point, end_source_point, segments);
451 prev_edge->to->data.distance_to_boundary = 0;
452 }
453
454#ifdef ARACHNE_DEBUG
456#endif
457
459
460 graph.collapseSmallEdges();
461
462 // Set [incident_edge] the first possible edge that way we can iterate over all reachable edges from node.incident_edge,
463 // without needing to iterate backward
464 for (edge_t& edge : graph.edges)
465 if (!edge.prev)
466 edge.from->incident_edge = &edge;
467}
void transferEdge(const Point &from, const Point &to, const VD::edge_type &vd_edge, edge_t *&prev_edge, const Point &start_source_point, const Point &end_source_point, const std::vector< Segment > &segments)
Definition SkeletalTrapezoidation.cpp:123
ankerl::unordered_dense::map< const VD::vertex_type *, node_t * > vd_node_to_he_node
Definition SkeletalTrapezoidation.hpp:173
void separatePointyQuadEndNodes()
Definition SkeletalTrapezoidation.cpp:469
ankerl::unordered_dense::map< const VD::edge_type *, edge_t * > vd_edge_to_he_edge
Definition SkeletalTrapezoidation.hpp:172
voronoi_diagram_type::cell_type cell_type
Definition Voronoi.hpp:42
boost::polygon::enable_if< typenameboost::polygon::gtl_if< typenameboost::polygon::is_segment_concept< typenameboost::polygon::geometry_concept< typenamestd::iterator_traits< SegmentIterator >::value_type >::type >::type >::type, void >::type construct_voronoi(SegmentIterator segment_begin, SegmentIterator segment_end, bool try_to_repair_if_needed=true)
Definition Voronoi.cpp:34
voronoi_diagram_type::edge_type edge_type
Definition Voronoi.hpp:41
static bool is_voronoi_diagram_planar_intersection(const VoronoiDiagram &voronoi_diagram)
Definition VoronoiUtilsCgal.cpp:164
static boost::polygon::enable_if< typenameboost::polygon::gtl_if< typenameboost::polygon::is_segment_concept< typenameboost::polygon::geometry_concept< typenamestd::iterator_traits< SegmentIterator >::value_type >::type >::type >::type, Geometry::SegmentCellRange< typenameboost::polygon::segment_point_type< typenamestd::iterator_traits< SegmentIterator >::value_type >::type > >::type compute_segment_cell_range(const VoronoiDiagram::cell_type &cell, SegmentIterator segment_begin, SegmentIterator segment_end)
Definition VoronoiUtils.cpp:218
static boost::polygon::enable_if< typenameboost::polygon::gtl_if< typenameboost::polygon::is_segment_concept< typenameboost::polygon::geometry_concept< typenamestd::iterator_traits< SegmentIterator >::value_type >::type >::type >::type, Geometry::PointCellRange< typenameboost::polygon::segment_point_type< typenamestd::iterator_traits< SegmentIterator >::value_type >::type > >::type compute_point_cell_range(const VoronoiDiagram::cell_type &cell, SegmentIterator segment_begin, SegmentIterator segment_end)
Definition VoronoiUtils.cpp:268
static Vec2i64 to_point(const VoronoiDiagram::vertex_type *vertex)
Definition VoronoiUtils.cpp:318
static bool is_in_range(double value)
Definition VoronoiUtils.hpp:143
static Points to_points(const ExtrusionLine &extrusion_line)
Definition ExtrusionLine.hpp:243
Slic3r::Geometry::VoronoiDiagram VD
Definition SkeletalTrapezoidation.hpp:32
std::string debug_out_path(const char *name,...)
Definition utils.cpp:382
BoundingBox get_extents(const ExPolygonsWithIds &e)
Definition Emboss.cpp:1374
Lines to_lines(const ExPolygon &src)
Definition ExPolygon.hpp:124
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:311
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:127

References Slic3r::Geometry::VoronoiDiagram::cells(), Slic3r::Geometry::VoronoiUtils::compute_point_cell_range(), Slic3r::Geometry::VoronoiUtils::compute_segment_cell_range(), Slic3r::Geometry::VoronoiDiagram::construct_voronoi(), Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::data, Slic3r::debug_out_path(), Slic3r::Arachne::SkeletalTrapezoidationJoint::distance_to_boundary, Slic3r::SVG::draw_outline(), Slic3r::dump_voronoi_to_svg(), Slic3r::Geometry::PointCellRange< PT >::edge_begin, Slic3r::Geometry::SegmentCellRange< PT >::edge_begin, Slic3r::Geometry::PointCellRange< PT >::edge_end, Slic3r::Geometry::SegmentCellRange< PT >::edge_end, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::from, Slic3r::get_extents(), graph, Slic3r::grid(), Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::incident_edge, Slic3r::Geometry::VoronoiUtils::is_in_range(), Slic3r::Geometry::PointCellRange< PT >::is_valid(), Slic3r::Geometry::SegmentCellRange< PT >::is_valid(), Slic3r::Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(), Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::prev, Slic3r::scaled(), separatePointyQuadEndNodes(), Slic3r::Geometry::PointCellRange< PT >::source_point, Slic3r::Geometry::SegmentCellRange< PT >::source_segment_end_point, Slic3r::Geometry::SegmentCellRange< PT >::source_segment_start_point, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::to, Slic3r::to_lines(), Slic3r::Geometry::VoronoiUtils::to_point(), Slic3r::Arachne::to_points(), transferEdge(), vd_edge_to_he_edge, and vd_node_to_he_node.

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::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.
231{
233
234 /*Terminology in this function assumes that the edge moves horizontally from
235 left to right. This is not necessarily the case; the edge can go in any
236 direction, but it helps to picture it in a certain direction in your head.*/
237
238 const VD::cell_type *left_cell = vd_edge.cell();
239 const VD::cell_type *right_cell = vd_edge.twin()->cell();
240
241 Point start = Geometry::VoronoiUtils::to_point(vd_edge.vertex0()).cast<coord_t>();
242 Point end = Geometry::VoronoiUtils::to_point(vd_edge.vertex1()).cast<coord_t>();
243
244 bool point_left = left_cell->contains_point();
245 bool point_right = right_cell->contains_point();
246 if ((!point_left && !point_right) || vd_edge.is_secondary()) // Source vert is directly connected to source segment
247 {
248 return Points({ start, end });
249 }
250 else if (point_left != point_right) //This is a parabolic edge between a point and a line.
251 {
252 Point p = Geometry::VoronoiUtils::get_source_point(*(point_left ? left_cell : right_cell), segments.begin(), segments.end());
253 const Segment& s = Geometry::VoronoiUtils::get_source_segment(*(point_left ? right_cell : left_cell), segments.begin(), segments.end());
255 }
256 else //This is a straight edge between two points.
257 {
258 /*While the edge is straight, it is still discretized since the part
259 becomes narrower between the two points. As such it may need different
260 beadings along the way.*/
261 Point left_point = Geometry::VoronoiUtils::get_source_point(*left_cell, segments.begin(), segments.end());
262 Point right_point = Geometry::VoronoiUtils::get_source_point(*right_cell, segments.begin(), segments.end());
263 coord_t d = (right_point - left_point).cast<int64_t>().norm();
264 Point middle = (left_point + right_point) / 2;
265 Point x_axis_dir = perp(Point(right_point - left_point));
266 coord_t x_axis_length = x_axis_dir.cast<int64_t>().norm();
267
268 const auto projected_x = [x_axis_dir, x_axis_length, middle](Point from) //Project a point on the edge.
269 {
270 Point vec = from - middle;
271 assert(( vec.cast<int64_t>().dot(x_axis_dir.cast<int64_t>())/ int64_t(x_axis_length)) <= std::numeric_limits<coord_t>::max());
272 coord_t x = vec.cast<int64_t>().dot(x_axis_dir.cast<int64_t>()) / int64_t(x_axis_length);
273 return x;
274 };
275
276 coord_t start_x = projected_x(start);
277 coord_t end_x = projected_x(end);
278
279 //Part of the edge will be bound to the markings on the endpoints of the edge. Calculate how far that is.
280 float bound = 0.5 / tan((M_PI - transitioning_angle) * 0.5);
281 int64_t marking_start_x = - int64_t(d) * bound;
282 int64_t marking_end_x = int64_t(d) * bound;
283
284 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());
285 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());
286 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());
287 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());
288 Point marking_start = middle + (x_axis_dir.cast<int64_t>() * marking_start_x / int64_t(x_axis_length)).cast<coord_t>();
289 Point marking_end = middle + (x_axis_dir.cast<int64_t>() * marking_end_x / int64_t(x_axis_length)).cast<coord_t>();
290 int64_t direction = 1;
291
292 if (start_x > end_x) //Oops, the Voronoi edge is the other way around.
293 {
294 direction = -1;
295 std::swap(marking_start, marking_end);
296 std::swap(marking_start_x, marking_end_x);
297 }
298
299 //Start generating points along the edge.
300 Point a = start;
301 Point b = end;
302 Points ret;
303 ret.emplace_back(a);
304
305 //Introduce an extra edge at the borders of the markings?
306 bool add_marking_start = marking_start_x * direction > int64_t(start_x) * direction;
307 bool add_marking_end = marking_end_x * direction > int64_t(start_x) * direction;
308
309 //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.
310 Point ab = b - a;
311 coord_t ab_size = ab.cast<int64_t>().norm();
312 coord_t step_count = (ab_size + discretization_step_size / 2) / discretization_step_size;
313 if (step_count % 2 == 1)
314 {
315 step_count++; // enforce a discretization point being added in the middle
316 }
317 for (coord_t step = 1; step < step_count; step++)
318 {
319 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!
320 coord_t x_here = projected_x(here); //If we've surpassed the position of the extra markings, we may need to insert them first.
321 if (add_marking_start && marking_start_x * direction < int64_t(x_here) * direction)
322 {
323 ret.emplace_back(marking_start);
324 add_marking_start = false;
325 }
326 if (add_marking_end && marking_end_x * direction < int64_t(x_here) * direction)
327 {
328 ret.emplace_back(marking_end);
329 add_marking_end = false;
330 }
331 ret.emplace_back(here);
332 }
333 if (add_marking_end && marking_end_x * direction < int64_t(end_x) * direction)
334 {
335 ret.emplace_back(marking_end);
336 }
337 ret.emplace_back(b);
338 return ret;
339 }
340}
#define M_PI
Definition ExtrusionSimulator.cpp:31
PolygonsSegmentIndex Segment
Definition SkeletalTrapezoidation.hpp:87
static boost::polygon::enable_if< typenameboost::polygon::gtl_if< typenameboost::polygon::is_segment_concept< typenameboost::polygon::geometry_concept< Segment >::type >::type >::type, Points >::type discretize_parabola(const Point &source_point, const Segment &source_segment, const Point &start, const Point &end, coord_t approximate_step_size, float transitioning_angle)
Definition VoronoiUtils.cpp:120
static boost::polygon::enable_if< typenameboost::polygon::gtl_if< typenameboost::polygon::is_segment_concept< typenameboost::polygon::geometry_concept< typenamestd::iterator_traits< SegmentIterator >::value_type >::type >::type >::type, typenamestd::iterator_traits< SegmentIterator >::reference >::type get_source_segment(const VoronoiDiagram::cell_type &cell, SegmentIterator segment_begin, SegmentIterator segment_end)
Definition VoronoiUtils.cpp:53
static boost::polygon::enable_if< typenameboost::polygon::gtl_if< typenameboost::polygon::is_segment_concept< typenameboost::polygon::geometry_concept< typenamestd::iterator_traits< SegmentIterator >::value_type >::type >::type >::type, typenameboost::polygon::segment_point_type< typenamestd::iterator_traits< SegmentIterator >::value_type >::type >::type get_source_point(const VoronoiDiagram::cell_type &cell, SegmentIterator segment_begin, SegmentIterator segment_end)
Definition VoronoiUtils.cpp:69
Eigen::Matrix< typename Derived::Scalar, 2, 1, Eigen::DontAlign > perp(const Eigen::MatrixBase< Derived > &v)
Definition Point.hpp:130
std::vector< Point, PointsAllocator< Point > > Points
Definition Point.hpp:73
T dot(const boost::geometry::model::d2::point_xy< T > &v1, const boost::geometry::model::d2::point_xy< T > &v2)
Definition ExtrusionSimulator.cpp:154
void step(Coord &crd, const Dir d, const long n=1)
Definition MarchingSquares.hpp:213

References Slic3r::cast(), discretization_step_size, Slic3r::Geometry::VoronoiUtils::discretize_parabola(), Slic3r::dot(), Slic3r::Geometry::VoronoiUtils::get_source_point(), Slic3r::Geometry::VoronoiUtils::get_source_segment(), Slic3r::Geometry::VoronoiUtils::is_in_range(), M_PI, Slic3r::perp(), Slic3r::Geometry::VoronoiUtils::to_point(), 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.
1002{
1003 assert(from_bead_count != to_bead_count);
1004 if (edge_to_start->to->data.bead_count != from_bead_count)
1005 return;
1006
1007 edge_to_start->to->data.bead_count = to_bead_count;
1008 for (edge_t* edge = edge_to_start->next; edge && edge != edge_to_start->twin; edge = edge->twin->next)
1009 {
1010 if (!edge->data.isCentral())
1011 {
1012 continue;
1013 }
1014 dissolveBeadCountRegion(edge, from_bead_count, to_bead_count);
1015 }
1016}
void dissolveBeadCountRegion(edge_t *edge_to_start, coord_t from_bead_count, coord_t to_bead_count)
Definition SkeletalTrapezoidation.cpp:1001

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.
942{
943 std::list<TransitionMidRef> to_be_dissolved;
944 if (traveled_dist > max_dist)
945 return to_be_dissolved;
946
947 bool should_dissolve = true;
948 for (edge_t* edge = edge_to_start->next; edge && edge != edge_to_start->twin; edge = edge->twin->next){
949 if (!edge->data.isCentral())
950 continue;
951
952 Point a = edge->from->p;
953 Point b = edge->to->p;
954 Point ab = b - a;
955 coord_t ab_size = ab.cast<int64_t>().norm();
956 bool is_aligned = edge->isUpward();
957 edge_t* aligned_edge = is_aligned? edge : edge->twin;
958 bool seen_transition_on_this_edge = false;
959
960 const coord_t origin_radius = origin_transition.feature_radius;
961 const coord_t radius_here = edge->from->data.distance_to_boundary;
962 const bool dissolve_result_is_odd = bool(origin_transition.lower_bead_count % 2) == going_up;
963 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
964 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
965 if (line_width_deviation > allowed_filter_deviation)
966 should_dissolve = false;
967
968 if (should_dissolve && aligned_edge->data.hasTransitions()) {
969 auto& transitions = *aligned_edge->data.getTransitions();
970 for (auto transition_it = transitions.begin(); transition_it != transitions.end(); ++ transition_it) { // Note: this is not necessarily iterating in the traveling direction!
971 // Check whether we should dissolve
972 coord_t pos = is_aligned? transition_it->pos : ab_size - transition_it->pos;
973 if (traveled_dist + pos < max_dist && transition_it->lower_bead_count == origin_transition.lower_bead_count) { // Only dissolve local optima
974 if (traveled_dist + pos < beading_strategy.getTransitioningLength(transition_it->lower_bead_count)) {
975 // Consecutive transitions both in/decreasing in bead count should never be closer together than the transition distance
976 assert(going_up != is_aligned || transition_it->lower_bead_count == 0);
977 }
978 to_be_dissolved.emplace_back(aligned_edge, transition_it);
979 seen_transition_on_this_edge = true;
980 }
981 }
982 }
983 if (should_dissolve && !seen_transition_on_this_edge) {
984 std::list<SkeletalTrapezoidation::TransitionMidRef> to_be_dissolved_here = dissolveNearbyTransitions(edge, origin_transition, traveled_dist + ab_size, max_dist, going_up);
985 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.
986 to_be_dissolved.clear();
987 return to_be_dissolved;
988 }
989 to_be_dissolved.splice(to_be_dissolved.end(), to_be_dissolved_here); // Transfer to_be_dissolved_here into to_be_dissolved
990 should_dissolve = should_dissolve && !to_be_dissolved.empty();
991 }
992 }
993
994 if (!should_dissolve)
995 to_be_dissolved.clear();
996
997 return to_be_dissolved;
998}
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:941
Vec3d pos(const Pt &p)
Definition ReprojectPointsOnMesh.hpp:16

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::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.

613{
614 for (edge_t& edge : graph.edges)
615 {
616 if (isEndOfCentral(edge) && edge.to->isLocalMaximum() && !edge.to->isLocalMaximum())
617 {
618 filterCentral(edge.twin, 0, max_length);
619 }
620 }
621}
bool isEndOfCentral(const edge_t &edge) const
Definition SkeletalTrapezoidation.cpp:1344
void filterCentral(coord_t max_length)
Definition SkeletalTrapezoidation.cpp:612

References filterCentral(), graph, isEndOfCentral(), Slic3r::Arachne::STHalfEdgeNode::isLocalMaximum(), 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 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.
624{
625 coord_t length = (starting_edge->from->p - starting_edge->to->p).cast<int64_t>().norm();
626 if (traveled_dist + length > max_length)
627 {
628 return false;
629 }
630
631 bool should_dissolve = true; //Should we unmark this as central and propagate that?
632 for (edge_t* next_edge = starting_edge->next; next_edge && next_edge != starting_edge->twin; next_edge = next_edge->twin->next)
633 {
634 if (next_edge->data.isCentral())
635 {
636 should_dissolve &= filterCentral(next_edge, traveled_dist + length, max_length);
637 }
638 }
639
640 should_dissolve &= !starting_edge->to->isLocalMaximum(); // Don't filter central regions with a local maximum!
641 if (should_dissolve)
642 {
643 starting_edge->data.setIsCentral(false);
644 starting_edge->twin->data.setIsCentral(false);
645 }
646 return should_dissolve;
647}

References Slic3r::cast(), 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.
1019{
1020 if (traveled_dist > max_dist)
1021 {
1022 return false;
1023 }
1024
1025 bool is_end_of_central = true;
1026 bool should_dissolve = false;
1027 for (edge_t* next_edge = edge_to_start->next; next_edge && next_edge != edge_to_start->twin; next_edge = next_edge->twin->next)
1028 {
1029 if (next_edge->data.isCentral())
1030 {
1031 coord_t length = (next_edge->to->p - next_edge->from->p).cast<int64_t>().norm();
1032 should_dissolve |= filterEndOfCentralTransition(next_edge, traveled_dist + length, max_dist, replacing_bead_count);
1033 is_end_of_central = false;
1034 }
1035 }
1036 if (is_end_of_central && traveled_dist < max_dist)
1037 {
1038 should_dissolve = true;
1039 }
1040
1041 if (should_dissolve)
1042 {
1043 edge_to_start->to->data.bead_count = replacing_bead_count;
1044 }
1045 return should_dissolve;
1046}
bool filterEndOfCentralTransition(edge_t *edge_to_start, coord_t traveled_dist, coord_t max_dist, coord_t replacing_bead_count)
Definition SkeletalTrapezoidation.cpp:1018

References Slic3r::Arachne::SkeletalTrapezoidationJoint::bead_count, Slic3r::cast(), 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.

693{
694 for (edge_t& edge : graph.edges)
695 {
696 if (!isEndOfCentral(edge))
697 {
698 continue;
699 }
700 if(edge.to->data.bead_count < 0 && edge.to->data.distance_to_boundary != 0)
701 {
702 BOOST_LOG_TRIVIAL(warning) << "Encountered an uninitialized bead at the boundary!";
703 }
704 assert(edge.to->data.bead_count >= 0 || edge.to->data.distance_to_boundary == 0);
705 const coord_t max_dist = scaled<coord_t>(0.4);
706 filterNoncentralRegions(&edge, edge.to->data.bead_count, 0, max_dist);
707 }
708}
void filterNoncentralRegions()
Definition SkeletalTrapezoidation.cpp:692

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, filterNoncentralRegions(), graph, isEndOfCentral(), Slic3r::scaled(), and Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::to.

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
711{
712 coord_t r = to_edge->to->data.distance_to_boundary;
713
714 edge_t* next_edge = to_edge->next;
715 for (; next_edge && next_edge != to_edge->twin; next_edge = next_edge->twin->next)
716 {
717 if (next_edge->to->data.distance_to_boundary >= r || shorter_then(next_edge->to->p - next_edge->from->p, scaled<coord_t>(0.01)))
718 {
719 break; // Only walk upward
720 }
721 }
722 if (next_edge == to_edge->twin || ! next_edge)
723 {
724 return false;
725 }
726
727 const coord_t length = (next_edge->to->p - next_edge->from->p).cast<int64_t>().norm();
728
729 bool dissolve = false;
730 if (next_edge->to->data.bead_count == bead_count)
731 {
732 dissolve = true;
733 }
734 else if (next_edge->to->data.bead_count < 0)
735 {
736 dissolve = filterNoncentralRegions(next_edge, bead_count, traveled_dist + length, max_dist);
737 }
738 else // Upward bead count is different
739 {
740 // Dissolve if two central regions with different bead count are closer together than the max_dist (= transition distance)
741 dissolve = (traveled_dist + length < max_dist) && std::abs(next_edge->to->data.bead_count - bead_count) == 1;
742 }
743
744 if (dissolve)
745 {
746 next_edge->data.setIsCentral(true);
747 next_edge->twin->data.setIsCentral(true);
748 next_edge->to->data.bead_count = beading_strategy.getOptimalBeadCount(next_edge->to->data.distance_to_boundary * 2);
749 next_edge->to->data.transition_ratio = 0;
750 }
751 return dissolve; // Dissolving only depend on the one edge going upward. There cannot be multiple edges going upward.
752}

References Slic3r::Arachne::SkeletalTrapezoidationJoint::bead_count, beading_strategy, Slic3r::cast(), 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::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::scaled(), 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.

650{
651 for (edge_t& edge : graph.edges)
652 {
653 if (!edge.prev)
654 {
655 edge.data.setIsCentral(false);
656 edge.twin->data.setIsCentral(false);
657 }
658 }
659}

References Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::data, graph, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::prev, Slic3r::Arachne::SkeletalTrapezoidationEdge::setIsCentral(), 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:

◆ 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.

873{
874 for (edge_t& edge : graph.edges)
875 {
876 if (! edge.data.hasTransitions())
877 {
878 continue;
879 }
880 auto& transitions = *edge.data.getTransitions();
881
882 // This is how stuff should be stored in transitions
883 assert(transitions.front().lower_bead_count <= transitions.back().lower_bead_count);
884 assert(edge.from->data.distance_to_boundary <= edge.to->data.distance_to_boundary);
885
886 const Point a = edge.from->p;
887 const Point b = edge.to->p;
888 Point ab = b - a;
889 coord_t ab_size = ab.cast<int64_t>().norm();
890
891 bool going_up = true;
892 std::list<TransitionMidRef> to_be_dissolved_back = dissolveNearbyTransitions(&edge, transitions.back(), ab_size - transitions.back().pos, transition_filter_dist, going_up);
893 bool should_dissolve_back = !to_be_dissolved_back.empty();
894 for (TransitionMidRef& ref : to_be_dissolved_back)
895 {
896 dissolveBeadCountRegion(&edge, transitions.back().lower_bead_count + 1, transitions.back().lower_bead_count);
897 ref.edge->data.getTransitions()->erase(ref.transition_it);
898 }
899
900 {
901 coord_t trans_bead_count = transitions.back().lower_bead_count;
902 coord_t upper_transition_half_length = (1.0 - beading_strategy.getTransitionAnchorPos(trans_bead_count)) * beading_strategy.getTransitioningLength(trans_bead_count);
903 should_dissolve_back |= filterEndOfCentralTransition(&edge, ab_size - transitions.back().pos, upper_transition_half_length, trans_bead_count);
904 }
905
906 if (should_dissolve_back)
907 {
908 transitions.pop_back();
909 }
910 if (transitions.empty())
911 { // FilterEndOfCentralTransition gives inconsistent new bead count when executing for the same transition in two directions.
912 continue;
913 }
914
915 going_up = false;
916 std::list<TransitionMidRef> to_be_dissolved_front = dissolveNearbyTransitions(edge.twin, transitions.front(), transitions.front().pos, transition_filter_dist, going_up);
917 bool should_dissolve_front = !to_be_dissolved_front.empty();
918 for (TransitionMidRef& ref : to_be_dissolved_front)
919 {
920 dissolveBeadCountRegion(edge.twin, transitions.front().lower_bead_count, transitions.front().lower_bead_count + 1);
921 ref.edge->data.getTransitions()->erase(ref.transition_it);
922 }
923
924 {
925 coord_t trans_bead_count = transitions.front().lower_bead_count;
926 coord_t lower_transition_half_length = beading_strategy.getTransitionAnchorPos(trans_bead_count) * beading_strategy.getTransitioningLength(trans_bead_count);
927 should_dissolve_front |= filterEndOfCentralTransition(edge.twin, transitions.front().pos, lower_transition_half_length, trans_bead_count + 1);
928 }
929
930 if (should_dissolve_front)
931 {
932 transitions.pop_front();
933 }
934 if (transitions.empty())
935 { // FilterEndOfCentralTransition gives inconsistent new bead count when executing for the same transition in two directions.
936 continue;
937 }
938 }
939}
Definition SkeletalTrapezoidation.hpp:144

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, dissolveBeadCountRegion(), dissolveNearbyTransitions(), Slic3r::Arachne::SkeletalTrapezoidationJoint::distance_to_boundary, filterEndOfCentralTransition(), Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::from, Slic3r::Arachne::SkeletalTrapezoidationEdge::getTransitions(), graph, Slic3r::Arachne::SkeletalTrapezoidationEdge::hasTransitions(), 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, transition_filter_dist, and Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::twin.

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.
1049{
1050 for (edge_t& edge : graph.edges)
1051 {
1052 if (! edge.data.hasTransitions())
1053 {
1054 continue;
1055 }
1056 auto& transition_positions = *edge.data.getTransitions();
1057
1058 assert(edge.from->data.distance_to_boundary <= edge.to->data.distance_to_boundary);
1059 for (TransitionMiddle& transition_middle : transition_positions)
1060 {
1061 assert(transition_positions.front().pos <= transition_middle.pos);
1062 assert(transition_middle.pos <= transition_positions.back().pos);
1063 generateTransitionEnds(edge, transition_middle.pos, transition_middle.lower_bead_count, edge_transition_ends);
1064 }
1065 }
1066}
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:1068

References 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, generateTransitionEnds(), Slic3r::Arachne::SkeletalTrapezoidationEdge::getTransitions(), graph, Slic3r::Arachne::SkeletalTrapezoidationEdge::hasTransitions(), and Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::to.

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.

1366{
1367 const auto _snap_dist = snap_dist();
1368 for (auto edge_it = graph.edges.begin(); edge_it != graph.edges.end(); ++edge_it)
1369 {
1370 edge_t& edge = *edge_it;
1371
1372 if (!edge.data.isCentral()
1373 || shorter_then(edge.to->p - edge.from->p, discretization_step_size)
1374 || edge.from->data.distance_to_boundary >= edge.to->data.distance_to_boundary)
1375 {
1376 continue;
1377 }
1378
1379
1380 std::vector<coord_t> rib_thicknesses = beading_strategy.getNonlinearThicknesses(edge.from->data.bead_count);
1381
1382 if (rib_thicknesses.empty()) continue;
1383
1384 // Preload some variables before [edge] gets changed
1385 node_t* from = edge.from;
1386 node_t* to = edge.to;
1387 Point a = from->p;
1388 Point b = to->p;
1389 Point ab = b - a;
1390 coord_t ab_size = ab.cast<int64_t>().norm();
1391 coord_t a_R = edge.from->data.distance_to_boundary;
1392 coord_t b_R = edge.to->data.distance_to_boundary;
1393
1394 edge_t* last_edge_replacing_input = &edge;
1395 for (coord_t rib_thickness : rib_thicknesses)
1396 {
1397 if (rib_thickness / 2 <= a_R)
1398 {
1399 continue;
1400 }
1401 if (rib_thickness / 2 >= b_R)
1402 {
1403 break;
1404 }
1405
1406 coord_t new_node_bead_count = std::min(edge.from->data.bead_count, edge.to->data.bead_count);
1407 coord_t end_pos = int64_t(ab_size) * int64_t(rib_thickness / 2 - a_R) / int64_t(b_R - a_R);
1408 assert(end_pos > 0);
1409 assert(end_pos < ab_size);
1410 node_t* close_node = (end_pos < ab_size / 2)? from : to;
1411 if ((end_pos < _snap_dist || end_pos > ab_size - _snap_dist)
1412 && close_node->data.bead_count == new_node_bead_count
1413 )
1414 {
1415 assert(end_pos <= ab_size);
1416 close_node->data.transition_ratio = 0;
1417 continue;
1418 }
1419 Point mid = a + normal(ab, end_pos);
1420
1421 assert(last_edge_replacing_input->data.isCentral());
1422 assert(last_edge_replacing_input->data.type != SkeletalTrapezoidationEdge::EdgeType::EXTRA_VD);
1423 last_edge_replacing_input = graph.insertNode(last_edge_replacing_input, mid, new_node_bead_count);
1424 assert(last_edge_replacing_input->data.type != SkeletalTrapezoidationEdge::EdgeType::EXTRA_VD);
1425 assert(last_edge_replacing_input->data.isCentral());
1426 }
1427 }
1428}

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::SkeletalTrapezoidationEdge::EXTRA_VD, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::from, graph, 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
1742{
1743 for (edge_t& edge_ : graph.edges)
1744 {
1745 edge_t* edge = &edge_;
1746 if (edge->from->data.distance_to_boundary > edge->to->data.distance_to_boundary)
1747 { // Only consider the upward half-edges
1748 continue;
1749 }
1750
1751 coord_t start_R = edge->to->data.distance_to_boundary; // higher R
1752 coord_t end_R = edge->from->data.distance_to_boundary; // lower R
1753
1754 if ((edge->from->data.bead_count == edge->to->data.bead_count && edge->from->data.bead_count >= 0)
1755 || end_R >= start_R)
1756 { // No beads to generate
1757 continue;
1758 }
1759
1760 Beading* beading = &getOrCreateBeading(edge->to, node_beadings)->beading;
1761 edge_junctions.emplace_back(std::make_shared<LineJunctions>());
1762 edge_.data.setExtrusionJunctions(edge_junctions.back()); // initialization
1763 LineJunctions& ret = *edge_junctions.back();
1764
1765 assert(beading->total_thickness >= edge->to->data.distance_to_boundary * 2);
1766 if(beading->total_thickness < edge->to->data.distance_to_boundary * 2)
1767 {
1768 BOOST_LOG_TRIVIAL(warning) << "Generated junction is beyond the center of total width.";
1769 }
1770
1771 Point a = edge->to->p;
1772 Point b = edge->from->p;
1773 Point ab = b - a;
1774
1775 const size_t num_junctions = beading->toolpath_locations.size();
1776 size_t junction_idx;
1777 // Compute starting junction_idx for this segment
1778 for (junction_idx = (std::max(size_t(1), beading->toolpath_locations.size()) - 1) / 2; junction_idx < num_junctions; junction_idx--)
1779 {
1780 coord_t bead_R = beading->toolpath_locations[junction_idx];
1781 // toolpath_locations computed inside DistributedBeadingStrategy could be off by 1 because of rounding errors.
1782 // In GH issue #8472, these roundings errors caused missing the middle extrusion.
1783 // Adding small epsilon should help resolve those cases.
1784 if (bead_R <= start_R + 1)
1785 { // Junction coinciding with start node is used in this function call
1786 break;
1787 }
1788 }
1789
1790 // Robustness against odd segments which might lie just slightly outside of the range due to rounding errors
1791 // not sure if this is really needed (TODO)
1792 if (junction_idx + 1 < num_junctions
1793 && beading->toolpath_locations[junction_idx + 1] <= start_R + scaled<coord_t>(0.005)
1794 && beading->total_thickness < start_R + scaled<coord_t>(0.005)
1795 )
1796 {
1797 junction_idx++;
1798 }
1799
1800 for (; junction_idx < num_junctions; junction_idx--) //When junction_idx underflows, it'll be more than num_junctions too.
1801 {
1802 coord_t bead_R = beading->toolpath_locations[junction_idx];
1803 assert(bead_R >= 0);
1804 if (bead_R < end_R)
1805 { // Junction coinciding with a node is handled by the next segment
1806 break;
1807 }
1808 Point junction(a + (ab.cast<int64_t>() * int64_t(bead_R - start_R) / int64_t(end_R - start_R)).cast<coord_t>());
1809 if (bead_R > start_R - scaled<coord_t>(0.005))
1810 { // Snap to start node if it is really close, in order to be able to see 3-way intersection later on more robustly
1811 junction = a;
1812 }
1813 ret.emplace_back(junction, beading->bead_widths[junction_idx], junction_idx);
1814 }
1815 }
1816}
std::shared_ptr< BeadingPropagation > getOrCreateBeading(node_t *node, ptr_vector_t< BeadingPropagation > &node_beadings)
Definition SkeletalTrapezoidation.cpp:1818
BeadingStrategy::Beading Beading
Definition SkeletalTrapezoidation.hpp:58

References Slic3r::Arachne::SkeletalTrapezoidationJoint::bead_count, Slic3r::Arachne::BeadingStrategy::Beading::bead_widths, Slic3r::cast(), 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, generateJunctions(), getOrCreateBeading(), graph, Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::p, Slic3r::scaled(), Slic3r::Arachne::SkeletalTrapezoidationEdge::setExtrusionJunctions(), 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.

Referenced by generateJunctions().

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

◆ generateLocalMaximaSingleBeads()

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

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

2071{
2072 std::vector<VariableWidthLines> &generated_toolpaths = *p_generated_toolpaths;
2073
2074 for (auto& node : graph.nodes)
2075 {
2076 if (! node.data.hasBeading())
2077 {
2078 continue;
2079 }
2080 Beading& beading = node.data.getBeading()->beading;
2081 if (beading.bead_widths.size() % 2 == 1 && node.isLocalMaximum(true) && !node.isCentral())
2082 {
2083 const size_t inset_index = beading.bead_widths.size() / 2;
2084 constexpr bool is_odd = true;
2085 if (inset_index >= generated_toolpaths.size())
2086 {
2087 generated_toolpaths.resize(inset_index + 1);
2088 }
2089 generated_toolpaths[inset_index].emplace_back(inset_index, is_odd);
2090 ExtrusionLine& line = generated_toolpaths[inset_index].back();
2091 const coord_t width = beading.bead_widths[inset_index];
2092 // total area to be extruded is pi*(w/2)^2 = pi*w*w/4
2093 // Width a constant extrusion width w, that would be a length of pi*w/4
2094 // If we make a small circle to fill up the hole, then that circle would have a circumference of 2*pi*r
2095 // So our circle needs to be such that r=w/8
2096 const coord_t r = width / 8;
2097 constexpr coord_t n_segments = 6;
2098 for (coord_t segment = 0; segment < n_segments; segment++) {
2099 float a = 2.0 * M_PI / n_segments * segment;
2100 line.junctions.emplace_back(node.p + Point(r * cos(a), r * sin(a)), width, inset_index);
2101 }
2102 }
2103 }
2104}
void segment(CGALMesh &src, std::vector< CGALMesh > &dst, double smoothing_alpha=0.5, int segment_number=5)
Definition MeshBoolean.cpp:480
coord_t width(const BoundingBox &box)
Definition Arrange.cpp:1184

References Slic3r::Arachne::BeadingStrategy::Beading::bead_widths, generateLocalMaximaSingleBeads(), graph, Slic3r::Arachne::ExtrusionLine::junctions, M_PI, and p_generated_toolpaths.

Referenced by generateLocalMaximaSingleBeads().

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

◆ generateSegments()

void Slic3r::Arachne::SkeletalTrapezoidation::generateSegments ( )
protected
Parameters
[out]segmentsthe generated segments
1439{
1440 std::vector<edge_t*> upward_quad_mids;
1441 for (edge_t& edge : graph.edges)
1442 {
1443 if (edge.prev && edge.next && edge.isUpward())
1444 {
1445 upward_quad_mids.emplace_back(&edge);
1446 }
1447 }
1448
1449 std::sort(upward_quad_mids.begin(), upward_quad_mids.end(), [](edge_t* a, edge_t* b)
1450 {
1451 if (a->to->data.distance_to_boundary == b->to->data.distance_to_boundary)
1452 { // Ordering between two 'upward' edges of the same distance is important when one of the edges is flat and connected to the other
1453 if (a->from->data.distance_to_boundary == a->to->data.distance_to_boundary
1454 && b->from->data.distance_to_boundary == b->to->data.distance_to_boundary)
1455 {
1456 coord_t max = std::numeric_limits<coord_t>::max();
1457 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();
1458 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();
1459 return a_dist_from_up < b_dist_from_up;
1460 }
1461 else if (a->from->data.distance_to_boundary == a->to->data.distance_to_boundary)
1462 {
1463 return true; // Edge a might be 'above' edge b
1464 }
1465 else if (b->from->data.distance_to_boundary == b->to->data.distance_to_boundary)
1466 {
1467 return false; // Edge b might be 'above' edge a
1468 }
1469 else
1470 {
1471 // Ordering is not important
1472 }
1473 }
1474 return a->to->data.distance_to_boundary > b->to->data.distance_to_boundary;
1475 });
1476
1478 { // Store beading
1479 for (node_t& node : graph.nodes)
1480 {
1481 if (node.data.bead_count <= 0)
1482 {
1483 continue;
1484 }
1485 if (node.data.transition_ratio == 0)
1486 {
1487 node_beadings.emplace_back(new BeadingPropagation(beading_strategy.compute(node.data.distance_to_boundary * 2, node.data.bead_count)));
1488 node.data.setBeading(node_beadings.back());
1489 assert(node_beadings.back()->beading.total_thickness == node.data.distance_to_boundary * 2);
1490 if(node_beadings.back()->beading.total_thickness != node.data.distance_to_boundary * 2)
1491 {
1492 BOOST_LOG_TRIVIAL(warning) << "If transitioning to an endpoint (ratio 0), the node should be exactly in the middle.";
1493 }
1494 }
1495 else
1496 {
1497 Beading low_count_beading = beading_strategy.compute(node.data.distance_to_boundary * 2, node.data.bead_count);
1498 Beading high_count_beading = beading_strategy.compute(node.data.distance_to_boundary * 2, node.data.bead_count + 1);
1499 Beading merged = interpolate(low_count_beading, 1.0 - node.data.transition_ratio, high_count_beading);
1500 node_beadings.emplace_back(new BeadingPropagation(merged));
1501 node.data.setBeading(node_beadings.back());
1502 assert(merged.total_thickness == node.data.distance_to_boundary * 2);
1503 if(merged.total_thickness != node.data.distance_to_boundary * 2)
1504 {
1505 BOOST_LOG_TRIVIAL(warning) << "If merging two beads, the new bead must be exactly in the middle.";
1506 }
1507 }
1508 }
1509 }
1510
1511#ifdef ARACHNE_DEBUG
1512 static int iRun = 0;
1513 export_graph_to_svg(debug_out_path("ST-generateSegments-before-propagation-%d.svg", iRun), this->graph, this->outline);
1514#endif
1515
1516 propagateBeadingsUpward(upward_quad_mids, node_beadings);
1517
1518#ifdef ARACHNE_DEBUG
1519 export_graph_to_svg(debug_out_path("ST-generateSegments-upward-propagation-%d.svg", iRun), this->graph, this->outline);
1520#endif
1521
1522 propagateBeadingsDownward(upward_quad_mids, node_beadings);
1523
1524#ifdef ARACHNE_DEBUG
1525 export_graph_to_svg(debug_out_path("ST-generateSegments-downward-propagation-%d.svg", iRun), this->graph, this->outline);
1526#endif
1527
1528 ptr_vector_t<LineJunctions> edge_junctions; // junctions ordered high R to low R
1529 generateJunctions(node_beadings, edge_junctions);
1530
1531#ifdef ARACHNE_DEBUG
1532 export_graph_to_svg(debug_out_path("ST-generateSegments-junctions-%d.svg", iRun), this->graph, this->outline, edge_junctions);
1533#endif
1534
1535 connectJunctions(edge_junctions);
1536
1538
1539#ifdef ARACHNE_DEBUG
1540 ++iRun;
1541#endif
1542}
void propagateBeadingsUpward(std::vector< edge_t * > &upward_quad_mids, ptr_vector_t< BeadingPropagation > &node_beadings)
Definition SkeletalTrapezoidation.cpp:1569
std::vector< std::shared_ptr< T > > ptr_vector_t
Definition SkeletalTrapezoidation.hpp:64
void connectJunctions(ptr_vector_t< LineJunctions > &edge_junctions)
Definition SkeletalTrapezoidation.cpp:1948
void generateLocalMaximaSingleBeads()
Definition SkeletalTrapezoidation.cpp:2070
void propagateBeadingsDownward(std::vector< edge_t * > &upward_quad_mids, ptr_vector_t< BeadingPropagation > &node_beadings)
Definition SkeletalTrapezoidation.cpp:1599
SkeletalTrapezoidationJoint::BeadingPropagation BeadingPropagation
Definition SkeletalTrapezoidation.hpp:59
static Beading interpolate(const Beading &left, double ratio_left_to_whole, const Beading &right, coord_t switching_radius)
Definition SkeletalTrapezoidation.cpp:1671
void generateJunctions(ptr_vector_t< BeadingPropagation > &node_beadings, ptr_vector_t< LineJunctions > &edge_junctions)
Definition SkeletalTrapezoidation.cpp:1741

References Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::data, Slic3r::Arachne::SkeletalTrapezoidationJoint::distance_to_boundary, graph, Slic3r::Arachne::STHalfEdge::isUpward(), 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.

Referenced by generateToolpaths().

Here is the call graph for this function:
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.
505{
506#ifdef ARACHNE_DEBUG
507 static int iRun = 0;
508#endif
509
510 p_generated_toolpaths = &generated_toolpaths;
511
513
514#ifdef ARACHNE_DEBUG
515 export_graph_to_svg(debug_out_path("ST-updateIsCentral-final-%d.svg", iRun), this->graph, this->outline);
516#endif
517
519
520#ifdef ARACHNE_DEBUG
521 export_graph_to_svg(debug_out_path("ST-filterCentral-final-%d.svg", iRun), this->graph, this->outline);
522#endif
523
524 if (filter_outermost_central_edges)
526
528
529#ifdef ARACHNE_DEBUG
530 export_graph_to_svg(debug_out_path("ST-updateBeadCount-final-%d.svg", iRun), this->graph, this->outline);
531#endif
532
534
535#ifdef ARACHNE_DEBUG
536 export_graph_to_svg(debug_out_path("ST-filterNoncentralRegions-final-%d.svg", iRun), this->graph, this->outline);
537#endif
538
540
541#ifdef ARACHNE_DEBUG
542 export_graph_to_svg(debug_out_path("ST-generateTransitioningRibs-final-%d.svg", iRun), this->graph, this->outline);
543#endif
544
546
547#ifdef ARACHNE_DEBUG
548 export_graph_to_svg(debug_out_path("ST-generateExtraRibs-final-%d.svg", iRun), this->graph, this->outline);
549#endif
550
552
553#ifdef ARACHNE_DEBUG
554 export_graph_to_svg(debug_out_path("ST-generateSegments-final-%d.svg", iRun), this->graph, this->outline);
555#endif
556
557#ifdef ARACHNE_DEBUG
558 ++iRun;
559#endif
560}
void generateTransitioningRibs()
Definition SkeletalTrapezoidation.cpp:754
void generateSegments()
Definition SkeletalTrapezoidation.cpp:1438
void filterOuterCentral()
Definition SkeletalTrapezoidation.cpp:649
coord_t central_filter_dist()
Generic arithmatic inaccuracy. Only used to determine whether a transition really needs to insert an ...
Definition SkeletalTrapezoidation.hpp:72
void updateBeadCount()
Definition SkeletalTrapezoidation.cpp:661
void generateExtraRibs()
Definition SkeletalTrapezoidation.cpp:1365
void updateIsCentral()
Definition SkeletalTrapezoidation.cpp:562

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).
1106{
1107 Point a = edge.from->p;
1108 Point b = edge.to->p;
1109 Point ab = b - a;
1110 coord_t ab_size = ab.cast<int64_t>().norm(); // TODO: prevent recalculation of these values
1111
1112 assert(start_pos <= ab_size);
1113 if(start_pos > ab_size)
1114 {
1115 BOOST_LOG_TRIVIAL(warning) << "Start position of edge is beyond edge range.";
1116 }
1117
1118 bool going_up = end_rest > start_rest;
1119
1120 assert(edge.data.isCentral());
1121 if (!edge.data.isCentral())
1122 {
1123 BOOST_LOG_TRIVIAL(warning) << "This function shouldn't generate ends in or beyond non-central regions.";
1124 return false;
1125 }
1126
1127 if (end_pos > ab_size)
1128 { // Recurse on all further edges
1129 float rest = end_rest - (start_rest - end_rest) * (end_pos - ab_size) / (start_pos - end_pos);
1130 assert(rest >= 0);
1131 assert(rest <= std::max(end_rest, start_rest));
1132 assert(rest >= std::min(end_rest, start_rest));
1133
1134 coord_t central_edge_count = 0;
1135 for (edge_t* outgoing = edge.next; outgoing && outgoing != edge.twin; outgoing = outgoing->twin->next)
1136 {
1137 if (!outgoing->data.isCentral()) continue;
1138 central_edge_count++;
1139 }
1140
1141 bool is_only_going_down = true;
1142 bool has_recursed = false;
1143 for (edge_t* outgoing = edge.next; outgoing && outgoing != edge.twin;)
1144 {
1145 edge_t* next = outgoing->twin->next; // Before we change the outgoing edge itself
1146 if (!outgoing->data.isCentral())
1147 {
1148 outgoing = next;
1149 continue; // Don't put transition ends in non-central regions
1150 }
1151 if (central_edge_count > 1 && going_up && isGoingDown(outgoing, 0, end_pos - ab_size + transition_half_length, lower_bead_count))
1152 { // We're after a 3-way_all-central_junction-node and going in the direction of lower bead count
1153 // don't introduce a transition end along this central direction, because this direction is the downward direction
1154 // while we are supposed to be [going_up]
1155 outgoing = next;
1156 continue;
1157 }
1158 bool is_going_down = generateTransitionEnd(*outgoing, 0, end_pos - ab_size, transition_half_length, rest, end_rest, lower_bead_count, edge_transition_ends);
1159 is_only_going_down &= is_going_down;
1160 outgoing = next;
1161 has_recursed = true;
1162 }
1163 if (!going_up || (has_recursed && !is_only_going_down))
1164 {
1165 edge.to->data.transition_ratio = rest;
1166 edge.to->data.bead_count = lower_bead_count;
1167 }
1168 return is_only_going_down;
1169 }
1170 else // end_pos < ab_size
1171 { // Add transition end point here
1172 bool is_lower_end = end_rest == 0; // TODO collapse this parameter into the bool for which it is used here!
1173 coord_t pos = -1;
1174
1175 edge_t* upward_edge = nullptr;
1176 if (edge.isUpward())
1177 {
1178 upward_edge = &edge;
1179 pos = end_pos;
1180 }
1181 else
1182 {
1183 upward_edge = edge.twin;
1184 pos = ab_size - end_pos;
1185 }
1186
1187 if(!upward_edge->data.hasTransitionEnds())
1188 {
1189 //This edge doesn't have a data structure yet for the transition ends. Make one.
1190 edge_transition_ends.emplace_back(std::make_shared<std::list<TransitionEnd>>());
1191 upward_edge->data.setTransitionEnds(edge_transition_ends.back());
1192 }
1193 auto transitions = upward_edge->data.getTransitionEnds();
1194
1195 //Add a transition to it (on the correct side).
1196 assert(ab_size == (edge.twin->from->p - edge.twin->to->p).cast<int64_t>().norm());
1197 assert(pos <= ab_size);
1198 if (transitions->empty() || pos < transitions->front().pos)
1199 { // Preorder so that sorting later on is faster
1200 transitions->emplace_front(pos, lower_bead_count, is_lower_end);
1201 }
1202 else
1203 {
1204 transitions->emplace_back(pos, lower_bead_count, is_lower_end);
1205 }
1206 return false;
1207 }
1208}
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:1105
bool isGoingDown(edge_t *outgoing, coord_t traveled_dist, coord_t transition_half_length, coord_t lower_bead_count) const
Definition SkeletalTrapezoidation.cpp:1211

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.
1069{
1070 const Point a = edge.from->p;
1071 const Point b = edge.to->p;
1072 const Point ab = b - a;
1073 const coord_t ab_size = ab.cast<int64_t>().norm();
1074
1075 const coord_t transition_length = beading_strategy.getTransitioningLength(lower_bead_count);
1076 const float transition_mid_position = beading_strategy.getTransitionAnchorPos(lower_bead_count);
1077 constexpr float inner_bead_width_ratio_after_transition = 1.0;
1078
1079 constexpr coord_t start_rest = 0;
1080 const float mid_rest = transition_mid_position * inner_bead_width_ratio_after_transition;
1081 constexpr float end_rest = inner_bead_width_ratio_after_transition;
1082
1083 { // Lower bead count transition end
1084 const coord_t start_pos = ab_size - mid_pos;
1085 const coord_t transition_half_length = transition_mid_position * int64_t(transition_length);
1086 const coord_t end_pos = start_pos + transition_half_length;
1087 generateTransitionEnd(*edge.twin, start_pos, end_pos, transition_half_length, mid_rest, start_rest, lower_bead_count, edge_transition_ends);
1088 }
1089
1090 { // Upper bead count transition end
1091 const coord_t start_pos = mid_pos;
1092 const coord_t transition_half_length = (1.0 - transition_mid_position) * transition_length;
1093 const coord_t end_pos = mid_pos + transition_half_length;
1094#ifdef DEBUG
1095 if (! generateTransitionEnd(edge, start_pos, end_pos, transition_half_length, mid_rest, end_rest, lower_bead_count, edge_transition_ends))
1096 {
1097 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!";
1098 }
1099#else
1100 generateTransitionEnd(edge, start_pos, end_pos, transition_half_length, mid_rest, end_rest, lower_bead_count, edge_transition_ends);
1101#endif
1102 }
1103}

References beading_strategy, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::from, generateTransitionEnd(), 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.

755{
756 // Store the upward edges to the transitions.
757 // We only store the halfedge for which the distance_to_boundary is higher at the end than at the beginning.
759 generateTransitionMids(edge_transitions);
760
761 for (edge_t& edge : graph.edges)
762 { // Check if there is a transition in between nodes with different bead counts
763 if (edge.data.isCentral() && edge.from->data.bead_count != edge.to->data.bead_count)
764 {
765 assert(edge.data.hasTransitions() || edge.twin->data.hasTransitions());
766 }
767 }
768
770
771#ifdef ARACHNE_DEBUG
772 static int iRun = 0;
773 export_graph_to_svg(debug_out_path("ST-generateTransitioningRibs-mids-%d.svg", iRun++), this->graph, this->outline);
774#endif
775
776 ptr_vector_t<std::list<TransitionEnd>> edge_transition_ends; // We only map the half edge in the upward direction. mapped items are not sorted
777 generateAllTransitionEnds(edge_transition_ends);
778
779#ifdef ARACHNE_DEBUG
780 export_graph_to_svg(debug_out_path("ST-generateTransitioningRibs-ends-%d.svg", iRun++), this->graph, this->outline);
781#endif
782
783 applyTransitions(edge_transition_ends);
784 // Note that the shared pointer lists will be out of scope and thus destroyed here, since the remaining refs are weak_ptr.
785
786#ifdef ARACHNE_DEBUG
787 ++iRun;
788#endif
789}
void generateAllTransitionEnds(ptr_vector_t< std::list< TransitionEnd > > &edge_transition_ends)
Definition SkeletalTrapezoidation.cpp:1048
void generateTransitionMids(ptr_vector_t< std::list< TransitionMiddle > > &edge_transitions)
Definition SkeletalTrapezoidation.cpp:792
void applyTransitions(ptr_vector_t< std::list< TransitionEnd > > &edge_transition_ends)
Definition SkeletalTrapezoidation.cpp:1277
void filterTransitionMids()
Definition SkeletalTrapezoidation.cpp:872

References applyTransitions(), 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::debug_out_path(), filterTransitionMids(), Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::from, generateAllTransitionEnds(), generateTransitionMids(), graph, Slic3r::Arachne::SkeletalTrapezoidationEdge::hasTransitions(), Slic3r::Arachne::SkeletalTrapezoidationEdge::isCentral(), 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:

◆ 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.
793{
794 for (edge_t& edge : graph.edges)
795 {
796 assert(edge.data.centralIsSet());
797 if (!edge.data.isCentral())
798 { // Only central regions introduce transitions
799 continue;
800 }
801 coord_t start_R = edge.from->data.distance_to_boundary;
802 coord_t end_R = edge.to->data.distance_to_boundary;
803 int start_bead_count = edge.from->data.bead_count;
804 int end_bead_count = edge.to->data.bead_count;
805
806 if (start_R == end_R)
807 { // No transitions occur when both end points have the same distance_to_boundary
808 assert(edge.from->data.bead_count == edge.to->data.bead_count);
809 if(edge.from->data.bead_count != edge.to->data.bead_count)
810 {
811 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.";
812 }
813 continue;
814 }
815 else if (start_R > end_R)
816 { // Only consider those half-edges which are going from a lower to a higher distance_to_boundary
817 continue;
818 }
819
820 if (edge.from->data.bead_count == edge.to->data.bead_count)
821 { // No transitions should occur according to the enforced bead counts
822 continue;
823 }
824
825 if (start_bead_count > beading_strategy.getOptimalBeadCount(start_R * 2)
826 || end_bead_count > beading_strategy.getOptimalBeadCount(end_R * 2))
827 { // Wasn't the case earlier in this function because of already introduced transitions
828 BOOST_LOG_TRIVIAL(error) << "transitioning segment overlap! (?)";
829 }
830 assert(start_R < end_R);
831 if(start_R >= end_R)
832 {
833 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;
834 }
835 coord_t edge_size = (edge.from->p - edge.to->p).cast<int64_t>().norm();
836 for (int transition_lower_bead_count = start_bead_count; transition_lower_bead_count < end_bead_count; transition_lower_bead_count++)
837 {
838 coord_t mid_R = beading_strategy.getTransitionThickness(transition_lower_bead_count) / 2;
839 if (mid_R > end_R)
840 {
841 BOOST_LOG_TRIVIAL(error) << "transition on segment lies outside of segment!";
842 mid_R = end_R;
843 }
844 if (mid_R < start_R)
845 {
846 BOOST_LOG_TRIVIAL(error) << "transition on segment lies outside of segment!";
847 mid_R = start_R;
848 }
849 coord_t mid_pos = int64_t(edge_size) * int64_t(mid_R - start_R) / int64_t(end_R - start_R);
850
851 assert(mid_pos >= 0);
852 assert(mid_pos <= edge_size);
853 if(mid_pos < 0 || mid_pos > edge_size)
854 {
855 BOOST_LOG_TRIVIAL(warning) << "Transition mid is out of bounds of the edge.";
856 }
857 auto transitions = edge.data.getTransitions();
858 constexpr bool ignore_empty = true;
859 assert((! edge.data.hasTransitions(ignore_empty)) || mid_pos >= transitions->back().pos);
860 if (! edge.data.hasTransitions(ignore_empty))
861 {
862 edge_transitions.emplace_back(std::make_shared<std::list<TransitionMiddle>>());
863 edge.data.setTransitions(edge_transitions.back()); // initialization
864 transitions = edge.data.getTransitions();
865 }
866 transitions->emplace_back(mid_pos, transition_lower_bead_count, mid_R);
867 }
868 assert((edge.from->data.bead_count == edge.to->data.bead_count) || edge.data.hasTransitions());
869 }
870}

References Slic3r::Arachne::SkeletalTrapezoidationJoint::bead_count, beading_strategy, Slic3r::cast(), Slic3r::Arachne::SkeletalTrapezoidationEdge::centralIsSet(), 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(), graph, Slic3r::Arachne::SkeletalTrapezoidationEdge::hasTransitions(), Slic3r::Arachne::SkeletalTrapezoidationEdge::isCentral(), Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::p, Slic3r::Arachne::SkeletalTrapezoidationEdge::setTransitions(), and Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::to.

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.
1861{
1862 struct DistEdge
1863 {
1864 edge_t* edge_to;
1865 coord_t dist;
1866 DistEdge(edge_t* edge_to, coord_t dist)
1867 : edge_to(edge_to), dist(dist)
1868 {}
1869 };
1870
1871 auto compare = [](const DistEdge& l, const DistEdge& r) -> bool { return l.dist > r.dist; };
1872 std::priority_queue<DistEdge, std::vector<DistEdge>, decltype(compare)> further_edges(compare);
1873 bool first = true;
1874 for (edge_t* outgoing = node->incident_edge; outgoing && (first || outgoing != node->incident_edge); outgoing = outgoing->twin->next)
1875 {
1876 further_edges.emplace(outgoing, (outgoing->to->p - outgoing->from->p).cast<int64_t>().norm());
1877 first = false;
1878 }
1879
1880 for (coord_t counter = 0; counter < SKELETAL_TRAPEZOIDATION_BEAD_SEARCH_MAX; counter++)
1881 { // Prevent endless recursion
1882 if (further_edges.empty()) return nullptr;
1883 DistEdge here = further_edges.top();
1884 further_edges.pop();
1885 if (here.dist > max_dist) return nullptr;
1886 if (here.edge_to->to->data.hasBeading())
1887 {
1888 return here.edge_to->to->data.getBeading();
1889 }
1890 else
1891 { // recurse
1892 for (edge_t* further_edge = here.edge_to->next; further_edge && further_edge != here.edge_to->twin; further_edge = further_edge->twin->next)
1893 {
1894 further_edges.emplace(further_edge, here.dist + (further_edge->to->p - further_edge->from->p).cast<int64_t>().norm());
1895 }
1896 }
1897 }
1898 return nullptr;
1899}
#define SKELETAL_TRAPEZOIDATION_BEAD_SEARCH_MAX
Definition SkeletalTrapezoidation.cpp:38
T dist(const boost::polygon::point_data< T > &p1, const boost::polygon::point_data< T > &p2)
Definition Geometry.cpp:298

References Slic3r::Geometry::dist(), getNearestBeading(), Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::incident_edge, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::next, SKELETAL_TRAPEZOIDATION_BEAD_SEARCH_MAX, and Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::twin.

Referenced by getNearestBeading(), and getOrCreateBeading().

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

◆ 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.
1819{
1820 if (! node->data.hasBeading())
1821 {
1822 if (node->data.bead_count == -1)
1823 { // This bug is due to too small central edges
1824 const coord_t nearby_dist = scaled<coord_t>(0.1);
1825 auto nearest_beading = getNearestBeading(node, nearby_dist);
1826 if (nearest_beading)
1827 {
1828 return nearest_beading;
1829 }
1830
1831 // Else make a new beading:
1832 bool has_central_edge = false;
1833 bool first = true;
1834 coord_t dist = std::numeric_limits<coord_t>::max();
1835 for (edge_t* edge = node->incident_edge; edge && (first || edge != node->incident_edge); edge = edge->twin->next)
1836 {
1837 if (edge->data.isCentral())
1838 {
1839 has_central_edge = true;
1840 }
1841 assert(edge->to->data.distance_to_boundary >= 0);
1842 dist = std::min(dist, edge->to->data.distance_to_boundary + coord_t((edge->to->p - edge->from->p).cast<int64_t>().norm()));
1843 first = false;
1844 }
1845 if (!has_central_edge)
1846 {
1847 BOOST_LOG_TRIVIAL(error) << "Unknown beading for non-central node!";
1848 }
1849 assert(dist != std::numeric_limits<coord_t>::max());
1850 node->data.bead_count = beading_strategy.getOptimalBeadCount(dist * 2);
1851 }
1852 assert(node->data.bead_count != -1);
1853 node_beadings.emplace_back(new BeadingPropagation(beading_strategy.compute(node->data.distance_to_boundary * 2, node->data.bead_count)));
1854 node->data.setBeading(node_beadings.back());
1855 }
1856 assert(node->data.hasBeading());
1857 return node->data.getBeading();
1858}
std::shared_ptr< BeadingPropagation > getNearestBeading(node_t *node, coord_t max_dist)
Definition SkeletalTrapezoidation.cpp:1860

References Slic3r::Arachne::SkeletalTrapezoidationJoint::bead_count, beading_strategy, Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::data, Slic3r::Geometry::dist(), Slic3r::Arachne::SkeletalTrapezoidationJoint::distance_to_boundary, Slic3r::Arachne::SkeletalTrapezoidationJoint::getBeading(), getNearestBeading(), getOrCreateBeading(), Slic3r::Arachne::SkeletalTrapezoidationJoint::hasBeading(), Slic3r::Arachne::HalfEdgeNode< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::incident_edge, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::next, Slic3r::scaled(), Slic3r::Arachne::SkeletalTrapezoidationJoint::setBeading(), and Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::twin.

Referenced by generateJunctions(), getOrCreateBeading(), and propagateBeadingsDownward().

Here is the call graph for this function:
Here is the caller 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.
1545{
1546 assert(quad_start_edge->prev == nullptr);
1547 assert(quad_start_edge->from->data.distance_to_boundary == 0);
1548 coord_t max_R = -1;
1549 edge_t* ret = nullptr;
1550 for (edge_t* edge = quad_start_edge; edge; edge = edge->next)
1551 {
1552 coord_t r = edge->to->data.distance_to_boundary;
1553 if (r > max_R)
1554 {
1555 max_R = r;
1556 ret = edge;
1557 }
1558 }
1559
1560 if (!ret->next && ret->to->data.distance_to_boundary - scaled<coord_t>(0.005) < ret->from->data.distance_to_boundary)
1561 {
1562 ret = ret->prev;
1563 }
1564 assert(ret);
1565 assert(ret->next);
1566 return ret;
1567}

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, getQuadMaxRedgeTo(), 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, Slic3r::scaled(), and Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::to.

Referenced by connectJunctions(), and getQuadMaxRedgeTo().

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

◆ interpolate() [1/2]

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

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.
1721{
1722 assert(ratio_left_to_whole >= 0.0 && ratio_left_to_whole <= 1.0);
1723 float ratio_right_to_whole = 1.0 - ratio_left_to_whole;
1724
1725 Beading ret = (left.total_thickness > right.total_thickness)? left : right;
1726 for (size_t inset_idx = 0; inset_idx < std::min(left.bead_widths.size(), right.bead_widths.size()); inset_idx++)
1727 {
1728 if(left.bead_widths[inset_idx] == 0 || right.bead_widths[inset_idx] == 0)
1729 {
1730 ret.bead_widths[inset_idx] = 0; //0-width wall markers stay 0-width.
1731 }
1732 else
1733 {
1734 ret.bead_widths[inset_idx] = ratio_left_to_whole * left.bead_widths[inset_idx] + ratio_right_to_whole * right.bead_widths[inset_idx];
1735 }
1736 ret.toolpath_locations[inset_idx] = ratio_left_to_whole * left.toolpath_locations[inset_idx] + ratio_right_to_whole * right.toolpath_locations[inset_idx];
1737 }
1738 return ret;
1739}
@ right
Definition MarchingSquares.hpp:190
@ left
Definition MarchingSquares.hpp:188

References Slic3r::Arachne::BeadingStrategy::Beading::bead_widths, interpolate(), and Slic3r::Arachne::BeadingStrategy::Beading::toolpath_locations.

Here is the call graph for this function:

◆ interpolate() [2/2]

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

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.
1672{
1673 assert(ratio_left_to_whole >= 0.0 && ratio_left_to_whole <= 1.0);
1674 Beading ret = interpolate(left, ratio_left_to_whole, right);
1675
1676 // TODO: don't use toolpath locations past the middle!
1677 // TODO: stretch bead widths and locations of the higher bead count beading to fit in the left over space
1678 coord_t next_inset_idx;
1679 for (next_inset_idx = left.toolpath_locations.size() - 1; next_inset_idx >= 0; next_inset_idx--)
1680 {
1681 if (switching_radius > left.toolpath_locations[next_inset_idx])
1682 {
1683 break;
1684 }
1685 }
1686 if (next_inset_idx < 0)
1687 { // There is no next inset, because there is only one
1688 assert(left.toolpath_locations.empty() || left.toolpath_locations.front() >= switching_radius);
1689 return ret;
1690 }
1691 if (next_inset_idx + 1 == coord_t(left.toolpath_locations.size()))
1692 { // We cant adjust to fit the next edge because there is no previous one?!
1693 return ret;
1694 }
1695 // ret follows the thicker of left/right, which can hold fewer insets than left when bead
1696 // count and thickness disagree; skip the adjustment rather than index ret past its end.
1697 if (next_inset_idx >= coord_t(ret.toolpath_locations.size()))
1698 {
1699 return ret;
1700 }
1701 assert(next_inset_idx < coord_t(left.toolpath_locations.size()));
1702 assert(left.toolpath_locations[next_inset_idx] <= switching_radius);
1703 assert(left.toolpath_locations[next_inset_idx + 1] >= switching_radius);
1704 if (ret.toolpath_locations[next_inset_idx] > switching_radius)
1705 { // One inset disappeared between left and the merged one
1706 // solve for ratio f:
1707 // f*l + (1-f)*r = s
1708 // f*l + r - f*r = s
1709 // f*(l-r) + r = s
1710 // f*(l-r) = s - r
1711 // f = (s-r) / (l-r)
1712 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]);
1713 new_ratio = std::min(1.0, new_ratio + 0.1);
1714 return interpolate(left, new_ratio, right);
1715 }
1716 return ret;
1717}

References interpolate(), and Slic3r::Arachne::BeadingStrategy::Beading::toolpath_locations.

Referenced by interpolate(), interpolate(), and propagateBeadingsDownward().

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

◆ 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).
1345{
1346 if (!edge_to.data.isCentral())
1347 {
1348 return false;
1349 }
1350 if (!edge_to.next)
1351 {
1352 return true;
1353 }
1354 for (const edge_t* edge = edge_to.next; edge && edge != edge_to.twin; edge = edge->twin->next)
1355 {
1356 if (edge->data.isCentral())
1357 {
1358 return false;
1359 }
1360 assert(edge->twin);
1361 }
1362 return true;
1363}

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.
1212{
1213 // NOTE: the logic below is not fully thought through.
1214 // TODO: take transition mids into account
1215 if (outgoing->to->data.distance_to_boundary == 0)
1216 {
1217 return true;
1218 }
1219 bool is_upward = outgoing->to->data.distance_to_boundary >= outgoing->from->data.distance_to_boundary;
1220 edge_t* upward_edge = is_upward? outgoing : outgoing->twin;
1221 if (outgoing->to->data.bead_count > lower_bead_count + 1)
1222 {
1223 assert(upward_edge->data.hasTransitions() && "If the bead count is going down there has to be a transition mid!");
1224 if(!upward_edge->data.hasTransitions())
1225 {
1226 BOOST_LOG_TRIVIAL(warning) << "If the bead count is going down there has to be a transition mid!";
1227 }
1228 return false;
1229 }
1230 coord_t length = (outgoing->to->p - outgoing->from->p).cast<int64_t>().norm();
1231 if (upward_edge->data.hasTransitions())
1232 {
1233 auto& transition_mids = *upward_edge->data.getTransitions();
1234 TransitionMiddle& mid = is_upward? transition_mids.front() : transition_mids.back();
1235 if (
1236 mid.lower_bead_count == lower_bead_count &&
1237 ((is_upward && mid.pos + traveled_dist < max_dist)
1238 || (!is_upward && length - mid.pos + traveled_dist < max_dist))
1239 )
1240 {
1241 return true;
1242 }
1243 }
1244 if (traveled_dist + length > max_dist)
1245 {
1246 return false;
1247 }
1248 if (outgoing->to->data.bead_count <= lower_bead_count
1249 && !(outgoing->to->data.bead_count == lower_bead_count && outgoing->to->data.transition_ratio > 0.0))
1250 {
1251 return true;
1252 }
1253
1254 bool is_only_going_down = true;
1255 bool has_recursed = false;
1256 for (edge_t* next = outgoing->next; next && next != outgoing->twin; next = next->twin->next)
1257 {
1258 if (!next->data.isCentral())
1259 {
1260 continue;
1261 }
1262 bool is_going_down = isGoingDown(next, traveled_dist + length, max_dist, lower_bead_count);
1263 is_only_going_down &= is_going_down;
1264 has_recursed = true;
1265 }
1266 return has_recursed && is_only_going_down;
1267}

References Slic3r::Arachne::SkeletalTrapezoidationJoint::bead_count, Slic3r::cast(), 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 ( const VD::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.

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

References graph, 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>&)

1623{
1624 coord_t length = (edge_to_peak->to->p - edge_to_peak->from->p).cast<int64_t>().norm();
1625 BeadingPropagation& top_beading = *getOrCreateBeading(edge_to_peak->to, node_beadings);
1626 assert(top_beading.beading.total_thickness >= edge_to_peak->to->data.distance_to_boundary * 2);
1627 if(top_beading.beading.total_thickness < edge_to_peak->to->data.distance_to_boundary * 2)
1628 {
1629 BOOST_LOG_TRIVIAL(warning) << "Top bead is beyond the center of the total width.";
1630 }
1631 assert(!top_beading.is_upward_propagated_only);
1632
1633 if(!edge_to_peak->from->data.hasBeading())
1634 { // Set new beading if there is no beading associated with the node yet
1635 BeadingPropagation propagated_beading = top_beading;
1636 propagated_beading.dist_from_top_source += length;
1637 node_beadings.emplace_back(new BeadingPropagation(propagated_beading));
1638 edge_to_peak->from->data.setBeading(node_beadings.back());
1639 assert(propagated_beading.beading.total_thickness >= edge_to_peak->from->data.distance_to_boundary * 2);
1640 if(propagated_beading.beading.total_thickness < edge_to_peak->from->data.distance_to_boundary * 2)
1641 {
1642 BOOST_LOG_TRIVIAL(warning) << "Propagated bead is beyond the center of the total width.";
1643 }
1644 }
1645 else
1646 {
1647 BeadingPropagation& bottom_beading = *edge_to_peak->from->data.getBeading();
1648 coord_t total_dist = top_beading.dist_from_top_source + length + bottom_beading.dist_to_bottom_source;
1649 double ratio_of_top = static_cast<float>(bottom_beading.dist_to_bottom_source) / std::min(total_dist, beading_propagation_transition_dist);
1650 ratio_of_top = std::max(0.0, ratio_of_top);
1651 if (ratio_of_top >= 1.0)
1652 {
1653 bottom_beading = top_beading;
1654 bottom_beading.dist_from_top_source += length;
1655 }
1656 else
1657 {
1658 Beading merged_beading = interpolate(top_beading.beading, ratio_of_top, bottom_beading.beading, edge_to_peak->from->data.distance_to_boundary);
1659 bottom_beading = BeadingPropagation(merged_beading);
1660 bottom_beading.is_upward_propagated_only = false;
1661 assert(merged_beading.total_thickness >= edge_to_peak->from->data.distance_to_boundary * 2);
1662 if(merged_beading.total_thickness < edge_to_peak->from->data.distance_to_boundary * 2)
1663 {
1664 BOOST_LOG_TRIVIAL(warning) << "Merged bead is beyond the center of the total width.";
1665 }
1666 }
1667 }
1668}

References Slic3r::Arachne::SkeletalTrapezoidationJoint::BeadingPropagation::beading, beading_propagation_transition_dist, Slic3r::cast(), 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(), getOrCreateBeading(), Slic3r::Arachne::SkeletalTrapezoidationJoint::hasBeading(), interpolate(), 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, propagateBeadingsDownward(), 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.
1600{
1601 for (edge_t* upward_quad_mid : upward_quad_mids)
1602 {
1603 // Transfer beading information to lower nodes
1604 if (!upward_quad_mid->data.isCentral())
1605 {
1606 // for equidistant edge: propagate from known beading to node with unknown beading
1607 if (upward_quad_mid->from->data.distance_to_boundary == upward_quad_mid->to->data.distance_to_boundary
1608 && upward_quad_mid->from->data.hasBeading()
1609 && ! upward_quad_mid->to->data.hasBeading()
1610 )
1611 {
1612 propagateBeadingsDownward(upward_quad_mid->twin, node_beadings);
1613 }
1614 else
1615 {
1616 propagateBeadingsDownward(upward_quad_mid, node_beadings);
1617 }
1618 }
1619 }
1620}

References propagateBeadingsDownward().

Referenced by propagateBeadingsDownward(), and propagateBeadingsDownward().

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

◆ 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.
1570{
1571 const auto _central_filter_dist = central_filter_dist();
1572 for (auto upward_quad_mids_it = upward_quad_mids.rbegin(); upward_quad_mids_it != upward_quad_mids.rend(); ++upward_quad_mids_it)
1573 {
1574 edge_t* upward_edge = *upward_quad_mids_it;
1575 if (upward_edge->to->data.bead_count >= 0)
1576 { // Don't override local beading
1577 continue;
1578 }
1579 if (! upward_edge->from->data.hasBeading())
1580 { // Only propagate if we have something to propagate
1581 continue;
1582 }
1583 BeadingPropagation& lower_beading = *upward_edge->from->data.getBeading();
1584 if (upward_edge->to->data.hasBeading())
1585 { // Only propagate to places where there is place
1586 continue;
1587 }
1588 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");
1589 coord_t length = (upward_edge->to->p - upward_edge->from->p).cast<int64_t>().norm();
1590 BeadingPropagation upper_beading = lower_beading;
1591 upper_beading.dist_to_bottom_source += length;
1592 upper_beading.is_upward_propagated_only = true;
1593 node_beadings.emplace_back(new BeadingPropagation(upper_beading));
1594 upward_edge->to->data.setBeading(node_beadings.back());
1595 assert(upper_beading.beading.total_thickness <= upward_edge->to->data.distance_to_boundary * 2);
1596 }
1597}

References Slic3r::Arachne::SkeletalTrapezoidationJoint::bead_count, Slic3r::Arachne::SkeletalTrapezoidationJoint::BeadingPropagation::beading, Slic3r::cast(), central_filter_dist(), 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, propagateBeadingsUpward(), 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.

Referenced by propagateBeadingsUpward().

Here is the call graph for this function:
Here is the caller 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)

470{
471 NodeSet visited_nodes;
472 for (edge_t& edge : graph.edges)
473 {
474 if (edge.prev)
475 {
476 continue;
477 }
478 edge_t* quad_start = &edge;
479 if (visited_nodes.find(quad_start->from) == visited_nodes.end())
480 {
481 visited_nodes.emplace(quad_start->from);
482 }
483 else
484 { // Needs to be duplicated
485 graph.nodes.emplace_back(*quad_start->from);
486 node_t* new_node = &graph.nodes.back();
487 new_node->incident_edge = quad_start;
488 quad_start->from = new_node;
489 quad_start->twin->to = new_node;
490 }
491 }
492}
ankerl::unordered_dense::set< node_t * > NodeSet
Definition SkeletalTrapezoidation.hpp:88

References 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::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, 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:

◆ snap_dist()

coord_t Slic3r::Arachne::SkeletalTrapezoidation::snap_dist ( )
inlineprivate
74{ return scaled<coord_t>(0.02); }

References Slic3r::scaled().

Referenced by applyTransitions(), and generateExtraRibs().

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

◆ transferEdge()

void Slic3r::Arachne::SkeletalTrapezoidation::transferEdge ( const Point & from,
const Point & to,
const VD::edge_type & vd_edge,
edge_t *& prev_edge,
const Point & start_source_point,
const 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.

123 {
124 auto he_edge_it = vd_edge_to_he_edge.find(vd_edge.twin());
125 if (he_edge_it != vd_edge_to_he_edge.end())
126 { // Twin segment(s) have already been made
127 edge_t* source_twin = he_edge_it->second;
128 assert(source_twin);
129 auto end_node_it = vd_node_to_he_node.find(vd_edge.vertex1());
130 assert(end_node_it != vd_node_to_he_node.end());
131 node_t* end_node = end_node_it->second;
132 for (edge_t* twin = source_twin; ;twin = twin->prev->twin->prev)
133 {
134 if(!twin)
135 {
136 BOOST_LOG_TRIVIAL(warning) << "Encountered a voronoi edge without twin.";
137 continue; //Prevent reading unallocated memory.
138 }
139 assert(twin);
140 graph.edges.emplace_front(SkeletalTrapezoidationEdge());
141 edge_t* edge = &graph.edges.front();
142 edge->from = twin->to;
143 edge->to = twin->from;
144 edge->twin = twin;
145 twin->twin = edge;
146 edge->from->incident_edge = edge;
147
148 if (prev_edge)
149 {
150 edge->prev = prev_edge;
151 prev_edge->next = edge;
152 }
153
154 prev_edge = edge;
155
156 if (prev_edge->to == end_node)
157 {
158 return;
159 }
160
161 if (!twin->prev || !twin->prev->twin || !twin->prev->twin->prev)
162 {
163 BOOST_LOG_TRIVIAL(error) << "Discretized segment behaves oddly!";
164 return;
165 }
166
167 assert(twin->prev); // Forth rib
168 assert(twin->prev->twin); // Back rib
169 assert(twin->prev->twin->prev); // Prev segment along parabola
170
171 graph.makeRib(prev_edge, start_source_point, end_source_point);
172 }
173 assert(prev_edge);
174 }
175 else
176 {
177 Points discretized = discretize(vd_edge, segments);
178 assert(discretized.size() >= 2);
179 if(discretized.size() < 2)
180 {
181 BOOST_LOG_TRIVIAL(warning) << "Discretized Voronoi edge is degenerate.";
182 }
183
184 assert(!prev_edge || prev_edge->to);
185 if(prev_edge && !prev_edge->to)
186 {
187 BOOST_LOG_TRIVIAL(warning) << "Previous edge doesn't go anywhere.";
188 }
189 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
190 Point p0 = discretized.front();
191 for (size_t p1_idx = 1; p1_idx < discretized.size(); p1_idx++)
192 {
193 Point p1 = discretized[p1_idx];
194 node_t* v1;
195 if (p1_idx < discretized.size() - 1)
196 {
197 graph.nodes.emplace_front(SkeletalTrapezoidationJoint(), p1);
198 v1 = &graph.nodes.front();
199 }
200 else
201 {
202 v1 = &makeNode(*vd_edge.vertex1(), to);
203 }
204
205 graph.edges.emplace_front(SkeletalTrapezoidationEdge());
206 edge_t* edge = &graph.edges.front();
207 edge->from = v0;
208 edge->to = v1;
209 edge->from->incident_edge = edge;
210
211 if (prev_edge)
212 {
213 edge->prev = prev_edge;
214 prev_edge->next = edge;
215 }
216
217 prev_edge = edge;
218 p0 = p1;
219 v0 = v1;
220
221 if (p1_idx < discretized.size() - 1) { // Rib for last segment gets introduced outside this function!
222 graph.makeRib(prev_edge, start_source_point, end_source_point);
223 }
224 }
225 assert(prev_edge);
226 vd_edge_to_he_edge.emplace(&vd_edge, prev_edge);
227 }
228}
Points discretize(const VD::edge_type &segment, const std::vector< Segment > &segments)
Definition SkeletalTrapezoidation.cpp:230
node_t & makeNode(const VD::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:108

References discretize(), 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::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, 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.

662{
663 for (edge_t& edge : graph.edges)
664 {
665 if (edge.data.isCentral())
666 {
667 edge.to->data.bead_count = beading_strategy.getOptimalBeadCount(edge.to->data.distance_to_boundary * 2);
668 }
669 }
670
671 // Fix bead count at locally maximal R, also for central regions!! See TODO s in generateTransitionEnd(.)
672 for (node_t& node : graph.nodes)
673 {
674 if (node.isLocalMaximum())
675 {
676 if (node.data.distance_to_boundary < 0)
677 {
678 BOOST_LOG_TRIVIAL(warning) << "Distance to boundary not yet computed for local maximum!";
679 node.data.distance_to_boundary = std::numeric_limits<coord_t>::max();
680 edge_t* edge = node.incident_edge;
681 do
682 {
683 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()));
684 } while (edge = edge->twin->next, edge != node.incident_edge);
685 }
686 coord_t bead_count = beading_strategy.getOptimalBeadCount(node.data.distance_to_boundary * 2);
687 node.data.bead_count = bead_count;
688 }
689 }
690}

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, 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::SkeletalTrapezoidationEdge::isCentral(), Slic3r::Arachne::STHalfEdgeNode::isLocalMaximum(), 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::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
563{
564 // _.-'^` A and B are the endpoints of an edge we're checking.
565 // _.-'^` Part of the line AB will be used as a cap,
566 // _.-'^` \ because the polygon is too narrow there.
567 // _.-'^` \ If |AB| minus the cap is still bigger than dR,
568 // _.-'^` \ R2 the edge AB is considered central. It's then
569 // _.-'^` \ _.-'\`\ significant compared to the edges around it.
570 // _.-'^` \R1 _.-'^` '`\ dR
571 // _.-'^`a/2 \_.-'^`a \ Line AR2 is parallel to the polygon contour.
572 // `^'-._````````````````A```````````v````````B``````` dR is the remaining diameter at B.
573 // `^'-._ dD = |AB| As a result, AB is less often central if the polygon
574 // `^'-._ corner is obtuse.
575 // sin a = dR / dD
576
577 coord_t outer_edge_filter_length = beading_strategy.getTransitionThickness(0) / 2;
578
579 float cap = sin(beading_strategy.getTransitioningAngle() * 0.5); // = cos(bisector_angle / 2)
580 for (edge_t& edge: graph.edges)
581 {
582 assert(edge.twin);
583 if(!edge.twin)
584 {
585 BOOST_LOG_TRIVIAL(warning) << "Encountered a Voronoi edge without twin!";
586 continue;
587 }
588 if(edge.twin->data.centralIsSet())
589 {
590 edge.data.setIsCentral(edge.twin->data.isCentral());
591 }
592 else if(edge.data.type == SkeletalTrapezoidationEdge::EdgeType::EXTRA_VD)
593 {
594 edge.data.setIsCentral(false);
595 }
596 else if(std::max(edge.from->data.distance_to_boundary, edge.to->data.distance_to_boundary) < outer_edge_filter_length)
597 {
598 edge.data.setIsCentral(false);
599 }
600 else
601 {
602 Point a = edge.from->p;
603 Point b = edge.to->p;
604 Point ab = b - a;
605 coord_t dR = std::abs(edge.to->data.distance_to_boundary - edge.from->data.distance_to_boundary);
606 coord_t dD = ab.cast<int64_t>().norm();
607 edge.data.setIsCentral(dR < dD * cap);
608 }
609 }
610}

References beading_strategy, Slic3r::Arachne::SkeletalTrapezoidationEdge::centralIsSet(), 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::SkeletalTrapezoidationEdge::EXTRA_VD, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::from, graph, Slic3r::Arachne::SkeletalTrapezoidationEdge::isCentral(), 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, Slic3r::Arachne::HalfEdge< node_data_t, edge_data_t, derived_node_t, derived_edge_t >::twin, and Slic3r::Arachne::SkeletalTrapezoidationEdge::type.

Referenced by generateToolpaths().

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

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 SkeletalTrapezoidation(), and 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 Filter areas marked as 'central' smaller than this.

Referenced by SkeletalTrapezoidation(), and propagateBeadingsDownward().

◆ 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 SkeletalTrapezoidation(), dissolveNearbyTransitions(), filterNoncentralRegions(), filterTransitionMids(), generateExtraRibs(), generateTransitionEnds(), generateTransitionMids(), getOrCreateBeading(), updateBeadCount(), and updateIsCentral().

◆ 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 SkeletalTrapezoidation(), 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(), connectJunctions(), constructFromPolygons(), filterCentral(), filterNoncentralRegions(), filterOuterCentral(), filterTransitionMids(), generateAllTransitionEnds(), generateExtraRibs(), generateJunctions(), generateLocalMaximaSingleBeads(), 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 addToolpathSegment(), generateLocalMaximaSingleBeads(), and generateToolpaths().

◆ transition_filter_dist

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

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

Referenced by SkeletalTrapezoidation(), and 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 SkeletalTrapezoidation(), and discretize().

◆ vd_edge_to_he_edge

ankerl::unordered_dense::map<const VD::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<const VD::vertex_type *, node_t *> Slic3r::Arachne::SkeletalTrapezoidation::vd_node_to_he_node
protected

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