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

Classes

struct  AreaIncreaseSettings
 
struct  Bifurcation
 
struct  Branch
 
struct  DrawArea
 
struct  Element
 
class  InterfacePlacer
 
class  RichInterfacePlacer
 
struct  Slice
 
struct  SupportElement
 
struct  SupportElementInfluenceAreas
 
struct  SupportElementMerging
 
struct  SupportElementState
 
struct  SupportElementStateBits
 
struct  Tree
 
class  TreeModelVolumes
 
struct  TreeSupportMeshGroupSettings
 
struct  TreeSupportSettings
 This struct contains settings used in the tree support. Thanks to this most functions do not need to know of meshes etc. Also makes the code shorter. More...
 

Typedefs

using Forest = std::vector< Tree >
 
using Trees = std::vector< Tree >
 
using LineInformation = std::vector< std::pair< Point, LineStatus > >
 
using LineInformations = std::vector< LineInformation >
 
using SupportElements = std::deque< SupportElement >
 
using LayerIndex = int
 

Enumerations

enum class  LineStatus {
  INVALID , TO_MODEL , TO_MODEL_GRACIOUS , TO_MODEL_GRACIOUS_SAFE ,
  TO_BP , TO_BP_SAFE
}
 
enum class  InterfacePreference {
  InterfaceAreaOverwritesSupport , SupportAreaOverwritesInterface , InterfaceLinesOverwriteSupport , SupportLinesOverwriteInterface ,
  Nothing
}
 

Functions

Element to_tree_element (const TreeSupportSettings &config, const SlicingParameters &slicing_params, SupportElement &element, bool is_root)
 
Forest make_forest (const TreeSupportSettings &config, const SlicingParameters &slicing_params, std::vector< SupportElements > &&move_bounds)
 
void trim_influence_areas_bottom_up (Forest &forest, const float dxy_dlayer)
 
void smooth_trees_inside_influence_areas (Branch &root, bool is_root)
 
void smooth_trees_inside_influence_areas (Forest &forest)
 
template<bool flip_normals>
void triangulate_fan (indexed_triangle_set &its, int ifan, int ibegin, int iend)
 
static void triangulate_strip (indexed_triangle_set &its, int ibegin1, int iend1, int ibegin2, int iend2)
 
static std::pair< int, int > discretize_circle (const Vec3f &center, const Vec3f &normal, const float radius, const float eps, std::vector< Vec3f > &pts)
 
static std::pair< float, float > extrude_branch (const std::vector< const SupportElement * > &path, const TreeSupportSettings &config, const SlicingParameters &slicing_params, const std::vector< SupportElements > &move_bounds, indexed_triangle_set &result)
 
static void organic_smooth_branches_avoid_collisions (const PrintObject &print_object, const TreeModelVolumes &volumes, const TreeSupportSettings &config, std::vector< SupportElements > &move_bounds, const std::vector< std::pair< SupportElement *, int > > &elements_with_link_down, const std::vector< size_t > &linear_data_layers, std::function< void()> throw_on_cancel)
 
void organic_draw_branches (PrintObject &print_object, TreeModelVolumes &volumes, const TreeSupportSettings &config, std::vector< SupportElements > &move_bounds, SupportGeneratorLayersPtr &bottom_contacts, SupportGeneratorLayersPtr &top_contacts, InterfacePlacer &interface_placer, SupportGeneratorLayersPtr &intermediate_layers, SupportGeneratorLayerStorage &layer_storage, std::function< void()> throw_on_cancel)
 
static Polygons calculateMachineBorderCollision (Polygon machine_border)
 
static void validate_range (const Point &pt)
 
static void validate_range (const Points &points)
 
static void validate_range (const MultiPoint &mp)
 
static void validate_range (const Polygons &polygons)
 
static void validate_range (const Polylines &polylines)
 
static void validate_range (const LineInformation &lines)
 
static void validate_range (const LineInformations &lines)
 
static void check_self_intersections (const Polygons &polygons, const std::string_view message)
 
static void check_self_intersections (const ExPolygon &expoly, const std::string_view message)
 
static std::vector< std::pair< TreeSupportSettings, std::vector< size_t > > > group_meshes (const Print &print, const std::vector< size_t > &print_object_ids)
 
static const std::vector< Polygonsgenerate_overhangs (const TreeSupportSettings &settings, const PrintObject &print_object, std::function< void()> throw_on_cancel)
 
static LayerIndex precalculate (const Print &print, const std::vector< Polygons > &overhangs, const TreeSupportSettings &config, const std::vector< size_t > &object_ids, TreeModelVolumes &volumes, std::function< void()> throw_on_cancel)
 Precalculates all avoidances, that could be required.
 
static LineInformations convert_lines_to_internal (const TreeModelVolumes &volumes, const TreeSupportSettings &config, const Polylines &polylines, LayerIndex layer_idx)
 Converts a Polygons object representing a line into the internal format.
 
static bool evaluate_point_for_next_layer_function (const TreeModelVolumes &volumes, const TreeSupportSettings &config, size_t current_layer, const std::pair< Point, LineStatus > &p)
 Evaluates if a point has to be added now. Required for a split_lines call in generate_initial_areas().
 
template<typename EvaluatePointFn >
static std::pair< LineInformations, LineInformationssplit_lines (const LineInformations &lines, EvaluatePointFn evaluatePoint)
 Evaluates which points of some lines are not valid one layer below and which are. Assumes all points are valid on the current layer. Validity is evaluated using supplied lambda.
 
static std::optional< std::pair< Point, size_t > > polyline_sample_next_point_at_distance (const Points &polyline, const Point &start_pt, size_t start_idx, double dist)
 
static Polylines ensure_maximum_distance_polyline (const Polylines &input, double distance, size_t min_points)
 Eensures that every line segment is about distance in length. The resulting lines may differ from the original but all points are on the original.
 
static Polylines generate_support_infill_lines (const Polygons &polygon, const SupportParameters &support_params, bool roof, LayerIndex layer_idx, coord_t support_infill_distance)
 Returns Polylines representing the (infill) lines that will result in slicing the given area.
 
static Polygons safe_union (const Polygons first, const Polygons second={})
 Unions two Polygons. Ensures that if the input is non empty that the output also will be non empty.
 
static Polygons safe_offset_inc (const Polygons &me, coord_t distance, const Polygons &collision, coord_t safe_step_size, coord_t last_step_offset_without_check, size_t min_amount_offset)
 Offsets (increases the area of) a polygons object in multiple steps to ensure that it does not lag through over a given obstacle.
 
int generate_raft_contact (const PrintObject &print_object, const TreeSupportSettings &config, InterfacePlacer &interface_placer)
 
void finalize_raft_contact (const PrintObject &print_object, const int raft_contact_layer_idx, SupportGeneratorLayersPtr &top_contacts, std::vector< SupportElements > &move_bounds)
 
void sample_overhang_area (Polygons &&overhang_area, const bool large_horizontal_roof, const size_t layer_idx, const size_t num_support_roof_layers, const coord_t connect_length, const TreeSupportMeshGroupSettings &mesh_group_settings, RichInterfacePlacer &interface_placer)
 
static void generate_initial_areas (const PrintObject &print_object, const TreeModelVolumes &volumes, const TreeSupportSettings &config, const std::vector< Polygons > &overhangs, std::vector< SupportElements > &move_bounds, InterfacePlacer &interface_placer, std::function< void()> throw_on_cancel)
 Creates the initial influence areas (that can later be propagated down) by placing them below the overhang.
 
static unsigned int move_inside (const Polygons &polygons, Point &from, int distance=0, int64_t maxDist2=std::numeric_limits< int64_t >::max())
 
static Point move_inside_if_outside (const Polygons &polygons, Point from, int distance=0, int64_t maxDist2=std::numeric_limits< int64_t >::max())
 
static std::optional< SupportElementStateincrease_single_area (const TreeModelVolumes &volumes, const TreeSupportSettings &config, const AreaIncreaseSettings &settings, const LayerIndex layer_idx, const SupportElement &parent, const Polygons &relevant_offset, Polygons &to_bp_data, Polygons &to_model_data, Polygons &increased, const coord_t overspeed, const bool mergelayer)
 Checks if an influence area contains a valid subsection and returns the corresponding metadata and the new Influence area.
 
static void increase_areas_one_layer (const TreeModelVolumes &volumes, const TreeSupportSettings &config, std::vector< SupportElementMerging > &merging_areas, const LayerIndex layer_idx, SupportElements &layer_elements, const bool mergelayer, std::function< void()> throw_on_cancel)
 Increases influence areas as far as required.
 
static SupportElementState merge_support_element_states (const SupportElementState &first, const SupportElementState &second, const Point &next_position, const coord_t layer_idx, const TreeSupportSettings &config)
 
static bool merge_influence_areas_two_elements (const TreeModelVolumes &volumes, const TreeSupportSettings &config, const LayerIndex layer_idx, SupportElementMerging &dst, SupportElementMerging &src)
 
static SupportElementMergingmerge_influence_areas_leaves (const TreeModelVolumes &volumes, const TreeSupportSettings &config, const LayerIndex layer_idx, SupportElementMerging *const dst_begin, SupportElementMerging *dst_end)
 Merges Influence Areas if possible.
 
static SupportElementMergingmerge_influence_areas_two_sets (const TreeModelVolumes &volumes, const TreeSupportSettings &config, const LayerIndex layer_idx, SupportElementMerging *const dst_begin, SupportElementMerging *dst_end, SupportElementMerging *src_begin, SupportElementMerging *const src_end)
 
static void merge_influence_areas (const TreeModelVolumes &volumes, const TreeSupportSettings &config, const LayerIndex layer_idx, std::vector< SupportElementMerging > &influence_areas, std::function< void()> throw_on_cancel)
 Merges Influence Areas at one layer if possible.
 
static void create_layer_pathing (const TreeModelVolumes &volumes, const TreeSupportSettings &config, std::vector< SupportElements > &move_bounds, std::function< void()> throw_on_cancel)
 Propagates influence downwards, and merges overlapping ones.
 
static void set_points_on_areas (const SupportElement &elem, SupportElements *layer_above)
 Sets the result_on_layer for all parents based on the SupportElement supplied.
 
static void set_to_model_contact_simple (SupportElement &elem)
 
static void set_to_model_contact_to_model_gracious (const TreeModelVolumes &volumes, const TreeSupportSettings &config, std::vector< SupportElements > &move_bounds, SupportElement &first_elem, std::function< void()> throw_on_cancel)
 Get the best point to connect to the model and set the result_on_layer of the relevant SupportElement accordingly.
 
static void remove_deleted_elements (std::vector< SupportElements > &move_bounds)
 
static void create_nodes_from_area (const TreeModelVolumes &volumes, const TreeSupportSettings &config, std::vector< SupportElements > &move_bounds, std::function< void()> throw_on_cancel)
 Set the result_on_layer point for all influence areas.
 
static void generate_branch_areas (const TreeModelVolumes &volumes, const TreeSupportSettings &config, const std::vector< SupportElements > &move_bounds, std::vector< DrawArea > &linear_data, std::function< void()> throw_on_cancel)
 Draws circles around result_on_layer points of the influence areas.
 
static void smooth_branch_areas (const TreeSupportSettings &config, std::vector< SupportElements > &move_bounds, std::vector< DrawArea > &linear_data, const std::vector< size_t > &linear_data_layers, std::function< void()> throw_on_cancel)
 Applies some smoothing to the outer wall, intended to smooth out sudden jumps as they can happen when a branch moves though a hole.
 
static void drop_non_gracious_areas (const TreeModelVolumes &volumes, const std::vector< DrawArea > &linear_data, std::vector< Polygons > &support_layer_storage, std::function< void()> throw_on_cancel)
 Drop down areas that do rest non-gracefully on the model to ensure the branch actually rests on something.
 
static void finalize_interface_and_support_areas (const PrintObject &print_object, const TreeModelVolumes &volumes, const TreeSupportSettings &config, const std::vector< Polygons > &overhangs, std::vector< Polygons > &support_layer_storage, std::vector< Polygons > &support_roof_storage, SupportGeneratorLayersPtr &bottom_contacts, SupportGeneratorLayersPtr &top_contacts, SupportGeneratorLayersPtr &intermediate_layers, SupportGeneratorLayerStorage &layer_storage, std::function< void()> throw_on_cancel)
 Generates Support Floor, ensures Support Roof can not cut of branches, and saves the branches as support to storage.
 
static void draw_areas (PrintObject &print_object, const TreeModelVolumes &volumes, const TreeSupportSettings &config, const std::vector< Polygons > &overhangs, std::vector< SupportElements > &move_bounds, SupportGeneratorLayersPtr &bottom_contacts, SupportGeneratorLayersPtr &top_contacts, SupportGeneratorLayersPtr &intermediate_layers, SupportGeneratorLayerStorage &layer_storage, std::function< void()> throw_on_cancel)
 Draws circles around result_on_layer points of the influence areas and applies some post processing.
 
static void generate_support_areas (Print &print, const BuildVolume &build_volume, const std::vector< size_t > &print_object_ids, std::function< void()> throw_on_cancel)
 Create the areas that need support.
 
size_t getEffectiveDTT (const TreeSupportSettings &settings, const SupportElementState &elem)
 Get the Distance to top regarding the real radius this part will have. This is different from distance_to_top, which is can be used to calculate the top most layer of the branch.
 
coord_t support_element_radius (const TreeSupportSettings &settings, const SupportElementState &elem)
 Get the Radius, that this element will have.
 
coord_t support_element_collision_radius (const TreeSupportSettings &settings, const SupportElementState &elem)
 Get the collision Radius of this Element. This can be smaller then the actual radius, as the drawAreas will cut off areas that may collide with the model.
 
coord_t support_element_radius (const TreeSupportSettings &settings, const SupportElement &elem)
 
coord_t support_element_collision_radius (const TreeSupportSettings &settings, const SupportElement &elem)
 
void tree_supports_show_error (std::string_view message, bool critical)
 
double layer_z (const SlicingParameters &slicing_params, const TreeSupportSettings &config, const size_t layer_idx)
 
LayerIndex layer_idx_ceil (const SlicingParameters &slicing_params, const TreeSupportSettings &config, const double z)
 
LayerIndex layer_idx_floor (const SlicingParameters &slicing_params, const TreeSupportSettings &config, const double z)
 
SupportGeneratorLayerlayer_initialize (SupportGeneratorLayer &layer_new, const SlicingParameters &slicing_params, const TreeSupportSettings &config, const size_t layer_idx)
 
SupportGeneratorLayerlayer_allocate_unguarded (SupportGeneratorLayerStorage &layer_storage, SupporLayerType layer_type, const SlicingParameters &slicing_params, const TreeSupportSettings &config, size_t layer_idx)
 
SupportGeneratorLayerlayer_allocate (SupportGeneratorLayerStorage &layer_storage, SupporLayerType layer_type, const SlicingParameters &slicing_params, const TreeSupportSettings &config, size_t layer_idx)
 

Variables

static constexpr const double SUPPORT_TREE_EXPONENTIAL_FACTOR = 1.5
 
static constexpr const coord_t SUPPORT_TREE_EXPONENTIAL_THRESHOLD = scaled<coord_t>(1. * SUPPORT_TREE_EXPONENTIAL_FACTOR)
 
static constexpr const coord_t SUPPORT_TREE_COLLISION_RESOLUTION = scaled<coord_t>(0.5)
 
static constexpr const bool SUPPORT_TREE_AVOID_SUPPORT_BLOCKER = true
 
bool g_showed_critical_error = false
 
bool g_showed_performance_warning = false
 
static constexpr const size_t SUPPORT_TREE_CIRCLE_RESOLUTION = 25
 
static constexpr const bool polygons_strictly_simple = false
 
static constexpr const auto tiny_area_threshold = sqr(scaled<double>(0.001))
 

Class Documentation

◆ Slic3r::FFFTreeSupport::Bifurcation

struct Slic3r::FFFTreeSupport::Bifurcation
+ Collaboration diagram for Slic3r::FFFTreeSupport::Bifurcation:
Class Members
double area
Branch * branch

◆ Slic3r::FFFTreeSupport::DrawArea

struct Slic3r::FFFTreeSupport::DrawArea
+ Collaboration diagram for Slic3r::FFFTreeSupport::DrawArea:
Class Members
SupportElement * child_element
SupportElement * element
Polygons polygons

◆ Slic3r::FFFTreeSupport::Element

struct Slic3r::FFFTreeSupport::Element
+ Collaboration diagram for Slic3r::FFFTreeSupport::Element:
Class Members
CollisionSphere collision_sphere
Polygons influence_area
Vec3f last_collision
double last_collision_depth
LayerIndex layer_idx
bool locked
Vec3f position
Vec3f prev_position
float radius

◆ Slic3r::FFFTreeSupport::Slice

struct Slic3r::FFFTreeSupport::Slice
+ Collaboration diagram for Slic3r::FFFTreeSupport::Slice:
Class Members
Polygons bottom_contacts
size_t num_branches { 0 }
Polygons polygons

Typedef Documentation

◆ Forest

using Slic3r::FFFTreeSupport::Forest = typedef std::vector<Tree>

◆ LayerIndex

◆ LineInformation

using Slic3r::FFFTreeSupport::LineInformation = typedef std::vector<std::pair<Point, LineStatus> >

◆ LineInformations

◆ SupportElements

◆ Trees

using Slic3r::FFFTreeSupport::Trees = typedef std::vector<Tree>

Enumeration Type Documentation

◆ InterfacePreference

◆ LineStatus

Function Documentation

◆ calculateMachineBorderCollision()

static Polygons Slic3r::FFFTreeSupport::calculateMachineBorderCollision ( Polygon  machine_border)
static
40{
41 // Put a border of 1m around the print volume so that we don't collide.
42#if 1
43 //FIXME just returning no border will let tree support legs collide with print bed boundary
44 return {};
45#else
46 //FIXME offsetting by 1000mm easily overflows int32_tr coordinate.
47 Polygons out = offset(machine_border, scaled<float>(1000.), jtMiter, 1.2);
48 machine_border.reverse(); // Makes the polygon negative so that we subtract the actual volume from the collision area.
49 out.emplace_back(std::move(machine_border));
50 return out;
51#endif
52}
void reverse()
Definition MultiPoint.hpp:34
std::vector< Polygon, PointsAllocator< Polygon > > Polygons
Definition Polygon.hpp:15

References Slic3r::offset(), and Slic3r::MultiPoint::reverse().

+ Here is the call graph for this function:

◆ check_self_intersections() [1/2]

static void Slic3r::FFFTreeSupport::check_self_intersections ( const ExPolygon expoly,
const std::string_view  message 
)
inlinestatic
112{
113#ifdef TREE_SUPPORT_SHOW_ERRORS_WIN32
114 check_self_intersections(to_polygons(expoly), message);
115#endif // TREE_SUPPORT_SHOW_ERRORS_WIN32
116}
static void check_self_intersections(const Polygons &polygons, const std::string_view message)
Definition TreeSupport.cpp:104
Polygons to_polygons(const ExPolygon &src)
Definition ExPolygon.hpp:281

References check_self_intersections(), and Slic3r::to_polygons().

+ Here is the call graph for this function:

◆ check_self_intersections() [2/2]

static void Slic3r::FFFTreeSupport::check_self_intersections ( const Polygons polygons,
const std::string_view  message 
)
inlinestatic
105{
106#ifdef TREE_SUPPORT_SHOW_ERRORS_WIN32
107 if (!intersecting_edges(polygons).empty())
108 ::MessageBoxA(nullptr, (std::string("TreeSupport infill self intersections: ") + std::string(message)).c_str(), "Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING);
109#endif // TREE_SUPPORT_SHOW_ERRORS_WIN32
110}
std::vector< std::pair< EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge > > intersecting_edges(const Polygons &polygons)
Definition EdgeGrid.cpp:1568
bool empty(const BoundingBoxBase< PointType, PointsType > &bb)
Definition BoundingBox.hpp:229

References Slic3r::empty(), and Slic3r::intersecting_edges().

Referenced by check_self_intersections(), and generate_support_infill_lines().

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

◆ convert_lines_to_internal()

static LineInformations Slic3r::FFFTreeSupport::convert_lines_to_internal ( const TreeModelVolumes volumes,
const TreeSupportSettings config,
const Polylines polylines,
LayerIndex  layer_idx 
)
static

Converts a Polygons object representing a line into the internal format.

Parameters
polylines[in]The Polyline that will be converted.
layer_idx[in]The current layer.
Returns
All lines of the polylines object, with information for each point regarding in which avoidance it is currently valid in.
357{
358 const bool min_xy_dist = config.xy_distance > config.xy_min_distance;
359
360 LineInformations result;
361 // Also checks if the position is valid, if it is NOT, it deletes that point
362 for (const Polyline &line : polylines) {
363 LineInformation res_line;
364 for (Point p : line) {
365 if (! contains(volumes.getAvoidance(config.getRadius(0), layer_idx, TreeModelVolumes::AvoidanceType::FastSafe, false, min_xy_dist), p))
366 res_line.emplace_back(p, LineStatus::TO_BP_SAFE);
367 else if (! contains(volumes.getAvoidance(config.getRadius(0), layer_idx, TreeModelVolumes::AvoidanceType::Fast, false, min_xy_dist), p))
368 res_line.emplace_back(p, LineStatus::TO_BP);
369 else if (config.support_rests_on_model && ! contains(volumes.getAvoidance(config.getRadius(0), layer_idx, TreeModelVolumes::AvoidanceType::FastSafe, true, min_xy_dist), p))
370 res_line.emplace_back(p, LineStatus::TO_MODEL_GRACIOUS_SAFE);
371 else if (config.support_rests_on_model && ! contains(volumes.getAvoidance(config.getRadius(0), layer_idx, TreeModelVolumes::AvoidanceType::Fast, true, min_xy_dist), p))
372 res_line.emplace_back(p, LineStatus::TO_MODEL_GRACIOUS);
373 else if (config.support_rests_on_model && ! contains(volumes.getCollision(config.getRadius(0), layer_idx, min_xy_dist), p))
374 res_line.emplace_back(p, LineStatus::TO_MODEL);
375 else if (!res_line.empty()) {
376 result.emplace_back(res_line);
377 res_line.clear();
378 }
379 }
380 if (!res_line.empty()) {
381 result.emplace_back(res_line);
382 res_line.clear();
383 }
384 }
385
386 validate_range(result);
387 return result;
388}
const Polygons & getAvoidance(coord_t radius, LayerIndex layer_idx, AvoidanceType type, bool to_model, bool min_xy_dist) const
Provides the areas that have to be avoided by the tree's branches in order to reach the build plate.
Definition TreeModelVolumes.cpp:327
const Polygons & getCollision(const coord_t radius, LayerIndex layer_idx, bool min_xy_dist) const
Provides the areas that have to be avoided by the tree's branches to prevent collision with the model...
Definition TreeModelVolumes.cpp:291
Definition Point.hpp:158
Definition Polyline.hpp:17
static void validate_range(const Point &pt)
Definition TreeSupport.cpp:62
std::vector< LineInformation > LineInformations
Definition TreeSupport.cpp:59
std::vector< std::pair< Point, LineStatus > > LineInformation
Definition TreeSupport.cpp:58
static int contains(const char c, const char *matrix, size_t len)
Definition semver.c:65
coord_t xy_distance
How far should support be from the model.
Definition TreeSupportCommon.hpp:276
coord_t getRadius(size_t distance_to_top, const double elephant_foot_increases=0) const
Get the Radius part will have based on numeric values.
Definition TreeSupportCommon.hpp:403
coord_t xy_min_distance
minimum xy_distance. Only relevant when Z overrides XY, otherwise equal to xy_distance-
Definition TreeSupportCommon.hpp:293
bool support_rests_on_model
True if the branches may connect to the model.
Definition TreeSupportCommon.hpp:272

References Slic3r::contains(), Slic3r::FFFTreeSupport::TreeModelVolumes::Fast, Slic3r::FFFTreeSupport::TreeModelVolumes::FastSafe, Slic3r::FFFTreeSupport::TreeModelVolumes::getAvoidance(), Slic3r::FFFTreeSupport::TreeModelVolumes::getCollision(), Slic3r::FFFTreeSupport::TreeSupportSettings::getRadius(), Slic3r::FFFTreeSupport::TreeSupportSettings::support_rests_on_model, TO_BP, TO_BP_SAFE, TO_MODEL, TO_MODEL_GRACIOUS, TO_MODEL_GRACIOUS_SAFE, validate_range(), Slic3r::FFFTreeSupport::TreeSupportSettings::xy_distance, and Slic3r::FFFTreeSupport::TreeSupportSettings::xy_min_distance.

Referenced by sample_overhang_area().

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

◆ create_layer_pathing()

static void Slic3r::FFFTreeSupport::create_layer_pathing ( const TreeModelVolumes volumes,
const TreeSupportSettings config,
std::vector< SupportElements > &  move_bounds,
std::function< void()>  throw_on_cancel 
)
static

Propagates influence downwards, and merges overlapping ones.

Parameters
move_bounds[in,out]All currently existing influence areas
2406{
2407#ifdef SLIC3R_TREESUPPORTS_PROGRESS
2408 const double data_size_inverse = 1 / double(move_bounds.size());
2409 double progress_total = TREE_PROGRESS_PRECALC_AVO + TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_GENERATE_NODES;
2410#endif // SLIC3R_TREESUPPORTS_PROGRESS
2411
2412 auto dur_inc = std::chrono::duration_values<std::chrono::nanoseconds>::zero();
2413 auto dur_total = std::chrono::duration_values<std::chrono::nanoseconds>::zero();
2414
2415 LayerIndex last_merge_layer_idx = move_bounds.size();
2416 bool new_element = false;
2417
2418 // Ensures at least one merge operation per 3mm height, 50 layers, 1 mm movement of slow speed or 5mm movement of fast speed (whatever is lowest). Values were guessed.
2419 size_t max_merge_every_x_layers = std::min(std::min(5000 / (std::max(config.maximum_move_distance, coord_t(100))), 1000 / std::max(config.maximum_move_distance_slow, coord_t(20))), 3000 / config.layer_height);
2420 size_t merge_every_x_layers = 1;
2421 // Calculate the influence areas for each layer below (Top down)
2422 // This is done by first increasing the influence area by the allowed movement distance, and merging them with other influence areas if possible
2423 for (int layer_idx = int(move_bounds.size()) - 1; layer_idx > 0; -- layer_idx)
2424 if (SupportElements &prev_layer = move_bounds[layer_idx]; ! prev_layer.empty()) {
2425 // merging is expensive and only parallelized to a max speedup of 2. As such it may be useful in some cases to only merge every few layers to improve performance.
2426 bool had_new_element = new_element;
2427 const bool merge_this_layer = had_new_element || size_t(last_merge_layer_idx - layer_idx) >= merge_every_x_layers;
2428 if (had_new_element)
2429 merge_every_x_layers = 1;
2430 const auto ta = std::chrono::high_resolution_clock::now();
2431
2432 // ### Increase the influence areas by the allowed movement distance
2433 std::vector<SupportElementMerging> influence_areas;
2434 influence_areas.reserve(prev_layer.size());
2435 for (int32_t element_idx = 0; element_idx < int32_t(prev_layer.size()); ++ element_idx) {
2436 SupportElement &el = prev_layer[element_idx];
2437 assert(!el.influence_area.empty());
2439 parents.emplace_back(element_idx);
2440 influence_areas.push_back({ el.state, parents });
2441 }
2442 increase_areas_one_layer(volumes, config, influence_areas, layer_idx, prev_layer, merge_this_layer, throw_on_cancel);
2443
2444 // Place already fully constructed elements to the output, remove them from influence_areas.
2445 SupportElements &this_layer = move_bounds[layer_idx - 1];
2446 influence_areas.erase(std::remove_if(influence_areas.begin(), influence_areas.end(),
2447 [&this_layer, layer_idx](SupportElementMerging &elem) {
2448 if (elem.areas.influence_areas.empty())
2449 // This area was removed completely due to collisions.
2450 return true;
2451 if (elem.areas.to_bp_areas.empty() && elem.areas.to_model_areas.empty()) {
2452 if (area(elem.areas.influence_areas) < tiny_area_threshold) {
2453 BOOST_LOG_TRIVIAL(error) << "Insert Error of Influence area bypass on layer " << layer_idx - 1;
2454 tree_supports_show_error("Insert error of area after bypassing merge.\n"sv, true);
2455 }
2456 // Move the area to output.
2457 this_layer.emplace_back(elem.state, std::move(elem.parents), std::move(elem.areas.influence_areas));
2458 return true;
2459 }
2460 // Keep the area.
2461 return false;
2462 }),
2463 influence_areas.end());
2464
2465 dur_inc += std::chrono::high_resolution_clock::now() - ta;
2466 new_element = ! move_bounds[layer_idx - 1].empty();
2467 if (merge_this_layer) {
2468 bool reduced_by_merging = false;
2469 if (size_t count_before_merge = influence_areas.size(); count_before_merge > 1) {
2470 // ### Calculate which influence areas overlap, and merge them into a new influence area (simplified: an intersection of influence areas that have such an intersection)
2471 merge_influence_areas(volumes, config, layer_idx, influence_areas, throw_on_cancel);
2472 reduced_by_merging = count_before_merge > influence_areas.size();
2473 }
2474 last_merge_layer_idx = layer_idx;
2475 if (! reduced_by_merging && ! had_new_element)
2476 merge_every_x_layers = std::min(max_merge_every_x_layers, merge_every_x_layers + 1);
2477 }
2478
2479 dur_total += std::chrono::high_resolution_clock::now() - ta;
2480
2481 // Save calculated elements to output, and allocate Polygons on heap, as they will not be changed again.
2482 for (SupportElementMerging &elem : influence_areas)
2483 if (! elem.areas.influence_areas.empty()) {
2484 Polygons new_area = safe_union(elem.areas.influence_areas);
2485 if (area(new_area) < tiny_area_threshold) {
2486 BOOST_LOG_TRIVIAL(error) << "Insert Error of Influence area on layer " << layer_idx - 1 << ". Origin of " << elem.parents.size() << " areas. Was to bp " << elem.state.to_buildplate;
2487 tree_supports_show_error("Insert error of area after merge.\n"sv, true);
2488 }
2489 this_layer.emplace_back(elem.state, std::move(elem.parents), std::move(new_area));
2490 }
2491
2492 #ifdef SLIC3R_TREESUPPORTS_PROGRESS
2493 progress_total += data_size_inverse * TREE_PROGRESS_AREA_CALC;
2494 Progress::messageProgress(Progress::Stage::SUPPORT, progress_total * m_progress_multiplier + m_progress_offset, TREE_PROGRESS_TOTAL);
2495 #endif
2496 throw_on_cancel();
2497 }
2498
2499 BOOST_LOG_TRIVIAL(info) << "Time spent with creating influence areas' subtasks: Increasing areas " << dur_inc.count() / 1000000 <<
2500 " ms merging areas: " << (dur_total - dur_inc).count() / 1000000 << " ms";
2501}
if(!(yy_init))
Definition lexer.c:1190
int32_t coord_t
Definition libslic3r.h:39
static void merge_influence_areas(const TreeModelVolumes &volumes, const TreeSupportSettings &config, const LayerIndex layer_idx, std::vector< SupportElementMerging > &influence_areas, std::function< void()> throw_on_cancel)
Merges Influence Areas at one layer if possible.
Definition TreeSupport.cpp:2310
int LayerIndex
Definition TreeSupportCommon.hpp:26
void tree_supports_show_error(std::string_view message, bool critical)
Definition TreeSupportCommon.cpp:173
std::deque< SupportElement > SupportElements
Definition TreeSupport.hpp:281
static Polygons safe_union(const Polygons first, const Polygons second={})
Unions two Polygons. Ensures that if the input is non empty that the output also will be non empty.
Definition TreeSupport.cpp:720
IGL_INLINE void count(const Eigen::SparseMatrix< XType > &X, const int dim, Eigen::SparseVector< SType > &S)
Definition count.cpp:12
Unit area(const Cntr &poly, const PathTag &)
Definition geometry_traits.hpp:971
Definition TreeSupport.hpp:252
std::vector< int32_t > ParentIndices
Definition TreeSupport.hpp:259
Polygons influence_area
The resulting influence area. Will only be set in the results of createLayerPathing,...
Definition TreeSupport.hpp:278
SupportElementState state
Definition TreeSupport.hpp:267
coord_t layer_height
Height of a single layer.
Definition TreeSupportCommon.hpp:224
coord_t maximum_move_distance
How far an influence area may move outward every layer at most.
Definition TreeSupportCommon.hpp:236
coord_t maximum_move_distance_slow
How far every influence area will move outward every layer if possible.
Definition TreeSupportCommon.hpp:240
static char error[256]
Definition tga.cpp:50
__int32 int32_t
Definition unistd.h:75

References create_layer_pathing(), error, increase_areas_one_layer(), Slic3r::FFFTreeSupport::SupportElement::influence_area, Slic3r::FFFTreeSupport::TreeSupportSettings::layer_height, Slic3r::FFFTreeSupport::TreeSupportSettings::maximum_move_distance, Slic3r::FFFTreeSupport::TreeSupportSettings::maximum_move_distance_slow, merge_influence_areas(), safe_union(), Slic3r::FFFTreeSupport::SupportElement::state, and tree_supports_show_error().

Referenced by create_layer_pathing(), and generate_support_areas().

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

◆ create_nodes_from_area()

static void Slic3r::FFFTreeSupport::create_nodes_from_area ( const TreeModelVolumes volumes,
const TreeSupportSettings config,
std::vector< SupportElements > &  move_bounds,
std::function< void()>  throw_on_cancel 
)
static

Set the result_on_layer point for all influence areas.

Parameters
move_bounds[in,out]All currently existing influence areas
2652{
2653 // Initialize points on layer 0, with a "random" point in the influence area.
2654 // Point is chosen based on an inaccurate estimate where the branches will split into two, but every point inside the influence area would produce a valid result.
2655 {
2656 SupportElements *layer_above = move_bounds.size() > 1 ? &move_bounds[1] : nullptr;
2657 if (layer_above) {
2658 for (SupportElement &elem : *layer_above)
2659 elem.state.marked = false;
2660 }
2661 for (SupportElement &init : move_bounds.front()) {
2662 init.state.result_on_layer = move_inside_if_outside(init.influence_area, init.state.next_position);
2663 // Also set the parent nodes, as these will be required for the first iteration of the loop below and mark the parent nodes.
2664 set_points_on_areas(init, layer_above);
2665 }
2666 }
2667
2668 throw_on_cancel();
2669
2670 for (LayerIndex layer_idx = 1; layer_idx < LayerIndex(move_bounds.size()); ++ layer_idx) {
2671 auto &layer = move_bounds[layer_idx];
2672 auto *layer_above = layer_idx + 1 < LayerIndex(move_bounds.size()) ? &move_bounds[layer_idx + 1] : nullptr;
2673 if (layer_above)
2674 for (SupportElement &elem : *layer_above)
2675 elem.state.marked = false;
2676 for (SupportElement &elem : layer) {
2677 assert(! elem.state.deleted);
2678 assert(elem.state.layer_idx == layer_idx);
2679 // check if the resulting center point is not yet set
2680 if (! elem.state.result_on_layer_is_set()) {
2681 if (elem.state.to_buildplate || (elem.state.distance_to_top < config.min_dtt_to_model && ! elem.state.supports_roof)) {
2682 if (elem.state.to_buildplate) {
2683 BOOST_LOG_TRIVIAL(error) << "Uninitialized Influence area targeting " << elem.state.target_position.x() << "," << elem.state.target_position.y() << ") "
2684 "at target_height: " << elem.state.target_height << " layer: " << layer_idx;
2685 tree_supports_show_error("Uninitialized support element! A branch could be missing or exist partially."sv, true);
2686 }
2687 // we dont need to remove yet the parents as they will have a lower dtt and also no result_on_layer set
2688 elem.state.deleted = true;
2689 } else {
2690 // set the point where the branch will be placed on the model
2691 if (elem.state.to_model_gracious)
2692 set_to_model_contact_to_model_gracious(volumes, config, move_bounds, elem, throw_on_cancel);
2693 else
2695 }
2696 }
2697 if (! elem.state.deleted && ! elem.state.marked && elem.state.target_height == layer_idx)
2698 // Just a tip surface with no supporting element.
2699 elem.state.deleted = true;
2700 if (elem.state.deleted) {
2701 for (int32_t parent_idx : elem.parents)
2702 // When the roof was not able to generate downwards enough, the top elements may have not moved, and have result_on_layer already set.
2703 // As this branch needs to be removed => all parents result_on_layer have to be invalidated.
2704 (*layer_above)[parent_idx].state.result_on_layer_reset();
2705 }
2706 if (! elem.state.deleted) {
2707 // Element is valid now setting points in the layer above and mark the parent nodes.
2708 set_points_on_areas(elem, layer_above);
2709 }
2710 }
2711 throw_on_cancel();
2712 }
2713
2714#ifndef NDEBUG
2715 // Verify the tree connectivity including the branch slopes.
2716 for (LayerIndex layer_idx = 0; layer_idx + 1 < LayerIndex(move_bounds.size()); ++ layer_idx) {
2717 auto &layer = move_bounds[layer_idx];
2718 auto &above = move_bounds[layer_idx + 1];
2719 for (SupportElement &elem : layer)
2720 if (! elem.state.deleted) {
2721 for (int32_t iparent : elem.parents) {
2722 SupportElement &parent = above[iparent];
2723 assert(! parent.state.deleted);
2724 assert(elem.state.result_on_layer_is_set() == parent.state.result_on_layer_is_set());
2725 if (elem.state.result_on_layer_is_set()) {
2726 double radius_increase = support_element_radius(config, elem) - support_element_radius(config, parent);
2727 assert(radius_increase >= 0);
2728 double shift = (elem.state.result_on_layer - parent.state.result_on_layer).cast<double>().norm();
2729 //FIXME this assert fails a lot. Is it correct?
2730// assert(shift < radius_increase + 2. * config.maximum_move_distance_slow);
2731 }
2732 }
2733 }
2734 }
2735#endif // NDEBUG
2736
2737 remove_deleted_elements(move_bounds);
2738
2739#ifndef NDEBUG
2740 // Verify the tree connectivity including the branch slopes.
2741 for (LayerIndex layer_idx = 0; layer_idx + 1 < LayerIndex(move_bounds.size()); ++ layer_idx) {
2742 auto &layer = move_bounds[layer_idx];
2743 auto &above = move_bounds[layer_idx + 1];
2744 for (SupportElement &elem : layer) {
2745 assert(! elem.state.deleted);
2746 for (int32_t iparent : elem.parents) {
2747 SupportElement &parent = above[iparent];
2748 assert(! parent.state.deleted);
2749 assert(elem.state.result_on_layer_is_set() == parent.state.result_on_layer_is_set());
2750 if (elem.state.result_on_layer_is_set()) {
2751 double radius_increase = support_element_radius(config, elem) - support_element_radius(config, parent);
2752 assert(radius_increase >= 0);
2753 double shift = (elem.state.result_on_layer - parent.state.result_on_layer).cast<double>().norm();
2754 //FIXME this assert fails a lot. Is it correct?
2755// assert(shift < radius_increase + 2. * config.maximum_move_distance_slow);
2756 }
2757 }
2758 }
2759 }
2760#endif // NDEBUG
2761}
static void set_points_on_areas(const SupportElement &elem, SupportElements *layer_above)
Sets the result_on_layer for all parents based on the SupportElement supplied.
Definition TreeSupport.cpp:2508
coord_t support_element_radius(const TreeSupportSettings &settings, const SupportElementState &elem)
Get the Radius, that this element will have.
Definition TreeSupport.hpp:236
static void remove_deleted_elements(std::vector< SupportElements > &move_bounds)
Definition TreeSupport.cpp:2600
static void set_to_model_contact_to_model_gracious(const TreeModelVolumes &volumes, const TreeSupportSettings &config, std::vector< SupportElements > &move_bounds, SupportElement &first_elem, std::function< void()> throw_on_cancel)
Get the best point to connect to the model and set the result_on_layer of the relevant SupportElement...
Definition TreeSupport.cpp:2552
static void set_to_model_contact_simple(SupportElement &elem)
Definition TreeSupport.cpp:2538
size_t min_dtt_to_model
If smaller (in layers) than that, all branches to model will be deleted.
Definition TreeSupportCommon.hpp:260

References create_nodes_from_area(), Slic3r::FFFTreeSupport::SupportElementStateBits::deleted, error, Slic3r::FFFTreeSupport::TreeSupportSettings::min_dtt_to_model, move_inside_if_outside(), remove_deleted_elements(), Slic3r::FFFTreeSupport::SupportElementState::result_on_layer, Slic3r::FFFTreeSupport::SupportElementState::result_on_layer_is_set(), set_points_on_areas(), set_to_model_contact_simple(), set_to_model_contact_to_model_gracious(), Slic3r::FFFTreeSupport::SupportElement::state, support_element_radius(), and tree_supports_show_error().

Referenced by create_nodes_from_area(), and generate_support_areas().

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

◆ discretize_circle()

static std::pair< int, int > Slic3r::FFFTreeSupport::discretize_circle ( const Vec3f center,
const Vec3f normal,
const float  radius,
const float  eps,
std::vector< Vec3f > &  pts 
)
static
562{
563 // Calculate discretization step and number of steps.
564 float angle_step = 2. * acos(1. - eps / radius);
565 auto nsteps = int(ceil(2 * M_PI / angle_step));
566 angle_step = 2 * M_PI / nsteps;
567
568 // Prepare coordinate system for the circle plane.
569 Vec3f x = normal.cross(Vec3f(0.f, -1.f, 0.f)).normalized();
570 Vec3f y = normal.cross(x).normalized();
571 assert(std::abs(x.cross(y).dot(normal) - 1.f) < EPSILON);
572
573 // Discretize the circle.
574 int begin = int(pts.size());
575 pts.reserve(pts.size() + nsteps);
576 float angle = 0;
577 x *= radius;
578 y *= radius;
579 for (int i = 0; i < nsteps; ++ i) {
580 pts.emplace_back(center + x * cos(angle) + y * sin(angle));
581 angle += angle_step;
582 }
583 return { begin, int(pts.size()) };
584}
EIGEN_DEVICE_FUNC const AcosReturnType acos() const
Definition ArrayCwiseUnaryOps.h:262
EIGEN_DEVICE_FUNC const CeilReturnType ceil() const
Definition ArrayCwiseUnaryOps.h:402
EIGEN_DEVICE_FUNC const CosReturnType cos() const
Definition ArrayCwiseUnaryOps.h:202
EIGEN_DEVICE_FUNC const SinReturnType sin() const
Definition ArrayCwiseUnaryOps.h:220
#define M_PI
Definition ExtrusionSimulator.cpp:20
static constexpr double EPSILON
Definition libslic3r.h:51
double angle(const Eigen::MatrixBase< Derived > &v1, const Eigen::MatrixBase< Derived2 > &v2)
Definition Point.hpp:112

References acos(), Slic3r::angle(), ceil(), cos(), EPSILON, M_PI, and sin().

Referenced by extrude_branch().

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

◆ draw_areas()

static void Slic3r::FFFTreeSupport::draw_areas ( PrintObject print_object,
const TreeModelVolumes volumes,
const TreeSupportSettings config,
const std::vector< Polygons > &  overhangs,
std::vector< SupportElements > &  move_bounds,
SupportGeneratorLayersPtr bottom_contacts,
SupportGeneratorLayersPtr top_contacts,
SupportGeneratorLayersPtr intermediate_layers,
SupportGeneratorLayerStorage layer_storage,
std::function< void()>  throw_on_cancel 
)
static

Draws circles around result_on_layer points of the influence areas and applies some post processing.

Parameters
move_bounds[in]All currently existing influence areas
storage[in,out]The storage where the support should be stored.
3250{
3251 std::vector<Polygons> support_layer_storage(move_bounds.size());
3252 std::vector<Polygons> support_roof_storage(move_bounds.size());
3253 // All SupportElements are put into a layer independent storage to improve parallelization.
3254 std::vector<DrawArea> linear_data;
3255 std::vector<size_t> linear_data_layers;
3256 {
3257 std::vector<std::pair<SupportElement*, SupportElement*>> map_downwards_old;
3258 std::vector<std::pair<SupportElement*, SupportElement*>> map_downwards_new;
3259 for (LayerIndex layer_idx = 0; layer_idx < LayerIndex(move_bounds.size()); ++ layer_idx) {
3260 SupportElements *layer_above = layer_idx + 1 < LayerIndex(move_bounds.size()) ? &move_bounds[layer_idx + 1] : nullptr;
3261 map_downwards_new.clear();
3262 linear_data_layers.emplace_back(linear_data.size());
3263 std::sort(map_downwards_old.begin(), map_downwards_old.end(), [](auto &l, auto &r) { return l.first < r.first; });
3264 for (SupportElement &elem : move_bounds[layer_idx]) {
3265 SupportElement *child = nullptr;
3266 if (layer_idx > 0) {
3267 auto it = std::lower_bound(map_downwards_old.begin(), map_downwards_old.end(), &elem, [](auto &l, const SupportElement *r) { return l.first < r; });
3268 if (it != map_downwards_old.end() && it->first == &elem) {
3269 child = it->second;
3270 // Only one link points to a node above from below.
3271 assert(! (++ it != map_downwards_old.end() && it->first == &elem));
3272 }
3273 assert(child ? child->state.result_on_layer_is_set() : elem.state.target_height > layer_idx);
3274 }
3275 for (int32_t parent_idx : elem.parents) {
3276 SupportElement &parent = (*layer_above)[parent_idx];
3277 if (parent.state.result_on_layer_is_set())
3278 map_downwards_new.emplace_back(&parent, &elem);
3279 }
3280 linear_data.push_back({ &elem, child });
3281 }
3282 std::swap(map_downwards_old, map_downwards_new);
3283 }
3284 linear_data_layers.emplace_back(linear_data.size());
3285 }
3286
3287 throw_on_cancel();
3288
3289#ifndef NDEBUG
3290 for (size_t i = 0; i < move_bounds.size(); ++ i) {
3291 size_t begin = linear_data_layers[i];
3292 size_t end = linear_data_layers[i + 1];
3293 for (size_t j = begin; j < end; ++ j)
3294 assert(linear_data[j].element == &move_bounds[i][j - begin]);
3295 }
3296#endif // NDEBUG
3297
3298 auto t_start = std::chrono::high_resolution_clock::now();
3299 // Generate the circles that will be the branches.
3300 generate_branch_areas(volumes, config, move_bounds, linear_data, throw_on_cancel);
3301
3302#if 0
3303 assert(linear_data_layers.size() == move_bounds.size() + 1);
3304 for (const auto &draw_area : linear_data)
3305 assert(contains(draw_area.polygons, draw_area.element->state.result_on_layer));
3306 for (size_t i = 0; i < move_bounds.size(); ++ i) {
3307 size_t begin = linear_data_layers[i];
3308 size_t end = linear_data_layers[i + 1];
3309 for (size_t j = begin; j < end; ++ j) {
3310 const auto &draw_area = linear_data[j];
3311 assert(draw_area.element == &move_bounds[i][j - begin]);
3312 assert(contains(draw_area.polygons, draw_area.element->state.result_on_layer));
3313 }
3314 }
3315#endif
3316
3317#if 0
3318 for (size_t area_layer_idx = 0; area_layer_idx + 1 < linear_data_layers.size(); ++ area_layer_idx) {
3319 size_t begin = linear_data_layers[area_layer_idx];
3320 size_t end = linear_data_layers[area_layer_idx + 1];
3321 Polygons polygons;
3322 for (size_t area_idx = begin; area_idx < end; ++ area_idx) {
3323 DrawArea &area = linear_data[area_idx];
3324 append(polygons, area.polygons);
3325 }
3326 SVG::export_expolygons(debug_out_path("treesupport-extrude_areas-raw-%d.svg", area_layer_idx),
3327 { { { union_ex(polygons) }, { "parent", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
3328 }
3329#endif
3330
3331 auto t_generate = std::chrono::high_resolution_clock::now();
3332 // In some edgecases a branch may go though a hole, where the regular radius does not fit. This can result in an apparent jump in branch radius. As such this cases need to be caught and smoothed out.
3333 smooth_branch_areas(config, move_bounds, linear_data, linear_data_layers, throw_on_cancel);
3334
3335#if 0
3336 for (size_t area_layer_idx = 0; area_layer_idx + 1 < linear_data_layers.size(); ++area_layer_idx) {
3337 size_t begin = linear_data_layers[area_layer_idx];
3338 size_t end = linear_data_layers[area_layer_idx + 1];
3339 Polygons polygons;
3340 for (size_t area_idx = begin; area_idx < end; ++area_idx) {
3341 DrawArea& area = linear_data[area_idx];
3342 append(polygons, area.polygons);
3343 }
3344 SVG::export_expolygons(debug_out_path("treesupport-extrude_areas-smooth-%d.svg", area_layer_idx),
3345 { { { union_ex(polygons) }, { "parent", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
3346 }
3347#endif
3348
3349 auto t_smooth = std::chrono::high_resolution_clock::now();
3350 // drop down all trees that connect non gracefully with the model
3351 drop_non_gracious_areas(volumes, linear_data, support_layer_storage, throw_on_cancel);
3352 auto t_drop = std::chrono::high_resolution_clock::now();
3353
3354 // Single threaded combining all support areas to the right layers.
3355 {
3356 auto begin = linear_data.begin();
3357 for (LayerIndex layer_idx = 0; layer_idx < LayerIndex(move_bounds.size()); ++ layer_idx) {
3358 size_t cnt_roofs = 0;
3359 size_t cnt_layers = 0;
3360 auto end = begin;
3361 for (; end != linear_data.end() && end->element->state.layer_idx == layer_idx; ++ end)
3362 ++ (end->element->state.missing_roof_layers > end->element->state.distance_to_top ? cnt_roofs : cnt_layers);
3363 auto &this_roofs = support_roof_storage[layer_idx];
3364 auto &this_layers = support_layer_storage[layer_idx];
3365 this_roofs.reserve(this_roofs.size() + cnt_roofs);
3366 this_layers.reserve(this_layers.size() + cnt_layers);
3367 for (auto it = begin; it != end; ++ it)
3368 std::move(std::begin(it->polygons), std::end(it->polygons), std::back_inserter(it->element->state.missing_roof_layers > it->element->state.distance_to_top ? this_roofs : this_layers));
3369 begin = end;
3370 }
3371 }
3372
3373 finalize_interface_and_support_areas(print_object, volumes, config, overhangs, support_layer_storage, support_roof_storage,
3374 bottom_contacts, top_contacts, intermediate_layers, layer_storage, throw_on_cancel);
3375 auto t_end = std::chrono::high_resolution_clock::now();
3376
3377 auto dur_gen_tips = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_generate - t_start).count();
3378 auto dur_smooth = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_smooth - t_generate).count();
3379 auto dur_drop = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_drop - t_smooth).count();
3380 auto dur_finalize = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_end - t_drop).count();
3381
3382 BOOST_LOG_TRIVIAL(info) <<
3383 "Time used for drawing subfuctions: generate_branch_areas: " << dur_gen_tips << " ms "
3384 "smooth_branch_areas: " << dur_smooth << " ms "
3385 "drop_non_gracious_areas: " << dur_drop << " ms "
3386 "finalize_interface_and_support_areas " << dur_finalize << " ms";
3387}
static void finalize_interface_and_support_areas(const PrintObject &print_object, const TreeModelVolumes &volumes, const TreeSupportSettings &config, const std::vector< Polygons > &overhangs, std::vector< Polygons > &support_layer_storage, std::vector< Polygons > &support_roof_storage, SupportGeneratorLayersPtr &bottom_contacts, SupportGeneratorLayersPtr &top_contacts, SupportGeneratorLayersPtr &intermediate_layers, SupportGeneratorLayerStorage &layer_storage, std::function< void()> throw_on_cancel)
Generates Support Floor, ensures Support Roof can not cut of branches, and saves the branches as supp...
Definition TreeSupport.cpp:3079
static void drop_non_gracious_areas(const TreeModelVolumes &volumes, const std::vector< DrawArea > &linear_data, std::vector< Polygons > &support_layer_storage, std::function< void()> throw_on_cancel)
Drop down areas that do rest non-gracefully on the model to ensure the branch actually rests on somet...
Definition TreeSupport.cpp:3044
static void smooth_branch_areas(const TreeSupportSettings &config, std::vector< SupportElements > &move_bounds, std::vector< DrawArea > &linear_data, const std::vector< size_t > &linear_data_layers, std::function< void()> throw_on_cancel)
Applies some smoothing to the outer wall, intended to smooth out sudden jumps as they can happen when...
Definition TreeSupport.cpp:2913
static void generate_branch_areas(const TreeModelVolumes &volumes, const TreeSupportSettings &config, const std::vector< SupportElements > &move_bounds, std::vector< DrawArea > &linear_data, std::function< void()> throw_on_cancel)
Draws circles around result_on_layer points of the influence areas.
Definition TreeSupport.cpp:2782
std::string debug_out_path(const char *name,...)
Definition utils.cpp:218
Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, ClipperLib::PolyFillType fill_type)
Definition ClipperUtils.cpp:774
S::iterator begin(S &sh, const PathTag &)
Definition geometry_traits.hpp:614
S::iterator end(S &sh, const PathTag &)
Definition geometry_traits.hpp:620
void append(SurfaceCut &sc, SurfaceCut &&sc_add)
Merge two surface cuts together Added surface cut will be consumed.
Definition CutSurface.cpp:3550
bool result_on_layer_is_set() const
Definition TreeSupport.hpp:177

References contains(), Slic3r::debug_out_path(), draw_areas(), drop_non_gracious_areas(), finalize_interface_and_support_areas(), generate_branch_areas(), Slic3r::FFFTreeSupport::SupportElementState::result_on_layer_is_set(), smooth_branch_areas(), Slic3r::FFFTreeSupport::SupportElement::state, and Slic3r::union_ex().

Referenced by draw_areas(), and generate_support_areas().

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

◆ drop_non_gracious_areas()

static void Slic3r::FFFTreeSupport::drop_non_gracious_areas ( const TreeModelVolumes volumes,
const std::vector< DrawArea > &  linear_data,
std::vector< Polygons > &  support_layer_storage,
std::function< void()>  throw_on_cancel 
)
static

Drop down areas that do rest non-gracefully on the model to ensure the branch actually rests on something.

Parameters
layer_tree_polygons[in]Resulting branch areas with the layerindex they appear on.
linear_data[in]All currently existing influence areas with the layer they are on
dropped_down_areas[out]Areas that have to be added to support all non-graceful areas.
inverse_tree_order[in]A mapping that returns the child of every influence area.
3049{
3050 std::vector<std::vector<std::pair<LayerIndex, Polygons>>> dropped_down_areas(linear_data.size());
3051 tbb::parallel_for(tbb::blocked_range<size_t>(0, linear_data.size()),
3052 [&](const tbb::blocked_range<size_t> &range) {
3053 for (size_t idx = range.begin(); idx < range.end(); ++ idx) {
3054 // If a element has no child, it connects to whatever is below as no support further down for it will exist.
3055 if (const DrawArea &draw_element = linear_data[idx]; ! draw_element.element->state.to_model_gracious && draw_element.child_element == nullptr) {
3056 Polygons rest_support;
3057 const LayerIndex layer_idx_first = draw_element.element->state.layer_idx - 1;
3058 for (LayerIndex layer_idx = layer_idx_first; area(rest_support) > tiny_area_threshold && layer_idx >= 0; -- layer_idx) {
3059 rest_support = diff_clipped(layer_idx == layer_idx_first ? draw_element.polygons : rest_support, volumes.getCollision(0, layer_idx, false));
3060 dropped_down_areas[idx].emplace_back(layer_idx, rest_support);
3061 }
3062 }
3063 throw_on_cancel();
3064 }
3065 });
3066
3067 for (coord_t i = 0; i < static_cast<coord_t>(dropped_down_areas.size()); i++)
3068 for (std::pair<LayerIndex, Polygons> &pair : dropped_down_areas[i])
3069 append(support_layer_storage[pair.first], std::move(pair.second));
3070}
STL namespace.

References drop_non_gracious_areas(), and Slic3r::range().

Referenced by draw_areas(), and drop_non_gracious_areas().

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

◆ ensure_maximum_distance_polyline()

static Polylines Slic3r::FFFTreeSupport::ensure_maximum_distance_polyline ( const Polylines input,
double  distance,
size_t  min_points 
)
static

Eensures that every line segment is about distance in length. The resulting lines may differ from the original but all points are on the original.

Parameters
input[in]The lines on which evenly spaced points should be placed.
distance[in]The distance the points should be from each other.
min_points[in]The amount of points that have to be placed. If not enough can be placed the distance will be reduced to place this many points.
Returns
A Polygons object containing the evenly spaced points. Does not represent an area, more a collection of points on lines.
525{
526 Polylines result;
527 for (Polyline part : input) {
528 if (part.empty())
529 continue;
530
531 double len = length(part.points);
532 Polyline line;
533 double current_distance = std::max(distance, scaled<double>(0.1));
534 if (len < 2 * distance && min_points <= 1)
535 {
536 // Insert the opposite point of the first one.
537 //FIXME pretty expensive
538 Polyline pl(part);
539 pl.clip_end(len / 2);
540 line.points.emplace_back(pl.points.back());
541 }
542 else
543 {
544 size_t optimal_end_index = part.size() - 1;
545
546 if (part.front() == part.back()) {
547 size_t optimal_start_index = 0;
548 // If the polyline was a polygon, there is a high chance it was an overhang. Overhangs that are <60� tend to be very thin areas, so lets get the beginning and end of them and ensure that they are supported.
549 // The first point of the line will always be supported, so rotate the order of points in this polyline that one of the two corresponding points that are furthest from each other is in the beginning.
550 // The other will be manually added (optimal_end_index)
551 coord_t max_dist2_between_vertecies = 0;
552 for (size_t idx = 0; idx < part.size() - 1; ++ idx) {
553 for (size_t inner_idx = 0; inner_idx < part.size() - 1; inner_idx++) {
554 if ((part[idx] - part[inner_idx]).cast<double>().squaredNorm() > max_dist2_between_vertecies) {
555 optimal_start_index = idx;
556 optimal_end_index = inner_idx;
557 max_dist2_between_vertecies = (part[idx] - part[inner_idx]).cast<double>().squaredNorm();
558 }
559 }
560 }
561 std::rotate(part.begin(), part.begin() + optimal_start_index, part.end() - 1);
562 part[part.size() - 1] = part[0]; // restore that property that this polyline ends where it started.
563 optimal_end_index = (part.size() + optimal_end_index - optimal_start_index - 1) % (part.size() - 1);
564 }
565
566 while (line.size() < min_points && current_distance >= scaled<double>(0.1))
567 {
568 line.clear();
569 Point current_point = part[0];
570 line.points.emplace_back(part[0]);
571 if (min_points > 1 || (part[0] - part[optimal_end_index]).cast<double>().norm() > current_distance)
572 line.points.emplace_back(part[optimal_end_index]);
573 size_t current_index = 0;
574 std::optional<std::pair<Point, size_t>> next_point;
575 double next_distance = current_distance;
576 // Get points so that at least min_points are added and they each are current_distance away from each other. If that is impossible, decrease current_distance a bit.
577 // The input are lines, that means that the line from the last to the first vertex does not have to exist, so exclude all points that are on this line!
578 while ((next_point = polyline_sample_next_point_at_distance(part.points, current_point, current_index, next_distance))) {
579 // Not every point that is distance away, is valid, as it may be much closer to another point. This is especially the case when the overhang is very thin.
580 // So this ensures that the points are actually a certain distance from each other.
581 // This assurance is only made on a per polygon basis, as different but close polygon may not be able to use support below the other polygon.
582 double min_distance_to_existing_point = std::numeric_limits<double>::max();
583 for (Point p : line)
584 min_distance_to_existing_point = std::min(min_distance_to_existing_point, (p - next_point->first).cast<double>().norm());
585 if (min_distance_to_existing_point >= current_distance) {
586 // viable point was found. Add to possible result.
587 line.points.emplace_back(next_point->first);
588 current_point = next_point->first;
589 current_index = next_point->second;
590 next_distance = current_distance;
591 } else {
592 if (current_point == next_point->first) {
593 // In case a fixpoint is encountered, better aggressively overcompensate so the code does not become stuck here...
594 BOOST_LOG_TRIVIAL(warning) << "Tree Support: Encountered a fixpoint in polyline_sample_next_point_at_distance. This is expected to happen if the distance (currently " << next_distance <<
595 ") is smaller than 100";
596 tree_supports_show_error("Encountered issue while placing tips. Some tips may be missing."sv, true);
597 if (next_distance > 2 * current_distance)
598 // This case should never happen, but better safe than sorry.
599 break;
600 next_distance += current_distance;
601 continue;
602 }
603 // if the point was too close, the next possible viable point is at least distance-min_distance_to_existing_point away from the one that was just checked.
604 next_distance = std::max(current_distance - min_distance_to_existing_point, scaled<double>(0.1));
605 current_point = next_point->first;
606 current_index = next_point->second;
607 }
608 }
609 current_distance *= 0.9;
610 }
611 }
612 result.emplace_back(std::move(line));
613 }
614 validate_range(result);
615 return result;
616}
EIGEN_DEVICE_FUNC CastXpr< NewType >::Type cast() const
Definition CommonCwiseUnaryOps.h:62
Points points
Definition MultiPoint.hpp:18
void clear()
Definition MultiPoint.hpp:70
size_t size() const
Definition MultiPoint.hpp:39
static int input(void)
EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half() min(const half &a, const half &b)
Definition Half.h:507
static std::optional< std::pair< Point, size_t > > polyline_sample_next_point_at_distance(const Points &polyline, const Point &start_pt, size_t start_idx, double dist)
Definition TreeSupport.cpp:470
std::vector< Polyline > Polylines
Definition Polyline.hpp:14
Kernel::Point_2 Point
Definition point_areas.cpp:20

References Slic3r::MultiPoint::clear(), Slic3r::Polyline::clip_end(), input(), Slic3r::length(), Slic3r::MultiPoint::points, polyline_sample_next_point_at_distance(), Slic3r::MultiPoint::size(), tree_supports_show_error(), and validate_range().

Referenced by sample_overhang_area().

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

◆ evaluate_point_for_next_layer_function()

static bool Slic3r::FFFTreeSupport::evaluate_point_for_next_layer_function ( const TreeModelVolumes volumes,
const TreeSupportSettings config,
size_t  current_layer,
const std::pair< Point, LineStatus > &  p 
)
static

Evaluates if a point has to be added now. Required for a split_lines call in generate_initial_areas().

Parameters
current_layer[in]The layer on which the point lies, point and its status.
Returns
whether the point is valid.
420{
421 using AvoidanceType = TreeModelVolumes::AvoidanceType;
422 const bool min_xy_dist = config.xy_distance > config.xy_min_distance;
423 if (! contains(volumes.getAvoidance(config.getRadius(0), current_layer - 1, p.second == LineStatus::TO_BP_SAFE ? AvoidanceType::FastSafe : AvoidanceType::Fast, false, min_xy_dist), p.first))
424 return true;
425 if (config.support_rests_on_model && (p.second != LineStatus::TO_BP && p.second != LineStatus::TO_BP_SAFE))
426 return ! contains(
427 p.second == LineStatus::TO_MODEL_GRACIOUS || p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE ?
428 volumes.getAvoidance(config.getRadius(0), current_layer - 1, p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE ? AvoidanceType::FastSafe : AvoidanceType::Fast, true, min_xy_dist) :
429 volumes.getCollision(config.getRadius(0), current_layer - 1, min_xy_dist),
430 p.first);
431 return false;
432}
AvoidanceType
Definition TreeModelVolumes.hpp:74

References Slic3r::contains(), Slic3r::FFFTreeSupport::TreeModelVolumes::getAvoidance(), Slic3r::FFFTreeSupport::TreeModelVolumes::getCollision(), Slic3r::FFFTreeSupport::TreeSupportSettings::getRadius(), Slic3r::FFFTreeSupport::TreeSupportSettings::support_rests_on_model, TO_BP, TO_BP_SAFE, TO_MODEL_GRACIOUS, TO_MODEL_GRACIOUS_SAFE, Slic3r::FFFTreeSupport::TreeSupportSettings::xy_distance, and Slic3r::FFFTreeSupport::TreeSupportSettings::xy_min_distance.

Referenced by Slic3r::FFFTreeSupport::RichInterfacePlacer::add_points_along_lines(), and sample_overhang_area().

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

◆ extrude_branch()

static std::pair< float, float > Slic3r::FFFTreeSupport::extrude_branch ( const std::vector< const SupportElement * > &  path,
const TreeSupportSettings config,
const SlicingParameters slicing_params,
const std::vector< SupportElements > &  move_bounds,
indexed_triangle_set result 
)
static
593{
594 Vec3d p1, p2, p3;
595 Vec3d v1, v2;
596 Vec3d nprev;
597 Vec3d ncurrent;
598 assert(path.size() >= 2);
599 static constexpr const float eps = 0.015f;
600 std::pair<int, int> prev_strip;
601
602// char fname[2048];
603// static int irun = 0;
604
605 float zmin = 0;
606 float zmax = 0;
607
608 for (size_t ipath = 1; ipath < path.size(); ++ ipath) {
609 const SupportElement &prev = *path[ipath - 1];
610 const SupportElement &current = *path[ipath];
611 assert(prev.state.layer_idx + 1 == current.state.layer_idx);
612 p1 = to_3d(unscaled<double>(prev .state.result_on_layer), layer_z(slicing_params, config, prev .state.layer_idx));
613 p2 = to_3d(unscaled<double>(current.state.result_on_layer), layer_z(slicing_params, config, current.state.layer_idx));
614 v1 = (p2 - p1).normalized();
615 if (ipath == 1) {
616 nprev = v1;
617 // Extrude the bottom half sphere.
618 float radius = unscaled<float>(support_element_radius(config, prev));
619 float angle_step = 2. * acos(1. - eps / radius);
620 auto nsteps = int(ceil(M_PI / (2. * angle_step)));
621 angle_step = M_PI / (2. * nsteps);
622 int ifan = int(result.vertices.size());
623 result.vertices.emplace_back((p1 - nprev * radius).cast<float>());
624 zmin = result.vertices.back().z();
625 float angle = angle_step;
626 for (int i = 1; i < nsteps; ++ i, angle += angle_step) {
627 std::pair<int, int> strip = discretize_circle((p1 - nprev * radius * cos(angle)).cast<float>(), nprev.cast<float>(), radius * sin(angle), eps, result.vertices);
628 if (i == 1)
629 triangulate_fan<false>(result, ifan, strip.first, strip.second);
630 else
631 triangulate_strip(result, prev_strip.first, prev_strip.second, strip.first, strip.second);
632// sprintf(fname, "d:\\temp\\meshes\\tree-partial-%d.obj", ++ irun);
633// its_write_obj(result, fname);
634 prev_strip = strip;
635 }
636 }
637 if (ipath + 1 == path.size()) {
638 // End of the tube.
639 ncurrent = v1;
640 // Extrude the top half sphere.
641 float radius = unscaled<float>(support_element_radius(config, current));
642 float angle_step = 2. * acos(1. - eps / radius);
643 auto nsteps = int(ceil(M_PI / (2. * angle_step)));
644 angle_step = M_PI / (2. * nsteps);
645 auto angle = float(M_PI / 2.);
646 for (int i = 0; i < nsteps; ++ i, angle -= angle_step) {
647 std::pair<int, int> strip = discretize_circle((p2 + ncurrent * radius * cos(angle)).cast<float>(), ncurrent.cast<float>(), radius * sin(angle), eps, result.vertices);
648 triangulate_strip(result, prev_strip.first, prev_strip.second, strip.first, strip.second);
649// sprintf(fname, "d:\\temp\\meshes\\tree-partial-%d.obj", ++ irun);
650// its_write_obj(result, fname);
651 prev_strip = strip;
652 }
653 int ifan = int(result.vertices.size());
654 result.vertices.emplace_back((p2 + ncurrent * radius).cast<float>());
655 zmax = result.vertices.back().z();
656 triangulate_fan<true>(result, ifan, prev_strip.first, prev_strip.second);
657// sprintf(fname, "d:\\temp\\meshes\\tree-partial-%d.obj", ++ irun);
658// its_write_obj(result, fname);
659 } else {
660 const SupportElement &next = *path[ipath + 1];
661 assert(current.state.layer_idx + 1 == next.state.layer_idx);
662 p3 = to_3d(unscaled<double>(next.state.result_on_layer), layer_z(slicing_params, config, next.state.layer_idx));
663 v2 = (p3 - p2).normalized();
664 ncurrent = (v1 + v2).normalized();
665 float radius = unscaled<float>(support_element_radius(config, current));
666 std::pair<int, int> strip = discretize_circle(p2.cast<float>(), ncurrent.cast<float>(), radius, eps, result.vertices);
667 triangulate_strip(result, prev_strip.first, prev_strip.second, strip.first, strip.second);
668 prev_strip = strip;
669// sprintf(fname, "d:\\temp\\meshes\\tree-partial-%d.obj", ++irun);
670// its_write_obj(result, fname);
671 }
672#if 0
673 if (circles_intersect(p1, nprev, support_element_radius(settings, prev), p2, ncurrent, support_element_radius(settings, current))) {
674 // Cannot connect previous and current slice using a simple zig-zag triangulation,
675 // because the two circles intersect.
676
677 } else {
678 // Continue with chaining.
679
680 }
681#endif
682 }
683
684 return std::make_pair(zmin, zmax);
685}
static void triangulate_strip(indexed_triangle_set &its, int ibegin1, int iend1, int ibegin2, int iend2)
Definition OrganicSupport.cpp:495
double layer_z(const SlicingParameters &slicing_params, const TreeSupportSettings &config, const size_t layer_idx)
Definition TreeSupportCommon.hpp:455
static std::pair< int, int > discretize_circle(const Vec3f &center, const Vec3f &normal, const float radius, const float eps, std::vector< Vec3f > &pts)
Definition OrganicSupport.cpp:561
Eigen::Matrix< typename Derived::Scalar, 3, 1, Eigen::DontAlign > to_3d(const Eigen::MatrixBase< Derived > &pt, const typename Derived::Scalar z)
Definition Point.hpp:127
LayerIndex layer_idx
The next height this support elements wants to reach.
Definition TreeSupport.hpp:160
Point result_on_layer
The resulting center point around which a circle will be drawn later. Will be set by setPointsOnAreas...
Definition TreeSupport.hpp:176
std::vector< stl_vertex > vertices
Definition stl.h:165

References acos(), Slic3r::angle(), ceil(), cos(), discretize_circle(), Slic3r::FFFTreeSupport::SupportElementState::layer_idx, layer_z(), M_PI, Slic3r::FFFTreeSupport::SupportElementState::result_on_layer, sin(), Slic3r::FFFTreeSupport::SupportElement::state, support_element_radius(), Slic3r::to_3d(), triangulate_strip(), and indexed_triangle_set::vertices.

+ Here is the call graph for this function:

◆ finalize_interface_and_support_areas()

static void Slic3r::FFFTreeSupport::finalize_interface_and_support_areas ( const PrintObject print_object,
const TreeModelVolumes volumes,
const TreeSupportSettings config,
const std::vector< Polygons > &  overhangs,
std::vector< Polygons > &  support_layer_storage,
std::vector< Polygons > &  support_roof_storage,
SupportGeneratorLayersPtr bottom_contacts,
SupportGeneratorLayersPtr top_contacts,
SupportGeneratorLayersPtr intermediate_layers,
SupportGeneratorLayerStorage layer_storage,
std::function< void()>  throw_on_cancel 
)
static

Generates Support Floor, ensures Support Roof can not cut of branches, and saves the branches as support to storage.

Parameters
support_layer_storage[in]Areas where support should be generated.
support_roof_storage[in]Areas where support was replaced with roof.
storage[in,out]The storage where the support should be stored.
3093{
3094 assert(std::all_of(bottom_contacts.begin(), bottom_contacts.end(), [](auto *p) { return p == nullptr; }));
3095// assert(std::all_of(top_contacts.begin(), top_contacts.end(), [](auto* p) { return p == nullptr; }));
3096 assert(std::all_of(intermediate_layers.begin(), intermediate_layers.end(), [](auto* p) { return p == nullptr; }));
3097
3098 InterfacePreference interface_pref = config.interface_preference; // InterfacePreference::InterfaceAreaOverwritesSupport;
3099
3100#ifdef SLIC3R_TREESUPPORTS_PROGRESS
3101 double progress_total = TREE_PROGRESS_PRECALC_AVO + TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_GENERATE_NODES + TREE_PROGRESS_AREA_CALC + TREE_PROGRESS_GENERATE_BRANCH_AREAS + TREE_PROGRESS_SMOOTH_BRANCH_AREAS;
3102#endif // SLIC3R_TREESUPPORTS_PROGRESS
3103
3104 // Iterate over the generated circles in parallel and clean them up. Also add support floor.
3105 tbb::parallel_for(tbb::blocked_range<size_t>(0, support_layer_storage.size()),
3106 [&](const tbb::blocked_range<size_t> &range) {
3107 for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
3108 // Subtract support lines of the branches from the roof
3109 SupportGeneratorLayer *support_roof = top_contacts[layer_idx];
3110 Polygons support_roof_polygons;
3111
3112 if (Polygons &src = support_roof_storage[layer_idx]; ! src.empty()) {
3113 if (support_roof != nullptr && ! support_roof->polygons.empty()) {
3114 support_roof_polygons = union_(src, support_roof->polygons);
3115 support_roof->polygons.clear();
3116 } else
3117 support_roof_polygons = std::move(src);
3118 } else if (support_roof != nullptr) {
3119 support_roof_polygons = std::move(support_roof->polygons);
3120 support_roof->polygons.clear();
3121 }
3122
3123 assert(intermediate_layers[layer_idx] == nullptr);
3124 Polygons base_layer_polygons = std::move(support_layer_storage[layer_idx]);
3125
3126 if (! base_layer_polygons.empty()) {
3127 // Most of the time in this function is this union call. Can take 300+ ms when a lot of areas are to be unioned.
3128 base_layer_polygons = smooth_outward(union_(base_layer_polygons), config.support_line_width); //FIXME was .smooth(50);
3129 //smooth_outward(closing(std::move(bottom), closing_distance + minimum_island_radius, closing_distance, SUPPORT_SURFACES_OFFSET_PARAMETERS), smoothing_distance) :
3130 // simplify a bit, to ensure the output does not contain outrageous amounts of vertices. Should not be necessary, just a precaution.
3131 base_layer_polygons = polygons_simplify(base_layer_polygons, std::min(scaled<double>(0.03), double(config.resolution)), polygons_strictly_simple);
3132 }
3133
3134 if (! support_roof_polygons.empty() && ! base_layer_polygons.empty()) {
3135// if (area(intersection(base_layer_polygons, support_roof_polygons)) > tiny_area_threshold)
3136 {
3137 switch (interface_pref) {
3138 case InterfacePreference::InterfaceAreaOverwritesSupport:
3139 base_layer_polygons = diff(base_layer_polygons, support_roof_polygons);
3140 break;
3141 case InterfacePreference::SupportAreaOverwritesInterface:
3142 support_roof_polygons = diff(support_roof_polygons, base_layer_polygons);
3143 break;
3144 //FIXME
3145 #if 1
3146 case InterfacePreference::InterfaceLinesOverwriteSupport:
3147 case InterfacePreference::SupportLinesOverwriteInterface:
3148 assert(false);
3149 [[fallthrough]];
3150 #else
3151 case InterfacePreference::InterfaceLinesOverwriteSupport:
3152 {
3153 // Hatch the support roof interfaces, offset them by their line width and subtract them from support base.
3154 Polygons interface_lines = offset(to_polylines(
3155 generate_support_infill_lines(support_roof->polygons, true, layer_idx, config.support_roof_line_distance)),
3156 config.support_roof_line_width / 2);
3157 base_layer_polygons = diff(base_layer_polygons, interface_lines);
3158 break;
3159 }
3160 case InterfacePreference::SupportLinesOverwriteInterface:
3161 {
3162 // Hatch the support roof interfaces, offset them by their line width and subtract them from support base.
3163 Polygons tree_lines = union_(offset(to_polylines(
3164 generate_support_infill_lines(base_layer_polygons, false, layer_idx, config.support_line_distance, true)),
3165 config.support_line_width / 2));
3166 // do not draw roof where the tree is. I prefer it this way as otherwise the roof may cut of a branch from its support below.
3167 support_roof->polygons = diff(support_roof->polygons, tree_lines);
3168 break;
3169 }
3170 #endif
3171 case InterfacePreference::Nothing:
3172 break;
3173 }
3174 }
3175 }
3176
3177 // Subtract support floors from the support area and add them to the support floor instead.
3178 if (config.support_bottom_layers > 0 && ! base_layer_polygons.empty()) {
3179 SupportGeneratorLayer*& support_bottom = bottom_contacts[layer_idx];
3180 Polygons layer_outset = diff_clipped(
3181 config.support_bottom_offset > 0 ? offset(base_layer_polygons, config.support_bottom_offset, jtMiter, 1.2) : base_layer_polygons,
3182 volumes.getCollision(0, layer_idx, false));
3183 Polygons floor_layer;
3184 size_t layers_below = 0;
3185 while (layers_below <= config.support_bottom_layers) {
3186 // one sample at 0 layers below, another at config.support_bottom_layers. In-between samples at config.performance_interface_skip_layers distance from each other.
3187 const size_t sample_layer = static_cast<size_t>(std::max(0, (static_cast<int>(layer_idx) - static_cast<int>(layers_below)) - static_cast<int>(config.z_distance_bottom_layers)));
3188 //FIXME subtract the wipe tower
3189 append(floor_layer, intersection(layer_outset, overhangs[sample_layer]));
3190 if (layers_below < config.support_bottom_layers)
3191 layers_below = std::min(layers_below + 1, config.support_bottom_layers);
3192 else
3193 break;
3194 }
3195 if (! floor_layer.empty()) {
3196 if (support_bottom == nullptr)
3197 support_bottom = &layer_allocate(layer_storage, SupporLayerType::BottomContact, print_object.slicing_parameters(), config, layer_idx);
3198 support_bottom->polygons = union_(floor_layer, support_bottom->polygons);
3199 base_layer_polygons = diff_clipped(base_layer_polygons, offset(support_bottom->polygons, scaled<float>(0.01), jtMiter, 1.2)); // Subtract the support floor from the normal support.
3200 }
3201 }
3202
3203 if (! support_roof_polygons.empty()) {
3204 if (support_roof == nullptr)
3205 support_roof = top_contacts[layer_idx] = &layer_allocate(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), config, layer_idx);
3206 support_roof->polygons = union_(support_roof_polygons);
3207 }
3208 if (! base_layer_polygons.empty()) {
3209 SupportGeneratorLayer *base_layer = intermediate_layers[layer_idx] = &layer_allocate(layer_storage, SupporLayerType::Base, print_object.slicing_parameters(), config, layer_idx);
3210 base_layer->polygons = union_(base_layer_polygons);
3211 }
3212
3213#ifdef SLIC3R_TREESUPPORTS_PROGRESS
3214 {
3215 std::lock_guard<std::mutex> critical_section_progress(critical_sections);
3216 progress_total += TREE_PROGRESS_FINALIZE_BRANCH_AREAS / support_layer_storage.size();
3217 Progress::messageProgress(Progress::Stage::SUPPORT, progress_total * m_progress_multiplier + m_progress_offset, TREE_PROGRESS_TOTAL);
3218 }
3219#endif
3220#if 0
3221 {
3222 std::lock_guard<std::mutex> lock(critical_sections);
3223 if (!storage.support.supportLayers[layer_idx].support_infill_parts.empty() || !storage.support.supportLayers[layer_idx].support_roof.empty())
3224 storage.support.layer_nr_max_filled_layer = std::max(storage.support.layer_nr_max_filled_layer, static_cast<int>(layer_idx));
3225 }
3226#endif
3227 throw_on_cancel();
3228 }
3229 });
3230}
InterfacePreference
Definition TreeSupportCommon.hpp:29
InterfacePreference interface_preference
Definition TreeSupportCommon.hpp:345

References finalize_interface_and_support_areas(), Slic3r::FFFTreeSupport::TreeSupportSettings::interface_preference, and Slic3r::range().

Referenced by draw_areas(), and finalize_interface_and_support_areas().

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

◆ finalize_raft_contact()

void Slic3r::FFFTreeSupport::finalize_raft_contact ( const PrintObject print_object,
const int  raft_contact_layer_idx,
SupportGeneratorLayersPtr top_contacts,
std::vector< SupportElements > &  move_bounds 
)
1005{
1006 if (raft_contact_layer_idx >= 0) {
1007 const size_t first_tree_layer = print_object.slicing_parameters().raft_layers() - 1;
1008 // Remove tree tips that start below the raft contact,
1009 // remove interface layers below the raft contact.
1010 for (size_t i = 0; i < first_tree_layer; ++i) {
1011 top_contacts[i] = nullptr;
1012 move_bounds[i].clear();
1013 }
1014 if (raft_contact_layer_idx >= 0 && print_object.config().raft_expansion.value > 0) {
1015 // If any tips at first_tree_layer now are completely inside the expanded raft layer, remove them as well before they are propagated to the ground.
1016 Polygons &raft_polygons = top_contacts[raft_contact_layer_idx]->polygons;
1017 EdgeGrid::Grid grid(get_extents(raft_polygons).inflated(SCALED_EPSILON));
1018 grid.create(raft_polygons, Polylines{}, coord_t(scale_(10.)));
1019 SupportElements &first_layer_move_bounds = move_bounds[first_tree_layer];
1020 double threshold = scaled<double>(print_object.config().raft_expansion.value) * 2.;
1021 first_layer_move_bounds.erase(std::remove_if(first_layer_move_bounds.begin(), first_layer_move_bounds.end(),
1022 [&grid, threshold](const SupportElement &el) {
1023 coordf_t dist;
1024 if (grid.signed_distance_edges(el.state.result_on_layer, threshold, dist)) {
1025 assert(std::abs(dist) < threshold + SCALED_EPSILON);
1026 // Support point is inside the expanded raft, remove it.
1027 return dist < - 0.;
1028 }
1029 return false;
1030 }), first_layer_move_bounds.end());
1031 #if 0
1032 // Remove the remaining tips from the raft: Closing operation on tip circles.
1033 if (! first_layer_move_bounds.empty()) {
1034 const double eps = 0.1;
1035 // All tips supporting this layer are expected to have the same radius.
1036 double radius = support_element_radius(config, first_layer_move_bounds.front());
1037 // Connect the tips with the following closing radius.
1038 double closing_distance = radius;
1039 Polygon circle = make_circle(radius + closing_distance, eps);
1040 Polygons circles;
1041 circles.reserve(first_layer_move_bounds.size());
1042 for (const SupportElement &el : first_layer_move_bounds) {
1043 circles.emplace_back(circle);
1044 circles.back().translate(el.state.result_on_layer);
1045 }
1046 raft_polygons = diff(raft_polygons, offset(union_(circles), - closing_distance));
1047 }
1048 #endif
1049 }
1050 }
1051}
Definition EdgeGrid.hpp:92
const PrintObjectConfig & config() const
Definition Print.hpp:246
const SlicingParameters & slicing_parameters() const
Definition Print.hpp:309
#define SCALED_EPSILON
Definition libslic3r.h:71
#define scale_(val)
Definition libslic3r.h:69
Slic3r::Polygons union_(const Slic3r::Polygons &subject)
Definition ClipperUtils.cpp:704
Polygon make_circle(double radius, double error)
Definition Polygon.cpp:659
BoundingBox get_extents(const ExPolygon &expolygon)
Definition ExPolygon.cpp:352
Slic3r::Polygons diff(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:672
void offset(Slic3r::ExPolygon &sh, coord_t distance, const PolygonTag &)
Definition geometries.hpp:132
Slic3r::Polygon Polygon
Definition Emboss.cpp:34
size_t raft_layers() const
Definition Slicing.hpp:39

References Slic3r::PrintObject::config(), Slic3r::diff(), Slic3r::get_extents(), Slic3r::grid(), Slic3r::make_circle(), Slic3r::offset(), Slic3r::SlicingParameters::raft_layers(), scale_, SCALED_EPSILON, Slic3r::PrintObject::slicing_parameters(), support_element_radius(), and Slic3r::union_().

+ Here is the call graph for this function:

◆ generate_branch_areas()

static void Slic3r::FFFTreeSupport::generate_branch_areas ( const TreeModelVolumes volumes,
const TreeSupportSettings config,
const std::vector< SupportElements > &  move_bounds,
std::vector< DrawArea > &  linear_data,
std::function< void()>  throw_on_cancel 
)
static

Draws circles around result_on_layer points of the influence areas.

Parameters
linear_data[in]All currently existing influence areas with the layer they are on
layer_tree_polygons[out]Resulting branch areas with the layerindex they appear on. layer_tree_polygons.size() has to be at least linear_data.size() as each Influence area in linear_data will save have at least one (that's why it's a vector<vector>) corresponding branch area in layer_tree_polygons.
inverse_tree_order[in]A mapping that returns the child of every influence area.
2788{
2789#ifdef SLIC3R_TREESUPPORTS_PROGRESS
2790 double progress_total = TREE_PROGRESS_PRECALC_AVO + TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_GENERATE_NODES + TREE_PROGRESS_AREA_CALC;
2791 constexpr int progress_report_steps = 10;
2792 const size_t progress_inserts_check_interval = linear_data.size() / progress_report_steps;
2793 std::mutex critical_sections;
2794#endif // SLIC3R_TREESUPPORTS_PROGRESS
2795
2796 // Pre-generate a circle with correct diameter so that we don't have to recompute those (co)sines every time.
2797 const Polygon branch_circle = make_circle(config.branch_radius, SUPPORT_TREE_CIRCLE_RESOLUTION);
2798
2799 tbb::parallel_for(tbb::blocked_range<size_t>(0, linear_data.size()),
2800 [&volumes, &config, &move_bounds, &linear_data, &branch_circle, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
2801 for (size_t idx = range.begin(); idx < range.end(); ++ idx) {
2802 DrawArea &draw_area = linear_data[idx];
2803 const LayerIndex layer_idx = draw_area.element->state.layer_idx;
2804 const coord_t radius = support_element_radius(config, *draw_area.element);
2805 bool parent_uses_min = false;
2806
2807 // Calculate multiple ovalized circles, to connect with every parent and child. Also generate regular circle for the current layer. Merge all these into one area.
2808 std::vector<std::pair<Point, coord_t>> movement_directions{ std::pair<Point, coord_t>(Point(0, 0), radius) };
2809 if (! draw_area.element->state.skip_ovalisation) {
2810 if (draw_area.child_element != nullptr) {
2811 const Point movement = draw_area.child_element->state.result_on_layer - draw_area.element->state.result_on_layer;
2812 movement_directions.emplace_back(movement, radius);
2813 }
2814 const SupportElements *layer_above = layer_idx + 1 < LayerIndex(move_bounds.size()) ? &move_bounds[layer_idx + 1] : nullptr;
2815 for (int32_t parent_idx : draw_area.element->parents) {
2816 const SupportElement &parent = (*layer_above)[parent_idx];
2817 const Point movement = parent.state.result_on_layer - draw_area.element->state.result_on_layer;
2818 //FIXME why max(..., config.support_line_width)?
2819 movement_directions.emplace_back(movement, std::max(support_element_radius(config, parent), config.support_line_width));
2820 parent_uses_min |= parent.state.use_min_xy_dist;
2821 }
2822 }
2823
2824 const Polygons &collision = volumes.getCollision(0, layer_idx, parent_uses_min || draw_area.element->state.use_min_xy_dist);
2825 auto generateArea = [&collision, &draw_area, &branch_circle, branch_radius = config.branch_radius, support_line_width = config.support_line_width, &movement_directions]
2826 (coord_t aoffset, double &max_speed) {
2827 Polygons poly;
2828 max_speed = 0;
2829 for (std::pair<Point, coord_t> movement : movement_directions) {
2830 max_speed = std::max(max_speed, movement.first.cast<double>().norm());
2831
2832 // Visualization: https://jsfiddle.net/0zvcq39L/2/
2833 // Ovalizes the circle to an ellipse, that contains both old center and new target position.
2834 double used_scale = (movement.second + aoffset) / (1.0 * branch_radius);
2835 Point center_position = draw_area.element->state.result_on_layer + movement.first / 2;
2836 const double moveX = movement.first.x() / (used_scale * branch_radius);
2837 const double moveY = movement.first.y() / (used_scale * branch_radius);
2838 const double vsize_inv = 0.5 / (0.01 + std::sqrt(moveX * moveX + moveY * moveY));
2839
2840 double matrix[] = {
2841 used_scale * (1 + moveX * moveX * vsize_inv),
2842 used_scale * (0 + moveX * moveY * vsize_inv),
2843 used_scale * (0 + moveX * moveY * vsize_inv),
2844 used_scale * (1 + moveY * moveY * vsize_inv),
2845 };
2846 Polygon circle;
2847 for (Point vertex : branch_circle)
2848 circle.points.emplace_back(center_position + Point(matrix[0] * vertex.x() + matrix[1] * vertex.y(), matrix[2] * vertex.x() + matrix[3] * vertex.y()));
2849 poly.emplace_back(std::move(circle));
2850 }
2851
2852 // There seem to be some rounding errors, causing a branch to be a tiny bit further away from the model that it has to be.
2853 // This can cause the tip to be slightly further away front the overhang (x/y wise) than optimal. This fixes it, and for every other part, 0.05mm will not be noticed.
2854 poly = diff_clipped(offset(union_(poly), std::min(coord_t(50), support_line_width / 4), jtMiter, 1.2), collision);
2855 return poly;
2856 };
2857
2858 // Ensure branch area will not overlap with model/collision. This can happen because of e.g. ovalization or increase_until_radius.
2859 double max_speed;
2860 Polygons polygons = generateArea(0, max_speed);
2861 const bool fast_relative_movement = max_speed > radius * 0.75;
2862
2863 if (fast_relative_movement || support_element_radius(config, *draw_area.element) - support_element_collision_radius(config, draw_area.element->state) > config.support_line_width) {
2864 // Simulate the path the nozzle will take on the outermost wall.
2865 // If multiple parts exist, the outer line will not go all around the support part potentially causing support material to be printed mid air.
2866 ExPolygons nozzle_path = offset_ex(polygons, - config.support_line_width / 2);
2867 if (nozzle_path.size() > 1) {
2868 // Just try to make the area a tiny bit larger.
2869 polygons = generateArea(config.support_line_width / 2, max_speed);
2870 nozzle_path = offset_ex(polygons, -config.support_line_width / 2);
2871 // If larger area did not fix the problem, all parts off the nozzle path that do not contain the center point are removed, hoping for the best.
2872 if (nozzle_path.size() > 1) {
2873 ExPolygons polygons_with_correct_center;
2874 for (ExPolygon &part : nozzle_path) {
2875 bool drop = false;
2876 if (! part.contains(draw_area.element->state.result_on_layer)) {
2877 // try a fuzzy inside as sometimes the point should be on the border, but is not because of rounding errors...
2878 Point pt = draw_area.element->state.result_on_layer;
2879 move_inside(to_polygons(part), pt, 0);
2880 drop = (draw_area.element->state.result_on_layer - pt).cast<double>().norm() >= scaled<double>(0.025);
2881 }
2882 if (! drop)
2883 polygons_with_correct_center.emplace_back(std::move(part));
2884 }
2885 // Increase the area again, to ensure the nozzle path when calculated later is very similar to the one assumed above.
2886 assert(contains(polygons, draw_area.element->state.result_on_layer));
2887 polygons = diff_clipped(offset(polygons_with_correct_center, config.support_line_width / 2, jtMiter, 1.2),
2888 //FIXME Vojtech: Clipping may split the region into multiple pieces again, reversing the fixing effort.
2889 collision);
2890 }
2891 }
2892 }
2893
2894 draw_area.polygons = std::move(polygons);
2895
2896#ifdef SLIC3R_TREESUPPORTS_PROGRESS
2897 if (idx % progress_inserts_check_interval == 0) {
2898 std::lock_guard<std::mutex> critical_section_progress(critical_sections);
2899 progress_total += TREE_PROGRESS_GENERATE_BRANCH_AREAS / progress_report_steps;
2900 Progress::messageProgress(Progress::Stage::SUPPORT, progress_total * m_progress_multiplier + m_progress_offset, TREE_PROGRESS_TOTAL);
2901 }
2902#endif
2903 throw_on_cancel();
2904 }
2905 });
2906}
Definition Polygon.hpp:24
coord_t branch_radius
Radius of a branch when it has left the tip.
Definition TreeSupportCommon.hpp:228

References Slic3r::FFFTreeSupport::TreeSupportSettings::branch_radius, generate_branch_areas(), Slic3r::make_circle(), and Slic3r::range().

Referenced by draw_areas(), and generate_branch_areas().

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

◆ generate_initial_areas()

static void Slic3r::FFFTreeSupport::generate_initial_areas ( const PrintObject print_object,
const TreeModelVolumes volumes,
const TreeSupportSettings config,
const std::vector< Polygons > &  overhangs,
std::vector< SupportElements > &  move_bounds,
InterfacePlacer interface_placer,
std::function< void()>  throw_on_cancel 
)
static

Creates the initial influence areas (that can later be propagated down) by placing them below the overhang.

Generates Points where the Model should be supported and creates the areas where these points have to be placed.

Parameters
mesh[in]The mesh that is currently processed.
move_bounds[out]Storage for the influence areas.
storage[in]Background storage, required for adding roofs.
1217{
1218 using AvoidanceType = TreeModelVolumes::AvoidanceType;
1219 TreeSupportMeshGroupSettings mesh_group_settings(print_object);
1220
1221 // To ensure z_distance_top_layers are left empty between the overhang (zeroth empty layer), the support has to be added z_distance_top_layers+1 layers below
1222 const size_t z_distance_delta = config.z_distance_top_layers + 1;
1223
1224 const bool min_xy_dist = config.xy_distance > config.xy_min_distance;
1225
1226#if 0
1227 if (mesh.overhang_areas.size() <= z_distance_delta)
1228 return;
1229#endif
1230
1231 const coord_t connect_length = (config.support_line_width * 100. / mesh_group_settings.support_tree_top_rate) + std::max(2. * config.min_radius - 1.0 * config.support_line_width, 0.0);
1232 // As r*r=x*x+y*y (circle equation): If a circle with center at (0,0) the top most point is at (0,r) as in y=r.
1233 // This calculates how far one has to move on the x-axis so that y=r-support_line_width/2.
1234 // In other words how far does one need to move on the x-axis to be support_line_width/2 away from the circle line.
1235 // As a circle is round this length is identical for every axis as long as the 90 degrees angle between both remains.
1236 const coord_t circle_length_to_half_linewidth_change = config.min_radius < config.support_line_width ?
1237 config.min_radius / 2 :
1238 scale_(sqrt(sqr(unscale<double>(config.min_radius)) - sqr(unscale<double>(config.min_radius - config.support_line_width / 2))));
1239 // Extra support offset to compensate for larger tip radiis. Also outset a bit more when z overwrites xy, because supporting something with a part of a support line is better than not supporting it at all.
1240 //FIXME Vojtech: This is not sufficient for support enforcers to work.
1241 //FIXME There is no account for the support overhang angle.
1242 //FIXME There is no account for the width of the collision regions.
1243 const coord_t extra_outset = std::max(coord_t(0), config.min_radius - config.support_line_width / 2) + (min_xy_dist ? config.support_line_width / 2 : 0)
1244 //FIXME this is a heuristic value for support enforcers to work.
1245// + 10 * config.support_line_width;
1246 ;
1247 const size_t num_support_roof_layers = mesh_group_settings.support_roof_layers;
1248 const bool roof_enabled = num_support_roof_layers > 0;
1249 const bool force_tip_to_roof = roof_enabled && (interface_placer.support_parameters.soluble_interface || sqr<double>(config.min_radius) * M_PI > mesh_group_settings.minimum_roof_area);
1250 // cap for how much layer below the overhang a new support point may be added, as other than with regular support every new inserted point
1251 // may cause extra material and time cost. Could also be an user setting or differently calculated. Idea is that if an overhang
1252 // does not turn valid in double the amount of layers a slope of support angle would take to travel xy_distance, nothing reasonable will come from it.
1253 // The 2*z_distance_delta is only a catch for when the support angle is very high.
1254 // Used only if not min_xy_dist.
1255 coord_t max_overhang_insert_lag = 0;
1256 if (config.z_distance_top_layers > 0) {
1257 max_overhang_insert_lag = 2 * config.z_distance_top_layers;
1258 if (mesh_group_settings.support_angle > EPSILON && mesh_group_settings.support_angle < 0.5 * M_PI - EPSILON) {
1259 //FIXME mesh_group_settings.support_angle does not apply to enforcers and also it does not apply to automatic support angle (by half the external perimeter width).
1260 //used by max_overhang_insert_lag, only if not min_xy_dist.
1261 const auto max_overhang_speed = coord_t(tan(mesh_group_settings.support_angle) * config.layer_height);
1262 max_overhang_insert_lag = std::max(max_overhang_insert_lag, round_up_divide(config.xy_distance, max_overhang_speed / 2));
1263 }
1264 }
1265
1266 size_t num_support_layers;
1267 int raft_contact_layer_idx;
1268 // Layers with their overhang regions.
1269 std::vector<std::pair<size_t, const Polygons*>> raw_overhangs;
1270
1271 {
1272 const size_t num_raft_layers = config.raft_layers.size();
1273 const size_t first_support_layer = std::max(int(num_raft_layers) - int(z_distance_delta), 1);
1274 num_support_layers = size_t(std::max(0, int(print_object.layer_count()) + int(num_raft_layers) - int(z_distance_delta)));
1275 raft_contact_layer_idx = generate_raft_contact(print_object, config, interface_placer);
1276 // Enumerate layers for which the support tips may be generated from overhangs above.
1277 raw_overhangs.reserve(num_support_layers - first_support_layer);
1278 for (size_t layer_idx = first_support_layer; layer_idx < num_support_layers; ++ layer_idx)
1279 if (const size_t overhang_idx = layer_idx + z_distance_delta; ! overhangs[overhang_idx].empty())
1280 raw_overhangs.push_back({ layer_idx, &overhangs[overhang_idx] });
1281 }
1282
1283 RichInterfacePlacer rich_interface_placer{ interface_placer, volumes, force_tip_to_roof, num_support_layers, move_bounds };
1284
1285 tbb::parallel_for(tbb::blocked_range<size_t>(0, raw_overhangs.size()),
1286 [&volumes, &config, &raw_overhangs, &mesh_group_settings,
1287 min_xy_dist, roof_enabled, num_support_roof_layers, extra_outset, circle_length_to_half_linewidth_change, connect_length,
1288 &rich_interface_placer, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
1289 for (size_t raw_overhang_idx = range.begin(); raw_overhang_idx < range.end(); ++ raw_overhang_idx) {
1290 size_t layer_idx = raw_overhangs[raw_overhang_idx].first;
1291 const Polygons &overhang_raw = *raw_overhangs[raw_overhang_idx].second;
1292
1293 // take the least restrictive avoidance possible
1294 Polygons relevant_forbidden;
1295 {
1296 const Polygons &relevant_forbidden_raw = config.support_rests_on_model ?
1297 volumes.getCollision(config.getRadius(0), layer_idx, min_xy_dist) :
1298 volumes.getAvoidance(config.getRadius(0), layer_idx, AvoidanceType::Fast, false, min_xy_dist);
1299 // prevent rounding errors down the line, points placed directly on the line of the forbidden area may not be added otherwise.
1300 relevant_forbidden = offset(union_ex(relevant_forbidden_raw), scaled<float>(0.005), jtMiter, 1.2);
1301 }
1302
1303 // every overhang has saved if a roof should be generated for it. This can NOT be done in the for loop as an area may NOT have a roof
1304 // even if it is larger than the minimum_roof_area when it is only larger because of the support horizontal expansion and
1305 // it would not have a roof if the overhang is offset by support roof horizontal expansion instead. (At least this is the current behavior of the regular support)
1306 Polygons overhang_regular;
1307 {
1308 // When support_offset = 0 safe_offset_inc will only be the difference between overhang_raw and relevant_forbidden, that has to be calculated anyway.
1309 overhang_regular = safe_offset_inc(overhang_raw, mesh_group_settings.support_offset, relevant_forbidden, config.min_radius * 1.75 + config.xy_min_distance, 0, 1);
1310 //check_self_intersections(overhang_regular, "overhang_regular1");
1311
1312 // offset ensures that areas that could be supported by a part of a support line, are not considered unsupported overhang
1313 Polygons remaining_overhang = intersection(
1314 diff(mesh_group_settings.support_offset == 0 ?
1315 overhang_raw :
1316 offset(union_ex(overhang_raw), mesh_group_settings.support_offset, jtMiter, 1.2),
1317 offset(union_ex(overhang_regular), config.support_line_width * 0.5, jtMiter, 1.2)),
1318 relevant_forbidden);
1319
1320 // Offset the area to compensate for large tip radiis. Offset happens in multiple steps to ensure the tip is as close to the original overhang as possible.
1321 //+config.support_line_width / 80 to avoid calculating very small (useless) offsets because of rounding errors.
1322 //FIXME likely a better approach would be to find correspondences between the full overhang and the trimmed overhang
1323 // and if there is no correspondence, project the missing points to the clipping curve.
1324 for (coord_t extra_total_offset_acc = 0; ! remaining_overhang.empty() && extra_total_offset_acc + config.support_line_width / 8 < extra_outset; ) {
1325 const coord_t offset_current_step = std::min(
1326 extra_total_offset_acc + 2 * config.support_line_width > config.min_radius ?
1327 config.support_line_width / 8 :
1328 circle_length_to_half_linewidth_change,
1329 extra_outset - extra_total_offset_acc);
1330 extra_total_offset_acc += offset_current_step;
1331 const Polygons &raw_collision = volumes.getCollision(0, layer_idx, true);
1332 const coord_t offset_step = config.xy_min_distance + config.support_line_width;
1333 // Reducing the remaining overhang by the areas already supported.
1334 //FIXME 1.5 * extra_total_offset_acc seems to be too much, it may remove some remaining overhang without being supported at all.
1335 remaining_overhang = diff(remaining_overhang, safe_offset_inc(overhang_regular, 1.5 * extra_total_offset_acc, raw_collision, offset_step, 0, 1));
1336 // Extending the overhangs by the inflated remaining overhangs.
1337 overhang_regular = union_(overhang_regular, diff(safe_offset_inc(remaining_overhang, extra_total_offset_acc, raw_collision, offset_step, 0, 1), relevant_forbidden));
1338 //check_self_intersections(overhang_regular, "overhang_regular2");
1339 }
1340#if 0
1341 // If the xy distance overrides the z distance, some support needs to be inserted further down.
1342 //=> Analyze which support points do not fit on this layer and check if they will fit a few layers down (while adding them an infinite amount of layers down would technically be closer the the setting description, it would not produce reasonable results. )
1343 if (! min_xy_dist) {
1344 LineInformations overhang_lines;
1345 {
1346 //Vojtech: Generate support heads at support_tree_branch_distance spacing by producing a zig-zag infill at support_tree_branch_distance spacing,
1347 // which is then resmapled
1348 // support_line_width to form a line here as otherwise most will be unsupported. Technically this violates branch distance,
1349 // mbut not only is this the only reasonable choice, but it ensures consistent behavior as some infill patterns generate
1350 // each line segment as its own polyline part causing a similar line forming behavior. Also it is assumed that
1351 // the area that is valid a layer below is to small for support roof.
1352 Polylines polylines = ensure_maximum_distance_polyline(
1353 generate_support_infill_lines(remaining_overhang, support_params, false, layer_idx, mesh_group_settings.support_tree_branch_distance),
1354 config.min_radius, 1);
1355 if (polylines.size() <= 3)
1356 // add the outer wall to ensure it is correct supported instead
1357 polylines = ensure_maximum_distance_polyline(to_polylines(remaining_overhang), connect_length, 3);
1358 for (const auto &line : polylines) {
1359 LineInformation res_line;
1360 for (Point p : line)
1361 res_line.emplace_back(p, LineStatus::INVALID);
1362 overhang_lines.emplace_back(res_line);
1363 }
1364 validate_range(overhang_lines);
1365 }
1366 for (size_t lag_ctr = 1; lag_ctr <= max_overhang_insert_lag && !overhang_lines.empty() && layer_idx - coord_t(lag_ctr) >= 1; lag_ctr++) {
1367 // get least restricted avoidance for layer_idx-lag_ctr
1368 const Polygons &relevant_forbidden_below = config.support_rests_on_model ?
1369 volumes.getCollision(config.getRadius(0), layer_idx - lag_ctr, min_xy_dist) :
1370 volumes.getAvoidance(config.getRadius(0), layer_idx - lag_ctr, AvoidanceType::Fast, false, min_xy_dist);
1371 // it is not required to offset the forbidden area here as the points wont change: If points here are not inside the forbidden area neither will they be later when placing these points, as these are the same points.
1372 auto evaluatePoint = [&](std::pair<Point, LineStatus> p) { return contains(relevant_forbidden_below, p.first); };
1373
1374 std::pair<LineInformations, LineInformations> split = split_lines(overhang_lines, evaluatePoint); // keep all lines that are invalid
1375 overhang_lines = split.first;
1376 // Set all now valid lines to their correct LineStatus. Easiest way is to just discard Avoidance information for each point and evaluate them again.
1377 LineInformations fresh_valid_points = convert_lines_to_internal(volumes, config, convert_internal_to_lines(split.second), layer_idx - lag_ctr);
1378 validate_range(fresh_valid_points);
1379
1380 rich_interface_placer.add_points_along_lines(fresh_valid_points, (force_tip_to_roof && lag_ctr <= num_support_roof_layers) ? num_support_roof_layers : 0, layer_idx - lag_ctr, false, roof_enabled ? num_support_roof_layers : 0);
1381 }
1382 }
1383#endif
1384 }
1385
1386 throw_on_cancel();
1387
1388 if (roof_enabled) {
1389 // Try to support the overhangs by dense interfaces for num_support_roof_layers, cover the bottom most interface with tree tips.
1390 static constexpr const coord_t support_roof_offset = 0;
1391 Polygons overhang_roofs = safe_offset_inc(overhang_raw, support_roof_offset, relevant_forbidden, config.min_radius * 2 + config.xy_min_distance, 0, 1);
1392 if (mesh_group_settings.minimum_support_area > 0)
1393 remove_small(overhang_roofs, mesh_group_settings.minimum_roof_area);
1394 overhang_regular = diff(overhang_regular, overhang_roofs, ApplySafetyOffset::Yes);
1395 //check_self_intersections(overhang_regular, "overhang_regular3");
1396 for (ExPolygon &roof_part : union_ex(overhang_roofs)) {
1397 sample_overhang_area(to_polygons(std::move(roof_part)), true, layer_idx, num_support_roof_layers, connect_length,
1398 mesh_group_settings, rich_interface_placer);
1399 throw_on_cancel();
1400 }
1401 }
1402 // Either the roof is not enabled, then these are all the overhangs to be supported,
1403 // or roof is enabled and these are the thin overhangs at object slopes (not horizontal overhangs).
1404 if (mesh_group_settings.minimum_support_area > 0)
1405 remove_small(overhang_regular, mesh_group_settings.minimum_support_area);
1406 for (ExPolygon &support_part : union_ex(overhang_regular)) {
1407 sample_overhang_area(to_polygons(std::move(support_part)),
1408 false, layer_idx, num_support_roof_layers, connect_length,
1409 mesh_group_settings, rich_interface_placer);
1410 throw_on_cancel();
1411 }
1412 }
1413 });
1414
1415 finalize_raft_contact(print_object, raft_contact_layer_idx, interface_placer.top_contacts_mutable(), move_bounds);
1416}
EIGEN_DEVICE_FUNC const TanReturnType tan() const
Definition ArrayCwiseUnaryOps.h:234
EIGEN_DEVICE_FUNC const SqrtReturnType sqrt() const
Definition ArrayCwiseUnaryOps.h:152
SupportGeneratorLayersPtr & top_contacts_mutable()
Definition TreeSupportCommon.hpp:534
const SupportParameters & support_parameters
Definition TreeSupportCommon.hpp:532
size_t layer_count() const
Definition Print.hpp:278
void finalize_raft_contact(const PrintObject &print_object, const int raft_contact_layer_idx, SupportGeneratorLayersPtr &top_contacts, std::vector< SupportElements > &move_bounds)
Definition TreeSupport.cpp:1000
INDEX_TYPE round_up_divide(const INDEX_TYPE dividend, const INDEX_TYPE divisor)
Definition Utils.hpp:213
constexpr T sqr(T x)
Definition libslic3r.h:258
bool soluble_interface
Definition SupportParameters.hpp:18
Definition TreeSupportCommon.hpp:37
coord_t min_radius
smallest allowed radius, required to ensure that even at DTT 0 every circle will still be printed
Definition TreeSupportCommon.hpp:232
size_t z_distance_top_layers
Amount of layers distance required the top of the support to the model.
Definition TreeSupportCommon.hpp:297
std::vector< coordf_t > raft_layers
Definition TreeSupportCommon.hpp:358
coord_t support_line_width
Width of a single line of support.
Definition TreeSupportCommon.hpp:220

References EPSILON, generate_initial_areas(), generate_raft_contact(), Slic3r::PrintObject::layer_count(), Slic3r::FFFTreeSupport::TreeSupportSettings::layer_height, M_PI, Slic3r::FFFTreeSupport::TreeSupportSettings::min_radius, Slic3r::FFFTreeSupport::TreeSupportMeshGroupSettings::minimum_roof_area, Slic3r::FFFTreeSupport::TreeSupportSettings::raft_layers, Slic3r::range(), Slic3r::round_up_divide(), scale_, Slic3r::FFFSupport::SupportParameters::soluble_interface, Slic3r::sqr(), sqrt(), Slic3r::FFFTreeSupport::TreeSupportMeshGroupSettings::support_angle, Slic3r::FFFTreeSupport::TreeSupportSettings::support_line_width, Slic3r::FFFTreeSupport::InterfacePlacer::support_parameters, Slic3r::FFFTreeSupport::TreeSupportMeshGroupSettings::support_roof_layers, Slic3r::FFFTreeSupport::TreeSupportMeshGroupSettings::support_tree_top_rate, tan(), Slic3r::FFFTreeSupport::TreeSupportSettings::xy_distance, Slic3r::FFFTreeSupport::TreeSupportSettings::xy_min_distance, and Slic3r::FFFTreeSupport::TreeSupportSettings::z_distance_top_layers.

Referenced by generate_initial_areas(), and generate_support_areas().

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

◆ generate_overhangs()

static const std::vector< Polygons > Slic3r::FFFTreeSupport::generate_overhangs ( const TreeSupportSettings settings,
const PrintObject print_object,
std::function< void()>  throw_on_cancel 
)
static
193{
194 const size_t num_raft_layers = settings.raft_layers.size();
195 const size_t num_object_layers = print_object.layer_count();
196 const size_t num_layers = num_object_layers + num_raft_layers;
197 std::vector<Polygons> out(num_layers, Polygons{});
198
199 const PrintConfig &print_config = print_object.print()->config();
200 const PrintObjectConfig &config = print_object.config();
201 const bool support_auto = config.support_material.value && config.support_material_auto.value;
202 const int support_enforce_layers = config.support_material_enforce_layers.value;
203 std::vector<Polygons> enforcers_layers{ print_object.slice_support_enforcers() };
204 std::vector<Polygons> blockers_layers{ print_object.slice_support_blockers() };
205 print_object.project_and_append_custom_facets(false, EnforcerBlockerType::ENFORCER, enforcers_layers);
206 print_object.project_and_append_custom_facets(false, EnforcerBlockerType::BLOCKER, blockers_layers);
207 const int support_threshold = config.support_material_threshold.value;
208 const bool support_threshold_auto = support_threshold == 0;
209 // +1 makes the threshold inclusive
210 double tan_threshold = support_threshold_auto ? 0. : tan(M_PI * double(support_threshold + 1) / 180.);
211 //FIXME this is a fudge constant!
212 auto enforcer_overhang_offset = scaled<double>(config.support_tree_tip_diameter.value);
213
214 size_t num_overhang_layers = support_auto ? num_object_layers : std::min(num_object_layers, std::max(size_t(support_enforce_layers), enforcers_layers.size()));
215 tbb::parallel_for(tbb::blocked_range<LayerIndex>(1, num_overhang_layers),
216 [&print_object, &config, &print_config, &enforcers_layers, &blockers_layers,
217 support_auto, support_enforce_layers, support_threshold_auto, tan_threshold, enforcer_overhang_offset, num_raft_layers, &throw_on_cancel, &out]
218 (const tbb::blocked_range<LayerIndex> &range) {
219 for (LayerIndex layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
220 const Layer &current_layer = *print_object.get_layer(layer_id);
221 const Layer &lower_layer = *print_object.get_layer(layer_id - 1);
222 // Full overhangs with zero lower_layer_offset and no blockers applied.
223 Polygons raw_overhangs;
224 bool raw_overhangs_calculated = false;
225 // Final overhangs.
226 Polygons overhangs;
227 // For how many layers full overhangs shall be supported.
228 const bool enforced_layer = layer_id < support_enforce_layers;
229 if (support_auto || enforced_layer) {
230 float lower_layer_offset;
231 if (enforced_layer)
232 lower_layer_offset = 0;
233 else if (support_threshold_auto) {
234 float external_perimeter_width = 0;
235 for (const LayerRegion *layerm : lower_layer.regions())
236 external_perimeter_width += layerm->flow(frExternalPerimeter).scaled_width();
237 external_perimeter_width /= lower_layer.region_count();
238 lower_layer_offset = float(0.5 * external_perimeter_width);
239 } else
240 lower_layer_offset = scaled<float>(lower_layer.height / tan_threshold);
241 overhangs = lower_layer_offset == 0 ?
242 diff(current_layer.lslices, lower_layer.lslices) :
243 diff(current_layer.lslices, offset(lower_layer.lslices, lower_layer_offset));
244 if (lower_layer_offset == 0) {
245 raw_overhangs = overhangs;
246 raw_overhangs_calculated = true;
247 }
248 if (! (enforced_layer || blockers_layers.empty() || blockers_layers[layer_id].empty()))
249 overhangs = diff(overhangs, blockers_layers[layer_id], ApplySafetyOffset::Yes);
250 if (config.dont_support_bridges) {
251 for (const LayerRegion *layerm : current_layer.regions())
252 remove_bridges_from_contacts(print_config, lower_layer, *layerm,
253 float(layerm->flow(frExternalPerimeter).scaled_width()), overhangs);
254 }
255 }
256 //check_self_intersections(overhangs, "generate_overhangs1");
257 if (! enforcers_layers.empty() && ! enforcers_layers[layer_id].empty()) {
258 // Has some support enforcers at this layer, apply them to the overhangs, don't apply the support threshold angle.
259 //enforcers_layers[layer_id] = union_(enforcers_layers[layer_id]);
260 //check_self_intersections(enforcers_layers[layer_id], "generate_overhangs - enforcers");
261 //check_self_intersections(to_polygons(lower_layer.lslices), "generate_overhangs - lowerlayers");
262 if (Polygons enforced_overhangs = intersection(raw_overhangs_calculated ? raw_overhangs : diff(current_layer.lslices, lower_layer.lslices), enforcers_layers[layer_id] /*, ApplySafetyOffset::Yes */);
263 ! enforced_overhangs.empty()) {
264 //FIXME this is a hack to make enforcers work on steep overhangs.
265 //check_self_intersections(enforced_overhangs, "generate_overhangs - enforced overhangs1");
266 //Polygons enforced_overhangs_prev = enforced_overhangs;
267 //check_self_intersections(to_polygons(union_ex(enforced_overhangs)), "generate_overhangs - enforced overhangs11");
268 //check_self_intersections(offset(union_ex(enforced_overhangs),
269 //FIXME enforcer_overhang_offset is a fudge constant!
270 enforced_overhangs = diff(offset(union_ex(enforced_overhangs), enforcer_overhang_offset),
271 lower_layer.lslices);
272#ifdef TREESUPPORT_DEBUG_SVG
273// if (! intersecting_edges(enforced_overhangs).empty())
274 {
275 static int irun = 0;
276 SVG::export_expolygons(debug_out_path("treesupport-self-intersections-%d.svg", ++irun),
277 { { { current_layer.lslices }, { "current_layer.lslices", "yellow", 0.5f } },
278 { { lower_layer.lslices }, { "lower_layer.lslices", "gray", 0.5f } },
279 { { union_ex(enforced_overhangs) }, { "enforced_overhangs", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
280 }
281#endif // TREESUPPORT_DEBUG_SVG
282 //check_self_intersections(enforced_overhangs, "generate_overhangs - enforced overhangs2");
283 overhangs = overhangs.empty() ? std::move(enforced_overhangs) : union_(overhangs, enforced_overhangs);
284 //check_self_intersections(overhangs, "generate_overhangs - enforcers");
285 }
286 }
287 out[layer_id + num_raft_layers] = std::move(overhangs);
288 throw_on_cancel();
289 }
290 });
291
292#if 0
293 if (num_raft_layers > 0) {
294 const Layer &first_layer = *print_object.get_layer(0);
295 // Final overhangs.
296 Polygons overhangs =
297 // Don't apply blockes on raft layer.
298 //(! blockers_layers.empty() && ! blockers_layers[layer_id].empty() ?
299 // diff(first_layer.lslices, blockers_layers[layer_id], ApplySafetyOffset::Yes) :
300 to_polygons(first_layer.lslices);
301#if 0
302 if (! enforcers_layers.empty() && ! enforcers_layers[layer_id].empty()) {
303 if (Polygons enforced_overhangs = intersection(first_layer.lslices, enforcers_layers[layer_id] /*, ApplySafetyOffset::Yes */);
304 ! enforced_overhangs.empty()) {
305 //FIXME this is a hack to make enforcers work on steep overhangs.
306 //FIXME enforcer_overhang_offset is a fudge constant!
307 enforced_overhangs = offset(union_ex(enforced_overhangs), enforcer_overhang_offset);
308 overhangs = overhangs.empty() ? std::move(enforced_overhangs) : union_(overhangs, enforced_overhangs);
309 }
310 }
311#endif
312 out[num_raft_layers] = std::move(overhangs);
313 throw_on_cancel();
314 }
315#endif
316
317 return out;
318}
const PrintConfig & config() const
Definition Print.hpp:597
PrintType * print()
Definition PrintBase.hpp:735
void project_and_append_custom_facets(bool seam, EnforcerBlockerType type, std::vector< Polygons > &expolys) const
Definition PrintObject.cpp:3066
std::vector< Polygons > slice_support_enforcers() const
Definition Print.hpp:333
std::vector< Polygons > slice_support_blockers() const
Definition Print.hpp:332
const Layer * get_layer(int idx) const
Definition Print.hpp:280
void remove_bridges_from_contacts(const PrintConfig &print_config, const Layer &lower_layer, const LayerRegion &layerm, float fw, Polygons &contact_polygons)
Definition SupportCommon.cpp:43
PrintObjectConfig
Definition PrintConfig.hpp:840
@ frExternalPerimeter
Definition Flow.hpp:17
auto range(Cont &&cont)
Definition libslic3r.h:356
Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:686

References Slic3r::BLOCKER, Slic3r::PrintObject::config(), Slic3r::Print::config(), Slic3r::debug_out_path(), Slic3r::diff(), Slic3r::ENFORCER, Slic3r::SVG::export_expolygons(), Slic3r::frExternalPerimeter, Slic3r::PrintObject::get_layer(), Slic3r::Layer::height, Slic3r::intersection(), Slic3r::PrintObject::layer_count(), Slic3r::Layer::lslices, M_PI, Slic3r::offset(), Slic3r::PrintObjectBaseWithState< PrintType, PrintObjectStepEnumType, COUNT >::print(), Slic3r::PrintObjectConfig, Slic3r::PrintObject::project_and_append_custom_facets(), Slic3r::FFFTreeSupport::TreeSupportSettings::raft_layers, Slic3r::range(), Slic3r::Layer::region_count(), Slic3r::Layer::regions(), Slic3r::FFFSupport::remove_bridges_from_contacts(), Slic3r::PrintObject::slice_support_blockers(), Slic3r::PrintObject::slice_support_enforcers(), tan(), Slic3r::to_polygons(), Slic3r::union_(), Slic3r::union_ex(), and Slic3r::Yes.

Referenced by generate_support_areas().

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

◆ generate_raft_contact()

int Slic3r::FFFTreeSupport::generate_raft_contact ( const PrintObject print_object,
const TreeSupportSettings config,
InterfacePlacer interface_placer 
)
983{
984 int raft_contact_layer_idx = -1;
985 if (print_object.has_raft() && print_object.layer_count() > 0) {
986 // Produce raft contact layer outside of the tree support loop, so that no trees will be generated for the raft contact layer.
987 // Raft layers supporting raft contact interface will be produced by the classic raft generator.
988 // Find the raft contact layer.
989 raft_contact_layer_idx = int(config.raft_layers.size()) - 1;
990 while (raft_contact_layer_idx > 0 && config.raft_layers[raft_contact_layer_idx] > print_object.slicing_parameters().raft_contact_top_z + EPSILON)
991 -- raft_contact_layer_idx;
992 // Create the raft contact layer.
993 const ExPolygons &lslices = print_object.get_layer(0)->lslices;
994 double expansion = print_object.config().raft_expansion.value;
995 interface_placer.add_roof_unguarded(expansion > 0 ? expand(lslices, scaled<float>(expansion)) : to_polygons(lslices), raft_contact_layer_idx, 0);
996 }
997 return raft_contact_layer_idx;
998}
void add_roof_unguarded(Polygons &&new_roofs, const size_t insert_layer_idx, const size_t dtt_roof)
Definition TreeSupportCommon.hpp:561
ExPolygons lslices
Definition Layer.hpp:339
bool has_raft() const
Definition Print.hpp:319
Slic3r::Polygons expand(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType=DefaultJoinType, double miterLimit=DefaultMiterLimit)
Definition ClipperUtils.hpp:363
std::vector< ExPolygon > ExPolygons
Definition ExPolygon.hpp:13
coordf_t raft_contact_top_z
Definition Slicing.hpp:95

References Slic3r::FFFTreeSupport::InterfacePlacer::add_roof_unguarded(), Slic3r::PrintObject::config(), EPSILON, Slic3r::expand(), Slic3r::PrintObject::get_layer(), Slic3r::PrintObject::has_raft(), Slic3r::PrintObject::layer_count(), Slic3r::Layer::lslices, Slic3r::SlicingParameters::raft_contact_top_z, Slic3r::FFFTreeSupport::TreeSupportSettings::raft_layers, Slic3r::PrintObject::slicing_parameters(), and Slic3r::to_polygons().

Referenced by generate_initial_areas(), and generate_support_areas().

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

◆ generate_support_areas()

static void Slic3r::FFFTreeSupport::generate_support_areas ( Print print,
const BuildVolume build_volume,
const std::vector< size_t > &  print_object_ids,
std::function< void()>  throw_on_cancel 
)
static

Create the areas that need support.

These areas are stored inside the given SliceDataStorage object.

Parameters
storageThe data storage where the mesh data is gotten from and where the resulting support areas are stored.
3400{
3403
3404 // Settings with the indexes of meshes that use these settings.
3405 std::vector<std::pair<TreeSupportSettings, std::vector<size_t>>> grouped_meshes = group_meshes(print, print_object_ids);
3406 if (grouped_meshes.empty())
3407 return;
3408
3409 size_t counter = 0;
3410
3411 // Process every mesh group. These groups can not be processed parallel, as the processing in each group is parallelized, and nested parallelization is disables and slow.
3412 for (std::pair<TreeSupportSettings, std::vector<size_t>> &processing : grouped_meshes)
3413 {
3414 // process each combination of meshes
3415 // this struct is used to easy retrieve setting. No other function except those in TreeModelVolumes and generate_initial_areas() have knowledge of the existence of multiple meshes being processed.
3416 //FIXME this is a copy
3417 // Contains config settings to avoid loading them in every function. This was done to improve readability of the code.
3418 const TreeSupportSettings &config = processing.first;
3419 BOOST_LOG_TRIVIAL(info) << "Processing support tree mesh group " << counter + 1 << " of " << grouped_meshes.size() << " containing " << grouped_meshes[counter].second.size() << " meshes.";
3420 auto t_start = std::chrono::high_resolution_clock::now();
3421#if 0
3422 std::vector<Polygons> exclude(num_support_layers);
3423 // get all already existing support areas and exclude them
3424 tbb::parallel_for(tbb::blocked_range<size_t>(0, num_support_layers),
3425 [&](const tbb::blocked_range<size_t> &range) {
3426 for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
3427 Polygons exlude_at_layer;
3428 append(exlude_at_layer, storage.support.supportLayers[layer_idx].support_bottom);
3429 append(exlude_at_layer, storage.support.supportLayers[layer_idx].support_roof);
3430 for (auto part : storage.support.supportLayers[layer_idx].support_infill_parts)
3431 append(exlude_at_layer, part.outline);
3432 exclude[layer_idx] = union_(exlude_at_layer);
3433 }
3434 });
3435#endif
3436#ifdef SLIC3R_TREESUPPORTS_PROGRESS
3437 m_progress_multiplier = 1.0 / double(grouped_meshes.size());
3438 m_progress_offset = counter == 0 ? 0 : TREE_PROGRESS_TOTAL * (double(counter) * m_progress_multiplier);
3439#endif // SLIC3R_TREESUPPORT_PROGRESS
3440 PrintObject &print_object = *print.get_object(processing.second.front());
3441 // Generator for model collision, avoidance and internal guide volumes.
3442 TreeModelVolumes volumes{ print_object, build_volume, config.maximum_move_distance, config.maximum_move_distance_slow, processing.second.front(),
3443#ifdef SLIC3R_TREESUPPORTS_PROGRESS
3444 m_progress_multiplier, m_progress_offset,
3445#endif // SLIC3R_TREESUPPORTS_PROGRESS
3446 /* additional_excluded_areas */{} };
3447
3448 //FIXME generating overhangs just for the furst mesh of the group.
3449 assert(processing.second.size() == 1);
3450 std::vector<Polygons> overhangs = generate_overhangs(config, *print.get_object(processing.second.front()), throw_on_cancel);
3451
3452 // ### Precalculate avoidances, collision etc.
3453 size_t num_support_layers = precalculate(print, overhangs, processing.first, processing.second, volumes, throw_on_cancel);
3454 bool has_support = num_support_layers > 0;
3455 bool has_raft = config.raft_layers.size() > 0;
3456 num_support_layers = std::max(num_support_layers, config.raft_layers.size());
3457
3458 SupportParameters support_params(print_object);
3459 support_params.with_sheath = true;
3460 support_params.support_density = 0;
3461
3462 SupportGeneratorLayerStorage layer_storage;
3463 SupportGeneratorLayersPtr top_contacts;
3464 SupportGeneratorLayersPtr bottom_contacts;
3465 SupportGeneratorLayersPtr interface_layers;
3466 SupportGeneratorLayersPtr base_interface_layers;
3467 SupportGeneratorLayersPtr intermediate_layers(num_support_layers, nullptr);
3468 if (support_params.has_top_contacts || has_raft)
3469 top_contacts.assign(num_support_layers, nullptr);
3470 if (support_params.has_bottom_contacts)
3471 bottom_contacts.assign(num_support_layers, nullptr);
3472 if (support_params.has_interfaces() || has_raft)
3473 interface_layers.assign(num_support_layers, nullptr);
3474 if (support_params.has_base_interfaces() || has_raft)
3475 base_interface_layers.assign(num_support_layers, nullptr);
3476
3477 auto remove_undefined_layers = [&bottom_contacts, &top_contacts, &interface_layers, &base_interface_layers, &intermediate_layers]() {
3478 auto doit = [](SupportGeneratorLayersPtr& layers) {
3479 layers.erase(std::remove_if(layers.begin(), layers.end(), [](const SupportGeneratorLayer* ptr) { return ptr == nullptr; }), layers.end());
3480 };
3481 doit(bottom_contacts);
3482 doit(top_contacts);
3483 doit(interface_layers);
3484 doit(base_interface_layers);
3485 doit(intermediate_layers);
3486 };
3487
3488 InterfacePlacer interface_placer{
3489 print_object.slicing_parameters(), support_params, config,
3490 // Outputs
3491 layer_storage, top_contacts, interface_layers, base_interface_layers };
3492
3493 if (has_support) {
3494 auto t_precalc = std::chrono::high_resolution_clock::now();
3495
3496 // value is the area where support may be placed. As this is calculated in CreateLayerPathing it is saved and reused in draw_areas
3497 std::vector<SupportElements> move_bounds(num_support_layers);
3498
3499 // ### Place tips of the support tree
3500 for (size_t mesh_idx : processing.second)
3501 generate_initial_areas(*print.get_object(mesh_idx), volumes, config, overhangs,
3502 move_bounds, interface_placer, throw_on_cancel);
3503 auto t_gen = std::chrono::high_resolution_clock::now();
3504
3505 #ifdef TREESUPPORT_DEBUG_SVG
3506 for (size_t layer_idx = 0; layer_idx < move_bounds.size(); ++layer_idx) {
3507 Polygons polys;
3508 for (auto& area : move_bounds[layer_idx])
3509 append(polys, area.influence_area);
3510 if (auto begin = move_bounds[layer_idx].begin(); begin != move_bounds[layer_idx].end())
3511 SVG::export_expolygons(debug_out_path("treesupport-initial_areas-%d.svg", layer_idx),
3512 { { { union_ex(volumes.getWallRestriction(support_element_collision_radius(config, begin->state), layer_idx, begin->state.use_min_xy_dist)) },
3513 { "wall_restricrictions", "gray", 0.5f } },
3514 { { union_ex(polys) }, { "parent", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
3515 }
3516 #endif // TREESUPPORT_DEBUG_SVG
3517
3518 // ### Propagate the influence areas downwards. This is an inherently serial operation.
3519 create_layer_pathing(volumes, config, move_bounds, throw_on_cancel);
3520 auto t_path = std::chrono::high_resolution_clock::now();
3521
3522 // ### Set a point in each influence area
3523 create_nodes_from_area(volumes, config, move_bounds, throw_on_cancel);
3524 auto t_place = std::chrono::high_resolution_clock::now();
3525
3526 // ### draw these points as circles
3527
3528 if (print_object.config().support_material_style == smsTree)
3529 draw_areas(*print.get_object(processing.second.front()), volumes, config, overhangs, move_bounds,
3530 bottom_contacts, top_contacts, intermediate_layers, layer_storage, throw_on_cancel);
3531 else {
3532 assert(print_object.config().support_material_style == smsOrganic);
3534 *print.get_object(processing.second.front()), volumes, config, move_bounds,
3535 bottom_contacts, top_contacts, interface_placer, intermediate_layers, layer_storage,
3536 throw_on_cancel);
3537 }
3538
3539 remove_undefined_layers();
3540
3541 std::tie(interface_layers, base_interface_layers) = generate_interface_layers(print_object.config(), support_params,
3542 bottom_contacts, top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage);
3543
3544 auto t_draw = std::chrono::high_resolution_clock::now();
3545 auto dur_pre_gen = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_precalc - t_start).count();
3546 auto dur_gen = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_gen - t_precalc).count();
3547 auto dur_path = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_path - t_gen).count();
3548 auto dur_place = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_place - t_path).count();
3549 auto dur_draw = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_draw - t_place).count();
3550 auto dur_total = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_draw - t_start).count();
3551 BOOST_LOG_TRIVIAL(info) <<
3552 "Total time used creating Tree support for the currently grouped meshes: " << dur_total << " ms. "
3553 "Different subtasks:\nCalculating Avoidance: " << dur_pre_gen << " ms "
3554 "Creating inital influence areas: " << dur_gen << " ms "
3555 "Influence area creation: " << dur_path << "ms "
3556 "Placement of Points in InfluenceAreas: " << dur_place << "ms "
3557 "Drawing result as support " << dur_draw << " ms";
3558 // if (config.branch_radius==2121)
3559 // BOOST_LOG_TRIVIAL(error) << "Why ask questions when you already know the answer twice.\n (This is not a real bug, please dont report it.)";
3560
3561 move_bounds.clear();
3562 } else if (generate_raft_contact(print_object, config, interface_placer) >= 0) {
3563 remove_undefined_layers();
3564 } else
3565 // No raft.
3566 continue;
3567
3568 // Produce the support G-code.
3569 // Used by both classic and tree supports.
3570 SupportGeneratorLayersPtr raft_layers = generate_raft_base(print_object, support_params, print_object.slicing_parameters(),
3571 top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage);
3572#if 1 //#ifdef SLIC3R_DEBUG
3573 SupportGeneratorLayersPtr layers_sorted =
3574#endif // SLIC3R_DEBUG
3575 generate_support_layers(print_object, raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers);
3576 // Don't fill in the tree supports, make them hollow with just a single sheath line.
3577 generate_support_toolpaths(print_object.support_layers(), print_object.config(), support_params, print_object.slicing_parameters(),
3578 raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers);
3579
3580 #if 0
3581//#ifdef SLIC3R_DEBUG
3582 {
3583 static int iRun = 0;
3584 ++ iRun;
3585 size_t layer_id = 0;
3586 for (int i = 0; i < int(layers_sorted.size());) {
3587 // Find the last layer with roughly the same print_z, find the minimum layer height of all.
3588 // Due to the floating point inaccuracies, the print_z may not be the same even if in theory they should.
3589 int j = i + 1;
3590 coordf_t zmax = layers_sorted[i]->print_z + EPSILON;
3591 bool empty = layers_sorted[i]->polygons.empty();
3592 for (; j < layers_sorted.size() && layers_sorted[j]->print_z <= zmax; ++j)
3593 if (!layers_sorted[j]->polygons.empty())
3594 empty = false;
3595 if (!empty) {
3597 debug_out_path("support-%d-%lf.svg", iRun, layers_sorted[i]->print_z).c_str(),
3598 layers_sorted.data() + i, j - i);
3600 debug_out_path("support-w-fills-%d-%lf.svg", iRun, layers_sorted[i]->print_z).c_str(),
3601 layers_sorted.data() + i, j - i,
3602 *print_object.support_layers()[layer_id]);
3603 ++layer_id;
3604 }
3605 i = j;
3606 }
3607 }
3608#endif /* SLIC3R_DEBUG */
3609
3610 ++ counter;
3611 }
3612
3613// storage.support.generated = true;
3614}
Definition SupportLayer.hpp:41
PrintObject * get_object(size_t idx)
Definition Print.hpp:601
double coordf_t
Definition libslic3r.h:45
void generate_support_toolpaths(SupportLayerPtrs &support_layers, const PrintObjectConfig &config, const SupportParameters &support_params, const SlicingParameters &slicing_params, const SupportGeneratorLayersPtr &raft_layers, const SupportGeneratorLayersPtr &bottom_contacts, const SupportGeneratorLayersPtr &top_contacts, const SupportGeneratorLayersPtr &intermediate_layers, const SupportGeneratorLayersPtr &interface_layers, const SupportGeneratorLayersPtr &base_interface_layers)
Definition SupportCommon.cpp:1458
std::vector< SupportGeneratorLayer * > SupportGeneratorLayersPtr
Definition SupportLayer.hpp:142
SupportGeneratorLayersPtr generate_support_layers(PrintObject &object, const SupportGeneratorLayersPtr &raft_layers, const SupportGeneratorLayersPtr &bottom_contacts, const SupportGeneratorLayersPtr &top_contacts, const SupportGeneratorLayersPtr &intermediate_layers, const SupportGeneratorLayersPtr &interface_layers, const SupportGeneratorLayersPtr &base_interface_layers)
Definition SupportCommon.cpp:1381
SupportGeneratorLayersPtr generate_raft_base(const PrintObject &object, const SupportParameters &support_params, const SlicingParameters &slicing_params, const SupportGeneratorLayersPtr &top_contacts, const SupportGeneratorLayersPtr &interface_layers, const SupportGeneratorLayersPtr &base_interface_layers, const SupportGeneratorLayersPtr &base_layers, SupportGeneratorLayerStorage &layer_storage)
Definition SupportCommon.cpp:312
void export_print_z_polygons_and_extrusions_to_svg(const char *path, SupportGeneratorLayer **const layers, int n_layers, SupportLayer &support_layer)
Definition SupportDebug.cpp:77
void export_print_z_polygons_to_svg(const char *path, SupportGeneratorLayer **const layers, int n_layers)
Definition SupportDebug.cpp:59
std::pair< SupportGeneratorLayersPtr, SupportGeneratorLayersPtr > generate_interface_layers(const PrintObjectConfig &config, const SupportParameters &support_params, const SupportGeneratorLayersPtr &bottom_contacts, const SupportGeneratorLayersPtr &top_contacts, SupportGeneratorLayersPtr &top_interface_layers, SupportGeneratorLayersPtr &top_base_interface_layers, SupportGeneratorLayersPtr &intermediate_layers, SupportGeneratorLayerStorage &layer_storage)
Definition SupportCommon.cpp:124
coord_t support_element_collision_radius(const TreeSupportSettings &settings, const SupportElementState &elem)
Get the collision Radius of this Element. This can be smaller then the actual radius,...
Definition TreeSupport.hpp:246
static void generate_initial_areas(const PrintObject &print_object, const TreeModelVolumes &volumes, const TreeSupportSettings &config, const std::vector< Polygons > &overhangs, std::vector< SupportElements > &move_bounds, InterfacePlacer &interface_placer, std::function< void()> throw_on_cancel)
Creates the initial influence areas (that can later be propagated down) by placing them below the ove...
Definition TreeSupport.cpp:1209
int generate_raft_contact(const PrintObject &print_object, const TreeSupportSettings &config, InterfacePlacer &interface_placer)
Definition TreeSupport.cpp:979
void organic_draw_branches(PrintObject &print_object, TreeModelVolumes &volumes, const TreeSupportSettings &config, std::vector< SupportElements > &move_bounds, SupportGeneratorLayersPtr &bottom_contacts, SupportGeneratorLayersPtr &top_contacts, InterfacePlacer &interface_placer, SupportGeneratorLayersPtr &intermediate_layers, SupportGeneratorLayerStorage &layer_storage, std::function< void()> throw_on_cancel)
Definition OrganicSupport.cpp:1006
static void create_nodes_from_area(const TreeModelVolumes &volumes, const TreeSupportSettings &config, std::vector< SupportElements > &move_bounds, std::function< void()> throw_on_cancel)
Set the result_on_layer point for all influence areas.
Definition TreeSupport.cpp:2647
static LayerIndex precalculate(const Print &print, const std::vector< Polygons > &overhangs, const TreeSupportSettings &config, const std::vector< size_t > &object_ids, TreeModelVolumes &volumes, std::function< void()> throw_on_cancel)
Precalculates all avoidances, that could be required.
Definition TreeSupport.cpp:326
bool g_showed_critical_error
Definition TreeSupportCommon.cpp:170
static void draw_areas(PrintObject &print_object, const TreeModelVolumes &volumes, const TreeSupportSettings &config, const std::vector< Polygons > &overhangs, std::vector< SupportElements > &move_bounds, SupportGeneratorLayersPtr &bottom_contacts, SupportGeneratorLayersPtr &top_contacts, SupportGeneratorLayersPtr &intermediate_layers, SupportGeneratorLayerStorage &layer_storage, std::function< void()> throw_on_cancel)
Draws circles around result_on_layer points of the influence areas and applies some post processing.
Definition TreeSupport.cpp:3238
static const std::vector< Polygons > generate_overhangs(const TreeSupportSettings &settings, const PrintObject &print_object, std::function< void()> throw_on_cancel)
Definition TreeSupport.cpp:192
static void create_layer_pathing(const TreeModelVolumes &volumes, const TreeSupportSettings &config, std::vector< SupportElements > &move_bounds, std::function< void()> throw_on_cancel)
Propagates influence downwards, and merges overlapping ones.
Definition TreeSupport.cpp:2405
static std::vector< std::pair< TreeSupportSettings, std::vector< size_t > > > group_meshes(const Print &print, const std::vector< size_t > &print_object_ids)
Definition TreeSupport.cpp:118
bool g_showed_performance_warning
Definition TreeSupportCommon.cpp:171
Definition SupportParameters.hpp:14
This struct contains settings used in the tree support. Thanks to this most functions do not need to ...
Definition TreeSupportCommon.hpp:209

References create_layer_pathing(), create_nodes_from_area(), Slic3r::debug_out_path(), draw_areas(), Slic3r::empty(), EPSILON, Slic3r::FFFSupport::export_print_z_polygons_and_extrusions_to_svg(), Slic3r::FFFSupport::export_print_z_polygons_to_svg(), g_showed_critical_error, g_showed_performance_warning, generate_initial_areas(), Slic3r::FFFSupport::generate_interface_layers(), generate_overhangs(), Slic3r::FFFSupport::generate_raft_base(), generate_raft_contact(), generate_support_areas(), Slic3r::FFFSupport::generate_support_layers(), Slic3r::FFFSupport::generate_support_toolpaths(), Slic3r::Print::get_object(), Slic3r::FFFTreeSupport::TreeModelVolumes::getWallRestriction(), group_meshes(), Slic3r::FFFSupport::SupportParameters::has_base_interfaces(), Slic3r::FFFSupport::SupportParameters::has_bottom_contacts, Slic3r::FFFSupport::SupportParameters::has_interfaces(), Slic3r::FFFSupport::SupportParameters::has_top_contacts, Slic3r::FFFTreeSupport::TreeSupportSettings::maximum_move_distance, Slic3r::FFFTreeSupport::TreeSupportSettings::maximum_move_distance_slow, organic_draw_branches(), precalculate(), Slic3r::FFFTreeSupport::TreeSupportSettings::raft_layers, Slic3r::range(), Slic3r::FFFTreeSupport::InterfacePlacer::slicing_parameters, Slic3r::smsOrganic, Slic3r::smsTree, Slic3r::FFFSupport::SupportParameters::support_density, support_element_collision_radius(), Slic3r::union_(), Slic3r::union_ex(), and Slic3r::FFFSupport::SupportParameters::with_sheath.

Referenced by generate_support_areas().

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

◆ generate_support_infill_lines()

static Polylines Slic3r::FFFTreeSupport::generate_support_infill_lines ( const Polygons polygon,
const SupportParameters support_params,
bool  roof,
LayerIndex  layer_idx,
coord_t  support_infill_distance 
)
static

Returns Polylines representing the (infill) lines that will result in slicing the given area.

Parameters
area[in]The area that has to be filled with infill.
roof[in]Whether the roofing or regular support settings should be used.
layer_idx[in]The current layer index.
support_infill_distance[in]The distance that should be between the infill lines.
Returns
A Polygons object that represents the resulting infill lines.
633{
634#if 0
635 Polygons gaps;
636 // as we effectivly use lines to place our supportPoints we may use the Infill class for it, while not made for it it works perfect
637
638 const EFillMethod pattern = roof ? config.roof_pattern : config.support_pattern;
639
640// const bool zig_zaggify_infill = roof ? pattern == EFillMethod::ZIG_ZAG : config.zig_zaggify_support;
641 const bool connect_polygons = false;
642 constexpr coord_t support_roof_overlap = 0;
643 constexpr size_t infill_multiplier = 1;
644 constexpr coord_t outline_offset = 0;
645 const int support_shift = roof ? 0 : support_infill_distance / 2;
646 const size_t wall_line_count = include_walls && !roof ? config.support_wall_count : 0;
647 const Point infill_origin;
648 constexpr Polygons* perimeter_gaps = nullptr;
649 constexpr bool use_endpieces = true;
650 const bool connected_zigzags = roof ? false : config.connect_zigzags;
651 const size_t zag_skip_count = roof ? 0 : config.zag_skip_count;
652 constexpr coord_t pocket_size = 0;
653 std::vector<AngleRadians> angles = roof ? config.support_roof_angles : config.support_infill_angles;
654 std::vector<VariableWidthLines> toolpaths;
655
656 const coord_t z = config.getActualZ(layer_idx);
657 int divisor = static_cast<int>(angles.size());
658 int index = ((layer_idx % divisor) + divisor) % divisor;
659 const AngleRadians fill_angle = angles[index];
660 Infill roof_computation(pattern, true /* zig_zaggify_infill */, connect_polygons, polygon,
661 roof ? config.support_roof_line_width : config.support_line_width, support_infill_distance, support_roof_overlap, infill_multiplier,
662 fill_angle, z, support_shift, config.resolution, wall_line_count, infill_origin,
663 perimeter_gaps, connected_zigzags, use_endpieces, false /* skip_some_zags */, zag_skip_count, pocket_size);
664 Polygons polygons;
665 Polygons lines;
666 roof_computation.generate(toolpaths, polygons, lines, config.settings);
667 append(lines, to_polylines(polygons));
668 return lines;
669#else
670// const bool connected_zigzags = roof ? false : config.connect_zigzags;
671// const int support_shift = roof ? 0 : support_infill_distance / 2;
672
673 const Flow &flow = roof ? support_params.support_material_interface_flow : support_params.support_material_flow;
674 std::unique_ptr<Fill> filler = std::unique_ptr<Fill>(Fill::new_from_type(roof ? support_params.interface_fill_pattern : support_params.base_fill_pattern));
675 FillParams fill_params;
676
677 filler->layer_id = layer_idx;
678 filler->spacing = flow.spacing();
679 filler->angle = roof ?
680 //fixme support_layer.interface_id() instead of layer_idx
681 (support_params.interface_angle + (layer_idx & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.)) :
682 support_params.base_angle;
683
684 fill_params.density = float(roof ? support_params.interface_density : scaled<float>(filler->spacing) / (scaled<float>(filler->spacing) + float(support_infill_distance)));
685 fill_params.dont_adjust = true;
686
687 Polylines out;
688 for (ExPolygon &expoly : union_ex(polygon)) {
689 // The surface type does not matter.
690 assert(area(expoly) > 0.);
691#ifdef TREE_SUPPORT_SHOW_ERRORS_WIN32
692 if (area(expoly) <= 0.)
693 ::MessageBoxA(nullptr, "TreeSupport infill negative area", "Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING);
694#endif // TREE_SUPPORT_SHOW_ERRORS_WIN32
695 assert(intersecting_edges(to_polygons(expoly)).empty());
696 check_self_intersections(expoly, "generate_support_infill_lines");
697 Surface surface(stInternal, std::move(expoly));
698 try {
699 Polylines pl = filler->fill_surface(&surface, fill_params);
700 assert(pl.empty() || get_extents(surface.expolygon).inflated(SCALED_EPSILON).contains(get_extents(pl)));
701#ifdef TREE_SUPPORT_SHOW_ERRORS_WIN32
702 if (! pl.empty() && ! get_extents(surface.expolygon).inflated(SCALED_EPSILON).contains(get_extents(pl)))
703 ::MessageBoxA(nullptr, "TreeSupport infill failure", "Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING);
704#endif // TREE_SUPPORT_SHOW_ERRORS_WIN32
705 append(out, std::move(pl));
706 } catch (InfillFailedException &) {
707 }
708 }
709 validate_range(out);
710 return out;
711#endif
712}
bool contains(const PointType &point) const
Definition BoundingBox.hpp:46
BoundingBox inflated(coordf_t delta) const
Definition BoundingBox.hpp:197
Definition ExPolygon.hpp:16
Definition Flow.hpp:52
float spacing() const
Definition Flow.hpp:66
Definition FillBase.hpp:29
Definition Surface.hpp:32
BoundingBox scaled(const BoundingBoxf &bb)
Definition BoundingBox.hpp:240
Polylines to_polylines(const ExPolygon &src)
Definition ExPolygon.hpp:209
coordf_t interface_density
Definition SupportParameters.hpp:64
Flow support_material_flow
Definition SupportParameters.hpp:45
Flow support_material_interface_flow
Definition SupportParameters.hpp:47
float interface_angle
Definition SupportParameters.hpp:61
InfillPattern interface_fill_pattern
Definition SupportParameters.hpp:73
Definition FillBase.hpp:35
bool dont_adjust
Definition FillBase.hpp:52
float density
Definition FillBase.hpp:41

References Slic3r::append(), Slic3r::area(), Slic3r::FFFSupport::SupportParameters::base_angle, Slic3r::FFFSupport::SupportParameters::base_fill_pattern, check_self_intersections(), Slic3r::BoundingBoxBase< PointType, APointsType >::contains(), Slic3r::FillParams::density, Slic3r::FillParams::dont_adjust, Slic3r::empty(), Slic3r::Surface::expolygon, Slic3r::get_extents(), Slic3r::Infill, Slic3r::BoundingBox::inflated(), Slic3r::FFFSupport::SupportParameters::interface_angle, Slic3r::FFFSupport::SupportParameters::interface_density, Slic3r::FFFSupport::SupportParameters::interface_fill_pattern, Slic3r::intersecting_edges(), M_PI, Slic3r::Fill::new_from_type(), Slic3r::FFFTreeSupport::TreeSupportSettings::resolution, Slic3r::FFFTreeSupport::TreeSupportSettings::roof_pattern, SCALED_EPSILON, Slic3r::FFFTreeSupport::TreeSupportSettings::settings, Slic3r::Flow::spacing(), Slic3r::stInternal, Slic3r::FFFTreeSupport::TreeSupportSettings::support_line_width, Slic3r::FFFSupport::SupportParameters::support_material_flow, Slic3r::FFFSupport::SupportParameters::support_material_interface_flow, Slic3r::FFFTreeSupport::TreeSupportSettings::support_pattern, Slic3r::FFFTreeSupport::TreeSupportSettings::support_roof_angles, Slic3r::FFFTreeSupport::TreeSupportSettings::support_roof_line_width, Slic3r::FFFTreeSupport::TreeSupportSettings::support_wall_count, Slic3r::to_polygons(), Slic3r::to_polylines(), Slic3r::union_ex(), and validate_range().

Referenced by Slic3r::FFFTreeSupport::RichInterfacePlacer::add_points_along_lines(), and sample_overhang_area().

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

◆ getEffectiveDTT()

size_t Slic3r::FFFTreeSupport::getEffectiveDTT ( const TreeSupportSettings settings,
const SupportElementState elem 
)
inline

Get the Distance to top regarding the real radius this part will have. This is different from distance_to_top, which is can be used to calculate the top most layer of the branch.

Parameters
elem[in]The SupportElement one wants to know the effectiveDTT
Returns
The Effective DTT.
225{
228 elem.effective_radius_height;
229}
uint32_t effective_radius_height
The Effective distance to top of this element regarding radius increases and collision calculations.
Definition TreeSupport.hpp:165
uint32_t distance_to_top
The amount of layers this element is below the topmost layer of this branch.
Definition TreeSupport.hpp:170
size_t increase_radius_until_layer
Same as increase_radius_until_radius, but contains the DTT at which the radius will be reached.
Definition TreeSupportCommon.hpp:268

References Slic3r::FFFTreeSupport::SupportElementState::distance_to_top, Slic3r::FFFTreeSupport::SupportElementState::effective_radius_height, and Slic3r::FFFTreeSupport::TreeSupportSettings::increase_radius_until_layer.

Referenced by support_element_radius().

+ Here is the caller graph for this function:

◆ group_meshes()

static std::vector< std::pair< TreeSupportSettings, std::vector< size_t > > > Slic3r::FFFTreeSupport::group_meshes ( const Print print,
const std::vector< size_t > &  print_object_ids 
)
static
119{
120 std::vector<std::pair<TreeSupportSettings, std::vector<size_t>>> grouped_meshes;
121
122 //FIXME this is ugly, it does not belong here.
123 for (size_t object_id : print_object_ids) {
124 const PrintObject &print_object = *print.get_object(object_id);
125 const PrintObjectConfig &object_config = print_object.config();
126 if (object_config.support_material_contact_distance < EPSILON)
127 // || min_feature_size < scaled<coord_t>(0.1) that is the minimum line width
128 TreeSupportSettings::soluble = true;
129 }
130
131 size_t largest_printed_mesh_idx = 0;
132
133 // Group all meshes that can be processed together. NOTE this is different from mesh-groups! Only one setting object is needed per group,
134 // as different settings in the same group may only occur in the tip, which uses the original settings objects from the meshes.
135 for (size_t object_id : print_object_ids) {
136 const PrintObject &print_object = *print.get_object(object_id);
137#ifndef NDEBUG
138 const PrintObjectConfig &object_config = print_object.config();
139#endif // NDEBUG
140 // Support must be enabled and set to Tree style.
141 assert(object_config.support_material || object_config.support_material_enforce_layers > 0);
142 assert(object_config.support_material_style == smsTree || object_config.support_material_style == smsOrganic);
143
144 bool found_existing_group = false;
145 TreeSupportSettings next_settings{ TreeSupportMeshGroupSettings{ print_object }, print_object.slicing_parameters() };
146 //FIXME for now only a single object per group is enabled.
147#if 0
148 for (size_t idx = 0; idx < grouped_meshes.size(); ++ idx)
149 if (next_settings == grouped_meshes[idx].first) {
150 found_existing_group = true;
151 grouped_meshes[idx].second.emplace_back(object_id);
152 // handle some settings that are only used for performance reasons. This ensures that a horrible set setting intended to improve performance can not reduce it drastically.
153 grouped_meshes[idx].first.performance_interface_skip_layers = std::min(grouped_meshes[idx].first.performance_interface_skip_layers, next_settings.performance_interface_skip_layers);
154 }
155#endif
156 if (! found_existing_group)
157 grouped_meshes.emplace_back(next_settings, std::vector<size_t>{ object_id });
158
159 // no need to do this per mesh group as adaptive layers and raft setting are not setable per mesh.
160 if (print.get_object(largest_printed_mesh_idx)->layers().back()->print_z < print_object.layers().back()->print_z)
161 largest_printed_mesh_idx = object_id;
162 }
163
164#if 0
165 {
166 std::vector<coord_t> known_z(storage.meshes[largest_printed_mesh_idx].layers.size());
167 for (size_t z = 0; z < storage.meshes[largest_printed_mesh_idx].layers.size(); z++)
168 known_z[z] = storage.meshes[largest_printed_mesh_idx].layers[z].printZ;
169 for (size_t idx = 0; idx < grouped_meshes.size(); ++ idx)
170 grouped_meshes[idx].first.setActualZ(known_z);
171 }
172#endif
173
174 return grouped_meshes;
175}
Definition Print.hpp:239
auto layers() const
Definition Print.hpp:247

References Slic3r::PrintObject::config(), EPSILON, Slic3r::Print::get_object(), Slic3r::PrintObject::layers(), Slic3r::PrintObjectConfig, Slic3r::PrintObject::slicing_parameters(), Slic3r::smsOrganic, Slic3r::smsTree, and Slic3r::FFFTreeSupport::TreeSupportSettings::soluble.

Referenced by generate_support_areas().

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

◆ increase_areas_one_layer()

static void Slic3r::FFFTreeSupport::increase_areas_one_layer ( const TreeModelVolumes volumes,
const TreeSupportSettings config,
std::vector< SupportElementMerging > &  merging_areas,
const LayerIndex  layer_idx,
SupportElements layer_elements,
const bool  mergelayer,
std::function< void()>  throw_on_cancel 
)
static

Increases influence areas as far as required.

Calculates influence areas of the layer below, based on the influence areas of the current layer. Increases every influence area by maximum_move_distance_slow. If this is not enough, as in it would change the gracious or to_buildplate status, the influence areas are instead increased by maximum_move_distance. Also ensures that increasing the radius of a branch, does not cause it to change its status (like to_buildplate ). If this were the case, the radius is not increased instead.

Warning: The used format inside this is different as the SupportElement does not have a valid area member. Instead this area is saved as value of the dictionary. This was done to avoid not needed heap allocations.

Parameters
to_bp_areas[out]Influence areas that can reach the buildplate
to_model_areas[out]Influence areas that do not have to reach the buildplate. This has overlap with new_layer_data, as areas that can reach the buildplate are also considered valid areas to the model. This redundancy is required if a to_buildplate influence area is allowed to merge with a to model influence area.
influence_areas[out]Area than can reach all further up support points. No assurance is made that the buildplate or the model can be reached in accordance to the user-supplied settings.
bypass_merge_areas[out]Influence areas ready to be added to the layer below that do not need merging.
last_layer[in]Influence areas of the current layer.
layer_idx[in]Number of the current layer.
mergelayer[in]Will the merge method be called on this layer. This information is required as some calculation can be avoided if they are not required for merging.
1747{
1748 using AvoidanceType = TreeModelVolumes::AvoidanceType;
1749
1750 tbb::parallel_for(tbb::blocked_range<size_t>(0, merging_areas.size(), 1),
1751 [&](const tbb::blocked_range<size_t> &range) {
1752 for (size_t merging_area_idx = range.begin(); merging_area_idx < range.end(); ++ merging_area_idx) {
1753 SupportElementMerging &merging_area = merging_areas[merging_area_idx];
1754 assert(merging_area.parents.size() == 1);
1755 SupportElement &parent = layer_elements[merging_area.parents.front()];
1756 SupportElementState elem = SupportElementState::propagate_down(parent.state);
1757 const Polygons &wall_restriction =
1758 // Abstract representation of the model outline. If an influence area would move through it, it could teleport through a wall.
1759 volumes.getWallRestriction(support_element_collision_radius(config, parent.state), layer_idx, parent.state.use_min_xy_dist);
1760
1761#ifdef TREESUPPORT_DEBUG_SVG
1762 SVG::export_expolygons(debug_out_path("treesupport-increase_areas_one_layer-%d-%ld.svg", layer_idx, int(merging_area_idx)),
1763 { { { union_ex(wall_restriction) }, { "wall_restricrictions", "gray", 0.5f } },
1764 { { union_ex(parent.influence_area) }, { "parent", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
1765#endif // TREESUPPORT_DEBUG_SVG
1766
1767 Polygons to_bp_data, to_model_data;
1768 coord_t radius = support_element_collision_radius(config, elem);
1769
1770 // When the radius increases, the outer "support wall" of the branch will have been moved farther away from the center (as this is the definition of radius).
1771 // As it is not specified that the support_tree_angle has to be one of the center of the branch, it is here seen as the smaller angle of the outer wall of the branch, to the outer wall of the same branch one layer above.
1772 // As the branch may have become larger the distance between these 2 walls is smaller than the distance of the center points.
1773 // These extra distance is added to the movement distance possible for this layer.
1774
1775 coord_t extra_speed = 5; // The extra speed is added to both movement distances. Also move 5 microns faster than allowed to avoid rounding errors, this may cause issues at VERY VERY small layer heights.
1776 coord_t extra_slow_speed = 0; // Only added to the slow movement distance.
1777 const coord_t ceiled_parent_radius = volumes.ceilRadius(support_element_collision_radius(config, parent.state), parent.state.use_min_xy_dist);
1778 coord_t projected_radius_increased = config.getRadius(parent.state.effective_radius_height + 1, parent.state.elephant_foot_increases);
1779 coord_t projected_radius_delta = projected_radius_increased - support_element_collision_radius(config, parent.state);
1780
1781 // When z distance is more than one layer up and down the Collision used to calculate the wall restriction will always include the wall (and not just the xy_min_distance) of the layer above and below like this (d = blocked area because of z distance):
1782 /*
1783 * layer z+1:dddddiiiiiioooo
1784 * layer z+0:xxxxxdddddddddd
1785 * layer z-1:dddddxxxxxxxxxx
1786 * For more detailed visualisation see calculateWallRestrictions
1787 */
1788 const coord_t safe_movement_distance =
1789 (elem.use_min_xy_dist ? config.xy_min_distance : config.xy_distance) +
1790 (std::min(config.z_distance_top_layers, config.z_distance_bottom_layers) > 0 ? config.min_feature_size : 0);
1791 if (ceiled_parent_radius == volumes.ceilRadius(projected_radius_increased, parent.state.use_min_xy_dist) ||
1792 projected_radius_increased < config.increase_radius_until_radius)
1793 // If it is guaranteed possible to increase the radius, the maximum movement speed can be increased, as it is assumed that the maximum movement speed is the one of the slower moving wall
1794 extra_speed += projected_radius_delta;
1795 else
1796 // if a guaranteed radius increase is not possible, only increase the slow speed
1797 // Ensure that the slow movement distance can not become larger than the fast one.
1798 extra_slow_speed += std::min(projected_radius_delta, (config.maximum_move_distance + extra_speed) - (config.maximum_move_distance_slow + extra_slow_speed));
1799
1800 if (config.layer_start_bp_radius > layer_idx &&
1801 config.recommendedMinRadius(layer_idx - 1) < config.getRadius(elem.effective_radius_height + 1, elem.elephant_foot_increases)) {
1802 // can guarantee elephant foot radius increase
1803 if (ceiled_parent_radius == volumes.ceilRadius(config.getRadius(parent.state.effective_radius_height + 1, parent.state.elephant_foot_increases + 1), parent.state.use_min_xy_dist))
1804 extra_speed += config.bp_radius_increase_per_layer;
1805 else
1806 extra_slow_speed += std::min(coord_t(config.bp_radius_increase_per_layer),
1807 config.maximum_move_distance - (config.maximum_move_distance_slow + extra_slow_speed));
1808 }
1809
1810 const coord_t fast_speed = config.maximum_move_distance + extra_speed;
1811 const coord_t slow_speed = config.maximum_move_distance_slow + extra_speed + extra_slow_speed;
1812
1813 Polygons offset_slow, offset_fast;
1814
1815 bool add = false;
1816 bool bypass_merge = false;
1817 constexpr bool increase_radius = true, no_error = true, use_min_radius = true, move = true; // aliases for better readability
1818
1819 // Determine in which order configurations are checked if they result in a valid influence area. Check will stop if a valid area is found
1820 std::vector<AreaIncreaseSettings> order;
1821 auto insertSetting = [&](AreaIncreaseSettings settings, bool back) {
1822 if (std::find(order.begin(), order.end(), settings) == order.end()) {
1823 if (back)
1824 order.emplace_back(settings);
1825 else
1826 order.insert(order.begin(), settings);
1827 }
1828 };
1829
1830 const bool parent_moved_slow = elem.last_area_increase.increase_speed < config.maximum_move_distance;
1831 const bool avoidance_speed_mismatch = parent_moved_slow && elem.last_area_increase.type != AvoidanceType::Slow;
1832 if (elem.last_area_increase.move && elem.last_area_increase.no_error && elem.can_use_safe_radius && !mergelayer &&
1833 !avoidance_speed_mismatch && (elem.distance_to_top >= config.tip_layers || parent_moved_slow)) {
1834 // assume that the avoidance type that was best for the parent is best for me. Makes this function about 7% faster.
1835 insertSetting({ elem.last_area_increase.type, elem.last_area_increase.increase_speed < config.maximum_move_distance ? slow_speed : fast_speed,
1836 increase_radius, elem.last_area_increase.no_error, !use_min_radius, elem.last_area_increase.move }, true);
1837 insertSetting({ elem.last_area_increase.type, elem.last_area_increase.increase_speed < config.maximum_move_distance ? slow_speed : fast_speed,
1838 !increase_radius, elem.last_area_increase.no_error, !use_min_radius, elem.last_area_increase.move }, true);
1839 }
1840 // branch may still go though a hole, so a check has to be done whether the hole was already passed, and the regular avoidance can be used.
1841 if (!elem.can_use_safe_radius) {
1842 // if the radius until which it is always increased can not be guaranteed, move fast. This is to avoid holes smaller than the real branch radius.
1843 // This does not guarantee the avoidance of such holes, but ensures they are avoided if possible.
1844 // order.emplace_back(AvoidanceType::Slow,!increase_radius,no_error,!use_min_radius,move);
1845 insertSetting({ AvoidanceType::Slow, slow_speed, increase_radius, no_error, !use_min_radius, !move }, true); // did we go through the hole
1846 // in many cases the definition of hole is overly restrictive, so to avoid unnecessary fast movement in the tip, it is ignored there for a bit.
1847 // This CAN cause a branch to go though a hole it otherwise may have avoided.
1848 if (elem.distance_to_top < round_up_divide(config.tip_layers, size_t(2)))
1849 insertSetting({ AvoidanceType::Fast, slow_speed, increase_radius, no_error, !use_min_radius, !move }, true);
1850 insertSetting({ AvoidanceType::FastSafe, fast_speed, increase_radius, no_error, !use_min_radius, !move }, true); // did we manage to avoid the hole
1851 insertSetting({ AvoidanceType::FastSafe, fast_speed, !increase_radius, no_error, !use_min_radius, move }, true);
1852 insertSetting({ AvoidanceType::Fast, fast_speed, !increase_radius, no_error, !use_min_radius, move }, true);
1853 } else {
1854 insertSetting({ AvoidanceType::Slow, slow_speed, increase_radius, no_error, !use_min_radius, move }, true);
1855 // while moving fast to be able to increase the radius (b) may seems preferable (over a) this can cause the a sudden skip in movement,
1856 // which looks similar to a layer shift and can reduce stability.
1857 // as such idx have chosen to only use the user setting for radius increases as a friendly recommendation.
1858 insertSetting({ AvoidanceType::Slow, slow_speed, !increase_radius, no_error, !use_min_radius, move }, true); // a
1859 if (elem.distance_to_top < config.tip_layers)
1860 insertSetting({ AvoidanceType::FastSafe, slow_speed, increase_radius, no_error, !use_min_radius, move }, true);
1861 insertSetting({ AvoidanceType::FastSafe, fast_speed, increase_radius, no_error, !use_min_radius, move }, true); // b
1862 insertSetting({ AvoidanceType::FastSafe, fast_speed, !increase_radius, no_error, !use_min_radius, move }, true);
1863 }
1864
1865 if (elem.use_min_xy_dist) {
1866 std::vector<AreaIncreaseSettings> new_order;
1867 // if the branch currently has to use min_xy_dist check if the configuration would also be valid
1868 // with the regular xy_distance before checking with use_min_radius (Only happens when Support Distance priority is z overrides xy )
1869 for (AreaIncreaseSettings settings : order) {
1870 new_order.emplace_back(settings);
1871 new_order.push_back({ settings.type, settings.increase_speed, settings.increase_radius, settings.no_error, use_min_radius, settings.move });
1872 }
1873 order = new_order;
1874 }
1875 if (elem.to_buildplate || (elem.to_model_gracious && intersection(parent.influence_area, volumes.getPlaceableAreas(radius, layer_idx, throw_on_cancel)).empty())) {
1876 // error case
1877 // it is normal that we wont be able to find a new area at some point in time if we wont be able to reach layer 0 aka have to connect with the model
1878 insertSetting({ AvoidanceType::Fast, fast_speed, !increase_radius, !no_error, elem.use_min_xy_dist, move }, true);
1879 }
1880 if (elem.distance_to_top < elem.dont_move_until && elem.can_use_safe_radius) // only do not move when holes would be avoided in every case.
1881 // Only do not move when already in a no hole avoidance with the regular xy distance.
1882 insertSetting({ AvoidanceType::Slow, 0, increase_radius, no_error, !use_min_radius, !move }, false);
1883
1884 Polygons inc_wo_collision;
1885 // Check whether it is faster to calculate the area increased with the fast speed independently from the slow area, or time could be saved by reusing the slow area to calculate the fast one.
1886 // Calculated by comparing the steps saved when calcualting idependently with the saved steps when not.
1887 bool offset_independant_faster = radius / safe_movement_distance - int(config.maximum_move_distance + extra_speed < radius + safe_movement_distance) >
1888 round_up_divide((extra_speed + extra_slow_speed + config.maximum_move_distance_slow), safe_movement_distance);
1889 for (const AreaIncreaseSettings &settings : order) {
1890 if (settings.move) {
1891 if (offset_slow.empty() && (settings.increase_speed == slow_speed || ! offset_independant_faster)) {
1892 // offsetting in 2 steps makes our offsetted area rounder preventing (rounding) errors created by to pointy areas. At this point one can see that the Polygons class
1893 // was never made for precision in the single digit micron range.
1894 offset_slow = safe_offset_inc(parent.influence_area, extra_speed + extra_slow_speed + config.maximum_move_distance_slow,
1895 wall_restriction, safe_movement_distance, offset_independant_faster ? safe_movement_distance + radius : 0, 2);
1896#ifdef TREESUPPORT_DEBUG_SVG
1897 SVG::export_expolygons(debug_out_path("treesupport-increase_areas_one_layer-slow-%d-%ld.svg", layer_idx, int(merging_area_idx)),
1898 { { { union_ex(wall_restriction) }, { "wall_restricrictions", "gray", 0.5f } },
1899 { { union_ex(offset_slow) }, { "offset_slow", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
1900#endif // TREESUPPORT_DEBUG_SVG
1901 }
1902 if (offset_fast.empty() && settings.increase_speed != slow_speed) {
1903 if (offset_independant_faster)
1904 offset_fast = safe_offset_inc(parent.influence_area, extra_speed + config.maximum_move_distance,
1905 wall_restriction, safe_movement_distance, offset_independant_faster ? safe_movement_distance + radius : 0, 1);
1906 else {
1907 const coord_t delta_slow_fast = config.maximum_move_distance - (config.maximum_move_distance_slow + extra_slow_speed);
1908 offset_fast = safe_offset_inc(offset_slow, delta_slow_fast, wall_restriction, safe_movement_distance, safe_movement_distance + radius, offset_independant_faster ? 2 : 1);
1909 }
1910#ifdef TREESUPPORT_DEBUG_SVG
1911 SVG::export_expolygons(debug_out_path("treesupport-increase_areas_one_layer-fast-%d-%ld.svg", layer_idx, int(merging_area_idx)),
1912 { { { union_ex(wall_restriction) }, { "wall_restricrictions", "gray", 0.5f } },
1913 { { union_ex(offset_fast) }, { "offset_fast", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
1914#endif // TREESUPPORT_DEBUG_SVG
1915 }
1916 }
1917 std::optional<SupportElementState> result;
1918 inc_wo_collision.clear();
1919 if (!settings.no_error) {
1920 // ERROR CASE
1921 // if the area becomes for whatever reason something that clipper sees as a line, offset would stop working, so ensure that even if if wrongly would be a line, it still actually has an area that can be increased
1922 Polygons lines_offset = offset(to_polylines(parent.influence_area), scaled<float>(0.005), jtMiter, 1.2);
1923 Polygons base_error_area = union_(parent.influence_area, lines_offset);
1924 result = increase_single_area(volumes, config, settings, layer_idx, parent,
1925 base_error_area, to_bp_data, to_model_data, inc_wo_collision, (config.maximum_move_distance + extra_speed) * 1.5, mergelayer);
1926#ifdef TREE_SUPPORT_SHOW_ERRORS
1927 BOOST_LOG_TRIVIAL(error)
1928#else // TREE_SUPPORT_SHOW_ERRORS
1929 BOOST_LOG_TRIVIAL(warning)
1930#endif // TREE_SUPPORT_SHOW_ERRORS
1931 << "Influence area could not be increased! Data about the Influence area: "
1932 "Radius: " << radius << " at layer: " << layer_idx - 1 << " NextTarget: " << elem.layer_idx << " Distance to top: " << elem.distance_to_top <<
1933 " Elephant foot increases " << elem.elephant_foot_increases << " use_min_xy_dist " << elem.use_min_xy_dist << " to buildplate " << elem.to_buildplate <<
1934 " gracious " << elem.to_model_gracious << " safe " << elem.can_use_safe_radius << " until move " << elem.dont_move_until << " \n "
1935 "Parent " << &parent << ": Radius: " << support_element_collision_radius(config, parent.state) << " at layer: " << layer_idx << " NextTarget: " << parent.state.layer_idx <<
1936 " Distance to top: " << parent.state.distance_to_top << " Elephant foot increases " << parent.state.elephant_foot_increases << " use_min_xy_dist " << parent.state.use_min_xy_dist <<
1937 " to buildplate " << parent.state.to_buildplate << " gracious " << parent.state.to_model_gracious << " safe " << parent.state.can_use_safe_radius << " until move " << parent.state.dont_move_until;
1938 tree_supports_show_error("Potentially lost branch!"sv, true);
1939#ifdef TREE_SUPPORTS_TRACK_LOST
1940 if (result)
1941 result->lost = true;
1942#endif // TREE_SUPPORTS_TRACK_LOST
1943 } else
1944 result = increase_single_area(volumes, config, settings, layer_idx, parent,
1945 settings.increase_speed == slow_speed ? offset_slow : offset_fast, to_bp_data, to_model_data, inc_wo_collision, 0, mergelayer);
1946
1947 if (result) {
1948 elem = *result;
1949 radius = support_element_collision_radius(config, elem);
1950 elem.last_area_increase = settings;
1951 add = true;
1952 // do not merge if the branch should not move or the priority has to be to get farther away from the model.
1953 bypass_merge = !settings.move || (settings.use_min_distance && elem.distance_to_top < config.tip_layers);
1954 if (settings.move)
1955 elem.dont_move_until = 0;
1956 else
1957 elem.result_on_layer = parent.state.result_on_layer;
1958
1959 elem.can_use_safe_radius = settings.type != AvoidanceType::Fast;
1960
1961 if (!settings.use_min_distance)
1962 elem.use_min_xy_dist = false;
1963 if (!settings.no_error)
1964#ifdef TREE_SUPPORT_SHOW_ERRORS
1965 BOOST_LOG_TRIVIAL(error)
1966#else // TREE_SUPPORT_SHOW_ERRORS
1967 BOOST_LOG_TRIVIAL(info)
1968#endif // TREE_SUPPORT_SHOW_ERRORS
1969 << "Trying to keep area by moving faster than intended: Success";
1970 break;
1971 } else if (!settings.no_error)
1972 BOOST_LOG_TRIVIAL(error) << "Trying to keep area by moving faster than intended: FAILURE! WRONG BRANCHES LIKLY!";
1973 }
1974
1975 if (add) {
1976 // Union seems useless, but some rounding errors somewhere can cause to_bp_data to be slightly bigger than it should be.
1977 assert(! inc_wo_collision.empty() || ! to_bp_data.empty() || ! to_model_data.empty());
1978 Polygons max_influence_area = safe_union(
1979 diff_clipped(inc_wo_collision, volumes.getCollision(radius, layer_idx - 1, elem.use_min_xy_dist)),
1980 safe_union(to_bp_data, to_model_data));
1981 merging_area.state = elem;
1982 assert(!max_influence_area.empty());
1983 merging_area.set_bbox(get_extents(max_influence_area));
1984 merging_area.areas.influence_areas = std::move(max_influence_area);
1985 if (! bypass_merge) {
1986 if (elem.to_buildplate)
1987 merging_area.areas.to_bp_areas = std::move(to_bp_data);
1988 if (config.support_rests_on_model)
1989 merging_area.areas.to_model_areas = std::move(to_model_data);
1990 }
1991 } else {
1992 // If the bottom most point of a branch is set, later functions will assume that the position is valid, and ignore it.
1993 // But as branches connecting with the model that are to small have to be culled, the bottom most point has to be not set.
1994 // A point can be set on the top most tip layer (maybe more if it should not move for a few layers).
1995 parent.state.result_on_layer_reset();
1996 parent.state.to_model_gracious = false;
1997#ifdef TREE_SUPPORTS_TRACK_LOST
1998 parent.state.verylost = true;
1999#endif // TREE_SUPPORTS_TRACK_LOST
2000 }
2001
2002 throw_on_cancel();
2003 }
2004 }, tbb::simple_partitioner());
2005}

References increase_areas_one_layer(), and Slic3r::range().

Referenced by create_layer_pathing(), and increase_areas_one_layer().

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

◆ increase_single_area()

static std::optional< SupportElementState > Slic3r::FFFTreeSupport::increase_single_area ( const TreeModelVolumes volumes,
const TreeSupportSettings config,
const AreaIncreaseSettings settings,
const LayerIndex  layer_idx,
const SupportElement parent,
const Polygons relevant_offset,
Polygons to_bp_data,
Polygons to_model_data,
Polygons increased,
const coord_t  overspeed,
const bool  mergelayer 
)
static

Checks if an influence area contains a valid subsection and returns the corresponding metadata and the new Influence area.

Calculates an influence areas of the layer below, based on the influence area of one element on the current layer. Increases every influence area by maximum_move_distance_slow. If this is not enough, as in we would change our gracious or to_buildplate status the influence areas are instead increased by maximum_move_distance_slow. Also ensures that increasing the radius of a branch, does not cause it to change its status (like to_buildplate ). If this were the case, the radius is not increased instead.

Warning: The used format inside this is different as the SupportElement does not have a valid area member. Instead this area is saved as value of the dictionary. This was done to avoid not needed heap allocations.

Parameters
settings[in]Which settings have to be used to check validity.
layer_idx[in]Number of the current layer.
parent[in]The metadata of the parents influence area.
relevant_offset[in]The maximal possible influence area. No guarantee regarding validity with current layer collision required, as it is ensured in-function!
to_bp_data[out]The part of the Influence area that can reach the buildplate.
to_model_data[out]The part of the Influence area that do not have to reach the buildplate. This has overlap with new_layer_data.
increased[out]Area than can reach all further up support points. No assurance is made that the buildplate or the model can be reached in accordance to the user-supplied settings.
overspeed[in]How much should the already offset area be offset again. Usually this is 0.
mergelayer[in]Will the merge method be called on this layer. This information is required as some calculation can be avoided if they are not required for merging.
Returns
A valid support element for the next layer regarding the calculated influence areas. Empty if no influence are can be created using the supplied influence area and settings.
1560{
1561 SupportElementState current_elem{ SupportElementState::propagate_down(parent.state) };
1562 Polygons check_layer_data;
1563 if (settings.increase_radius)
1564 current_elem.effective_radius_height += 1;
1565 coord_t radius = support_element_collision_radius(config, current_elem);
1566
1567 if (settings.move) {
1568 increased = relevant_offset;
1569 if (overspeed > 0) {
1570 const coord_t safe_movement_distance =
1571 (current_elem.use_min_xy_dist ? config.xy_min_distance : config.xy_distance) +
1572 (std::min(config.z_distance_top_layers, config.z_distance_bottom_layers) > 0 ? config.min_feature_size : 0);
1573 // The difference to ensure that the result not only conforms to wall_restriction, but collision/avoidance is done later.
1574 // The higher last_safe_step_movement_distance comes exactly from the fact that the collision will be subtracted later.
1575 increased = safe_offset_inc(increased, overspeed, volumes.getWallRestriction(support_element_collision_radius(config, parent.state), layer_idx, parent.state.use_min_xy_dist),
1576 safe_movement_distance, safe_movement_distance + radius, 1);
1577 }
1578 if (settings.no_error && settings.move)
1579 // as ClipperLib::jtRound has to be used for offsets this simplify is VERY important for performance.
1580 polygons_simplify(increased, scaled<float>(0.025), polygons_strictly_simple);
1581 } else
1582 // if no movement is done the areas keep parent area as no move == offset(0)
1583 increased = parent.influence_area;
1584
1585 if (mergelayer || current_elem.to_buildplate) {
1586 to_bp_data = safe_union(diff_clipped(increased, volumes.getAvoidance(radius, layer_idx - 1, settings.type, false, settings.use_min_distance)));
1587 if (! current_elem.to_buildplate && area(to_bp_data) > tiny_area_threshold) {
1588 // mostly happening in the tip, but with merges one should check every time, just to be sure.
1589 current_elem.to_buildplate = true; // sometimes nodes that can reach the buildplate are marked as cant reach, tainting subtrees. This corrects it.
1590 BOOST_LOG_TRIVIAL(debug) << "Corrected taint leading to a wrong to model value on layer " << layer_idx - 1 << " targeting " <<
1591 current_elem.target_height << " with radius " << radius;
1592 }
1593 }
1594 if (config.support_rests_on_model) {
1595 if (mergelayer || current_elem.to_model_gracious)
1596 to_model_data = safe_union(diff_clipped(increased, volumes.getAvoidance(radius, layer_idx - 1, settings.type, true, settings.use_min_distance)));
1597
1598 if (!current_elem.to_model_gracious) {
1599 if (mergelayer && area(to_model_data) >= tiny_area_threshold) {
1600 current_elem.to_model_gracious = true;
1601 BOOST_LOG_TRIVIAL(debug) << "Corrected taint leading to a wrong non gracious value on layer " << layer_idx - 1 << " targeting " <<
1602 current_elem.target_height << " with radius " << radius;
1603 } else
1604 // Cannot route to gracious areas. Push the tree away from object and route it down anyways.
1605 to_model_data = safe_union(diff_clipped(increased, volumes.getCollision(radius, layer_idx - 1, settings.use_min_distance)));
1606 }
1607 }
1608
1609 check_layer_data = current_elem.to_buildplate ? to_bp_data : to_model_data;
1610
1611 if (settings.increase_radius && area(check_layer_data) > tiny_area_threshold) {
1612 auto validWithRadius = [&](coord_t next_radius) {
1613 if (volumes.ceilRadius(next_radius, settings.use_min_distance) <= volumes.ceilRadius(radius, settings.use_min_distance))
1614 return true;
1615
1616 Polygons to_bp_data_2;
1617 if (current_elem.to_buildplate)
1618 // regular union as output will not be used later => this area should always be a subset of the safe_union one (i think)
1619 to_bp_data_2 = diff_clipped(increased, volumes.getAvoidance(next_radius, layer_idx - 1, settings.type, false, settings.use_min_distance));
1620 Polygons to_model_data_2;
1621 if (config.support_rests_on_model && !current_elem.to_buildplate)
1622 to_model_data_2 = diff_clipped(increased,
1623 current_elem.to_model_gracious ?
1624 volumes.getAvoidance(next_radius, layer_idx - 1, settings.type, true, settings.use_min_distance) :
1625 volumes.getCollision(next_radius, layer_idx - 1, settings.use_min_distance));
1626 Polygons check_layer_data_2 = current_elem.to_buildplate ? to_bp_data_2 : to_model_data_2;
1627 return area(check_layer_data_2) > tiny_area_threshold;
1628 };
1629 coord_t ceil_radius_before = volumes.ceilRadius(radius, settings.use_min_distance);
1630
1631 if (support_element_collision_radius(config, current_elem) < config.increase_radius_until_radius && support_element_collision_radius(config, current_elem) < support_element_radius(config, current_elem)) {
1632 coord_t target_radius = std::min(support_element_radius(config, current_elem), config.increase_radius_until_radius);
1633 coord_t current_ceil_radius = volumes.getRadiusNextCeil(radius, settings.use_min_distance);
1634
1635 while (current_ceil_radius < target_radius && validWithRadius(volumes.getRadiusNextCeil(current_ceil_radius + 1, settings.use_min_distance)))
1636 current_ceil_radius = volumes.getRadiusNextCeil(current_ceil_radius + 1, settings.use_min_distance);
1637 size_t resulting_eff_dtt = current_elem.effective_radius_height;
1638 while (resulting_eff_dtt + 1 < current_elem.distance_to_top &&
1639 config.getRadius(resulting_eff_dtt + 1, current_elem.elephant_foot_increases) <= current_ceil_radius &&
1640 config.getRadius(resulting_eff_dtt + 1, current_elem.elephant_foot_increases) <= support_element_radius(config, current_elem))
1641 ++ resulting_eff_dtt;
1642 current_elem.effective_radius_height = resulting_eff_dtt;
1643 }
1644 radius = support_element_collision_radius(config, current_elem);
1645
1646 const coord_t foot_radius_increase = std::max(config.bp_radius_increase_per_layer - config.branch_radius_increase_per_layer, 0.0);
1647 // Is nearly all of the time 1, but sometimes an increase of 1 could cause the radius to become bigger than recommendedMinRadius,
1648 // which could cause the radius to become bigger than precalculated.
1649 double planned_foot_increase = std::min(1.0, double(config.recommendedMinRadius(layer_idx - 1) - support_element_radius(config, current_elem)) / foot_radius_increase);
1650//FIXME
1651 bool increase_bp_foot = planned_foot_increase > 0 && current_elem.to_buildplate;
1652// bool increase_bp_foot = false;
1653
1654 if (increase_bp_foot && support_element_radius(config, current_elem) >= config.branch_radius && support_element_radius(config, current_elem) >= config.increase_radius_until_radius)
1655 if (validWithRadius(config.getRadius(current_elem.effective_radius_height, current_elem.elephant_foot_increases + planned_foot_increase))) {
1656 current_elem.elephant_foot_increases += planned_foot_increase;
1657 radius = support_element_collision_radius(config, current_elem);
1658 }
1659
1660 if (ceil_radius_before != volumes.ceilRadius(radius, settings.use_min_distance)) {
1661 if (current_elem.to_buildplate)
1662 to_bp_data = safe_union(diff_clipped(increased, volumes.getAvoidance(radius, layer_idx - 1, settings.type, false, settings.use_min_distance)));
1663 if (config.support_rests_on_model && (!current_elem.to_buildplate || mergelayer))
1664 to_model_data = safe_union(diff_clipped(increased,
1665 current_elem.to_model_gracious ?
1666 volumes.getAvoidance(radius, layer_idx - 1, settings.type, true, settings.use_min_distance) :
1667 volumes.getCollision(radius, layer_idx - 1, settings.use_min_distance)
1668 ));
1669 check_layer_data = current_elem.to_buildplate ? to_bp_data : to_model_data;
1670 if (area(check_layer_data) < tiny_area_threshold) {
1671 BOOST_LOG_TRIVIAL(error) << "Lost area by doing catch up from " << ceil_radius_before << " to radius " <<
1672 volumes.ceilRadius(support_element_collision_radius(config, current_elem), settings.use_min_distance);
1673 tree_supports_show_error("Area lost catching up radius. May not cause visible malformation."sv, true);
1674 }
1675 }
1676 }
1677
1678 return area(check_layer_data) > tiny_area_threshold ? std::optional<SupportElementState>(current_elem) : std::optional<SupportElementState>();
1679}
coord_t ceilRadius(const coord_t radius, const bool min_xy_dist) const
Round radius upwards to either a multiple of m_radius_sample_resolution or a exponentially increasing...
Definition TreeModelVolumes.hpp:149
coord_t getRadiusNextCeil(coord_t radius, bool min_xy_dist) const
Round radius upwards to the maximum that would still round up to the same value as the provided one.
Definition TreeModelVolumes.hpp:163
const Polygons & getWallRestriction(coord_t radius, LayerIndex layer_idx, bool min_xy_dist) const
Provides the area that represents the walls, as in the printed area, of the model....
Definition TreeModelVolumes.cpp:373
Slic3r::Polygons diff_clipped(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:676
TreeModelVolumes::AvoidanceType type
Definition TreeSupport.hpp:62
bool use_min_distance
Definition TreeSupport.hpp:65
bool no_error
Definition TreeSupport.hpp:64
bool increase_radius
Definition TreeSupport.hpp:63
bool move
Definition TreeSupport.hpp:66
bool use_min_xy_dist
Whether the min_xy_distance can be used to get avoidance or similar. Will only be true if support_xy_...
Definition TreeSupport.hpp:110
Definition TreeSupport.hpp:141
double bp_radius_increase_per_layer
How much one is allowed to increase the tree branch radius close to print bed to reach the required b...
Definition TreeSupportCommon.hpp:289
coord_t increase_radius_until_radius
Increase radius in the resulting drawn branches, even if the avoidance does not allow it....
Definition TreeSupportCommon.hpp:264
coord_t recommendedMinRadius(LayerIndex layer_idx) const
Get the Radius an element should at least have at a given layer.
Definition TreeSupportCommon.hpp:417
coord_t min_feature_size
Definition TreeSupportCommon.hpp:355
size_t z_distance_bottom_layers
Amount of layers distance required from the top of the model to the bottom of a support structure.
Definition TreeSupportCommon.hpp:301
double branch_radius_increase_per_layer
How much a branch radius increases with each layer to guarantee the prescribed tree widening.
Definition TreeSupportCommon.hpp:252

References Slic3r::FFFTreeSupport::TreeSupportSettings::bp_radius_increase_per_layer, Slic3r::FFFTreeSupport::TreeSupportSettings::branch_radius, Slic3r::FFFTreeSupport::TreeSupportSettings::branch_radius_increase_per_layer, Slic3r::FFFTreeSupport::TreeModelVolumes::ceilRadius(), Slic3r::diff_clipped(), Slic3r::FFFTreeSupport::SupportElementState::effective_radius_height, error, Slic3r::FFFTreeSupport::TreeModelVolumes::getAvoidance(), Slic3r::FFFTreeSupport::TreeModelVolumes::getCollision(), Slic3r::FFFTreeSupport::TreeSupportSettings::getRadius(), Slic3r::FFFTreeSupport::TreeModelVolumes::getRadiusNextCeil(), Slic3r::FFFTreeSupport::TreeModelVolumes::getWallRestriction(), Slic3r::FFFTreeSupport::AreaIncreaseSettings::increase_radius, Slic3r::FFFTreeSupport::TreeSupportSettings::increase_radius_until_radius, increase_single_area(), Slic3r::FFFTreeSupport::SupportElement::influence_area, Slic3r::FFFTreeSupport::TreeSupportSettings::min_feature_size, Slic3r::FFFTreeSupport::AreaIncreaseSettings::move, Slic3r::FFFTreeSupport::AreaIncreaseSettings::no_error, Slic3r::polygons_simplify(), Slic3r::FFFTreeSupport::TreeSupportSettings::recommendedMinRadius(), safe_offset_inc(), safe_union(), Slic3r::FFFTreeSupport::SupportElement::state, support_element_collision_radius(), support_element_radius(), Slic3r::FFFTreeSupport::TreeSupportSettings::support_rests_on_model, tree_supports_show_error(), Slic3r::FFFTreeSupport::AreaIncreaseSettings::type, Slic3r::FFFTreeSupport::AreaIncreaseSettings::use_min_distance, Slic3r::FFFTreeSupport::SupportElementStateBits::use_min_xy_dist, Slic3r::FFFTreeSupport::TreeSupportSettings::xy_distance, Slic3r::FFFTreeSupport::TreeSupportSettings::xy_min_distance, Slic3r::FFFTreeSupport::TreeSupportSettings::z_distance_bottom_layers, and Slic3r::FFFTreeSupport::TreeSupportSettings::z_distance_top_layers.

Referenced by increase_single_area().

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

◆ layer_allocate()

SupportGeneratorLayer & Slic3r::FFFTreeSupport::layer_allocate ( SupportGeneratorLayerStorage layer_storage,
SupporLayerType  layer_type,
const SlicingParameters slicing_params,
const TreeSupportSettings config,
size_t  layer_idx 
)
inline
506{
507 SupportGeneratorLayer &layer = layer_storage.allocate(layer_type);
508 return layer_initialize(layer, slicing_params, config, layer_idx);
509}
SupportGeneratorLayer & allocate(SupporLayerType layer_type)
Definition SupportLayer.hpp:126
SupportGeneratorLayer & layer_initialize(SupportGeneratorLayer &layer_new, const SlicingParameters &slicing_params, const TreeSupportSettings &config, const size_t layer_idx)
Definition TreeSupportCommon.hpp:476

References Slic3r::FFFSupport::SupportGeneratorLayerStorage::allocate(), and layer_initialize().

+ Here is the call graph for this function:

◆ layer_allocate_unguarded()

SupportGeneratorLayer & Slic3r::FFFTreeSupport::layer_allocate_unguarded ( SupportGeneratorLayerStorage layer_storage,
SupporLayerType  layer_type,
const SlicingParameters slicing_params,
const TreeSupportSettings config,
size_t  layer_idx 
)
inline
495{
496 SupportGeneratorLayer &layer = layer_storage.allocate_unguarded(layer_type);
497 return layer_initialize(layer, slicing_params, config, layer_idx);
498}
SupportGeneratorLayer & allocate_unguarded(SupporLayerType layer_type)
Definition SupportLayer.hpp:120

References Slic3r::FFFSupport::SupportGeneratorLayerStorage::allocate_unguarded(), and layer_initialize().

Referenced by Slic3r::FFFTreeSupport::InterfacePlacer::add_roof_unguarded().

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

◆ layer_idx_ceil()

LayerIndex Slic3r::FFFTreeSupport::layer_idx_ceil ( const SlicingParameters slicing_params,
const TreeSupportSettings config,
const double  z 
)
inline
463{
464 return
465 LayerIndex(config.raft_layers.size()) +
466 std::max<LayerIndex>(0, ceil((z - slicing_params.object_print_z_min - slicing_params.first_object_layer_height) / slicing_params.layer_height));
467}
coordf_t layer_height
Definition Slicing.hpp:61
coordf_t object_print_z_min
Definition Slicing.hpp:97
coordf_t first_object_layer_height
Definition Slicing.hpp:75

References ceil(), Slic3r::SlicingParameters::first_object_layer_height, Slic3r::SlicingParameters::layer_height, Slic3r::SlicingParameters::object_print_z_min, and Slic3r::FFFTreeSupport::TreeSupportSettings::raft_layers.

+ Here is the call graph for this function:

◆ layer_idx_floor()

LayerIndex Slic3r::FFFTreeSupport::layer_idx_floor ( const SlicingParameters slicing_params,
const TreeSupportSettings config,
const double  z 
)
inline
470{
471 return
472 LayerIndex(config.raft_layers.size()) +
473 std::max<LayerIndex>(0, floor((z - slicing_params.object_print_z_min - slicing_params.first_object_layer_height) / slicing_params.layer_height));
474}
EIGEN_DEVICE_FUNC const FloorReturnType floor() const
Definition ArrayCwiseUnaryOps.h:388

References Slic3r::SlicingParameters::first_object_layer_height, floor(), Slic3r::SlicingParameters::layer_height, Slic3r::SlicingParameters::object_print_z_min, and Slic3r::FFFTreeSupport::TreeSupportSettings::raft_layers.

+ Here is the call graph for this function:

◆ layer_initialize()

SupportGeneratorLayer & Slic3r::FFFTreeSupport::layer_initialize ( SupportGeneratorLayer layer_new,
const SlicingParameters slicing_params,
const TreeSupportSettings config,
const size_t  layer_idx 
)
inline
481{
482 layer_new.print_z = layer_z(slicing_params, config, layer_idx);
483 layer_new.bottom_z = layer_idx > 0 ? layer_z(slicing_params, config, layer_idx - 1) : 0;
484 layer_new.height = layer_new.print_z - layer_new.bottom_z;
485 return layer_new;
486}
coordf_t height
Definition SupportLayer.hpp:96
coordf_t bottom_z
Definition SupportLayer.hpp:94
coordf_t print_z
Definition SupportLayer.hpp:91

References Slic3r::FFFSupport::SupportGeneratorLayer::bottom_z, Slic3r::FFFSupport::SupportGeneratorLayer::height, layer_z(), and Slic3r::FFFSupport::SupportGeneratorLayer::print_z.

Referenced by layer_allocate(), and layer_allocate_unguarded().

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

◆ layer_z()

double Slic3r::FFFTreeSupport::layer_z ( const SlicingParameters slicing_params,
const TreeSupportSettings config,
const size_t  layer_idx 
)
inline
456{
457 return layer_idx >= config.raft_layers.size() ?
458 slicing_params.object_print_z_min + slicing_params.first_object_layer_height + (layer_idx - config.raft_layers.size()) * slicing_params.layer_height :
459 config.raft_layers[layer_idx];
460}

References Slic3r::SlicingParameters::first_object_layer_height, Slic3r::SlicingParameters::layer_height, Slic3r::SlicingParameters::object_print_z_min, and Slic3r::FFFTreeSupport::TreeSupportSettings::raft_layers.

Referenced by extrude_branch(), layer_initialize(), and to_tree_element().

+ Here is the caller graph for this function:

◆ make_forest()

Forest Slic3r::FFFTreeSupport::make_forest ( const TreeSupportSettings config,
const SlicingParameters slicing_params,
std::vector< SupportElements > &&  move_bounds 
)
142{
143 struct TreeVisitor {
144 void visit_recursive(std::vector<SupportElements> &move_bounds, SupportElement &start_element, Branch *parent_branch, Tree &out) const {
145 assert(! start_element.state.marked && ! start_element.parents.empty());
146 // Collect elements up to a bifurcation above.
147 start_element.state.marked = true;
148 // For each branch bifurcating from this point:
149// SupportElements &layer = move_bounds[start_element.state.layer_idx];
150 SupportElements &layer_above = move_bounds[start_element.state.layer_idx + 1];
151 for (size_t parent_idx = 0; parent_idx < start_element.parents.size(); ++ parent_idx) {
152 Branch branch;
153 if (parent_branch)
154 // Duplicate the last element of the trunk below.
155 // If this branch has a smaller diameter than the trunk below, its centerline will not be aligned with the centerline of the trunk.
156 branch.path.emplace_back(parent_branch->path.back());
157 branch.path.emplace_back(to_tree_element(config, slicing_params, start_element, parent_branch == nullptr));
158 // Traverse each branch until it branches again.
159 SupportElement &first_parent = layer_above[start_element.parents[parent_idx]];
160 assert(! first_parent.state.marked);
161 assert(branch.path.back().layer_idx + 1 == first_parent.state.layer_idx);
162 branch.path.emplace_back(to_tree_element(config, slicing_params, first_parent, false));
163 if (first_parent.parents.size() < 2)
164 first_parent.state.marked = true;
165 SupportElement *next_branch = nullptr;
166 if (first_parent.parents.size() == 1) {
167 for (SupportElement *parent = &first_parent;;) {
168 assert(parent->state.marked);
169 SupportElement &next_parent = move_bounds[parent->state.layer_idx + 1][parent->parents.front()];
170 assert(! next_parent.state.marked);
171 assert(branch.path.back().layer_idx + 1 == next_parent.state.layer_idx);
172 branch.path.emplace_back(to_tree_element(config, slicing_params, next_parent, false));
173 if (next_parent.parents.size() > 1) {
174 // Branching point was reached.
175 next_branch = &next_parent;
176 break;
177 }
178 next_parent.state.marked = true;
179 if (next_parent.parents.size() == 0)
180 // Tip is reached.
181 break;
182 parent = &next_parent;
183 }
184 } else if (first_parent.parents.size() > 1)
185 // Branching point was reached.
186 next_branch = &first_parent;
187 assert(branch.path.size() >= 2);
188 assert(next_branch == nullptr || ! next_branch->state.marked);
189 out.branches.emplace_back(std::move(branch));
190 Branch *pbranch = &out.branches.back();
191 if (parent_branch) {
192 parent_branch->up.push_back({ pbranch });
193 pbranch->down = { parent_branch };
194 }
195 if (next_branch)
196 this->visit_recursive(move_bounds, *next_branch, pbranch, out);
197 }
198
199 if (parent_branch) {
200 // Update initial radii of thin branches merging with a trunk.
201 auto it_up_max_r = std::max_element(parent_branch->up.begin(), parent_branch->up.end(),
202 [](const Bifurcation &l, const Bifurcation &r){ return l.branch->path[1].radius < r.branch->path[1].radius; });
203 const float r1 = it_up_max_r->branch->path[1].radius;
204 const float radius_increment = unscaled<float>(config.branch_radius_increase_per_layer);
205 for (auto it = parent_branch->up.begin(); it != parent_branch->up.end(); ++ it)
206 if (it != it_up_max_r) {
207 Element &el = it->branch->path.front();
208 Element &el2 = it->branch->path[1];
209 if (! is_approx(r1, el2.radius))
210 el.radius = std::min(el.radius, el2.radius + radius_increment);
211 }
212 // Sort children of parent_branch by decreasing radius.
213 std::sort(parent_branch->up.begin(), parent_branch->up.end(),
214 [](const Bifurcation &l, const Bifurcation &r){ return l.branch->path.front().radius > r.branch->path.front().radius; });
215 // Update number of branches to be considered a continuation of the trunk during smoothing.
216 {
217 const float r_trunk = 0.75 * it_up_max_r->branch->path.front().radius;
218 parent_branch->num_up_trunk = 0;
219 for (const Bifurcation& up : parent_branch->up)
220 if (up.branch->path.front().radius < r_trunk)
221 break;
222 else
223 ++ parent_branch->num_up_trunk;
224 }
225 }
226 }
227
228 const TreeSupportSettings &config;
229 const SlicingParameters &slicing_params;
230 };
231
232 TreeVisitor visitor{ config, slicing_params };
233
234 for (SupportElements &elements : move_bounds)
235 for (SupportElement &el : elements)
236 el.state.marked = false;
237
238 Trees trees;
239 for (LayerIndex layer_idx = 0; layer_idx + 1 < LayerIndex(move_bounds.size()); ++ layer_idx) {
240 for (SupportElement &start_element : move_bounds[layer_idx]) {
241 if (! start_element.state.marked && ! start_element.parents.empty()) {
242#if 0
243 {
244 // Verify that this node is a root, such that there is no element in the layer below
245 // that points to it.
246 int ielement = &start_element - move_bounds.data();
247 int found = 0;
248 if (layer_idx > 0) {
249 for (auto &el : move_bounds[layer_idx - 1]) {
250 for (auto iparent : el.parents)
251 if (iparent == ielement)
252 ++ found;
253 }
254 if (found != 0)
255 printf("Found: %d\n", found);
256 }
257 }
258#endif
259 trees.push_back({});
260 visitor.visit_recursive(move_bounds, start_element, nullptr, trees.back());
261 assert(! trees.back().branches.empty());
262 assert(! trees.back().branches.front().path.empty());
263#if 0
264 // Debugging: Only build trees with specific properties.
265 if (start_element.state.lost) {
266 }
267 else if (start_element.state.verylost) {
268 }
269 else
270 trees.pop_back();
271#endif
272 }
273 }
274 }
275
276#if 1
277 move_bounds.clear();
278#else
279 for (SupportElements &elements : move_bounds)
280 for (SupportElement &el : elements)
281 el.state.marked = false;
282#endif
283
284 return trees;
285}
Element to_tree_element(const TreeSupportSettings &config, const SlicingParameters &slicing_params, SupportElement &element, bool is_root)
Definition OrganicSupport.cpp:128
constexpr bool is_approx(Number value, Number test_value, Number precision=EPSILON)
Definition libslic3r.h:271
TPoint< P > front(const P &p)
Definition geometry_traits.hpp:872
std::vector< Tree > Trees
Definition CutSurface.cpp:2733
Definition OrganicSupport.cpp:87
std::vector< Element > path
Definition OrganicSupport.cpp:88
size_t num_up_trunk
Definition OrganicSupport.cpp:104
Bifurcations up
Definition OrganicSupport.cpp:99
ParentIndices parents
All elements in the layer above the current one that are supported by this element.
Definition TreeSupport.hpp:272
bool lost
Definition TreeSupport.hpp:129
bool verylost
Definition TreeSupport.hpp:130
bool marked
Definition TreeSupport.hpp:137
Definition OrganicSupport.cpp:111
Slic3r::deque< Branch > branches
Definition OrganicSupport.cpp:114

References Slic3r::FFFTreeSupport::TreeSupportSettings::branch_radius_increase_per_layer, Slic3r::FFFTreeSupport::Tree::branches, Slic3r::FFFTreeSupport::Branch::down, Slic3r::is_approx(), Slic3r::FFFTreeSupport::SupportElementState::layer_idx, Slic3r::FFFTreeSupport::SupportElementStateBits::marked, Slic3r::FFFTreeSupport::Branch::num_up_trunk, Slic3r::FFFTreeSupport::SupportElement::parents, Slic3r::FFFTreeSupport::Branch::path, Slic3r::FFFTreeSupport::Element::radius, Slic3r::FFFTreeSupport::SupportElement::state, to_tree_element(), and Slic3r::FFFTreeSupport::Branch::up.

+ Here is the call graph for this function:

◆ merge_influence_areas()

static void Slic3r::FFFTreeSupport::merge_influence_areas ( const TreeModelVolumes volumes,
const TreeSupportSettings config,
const LayerIndex  layer_idx,
std::vector< SupportElementMerging > &  influence_areas,
std::function< void()>  throw_on_cancel 
)
static

Merges Influence Areas at one layer if possible.

Branches which do overlap have to be merged. This manages the helper and uses a divide and conquer approach to parallelize this problem. This parallelization can at most accelerate the merging by a factor of 2.

Parameters
to_bp_areas[in]The Elements of the current Layer that will reach the buildplate. Value is the influence area where the center of a circle of support may be placed.
to_model_areas[in]The Elements of the current Layer that do not have to reach the buildplate. Also contains main as every element that can reach the buildplate is not forced to. Value is the influence area where the center of a circle of support may be placed.
influence_areas[in]The Elements of the current Layer without avoidances removed. This is the largest possible influence area for this layer. Value is the influence area where the center of a circle of support may be placed.
layer_idx[in]The current layer.
2316{
2317 const size_t input_size = influence_areas.size();
2318 if (input_size == 0)
2319 return;
2320
2321 // Merging by divide & conquer.
2322 // The majority of time is consumed by Clipper polygon operations, intersection is accelerated by bounding boxes.
2323 // Sorting input into an AABB tree helps to perform most of the intersections at first iterations,
2324 // thus reducing computation when merging larger subtrees.
2325 // The actual merge logic is found in merge_influence_areas_two_sets.
2326
2327 // Build an AABB tree over the influence areas.
2328 //FIXME A full tree does not need to be built, the lowest level branches will be always bucketed.
2329 // However the additional time consumed is negligible.
2331 // Sort influence_areas in place.
2332 tree.build_modify_input(influence_areas);
2333
2334 throw_on_cancel();
2335
2336 // Prepare the initial buckets as ranges of influence areas. The initial buckets contain power of 2 influence areas to follow
2337 // the branching of the AABB tree.
2338 // Vectors of ranges of influence areas, following the branching of the AABB tree:
2339 std::vector<std::pair<SupportElementMerging*, SupportElementMerging*>> buckets;
2340 // Initial number of buckets for 1st round of merging.
2341 size_t num_buckets_initial;
2342 {
2343 // How many buckets per first merge iteration?
2344 const size_t num_threads = tbb::this_task_arena::max_concurrency();
2345 // 4 buckets per thread if possible,
2346 const size_t num_buckets_min = (input_size + 2) / 4;
2347 // 2 buckets per thread otherwise.
2348 const size_t num_buckets_max = input_size / 2;
2349 num_buckets_initial = num_buckets_min >= num_threads ? num_buckets_min : num_buckets_max;
2350 const size_t bucket_size = num_buckets_min >= num_threads ? 4 : 2;
2351 // Fill in the buckets.
2352 SupportElementMerging *it = influence_areas.data();
2353 // Reserve one more bucket to keep a single influence area which will not be merged in the first iteration.
2354 buckets.reserve(num_buckets_initial + 1);
2355 for (size_t i = 0; i < num_buckets_initial; ++ i, it += bucket_size)
2356 buckets.emplace_back(std::make_pair(it, it + bucket_size));
2357 SupportElementMerging *it_end = influence_areas.data() + influence_areas.size();
2358 if (buckets.back().second >= it_end) {
2359 // Last bucket is less than size 4, but bigger than size 1.
2360 buckets.back().second = std::min(buckets.back().second, it_end);
2361 } else {
2362 // Last bucket is size 1, it will not be merged in the first iteration.
2363 assert(it + 1 == it_end);
2364 buckets.emplace_back(std::make_pair(it, it_end));
2365 }
2366 }
2367
2368 // 1st merge iteration, merge one with each other.
2369 tbb::parallel_for(tbb::blocked_range<size_t>(0, num_buckets_initial),
2370 [&](const tbb::blocked_range<size_t> &range) {
2371 for (size_t idx = range.begin(); idx < range.end(); ++ idx) {
2372 // Merge bucket_count adjacent to each other, merging uneven bucket numbers into even buckets
2373 buckets[idx].second = merge_influence_areas_leaves(volumes, config, layer_idx, buckets[idx].first, buckets[idx].second);
2374 throw_on_cancel();
2375 }
2376 });
2377
2378 // Further merge iterations, merging one AABB subtree with another one, hopefully minimizing intersections between the elements
2379 // of each of the subtree.
2380 while (buckets.size() > 1) {
2381 tbb::parallel_for(tbb::blocked_range<size_t>(0, buckets.size() / 2),
2382 [&](const tbb::blocked_range<size_t> &range) {
2383 for (size_t idx = range.begin(); idx < range.end(); ++ idx) {
2384 const size_t bucket_pair_idx = idx * 2;
2385 // Merge bucket_count adjacent to each other, merging uneven bucket numbers into even buckets
2386 buckets[bucket_pair_idx].second = merge_influence_areas_two_sets(volumes, config, layer_idx,
2387 buckets[bucket_pair_idx].first, buckets[bucket_pair_idx].second,
2388 buckets[bucket_pair_idx + 1].first, buckets[bucket_pair_idx + 1].second);
2389 throw_on_cancel();
2390 }
2391 });
2392 // Remove odd buckets, which were merged into even buckets.
2393 size_t new_size = (buckets.size() + 1) / 2;
2394 for (size_t i = 1; i < new_size; ++ i)
2395 buckets[i] = std::move(buckets[i * 2]);
2396 buckets.erase(buckets.begin() + new_size, buckets.end());
2397 }
2398}
Definition AABBTreeIndirect.hpp:40
void build_modify_input(std::vector< SourceNode > &input)
Definition AABBTreeIndirect.hpp:93
static SupportElementMerging * merge_influence_areas_leaves(const TreeModelVolumes &volumes, const TreeSupportSettings &config, const LayerIndex layer_idx, SupportElementMerging *const dst_begin, SupportElementMerging *dst_end)
Merges Influence Areas if possible.
Definition TreeSupport.cpp:2230
Definition TreeSupport.cpp:1696

References Slic3r::AABBTreeIndirect::Tree< ANumDimensions, ACoordType >::build_modify_input(), merge_influence_areas(), merge_influence_areas_leaves(), and Slic3r::range().

Referenced by create_layer_pathing(), and merge_influence_areas().

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

◆ merge_influence_areas_leaves()

static SupportElementMerging * Slic3r::FFFTreeSupport::merge_influence_areas_leaves ( const TreeModelVolumes volumes,
const TreeSupportSettings config,
const LayerIndex  layer_idx,
SupportElementMerging *const  dst_begin,
SupportElementMerging dst_end 
)
static

Merges Influence Areas if possible.

Branches which do overlap have to be merged. This helper merges all elements in input with the elements into reduced_new_layer. Elements in input_aabb are merged together if possible, while elements reduced_new_layer_aabb are not checked against each other.

Parameters
reduced_aabb[in,out]The already processed elements.
input_aabb[in]Not yet processed elements
to_bp_areas[in]The Elements of the current Layer that will reach the buildplate. Value is the influence area where the center of a circle of support may be placed.
to_model_areas[in]The Elements of the current Layer that do not have to reach the buildplate. Also contains main as every element that can reach the buildplate is not forced to. Value is the influence area where the center of a circle of support may be placed.
influence_areas[in]The influence areas without avoidance removed.
insert_bp_areas[out]Elements to be inserted into the main dictionary after the Helper terminates.
insert_model_areas[out]Elements to be inserted into the secondary dictionary after the Helper terminates.
insert_influence[out]Elements to be inserted into the dictionary containing the largest possibly valid influence area (ignoring if the area may not be there because of avoidance)
erase[out]Elements that should be deleted from the above dictionaries.
layer_idx[in]The Index of the current Layer.
2233{
2234 // Merging at the lowest level of the AABB tree. Checking one against each other, O(n^2).
2235 assert(dst_begin < dst_end);
2236 for (SupportElementMerging *i = dst_begin; i + 1 < dst_end;) {
2237 for (SupportElementMerging *j = i + 1; j != dst_end;)
2238 if (merge_influence_areas_two_elements(volumes, config, layer_idx, *i, *j)) {
2239 // i was merged with j, j is empty.
2240 if (j != -- dst_end)
2241 *j = std::move(*dst_end);
2242 goto merged;
2243 } else
2244 ++ j;
2245 // not merged
2246 ++ i;
2247 merged:
2248 ;
2249 }
2250 return dst_end;
2251}
static bool merge_influence_areas_two_elements(const TreeModelVolumes &volumes, const TreeSupportSettings &config, const LayerIndex layer_idx, SupportElementMerging &dst, SupportElementMerging &src)
Definition TreeSupport.cpp:2053

References merge_influence_areas_leaves(), and merge_influence_areas_two_elements().

Referenced by merge_influence_areas(), and merge_influence_areas_leaves().

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

◆ merge_influence_areas_two_elements()

static bool Slic3r::FFFTreeSupport::merge_influence_areas_two_elements ( const TreeModelVolumes volumes,
const TreeSupportSettings config,
const LayerIndex  layer_idx,
SupportElementMerging dst,
SupportElementMerging src 
)
static
2056{
2057 // Don't merge gracious with a non gracious area as bad placement could negatively impact reliability of the whole subtree.
2058 const bool merging_gracious_and_non_gracious = dst.state.to_model_gracious != src.state.to_model_gracious;
2059 // Could cause some issues with the increase of one area, as it is assumed that if the smaller is increased
2060 // by the delta to the larger it is engulfed by it already. But because a different collision
2061 // may be removed from the in draw_area() generated circles, this assumption could be wrong.
2062 const bool merging_min_and_regular_xy = dst.state.use_min_xy_dist != src.state.use_min_xy_dist;
2063
2064 if (merging_gracious_and_non_gracious || merging_min_and_regular_xy)
2065 return false;
2066
2067 const bool dst_radius_bigger = support_element_collision_radius(config, dst.state) > support_element_collision_radius(config, src.state);
2068 const SupportElementMerging &smaller_rad = dst_radius_bigger ? src : dst;
2069 const SupportElementMerging &bigger_rad = dst_radius_bigger ? dst : src;
2070 const coord_t real_radius_delta = std::abs(support_element_radius(config, bigger_rad.state) - support_element_radius(config, smaller_rad.state));
2071 {
2072 // Testing intersection of bounding boxes.
2073 // Expand the smaller radius branch bounding box to match the lambda intersect_small_with_bigger() below.
2074 // Because the lambda intersect_small_with_bigger() applies a rounded offset, a snug offset of the bounding box
2075 // is sufficient. On the other side, if a mitered offset was used by the lambda,
2076 // the bounding box expansion would have to account for the mitered extension of the sharp corners.
2077 Eigen::AlignedBox<coord_t, 2> smaller_bbox = smaller_rad.bbox();
2078 smaller_bbox.min() -= Point{ real_radius_delta, real_radius_delta };
2079 smaller_bbox.max() += Point{ real_radius_delta, real_radius_delta };
2080 if (! smaller_bbox.intersects(bigger_rad.bbox()))
2081 return false;
2082 }
2083
2084 // Accumulator of a radius increase of a "to model" branch by merging in a "to build plate" branch.
2085 coord_t increased_to_model_radius = 0;
2086 const bool merging_to_bp = dst.state.to_buildplate && src.state.to_buildplate;
2087 if (! merging_to_bp) {
2088 // Get the real radius increase as the user does not care for the collision model.
2089 if (dst.state.to_buildplate != src.state.to_buildplate) {
2090 // Merging a "to build plate" branch with a "to model" branch.
2091 // Don't allow merging a thick "to build plate" branch into a thinner "to model" branch.
2092 const coord_t rdst = support_element_radius(config, dst.state);
2093 const coord_t rsrc = support_element_radius(config, src.state);
2094 if (dst.state.to_buildplate) {
2095 if (rsrc < rdst)
2096 increased_to_model_radius = src.state.increased_to_model_radius + rdst - rsrc;
2097 } else {
2098 if (rsrc > rdst)
2099 increased_to_model_radius = dst.state.increased_to_model_radius + rsrc - rdst;
2100 }
2101 if (increased_to_model_radius > config.max_to_model_radius_increase)
2102 return false;
2103 }
2104 // if a merge could place a stable branch on unstable ground, would be increasing the radius further
2105 // than allowed to when merging to model and to_bp trees or would merge to model before it is known
2106 // they will even been drawn the merge is skipped
2107 if (! dst.state.supports_roof && ! src.state.supports_roof &&
2108 std::max(src.state.distance_to_top, dst.state.distance_to_top) < config.min_dtt_to_model)
2109 return false;
2110 }
2111
2112 // Area of the bigger radius is used to ensure correct placement regarding the relevant avoidance,
2113 // so if that would change an invalid area may be created.
2114 if (! bigger_rad.state.can_use_safe_radius && smaller_rad.state.can_use_safe_radius)
2115 return false;
2116
2117 // the bigger radius is used to verify that the area is still valid after the increase with the delta.
2118 // If there were a point where the big influence area could be valid with can_use_safe_radius
2119 // the element would already be can_use_safe_radius.
2120 // the smaller radius, which gets increased by delta may reach into the area where use_min_xy_dist is no longer required.
2121 const bool use_min_radius = bigger_rad.state.use_min_xy_dist && smaller_rad.state.use_min_xy_dist;
2122
2123 // The idea is that the influence area with the smaller collision radius is increased by the radius difference.
2124 // If this area has any intersections with the influence area of the larger collision radius, a branch (of the larger collision radius) placed in this intersection, has already engulfed the branch of the smaller collision radius.
2125 // Because of this a merge may happen even if the influence areas (that represent possible center points of branches) do not intersect yet.
2126 // Remember that collision radius <= real radius as otherwise this assumption would be false.
2127 const coord_t smaller_collision_radius = support_element_collision_radius(config, smaller_rad.state);
2128 const Polygons &collision = volumes.getCollision(smaller_collision_radius, layer_idx - 1, use_min_radius);
2129 auto intersect_small_with_bigger = [real_radius_delta, smaller_collision_radius, &collision, &config](const Polygons &small, const Polygons &bigger) {
2130 return intersection(
2132 small, real_radius_delta, collision,
2133 // -3 avoids possible rounding errors
2134 2 * (config.xy_distance + smaller_collision_radius - 3), 0, 0),
2135 bigger);
2136 };
2137//#define TREES_MERGE_RATHER_LATER
2139#ifdef TREES_MERGE_RATHER_LATER
2141#else
2142 intersect_small_with_bigger(
2143#endif
2144 merging_to_bp ? smaller_rad.areas.to_bp_areas : smaller_rad.areas.to_model_areas,
2145 merging_to_bp ? bigger_rad.areas.to_bp_areas : bigger_rad.areas.to_model_areas);
2146
2147 // dont use empty as a line is not empty, but for this use-case it very well may be (and would be one layer down as union does not keep lines)
2148 // check if the overlap is large enough (Small ares tend to attract rounding errors in clipper).
2149 if (area(intersect) <= tiny_area_threshold)
2150 return false;
2151
2152 // While 0.025 was guessed as enough, i did not have reason to change it.
2153 if (area(offset(intersect, scaled<float>(-0.025), jtMiter, 1.2)) <= tiny_area_threshold)
2154 return false;
2155
2156#ifdef TREES_MERGE_RATHER_LATER
2157 intersect =
2158 intersect_small_with_bigger(
2159 merging_to_bp ? smaller_rad.areas.to_bp_areas : smaller_rad.areas.to_model_areas,
2160 merging_to_bp ? bigger_rad.areas.to_bp_areas : bigger_rad.areas.to_model_areas);
2161#endif
2162
2163 // Do the actual merge now that the branches are confirmed to be able to intersect.
2164 // calculate which point is closest to the point of the last merge (or tip center if no merge above it has happened)
2165 // used at the end to estimate where to best place the branch on the bottom most layer
2166 // could be replaced with a random point inside the new area
2167 Point new_pos = move_inside_if_outside(intersect, dst.state.next_position);
2168
2169 SupportElementState new_state = merge_support_element_states(dst.state, src.state, new_pos, layer_idx - 1, config);
2170 new_state.increased_to_model_radius = increased_to_model_radius == 0 ?
2171 // increased_to_model_radius was not set yet. Propagate maximum.
2173 increased_to_model_radius;
2174
2175 // Rather unioning with "intersect" due to some rounding errors.
2176 Polygons influence_areas = safe_union(
2177 intersect_small_with_bigger(smaller_rad.areas.influence_areas, bigger_rad.areas.influence_areas),
2178 intersect);
2179
2180 Polygons to_model_areas;
2181 if (merging_to_bp && config.support_rests_on_model)
2182 to_model_areas = new_state.to_model_gracious ?
2183 // Rather unioning with "intersect" due to some rounding errors.
2184 safe_union(
2185 intersect_small_with_bigger(smaller_rad.areas.to_model_areas, bigger_rad.areas.to_model_areas),
2186 intersect) :
2187 influence_areas;
2188
2189 dst.parents.insert(dst.parents.end(), src.parents.begin(), src.parents.end());
2190 dst.state = new_state;
2191 dst.areas.influence_areas = std::move(influence_areas);
2192 dst.areas.to_bp_areas.clear();
2193 dst.areas.to_model_areas.clear();
2194 if (merging_to_bp) {
2195 dst.areas.to_bp_areas = std::move(intersect);
2196 if (config.support_rests_on_model)
2197 dst.areas.to_model_areas = std::move(to_model_areas);
2198 } else
2199 dst.areas.to_model_areas = std::move(intersect);
2200 // Update the bounding box.
2201 BoundingBox bbox(get_extents(dst.areas.influence_areas));
2202 bbox.merge(get_extents(dst.areas.to_bp_areas));
2203 bbox.merge(get_extents(dst.areas.to_model_areas));
2204 dst.set_bbox(bbox);
2205 // Clear the source data.
2206 src.areas.clear();
2207 src.parents.clear();
2208 return true;
2209}
EIGEN_DEVICE_FUNC const VectorType &() min() const
Definition AlignedBox.h:106
EIGEN_DEVICE_FUNC bool intersects(const AlignedBox &b) const
Definition AlignedBox.h:194
EIGEN_DEVICE_FUNC const VectorType &() max() const
Definition AlignedBox.h:110
An axis aligned box.
Definition AlignedBox.h:31
static Polygons safe_offset_inc(const Polygons &me, coord_t distance, const Polygons &collision, coord_t safe_step_size, coord_t last_step_offset_without_check, size_t min_amount_offset)
Offsets (increases the area of) a polygons object in multiple steps to ensure that it does not lag th...
Definition TreeSupport.cpp:763
static Point move_inside_if_outside(const Polygons &polygons, Point from, int distance=0, int64_t maxDist2=std::numeric_limits< int64_t >::max())
Definition TreeSupport.cpp:1521
static SupportElementState merge_support_element_states(const SupportElementState &first, const SupportElementState &second, const Point &next_position, const coord_t layer_idx, const TreeSupportSettings &config)
Definition TreeSupport.cpp:2007
IGL_INLINE void intersect(const M &A, const M &B, M &C)
Definition intersect.cpp:10
Polygons influence_areas
Definition TreeSupport.cpp:1683
void clear()
Definition TreeSupport.cpp:1689
Polygons to_bp_areas
Definition TreeSupport.cpp:1685
Polygons to_model_areas
Definition TreeSupport.cpp:1687
SupportElement::ParentIndices parents
All elements in the layer above the current one that are supported by this element.
Definition TreeSupport.cpp:1701
SupportElementInfluenceAreas areas
Definition TreeSupport.cpp:1703
void set_bbox(const BoundingBox &abbox)
Definition TreeSupport.cpp:1709
const Eigen::AlignedBox< coord_t, 2 > & bbox() const
Definition TreeSupport.cpp:1707
SupportElementState state
Definition TreeSupport.cpp:1697
bool to_buildplate
The element trys to reach the buildplate.
Definition TreeSupport.hpp:100
bool can_use_safe_radius
An influence area is considered safe when it can use the holefree avoidance <=> It will not have to e...
Definition TreeSupport.hpp:120
bool to_model_gracious
Will the branch be able to rest completely on a flat surface, be it buildplate or model ?
Definition TreeSupport.hpp:105
bool supports_roof
True if this Element or any parent (element above) provides support to a support roof.
Definition TreeSupport.hpp:115
Point next_position
The next position this support elements wants to reach. NOTE: This is mainly a suggestion regarding d...
Definition TreeSupport.hpp:155
coord_t increased_to_model_radius
The amount of extra radius we got from merging branches that could have reached the buildplate,...
Definition TreeSupport.hpp:182
coord_t max_to_model_radius_increase
How much a branch resting on the model may grow in radius by merging with branches that can reach the...
Definition TreeSupportCommon.hpp:256

References Slic3r::FFFTreeSupport::SupportElementMerging::areas, Slic3r::FFFTreeSupport::SupportElementMerging::bbox(), Slic3r::FFFTreeSupport::SupportElementStateBits::can_use_safe_radius, Slic3r::FFFTreeSupport::SupportElementInfluenceAreas::clear(), Slic3r::FFFTreeSupport::SupportElementState::distance_to_top, Slic3r::get_extents(), Slic3r::FFFTreeSupport::TreeModelVolumes::getCollision(), Slic3r::FFFTreeSupport::SupportElementState::increased_to_model_radius, Slic3r::FFFTreeSupport::SupportElementInfluenceAreas::influence_areas, Slic3r::intersection(), Eigen::AlignedBox< _Scalar, _AmbientDim >::intersects(), Eigen::AlignedBox< _Scalar, _AmbientDim >::max(), Slic3r::FFFTreeSupport::TreeSupportSettings::max_to_model_radius_increase, Slic3r::BoundingBoxBase< PointType, APointsType >::merge(), merge_influence_areas_two_elements(), merge_support_element_states(), Eigen::AlignedBox< _Scalar, _AmbientDim >::min(), Slic3r::FFFTreeSupport::TreeSupportSettings::min_dtt_to_model, move_inside_if_outside(), Slic3r::FFFTreeSupport::SupportElementState::next_position, Slic3r::FFFTreeSupport::SupportElementMerging::parents, safe_offset_inc(), safe_union(), Slic3r::FFFTreeSupport::SupportElementMerging::set_bbox(), Slic3r::FFFTreeSupport::SupportElementMerging::state, support_element_collision_radius(), support_element_radius(), Slic3r::FFFTreeSupport::TreeSupportSettings::support_rests_on_model, Slic3r::FFFTreeSupport::SupportElementStateBits::supports_roof, Slic3r::FFFTreeSupport::SupportElementInfluenceAreas::to_bp_areas, Slic3r::FFFTreeSupport::SupportElementStateBits::to_buildplate, Slic3r::FFFTreeSupport::SupportElementInfluenceAreas::to_model_areas, Slic3r::FFFTreeSupport::SupportElementStateBits::to_model_gracious, Slic3r::FFFTreeSupport::SupportElementStateBits::use_min_xy_dist, and Slic3r::FFFTreeSupport::TreeSupportSettings::xy_distance.

Referenced by merge_influence_areas_leaves(), merge_influence_areas_two_elements(), and merge_influence_areas_two_sets().

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

◆ merge_influence_areas_two_sets()

static SupportElementMerging * Slic3r::FFFTreeSupport::merge_influence_areas_two_sets ( const TreeModelVolumes volumes,
const TreeSupportSettings config,
const LayerIndex  layer_idx,
SupportElementMerging *const  dst_begin,
SupportElementMerging dst_end,
SupportElementMerging src_begin,
SupportElementMerging *const  src_end 
)
static
2257{
2258 // Merging src into dst.
2259 // Areas of src should not overlap with areas of another elements of src.
2260 // Areas of dst should not overlap with areas of another elements of dst.
2261 // The memory from dst_begin to src_end is reserved for the merging operation,
2262 // src follows dst.
2263 assert(src_begin < src_end);
2264 assert(dst_begin < dst_end);
2265 assert(dst_end <= src_begin);
2266 for (SupportElementMerging *src = src_begin; src != src_end; ++ src) {
2267 SupportElementMerging *dst = dst_begin;
2268 SupportElementMerging *merged = nullptr;
2269 for (; dst != dst_end; ++ dst)
2270 if (merge_influence_areas_two_elements(volumes, config, layer_idx, *dst, *src)) {
2271 merged = dst ++;
2272 if (src != src_begin)
2273 // Compactify src.
2274 *src = std::move(*src_begin);
2275 ++ src_begin;
2276 break;
2277 }
2278 for (; dst != dst_end;)
2279 if (merge_influence_areas_two_elements(volumes, config, layer_idx, *merged, *dst)) {
2280 // Compactify dst.
2281 if (dst != -- dst_end)
2282 *dst = std::move(*dst_end);
2283 } else
2284 ++ dst;
2285 }
2286 // Compactify src elements that were not merged with dst to the end of dst.
2287 assert(dst_end <= src_begin);
2288 if (dst_end == src_begin)
2289 dst_end = src_end;
2290 else
2291 while (src_begin != src_end)
2292 *dst_end ++ = std::move(*src_begin ++);
2293
2294 return dst_end;
2295}

References merge_influence_areas_two_elements(), and merge_influence_areas_two_sets().

Referenced by merge_influence_areas_two_sets().

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

◆ merge_support_element_states()

static SupportElementState Slic3r::FFFTreeSupport::merge_support_element_states ( const SupportElementState first,
const SupportElementState second,
const Point next_position,
const coord_t  layer_idx,
const TreeSupportSettings config 
)
static
2010{
2012 out.next_position = next_position;
2013 out.layer_idx = layer_idx;
2014 out.use_min_xy_dist = first.use_min_xy_dist || second.use_min_xy_dist;
2015 out.supports_roof = first.supports_roof || second.supports_roof;
2016 out.dont_move_until = std::max(first.dont_move_until, second.dont_move_until);
2018 out.missing_roof_layers = std::min(first.missing_roof_layers, second.missing_roof_layers);
2019 out.skip_ovalisation = false;
2020 if (first.target_height > second.target_height) {
2021 out.target_height = first.target_height;
2022 out.target_position = first.target_position;
2023 } else {
2024 out.target_height = second.target_height;
2025 out.target_position = second.target_position;
2026 }
2028 out.distance_to_top = std::max(first.distance_to_top, second.distance_to_top);
2029
2030 out.to_buildplate = first.to_buildplate && second.to_buildplate;
2031 out.to_model_gracious = first.to_model_gracious && second.to_model_gracious; // valid as we do not merge non-gracious with gracious
2032
2034 if (config.bp_radius_increase_per_layer > 0) {
2035 coord_t foot_increase_radius = std::abs(std::max(support_element_collision_radius(config, second), support_element_collision_radius(config, first)) - support_element_collision_radius(config, out));
2036 // elephant_foot_increases has to be recalculated, as when a smaller tree with a larger elephant_foot_increases merge with a larger branch
2037 // the elephant_foot_increases may have to be lower as otherwise the radius suddenly increases. This results often in a non integer value.
2038 out.elephant_foot_increases = foot_increase_radius / (config.bp_radius_increase_per_layer - config.branch_radius_increase_per_layer);
2039 }
2040
2041 // set last settings to the best out of both parents. If this is wrong, it will only cause a small performance penalty instead of weird behavior.
2042 out.last_area_increase = {
2043 std::min(first.last_area_increase.type, second.last_area_increase.type),
2049
2050 return out;
2051}
coord_t increase_speed
Definition TreeSupport.hpp:60
bool skip_ovalisation
Skip the ovalisation to parent and children when generating the final circles.
Definition TreeSupport.hpp:125
double elephant_foot_increases
Counter about the times the elephant foot was increased. Can be fractions for merge reasons.
Definition TreeSupport.hpp:187
LayerIndex target_height
The layer this support elements wants reach.
Definition TreeSupport.hpp:145
AreaIncreaseSettings last_area_increase
Settings used to increase the influence area to its current state.
Definition TreeSupport.hpp:197
uint32_t dont_move_until
The element tries to not move until this dtt is reached, is set to 0 if the element had to move.
Definition TreeSupport.hpp:192
Point target_position
The position this support elements wants to support on layer=target_height.
Definition TreeSupport.hpp:150
uint32_t missing_roof_layers
Amount of roof layers that were not yet added, because the branch needed to move.
Definition TreeSupport.hpp:202

References Slic3r::FFFTreeSupport::TreeSupportSettings::bp_radius_increase_per_layer, Slic3r::FFFTreeSupport::TreeSupportSettings::branch_radius_increase_per_layer, Slic3r::FFFTreeSupport::SupportElementStateBits::can_use_safe_radius, Slic3r::FFFTreeSupport::SupportElementState::distance_to_top, Slic3r::FFFTreeSupport::SupportElementState::dont_move_until, Slic3r::FFFTreeSupport::SupportElementState::effective_radius_height, Slic3r::FFFTreeSupport::SupportElementState::elephant_foot_increases, Slic3r::FFFTreeSupport::AreaIncreaseSettings::increase_radius, Slic3r::FFFTreeSupport::AreaIncreaseSettings::increase_speed, Slic3r::FFFTreeSupport::SupportElementState::last_area_increase, Slic3r::FFFTreeSupport::SupportElementState::layer_idx, merge_support_element_states(), Slic3r::FFFTreeSupport::SupportElementState::missing_roof_layers, Slic3r::FFFTreeSupport::AreaIncreaseSettings::move, Slic3r::FFFTreeSupport::SupportElementState::next_position, Slic3r::FFFTreeSupport::AreaIncreaseSettings::no_error, Slic3r::FFFTreeSupport::SupportElementStateBits::skip_ovalisation, support_element_collision_radius(), Slic3r::FFFTreeSupport::SupportElementStateBits::supports_roof, Slic3r::FFFTreeSupport::SupportElementState::target_height, Slic3r::FFFTreeSupport::SupportElementState::target_position, Slic3r::FFFTreeSupport::SupportElementStateBits::to_buildplate, Slic3r::FFFTreeSupport::SupportElementStateBits::to_model_gracious, Slic3r::FFFTreeSupport::AreaIncreaseSettings::type, Slic3r::FFFTreeSupport::AreaIncreaseSettings::use_min_distance, and Slic3r::FFFTreeSupport::SupportElementStateBits::use_min_xy_dist.

Referenced by merge_influence_areas_two_elements(), and merge_support_element_states().

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

◆ move_inside()

static unsigned int Slic3r::FFFTreeSupport::move_inside ( const Polygons polygons,
Point from,
int  distance = 0,
int64_t  maxDist2 = std::numeric_limits<int64_t>::max() 
)
static
1419{
1420 Point ret = from;
1421 double bestDist2 = std::numeric_limits<double>::max();
1422 auto bestPoly = static_cast<unsigned int>(-1);
1423 bool is_already_on_correct_side_of_boundary = false; // whether [from] is already on the right side of the boundary
1424 for (unsigned int poly_idx = 0; poly_idx < polygons.size(); ++ poly_idx) {
1425 const Polygon &poly = polygons[poly_idx];
1426 if (poly.size() < 2)
1427 continue;
1428 Point p0 = poly[poly.size() - 2];
1429 Point p1 = poly.back();
1430 // because we compare with vSize2 here (no division by zero), we also need to compare by vSize2 inside the loop
1431 // to avoid integer rounding edge cases
1432 bool projected_p_beyond_prev_segment = (p1 - p0).cast<int64_t>().dot((from - p0).cast<int64_t>()) >= (p1 - p0).cast<int64_t>().squaredNorm();
1433 for (const Point& p2 : poly) {
1434 // X = A + Normal(B-A) * (((B-A) dot (P-A)) / VSize(B-A));
1435 // = A + (B-A) * ((B-A) dot (P-A)) / VSize2(B-A);
1436 // X = P projected on AB
1437 const Point& a = p1;
1438 const Point& b = p2;
1439 const Point& p = from;
1440 auto ab = (b - a).cast<int64_t>();
1441 auto ap = (p - a).cast<int64_t>();
1442 int64_t ab_length2 = ab.squaredNorm();
1443 if (ab_length2 <= 0) { //A = B, i.e. the input polygon had two adjacent points on top of each other.
1444 p1 = p2; //Skip only one of the points.
1445 continue;
1446 }
1447 int64_t dot_prod = ab.dot(ap);
1448 if (dot_prod <= 0) { // x is projected to before ab
1449 if (projected_p_beyond_prev_segment) {
1450 // case which looks like: > .
1451 projected_p_beyond_prev_segment = false;
1452 Point& x = p1;
1453
1454 auto dist2 = (x - p).cast<int64_t>().squaredNorm();
1455 if (dist2 < bestDist2) {
1456 bestDist2 = dist2;
1457 bestPoly = poly_idx;
1458 if (distance == 0)
1459 ret = x;
1460 else {
1461 Vec2d abd = ab.cast<double>();
1462 Vec2d p1p2 = (p1 - p0).cast<double>();
1463 double lab = abd.norm();
1464 double lp1p2 = p1p2.norm();
1465 // inward direction irrespective of sign of [distance]
1466 auto inward_dir = perp(abd * (scaled<double>(10.0) / lab) + p1p2 * (scaled<double>(10.0) / lp1p2));
1467 // MM2INT(10.0) to retain precision for the eventual normalization
1468 ret = x + (inward_dir * (distance / inward_dir.norm())).cast<coord_t>();
1469 is_already_on_correct_side_of_boundary = inward_dir.dot((p - x).cast<double>()) * distance >= 0;
1470 }
1471 }
1472 } else {
1473 projected_p_beyond_prev_segment = false;
1474 p0 = p1;
1475 p1 = p2;
1476 continue;
1477 }
1478 } else if (dot_prod >= ab_length2) {
1479 // x is projected to beyond ab
1480 projected_p_beyond_prev_segment = true;
1481 p0 = p1;
1482 p1 = p2;
1483 continue;
1484 } else {
1485 // x is projected to a point properly on the line segment (not onto a vertex). The case which looks like | .
1486 projected_p_beyond_prev_segment = false;
1487 Point x = a + (ab.cast<double>() * (double(dot_prod) / double(ab_length2))).cast<coord_t>();
1488 auto dist2 = (p - x).cast<int64_t>().squaredNorm();
1489 if (dist2 < bestDist2) {
1490 bestDist2 = dist2;
1491 bestPoly = poly_idx;
1492 if (distance == 0)
1493 ret = x;
1494 else {
1495 Vec2d abd = ab.cast<double>();
1496 Vec2d inward_dir = perp(abd * (distance / abd.norm())); // inward or outward depending on the sign of [distance]
1497 ret = x + inward_dir.cast<coord_t>();
1498 is_already_on_correct_side_of_boundary = inward_dir.dot((p - x).cast<double>()) >= 0;
1499 }
1500 }
1501 }
1502 p0 = p1;
1503 p1 = p2;
1504 }
1505 }
1506 // when the best point is already inside and we're moving inside, or when the best point is already outside and we're moving outside
1507 if (is_already_on_correct_side_of_boundary) {
1508 if (bestDist2 < distance * distance)
1509 from = ret;
1510 else {
1511 // from = from; // original point stays unaltered. It is already inside by enough distance
1512 }
1513 return bestPoly;
1514 } else if (bestDist2 < maxDist2) {
1515 from = ret;
1516 return bestPoly;
1517 }
1518 return -1;
1519}
const Point & back() const
Definition MultiPoint.hpp:37
Eigen::Matrix< double, 2, 1, Eigen::DontAlign > Vec2d
Definition Point.hpp:51
TCoord< P > x(const P &p)
Definition geometry_traits.hpp:297
Pt perp(const Pt &p)
Definition geometry_traits.hpp:335
__int64 int64_t
Definition unistd.h:76

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

Referenced by move_inside(), and move_inside_if_outside().

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

◆ move_inside_if_outside()

static Point Slic3r::FFFTreeSupport::move_inside_if_outside ( const Polygons polygons,
Point  from,
int  distance = 0,
int64_t  maxDist2 = std::numeric_limits<int64_t>::max() 
)
static
1522{
1523 if (! contains(polygons, from))
1524 move_inside(polygons, from);
1525 return from;
1526}
static unsigned int move_inside(const Polygons &polygons, Point &from, int distance=0, int64_t maxDist2=std::numeric_limits< int64_t >::max())
Definition TreeSupport.cpp:1418

References contains(), move_inside(), and move_inside_if_outside().

Referenced by create_nodes_from_area(), merge_influence_areas_two_elements(), move_inside_if_outside(), set_points_on_areas(), set_to_model_contact_simple(), and set_to_model_contact_to_model_gracious().

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

◆ organic_draw_branches()

void Slic3r::FFFTreeSupport::organic_draw_branches ( PrintObject print_object,
TreeModelVolumes volumes,
const TreeSupportSettings config,
std::vector< SupportElements > &  move_bounds,
SupportGeneratorLayersPtr bottom_contacts,
SupportGeneratorLayersPtr top_contacts,
InterfacePlacer interface_placer,
SupportGeneratorLayersPtr intermediate_layers,
SupportGeneratorLayerStorage layer_storage,
std::function< void()>  throw_on_cancel 
)
1022{
1023 // All SupportElements are put into a layer independent storage to improve parallelization.
1024 std::vector<std::pair<SupportElement*, int>> elements_with_link_down;
1025 std::vector<size_t> linear_data_layers;
1026 {
1027 std::vector<std::pair<SupportElement*, int>> map_downwards_old;
1028 std::vector<std::pair<SupportElement*, int>> map_downwards_new;
1029 linear_data_layers.emplace_back(0);
1030 for (LayerIndex layer_idx = 0; layer_idx < LayerIndex(move_bounds.size()); ++ layer_idx) {
1031 SupportElements *layer_above = layer_idx + 1 < LayerIndex(move_bounds.size()) ? &move_bounds[layer_idx + 1] : nullptr;
1032 map_downwards_new.clear();
1033 std::sort(map_downwards_old.begin(), map_downwards_old.end(), [](auto& l, auto& r) { return l.first < r.first; });
1034 SupportElements &layer = move_bounds[layer_idx];
1035 for (size_t elem_idx = 0; elem_idx < layer.size(); ++ elem_idx) {
1036 SupportElement &elem = layer[elem_idx];
1037 int child = -1;
1038 if (layer_idx > 0) {
1039 auto it = std::lower_bound(map_downwards_old.begin(), map_downwards_old.end(), &elem, [](auto& l, const SupportElement* r) { return l.first < r; });
1040 if (it != map_downwards_old.end() && it->first == &elem) {
1041 child = it->second;
1042 // Only one link points to a node above from below.
1043 assert(!(++it != map_downwards_old.end() && it->first == &elem));
1044 }
1045#ifndef NDEBUG
1046 {
1047 const SupportElement *pchild = child == -1 ? nullptr : &move_bounds[layer_idx - 1][child];
1048 assert(pchild ? pchild->state.result_on_layer_is_set() : elem.state.target_height > layer_idx);
1049 }
1050#endif // NDEBUG
1051 }
1052 for (int32_t parent_idx : elem.parents) {
1053 SupportElement &parent = (*layer_above)[parent_idx];
1054 if (parent.state.result_on_layer_is_set())
1055 map_downwards_new.emplace_back(&parent, elem_idx);
1056 }
1057
1058 elements_with_link_down.push_back({ &elem, int(child) });
1059 }
1060 std::swap(map_downwards_old, map_downwards_new);
1061 linear_data_layers.emplace_back(elements_with_link_down.size());
1062 }
1063 }
1064
1065 throw_on_cancel();
1066
1067 organic_smooth_branches_avoid_collisions(print_object, volumes, config, move_bounds, elements_with_link_down, linear_data_layers, throw_on_cancel);
1068
1069 // Reduce memory footprint. After this point only finalize_interface_and_support_areas() will use volumes and from that only collisions with zero radius will be used.
1071
1072 // Unmark all nodes.
1073 for (SupportElements &elements : move_bounds)
1074 for (SupportElement &element : elements)
1075 element.state.marked = false;
1076
1077 // Traverse all nodes, generate tubes.
1078 // Traversal stack with nodes and their current parent
1079
1080 struct Branch {
1081 std::vector<const SupportElement*> path;
1082 bool has_root{ false };
1083 bool has_tip { false };
1084 };
1085
1086 struct Slice {
1087 Polygons polygons;
1088 Polygons bottom_contacts;
1089 size_t num_branches{ 0 };
1090 };
1091
1092 struct Tree {
1093 std::vector<Branch> branches;
1094
1095 std::vector<Slice> slices;
1096 LayerIndex first_layer_id{ -1 };
1097 };
1098
1099 std::vector<Tree> trees;
1100
1101 struct TreeVisitor {
1102 static void visit_recursive(std::vector<SupportElements> &move_bounds, SupportElement &start_element, Tree &out) {
1103 assert(! start_element.state.marked && ! start_element.parents.empty());
1104 // Collect elements up to a bifurcation above.
1105 start_element.state.marked = true;
1106 // For each branch bifurcating from this point:
1107 //SupportElements &layer = move_bounds[start_element.state.layer_idx];
1108 SupportElements &layer_above = move_bounds[start_element.state.layer_idx + 1];
1109 bool root = out.branches.empty();
1110 for (size_t parent_idx = 0; parent_idx < start_element.parents.size(); ++ parent_idx) {
1111 Branch branch;
1112 branch.path.emplace_back(&start_element);
1113 // Traverse each branch until it branches again.
1114 SupportElement &first_parent = layer_above[start_element.parents[parent_idx]];
1115 assert(! first_parent.state.marked);
1116 assert(branch.path.back()->state.layer_idx + 1 == first_parent.state.layer_idx);
1117 branch.path.emplace_back(&first_parent);
1118 if (first_parent.parents.size() < 2)
1119 first_parent.state.marked = true;
1120 SupportElement *next_branch = nullptr;
1121 if (first_parent.parents.size() == 1) {
1122 for (SupportElement *parent = &first_parent;;) {
1123 assert(parent->state.marked);
1124 SupportElement &next_parent = move_bounds[parent->state.layer_idx + 1][parent->parents.front()];
1125 assert(! next_parent.state.marked);
1126 assert(branch.path.back()->state.layer_idx + 1 == next_parent.state.layer_idx);
1127 branch.path.emplace_back(&next_parent);
1128 if (next_parent.parents.size() > 1) {
1129 // Branching point was reached.
1130 next_branch = &next_parent;
1131 break;
1132 }
1133 next_parent.state.marked = true;
1134 if (next_parent.parents.size() == 0)
1135 // Tip is reached.
1136 break;
1137 parent = &next_parent;
1138 }
1139 } else if (first_parent.parents.size() > 1)
1140 // Branching point was reached.
1141 next_branch = &first_parent;
1142 assert(branch.path.size() >= 2);
1143 assert(next_branch == nullptr || ! next_branch->state.marked);
1144 branch.has_root = root;
1145 branch.has_tip = ! next_branch;
1146 out.branches.emplace_back(std::move(branch));
1147 if (next_branch)
1148 visit_recursive(move_bounds, *next_branch, out);
1149 }
1150 }
1151 };
1152
1153 for (LayerIndex layer_idx = 0; layer_idx + 1 < LayerIndex(move_bounds.size()); ++ layer_idx) {
1154// int ielement;
1155 for (SupportElement& start_element : move_bounds[layer_idx]) {
1156 if (!start_element.state.marked && !start_element.parents.empty()) {
1157#if 0
1158 int found = 0;
1159 if (layer_idx > 0) {
1160 for (auto& el : move_bounds[layer_idx - 1]) {
1161 for (auto iparent : el.parents)
1162 if (iparent == ielement)
1163 ++found;
1164 }
1165 if (found != 0)
1166 printf("Found: %d\n", found);
1167 }
1168#endif
1169 trees.push_back({});
1170 TreeVisitor::visit_recursive(move_bounds, start_element, trees.back());
1171 assert(!trees.back().branches.empty());
1172 //FIXME debugging
1173#if 0
1174 if (start_element.state.lost) {
1175 }
1176 else if (start_element.state.verylost) {
1177 } else
1178 trees.pop_back();
1179#endif
1180 }
1181// ++ ielement;
1182 }
1183 }
1184
1185 const SlicingParameters &slicing_params = print_object.slicing_parameters();
1186 MeshSlicingParams mesh_slicing_params;
1187 mesh_slicing_params.mode = MeshSlicingParams::SlicingMode::Positive;
1188
1189 tbb::parallel_for(tbb::blocked_range<size_t>(0, trees.size(), 1),
1190 [&trees, &volumes, &config, &slicing_params, &move_bounds, &mesh_slicing_params, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
1191 indexed_triangle_set partial_mesh;
1192 std::vector<float> slice_z;
1193 std::vector<Polygons> bottom_contacts;
1194 for (size_t tree_id = range.begin(); tree_id < range.end(); ++ tree_id) {
1195 Tree &tree = trees[tree_id];
1196 for (const Branch &branch : tree.branches) {
1197 // Triangulate the tube.
1198 partial_mesh.clear();
1199 std::pair<float, float> zspan = extrude_branch(branch.path, config, slicing_params, move_bounds, partial_mesh);
1200 LayerIndex layer_begin = branch.has_root ?
1201 branch.path.front()->state.layer_idx :
1202 std::min(branch.path.front()->state.layer_idx, layer_idx_ceil(slicing_params, config, zspan.first));
1203 LayerIndex layer_end = (branch.has_tip ?
1204 branch.path.back()->state.layer_idx :
1205 std::max(branch.path.back()->state.layer_idx, layer_idx_floor(slicing_params, config, zspan.second))) + 1;
1206 slice_z.clear();
1207 for (LayerIndex layer_idx = layer_begin; layer_idx < layer_end; ++ layer_idx) {
1208 const double print_z = layer_z(slicing_params, config, layer_idx);
1209 const double bottom_z = layer_idx > 0 ? layer_z(slicing_params, config, layer_idx - 1) : 0.;
1210 slice_z.emplace_back(float(0.5 * (bottom_z + print_z)));
1211 }
1212 std::vector<Polygons> slices = slice_mesh(partial_mesh, slice_z, mesh_slicing_params, throw_on_cancel);
1213 bottom_contacts.clear();
1214 //FIXME parallelize?
1215 for (LayerIndex i = 0; i < LayerIndex(slices.size()); ++ i)
1216 slices[i] = diff_clipped(slices[i], volumes.getCollision(0, layer_begin + i, true)); //FIXME parent_uses_min || draw_area.element->state.use_min_xy_dist);
1217
1218 size_t num_empty = 0;
1219 if (slices.front().empty()) {
1220 // Some of the initial layers are empty.
1221 num_empty = std::find_if(slices.begin(), slices.end(), [](auto &s) { return !s.empty(); }) - slices.begin();
1222 } else {
1223 if (branch.has_root) {
1224 if (branch.path.front()->state.to_model_gracious) {
1225 if (config.settings.support_floor_layers > 0)
1226 //FIXME one may just take the whole tree slice as bottom interface.
1227 bottom_contacts.emplace_back(intersection_clipped(slices.front(), volumes.getPlaceableAreas(0, layer_begin, [] {})));
1228 } else if (layer_begin > 0) {
1229 // Drop down areas that do rest non - gracefully on the model to ensure the branch actually rests on something.
1230 struct BottomExtraSlice {
1231 Polygons polygons;
1232 double area;
1233 };
1234 std::vector<BottomExtraSlice> bottom_extra_slices;
1235 Polygons rest_support;
1236 coord_t bottom_radius = support_element_radius(config, *branch.path.front());
1237 // Don't propagate further than 1.5 * bottom radius.
1238 //LayerIndex layers_propagate_max = 2 * bottom_radius / config.layer_height;
1239 LayerIndex layers_propagate_max = 5 * bottom_radius / config.layer_height;
1240 LayerIndex layer_bottommost = branch.path.front()->state.verylost ?
1241 // If the tree bottom is hanging in the air, bring it down to some surface.
1242 0 :
1243 //FIXME the "verylost" branches should stop when crossing another support.
1244 std::max(0, layer_begin - layers_propagate_max);
1245 double support_area_min_radius = M_PI * sqr(double(config.branch_radius));
1246 double support_area_stop = std::max(0.2 * M_PI * sqr(double(bottom_radius)), 0.5 * support_area_min_radius);
1247 // Only propagate until the rest area is smaller than this threshold.
1248 //double support_area_min = 0.1 * support_area_min_radius;
1249 for (LayerIndex layer_idx = layer_begin - 1; layer_idx >= layer_bottommost; -- layer_idx) {
1250 rest_support = diff_clipped(rest_support.empty() ? slices.front() : rest_support, volumes.getCollision(0, layer_idx, false));
1251 double rest_support_area = area(rest_support);
1252 if (rest_support_area < support_area_stop)
1253 // Don't propagate a fraction of the tree contact surface.
1254 break;
1255 bottom_extra_slices.push_back({ rest_support, rest_support_area });
1256 }
1257 // Now remove those bottom slices that are not supported at all.
1258#if 0
1259 while (! bottom_extra_slices.empty()) {
1260 Polygons this_bottom_contacts = intersection_clipped(
1261 bottom_extra_slices.back().polygons, volumes.getPlaceableAreas(0, layer_begin - LayerIndex(bottom_extra_slices.size()), [] {}));
1262 if (area(this_bottom_contacts) < support_area_min)
1263 bottom_extra_slices.pop_back();
1264 else {
1265 // At least a fraction of the tree bottom is considered to be supported.
1266 if (config.settings.support_floor_layers > 0)
1267 // Turn this fraction of the tree bottom into a contact layer.
1268 bottom_contacts.emplace_back(std::move(this_bottom_contacts));
1269 break;
1270 }
1271 }
1272#endif
1273 if (config.settings.support_floor_layers > 0)
1274 for (int i = int(bottom_extra_slices.size()) - 2; i >= 0; -- i)
1275 bottom_contacts.emplace_back(
1276 intersection_clipped(bottom_extra_slices[i].polygons, volumes.getPlaceableAreas(0, layer_begin - i - 1, [] {})));
1277 layer_begin -= LayerIndex(bottom_extra_slices.size());
1278 slices.insert(slices.begin(), bottom_extra_slices.size(), {});
1279 auto it_dst = slices.begin();
1280 for (auto it_src = bottom_extra_slices.rbegin(); it_src != bottom_extra_slices.rend(); ++ it_src)
1281 *it_dst ++ = std::move(it_src->polygons);
1282 }
1283 }
1284
1285#if 0
1286 //FIXME branch.has_tip seems to not be reliable.
1287 if (branch.has_tip && interface_placer.support_parameters.has_top_contacts)
1288 // Add top slices to top contacts / interfaces / base interfaces.
1289 for (int i = int(branch.path.size()) - 1; i >= 0; -- i) {
1290 const SupportElement &el = *branch.path[i];
1291 if (el.state.missing_roof_layers == 0)
1292 break;
1293 //FIXME Move or not?
1294 interface_placer.add_roof(std::move(slices[int(slices.size()) - i - 1]), el.state.layer_idx,
1295 interface_placer.support_parameters.num_top_interface_layers + 1 - el.state.missing_roof_layers);
1296 }
1297#endif
1298 }
1299
1300 layer_begin += LayerIndex(num_empty);
1301 while (! slices.empty() && slices.back().empty()) {
1302 slices.pop_back();
1303 -- layer_end;
1304 }
1305 if (layer_begin < layer_end) {
1306 LayerIndex new_begin = tree.first_layer_id == -1 ? layer_begin : std::min(tree.first_layer_id, layer_begin);
1307 LayerIndex new_end = tree.first_layer_id == -1 ? layer_end : std::max(tree.first_layer_id + LayerIndex(tree.slices.size()), layer_end);
1308 size_t new_size = size_t(new_end - new_begin);
1309 if (tree.first_layer_id == -1) {
1310 } else if (tree.slices.capacity() < new_size) {
1311 std::vector<Slice> new_slices;
1312 new_slices.reserve(new_size);
1313 if (LayerIndex dif = tree.first_layer_id - new_begin; dif > 0)
1314 new_slices.insert(new_slices.end(), dif, {});
1315 append(new_slices, std::move(tree.slices));
1316 tree.slices.swap(new_slices);
1317 } else if (LayerIndex dif = tree.first_layer_id - new_begin; dif > 0)
1318 tree.slices.insert(tree.slices.begin(), tree.first_layer_id - new_begin, {});
1319 tree.slices.insert(tree.slices.end(), new_size - tree.slices.size(), {});
1320 layer_begin -= LayerIndex(num_empty);
1321 for (LayerIndex i = layer_begin; i != layer_end; ++ i) {
1322 int j = i - layer_begin;
1323 if (Polygons &src = slices[j]; ! src.empty()) {
1324 Slice &dst = tree.slices[i - new_begin];
1325 if (++ dst.num_branches > 1) {
1326 append(dst.polygons, std::move(src));
1327 if (j < int(bottom_contacts.size()))
1328 append(dst.bottom_contacts, std::move(bottom_contacts[j]));
1329 } else {
1330 dst.polygons = std::move(std::move(src));
1331 if (j < int(bottom_contacts.size()))
1332 dst.bottom_contacts = std::move(bottom_contacts[j]);
1333 }
1334 }
1335 }
1336 tree.first_layer_id = new_begin;
1337 }
1338 }
1339 }
1340 }, tbb::simple_partitioner());
1341
1342 tbb::parallel_for(tbb::blocked_range<size_t>(0, trees.size(), 1),
1343 [&trees, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
1344 for (size_t tree_id = range.begin(); tree_id < range.end(); ++ tree_id) {
1345 Tree &tree = trees[tree_id];
1346 for (Slice &slice : tree.slices)
1347 if (slice.num_branches > 1) {
1348 slice.polygons = union_(slice.polygons);
1349 slice.bottom_contacts = union_(slice.bottom_contacts);
1350 slice.num_branches = 1;
1351 }
1352 throw_on_cancel();
1353 }
1354 }, tbb::simple_partitioner());
1355
1356 size_t num_layers = 0;
1357 for (Tree &tree : trees)
1358 if (tree.first_layer_id >= 0)
1359 num_layers = std::max(num_layers, size_t(tree.first_layer_id + tree.slices.size()));
1360
1361 std::vector<Slice> slices(num_layers, Slice{});
1362 for (Tree &tree : trees)
1363 if (tree.first_layer_id >= 0) {
1364 for (LayerIndex i = tree.first_layer_id; i != tree.first_layer_id + LayerIndex(tree.slices.size()); ++ i)
1365 if (Slice &src = tree.slices[i - tree.first_layer_id]; ! src.polygons.empty()) {
1366 Slice &dst = slices[i];
1367 if (++ dst.num_branches > 1) {
1368 append(dst.polygons, std::move(src.polygons));
1369 append(dst.bottom_contacts, std::move(src.bottom_contacts));
1370 } else {
1371 dst.polygons = std::move(src.polygons);
1372 dst.bottom_contacts = std::move(src.bottom_contacts);
1373 }
1374 }
1375 }
1376
1377 tbb::parallel_for(tbb::blocked_range<size_t>(0, std::min(move_bounds.size(), slices.size()), 1),
1378 [&print_object, &config, &slices, &bottom_contacts, &top_contacts, &intermediate_layers, &layer_storage, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
1379 for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
1380 Slice &slice = slices[layer_idx];
1381 assert(intermediate_layers[layer_idx] == nullptr);
1382 Polygons base_layer_polygons = slice.num_branches > 1 ? union_(slice.polygons) : std::move(slice.polygons);
1383 Polygons bottom_contact_polygons = slice.num_branches > 1 ? union_(slice.bottom_contacts) : std::move(slice.bottom_contacts);
1384
1385 if (! base_layer_polygons.empty()) {
1386 // Most of the time in this function is this union call. Can take 300+ ms when a lot of areas are to be unioned.
1387 base_layer_polygons = smooth_outward(union_(base_layer_polygons), config.support_line_width); //FIXME was .smooth(50);
1388 //smooth_outward(closing(std::move(bottom), closing_distance + minimum_island_radius, closing_distance, SUPPORT_SURFACES_OFFSET_PARAMETERS), smoothing_distance) :
1389 // simplify a bit, to ensure the output does not contain outrageous amounts of vertices. Should not be necessary, just a precaution.
1390 base_layer_polygons = polygons_simplify(base_layer_polygons, std::min(scaled<double>(0.03), double(config.resolution)), polygons_strictly_simple);
1391 }
1392
1393 // Subtract top contact layer polygons from support base.
1394 SupportGeneratorLayer *top_contact_layer = top_contacts.empty() ? nullptr : top_contacts[layer_idx];
1395 if (top_contact_layer && ! top_contact_layer->polygons.empty() && ! base_layer_polygons.empty()) {
1396 base_layer_polygons = diff(base_layer_polygons, top_contact_layer->polygons);
1397 if (! bottom_contact_polygons.empty())
1398 //FIXME it may be better to clip bottom contacts with top contacts first after they are propagated to produce interface layers.
1399 bottom_contact_polygons = diff(bottom_contact_polygons, top_contact_layer->polygons);
1400 }
1401 if (! bottom_contact_polygons.empty()) {
1402 base_layer_polygons = diff(base_layer_polygons, bottom_contact_polygons);
1403 SupportGeneratorLayer *bottom_contact_layer = bottom_contacts[layer_idx] = &layer_allocate(
1404 layer_storage, SupporLayerType::BottomContact, print_object.slicing_parameters(), config, layer_idx);
1405 bottom_contact_layer->polygons = std::move(bottom_contact_polygons);
1406 }
1407 if (! base_layer_polygons.empty()) {
1408 SupportGeneratorLayer *base_layer = intermediate_layers[layer_idx] = &layer_allocate(
1409 layer_storage, SupporLayerType::Base, print_object.slicing_parameters(), config, layer_idx);
1410 base_layer->polygons = union_(base_layer_polygons);
1411 }
1412
1413 throw_on_cancel();
1414 }
1415 }, tbb::simple_partitioner());
1416}
void clear_all_but_object_collision()
Definition TreeModelVolumes.hpp:59
EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC half() max(const half &a, const half &b)
Definition Half.h:516
static void organic_smooth_branches_avoid_collisions(const PrintObject &print_object, const TreeModelVolumes &volumes, const TreeSupportSettings &config, std::vector< SupportElements > &move_bounds, const std::vector< std::pair< SupportElement *, int > > &elements_with_link_down, const std::vector< size_t > &linear_data_layers, std::function< void()> throw_on_cancel)
Definition OrganicSupport.cpp:690
constexpr auto size(const C &c) -> decltype(c.size())
Definition span.hpp:183
CGAL::AABB_tree< AABB_triangle_traits > Tree
Definition points_inside_component.cpp:38

References Slic3r::FFFTreeSupport::Tree::branches, Slic3r::FFFTreeSupport::TreeModelVolumes::clear_all_but_object_collision(), Slic3r::FFFTreeSupport::Branch::has_root(), Slic3r::FFFTreeSupport::Branch::has_tip(), Slic3r::FFFTreeSupport::SupportElementState::layer_idx, Slic3r::FFFTreeSupport::SupportElementStateBits::marked, Slic3r::MeshSlicingParams::mode, organic_draw_branches(), organic_smooth_branches_avoid_collisions(), Slic3r::FFFTreeSupport::SupportElement::parents, Slic3r::FFFTreeSupport::Branch::path, Slic3r::range(), Slic3r::FFFTreeSupport::SupportElementState::result_on_layer_is_set(), Slic3r::PrintObject::slicing_parameters(), Slic3r::FFFTreeSupport::SupportElement::state, and Slic3r::FFFTreeSupport::SupportElementState::target_height.

Referenced by generate_support_areas(), and organic_draw_branches().

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

◆ organic_smooth_branches_avoid_collisions()

static void Slic3r::FFFTreeSupport::organic_smooth_branches_avoid_collisions ( const PrintObject print_object,
const TreeModelVolumes volumes,
const TreeSupportSettings config,
std::vector< SupportElements > &  move_bounds,
const std::vector< std::pair< SupportElement *, int > > &  elements_with_link_down,
const std::vector< size_t > &  linear_data_layers,
std::function< void()>  throw_on_cancel 
)
static
698{
699 struct LayerCollisionCache {
700 coord_t min_element_radius{ std::numeric_limits<coord_t>::max() };
701 bool min_element_radius_known() const { return this->min_element_radius != std::numeric_limits<coord_t>::max(); }
702 coord_t collision_radius{ 0 };
703 std::vector<Linef> lines;
704 AABBTreeIndirect::Tree<2, double> aabbtree_lines;
705 bool empty() const { return this->lines.empty(); }
706 };
707 std::vector<LayerCollisionCache> layer_collision_cache;
708 layer_collision_cache.reserve(1024);
709 const SlicingParameters &slicing_params = print_object.slicing_parameters();
710 for (const std::pair<SupportElement*, int>& element : elements_with_link_down) {
711 LayerIndex layer_idx = element.first->state.layer_idx;
712 if (size_t num_layers = layer_idx + 1; num_layers > layer_collision_cache.size()) {
713 if (num_layers > layer_collision_cache.capacity())
714 reserve_power_of_2(layer_collision_cache, num_layers);
715 layer_collision_cache.resize(num_layers, {});
716 }
717 auto& l = layer_collision_cache[layer_idx];
718 l.min_element_radius = std::min(l.min_element_radius, support_element_radius(config, *element.first));
719 }
720
721 throw_on_cancel();
722
723 for (LayerIndex layer_idx = 0; layer_idx < LayerIndex(layer_collision_cache.size()); ++layer_idx)
724 if (LayerCollisionCache& l = layer_collision_cache[layer_idx]; !l.min_element_radius_known())
725 l.min_element_radius = 0;
726 else {
727 //FIXME
728 l.min_element_radius = 0;
729 std::optional<std::pair<coord_t, std::reference_wrapper<const Polygons>>> res = volumes.get_collision_lower_bound_area(layer_idx, l.min_element_radius);
730 assert(res.has_value());
731 l.collision_radius = res->first;
732 Lines alines = to_lines(res->second.get());
733 l.lines.reserve(alines.size());
734 for (const Line &line : alines)
735 l.lines.push_back({ unscaled<double>(line.a), unscaled<double>(line.b) });
736 l.aabbtree_lines = AABBTreeLines::build_aabb_tree_over_indexed_lines(l.lines);
737 throw_on_cancel();
738 }
739
740 struct CollisionSphere {
741 const SupportElement& element;
742 int element_below_id;
743 const bool locked;
744 float radius;
745 // Current position, when nudged away from the collision.
746 Vec3f position;
747 // Previous position, for Laplacian smoothing.
748 Vec3f prev_position;
749 //
750 Vec3f last_collision;
751 double last_collision_depth;
752 // Minimum Z for which the sphere collision will be evaluated.
753 // Limited by the minimum sloping angle and by the bottom of the tree.
754 float min_z{ -std::numeric_limits<float>::max() };
755 // Maximum Z for which the sphere collision will be evaluated.
756 // Limited by the minimum sloping angle and by the tip of the current branch.
757 float max_z{ std::numeric_limits<float>::max() };
758 uint32_t layer_begin;
759 uint32_t layer_end;
760 };
761
762 std::vector<CollisionSphere> collision_spheres;
763 collision_spheres.reserve(elements_with_link_down.size());
764 for (const std::pair<SupportElement*, int> &element_with_link : elements_with_link_down) {
765 const SupportElement &element = *element_with_link.first;
766 const int link_down = element_with_link.second;
767 collision_spheres.push_back({
768 element,
769 link_down,
770 // locked
771 element.parents.empty() || (link_down == -1 && element.state.layer_idx > 0),
772 unscaled<float>(support_element_radius(config, element)),
773 // 3D position
774 to_3d(unscaled<float>(element.state.result_on_layer), float(layer_z(slicing_params, config, element.state.layer_idx)))
775 });
776 // Update min_z coordinate to min_z of the tree below.
777 CollisionSphere &collision_sphere = collision_spheres.back();
778 if (link_down != -1) {
779 const size_t offset_below = linear_data_layers[element.state.layer_idx - 1];
780 collision_sphere.min_z = collision_spheres[offset_below + link_down].min_z;
781 } else
782 collision_sphere.min_z = collision_sphere.position.z();
783 }
784 // Update max_z by propagating max_z from the tips of the branches.
785 for (int collision_sphere_id = int(collision_spheres.size()) - 1; collision_sphere_id >= 0; -- collision_sphere_id) {
786 CollisionSphere &collision_sphere = collision_spheres[collision_sphere_id];
787 if (collision_sphere.element.parents.empty())
788 // Tip
789 collision_sphere.max_z = collision_sphere.position.z();
790 else {
791 // Below tip
792 const size_t offset_above = linear_data_layers[collision_sphere.element.state.layer_idx + 1];
793 for (auto iparent : collision_sphere.element.parents) {
794 float parent_z = collision_spheres[offset_above + iparent].max_z;
795// collision_sphere.max_z = collision_sphere.max_z == std::numeric_limits<float>::max() ? parent_z : std::max(collision_sphere.max_z, parent_z);
796 collision_sphere.max_z = std::min(collision_sphere.max_z, parent_z);
797 }
798 }
799 }
800 // Update min_z / max_z to limit the search Z span of a given sphere for collision detection.
801 for (CollisionSphere &collision_sphere : collision_spheres) {
802 //FIXME limit the collision span by the tree slope.
803 collision_sphere.min_z = std::max(collision_sphere.min_z, collision_sphere.position.z() - collision_sphere.radius);
804 collision_sphere.max_z = std::min(collision_sphere.max_z, collision_sphere.position.z() + collision_sphere.radius);
805 collision_sphere.layer_begin = std::min(collision_sphere.element.state.layer_idx, layer_idx_ceil(slicing_params, config, collision_sphere.min_z));
806 assert(collision_sphere.layer_begin < layer_collision_cache.size());
807 collision_sphere.layer_end = std::min(LayerIndex(layer_collision_cache.size()), std::max(collision_sphere.element.state.layer_idx, layer_idx_floor(slicing_params, config, collision_sphere.max_z)) + 1);
808 }
809
810 throw_on_cancel();
811
812 static constexpr const double collision_extra_gap = 0.1;
813 static constexpr const double max_nudge_collision_avoidance = 0.5;
814 static constexpr const double max_nudge_smoothing = 0.2;
815 static constexpr const size_t num_iter = 100; // 1000;
816 for (size_t iter = 0; iter < num_iter; ++ iter) {
817 // Back up prev position before Laplacian smoothing.
818 for (CollisionSphere &collision_sphere : collision_spheres)
819 collision_sphere.prev_position = collision_sphere.position;
820 std::atomic<size_t> num_moved{ 0 };
821 tbb::parallel_for(tbb::blocked_range<size_t>(0, collision_spheres.size()),
822 [&collision_spheres, &layer_collision_cache, &slicing_params, &config, &linear_data_layers, &num_moved, &throw_on_cancel](const tbb::blocked_range<size_t> range) {
823 for (size_t collision_sphere_id = range.begin(); collision_sphere_id < range.end(); ++ collision_sphere_id)
824 if (CollisionSphere &collision_sphere = collision_spheres[collision_sphere_id]; ! collision_sphere.locked) {
825 // Calculate collision of multiple 2D layers against a collision sphere.
826 collision_sphere.last_collision_depth = - std::numeric_limits<double>::max();
827 for (uint32_t layer_id = collision_sphere.layer_begin; layer_id != collision_sphere.layer_end; ++ layer_id) {
828 double dz = (layer_id - collision_sphere.element.state.layer_idx) * slicing_params.layer_height;
829 if (double r2 = sqr(collision_sphere.radius) - sqr(dz); r2 > 0) {
830 if (const LayerCollisionCache &layer_collision_cache_item = layer_collision_cache[layer_id]; ! layer_collision_cache_item.empty()) {
831 size_t hit_idx_out;
832 Vec2d hit_point_out;
833 if (double dist = sqrt(AABBTreeLines::squared_distance_to_indexed_lines(
834 layer_collision_cache_item.lines, layer_collision_cache_item.aabbtree_lines, Vec2d(to_2d(collision_sphere.position).cast<double>()),
835 hit_idx_out, hit_point_out, r2)); dist >= 0.) {
836 double collision_depth = sqrt(r2) - dist;
837 if (collision_depth > collision_sphere.last_collision_depth) {
838 collision_sphere.last_collision_depth = collision_depth;
839 collision_sphere.last_collision = to_3d(hit_point_out.cast<float>(), float(layer_z(slicing_params, config, layer_id)));
840 }
841 }
842 }
843 }
844 }
845 if (collision_sphere.last_collision_depth > 0) {
846 // Collision detected to be removed.
847 // Nudge the circle center away from the collision.
848 if (collision_sphere.last_collision_depth > EPSILON)
849 // a little bit of hysteresis to detect end of
850 ++ num_moved;
851 // Shift by maximum 2mm.
852 double nudge_dist = std::min(std::max(0., collision_sphere.last_collision_depth + collision_extra_gap), max_nudge_collision_avoidance);
853 Vec2d nudge_vector = (to_2d(collision_sphere.position) - to_2d(collision_sphere.last_collision)).cast<double>().normalized() * nudge_dist;
854 collision_sphere.position.head<2>() += (nudge_vector * nudge_dist).cast<float>();
855 }
856 // Laplacian smoothing
857 Vec2d avg{ 0, 0 };
858 //const SupportElements &above = move_bounds[collision_sphere.element.state.layer_idx + 1];
859 const size_t offset_above = linear_data_layers[collision_sphere.element.state.layer_idx + 1];
860 double weight = 0.;
861 for (auto iparent : collision_sphere.element.parents) {
862 double w = collision_sphere.radius;
863 avg += w * to_2d(collision_spheres[offset_above + iparent].prev_position.cast<double>());
864 weight += w;
865 }
866 if (collision_sphere.element_below_id != -1) {
867 const size_t offset_below = linear_data_layers[collision_sphere.element.state.layer_idx - 1];
868 const double w = weight; // support_element_radius(config, move_bounds[element.state.layer_idx - 1][below]);
869 avg += w * to_2d(collision_spheres[offset_below + collision_sphere.element_below_id].prev_position.cast<double>());
870 weight += w;
871 }
872 avg /= weight;
873 static constexpr const double smoothing_factor = 0.5;
874 Vec2d old_pos = to_2d(collision_sphere.position).cast<double>();
875 Vec2d new_pos = (1. - smoothing_factor) * old_pos + smoothing_factor * avg;
876 Vec2d shift = new_pos - old_pos;
877 double nudge_dist_max = shift.norm();
878 // Shift by maximum 1mm, less than the collision avoidance factor.
879 double nudge_dist = std::min(std::max(0., nudge_dist_max), max_nudge_smoothing);
880 collision_sphere.position.head<2>() += (shift.normalized() * nudge_dist).cast<float>();
881
882 throw_on_cancel();
883 }
884 });
885#if 0
886 std::vector<double> stat;
887 for (CollisionSphere& collision_sphere : collision_spheres)
888 if (!collision_sphere.locked)
889 stat.emplace_back(collision_sphere.last_collision_depth);
890 std::sort(stat.begin(), stat.end());
891 printf("iteration: %d, moved: %d, collision depth: min %lf, max %lf, median %lf\n", int(iter), int(num_moved), stat.front(), stat.back(), stat[stat.size() / 2]);
892#endif
893 if (num_moved == 0)
894 break;
895 }
896
897 for (size_t i = 0; i < collision_spheres.size(); ++ i)
898 elements_with_link_down[i].first->state.result_on_layer = scaled<coord_t>(to_2d(collision_spheres[i].position));
899}
std::optional< std::pair< coord_t, std::reference_wrapper< const Polygons > > > get_collision_lower_bound_area(LayerIndex layer_id, coord_t max_radius) const
Definition TreeModelVolumes.cpp:307
LayerIndex layer_idx_floor(const SlicingParameters &slicing_params, const TreeSupportSettings &config, const double z)
Definition TreeSupportCommon.hpp:469
LayerIndex layer_idx_ceil(const SlicingParameters &slicing_params, const TreeSupportSettings &config, const double z)
Definition TreeSupportCommon.hpp:462
long num_iter(const std::array< size_t, N > &idx, size_t gridsz)
Definition BruteforceOptimizer.hpp:13
std::vector< Line > Lines
Definition Line.hpp:17
Eigen::Matrix< float, 3, 1, Eigen::DontAlign > Vec3f
Definition Point.hpp:49
Eigen::Matrix< typename Derived::Scalar, 2, 1, Eigen::DontAlign > to_2d(const Eigen::MatrixBase< Derived > &ptN)
Definition Point.hpp:121
Lines to_lines(const ExPolygon &src)
Definition ExPolygon.hpp:117
void reserve_power_of_2(VectorType &vector, size_t n)
Definition Utils.hpp:179
#define stat
Definition unistd.h:53
unsigned __int32 uint32_t
Definition unistd.h:79

References Slic3r::AABBTreeLines::build_aabb_tree_over_indexed_lines(), Slic3r::empty(), Slic3r::FFFTreeSupport::TreeModelVolumes::get_collision_lower_bound_area(), Slic3r::reserve_power_of_2(), Slic3r::PrintObject::slicing_parameters(), support_element_radius(), and Slic3r::to_lines().

Referenced by organic_draw_branches().

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

◆ polyline_sample_next_point_at_distance()

static std::optional< std::pair< Point, size_t > > Slic3r::FFFTreeSupport::polyline_sample_next_point_at_distance ( const Points polyline,
const Point start_pt,
size_t  start_idx,
double  dist 
)
static
471{
472 const double dist2 = sqr(dist);
473 const auto dist2i = int64_t(dist2);
474 static constexpr const auto eps = scaled<double>(0.01);
475
476 for (size_t i = start_idx + 1; i < polyline.size(); ++ i) {
477 const Point p1 = polyline[i];
478 if ((p1 - start_pt).cast<int64_t>().squaredNorm() >= dist2i) {
479 // The end point is outside the circle with center "start_pt" and radius "dist".
480 const Point p0 = polyline[i - 1];
481 Vec2d v = (p1 - p0).cast<double>();
482 double l2v = v.squaredNorm();
483 if (l2v < sqr(eps)) {
484 // Very short segment.
485 Point c = (p0 + p1) / 2;
486 if (std::abs((start_pt - c).cast<double>().norm() - dist) < eps)
487 return std::pair<Point, size_t>{ c, i - 1 };
488 else
489 continue;
490 }
491 Vec2d p0f = (start_pt - p0).cast<double>();
492 // Foot point of start_pt into v.
493 Vec2d foot_pt = v * (p0f.dot(v) / l2v);
494 // Vector from foot point of "start_pt" to "start_pt".
495 Vec2d xf = p0f - foot_pt;
496 // Squared distance of "start_pt" from the ray (p0, p1).
497 double l2_from_line = xf.squaredNorm();
498 // Squared distance of an intersection point of a circle with center at the foot point.
499 if (double l2_intersection = dist2 - l2_from_line;
500 l2_intersection > - SCALED_EPSILON) {
501 // The ray (p0, p1) touches or intersects a circle centered at "start_pt" with radius "dist".
502 // Distance of the circle intersection point from the foot point.
503 l2_intersection = std::max(l2_intersection, 0.);
504 if ((v - foot_pt).cast<double>().squaredNorm() >= l2_intersection) {
505 // Intersection of the circle with the segment (p0, p1) is on the right side (close to p1) from the foot point.
506 Point p = p0 + (foot_pt + v * sqrt(l2_intersection / l2v)).cast<coord_t>();
507 validate_range(p);
508 return std::pair<Point, size_t>{ p, i - 1 };
509 }
510 }
511 }
512 }
513 return {};
514}

References Slic3r::foot_pt(), SCALED_EPSILON, Slic3r::sqr(), sqrt(), and validate_range().

Referenced by ensure_maximum_distance_polyline().

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

◆ precalculate()

static LayerIndex Slic3r::FFFTreeSupport::precalculate ( const Print print,
const std::vector< Polygons > &  overhangs,
const TreeSupportSettings config,
const std::vector< size_t > &  object_ids,
TreeModelVolumes volumes,
std::function< void()>  throw_on_cancel 
)
static

Precalculates all avoidances, that could be required.

Parameters
storage[in]Background storage to access meshes.
currently_processing_meshes[in]Indexes of all meshes that are processed in this iteration
327{
328 // calculate top most layer that is relevant for support
329 LayerIndex max_layer = 0;
330 for (size_t object_id : object_ids) {
331 const PrintObject &print_object = *print.get_object(object_id);
332 const int num_raft_layers = int(config.raft_layers.size());
333 const int num_layers = int(print_object.layer_count()) + num_raft_layers;
334 int max_support_layer_id = 0;
335 for (int layer_id = std::max<int>(num_raft_layers, 1); layer_id < num_layers; ++ layer_id)
336 if (! overhangs[layer_id].empty())
337 max_support_layer_id = layer_id;
338 max_layer = std::max(max_support_layer_id - int(config.z_distance_top_layers), 0);
339 }
340 if (max_layer > 0)
341 // The actual precalculation happens in TreeModelVolumes.
342 volumes.precalculate(*print.get_object(object_ids.front()), max_layer, throw_on_cancel);
343 return max_layer;
344}
void precalculate(const PrintObject &print_object, const coord_t max_layer, std::function< void()> throw_on_cancel)
Precalculate avoidances and collisions up to max_layer.
Definition TreeModelVolumes.cpp:156

References Slic3r::empty(), Slic3r::Print::get_object(), Slic3r::PrintObject::layer_count(), Slic3r::FFFTreeSupport::TreeModelVolumes::precalculate(), Slic3r::FFFTreeSupport::TreeSupportSettings::raft_layers, and Slic3r::FFFTreeSupport::TreeSupportSettings::z_distance_top_layers.

Referenced by generate_support_areas().

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

◆ remove_deleted_elements()

static void Slic3r::FFFTreeSupport::remove_deleted_elements ( std::vector< SupportElements > &  move_bounds)
static
2601{
2602 std::vector<int32_t> map_parents;
2603 std::vector<int32_t> map_current;
2604 for (LayerIndex layer_idx = LayerIndex(move_bounds.size()) - 1; layer_idx >= 0; -- layer_idx) {
2605 SupportElements &layer = move_bounds[layer_idx];
2606 map_current.clear();
2607 for (int32_t i = 0; i < int32_t(layer.size());) {
2608 SupportElement &element = layer[i];
2609 if (element.state.deleted) {
2610 if (map_current.empty()) {
2611 // Initialize with identity map.
2612 map_current.assign(layer.size(), 0);
2613 std::iota(map_current.begin(), map_current.end(), 0);
2614 }
2615 // Delete all "deleted" elements from the end of the layer vector.
2616 while (i < int32_t(layer.size()) && layer.back().state.deleted) {
2617 layer.pop_back();
2618 // Mark as deleted in the map.
2619 map_current[layer.size()] = -1;
2620 }
2621 assert(i == layer.size() || i + 1 < layer.size());
2622 if (i + 1 < int32_t(layer.size())) {
2623 element = std::move(layer.back());
2624 layer.pop_back();
2625 // Mark the current element as deleted.
2626 map_current[i] = -1;
2627 // Mark the moved element as moved to index i.
2628 map_current[layer.size()] = i;
2629 }
2630 } else {
2631 // Current element is not deleted. Update its parent indices.
2632 if (! map_parents.empty())
2633 for (int32_t &parent_idx : element.parents)
2634 parent_idx = map_parents[parent_idx];
2635 ++ i;
2636 }
2637 }
2638 std::swap(map_current, map_parents);
2639 }
2640}
bool deleted
Definition TreeSupport.hpp:134

References Slic3r::FFFTreeSupport::SupportElementStateBits::deleted, Slic3r::FFFTreeSupport::SupportElement::parents, remove_deleted_elements(), and Slic3r::FFFTreeSupport::SupportElement::state.

Referenced by create_nodes_from_area(), and remove_deleted_elements().

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

◆ safe_offset_inc()

static Polygons Slic3r::FFFTreeSupport::safe_offset_inc ( const Polygons me,
coord_t  distance,
const Polygons collision,
coord_t  safe_step_size,
coord_t  last_step_offset_without_check,
size_t  min_amount_offset 
)
static

Offsets (increases the area of) a polygons object in multiple steps to ensure that it does not lag through over a given obstacle.

Parameters
me[in]Polygons object that has to be offset.
distance[in]The distance by which me should be offset. Expects values >=0.
collision[in]The area representing obstacles.
last_step_offset_without_check[in]The most it is allowed to offset in one step.
min_amount_offset[in]How many steps have to be done at least. As this uses round offset this increases the amount of vertices, which may be required if Polygons get very small. Required as arcTolerance is not exposed in offset, which should result with a similar result.
Returns
The resulting Polygons object.
764{
765 bool do_final_difference = last_step_offset_without_check == 0;
766 Polygons ret = safe_union(me); // ensure sane input
767
768 // Trim the collision polygons with the region of interest for diff() efficiency.
769 Polygons collision_trimmed_buffer;
770 auto collision_trimmed = [&collision_trimmed_buffer, &collision, &ret, distance]() -> const Polygons& {
771 if (collision_trimmed_buffer.empty() && ! collision.empty())
772 collision_trimmed_buffer = ClipperUtils::clip_clipper_polygons_with_subject_bbox(collision, get_extents(ret).inflated(std::max(0, distance) + SCALED_EPSILON));
773 return collision_trimmed_buffer;
774 };
775
776 if (distance == 0)
777 return do_final_difference ? diff(ret, collision_trimmed()) : union_(ret);
778 if (safe_step_size < 0 || last_step_offset_without_check < 0) {
779 BOOST_LOG_TRIVIAL(error) << "Offset increase got invalid parameter!";
780 tree_supports_show_error("Negative offset distance... How did you manage this ?"sv, true);
781 return do_final_difference ? diff(ret, collision_trimmed()) : union_(ret);
782 }
783
784 coord_t step_size = safe_step_size;
785 int steps = distance > last_step_offset_without_check ? (distance - last_step_offset_without_check) / step_size : 0;
786 if (distance - steps * step_size > last_step_offset_without_check) {
787 if ((steps + 1) * step_size <= distance)
788 // This will be the case when last_step_offset_without_check >= safe_step_size
789 ++ steps;
790 else
791 do_final_difference = true;
792 }
793 if (steps + (distance < last_step_offset_without_check || (distance % step_size) != 0) < int(min_amount_offset) && min_amount_offset > 1) {
794 // yes one can add a bool as the standard specifies that a result from compare operators has to be 0 or 1
795 // reduce the stepsize to ensure it is offset the required amount of times
796 step_size = distance / min_amount_offset;
797 if (step_size >= safe_step_size) {
798 // effectivly reduce last_step_offset_without_check
799 step_size = safe_step_size;
800 steps = min_amount_offset;
801 } else
802 steps = distance / step_size;
803 }
804 // offset in steps
805 for (int i = 0; i < steps; ++ i) {
806 ret = diff(offset(ret, step_size, ClipperLib::jtRound, scaled<float>(0.01)), collision_trimmed());
807 // ensure that if many offsets are done the performance does not suffer extremely by the new vertices of jtRound.
808 if (i % 10 == 7)
809 ret = polygons_simplify(ret, scaled<double>(0.015), polygons_strictly_simple);
810 }
811 // offset the remainder
812 float last_offset = distance - steps * step_size;
813 if (last_offset > SCALED_EPSILON)
814 ret = offset(ret, distance - steps * step_size, ClipperLib::jtRound, scaled<float>(0.01));
815 ret = polygons_simplify(ret, scaled<double>(0.015), polygons_strictly_simple);
816
817 if (do_final_difference)
818 ret = diff(ret, collision_trimmed());
819 return union_(ret);
820}
@ jtRound
Definition clipper.hpp:138
Polygons polygons_simplify(Polygons &&source_polygons, double tolerance, bool strictly_simple)
Definition Polygon.cpp:595
double distance(const P &p1, const P &p2)
Definition geometry_traits.hpp:329

References Slic3r::ClipperUtils::clip_clipper_polygons_with_subject_bbox(), Slic3r::diff(), error, Slic3r::get_extents(), Slic3r::BoundingBox::inflated(), ClipperLib::jtRound, Slic3r::offset(), Slic3r::polygons_simplify(), polygons_strictly_simple, safe_union(), SCALED_EPSILON, tree_supports_show_error(), and Slic3r::union_().

Referenced by increase_single_area(), and merge_influence_areas_two_elements().

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

◆ safe_union()

static Polygons Slic3r::FFFTreeSupport::safe_union ( const Polygons  first,
const Polygons  second = {} 
)
static

Unions two Polygons. Ensures that if the input is non empty that the output also will be non empty.

Parameters
first[in]The first Polygon.
second[in]The second Polygon.
Returns
The union of both Polygons
720 {})
721{
722 // unionPolygons can slowly remove Polygons under certain circumstances, because of rounding issues (Polygons that have a thin area).
723 // This does not cause a problem when actually using it on large areas, but as influence areas (representing centerpoints) can be very thin, this does occur so this ugly workaround is needed
724 // Here is an example of a Polygons object that will loose vertices when unioning, and will be gone after a few times unionPolygons was called:
725 /*
726 Polygons example;
727 Polygon exampleInner;
728 exampleInner.add(Point(120410,83599));//A
729 exampleInner.add(Point(120384,83643));//B
730 exampleInner.add(Point(120399,83618));//C
731 exampleInner.add(Point(120414,83591));//D
732 exampleInner.add(Point(120423,83570));//E
733 exampleInner.add(Point(120419,83580));//F
734 example.add(exampleInner);
735 for(int i=0;i<10;i++){
736 log("Iteration %d Example area: %f\n",i,area(example));
737 example=example.unionPolygons();
738 }
739*/
740
741 Polygons result;
742 if (! first.empty() || ! second.empty()) {
743 result = union_(first, second);
744 if (result.empty()) {
745 BOOST_LOG_TRIVIAL(debug) << "Caught an area destroying union, enlarging areas a bit.";
746 // just take the few lines we have, and offset them a tiny bit. Needs to be offsetPolylines, as offset may aleady have problems with the area.
747 result = union_(offset(to_polylines(first), scaled<float>(0.002), jtMiter, 1.2), offset(to_polylines(second), scaled<float>(0.002), jtMiter, 1.2));
748 }
749 }
750
751 return result;
752}

Referenced by create_layer_pathing(), increase_single_area(), merge_influence_areas_two_elements(), and safe_offset_inc().

+ Here is the caller graph for this function:

◆ sample_overhang_area()

void Slic3r::FFFTreeSupport::sample_overhang_area ( Polygons &&  overhang_area,
const bool  large_horizontal_roof,
const size_t  layer_idx,
const size_t  num_support_roof_layers,
const coord_t  connect_length,
const TreeSupportMeshGroupSettings mesh_group_settings,
RichInterfacePlacer interface_placer 
)
1078{
1079 // Assumption is that roof will support roof further up to avoid a lot of unnecessary branches. Each layer down it is checked whether the roof area
1080 // is still large enough to be a roof and aborted as soon as it is not. This part was already reworked a few times, and there could be an argument
1081 // made to change it again if there are actual issues encountered regarding supporting roofs.
1082 // Main problem is that some patterns change each layer, so just calculating points and checking if they are still valid an layer below is not useful,
1083 // as the pattern may be different one layer below. Same with calculating which points are now no longer being generated as result from
1084 // a decreasing roof, as there is no guarantee that a line will be above these points. Implementing a separate roof support behavior
1085 // for each pattern harms maintainability as it very well could be >100 LOC
1086 auto generate_roof_lines = [&interface_placer, &mesh_group_settings](const Polygons &area, LayerIndex layer_idx) -> Polylines {
1087 return generate_support_infill_lines(area, interface_placer.support_parameters, true, layer_idx, mesh_group_settings.support_roof_line_distance);
1088 };
1089
1090 LineInformations overhang_lines;
1091 // Track how many top contact / interface layers were already generated.
1092 size_t dtt_roof = 0;
1093 size_t layer_generation_dtt = 0;
1094
1095 if (large_horizontal_roof) {
1096 assert(num_support_roof_layers > 0);
1097 // Sometimes roofs could be empty as the pattern does not generate lines if the area is narrow enough (i am looking at you, concentric infill).
1098 // To catch these cases the added roofs are saved to be evaluated later.
1099 std::vector<Polygons> added_roofs(num_support_roof_layers);
1100 Polygons last_overhang = overhang_area;
1101 for (dtt_roof = 0; dtt_roof < num_support_roof_layers && layer_idx - dtt_roof >= 1; ++ dtt_roof) {
1102 // here the roof is handled. If roof can not be added the branches will try to not move instead
1103 Polygons forbidden_next;
1104 {
1105 const bool min_xy_dist = interface_placer.config.xy_distance > interface_placer.config.xy_min_distance;
1106 const Polygons &forbidden_next_raw = interface_placer.config.support_rests_on_model ?
1107 interface_placer.volumes.getCollision(interface_placer.config.getRadius(0), layer_idx - (dtt_roof + 1), min_xy_dist) :
1108 interface_placer.volumes.getAvoidance(interface_placer.config.getRadius(0), layer_idx - (dtt_roof + 1), TreeModelVolumes::AvoidanceType::Fast, false, min_xy_dist);
1109 // prevent rounding errors down the line
1110 //FIXME maybe use SafetyOffset::Yes at the following diff() instead?
1111 forbidden_next = offset(union_ex(forbidden_next_raw), scaled<float>(0.005), jtMiter, 1.2);
1112 }
1113 Polygons overhang_area_next = diff(overhang_area, forbidden_next);
1114 if (area(overhang_area_next) < mesh_group_settings.minimum_roof_area) {
1115 // Next layer down the roof area would be to small so we have to insert our roof support here.
1116 if (dtt_roof > 0) {
1117 size_t dtt_before = dtt_roof - 1;
1118 // Produce support head points supporting an interface layer: First produce the interface lines, then sample them.
1119 overhang_lines = split_lines(
1120 convert_lines_to_internal(interface_placer.volumes, interface_placer.config,
1121 ensure_maximum_distance_polyline(generate_roof_lines(last_overhang, layer_idx - dtt_before), connect_length, 1), layer_idx - dtt_before),
1122 [&interface_placer, layer_idx, dtt_before](const std::pair<Point, LineStatus> &p)
1123 { return evaluate_point_for_next_layer_function(interface_placer.volumes, interface_placer.config, layer_idx - dtt_before, p); })
1124 .first;
1125 }
1126 break;
1127 }
1128 added_roofs[dtt_roof] = overhang_area;
1129 last_overhang = std::move(overhang_area);
1130 overhang_area = std::move(overhang_area_next);
1131 }
1132
1133 layer_generation_dtt = std::max(dtt_roof, size_t(1)) - 1; // 1 inside max and -1 outside to avoid underflow. layer_generation_dtt=dtt_roof-1 if dtt_roof!=0;
1134 // if the roof should be valid, check that the area does generate lines. This is NOT guaranteed.
1135 if (overhang_lines.empty() && dtt_roof != 0 && generate_roof_lines(overhang_area, layer_idx - layer_generation_dtt).empty())
1136 for (size_t idx = 0; idx < dtt_roof; idx++) {
1137 // check for every roof area that it has resulting lines. Remember idx 1 means the 2. layer of roof => higher idx == lower layer
1138 if (generate_roof_lines(added_roofs[idx], layer_idx - idx).empty()) {
1139 dtt_roof = idx;
1140 layer_generation_dtt = std::max(dtt_roof, size_t(1)) - 1;
1141 break;
1142 }
1143 }
1144 added_roofs.erase(added_roofs.begin() + dtt_roof, added_roofs.end());
1145 interface_placer.add_roofs(std::move(added_roofs), layer_idx);
1146 }
1147
1148 if (overhang_lines.empty()) {
1149 // support_line_width to form a line here as otherwise most will be unsupported. Technically this violates branch distance, but not only is this the only reasonable choice,
1150 // but it ensures consistant behaviour as some infill patterns generate each line segment as its own polyline part causing a similar line forming behaviour.
1151 // This is not doen when a roof is above as the roof will support the model and the trees only need to support the roof
1152 bool supports_roof = dtt_roof > 0;
1153 bool continuous_tips = ! supports_roof && large_horizontal_roof;
1155 generate_support_infill_lines(overhang_area, interface_placer.support_parameters, supports_roof, layer_idx - layer_generation_dtt,
1156 supports_roof ? mesh_group_settings.support_roof_line_distance : mesh_group_settings.support_tree_branch_distance),
1157 continuous_tips ? interface_placer.config.min_radius / 2 : connect_length, 1);
1158 size_t point_count = 0;
1159 for (const Polyline &poly : polylines)
1160 point_count += poly.size();
1161 const size_t min_support_points = std::max(coord_t(1), std::min(coord_t(3), coord_t(total_length(overhang_area) / connect_length)));
1162 if (point_count <= min_support_points) {
1163 // add the outer wall (of the overhang) to ensure it is correct supported instead. Try placing the support points in a way that they fully support the outer wall, instead of just the with half of the the support line width.
1164 // I assume that even small overhangs are over one line width wide, so lets try to place the support points in a way that the full support area generated from them
1165 // will support the overhang (if this is not done it may only be half). This WILL NOT be the case when supporting an angle of about < 60� so there is a fallback,
1166 // as some support is better than none.
1167 Polygons reduced_overhang_area = offset(union_ex(overhang_area), - interface_placer.config.support_line_width / 2.2, jtMiter, 1.2);
1170 ! reduced_overhang_area.empty() &&
1171 area(offset(diff_ex(overhang_area, reduced_overhang_area), std::max(interface_placer.config.support_line_width, connect_length), jtMiter, 1.2)) < sqr(scaled<double>(0.001)) ?
1172 reduced_overhang_area :
1173 overhang_area),
1174 connect_length, min_support_points);
1175 }
1176 overhang_lines = convert_lines_to_internal(interface_placer.volumes, interface_placer.config, polylines, layer_idx - dtt_roof);
1177 }
1178
1179 assert(dtt_roof <= layer_idx);
1180 if (dtt_roof >= layer_idx && large_horizontal_roof)
1181 // Reached buildplate when generating contact, interface and base interface layers.
1182 interface_placer.add_roof_build_plate(std::move(overhang_area), dtt_roof);
1183 else {
1184 // normal trees have to be generated
1185 const bool roof_enabled = num_support_roof_layers > 0;
1186 interface_placer.add_points_along_lines(
1187 // Sample along these lines
1188 overhang_lines,
1189 // First layer index to insert the tree tips or interfaces.
1190 layer_idx - dtt_roof,
1191 // Remaining roof tip layers.
1192 interface_placer.force_tip_to_roof ? num_support_roof_layers - dtt_roof : 0,
1193 // Supports roof already? How many roof layers were already produced above these tips?
1194 dtt_roof,
1195 // Don't move until the following distance to top is reached.
1196 roof_enabled ? num_support_roof_layers - dtt_roof : 0);
1197 }
1198}
void add_roofs(std::vector< Polygons > &&new_roofs, const size_t insert_layer_idx)
Definition TreeSupportCommon.hpp:538
const TreeSupportSettings & config
Definition TreeSupportCommon.hpp:533
void add_roof_build_plate(Polygons &&overhang_areas, size_t dtt_roof)
Definition TreeSupportCommon.hpp:555
void add_points_along_lines(LineInformations lines, LayerIndex insert_layer_idx, size_t roof_tip_layers, size_t supports_roof_layers, size_t dont_move_until)
Definition TreeSupport.cpp:844
const bool force_tip_to_roof
Definition TreeSupport.cpp:839
const TreeModelVolumes & volumes
Definition TreeSupport.cpp:837
static Polylines ensure_maximum_distance_polyline(const Polylines &input, double distance, size_t min_points)
Eensures that every line segment is about distance in length. The resulting lines may differ from the...
Definition TreeSupport.cpp:524
static LineInformations convert_lines_to_internal(const TreeModelVolumes &volumes, const TreeSupportSettings &config, const Polylines &polylines, LayerIndex layer_idx)
Converts a Polygons object representing a line into the internal format.
Definition TreeSupport.cpp:354
static Polylines generate_support_infill_lines(const Polygons &polygon, const SupportParameters &support_params, bool roof, LayerIndex layer_idx, coord_t support_infill_distance)
Returns Polylines representing the (infill) lines that will result in slicing the given area.
Definition TreeSupport.cpp:628
Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:726
double total_length(const Polygons &polylines)
Definition Polygon.hpp:112
coord_t support_roof_line_distance
Definition TreeSupportCommon.hpp:132
double minimum_roof_area
Definition TreeSupportCommon.hpp:107

References Slic3r::FFFTreeSupport::RichInterfacePlacer::add_points_along_lines(), Slic3r::FFFTreeSupport::InterfacePlacer::add_roof_build_plate(), Slic3r::FFFTreeSupport::InterfacePlacer::add_roofs(), Slic3r::area(), Slic3r::FFFTreeSupport::InterfacePlacer::config, convert_lines_to_internal(), Slic3r::diff(), Slic3r::diff_ex(), Slic3r::empty(), ensure_maximum_distance_polyline(), evaluate_point_for_next_layer_function(), Slic3r::FFFTreeSupport::RichInterfacePlacer::force_tip_to_roof, generate_support_infill_lines(), Slic3r::FFFTreeSupport::TreeModelVolumes::getAvoidance(), Slic3r::FFFTreeSupport::TreeModelVolumes::getCollision(), Slic3r::FFFTreeSupport::TreeSupportSettings::getRadius(), Slic3r::FFFTreeSupport::TreeSupportSettings::min_radius, Slic3r::FFFTreeSupport::TreeSupportMeshGroupSettings::minimum_roof_area, Slic3r::offset(), sample_overhang_area(), split_lines(), Slic3r::sqr(), Slic3r::FFFTreeSupport::TreeSupportSettings::support_line_width, Slic3r::FFFTreeSupport::InterfacePlacer::support_parameters, Slic3r::FFFTreeSupport::TreeSupportSettings::support_rests_on_model, Slic3r::FFFTreeSupport::TreeSupportMeshGroupSettings::support_roof_line_distance, Slic3r::FFFTreeSupport::TreeSupportMeshGroupSettings::support_tree_branch_distance, Slic3r::to_polylines(), Slic3r::total_length(), Slic3r::union_ex(), Slic3r::FFFTreeSupport::RichInterfacePlacer::volumes, Slic3r::FFFTreeSupport::TreeSupportSettings::xy_distance, and Slic3r::FFFTreeSupport::TreeSupportSettings::xy_min_distance.

Referenced by sample_overhang_area().

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

◆ set_points_on_areas()

static void Slic3r::FFFTreeSupport::set_points_on_areas ( const SupportElement elem,
SupportElements layer_above 
)
static

Sets the result_on_layer for all parents based on the SupportElement supplied.

Parameters
elem[in]The SupportElements, which parent's position should be determined.
2509{
2510 assert(!elem.state.deleted);
2511 assert(layer_above != nullptr || elem.parents.empty());
2512
2513 // Based on the branch center point of the current layer, the point on the next (further up) layer is calculated.
2514 if (! elem.state.result_on_layer_is_set()) {
2515 BOOST_LOG_TRIVIAL(error) << "Uninitialized support element";
2516 tree_supports_show_error("Uninitialized support element. A branch may be missing.\n"sv, true);
2517 return;
2518 }
2519
2520 if (layer_above)
2521 for (int32_t next_elem_idx : elem.parents) {
2522 assert(next_elem_idx >= 0);
2523 SupportElement &next_elem = (*layer_above)[next_elem_idx];
2524 assert(! next_elem.state.deleted);
2525 // if the value was set somewhere else it it kept. This happens when a branch tries not to move after being unable to create a roof.
2526 if (! next_elem.state.result_on_layer_is_set()) {
2527 // Move inside has edgecases (see tests) so DONT use Polygons.inside to confirm correct move, Error with distance 0 is <= 1
2528 // it is not required to check if how far this move moved a point as is can be larger than maximum_movement_distance.
2529 // While this seems like a problem it may for example occur after merges.
2530 next_elem.state.result_on_layer = move_inside_if_outside(next_elem.influence_area, elem.state.result_on_layer);
2531 // do not call recursive because then amount of layers would be restricted by the stack size
2532 }
2533 // Mark the parent element as accessed from a valid child element.
2534 next_elem.state.marked = true;
2535 }
2536}

References Slic3r::FFFTreeSupport::SupportElementStateBits::deleted, error, Slic3r::FFFTreeSupport::SupportElement::influence_area, Slic3r::FFFTreeSupport::SupportElementStateBits::marked, move_inside_if_outside(), Slic3r::FFFTreeSupport::SupportElement::parents, Slic3r::FFFTreeSupport::SupportElementState::result_on_layer, Slic3r::FFFTreeSupport::SupportElementState::result_on_layer_is_set(), set_points_on_areas(), Slic3r::FFFTreeSupport::SupportElement::state, and tree_supports_show_error().

Referenced by create_nodes_from_area(), and set_points_on_areas().

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

◆ set_to_model_contact_simple()

static void Slic3r::FFFTreeSupport::set_to_model_contact_simple ( SupportElement elem)
static
2539{
2541 elem.state.result_on_layer = best;
2542 BOOST_LOG_TRIVIAL(debug) << "Added NON gracious Support On Model Point (" << best.x() << "," << best.y() << "). The current layer is " << elem.state.layer_idx;
2543}

References Slic3r::FFFTreeSupport::SupportElement::influence_area, Slic3r::FFFTreeSupport::SupportElementState::layer_idx, move_inside_if_outside(), Slic3r::FFFTreeSupport::SupportElementState::next_position, Slic3r::FFFTreeSupport::SupportElementState::result_on_layer, set_to_model_contact_simple(), and Slic3r::FFFTreeSupport::SupportElement::state.

Referenced by create_nodes_from_area(), set_to_model_contact_simple(), and set_to_model_contact_to_model_gracious().

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

◆ set_to_model_contact_to_model_gracious()

static void Slic3r::FFFTreeSupport::set_to_model_contact_to_model_gracious ( const TreeModelVolumes volumes,
const TreeSupportSettings config,
std::vector< SupportElements > &  move_bounds,
SupportElement first_elem,
std::function< void()>  throw_on_cancel 
)
static

Get the best point to connect to the model and set the result_on_layer of the relevant SupportElement accordingly.

Parameters
move_bounds[in,out]All currently existing influence areas
first_elem[in,out]SupportElement that did not have its result_on_layer set meaning that it does not have a child element.
layer_idx[in]The current layer.
2558{
2559 SupportElement *last_successfull_layer = nullptr;
2560
2561 // check for every layer upwards, up to the point where this influence area was created (either by initial insert or merge) if the branch could be placed on it, and highest up layer index.
2562 {
2563 SupportElement *elem = &first_elem;
2564 for (LayerIndex layer_check = elem->state.layer_idx;
2565 ! intersection(elem->influence_area, volumes.getPlaceableAreas(support_element_collision_radius(config, elem->state), layer_check, throw_on_cancel)).empty();
2566 elem = &move_bounds[++ layer_check][elem->parents.front()]) {
2567 assert(elem->state.layer_idx == layer_check);
2568 assert(! elem->state.deleted);
2569 assert(elem->state.to_model_gracious);
2570 last_successfull_layer = elem;
2571 if (elem->parents.size() != 1)
2572 // Reached merge point.
2573 break;
2574 }
2575 }
2576
2577 // Could not find valid placement, even though it should exist => error handling
2578 if (last_successfull_layer == nullptr) {
2579 BOOST_LOG_TRIVIAL(warning) << "No valid placement found for to model gracious element on layer " << first_elem.state.layer_idx;
2580 tree_supports_show_error("Could not fine valid placement on model! Just placing it down anyway. Could cause floating branches."sv, true);
2581 first_elem.state.to_model_gracious = false;
2582 set_to_model_contact_simple(first_elem);
2583 } else {
2584 // Found a gracious area above first_elem. Remove all below last_successfull_layer.
2585 {
2586 LayerIndex parent_layer_idx = first_elem.state.layer_idx;
2587 for (SupportElement *elem = &first_elem; elem != last_successfull_layer; elem = &move_bounds[++ parent_layer_idx][elem->parents.front()]) {
2588 assert(! elem->state.deleted);
2589 elem->state.deleted = true;
2590 }
2591 }
2592 // Guess a point inside the influence area, in which the branch will be placed in.
2593 const Point best = move_inside_if_outside(last_successfull_layer->influence_area, last_successfull_layer->state.next_position);
2594 last_successfull_layer->state.result_on_layer = best;
2595 BOOST_LOG_TRIVIAL(debug) << "Added gracious Support On Model Point (" << best.x() << "," << best.y() << "). The current layer is " << last_successfull_layer;
2596 }
2597}
const Polygons & getPlaceableAreas(coord_t radius, LayerIndex layer_idx, std::function< void()> throw_on_cancel) const
Provides the area represents all areas on the model where the branch does completely fit on the given...
Definition TreeModelVolumes.cpp:356

References Slic3r::FFFTreeSupport::SupportElementStateBits::deleted, Slic3r::FFFTreeSupport::TreeModelVolumes::getPlaceableAreas(), Slic3r::FFFTreeSupport::SupportElement::influence_area, Slic3r::intersection(), Slic3r::FFFTreeSupport::SupportElementState::layer_idx, move_inside_if_outside(), Slic3r::FFFTreeSupport::SupportElementState::next_position, Slic3r::FFFTreeSupport::SupportElement::parents, Slic3r::FFFTreeSupport::SupportElementState::result_on_layer, set_to_model_contact_simple(), set_to_model_contact_to_model_gracious(), Slic3r::FFFTreeSupport::SupportElement::state, support_element_collision_radius(), Slic3r::FFFTreeSupport::SupportElementStateBits::to_model_gracious, and tree_supports_show_error().

Referenced by create_nodes_from_area(), and set_to_model_contact_to_model_gracious().

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

◆ smooth_branch_areas()

static void Slic3r::FFFTreeSupport::smooth_branch_areas ( const TreeSupportSettings config,
std::vector< SupportElements > &  move_bounds,
std::vector< DrawArea > &  linear_data,
const std::vector< size_t > &  linear_data_layers,
std::function< void()>  throw_on_cancel 
)
static

Applies some smoothing to the outer wall, intended to smooth out sudden jumps as they can happen when a branch moves though a hole.

Parameters
layer_tree_polygons[in,out]Resulting branch areas with the layerindex they appear on.
2919{
2920#ifdef SLIC3R_TREESUPPORTS_PROGRESS
2921 double progress_total = TREE_PROGRESS_PRECALC_AVO + TREE_PROGRESS_PRECALC_COLL + TREE_PROGRESS_GENERATE_NODES + TREE_PROGRESS_AREA_CALC + TREE_PROGRESS_GENERATE_BRANCH_AREAS;
2922#endif // SLIC3R_TREESUPPORTS_PROGRESS
2923
2924 const coord_t max_radius_change_per_layer = 1 + config.support_line_width / 2; // this is the upper limit a radius may change per layer. +1 to avoid rounding errors
2925
2926 // smooth upwards
2927 for (LayerIndex layer_idx = 0; layer_idx < LayerIndex(move_bounds.size()) - 1; ++ layer_idx) {
2928 const size_t processing_base = linear_data_layers[layer_idx];
2929 const size_t processing_base_above = linear_data_layers[layer_idx + 1];
2930 const SupportElements &layer_above = move_bounds[layer_idx + 1];
2931 tbb::parallel_for(tbb::blocked_range<size_t>(0, processing_base_above - processing_base),
2932 [&](const tbb::blocked_range<size_t> &range) {
2933 for (size_t processing_idx = range.begin(); processing_idx < range.end(); ++ processing_idx) {
2934 DrawArea &draw_area = linear_data[processing_base + processing_idx];
2935 assert(draw_area.element->state.layer_idx == layer_idx);
2936 double max_outer_wall_distance = 0;
2937 bool do_something = false;
2938 for (int32_t parent_idx : draw_area.element->parents) {
2939 const SupportElement &parent = layer_above[parent_idx];
2940 assert(parent.state.layer_idx == layer_idx + 1);
2941 if (support_element_radius(config, parent) != support_element_collision_radius(config, parent)) {
2942 do_something = true;
2943 max_outer_wall_distance = std::max(max_outer_wall_distance,
2944 (draw_area.element->state.result_on_layer - parent.state.result_on_layer).cast<double>().norm() - (support_element_radius(config, *draw_area.element) - support_element_radius(config, parent)));
2945 }
2946 }
2947 max_outer_wall_distance += max_radius_change_per_layer; // As this change is a bit larger than what usually appears, lost radius can be slowly reclaimed over the layers.
2948 if (do_something) {
2949 assert(contains(draw_area.polygons, draw_area.element->state.result_on_layer));
2950 Polygons max_allowed_area = offset(draw_area.polygons, float(max_outer_wall_distance), jtMiter, 1.2);
2951 for (int32_t parent_idx : draw_area.element->parents) {
2952 const SupportElement &parent = layer_above[parent_idx];
2953#ifndef NDEBUG
2954 assert(parent.state.layer_idx == layer_idx + 1);
2955 assert(contains(linear_data[processing_base_above + parent_idx].polygons, parent.state.result_on_layer));
2956 double radius_increase = support_element_radius(config, *draw_area.element) - support_element_radius(config, parent);
2957 assert(radius_increase >= 0);
2958 double shift = (draw_area.element->state.result_on_layer - parent.state.result_on_layer).cast<double>().norm();
2959 assert(shift < radius_increase + 2. * config.maximum_move_distance_slow);
2960#endif // NDEBUG
2961 if (support_element_radius(config, parent) != support_element_collision_radius(config, parent)) {
2962 // No other element on this layer than the current one may be connected to &parent,
2963 // thus it is safe to update parent's DrawArea directly.
2964 Polygons &dst = linear_data[processing_base_above + parent_idx].polygons;
2965// Polygons orig = dst;
2966 if (! dst.empty()) {
2967 dst = intersection(dst, max_allowed_area);
2968#if 0
2969 if (dst.empty()) {
2970 static int irun = 0;
2971 SVG::export_expolygons(debug_out_path("treesupport-extrude_areas-smooth-error-%d.svg", irun ++),
2972 { { { union_ex(max_allowed_area) }, { "max_allowed_area", "yellow", 0.5f } },
2973 { { union_ex(orig) }, { "orig", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
2974 ::MessageBoxA(nullptr, "TreeSupport smoothing bug", "Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING);
2975 }
2976#endif
2977 }
2978 }
2979 }
2980 }
2981 throw_on_cancel();
2982 }
2983 });
2984 }
2985
2986#ifdef SLIC3R_TREESUPPORTS_PROGRESS
2987 progress_total += TREE_PROGRESS_SMOOTH_BRANCH_AREAS / 2;
2988 Progress::messageProgress(Progress::Stage::SUPPORT, progress_total * m_progress_multiplier + m_progress_offset, TREE_PROGRESS_TOTAL); // It is just assumed that both smoothing loops together are one third of the time spent in this function. This was guessed. As the whole function is only 10%, and the smoothing is hard to predict a progress report in the loop may be not useful.
2989#endif
2990
2991 // smooth downwards
2992 for (auto& element : move_bounds.back())
2993 element.state.marked = false;
2994 for (int layer_idx = int(move_bounds.size()) - 2; layer_idx >= 0; -- layer_idx) {
2995 const size_t processing_base = linear_data_layers[layer_idx];
2996 const size_t processing_base_above = linear_data_layers[layer_idx + 1];
2997 const SupportElements &layer_above = move_bounds[layer_idx + 1];
2998 tbb::parallel_for(tbb::blocked_range<size_t>(0, processing_base_above - processing_base),
2999 [&](const tbb::blocked_range<size_t> &range) {
3000 for (size_t processing_idx = range.begin(); processing_idx < range.end(); ++ processing_idx) {
3001 DrawArea &draw_area = linear_data[processing_base + processing_idx];
3002 bool do_something = false;
3003 Polygons max_allowed_area;
3004 for (int32_t parent_idx : draw_area.element->parents) {
3005 const SupportElement &parent = layer_above[parent_idx];
3006 coord_t max_outer_line_increase = max_radius_change_per_layer;
3007 Polygons result = offset(linear_data[processing_base_above + parent_idx].polygons, max_outer_line_increase, jtMiter, 1.2);
3008 Point direction = draw_area.element->state.result_on_layer - parent.state.result_on_layer;
3009 // move the polygons object
3010 for (auto &outer : result)
3011 for (Point& p : outer)
3012 p += direction;
3013 append(max_allowed_area, std::move(result));
3014 do_something = do_something || parent.state.marked || support_element_collision_radius(config, parent) != support_element_radius(config, parent);
3015 }
3016 if (do_something) {
3017 // Trim the current drawing areas with max_allowed_area.
3018 Polygons result = intersection(max_allowed_area, draw_area.polygons);
3019 if (area(result) < area(draw_area.polygons)) {
3020 // Mark parent as modified to propagate down.
3021 draw_area.element->state.marked = true;
3022 draw_area.polygons = std::move(result);
3023 }
3024 }
3025 throw_on_cancel();
3026 }
3027 });
3028 }
3029
3030#ifdef SLIC3R_TREESUPPORTS_PROGRESS
3031 progress_total += TREE_PROGRESS_SMOOTH_BRANCH_AREAS / 2;
3032 Progress::messageProgress(Progress::Stage::SUPPORT, progress_total * m_progress_multiplier + m_progress_offset, TREE_PROGRESS_TOTAL);
3033#endif
3034}
Polygons polygons
Definition TreeSupport.cpp:2772
SupportElement * element
Definition TreeSupport.cpp:2768
Definition TreeSupport.cpp:2766
TPoint< P > back(const P &p)
Definition geometry_traits.hpp:873

References contains(), Slic3r::debug_out_path(), Slic3r::FFFTreeSupport::DrawArea::element, Slic3r::intersection(), Slic3r::FFFTreeSupport::SupportElementState::layer_idx, Slic3r::FFFTreeSupport::SupportElementStateBits::marked, Slic3r::FFFTreeSupport::TreeSupportSettings::maximum_move_distance_slow, Slic3r::FFFTreeSupport::SupportElement::parents, Slic3r::FFFTreeSupport::DrawArea::polygons, Slic3r::range(), Slic3r::FFFTreeSupport::SupportElementState::result_on_layer, smooth_branch_areas(), Slic3r::FFFTreeSupport::SupportElement::state, support_element_collision_radius(), support_element_radius(), Slic3r::FFFTreeSupport::TreeSupportSettings::support_line_width, and Slic3r::union_ex().

Referenced by draw_areas(), and smooth_branch_areas().

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

◆ smooth_trees_inside_influence_areas() [1/2]

void Slic3r::FFFTreeSupport::smooth_trees_inside_influence_areas ( Branch root,
bool  is_root 
)
317{
318 // Smooth the subtree:
319 //
320 // Apply laplacian and bilaplacian smoothing inside a branch,
321 // apply laplacian smoothing only at a bifurcation point.
322 //
323 // Applying a bilaplacian smoothing inside a branch should ensure curvature of the brach to be lower
324 // than the radius at each particular point of the centerline,
325 // while omitting bilaplacian smoothing at bifurcation points will create sharp bifurcations.
326 // Sharp bifurcations have a smaller volume, but just a tiny bit larger surfaces than smooth bifurcations
327 // where each continuation of the trunk satifies the path radius > centerline element radius.
328 const size_t num_iterations = 100;
329 struct StackElement {
330 Branch &branch;
331 size_t idx_up;
332 };
333 std::vector<StackElement> stack;
334
335 auto adjust_position = [](Element &el, Vec2f new_pos) {
336 Point new_pos_scaled = scaled<coord_t>(new_pos);
337 if (! contains(el.influence_area, new_pos_scaled)) {
338 int64_t min_dist = std::numeric_limits<int64_t>::max();
339 Point min_proj_scaled;
340 for (const Polygon& polygon : el.influence_area) {
341 Point proj_scaled = polygon.point_projection(new_pos_scaled);
342 if (int64_t dist = (proj_scaled - new_pos_scaled).cast<int64_t>().squaredNorm(); dist < min_dist) {
343 min_dist = dist;
344 min_proj_scaled = proj_scaled;
345 }
346 }
347 new_pos = unscaled<float>(min_proj_scaled);
348 }
349 el.position.head<2>() = new_pos;
350 };
351
352 for (size_t iter = 0; iter < num_iterations; ++ iter) {
353 // 1) Back-up the current positions.
354 stack.push_back({ root, 0 });
355 while (! stack.empty()) {
356 StackElement &state = stack.back();
357 if (state.idx_up == state.branch.num_up_trunk) {
358 // Process this path.
359 for (auto &el : state.branch.path)
360 el.prev_position = el.position;
361 stack.pop_back();
362 } else {
363 // Open another up node of this branch.
364 stack.push_back({ *state.branch.up[state.idx_up].branch, 0 });
365 ++ state.idx_up;
366 }
367 }
368 // 2) Calculate new position.
369 stack.push_back({ root, 0 });
370 while (! stack.empty()) {
371 StackElement &state = stack.back();
372 if (state.idx_up == state.branch.num_up_trunk) {
373 // Process this path.
374 for (size_t i = 1; i + 1 < state.branch.path.size(); ++ i)
375 if (auto &el = state.branch.path[i]; ! el.locked) {
376 // Laplacian smoothing with 0.5 weight.
377 const Vec3f &p0 = state.branch.path[i - 1].prev_position;
378 const Vec3f &p1 = el.prev_position;
379 const Vec3f &p2 = state.branch.path[i + 1].prev_position;
380 adjust_position(el, 0.5 * p1.head<2>() + 0.25 * (p0.head<2>() + p2.head<2>()));
381#if 0
382 // Only apply bilaplacian smoothing if the current curvature is smaller than el.radius.
383 // Interpolate p0, p1, p2 with a circle.
384 // First project p0, p1, p2 into a common plane.
385 const Vec3f n = (p1 - p0).cross(p2 - p1);
386 const Vec3f y = Vec3f(n.y(), n.x(), 0).normalized();
387 const Vec2f q0{ p0.z(), p0.dot(y) };
388 const Vec2f q1{ p1.z(), p1.dot(y) };
389 const Vec2f q2{ p2.z(), p2.dot(y) };
390 // Interpolate q0, q1, q2 with a circle, calculate its radius.
391 Vec2f b = q1 - q0;
392 Vec2f c = q2 - q0;
393 float lb = b.squaredNorm();
394 float lc = c.squaredNorm();
395 if (float d = b.x() * c.y() - b.y() * c.x(); std::abs(d) > EPSILON) {
396 Vec2f v = lc * b - lb * c;
397 float r2 = 0.25f * v.squaredNorm() / sqr(d);
398 if (r2 )
399 }
400#endif
401 }
402 {
403 // Laplacian smoothing with 0.5 weight, branching point.
404 float weight = 0;
405 Vec2f new_pos = Vec2f::Zero();
406 for (size_t i = 0; i < state.branch.num_up_trunk; ++i) {
407 const Element &el = state.branch.up[i].branch->path.front();
408 new_pos += el.prev_position.head<2>();
409 weight += el.radius;
410 }
411 {
412 const Element &el = state.branch.path[state.branch.path.size() - 2];
413 new_pos += el.prev_position.head<2>();
414 weight *= 2.f;
415 }
416 adjust_position(state.branch.path.back(), 0.5f * state.branch.path.back().prev_position.head<2>() + 0.5f * weight * new_pos);
417 }
418 stack.pop_back();
419 } else {
420 // Open another up node of this branch.
421 stack.push_back({ *state.branch.up[state.idx_up].branch, 0 });
422 ++ state.idx_up;
423 }
424 }
425 }
426 // Also smoothen start of the path.
427 if (Element &first = root.path.front(); ! first.locked) {
428 Element &second = root.path[1];
429 Vec2f new_pos = 0.75f * first.prev_position.head<2>() + 0.25f * second.prev_position.head<2>();
430 if (is_root)
431 // Let the root of the tree float inside its influence area.
432 adjust_position(first, new_pos);
433 else {
434 // Keep the start of a thin branch inside the trunk.
435 const Element &trunk = root.down.branch->path.back();
436 const float rdif = trunk.radius - root.path.front().radius;
437 assert(rdif >= 0);
438 Vec2f vdif = new_pos - trunk.prev_position.head<2>();
439 float ldif = vdif.squaredNorm();
440 if (ldif > sqr(rdif))
441 // Clamp new position.
442 new_pos = trunk.prev_position.head<2>() + vdif * rdif / sqrt(ldif);
443 first.position.head<2>() = new_pos;
444 }
445 }
446}
const Scalar & y
Definition MathFunctions.h:552
Branch * branch
Definition OrganicSupport.cpp:81
Eigen::Matrix< float, 2, 1, Eigen::DontAlign > Vec2f
Definition Point.hpp:48
IGL_INLINE void cross(const double *a, const double *b, double *out)
Definition cross.cpp:11
Bifurcation down
Definition OrganicSupport.cpp:100

References Slic3r::FFFTreeSupport::Bifurcation::branch, Slic3r::contains(), Slic3r::cross(), Slic3r::FFFTreeSupport::Branch::down, EPSILON, Slic3r::FFFTreeSupport::Element::influence_area, Slic3r::FFFTreeSupport::Element::locked, Slic3r::FFFTreeSupport::Branch::path, Slic3r::FFFTreeSupport::Element::position, Slic3r::FFFTreeSupport::Element::prev_position, Slic3r::FFFTreeSupport::Element::radius, Slic3r::sqr(), and sqrt().

Referenced by smooth_trees_inside_influence_areas().

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

◆ smooth_trees_inside_influence_areas() [2/2]

void Slic3r::FFFTreeSupport::smooth_trees_inside_influence_areas ( Forest forest)
449{
450 // Parallel for!
451 for (Tree &tree : forest)
452 smooth_trees_inside_influence_areas(tree.root(), true);
453}
void smooth_trees_inside_influence_areas(Branch &root, bool is_root)
Definition OrganicSupport.cpp:316

References smooth_trees_inside_influence_areas().

+ Here is the call graph for this function:

◆ split_lines()

template<typename EvaluatePointFn >
static std::pair< LineInformations, LineInformations > Slic3r::FFFTreeSupport::split_lines ( const LineInformations lines,
EvaluatePointFn  evaluatePoint 
)
static

Evaluates which points of some lines are not valid one layer below and which are. Assumes all points are valid on the current layer. Validity is evaluated using supplied lambda.

Parameters
lines[in]The lines that have to be evaluated.
evaluatePoint[in]The function used to evaluate the points.
Returns
A pair with which points are still valid in the first slot and which are not in the second slot.
443{
444 // assumes all Points on the current line are valid
445
446 LineInformations keep;
447 LineInformations set_free;
448 for (const std::vector<std::pair<Point, LineStatus>> &line : lines) {
449 bool current_keep = true;
450 LineInformation resulting_line;
451 for (const std::pair<Point, LineStatus> &me : line) {
452 if (evaluatePoint(me) != current_keep) {
453 if (! resulting_line.empty())
454 (current_keep ? &keep : &set_free)->emplace_back(std::move(resulting_line));
455 current_keep = !current_keep;
456 }
457 resulting_line.emplace_back(me);
458 }
459 if (! resulting_line.empty())
460 (current_keep ? &keep : &set_free)->emplace_back(std::move(resulting_line));
461 }
462 validate_range(keep);
463 validate_range(set_free);
464 return std::pair<std::vector<std::vector<std::pair<Point, LineStatus>>>, std::vector<std::vector<std::pair<Point, LineStatus>>>>(keep, set_free);
465}

References validate_range().

Referenced by Slic3r::FFFTreeSupport::RichInterfacePlacer::add_points_along_lines(), and sample_overhang_area().

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

◆ support_element_collision_radius() [1/2]

coord_t Slic3r::FFFTreeSupport::support_element_collision_radius ( const TreeSupportSettings settings,
const SupportElement elem 
)
inline
289{
290 return support_element_collision_radius(settings, elem.state);
291}

References Slic3r::FFFTreeSupport::SupportElement::state, and support_element_collision_radius().

+ Here is the call graph for this function:

◆ support_element_collision_radius() [2/2]

coord_t Slic3r::FFFTreeSupport::support_element_collision_radius ( const TreeSupportSettings settings,
const SupportElementState elem 
)
inline

Get the collision Radius of this Element. This can be smaller then the actual radius, as the drawAreas will cut off areas that may collide with the model.

Parameters
elem[in]The Element.
Returns
The collision radius the element has.
247{
249}

References Slic3r::FFFTreeSupport::SupportElementState::effective_radius_height, Slic3r::FFFTreeSupport::SupportElementState::elephant_foot_increases, and Slic3r::FFFTreeSupport::TreeSupportSettings::getRadius().

Referenced by generate_support_areas(), increase_single_area(), merge_influence_areas_two_elements(), merge_support_element_states(), set_to_model_contact_to_model_gracious(), smooth_branch_areas(), and support_element_collision_radius().

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

◆ support_element_radius() [1/2]

coord_t Slic3r::FFFTreeSupport::support_element_radius ( const TreeSupportSettings settings,
const SupportElement elem 
)
inline
284{
285 return support_element_radius(settings, elem.state);
286}

References Slic3r::FFFTreeSupport::SupportElement::state, and support_element_radius().

+ Here is the call graph for this function:

◆ support_element_radius() [2/2]

coord_t Slic3r::FFFTreeSupport::support_element_radius ( const TreeSupportSettings settings,
const SupportElementState elem 
)
inline

Get the Radius, that this element will have.

Parameters
elem[in]The Element.
Returns
The radius the element has.
237{
238 return settings.getRadius(getEffectiveDTT(settings, elem), elem.elephant_foot_increases);
239}
size_t getEffectiveDTT(const TreeSupportSettings &settings, const SupportElementState &elem)
Get the Distance to top regarding the real radius this part will have. This is different from distanc...
Definition TreeSupport.hpp:224

References Slic3r::FFFTreeSupport::SupportElementState::elephant_foot_increases, getEffectiveDTT(), and Slic3r::FFFTreeSupport::TreeSupportSettings::getRadius().

Referenced by create_nodes_from_area(), extrude_branch(), finalize_raft_contact(), increase_single_area(), merge_influence_areas_two_elements(), organic_smooth_branches_avoid_collisions(), smooth_branch_areas(), support_element_radius(), and to_tree_element().

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

◆ to_tree_element()

Element Slic3r::FFFTreeSupport::to_tree_element ( const TreeSupportSettings config,
const SlicingParameters slicing_params,
SupportElement element,
bool  is_root 
)
129{
130 Element out;
131 out.position = to_3d(unscaled<float>(element.state.result_on_layer), float(layer_z(slicing_params, config, element.state.layer_idx)));
132 out.radius = support_element_radius(config, element);
133 out.layer_idx = element.state.layer_idx;
134 out.influence_area = std::move(element.influence_area);
135 out.locked = (is_root && element.state.layer_idx > 0) || element.state.locked();
136 return out;
137}
Polygons influence_area
Definition OrganicSupport.cpp:50
Vec3f position
Definition OrganicSupport.cpp:43
float radius
Definition OrganicSupport.cpp:44
bool locked
Definition OrganicSupport.cpp:53
LayerIndex layer_idx
Definition OrganicSupport.cpp:47
Definition OrganicSupport.cpp:41
bool locked() const
Definition TreeSupport.hpp:216

References Slic3r::FFFTreeSupport::Element::influence_area, Slic3r::FFFTreeSupport::SupportElement::influence_area, Slic3r::FFFTreeSupport::Element::layer_idx, Slic3r::FFFTreeSupport::SupportElementState::layer_idx, layer_z(), Slic3r::FFFTreeSupport::Element::locked, Slic3r::FFFTreeSupport::SupportElementState::locked(), Slic3r::FFFTreeSupport::Element::position, Slic3r::FFFTreeSupport::Element::radius, Slic3r::FFFTreeSupport::SupportElementState::result_on_layer, Slic3r::FFFTreeSupport::SupportElement::state, support_element_radius(), and Slic3r::to_3d().

Referenced by make_forest().

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

◆ tree_supports_show_error()

void Slic3r::FFFTreeSupport::tree_supports_show_error ( std::string_view  message,
bool  critical 
)
174{ // todo Remove! ONLY FOR PUBLIC BETA!!
175 printf("Error: %s, critical: %d\n", message.data(), int(critical));
176#ifdef TREE_SUPPORT_SHOW_ERRORS_WIN32
177 static bool showed_critical = false;
178 static bool showed_performance = false;
179 auto bugtype = std::string(critical ? " This is a critical bug. It may cause missing or malformed branches.\n" : "This bug should only decrease performance.\n");
180 bool show = (critical && !g_showed_critical_error) || (!critical && !g_showed_performance_warning);
182 if (show)
183 MessageBoxA(nullptr, std::string("TreeSupport_2 MOD detected an error while generating the tree support.\nPlease report this back to me with profile and model.\nRevision 5.0\n" + std::string(message) + "\n" + bugtype).c_str(),
184 "Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING);
185#endif // TREE_SUPPORT_SHOW_ERRORS_WIN32
186}

References g_showed_critical_error, and g_showed_performance_warning.

Referenced by Slic3r::FFFTreeSupport::RichInterfacePlacer::add_point_as_influence_area(), create_layer_pathing(), create_nodes_from_area(), ensure_maximum_distance_polyline(), Slic3r::FFFTreeSupport::TreeModelVolumes::getAvoidance(), Slic3r::FFFTreeSupport::TreeModelVolumes::getCollision(), Slic3r::FFFTreeSupport::TreeModelVolumes::getCollisionHolefree(), Slic3r::FFFTreeSupport::TreeModelVolumes::getPlaceableAreas(), Slic3r::FFFTreeSupport::TreeModelVolumes::getWallRestriction(), increase_single_area(), safe_offset_inc(), set_points_on_areas(), and set_to_model_contact_to_model_gracious().

+ Here is the caller graph for this function:

◆ triangulate_fan()

template<bool flip_normals>
void Slic3r::FFFTreeSupport::triangulate_fan ( indexed_triangle_set its,
int  ifan,
int  ibegin,
int  iend 
)
480{
481 // at least 3 vertices, increasing order.
482 assert(ibegin + 3 <= iend);
483 assert(ibegin >= 0 && iend <= its.vertices.size());
484 assert(ifan >= 0 && ifan < its.vertices.size());
485 int num_faces = iend - ibegin;
486 its.indices.reserve(its.indices.size() + num_faces * 3);
487 for (int v = ibegin, u = iend - 1; v < iend; u = v ++) {
488 if (flip_normals)
489 its.indices.push_back({ ifan, u, v });
490 else
491 its.indices.push_back({ ifan, v, u });
492 }
493}
std::vector< stl_triangle_vertex_indices > indices
Definition stl.h:164

References indexed_triangle_set::indices, and indexed_triangle_set::vertices.

◆ triangulate_strip()

static void Slic3r::FFFTreeSupport::triangulate_strip ( indexed_triangle_set its,
int  ibegin1,
int  iend1,
int  ibegin2,
int  iend2 
)
static
496{
497 // at least 3 vertices, increasing order.
498 assert(ibegin1 + 3 <= iend1);
499 assert(ibegin1 >= 0 && iend1 <= its.vertices.size());
500 assert(ibegin2 + 3 <= iend2);
501 assert(ibegin2 >= 0 && iend2 <= its.vertices.size());
502 int n1 = iend1 - ibegin1;
503 int n2 = iend2 - ibegin2;
504 its.indices.reserve(its.indices.size() + (n1 + n2) * 3);
505
506 // For the first vertex of 1st strip, find the closest vertex on the 2nd strip.
507 int istart2 = ibegin2;
508 {
509 const Vec3f &p1 = its.vertices[ibegin1];
510 auto d2min = std::numeric_limits<float>::max();
511 for (int i = ibegin2; i < iend2; ++ i) {
512 const Vec3f &p2 = its.vertices[i];
513 const float d2 = (p2 - p1).squaredNorm();
514 if (d2 < d2min) {
515 d2min = d2;
516 istart2 = i;
517 }
518 }
519 }
520
521 // Now triangulate the strip zig-zag fashion taking always the shortest connection if possible.
522 for (int u = ibegin1, v = istart2; n1 > 0 || n2 > 0;) {
523 bool take_first;
524 int u2, v2;
525 auto update_u2 = [&u2, u, ibegin1, iend1]() {
526 u2 = u;
527 if (++ u2 == iend1)
528 u2 = ibegin1;
529 };
530 auto update_v2 = [&v2, v, ibegin2, iend2]() {
531 v2 = v;
532 if (++ v2 == iend2)
533 v2 = ibegin2;
534 };
535 if (n1 == 0) {
536 take_first = false;
537 update_v2();
538 } else if (n2 == 0) {
539 take_first = true;
540 update_u2();
541 } else {
542 update_u2();
543 update_v2();
544 float l1 = (its.vertices[u2] - its.vertices[v]).squaredNorm();
545 float l2 = (its.vertices[v2] - its.vertices[u]).squaredNorm();
546 take_first = l1 < l2;
547 }
548 if (take_first) {
549 its.indices.push_back({ u, u2, v });
550 -- n1;
551 u = u2;
552 } else {
553 its.indices.push_back({ u, v2, v });
554 -- n2;
555 v = v2;
556 }
557 }
558}
T l2(const boost::geometry::model::d2::point_xy< T > &v)
Definition ExtrusionSimulator.cpp:166

References indexed_triangle_set::indices, Slic3r::l2(), and indexed_triangle_set::vertices.

Referenced by extrude_branch().

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

◆ trim_influence_areas_bottom_up()

void Slic3r::FFFTreeSupport::trim_influence_areas_bottom_up ( Forest forest,
const float  dxy_dlayer 
)
291{
292 struct Trimmer {
293 static void trim_recursive(Branch &branch, const float delta_r, const float dxy_dlayer) {
294 assert(delta_r >= 0);
295 if (delta_r > 0)
296 branch.path.front().influence_area = offset(branch.path.front().influence_area, delta_r);
297 for (size_t i = 1; i < branch.path.size(); ++ i)
298 branch.path[i].influence_area = intersection(branch.path[i].influence_area, offset(branch.path[i - 1].influence_area, dxy_dlayer));
299 const float r0 = branch.path.back().radius;
300 for (Bifurcation &up : branch.up) {
301 up.branch->path.front().influence_area = branch.path.back().influence_area;
302 trim_recursive(*up.branch, r0 - up.branch->path.front().radius, dxy_dlayer);
303 }
304 }
305 };
306
307 for (Tree &tree : forest) {
308 Branch &root = tree.root();
309 const float r0 = root.path.back().radius;
310 for (Bifurcation &up : root.up)
311 Trimmer::trim_recursive(*up.branch, r0 - up.branch->path.front().radius, dxy_dlayer);
312 }
313}
Definition OrganicSupport.cpp:80

References Slic3r::intersection(), Slic3r::FFFTreeSupport::Branch::path, and Slic3r::FFFTreeSupport::Branch::up.

+ Here is the call graph for this function:

◆ validate_range() [1/7]

static void Slic3r::FFFTreeSupport::validate_range ( const LineInformation lines)
inlinestatic
93{
94 for (const auto& p : lines)
95 validate_range(p.first);
96}

References validate_range().

+ Here is the call graph for this function:

◆ validate_range() [2/7]

static void Slic3r::FFFTreeSupport::validate_range ( const LineInformations lines)
inlinestatic
99{
100 for (const LineInformation &l : lines)
102}

References validate_range().

+ Here is the call graph for this function:

◆ validate_range() [3/7]

static void Slic3r::FFFTreeSupport::validate_range ( const MultiPoint mp)
inlinestatic
76{
78}

References Slic3r::MultiPoint::points, and validate_range().

+ Here is the call graph for this function:

◆ validate_range() [4/7]

static void Slic3r::FFFTreeSupport::validate_range ( const Point pt)
inlinestatic
63{
64 static constexpr const int32_t hi = 65536 * 16384;
65 if (pt.x() > hi || pt.y() > hi || -pt.x() > hi || -pt.y() > hi)
66 throw ClipperLib::clipperException("Coordinate outside allowed range");
67}
Definition clipper.hpp:580

Referenced by Slic3r::FFFTreeSupport::RichInterfacePlacer::add_points_along_lines(), convert_lines_to_internal(), ensure_maximum_distance_polyline(), generate_support_infill_lines(), polyline_sample_next_point_at_distance(), split_lines(), validate_range(), validate_range(), validate_range(), validate_range(), validate_range(), and validate_range().

+ Here is the caller graph for this function:

◆ validate_range() [5/7]

static void Slic3r::FFFTreeSupport::validate_range ( const Points points)
inlinestatic
70{
71 for (const Point &p : points)
73}

References validate_range().

+ Here is the call graph for this function:

◆ validate_range() [6/7]

static void Slic3r::FFFTreeSupport::validate_range ( const Polygons polygons)
inlinestatic
81{
82 for (const Polygon &p : polygons)
84}

References validate_range().

+ Here is the call graph for this function:

◆ validate_range() [7/7]

static void Slic3r::FFFTreeSupport::validate_range ( const Polylines polylines)
inlinestatic
87{
88 for (const Polyline &p : polylines)
90}

References validate_range().

+ Here is the call graph for this function:

Variable Documentation

◆ g_showed_critical_error

bool Slic3r::FFFTreeSupport::g_showed_critical_error = false

◆ g_showed_performance_warning

bool Slic3r::FFFTreeSupport::g_showed_performance_warning = false

◆ polygons_strictly_simple

constexpr const bool Slic3r::FFFTreeSupport::polygons_strictly_simple = false
staticconstexpr

◆ SUPPORT_TREE_AVOID_SUPPORT_BLOCKER

constexpr const bool Slic3r::FFFTreeSupport::SUPPORT_TREE_AVOID_SUPPORT_BLOCKER = true
staticconstexpr

◆ SUPPORT_TREE_CIRCLE_RESOLUTION

constexpr const size_t Slic3r::FFFTreeSupport::SUPPORT_TREE_CIRCLE_RESOLUTION = 25
staticconstexpr

◆ SUPPORT_TREE_COLLISION_RESOLUTION

constexpr const coord_t Slic3r::FFFTreeSupport::SUPPORT_TREE_COLLISION_RESOLUTION = scaled<coord_t>(0.5)
staticconstexpr

◆ SUPPORT_TREE_EXPONENTIAL_FACTOR

constexpr const double Slic3r::FFFTreeSupport::SUPPORT_TREE_EXPONENTIAL_FACTOR = 1.5
staticconstexpr

◆ SUPPORT_TREE_EXPONENTIAL_THRESHOLD

constexpr const coord_t Slic3r::FFFTreeSupport::SUPPORT_TREE_EXPONENTIAL_THRESHOLD = scaled<coord_t>(1. * SUPPORT_TREE_EXPONENTIAL_FACTOR)
staticconstexpr

◆ tiny_area_threshold

constexpr const auto Slic3r::FFFTreeSupport::tiny_area_threshold = sqr(scaled<double>(0.001))
staticconstexpr